diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..fea014e1 --- /dev/null +++ b/.clang-format @@ -0,0 +1,26 @@ +BasedOnStyle: Google +AccessModifierOffset: -1 +ColumnLimit: 120 +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: false + AfterEnum: true + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +DerivePointerAlignment: false +SortIncludes: false +PointerAlignment: Left +SpacesInParentheses: True +SpaceInEmptyParentheses: False diff --git a/.github/actions/apply-style/Dockerfile b/.github/actions/apply-style/Dockerfile new file mode 100644 index 00000000..83026577 --- /dev/null +++ b/.github/actions/apply-style/Dockerfile @@ -0,0 +1,7 @@ +FROM ghcr.io/llnl/radiuss:clang-14-ubuntu-22.04 + +COPY entrypoint.sh /entrypoint.sh + +USER root + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/.github/actions/apply-style/entrypoint.sh b/.github/actions/apply-style/entrypoint.sh new file mode 100755 index 00000000..bdf8012d --- /dev/null +++ b/.github/actions/apply-style/entrypoint.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +# This is a bare minimum of options needed to create the `style` build target +# This does not create a working build. +CMAKE_ARGS=-DCMAKE_CXX_COMPILER=clang++ +CMAKE_ARGS="$CMAKE_ARGS -DENABLE_CLANGFORMAT=ON" +CMAKE_ARGS="$CMAKE_ARGS -DCLANGFORMAT_EXECUTABLE=/usr/bin/clang-format" +CMAKE_ARGS="$CMAKE_ARGS -DTRIBOL_STYLE_CI_ONLY=ON" + +# Avoid error "fatal: detected dubious ownership in repository at '/github/workspace'" +REPO_PATH=/github/workspace +git config --global --add safe.directory "$REPO_PATH" +find "$REPO_PATH" -type d | while read -r dir; do + git config --global --add safe.directory "$dir" +done + +git fetch + +### +# Attempt to find the branch of the PR from the detached head state +## + +echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" +echo "Attempting to find branch that matches commit..." + +# Get the current commit SHA +current_commit_sha=$(git rev-parse HEAD) +# List all branches containing the current commit SHA +branches=$(git branch -r --contains $current_commit_sha) + +# Loop over the string split by whitespace +branch="" +num_branches_found=0 +for _possible_branch in $branches; do + # Skip items that start with "pull/" + if [[ $_possible_branch == pull/* ]]; then + continue + fi + if [[ $_possible_branch == origin/* ]]; then + _possible_branch=$(echo "$_possible_branch" | sed 's/origin\///') + fi + echo "Possible Branch: $_possible_branch" + branch=$_possible_branch + num_branches_found=$((num_branches_found+1)) +done + +if [ "$num_branches_found" -ne 1 ]; then + echo "Error: Unable to find a single branch that matched git sha $current_commit_sha" + exit 1 +fi + +echo "Found branch: $branch" +echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + +git checkout $branch + +git submodule update --init --recursive + +mkdir build && cd build +cmake $CMAKE_ARGS .. +make style +cd .. + +git config user.name "Agent Style" +git config user.email "no-reply@llnl.gov" +if [ -n "$(git status --porcelain)" ]; then + git commit -am 'Apply style updates' + git push +fi diff --git a/.github/workflows/apply-style.yml b/.github/workflows/apply-style.yml new file mode 100644 index 00000000..77c36f4f --- /dev/null +++ b/.github/workflows/apply-style.yml @@ -0,0 +1,28 @@ +name: Apply Style + +on: + issue_comment: + types: [created] + +jobs: + apply-style: + if: startsWith(github.event.comment.body, '/style') + name: Apply Style to Source + runs-on: ubuntu-latest + + steps: + # Checkout the GitHub created reference for the PR. + # The only way to do this is by using the "issue" number + # but that is really the PR number in this context. + # This is due to using an `issue_comment` event which + # is due to the GitHub Actions API that does not have + # a `pull_request_comment` event or something similar. + # This leaves us in a detached head state which is corrected + # in `apply-style/entrypoint.sh` + - name: Checkout pull request + uses: actions/checkout@v3 + with: + ref: refs/pull/${{ github.event.issue.number }}/head + + - name: Apply style updates + uses: ./.github/actions/apply-style diff --git a/CMakeLists.txt b/CMakeLists.txt index 280112b8..5894a9a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,9 +48,35 @@ if ("${PROJECT_SOURCE_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}") set(BLT_CXX_STD "c++14" CACHE STRING "") endif() - set(ENABLE_ASTYLE OFF CACHE BOOL "") - set(ENABLE_CLANGFORMAT OFF CACHE BOOL "") - set(ENABLE_UNCRUSTIFY OFF CACHE BOOL "") + # These BLT tools are not used in Tribol, turn them off + set(_unused_blt_tools + CLANGQUERY + VALGRIND + ASTYLE + CMAKEFORMAT + UNCRUSTIFY + YAPF) + foreach(_tool ${_unused_blt_tools}) + set(ENABLE_${_tool} OFF CACHE BOOL "") + endforeach() + + # These BLT tools are only used by Tribol developers, so turn them off + # unless an explicit executable path is given + set(_used_blt_tools + CLANGFORMAT + CLANGTIDY + CPPCHECK + DOXYGEN + SPHINX) + foreach(_tool ${_used_blt_tools}) + if(NOT ${_tool}_EXECUTABLE) + set(ENABLE_${_tool} OFF CACHE BOOL "") + else() + set(ENABLE_${_tool} ON CACHE BOOL "") + endif() + endforeach() + + set(BLT_REQUIRED_CLANGFORMAT_VERSION "14" CACHE STRING "") endif() include(${BLT_SOURCE_DIR}/SetupBLT.cmake) @@ -75,6 +101,32 @@ include(cmake/TribolMacros.cmake) include(cmake/Options.cmake) include(cmake/TribolCompilerFlags.cmake) +#------------------------------------------------------------------------------ +# Add Code Checks +#------------------------------------------------------------------------------ + +message(STATUS "Tribol Code Checks: ${TRIBOL_ENABLE_CODE_CHECKS}") + +# Add extra file extensions for code styling +# Note: Add them also to tribol_add_code_checks in cmake/TribolMacros.cmake + +# CUDA +list(APPEND BLT_C_FILE_EXTS ".cuh") +# Configured C++ files +list(APPEND BLT_C_FILE_EXTS ".cpp.in" ".hpp.in") + +if (TRIBOL_STYLE_CI_ONLY OR TRIBOL_ENABLE_CODE_CHECKS) + tribol_add_code_checks(PREFIX tribol) +endif() + +if (TRIBOL_STYLE_CI_ONLY) + # Exit processing the rest of the build in style only build to avoid any possible + # CMake configuration issues outside of just enabling `make style`. This build, + # is not capable of building Tribol and should not be advertised. It is for CI + # purposes only. + return() +endif() + #------------------------------------------------------------------------------ # Add TPLs and source directories #------------------------------------------------------------------------------ diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ca5dc0c8..1b5aac97 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -67,42 +67,47 @@ jobs: testRunTitle: '$(TEST_TARGET) Tests' failTaskOnFailedTests: true -#TODO: Enable if you want code checks -#- job: Check_Code -# variables: -# VM_ImageName: 'ubuntu-22.04' -# Compiler_ImageName: $(Clang_14_ImageName) -# TEST_TARGET: 'linux_clang14' -# HOST_CONFIG: 'clang@14.0.0.cmake' -# -# strategy: -# matrix: -#TODO: Enable if you want codecov support -# coverage: -# DO_COVERAGE_CHECK: 'yes' -# DO_DOCS_CHECK: 'no' -# DO_STYLE_CHECK: 'no' -#TODO: Enable if you want to ensure documentation does not have warnings -# docs: -# DO_COVERAGE_CHECK: 'no' -# DO_DOCS_CHECK: 'yes' -# DO_STYLE_CHECK: 'no' -#TODO: Enable if you want to enforce style checks -# style: -# DO_COVERAGE_CHECK: 'no' -# DO_DOCS_CHECK: 'no' -# DO_STYLE_CHECK: 'yes' -# -# pool: -# vmImage: $(VM_ImageName) -# -# steps: -# - checkout: self -# clean: true -# submodules: recursive -# # https://docs.codecov.io/docs/testing-with-docker -# - script: | -# ci_env=`bash <(curl -s https://codecov.io/env)` -# echo " -e DO_COVERAGE_CHECK -e DO_DOCS_CHECK -e DO_STYLE_CHECK -e HOST_CONFIG ./tribol/scripts/azure-pipelines/linux-check.sh" -# docker run --rm -v `pwd`:$(HOME_DIR)/tribol $ci_env -e DO_COVERAGE_CHECK -e DO_DOCS_CHECK -e DO_STYLE_CHECK -e HOST_CONFIG $(Compiler_ImageName) ./tribol/scripts/azure-pipelines/linux-check.sh -# displayName: '$(TEST_TARGET) Check' +- job: Check_Code + variables: + VM_ImageName: 'ubuntu-22.04' + Compiler_ImageName: $(Clang_14_ImageName) + TEST_TARGET: 'linux_clang14' + HOST_CONFIG: 'clang@14.0.0.cmake' + + strategy: + matrix: + coverage: + DO_COVERAGE_CHECK: 'yes' + DO_DOCS_CHECK: 'no' + DO_STYLE_CHECK: 'no' + DO_HEADER_CHECK: 'no' +# TODO: enable to check doxygen for warnings + # docs: + # DO_COVERAGE_CHECK: 'no' + # DO_DOCS_CHECK: 'yes' + # DO_STYLE_CHECK: 'no' + # DO_HEADER_CHECK: 'no' + style: + DO_COVERAGE_CHECK: 'no' + DO_DOCS_CHECK: 'no' + DO_STYLE_CHECK: 'yes' + DO_HEADER_CHECK: 'no' + header: + DO_COVERAGE_CHECK: 'no' + DO_DOCS_CHECK: 'no' + DO_STYLE_CHECK: 'no' + DO_HEADER_CHECK: 'yes' + + pool: + vmImage: $(VM_ImageName) + + steps: + - checkout: self + clean: true + submodules: recursive + # https://docs.codecov.io/docs/testing-with-docker + - script: | + ci_env=`bash <(curl -s https://codecov.io/env)` + echo " -e DO_COVERAGE_CHECK -e DO_DOCS_CHECK -e DO_STYLE_CHECK -e HOST_CONFIG ./tribol/scripts/azure-pipelines/linux-check.sh" + docker run --rm -v `pwd`:$(HOME_DIR)/tribol $ci_env -e DO_COVERAGE_CHECK -e DO_DOCS_CHECK -e DO_STYLE_CHECK -e HOST_CONFIG $(Compiler_ImageName) ./tribol/scripts/azure-pipelines/linux-check.sh + displayName: '$(TEST_TARGET) Check' diff --git a/cmake/TribolMacros.cmake b/cmake/TribolMacros.cmake index dfd88ca1..47b0cf10 100644 --- a/cmake/TribolMacros.cmake +++ b/cmake/TribolMacros.cmake @@ -3,6 +3,84 @@ # # SPDX-License-Identifier: (MIT) + +#------------------------------------------------------------------------------ +# tribol_add_code_checks( PREFIX [prefix] ) +# +# Adds code checks for all cpp/hpp files recursively under the current directory +# that regex match INCLUDES and excludes any files that regex match EXCLUDES +# +# This creates the following parent build targets: +# check - Runs a non file changing style check and CppCheck +# style - In-place code formatting +# +# Creates various child build targets that follow this pattern: +# tribol_ +# tribol__ +# +# This also creates targets for running clang-tidy on the src/ and test/ +# directories, with a more permissive set of checks for the tests, +# called tribol_guidelines_check and tribol_guidelines_check_tests, respectively +#------------------------------------------------------------------------------ +macro(tribol_add_code_checks) + + set(options) + set(singleValueArgs PREFIX) + set(multiValueArgs) + + # Parse the arguments to the macro + cmake_parse_arguments(arg + "${options}" "${singleValueArgs}" "${multiValueArgs}" ${ARGN}) + + # Create file globbing expressions that only include directories that contain source + set(_base_dirs "src") + # Note: any extensions added here should also be added to BLT's lists in CMakeLists.txt + set(_ext_expressions "*.cpp" "*.hpp" "*.inl" "*.cuh" "*.cu" "*.cpp.in" "*.hpp.in") + + set(_glob_expressions) + foreach(_exp ${_ext_expressions}) + foreach(_base_dir ${_base_dirs}) + list(APPEND _glob_expressions "${PROJECT_SOURCE_DIR}/${_base_dir}/${_exp}") + endforeach() + endforeach() + + # Glob for list of files to run code checks on + set(_sources) + file(GLOB_RECURSE _sources ${_glob_expressions}) + + blt_add_code_checks(PREFIX ${arg_PREFIX} + SOURCES ${_sources} + CLANGFORMAT_CFG_FILE ${PROJECT_SOURCE_DIR}/.clang-format + CPPCHECK_FLAGS --enable=all --inconclusive) + + + set(_src_sources) + file(GLOB_RECURSE _src_sources "src/*.cpp" "src/*.hpp" "src/*.inl") + list(FILTER _src_sources EXCLUDE REGEX ".*/tests/.*pp") + + blt_add_clang_tidy_target(NAME ${arg_PREFIX}_guidelines_check + CHECKS "clang-analyzer-*,clang-analyzer-cplusplus*,cppcoreguidelines-*" + SRC_FILES ${_src_sources}) + + # Create list of recursive test directory glob expressions + # NOTE: GLOB operator ** did not appear to be supported by cmake and did not recursively find test subdirectories + # NOTE: Do not include all directories at root (for example: blt) + + file(GLOB_RECURSE _test_sources "${PROJECT_SOURCE_DIR}/src/*.cpp" "${PROJECT_SOURCE_DIR}/tests/*.cpp") + list(FILTER _test_sources INCLUDE REGEX ".*/tests/.*pp") + + blt_add_clang_tidy_target(NAME ${arg_PREFIX}_guidelines_check_tests + CHECKS "clang-analyzer-*,clang-analyzer-cplusplus*,cppcoreguidelines-*,-cppcoreguidelines-avoid-magic-numbers" + SRC_FILES ${_test_sources}) + + if (ENABLE_COVERAGE) + blt_add_code_coverage_target(NAME ${arg_PREFIX}_coverage + RUNNER ${CMAKE_MAKE_PROGRAM} test + SOURCE_DIRECTORIES ${PROJECT_SOURCE_DIR}/src ) + endif() + +endmacro(tribol_add_code_checks) + ##------------------------------------------------------------------------------ ## tribol_assert_path_exists( path ) ## diff --git a/host-configs/docker/clang@14.0.0.cmake b/host-configs/docker/clang@14.0.0.cmake index 41fd62ce..f27c200c 100644 --- a/host-configs/docker/clang@14.0.0.cmake +++ b/host-configs/docker/clang@14.0.0.cmake @@ -63,6 +63,12 @@ set(ENABLE_MPI ON CACHE BOOL "") set(ENABLE_OPENMP ON CACHE BOOL "") +#------------------------------------------------------------------------------ +# Options +#------------------------------------------------------------------------------ + +set(BUILD_REDECOMP ON CACHE BOOL "") + #------------------------------------------------------------------------------ # TPLs #------------------------------------------------------------------------------ diff --git a/host-configs/docker/gcc@13.1.0.cmake b/host-configs/docker/gcc@13.1.0.cmake index d44794a3..3738a64d 100644 --- a/host-configs/docker/gcc@13.1.0.cmake +++ b/host-configs/docker/gcc@13.1.0.cmake @@ -63,6 +63,12 @@ set(ENABLE_MPI ON CACHE BOOL "") set(ENABLE_OPENMP ON CACHE BOOL "") +#------------------------------------------------------------------------------ +# Options +#------------------------------------------------------------------------------ + +set(BUILD_REDECOMP ON CACHE BOOL "") + #------------------------------------------------------------------------------ # TPLs #------------------------------------------------------------------------------ diff --git a/host-configs/ruby-toss_4_x86_64_ib-clang@14.0.6.cmake b/host-configs/ruby-toss_4_x86_64_ib-clang@14.0.6.cmake index aa7f6108..c131c2fa 100644 --- a/host-configs/ruby-toss_4_x86_64_ib-clang@14.0.6.cmake +++ b/host-configs/ruby-toss_4_x86_64_ib-clang@14.0.6.cmake @@ -85,6 +85,10 @@ set(UMPIRE_DIR "${TPL_ROOT}/umpire-2024.02.0-g6kj4glsxlrqr4jtsbacehorawncptex" C set(DEVTOOLS_ROOT "/usr/WS2/smithdev/devtools/toss_4_x86_64_ib/2023_11_04_22_38_05/._view/i6qqmxmihtba7m3eojwix64slyuqqcsv" CACHE PATH "") +set(TRIBOL_ENABLE_CODE_CHECKS ON CACHE BOOL "") + +set(ENABLE_CLANGFORMAT ON CACHE BOOL "") + set(CLANGFORMAT_EXECUTABLE "/usr/tce/packages/clang/clang-14.0.6/bin/clang-format" CACHE PATH "") set(CLANGTIDY_EXECUTABLE "/usr/tce/packages/clang/clang-14.0.6/bin/clang-tidy" CACHE PATH "") diff --git a/host-configs/ruby-toss_4_x86_64_ib-gcc@10.3.1.cmake b/host-configs/ruby-toss_4_x86_64_ib-gcc@10.3.1.cmake index 549e7d15..bb6d32b8 100644 --- a/host-configs/ruby-toss_4_x86_64_ib-gcc@10.3.1.cmake +++ b/host-configs/ruby-toss_4_x86_64_ib-gcc@10.3.1.cmake @@ -86,6 +86,10 @@ set(UMPIRE_DIR "${TPL_ROOT}/umpire-2024.02.0-fv6f2qx4x63mcrzdkpmw7ls4tlrckdyq" C set(DEVTOOLS_ROOT "/usr/WS2/smithdev/devtools/toss_4_x86_64_ib/2023_11_04_22_38_05/._view/i6qqmxmihtba7m3eojwix64slyuqqcsv" CACHE PATH "") +set(TRIBOL_ENABLE_CODE_CHECKS ON CACHE BOOL "") + +set(ENABLE_CLANGFORMAT ON CACHE BOOL "") + set(CLANGFORMAT_EXECUTABLE "/usr/tce/packages/clang/clang-14.0.6/bin/clang-format" CACHE PATH "") set(CLANGTIDY_EXECUTABLE "/usr/tce/packages/clang/clang-14.0.6/bin/clang-tidy" CACHE PATH "") diff --git a/host-configs/rzgenie-toss_4_x86_64_ib-clang@14.0.6.cmake b/host-configs/rzgenie-toss_4_x86_64_ib-clang@14.0.6.cmake index 195bcc43..d58f96cd 100644 --- a/host-configs/rzgenie-toss_4_x86_64_ib-clang@14.0.6.cmake +++ b/host-configs/rzgenie-toss_4_x86_64_ib-clang@14.0.6.cmake @@ -85,6 +85,10 @@ set(UMPIRE_DIR "${TPL_ROOT}/umpire-2024.02.0-g6kj4glsxlrqr4jtsbacehorawncptex" C set(DEVTOOLS_ROOT "/usr/WS2/smithdev/devtools/toss_4_x86_64_ib/2023_11_03_16_58_50/._view/ronvl3f6draudlqfuywypbscahbjsugl" CACHE PATH "") +set(TRIBOL_ENABLE_CODE_CHECKS ON CACHE BOOL "") + +set(ENABLE_CLANGFORMAT ON CACHE BOOL "") + set(CLANGFORMAT_EXECUTABLE "/usr/tce/packages/clang/clang-14.0.6/bin/clang-format" CACHE PATH "") set(CLANGTIDY_EXECUTABLE "/usr/tce/packages/clang/clang-14.0.6/bin/clang-tidy" CACHE PATH "") diff --git a/host-configs/rzgenie-toss_4_x86_64_ib-gcc@10.3.1.cmake b/host-configs/rzgenie-toss_4_x86_64_ib-gcc@10.3.1.cmake index daaac240..dc34f388 100644 --- a/host-configs/rzgenie-toss_4_x86_64_ib-gcc@10.3.1.cmake +++ b/host-configs/rzgenie-toss_4_x86_64_ib-gcc@10.3.1.cmake @@ -85,6 +85,10 @@ set(UMPIRE_DIR "${TPL_ROOT}/umpire-2024.02.0-fv6f2qx4x63mcrzdkpmw7ls4tlrckdyq" C set(DEVTOOLS_ROOT "/usr/WS2/smithdev/devtools/toss_4_x86_64_ib/2023_11_03_16_58_50/._view/ronvl3f6draudlqfuywypbscahbjsugl" CACHE PATH "") +set(TRIBOL_ENABLE_CODE_CHECKS ON CACHE BOOL "") + +set(ENABLE_CLANGFORMAT ON CACHE BOOL "") + set(CLANGFORMAT_EXECUTABLE "/usr/tce/packages/clang/clang-14.0.6/bin/clang-format" CACHE PATH "") set(CLANGTIDY_EXECUTABLE "/usr/tce/packages/clang/clang-14.0.6/bin/clang-tidy" CACHE PATH "") diff --git a/scripts/azure-pipelines/docs_ignore_regexs.txt b/scripts/azure-pipelines/docs_ignore_regexs.txt new file mode 100644 index 00000000..42c4a32a --- /dev/null +++ b/scripts/azure-pipelines/docs_ignore_regexs.txt @@ -0,0 +1,7 @@ +# latex error that is specific to the docker image +Error: \/undefined in getenv +Package newunicodechar Warning: Redefining Unicode character on input line + +# doxygen is having problems handling the inherited constructors +.*argument 'mesh' of command @param is not found in the argument list of serac::.*::FiniteElementVector\(typename FunctionSpace\) +.*argument 'name' of command @param is not found in the argument list of serac::.*::FiniteElementVector\(typename FunctionSpace\) \ No newline at end of file diff --git a/scripts/azure-pipelines/linux-check.sh b/scripts/azure-pipelines/linux-check.sh index 8aba5605..d9080b27 100755 --- a/scripts/azure-pipelines/linux-check.sh +++ b/scripts/azure-pipelines/linux-check.sh @@ -25,7 +25,7 @@ echo HOST_CONFIG echo $HOST_CONFIG echo "~~~~~~ RUNNING CMAKE ~~~~~~~~" -cmake_args="-DENABLE_CLANGTIDY=OFF" +cmake_args="-DENABLE_CLANGTIDY=OFF -DTRIBOL_ENABLE_CODE_CHECKS=ON" if [[ "$DO_COVERAGE_CHECK" == "yes" ]] ; then # Alias llvm-cov to gcov so it acts like gcov @@ -61,9 +61,9 @@ or_die cd build-check-debug if [[ "$DO_COVERAGE_CHECK" == "yes" ]] ; then or_die make -j4 - or_die make serac_coverage + or_die make tribol_coverage # Rename to file expected by codecov - cp serac_coverage.info.cleaned lcov.info + cp tribol_coverage.info.cleaned lcov.info or_die curl -s https://codecov.io/bash | bash /dev/stdin -X gcov fi @@ -73,7 +73,13 @@ if [[ "$DO_DOCS_CHECK" == "yes" ]] ; then fi if [[ "$DO_STYLE_CHECK" == "yes" ]] ; then - or_die make VERBOSE=1 check + or_die make VERBOSE=1 clangformat_check +fi + +if [[ "$DO_HEADER_CHECK" == "yes" ]] ; then + or_die make -j4 + or_die make install -j4 + or_die ../scripts/check_for_missing_headers.py -i ../install-check-debug -s ../src fi exit 0 diff --git a/scripts/check_for_missing_headers.py b/scripts/check_for_missing_headers.py new file mode 100755 index 00000000..8df81527 --- /dev/null +++ b/scripts/check_for_missing_headers.py @@ -0,0 +1,93 @@ +#!/bin/sh +"exec" "python3" "-u" "-B" "$0" "$@" + +# Copyright (c) 2017-2023, Lawrence Livermore National Security, LLC. + +""" + file: check_for_missing_headers.py + + description: + This script takes a Tribol install and source directory and checks to see + if install includes the same header files. + +""" + +import os +import sys +import argparse + +def parse_arguments(): + parser = argparse.ArgumentParser() + parser.add_argument("-i", + "--install-dir", + default="", + dest="install_dir", + help="specify path of the install directory.") + parser.add_argument("-s", + "--src-dir", + default="", + dest="src_dir", + help="specify path of the src directory.") + return parser.parse_known_args() + +# return list of relative paths to header files +def get_headers_from(dir): + headers = [] + for (dirpath, dirnames, filenames) in os.walk(dir, topdown=True): + # skip tests directories + if "tests" in dirnames: + dirnames.remove("tests") + for f in filenames: + if ".hpp" in f and ".in" not in f: + relative_header_path = dirpath.replace(dir + "/", "") + headers.append({"path": relative_header_path, "headerfile": f}) + return headers + +def main(): + args, unknown_args = parse_arguments() + + # ensure args are valid + install_dir = args.install_dir + if not os.path.isdir(install_dir): + print("Error: install_dir is not a directory or does not exist: {}".format(install_dir)) + return 1 + install_dir = os.path.abspath(install_dir) + + src_dir = args.src_dir + if not os.path.isdir(src_dir): + print("Error: src_dir is not a directory or does not exist: {}".format(src_dir)) + return 1 + src_dir = os.path.abspath(src_dir) + + print("============================================================") + print("check_for_missing_headers.py args") + print("install_dir: {0}".format(install_dir)) + print("src_dir: {0}".format(src_dir)) + print("============================================================") + + # grab headers from install and src + install_headers = get_headers_from(os.path.join(install_dir, "include", "tribol")) + src_headers = get_headers_from(os.path.join(src_dir, "tribol")) + + # check if each header in src is in install as well + res = 0 + for sh in src_headers: + found = False + for ih in install_headers: + src_relative_header = "{0}/{1}".format(sh["path"], sh["headerfile"]) + install_relative_header = "{0}/{1}".format(ih["path"], ih["headerfile"]) + if src_relative_header == install_relative_header: + found = True + break + if not found: + cmakelists_path = os.path.join(src_dir, sh["path"], "CMakeLists.txt") + print("Header '{0}' is missing; it should probably be listed in {1}".format(sh["headerfile"], cmakelists_path)) + res = 1 + + if res == 0: + print("No missing headers found.") + + return res + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/check_log.py b/scripts/check_log.py new file mode 100755 index 00000000..7005e4ce --- /dev/null +++ b/scripts/check_log.py @@ -0,0 +1,95 @@ +#!/bin/sh +"exec" "python3" "-u" "-B" "$0" "$@" + +# Copyright (c) 2017-2023, Lawrence Livermore National Security, LLC. + +import argparse +import os +import re +import sys + +def parse_args(): + parser = argparse.ArgumentParser(description="Checks log for warnings and errors") + + parser.add_argument("-l", "--log", type=str, required=True, help="Path to log file to be checked") + parser.add_argument("-i", "--ignore", type=str, required=False, help="Path to file that includes regex's to ignore lines") + + args = parser.parse_args() + + print("~~~~~~~ Given Command line Arguments ~~~~~~~") + print("Ignore file Path: {0}".format(args.ignore)) + print("Log Path: {0}".format(args.log)) + print("") + + return args + + +def main(): + args = parse_args() + + # read lines from log + with open(args.log, "r") as f: + log_lines = f.readlines() + + # read ignore regex's + ignore_regexs = [] + if args.ignore: + with open(args.ignore, "r") as f: + # Only save lines that aren't comments ("#") + for currline in f.readlines(): + stripped = currline.strip() + if stripped != "" and not stripped.startswith("#"): + # Add regex w/o trailing newline characters + ignore_regexs.append(currline.rstrip()) + + # Get warnings/errors out of log file and print ignored as you find them + # with what regex matched + warnings = [] + errors = [] + ignored_count = 0 + print("~~~~~~~~~~~~~~~ Ignored ~~~~~~~~~~~~~~~~") + for log_line in log_lines: + # First, check if it is a warning/error + lowered = log_line.lower() + has_warning = "warning:" in lowered + has_error = "error:" in lowered + + # Then, check if it matches any ignore regex's + matches_ignore = False + if has_error or has_warning: + for ignore_regex in ignore_regexs: + if re.search(ignore_regex, log_line.rstrip()): + print("line : {0}".format(log_line.rstrip())) + print("regex: {0}\n".format(ignore_regex)) + matches_ignore = True + ignored_count += 1 + break + + if not matches_ignore: + if has_error: + errors.append(log_line) + elif has_warning: + warnings.append(log_line) + + # Print warnings/errors that are found + print("~~~~~~~~~~~~~~~ Warnings ~~~~~~~~~~~~~~~") + for warning in warnings: + print(warning) + print("") + + print("~~~~~~~~~~~~~~~ Errors ~~~~~~~~~~~~~~~~~") + for error in errors: + print(error) + print("") + + # Print summary info + print("~~~~~~~~~~~~~~~~~ Summary ~~~~~~~~~~~~~~~~~~") + print("Warning Count: {0}".format(len(warnings))) + print("Error Count: {0}".format(len(errors))) + print("Ignored Count: {0}".format(ignored_count)) + + # Error out if any found + return len(warnings) + len(errors) + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/spack/configs/macos_sonoma_aarch64/spack.yaml b/scripts/spack/configs/macos_sequoia_aarch64/spack.yaml similarity index 70% rename from scripts/spack/configs/macos_sonoma_aarch64/spack.yaml rename to scripts/spack/configs/macos_sequoia_aarch64/spack.yaml index a9f41ed3..f20c39d1 100644 --- a/scripts/spack/configs/macos_sonoma_aarch64/spack.yaml +++ b/scripts/spack/configs/macos_sequoia_aarch64/spack.yaml @@ -1,8 +1,26 @@ spack: + # The "::" removes all found/known compilers from Spack except for these. + compilers:: + - compiler: + spec: clang@=19.1.4 + paths: + cc: /opt/homebrew/opt/llvm/bin/clang + cxx: /opt/homebrew/opt/llvm/bin/clang++ + f77: /opt/homebrew/bin/gfortran-14 + fc: /opt/homebrew/bin/gfortran-14 + flags: {} + operating_system: sequoia + target: aarch64 + modules: [] + environment: {} + extra_rpaths: + - /opt/homebrew/lib/gcc/14 # add package specs to the `specs` list + view: true concretizer: unify: true + packages: all: compiler: [clang, gcc] @@ -10,6 +28,7 @@ spack: blas: [netlib-lapack] lapack: [netlib-lapack] mpi: [openmpi] + mpi: buildable: false openmpi: @@ -17,43 +36,54 @@ spack: externals: - spec: openmpi@5.0.6 prefix: /opt/homebrew + + hdf5: + version: [1.14.5] + buildable: false + externals: + - spec: hdf5@1.14.5 + prefix: /opt/homebrew + lua: + version: [5.4.7] + buildable: false + externals: + - spec: lua@5.4.7 + prefix: /opt/homebrew netlib-lapack: buildable: false externals: - spec: netlib-lapack@3.12.0 prefix: /opt/homebrew/opt/lapack + + # Devtools cmake: version: [3.31.1] buildable: false externals: - spec: cmake@3.31.1 prefix: /opt/homebrew - hdf5: - version: [1.14.5] + doxygen: + version: [1.12.0] buildable: false externals: - - spec: hdf5@1.14.5 + - spec: doxygen@1.12.0 prefix: /opt/homebrew - lua: - version: [5.4.7] + llvm: + version: [14.0.6] buildable: false externals: - - spec: lua@5.4.7 + - spec: llvm@14.0.6+clang + prefix: /opt/homebrew/opt/llvm@14 + python: + version: [3.13.1] + buildable: false + externals: + - spec: python@3.13.1 + prefix: /opt/homebrew + py-sphinx: + version: [8.1.3] + buildable: false + externals: + - spec: py-sphinx@8.1.3 prefix: /opt/homebrew - # The "::" removes all found/known compilers from Spack except for these. - compilers:: - - compiler: - spec: clang@=19.1.4 - paths: - cc: /opt/homebrew/opt/llvm/bin/clang - cxx: /opt/homebrew/opt/llvm/bin/clang++ - f77: /opt/homebrew/bin/gfortran-14 - fc: /opt/homebrew/bin/gfortran-14 - flags: {} - operating_system: sequoia - target: aarch64 - modules: [] - environment: {} - extra_rpaths: - - /opt/homebrew/lib/gcc/14 diff --git a/scripts/spack/packages/tribol/package.py b/scripts/spack/packages/tribol/package.py index bbbc9646..23672c41 100644 --- a/scripts/spack/packages/tribol/package.py +++ b/scripts/spack/packages/tribol/package.py @@ -142,7 +142,7 @@ class Tribol(CachedCMakePackage, CudaPackage, ROCmPackage): depends_on("python", when="+devtools") depends_on("py-shroud", when="+devtools+fortran") depends_on("py-sphinx", when="+devtools") - depends_on("llvm+clang@10.0.0", when="+devtools", type="build") + depends_on("llvm+clang@14", when="+devtools", type="build") conflicts("+cuda", when="+rocm") conflicts("+openmp", when="+rocm") diff --git a/src/examples/common_plane.cpp b/src/examples/common_plane.cpp index 59c35d3a..c39054b0 100644 --- a/src/examples/common_plane.cpp +++ b/src/examples/common_plane.cpp @@ -3,7 +3,7 @@ // // SPDX-License-Identifier: (MIT) -#include "examples_common.hpp" // for common functionality used in examples +#include "examples_common.hpp" // for common functionality used in examples #include "tribol/interface/tribol.hpp" #include "tribol/utils/TestUtils.hpp" @@ -28,14 +28,17 @@ #include // for std::string and operators #include // for std::ostringstream -// Example command line arguments for running this example. This test creates two rectangular blocks of dimensions (l x w x h). The dimensions are -// set such that an initial block-intersection exists, triggering the contact interaction. The blocks are discretized per the "block#_res xx yy zz" input -// arguments (e.g. block1_res 5 3 4, and block2_res 3 2 1), where "xx", "yy", and "zz" are the number of elements in the x, y and z directions. -// Varying "xx" and "yy" will vary the number of contact overlaps that exist between the two surfaces, and therefore, the amount of contact work. +// Example command line arguments for running this example. This test creates two rectangular blocks of dimensions (l x +// w x h). The dimensions are set such that an initial block-intersection exists, triggering the contact interaction. +// The blocks are discretized per the "block#_res xx yy zz" input arguments (e.g. block1_res 5 3 4, and block2_res 3 2 +// 1), where "xx", "yy", and "zz" are the number of elements in the x, y and z directions. Varying "xx" and "yy" will +// vary the number of contact overlaps that exist between the two surfaces, and therefore, the amount of contact work. // -// srun -n1 ./common_plane_ex --block1_res 100 50 10 --block1_min 0 0 0 --block1_max 10 5 1 --block2_res 150 75 10 --block2_min 0 0 0.95 --block2_max 10 5 1.95 -// srun -n1 ./common_plane_ex --block1_res 100 50 4 --block1_min 0 0 0. --block1_max 1 1 1.05 --block2_res 150 75 4 --block2_min 0 0 0.95 --block2_max 1 1 2 -// srun -n1 ./common_plane_ex --block1_res 4 4 4 --block1_min 0 0 0. --block1_max 1 1 1.05 --block2_res 4 4 4 --block2_min 0 0 0.95 --block2_max 1 1 2 +// srun -n1 ./common_plane_ex --block1_res 100 50 10 --block1_min 0 0 0 --block1_max 10 5 1 --block2_res 150 75 10 +// --block2_min 0 0 0.95 --block2_max 10 5 1.95 srun -n1 ./common_plane_ex --block1_res 100 50 4 --block1_min 0 0 0. +// --block1_max 1 1 1.05 --block2_res 150 75 4 --block2_min 0 0 0.95 --block2_max 1 1 2 srun -n1 ./common_plane_ex +// --block1_res 4 4 4 --block1_min 0 0 0. --block1_max 1 1 1.05 --block2_res 4 4 4 --block2_min 0 0 0.95 +// --block2_max 1 1 2 /*! * \brief Program main. @@ -68,25 +71,25 @@ int main( int argc, char** argv ) // parse command line arguments Arguments args; - { + { // set common plane + penalty specific options - args.dimension = 3; // problems currently only setup for 3D + args.dimension = 3; // problems currently only setup for 3D args.penalty_stiffness = 1.0; args.dump_vis = true; // parse the command line arguments - parse_command_line_args( "Common plane example", args, argc, argv ); + parse_command_line_args( "Common plane example", args, argc, argv ); } - - // instantiate test mesh object. Note, this mesh object is a Tribol - // utility for testing. In general, a physics application will have + + // instantiate test mesh object. Note, this mesh object is a Tribol + // utility for testing. In general, a physics application will have // their own mesh data. tribol::TestMesh mesh; build_mesh_3D( mesh, args, NO_BCS ); tribol::TestControlParameters parameters; parameters.penalty_ratio = false; - parameters.const_penalty = args.penalty_stiffness; + parameters.const_penalty = args.penalty_stiffness; //////////////////////////////////////////////////// // // @@ -97,17 +100,13 @@ int main( int argc, char** argv ) // API function calls // // // //////////////////////////////////////////////////// - int err = tribol_register_and_update( mesh, tribol::COMMON_PLANE, - tribol::PENALTY, tribol::FRICTIONLESS, + int err = tribol_register_and_update( mesh, tribol::COMMON_PLANE, tribol::PENALTY, tribol::FRICTIONLESS, args.dump_vis, ¶meters ); - if (err == 1) - { - SLIC_WARNING("Returned from tribol_register_and_update with error."); - } - else - { - SLIC_INFO("Example ran successfully."); + if ( err == 1 ) { + SLIC_WARNING( "Returned from tribol_register_and_update with error." ); + } else { + SLIC_INFO( "Example ran successfully." ); } axom::slic::flushStreams(); @@ -119,4 +118,3 @@ int main( int argc, char** argv ) return 0; } - diff --git a/src/examples/common_plane_gpu.cpp b/src/examples/common_plane_gpu.cpp index ccb69768..31bb36bd 100644 --- a/src/examples/common_plane_gpu.cpp +++ b/src/examples/common_plane_gpu.cpp @@ -5,9 +5,9 @@ /** * @file common_plane_gpu.cpp - * + * * @brief Example computing common plane forces on host and/or device - * + * * This example demostrates using Tribol's common plane contact method on host * and/or device (where available). This text assumes the code is running on * device, as data transfers are trivial for the case of running on host. First, @@ -15,15 +15,15 @@ * coordinates, velocity, force (response), and connectivity are transferred to * device and registered with Tribol. Then, tribol::update() is called to * compute forces. - * + * * This process is repeated for several different levels of mesh refinement, * giving an idea of throughput running on different platforms. - * + * * Example runs (from repo root directory): * - {build_dir}/examples/common_plane_gpu_ex -r 5 -d cpu * - {build_dir}/examples/common_plane_gpu_ex -r 5 -d gpu * - {build_dir}/examples/common_plane_gpu_ex -r 5 -d omp - * + * * @note This example is only for serial meshes. Running in parallel requires * integration with the redecomp domain repartitioning library. */ @@ -42,7 +42,7 @@ #endif template -int runExample(int num_elems_1d); +int runExample( int num_elems_1d ); /*! * \brief Program main. @@ -56,19 +56,18 @@ int runExample(int num_elems_1d); */ int main( int argc, char** argv ) { - // initialize MPI - int np {1}; - int rank{0}; + int np{ 1 }; + int rank{ 0 }; #ifdef TRIBOL_USE_MPI MPI_Init( &argc, &argv ); - MPI_Comm_size(TRIBOL_COMM_WORLD, &np); - MPI_Comm_rank(TRIBOL_COMM_WORLD, &rank); + MPI_Comm_size( TRIBOL_COMM_WORLD, &np ); + MPI_Comm_rank( TRIBOL_COMM_WORLD, &rank ); #endif // initialize logger axom::slic::SimpleLogger logger; - axom::slic::setIsRoot(rank == 0); + axom::slic::setIsRoot( rank == 0 ); #ifdef TRIBOL_USE_UMPIRE umpire::ResourceManager::getInstance(); // initialize umpire's ResourceManager @@ -80,51 +79,50 @@ int main( int argc, char** argv ) // Target device where the code should be run std::string device_str = "cpu"; - axom::CLI::App app { "common_plane_gpu" }; - app.add_option("-r,--refine", ref_levels, - "Number of times to run the problem at different levels of mesh refinement.") - ->capture_default_str()->check(axom::CLI::PositiveNumber); - app.add_option("-d,--device", device_str, - "Target device where the code should be run.") - ->capture_default_str()->check(axom::CLI::IsMember({"cpu" -#if defined(TRIBOL_USE_CUDA) || defined(TRIBOL_USE_HIP) - , "gpu" + axom::CLI::App app{ "common_plane_gpu" }; + app.add_option( "-r,--refine", ref_levels, + "Number of times to run the problem at different levels of mesh refinement." ) + ->capture_default_str() + ->check( axom::CLI::PositiveNumber ); + app.add_option( "-d,--device", device_str, "Target device where the code should be run." ) + ->capture_default_str() + ->check( axom::CLI::IsMember( { + "cpu" +#if defined( TRIBOL_USE_CUDA ) || defined( TRIBOL_USE_HIP ) + , + "gpu" #endif #ifdef TRIBOL_USE_OPENMP - , "omp" + , + "omp" #endif - })); - CLI11_PARSE(app, argc, argv); + } ) ); + CLI11_PARSE( app, argc, argv ); - SLIC_INFO_ROOT("Running common_plane_gpu with the following options:"); - SLIC_INFO_ROOT(axom::fmt::format("refine: {0}", ref_levels)); - SLIC_INFO_ROOT(axom::fmt::format("device: {0}", device_str)); + SLIC_INFO_ROOT( "Running common_plane_gpu with the following options:" ); + SLIC_INFO_ROOT( axom::fmt::format( "refine: {0}", ref_levels ) ); + SLIC_INFO_ROOT( axom::fmt::format( "device: {0}", device_str ) ); int err = 0; - for (int i{0}; i < ref_levels; ++i) - { - int num_elems_1d = std::pow(2, i); - if (device_str == "cpu") - { - err = runExample(num_elems_1d); + for ( int i{ 0 }; i < ref_levels; ++i ) { + int num_elems_1d = std::pow( 2, i ); + if ( device_str == "cpu" ) { + err = runExample( num_elems_1d ); } #ifdef TRIBOL_USE_CUDA - else if (device_str == "gpu") - { - err = runExample(num_elems_1d); + else if ( device_str == "gpu" ) { + err = runExample( num_elems_1d ); } #endif #ifdef TRIBOL_USE_HIP - else if (device_str == "gpu") - { - err = runExample(num_elems_1d); + else if ( device_str == "gpu" ) { + err = runExample( num_elems_1d ); } #endif #ifdef TRIBOL_USE_OPENMP - else if (device_str == "omp") - { - err = runExample(num_elems_1d); + else if ( device_str == "omp" ) { + err = runExample( num_elems_1d ); } #endif } @@ -137,64 +135,54 @@ int main( int argc, char** argv ) } template -int runExample(int num_elems_1d) +int runExample( int num_elems_1d ) { - // This controls which device/programming model is targeted in MFEM. When set // to "cuda" or "hip", on device pointers point to GPU memory. mfem::Device device; - switch (MSPACE) - { + switch ( MSPACE ) { #ifdef TRIBOL_USE_CUDA case tribol::MemorySpace::Device: - device.Configure("cuda"); + device.Configure( "cuda" ); break; #endif #ifdef TRIBOL_USE_HIP case tribol::MemorySpace::Device: - device.Configure("hip"); + device.Configure( "hip" ); break; #endif default: #ifdef TRIBOL_USE_OPENMP - if (EXEC == tribol::ExecutionMode::OpenMP) - { - device.Configure("omp"); - } - else + if ( EXEC == tribol::ExecutionMode::OpenMP ) { + device.Configure( "omp" ); + } else #endif { - device.Configure("cpu"); + device.Configure( "cpu" ); } break; } device.Print(); std::cout << "Creating MFEM mesh..." << std::endl; - axom::utilities::Timer timer(true); + axom::utilities::Timer timer( true ); int num_contact_elems = num_elems_1d * num_elems_1d; - double elem_height = 1.0/static_cast(num_elems_1d); + double elem_height = 1.0 / static_cast( num_elems_1d ); // create top mesh - mfem::Mesh top_mesh = mfem::Mesh::MakeCartesian3D( - num_elems_1d, num_elems_1d, 1, mfem::Element::Type::HEXAHEDRON, - 1.0, 1.0, elem_height - ); + mfem::Mesh top_mesh = mfem::Mesh::MakeCartesian3D( num_elems_1d, num_elems_1d, 1, mfem::Element::Type::HEXAHEDRON, + 1.0, 1.0, elem_height ); // shift down 5% height of element (10% elem thickness interpenetration) - for (int i{0}; i < top_mesh.GetNV(); ++i) - { - top_mesh.GetVertex(i)[2] -= 0.05*elem_height; + for ( int i{ 0 }; i < top_mesh.GetNV(); ++i ) { + top_mesh.GetVertex( i )[2] -= 0.05 * elem_height; } // create bottom mesh - mfem::Mesh bottom_mesh = mfem::Mesh::MakeCartesian3D( - num_elems_1d, num_elems_1d, 1, mfem::Element::Type::HEXAHEDRON, - 1.0, 1.0, elem_height - ); + mfem::Mesh bottom_mesh = mfem::Mesh::MakeCartesian3D( num_elems_1d, num_elems_1d, 1, mfem::Element::Type::HEXAHEDRON, + 1.0, 1.0, elem_height ); // shift down 95% height of element (10% elem thickness interpenetration) - for (int i{0}; i < bottom_mesh.GetNV(); ++i) - { - bottom_mesh.GetVertex(i)[2] -= 0.95*elem_height; + for ( int i{ 0 }; i < bottom_mesh.GetNV(); ++i ) { + bottom_mesh.GetVertex( i )[2] -= 0.95 * elem_height; } std::cout << "MFEM mesh created (" << timer.elapsedTimeInMilliSec() << " ms)" << std::endl; @@ -202,27 +190,23 @@ int runExample(int num_elems_1d) std::cout << "Creating MFEM grid functions..." << std::endl; timer.start(); - mfem::H1_FECollection top_fe_coll(1, top_mesh.SpaceDimension()); - mfem::FiniteElementSpace top_fe_space( - &top_mesh, &top_fe_coll, top_mesh.SpaceDimension() - ); - mfem::GridFunction top_coords(&top_fe_space); - top_mesh.SetNodalGridFunction(&top_coords, false); + mfem::H1_FECollection top_fe_coll( 1, top_mesh.SpaceDimension() ); + mfem::FiniteElementSpace top_fe_space( &top_mesh, &top_fe_coll, top_mesh.SpaceDimension() ); + mfem::GridFunction top_coords( &top_fe_space ); + top_mesh.SetNodalGridFunction( &top_coords, false ); auto top_coords_ptr = top_coords.Read(); - auto top_x_coords_ptr = &top_coords_ptr[top_fe_space.DofToVDof(0, 0)]; - auto top_y_coords_ptr = &top_coords_ptr[top_fe_space.DofToVDof(0, 1)]; - auto top_z_coords_ptr = &top_coords_ptr[top_fe_space.DofToVDof(0, 2)]; - - mfem::H1_FECollection bottom_fe_coll(1, bottom_mesh.SpaceDimension()); - mfem::FiniteElementSpace bottom_fe_space( - &bottom_mesh, &bottom_fe_coll, bottom_mesh.SpaceDimension() - ); - mfem::GridFunction bottom_coords(&bottom_fe_space); - bottom_mesh.SetNodalGridFunction(&bottom_coords, false); + auto top_x_coords_ptr = &top_coords_ptr[top_fe_space.DofToVDof( 0, 0 )]; + auto top_y_coords_ptr = &top_coords_ptr[top_fe_space.DofToVDof( 0, 1 )]; + auto top_z_coords_ptr = &top_coords_ptr[top_fe_space.DofToVDof( 0, 2 )]; + + mfem::H1_FECollection bottom_fe_coll( 1, bottom_mesh.SpaceDimension() ); + mfem::FiniteElementSpace bottom_fe_space( &bottom_mesh, &bottom_fe_coll, bottom_mesh.SpaceDimension() ); + mfem::GridFunction bottom_coords( &bottom_fe_space ); + bottom_mesh.SetNodalGridFunction( &bottom_coords, false ); auto bottom_coords_ptr = bottom_coords.Read(); - auto bottom_x_coords_ptr = &bottom_coords_ptr[bottom_fe_space.DofToVDof(0, 0)]; - auto bottom_y_coords_ptr = &bottom_coords_ptr[bottom_fe_space.DofToVDof(0, 1)]; - auto bottom_z_coords_ptr = &bottom_coords_ptr[bottom_fe_space.DofToVDof(0, 2)]; + auto bottom_x_coords_ptr = &bottom_coords_ptr[bottom_fe_space.DofToVDof( 0, 0 )]; + auto bottom_y_coords_ptr = &bottom_coords_ptr[bottom_fe_space.DofToVDof( 0, 1 )]; + auto bottom_z_coords_ptr = &bottom_coords_ptr[bottom_fe_space.DofToVDof( 0, 2 )]; std::cout << "MFEM coordinate grid functions created (" << timer.elapsedTimeInMilliSec() << " ms)" << std::endl; @@ -230,48 +214,38 @@ int runExample(int num_elems_1d) timer.start(); // top mesh connectivity (build on cpu) - auto top_bdry_attrib = 1; // corresponds to bottom of top mesh - tribol::ArrayT host_top_conn( - num_contact_elems, 4 - ); + auto top_bdry_attrib = 1; // corresponds to bottom of top mesh + tribol::ArrayT host_top_conn( num_contact_elems, 4 ); int elem_ct = 0; - for (int be{0}; be < top_mesh.GetNBE(); ++be) - { - if (top_mesh.GetBdrAttribute(be) == top_bdry_attrib) - { - mfem::Array be_dofs(4);//, mfem::MemoryType::Host_UMPIRE); - top_fe_space.GetBdrElementDofs(be, be_dofs); - for (int i{0}; i < 4; ++i) - { - host_top_conn(elem_ct, i) = be_dofs[i]; + for ( int be{ 0 }; be < top_mesh.GetNBE(); ++be ) { + if ( top_mesh.GetBdrAttribute( be ) == top_bdry_attrib ) { + mfem::Array be_dofs( 4 ); //, mfem::MemoryType::Host_UMPIRE); + top_fe_space.GetBdrElementDofs( be, be_dofs ); + for ( int i{ 0 }; i < 4; ++i ) { + host_top_conn( elem_ct, i ) = be_dofs[i]; } ++elem_ct; } } // move to gpu if MSPACE is device, just (deep) copy otherwise - tribol::ArrayT top_conn(host_top_conn); + tribol::ArrayT top_conn( host_top_conn ); // bottom mesh connectivity (build on cpu) - auto bottom_bdry_attrib = 6; // corresponds to top of bottom mesh - tribol::ArrayT host_bottom_conn( - num_contact_elems, 4 - ); + auto bottom_bdry_attrib = 6; // corresponds to top of bottom mesh + tribol::ArrayT host_bottom_conn( num_contact_elems, 4 ); elem_ct = 0; - for (int be{0}; be < bottom_mesh.GetNBE(); ++be) - { - if (bottom_mesh.GetBdrAttribute(be) == bottom_bdry_attrib) - { - mfem::Array be_dofs(4);//, mfem::MemoryType::Host_UMPIRE); - bottom_fe_space.GetBdrElementDofs(be, be_dofs); - for (int i{0}; i < 4; ++i) - { - host_bottom_conn(elem_ct, i) = be_dofs[i]; + for ( int be{ 0 }; be < bottom_mesh.GetNBE(); ++be ) { + if ( bottom_mesh.GetBdrAttribute( be ) == bottom_bdry_attrib ) { + mfem::Array be_dofs( 4 ); //, mfem::MemoryType::Host_UMPIRE); + bottom_fe_space.GetBdrElementDofs( be, be_dofs ); + for ( int i{ 0 }; i < 4; ++i ) { + host_bottom_conn( elem_ct, i ) = be_dofs[i]; } ++elem_ct; } } // move to gpu if MSPACE is device, just (deep) copy otherwise - tribol::ArrayT bottom_conn(host_bottom_conn); + tribol::ArrayT bottom_conn( host_bottom_conn ); std::cout << "Tribol connectivity created (" << timer.elapsedTimeInMilliSec() << " ms)" << std::endl; @@ -279,95 +253,77 @@ int runExample(int num_elems_1d) timer.start(); constexpr tribol::IndexT top_mesh_id = 0; - tribol::registerMesh( - top_mesh_id, num_contact_elems, top_fe_space.GetNDofs(), - top_conn.data(), tribol::LINEAR_QUAD, - top_x_coords_ptr, top_y_coords_ptr, top_z_coords_ptr, MSPACE - ); + tribol::registerMesh( top_mesh_id, num_contact_elems, top_fe_space.GetNDofs(), top_conn.data(), tribol::LINEAR_QUAD, + top_x_coords_ptr, top_y_coords_ptr, top_z_coords_ptr, MSPACE ); constexpr tribol::IndexT bottom_mesh_id = 1; - tribol::registerMesh( - bottom_mesh_id, num_contact_elems, bottom_fe_space.GetNDofs(), - bottom_conn.data(), tribol::LINEAR_QUAD, - bottom_x_coords_ptr, bottom_y_coords_ptr, bottom_z_coords_ptr, MSPACE - ); + tribol::registerMesh( bottom_mesh_id, num_contact_elems, bottom_fe_space.GetNDofs(), bottom_conn.data(), + tribol::LINEAR_QUAD, bottom_x_coords_ptr, bottom_y_coords_ptr, bottom_z_coords_ptr, MSPACE ); constexpr tribol::RealT penalty = 5000.0; - tribol::setKinematicConstantPenalty(top_mesh_id, penalty); - tribol::setKinematicConstantPenalty(bottom_mesh_id, penalty); - + tribol::setKinematicConstantPenalty( top_mesh_id, penalty ); + tribol::setKinematicConstantPenalty( bottom_mesh_id, penalty ); + std::cout << "Tribol mesh data registered (" << timer.elapsedTimeInMilliSec() << " ms)" << std::endl; std::cout << "Creating and registering velocity and force..." << std::endl; timer.start(); - mfem::GridFunction top_velocity(&top_fe_space); + mfem::GridFunction top_velocity( &top_fe_space ); top_velocity = 0.0; // Get a (device, if on GPU) pointer to read velocity data auto top_velocity_ptr = top_velocity.Read(); - auto top_x_velocity_ptr = &top_velocity_ptr[top_fe_space.DofToVDof(0, 0)]; - auto top_y_velocity_ptr = &top_velocity_ptr[top_fe_space.DofToVDof(0, 1)]; - auto top_z_velocity_ptr = &top_velocity_ptr[top_fe_space.DofToVDof(0, 2)]; - tribol::registerNodalVelocities( - top_mesh_id, top_x_velocity_ptr, top_y_velocity_ptr, top_z_velocity_ptr - ); - - mfem::GridFunction bottom_velocity(&bottom_fe_space); + auto top_x_velocity_ptr = &top_velocity_ptr[top_fe_space.DofToVDof( 0, 0 )]; + auto top_y_velocity_ptr = &top_velocity_ptr[top_fe_space.DofToVDof( 0, 1 )]; + auto top_z_velocity_ptr = &top_velocity_ptr[top_fe_space.DofToVDof( 0, 2 )]; + tribol::registerNodalVelocities( top_mesh_id, top_x_velocity_ptr, top_y_velocity_ptr, top_z_velocity_ptr ); + + mfem::GridFunction bottom_velocity( &bottom_fe_space ); bottom_velocity = 0.0; // Get a (device, if on GPU) pointer to read velocity data auto bottom_velocity_ptr = bottom_velocity.Read(); - auto bottom_x_velocity_ptr = &bottom_velocity_ptr[bottom_fe_space.DofToVDof(0, 0)]; - auto bottom_y_velocity_ptr = &bottom_velocity_ptr[bottom_fe_space.DofToVDof(0, 1)]; - auto bottom_z_velocity_ptr = &bottom_velocity_ptr[bottom_fe_space.DofToVDof(0, 2)]; - tribol::registerNodalVelocities( - bottom_mesh_id, bottom_x_velocity_ptr, bottom_y_velocity_ptr, bottom_z_velocity_ptr - ); - - mfem::Vector top_force(top_fe_space.GetVSize()); + auto bottom_x_velocity_ptr = &bottom_velocity_ptr[bottom_fe_space.DofToVDof( 0, 0 )]; + auto bottom_y_velocity_ptr = &bottom_velocity_ptr[bottom_fe_space.DofToVDof( 0, 1 )]; + auto bottom_z_velocity_ptr = &bottom_velocity_ptr[bottom_fe_space.DofToVDof( 0, 2 )]; + tribol::registerNodalVelocities( bottom_mesh_id, bottom_x_velocity_ptr, bottom_y_velocity_ptr, + bottom_z_velocity_ptr ); + + mfem::Vector top_force( top_fe_space.GetVSize() ); // For mfem::Vectors, the assumption is a single vector on host. Calling // UseDevice(true) creates a version on device. Note this call isn't needed // for mfem::GridFunctions, which call UseDevice(true) in the constructor. - top_force.UseDevice(true); + top_force.UseDevice( true ); top_force = 0.0; // Get a (device, if on GPU) pointer to read and write to force data auto top_force_ptr = top_force.ReadWrite(); - auto top_x_force_ptr = &top_force_ptr[top_fe_space.DofToVDof(0, 0)]; - auto top_y_force_ptr = &top_force_ptr[top_fe_space.DofToVDof(0, 1)]; - auto top_z_force_ptr = &top_force_ptr[top_fe_space.DofToVDof(0, 2)]; - tribol::registerNodalResponse( - top_mesh_id, top_x_force_ptr, top_y_force_ptr, top_z_force_ptr - ); - - mfem::Vector bottom_force(bottom_fe_space.GetVSize()); + auto top_x_force_ptr = &top_force_ptr[top_fe_space.DofToVDof( 0, 0 )]; + auto top_y_force_ptr = &top_force_ptr[top_fe_space.DofToVDof( 0, 1 )]; + auto top_z_force_ptr = &top_force_ptr[top_fe_space.DofToVDof( 0, 2 )]; + tribol::registerNodalResponse( top_mesh_id, top_x_force_ptr, top_y_force_ptr, top_z_force_ptr ); + + mfem::Vector bottom_force( bottom_fe_space.GetVSize() ); // For mfem::Vectors, the assumption is a single vector on host. Calling // UseDevice(true) creates a version on device. Note this call isn't needed // for mfem::GridFunctions, which call UseDevice(true) in the constructor. - bottom_force.UseDevice(true); + bottom_force.UseDevice( true ); bottom_force = 0.0; // Get a (device, if on GPU) pointer to read and write to force data auto bottom_force_ptr = bottom_force.ReadWrite(); - auto bottom_x_force_ptr = &bottom_force_ptr[bottom_fe_space.DofToVDof(0, 0)]; - auto bottom_y_force_ptr = &bottom_force_ptr[bottom_fe_space.DofToVDof(0, 1)]; - auto bottom_z_force_ptr = &bottom_force_ptr[bottom_fe_space.DofToVDof(0, 2)]; - tribol::registerNodalResponse( - bottom_mesh_id, bottom_x_force_ptr, bottom_y_force_ptr, bottom_z_force_ptr - ); - + auto bottom_x_force_ptr = &bottom_force_ptr[bottom_fe_space.DofToVDof( 0, 0 )]; + auto bottom_y_force_ptr = &bottom_force_ptr[bottom_fe_space.DofToVDof( 0, 1 )]; + auto bottom_z_force_ptr = &bottom_force_ptr[bottom_fe_space.DofToVDof( 0, 2 )]; + tribol::registerNodalResponse( bottom_mesh_id, bottom_x_force_ptr, bottom_y_force_ptr, bottom_z_force_ptr ); + std::cout << "Velocity and force registered (" << timer.elapsedTimeInMilliSec() << " ms)" << std::endl; std::cout << "Registering Tribol coupling scheme..." << std::endl; timer.start(); constexpr tribol::IndexT cs_id = 0; - tribol::registerCouplingScheme(cs_id, top_mesh_id, bottom_mesh_id, - tribol::SURFACE_TO_SURFACE, - tribol::NO_CASE, - tribol::COMMON_PLANE, - tribol::FRICTIONLESS, - tribol::PENALTY, - tribol::BINNING_BVH, - EXEC); + tribol::registerCouplingScheme( cs_id, top_mesh_id, bottom_mesh_id, tribol::SURFACE_TO_SURFACE, tribol::NO_CASE, + tribol::COMMON_PLANE, tribol::FRICTIONLESS, tribol::PENALTY, tribol::BINNING_BVH, + EXEC ); - tribol::setPenaltyOptions(cs_id, tribol::KINEMATIC, tribol::KINEMATIC_CONSTANT); + tribol::setPenaltyOptions( cs_id, tribol::KINEMATIC, tribol::KINEMATIC_CONSTANT ); std::cout << "Tribol coupling scheme registered (" << timer.elapsedTimeInMilliSec() << " ms)" << std::endl; @@ -377,7 +333,7 @@ int runExample(int num_elems_1d) constexpr int cycle = 1; constexpr tribol::RealT t = 1.0; tribol::RealT dt = 1.0; - tribol::update(cycle, t, dt); + tribol::update( cycle, t, dt ); std::cout << "Tribol update complete (" << timer.elapsedTimeInMilliSec() << " ms)" << std::endl; tribol::RealT top_max_force = top_force.Max(); @@ -386,18 +342,17 @@ int runExample(int num_elems_1d) tribol::RealT bottom_max_force = bottom_force.Max(); std::cout << "Bottom max force: " << bottom_max_force << std::endl; - // MFEM verification fails with this call on CUDA/HIP - #ifdef TRIBOL_USE_HOST +// MFEM verification fails with this call on CUDA/HIP +#ifdef TRIBOL_USE_HOST tribol::RealT top_min_force = top_force.Min(); std::cout << "Top min force: " << top_min_force << std::endl; - + tribol::RealT bottom_min_force = bottom_force.Min(); std::cout << "Bottom min force: " << bottom_min_force << std::endl; - #endif +#endif tribol::RealT tot_force = top_force.Norml1() + bottom_force.Norml1(); std::cout << "Total |force|: " << tot_force << std::endl; return 0; } - diff --git a/src/examples/domain_redecomp.cpp b/src/examples/domain_redecomp.cpp index 7355e332..d30fc9ba 100644 --- a/src/examples/domain_redecomp.cpp +++ b/src/examples/domain_redecomp.cpp @@ -23,7 +23,7 @@ * redecomp::RedecompMesh, but are not transferred back to the mfem::ParMesh. * * This example illustrates the envisioned typical workflow for using - * redecomp::RedecompMesh: + * redecomp::RedecompMesh: * 1. Create a RedecompMesh by providing a parent ParMesh (and optionally a * method of repartitioning the domain) * 2. Create fields on the RedecompMesh (mfem::GridFunction and/or @@ -62,7 +62,7 @@ #include "tribol/config.hpp" template -void RedecompExample(mfem::ParMesh& pmesh, int order, double max_out_of_balance); +void RedecompExample( mfem::ParMesh& pmesh, int order, double max_out_of_balance ); int main( int argc, char** argv ) { @@ -73,12 +73,12 @@ int main( int argc, char** argv ) // initialize MPI MPI_Init( &argc, &argv ); int np, rank; - MPI_Comm_size(MPI_COMM_WORLD, &np); - MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size( MPI_COMM_WORLD, &np ); + MPI_Comm_rank( MPI_COMM_WORLD, &rank ); // initialize logger axom::slic::SimpleLogger logger; - axom::slic::setIsRoot(rank == 0); + axom::slic::setIsRoot( rank == 0 ); // command line options // location of mesh file. TRIBOL_REPO_DIR is defined in tribol/config.hpp @@ -97,69 +97,58 @@ int main( int argc, char** argv ) // checks implemented. double max_out_of_balance = 0.05; - axom::CLI::App app { "domain_redecomp" }; - app.add_option("-m,--mesh", mesh_file, "Mesh file to use.") - ->check(axom::CLI::ExistingFile) - ->capture_default_str(); - app.add_option("-r,--refine", ref_levels, - "Number of times to refine the mesh uniformly.") - ->capture_default_str(); - app.add_option("-o,--order", order, - "Finite element order (polynomial degree).") - ->capture_default_str(); - app.add_option("-t,--tol", max_out_of_balance, - "Max proportion of out-of-balance elements in RCB decomposition.") - ->capture_default_str(); - CLI11_PARSE(app, argc, argv); - - SLIC_INFO_ROOT("Running domain_redecomp with the following options:"); - SLIC_INFO_ROOT(axom::fmt::format("mesh: {0}", mesh_file)); - SLIC_INFO_ROOT(axom::fmt::format("refine: {0}", ref_levels)); - SLIC_INFO_ROOT(axom::fmt::format("order: {0}", order)); - SLIC_INFO_ROOT(axom::fmt::format("tol: {0}\n", max_out_of_balance)); + axom::CLI::App app{ "domain_redecomp" }; + app.add_option( "-m,--mesh", mesh_file, "Mesh file to use." ) + ->check( axom::CLI::ExistingFile ) + ->capture_default_str(); + app.add_option( "-r,--refine", ref_levels, "Number of times to refine the mesh uniformly." )->capture_default_str(); + app.add_option( "-o,--order", order, "Finite element order (polynomial degree)." )->capture_default_str(); + app.add_option( "-t,--tol", max_out_of_balance, "Max proportion of out-of-balance elements in RCB decomposition." ) + ->capture_default_str(); + CLI11_PARSE( app, argc, argv ); + + SLIC_INFO_ROOT( "Running domain_redecomp with the following options:" ); + SLIC_INFO_ROOT( axom::fmt::format( "mesh: {0}", mesh_file ) ); + SLIC_INFO_ROOT( axom::fmt::format( "refine: {0}", ref_levels ) ); + SLIC_INFO_ROOT( axom::fmt::format( "order: {0}", order ) ); + SLIC_INFO_ROOT( axom::fmt::format( "tol: {0}\n", max_out_of_balance ) ); // read serial mesh - auto mesh = std::make_unique(mesh_file.c_str(), 1, 1); + auto mesh = std::make_unique( mesh_file.c_str(), 1, 1 ); // refine serial mesh - if (ref_levels > 0) - { - for (int i{0}; i < ref_levels; ++i) - { + if ( ref_levels > 0 ) { + for ( int i{ 0 }; i < ref_levels; ++i ) { mesh->UniformRefinement(); } } - + // create parallel mesh from serial - axom::utilities::Timer timer { false }; + axom::utilities::Timer timer{ false }; timer.start(); - auto pmesh = std::make_unique(MPI_COMM_WORLD, *mesh); - mesh.reset(nullptr); + auto pmesh = std::make_unique( MPI_COMM_WORLD, *mesh ); + mesh.reset( nullptr ); timer.stop(); - SLIC_INFO_ROOT(axom::fmt::format( - "Time to create parallel mesh: {0:f}ms", timer.elapsedTimeInMilliSec() - )); + SLIC_INFO_ROOT( axom::fmt::format( "Time to create parallel mesh: {0:f}ms", timer.elapsedTimeInMilliSec() ) ); // further refinement of parallel mesh { int par_ref_levels = 2; - for (int i{0}; i < par_ref_levels; ++i) - { + for ( int i{ 0 }; i < par_ref_levels; ++i ) { pmesh->UniformRefinement(); } } // call dimension specific version of RedecompExample - switch (pmesh->SpaceDimension()) - { + switch ( pmesh->SpaceDimension() ) { case 2: - RedecompExample<2>(*pmesh, order, max_out_of_balance); + RedecompExample<2>( *pmesh, order, max_out_of_balance ); break; case 3: - RedecompExample<3>(*pmesh, order, max_out_of_balance); + RedecompExample<3>( *pmesh, order, max_out_of_balance ); break; default: - SLIC_ERROR("Space dimension of the mesh must be 2 or 3."); + SLIC_ERROR( "Space dimension of the mesh must be 2 or 3." ); } // cleanup @@ -169,22 +158,19 @@ int main( int argc, char** argv ) } template -void RedecompExample(mfem::ParMesh& pmesh, int order, double max_out_of_balance) +void RedecompExample( mfem::ParMesh& pmesh, int order, double max_out_of_balance ) { auto rank = pmesh.GetMyRank(); auto np = pmesh.GetNRanks(); // grid function for higher-order nodes - auto fe_coll = mfem::H1_FECollection(order, NDIMS); - auto par_fe_space = mfem::ParFiniteElementSpace(&pmesh, &fe_coll, NDIMS); - auto par_x_ref_elem = mfem::ParGridFunction(&par_fe_space); - if (order > 1) - { - pmesh.SetNodalGridFunction(&par_x_ref_elem, false); - } - else - { - pmesh.GetNodes(par_x_ref_elem); + auto fe_coll = mfem::H1_FECollection( order, NDIMS ); + auto par_fe_space = mfem::ParFiniteElementSpace( &pmesh, &fe_coll, NDIMS ); + auto par_x_ref_elem = mfem::ParGridFunction( &par_fe_space ); + if ( order > 1 ) { + pmesh.SetNodalGridFunction( &par_x_ref_elem, false ); + } else { + pmesh.GetNodes( par_x_ref_elem ); } // This is the grid function we are transferring. The "node" and "element" // suffixes refer to the transfer methods that will be used later @@ -193,7 +179,7 @@ void RedecompExample(mfem::ParMesh& pmesh, int order, double max_out_of_balance) auto par_x_ref_node = par_x_ref_elem; // create visit output data collection for pmesh - auto pmesh_dc = mfem::VisItDataCollection("pmesh", &pmesh); + auto pmesh_dc = mfem::VisItDataCollection( "pmesh", &pmesh ); ///////////////////////////////////////////////////////////////////////////// // STEP 1: Create RedecompMesh @@ -204,27 +190,17 @@ void RedecompExample(mfem::ParMesh& pmesh, int order, double max_out_of_balance) // auto redecomp_mesh = redecomp::RedecompMesh(&pmesh); // See RedecompMesh.hpp for all available constructors - axom::utilities::Timer timer { false }; + axom::utilities::Timer timer{ false }; timer.start(); auto redecomp_mesh = redecomp::RedecompMesh( - pmesh, - std::make_unique>( - std::make_unique>(), - std::make_unique>( - MPI_COMM_WORLD, - max_out_of_balance - ) - ) - ); + pmesh, std::make_unique>( + std::make_unique>(), + std::make_unique>( MPI_COMM_WORLD, max_out_of_balance ) ) ); timer.stop(); - SLIC_INFO_ROOT(axom::fmt::format( - "Time to create Redecomp mesh: {0:f}ms", timer.elapsedTimeInMilliSec() - )); + SLIC_INFO_ROOT( axom::fmt::format( "Time to create Redecomp mesh: {0:f}ms", timer.elapsedTimeInMilliSec() ) ); // create visit output data collection for redecomp - auto redecomp_dc = mfem::VisItDataCollection( - "redecomp" + std::to_string(rank), &redecomp_mesh - ); + auto redecomp_dc = mfem::VisItDataCollection( "redecomp" + std::to_string( rank ), &redecomp_mesh ); ///////////////////////////////////////////////////////////////////////////// // GridFunction transfer @@ -234,14 +210,10 @@ void RedecompExample(mfem::ParMesh& pmesh, int order, double max_out_of_balance) // STEP 2: Create GridFunctions on RedecompMesh ///////////////////////////////////////////////////////////////////////////// - auto redecomp_fe_space = mfem::FiniteElementSpace( - &redecomp_mesh, - &fe_coll, - NDIMS - ); - auto redecomp_x_ref_elem = mfem::GridFunction(&redecomp_fe_space); - auto redecomp_x_ref_node = mfem::GridFunction(&redecomp_fe_space); - + auto redecomp_fe_space = mfem::FiniteElementSpace( &redecomp_mesh, &fe_coll, NDIMS ); + auto redecomp_x_ref_elem = mfem::GridFunction( &redecomp_fe_space ); + auto redecomp_x_ref_node = mfem::GridFunction( &redecomp_fe_space ); + ///////////////////////////////////////////////////////////////////////////// // STEP 3A: Create transfer object (element-by-element) ///////////////////////////////////////////////////////////////////////////// @@ -259,51 +231,39 @@ void RedecompExample(mfem::ParMesh& pmesh, int order, double max_out_of_balance) timer.start(); auto elem_transfer = redecomp::RedecompTransfer(); timer.stop(); - SLIC_INFO_ROOT(axom::fmt::format( - "Time to create element transfer object: {0:f}ms", - timer.elapsedTimeInMilliSec() - )); + SLIC_INFO_ROOT( + axom::fmt::format( "Time to create element transfer object: {0:f}ms", timer.elapsedTimeInMilliSec() ) ); ///////////////////////////////////////////////////////////////////////////// // STEP 4A: Transfer to GridFunction on RedecompMesh (element-by-element) ///////////////////////////////////////////////////////////////////////////// timer.start(); - elem_transfer.TransferToSerial(par_x_ref_elem, redecomp_x_ref_elem); + elem_transfer.TransferToSerial( par_x_ref_elem, redecomp_x_ref_elem ); timer.stop(); - SLIC_INFO_ROOT(axom::fmt::format( - "Time to transfer vector field by element: {0:f}ms", - timer.elapsedTimeInMilliSec() - )); - redecomp_dc.RegisterField("pos_elem", &redecomp_x_ref_elem); - + SLIC_INFO_ROOT( + axom::fmt::format( "Time to transfer vector field by element: {0:f}ms", timer.elapsedTimeInMilliSec() ) ); + redecomp_dc.RegisterField( "pos_elem", &redecomp_x_ref_elem ); + ///////////////////////////////////////////////////////////////////////////// // STEP 3B: Create transfer object (node-by-node) ///////////////////////////////////////////////////////////////////////////// timer.start(); - auto node_transfer = redecomp::RedecompTransfer( - par_fe_space, - redecomp_fe_space - ); + auto node_transfer = redecomp::RedecompTransfer( par_fe_space, redecomp_fe_space ); timer.stop(); - SLIC_INFO_ROOT(axom::fmt::format( - "Time to create nodal transfer object: {0:f}ms", - timer.elapsedTimeInMilliSec() - )); - + SLIC_INFO_ROOT( axom::fmt::format( "Time to create nodal transfer object: {0:f}ms", timer.elapsedTimeInMilliSec() ) ); + ///////////////////////////////////////////////////////////////////////////// // STEP 4B: Transfer to GridFunction on RedecompMesh (node-by-node) ///////////////////////////////////////////////////////////////////////////// timer.start(); - node_transfer.TransferToSerial(par_x_ref_node, redecomp_x_ref_node); + node_transfer.TransferToSerial( par_x_ref_node, redecomp_x_ref_node ); timer.stop(); - SLIC_INFO_ROOT(axom::fmt::format( - "Time to transfer vector field by node: {0:f}ms", - timer.elapsedTimeInMilliSec() - )); - redecomp_dc.RegisterField("pos_node", &redecomp_x_ref_node); + SLIC_INFO_ROOT( + axom::fmt::format( "Time to transfer vector field by node: {0:f}ms", timer.elapsedTimeInMilliSec() ) ); + redecomp_dc.RegisterField( "pos_node", &redecomp_x_ref_node ); ///////////////////////////////////////////////////////////////////////////// // STEP 5: Operate on data (no-op here) @@ -314,41 +274,35 @@ void RedecompExample(mfem::ParMesh& pmesh, int order, double max_out_of_balance) ///////////////////////////////////////////////////////////////////////////// timer.start(); - elem_transfer.TransferToParallel(redecomp_x_ref_elem, par_x_ref_elem); + elem_transfer.TransferToParallel( redecomp_x_ref_elem, par_x_ref_elem ); timer.stop(); - SLIC_INFO_ROOT(axom::fmt::format( - "Time to transfer back vector field by element: {0:f}ms", - timer.elapsedTimeInMilliSec() - )); - pmesh_dc.RegisterField("pos_elem", &par_x_ref_elem); + SLIC_INFO_ROOT( + axom::fmt::format( "Time to transfer back vector field by element: {0:f}ms", timer.elapsedTimeInMilliSec() ) ); + pmesh_dc.RegisterField( "pos_elem", &par_x_ref_elem ); ///////////////////////////////////////////////////////////////////////////// // STEP 6B: Transfer back to ParGridFunction on ParMesh (node-by-node) ///////////////////////////////////////////////////////////////////////////// timer.start(); - node_transfer.TransferToParallel(redecomp_x_ref_node, par_x_ref_node); + node_transfer.TransferToParallel( redecomp_x_ref_node, par_x_ref_node ); timer.stop(); - SLIC_INFO_ROOT(axom::fmt::format( - "Time to transfer back vector field by node: {0:f}ms", - timer.elapsedTimeInMilliSec() - )); - pmesh_dc.RegisterField("pos_node", &par_x_ref_node); + SLIC_INFO_ROOT( + axom::fmt::format( "Time to transfer back vector field by node: {0:f}ms", timer.elapsedTimeInMilliSec() ) ); + pmesh_dc.RegisterField( "pos_node", &par_x_ref_node ); ///////////////////////////////////////////////////////////////////////////// // QuadratureFunction transfer ///////////////////////////////////////////////////////////////////////////// // store global element number as a QuadratureFunction - auto quad_space = mfem::QuadratureSpace(&pmesh, 0); - auto quad_fn = mfem::QuadratureFunction(&quad_space); - for (int e{0}; e < pmesh.GetNE(); ++e) - { + auto quad_space = mfem::QuadratureSpace( &pmesh, 0 ); + auto quad_fn = mfem::QuadratureFunction( &quad_space ); + for ( int e{ 0 }; e < pmesh.GetNE(); ++e ) { auto quad_val = mfem::Vector(); - quad_fn.GetValues(e, quad_val); - for (int i{0}; i < quad_val.Size(); ++i) - { - quad_val[i] = static_cast(pmesh.GetGlobalElementNum(e)); + quad_fn.GetValues( e, quad_val ); + for ( int i{ 0 }; i < quad_val.Size(); ++i ) { + quad_val[i] = static_cast( pmesh.GetGlobalElementNum( e ) ); } } @@ -361,9 +315,9 @@ void RedecompExample(mfem::ParMesh& pmesh, int order, double max_out_of_balance) // STEP 2: Create QuadratureFunctions on RedecompMesh ///////////////////////////////////////////////////////////////////////////// - auto redecomp_quad_space = mfem::QuadratureSpace(&redecomp_mesh, 0); - auto redecomp_quad_fn = mfem::QuadratureFunction(&redecomp_quad_space); - + auto redecomp_quad_space = mfem::QuadratureSpace( &redecomp_mesh, 0 ); + auto redecomp_quad_fn = mfem::QuadratureFunction( &redecomp_quad_space ); + ///////////////////////////////////////////////////////////////////////////// // STEP 3: Create transfer object (element-by-element) ///////////////////////////////////////////////////////////////////////////// @@ -374,13 +328,10 @@ void RedecompExample(mfem::ParMesh& pmesh, int order, double max_out_of_balance) ///////////////////////////////////////////////////////////////////////////// timer.start(); - elem_transfer.TransferToSerial(quad_fn, redecomp_quad_fn); + elem_transfer.TransferToSerial( quad_fn, redecomp_quad_fn ); timer.stop(); - SLIC_INFO_ROOT(axom::fmt::format( - "Time to transfer quadrature function: {0:f}ms", - timer.elapsedTimeInMilliSec() - )); - redecomp_dc.RegisterQField("elem_id", &redecomp_quad_fn); + SLIC_INFO_ROOT( axom::fmt::format( "Time to transfer quadrature function: {0:f}ms", timer.elapsedTimeInMilliSec() ) ); + redecomp_dc.RegisterQField( "elem_id", &redecomp_quad_fn ); ///////////////////////////////////////////////////////////////////////////// // STEP 5: Operate on data (no-op here) @@ -391,51 +342,42 @@ void RedecompExample(mfem::ParMesh& pmesh, int order, double max_out_of_balance) ///////////////////////////////////////////////////////////////////////////// timer.start(); - elem_transfer.TransferToParallel(redecomp_quad_fn, quad_fn); + elem_transfer.TransferToParallel( redecomp_quad_fn, quad_fn ); timer.stop(); - SLIC_INFO_ROOT(axom::fmt::format( - "Time to transfer back quadrature function: {0:f}ms\n", - timer.elapsedTimeInMilliSec() - )); - pmesh_dc.RegisterQField("elem_id", &quad_fn); + SLIC_INFO_ROOT( + axom::fmt::format( "Time to transfer back quadrature function: {0:f}ms\n", timer.elapsedTimeInMilliSec() ) ); + pmesh_dc.RegisterQField( "elem_id", &quad_fn ); pmesh_dc.Save(); redecomp_dc.Save(); // print mesh stats to screen - if (rank == 0) - { - auto n_els = std::vector(np); + if ( rank == 0 ) { + auto n_els = std::vector( np ); n_els[0] = pmesh.GetNE(); - for (int i{1}; i < np; ++i) - { + for ( int i{ 1 }; i < np; ++i ) { MPI_Status status; - MPI_Recv(&n_els[i], 1, MPI_INT, i, 0, MPI_COMM_WORLD, &status); + MPI_Recv( &n_els[i], 1, MPI_INT, i, 0, MPI_COMM_WORLD, &status ); } - SLIC_INFO("Original ParMesh stats"); - SLIC_INFO("----------------------"); - for (int i{0}; i < np; ++i) - { - SLIC_INFO(axom::fmt::format("Rank {0}: {1} elements", i, n_els[i])); + SLIC_INFO( "Original ParMesh stats" ); + SLIC_INFO( "----------------------" ); + for ( int i{ 0 }; i < np; ++i ) { + SLIC_INFO( axom::fmt::format( "Rank {0}: {1} elements", i, n_els[i] ) ); } n_els[0] = redecomp_mesh.GetNE(); - for (int i{1}; i < np; ++i) - { + for ( int i{ 1 }; i < np; ++i ) { MPI_Status status; - MPI_Recv(&n_els[i], 1, MPI_INT, i, 0, MPI_COMM_WORLD, &status); + MPI_Recv( &n_els[i], 1, MPI_INT, i, 0, MPI_COMM_WORLD, &status ); } - SLIC_INFO("Redecomposed Mesh stats"); - SLIC_INFO("-----------------------"); - for (int i{0}; i < np; ++i) - { - SLIC_INFO(axom::fmt::format("Rank {0}: {1} elements", i, n_els[i])); + SLIC_INFO( "Redecomposed Mesh stats" ); + SLIC_INFO( "-----------------------" ); + for ( int i{ 0 }; i < np; ++i ) { + SLIC_INFO( axom::fmt::format( "Rank {0}: {1} elements", i, n_els[i] ) ); } - } - else - { + } else { int n_els = pmesh.GetNE(); - MPI_Send(&n_els, 1, MPI_INT, 0, 0, MPI_COMM_WORLD); + MPI_Send( &n_els, 1, MPI_INT, 0, 0, MPI_COMM_WORLD ); n_els = redecomp_mesh.GetNE(); - MPI_Send(&n_els, 1, MPI_INT, 0, 0, MPI_COMM_WORLD); + MPI_Send( &n_els, 1, MPI_INT, 0, 0, MPI_COMM_WORLD ); } } diff --git a/src/examples/element_matrix_redecomp.cpp b/src/examples/element_matrix_redecomp.cpp index 32510d52..14756507 100644 --- a/src/examples/element_matrix_redecomp.cpp +++ b/src/examples/element_matrix_redecomp.cpp @@ -48,12 +48,12 @@ int main( int argc, char** argv ) // initialize MPI MPI_Init( &argc, &argv ); int np, rank; - MPI_Comm_size(MPI_COMM_WORLD, &np); - MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size( MPI_COMM_WORLD, &np ); + MPI_Comm_rank( MPI_COMM_WORLD, &rank ); // initialize logger axom::slic::SimpleLogger logger; - axom::slic::setIsRoot(rank == 0); + axom::slic::setIsRoot( rank == 0 ); // command line options // location of mesh file. TRIBOL_REPO_DIR is defined in tribol/config.hpp @@ -64,123 +64,100 @@ int main( int argc, char** argv ) // polynomial order of the finite element discretization int order = 1; - axom::CLI::App app { "element_matrix_redecomp" }; - app.add_option("-m,--mesh", mesh_file, "Mesh file to use.") - ->check(axom::CLI::ExistingFile) - ->capture_default_str(); - app.add_option("-r,--refine", ref_levels, - "Number of times to refine the mesh uniformly.") - ->capture_default_str(); - app.add_option("-o,--order", order, - "Finite element order (polynomial degree).") - ->capture_default_str(); - CLI11_PARSE(app, argc, argv); - - SLIC_INFO_ROOT("Running element_matrix_redecomp with the following options:"); - SLIC_INFO_ROOT(axom::fmt::format("mesh: {0}", mesh_file)); - SLIC_INFO_ROOT(axom::fmt::format("refine: {0}", ref_levels)); - SLIC_INFO_ROOT(axom::fmt::format("order: {0}\n", order)); - - SLIC_INFO_ROOT("Creating mfem::ParMesh..."); + axom::CLI::App app{ "element_matrix_redecomp" }; + app.add_option( "-m,--mesh", mesh_file, "Mesh file to use." ) + ->check( axom::CLI::ExistingFile ) + ->capture_default_str(); + app.add_option( "-r,--refine", ref_levels, "Number of times to refine the mesh uniformly." )->capture_default_str(); + app.add_option( "-o,--order", order, "Finite element order (polynomial degree)." )->capture_default_str(); + CLI11_PARSE( app, argc, argv ); + + SLIC_INFO_ROOT( "Running element_matrix_redecomp with the following options:" ); + SLIC_INFO_ROOT( axom::fmt::format( "mesh: {0}", mesh_file ) ); + SLIC_INFO_ROOT( axom::fmt::format( "refine: {0}", ref_levels ) ); + SLIC_INFO_ROOT( axom::fmt::format( "order: {0}\n", order ) ); + + SLIC_INFO_ROOT( "Creating mfem::ParMesh..." ); // read serial mesh - auto mesh = std::make_unique(mesh_file.c_str(), 1, 1); + auto mesh = std::make_unique( mesh_file.c_str(), 1, 1 ); // refine serial mesh - for (int i{0}; i < ref_levels; ++i) - { + for ( int i{ 0 }; i < ref_levels; ++i ) { mesh->UniformRefinement(); } - + // create parallel mesh from serial - auto pmesh = std::make_unique(MPI_COMM_WORLD, *mesh); - mesh.reset(nullptr); + auto pmesh = std::make_unique( MPI_COMM_WORLD, *mesh ); + mesh.reset( nullptr ); // further refinement of parallel mesh { int par_ref_levels = 0; - for (int i{0}; i < par_ref_levels; ++i) - { + for ( int i{ 0 }; i < par_ref_levels; ++i ) { pmesh->UniformRefinement(); } } - auto pmesh_dc = mfem::VisItDataCollection("pmesh", pmesh.get()); + auto pmesh_dc = mfem::VisItDataCollection( "pmesh", pmesh.get() ); - SLIC_INFO_ROOT("Computing mass matrix on mfem::ParMesh..."); + SLIC_INFO_ROOT( "Computing mass matrix on mfem::ParMesh..." ); // compute mass matrix on parallel mesh - mfem::H1_FECollection fe_coll { order, pmesh->SpaceDimension() }; - mfem::ParFiniteElementSpace par_fe_space { - pmesh.get(), - &fe_coll, - pmesh->SpaceDimension() - }; - mfem::ParBilinearForm M_par { &par_fe_space }; - mfem::ConstantCoefficient rho0 { 1.0 }; - M_par.AddDomainIntegrator(new mfem::VectorMassIntegrator(rho0)); + mfem::H1_FECollection fe_coll{ order, pmesh->SpaceDimension() }; + mfem::ParFiniteElementSpace par_fe_space{ pmesh.get(), &fe_coll, pmesh->SpaceDimension() }; + mfem::ParBilinearForm M_par{ &par_fe_space }; + mfem::ConstantCoefficient rho0{ 1.0 }; + M_par.AddDomainIntegrator( new mfem::VectorMassIntegrator( rho0 ) ); M_par.Assemble(); M_par.Finalize(); - std::unique_ptr Mmat_par(M_par.ParallelAssemble()); + std::unique_ptr Mmat_par( M_par.ParallelAssemble() ); // grid function for higher-order nodes - auto par_x_ref_elem = mfem::ParGridFunction(&par_fe_space); - if (order > 1) - { - pmesh->SetNodalGridFunction(&par_x_ref_elem, false); + auto par_x_ref_elem = mfem::ParGridFunction( &par_fe_space ); + if ( order > 1 ) { + pmesh->SetNodalGridFunction( &par_x_ref_elem, false ); + } else { + pmesh->GetNodes( par_x_ref_elem ); } - else - { - pmesh->GetNodes(par_x_ref_elem); - } - pmesh_dc.RegisterField("ref_coord", &par_x_ref_elem); + pmesh_dc.RegisterField( "ref_coord", &par_x_ref_elem ); pmesh_dc.Save(); - SLIC_INFO_ROOT("Creating redecomp::RedecompMesh..."); + SLIC_INFO_ROOT( "Creating redecomp::RedecompMesh..." ); // create redecomp mesh - redecomp::RedecompMesh redecomp_mesh { *pmesh }; + redecomp::RedecompMesh redecomp_mesh{ *pmesh }; - SLIC_INFO_ROOT("Computing mass matrix on redecomp::RedecompMesh..."); + SLIC_INFO_ROOT( "Computing mass matrix on redecomp::RedecompMesh..." ); // compute mass matrix on redecomp mesh - mfem::FiniteElementSpace redecomp_fe_space { - &redecomp_mesh, - &fe_coll, - redecomp_mesh.SpaceDimension() - }; - mfem::BilinearForm M_redecomp { &redecomp_fe_space }; - M_redecomp.AddDomainIntegrator(new mfem::VectorMassIntegrator(rho0)); + mfem::FiniteElementSpace redecomp_fe_space{ &redecomp_mesh, &fe_coll, redecomp_mesh.SpaceDimension() }; + mfem::BilinearForm M_redecomp{ &redecomp_fe_space }; + M_redecomp.AddDomainIntegrator( new mfem::VectorMassIntegrator( rho0 ) ); int n_els = redecomp_fe_space.GetNE(); - auto elem_idx = redecomp::ArrayUtility::IndexArray(n_els); - axom::Array elem_mats { n_els, n_els }; - for (int i{0}; i < n_els; ++i) - { - M_redecomp.ComputeElementMatrix(i, elem_mats[i]); + auto elem_idx = redecomp::ArrayUtility::IndexArray( n_els ); + axom::Array elem_mats{ n_els, n_els }; + for ( int i{ 0 }; i < n_els; ++i ) { + M_redecomp.ComputeElementMatrix( i, elem_mats[i] ); } - SLIC_INFO_ROOT("Transferring mass matrix from RedecompMesh to ParMesh..."); + SLIC_INFO_ROOT( "Transferring mass matrix from RedecompMesh to ParMesh..." ); // transfer redecomp mass matrix to parallel mesh - redecomp::MatrixTransfer matrix_xfer { - par_fe_space, - par_fe_space, - redecomp_fe_space, - redecomp_fe_space - }; - auto Mmat_xfer = matrix_xfer.TransferToParallel(elem_idx, elem_idx, elem_mats); - - SLIC_INFO_ROOT("Computing max norm of difference..."); + redecomp::MatrixTransfer matrix_xfer{ par_fe_space, par_fe_space, redecomp_fe_space, redecomp_fe_space }; + auto Mmat_xfer = matrix_xfer.TransferToParallel( elem_idx, elem_idx, elem_mats ); + + SLIC_INFO_ROOT( "Computing max norm of difference..." ); // compare Mmat_par and Mmat_xfer mfem::SparseMatrix Msm_par; - Mmat_par->MergeDiagAndOffd(Msm_par); + Mmat_par->MergeDiagAndOffd( Msm_par ); mfem::DenseMatrix Mf_par; - Msm_par.ToDenseMatrix(Mf_par); + Msm_par.ToDenseMatrix( Mf_par ); mfem::SparseMatrix Msm_xfer2; - Mmat_xfer->MergeDiagAndOffd(Msm_xfer2); + Mmat_xfer->MergeDiagAndOffd( Msm_xfer2 ); mfem::DenseMatrix Mf_xfer2; - Msm_xfer2.ToDenseMatrix(Mf_xfer2); - mfem::DenseMatrix Mf_diff(Mf_par); - Mf_diff.Add(-1.0, Mf_xfer2); + Msm_xfer2.ToDenseMatrix( Mf_xfer2 ); + mfem::DenseMatrix Mf_diff( Mf_par ); + Mf_diff.Add( -1.0, Mf_xfer2 ); auto diff = Mf_diff.MaxMaxNorm(); - redecomp::MPIUtility mpi_util(MPI_COMM_WORLD); - diff = mpi_util.AllreduceValue(diff, MPI_MAX); - SLIC_INFO_ROOT(axom::fmt::format("max |M_diff| = {0:e}", diff)); + redecomp::MPIUtility mpi_util( MPI_COMM_WORLD ); + diff = mpi_util.AllreduceValue( diff, MPI_MAX ); + SLIC_INFO_ROOT( axom::fmt::format( "max |M_diff| = {0:e}", diff ) ); // cleanup MPI_Finalize(); diff --git a/src/examples/examples_common.hpp b/src/examples/examples_common.hpp index 2d0c9fee..7628f1a5 100644 --- a/src/examples/examples_common.hpp +++ b/src/examples/examples_common.hpp @@ -19,14 +19,14 @@ #include "tribol/utils/TestUtils.hpp" // C/C++ includes -#include // for std::fill_n() +#include // for std::fill_n() #include // namespace aliases -namespace primal = axom::primal; -namespace slic = axom::slic; +namespace primal = axom::primal; +namespace slic = axom::slic; namespace utilities = axom::utilities; -namespace CLI = axom::CLI; +namespace CLI = axom::CLI; using RealT = tribol::RealT; @@ -39,72 +39,67 @@ using RealT = tribol::RealT; */ enum BLOCK_EX_BCS { - NO_BCS, ///! no boundary conditions - PATCH_BCS, ///! patch test boundary conditions - NUM_BLOCK_EX_BCS + NO_BCS, ///! no boundary conditions + PATCH_BCS, ///! patch test boundary conditions + NUM_BLOCK_EX_BCS }; /** * Custom CLI11 Validator to check that a command line argument * is a number greater than or equal to \a n */ -class NumberAtLeast : public CLI::Validator -{ - public: - NumberAtLeast(int n) : Validator(axom::fmt::format(">={} ",n)) - { - func_ = [=](std::string &number_str) { - int number; - if(!CLI::detail::lexical_cast(number_str, number)) - { - return "Failed parsing as a number " + number_str; - } - if(number < n) - { - return axom::fmt::format("Number ({}) must be at least {} ", number, n); - } - return std::string(); - }; - } +class NumberAtLeast : public CLI::Validator { + public: + NumberAtLeast( int n ) : Validator( axom::fmt::format( ">={} ", n ) ) + { + func_ = [=]( std::string& number_str ) { + int number; + if ( !CLI::detail::lexical_cast( number_str, number ) ) { + return "Failed parsing as a number " + number_str; + } + if ( number < n ) { + return axom::fmt::format( "Number ({}) must be at least {} ", number, n ); + } + return std::string(); + }; + } }; /// Utility function to return an axom::Point from a vector -template -primal::Point getPoint(const std::vector& vec) +template +primal::Point getPoint( const std::vector& vec ) { - return primal::Point(vec.data(), D); + return primal::Point( vec.data(), D ); } /// Utility function to return an axom BoundingBox from a pair of vector. /// The expectation is that the vectors represent the min and max coords of the box -template -primal::BoundingBox getBoundingBox(const std::vector& mins, - const std::vector& maxs) +template +primal::BoundingBox getBoundingBox( const std::vector& mins, const std::vector& maxs ) { - return primal::BoundingBox(getPoint(mins), getPoint(maxs)); + return primal::BoundingBox( getPoint( mins ), getPoint( maxs ) ); } /*! * \brief Holds command line arguments (with defaults) for the examples. */ -struct Arguments -{ - int dimension{3}; // problem dimension, i.e., 2 or 3. - int refinement_factor{0}; // factor to control number of contact cells - int numcycles{10}; // total number of cycles - int m; // number of vertices in polygon A (intersection ex.) - int n; // number of vertices in polygon B (intersection ex.) - std::string output_dir{}; // directory for file output - double penalty_stiffness{1.}; // penalty stiffness - bool dump_vis{false}; // should the example dump visualization files? +struct Arguments { + int dimension{ 3 }; // problem dimension, i.e., 2 or 3. + int refinement_factor{ 0 }; // factor to control number of contact cells + int numcycles{ 10 }; // total number of cycles + int m; // number of vertices in polygon A (intersection ex.) + int n; // number of vertices in polygon B (intersection ex.) + std::string output_dir{}; // directory for file output + double penalty_stiffness{ 1. }; // penalty stiffness + bool dump_vis{ false }; // should the example dump visualization files? /// input arguments for examples that use the TestMesh class - std::vector block1_res{4, 4, 4}; // block1 -- number of elements in each direction - std::vector block1_min {0., 0., 0.}; // block1 -- bounding box min - std::vector block1_max {1., 1., 1.05}; // block1 -- bounding box max - std::vector block2_res{4, 4, 4}; // block2 -- number of elements in each direction - std::vector block2_min {0., 0., 0.95}; // block2 -- bounding box min - std::vector block2_max {1., 1., 2.}; // block2 -- bounding box max + std::vector block1_res{ 4, 4, 4 }; // block1 -- number of elements in each direction + std::vector block1_min{ 0., 0., 0. }; // block1 -- bounding box min + std::vector block1_max{ 1., 1., 1.05 }; // block1 -- bounding box max + std::vector block2_res{ 4, 4, 4 }; // block2 -- number of elements in each direction + std::vector block2_min{ 0., 0., 0.95 }; // block2 -- bounding box min + std::vector block2_max{ 1., 1., 2. }; // block2 -- bounding box max }; //------------------------------------------------------------------------------ @@ -113,382 +108,301 @@ struct Arguments /*! * \brief Parse command line arguments - * \param [in] + * \param [in] */ -int parse_command_line_args( std::string ex_name, Arguments &args, int argc, char** argv) +int parse_command_line_args( std::string ex_name, Arguments& args, int argc, char** argv ) { - CLI::App app {ex_name}; - app.add_option("--block1_res", - args.block1_res, - "Mesh 1 x,y,z discretization") - ->expected(3); - - app.add_option("--block1_min", - args.block1_min, - "Bounding box min for Mesh 1") - ->expected(3); - - app.add_option("--block1_max", - args.block1_max, - "Bounding box max for Mesh 1") - ->expected(3); - - app.add_option("--block2_res", - args.block2_res, - "Mesh 2 x,y,z discretization") - ->expected(3); - - app.add_option("--block2_min", - args.block2_min, - "Bounding box min for Mesh 2") - ->expected(3); - app.add_option("--block2_max", - args.block2_max, - "Bounding box max for Mesh 2") - ->expected(3); - - app.add_flag("--dump-vis,!--no-dump-vis", - args.dump_vis, - "Dump vis files?") - ->capture_default_str(); - - app.get_formatter()->column_width(35); - - CLI11_PARSE(app,argc,argv); - - // print parsed args to screen - SLIC_INFO("Mesh 1" - << "\n\t x,y,z discretization: " << getPoint(args.block1_res) - << "\n\t Bounding box: " << getBoundingBox(args.block1_min, args.block1_max)); - SLIC_INFO("Mesh 2" - << "\n\t x,y,z discretization: " << getPoint(args.block2_res) - << "\n\t Bounding box: " << getBoundingBox(args.block2_min, args.block2_max)); - - return 0; + CLI::App app{ ex_name }; + app.add_option( "--block1_res", args.block1_res, "Mesh 1 x,y,z discretization" )->expected( 3 ); + + app.add_option( "--block1_min", args.block1_min, "Bounding box min for Mesh 1" )->expected( 3 ); + + app.add_option( "--block1_max", args.block1_max, "Bounding box max for Mesh 1" )->expected( 3 ); + + app.add_option( "--block2_res", args.block2_res, "Mesh 2 x,y,z discretization" )->expected( 3 ); + + app.add_option( "--block2_min", args.block2_min, "Bounding box min for Mesh 2" )->expected( 3 ); + app.add_option( "--block2_max", args.block2_max, "Bounding box max for Mesh 2" )->expected( 3 ); + + app.add_flag( "--dump-vis,!--no-dump-vis", args.dump_vis, "Dump vis files?" )->capture_default_str(); + + app.get_formatter()->column_width( 35 ); + + CLI11_PARSE( app, argc, argv ); + + // print parsed args to screen + SLIC_INFO( "Mesh 1" + << "\n\t x,y,z discretization: " << getPoint( args.block1_res ) + << "\n\t Bounding box: " << getBoundingBox( args.block1_min, args.block1_max ) ); + SLIC_INFO( "Mesh 2" + << "\n\t x,y,z discretization: " << getPoint( args.block2_res ) + << "\n\t Bounding box: " << getBoundingBox( args.block2_min, args.block2_max ) ); + + return 0; } -void build_mesh_3D( tribol::TestMesh &mesh, Arguments &args, BLOCK_EX_BCS bc_type ) +void build_mesh_3D( tribol::TestMesh& mesh, Arguments& args, BLOCK_EX_BCS bc_type ) { - auto& res1 = args.block1_res; - auto& min1 = args.block1_min; - auto& max1 = args.block1_max; - - auto& res2 = args.block2_res; - auto& min2 = args.block2_min; - auto& max2 = args.block2_max; - - mesh.setupContactMeshHex( res1[0], res1[1], res1[2], - min1[0], min1[1], min1[2], - max1[0], max1[1], max1[2], - res2[0], res2[1], res2[2], - min2[0], min2[1], min2[2], - max2[0], max2[1], max2[2], - 0., 0. ); - - switch (bc_type) - { - case NO_BCS: - { - // no boundary conditions required on mesh object - break; - } - case PATCH_BCS: - { - // setup block 1 homogeneous Dirichlet BCs - mesh.setupPatchTestDirichletBCs( mesh.mortarMeshId, res1[0], res1[1], res1[2], - 0, false, 0. ); - - // setup block 2 homogeneous Dirichlet BCs - mesh.setupPatchTestDirichletBCs( mesh.nonmortarMeshId, res2[0], res2[1], res2[2], - mesh.numMortarNodes, false, 0. ); - - // setup DUMMY block 1 pressure dof array - mesh.setupPatchTestPressureDofs( mesh.mortarMeshId, res1[0], res1[1], res1[2], 0, false ); - - // setup block 2 pressure dofs - mesh.setupPatchTestPressureDofs( mesh.nonmortarMeshId, res2[0], res2[1], res2[2], - mesh.numMortarNodes, true ); - break; - } - default: - { - SLIC_ERROR( "build_mesh_3D(): boundary condition type on 3D test mesh not supported." ); - } - } + auto& res1 = args.block1_res; + auto& min1 = args.block1_min; + auto& max1 = args.block1_max; + + auto& res2 = args.block2_res; + auto& min2 = args.block2_min; + auto& max2 = args.block2_max; + + mesh.setupContactMeshHex( res1[0], res1[1], res1[2], min1[0], min1[1], min1[2], max1[0], max1[1], max1[2], res2[0], + res2[1], res2[2], min2[0], min2[1], min2[2], max2[0], max2[1], max2[2], 0., 0. ); - return; + switch ( bc_type ) { + case NO_BCS: { + // no boundary conditions required on mesh object + break; + } + case PATCH_BCS: { + // setup block 1 homogeneous Dirichlet BCs + mesh.setupPatchTestDirichletBCs( mesh.mortarMeshId, res1[0], res1[1], res1[2], 0, false, 0. ); + + // setup block 2 homogeneous Dirichlet BCs + mesh.setupPatchTestDirichletBCs( mesh.nonmortarMeshId, res2[0], res2[1], res2[2], mesh.numMortarNodes, false, + 0. ); + + // setup DUMMY block 1 pressure dof array + mesh.setupPatchTestPressureDofs( mesh.mortarMeshId, res1[0], res1[1], res1[2], 0, false ); + + // setup block 2 pressure dofs + mesh.setupPatchTestPressureDofs( mesh.nonmortarMeshId, res2[0], res2[1], res2[2], mesh.numMortarNodes, true ); + break; + } + default: { + SLIC_ERROR( "build_mesh_3D(): boundary condition type on 3D test mesh not supported." ); + } + } + + return; } -int tribol_register_and_update( tribol::TestMesh &mesh, - tribol::ContactMethod method, - tribol::EnforcementMethod enforcement, - tribol::ContactModel model, - bool visualization, +int tribol_register_and_update( tribol::TestMesh& mesh, tribol::ContactMethod method, + tribol::EnforcementMethod enforcement, tribol::ContactModel model, bool visualization, tribol::TestControlParameters* params ) { - ///////////////////////////////////////////// - // // - // STEP 1: register the interacting meshes // - // // - // note: the physics application // - // (host code) calling Tribol will have // - // their own mesh data. For this example, // - // Tribol makes use of an internal // - // mesh class typically used for testing. // - // // - ///////////////////////////////////////////// - const int cellType = mesh.cellType; - - - // set the mesh ids for block 1 and block 2 - const int block1_id = 0; - const int block2_id = 1; - - // register the two meshes with Tribol - tribol::registerMesh( block1_id, mesh.numMortarFaces, - mesh.numTotalNodes, - mesh.faceConn1, cellType, - mesh.x, mesh.y, mesh.z, tribol::MemorySpace::Host ); - tribol::registerMesh( block2_id, mesh.numNonmortarFaces, - mesh.numTotalNodes, - mesh.faceConn2, cellType, - mesh.x, mesh.y, mesh.z, tribol::MemorySpace::Host ); - /////////////////////////////////// - // // - // STEP 2: register field arrays // - // // - /////////////////////////////////// - { - - /////////////////////////////////////////////// - // // - // STEP 2.0: register the nodal force arrays // - // // - // note: these are the residuals for each // - // mesh sized such that the connectivity // - // array ids can properly index into each // - // array. For clarity, separate x,y, and z // - // arrays are created for each block, but // - // in practice these can be the same // - // arrays for both blocks. // - // // - /////////////////////////////////////////////// - tribol::allocRealArray( &mesh.fx1, mesh.numTotalNodes, 0. ); - tribol::allocRealArray( &mesh.fy1, mesh.numTotalNodes, 0. ); - tribol::allocRealArray( &mesh.fz1, mesh.numTotalNodes, 0. ); - tribol::allocRealArray( &mesh.fx2, mesh.numTotalNodes, 0. ); - tribol::allocRealArray( &mesh.fy2, mesh.numTotalNodes, 0. ); - tribol::allocRealArray( &mesh.fz2, mesh.numTotalNodes, 0. ); - - tribol::registerNodalResponse( block1_id, - mesh.fx1, - mesh.fy1, - mesh.fz1 ); - - tribol::registerNodalResponse( block2_id, - mesh.fx2, - mesh.fy2, - mesh.fz2 ); - - ////////////////////////////////////////////////////// - // // - // STEP 2.1: register nodal pressure and gap arrays // - // // - // note: this is for mortar-based methods ONLY. // - // // - ////////////////////////////////////////////////////// - if ( method == tribol::SINGLE_MORTAR || - method == tribol::ALIGNED_MORTAR ) - { - tribol::allocRealArray( &mesh.gaps, mesh.numTotalNodes, 0. ); - tribol::allocRealArray( &mesh.pressures, mesh.numTotalNodes, 1. ); - - // register nodal gaps and pressures. Note: for single sided mortar - // methods these fields are only registered for one mesh. By - // convention, this is the second mesh/block. - tribol::registerMortarGaps( block2_id, mesh.gaps ); - tribol::registerMortarPressures( block2_id, mesh.pressures ); - } - else if (method == tribol::MORTAR_WEIGHTS) - { - tribol::allocRealArray( &mesh.gaps, mesh.numTotalNodes, 0. ); - mesh.pressures = nullptr; // not needed - - tribol::registerMortarGaps( block2_id, mesh.gaps ); - } - - } // end STEP 2 scope - - ///////////////////////////////////////////// - // // - // STEP 3: register enforcement parameters // - // // - // note: these are method specific // - // and may not be applicable for a given // - // method. // - // // - ///////////////////////////////////////////// - { - /////////////////////////////////////////////////////// - // // - // STEP 3.0: register penalty enforcement parameters // - // // - /////////////////////////////////////////////////////// - if (enforcement == tribol::PENALTY) - { - if (params == nullptr) - { - SLIC_ERROR( "tribol_register_and_update(): " << - "tribol::TestControlParameters pointer is null. " << - "Parameters are required for penalty enforcement." ); - } - - if (!params->penalty_ratio) - { - tribol::setKinematicConstantPenalty( block1_id, params->const_penalty ); - tribol::setKinematicConstantPenalty( block2_id, params->const_penalty ); - } - - else - { - // mortar penalty data - tribol::allocRealArray( &mesh.mortar_bulk_mod, mesh.numMortarFaces, - params->const_penalty ); - tribol::allocRealArray( &mesh.mortar_element_thickness, mesh.numMortarFaces, 1.0 ); - tribol::registerRealElementField( block1_id, tribol::BULK_MODULUS, - mesh.mortar_bulk_mod ); - tribol::registerRealElementField( block1_id, tribol::ELEMENT_THICKNESS, - mesh.mortar_element_thickness ); - - // nonmortar penalty data - tribol::allocRealArray( &mesh.nonmortar_bulk_mod, mesh.numNonmortarFaces, - params->const_penalty ); - tribol::allocRealArray( &mesh.nonmortar_element_thickness, mesh.numNonmortarFaces, 1.0 ); - tribol::registerRealElementField( block2_id, tribol::BULK_MODULUS, - mesh.nonmortar_bulk_mod ); - tribol::registerRealElementField( block2_id, tribol::ELEMENT_THICKNESS, - mesh.nonmortar_element_thickness ); - } - } // end if-penalty - - } // end STEP 3 scope - - /////////////////////////////////////////////// - // // - // STEP 4: register Tribol "coupling scheme" // - // // - /////////////////////////////////////////////// - const int csIndex = 0; - registerCouplingScheme( csIndex, - block1_id, - block2_id, - tribol::SURFACE_TO_SURFACE, - tribol::AUTO, - method, - model, - enforcement, - tribol::BINNING_GRID, - tribol::ExecutionMode::Sequential ); - - ////////////////////////////////////////////////////////// - // // - // STEP 4.5: set enforcement options on coupling scheme // - // // - ////////////////////////////////////////////////////////// - if ( method == tribol::COMMON_PLANE && enforcement == tribol::PENALTY ) - { - tribol::PenaltyConstraintType constraint_type = (params->constant_rate_penalty || params->percent_rate_penalty) - ? tribol::KINEMATIC_AND_RATE : tribol::KINEMATIC; - tribol::KinematicPenaltyCalculation pen_calc = (params->penalty_ratio) - ? tribol::KINEMATIC_ELEMENT : tribol::KINEMATIC_CONSTANT; - - tribol::RatePenaltyCalculation rate_calc; - rate_calc = tribol::NO_RATE_PENALTY; - if (params->constant_rate_penalty) - { - rate_calc = tribol::RATE_CONSTANT; + ///////////////////////////////////////////// + // // + // STEP 1: register the interacting meshes // + // // + // note: the physics application // + // (host code) calling Tribol will have // + // their own mesh data. For this example, // + // Tribol makes use of an internal // + // mesh class typically used for testing. // + // // + ///////////////////////////////////////////// + const int cellType = mesh.cellType; + + // set the mesh ids for block 1 and block 2 + const int block1_id = 0; + const int block2_id = 1; + + // register the two meshes with Tribol + tribol::registerMesh( block1_id, mesh.numMortarFaces, mesh.numTotalNodes, mesh.faceConn1, cellType, mesh.x, mesh.y, + mesh.z, tribol::MemorySpace::Host ); + tribol::registerMesh( block2_id, mesh.numNonmortarFaces, mesh.numTotalNodes, mesh.faceConn2, cellType, mesh.x, mesh.y, + mesh.z, tribol::MemorySpace::Host ); + /////////////////////////////////// + // // + // STEP 2: register field arrays // + // // + /////////////////////////////////// + { + /////////////////////////////////////////////// + // // + // STEP 2.0: register the nodal force arrays // + // // + // note: these are the residuals for each // + // mesh sized such that the connectivity // + // array ids can properly index into each // + // array. For clarity, separate x,y, and z // + // arrays are created for each block, but // + // in practice these can be the same // + // arrays for both blocks. // + // // + /////////////////////////////////////////////// + tribol::allocRealArray( &mesh.fx1, mesh.numTotalNodes, 0. ); + tribol::allocRealArray( &mesh.fy1, mesh.numTotalNodes, 0. ); + tribol::allocRealArray( &mesh.fz1, mesh.numTotalNodes, 0. ); + tribol::allocRealArray( &mesh.fx2, mesh.numTotalNodes, 0. ); + tribol::allocRealArray( &mesh.fy2, mesh.numTotalNodes, 0. ); + tribol::allocRealArray( &mesh.fz2, mesh.numTotalNodes, 0. ); + + tribol::registerNodalResponse( block1_id, mesh.fx1, mesh.fy1, mesh.fz1 ); + + tribol::registerNodalResponse( block2_id, mesh.fx2, mesh.fy2, mesh.fz2 ); + + ////////////////////////////////////////////////////// + // // + // STEP 2.1: register nodal pressure and gap arrays // + // // + // note: this is for mortar-based methods ONLY. // + // // + ////////////////////////////////////////////////////// + if ( method == tribol::SINGLE_MORTAR || method == tribol::ALIGNED_MORTAR ) { + tribol::allocRealArray( &mesh.gaps, mesh.numTotalNodes, 0. ); + tribol::allocRealArray( &mesh.pressures, mesh.numTotalNodes, 1. ); + + // register nodal gaps and pressures. Note: for single sided mortar + // methods these fields are only registered for one mesh. By + // convention, this is the second mesh/block. + tribol::registerMortarGaps( block2_id, mesh.gaps ); + tribol::registerMortarPressures( block2_id, mesh.pressures ); + } else if ( method == tribol::MORTAR_WEIGHTS ) { + tribol::allocRealArray( &mesh.gaps, mesh.numTotalNodes, 0. ); + mesh.pressures = nullptr; // not needed + + tribol::registerMortarGaps( block2_id, mesh.gaps ); + } + + } // end STEP 2 scope + + ///////////////////////////////////////////// + // // + // STEP 3: register enforcement parameters // + // // + // note: these are method specific // + // and may not be applicable for a given // + // method. // + // // + ///////////////////////////////////////////// + { + /////////////////////////////////////////////////////// + // // + // STEP 3.0: register penalty enforcement parameters // + // // + /////////////////////////////////////////////////////// + if ( enforcement == tribol::PENALTY ) { + if ( params == nullptr ) { + SLIC_ERROR( "tribol_register_and_update(): " + << "tribol::TestControlParameters pointer is null. " + << "Parameters are required for penalty enforcement." ); } - else if (params->percent_rate_penalty) - { - rate_calc = tribol::RATE_PERCENT; + + if ( !params->penalty_ratio ) { + tribol::setKinematicConstantPenalty( block1_id, params->const_penalty ); + tribol::setKinematicConstantPenalty( block2_id, params->const_penalty ); } - // set penalty options after registering coupling scheme - tribol::setPenaltyOptions( csIndex, constraint_type, pen_calc, rate_calc ); - } - else if ( (method == tribol::SINGLE_MORTAR || method == tribol::ALIGNED_MORTAR) && - enforcement == tribol::LAGRANGE_MULTIPLIER ) - { - tribol::setLagrangeMultiplierOptions( csIndex, tribol::ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN, - tribol::SparseMode::MFEM_LINKED_LIST ); - } - else if ( method == tribol::MORTAR_WEIGHTS ) - { - tribol::setLagrangeMultiplierOptions( csIndex, tribol::ImplicitEvalMode::MORTAR_WEIGHTS_EVAL, - tribol::SparseMode::MFEM_LINKED_LIST ); - } - else - { - SLIC_ERROR( "Method and enforcement are not supported in the examples." ); - } - - /////////////////////////////////// - // // - // STEP 5: optional registration // - // // - /////////////////////////////////// - { - //////////////////////////////////////////////////////////////////// - // // - // STEP 5.0 (optional): register plot cycle increment // - // for visualizing registered meshes, // - // active surface faces, and patch overlaps. // - // This is hardcoded as default behavior // - // for examples. // - // // - //////////////////////////////////////////////////////////////////// - if (visualization) - { - tribol::setPlotCycleIncrement( csIndex, 1 ); - tribol::setPlotOptions( csIndex, tribol::VIS_MESH_FACES_AND_OVERLAPS ); - } - - ////////////////////////////////////////////////////////////// - // // - // STEP 5.1 (optional): register the area fraction for the // - // smallest considered contact overlap // - // patch // - // // - // note: this is not required and a default value is // - // used by Tribol that should be sufficient for all // - // methods. // - // // - ////////////////////////////////////////////////////////////// - tribol::setContactAreaFrac( csIndex, 1.e-6 ); - - } // end STEP 5 scope - - - ////////////////////////////////////// - // // - // STEP 6: call Tribol update // - // // - // note: this updates all pertinent // - // fields registered with Tribol // - // per a given interface numerical // - // method. // - // // - ////////////////////////////////////// - double dt = 1.0; - int err = tribol::update( 1, 1., dt ); - - // specific to TestMesh class; dump mesh to vtk for visualization - if (visualization) - { - mesh.testMeshToVtk( "", 1, 1 ); - } - - return err; + else { + // mortar penalty data + tribol::allocRealArray( &mesh.mortar_bulk_mod, mesh.numMortarFaces, params->const_penalty ); + tribol::allocRealArray( &mesh.mortar_element_thickness, mesh.numMortarFaces, 1.0 ); + tribol::registerRealElementField( block1_id, tribol::BULK_MODULUS, mesh.mortar_bulk_mod ); + tribol::registerRealElementField( block1_id, tribol::ELEMENT_THICKNESS, mesh.mortar_element_thickness ); + + // nonmortar penalty data + tribol::allocRealArray( &mesh.nonmortar_bulk_mod, mesh.numNonmortarFaces, params->const_penalty ); + tribol::allocRealArray( &mesh.nonmortar_element_thickness, mesh.numNonmortarFaces, 1.0 ); + tribol::registerRealElementField( block2_id, tribol::BULK_MODULUS, mesh.nonmortar_bulk_mod ); + tribol::registerRealElementField( block2_id, tribol::ELEMENT_THICKNESS, mesh.nonmortar_element_thickness ); + } + } // end if-penalty + + } // end STEP 3 scope + + /////////////////////////////////////////////// + // // + // STEP 4: register Tribol "coupling scheme" // + // // + /////////////////////////////////////////////// + const int csIndex = 0; + registerCouplingScheme( csIndex, block1_id, block2_id, tribol::SURFACE_TO_SURFACE, tribol::AUTO, method, model, + enforcement, tribol::BINNING_GRID, tribol::ExecutionMode::Sequential ); + + ////////////////////////////////////////////////////////// + // // + // STEP 4.5: set enforcement options on coupling scheme // + // // + ////////////////////////////////////////////////////////// + if ( method == tribol::COMMON_PLANE && enforcement == tribol::PENALTY ) { + tribol::PenaltyConstraintType constraint_type = ( params->constant_rate_penalty || params->percent_rate_penalty ) + ? tribol::KINEMATIC_AND_RATE + : tribol::KINEMATIC; + tribol::KinematicPenaltyCalculation pen_calc = + ( params->penalty_ratio ) ? tribol::KINEMATIC_ELEMENT : tribol::KINEMATIC_CONSTANT; + + tribol::RatePenaltyCalculation rate_calc; + rate_calc = tribol::NO_RATE_PENALTY; + if ( params->constant_rate_penalty ) { + rate_calc = tribol::RATE_CONSTANT; + } else if ( params->percent_rate_penalty ) { + rate_calc = tribol::RATE_PERCENT; + } + + // set penalty options after registering coupling scheme + tribol::setPenaltyOptions( csIndex, constraint_type, pen_calc, rate_calc ); + } else if ( ( method == tribol::SINGLE_MORTAR || method == tribol::ALIGNED_MORTAR ) && + enforcement == tribol::LAGRANGE_MULTIPLIER ) { + tribol::setLagrangeMultiplierOptions( csIndex, tribol::ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN, + tribol::SparseMode::MFEM_LINKED_LIST ); + } else if ( method == tribol::MORTAR_WEIGHTS ) { + tribol::setLagrangeMultiplierOptions( csIndex, tribol::ImplicitEvalMode::MORTAR_WEIGHTS_EVAL, + tribol::SparseMode::MFEM_LINKED_LIST ); + } else { + SLIC_ERROR( "Method and enforcement are not supported in the examples." ); + } + + /////////////////////////////////// + // // + // STEP 5: optional registration // + // // + /////////////////////////////////// + { + //////////////////////////////////////////////////////////////////// + // // + // STEP 5.0 (optional): register plot cycle increment // + // for visualizing registered meshes, // + // active surface faces, and patch overlaps. // + // This is hardcoded as default behavior // + // for examples. // + // // + //////////////////////////////////////////////////////////////////// + if ( visualization ) { + tribol::setPlotCycleIncrement( csIndex, 1 ); + tribol::setPlotOptions( csIndex, tribol::VIS_MESH_FACES_AND_OVERLAPS ); + } + + ////////////////////////////////////////////////////////////// + // // + // STEP 5.1 (optional): register the area fraction for the // + // smallest considered contact overlap // + // patch // + // // + // note: this is not required and a default value is // + // used by Tribol that should be sufficient for all // + // methods. // + // // + ////////////////////////////////////////////////////////////// + tribol::setContactAreaFrac( csIndex, 1.e-6 ); + + } // end STEP 5 scope + + ////////////////////////////////////// + // // + // STEP 6: call Tribol update // + // // + // note: this updates all pertinent // + // fields registered with Tribol // + // per a given interface numerical // + // method. // + // // + ////////////////////////////////////// + double dt = 1.0; + int err = tribol::update( 1, 1., dt ); + + // specific to TestMesh class; dump mesh to vtk for visualization + if ( visualization ) { + mesh.testMeshToVtk( "", 1, 1 ); + } + + return err; } /*! @@ -502,23 +416,16 @@ void initialize_logger( tribol::CommT problem_comm ) std::string msg_format = "[] \n"; #ifdef TRIBOL_USE_MPI - slic::addStreamToAllMsgLevels( - new slic::SynchronizedStream( &std::cout, problem_comm, msg_format ) ); + slic::addStreamToAllMsgLevels( new slic::SynchronizedStream( &std::cout, problem_comm, msg_format ) ); #else - slic::addStreamToAllMsgLevels( - new slic::GenericOutputStream( &std::cout, msg_format ) ); - static_cast(problem_comm); // elide warning about unused vars + slic::addStreamToAllMsgLevels( new slic::GenericOutputStream( &std::cout, msg_format ) ); + static_cast( problem_comm ); // elide warning about unused vars #endif } /*! * \brief Finalize logger */ -void finalize_logger( ) -{ - slic::finalize( ); -} - - +void finalize_logger() { slic::finalize(); } #endif /* EXAMPLES_COMMON_HPP_ */ diff --git a/src/examples/mfem_common_plane.cpp b/src/examples/mfem_common_plane.cpp index d4c9756c..298652dd 100644 --- a/src/examples/mfem_common_plane.cpp +++ b/src/examples/mfem_common_plane.cpp @@ -55,8 +55,8 @@ int main( int argc, char** argv ) // initialize MPI MPI_Init( &argc, &argv ); int np, rank; - MPI_Comm_size(MPI_COMM_WORLD, &np); - MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size( MPI_COMM_WORLD, &np ); + MPI_Comm_rank( MPI_COMM_WORLD, &rank ); #ifdef TRIBOL_USE_UMPIRE umpire::ResourceManager::getInstance(); // initialize umpire's ResouceManager @@ -64,7 +64,7 @@ int main( int argc, char** argv ) // initialize logger axom::slic::SimpleLogger logger; - axom::slic::setIsRoot(rank == 0); + axom::slic::setIsRoot( rank == 0 ); // define command line options // number of times to uniformly refine the serial mesh before constructing the @@ -90,118 +90,96 @@ int main( int argc, char** argv ) double mu = 100000.0; // kinematic penalty parameter (only for constant penalty) // the kinematic constant penalty is chosen here to match the kinematic element penalty - double p_kine = (lambda + 2.0 / 3.0 * mu) / (1.0 / std::pow(2.0, ref_levels)); + double p_kine = ( lambda + 2.0 / 3.0 * mu ) / ( 1.0 / std::pow( 2.0, ref_levels ) ); // parse command line options - axom::CLI::App app { "mfem_common_plane" }; - app.add_option("-r,--refine", ref_levels, - "Number of times to refine the mesh uniformly.") - ->capture_default_str(); - app.add_option("-o,--order", order, - "Finite element order (polynomial degree).") - ->capture_default_str(); - app.add_option("-v,--initialv", init_velocity, - "Initial velocity of the top block.") - ->capture_default_str(); - app.add_option("-d,--dt", dt, - "Timestep size (fixed).") - ->capture_default_str(); - app.add_option("-e,--endtime", t_end, - "End time of the simulation.") - ->capture_default_str(); - app.add_flag("-C,--use-constant-penalty", use_const_penalty, - "Use a constant penalty parameter (element thickness-based penalty otherwise)") - ->capture_default_str(); - app.add_option("-p,--kinematicpenalty", p_kine, - "Kinematic penalty parameter (if --use-element-penalty is false).") - ->capture_default_str()->needs("-C"); - app.add_option("-c,--outputcycles", output_cycles, - "Number of cycles to skip before next output.") - ->capture_default_str(); - app.add_option("-R,--rho", rho, - "Material density.") - ->capture_default_str(); - app.add_option("-l,--lambda", lambda, - "Lame parameter lambda.") - ->capture_default_str(); - app.add_option("-m,--mu", mu, - "Lame parameter mu (shear modulus).") - ->capture_default_str(); - CLI11_PARSE(app, argc, argv); - - SLIC_INFO_ROOT("Running mfem_common_plane with the following options:"); - SLIC_INFO_ROOT(axom::fmt::format("refinement levels: {0}", ref_levels)); - SLIC_INFO_ROOT(axom::fmt::format("polynomial order: {0}", order)); - SLIC_INFO_ROOT(axom::fmt::format("initial velocity: {0}", init_velocity)); - SLIC_INFO_ROOT(axom::fmt::format("timestep size: {0}", dt)); - SLIC_INFO_ROOT(axom::fmt::format("end time: {0}", t_end)); - SLIC_INFO_ROOT(axom::fmt::format("kinematic penalty: {0}", p_kine)); - SLIC_INFO_ROOT(axom::fmt::format("output interval: {0}", output_cycles)); - SLIC_INFO_ROOT(axom::fmt::format("density: {0}", rho)); - SLIC_INFO_ROOT(axom::fmt::format("lambda: {0}", lambda)); - SLIC_INFO_ROOT(axom::fmt::format("mu: {0}\n", mu)); + axom::CLI::App app{ "mfem_common_plane" }; + app.add_option( "-r,--refine", ref_levels, "Number of times to refine the mesh uniformly." )->capture_default_str(); + app.add_option( "-o,--order", order, "Finite element order (polynomial degree)." )->capture_default_str(); + app.add_option( "-v,--initialv", init_velocity, "Initial velocity of the top block." )->capture_default_str(); + app.add_option( "-d,--dt", dt, "Timestep size (fixed)." )->capture_default_str(); + app.add_option( "-e,--endtime", t_end, "End time of the simulation." )->capture_default_str(); + app.add_flag( "-C,--use-constant-penalty", use_const_penalty, + "Use a constant penalty parameter (element thickness-based penalty otherwise)" ) + ->capture_default_str(); + app.add_option( "-p,--kinematicpenalty", p_kine, "Kinematic penalty parameter (if --use-element-penalty is false)." ) + ->capture_default_str() + ->needs( "-C" ); + app.add_option( "-c,--outputcycles", output_cycles, "Number of cycles to skip before next output." ) + ->capture_default_str(); + app.add_option( "-R,--rho", rho, "Material density." )->capture_default_str(); + app.add_option( "-l,--lambda", lambda, "Lame parameter lambda." )->capture_default_str(); + app.add_option( "-m,--mu", mu, "Lame parameter mu (shear modulus)." )->capture_default_str(); + CLI11_PARSE( app, argc, argv ); + + SLIC_INFO_ROOT( "Running mfem_common_plane with the following options:" ); + SLIC_INFO_ROOT( axom::fmt::format( "refinement levels: {0}", ref_levels ) ); + SLIC_INFO_ROOT( axom::fmt::format( "polynomial order: {0}", order ) ); + SLIC_INFO_ROOT( axom::fmt::format( "initial velocity: {0}", init_velocity ) ); + SLIC_INFO_ROOT( axom::fmt::format( "timestep size: {0}", dt ) ); + SLIC_INFO_ROOT( axom::fmt::format( "end time: {0}", t_end ) ); + SLIC_INFO_ROOT( axom::fmt::format( "kinematic penalty: {0}", p_kine ) ); + SLIC_INFO_ROOT( axom::fmt::format( "output interval: {0}", output_cycles ) ); + SLIC_INFO_ROOT( axom::fmt::format( "density: {0}", rho ) ); + SLIC_INFO_ROOT( axom::fmt::format( "lambda: {0}", lambda ) ); + SLIC_INFO_ROOT( axom::fmt::format( "mu: {0}\n", mu ) ); // fixed options // location of mesh file. TRIBOL_REPO_DIR is defined in tribol/config.hpp std::string mesh_file = TRIBOL_REPO_DIR "/data/two_hex_apart.mesh"; // boundary element attributes of contact surface 1, the z = 1 plane of the // first block - auto contact_surf_1 = std::set({4}); + auto contact_surf_1 = std::set( { 4 } ); // boundary element attributes of contact surface 2, the z = 1.01 plane of the // second block - auto contact_surf_2 = std::set({5}); + auto contact_surf_2 = std::set( { 5 } ); // boundary element attributes of fixed surface (z = 0 plane of the first // block, all t) - auto fixed_attrs = std::set({3}); + auto fixed_attrs = std::set( { 3 } ); // element attribute corresponding to volume elements where an initial // velocity will be applied in the z-direction - auto moving_attrs = std::set({2}); + auto moving_attrs = std::set( { 2 } ); // create an axom timer to give wall times for each step - axom::utilities::Timer timer { false }; + axom::utilities::Timer timer{ false }; // This block of code will read the mesh data given in two_hex_apart.mesh, // create an mfem::Mesh, refine the mesh, then create an mfem::ParMesh. // Optionally, the mfem::ParMesh can be refined further on each rank by // setting par_ref_levels >= 1, though this is disabled below. timer.start(); - std::unique_ptr pmesh { nullptr }; + std::unique_ptr pmesh{ nullptr }; { // read serial mesh - auto mesh = std::make_unique(mesh_file.c_str(), 1, 1); + auto mesh = std::make_unique( mesh_file.c_str(), 1, 1 ); // refine serial mesh - if (ref_levels > 0) - { - for (int i{0}; i < ref_levels; ++i) - { + if ( ref_levels > 0 ) { + for ( int i{ 0 }; i < ref_levels; ++i ) { mesh->UniformRefinement(); } } - + // create parallel mesh from serial - pmesh = std::make_unique(MPI_COMM_WORLD, *mesh); - mesh.reset(nullptr); + pmesh = std::make_unique( MPI_COMM_WORLD, *mesh ); + mesh.reset( nullptr ); // further refinement of parallel mesh { // set this to >= 1 to refine the mesh on each rank further int par_ref_levels = 0; - for (int i{0}; i < par_ref_levels; ++i) - { + for ( int i{ 0 }; i < par_ref_levels; ++i ) { pmesh->UniformRefinement(); } } } timer.stop(); - SLIC_INFO_ROOT(axom::fmt::format( - "Time to create parallel mesh: {0:f}ms", timer.elapsedTimeInMilliSec() - )); - + SLIC_INFO_ROOT( axom::fmt::format( "Time to create parallel mesh: {0:f}ms", timer.elapsedTimeInMilliSec() ) ); + // Set up an MFEM data collection for output. We output data in Paraview and // VisIt formats. - auto paraview_datacoll = mfem::ParaViewDataCollection("common_plane_pv", pmesh.get()); - auto visit_datacoll = mfem::VisItDataCollection("common_plane_vi", pmesh.get()); + auto paraview_datacoll = mfem::ParaViewDataCollection( "common_plane_pv", pmesh.get() ); + auto visit_datacoll = mfem::VisItDataCollection( "common_plane_vi", pmesh.get() ); // This block of code creates position, displacement, and velocity grid // functions (and associated finite element collections and finite element @@ -211,40 +189,37 @@ int main( int argc, char** argv ) // collections for output. timer.start(); // Finite element collection (shared between all grid functions). - mfem::H1_FECollection fe_coll { order, pmesh->SpaceDimension() }; + mfem::H1_FECollection fe_coll{ order, pmesh->SpaceDimension() }; // Finite element space (shared between all grid functions). - mfem::ParFiniteElementSpace par_fe_space { - pmesh.get(), &fe_coll, pmesh->SpaceDimension() }; + mfem::ParFiniteElementSpace par_fe_space{ pmesh.get(), &fe_coll, pmesh->SpaceDimension() }; // Create coordinate grid function - mfem::ParGridFunction coords { &par_fe_space }; + mfem::ParGridFunction coords{ &par_fe_space }; // Set coordinate grid function based on nodal locations. In MFEM, nodal // locations of higher order meshes are stored in a grid function. For linear // MFEM meshes, nodal locations can be stored in a grid function or through // the vertex coordinates. Since we track coordinates in a grid function even // for linear meshes, it makes sense to create a nodes grid function so we // don't have to manually update the vertices. - pmesh->SetNodalGridFunction(&coords, false); - paraview_datacoll.RegisterField("pos", &coords); - visit_datacoll.RegisterField("pos", &coords); + pmesh->SetNodalGridFunction( &coords, false ); + paraview_datacoll.RegisterField( "pos", &coords ); + visit_datacoll.RegisterField( "pos", &coords ); // Save reference coordinates - mfem::ParGridFunction ref_coords { coords }; + mfem::ParGridFunction ref_coords{ coords }; // Create a grid function for displacement - mfem::ParGridFunction displacement { &par_fe_space }; - paraview_datacoll.RegisterField("disp", &displacement); - visit_datacoll.RegisterField("disp", &displacement); + mfem::ParGridFunction displacement{ &par_fe_space }; + paraview_datacoll.RegisterField( "disp", &displacement ); + visit_datacoll.RegisterField( "disp", &displacement ); displacement = 0.0; // Create a grid function for velocity - mfem::ParGridFunction velocity { &par_fe_space }; - paraview_datacoll.RegisterField("vel", &velocity); - visit_datacoll.RegisterField("vel", &velocity); + mfem::ParGridFunction velocity{ &par_fe_space }; + paraview_datacoll.RegisterField( "vel", &velocity ); + visit_datacoll.RegisterField( "vel", &velocity ); velocity = 0.0; timer.stop(); - SLIC_INFO_ROOT(axom::fmt::format( - "Time to create grid functions: {0:f}ms", timer.elapsedTimeInMilliSec() - )); + SLIC_INFO_ROOT( axom::fmt::format( "Time to create grid functions: {0:f}ms", timer.elapsedTimeInMilliSec() ) ); timer.start(); { @@ -257,23 +232,18 @@ int main( int argc, char** argv ) // PWVectorCoefficient class, zeros are assigned to missing attribute // numbers. Finally, the velocity coefficient is projected onto the velocity // grid function. - mfem::Vector init_velocity_vector({0.0, 0.0, -std::abs(init_velocity)}); - mfem::VectorConstantCoefficient init_velocity_coeff(init_velocity_vector); + mfem::Vector init_velocity_vector( { 0.0, 0.0, -std::abs( init_velocity ) } ); + mfem::VectorConstantCoefficient init_velocity_coeff( init_velocity_vector ); mfem::Array moving_attrs_array; mfem::Array init_velocity_coeff_array; - moving_attrs_array.Reserve(moving_attrs.size()); - init_velocity_coeff_array.Reserve(moving_attrs.size()); - for (auto moving_attr : moving_attrs) - { - moving_attrs_array.Append(moving_attr); - init_velocity_coeff_array.Append(&init_velocity_coeff); + moving_attrs_array.Reserve( moving_attrs.size() ); + init_velocity_coeff_array.Reserve( moving_attrs.size() ); + for ( auto moving_attr : moving_attrs ) { + moving_attrs_array.Append( moving_attr ); + init_velocity_coeff_array.Append( &init_velocity_coeff ); } - mfem::PWVectorCoefficient initial_v_coeff( - pmesh->SpaceDimension(), - moving_attrs_array, - init_velocity_coeff_array - ); - velocity.ProjectCoefficient(initial_v_coeff); + mfem::PWVectorCoefficient initial_v_coeff( pmesh->SpaceDimension(), moving_attrs_array, init_velocity_coeff_array ); + velocity.ProjectCoefficient( initial_v_coeff ); } // This block of code builds a list of degrees of freedom in the x, y, and z @@ -285,40 +255,36 @@ int main( int argc, char** argv ) // are in the list. mfem::Array ess_vdof_marker; // Also, convert active boundary attributes into markers. - mfem::Array ess_bdr(pmesh->bdr_attributes.Max()); + mfem::Array ess_bdr( pmesh->bdr_attributes.Max() ); ess_bdr = 0; - for (auto fixed_attr : fixed_attrs) - { - ess_bdr[fixed_attr-1] = 1; + for ( auto fixed_attr : fixed_attrs ) { + ess_bdr[fixed_attr - 1] = 1; } // Note no component is given as an argument here, so all components (x, y, // and z) are returned. - par_fe_space.GetEssentialVDofs(ess_bdr, ess_vdof_marker); + par_fe_space.GetEssentialVDofs( ess_bdr, ess_vdof_marker ); // Convert the marker array to a list. - mfem::FiniteElementSpace::MarkerToList(ess_vdof_marker, ess_vdof_list); + mfem::FiniteElementSpace::MarkerToList( ess_vdof_marker, ess_vdof_list ); } timer.stop(); - SLIC_INFO_ROOT(axom::fmt::format( - "Time to set up boundary conditions: {0:f}ms", timer.elapsedTimeInMilliSec() - )); + SLIC_INFO_ROOT( axom::fmt::format( "Time to set up boundary conditions: {0:f}ms", timer.elapsedTimeInMilliSec() ) ); // This block of code builds a small-deformation elasticity explicit, lumped // mass update operator. Lumping is performed using row summation. timer.start(); - mfem::ConstantCoefficient rho_coeff {rho}; - mfem::ConstantCoefficient lambda_coeff {lambda}; - mfem::ConstantCoefficient mu_coeff {mu}; - mfem_ext::ExplicitMechanics op {par_fe_space, rho_coeff, lambda_coeff, mu_coeff}; + mfem::ConstantCoefficient rho_coeff{ rho }; + mfem::ConstantCoefficient lambda_coeff{ lambda }; + mfem::ConstantCoefficient mu_coeff{ mu }; + mfem_ext::ExplicitMechanics op{ par_fe_space, rho_coeff, lambda_coeff, mu_coeff }; timer.stop(); - SLIC_INFO_ROOT(axom::fmt::format( - "Time to set up elasticity bilinear form: {0:f}ms", timer.elapsedTimeInMilliSec() - )); + SLIC_INFO_ROOT( + axom::fmt::format( "Time to set up elasticity bilinear form: {0:f}ms", timer.elapsedTimeInMilliSec() ) ); // This block of code sets up a central difference time integration scheme. // The constructor takes a list of degrees of freedom in the velocity grid // function that will have homogeneous boundary conditions applied to them. - mfem_ext::CentralDiffSolver solver { ess_vdof_list }; - solver.Init(op); + mfem_ext::CentralDiffSolver solver{ ess_vdof_list }; + solver.Init( op ); // This block of code does initial setup of Tribol. timer.start(); @@ -333,55 +299,41 @@ int main( int argc, char** argv ) int coupling_scheme_id = 0; int mesh1_id = 0; int mesh2_id = 1; - tribol::registerMfemCouplingScheme( - coupling_scheme_id, mesh1_id, mesh2_id, - *pmesh, coords, contact_surf_1, contact_surf_2, - tribol::SURFACE_TO_SURFACE, - tribol::NO_CASE, - tribol::COMMON_PLANE, - tribol::FRICTIONLESS, - tribol::PENALTY, - tribol::BINNING_GRID - ); + tribol::registerMfemCouplingScheme( coupling_scheme_id, mesh1_id, mesh2_id, *pmesh, coords, contact_surf_1, + contact_surf_2, tribol::SURFACE_TO_SURFACE, tribol::NO_CASE, tribol::COMMON_PLANE, + tribol::FRICTIONLESS, tribol::PENALTY, tribol::BINNING_GRID ); // This API call adds a velocity field to the coupling scheme. This is used // for computing the maximum common plane timestep and, if activated, gap rate // penalty. - tribol::registerMfemVelocity(coupling_scheme_id, velocity); + tribol::registerMfemVelocity( coupling_scheme_id, velocity ); // The type of penalty enforcement and the penalty parameters are set here (i.e. rate vs. kinematic and the method of // setting the penalty value). - if (use_const_penalty) - { - tribol::setMfemKinematicConstantPenalty(coupling_scheme_id, p_kine, p_kine); - } - else - { + if ( use_const_penalty ) { + tribol::setMfemKinematicConstantPenalty( coupling_scheme_id, p_kine, p_kine ); + } else { // Any MFEM scalar coefficient can be used to set material moduli, but this example uses a constant bulk modulus for // each boundary attribute of the volume mesh. - mfem::Vector bulk_moduli_by_bdry_attrib(pmesh->bdr_attributes.Max()); + mfem::Vector bulk_moduli_by_bdry_attrib( pmesh->bdr_attributes.Max() ); bulk_moduli_by_bdry_attrib = lambda + 2.0 / 3.0 * mu; - mfem::PWConstCoefficient mat_coeff(bulk_moduli_by_bdry_attrib); - tribol::setMfemKinematicElementPenalty(coupling_scheme_id, mat_coeff); + mfem::PWConstCoefficient mat_coeff( bulk_moduli_by_bdry_attrib ); + tribol::setMfemKinematicElementPenalty( coupling_scheme_id, mat_coeff ); } timer.stop(); - SLIC_INFO_ROOT(axom::fmt::format( - "Time to set up Tribol: {0:f}ms", timer.elapsedTimeInMilliSec() - )); + SLIC_INFO_ROOT( axom::fmt::format( "Time to set up Tribol: {0:f}ms", timer.elapsedTimeInMilliSec() ) ); // This is the main timestepping loop. - int cycle {0}; - for (double t {0.0}; t < t_end; t+=dt) - { + int cycle{ 0 }; + for ( double t{ 0.0 }; t < t_end; t += dt ) { // Update the cycle information for the data collections. - paraview_datacoll.SetCycle(cycle); - paraview_datacoll.SetTime(t); - paraview_datacoll.SetTimeStep(dt); - visit_datacoll.SetCycle(cycle); - visit_datacoll.SetTime(t); - visit_datacoll.SetTimeStep(dt); + paraview_datacoll.SetCycle( cycle ); + paraview_datacoll.SetTime( t ); + paraview_datacoll.SetTimeStep( dt ); + visit_datacoll.SetCycle( cycle ); + visit_datacoll.SetTime( t ); + visit_datacoll.SetTimeStep( dt ); // Write output if we are at the right cycle. - if (cycle % output_cycles == 0) - { + if ( cycle % output_cycles == 0 ) { paraview_datacoll.Save(); visit_datacoll.Save(); } @@ -392,20 +344,20 @@ int main( int argc, char** argv ) tribol::updateMfemParallelDecomposition(); // This API call computes the contact response given the current mesh // configuration. - tribol::update(cycle, t, dt); + tribol::update( cycle, t, dt ); // Nodal forces are returned to an MFEM grid function using this API // function. We set the contact nodal forces equal to the external forces in // the explicit mechanics update operator. op.f_ext = 0.0; - tribol::getMfemResponse(coupling_scheme_id, op.f_ext); + tribol::getMfemResponse( coupling_scheme_id, op.f_ext ); // Update the time in the explicit mechanics update. - op.SetTime(t); + op.SetTime( t ); // Take a step in time in the time integration scheme. - solver.Step(displacement, velocity, t, dt); + solver.Step( displacement, velocity, t, dt ); // Update the coordinates based on the new displacement. - coords.Set(1.0, ref_coords); + coords.Set( 1.0, ref_coords ); coords += displacement; // Increment the cycle count. diff --git a/src/examples/mfem_mortar_lm_patch.cpp b/src/examples/mfem_mortar_lm_patch.cpp index 82c299b9..021afe25 100644 --- a/src/examples/mfem_mortar_lm_patch.cpp +++ b/src/examples/mfem_mortar_lm_patch.cpp @@ -71,8 +71,8 @@ int main( int argc, char** argv ) // initialize MPI MPI_Init( &argc, &argv ); int np, rank; - MPI_Comm_size(MPI_COMM_WORLD, &np); - MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size( MPI_COMM_WORLD, &np ); + MPI_Comm_rank( MPI_COMM_WORLD, &rank ); #ifdef TRIBOL_USE_UMPIRE umpire::ResourceManager::getInstance(); // initialize umpire's ResouceManager @@ -80,7 +80,7 @@ int main( int argc, char** argv ) // initialize logger axom::slic::SimpleLogger logger; - axom::slic::setIsRoot(rank == 0); + axom::slic::setIsRoot( rank == 0 ); // define command line options // number of times to uniformly refine the serial mesh before constructing the @@ -94,89 +94,78 @@ int main( int argc, char** argv ) double mu = 50.0; // parse command line options - axom::CLI::App app { "mfem_mortar_lm_patch" }; - app.add_option("-r,--refine", ref_levels, - "Number of times to refine the mesh uniformly.") - ->capture_default_str(); + axom::CLI::App app{ "mfem_mortar_lm_patch" }; + app.add_option( "-r,--refine", ref_levels, "Number of times to refine the mesh uniformly." )->capture_default_str(); // TODO: LOR support for implicit contact - // app.add_option("-o,--order", order, + // app.add_option("-o,--order", order, // "Finite element order (polynomial degree).") // ->capture_default_str(); - app.add_option("-l,--lambda", lambda, - "Lame parameter lambda.") - ->capture_default_str(); - app.add_option("-m,--mu", mu, - "Lame parameter mu (shear modulus).") - ->capture_default_str(); - CLI11_PARSE(app, argc, argv); - - SLIC_INFO_ROOT("Running mfem_mortar_lm_patch with the following options:"); - SLIC_INFO_ROOT(axom::fmt::format("refine: {0}", ref_levels)); - SLIC_INFO_ROOT(axom::fmt::format("lambda: {0}", lambda)); - SLIC_INFO_ROOT(axom::fmt::format("mu: {0}\n", mu)); + app.add_option( "-l,--lambda", lambda, "Lame parameter lambda." )->capture_default_str(); + app.add_option( "-m,--mu", mu, "Lame parameter mu (shear modulus)." )->capture_default_str(); + CLI11_PARSE( app, argc, argv ); + + SLIC_INFO_ROOT( "Running mfem_mortar_lm_patch with the following options:" ); + SLIC_INFO_ROOT( axom::fmt::format( "refine: {0}", ref_levels ) ); + SLIC_INFO_ROOT( axom::fmt::format( "lambda: {0}", lambda ) ); + SLIC_INFO_ROOT( axom::fmt::format( "mu: {0}\n", mu ) ); // fixed options // location of mesh file. TRIBOL_REPO_DIR is defined in tribol/config.hpp std::string mesh_file = TRIBOL_REPO_DIR "/data/two_hex_overlap.mesh"; // boundary element attributes of mortar surface, the z = 1 plane of the first // block - auto mortar_attrs = std::set({4}); + auto mortar_attrs = std::set( { 4 } ); // boundary element attributes of nonmortar surface, the z = 0.99 plane of the // second block - auto nonmortar_attrs = std::set({5}); + auto nonmortar_attrs = std::set( { 5 } ); // boundary element attributes of x-fixed surfaces (at x = 0) - auto xfixed_attrs = std::set({1}); + auto xfixed_attrs = std::set( { 1 } ); // boundary element attributes of y-fixed surfaces (at y = 0) - auto yfixed_attrs = std::set({2}); + auto yfixed_attrs = std::set( { 2 } ); // boundary element attributes of z-fixed surfaces (3: surface at z = 0, 6: // surface at z = 1.99) - auto zfix_attribs = std::set({3, 6}); - + auto zfix_attribs = std::set( { 3, 6 } ); + // create an axom timer to give wall times for each step - axom::utilities::Timer timer { false }; + axom::utilities::Timer timer{ false }; // This block of code will read the mesh data given in two_hex_overlap.mesh, // create an mfem::Mesh, refine the mesh, then create an mfem::ParMesh. // Optionally, the mfem::ParMesh can be refined further on each rank by // setting par_ref_levels >= 1, though this is disabled below. timer.start(); - std::unique_ptr pmesh { nullptr }; + std::unique_ptr pmesh{ nullptr }; { // read serial mesh - auto mesh = std::make_unique(mesh_file.c_str(), 1, 1); + auto mesh = std::make_unique( mesh_file.c_str(), 1, 1 ); // refine serial mesh - if (ref_levels > 0) - { - for (int i{0}; i < ref_levels; ++i) - { + if ( ref_levels > 0 ) { + for ( int i{ 0 }; i < ref_levels; ++i ) { mesh->UniformRefinement(); } } - + // create parallel mesh from serial - pmesh = std::make_unique(MPI_COMM_WORLD, *mesh); - mesh.reset(nullptr); + pmesh = std::make_unique( MPI_COMM_WORLD, *mesh ); + mesh.reset( nullptr ); // further refinement of parallel mesh { // set this to >= 1 to refine the mesh on each rank further int par_ref_levels = 0; - for (int i{0}; i < par_ref_levels; ++i) - { + for ( int i{ 0 }; i < par_ref_levels; ++i ) { pmesh->UniformRefinement(); } } } timer.stop(); - SLIC_INFO_ROOT(axom::fmt::format( - "Time to create parallel mesh: {0:f}ms", timer.elapsedTimeInMilliSec() - )); - + SLIC_INFO_ROOT( axom::fmt::format( "Time to create parallel mesh: {0:f}ms", timer.elapsedTimeInMilliSec() ) ); + // Set up an MFEM data collection for output. We output data in Paraview and // VisIt formats. - auto paraview_datacoll = mfem::ParaViewDataCollection("mortar_patch_pv", pmesh.get()); - auto visit_datacoll = mfem::VisItDataCollection("mortar_patch_vi", pmesh.get()); + auto paraview_datacoll = mfem::ParaViewDataCollection( "mortar_patch_pv", pmesh.get() ); + auto visit_datacoll = mfem::VisItDataCollection( "mortar_patch_vi", pmesh.get() ); // This block of code creates position and displacement grid functions (and // associated finite element collections and finite element spaces) on the @@ -185,30 +174,27 @@ int main( int argc, char** argv ) // functions are registered with the data collections for output. timer.start(); // Finite element collection (shared between all grid functions). - auto fe_coll = mfem::H1_FECollection(order, pmesh->SpaceDimension()); + auto fe_coll = mfem::H1_FECollection( order, pmesh->SpaceDimension() ); // Finite element space (shared between all grid functions). - auto par_fe_space = mfem::ParFiniteElementSpace( - pmesh.get(), &fe_coll, pmesh->SpaceDimension()); + auto par_fe_space = mfem::ParFiniteElementSpace( pmesh.get(), &fe_coll, pmesh->SpaceDimension() ); // Create coordinate grid function - auto coords = mfem::ParGridFunction(&par_fe_space); + auto coords = mfem::ParGridFunction( &par_fe_space ); // Set coordinate grid function based on nodal locations. In MFEM, nodal // locations of higher order meshes are stored in a grid function. For linear // MFEM meshes, nodal locations can be stored in a grid function or through // the vertex coordinates. For consistency, we will create a nodal grid // function even for linear meshes. - pmesh->SetNodalGridFunction(&coords, false); - paraview_datacoll.RegisterField("pos", &coords); - visit_datacoll.RegisterField("pos", &coords); + pmesh->SetNodalGridFunction( &coords, false ); + paraview_datacoll.RegisterField( "pos", &coords ); + visit_datacoll.RegisterField( "pos", &coords ); // Create a grid function for displacement - mfem::ParGridFunction displacement { &par_fe_space }; - paraview_datacoll.RegisterField("disp", &displacement); - visit_datacoll.RegisterField("disp", &displacement); + mfem::ParGridFunction displacement{ &par_fe_space }; + paraview_datacoll.RegisterField( "disp", &displacement ); + visit_datacoll.RegisterField( "disp", &displacement ); displacement = 0.0; timer.stop(); - SLIC_INFO_ROOT(axom::fmt::format( - "Time to create grid functions: {0:f}ms", timer.elapsedTimeInMilliSec() - )); + SLIC_INFO_ROOT( axom::fmt::format( "Time to create grid functions: {0:f}ms", timer.elapsedTimeInMilliSec() ) ); // This block of code builds a list of degrees of freedom to which homogeneous // displacement boundary conditions will be applied. These boundary conditions @@ -222,70 +208,62 @@ int main( int argc, char** argv ) // are in the list. mfem::Array ess_vdof_marker; // Convert x-fixed boundary attributes into markers - mfem::Array ess_bdr(pmesh->bdr_attributes.Max()); + mfem::Array ess_bdr( pmesh->bdr_attributes.Max() ); ess_bdr = 0; - for (auto xfixed_attr : xfixed_attrs) - { - ess_bdr[xfixed_attr-1] = 1; + for ( auto xfixed_attr : xfixed_attrs ) { + ess_bdr[xfixed_attr - 1] = 1; } // Find all x-direction vdofs with the given boundary marker - par_fe_space.GetEssentialVDofs(ess_bdr, ess_vdof_marker, 0); + par_fe_space.GetEssentialVDofs( ess_bdr, ess_vdof_marker, 0 ); // Convert y-fixed boundary attributes into markers mfem::Array new_ess_vdof_marker; ess_bdr = 0; - for (auto yfixed_attr : yfixed_attrs) - { - ess_bdr[yfixed_attr-1] = 1; + for ( auto yfixed_attr : yfixed_attrs ) { + ess_bdr[yfixed_attr - 1] = 1; } // Find all y-direction vdofs with the given boundary marker - par_fe_space.GetEssentialVDofs(ess_bdr, new_ess_vdof_marker, 1); + par_fe_space.GetEssentialVDofs( ess_bdr, new_ess_vdof_marker, 1 ); // Compute union of x-direction and y-direction vdofs - for (int i{0}; i < ess_vdof_marker.Size(); ++i) - { + for ( int i{ 0 }; i < ess_vdof_marker.Size(); ++i ) { ess_vdof_marker[i] = ess_vdof_marker[i] || new_ess_vdof_marker[i]; } // Convert z-fixed boundary attributes into markers ess_bdr = 0; - for (auto zfix_attrib : zfix_attribs) - { - ess_bdr[zfix_attrib-1] = 1; + for ( auto zfix_attrib : zfix_attribs ) { + ess_bdr[zfix_attrib - 1] = 1; } // Find all z-direction vdofs with the given boundary marker - par_fe_space.GetEssentialVDofs(ess_bdr, new_ess_vdof_marker, 2); + par_fe_space.GetEssentialVDofs( ess_bdr, new_ess_vdof_marker, 2 ); // Compute union of x-direction, y-direction, and z-direction vdofs - for (int i{0}; i < ess_vdof_marker.Size(); ++i) - { + for ( int i{ 0 }; i < ess_vdof_marker.Size(); ++i ) { ess_vdof_marker[i] = ess_vdof_marker[i] || new_ess_vdof_marker[i]; } // Convert the vdofs to tdofs to remove duplicate values over ranks mfem::Array ess_tdof_marker; - par_fe_space.GetRestrictionMatrix()->BooleanMult(ess_vdof_marker, ess_tdof_marker); + par_fe_space.GetRestrictionMatrix()->BooleanMult( ess_vdof_marker, ess_tdof_marker ); // Convert the tdof marker array to a tdof list - mfem::FiniteElementSpace::MarkerToList(ess_tdof_marker, ess_tdof_list); + mfem::FiniteElementSpace::MarkerToList( ess_tdof_marker, ess_tdof_list ); } timer.stop(); - SLIC_INFO_ROOT(axom::fmt::format( - "Time to set up boundary conditions: {0:f}ms", timer.elapsedTimeInMilliSec() - )); + SLIC_INFO_ROOT( axom::fmt::format( "Time to set up boundary conditions: {0:f}ms", timer.elapsedTimeInMilliSec() ) ); // This block of code constructs a small-deformation linear elastic bilinear // form. timer.start(); - mfem::ParBilinearForm a(&par_fe_space); - mfem::ConstantCoefficient lambda_coeff(lambda); - mfem::ConstantCoefficient mu_coeff(mu); - a.AddDomainIntegrator(new mfem::ElasticityIntegrator(lambda_coeff, mu_coeff)); + mfem::ParBilinearForm a( &par_fe_space ); + mfem::ConstantCoefficient lambda_coeff( lambda ); + mfem::ConstantCoefficient mu_coeff( mu ); + a.AddDomainIntegrator( new mfem::ElasticityIntegrator( lambda_coeff, mu_coeff ) ); // Assemble the on-rank bilinear form stiffness matrix. a.Assemble(); // Reduce to tdofs and form a hypre parallel matrix for parallel solution of // the linear system. auto A = std::make_unique(); - a.FormSystemMatrix(ess_tdof_list, *A); + a.FormSystemMatrix( ess_tdof_list, *A ); timer.stop(); - SLIC_INFO_ROOT(axom::fmt::format( - "Time to create and assemble internal stiffness: {0:f}ms", timer.elapsedTimeInMilliSec() - )); + SLIC_INFO_ROOT( + axom::fmt::format( "Time to create and assemble internal stiffness: {0:f}ms", timer.elapsedTimeInMilliSec() ) ); // This block of code does initial setup of Tribol. timer.start(); @@ -303,44 +281,35 @@ int main( int argc, char** argv ) int coupling_scheme_id = 0; int mesh1_id = 0; int mesh2_id = 1; - tribol::registerMfemCouplingScheme( - coupling_scheme_id, mesh1_id, mesh2_id, - *pmesh, coords, mortar_attrs, nonmortar_attrs, - tribol::SURFACE_TO_SURFACE, - tribol::NO_SLIDING, - tribol::SINGLE_MORTAR, - tribol::FRICTIONLESS, - tribol::LAGRANGE_MULTIPLIER, - tribol::BINNING_GRID - ); + tribol::registerMfemCouplingScheme( coupling_scheme_id, mesh1_id, mesh2_id, *pmesh, coords, mortar_attrs, + nonmortar_attrs, tribol::SURFACE_TO_SURFACE, tribol::NO_SLIDING, + tribol::SINGLE_MORTAR, tribol::FRICTIONLESS, tribol::LAGRANGE_MULTIPLIER, + tribol::BINNING_GRID ); // The basic Lagrange multiplier options are set here. For this problem, we // ask Tribol to compute a contact residual and a Jacobian (though we only use // the Jacobian). - tribol::setLagrangeMultiplierOptions( - coupling_scheme_id, - tribol::ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN - ); + tribol::setLagrangeMultiplierOptions( coupling_scheme_id, tribol::ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN ); // save initial configuration paraview_datacoll.Save(); visit_datacoll.Save(); - + // Update the cycle information for the data collections. Also update time // with a pseudotime for the solution. - paraview_datacoll.SetCycle(1); - paraview_datacoll.SetTime(1.0); - paraview_datacoll.SetTimeStep(1.0); - visit_datacoll.SetCycle(1); - visit_datacoll.SetTime(1.0); - visit_datacoll.SetTimeStep(1.0); + paraview_datacoll.SetCycle( 1 ); + paraview_datacoll.SetTime( 1.0 ); + paraview_datacoll.SetTimeStep( 1.0 ); + visit_datacoll.SetCycle( 1 ); + visit_datacoll.SetTime( 1.0 ); + visit_datacoll.SetTimeStep( 1.0 ); // This creates the parallel adjacency-based mesh redecomposition. It also // constructs new Tribol meshes as subsets of the redecomposed mesh. tribol::updateMfemParallelDecomposition(); - double dt {1.0}; // time is arbitrary here (no timesteps) + double dt{ 1.0 }; // time is arbitrary here (no timesteps) // This API call computes the contact response and Jacobian given the current // mesh configuration. - tribol::update(1, 1.0, dt); + tribol::update( 1, 1.0, dt ); // HypreParMatrices holding the Jacobian from contact and stored in an MFEM // block operator are returned from this API call. @@ -349,136 +318,115 @@ int main( int argc, char** argv ) // pressure DOFs are only present on the nonmortar surface. The pressure DOFs // on the mortar surface are eliminated in the returned matrix with ones on // the diagonal. - auto A_blk = tribol::getMfemBlockJacobian(coupling_scheme_id); + auto A_blk = tribol::getMfemBlockJacobian( coupling_scheme_id ); // Add the Jacobian from the elasticity bilinear form to the top left block - A_blk->SetBlock(0, 0, A.release()); + A_blk->SetBlock( 0, 0, A.release() ); timer.stop(); - SLIC_INFO_ROOT(axom::fmt::format( - "Time to setup Tribol and compute Jacobian: {0:f}ms", timer.elapsedTimeInMilliSec() - )); + SLIC_INFO_ROOT( + axom::fmt::format( "Time to setup Tribol and compute Jacobian: {0:f}ms", timer.elapsedTimeInMilliSec() ) ); timer.start(); // Create a block RHS vector storing forces and gaps. Note no external forces // are present in this problem. - mfem::BlockVector B_blk { A_blk->RowOffsets() }; + mfem::BlockVector B_blk{ A_blk->RowOffsets() }; B_blk = 0.0; // Create a block solution vector storing displacement and pressures. - mfem::BlockVector X_blk { A_blk->ColOffsets() }; + mfem::BlockVector X_blk{ A_blk->ColOffsets() }; X_blk = 0.0; // This API call returns the mortar nodal gap vector to an (uninitialized) // vector. The function sizes and initializes the vector. mfem::Vector g; - tribol::getMfemGap(coupling_scheme_id, g); + tribol::getMfemGap( coupling_scheme_id, g ); // Apply a transpose prolongation operator on the submesh: maps dofs stored in g to tdofs stored in G for parallel // solution of the linear system. // // NOTE: gap is a dual field so we apply the transpose prolongation operator { - auto& G = B_blk.GetBlock(1); - auto& P_submesh = *tribol::getMfemPressure(coupling_scheme_id) - .ParFESpace()->GetProlongationMatrix(); - P_submesh.MultTranspose(g, G); + auto& G = B_blk.GetBlock( 1 ); + auto& P_submesh = *tribol::getMfemPressure( coupling_scheme_id ).ParFESpace()->GetProlongationMatrix(); + P_submesh.MultTranspose( g, G ); } // Create a single HypreParMatrix from blocks. This process requires two steps: (1) store // pointer to the BlockOperator HypreParMatrixs in a 2D array and (2) call // mfem::HypreParMatrixFromBlocks() to create the merged, single // HypreParMatrix (without blocks). - mfem::Array2D hypre_blocks(2, 2); - for (int i{0}; i < 2; ++i) - { - for (int j{0}; j < 2; ++j) - { - if (A_blk->GetBlock(i, j).Height() != 0 && A_blk->GetBlock(i, j).Width() != 0) - { - hypre_blocks(i, j) = const_cast( - dynamic_cast(&A_blk->GetBlock(i, j))); - } - else - { - hypre_blocks(i, j) = nullptr; + mfem::Array2D hypre_blocks( 2, 2 ); + for ( int i{ 0 }; i < 2; ++i ) { + for ( int j{ 0 }; j < 2; ++j ) { + if ( A_blk->GetBlock( i, j ).Height() != 0 && A_blk->GetBlock( i, j ).Width() != 0 ) { + hypre_blocks( i, j ) = + const_cast( dynamic_cast( &A_blk->GetBlock( i, j ) ) ); + } else { + hypre_blocks( i, j ) = nullptr; } } } - auto A_merged = std::unique_ptr( - mfem::HypreParMatrixFromBlocks(hypre_blocks) - ); + auto A_merged = std::unique_ptr( mfem::HypreParMatrixFromBlocks( hypre_blocks ) ); // Use a linear solver to find the block displacement/pressure vector. - mfem::MINRESSolver solver(MPI_COMM_WORLD); - solver.SetRelTol(1.0e-8); - solver.SetAbsTol(1.0e-12); - solver.SetMaxIter(5000); - solver.SetPrintLevel(3); - solver.SetOperator(*A_merged); - solver.Mult(B_blk, X_blk); + mfem::MINRESSolver solver( MPI_COMM_WORLD ); + solver.SetRelTol( 1.0e-8 ); + solver.SetAbsTol( 1.0e-12 ); + solver.SetMaxIter( 5000 ); + solver.SetPrintLevel( 3 ); + solver.SetOperator( *A_merged ); + solver.Mult( B_blk, X_blk ); // Move the block displacements to the displacement grid function. { - auto& U = X_blk.GetBlock(0); + auto& U = X_blk.GetBlock( 0 ); auto& P = *par_fe_space.GetProlongationMatrix(); - P.Mult(U, displacement); + P.Mult( U, displacement ); } // Fix the sign on the displacements. displacement.Neg(); // Update the pressure degrees of freedom (not used here) - auto& pressure_true = X_blk.GetBlock(1); - auto& pressure = tribol::getMfemPressure(0); - pressure.ParFESpace()->GetProlongationMatrix()->Mult(pressure_true, pressure); + auto& pressure_true = X_blk.GetBlock( 1 ); + auto& pressure = tribol::getMfemPressure( 0 ); + pressure.ParFESpace()->GetProlongationMatrix()->Mult( pressure_true, pressure ); // Update mesh coordinates given the displacement. coords += displacement; timer.stop(); - SLIC_INFO_ROOT(axom::fmt::format( - "Time to solve for updated displacements: {0:f}ms", timer.elapsedTimeInMilliSec() - )); + SLIC_INFO_ROOT( + axom::fmt::format( "Time to solve for updated displacements: {0:f}ms", timer.elapsedTimeInMilliSec() ) ); // Verify the forces are in equilibrium, i.e. f_int = A*u = f_contact = B^T*p. // This should be true if the solver converges. - mfem::Vector int_force_true(par_fe_space.GetTrueVSize()); + mfem::Vector int_force_true( par_fe_space.GetTrueVSize() ); int_force_true = 0.0; - mfem::Vector contact_force_true(int_force_true); - auto& displacement_true = X_blk.GetBlock(0); - A_blk->GetBlock(0, 0).Mult(displacement_true, int_force_true); - A_blk->GetBlock(0, 1).Mult(pressure_true, contact_force_true); - mfem::Vector force_resid_true(int_force_true); + mfem::Vector contact_force_true( int_force_true ); + auto& displacement_true = X_blk.GetBlock( 0 ); + A_blk->GetBlock( 0, 0 ).Mult( displacement_true, int_force_true ); + A_blk->GetBlock( 0, 1 ).Mult( pressure_true, contact_force_true ); + mfem::Vector force_resid_true( int_force_true ); force_resid_true += contact_force_true; - for (int i{0}; i < ess_tdof_list.Size(); ++i) - { + for ( int i{ 0 }; i < ess_tdof_list.Size(); ++i ) { force_resid_true[ess_tdof_list[i]] = 0.0; } auto force_resid_linf = force_resid_true.Normlinf(); - if (rank == 0) - { - MPI_Reduce(MPI_IN_PLACE, &force_resid_linf, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - SLIC_INFO(axom::fmt::format( - "|| force residual ||_(infty) = {0:e}", force_resid_linf - )); - } - else - { - MPI_Reduce(&force_resid_linf, &force_resid_linf, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); + if ( rank == 0 ) { + MPI_Reduce( MPI_IN_PLACE, &force_resid_linf, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD ); + SLIC_INFO( axom::fmt::format( "|| force residual ||_(infty) = {0:e}", force_resid_linf ) ); + } else { + MPI_Reduce( &force_resid_linf, &force_resid_linf, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD ); } // Verify the gap is closed by the displacements, i.e. B*u = gap. // This should be true if the solver converges. - auto& gap_resid_true = B_blk.GetBlock(1); - mfem::Vector gap_from_disp_true(gap_resid_true.Size()); - A_blk->GetBlock(1,0).Mult(displacement_true, gap_from_disp_true); + auto& gap_resid_true = B_blk.GetBlock( 1 ); + mfem::Vector gap_from_disp_true( gap_resid_true.Size() ); + A_blk->GetBlock( 1, 0 ).Mult( displacement_true, gap_from_disp_true ); gap_resid_true -= gap_from_disp_true; auto gap_resid_linf = gap_resid_true.Normlinf(); - if (rank == 0) - { - MPI_Reduce(MPI_IN_PLACE, &gap_resid_linf, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - SLIC_INFO(axom::fmt::format( - "|| gap residual ||_(infty) = {0:e}", gap_resid_linf - )); - } - else - { - MPI_Reduce(&gap_resid_linf, &gap_resid_linf, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); + if ( rank == 0 ) { + MPI_Reduce( MPI_IN_PLACE, &gap_resid_linf, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD ); + SLIC_INFO( axom::fmt::format( "|| gap residual ||_(infty) = {0:e}", gap_resid_linf ) ); + } else { + MPI_Reduce( &gap_resid_linf, &gap_resid_linf, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD ); } // Save the deformed configuration diff --git a/src/examples/mortar_lm_patch_test.cpp b/src/examples/mortar_lm_patch_test.cpp index 3049c7d9..f3a0da76 100644 --- a/src/examples/mortar_lm_patch_test.cpp +++ b/src/examples/mortar_lm_patch_test.cpp @@ -3,7 +3,7 @@ // // SPDX-License-Identifier: (MIT) -#include "examples_common.hpp" // for common functionality used in examples +#include "examples_common.hpp" // for common functionality used in examples // Tribol includes #include "tribol/interface/tribol.hpp" @@ -30,30 +30,31 @@ #include // for std::ostringstream //---------------------------------------------------------------------------------- -// Example command line arguments for running this example. This test creates two -// rectangular blocks of dimensions (l x w x h). The dimensions are set such that -// an initial block-intersection exists, triggering the contact interaction. The -// blocks are discretized per the "block#_res xx yy zz" input arguments, where -// "xx", "yy", and "zz" are the number of elements in the x, y and z directions. -// Varying "xx" and "yy#" will vary the number of contact overlaps that exist -// between the two surfaces, and therefore, the amount of contact work. +// Example command line arguments for running this example. This test creates two +// rectangular blocks of dimensions (l x w x h). The dimensions are set such that +// an initial block-intersection exists, triggering the contact interaction. The +// blocks are discretized per the "block#_res xx yy zz" input arguments, where +// "xx", "yy", and "zz" are the number of elements in the x, y and z directions. +// Varying "xx" and "yy#" will vary the number of contact overlaps that exist +// between the two surfaces, and therefore, the amount of contact work. // -// This example also sets homogeneous Dirichlet boundary conditions on the TestMesh -// object suitable for a contact patch test. This example calls Tribol to obtain -// the contact contributions to the residual and Jacobian, and calls MFEM to obtain -// these contributions from the stress divergence residual term -// (i.e. equilibrium contributions). Then, the Tribol and equilibrium contributions -// are combined into one monolithic system of equations and the pressure Lagrange -// multipliers and displacement degrees of freedom are solved for. This example -// mimics how a physics application would interact with Tribol, pre and post -// tribol::update() call. More specifically, this example demonstrates how to use +// This example also sets homogeneous Dirichlet boundary conditions on the TestMesh +// object suitable for a contact patch test. This example calls Tribol to obtain +// the contact contributions to the residual and Jacobian, and calls MFEM to obtain +// these contributions from the stress divergence residual term +// (i.e. equilibrium contributions). Then, the Tribol and equilibrium contributions +// are combined into one monolithic system of equations and the pressure Lagrange +// multipliers and displacement degrees of freedom are solved for. This example +// mimics how a physics application would interact with Tribol, pre and post +// tribol::update() call. More specifically, this example demonstrates how to use // the Tribol API to obtain Jacobian (i.e. sparse matrix) contributions. // -// Note: This example uses MFEM linear algebra features; namely, the LU solver. -// There may be issues if the mesh blocks are too refined. Additionally, the +// Note: This example uses MFEM linear algebra features; namely, the LU solver. +// There may be issues if the mesh blocks are too refined. Additionally, the // runtime will be very slow. // -// srun -n1 ./mortar_lm_patch_test_ex --block1_res 5 5 1 --block1_min 0 0 0. --block1_max 1 1 1.05 --block2_res 4 4 1 --block2_min 0 0 0.95 --block2_max 1 1 2 +// srun -n1 ./mortar_lm_patch_test_ex --block1_res 5 5 1 --block1_min 0 0 0. --block1_max 1 1 1.05 --block2_res 4 4 1 +// --block2_min 0 0 0.95 --block2_max 1 1 2 // //---------------------------------------------------------------------------------- @@ -61,7 +62,7 @@ * \brief Program main. * * This example runs the mortar + Lagrange multiplier algorithm for two opposing blocks, - * and shows an example of how to post-process Tribol data to solve for the + * and shows an example of how to post-process Tribol data to solve for the * Lagrange multiplier field * * \param [in] argc argument counter @@ -84,25 +85,25 @@ int main( int argc, char** argv ) tribol::CommT problem_comm = TRIBOL_COMM_WORLD; #ifdef TRIBOL_USE_UMPIRE - umpire::ResourceManager::getInstance(); // initialize umpire's ResouceManager + umpire::ResourceManager::getInstance(); // initialize umpire's ResouceManager #endif initialize_logger( problem_comm ); - // parse command line arguments. This is + // parse command line arguments. This is Arguments args; - { + { // set common plane + penalty specific options - args.dimension = 3; // problems currently only setup for 3D + args.dimension = 3; // problems currently only setup for 3D args.penalty_stiffness = 1.0; args.dump_vis = true; // parse the command line arguments - parse_command_line_args( "Mortar Lm patch test example", args, argc, argv ); + parse_command_line_args( "Mortar Lm patch test example", args, argc, argv ); } - - // instantiate test mesh object. Note, this mesh object is a Tribol - // utility for testing. In general, a physics application will have + + // instantiate test mesh object. Note, this mesh object is a Tribol + // utility for testing. In general, a physics application will have // their own mesh data. tribol::TestMesh mesh; build_mesh_3D( mesh, args, PATCH_BCS ); @@ -116,20 +117,15 @@ int main( int argc, char** argv ) // API function calls // // // //////////////////////////////////////////////////// - int err = tribol_register_and_update( mesh, tribol::SINGLE_MORTAR, - tribol::LAGRANGE_MULTIPLIER, - tribol::FRICTIONLESS, + int err = tribol_register_and_update( mesh, tribol::SINGLE_MORTAR, tribol::LAGRANGE_MULTIPLIER, tribol::FRICTIONLESS, args.dump_vis, nullptr ); - if (err == 1) - { - SLIC_WARNING("Returned from tribol_register_and_update with error."); - } - else - { - SLIC_INFO("Tribol update executed successfully. " << - "Preparing mfem mesh and linear algebra for " << - "computation of pressure Lagrange multipliers."); + if ( err == 1 ) { + SLIC_WARNING( "Returned from tribol_register_and_update with error." ); + } else { + SLIC_INFO( "Tribol update executed successfully. " + << "Preparing mfem mesh and linear algebra for " + << "computation of pressure Lagrange multipliers." ); } ////////////////////////////////////////////////////////// @@ -145,136 +141,135 @@ int main( int argc, char** argv ) // equations and solve it. In the Lagrange multiplier // // enforcement of this method, the system of equations // // is modified to include gap constraint equations // - // and a stacked solution vector of nodal displacements // + // and a stacked solution vector of nodal displacements // // AND pressure Lagrange multipliers, which are the // // auxiliary variables enforcing the gap constraints. // // // ////////////////////////////////////////////////////////// { - // setup MFEM mesh representation of the TestMesh - // in order to facilitate sparse data structure and linear - // algebra features - mesh.setupMfemMesh(); - - // get Jacobian sparse matrix from Tribol. Note: the Tribol MFEM - // matrix option does not return CSR data. Rather, the matrix is in - // a row-wise linked list format. This will make it convenient (i.e. easy) - // to sum in MFEM equilibrium contributions after which the matrix - // will be finalized. - mfem::SparseMatrix * tribolJac { nullptr }; - int sparseMatErr = tribol::getJacobianSparseMatrix( &tribolJac, 0 ); - - SLIC_ERROR_IF( sparseMatErr != 0, "mortar_lm_patch_test.cpp: error " << - "gettting Tribol mfem sparse matrix." ); - - SLIC_INFO( "Setup mfem mesh and grabbed Tribol sparse data." ); - - //////////////////////////////////////////////////// - // // - // Compute Equilibrium contributions on MFEM mesh // - // // - // note: Tribol only provides the contact // - // contributions. Here we compute the equilibrium // - // physics contributions making use of the MFEM // - // linear elasticity integrator. // - // // - //////////////////////////////////////////////////// - - // setup material properties - RealT nu_val {0.33}; // Poisson's ratio - RealT youngs_val {3.E7}; // Young's modulus (approximate, mild steel (psi)) - - ///////////////////////////////////////////////////////////////////////// - // compute equilibrium contributions and sum into Tribol sparse matrix // - ///////////////////////////////////////////////////////////////////////// - mesh.computeEquilibriumJacobian( tribolJac, nu_val, youngs_val ); - - SLIC_INFO( "Assembled equilibrium contributions from mfem." ); - - // finalize the sparse matrix. This converts the row-wise linked list - // format into CSR format. We could use CSR data directly, but for ease - // we will then convert the sparse matrix to a dense matrix hopefully - // to ease clarity - tribolJac->Finalize(); - mfem::DenseMatrix tribolA; // full global system in dense matrix format - tribolJac->ToDenseMatrix(tribolA); - - // instantiate mfem vector for RHS contributions - int rhs_size = mesh.dim * mesh.numTotalNodes + // equilibrium equations - mesh.numNonmortarSurfaceNodes; // gap equations - RealT b[ rhs_size ]; - tribol::initRealArray( &b[0], rhs_size, 0. ); - mfem::Vector rhs( &b[0], rhs_size ); - rhs = 0.; // initialize - - SLIC_INFO( "Finalized initial oversized sparse matrix and created rhs vector." ); - - ////////////////////////////////////////////////////////// - // // - // Condense the matrix tribolA into a system that is // - // (dim*numTotalNodes + numPressureDof). For clarity, // - // sum contributions into another sparse matrix of this // - // proper size. // - // // - // note: The Tribol Jacobian was oversized in order to // - // use the global connectivity provided by the physics // - // application. Specifically, we had to account for // - // block 1 pressure dofs when there are none in a // - // single mortar method. Now, we have to condense the // - // system of equations into the proper size and then // - // solve. // - // // - ////////////////////////////////////////////////////////// - int solveSize = rhs_size; - mfem::SparseMatrix A_s( solveSize ); // A_s for "s"parse matrix "A" - //int numRows = A_s.NumRows(); - - mesh.tribolMatrixToSystemMatrix( &tribolA, &A_s ); - - SLIC_INFO("Condensed oversized matrix into properly sized matrix." ); - - ///////////////////////////////////////// - // populate RHS with gap contributions // - ///////////////////////////////////////// - mesh.getGapEvals( &b[0] ); - - SLIC_INFO( "Populated RHS gap contributions." ); - - ////////////////////////////////////////////////////////////////////////// - // zero out all homogeneous Dirichlet BC components for each mesh block // - ////////////////////////////////////////////////////////////////////////// - mesh.enforceDirichletBCs( &A_s, &rhs ); - - SLIC_INFO( "Applied homogeneous Dirichlet BCs to global system." ); - - // Finalize A_s and convert to dense matrix to solve using - // MFEM LU solver - A_s.Finalize(); - mfem::DenseMatrix A; - A_s.ToDenseMatrix( A ); - int rank = A.Rank(1.e-15); - SLIC_INFO( "Matrix rank: " << rank ); - - // instantiate MFEM dense matrix inverse object - // and solution vector - mfem::DenseMatrixInverse invA( A ); - mfem::Vector sol; - invA.Mult( rhs, sol ); // solve the system - RealT * sol_data = sol.GetData(); // get solution data - - SLIC_INFO( "Solved global system of equations." ); - - RealT pressureSum = 0.; - for (int i=0; iFinalize(); + mfem::DenseMatrix tribolA; // full global system in dense matrix format + tribolJac->ToDenseMatrix( tribolA ); + + // instantiate mfem vector for RHS contributions + int rhs_size = mesh.dim * mesh.numTotalNodes + // equilibrium equations + mesh.numNonmortarSurfaceNodes; // gap equations + RealT b[rhs_size]; + tribol::initRealArray( &b[0], rhs_size, 0. ); + mfem::Vector rhs( &b[0], rhs_size ); + rhs = 0.; // initialize + + SLIC_INFO( "Finalized initial oversized sparse matrix and created rhs vector." ); + + ////////////////////////////////////////////////////////// + // // + // Condense the matrix tribolA into a system that is // + // (dim*numTotalNodes + numPressureDof). For clarity, // + // sum contributions into another sparse matrix of this // + // proper size. // + // // + // note: The Tribol Jacobian was oversized in order to // + // use the global connectivity provided by the physics // + // application. Specifically, we had to account for // + // block 1 pressure dofs when there are none in a // + // single mortar method. Now, we have to condense the // + // system of equations into the proper size and then // + // solve. // + // // + ////////////////////////////////////////////////////////// + int solveSize = rhs_size; + mfem::SparseMatrix A_s( solveSize ); // A_s for "s"parse matrix "A" + // int numRows = A_s.NumRows(); + + mesh.tribolMatrixToSystemMatrix( &tribolA, &A_s ); + + SLIC_INFO( "Condensed oversized matrix into properly sized matrix." ); + + ///////////////////////////////////////// + // populate RHS with gap contributions // + ///////////////////////////////////////// + mesh.getGapEvals( &b[0] ); + + SLIC_INFO( "Populated RHS gap contributions." ); + + ////////////////////////////////////////////////////////////////////////// + // zero out all homogeneous Dirichlet BC components for each mesh block // + ////////////////////////////////////////////////////////////////////////// + mesh.enforceDirichletBCs( &A_s, &rhs ); + + SLIC_INFO( "Applied homogeneous Dirichlet BCs to global system." ); + + // Finalize A_s and convert to dense matrix to solve using + // MFEM LU solver + A_s.Finalize(); + mfem::DenseMatrix A; + A_s.ToDenseMatrix( A ); + int rank = A.Rank( 1.e-15 ); + SLIC_INFO( "Matrix rank: " << rank ); + + // instantiate MFEM dense matrix inverse object + // and solution vector + mfem::DenseMatrixInverse invA( A ); + mfem::Vector sol; + invA.Mult( rhs, sol ); // solve the system + RealT* sol_data = sol.GetData(); // get solution data + + SLIC_INFO( "Solved global system of equations." ); + + RealT pressureSum = 0.; + for ( int i = 0; i < mesh.numNonmortarSurfaceNodes; ++i ) { + int offset = mesh.dim * mesh.numTotalNodes; + // exploit offset and contiguous node numbering in the indexing here. + pressureSum += sol_data[offset + i]; + } + + SLIC_INFO( "Pressure Sum: " << pressureSum << "." ); + + } // end of post Tribol scope tribol::finalize(); @@ -289,4 +284,3 @@ int main( int argc, char** argv ) return 0; } - diff --git a/src/examples/multidomain_redecomp.cpp b/src/examples/multidomain_redecomp.cpp index 7b0ecaef..1850eae4 100644 --- a/src/examples/multidomain_redecomp.cpp +++ b/src/examples/multidomain_redecomp.cpp @@ -57,19 +57,9 @@ #include "tribol/config.hpp" template -void RedecompExample( - mfem::ParMesh& pmesh1, - mfem::ParMesh& pmesh2, - int order, - double max_out_of_balance -); - -std::unique_ptr MakePMesh( - MPI_Comm comm, - const std::string& mesh_file, - double theta, - int ref_levels -); +void RedecompExample( mfem::ParMesh& pmesh1, mfem::ParMesh& pmesh2, int order, double max_out_of_balance ); + +std::unique_ptr MakePMesh( MPI_Comm comm, const std::string& mesh_file, double theta, int ref_levels ); int main( int argc, char** argv ) { @@ -80,12 +70,12 @@ int main( int argc, char** argv ) // initialize MPI MPI_Init( &argc, &argv ); int np, rank; - MPI_Comm_size(MPI_COMM_WORLD, &np); - MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size( MPI_COMM_WORLD, &np ); + MPI_Comm_rank( MPI_COMM_WORLD, &rank ); // initialize logger axom::slic::SimpleLogger logger; - axom::slic::setIsRoot(rank == 0); + axom::slic::setIsRoot( rank == 0 ); // command line options // location of mesh file. TRIBOL_REPO_DIR is defined in tribol/config.hpp @@ -108,56 +98,47 @@ int main( int argc, char** argv ) // checks implemented. double max_out_of_balance = 0.05; - axom::CLI::App app { "multidomain_redecomp" }; - app.add_option("--m1,--mesh1", mesh_file1, "First mesh file to use.") - ->check(axom::CLI::ExistingFile) - ->capture_default_str(); - app.add_option("--m2,--mesh2", mesh_file2, "Second mesh file to use.") - ->check(axom::CLI::ExistingFile) - ->capture_default_str(); - app.add_option("--th1,--theta1", rot1, - "First mesh angle of rotation in x-y plane (degrees).") - ->capture_default_str(); - app.add_option("--th2,--theta2", rot2, - "Second mesh angle of rotation in x-y plane (degrees).") - ->capture_default_str(); - app.add_option("-r,--refine", ref_levels, - "Number of times to refine the mesh uniformly.") - ->capture_default_str(); - app.add_option("-o,--order", order, - "Finite element order (polynomial degree).") - ->capture_default_str(); - app.add_option("-t,--tol", max_out_of_balance, - "Max proportion of out-of-balance elements in RCB decomposition.") - ->capture_default_str(); - CLI11_PARSE(app, argc, argv); - - SLIC_INFO_ROOT("Running multidomain_redecomp with the following options:"); - SLIC_INFO_ROOT(axom::fmt::format("mesh1: {0}", mesh_file1)); - SLIC_INFO_ROOT(axom::fmt::format("mesh2: {0}", mesh_file2)); - SLIC_INFO_ROOT(axom::fmt::format("theta1: {0}", rot1)); - SLIC_INFO_ROOT(axom::fmt::format("theta2: {0}", rot2)); - SLIC_INFO_ROOT(axom::fmt::format("refine: {0}", ref_levels)); - SLIC_INFO_ROOT(axom::fmt::format("order: {0}", order)); - SLIC_INFO_ROOT(axom::fmt::format("tol: {0}\n", max_out_of_balance)); + axom::CLI::App app{ "multidomain_redecomp" }; + app.add_option( "--m1,--mesh1", mesh_file1, "First mesh file to use." ) + ->check( axom::CLI::ExistingFile ) + ->capture_default_str(); + app.add_option( "--m2,--mesh2", mesh_file2, "Second mesh file to use." ) + ->check( axom::CLI::ExistingFile ) + ->capture_default_str(); + app.add_option( "--th1,--theta1", rot1, "First mesh angle of rotation in x-y plane (degrees)." ) + ->capture_default_str(); + app.add_option( "--th2,--theta2", rot2, "Second mesh angle of rotation in x-y plane (degrees)." ) + ->capture_default_str(); + app.add_option( "-r,--refine", ref_levels, "Number of times to refine the mesh uniformly." )->capture_default_str(); + app.add_option( "-o,--order", order, "Finite element order (polynomial degree)." )->capture_default_str(); + app.add_option( "-t,--tol", max_out_of_balance, "Max proportion of out-of-balance elements in RCB decomposition." ) + ->capture_default_str(); + CLI11_PARSE( app, argc, argv ); + + SLIC_INFO_ROOT( "Running multidomain_redecomp with the following options:" ); + SLIC_INFO_ROOT( axom::fmt::format( "mesh1: {0}", mesh_file1 ) ); + SLIC_INFO_ROOT( axom::fmt::format( "mesh2: {0}", mesh_file2 ) ); + SLIC_INFO_ROOT( axom::fmt::format( "theta1: {0}", rot1 ) ); + SLIC_INFO_ROOT( axom::fmt::format( "theta2: {0}", rot2 ) ); + SLIC_INFO_ROOT( axom::fmt::format( "refine: {0}", ref_levels ) ); + SLIC_INFO_ROOT( axom::fmt::format( "order: {0}", order ) ); + SLIC_INFO_ROOT( axom::fmt::format( "tol: {0}\n", max_out_of_balance ) ); // create parallel meshes - auto pmesh1 = MakePMesh(MPI_COMM_WORLD, mesh_file1, rot1, ref_levels); - auto pmesh2 = MakePMesh(MPI_COMM_WORLD, mesh_file2, rot2, ref_levels); + auto pmesh1 = MakePMesh( MPI_COMM_WORLD, mesh_file1, rot1, ref_levels ); + auto pmesh2 = MakePMesh( MPI_COMM_WORLD, mesh_file2, rot2, ref_levels ); // call dimension specific version of RedecompExample - SLIC_ERROR_ROOT_IF(pmesh1->Dimension() != pmesh2->Dimension(), - "Dimension of meshes must match."); - switch (pmesh1->Dimension()) - { + SLIC_ERROR_ROOT_IF( pmesh1->Dimension() != pmesh2->Dimension(), "Dimension of meshes must match." ); + switch ( pmesh1->Dimension() ) { case 2: - RedecompExample<2>(*pmesh1, *pmesh2, order, max_out_of_balance); + RedecompExample<2>( *pmesh1, *pmesh2, order, max_out_of_balance ); break; case 3: - RedecompExample<3>(*pmesh1, *pmesh2, order, max_out_of_balance); + RedecompExample<3>( *pmesh1, *pmesh2, order, max_out_of_balance ); break; default: - SLIC_ERROR("Space dimension of the mesh must be 2 or 3."); + SLIC_ERROR( "Space dimension of the mesh must be 2 or 3." ); } // cleanup @@ -167,74 +148,51 @@ int main( int argc, char** argv ) } template -void RedecompExample( - mfem::ParMesh& pmesh1, - mfem::ParMesh& pmesh2, - int order, - double max_out_of_balance -) +void RedecompExample( mfem::ParMesh& pmesh1, mfem::ParMesh& pmesh2, int order, double max_out_of_balance ) { auto rank = pmesh1.GetMyRank(); auto np = pmesh1.GetNRanks(); // grid function for higher-order nodes - auto fe_coll1 = mfem::H1_FECollection(order, NDIMS); - auto fe_coll2 = mfem::H1_FECollection(order, NDIMS); - auto par_fe_space1 = mfem::ParFiniteElementSpace(&pmesh1, &fe_coll1, NDIMS); - auto par_fe_space2 = mfem::ParFiniteElementSpace(&pmesh2, &fe_coll2, NDIMS); - auto par_x_ref_elem1 = mfem::ParGridFunction(&par_fe_space1); - auto par_x_ref_elem2 = mfem::ParGridFunction(&par_fe_space2); - if (order > 1) - { - pmesh1.SetNodalGridFunction(&par_x_ref_elem1, false); - pmesh2.SetNodalGridFunction(&par_x_ref_elem2, false); - } - else - { - pmesh1.GetNodes(par_x_ref_elem1); - pmesh2.GetNodes(par_x_ref_elem2); + auto fe_coll1 = mfem::H1_FECollection( order, NDIMS ); + auto fe_coll2 = mfem::H1_FECollection( order, NDIMS ); + auto par_fe_space1 = mfem::ParFiniteElementSpace( &pmesh1, &fe_coll1, NDIMS ); + auto par_fe_space2 = mfem::ParFiniteElementSpace( &pmesh2, &fe_coll2, NDIMS ); + auto par_x_ref_elem1 = mfem::ParGridFunction( &par_fe_space1 ); + auto par_x_ref_elem2 = mfem::ParGridFunction( &par_fe_space2 ); + if ( order > 1 ) { + pmesh1.SetNodalGridFunction( &par_x_ref_elem1, false ); + pmesh2.SetNodalGridFunction( &par_x_ref_elem2, false ); + } else { + pmesh1.GetNodes( par_x_ref_elem1 ); + pmesh2.GetNodes( par_x_ref_elem2 ); } auto par_x_ref_node1 = par_x_ref_elem1; auto par_x_ref_node2 = par_x_ref_elem2; // create visit output data collection for pmesh - auto pmesh_dc1 = mfem::VisItDataCollection("pmesh1", &pmesh1); - auto pmesh_dc2 = mfem::VisItDataCollection("pmesh2", &pmesh2); + auto pmesh_dc1 = mfem::VisItDataCollection( "pmesh1", &pmesh1 ); + auto pmesh_dc2 = mfem::VisItDataCollection( "pmesh2", &pmesh2 ); ///////////////////////////////////////////////////////////////////////////// // STEP 1: Create RedecompMeshes using MultiRedecomp ///////////////////////////////////////////////////////////////////////////// // Note: see MultiRedecomp.hpp for a simpler constructor - axom::utilities::Timer timer { false }; + axom::utilities::Timer timer{ false }; timer.start(); auto multiredecomp = redecomp::MultiRedecomp( - // redecomp::Redecomp::RCB - std::make_unique>( - std::make_unique>(), - std::make_unique>( - MPI_COMM_WORLD, - max_out_of_balance - ) - ) - ); - auto redecomp_meshes = multiredecomp.createRedecompMeshes( - {&pmesh1, &pmesh2} - ); + // redecomp::Redecomp::RCB + std::make_unique>( + std::make_unique>(), + std::make_unique>( MPI_COMM_WORLD, max_out_of_balance ) ) ); + auto redecomp_meshes = multiredecomp.createRedecompMeshes( { &pmesh1, &pmesh2 } ); timer.stop(); - SLIC_INFO_ROOT(axom::fmt::format( - "Time to create Redecomp meshes: {0:f}ms", timer.elapsedTimeInMilliSec() - )); + SLIC_INFO_ROOT( axom::fmt::format( "Time to create Redecomp meshes: {0:f}ms", timer.elapsedTimeInMilliSec() ) ); // create visit output data collection for redecomp - auto redecomp_dc1 = mfem::VisItDataCollection( - "redecomp1_" + std::to_string(rank), - redecomp_meshes[0].get() - ); - auto redecomp_dc2 = mfem::VisItDataCollection( - "redecomp2_" + std::to_string(rank), - redecomp_meshes[1].get() - ); + auto redecomp_dc1 = mfem::VisItDataCollection( "redecomp1_" + std::to_string( rank ), redecomp_meshes[0].get() ); + auto redecomp_dc2 = mfem::VisItDataCollection( "redecomp2_" + std::to_string( rank ), redecomp_meshes[1].get() ); ///////////////////////////////////////////////////////////////////////////// // GridFunction transfer @@ -244,21 +202,13 @@ void RedecompExample( // STEP 2: Create GridFunctions on each RedecompMesh ///////////////////////////////////////////////////////////////////////////// - auto redecomp_fe_space1 = mfem::FiniteElementSpace( - redecomp_meshes[0].get(), - &fe_coll1, - NDIMS - ); - auto redecomp_fe_space2 = mfem::FiniteElementSpace( - redecomp_meshes[1].get(), - &fe_coll2, - NDIMS - ); - auto redecomp_x_ref_elem1 = mfem::GridFunction(&redecomp_fe_space1); - auto redecomp_x_ref_elem2 = mfem::GridFunction(&redecomp_fe_space2); - auto redecomp_x_ref_node1 = mfem::GridFunction(&redecomp_fe_space1); - auto redecomp_x_ref_node2 = mfem::GridFunction(&redecomp_fe_space2); - + auto redecomp_fe_space1 = mfem::FiniteElementSpace( redecomp_meshes[0].get(), &fe_coll1, NDIMS ); + auto redecomp_fe_space2 = mfem::FiniteElementSpace( redecomp_meshes[1].get(), &fe_coll2, NDIMS ); + auto redecomp_x_ref_elem1 = mfem::GridFunction( &redecomp_fe_space1 ); + auto redecomp_x_ref_elem2 = mfem::GridFunction( &redecomp_fe_space2 ); + auto redecomp_x_ref_node1 = mfem::GridFunction( &redecomp_fe_space1 ); + auto redecomp_x_ref_node2 = mfem::GridFunction( &redecomp_fe_space2 ); + ///////////////////////////////////////////////////////////////////////////// // STEP 3A: Create transfer object (element-by-element) ///////////////////////////////////////////////////////////////////////////// @@ -276,49 +226,44 @@ void RedecompExample( timer.start(); auto elem_transfer = redecomp::RedecompTransfer(); timer.stop(); - SLIC_INFO_ROOT(axom::fmt::format( - "Time to create element transfer object: {0:f}ms", timer.elapsedTimeInMilliSec() - )); + SLIC_INFO_ROOT( + axom::fmt::format( "Time to create element transfer object: {0:f}ms", timer.elapsedTimeInMilliSec() ) ); ///////////////////////////////////////////////////////////////////////////// // STEP 4A: Transfer to GridFunctions on RedecompMeshes (element-by-element) ///////////////////////////////////////////////////////////////////////////// timer.start(); - elem_transfer.TransferToSerial(par_x_ref_elem1, redecomp_x_ref_elem1); - elem_transfer.TransferToSerial(par_x_ref_elem2, redecomp_x_ref_elem2); + elem_transfer.TransferToSerial( par_x_ref_elem1, redecomp_x_ref_elem1 ); + elem_transfer.TransferToSerial( par_x_ref_elem2, redecomp_x_ref_elem2 ); timer.stop(); - SLIC_INFO_ROOT(axom::fmt::format( - "Time to transfer vector field by element: {0:f}ms", timer.elapsedTimeInMilliSec() - )); - redecomp_dc1.RegisterField("pos_elem", &redecomp_x_ref_elem1); - redecomp_dc2.RegisterField("pos_elem", &redecomp_x_ref_elem2); - + SLIC_INFO_ROOT( + axom::fmt::format( "Time to transfer vector field by element: {0:f}ms", timer.elapsedTimeInMilliSec() ) ); + redecomp_dc1.RegisterField( "pos_elem", &redecomp_x_ref_elem1 ); + redecomp_dc2.RegisterField( "pos_elem", &redecomp_x_ref_elem2 ); + ///////////////////////////////////////////////////////////////////////////// // STEP 3B: Create transfer objects (node-by-node) ///////////////////////////////////////////////////////////////////////////// timer.start(); - auto node_transfer1 = redecomp::RedecompTransfer(par_fe_space1, redecomp_fe_space1); - auto node_transfer2 = redecomp::RedecompTransfer(par_fe_space2, redecomp_fe_space2); + auto node_transfer1 = redecomp::RedecompTransfer( par_fe_space1, redecomp_fe_space1 ); + auto node_transfer2 = redecomp::RedecompTransfer( par_fe_space2, redecomp_fe_space2 ); timer.stop(); - SLIC_INFO_ROOT(axom::fmt::format( - "Time to create nodal transfer object: {0:f}ms", timer.elapsedTimeInMilliSec() - )); - + SLIC_INFO_ROOT( axom::fmt::format( "Time to create nodal transfer object: {0:f}ms", timer.elapsedTimeInMilliSec() ) ); + ///////////////////////////////////////////////////////////////////////////// // STEP 4B: Transfer to GridFunctions on RedecompMeshes (node-by-node) ///////////////////////////////////////////////////////////////////////////// timer.start(); - node_transfer1.TransferToSerial(par_x_ref_node1, redecomp_x_ref_node1); - node_transfer2.TransferToSerial(par_x_ref_node2, redecomp_x_ref_node2); + node_transfer1.TransferToSerial( par_x_ref_node1, redecomp_x_ref_node1 ); + node_transfer2.TransferToSerial( par_x_ref_node2, redecomp_x_ref_node2 ); timer.stop(); - SLIC_INFO_ROOT(axom::fmt::format( - "Time to transfer vector field by node: {0:f}ms", timer.elapsedTimeInMilliSec() - )); - redecomp_dc1.RegisterField("pos_node", &redecomp_x_ref_node1); - redecomp_dc2.RegisterField("pos_node", &redecomp_x_ref_node2); + SLIC_INFO_ROOT( + axom::fmt::format( "Time to transfer vector field by node: {0:f}ms", timer.elapsedTimeInMilliSec() ) ); + redecomp_dc1.RegisterField( "pos_node", &redecomp_x_ref_node1 ); + redecomp_dc2.RegisterField( "pos_node", &redecomp_x_ref_node2 ); ///////////////////////////////////////////////////////////////////////////// // STEP 5: Operate on data (no-op here) @@ -329,56 +274,48 @@ void RedecompExample( ///////////////////////////////////////////////////////////////////////////// timer.start(); - elem_transfer.TransferToParallel(redecomp_x_ref_elem1, par_x_ref_elem1); - elem_transfer.TransferToParallel(redecomp_x_ref_elem2, par_x_ref_elem2); + elem_transfer.TransferToParallel( redecomp_x_ref_elem1, par_x_ref_elem1 ); + elem_transfer.TransferToParallel( redecomp_x_ref_elem2, par_x_ref_elem2 ); timer.stop(); - SLIC_INFO_ROOT(axom::fmt::format( - "Time to transfer back vector field by element: {0:f}ms", - timer.elapsedTimeInMilliSec() - )); - pmesh_dc1.RegisterField("pos_elem", &par_x_ref_elem1); - pmesh_dc2.RegisterField("pos_elem", &par_x_ref_elem2); + SLIC_INFO_ROOT( + axom::fmt::format( "Time to transfer back vector field by element: {0:f}ms", timer.elapsedTimeInMilliSec() ) ); + pmesh_dc1.RegisterField( "pos_elem", &par_x_ref_elem1 ); + pmesh_dc2.RegisterField( "pos_elem", &par_x_ref_elem2 ); ///////////////////////////////////////////////////////////////////////////// // STEP 6B: Transfer back to ParGridFunctions on ParMeshes (node-by-node) ///////////////////////////////////////////////////////////////////////////// timer.start(); - node_transfer1.TransferToParallel(redecomp_x_ref_node1, par_x_ref_node1); - node_transfer2.TransferToParallel(redecomp_x_ref_node2, par_x_ref_node2); + node_transfer1.TransferToParallel( redecomp_x_ref_node1, par_x_ref_node1 ); + node_transfer2.TransferToParallel( redecomp_x_ref_node2, par_x_ref_node2 ); timer.stop(); - SLIC_INFO_ROOT(axom::fmt::format( - "Time to transfer back vector field by node: {0:f}ms", - timer.elapsedTimeInMilliSec() - )); - pmesh_dc1.RegisterField("pos_node", &par_x_ref_node1); - pmesh_dc2.RegisterField("pos_node", &par_x_ref_node2); + SLIC_INFO_ROOT( + axom::fmt::format( "Time to transfer back vector field by node: {0:f}ms", timer.elapsedTimeInMilliSec() ) ); + pmesh_dc1.RegisterField( "pos_node", &par_x_ref_node1 ); + pmesh_dc2.RegisterField( "pos_node", &par_x_ref_node2 ); ///////////////////////////////////////////////////////////////////////////// // QuadratureFunction transfer ///////////////////////////////////////////////////////////////////////////// // store global element number as a QuadratureFunction - auto quad_space1 = mfem::QuadratureSpace(&pmesh1, 0); - auto quad_space2 = mfem::QuadratureSpace(&pmesh2, 0); - auto quad_fn1 = mfem::QuadratureFunction(&quad_space1); - auto quad_fn2 = mfem::QuadratureFunction(&quad_space2); - for (int e{0}; e < pmesh1.GetNE(); ++e) - { + auto quad_space1 = mfem::QuadratureSpace( &pmesh1, 0 ); + auto quad_space2 = mfem::QuadratureSpace( &pmesh2, 0 ); + auto quad_fn1 = mfem::QuadratureFunction( &quad_space1 ); + auto quad_fn2 = mfem::QuadratureFunction( &quad_space2 ); + for ( int e{ 0 }; e < pmesh1.GetNE(); ++e ) { auto quad_val = mfem::Vector(); - quad_fn1.GetValues(e, quad_val); - for (int i{0}; i < quad_val.Size(); ++i) - { - quad_val[i] = static_cast(pmesh1.GetGlobalElementNum(e)); + quad_fn1.GetValues( e, quad_val ); + for ( int i{ 0 }; i < quad_val.Size(); ++i ) { + quad_val[i] = static_cast( pmesh1.GetGlobalElementNum( e ) ); } } - for (int e{0}; e < pmesh2.GetNE(); ++e) - { + for ( int e{ 0 }; e < pmesh2.GetNE(); ++e ) { auto quad_val = mfem::Vector(); - quad_fn2.GetValues(e, quad_val); - for (int i{0}; i < quad_val.Size(); ++i) - { - quad_val[i] = static_cast(pmesh2.GetGlobalElementNum(e)); + quad_fn2.GetValues( e, quad_val ); + for ( int i{ 0 }; i < quad_val.Size(); ++i ) { + quad_val[i] = static_cast( pmesh2.GetGlobalElementNum( e ) ); } } @@ -391,16 +328,10 @@ void RedecompExample( // STEP 2: Create QuadratureFunctions on RedecompMeshes ///////////////////////////////////////////////////////////////////////////// - auto redecomp_quad_space1 = mfem::QuadratureSpace( - redecomp_meshes[0].get(), - 0 - ); - auto redecomp_quad_space2 = mfem::QuadratureSpace( - redecomp_meshes[1].get(), - 0 - ); - auto redecomp_quad_fn1 = mfem::QuadratureFunction(&redecomp_quad_space1); - auto redecomp_quad_fn2 = mfem::QuadratureFunction(&redecomp_quad_space2); + auto redecomp_quad_space1 = mfem::QuadratureSpace( redecomp_meshes[0].get(), 0 ); + auto redecomp_quad_space2 = mfem::QuadratureSpace( redecomp_meshes[1].get(), 0 ); + auto redecomp_quad_fn1 = mfem::QuadratureFunction( &redecomp_quad_space1 ); + auto redecomp_quad_fn2 = mfem::QuadratureFunction( &redecomp_quad_space2 ); ///////////////////////////////////////////////////////////////////////////// // STEP 3: Create transfer object (element-by-element) @@ -412,15 +343,12 @@ void RedecompExample( ///////////////////////////////////////////////////////////////////////////// timer.start(); - elem_transfer.TransferToSerial(quad_fn1, redecomp_quad_fn1); - elem_transfer.TransferToSerial(quad_fn2, redecomp_quad_fn2); + elem_transfer.TransferToSerial( quad_fn1, redecomp_quad_fn1 ); + elem_transfer.TransferToSerial( quad_fn2, redecomp_quad_fn2 ); timer.stop(); - SLIC_INFO_ROOT(axom::fmt::format( - "Time to transfer quadrature function: {0:f}ms", - timer.elapsedTimeInMilliSec() - )); - redecomp_dc1.RegisterQField("elem_id", &redecomp_quad_fn1); - redecomp_dc2.RegisterQField("elem_id", &redecomp_quad_fn2); + SLIC_INFO_ROOT( axom::fmt::format( "Time to transfer quadrature function: {0:f}ms", timer.elapsedTimeInMilliSec() ) ); + redecomp_dc1.RegisterQField( "elem_id", &redecomp_quad_fn1 ); + redecomp_dc2.RegisterQField( "elem_id", &redecomp_quad_fn2 ); ///////////////////////////////////////////////////////////////////////////// // STEP 5: Operate on data (no-op here) @@ -431,15 +359,13 @@ void RedecompExample( ///////////////////////////////////////////////////////////////////////////// timer.start(); - elem_transfer.TransferToParallel(redecomp_quad_fn1, quad_fn1); - elem_transfer.TransferToParallel(redecomp_quad_fn2, quad_fn2); + elem_transfer.TransferToParallel( redecomp_quad_fn1, quad_fn1 ); + elem_transfer.TransferToParallel( redecomp_quad_fn2, quad_fn2 ); timer.stop(); - SLIC_INFO_ROOT(axom::fmt::format( - "Time to transfer back quadrature function: {0:f}ms\n", - timer.elapsedTimeInMilliSec() - )); - pmesh_dc1.RegisterQField("elem_id", &quad_fn1); - pmesh_dc2.RegisterQField("elem_id", &quad_fn2); + SLIC_INFO_ROOT( + axom::fmt::format( "Time to transfer back quadrature function: {0:f}ms\n", timer.elapsedTimeInMilliSec() ) ); + pmesh_dc1.RegisterQField( "elem_id", &quad_fn1 ); + pmesh_dc2.RegisterQField( "elem_id", &quad_fn2 ); pmesh_dc1.Save(); pmesh_dc2.Save(); @@ -447,147 +373,123 @@ void RedecompExample( redecomp_dc2.Save(); // print mesh stats to screen - if (rank == 0) - { - auto n_els = std::vector(np); + if ( rank == 0 ) { + auto n_els = std::vector( np ); n_els[0] = pmesh1.GetNE(); - for (int i{1}; i < np; ++i) - { + for ( int i{ 1 }; i < np; ++i ) { MPI_Status status; - MPI_Recv(&n_els[i], 1, MPI_INT, i, 0, MPI_COMM_WORLD, &status); + MPI_Recv( &n_els[i], 1, MPI_INT, i, 0, MPI_COMM_WORLD, &status ); } - SLIC_INFO("First original ParMesh stats"); - SLIC_INFO("----------------------------"); - for (int i{0}; i < np; ++i) - { - SLIC_INFO(axom::fmt::format("Rank {0}: {1} elements", i, n_els[i])); + SLIC_INFO( "First original ParMesh stats" ); + SLIC_INFO( "----------------------------" ); + for ( int i{ 0 }; i < np; ++i ) { + SLIC_INFO( axom::fmt::format( "Rank {0}: {1} elements", i, n_els[i] ) ); } n_els[0] = redecomp_meshes[0]->GetNE(); - for (int i{1}; i < np; ++i) - { + for ( int i{ 1 }; i < np; ++i ) { MPI_Status status; - MPI_Recv(&n_els[i], 1, MPI_INT, i, 0, MPI_COMM_WORLD, &status); + MPI_Recv( &n_els[i], 1, MPI_INT, i, 0, MPI_COMM_WORLD, &status ); } - SLIC_INFO("First redecomposed Mesh stats"); - SLIC_INFO("-----------------------------"); - for (int i{0}; i < np; ++i) - { - SLIC_INFO(axom::fmt::format("Rank {0}: {1} elements", i, n_els[i])); + SLIC_INFO( "First redecomposed Mesh stats" ); + SLIC_INFO( "-----------------------------" ); + for ( int i{ 0 }; i < np; ++i ) { + SLIC_INFO( axom::fmt::format( "Rank {0}: {1} elements", i, n_els[i] ) ); } - } - else - { + } else { int n_els = pmesh1.GetNE(); - MPI_Send(&n_els, 1, MPI_INT, 0, 0, MPI_COMM_WORLD); + MPI_Send( &n_els, 1, MPI_INT, 0, 0, MPI_COMM_WORLD ); n_els = redecomp_meshes[0]->GetNE(); - MPI_Send(&n_els, 1, MPI_INT, 0, 0, MPI_COMM_WORLD); + MPI_Send( &n_els, 1, MPI_INT, 0, 0, MPI_COMM_WORLD ); } - if (rank == 0) - { - auto n_els = std::vector(np); + if ( rank == 0 ) { + auto n_els = std::vector( np ); n_els[0] = pmesh2.GetNE(); - for (int i{1}; i < np; ++i) - { + for ( int i{ 1 }; i < np; ++i ) { MPI_Status status; - MPI_Recv(&n_els[i], 1, MPI_INT, i, 0, MPI_COMM_WORLD, &status); + MPI_Recv( &n_els[i], 1, MPI_INT, i, 0, MPI_COMM_WORLD, &status ); } - SLIC_INFO("Second original ParMesh stats"); - SLIC_INFO("-----------------------------"); - for (int i{0}; i < np; ++i) - { - SLIC_INFO(axom::fmt::format("Rank {0}: {1} elements", i, n_els[i])); + SLIC_INFO( "Second original ParMesh stats" ); + SLIC_INFO( "-----------------------------" ); + for ( int i{ 0 }; i < np; ++i ) { + SLIC_INFO( axom::fmt::format( "Rank {0}: {1} elements", i, n_els[i] ) ); } n_els[0] = redecomp_meshes[1]->GetNE(); - for (int i{1}; i < np; ++i) - { + for ( int i{ 1 }; i < np; ++i ) { MPI_Status status; - MPI_Recv(&n_els[i], 1, MPI_INT, i, 0, MPI_COMM_WORLD, &status); + MPI_Recv( &n_els[i], 1, MPI_INT, i, 0, MPI_COMM_WORLD, &status ); } - SLIC_INFO("Second redecomposed Mesh stats"); - SLIC_INFO("------------------------------"); - for (int i{0}; i < np; ++i) - { - SLIC_INFO(axom::fmt::format("Rank {0}: {1} elements", i, n_els[i])); + SLIC_INFO( "Second redecomposed Mesh stats" ); + SLIC_INFO( "------------------------------" ); + for ( int i{ 0 }; i < np; ++i ) { + SLIC_INFO( axom::fmt::format( "Rank {0}: {1} elements", i, n_els[i] ) ); } - } - else - { + } else { int n_els = pmesh2.GetNE(); - MPI_Send(&n_els, 1, MPI_INT, 0, 0, MPI_COMM_WORLD); + MPI_Send( &n_els, 1, MPI_INT, 0, 0, MPI_COMM_WORLD ); n_els = redecomp_meshes[1]->GetNE(); - MPI_Send(&n_els, 1, MPI_INT, 0, 0, MPI_COMM_WORLD); + MPI_Send( &n_els, 1, MPI_INT, 0, 0, MPI_COMM_WORLD ); } } -std::unique_ptr MakePMesh( - MPI_Comm comm, - const std::string& mesh_file, - double theta, - int ref_levels -) +std::unique_ptr MakePMesh( MPI_Comm comm, const std::string& mesh_file, double theta, int ref_levels ) { - auto mesh = std::make_unique(mesh_file.c_str(), 1, 1); + auto mesh = std::make_unique( mesh_file.c_str(), 1, 1 ); // rotate unrefined serial mesh - if (theta != 0.0) - { + if ( theta != 0.0 ) { theta = theta * redecomp::pi / 180.0; - auto R = axom::numerics::Matrix::zeros(3, 3); - R(0, 0) = cos(theta); R(0, 1) = -sin(theta); R(0, 2) = 0.0; - R(1, 0) = sin(theta); R(1, 1) = cos(theta); R(1, 2) = 0.0; - R(2, 0) = 0.0; R(2, 1) = 0.0; R(2, 2) = 1.0; + auto R = axom::numerics::Matrix::zeros( 3, 3 ); + R( 0, 0 ) = cos( theta ); + R( 0, 1 ) = -sin( theta ); + R( 0, 2 ) = 0.0; + R( 1, 0 ) = sin( theta ); + R( 1, 1 ) = cos( theta ); + R( 1, 2 ) = 0.0; + R( 2, 0 ) = 0.0; + R( 2, 1 ) = 0.0; + R( 2, 2 ) = 1.0; auto dim = mesh->Dimension(); - for (int v{0}; v < mesh->GetNV(); ++v) - { - auto tmp_vert = axom::Array(dim, dim); - auto vert = mesh->GetVertex(v); - axom::numerics::matrix_vector_multiply(R, vert, tmp_vert.data()); - for (int i{0}; i < dim; ++i) - { + for ( int v{ 0 }; v < mesh->GetNV(); ++v ) { + auto tmp_vert = axom::Array( dim, dim ); + auto vert = mesh->GetVertex( v ); + axom::numerics::matrix_vector_multiply( R, vert, tmp_vert.data() ); + for ( int i{ 0 }; i < dim; ++i ) { vert[i] = tmp_vert[i]; } } auto node_gf = mesh->GetNodes(); - if (node_gf) - { + if ( node_gf ) { auto node_fes = node_gf->FESpace(); auto vdim = node_fes->GetVDim(); - for (int n{0}; n < node_fes->GetNDofs(); ++n) - { - auto tmp_node = axom::Array(vdim, vdim); - for (int i{0}; i < vdim; ++i) - { - for (int j{0}; j < vdim; ++j) - { - tmp_node[i] = tmp_node[i] + R(i, j)*(*node_gf)(node_fes->DofToVDof(n, j)); + for ( int n{ 0 }; n < node_fes->GetNDofs(); ++n ) { + auto tmp_node = axom::Array( vdim, vdim ); + for ( int i{ 0 }; i < vdim; ++i ) { + for ( int j{ 0 }; j < vdim; ++j ) { + tmp_node[i] = tmp_node[i] + R( i, j ) * ( *node_gf )( node_fes->DofToVDof( n, j ) ); } } - for (int i{0}; i < vdim; ++i) - { - (*node_gf)(node_fes->DofToVDof(n, i)) = tmp_node[i]; + for ( int i{ 0 }; i < vdim; ++i ) { + ( *node_gf )( node_fes->DofToVDof( n, i ) ) = tmp_node[i]; } } } } - + // refine serial mesh - if (ref_levels > 0) - { - for (int i{0}; i < ref_levels; ++i) - { + if ( ref_levels > 0 ) { + for ( int i{ 0 }; i < ref_levels; ++i ) { mesh->UniformRefinement(); } } // create parallel mesh from serial - auto pmesh = std::make_unique(comm, *mesh); + auto pmesh = std::make_unique( comm, *mesh ); // further refinement of parallel mesh { int par_ref_levels = 2; - for (int i{0}; i < par_ref_levels; ++i) - { + for ( int i{ 0 }; i < par_ref_levels; ++i ) { pmesh->UniformRefinement(); } } diff --git a/src/examples/sparse_matrix_redecomp.cpp b/src/examples/sparse_matrix_redecomp.cpp index d4e27e94..ed7ec455 100644 --- a/src/examples/sparse_matrix_redecomp.cpp +++ b/src/examples/sparse_matrix_redecomp.cpp @@ -47,99 +47,95 @@ int main( int argc, char** argv ) // initialize MPI MPI_Init( &argc, &argv ); int np, rank; - MPI_Comm_size(MPI_COMM_WORLD, &np); - MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size( MPI_COMM_WORLD, &np ); + MPI_Comm_rank( MPI_COMM_WORLD, &rank ); // initialize logger axom::slic::SimpleLogger logger; - axom::slic::setIsRoot(rank == 0); + axom::slic::setIsRoot( rank == 0 ); // command line options // spatial dimension of the mesh - int dim { 2 }; + int dim{ 2 }; // number of times to uniformly refine the serial mesh before constructing the parallel mesh - int ref_levels { 0 }; + int ref_levels{ 0 }; // filter radius of the explicit density filter - double filter_radius { 1.0 }; + double filter_radius{ 1.0 }; // set and parse options - axom::CLI::App app { "sparse_matrix_redecomp" }; - app.add_option("-d,--dim", dim, "Spatial dimension of the mesh") - ->capture_default_str(); - app.add_option("-r,--refine", ref_levels, "Number of times to refine the mesh") - ->capture_default_str(); - app.add_option("-R,--filter-radius", filter_radius, "Filter radius of the explicit density filter") - ->capture_default_str(); - CLI11_PARSE(app, argc, argv); + axom::CLI::App app{ "sparse_matrix_redecomp" }; + app.add_option( "-d,--dim", dim, "Spatial dimension of the mesh" )->capture_default_str(); + app.add_option( "-r,--refine", ref_levels, "Number of times to refine the mesh" )->capture_default_str(); + app.add_option( "-R,--filter-radius", filter_radius, "Filter radius of the explicit density filter" ) + ->capture_default_str(); + CLI11_PARSE( app, argc, argv ); // print options - SLIC_INFO_ROOT("Running sparse_matrix_redecomp with the following options:"); - SLIC_INFO_ROOT(axom::fmt::format("dim: {0}", dim)); - SLIC_INFO_ROOT(axom::fmt::format("refine: {0}", ref_levels)); - SLIC_INFO_ROOT(axom::fmt::format("filter-radius: {0}\n", filter_radius)); - - SLIC_INFO_ROOT("Creating mfem::ParMesh..."); - double side_length { 1.0 }; // side length of square domain - int n_elem_per_dim { 4 }; // number of elements per dimension + SLIC_INFO_ROOT( "Running sparse_matrix_redecomp with the following options:" ); + SLIC_INFO_ROOT( axom::fmt::format( "dim: {0}", dim ) ); + SLIC_INFO_ROOT( axom::fmt::format( "refine: {0}", ref_levels ) ); + SLIC_INFO_ROOT( axom::fmt::format( "filter-radius: {0}\n", filter_radius ) ); + + SLIC_INFO_ROOT( "Creating mfem::ParMesh..." ); + double side_length{ 1.0 }; // side length of square domain + int n_elem_per_dim{ 4 }; // number of elements per dimension mfem::Mesh serial_mesh; - if (dim == 2) { - serial_mesh = mfem::Mesh::MakeCartesian2D(n_elem_per_dim, n_elem_per_dim, - mfem::Element::Type::QUADRILATERAL, false, side_length, side_length); - } - else { - serial_mesh = mfem::Mesh::MakeCartesian3D(n_elem_per_dim, n_elem_per_dim, n_elem_per_dim, - mfem::Element::Type::HEXAHEDRON, side_length, side_length, side_length); + if ( dim == 2 ) { + serial_mesh = mfem::Mesh::MakeCartesian2D( n_elem_per_dim, n_elem_per_dim, mfem::Element::Type::QUADRILATERAL, + false, side_length, side_length ); + } else { + serial_mesh = mfem::Mesh::MakeCartesian3D( n_elem_per_dim, n_elem_per_dim, n_elem_per_dim, + mfem::Element::Type::HEXAHEDRON, side_length, side_length, side_length ); } // refine serial mesh - for (int i{0}; i < ref_levels; ++i) - { + for ( int i{ 0 }; i < ref_levels; ++i ) { serial_mesh.UniformRefinement(); } // update the number of elements per dimension to reflect the refined mesh - n_elem_per_dim *= std::pow(2, ref_levels); + n_elem_per_dim *= std::pow( 2, ref_levels ); // compute the element side length - double dx { side_length / n_elem_per_dim }; + double dx{ side_length / n_elem_per_dim }; // create parallel mesh from serial - mfem::ParMesh par_mesh { MPI_COMM_WORLD, serial_mesh }; + mfem::ParMesh par_mesh{ MPI_COMM_WORLD, serial_mesh }; - SLIC_INFO_ROOT("Creating redecomp::RedecompMesh..."); + SLIC_INFO_ROOT( "Creating redecomp::RedecompMesh..." ); // the last argument sets the ghost radius == filter_radius - redecomp::RedecompMesh redecomp_mesh { par_mesh, filter_radius, redecomp::RedecompMesh::RCB }; - - SLIC_INFO_ROOT("Creating finite element spaces..."); + redecomp::RedecompMesh redecomp_mesh{ par_mesh, filter_radius, redecomp::RedecompMesh::RCB }; + + SLIC_INFO_ROOT( "Creating finite element spaces..." ); // create 0-order L2 finite element space on parmesh (1 point per element) - mfem::L2_FECollection l2_elems { 0, par_mesh.SpaceDimension() }; - mfem::ParFiniteElementSpace par_fes { &par_mesh, &l2_elems }; + mfem::L2_FECollection l2_elems{ 0, par_mesh.SpaceDimension() }; + mfem::ParFiniteElementSpace par_fes{ &par_mesh, &l2_elems }; // create 0-order L2 finite element space on redecomp mesh (1 point per element) - mfem::FiniteElementSpace redecomp_fes { &redecomp_mesh, &l2_elems }; + mfem::FiniteElementSpace redecomp_fes{ &redecomp_mesh, &l2_elems }; - SLIC_INFO_ROOT("Creating sparse matrix transfer operator..."); - redecomp::SparseMatrixTransfer matrix_xfer { par_fes, par_fes, redecomp_fes, redecomp_fes }; + SLIC_INFO_ROOT( "Creating sparse matrix transfer operator..." ); + redecomp::SparseMatrixTransfer matrix_xfer{ par_fes, par_fes, redecomp_fes, redecomp_fes }; - SLIC_INFO_ROOT("Create sparse matrix on redecomp::RedecompMesh finite element space..."); - mfem::SparseMatrix W_redecomp { redecomp_fes.GetVSize(), redecomp_fes.GetVSize() }; + SLIC_INFO_ROOT( "Create sparse matrix on redecomp::RedecompMesh finite element space..." ); + mfem::SparseMatrix W_redecomp{ redecomp_fes.GetVSize(), redecomp_fes.GetVSize() }; // define kernel of density filter (using isotropic "cone" filter here) - auto filter_kernel = [&filter_radius](const mfem::Vector &xi, const mfem::Vector &xj) { - return std::max(0.0, filter_radius - xi.DistanceTo(xj)); + auto filter_kernel = [&filter_radius]( const mfem::Vector& xi, const mfem::Vector& xj ) { + return std::max( 0.0, filter_radius - xi.DistanceTo( xj ) ); }; // intialize containers needed for filter evaluation int n_local_elem = redecomp_mesh.GetNE(); - mfem::Vector xi(dim), xj(dim); - std::vector n_row_entries(n_local_elem); + mfem::Vector xi( dim ), xj( dim ); + std::vector n_row_entries( n_local_elem ); // loop over each element to form a row of the matrix - for (int i=0; i 0.0) { - W_redecomp.Add(i, j, Wij); + if ( Wij > 0.0 ) { + W_redecomp.Add( i, j, Wij ); n_row_entries[i]++; } } @@ -150,49 +146,48 @@ int main( int argc, char** argv ) W_redecomp.Finalize(); // normalize each row to conserve mass - for (int i=0; iMult(x, xf_filt); + W->Mult( x, xf_filt ); // compute analytical filtered field - xf_func.ProjectCoefficient(xfCoef); + xf_func.ProjectCoefficient( xfCoef ); // compute error - mfem::ParGridFunction error = xf_filt; - error -= xf_func; - auto l2_error = std::sqrt(mfem::InnerProduct(par_mesh.GetComm(), error, error)); + mfem::ParGridFunction error = xf_filt; + error -= xf_func; + auto l2_error = std::sqrt( mfem::InnerProduct( par_mesh.GetComm(), error, error ) ); // write output - SLIC_INFO_ROOT(axom::fmt::format("L2 error between operator and exact solution: {0}\n", l2_error)); - SLIC_INFO_ROOT("Writing output to disk..."); + SLIC_INFO_ROOT( axom::fmt::format( "L2 error between operator and exact solution: {0}\n", l2_error ) ); + SLIC_INFO_ROOT( "Writing output to disk..." ); // write redecomp mesh - mfem::VisItDataCollection redecomp_dc { "redecomp_rank" + std::to_string(rank), &redecomp_mesh }; + mfem::VisItDataCollection redecomp_dc{ "redecomp_rank" + std::to_string( rank ), &redecomp_mesh }; redecomp_dc.Save(); // write parmesh and fields - mfem::VisItDataCollection par_dc { "parmesh", &par_mesh }; - par_dc.RegisterField("x", &x); - par_dc.RegisterField("xf_operator", &xf_filt); - par_dc.RegisterField("xf_analytic", &xf_func); - par_dc.RegisterField("xf_error", &error); + mfem::VisItDataCollection par_dc{ "parmesh", &par_mesh }; + par_dc.RegisterField( "x", &x ); + par_dc.RegisterField( "xf_operator", &xf_filt ); + par_dc.RegisterField( "xf_analytic", &xf_func ); + par_dc.RegisterField( "xf_error", &error ); par_dc.Save(); // cleanup diff --git a/src/redecomp/MultiRedecomp.cpp b/src/redecomp/MultiRedecomp.cpp index a49a4664..be619773 100644 --- a/src/redecomp/MultiRedecomp.cpp +++ b/src/redecomp/MultiRedecomp.cpp @@ -11,143 +11,110 @@ #include "redecomp/partition/PartitionElements.hpp" #include "redecomp/partition/RCB.hpp" -namespace redecomp -{ +namespace redecomp { -MultiRedecomp::MultiRedecomp( - int dim, - MPI_Comm comm, - PartitionType method, - double ghost_len_multiplier -) -: ghost_len_multiplier_ { ghost_len_multiplier } +MultiRedecomp::MultiRedecomp( int dim, MPI_Comm comm, PartitionType method, double ghost_len_multiplier ) + : ghost_len_multiplier_{ ghost_len_multiplier } { // create partitioner - switch (dim) - { + switch ( dim ) { case 2: - switch (method) - { + switch ( method ) { case RCB: - partitioner_ = std::make_unique( - std::make_unique(), - std::make_unique(comm) - ); + partitioner_ = std::make_unique( std::make_unique(), + std::make_unique( comm ) ); break; default: - SLIC_ERROR_ROOT("Only recursive coordinate bisection (RCB) decompositions " - "are currently supported."); + SLIC_ERROR_ROOT( + "Only recursive coordinate bisection (RCB) decompositions " + "are currently supported." ); } break; case 3: - switch (method) - { + switch ( method ) { case RCB: - partitioner_ = std::make_unique( - std::make_unique(), - std::make_unique(comm) - ); + partitioner_ = std::make_unique( std::make_unique(), + std::make_unique( comm ) ); break; default: - SLIC_ERROR_ROOT("Only recursive coordinate bisection (RCB) decompositions " - "are currently supported."); + SLIC_ERROR_ROOT( + "Only recursive coordinate bisection (RCB) decompositions " + "are currently supported." ); } break; default: - SLIC_ERROR_ROOT("Only 2D and 3D meshes are supported."); + SLIC_ERROR_ROOT( "Only 2D and 3D meshes are supported." ); } } -MultiRedecomp::MultiRedecomp( - std::unique_ptr partitioner, - double ghost_len_multiplier -) -: partitioner_ { std::move(partitioner) }, - ghost_len_multiplier_ { ghost_len_multiplier } -{} +MultiRedecomp::MultiRedecomp( std::unique_ptr partitioner, double ghost_len_multiplier ) + : partitioner_{ std::move( partitioner ) }, ghost_len_multiplier_{ ghost_len_multiplier } +{ +} std::vector> MultiRedecomp::createRedecompMeshes( - const std::vector& parents -) + const std::vector& parents ) { std::vector> redecomps; // check the validity of parents - SLIC_ERROR_ROOT_IF(parents.empty(), "At least one mesh in parents required."); - for (size_t m{1}; m < parents.size(); ++m) - { - SLIC_ERROR_ROOT_IF(parents[m-1]->SpaceDimension() != parents[m]->SpaceDimension(), - "SpaceDimension must match on all parent meshes."); - SLIC_ERROR_ROOT_IF(parents[m-1]->GetComm() != parents[m]->GetComm(), - "MPI_Comm must match on all parent meshes."); + SLIC_ERROR_ROOT_IF( parents.empty(), "At least one mesh in parents required." ); + for ( size_t m{ 1 }; m < parents.size(); ++m ) { + SLIC_ERROR_ROOT_IF( parents[m - 1]->SpaceDimension() != parents[m]->SpaceDimension(), + "SpaceDimension must match on all parent meshes." ); + SLIC_ERROR_ROOT_IF( parents[m - 1]->GetComm() != parents[m]->GetComm(), + "MPI_Comm must match on all parent meshes." ); } // make sure the partitioner works with parents auto dim = parents[0]->SpaceDimension(); - switch (dim) - { - case 2: - { - auto partitioner2d = dynamic_cast(partitioner_.get()); - SLIC_ERROR_ROOT_IF(partitioner2d == nullptr, "Partitioner must be Partitioner2D."); - auto partition_elems2d = dynamic_cast( - partitioner2d->getPartitionEntity() - ); - SLIC_ERROR_ROOT_IF(partition_elems2d == nullptr, "Redecomp requires the PartitionEntity " - "to be PartitionElements."); + switch ( dim ) { + case 2: { + auto partitioner2d = dynamic_cast( partitioner_.get() ); + SLIC_ERROR_ROOT_IF( partitioner2d == nullptr, "Partitioner must be Partitioner2D." ); + auto partition_elems2d = dynamic_cast( partitioner2d->getPartitionEntity() ); + SLIC_ERROR_ROOT_IF( partition_elems2d == nullptr, + "Redecomp requires the PartitionEntity " + "to be PartitionElements." ); break; } - case 3: - { - auto partitioner3d = dynamic_cast(partitioner_.get()); - SLIC_ERROR_ROOT_IF(partitioner3d == nullptr, "Partitioner must be Partitioner3D."); - auto partition_elems3d = dynamic_cast( - partitioner3d->getPartitionEntity() - ); - SLIC_ERROR_ROOT_IF(partition_elems3d == nullptr, "Redecomp requires the PartitionEntity " - "to be PartitionElements."); + case 3: { + auto partitioner3d = dynamic_cast( partitioner_.get() ); + SLIC_ERROR_ROOT_IF( partitioner3d == nullptr, "Partitioner must be Partitioner3D." ); + auto partition_elems3d = dynamic_cast( partitioner3d->getPartitionEntity() ); + SLIC_ERROR_ROOT_IF( partition_elems3d == nullptr, + "Redecomp requires the PartitionEntity " + "to be PartitionElements." ); break; } default: - SLIC_ERROR_ROOT("Only 2D and 3D meshes are supported."); + SLIC_ERROR_ROOT( "Only 2D and 3D meshes are supported." ); } // Compute the number of parts the redecomp mesh should have and the size of // the ghost layer auto n_parts = 0; auto ghost_size = 0.0; - for (auto parent : parents) - { - n_parts = std::max(n_parts, static_cast(parent->GetGlobalNE())); - ghost_size = std::max( - ghost_size, - RedecompMesh::MaxElementSize(*parent, partitioner_->getMPIUtility()) - ); + for ( auto parent : parents ) { + n_parts = std::max( n_parts, static_cast( parent->GetGlobalNE() ) ); + ghost_size = std::max( ghost_size, RedecompMesh::MaxElementSize( *parent, partitioner_->getMPIUtility() ) ); } - n_parts = std::min(partitioner_->getMPIUtility().NRanks(), n_parts); + n_parts = std::min( partitioner_->getMPIUtility().NRanks(), n_parts ); ghost_size *= ghost_len_multiplier_; // Generate the new parent to redecomp (p2r) element partitioning for all // parent meshes - auto p2r_elems_by_mesh = partitioner_->generatePartitioning( - n_parts, - parents, - ghost_size - ); + auto p2r_elems_by_mesh = partitioner_->generatePartitioning( n_parts, parents, ghost_size ); // Build redecomp meshes using the generated parent-to-redecomp partitioning // of all parent meshes - redecomps.reserve(parents.size()); - for (size_t m{0}; m < parents.size(); ++m) - { - redecomps.emplace_back(new RedecompMesh( - *parents[m], - std::move(p2r_elems_by_mesh[m]) - )); + redecomps.reserve( parents.size() ); + for ( size_t m{ 0 }; m < parents.size(); ++m ) { + redecomps.emplace_back( new RedecompMesh( *parents[m], std::move( p2r_elems_by_mesh[m] ) ) ); } return redecomps; } -} // end namespace redecomp +} // end namespace redecomp diff --git a/src/redecomp/MultiRedecomp.hpp b/src/redecomp/MultiRedecomp.hpp index 7fbdb28d..81d184d4 100644 --- a/src/redecomp/MultiRedecomp.hpp +++ b/src/redecomp/MultiRedecomp.hpp @@ -12,16 +12,17 @@ #include "redecomp/RedecompMesh.hpp" -namespace redecomp -{ +namespace redecomp { -class MultiRedecomp -{ -public: +class MultiRedecomp { + public: /** * @brief List of partitioning methods verified to work with MultiRedecomp */ - enum PartitionType { RCB }; + enum PartitionType + { + RCB + }; /** * @brief Construct a new MultiRedecomp object @@ -33,12 +34,7 @@ class MultiRedecomp * @param ghost_len_multiplier Multiplier for the ghost element layer (base * value set by the approximate largest element size) */ - MultiRedecomp( - int dim, - MPI_Comm comm, - PartitionType method = RCB, - double ghost_len_multiplier = 1.25 - ); + MultiRedecomp( int dim, MPI_Comm comm, PartitionType method = RCB, double ghost_len_multiplier = 1.25 ); /** * @brief Construct a new MultiRedecomp object @@ -46,15 +42,12 @@ class MultiRedecomp * @note This constructor requires the Partitioner object passed directly to * it. This can be used to customize the Partitioner used (for example, with * non-default options with RCB or for a user-defined Partitioner). - * + * * @param partitioner Partitioning object used to define redecomposition * @param ghost_len_multiplier Multiplier for the ghost element layer (base * value set by the approximate largest element size) */ - MultiRedecomp( - std::unique_ptr partitioner, - double ghost_len_multiplier = 1.25 - ); + MultiRedecomp( std::unique_ptr partitioner, double ghost_len_multiplier = 1.25 ); /** * @brief Get a vector of Redecomp meshes associated with MultiRedecomp @@ -67,12 +60,9 @@ class MultiRedecomp * * @return Vector of Redecomp unique pointers */ - std::vector> createRedecompMeshes( - const std::vector& parents - ); - -private: + std::vector> createRedecompMeshes( const std::vector& parents ); + private: /** * @brief Partitioning method */ @@ -85,6 +75,6 @@ class MultiRedecomp double ghost_len_multiplier_; }; -} +} // namespace redecomp #endif /* SRC_REDECOMP_MULTIREDECOMP_HPP_ */ diff --git a/src/redecomp/RedecompMesh.cpp b/src/redecomp/RedecompMesh.cpp index f6807183..1e03deac 100644 --- a/src/redecomp/RedecompMesh.cpp +++ b/src/redecomp/RedecompMesh.cpp @@ -13,149 +13,110 @@ #include "redecomp/partition/PartitionElements.hpp" #include "redecomp/partition/RCB.hpp" -namespace redecomp -{ +namespace redecomp { -RedecompMesh::RedecompMesh( - const mfem::ParMesh& parent, - PartitionType method -) -: RedecompMesh(parent, DefaultGhostLength(parent), method) -{} +RedecompMesh::RedecompMesh( const mfem::ParMesh& parent, PartitionType method ) + : RedecompMesh( parent, DefaultGhostLength( parent ), method ) +{ +} -RedecompMesh::RedecompMesh( - const mfem::ParMesh& parent, - double ghost_length, - PartitionType method -) -: parent_ { parent }, - mpi_ { parent.GetComm() } +RedecompMesh::RedecompMesh( const mfem::ParMesh& parent, double ghost_length, PartitionType method ) + : parent_{ parent }, mpi_{ parent.GetComm() } { // build partitioner std::unique_ptr partitioner = nullptr; - switch (parent_.SpaceDimension()) - { + switch ( parent_.SpaceDimension() ) { case 2: - switch (method) - { + switch ( method ) { case RCB: - partitioner = std::make_unique( - std::make_unique(), - std::make_unique(parent.GetComm()) - ); + partitioner = std::make_unique( std::make_unique(), + std::make_unique( parent.GetComm() ) ); break; default: - SLIC_ERROR_ROOT("Only recursive coordinate bisection (RCB) decompositions " - "are currently supported."); + SLIC_ERROR_ROOT( + "Only recursive coordinate bisection (RCB) decompositions " + "are currently supported." ); } break; case 3: - switch (method) - { + switch ( method ) { case RCB: - partitioner = std::make_unique( - std::make_unique(), - std::make_unique(parent.GetComm()) - ); + partitioner = std::make_unique( std::make_unique(), + std::make_unique( parent.GetComm() ) ); break; default: - SLIC_ERROR_ROOT("Only recursive coordinate bisection (RCB) decompositions " - "are currently supported."); + SLIC_ERROR_ROOT( + "Only recursive coordinate bisection (RCB) decompositions " + "are currently supported." ); } break; default: - SLIC_ERROR_ROOT("Only 2D and 3D meshes are supported."); + SLIC_ERROR_ROOT( "Only 2D and 3D meshes are supported." ); } // preclude degenerate case where num elements/2 < num ranks // factor of 2 on num elements are due to elements being paired for contact - auto n_parts = std::min( - parent.GetNRanks(), - (static_cast(parent.GetGlobalNE()) + 1) / 2 - ); - p2r_elems_ = BuildP2RElementList(*partitioner, n_parts, ghost_length); + auto n_parts = std::min( parent.GetNRanks(), ( static_cast( parent.GetGlobalNE() ) + 1 ) / 2 ); + p2r_elems_ = BuildP2RElementList( *partitioner, n_parts, ghost_length ); BuildRedecomp(); } -RedecompMesh::RedecompMesh( - const mfem::ParMesh& parent, - std::unique_ptr partitioner -) -: RedecompMesh(parent, DefaultGhostLength(parent), std::move(partitioner)) -{} +RedecompMesh::RedecompMesh( const mfem::ParMesh& parent, std::unique_ptr partitioner ) + : RedecompMesh( parent, DefaultGhostLength( parent ), std::move( partitioner ) ) +{ +} -RedecompMesh::RedecompMesh( - const mfem::ParMesh& parent, - double ghost_length, - std::unique_ptr partitioner -) -: parent_ { parent }, - mpi_ { parent.GetComm() } +RedecompMesh::RedecompMesh( const mfem::ParMesh& parent, double ghost_length, + std::unique_ptr partitioner ) + : parent_{ parent }, mpi_{ parent.GetComm() } { // check partitioner - switch (parent_.SpaceDimension()) - { - case 2: - { - auto partitioner2d = dynamic_cast(partitioner.get()); - SLIC_ERROR_ROOT_IF(partitioner2d == nullptr, "Partitioner must be Partitioner2D."); - auto partition_elems2d = dynamic_cast( - partitioner2d->getPartitionEntity() - ); - SLIC_ERROR_ROOT_IF(partition_elems2d == nullptr, "Redecomp requires the PartitionEntity " - "to be PartitionElements."); + switch ( parent_.SpaceDimension() ) { + case 2: { + auto partitioner2d = dynamic_cast( partitioner.get() ); + SLIC_ERROR_ROOT_IF( partitioner2d == nullptr, "Partitioner must be Partitioner2D." ); + auto partition_elems2d = dynamic_cast( partitioner2d->getPartitionEntity() ); + SLIC_ERROR_ROOT_IF( partition_elems2d == nullptr, + "Redecomp requires the PartitionEntity " + "to be PartitionElements." ); break; } - case 3: - { - auto partitioner3d = dynamic_cast(partitioner.get()); - SLIC_ERROR_ROOT_IF(partitioner3d == nullptr, "Partitioner must be Partitioner3D."); - auto partition_elems3d = dynamic_cast( - partitioner3d->getPartitionEntity() - ); - SLIC_ERROR_ROOT_IF(partition_elems3d == nullptr, "Redecomp requires the PartitionEntity " - "to be PartitionElements."); + case 3: { + auto partitioner3d = dynamic_cast( partitioner.get() ); + SLIC_ERROR_ROOT_IF( partitioner3d == nullptr, "Partitioner must be Partitioner3D." ); + auto partition_elems3d = dynamic_cast( partitioner3d->getPartitionEntity() ); + SLIC_ERROR_ROOT_IF( partition_elems3d == nullptr, + "Redecomp requires the PartitionEntity " + "to be PartitionElements." ); break; } default: - SLIC_ERROR_ROOT("Only 2D and 3D meshes are supported."); + SLIC_ERROR_ROOT( "Only 2D and 3D meshes are supported." ); } // preclude degenerate case where num elements < num ranks - auto n_parts = std::min(parent.GetNRanks(), static_cast(parent.GetGlobalNE())); + auto n_parts = std::min( parent.GetNRanks(), static_cast( parent.GetGlobalNE() ) ); // p2r = parent to redecomp - p2r_elems_ = BuildP2RElementList(*partitioner, n_parts, ghost_length); + p2r_elems_ = BuildP2RElementList( *partitioner, n_parts, ghost_length ); BuildRedecomp(); } -RedecompMesh::RedecompMesh( - const mfem::ParMesh& parent, - EntityIndexByRank&& p2r_elems -) -: parent_ { parent }, - mpi_ { parent.GetComm() }, - p2r_elems_ { std::move(p2r_elems) } +RedecompMesh::RedecompMesh( const mfem::ParMesh& parent, EntityIndexByRank&& p2r_elems ) + : parent_{ parent }, mpi_{ parent.GetComm() }, p2r_elems_{ std::move( p2r_elems ) } { BuildRedecomp(); } -double RedecompMesh::DefaultGhostLength(const mfem::ParMesh& parent) const +double RedecompMesh::DefaultGhostLength( const mfem::ParMesh& parent ) const { - return 1.25 * MaxElementSize(parent, MPIUtility(parent.GetComm())); + return 1.25 * MaxElementSize( parent, MPIUtility( parent.GetComm() ) ); } -EntityIndexByRank RedecompMesh::BuildP2RElementList( - const Partitioner& partitioner, - int n_parts, - double ghost_length -) const +EntityIndexByRank RedecompMesh::BuildP2RElementList( const Partitioner& partitioner, int n_parts, + double ghost_length ) const { - SLIC_ERROR_ROOT_IF(ghost_length < 0.0, "Ghost element layer length should be 0 or larger."); - return partitioner.generatePartitioning( - n_parts, - { &parent_ }, - ghost_length - )[0]; + SLIC_ERROR_ROOT_IF( ghost_length < 0.0, "Ghost element layer length should be 0 or larger." ); + return partitioner.generatePartitioning( n_parts, { &parent_ }, ghost_length )[0]; } void RedecompMesh::BuildRedecomp() @@ -165,255 +126,192 @@ void RedecompMesh::BuildRedecomp() spaceDim = parent_.SpaceDimension(); // construct list of global vertices to move from parent to redecomp domain - auto parent_vertex_fec = std::make_unique(1, parent_.Dimension()); - auto parent_vertex_fes = mfem::ParFiniteElementSpace( - const_cast(&parent_), - parent_vertex_fec.get() - ); - auto vertex_transfer = TransferByNodes(parent_vertex_fes, *this); + auto parent_vertex_fec = std::make_unique( 1, parent_.Dimension() ); + auto parent_vertex_fes = + mfem::ParFiniteElementSpace( const_cast( &parent_ ), parent_vertex_fec.get() ); + auto vertex_transfer = TransferByNodes( parent_vertex_fes, *this ); // p2r = parent to redecomp - auto p2r_verts = vertex_transfer.P2RNodeList(true); + auto p2r_verts = vertex_transfer.P2RNodeList( true ); // r2p = redecomp to parent - auto r2p_vert_idx = MPIArray(&mpi_); + auto r2p_vert_idx = MPIArray( &mpi_ ); // map global parent vertex IDs to redecomp vertex IDs - r2p_vert_idx.SendRecvArrayEach(p2r_verts.first); + r2p_vert_idx.SendRecvArrayEach( p2r_verts.first ); auto n_ranks = mpi_.NRanks(); auto vert_idx_map = std::unordered_map(); auto vert_ct = 0; - for (int r{0}; r < n_ranks; ++r) - { - for (int v{0}; v < r2p_vert_idx[r].size(); ++v) - { - auto vert_idx_it = vert_idx_map.emplace(r2p_vert_idx[r][v], vert_ct); + for ( int r{ 0 }; r < n_ranks; ++r ) { + for ( int v{ 0 }; v < r2p_vert_idx[r].size(); ++v ) { + auto vert_idx_it = vert_idx_map.emplace( r2p_vert_idx[r][v], vert_ct ); r2p_vert_idx[r][v] = vert_idx_it.first->second; - if (vert_idx_it.second) - { + if ( vert_idx_it.second ) { ++vert_ct; } } } // send vertex coords from parent to redecomp - p2r_verts = vertex_transfer.P2RNodeList(false); - auto redecomp_coords = MPIArray(&mpi_); - redecomp_coords.SendRecvEach( - [this, &p2r_verts](int dest) - { - auto parent_coords = axom::Array(); - const auto& vert_idx = p2r_verts.first[dest]; - auto n_coords = vert_idx.size(); - parent_coords.reserve(3*n_coords); - parent_coords.resize(n_coords, 3); - for (int v{0}; v < n_coords; ++v) - { - for (int d{0}; d < parent_.SpaceDimension(); ++d) - { - parent_coords(v, d) = parent_.GetVertex(vert_idx[v])[d]; - } + p2r_verts = vertex_transfer.P2RNodeList( false ); + auto redecomp_coords = MPIArray( &mpi_ ); + redecomp_coords.SendRecvEach( [this, &p2r_verts]( int dest ) { + auto parent_coords = axom::Array(); + const auto& vert_idx = p2r_verts.first[dest]; + auto n_coords = vert_idx.size(); + parent_coords.reserve( 3 * n_coords ); + parent_coords.resize( n_coords, 3 ); + for ( int v{ 0 }; v < n_coords; ++v ) { + for ( int d{ 0 }; d < parent_.SpaceDimension(); ++d ) { + parent_coords( v, d ) = parent_.GetVertex( vert_idx[v] )[d]; } - return parent_coords; } - ); + return parent_coords; + } ); // create vertices on redecomp NumOfVertices = vert_idx_map.size(); - vertices.SetSize(NumOfVertices); - for (int r{0}; r < n_ranks; ++r) - { - for (int v{0}; v < r2p_vert_idx[r].size(); ++v) - { - for (int d{0}; d < parent_.SpaceDimension(); ++d) - { - vertices[r2p_vert_idx[r][v]](d) = redecomp_coords[r](v, d); + vertices.SetSize( NumOfVertices ); + for ( int r{ 0 }; r < n_ranks; ++r ) { + for ( int v{ 0 }; v < r2p_vert_idx[r].size(); ++v ) { + for ( int d{ 0 }; d < parent_.SpaceDimension(); ++d ) { + vertices[r2p_vert_idx[r][v]]( d ) = redecomp_coords[r]( v, d ); } } } // Create elements on this // Send and receive element types - auto redecomp_etypes = MPIArray(&mpi_); - redecomp_etypes.SendRecvEach( - [this](int dest) - { - auto parent_etypes = axom::Array( - p2r_elems_.first[dest].size(), p2r_elems_.first[dest].size()); - for (int e{0}; e < p2r_elems_.first[dest].size(); ++e) - { - parent_etypes[e] = parent_.GetElementType(p2r_elems_.first[dest][e]); - } - return parent_etypes; + auto redecomp_etypes = MPIArray( &mpi_ ); + redecomp_etypes.SendRecvEach( [this]( int dest ) { + auto parent_etypes = axom::Array( p2r_elems_.first[dest].size(), p2r_elems_.first[dest].size() ); + for ( int e{ 0 }; e < p2r_elems_.first[dest].size(); ++e ) { + parent_etypes[e] = parent_.GetElementType( p2r_elems_.first[dest][e] ); } - ); + return parent_etypes; + } ); // Find length of element connectivities - auto parent_tot_conns = axom::Array(n_ranks, n_ranks); - for (int r{0}; r < n_ranks; ++r) - { + auto parent_tot_conns = axom::Array( n_ranks, n_ranks ); + for ( int r{ 0 }; r < n_ranks; ++r ) { const auto& elem_idx = p2r_elems_.first[r]; auto n_elems = elem_idx.size(); - for (int e{0}; e < n_elems; ++e) - { - parent_tot_conns[r] += parent_.GetElement(elem_idx[e])->GetNVertices(); + for ( int e{ 0 }; e < n_elems; ++e ) { + parent_tot_conns[r] += parent_.GetElement( elem_idx[e] )->GetNVertices(); } } // Send and receive element connectivities - auto redecomp_conns = MPIArray(&mpi_); - redecomp_conns.SendRecvEach( - [this, &parent_tot_conns, &parent_vertex_fes](int dest) - { - auto parent_conns = axom::Array(parent_tot_conns[dest], parent_tot_conns[dest]); - parent_tot_conns[dest] = 0; - const auto& elem_idx = p2r_elems_.first[dest]; - for (int e{0}; e < elem_idx.size(); ++e) - { - mfem::Array elem_conn; - parent_.GetElement(elem_idx[e])->GetVertices(elem_conn); - for (int v{0}; v < elem_conn.Size(); ++v) - { - parent_conns[parent_tot_conns[dest]+v] = parent_vertex_fes.GetGlobalTDofNumber(elem_conn[v]); - } - parent_tot_conns[dest] += elem_conn.Size(); + auto redecomp_conns = MPIArray( &mpi_ ); + redecomp_conns.SendRecvEach( [this, &parent_tot_conns, &parent_vertex_fes]( int dest ) { + auto parent_conns = axom::Array( parent_tot_conns[dest], parent_tot_conns[dest] ); + parent_tot_conns[dest] = 0; + const auto& elem_idx = p2r_elems_.first[dest]; + for ( int e{ 0 }; e < elem_idx.size(); ++e ) { + mfem::Array elem_conn; + parent_.GetElement( elem_idx[e] )->GetVertices( elem_conn ); + for ( int v{ 0 }; v < elem_conn.Size(); ++v ) { + parent_conns[parent_tot_conns[dest] + v] = parent_vertex_fes.GetGlobalTDofNumber( elem_conn[v] ); } - return parent_conns; + parent_tot_conns[dest] += elem_conn.Size(); } - ); + return parent_conns; + } ); // Send and receive element attributes - auto redecomp_attribs = MPIArray(&mpi_); - redecomp_attribs.SendRecvEach( - [this](int dest) - { - const auto& elem_idx = p2r_elems_.first[dest]; - auto n_elems = elem_idx.size(); - auto parent_attribs = axom::Array(n_elems, n_elems); - for (int e{0}; e < n_elems; ++e) - { - parent_attribs[e] = parent_.GetElement(elem_idx[e])->GetAttribute(); - } - return parent_attribs; + auto redecomp_attribs = MPIArray( &mpi_ ); + redecomp_attribs.SendRecvEach( [this]( int dest ) { + const auto& elem_idx = p2r_elems_.first[dest]; + auto n_elems = elem_idx.size(); + auto parent_attribs = axom::Array( n_elems, n_elems ); + for ( int e{ 0 }; e < n_elems; ++e ) { + parent_attribs[e] = parent_.GetElement( elem_idx[e] )->GetAttribute(); } - ); + return parent_attribs; + } ); // Count number of elements // NOTE: Don't use NumOfElements. AddElement() will increment it. auto n_els = 0; - for (const auto& redecomp_etype : redecomp_etypes) - { + for ( const auto& redecomp_etype : redecomp_etypes ) { n_els += redecomp_etype.size(); } - elements.SetSize(n_els); + elements.SetSize( n_els ); // Create elements - for (int r{0}; r < n_ranks; ++r) - { + for ( int r{ 0 }; r < n_ranks; ++r ) { auto conn_ct = 0; - for (int e{0}; e < redecomp_etypes[r].size(); ++e) - { - auto el = NewElement(redecomp_etypes[r][e]); - el->SetVertices(&redecomp_conns[r][conn_ct]); - for (int k{0}; k < el->GetNVertices(); ++k) - { + for ( int e{ 0 }; e < redecomp_etypes[r].size(); ++e ) { + auto el = NewElement( redecomp_etypes[r][e] ); + el->SetVertices( &redecomp_conns[r][conn_ct] ); + for ( int k{ 0 }; k < el->GetNVertices(); ++k ) { el->GetVertices()[k] = vert_idx_map[el->GetVertices()[k]]; } conn_ct += el->GetNVertices(); - el->SetAttribute(redecomp_attribs[r][e]); - AddElement(el); + el->SetAttribute( redecomp_attribs[r][e] ); + AddElement( el ); } } // Finalize mesh topology auto generate_boundary = false; - FinalizeTopology(generate_boundary); + FinalizeTopology( generate_boundary ); // Fill r2p_elem_offsets_ with element rank offsets // r2p = redecomp to parent - r2p_elem_offsets_.reserve(n_ranks+1); - r2p_elem_offsets_.resize(n_ranks+1); - mpi_.SendRecvEach(type>(), - [this](int dest) - { - return axom::Array({p2r_elems_.first[dest].size()}); - }, - [this](axom::Array&& recv_data, int source) - { - r2p_elem_offsets_[source+1] = recv_data[0]; - } - ); - for (int i{2}; i < n_ranks+1; ++i) - { - r2p_elem_offsets_[i] += r2p_elem_offsets_[i-1]; + r2p_elem_offsets_.reserve( n_ranks + 1 ); + r2p_elem_offsets_.resize( n_ranks + 1 ); + mpi_.SendRecvEach( + type>(), [this]( int dest ) { return axom::Array( { p2r_elems_.first[dest].size() } ); }, + [this]( axom::Array&& recv_data, int source ) { r2p_elem_offsets_[source + 1] = recv_data[0]; } ); + for ( int i{ 2 }; i < n_ranks + 1; ++i ) { + r2p_elem_offsets_[i] += r2p_elem_offsets_[i - 1]; } // Fill r2p_ghost_elems_ with local indices of ghost elements // r2p = redecomp to parent - r2p_ghost_elems_ = MPIArray(&mpi_); - r2p_ghost_elems_.SendRecvEach( - [this](int dest) - { - auto n_elems = p2r_elems_.second[dest].size(); - auto parent_gelems = axom::Array(0, n_elems); - for (int i{0}; i < n_elems; ++i) - { - if (p2r_elems_.second[dest][i]) - { - parent_gelems.push_back(i); - } + r2p_ghost_elems_ = MPIArray( &mpi_ ); + r2p_ghost_elems_.SendRecvEach( [this]( int dest ) { + auto n_elems = p2r_elems_.second[dest].size(); + auto parent_gelems = axom::Array( 0, n_elems ); + for ( int i{ 0 }; i < n_elems; ++i ) { + if ( p2r_elems_.second[dest][i] ) { + parent_gelems.push_back( i ); } - return parent_gelems; } - ); - for (int i{1}; i < r2p_ghost_elems_.size(); ++i) - { - for (auto& recv_gelem : r2p_ghost_elems_[i]) - { + return parent_gelems; + } ); + for ( int i{ 1 }; i < r2p_ghost_elems_.size(); ++i ) { + for ( auto& recv_gelem : r2p_ghost_elems_[i] ) { recv_gelem += r2p_elem_offsets_[i]; } } // p > 1 case: set mesh as curved Mesh (create Nodes) and transfer Nodes from // parent - auto parent_node_fes = dynamic_cast( - parent_.GetNodalFESpace() - ); - if (parent_node_fes) - { - SetCurvature( - parent_node_fes->FEColl()->GetOrder(), - parent_node_fes->IsDGSpace(), - spaceDim, - parent_node_fes->GetOrdering() - ); + auto parent_node_fes = dynamic_cast( parent_.GetNodalFESpace() ); + if ( parent_node_fes ) { + SetCurvature( parent_node_fes->FEColl()->GetOrder(), parent_node_fes->IsDGSpace(), spaceDim, + parent_node_fes->GetOrdering() ); - auto parent_node_pargf = dynamic_cast( - parent_.GetNodes() - ); - SLIC_ERROR_ROOT_IF(parent_node_pargf == nullptr, - "Nodes in ParMesh parent_ must be a ParGridFunction."); + auto parent_node_pargf = dynamic_cast( parent_.GetNodes() ); + SLIC_ERROR_ROOT_IF( parent_node_pargf == nullptr, "Nodes in ParMesh parent_ must be a ParGridFunction." ); auto node_transfer = RedecompTransfer(); - node_transfer.TransferToSerial(*parent_node_pargf, *Nodes); + node_transfer.TransferToSerial( *parent_node_pargf, *Nodes ); } SetAttributes(); Finalize(); - } // TODO: potentially improve the way this is calculated? -double RedecompMesh::MaxElementSize(const mfem::ParMesh& parent, const MPIUtility& mpi) +double RedecompMesh::MaxElementSize( const mfem::ParMesh& parent, const MPIUtility& mpi ) { auto max_elem_size = 0.0; - for (auto i = 0; i < parent.GetNE(); ++i) - { + for ( auto i = 0; i < parent.GetNE(); ++i ) { // TODO: const version of GetElementSize() NOTE: h_max (type = 2) not // implemented for surface meshes in GetElementSize(). The version called // (type = 0) returns average stretch at the element center - max_elem_size = std::max( - const_cast(parent).GetElementSize(i, 0), - max_elem_size - ); + max_elem_size = std::max( const_cast( parent ).GetElementSize( i, 0 ), max_elem_size ); } - + // Accounts for element diagonal lengths greater than aligned lengths - max_elem_size = max_elem_size*std::sqrt(static_cast(parent.Dimension())); + max_elem_size = max_elem_size * std::sqrt( static_cast( parent.Dimension() ) ); - return mpi.AllreduceValue(max_elem_size, MPI_MAX); + return mpi.AllreduceValue( max_elem_size, MPI_MAX ); } -} // end namespace redecomp +} // end namespace redecomp diff --git a/src/redecomp/RedecompMesh.hpp b/src/redecomp/RedecompMesh.hpp index ae1d89fb..10fe8b02 100644 --- a/src/redecomp/RedecompMesh.hpp +++ b/src/redecomp/RedecompMesh.hpp @@ -12,8 +12,7 @@ #include "redecomp/common/TypeDefs.hpp" -namespace redecomp -{ +namespace redecomp { class Partitioner; @@ -30,13 +29,15 @@ class Partitioner; * @note Though RedecompMesh is distributed across ranks, the RedecompMesh on * each rank is an independent, serial mesh that derives from mfem::Mesh. */ -class RedecompMesh : public mfem::Mesh -{ -public: +class RedecompMesh : public mfem::Mesh { + public: /** * @brief List of partitioning methods verified to work with RedecompMesh */ - enum PartitionType { RCB }; + enum PartitionType + { + RCB + }; /** * @brief Construct a new RedecompMesh object @@ -47,10 +48,7 @@ class RedecompMesh : public mfem::Mesh * @param parent The mfem::ParMesh that will be redecomposed * @param method The method of redecomposition (optional) */ - RedecompMesh( - const mfem::ParMesh& parent, - PartitionType method = RCB - ); + RedecompMesh( const mfem::ParMesh& parent, PartitionType method = RCB ); /** * @brief Construct a new RedecompMesh object @@ -62,11 +60,7 @@ class RedecompMesh : public mfem::Mesh * @param ghost_length Size of layer of un-owned ghost elements to include around the edge of the on-rank domain * @param method The method of redecomposition (optional) */ - RedecompMesh( - const mfem::ParMesh& parent, - double ghost_length, - PartitionType method = RCB - ); + RedecompMesh( const mfem::ParMesh& parent, double ghost_length, PartitionType method = RCB ); /** * @brief Construct a new RedecompMesh object @@ -78,10 +72,7 @@ class RedecompMesh : public mfem::Mesh * @param parent The mfem::ParMesh that will be redecomposed * @param partitioner Partitioning object used to define redecomposition */ - RedecompMesh( - const mfem::ParMesh& parent, - std::unique_ptr partitioner - ); + RedecompMesh( const mfem::ParMesh& parent, std::unique_ptr partitioner ); /** * @brief Construct a new RedecompMesh object @@ -94,11 +85,7 @@ class RedecompMesh : public mfem::Mesh * @param ghost_length Size of layer of un-owned ghost elements to include around the edge of the on-rank domain * @param partitioner Partitioning object used to define redecomposition */ - RedecompMesh( - const mfem::ParMesh& parent, - double ghost_length, - std::unique_ptr partitioner - ); + RedecompMesh( const mfem::ParMesh& parent, double ghost_length, std::unique_ptr partitioner ); /** * @brief Construct a new RedecompMesh object @@ -107,22 +94,19 @@ class RedecompMesh : public mfem::Mesh * @param p2r_elems List of local parent element ids to put on each * RedecompMesh rank */ - RedecompMesh( - const mfem::ParMesh& parent, - EntityIndexByRank&& p2r_elems - ); + RedecompMesh( const mfem::ParMesh& parent, EntityIndexByRank&& p2r_elems ); /** * @brief Get the parent mfem::ParMesh - * - * @return const mfem::ParMesh* + * + * @return const mfem::ParMesh* */ const mfem::ParMesh& getParent() const { return parent_; } /** * @brief Return the MPIUtility - * - * @return const MPIUtility& + * + * @return const MPIUtility& */ const MPIUtility& getMPIUtility() const { return mpi_; } @@ -130,12 +114,9 @@ class RedecompMesh : public mfem::Mesh * @brief Get the list of local parent elements that belong on each rank of * RedecompMesh * - * @return const EntityIndexByRank& + * @return const EntityIndexByRank& */ - const EntityIndexByRank& getParentToRedecompElems() const - { - return p2r_elems_; - } + const EntityIndexByRank& getParentToRedecompElems() const { return p2r_elems_; } /** * @brief Get Redecomp mesh element offsets denoting parent elements on each @@ -146,22 +127,16 @@ class RedecompMesh : public mfem::Mesh * id. The first redecomp element on parent rank m is given by entry m of the * array. * - * @return const axom::Array& + * @return const axom::Array& */ - const axom::Array& getRedecompToParentElemOffsets() const - { - return r2p_elem_offsets_; - } + const axom::Array& getRedecompToParentElemOffsets() const { return r2p_elem_offsets_; } /** * @brief Get a list of redecomp ghost elements on each parent rank - * - * @return const MPIArray& + * + * @return const MPIArray& */ - const MPIArray& getRedecompToParentGhostElems() const - { - return r2p_ghost_elems_; - } + const MPIArray& getRedecompToParentGhostElems() const { return r2p_ghost_elems_; } /** * @brief Computes the largest element length in terms of stretch at the @@ -174,9 +149,9 @@ class RedecompMesh : public mfem::Mesh * * @return Largest element length in terms of stretch component at element centroid */ - static double MaxElementSize(const mfem::ParMesh& parent, const MPIUtility& mpi); + static double MaxElementSize( const mfem::ParMesh& parent, const MPIUtility& mpi ); -private: + private: /** * @brief Computes a default ghost element length: 1.25 * max element size * @@ -184,21 +159,17 @@ class RedecompMesh : public mfem::Mesh * * @return Default ghost element lemgth */ - double DefaultGhostLength(const mfem::ParMesh& parent) const; + double DefaultGhostLength( const mfem::ParMesh& parent ) const; /** * @brief Builds list of parent elements to be transfered to Redecomp ranks - * + * * @param partitioner Method of partitioning the elements * @param n_parts Number of parts to partition the mesh into * @param ghost_length Size of layer of un-owned ghost elements to include around the edge of the on-rank domain * @return List of parent element IDs and ghost elements sorted by Redecomp rank */ - EntityIndexByRank BuildP2RElementList( - const Partitioner& partitioner, - int n_parts, - double ghost_length - ) const; + EntityIndexByRank BuildP2RElementList( const Partitioner& partitioner, int n_parts, double ghost_length ) const; /** * @brief Builds the Redecomp mesh and inverse element transfer list @@ -206,12 +177,12 @@ class RedecompMesh : public mfem::Mesh void BuildRedecomp(); /** - * @brief Linked parent mfem::ParMesh + * @brief Linked parent mfem::ParMesh */ const mfem::ParMesh& parent_; /** - * @brief MPI utility for the Redecomp MPI_Comm + * @brief MPI utility for the Redecomp MPI_Comm */ MPIUtility mpi_; @@ -226,11 +197,11 @@ class RedecompMesh : public mfem::Mesh axom::Array r2p_elem_offsets_; /** - * @brief Ghost redecomp elements sorted by parent rank + * @brief Ghost redecomp elements sorted by parent rank */ MPIArray r2p_ghost_elems_; }; -} +} // namespace redecomp #endif /* SRC_REDECOMP_REDECOMPMESH_HPP_ */ diff --git a/src/redecomp/RedecompTransfer.cpp b/src/redecomp/RedecompTransfer.cpp index 2a7305e5..bd25a3da 100644 --- a/src/redecomp/RedecompTransfer.cpp +++ b/src/redecomp/RedecompTransfer.cpp @@ -11,157 +11,123 @@ #include "redecomp/transfer/TransferByNodes.hpp" #include "redecomp/transfer/TransferByElements.hpp" -namespace redecomp -{ +namespace redecomp { -RedecompTransfer::RedecompTransfer( - std::unique_ptr gf_transfer -) -: gf_transfer_ { std::move(gf_transfer) } {} +RedecompTransfer::RedecompTransfer( std::unique_ptr gf_transfer ) + : gf_transfer_{ std::move( gf_transfer ) } +{ +} -RedecompTransfer::RedecompTransfer( - const mfem::ParFiniteElementSpace& parent_fes, - const mfem::FiniteElementSpace& redecomp_fes -) -: RedecompTransfer(std::make_unique(parent_fes, redecomp_fes)) {} +RedecompTransfer::RedecompTransfer( const mfem::ParFiniteElementSpace& parent_fes, + const mfem::FiniteElementSpace& redecomp_fes ) + : RedecompTransfer( std::make_unique( parent_fes, redecomp_fes ) ) +{ +} -RedecompTransfer::RedecompTransfer() -: RedecompTransfer(std::make_unique()) {} +RedecompTransfer::RedecompTransfer() : RedecompTransfer( std::make_unique() ) {} -void RedecompTransfer::TransferToSerial( - const mfem::ParGridFunction& src, - mfem::GridFunction& dst -) const +void RedecompTransfer::TransferToSerial( const mfem::ParGridFunction& src, mfem::GridFunction& dst ) const { - gf_transfer_->TransferToSerial(src, dst); + gf_transfer_->TransferToSerial( src, dst ); } -void RedecompTransfer::TransferToParallel( - const mfem::GridFunction& src, - mfem::ParGridFunction& dst -) const +void RedecompTransfer::TransferToParallel( const mfem::GridFunction& src, mfem::ParGridFunction& dst ) const { - gf_transfer_->TransferToParallel(src, dst); + gf_transfer_->TransferToParallel( src, dst ); } -void RedecompTransfer::TransferToSerial( - const mfem::QuadratureFunction& src, - mfem::QuadratureFunction& dst -) const +void RedecompTransfer::TransferToSerial( const mfem::QuadratureFunction& src, mfem::QuadratureFunction& dst ) const { // checks to make sure src and dst are valid - auto redecomp = dynamic_cast(dst.GetSpace()->GetMesh()); - SLIC_ERROR_ROOT_IF(redecomp == nullptr, - "The Mesh of QuadratureFunction dst must be a Redecomp mesh."); - SLIC_ERROR_ROOT_IF(src.GetSpace()->GetMesh() != &redecomp->getParent(), - "The Meshes of the specified QuadratureFunctions are not related in a " - "Redecomp -> ParMesh relationship."); + auto redecomp = dynamic_cast( dst.GetSpace()->GetMesh() ); + SLIC_ERROR_ROOT_IF( redecomp == nullptr, "The Mesh of QuadratureFunction dst must be a Redecomp mesh." ); + SLIC_ERROR_ROOT_IF( src.GetSpace()->GetMesh() != &redecomp->getParent(), + "The Meshes of the specified QuadratureFunctions are not related in a " + "Redecomp -> ParMesh relationship." ); // send and receive quadrature point values from other ranks - auto dst_vals = MPIArray(&redecomp->getMPIUtility()); - dst_vals.SendRecvEach( - [redecomp, &src](int dest) - { - auto src_vals = axom::Array(); - const auto& src_elem_idx = redecomp->getParentToRedecompElems().first[dest]; - auto n_els = src_elem_idx.size(); - if (n_els > 0) - { - auto vals = mfem::Vector(); - // guess the size of send_vals based on the size of the first element - src.GetValues(src_elem_idx[0], vals); - src_vals.reserve(vals.Size()*n_els); - auto quadpt_ct = 0; - for (auto src_elem_id : src_elem_idx) - { - src.GetValues(src_elem_id, vals); - src_vals.insert(quadpt_ct, vals.Size(), vals.GetData()); - quadpt_ct += vals.Size(); - } + auto dst_vals = MPIArray( &redecomp->getMPIUtility() ); + dst_vals.SendRecvEach( [redecomp, &src]( int dest ) { + auto src_vals = axom::Array(); + const auto& src_elem_idx = redecomp->getParentToRedecompElems().first[dest]; + auto n_els = src_elem_idx.size(); + if ( n_els > 0 ) { + auto vals = mfem::Vector(); + // guess the size of send_vals based on the size of the first element + src.GetValues( src_elem_idx[0], vals ); + src_vals.reserve( vals.Size() * n_els ); + auto quadpt_ct = 0; + for ( auto src_elem_id : src_elem_idx ) { + src.GetValues( src_elem_id, vals ); + src_vals.insert( quadpt_ct, vals.Size(), vals.GetData() ); + quadpt_ct += vals.Size(); } - return src_vals; } - ); + return src_vals; + } ); // map received quadrature point values to local quadrature points auto vals = mfem::Vector(); auto n_ranks = redecomp->getMPIUtility().NRanks(); - for (int r{0}; r < n_ranks; ++r) - { + for ( int r{ 0 }; r < n_ranks; ++r ) { auto first_el = redecomp->getRedecompToParentElemOffsets()[r]; - auto last_el = redecomp->getRedecompToParentElemOffsets()[r+1]; + auto last_el = redecomp->getRedecompToParentElemOffsets()[r + 1]; auto quadpt_ct = 0; - for (int e{first_el}; e < last_el; ++e) - { - dst.GetValues(e, vals); + for ( int e{ first_el }; e < last_el; ++e ) { + dst.GetValues( e, vals ); vals = &dst_vals[r][quadpt_ct]; quadpt_ct += vals.Size(); } } } -void RedecompTransfer::TransferToParallel( - const mfem::QuadratureFunction& src, - mfem::QuadratureFunction& dst -) const +void RedecompTransfer::TransferToParallel( const mfem::QuadratureFunction& src, mfem::QuadratureFunction& dst ) const { // checks to make sure src and dst are valid - auto redecomp = dynamic_cast(src.GetSpace()->GetMesh()); - SLIC_ERROR_ROOT_IF(redecomp == nullptr, - "The Mesh of QuadratureFunction src must be a Redecomp mesh."); - SLIC_ERROR_ROOT_IF(dst.GetSpace()->GetMesh() != &redecomp->getParent(), - "The Meshes of the specified QuadratureFunctions are not related in a " - "Redecomp -> ParMesh relationship."); + auto redecomp = dynamic_cast( src.GetSpace()->GetMesh() ); + SLIC_ERROR_ROOT_IF( redecomp == nullptr, "The Mesh of QuadratureFunction src must be a Redecomp mesh." ); + SLIC_ERROR_ROOT_IF( dst.GetSpace()->GetMesh() != &redecomp->getParent(), + "The Meshes of the specified QuadratureFunctions are not related in a " + "Redecomp -> ParMesh relationship." ); // send and receive quadrature point values from other ranks - auto dst_vals = MPIArray(&redecomp->getMPIUtility()); - dst_vals.SendRecvEach( - [redecomp, &src](int dest) - { - auto src_vals = axom::Array(); - auto first_el = redecomp->getRedecompToParentElemOffsets()[dest]; - auto last_el = redecomp->getRedecompToParentElemOffsets()[dest+1]; - auto n_els = last_el - first_el; - if (n_els > 0) - { - auto vals = mfem::Vector(); - // guess the size of send_vals based on the size of the first element - src.GetValues(first_el, vals); - src_vals.reserve(vals.Size()*n_els); - auto quadpt_ct = 0; - auto ghost_ct = 0; - for (int e{first_el}; e < last_el; ++e) - { - // skip ghost elements - if (ghost_ct < redecomp->getRedecompToParentGhostElems()[dest].size() && - redecomp->getRedecompToParentGhostElems()[dest][ghost_ct] == e) - { - ++ghost_ct; - } - else - { - src.GetValues(e, vals); - src_vals.insert(quadpt_ct, vals.Size(), vals.GetData()); - quadpt_ct += vals.Size(); - } + auto dst_vals = MPIArray( &redecomp->getMPIUtility() ); + dst_vals.SendRecvEach( [redecomp, &src]( int dest ) { + auto src_vals = axom::Array(); + auto first_el = redecomp->getRedecompToParentElemOffsets()[dest]; + auto last_el = redecomp->getRedecompToParentElemOffsets()[dest + 1]; + auto n_els = last_el - first_el; + if ( n_els > 0 ) { + auto vals = mfem::Vector(); + // guess the size of send_vals based on the size of the first element + src.GetValues( first_el, vals ); + src_vals.reserve( vals.Size() * n_els ); + auto quadpt_ct = 0; + auto ghost_ct = 0; + for ( int e{ first_el }; e < last_el; ++e ) { + // skip ghost elements + if ( ghost_ct < redecomp->getRedecompToParentGhostElems()[dest].size() && + redecomp->getRedecompToParentGhostElems()[dest][ghost_ct] == e ) { + ++ghost_ct; + } else { + src.GetValues( e, vals ); + src_vals.insert( quadpt_ct, vals.Size(), vals.GetData() ); + quadpt_ct += vals.Size(); } } - return src_vals; } - ); + return src_vals; + } ); // map received quadrature point values to local quadrature points auto vals = mfem::Vector(); auto n_ranks = redecomp->getMPIUtility().NRanks(); - for (int r{0}; r < n_ranks; ++r) - { + for ( int r{ 0 }; r < n_ranks; ++r ) { auto quadpt_ct = 0; - for (int e{0}; e < redecomp->getParentToRedecompElems().first[r].size(); ++e) - { + for ( int e{ 0 }; e < redecomp->getParentToRedecompElems().first[r].size(); ++e ) { // skip ghost elements - if (!redecomp->getParentToRedecompElems().second[r][e]) - { - dst.GetValues(redecomp->getParentToRedecompElems().first[r][e], vals); + if ( !redecomp->getParentToRedecompElems().second[r][e] ) { + dst.GetValues( redecomp->getParentToRedecompElems().first[r][e], vals ); vals = &dst_vals[r][quadpt_ct]; quadpt_ct += vals.Size(); } @@ -169,4 +135,4 @@ void RedecompTransfer::TransferToParallel( } } -} // end namespace redecomp +} // end namespace redecomp diff --git a/src/redecomp/RedecompTransfer.hpp b/src/redecomp/RedecompTransfer.hpp index 03a1dd5c..7f5415b8 100644 --- a/src/redecomp/RedecompTransfer.hpp +++ b/src/redecomp/RedecompTransfer.hpp @@ -10,26 +10,22 @@ #include "redecomp/transfer/GridFnTransfer.hpp" -namespace redecomp -{ +namespace redecomp { /** * @brief Transfer GridFunctions and QuadratureFunctions to/from Redecomp - * + * * Maps Redecomp-based mfem::GridFunctions and mfem::QuadratureFunctions to and * from a parent mfem::ParGridFunction. */ -class RedecompTransfer -{ -public: +class RedecompTransfer { + public: /** * @brief Construct a new RedecompTransfer object - * + * * @param gf_transfer A pointer to a custom GridFnTransfer object */ - RedecompTransfer( - std::unique_ptr gf_transfer - ); + RedecompTransfer( std::unique_ptr gf_transfer ); /** * @brief Construct a new RedecompTransfer object with nodal GridFunction @@ -42,10 +38,7 @@ class RedecompTransfer * @param parent_fes A ParFiniteElementSpace built on parent * @param redecomp_fes A FiniteElementSpace built on a RedecompMesh */ - RedecompTransfer( - const mfem::ParFiniteElementSpace& parent_fes, - const mfem::FiniteElementSpace& redecomp_fes - ); + RedecompTransfer( const mfem::ParFiniteElementSpace& parent_fes, const mfem::FiniteElementSpace& redecomp_fes ); /** * @brief Construct a new RedecompTransfer object with element GridFunction transfer @@ -61,10 +54,7 @@ class RedecompTransfer * @param dst A redecomp GridFunction which receives values from a parent * ParGridFunction (src) */ - void TransferToSerial( - const mfem::ParGridFunction& src, - mfem::GridFunction& dst - ) const; + void TransferToSerial( const mfem::ParGridFunction& src, mfem::GridFunction& dst ) const; /** * @brief Copies RedecompMesh-based mfem::GridFunction values to a @@ -75,24 +65,18 @@ class RedecompTransfer * @param dst A parent ParGridFunction which receives values from a redecomp * GridFunction (src) */ - void TransferToParallel( - const mfem::GridFunction& src, - mfem::ParGridFunction& dst - ) const; + void TransferToParallel( const mfem::GridFunction& src, mfem::ParGridFunction& dst ) const; /** - * @brief Copies parent-based mfem::QuadratureFunction values to a - * RedecompMesh-based mfem::QuadratureFunction + * @brief Copies parent-based mfem::QuadratureFunction values to a + * RedecompMesh-based mfem::QuadratureFunction * * @param src A parent QuadratureFunction to be copied to corresponding - * redecomp QuadratureFunction (dst) + * redecomp QuadratureFunction (dst) * @param dst A redecomp QuadratureFunction which receives values from a - * parent QuadratureFunction (src) + * parent QuadratureFunction (src) */ - void TransferToSerial( - const mfem::QuadratureFunction& src, - mfem::QuadratureFunction& dst - ) const; + void TransferToSerial( const mfem::QuadratureFunction& src, mfem::QuadratureFunction& dst ) const; /** * @brief Copies RedecompMesh-based mfem::QuadratureFunction values to a @@ -103,18 +87,15 @@ class RedecompTransfer * @param dst A parent QuadratureFunction which receives values from a * redecomp GridFunction (src) */ - void TransferToParallel( - const mfem::QuadratureFunction& src, - mfem::QuadratureFunction& dst - ) const; + void TransferToParallel( const mfem::QuadratureFunction& src, mfem::QuadratureFunction& dst ) const; -private: + private: /** * @brief Grid function transfer object */ std::unique_ptr gf_transfer_; }; -} // end namespace redecomp +} // end namespace redecomp #endif /* SRC_REDECOMP_REDECOMPTRANSFER_HPP_ */ diff --git a/src/redecomp/common/TypeDefs.hpp b/src/redecomp/common/TypeDefs.hpp index ecc3bc7d..635172c9 100644 --- a/src/redecomp/common/TypeDefs.hpp +++ b/src/redecomp/common/TypeDefs.hpp @@ -13,25 +13,24 @@ #include "redecomp/utils/MPIArray.hpp" -namespace redecomp -{ - constexpr double pi = M_PI; - - template - using Point = axom::primal::Point; - - template - using BoundingBox = axom::primal::BoundingBox; - - /** - * @brief In its usage in redecomp, the first pair entry contains a list of - * entity (node or element) indices per rank. The second pair entry is sized - * the same as the first pair entry, and if the entry is true, it denotes the - * entity is in the ghost layer on a corresponding RedecompMesh. This data - * structure lists entities that need to be sent to or are received from other - * MPI ranks. - */ - using EntityIndexByRank = std::pair, MPIArray>; -} // end namespace redecomp +namespace redecomp { +constexpr double pi = M_PI; + +template +using Point = axom::primal::Point; + +template +using BoundingBox = axom::primal::BoundingBox; + +/** + * @brief In its usage in redecomp, the first pair entry contains a list of + * entity (node or element) indices per rank. The second pair entry is sized + * the same as the first pair entry, and if the entry is true, it denotes the + * entity is in the ghost layer on a corresponding RedecompMesh. This data + * structure lists entities that need to be sent to or are received from other + * MPI ranks. + */ +using EntityIndexByRank = std::pair, MPIArray>; +} // end namespace redecomp #endif /* SRC_REDECOMP_COMMON_TYPEDEFS_HPP_ */ diff --git a/src/redecomp/partition/PartitionElements.cpp b/src/redecomp/partition/PartitionElements.cpp index 364cb5fc..d27151b6 100644 --- a/src/redecomp/partition/PartitionElements.cpp +++ b/src/redecomp/partition/PartitionElements.cpp @@ -5,25 +5,21 @@ #include "PartitionElements.hpp" -namespace redecomp -{ +namespace redecomp { template std::vector>> PartitionElements::EntityCoordinates( - const std::vector& par_meshes -) const + const std::vector& par_meshes ) const { auto elem_centroids = std::vector>>(); - elem_centroids.reserve(par_meshes.size()); - for (auto par_mesh : par_meshes) - { + elem_centroids.reserve( par_meshes.size() ); + for ( auto par_mesh : par_meshes ) { auto n_elems = par_mesh->GetNE(); - elem_centroids.emplace_back(n_elems, n_elems); - for (int i{0}; i < n_elems; ++i) - { - auto vec_centroid = mfem::Vector(elem_centroids.back()[i].data(), NDIMS); + elem_centroids.emplace_back( n_elems, n_elems ); + for ( int i{ 0 }; i < n_elems; ++i ) { + auto vec_centroid = mfem::Vector( elem_centroids.back()[i].data(), NDIMS ); // TODO: const version of GetElementCenter() - const_cast(par_mesh)->GetElementCenter(i, vec_centroid); + const_cast( par_mesh )->GetElementCenter( i, vec_centroid ); } } return elem_centroids; @@ -32,4 +28,4 @@ std::vector>> PartitionElements::EntityCoordinat template class PartitionElements<2>; template class PartitionElements<3>; -} // end namespace redecomp +} // end namespace redecomp diff --git a/src/redecomp/partition/PartitionElements.hpp b/src/redecomp/partition/PartitionElements.hpp index dad35c40..e887c727 100644 --- a/src/redecomp/partition/PartitionElements.hpp +++ b/src/redecomp/partition/PartitionElements.hpp @@ -8,32 +8,29 @@ #include "redecomp/partition/PartitionEntity.hpp" -namespace redecomp -{ +namespace redecomp { /** * @brief PartitionEntity class for elements - * + * * @tparam NDIMS number of dimensions */ template -class PartitionElements : public PartitionEntity -{ -public: +class PartitionElements : public PartitionEntity { + public: /** * @brief Returns lists of entity coordinates (points) from the par_meshes, sorted by mesh - * + * * @param par_meshes Input meshes * @return Vector of array of points from each entity on each mesh */ std::vector>> EntityCoordinates( - const std::vector& par_meshes - ) const override; + const std::vector& par_meshes ) const override; }; using PartitionElements2D = PartitionElements<2>; using PartitionElements3D = PartitionElements<3>; -} // end namespace redecomp +} // end namespace redecomp #endif /* SRC_REDECOMP_PARTITION_PARTITIONELEMENTS_HPP_ */ diff --git a/src/redecomp/partition/PartitionEntity.hpp b/src/redecomp/partition/PartitionEntity.hpp index 60d2bd9d..3bd56377 100644 --- a/src/redecomp/partition/PartitionEntity.hpp +++ b/src/redecomp/partition/PartitionEntity.hpp @@ -12,8 +12,7 @@ #include "redecomp/common/TypeDefs.hpp" -namespace redecomp -{ +namespace redecomp { /** * @brief PartitionEntity interface class @@ -26,29 +25,26 @@ namespace redecomp * @tparam NDIMS number of dimensions */ template -class PartitionEntity -{ -public: +class PartitionEntity { + public: /** * @brief Returns lists of entity coordinates (points) from the par_meshes, sorted by mesh - * + * * @param par_meshes Input meshes * @return Vector of array of points from each entity on each mesh */ virtual std::vector>> EntityCoordinates( - const std::vector& par_meshes - ) const = 0; + const std::vector& par_meshes ) const = 0; /** * @brief Destroy the PartitionEntity object */ virtual ~PartitionEntity() = default; - }; using PartitionEntity2D = PartitionEntity<2>; using PartitionEntity3D = PartitionEntity<3>; -} // end namespace redecomp +} // end namespace redecomp #endif /* SRC_REDECOMP_PARTITION_PARTITIONENTITY_HPP_ */ diff --git a/src/redecomp/partition/PartitionMethod.cpp b/src/redecomp/partition/PartitionMethod.cpp index 6896a6ac..681e05d5 100644 --- a/src/redecomp/partition/PartitionMethod.cpp +++ b/src/redecomp/partition/PartitionMethod.cpp @@ -5,13 +5,12 @@ #include "PartitionMethod.hpp" -namespace redecomp -{ +namespace redecomp { template -PartitionMethod::PartitionMethod(const MPI_Comm& comm) -: mpi_ { comm } -{} +PartitionMethod::PartitionMethod( const MPI_Comm& comm ) : mpi_{ comm } +{ +} template const MPIUtility& PartitionMethod::getMPIUtility() const @@ -22,4 +21,4 @@ const MPIUtility& PartitionMethod::getMPIUtility() const template class PartitionMethod<2>; template class PartitionMethod<3>; -} // end namespace redecomp +} // end namespace redecomp diff --git a/src/redecomp/partition/PartitionMethod.hpp b/src/redecomp/partition/PartitionMethod.hpp index fc3ebaa5..10550a5a 100644 --- a/src/redecomp/partition/PartitionMethod.hpp +++ b/src/redecomp/partition/PartitionMethod.hpp @@ -8,8 +8,7 @@ #include "redecomp/common/TypeDefs.hpp" -namespace redecomp -{ +namespace redecomp { /** * @brief PartitionMethod base class @@ -23,9 +22,8 @@ namespace redecomp * @tparam NDIMS number of dimensions */ template -class PartitionMethod -{ -public: +class PartitionMethod { + public: /** * @brief Returns a list of entity ids on each rank/subdomain determined by * the partitioning method @@ -39,14 +37,11 @@ class PartitionMethod * by mesh */ virtual std::vector generatePartitioning( - int n_parts, - const std::vector>>& coords_by_mesh, - double ghost_size - ) const = 0; - + int n_parts, const std::vector>>& coords_by_mesh, double ghost_size ) const = 0; + /** * @brief Returns the MPIUtility - * + * * @return MPIUtility reference */ const MPIUtility& getMPIUtility() const; @@ -56,25 +51,24 @@ class PartitionMethod */ virtual ~PartitionMethod() = default; -protected: + protected: /** * @brief Construct a new PartitionMethod object - * + * * @param comm MPI_Comm to used to build MPIUtility */ - PartitionMethod(const MPI_Comm& comm); + PartitionMethod( const MPI_Comm& comm ); -private: + private: /** * @brief MPIUtility used to hold MPI communication patterns used in redecomp */ const MPIUtility mpi_; - }; using PartitionMethod2D = PartitionMethod<2>; using PartitionMethod3D = PartitionMethod<3>; -} // end namespace redecomp +} // end namespace redecomp #endif /* SRC_REDECOMP_PARTITION_PARTITIONMETHOD_HPP_ */ diff --git a/src/redecomp/partition/Partitioner.cpp b/src/redecomp/partition/Partitioner.cpp index 7e66d2a3..aecd5a17 100644 --- a/src/redecomp/partition/Partitioner.cpp +++ b/src/redecomp/partition/Partitioner.cpp @@ -8,17 +8,14 @@ #include "redecomp/partition/PartitionMethod.hpp" #include "redecomp/partition/PartitionEntity.hpp" -namespace redecomp -{ +namespace redecomp { template -PartitionerByDim::PartitionerByDim( - std::unique_ptr> partition_entity, - std::unique_ptr> partition_method -) -: partition_entity_ { std::move(partition_entity) }, - partition_method_ { std::move(partition_method) } -{} +PartitionerByDim::PartitionerByDim( std::unique_ptr> partition_entity, + std::unique_ptr> partition_method ) + : partition_entity_{ std::move( partition_entity ) }, partition_method_{ std::move( partition_method ) } +{ +} template const MPIUtility& PartitionerByDim::getMPIUtility() const @@ -28,16 +25,10 @@ const MPIUtility& PartitionerByDim::getMPIUtility() const template std::vector PartitionerByDim::generatePartitioning( - int n_parts, - const std::vector& par_meshes, - double ghost_size -) const + int n_parts, const std::vector& par_meshes, double ghost_size ) const { - return partition_method_->generatePartitioning( - n_parts, - partition_entity_->EntityCoordinates(par_meshes), - ghost_size - ); + return partition_method_->generatePartitioning( n_parts, partition_entity_->EntityCoordinates( par_meshes ), + ghost_size ); } template @@ -55,4 +46,4 @@ const PartitionMethod* PartitionerByDim::getPartitionMethod() cons template class PartitionerByDim<2>; template class PartitionerByDim<3>; -} // end namespace redecomp +} // end namespace redecomp diff --git a/src/redecomp/partition/Partitioner.hpp b/src/redecomp/partition/Partitioner.hpp index 905a0303..eb002284 100644 --- a/src/redecomp/partition/Partitioner.hpp +++ b/src/redecomp/partition/Partitioner.hpp @@ -12,32 +12,29 @@ #include "redecomp/common/TypeDefs.hpp" -namespace redecomp -{ +namespace redecomp { /** - * @brief Partitioner interface class + * @brief Partitioner interface class */ -class Partitioner -{ -public: +class Partitioner { + public: /** * @brief Partitions entities in all par_meshes into n_parts pieces - * + * * @param n_parts Number of subdomains to cut par_mesh entities into * @param par_meshes Original meshes - * @param ghost_size Entities within ghost_size distance from the edge of each subdomain are included in the subdomain as a ghost entity + * @param ghost_size Entities within ghost_size distance from the edge of each subdomain are included in the subdomain + * as a ghost entity * @return vector of EntityIndexByRank; lists of entities and ghost entities sorted by each subdomain */ - virtual std::vector generatePartitioning( - int n_parts, - const std::vector& par_meshes, - double ghost_size - ) const = 0; - + virtual std::vector generatePartitioning( int n_parts, + const std::vector& par_meshes, + double ghost_size ) const = 0; + /** * @brief Returns the MPIUtility associated with the Partitioner - * + * * @return MPIUtility pointer */ virtual const MPIUtility& getMPIUtility() const = 0; @@ -46,7 +43,6 @@ class Partitioner * @brief Destroy the Partitioner object */ virtual ~Partitioner() = default; - }; template @@ -61,23 +57,20 @@ class PartitionMethod; * @tparam NDIMS number of dimensions */ template -class PartitionerByDim : public Partitioner -{ -public: +class PartitionerByDim : public Partitioner { + public: /** * @brief Construct a new PartitionerByDim object - * + * * @param partition_entity Pointer to PartitionEntity member * @param partition_method Pointer to a PartitionMethod member */ - PartitionerByDim( - std::unique_ptr> partition_entity, - std::unique_ptr> partition_method - ); + PartitionerByDim( std::unique_ptr> partition_entity, + std::unique_ptr> partition_method ); /** * @brief Returns the MPIUtility associated with the Partitioner - * + * * @return MPIUtility pointer */ const MPIUtility& getMPIUtility() const override; @@ -92,27 +85,24 @@ class PartitionerByDim : public Partitioner * @return vector of EntityIndexByRank; lists of entities and ghost entities * sorted by each subdomain */ - std::vector generatePartitioning( - int n_parts, - const std::vector& par_meshes, - double ghost_size - ) const override; + std::vector generatePartitioning( int n_parts, const std::vector& par_meshes, + double ghost_size ) const override; /** * @brief Get the PartitionEntity object - * + * * @return PartitionEntity pointer */ const PartitionEntity* getPartitionEntity() const; /** * @brief Get the PartitionMethod object - * + * * @return PartitionMethod pointer */ const PartitionMethod* getPartitionMethod() const; -private: + private: /** * @brief Owned pointer to a PartitionEntity object * @@ -123,7 +113,7 @@ class PartitionerByDim : public Partitioner * */ const std::unique_ptr> partition_entity_; - + /** * @brief Owned pointer to a PartitionMethod object * @@ -134,12 +124,11 @@ class PartitionerByDim : public Partitioner * */ const std::unique_ptr> partition_method_; - }; using Partitioner2D = PartitionerByDim<2>; using Partitioner3D = PartitionerByDim<3>; -} // end namespace redecomp +} // end namespace redecomp #endif /* SRC_REDECOMP_PARTITION_PARTITIONER_HPP_ */ diff --git a/src/redecomp/partition/RCB.cpp b/src/redecomp/partition/RCB.cpp index 4c8923a2..43d91e6a 100644 --- a/src/redecomp/partition/RCB.cpp +++ b/src/redecomp/partition/RCB.cpp @@ -11,99 +11,83 @@ #include "redecomp/utils/ArrayUtility.hpp" #include "redecomp/utils/MPIUtility.hpp" -namespace redecomp -{ +namespace redecomp { template -RCB::RCB(const MPI_Comm& comm, double max_out_of_balance, int n_try_new_axis) -: PartitionMethod { comm }, - max_out_of_balance_ { max_out_of_balance }, - n_try_new_axis_ { n_try_new_axis } -{} +RCB::RCB( const MPI_Comm& comm, double max_out_of_balance, int n_try_new_axis ) + : PartitionMethod{ comm }, max_out_of_balance_{ max_out_of_balance }, n_try_new_axis_{ n_try_new_axis } +{ +} template std::vector RCB::generatePartitioning( - int n_parts, - const std::vector>>& coords_by_mesh, - double ghost_len -) const + int n_parts, const std::vector>>& coords_by_mesh, double ghost_len ) const { auto partitioning = std::vector(); - partitioning.reserve(coords_by_mesh.size()); + partitioning.reserve( coords_by_mesh.size() ); // Build a partitioning using recursive coordinate bisection - auto problem_tree = BuildProblemTree(n_parts, coords_by_mesh, ghost_len); + auto problem_tree = BuildProblemTree( n_parts, coords_by_mesh, ghost_len ); - for (const auto& coords : coords_by_mesh) - { + for ( const auto& coords : coords_by_mesh ) { // Build a list of coords that belong in each of the RCB entity parts - auto ent_idx = MPIArray(&this->getMPIUtility()); - auto ent_ghost = MPIArray(&this->getMPIUtility()); - for (int i{0}; i < n_parts; ++i) - { - ent_idx[i].reserve(coords.size()); - ent_ghost[i].reserve(coords.size()); + auto ent_idx = MPIArray( &this->getMPIUtility() ); + auto ent_ghost = MPIArray( &this->getMPIUtility() ); + for ( int i{ 0 }; i < n_parts; ++i ) { + ent_idx[i].reserve( coords.size() ); + ent_ghost[i].reserve( coords.size() ); } - for (int i{0}; i < coords.size(); ++i) - { - auto dest = DetermineDomain(problem_tree, coords[i]); - ent_idx[dest].push_back(i); - ent_ghost[dest].push_back(false); - const auto& neighbors = problem_tree(dest).neighbor_bboxes_; - for (int j{0}; j < neighbors.size(); ++j) - { - if (problem_tree(neighbors[j]).ghost_bbox_.contains(coords[i])) - { - ent_idx[neighbors[j]].push_back(i); - ent_ghost[neighbors[j]].push_back(true); + for ( int i{ 0 }; i < coords.size(); ++i ) { + auto dest = DetermineDomain( problem_tree, coords[i] ); + ent_idx[dest].push_back( i ); + ent_ghost[dest].push_back( false ); + const auto& neighbors = problem_tree( dest ).neighbor_bboxes_; + for ( int j{ 0 }; j < neighbors.size(); ++j ) { + if ( problem_tree( neighbors[j] ).ghost_bbox_.contains( coords[i] ) ) { + ent_idx[neighbors[j]].push_back( i ); + ent_ghost[neighbors[j]].push_back( true ); } } } - for (int i{0}; i < n_parts; ++i) - { + for ( int i{ 0 }; i < n_parts; ++i ) { ent_idx[i].shrink(); ent_ghost[i].shrink(); } - partitioning.emplace_back(std::move(ent_idx), std::move(ent_ghost)); + partitioning.emplace_back( std::move( ent_idx ), std::move( ent_ghost ) ); } return partitioning; } template -BisecTree> RCB::BuildProblemTree( - int n_parts, - const std::vector>>& coords_by_mesh, - double ghost_len) const +BisecTree> RCB::BuildProblemTree( int n_parts, + const std::vector>>& coords_by_mesh, + double ghost_len ) const { // subdivide the domain into n_parts pieces. create a bisection tree of the // domain so we can focus on one piece at a time. - auto problem_tree = BisecTree>(n_parts); + auto problem_tree = BisecTree>( n_parts ); // store the desired fraction of each piece in the lowest level of the tree { - auto base_problem_frac = 1.0 / static_cast(n_parts); + auto base_problem_frac = 1.0 / static_cast( n_parts ); auto lvl_it = --problem_tree.end(); - for (auto node_it = problem_tree.begin(lvl_it); node_it != problem_tree.end(lvl_it); ++node_it) - { - (*node_it).desired_frac_ = base_problem_frac; + for ( auto node_it = problem_tree.begin( lvl_it ); node_it != problem_tree.end( lvl_it ); ++node_it ) { + ( *node_it ).desired_frac_ = base_problem_frac; } // define desired_frac_ for each node by summing up the children // (reverse breadth-first traversal). should sum to 1 for the root node. - for ( ; lvl_it != problem_tree.begin(); ) - { + for ( ; lvl_it != problem_tree.begin(); ) { --lvl_it; - for (auto node_it = problem_tree.begin(lvl_it); node_it != problem_tree.end(lvl_it); ++node_it) - { + for ( auto node_it = problem_tree.begin( lvl_it ); node_it != problem_tree.end( lvl_it ); ++node_it ) { // there always will be a left child - (*node_it).desired_frac_ += (*problem_tree.left(lvl_it, node_it).second).desired_frac_; + ( *node_it ).desired_frac_ += ( *problem_tree.left( lvl_it, node_it ).second ).desired_frac_; // there may not be a right child - auto right_it = problem_tree.right(lvl_it, node_it); - if (right_it.second != problem_tree.end(right_it.first)) - { - (*node_it).desired_frac_ += (*right_it.second).desired_frac_; + auto right_it = problem_tree.right( lvl_it, node_it ); + if ( right_it.second != problem_tree.end( right_it.first ) ) { + ( *node_it ).desired_frac_ += ( *right_it.second ).desired_frac_; } } } @@ -111,230 +95,198 @@ BisecTree> RCB::BuildProblemTree( // compute total number of entities in the domain auto total_ents = 0; - for (const auto& coords : coords_by_mesh) - { + for ( const auto& coords : coords_by_mesh ) { total_ents += coords.size(); } - total_ents = TotalEntities(total_ents); + total_ents = TotalEntities( total_ents ); // construct an AABB of the whole domain in the root node - (*problem_tree.begin())[0].bbox_ = DomainBoundingBox(coords_by_mesh); + ( *problem_tree.begin() )[0].bbox_ = DomainBoundingBox( coords_by_mesh ); // start splitting. overview of the nested loops: // 1) breadth-first traversal of the bisection tree // 2) coordinate-axis of the cut (in case the nodes are aligned along an axis) // 3) cut coordinate - for (auto lvl_it = problem_tree.begin(); lvl_it != --problem_tree.end(); ++lvl_it) - { - for (auto node_it = problem_tree.begin(lvl_it); node_it != problem_tree.end(lvl_it); ++node_it) - { - auto left_it = problem_tree.left(lvl_it, node_it); - auto right_it = problem_tree.right(lvl_it, node_it); + for ( auto lvl_it = problem_tree.begin(); lvl_it != --problem_tree.end(); ++lvl_it ) { + for ( auto node_it = problem_tree.begin( lvl_it ); node_it != problem_tree.end( lvl_it ); ++node_it ) { + auto left_it = problem_tree.left( lvl_it, node_it ); + auto right_it = problem_tree.right( lvl_it, node_it ); // loop shortcut if no right child exists - if (right_it.second == problem_tree.end(right_it.first)) - { - (*left_it.second).bbox_ = (*node_it).bbox_; - (*left_it.second).actual_frac_ = (*node_it).actual_frac_; - (*left_it.second).ghost_bbox_ = (*node_it).ghost_bbox_; + if ( right_it.second == problem_tree.end( right_it.first ) ) { + ( *left_it.second ).bbox_ = ( *node_it ).bbox_; + ( *left_it.second ).actual_frac_ = ( *node_it ).actual_frac_; + ( *left_it.second ).ghost_bbox_ = ( *node_it ).ghost_bbox_; continue; } // compute proportion of node fractions - auto left_frac = (*left_it.second).desired_frac_; - auto left_prop = left_frac / (left_frac + (*right_it.second).desired_frac_); - + auto left_frac = ( *left_it.second ).desired_frac_; + auto left_prop = left_frac / ( left_frac + ( *right_it.second ).desired_frac_ ); + // rank coordinate axes by cut desirability (largest AABB length to smallest) - auto bbox_range = (*node_it).bbox_.range(); + auto bbox_range = ( *node_it ).bbox_.range(); auto best_axes = ArrayUtility::IndexArray(); - std::stable_sort(best_axes.begin(), best_axes.end(), - [&bbox_range](int i, int j) {return bbox_range[i] > bbox_range[j];}); + std::stable_sort( best_axes.begin(), best_axes.end(), + [&bbox_range]( int i, int j ) { return bbox_range[i] > bbox_range[j]; } ); auto axis_ok = false; auto actual_left_prop = 0.0; auto node_max_out_of_balance = 1.0; auto total_node_ents = 0.0; - for (auto cut_ax : best_axes) - { - auto bbox_min = (*node_it).bbox_.getMin(); - auto bbox_max = (*node_it).bbox_.getMax(); + for ( auto cut_ax : best_axes ) { + auto bbox_min = ( *node_it ).bbox_.getMin(); + auto bbox_max = ( *node_it ).bbox_.getMax(); auto left_max = bbox_max; auto right_min = bbox_min; // cut the axis by the proportion of node fractions auto min_coord = bbox_min[cut_ax]; auto max_coord = bbox_max[cut_ax]; - auto cut_coord = min_coord * (1.0 - left_prop) + max_coord * left_prop; + auto cut_coord = min_coord * ( 1.0 - left_prop ) + max_coord * left_prop; // use bisection method to find a cut coordinate within the relative tolerance mesh_max_out_of_balance auto prev_actual_left_prop = 0.0; - int same_prop_ct {0}; - while (true) - { + int same_prop_ct{ 0 }; + while ( true ) { left_max[cut_ax] = cut_coord; right_min[cut_ax] = cut_coord; - (*left_it.second).bbox_ = BoundingBox(bbox_min, left_max); - (*right_it.second).bbox_ = BoundingBox(right_min, bbox_max); + ( *left_it.second ).bbox_ = BoundingBox( bbox_min, left_max ); + ( *right_it.second ).bbox_ = BoundingBox( right_min, bbox_max ); - auto n_ents = CountEntities((*left_it.second).bbox_, (*right_it.second).bbox_, coords_by_mesh); - total_node_ents = static_cast(n_ents.first + n_ents.second); - actual_left_prop = static_cast(n_ents.first) / total_node_ents; + auto n_ents = CountEntities( ( *left_it.second ).bbox_, ( *right_it.second ).bbox_, coords_by_mesh ); + total_node_ents = static_cast( n_ents.first + n_ents.second ); + actual_left_prop = static_cast( n_ents.first ) / total_node_ents; node_max_out_of_balance = 1.0; - if (total_node_ents > 0.0) - { - node_max_out_of_balance = std::max(max_out_of_balance_, - 2.0 / total_node_ents); + if ( total_node_ents > 0.0 ) { + node_max_out_of_balance = std::max( max_out_of_balance_, 2.0 / total_node_ents ); } - if (actual_left_prop < left_prop - node_max_out_of_balance) - { + if ( actual_left_prop < left_prop - node_max_out_of_balance ) { // cut coordinate needs to be increased min_coord = cut_coord; - } - else if (actual_left_prop > left_prop + node_max_out_of_balance) - { + } else if ( actual_left_prop > left_prop + node_max_out_of_balance ) { // cut coordinate needs to be reduced max_coord = cut_coord; - } - else - { + } else { // cut coordinate is ok if we get here axis_ok = true; - (*left_it.second).actual_frac_ = - static_cast(n_ents.first) / static_cast(total_ents); - (*right_it.second).actual_frac_ = - static_cast(n_ents.second) / static_cast(total_ents); + ( *left_it.second ).actual_frac_ = static_cast( n_ents.first ) / static_cast( total_ents ); + ( *right_it.second ).actual_frac_ = + static_cast( n_ents.second ) / static_cast( total_ents ); // set ghost bounding boxes - for (int d{0}; d < NDIMS; ++d) - { + for ( int d{ 0 }; d < NDIMS; ++d ) { bbox_min[d] -= ghost_len; left_max[d] += ghost_len; right_min[d] -= ghost_len; bbox_max[d] += ghost_len; } - (*left_it.second).ghost_bbox_ = BoundingBox(bbox_min, left_max); - (*right_it.second).ghost_bbox_ = BoundingBox(right_min, bbox_max); + ( *left_it.second ).ghost_bbox_ = BoundingBox( bbox_min, left_max ); + ( *right_it.second ).ghost_bbox_ = BoundingBox( right_min, bbox_max ); break; } // check if we should try a different axis - if (prev_actual_left_prop == actual_left_prop) ++same_prop_ct; - if (same_prop_ct == n_try_new_axis_) break; + if ( prev_actual_left_prop == actual_left_prop ) ++same_prop_ct; + if ( same_prop_ct == n_try_new_axis_ ) break; prev_actual_left_prop = actual_left_prop; // try a new cut coordinate cut_coord = 0.5 * min_coord + 0.5 * max_coord; } - if (axis_ok) break; + if ( axis_ok ) break; } // none of the axes worked. issue a warning but continue. - SLIC_WARNING_ROOT_IF(!axis_ok, - axom::fmt::format("RCB domain decomposition unsuccessful.\n" - " Max out of balance tolerance: {}\n" - " Total entities to split: {}\n" - " Proportion of entities on left of cut: {}\n" - " Desired proportion of entites on left of cut: {}\n", - node_max_out_of_balance, total_node_ents, left_prop, actual_left_prop)); + SLIC_WARNING_ROOT_IF( + !axis_ok, axom::fmt::format( "RCB domain decomposition unsuccessful.\n" + " Max out of balance tolerance: {}\n" + " Total entities to split: {}\n" + " Proportion of entities on left of cut: {}\n" + " Desired proportion of entites on left of cut: {}\n", + node_max_out_of_balance, total_node_ents, left_prop, actual_left_prop ) ); } } // solve for neighboring bounding boxes (used for testing for ghost elements) // TODO: is there a better (non-O(n^2)) way to do this? - for (int i{0}; i < n_parts; ++i) - { - for (int j{0}; j < i; ++j) - { + for ( int i{ 0 }; i < n_parts; ++i ) { + for ( int j{ 0 }; j < i; ++j ) { // shared root OR overlap - if ((j == (i-1) && (i+j+1)/2 % 2 == 1) || - problem_tree(i).ghost_bbox_.intersectsWith( - problem_tree(j).ghost_bbox_)) - { - problem_tree(i).neighbor_bboxes_.push_back(j); - problem_tree(j).neighbor_bboxes_.push_back(i); + if ( ( j == ( i - 1 ) && ( i + j + 1 ) / 2 % 2 == 1 ) || + problem_tree( i ).ghost_bbox_.intersectsWith( problem_tree( j ).ghost_bbox_ ) ) { + problem_tree( i ).neighbor_bboxes_.push_back( j ); + problem_tree( j ).neighbor_bboxes_.push_back( i ); } } } - + return problem_tree; } template -BoundingBox RCB::DomainBoundingBox( - const std::vector>>& coords_by_mesh -) const +BoundingBox RCB::DomainBoundingBox( const std::vector>>& coords_by_mesh ) const { auto on_rank_bbox = BoundingBox(); - size_t num_coords {0}; - for (const auto& coords : coords_by_mesh) - { - on_rank_bbox.addBox(BoundingBox(coords.data(), coords.size())); + size_t num_coords{ 0 }; + for ( const auto& coords : coords_by_mesh ) { + on_rank_bbox.addBox( BoundingBox( coords.data(), coords.size() ) ); num_coords += coords.size(); } auto min_coord = on_rank_bbox.getMin(); auto max_coord = on_rank_bbox.getMax(); - if (num_coords == 0) - { + if ( num_coords == 0 ) { on_rank_bbox.clear(); } // reduce to all ranks - this->getMPIUtility().Allreduce(&min_coord.array(), MPI_MIN); - this->getMPIUtility().Allreduce(&max_coord.array(), MPI_MAX); + this->getMPIUtility().Allreduce( &min_coord.array(), MPI_MIN ); + this->getMPIUtility().Allreduce( &max_coord.array(), MPI_MAX ); - return BoundingBox(min_coord, max_coord); + return BoundingBox( min_coord, max_coord ); } template -std::pair RCB::CountEntities( - const BoundingBox& left_bbox, - const BoundingBox& right_bbox, - const std::vector>>& coords_by_mesh -) const +std::pair RCB::CountEntities( const BoundingBox& left_bbox, + const BoundingBox& right_bbox, + const std::vector>>& coords_by_mesh ) const { - auto n_ents = std::make_pair(0, 0); - - for (const auto& coords : coords_by_mesh) - { - for (const auto& coord : coords) - { - if (left_bbox.contains(coord)) n_ents.first += 1; - else if (right_bbox.contains(coord)) n_ents.second += 1; + auto n_ents = std::make_pair( 0, 0 ); + + for ( const auto& coords : coords_by_mesh ) { + for ( const auto& coord : coords ) { + if ( left_bbox.contains( coord ) ) + n_ents.first += 1; + else if ( right_bbox.contains( coord ) ) + n_ents.second += 1; } } // reduce to all ranks - n_ents.first = this->getMPIUtility().AllreduceValue(n_ents.first, MPI_SUM); - n_ents.second = this->getMPIUtility().AllreduceValue(n_ents.second, MPI_SUM); + n_ents.first = this->getMPIUtility().AllreduceValue( n_ents.first, MPI_SUM ); + n_ents.second = this->getMPIUtility().AllreduceValue( n_ents.second, MPI_SUM ); return n_ents; } template -int RCB::DetermineDomain( - const BisecTree>& problem_tree, - const Point& coord -) const +int RCB::DetermineDomain( const BisecTree>& problem_tree, const Point& coord ) const { - auto it = std::make_pair(problem_tree.begin(), problem_tree.begin(problem_tree.begin())); - for (size_t i{0}; i < (problem_tree.NumLevels()-1); ++i) - { - auto left_it = problem_tree.left(it); - if ((*left_it.second).bbox_.contains(coord)) - { + auto it = std::make_pair( problem_tree.begin(), problem_tree.begin( problem_tree.begin() ) ); + for ( size_t i{ 0 }; i < ( problem_tree.NumLevels() - 1 ); ++i ) { + auto left_it = problem_tree.left( it ); + if ( ( *left_it.second ).bbox_.contains( coord ) ) { it = left_it; - } - else - { - it = problem_tree.right(it); + } else { + it = problem_tree.right( it ); } } - return problem_tree.position(it); + return problem_tree.position( it ); } template -int RCB::TotalEntities(int n_local_ents) const +int RCB::TotalEntities( int n_local_ents ) const { - return this->getMPIUtility().AllreduceValue(n_local_ents, MPI_SUM); + return this->getMPIUtility().AllreduceValue( n_local_ents, MPI_SUM ); } template class RCB<2>; template class RCB<3>; -} // end namespace redecomp +} // end namespace redecomp diff --git a/src/redecomp/partition/RCB.hpp b/src/redecomp/partition/RCB.hpp index 2ee2404f..bbbb479d 100644 --- a/src/redecomp/partition/RCB.hpp +++ b/src/redecomp/partition/RCB.hpp @@ -14,39 +14,37 @@ #include "redecomp/partition/PartitionMethod.hpp" #include "redecomp/utils/BisecTree.hpp" -namespace redecomp -{ +namespace redecomp { /** * @brief Stores information about the partition in BisecTree - * + * * @tparam NDIMS number of dimensions */ template -struct RCBInfo -{ +struct RCBInfo { /** * @brief Desired fraction of entities in the partition */ double desired_frac_; /** - * @brief Actual fraction of entities in the partition + * @brief Actual fraction of entities in the partition */ double actual_frac_; /** - * @brief Bounding box defining the bounds of the partition + * @brief Bounding box defining the bounds of the partition */ BoundingBox bbox_; /** - * @brief Bounding box defining the bounds of the partition including ghost regions + * @brief Bounding box defining the bounds of the partition including ghost regions */ BoundingBox ghost_bbox_; /** - * @brief List of bounding boxes that are adjacent to this bounding box + * @brief List of bounding boxes that are adjacent to this bounding box */ axom::Array neighbor_bboxes_; }; @@ -66,90 +64,76 @@ struct RCBInfo * See Hendrickson and Devine (2000), Comput Methods Appl Mech Eng. */ template -class RCB : public PartitionMethod -{ -public: +class RCB : public PartitionMethod { + public: /** * @brief Construct a new RCB object - * + * * @param comm MPI_Comm for the partitioning * @param max_out_of_balance Allowable deviation from desired fraction of entities on each partition * @param n_try_new_axis Number of attempted cuts with no change in entity counts before trying a new axis */ - RCB(const MPI_Comm& comm, double max_out_of_balance = 0.1, int n_try_new_axis = 5); + RCB( const MPI_Comm& comm, double max_out_of_balance = 0.1, int n_try_new_axis = 5 ); /** * @brief Build entity partitioning using recursive coordinate bisection - * + * * @param n_parts Number of subdomains to cut the list of coords into * @param coords_by_mesh List of on-rank points to subdivide sorted by mesh * @param ghost_len Sets length to include entities as ghost on each subdomain * @return List of points and ghost entities on each subdomain sorted by mesh */ - std::vector generatePartitioning( - int n_parts, - const std::vector>>& coords_by_mesh, - double ghost_len - ) const override; + std::vector generatePartitioning( int n_parts, + const std::vector>>& coords_by_mesh, + double ghost_len ) const override; -private: + private: /** * @brief Builds a binary bisection tree of domain cut locations (all ranks) - * + * * @param n_parts Number of subdomains * @param coords_by_mesh Entity coordinates sorted by mesh (on-rank) * @param ghost_len Sets length to include entities as ghost on each subdomain * @return BisecTree of bounding boxes defining subdomains and ghost subdomains */ - BisecTree> BuildProblemTree( - int n_parts, - const std::vector>>& coords_by_mesh, - double ghost_len - ) const; + BisecTree> BuildProblemTree( int n_parts, const std::vector>>& coords_by_mesh, + double ghost_len ) const; /** * @brief Bounding box of coords on all ranks - * + * * @param coords_by_mesh On-rank coords sorted by mesh * @return BoundingBox Bounding box of coords on all ranks */ - BoundingBox DomainBoundingBox( - const std::vector>>& coords_by_mesh - ) const; + BoundingBox DomainBoundingBox( const std::vector>>& coords_by_mesh ) const; /** * @brief Counts the entities across all ranks in the two given bounding boxes - * + * * @param left_bbox First (left) bounding box * @param right_bbox Second (right) bounding box * @param coords_by_mesh On-rank entity coordinates sorted by mesh * @return std::pair Entity counts in each bounding box */ - std::pair CountEntities( - const BoundingBox& left_bbox, - const BoundingBox& right_bbox, - const std::vector>>& coords_by_mesh - ) const; + std::pair CountEntities( const BoundingBox& left_bbox, const BoundingBox& right_bbox, + const std::vector>>& coords_by_mesh ) const; /** * @brief Find the domain at the base of the BisecTree the coord belongs to - * + * * @param problem_tree BisecTree holding tree of bounding boxes of each domain * @param coord Entity coordinate to test * @return int Domain index */ - int DetermineDomain( - const BisecTree>& problem_tree, - const Point& coord - ) const; + int DetermineDomain( const BisecTree>& problem_tree, const Point& coord ) const; /** * @brief Sums number of entities over all processors - * + * * @param n_local_ents Number of on-rank entities * @return int Total number of entities */ - int TotalEntities(int n_local_ents) const; + int TotalEntities( int n_local_ents ) const; /** * @brief Tolerance for out-of-balance loading (default = 0.1, i.e. 10%) @@ -157,7 +141,8 @@ class RCB : public PartitionMethod double max_out_of_balance_; /** - * @brief Number of cuts to try before switching to a new axis (default = 5). Designed to guard against cases where e.g. all elements are aligned along an axis. + * @brief Number of cuts to try before switching to a new axis (default = 5). Designed to guard against cases where + * e.g. all elements are aligned along an axis. */ int n_try_new_axis_; }; @@ -165,6 +150,6 @@ class RCB : public PartitionMethod using RCB2D = RCB<2>; using RCB3D = RCB<3>; -} // end namespace redecomp +} // end namespace redecomp #endif /* SRC_REDECOMP_PARTITION_RCB_HPP_ */ diff --git a/src/redecomp/transfer/GridFnTransfer.hpp b/src/redecomp/transfer/GridFnTransfer.hpp index 55446778..3a5944c5 100644 --- a/src/redecomp/transfer/GridFnTransfer.hpp +++ b/src/redecomp/transfer/GridFnTransfer.hpp @@ -8,15 +8,13 @@ #include "mfem.hpp" -namespace redecomp -{ +namespace redecomp { /** - * @brief GridFnTransfer interface base class + * @brief GridFnTransfer interface base class */ -class GridFnTransfer -{ -public: +class GridFnTransfer { + public: /** * @brief Transfers nodal values from src to dst * @@ -25,10 +23,7 @@ class GridFnTransfer * @param dst A redecomp GridFunction which receives values from a parent * ParGridFunction (src) */ - virtual void TransferToSerial( - const mfem::ParGridFunction& src, - mfem::GridFunction& dst - ) const = 0; + virtual void TransferToSerial( const mfem::ParGridFunction& src, mfem::GridFunction& dst ) const = 0; /** * @brief Transfers nodal values from src to dst @@ -38,18 +33,14 @@ class GridFnTransfer * @param dst A parent ParGridFunction which receives values from a redecomp * GridFunction (src) */ - virtual void TransferToParallel( - const mfem::GridFunction& src, - mfem::ParGridFunction& dst - ) const = 0; + virtual void TransferToParallel( const mfem::GridFunction& src, mfem::ParGridFunction& dst ) const = 0; /** * @brief Destroy the GridFnTransfer object */ virtual ~GridFnTransfer() = default; - }; -} // end namespace redecomp +} // end namespace redecomp #endif /* SRC_REDECOMP_GRIDFNTRANSFER_HPP_ */ diff --git a/src/redecomp/transfer/MatrixTransfer.cpp b/src/redecomp/transfer/MatrixTransfer.cpp index 18b28a26..36ce4cb0 100644 --- a/src/redecomp/transfer/MatrixTransfer.cpp +++ b/src/redecomp/transfer/MatrixTransfer.cpp @@ -9,230 +9,177 @@ #include "redecomp/RedecompMesh.hpp" -namespace redecomp +namespace redecomp { + +MatrixTransfer::MatrixTransfer( const mfem::ParFiniteElementSpace& parent_test_fes, + const mfem::ParFiniteElementSpace& parent_trial_fes, + const mfem::FiniteElementSpace& redecomp_test_fes, + const mfem::FiniteElementSpace& redecomp_trial_fes ) + : parent_test_fes_{ parent_test_fes }, + parent_trial_fes_{ parent_trial_fes }, + redecomp_test_fes_{ redecomp_test_fes }, + redecomp_trial_fes_{ redecomp_trial_fes } { - -MatrixTransfer::MatrixTransfer( - const mfem::ParFiniteElementSpace& parent_test_fes, - const mfem::ParFiniteElementSpace& parent_trial_fes, - const mfem::FiniteElementSpace& redecomp_test_fes, - const mfem::FiniteElementSpace& redecomp_trial_fes -) -: parent_test_fes_ { parent_test_fes }, - parent_trial_fes_ { parent_trial_fes }, - redecomp_test_fes_ { redecomp_test_fes }, - redecomp_trial_fes_ { redecomp_trial_fes } -{ - auto test_redecomp = dynamic_cast(redecomp_test_fes_.GetMesh()); - auto trial_redecomp = dynamic_cast(redecomp_trial_fes_.GetMesh()); - SLIC_ERROR_ROOT_IF(test_redecomp == nullptr, - "The Redecomp test finite element space must have a Redecomp mesh."); - SLIC_ERROR_ROOT_IF(trial_redecomp == nullptr, - "The Redecomp trial finite element space must have a Redecomp mesh."); - SLIC_ERROR_ROOT_IF(&test_redecomp->getParent() != parent_test_fes_.GetParMesh(), - "The parent test finite element space mesh must be linked to the test Redecomp mesh."); - SLIC_ERROR_ROOT_IF(&trial_redecomp->getParent() != parent_trial_fes_.GetParMesh(), - "The parent trial finite element space mesh must be linked to the trial Redecomp mesh."); - SLIC_ERROR_ROOT_IF(&test_redecomp->getMPIUtility().MPIComm() != &trial_redecomp->getMPIUtility().MPIComm(), - "MPI Communicator must match in test and trial spaces."); - - trial_r2p_elem_rank_ = buildRedecomp2ParentElemRank(*trial_redecomp, false); - test_r2p_elem_rank_ = buildRedecomp2ParentElemRank(*test_redecomp, true); + auto test_redecomp = dynamic_cast( redecomp_test_fes_.GetMesh() ); + auto trial_redecomp = dynamic_cast( redecomp_trial_fes_.GetMesh() ); + SLIC_ERROR_ROOT_IF( test_redecomp == nullptr, "The Redecomp test finite element space must have a Redecomp mesh." ); + SLIC_ERROR_ROOT_IF( trial_redecomp == nullptr, "The Redecomp trial finite element space must have a Redecomp mesh." ); + SLIC_ERROR_ROOT_IF( &test_redecomp->getParent() != parent_test_fes_.GetParMesh(), + "The parent test finite element space mesh must be linked to the test Redecomp mesh." ); + SLIC_ERROR_ROOT_IF( &trial_redecomp->getParent() != parent_trial_fes_.GetParMesh(), + "The parent trial finite element space mesh must be linked to the trial Redecomp mesh." ); + SLIC_ERROR_ROOT_IF( &test_redecomp->getMPIUtility().MPIComm() != &trial_redecomp->getMPIUtility().MPIComm(), + "MPI Communicator must match in test and trial spaces." ); + + trial_r2p_elem_rank_ = buildRedecomp2ParentElemRank( *trial_redecomp, false ); + test_r2p_elem_rank_ = buildRedecomp2ParentElemRank( *test_redecomp, true ); } std::unique_ptr MatrixTransfer::TransferToParallel( - const axom::Array& test_elem_idx, - const axom::Array& trial_elem_idx, - const axom::Array& src_elem_mat, - bool parallel_assemble -) const + const axom::Array& test_elem_idx, const axom::Array& trial_elem_idx, + const axom::Array& src_elem_mat, bool parallel_assemble ) const { - auto J_sparse = TransferToParallelSparse(test_elem_idx, trial_elem_idx, src_elem_mat); + auto J_sparse = TransferToParallelSparse( test_elem_idx, trial_elem_idx, src_elem_mat ); J_sparse.Finalize(); - return ConvertToHypreParMatrix(J_sparse, parallel_assemble); + return ConvertToHypreParMatrix( J_sparse, parallel_assemble ); } -mfem::SparseMatrix MatrixTransfer::TransferToParallelSparse( - const axom::Array& test_elem_idx, - const axom::Array& trial_elem_idx, - const axom::Array& src_elem_mat -) const +mfem::SparseMatrix MatrixTransfer::TransferToParallelSparse( const axom::Array& test_elem_idx, + const axom::Array& trial_elem_idx, + const axom::Array& src_elem_mat ) const { - auto parentJ = mfem::SparseMatrix( - parent_test_fes_.GetVSize(), - parent_trial_fes_.GlobalVSize() - ); + auto parentJ = mfem::SparseMatrix( parent_test_fes_.GetVSize(), parent_trial_fes_.GlobalVSize() ); // verify inputs - SLIC_ERROR_IF(test_elem_idx.size() != trial_elem_idx.size() - || test_elem_idx.size() != src_elem_mat.size(), - "Element index arrays and element Jacobian contribution array must be the same size."); - for (int i{0}; i < src_elem_mat.size(); ++i) - { + SLIC_ERROR_IF( test_elem_idx.size() != trial_elem_idx.size() || test_elem_idx.size() != src_elem_mat.size(), + "Element index arrays and element Jacobian contribution array must be the same size." ); + for ( int i{ 0 }; i < src_elem_mat.size(); ++i ) { auto test_e = test_elem_idx[i]; auto trial_e = trial_elem_idx[i]; - SLIC_ERROR_IF(test_e < 0, "Invalid primary index value."); - SLIC_ERROR_IF(trial_e < 0, "Invalid secondary index value."); - - auto n_test_elem_vdofs = - redecomp_test_fes_.GetFE(test_e)->GetDof() * redecomp_test_fes_.GetVDim(); - auto n_trial_elem_vdofs = - redecomp_trial_fes_.GetFE(trial_e)->GetDof() * redecomp_trial_fes_.GetVDim(); - - SLIC_ERROR_IF(src_elem_mat[i].Height() != n_test_elem_vdofs, - "The number of test DOFs does not match the size of the element DenseMatrix."); - SLIC_ERROR_IF(src_elem_mat[i].Width() != n_trial_elem_vdofs, - "The number of trial DOFs does not match the size of the element DenseMatrix."); + SLIC_ERROR_IF( test_e < 0, "Invalid primary index value." ); + SLIC_ERROR_IF( trial_e < 0, "Invalid secondary index value." ); + + auto n_test_elem_vdofs = redecomp_test_fes_.GetFE( test_e )->GetDof() * redecomp_test_fes_.GetVDim(); + auto n_trial_elem_vdofs = redecomp_trial_fes_.GetFE( trial_e )->GetDof() * redecomp_trial_fes_.GetVDim(); + + SLIC_ERROR_IF( src_elem_mat[i].Height() != n_test_elem_vdofs, + "The number of test DOFs does not match the size of the element DenseMatrix." ); + SLIC_ERROR_IF( src_elem_mat[i].Width() != n_trial_elem_vdofs, + "The number of trial DOFs does not match the size of the element DenseMatrix." ); } - auto test_redecomp = dynamic_cast(redecomp_test_fes_.GetMesh()); - auto trial_redecomp = dynamic_cast(redecomp_trial_fes_.GetMesh()); + auto test_redecomp = dynamic_cast( redecomp_test_fes_.GetMesh() ); + auto trial_redecomp = dynamic_cast( redecomp_trial_fes_.GetMesh() ); // List of entries in src_elem_mat that belong on each parent test space rank. - // This is needed so we know which rank to send entries in src_elem_mat to. - auto send_array_ids = buildSendArrayIDs(test_elem_idx); + // This is needed so we know which rank to send entries in src_elem_mat to. + auto send_array_ids = buildSendArrayIDs( test_elem_idx ); // Number of matrix entries to be sent to each parent test space rank. This // is used to size the array of element matrix values to be sent to other ranks. - auto send_num_mat_entries = buildSendNumMatEntries(test_elem_idx, trial_elem_idx); + auto send_num_mat_entries = buildSendNumMatEntries( test_elem_idx, trial_elem_idx ); // Number of test and trial vdofs received from test space redecomp ranks. // This is used to determine the beginning and end of each element matrix // received as a single array of values and the beginning and end of vdof indices // received as a single array of values. - auto recv_mat_sizes = buildRecvMatSizes(test_elem_idx, trial_elem_idx); + auto recv_mat_sizes = buildRecvMatSizes( test_elem_idx, trial_elem_idx ); // List of test element offsets received from test space redecomp ranks. The // offset is used with the parent to redecomp map (and the redecomp rank // received from) to determine the parent element ID. Parent element ID is // used to determine the test vldofs of the element matrix entries received // from redecomp ranks. - auto recv_test_elem_offsets = buildRecvTestElemOffsets(*test_redecomp, test_elem_idx); + auto recv_test_elem_offsets = buildRecvTestElemOffsets( *test_redecomp, test_elem_idx ); // List of trial element global vdofs corresponding to the element matrix // entries received from redecomp ranks. The second column of recv_mat_sizes // determines the offset for each trial element. - auto recv_trial_elem_dofs = - buildRecvTrialElemDofs(*trial_redecomp, test_elem_idx, trial_elem_idx); - + auto recv_trial_elem_dofs = buildRecvTrialElemDofs( *trial_redecomp, test_elem_idx, trial_elem_idx ); + // aggregate dense matrix values, send and assemble getMPIUtility().SendRecvEach( - type>(), - [&send_array_ids, &send_num_mat_entries, &src_elem_mat](axom::IndexType dst) - { - auto send_vals = axom::Array(0, send_num_mat_entries[dst]); - - for (auto src_array_idx : send_array_ids[dst]) - { - send_vals.append(axom::ArrayView( - src_elem_mat[src_array_idx].Data(), - src_elem_mat[src_array_idx].Width() * src_elem_mat[src_array_idx].Height() - )); - } + type>(), + [&send_array_ids, &send_num_mat_entries, &src_elem_mat]( axom::IndexType dst ) { + auto send_vals = axom::Array( 0, send_num_mat_entries[dst] ); + + for ( auto src_array_idx : send_array_ids[dst] ) { + send_vals.append( + axom::ArrayView( src_elem_mat[src_array_idx].Data(), + src_elem_mat[src_array_idx].Width() * src_elem_mat[src_array_idx].Height() ) ); + } - return send_vals; - }, - [this, test_redecomp, &parentJ, &recv_mat_sizes, &recv_trial_elem_dofs, &recv_test_elem_offsets]( - axom::Array&& send_vals, - axom::IndexType src - ) - { - if (recv_trial_elem_dofs[src].empty()) - { - return; - } - auto trial_dof_ct = 0; - auto dof_ct = 0; - // element loop - for (int e{0}; e < recv_test_elem_offsets[src].size(); ++e) - { - auto test_elem_id = test_redecomp->getParentToRedecompElems() - .first[src][recv_test_elem_offsets[src][e]]; - auto test_elem_dofs = mfem::Array(); - parent_test_fes_.GetElementVDofs(test_elem_id, test_elem_dofs); - auto trial_elem_dofs = mfem::Array( - &recv_trial_elem_dofs[src][trial_dof_ct], - recv_mat_sizes[src](e, 1) - ); - // trial loop - for (int j{0}; j < trial_elem_dofs.Size(); ++j) - { - // test loop - for (int i{0}; i < test_elem_dofs.Size(); ++i) - { - parentJ.Add( - test_elem_dofs[i], - trial_elem_dofs[j], - // send_vals comes from mfem::SparseMatrix (column major) - send_vals[dof_ct + i + j*test_elem_dofs.Size()] - ); + return send_vals; + }, + [this, test_redecomp, &parentJ, &recv_mat_sizes, &recv_trial_elem_dofs, &recv_test_elem_offsets]( + axom::Array&& send_vals, axom::IndexType src ) { + if ( recv_trial_elem_dofs[src].empty() ) { + return; + } + auto trial_dof_ct = 0; + auto dof_ct = 0; + // element loop + for ( int e{ 0 }; e < recv_test_elem_offsets[src].size(); ++e ) { + auto test_elem_id = test_redecomp->getParentToRedecompElems().first[src][recv_test_elem_offsets[src][e]]; + auto test_elem_dofs = mfem::Array(); + parent_test_fes_.GetElementVDofs( test_elem_id, test_elem_dofs ); + auto trial_elem_dofs = + mfem::Array( &recv_trial_elem_dofs[src][trial_dof_ct], recv_mat_sizes[src]( e, 1 ) ); + // trial loop + for ( int j{ 0 }; j < trial_elem_dofs.Size(); ++j ) { + // test loop + for ( int i{ 0 }; i < test_elem_dofs.Size(); ++i ) { + parentJ.Add( test_elem_dofs[i], trial_elem_dofs[j], + // send_vals comes from mfem::SparseMatrix (column major) + send_vals[dof_ct + i + j * test_elem_dofs.Size()] ); + } } + trial_dof_ct += recv_mat_sizes[src]( e, 1 ); + dof_ct += recv_mat_sizes[src]( e, 0 ) * recv_mat_sizes[src]( e, 1 ); } - trial_dof_ct += recv_mat_sizes[src](e, 1); - dof_ct += recv_mat_sizes[src](e, 0) * recv_mat_sizes[src](e, 1); - } - } - ); + } ); return parentJ; } -std::unique_ptr MatrixTransfer::ConvertToHypreParMatrix( - mfem::SparseMatrix& sparse, - bool parallel_assemble -) const +std::unique_ptr MatrixTransfer::ConvertToHypreParMatrix( mfem::SparseMatrix& sparse, + bool parallel_assemble ) const { - SLIC_ERROR_IF(sparse.Height() != parent_test_fes_.GetVSize(), - "Height of sparse must match number of test ParFiniteElementSpace L-dofs."); - SLIC_ERROR_IF(sparse.Width() != parent_trial_fes_.GlobalVSize(), - "Width of sparse must match number of trial ParFiniteElementSpace global dofs."); + SLIC_ERROR_IF( sparse.Height() != parent_test_fes_.GetVSize(), + "Height of sparse must match number of test ParFiniteElementSpace L-dofs." ); + SLIC_ERROR_IF( sparse.Width() != parent_trial_fes_.GlobalVSize(), + "Width of sparse must match number of trial ParFiniteElementSpace global dofs." ); auto J_full = std::make_unique( - getMPIUtility().MPIComm(), parent_test_fes_.GetVSize(), - parent_test_fes_.GlobalVSize(), parent_trial_fes_.GlobalVSize(), - sparse.GetI(), sparse.GetJ(), sparse.GetData(), - parent_test_fes_.GetDofOffsets(), parent_trial_fes_.GetDofOffsets() - ); - if (!parallel_assemble) - { + getMPIUtility().MPIComm(), parent_test_fes_.GetVSize(), parent_test_fes_.GlobalVSize(), + parent_trial_fes_.GlobalVSize(), sparse.GetI(), sparse.GetJ(), sparse.GetData(), parent_test_fes_.GetDofOffsets(), + parent_trial_fes_.GetDofOffsets() ); + if ( !parallel_assemble ) { return J_full; - } - else - { - auto J_true = std::unique_ptr(mfem::RAP( - parent_test_fes_.Dof_TrueDof_Matrix(), - J_full.get(), - parent_trial_fes_.Dof_TrueDof_Matrix() - )); + } else { + auto J_true = std::unique_ptr( + mfem::RAP( parent_test_fes_.Dof_TrueDof_Matrix(), J_full.get(), parent_trial_fes_.Dof_TrueDof_Matrix() ) ); return J_true; } } -axom::Array MatrixTransfer::buildRedecomp2ParentElemRank( - const RedecompMesh& redecomp, - bool mark_ghost -) +axom::Array MatrixTransfer::buildRedecomp2ParentElemRank( const RedecompMesh& redecomp, bool mark_ghost ) { - auto r2p_elem_rank = axom::Array(0, redecomp.GetNE()); + auto r2p_elem_rank = axom::Array( 0, redecomp.GetNE() ); const auto& elem_offsets = redecomp.getRedecompToParentElemOffsets(); - for (int r{0}; r < getMPIUtility().NRanks(); ++r) - { - r2p_elem_rank.insert(elem_offsets[r], elem_offsets[r+1] - elem_offsets[r], r); + for ( int r{ 0 }; r < getMPIUtility().NRanks(); ++r ) { + r2p_elem_rank.insert( elem_offsets[r], elem_offsets[r + 1] - elem_offsets[r], r ); } - if (mark_ghost) - { + if ( mark_ghost ) { // mark ghost elements as rank -1 const auto& ghost_elems = redecomp.getRedecompToParentGhostElems(); auto ghost_ct = 0; auto r = 0; - for (int e{0}; e < redecomp.GetNE(); ++e) - { - if (r != r2p_elem_rank[e]) - { + for ( int e{ 0 }; e < redecomp.GetNE(); ++e ) { + if ( r != r2p_elem_rank[e] ) { r = r2p_elem_rank[e]; ghost_ct = 0; } - if (ghost_ct < ghost_elems[r].size() && ghost_elems[r][ghost_ct] == e) - { + if ( ghost_ct < ghost_elems[r].size() && ghost_elems[r][ghost_ct] == e ) { r2p_elem_rank[e] = -1; ++ghost_ct; } @@ -242,58 +189,46 @@ axom::Array MatrixTransfer::buildRedecomp2ParentElemRank( return r2p_elem_rank; } -MPIArray MatrixTransfer::buildSendArrayIDs( - const axom::Array& test_elem_idx -) const +MPIArray MatrixTransfer::buildSendArrayIDs( const axom::Array& test_elem_idx ) const { - auto send_array_ids = MPIArray(&getMPIUtility()); + auto send_array_ids = MPIArray( &getMPIUtility() ); auto n_ranks = getMPIUtility().NRanks(); auto est_max_elems = 2 * test_elem_idx.size() / n_ranks; - for (int r{0}; r < n_ranks; ++r) - { - send_array_ids[r].reserve(est_max_elems); + for ( int r{ 0 }; r < n_ranks; ++r ) { + send_array_ids[r].reserve( est_max_elems ); } - for (int i{0}; i < test_elem_idx.size(); ++i) - { + for ( int i{ 0 }; i < test_elem_idx.size(); ++i ) { auto test_e = test_elem_idx[i]; // rank is selected by the test element space auto r = test_r2p_elem_rank_[test_e]; // test element is owned by this rank (since -1 denotes a ghost element) - if (r != -1) - { - send_array_ids[r].push_back(i); + if ( r != -1 ) { + send_array_ids[r].push_back( i ); } } - for (auto send_array_ids_rank : send_array_ids) - { + for ( auto send_array_ids_rank : send_array_ids ) { send_array_ids_rank.shrink(); } return send_array_ids; } -axom::Array MatrixTransfer::buildSendNumMatEntries( - const axom::Array& test_elem_idx, - const axom::Array& trial_elem_idx -) const +axom::Array MatrixTransfer::buildSendNumMatEntries( const axom::Array& test_elem_idx, + const axom::Array& trial_elem_idx ) const { auto n_ranks = getMPIUtility().NRanks(); - auto send_num_mat_entries = axom::Array(n_ranks, n_ranks); + auto send_num_mat_entries = axom::Array( n_ranks, n_ranks ); - for (int i{0}; i < test_elem_idx.size(); ++i) - { + for ( int i{ 0 }; i < test_elem_idx.size(); ++i ) { auto test_e = test_elem_idx[i]; auto trial_e = trial_elem_idx[i]; - auto n_test_elem_vdofs = - redecomp_test_fes_.GetFE(test_e)->GetDof() * redecomp_test_fes_.GetVDim(); - auto n_trial_elem_vdofs = - redecomp_trial_fes_.GetFE(trial_e)->GetDof() * redecomp_trial_fes_.GetVDim(); + auto n_test_elem_vdofs = redecomp_test_fes_.GetFE( test_e )->GetDof() * redecomp_test_fes_.GetVDim(); + auto n_trial_elem_vdofs = redecomp_trial_fes_.GetFE( trial_e )->GetDof() * redecomp_trial_fes_.GetVDim(); // rank is selected by the test element space auto r = test_r2p_elem_rank_[test_e]; // test element is owned by this rank (since -1 denotes a ghost element) - if (r != -1) - { + if ( r != -1 ) { send_num_mat_entries[r] += n_test_elem_vdofs * n_trial_elem_vdofs; } } @@ -301,88 +236,73 @@ axom::Array MatrixTransfer::buildSendNumMatEntries( return send_num_mat_entries; } -MPIArray MatrixTransfer::buildRecvMatSizes( - const axom::Array& test_elem_idx, - const axom::Array& trial_elem_idx -) const +MPIArray MatrixTransfer::buildRecvMatSizes( const axom::Array& test_elem_idx, + const axom::Array& trial_elem_idx ) const { - auto recv_mat_sizes = MPIArray(&getMPIUtility()); + auto recv_mat_sizes = MPIArray( &getMPIUtility() ); // Number of test and trial vdofs for each element matrix to be sent to each // parent test space rank. MPI communication is used to turn this array into // recv_mat_sizes. - auto send_mat_sizes = MPIArray(&getMPIUtility()); + auto send_mat_sizes = MPIArray( &getMPIUtility() ); auto n_ranks = getMPIUtility().NRanks(); auto est_max_elems = 2 * test_elem_idx.size() / n_ranks; - for (int r{0}; r < n_ranks; ++r) - { - send_mat_sizes[r].reserve(2 * est_max_elems); + for ( int r{ 0 }; r < n_ranks; ++r ) { + send_mat_sizes[r].reserve( 2 * est_max_elems ); } - for (int i{0}; i < test_elem_idx.size(); ++i) - { + for ( int i{ 0 }; i < test_elem_idx.size(); ++i ) { auto test_e = test_elem_idx[i]; auto trial_e = trial_elem_idx[i]; - auto n_test_elem_vdofs = - redecomp_test_fes_.GetFE(test_e)->GetDof() * redecomp_test_fes_.GetVDim(); - auto n_trial_elem_vdofs = - redecomp_trial_fes_.GetFE(trial_e)->GetDof() * redecomp_trial_fes_.GetVDim(); + auto n_test_elem_vdofs = redecomp_test_fes_.GetFE( test_e )->GetDof() * redecomp_test_fes_.GetVDim(); + auto n_trial_elem_vdofs = redecomp_trial_fes_.GetFE( trial_e )->GetDof() * redecomp_trial_fes_.GetVDim(); // rank is selected by the test element space auto r = test_r2p_elem_rank_[test_e]; // test element is owned by this rank (since -1 denotes a ghost element) - if (r != -1) - { + if ( r != -1 ) { auto row = send_mat_sizes[r].shape()[0]; - send_mat_sizes[r].resize(row + 1, 2); - send_mat_sizes[r](row, 0) = n_test_elem_vdofs; - send_mat_sizes[r](row, 1) = n_trial_elem_vdofs; + send_mat_sizes[r].resize( row + 1, 2 ); + send_mat_sizes[r]( row, 0 ) = n_test_elem_vdofs; + send_mat_sizes[r]( row, 1 ) = n_trial_elem_vdofs; } } - recv_mat_sizes.SendRecvArrayEach(send_mat_sizes); + recv_mat_sizes.SendRecvArrayEach( send_mat_sizes ); return recv_mat_sizes; } -MPIArray MatrixTransfer::buildRecvTestElemOffsets( - const RedecompMesh& test_redecomp, - const axom::Array& test_elem_idx -) const +MPIArray MatrixTransfer::buildRecvTestElemOffsets( const RedecompMesh& test_redecomp, + const axom::Array& test_elem_idx ) const { - auto recv_test_elem_offsets = MPIArray(&getMPIUtility()); + auto recv_test_elem_offsets = MPIArray( &getMPIUtility() ); // List of test element offsets that will be sent to each parent test space rank. // MPI communication is used to turn this array into recv_test_elem_offsets. - auto send_test_elem_offsets = MPIArray(&getMPIUtility()); + auto send_test_elem_offsets = MPIArray( &getMPIUtility() ); auto n_ranks = getMPIUtility().NRanks(); auto est_max_elems = 2 * test_elem_idx.size() / n_ranks; - for (int r{0}; r < n_ranks; ++r) - { - send_test_elem_offsets[r].reserve(est_max_elems); + for ( int r{ 0 }; r < n_ranks; ++r ) { + send_test_elem_offsets[r].reserve( est_max_elems ); } - const auto& test_r2p_elem_offsets = test_redecomp. - getRedecompToParentElemOffsets(); - for (int i{0}; i < test_elem_idx.size(); ++i) - { + const auto& test_r2p_elem_offsets = test_redecomp.getRedecompToParentElemOffsets(); + for ( int i{ 0 }; i < test_elem_idx.size(); ++i ) { auto test_e = test_elem_idx[i]; // rank is selected by the test element space auto r = test_r2p_elem_rank_[test_e]; // test element is owned by this rank (since -1 denotes a ghost element) - if (r != -1) - { - send_test_elem_offsets[r].push_back(test_e - test_r2p_elem_offsets[r]); + if ( r != -1 ) { + send_test_elem_offsets[r].push_back( test_e - test_r2p_elem_offsets[r] ); } } - recv_test_elem_offsets.SendRecvArrayEach(send_test_elem_offsets); + recv_test_elem_offsets.SendRecvArrayEach( send_test_elem_offsets ); return recv_test_elem_offsets; } -MPIArray MatrixTransfer::buildRecvTrialElemDofs( - const RedecompMesh& trial_redecomp, - const axom::Array& test_elem_idx, - const axom::Array& trial_elem_idx -) const +MPIArray MatrixTransfer::buildRecvTrialElemDofs( const RedecompMesh& trial_redecomp, + const axom::Array& test_elem_idx, + const axom::Array& trial_elem_idx ) const { - auto recv_trial_elem_dofs = MPIArray(&getMPIUtility()); + auto recv_trial_elem_dofs = MPIArray( &getMPIUtility() ); auto rank = getMPIUtility().MyRank(); auto n_ranks = getMPIUtility().NRanks(); @@ -390,158 +310,131 @@ MPIArray MatrixTransfer::buildRecvTrialElemDofs( // List of trial element offsets sorted by the parent test space rank and the // parent trial space rank it belongs to. Used to get the trial space parent // vdofs (on the parent trial space rank) onto the parent test space rank. - auto send_trial_elem_offsets = MPIArray>(&getMPIUtility()); - // Used to order the trial vdofs by element in the same order as received test + auto send_trial_elem_offsets = MPIArray>( &getMPIUtility() ); + // Used to order the trial vdofs by element in the same order as received test // elements. Stores the ordered trial rank of the vdofs. - auto send_trial_elem_rank = MPIArray(&getMPIUtility()); - // Used to order the trial vdofs by element in the same order as received test + auto send_trial_elem_rank = MPIArray( &getMPIUtility() ); + // Used to order the trial vdofs by element in the same order as received test // elements. Stores the start index and length of the vdofs for each element. - auto send_trial_dof_extents = MPIArray(&getMPIUtility()); + auto send_trial_dof_extents = MPIArray( &getMPIUtility() ); // Total trial element vdofs to be sent to each parent test space rank - auto send_trial_dof_sizes = axom::Array(n_ranks, n_ranks); + auto send_trial_dof_sizes = axom::Array( n_ranks, n_ranks ); // Running count of number of DOFs in each send test/trial rank combo. Used // in send_trial_dof_extents. - auto trial_elem_offsets_dof_ct = MPIArray(&getMPIUtility()); + auto trial_elem_offsets_dof_ct = MPIArray( &getMPIUtility() ); // List of trial element global vtdofs corresponding to the element matrix // entries to send to parent ranks. The second column of send_mat_sizes // determines the offset for each trial element. MPI communication is used // to turn this array into recv_trial_elem_dofs. - auto send_trial_elem_dofs = MPIArray(&getMPIUtility()); + auto send_trial_elem_dofs = MPIArray( &getMPIUtility() ); // allocate space for the above arrays auto est_max_elems = 2 * test_elem_idx.size() / n_ranks; - for (int r{0}; r < n_ranks; ++r) - { - send_trial_elem_offsets[r].resize(n_ranks); + for ( int r{ 0 }; r < n_ranks; ++r ) { + send_trial_elem_offsets[r].resize( n_ranks ); send_trial_elem_offsets[r].shrink(); - for (auto& send_trial_elem_offsets_rank : send_trial_elem_offsets[r]) - { - send_trial_elem_offsets_rank.reserve(est_max_elems / n_ranks); + for ( auto& send_trial_elem_offsets_rank : send_trial_elem_offsets[r] ) { + send_trial_elem_offsets_rank.reserve( est_max_elems / n_ranks ); } - send_trial_elem_rank[r].reserve(est_max_elems); - send_trial_dof_extents[r].reserve(2 * est_max_elems); - trial_elem_offsets_dof_ct[r].resize(n_ranks); + send_trial_elem_rank[r].reserve( est_max_elems ); + send_trial_dof_extents[r].reserve( 2 * est_max_elems ); + trial_elem_offsets_dof_ct[r].resize( n_ranks ); trial_elem_offsets_dof_ct[r].shrink(); } - const auto& trial_r2p_elem_offsets = trial_redecomp. - getRedecompToParentElemOffsets(); + const auto& trial_r2p_elem_offsets = trial_redecomp.getRedecompToParentElemOffsets(); // loop over dense matrices and determine what needs to be sent where - for (int i{0}; i < test_elem_idx.size(); ++i) - { + for ( int i{ 0 }; i < test_elem_idx.size(); ++i ) { auto test_e = test_elem_idx[i]; auto trial_e = trial_elem_idx[i]; - auto n_trial_elem_vdofs = - redecomp_trial_fes_.GetFE(trial_e)->GetDof() * redecomp_trial_fes_.GetVDim(); + auto n_trial_elem_vdofs = redecomp_trial_fes_.GetFE( trial_e )->GetDof() * redecomp_trial_fes_.GetVDim(); // rank is selected by the test element space auto r = test_r2p_elem_rank_[test_e]; // test element is owned by this rank (since -1 denotes a ghost element) - if (r != -1) - { + if ( r != -1 ) { // mark trial elements rank auto trial_r = trial_r2p_elem_rank_[trial_e]; - send_trial_elem_offsets[r][trial_r].push_back(trial_e - trial_r2p_elem_offsets[trial_r]); - send_trial_elem_rank[r].push_back(trial_r); + send_trial_elem_offsets[r][trial_r].push_back( trial_e - trial_r2p_elem_offsets[trial_r] ); + send_trial_elem_rank[r].push_back( trial_r ); auto row = send_trial_dof_extents[r].shape()[0]; - send_trial_dof_extents[r].resize(row + 1, 2); - send_trial_dof_extents[r](row, 0) = trial_elem_offsets_dof_ct[r][trial_r]; - send_trial_dof_extents[r](row, 1) = n_trial_elem_vdofs; + send_trial_dof_extents[r].resize( row + 1, 2 ); + send_trial_dof_extents[r]( row, 0 ) = trial_elem_offsets_dof_ct[r][trial_r]; + send_trial_dof_extents[r]( row, 1 ) = n_trial_elem_vdofs; trial_elem_offsets_dof_ct[r][trial_r] += n_trial_elem_vdofs; send_trial_dof_sizes[r] += n_trial_elem_vdofs; } } // grab test DOFs from the parent rank and return them to the redecomp rank - auto unsorted_trial_dofs = axom::Array>>(n_ranks, n_ranks); - for (auto& test_dof_rank : unsorted_trial_dofs) - { - test_dof_rank = axom::Array>(n_ranks, n_ranks); + auto unsorted_trial_dofs = axom::Array>>( n_ranks, n_ranks ); + for ( auto& test_dof_rank : unsorted_trial_dofs ) { + test_dof_rank = axom::Array>( n_ranks, n_ranks ); } const auto& trial_p2r_elems = trial_redecomp.getParentToRedecompElems(); - for (int dst_test_r{0}; dst_test_r < n_ranks; ++dst_test_r) - { + for ( int dst_test_r{ 0 }; dst_test_r < n_ranks; ++dst_test_r ) { // send parent trial element offsets to parent trial rank - auto recv_trial_elem_offsets = MPIArray(&getMPIUtility()); + auto recv_trial_elem_offsets = MPIArray( &getMPIUtility() ); getMPIUtility().SendRecvEach( - type>(), - [dst_test_r, &send_trial_elem_offsets](axom::IndexType dst_trial_r) - { - return send_trial_elem_offsets[dst_test_r][dst_trial_r]; - }, - [&recv_trial_elem_offsets](axom::Array&& send_vals, axom::IndexType src_trial_r) - { - recv_trial_elem_offsets[src_trial_r] = std::move(send_vals); - } - ); + type>(), + [dst_test_r, &send_trial_elem_offsets]( axom::IndexType dst_trial_r ) { + return send_trial_elem_offsets[dst_test_r][dst_trial_r]; + }, + [&recv_trial_elem_offsets]( axom::Array&& send_vals, axom::IndexType src_trial_r ) { + recv_trial_elem_offsets[src_trial_r] = std::move( send_vals ); + } ); // send parent trial vdofs back to test redecomp rank auto dof_offsets = parent_trial_fes_.GetDofOffsets(); auto first_dof = HYPRE_AssumedPartitionCheck() ? dof_offsets[0] : dof_offsets[rank]; - auto trial_dofs_by_rank = MPIArray(&getMPIUtility()); + auto trial_dofs_by_rank = MPIArray( &getMPIUtility() ); trial_dofs_by_rank.SendRecvEach( - [this, &trial_p2r_elems, &recv_trial_elem_offsets, first_dof](axom::IndexType src_trial_r) - { - auto trial_dofs = axom::Array(); - - const auto& rank_elem_offsets = recv_trial_elem_offsets[src_trial_r]; - auto n_elems = rank_elem_offsets.size(); - if (n_elems > 0) - { - auto elem_id = trial_p2r_elems.first[src_trial_r][rank_elem_offsets[0]]; - auto est_n_dofs = n_elems * parent_trial_fes_.GetFE(elem_id)->GetDof() - * parent_trial_fes_.GetVDim(); - trial_dofs.reserve(est_n_dofs); - for (auto elem_offset : rank_elem_offsets) - { - elem_id = trial_p2r_elems.first[src_trial_r][elem_offset]; - auto n_elem_dofs = parent_trial_fes_.GetFE(elem_id)->GetDof() - * parent_trial_fes_.GetVDim(); - trial_dofs.resize(trial_dofs.size() + n_elem_dofs); - auto dof_array = mfem::Array( - &trial_dofs[trial_dofs.size() - n_elem_dofs], n_elem_dofs); - parent_trial_fes_.GetElementVDofs(elem_id, dof_array); - for (auto& dof : dof_array) - { - dof += first_dof; + [this, &trial_p2r_elems, &recv_trial_elem_offsets, first_dof]( axom::IndexType src_trial_r ) { + auto trial_dofs = axom::Array(); + + const auto& rank_elem_offsets = recv_trial_elem_offsets[src_trial_r]; + auto n_elems = rank_elem_offsets.size(); + if ( n_elems > 0 ) { + auto elem_id = trial_p2r_elems.first[src_trial_r][rank_elem_offsets[0]]; + auto est_n_dofs = n_elems * parent_trial_fes_.GetFE( elem_id )->GetDof() * parent_trial_fes_.GetVDim(); + trial_dofs.reserve( est_n_dofs ); + for ( auto elem_offset : rank_elem_offsets ) { + elem_id = trial_p2r_elems.first[src_trial_r][elem_offset]; + auto n_elem_dofs = parent_trial_fes_.GetFE( elem_id )->GetDof() * parent_trial_fes_.GetVDim(); + trial_dofs.resize( trial_dofs.size() + n_elem_dofs ); + auto dof_array = mfem::Array( &trial_dofs[trial_dofs.size() - n_elem_dofs], n_elem_dofs ); + parent_trial_fes_.GetElementVDofs( elem_id, dof_array ); + for ( auto& dof : dof_array ) { + dof += first_dof; + } } } - } - return trial_dofs; - } - ); - for (int dst_trial_r{0}; dst_trial_r < n_ranks; ++dst_trial_r) - { - unsorted_trial_dofs[dst_test_r][dst_trial_r] - = std::move(trial_dofs_by_rank[dst_trial_r]); + return trial_dofs; + } ); + for ( int dst_trial_r{ 0 }; dst_trial_r < n_ranks; ++dst_trial_r ) { + unsorted_trial_dofs[dst_test_r][dst_trial_r] = std::move( trial_dofs_by_rank[dst_trial_r] ); } } // construct trial vdof vectors for each test rank - for (int test_r{0}; test_r < n_ranks; ++test_r) - { - send_trial_elem_dofs[test_r].reserve(send_trial_dof_sizes[test_r]); + for ( int test_r{ 0 }; test_r < n_ranks; ++test_r ) { + send_trial_elem_dofs[test_r].reserve( send_trial_dof_sizes[test_r] ); auto n_trial_elems = send_trial_elem_rank[test_r].size(); - for (int e{0}; e < n_trial_elems; ++e) - { + for ( int e{ 0 }; e < n_trial_elems; ++e ) { auto trial_elem_dofs = axom::ArrayView( - &unsorted_trial_dofs - [test_r] - [send_trial_elem_rank[test_r][e]] - [send_trial_dof_extents[test_r](e, 0)], - {send_trial_dof_extents[test_r](e, 1)} - ); - send_trial_elem_dofs[test_r].append(trial_elem_dofs); + &unsorted_trial_dofs[test_r][send_trial_elem_rank[test_r][e]][send_trial_dof_extents[test_r]( e, 0 )], + { send_trial_dof_extents[test_r]( e, 1 ) } ); + send_trial_elem_dofs[test_r].append( trial_elem_dofs ); } } - recv_trial_elem_dofs.SendRecvArrayEach(send_trial_elem_dofs); + recv_trial_elem_dofs.SendRecvArrayEach( send_trial_elem_dofs ); return recv_trial_elem_dofs; } const MPIUtility& MatrixTransfer::getMPIUtility() const { - return static_cast(redecomp_test_fes_.GetMesh())->getMPIUtility(); + return static_cast( redecomp_test_fes_.GetMesh() )->getMPIUtility(); } -} // end namespace redecomp +} // end namespace redecomp diff --git a/src/redecomp/transfer/MatrixTransfer.hpp b/src/redecomp/transfer/MatrixTransfer.hpp index 2f953e5b..cc05676b 100644 --- a/src/redecomp/transfer/MatrixTransfer.hpp +++ b/src/redecomp/transfer/MatrixTransfer.hpp @@ -10,8 +10,7 @@ #include "redecomp/common/TypeDefs.hpp" -namespace redecomp -{ +namespace redecomp { class RedecompMesh; @@ -33,23 +32,20 @@ class RedecompMesh; * matrices are supported, necessitating test and trial finite element spaces in * the constructor. */ -class MatrixTransfer -{ -public: +class MatrixTransfer { + public: /** * @brief Construct a new Matrix Transfer object - * + * * @param parent_test_fes Test finite element space on parent mfem::ParMesh * @param parent_trial_fes Trial finite element space on parent mfem::ParMesh * @param redecomp_test_fes Test finite element space on RedecompMesh * @param redecomp_trial_fes Trial finite element space on RedecompMesh */ - MatrixTransfer( - const mfem::ParFiniteElementSpace& parent_test_fes, - const mfem::ParFiniteElementSpace& parent_trial_fes, - const mfem::FiniteElementSpace& redecomp_test_fes, - const mfem::FiniteElementSpace& redecomp_trial_fes - ); + MatrixTransfer( const mfem::ParFiniteElementSpace& parent_test_fes, + const mfem::ParFiniteElementSpace& parent_trial_fes, + const mfem::FiniteElementSpace& redecomp_test_fes, + const mfem::FiniteElementSpace& redecomp_trial_fes ); /** * @brief Transfers element RedecompMesh matrices to parent mfem::ParMesh @@ -65,12 +61,10 @@ class MatrixTransfer * HypreParMatrix returned to transform it to the t-dofs if parallel_assemble * is false. */ - std::unique_ptr TransferToParallel( - const axom::Array& test_elem_idx, - const axom::Array& trial_elem_idx, - const axom::Array& src_elem_mat, - bool parallel_assemble = true - ) const; + std::unique_ptr TransferToParallel( const axom::Array& test_elem_idx, + const axom::Array& trial_elem_idx, + const axom::Array& src_elem_mat, + bool parallel_assemble = true ) const; /** * @brief Transfers element RedecompMesh matrices to parent mfem::ParMesh @@ -84,11 +78,9 @@ class MatrixTransfer * @note Linked-list format is returned here to enable additional matrix * contributions to be added before Finalize() is called on the SparseMatrix */ - mfem::SparseMatrix TransferToParallelSparse( - const axom::Array& test_elem_idx, - const axom::Array& trial_elem_idx, - const axom::Array& src_elem_mat - ) const; + mfem::SparseMatrix TransferToParallelSparse( const axom::Array& test_elem_idx, + const axom::Array& trial_elem_idx, + const axom::Array& src_elem_mat ) const; /** * @brief Converts SparseMatrix from TransferToParallel to HypreParMatrix @@ -102,89 +94,73 @@ class MatrixTransfer * HypreParMatrix returned to transform it to the t-dofs if parallel_assemble * is false. */ - std::unique_ptr ConvertToHypreParMatrix( - mfem::SparseMatrix& sparse, - bool parallel_assemble = true - ) const; + std::unique_ptr ConvertToHypreParMatrix( mfem::SparseMatrix& sparse, + bool parallel_assemble = true ) const; -private: + private: /** * @brief Returns a map of the corresponding parent rank for a given redecomp index - * + * * @param redecomp Redecomp mesh holding elements whose parent rank should be mapped * @param mark_ghost If true, mark ghost element ranks as -1 * @return axom::Array Map indicating parent rank for given redecomp element id */ - axom::Array buildRedecomp2ParentElemRank( - const RedecompMesh& redecomp, - bool mark_ghost - ); + axom::Array buildRedecomp2ParentElemRank( const RedecompMesh& redecomp, bool mark_ghost ); /** * @brief List of entries in test_elem_idx that belong on each parent test space rank * * @param test_elem_idx List of element IDs on the redecomp test space - * @return MPIArray + * @return MPIArray */ - MPIArray buildSendArrayIDs( - const axom::Array& test_elem_idx - ) const; + MPIArray buildSendArrayIDs( const axom::Array& test_elem_idx ) const; /** * @brief Number of matrix entries to be sent to each parent test space rank - * + * * @param test_elem_idx List of element IDs on the redecomp test space * @param trial_elem_idx List of element IDs on the redecomp trial space - * @return axom::Array + * @return axom::Array */ - axom::Array buildSendNumMatEntries( - const axom::Array& test_elem_idx, - const axom::Array& trial_elem_idx - ) const; + axom::Array buildSendNumMatEntries( const axom::Array& test_elem_idx, + const axom::Array& trial_elem_idx ) const; /** * @brief Number of test and trial vdofs received from test space redecomp ranks - * + * * @param test_elem_idx List of element IDs on the redecomp test space * @param trial_elem_idx List of element IDs on the redecomp trial space - * @return MPIArray + * @return MPIArray */ - MPIArray buildRecvMatSizes( - const axom::Array& test_elem_idx, - const axom::Array& trial_elem_idx - ) const; + MPIArray buildRecvMatSizes( const axom::Array& test_elem_idx, + const axom::Array& trial_elem_idx ) const; /** * @brief List of test element offsets received from test space redecomp ranks - * + * * @param test_redecomp Redecomp mesh of the test space * @param test_elem_idx List of element IDs on the redecomp test space - * @return MPIArray + * @return MPIArray */ - MPIArray buildRecvTestElemOffsets( - const RedecompMesh& test_redecomp, - const axom::Array& test_elem_idx - ) const; + MPIArray buildRecvTestElemOffsets( const RedecompMesh& test_redecomp, + const axom::Array& test_elem_idx ) const; /** * @brief List of trial element global vdofs corresponding to the element matrix * entries received from redecomp ranks - * + * * @param trial_redecomp Redecomp mesh of the trial space * @param test_elem_idx List of element IDs on the redecomp test space * @param trial_elem_idx List of element IDs on the redecomp trial space - * @return MPIArray + * @return MPIArray */ - MPIArray buildRecvTrialElemDofs( - const RedecompMesh& trial_redecomp, - const axom::Array& test_elem_idx, - const axom::Array& trial_elem_idx - ) const; + MPIArray buildRecvTrialElemDofs( const RedecompMesh& trial_redecomp, const axom::Array& test_elem_idx, + const axom::Array& trial_elem_idx ) const; /** * @brief Returns MPIUtility pointer for the MatrixTransfer object - * - * @return const MPIUtility* + * + * @return const MPIUtility* */ const MPIUtility& getMPIUtility() const; @@ -221,9 +197,8 @@ class MatrixTransfer * Note: r2p = redecomp to parent */ axom::Array trial_r2p_elem_rank_; - }; -} // end namespace redecomp +} // end namespace redecomp #endif /* SRC_REDECOMP_MATRIXTRANSFER_HPP_ */ diff --git a/src/redecomp/transfer/SparseMatrixTransfer.cpp b/src/redecomp/transfer/SparseMatrixTransfer.cpp index 4f498185..100e3936 100644 --- a/src/redecomp/transfer/SparseMatrixTransfer.cpp +++ b/src/redecomp/transfer/SparseMatrixTransfer.cpp @@ -13,84 +13,81 @@ #include "redecomp/utils/MPIArray.hpp" #include "redecomp/RedecompMesh.hpp" -namespace redecomp -{ +namespace redecomp { -SparseMatrixTransfer::SparseMatrixTransfer( - const mfem::ParFiniteElementSpace& parent_test_space, - const mfem::ParFiniteElementSpace& parent_trial_space, - const mfem::FiniteElementSpace& redecomp_test_space, - const mfem::FiniteElementSpace& redecomp_trial_space -) -: parent_test_space_ { parent_test_space }, - parent_trial_space_ { parent_trial_space }, - redecomp_test_space_ { redecomp_test_space }, - redecomp_trial_space_ { redecomp_trial_space }, - redecomp_test_mesh_ { getRedecompMesh(redecomp_test_space_) }, - redecomp_trial_mesh_ { getRedecompMesh(redecomp_trial_space_) } +SparseMatrixTransfer::SparseMatrixTransfer( const mfem::ParFiniteElementSpace& parent_test_space, + const mfem::ParFiniteElementSpace& parent_trial_space, + const mfem::FiniteElementSpace& redecomp_test_space, + const mfem::FiniteElementSpace& redecomp_trial_space ) + : parent_test_space_{ parent_test_space }, + parent_trial_space_{ parent_trial_space }, + redecomp_test_space_{ redecomp_test_space }, + redecomp_trial_space_{ redecomp_trial_space }, + redecomp_test_mesh_{ getRedecompMesh( redecomp_test_space_ ) }, + redecomp_trial_mesh_{ getRedecompMesh( redecomp_trial_space_ ) } { - SLIC_ERROR_ROOT_IF(static_cast(&redecomp_test_mesh_.getParent()) != parent_test_space_.GetMesh(), - "The parent test finite element space mesh must be linked to the test Redecomp mesh."); - SLIC_ERROR_ROOT_IF(static_cast(&redecomp_trial_mesh_.getParent()) != parent_trial_space_.GetMesh(), - "The parent trial finite element space mesh must be linked to the trial Redecomp mesh."); - SLIC_ERROR_ROOT_IF(&redecomp_test_mesh_.getMPIUtility().MPIComm() != &redecomp_trial_mesh_.getMPIUtility().MPIComm(), - "MPI Communicator must match in test and trial spaces."); + SLIC_ERROR_ROOT_IF( + static_cast( &redecomp_test_mesh_.getParent() ) != parent_test_space_.GetMesh(), + "The parent test finite element space mesh must be linked to the test Redecomp mesh." ); + SLIC_ERROR_ROOT_IF( + static_cast( &redecomp_trial_mesh_.getParent() ) != parent_trial_space_.GetMesh(), + "The parent trial finite element space mesh must be linked to the trial Redecomp mesh." ); + SLIC_ERROR_ROOT_IF( &redecomp_test_mesh_.getMPIUtility().MPIComm() != &redecomp_trial_mesh_.getMPIUtility().MPIComm(), + "MPI Communicator must match in test and trial spaces." ); // one value per element for initial implementation - SLIC_ERROR_ROOT_IF(!parent_test_space_.IsDGSpace() || !parent_trial_space_.IsDGSpace() || - !redecomp_test_space_.IsDGSpace() || !redecomp_trial_space_.IsDGSpace(), - "Only DG (L2) spaces are currently supported."); - SLIC_ERROR_ROOT_IF(parent_test_space_.GetMaxElementOrder() != 0 || parent_trial_space_.GetMaxElementOrder() != 0 || - redecomp_test_space_.GetMaxElementOrder() != 0 || redecomp_trial_space_.GetMaxElementOrder() != 0, - "Only one value per element (zero-th order) is currently supported."); - SLIC_ERROR_ROOT_IF(parent_test_space_.GetVDim() != 1 || parent_trial_space_.GetVDim() != 1 || - redecomp_test_space_.GetVDim() != 1 || redecomp_trial_space_.GetVDim() != 1, - "Only one value per element (zero-th order) is currently supported."); + SLIC_ERROR_ROOT_IF( !parent_test_space_.IsDGSpace() || !parent_trial_space_.IsDGSpace() || + !redecomp_test_space_.IsDGSpace() || !redecomp_trial_space_.IsDGSpace(), + "Only DG (L2) spaces are currently supported." ); + SLIC_ERROR_ROOT_IF( parent_test_space_.GetMaxElementOrder() != 0 || parent_trial_space_.GetMaxElementOrder() != 0 || + redecomp_test_space_.GetMaxElementOrder() != 0 || + redecomp_trial_space_.GetMaxElementOrder() != 0, + "Only one value per element (zero-th order) is currently supported." ); + SLIC_ERROR_ROOT_IF( parent_test_space_.GetVDim() != 1 || parent_trial_space_.GetVDim() != 1 || + redecomp_test_space_.GetVDim() != 1 || redecomp_trial_space_.GetVDim() != 1, + "Only one value per element (zero-th order) is currently supported." ); } -std::unique_ptr SparseMatrixTransfer::TransferToParallel( - const mfem::SparseMatrix& src -) const +std::unique_ptr SparseMatrixTransfer::TransferToParallel( const mfem::SparseMatrix& src ) const { // check sizing on src - SLIC_ERROR_ROOT_IF(src.Height() != redecomp_test_space_.GetVSize(), - "The number of rows in src must match the size of the test FiniteElementSpace."); - SLIC_ERROR_ROOT_IF(src.Width() != redecomp_trial_space_.GetVSize(), - "The number of columns in src must match the size of the trial FiniteElementSpace."); - + SLIC_ERROR_ROOT_IF( src.Height() != redecomp_test_space_.GetVSize(), + "The number of rows in src must match the size of the test FiniteElementSpace." ); + SLIC_ERROR_ROOT_IF( src.Width() != redecomp_trial_space_.GetVSize(), + "The number of columns in src must match the size of the trial FiniteElementSpace." ); + // we want a csr matrix. make sure src is finalized - SLIC_ERROR_ROOT_IF(!src.Finalized(), "src must be finalized first."); + SLIC_ERROR_ROOT_IF( !src.Finalized(), "src must be finalized first." ); auto my_rank = getMPIUtility().MyRank(); auto n_ranks = getMPIUtility().NRanks(); - + // The corresponding parent rank of redecomp mesh elements are ordered, i.e. the parent rank of redecomp element i-1 // is <= the parent rank of redecomp element i. As a result, the map from redecomp elements to parent ranks only // stores element offsets. Elements that lie in the ghost region (ghost elements) are stored as an array of arrays, // with the first index corresponding to the parent rank and the nested array holding sorted ghost element indices. // package the entries to send to other ranks - MPIArray recv_matrix_data(&getMPIUtility()); - MPIArray recv_parent_local_row(&getMPIUtility()); - MPIArray recv_parent_local_col(&getMPIUtility()); - MPIArray recv_parent_col_ranks(&getMPIUtility()); + MPIArray recv_matrix_data( &getMPIUtility() ); + MPIArray recv_parent_local_row( &getMPIUtility() ); + MPIArray recv_parent_local_col( &getMPIUtility() ); + MPIArray recv_parent_col_ranks( &getMPIUtility() ); { // values to send to parent ranks - MPIArray send_matrix_data(&getMPIUtility()); + MPIArray send_matrix_data( &getMPIUtility() ); // indices in redecomp_mesh_.p2r_elems_ (to obtain parent element id) - MPIArray send_parent_local_row_offsets(&getMPIUtility()); + MPIArray send_parent_local_row_offsets( &getMPIUtility() ); // indices in redecomp_mesh_.p2r_elems_ (to obtain parent element id) - MPIArray send_parent_local_col_offsets(&getMPIUtility()); + MPIArray send_parent_local_col_offsets( &getMPIUtility() ); // parent rank which owns the column (trial) space element. we need this to convert offsets to parent element ids. - MPIArray send_parent_col_ranks(&getMPIUtility()); + MPIArray send_parent_col_ranks( &getMPIUtility() ); // guess size and preallocate auto approx_size = 2 * src.NumNonZeroElems() / n_ranks; - for (int r{0}; r < n_ranks; ++r) - { - send_matrix_data[r].reserve(approx_size); - send_parent_local_row_offsets[r].reserve(approx_size); - send_parent_local_col_offsets[r].reserve(approx_size); - send_parent_col_ranks[r].reserve(approx_size); + for ( int r{ 0 }; r < n_ranks; ++r ) { + send_matrix_data[r].reserve( approx_size ); + send_parent_local_row_offsets[r].reserve( approx_size ); + send_parent_local_col_offsets[r].reserve( approx_size ); + send_parent_col_ranks[r].reserve( approx_size ); } auto src_I = src.GetI(); @@ -105,19 +102,16 @@ std::unique_ptr SparseMatrixTransfer::TransferToParallel( auto test_parent_rank = 0; // tracks index of ghost elements for each rank auto test_ghost_ct = 0; - for (int i{0}; i < src.Height(); ++i) - { - const auto test_elem_id = i; // assumes 1 point per element - while (test_elem_id == test_elem_offsets[test_parent_rank + 1]) - { + for ( int i{ 0 }; i < src.Height(); ++i ) { + const auto test_elem_id = i; // assumes 1 point per element + while ( test_elem_id == test_elem_offsets[test_parent_rank + 1] ) { ++test_parent_rank; // the rank increased, reset the ghost element index test_ghost_ct = 0; } // skip unowned elements - if (test_ghost_ct < test_ghost_elems[test_parent_rank].size() - && test_elem_id == test_ghost_elems[test_parent_rank][test_ghost_ct]) - { + if ( test_ghost_ct < test_ghost_elems[test_parent_rank].size() && + test_elem_id == test_ghost_elems[test_parent_rank][test_ghost_ct] ) { // the element is a ghost on this rank. increase the index to check for the next ghost (since they are sorted in // ascending order). ++test_ghost_ct; @@ -126,75 +120,65 @@ std::unique_ptr SparseMatrixTransfer::TransferToParallel( // at this point, we know the element in the row space is own by the redecomp mesh. loop through the column (trial // space) data in the SparseMatrix for this row. auto trial_parent_rank = 0; - for (int Ij{src_I[i]}; Ij < src_I[i+1]; ++Ij) - { - const int j{src_J[Ij]}; - const auto trial_elem_id = j; // assumes 1 point per element + for ( int Ij{ src_I[i] }; Ij < src_I[i + 1]; ++Ij ) { + const int j{ src_J[Ij] }; + const auto trial_elem_id = j; // assumes 1 point per element // use the offset data to find the parent rank where this element is stored. - while (trial_elem_id >= trial_elem_offsets[trial_parent_rank + 1]) - { + while ( trial_elem_id >= trial_elem_offsets[trial_parent_rank + 1] ) { ++trial_parent_rank; } // if the entries in J aren't ordered, we might need to go backwards too - while (trial_parent_rank > 0 && trial_elem_id < trial_elem_offsets[trial_parent_rank]) - { + while ( trial_parent_rank > 0 && trial_elem_id < trial_elem_offsets[trial_parent_rank] ) { --trial_parent_rank; } // we have everything now. package it up into send arrays - send_matrix_data[test_parent_rank].push_back(src_data[Ij]); - send_parent_local_row_offsets[test_parent_rank].push_back(test_elem_id - test_elem_offsets[test_parent_rank]); - send_parent_local_col_offsets[test_parent_rank].push_back(trial_elem_id - trial_elem_offsets[trial_parent_rank]); - send_parent_col_ranks[test_parent_rank].push_back(trial_parent_rank); + send_matrix_data[test_parent_rank].push_back( src_data[Ij] ); + send_parent_local_row_offsets[test_parent_rank].push_back( test_elem_id - test_elem_offsets[test_parent_rank] ); + send_parent_local_col_offsets[test_parent_rank].push_back( trial_elem_id - + trial_elem_offsets[trial_parent_rank] ); + send_parent_col_ranks[test_parent_rank].push_back( trial_parent_rank ); } } // convert local col offsets to parent element ids: MPI transfer to the parent rank where the element lives, then // use the p2r_elems_ map to convert to an element ID - for (int r{0}; r < n_ranks; ++r) - { + for ( int r{ 0 }; r < n_ranks; ++r ) { // do per-rank communication to transform local_col from offsets to element ids - auto send_col_offsets_by_rank = MPIArray(&getMPIUtility()); + auto send_col_offsets_by_rank = MPIArray( &getMPIUtility() ); // guess size and preallocate auto approx_rank_size = 2 * send_parent_local_col_offsets[r].size() / n_ranks; - for (auto& send_col_offsets : send_col_offsets_by_rank) - { - send_col_offsets.reserve(approx_rank_size); + for ( auto& send_col_offsets : send_col_offsets_by_rank ) { + send_col_offsets.reserve( approx_rank_size ); } - for (int i{0}; i < send_parent_local_col_offsets[r].size(); ++i) - { - send_col_offsets_by_rank[send_parent_col_ranks[r][i]].push_back(send_parent_local_col_offsets[r][i]); + for ( int i{ 0 }; i < send_parent_local_col_offsets[r].size(); ++i ) { + send_col_offsets_by_rank[send_parent_col_ranks[r][i]].push_back( send_parent_local_col_offsets[r][i] ); } - auto recv_col_offsets_by_rank = MPIArray(&getMPIUtility()); - recv_col_offsets_by_rank.SendRecvArrayEach(send_col_offsets_by_rank); + auto recv_col_offsets_by_rank = MPIArray( &getMPIUtility() ); + recv_col_offsets_by_rank.SendRecvArrayEach( send_col_offsets_by_rank ); // convert (now on-rank) offsets to element ids - for (int trial_r{0}; trial_r < n_ranks; ++trial_r) - { - for (auto& recv_col_offset : recv_col_offsets_by_rank[trial_r]) - { + for ( int trial_r{ 0 }; trial_r < n_ranks; ++trial_r ) { + for ( auto& recv_col_offset : recv_col_offsets_by_rank[trial_r] ) { recv_col_offset = redecomp_trial_mesh_.getParentToRedecompElems().first[trial_r][recv_col_offset]; } } // send back to original rank - send_col_offsets_by_rank.SendRecvArrayEach(recv_col_offsets_by_rank); + send_col_offsets_by_rank.SendRecvArrayEach( recv_col_offsets_by_rank ); // fill send_parent_local_col_offsets with local element ids - axom::Array per_rank_ct(n_ranks, n_ranks); - for (int i{0}; i < send_parent_local_col_offsets[r].size(); ++i) - { + axom::Array per_rank_ct( n_ranks, n_ranks ); + for ( int i{ 0 }; i < send_parent_local_col_offsets[r].size(); ++i ) { auto col_rank = send_parent_col_ranks[r][i]; send_parent_local_col_offsets[r][i] = send_col_offsets_by_rank[col_rank][per_rank_ct[col_rank]++]; } } // do MPI communication - recv_matrix_data.SendRecvArrayEach(send_matrix_data); + recv_matrix_data.SendRecvArrayEach( send_matrix_data ); // NOTE: these hold offsets now; need to convert these to element numbers - recv_parent_local_row.SendRecvArrayEach(send_parent_local_row_offsets); + recv_parent_local_row.SendRecvArrayEach( send_parent_local_row_offsets ); // NOTE: the send values were converted from offsets to element numbers in the block above - recv_parent_local_col.SendRecvArrayEach(send_parent_local_col_offsets); - recv_parent_col_ranks.SendRecvArrayEach(send_parent_col_ranks); + recv_parent_local_col.SendRecvArrayEach( send_parent_local_col_offsets ); + recv_parent_col_ranks.SendRecvArrayEach( send_parent_col_ranks ); // convert local row offsets to element numbers - for (int r{0}; r < n_ranks; ++r) - { - for (auto& row_elem : recv_parent_local_row[r]) - { + for ( int r{ 0 }; r < n_ranks; ++r ) { + for ( auto& row_elem : recv_parent_local_row[r] ) { row_elem = redecomp_test_mesh_.getParentToRedecompElems().first[r][row_elem]; } } @@ -204,84 +188,70 @@ std::unique_ptr SparseMatrixTransfer::TransferToParallel( // on-rank and offd holds data with rows on-rank and cols off-rank. the HypreParMatrix constructor that takes // diagonal and offdiagonal CSR contributions is used. see MFEM documentation and hypre documentation on the // hypre_ParCSRMatrix for more details. - auto col_starts = BuildParentElementRankOffsets(redecomp_trial_mesh_); + auto col_starts = BuildParentElementRankOffsets( redecomp_trial_mesh_ ); // size i_diag and i_offd, number of columms per row of matrix, and build a global dof to local offd column map auto diag_nnz = 0; auto offd_nnz = 0; // NOTE: these will be owned by the HypreParMatrix. using mfem::Memory, the data will not be deleted upon destruction. - mfem::Memory i_diag(parent_test_space_.GetVSize() + 1); - mfem::Memory i_offd(parent_test_space_.GetVSize() + 1); - for (int i{0}; i < parent_test_space_.GetVSize() + 1; ++i) - { + mfem::Memory i_diag( parent_test_space_.GetVSize() + 1 ); + mfem::Memory i_offd( parent_test_space_.GetVSize() + 1 ); + for ( int i{ 0 }; i < parent_test_space_.GetVSize() + 1; ++i ) { i_diag[i] = 0; i_offd[i] = 0; } // maps from global j to order of appearance from received data. this is used to map offdiagonal J values from the // local offdiagonal matrix to the global offdiagonal matrix. std::map cmap_j_offd; - for (int r{0}; r < n_ranks; ++r) - { - for (int i{0}; i < recv_parent_col_ranks[r].size(); ++i) - { + for ( int r{ 0 }; r < n_ranks; ++r ) { + for ( int i{ 0 }; i < recv_parent_col_ranks[r].size(); ++i ) { auto col_rank = recv_parent_col_ranks[r][i]; - if (col_rank == my_rank) - { + if ( col_rank == my_rank ) { ++diag_nnz; ++i_diag[recv_parent_local_row[r][i] + 1]; - } - else - { + } else { ++offd_nnz; ++i_offd[recv_parent_local_row[r][i] + 1]; - cmap_j_offd.insert(std::make_pair(col_starts[col_rank] + recv_parent_local_col[r][i], cmap_j_offd.size())); + cmap_j_offd.insert( std::make_pair( col_starts[col_rank] + recv_parent_local_col[r][i], cmap_j_offd.size() ) ); } } } // sum up i_diag and i_offd. the definition of i_diag and i_offd is complete after summation. - for (int i{0}; i < parent_test_space_.GetVSize(); ++i) - { - i_diag[i+1] += i_diag[i]; - i_offd[i+1] += i_offd[i]; + for ( int i{ 0 }; i < parent_test_space_.GetVSize(); ++i ) { + i_diag[i + 1] += i_diag[i]; + i_offd[i + 1] += i_offd[i]; } - SLIC_ASSERT(i_diag[parent_test_space_.GetVSize()] == diag_nnz); - SLIC_ASSERT(i_offd[parent_test_space_.GetVSize()] == offd_nnz); + SLIC_ASSERT( i_diag[parent_test_space_.GetVSize()] == diag_nnz ); + SLIC_ASSERT( i_offd[parent_test_space_.GetVSize()] == offd_nnz ); // re-order cmap_j_offd { auto offd_ct = 0; - for (auto& cmap_val : cmap_j_offd) - { + for ( auto& cmap_val : cmap_j_offd ) { cmap_val.second = offd_ct++; } } // create cmap, offd column map from local to global element IDs - mfem::Memory cmap(cmap_j_offd.size()); - for (auto& cmap_val : cmap_j_offd) - { + mfem::Memory cmap( cmap_j_offd.size() ); + for ( auto& cmap_val : cmap_j_offd ) { cmap[cmap_val.second] = cmap_val.first; } // populate j_diag and j_offd, the columns for each row (row offsets given by i). will not be sorted (i.e. larger // column indices may appear before smaller ones in the same row) - axom::Array i_diag_ct(parent_test_space_.GetVSize(), parent_test_space_.GetVSize()); - axom::Array i_offd_ct(parent_test_space_.GetVSize(), parent_test_space_.GetVSize()); - mfem::Memory j_diag(diag_nnz); - mfem::Memory data_diag(diag_nnz); - mfem::Memory j_offd(offd_nnz); - mfem::Memory data_offd(offd_nnz); - for (int r{0}; r < n_ranks; ++r) - { - for (int i{0}; i < recv_parent_col_ranks[r].size(); ++i) - { + axom::Array i_diag_ct( parent_test_space_.GetVSize(), parent_test_space_.GetVSize() ); + axom::Array i_offd_ct( parent_test_space_.GetVSize(), parent_test_space_.GetVSize() ); + mfem::Memory j_diag( diag_nnz ); + mfem::Memory data_diag( diag_nnz ); + mfem::Memory j_offd( offd_nnz ); + mfem::Memory data_offd( offd_nnz ); + for ( int r{ 0 }; r < n_ranks; ++r ) { + for ( int i{ 0 }; i < recv_parent_col_ranks[r].size(); ++i ) { auto curr_row = recv_parent_local_row[r][i]; auto curr_col = recv_parent_local_col[r][i]; auto col_rank = recv_parent_col_ranks[r][i]; - if (col_rank == my_rank) - { + if ( col_rank == my_rank ) { j_diag[i_diag[curr_row] + i_diag_ct[curr_row]] = curr_col; data_diag[i_diag[curr_row] + i_diag_ct[curr_row]] = recv_matrix_data[r][i]; ++i_diag_ct[curr_row]; - } - else - { + } else { j_offd[i_offd[curr_row] + i_offd_ct[curr_row]] = cmap_j_offd[col_starts[col_rank] + curr_col]; data_offd[i_offd[curr_row] + i_offd_ct[curr_row]] = recv_matrix_data[r][i]; ++i_offd_ct[curr_row]; @@ -289,37 +259,32 @@ std::unique_ptr SparseMatrixTransfer::TransferToParallel( } } // create hypre par matrix - return std::make_unique(getMPIUtility().MPIComm(), - parent_test_space_.GlobalVSize(), parent_trial_space_.GlobalVSize(), - parent_test_space_.GetDofOffsets(), parent_trial_space_.GetDofOffsets(), - i_diag, j_diag, data_diag, i_offd, j_offd, data_offd, cmap_j_offd.size(), cmap - ); + return std::make_unique( getMPIUtility().MPIComm(), parent_test_space_.GlobalVSize(), + parent_trial_space_.GlobalVSize(), parent_test_space_.GetDofOffsets(), + parent_trial_space_.GetDofOffsets(), i_diag, j_diag, data_diag, i_offd, + j_offd, data_offd, cmap_j_offd.size(), cmap ); } -const RedecompMesh& SparseMatrixTransfer::getRedecompMesh(const mfem::FiniteElementSpace& fe_space) +const RedecompMesh& SparseMatrixTransfer::getRedecompMesh( const mfem::FiniteElementSpace& fe_space ) { - auto redecomp_mesh = dynamic_cast(fe_space.GetMesh()); - SLIC_ERROR_ROOT_IF(redecomp_mesh == nullptr, "The finite element space must have a Redecomp mesh."); + auto redecomp_mesh = dynamic_cast( fe_space.GetMesh() ); + SLIC_ERROR_ROOT_IF( redecomp_mesh == nullptr, "The finite element space must have a Redecomp mesh." ); return *redecomp_mesh; } -axom::Array SparseMatrixTransfer::BuildParentElementRankOffsets(const RedecompMesh& redecomp_mesh) +axom::Array SparseMatrixTransfer::BuildParentElementRankOffsets( const RedecompMesh& redecomp_mesh ) { auto n_ranks = redecomp_mesh.getMPIUtility().NRanks(); auto my_rank = redecomp_mesh.getMPIUtility().MyRank(); - axom::Array elem_offsets(n_ranks + 1, n_ranks + 1); + axom::Array elem_offsets( n_ranks + 1, n_ranks + 1 ); elem_offsets[my_rank + 1] = redecomp_mesh.getParent().GetNE(); - redecomp_mesh.getMPIUtility().Allreduce(&elem_offsets, MPI_SUM); - for (int r{2}; r < elem_offsets.size(); ++r) - { + redecomp_mesh.getMPIUtility().Allreduce( &elem_offsets, MPI_SUM ); + for ( int r{ 2 }; r < elem_offsets.size(); ++r ) { elem_offsets[r] += elem_offsets[r - 1]; } return elem_offsets; } -const MPIUtility& SparseMatrixTransfer::getMPIUtility() const -{ - return redecomp_test_mesh_.getMPIUtility(); -} +const MPIUtility& SparseMatrixTransfer::getMPIUtility() const { return redecomp_test_mesh_.getMPIUtility(); } -} // end namespace redecomp +} // end namespace redecomp diff --git a/src/redecomp/transfer/SparseMatrixTransfer.hpp b/src/redecomp/transfer/SparseMatrixTransfer.hpp index cbf26f31..5ba6a1e6 100644 --- a/src/redecomp/transfer/SparseMatrixTransfer.hpp +++ b/src/redecomp/transfer/SparseMatrixTransfer.hpp @@ -12,8 +12,7 @@ #include "axom/core.hpp" -namespace redecomp -{ +namespace redecomp { class MPIUtility; class RedecompMesh; @@ -28,23 +27,20 @@ class RedecompMesh; * * The class currently supports order 0 L2 fields (one value per element). */ -class SparseMatrixTransfer -{ -public: +class SparseMatrixTransfer { + public: /** * @brief Construct a new SparseMatrixTransfer object - * + * * @param parent_test_space Test finite element space on parent mfem::ParMesh * @param parent_trial_space Trial finite element space on parent mfem::ParMesh * @param redecomp_test_space Test finite element space on RedecompMesh * @param redecomp_trial_space Trial finite element space on RedecompMesh */ - SparseMatrixTransfer( - const mfem::ParFiniteElementSpace& parent_test_space, - const mfem::ParFiniteElementSpace& parent_trial_space, - const mfem::FiniteElementSpace& redecomp_test_space, - const mfem::FiniteElementSpace& redecomp_trial_space - ); + SparseMatrixTransfer( const mfem::ParFiniteElementSpace& parent_test_space, + const mfem::ParFiniteElementSpace& parent_trial_space, + const mfem::FiniteElementSpace& redecomp_test_space, + const mfem::FiniteElementSpace& redecomp_trial_space ); /** * @brief Transfers RedecompMesh sparse matrix to parent mfem::ParMesh hypre par matrix @@ -55,32 +51,32 @@ class SparseMatrixTransfer * * @note The HypreParMatrix is on the global dofs. Apply mfem::RAP to reduce to tdofs. */ - std::unique_ptr TransferToParallel(const mfem::SparseMatrix& src) const; + std::unique_ptr TransferToParallel( const mfem::SparseMatrix& src ) const; -private: + private: /** * @brief Get the RedecompMesh from the finite element space * * This method verifies the finite element space does have a RedecompMesh before returning. Finite element spaces with * no RedecompMesh trigger a SLIC_ERROR macro. - * + * * @param fe_space Finite element space linked to a RedecompMesh * @return const RedecompMesh& Redecomp mesh associated with finite element space */ - static const RedecompMesh& getRedecompMesh(const mfem::FiniteElementSpace& fe_space); + static const RedecompMesh& getRedecompMesh( const mfem::FiniteElementSpace& fe_space ); /** * @brief Build vector of parent element offsets for each MPI rank - * + * * @param redecomp_mesh Offset vector is computed on the parent-linked mesh of this mesh * @return Array of element offsets for each MPI rank */ - static axom::Array BuildParentElementRankOffsets(const RedecompMesh& redecomp_mesh); + static axom::Array BuildParentElementRankOffsets( const RedecompMesh& redecomp_mesh ); /** * @brief Returns MPIUtility pointer for the MatrixTransfer object - * - * @return const MPIUtility* + * + * @return const MPIUtility* */ const MPIUtility& getMPIUtility() const; @@ -113,9 +109,8 @@ class SparseMatrixTransfer * @brief Trial RedecompMesh */ const RedecompMesh& redecomp_trial_mesh_; - }; -} // end namespace redecomp +} // end namespace redecomp #endif /* SRC_REDECOMP_SPARSEMATRIXTRANSFER_HPP_ */ diff --git a/src/redecomp/transfer/TransferByElements.cpp b/src/redecomp/transfer/TransferByElements.cpp index 63842a72..c16a9573 100644 --- a/src/redecomp/transfer/TransferByElements.cpp +++ b/src/redecomp/transfer/TransferByElements.cpp @@ -9,152 +9,123 @@ #include "redecomp/RedecompMesh.hpp" -namespace redecomp -{ +namespace redecomp { -void TransferByElements::TransferToSerial( - const mfem::ParGridFunction& src, - mfem::GridFunction& dst -) const +void TransferByElements::TransferToSerial( const mfem::ParGridFunction& src, mfem::GridFunction& dst ) const { // checks to make sure src and dst are valid - auto redecomp = dynamic_cast(dst.FESpace()->GetMesh()); - SLIC_ERROR_ROOT_IF(redecomp == nullptr, - "The Mesh of GridFunction dst must be a Redecomp mesh."); - SLIC_ERROR_ROOT_IF(src.ParFESpace()->GetParMesh() != &redecomp->getParent(), - "The Meshes of the specified GridFunctions are not related in a " - "Redecomp -> ParMesh relationship."); - SLIC_ERROR_ROOT_IF(strcmp(src.FESpace()->FEColl()->Name(), - dst.FESpace()->FEColl()->Name()) != 0, - "The FiniteElementCollections of the specified GridFunctions are not " - "the same."); - SLIC_ERROR_ROOT_IF(src.FESpace()->GetVDim() != dst.FESpace()->GetVDim(), - "The vdim of the FiniteElementSpaces of the specified GridFunctions are " - "not the same."); + auto redecomp = dynamic_cast( dst.FESpace()->GetMesh() ); + SLIC_ERROR_ROOT_IF( redecomp == nullptr, "The Mesh of GridFunction dst must be a Redecomp mesh." ); + SLIC_ERROR_ROOT_IF( src.ParFESpace()->GetParMesh() != &redecomp->getParent(), + "The Meshes of the specified GridFunctions are not related in a " + "Redecomp -> ParMesh relationship." ); + SLIC_ERROR_ROOT_IF( strcmp( src.FESpace()->FEColl()->Name(), dst.FESpace()->FEColl()->Name() ) != 0, + "The FiniteElementCollections of the specified GridFunctions are not " + "the same." ); + SLIC_ERROR_ROOT_IF( src.FESpace()->GetVDim() != dst.FESpace()->GetVDim(), + "The vdim of the FiniteElementSpaces of the specified GridFunctions are " + "not the same." ); // send and receive DOF values from other ranks - auto dst_dofs = MPIArray(&redecomp->getMPIUtility()); - dst_dofs.SendRecvEach( - [redecomp, &src](int dest) - { - auto src_dofs = axom::Array(); - const auto& src_elem_idx = redecomp->getParentToRedecompElems().first[dest]; - auto n_els = src_elem_idx.size(); - if (n_els > 0) - { - auto elem_vdofs = mfem::Array(); - auto dof_vals = mfem::Vector(); - // guess the size of src_dofs based on the size of the first element - src.FESpace()->GetElementVDofs(src_elem_idx[0], elem_vdofs); - src_dofs.reserve(elem_vdofs.Size()*n_els); - auto vdof_ct = 0; - for (int e{0}; e < n_els; ++e) - { - src.FESpace()->GetElementVDofs(src_elem_idx[e], elem_vdofs); - src.GetSubVector(elem_vdofs, dof_vals); - src_dofs.insert(vdof_ct, dof_vals.Size(), dof_vals.GetData()); - vdof_ct += dof_vals.Size(); - } + auto dst_dofs = MPIArray( &redecomp->getMPIUtility() ); + dst_dofs.SendRecvEach( [redecomp, &src]( int dest ) { + auto src_dofs = axom::Array(); + const auto& src_elem_idx = redecomp->getParentToRedecompElems().first[dest]; + auto n_els = src_elem_idx.size(); + if ( n_els > 0 ) { + auto elem_vdofs = mfem::Array(); + auto dof_vals = mfem::Vector(); + // guess the size of src_dofs based on the size of the first element + src.FESpace()->GetElementVDofs( src_elem_idx[0], elem_vdofs ); + src_dofs.reserve( elem_vdofs.Size() * n_els ); + auto vdof_ct = 0; + for ( int e{ 0 }; e < n_els; ++e ) { + src.FESpace()->GetElementVDofs( src_elem_idx[e], elem_vdofs ); + src.GetSubVector( elem_vdofs, dof_vals ); + src_dofs.insert( vdof_ct, dof_vals.Size(), dof_vals.GetData() ); + vdof_ct += dof_vals.Size(); } - return src_dofs; } - ); + return src_dofs; + } ); // map received DOF values to local DOFs auto elem_vdofs = mfem::Array(); auto n_ranks = redecomp->getMPIUtility().NRanks(); - for (int r{0}; r < n_ranks; ++r) - { + for ( int r{ 0 }; r < n_ranks; ++r ) { auto vdof_ct = 0; auto first_el = redecomp->getRedecompToParentElemOffsets()[r]; - auto last_el = redecomp->getRedecompToParentElemOffsets()[r+1]; - for (int e{first_el}; e < last_el; ++e) - { - dst.FESpace()->GetElementVDofs(e, elem_vdofs); - auto dof_vals = mfem::Vector(&dst_dofs[r][vdof_ct], elem_vdofs.Size()); - dst.SetSubVector(elem_vdofs, dof_vals); + auto last_el = redecomp->getRedecompToParentElemOffsets()[r + 1]; + for ( int e{ first_el }; e < last_el; ++e ) { + dst.FESpace()->GetElementVDofs( e, elem_vdofs ); + auto dof_vals = mfem::Vector( &dst_dofs[r][vdof_ct], elem_vdofs.Size() ); + dst.SetSubVector( elem_vdofs, dof_vals ); vdof_ct += elem_vdofs.Size(); } } } -void TransferByElements::TransferToParallel( - const mfem::GridFunction& src, - mfem::ParGridFunction& dst -) const +void TransferByElements::TransferToParallel( const mfem::GridFunction& src, mfem::ParGridFunction& dst ) const { // checks to make sure src and dst are valid - auto redecomp = dynamic_cast(src.FESpace()->GetMesh()); - SLIC_ERROR_ROOT_IF(redecomp == nullptr, - "The Mesh of GridFunction dst must be a Redecomp mesh."); - SLIC_ERROR_ROOT_IF(dst.ParFESpace()->GetParMesh() != &redecomp->getParent(), - "The Meshes of the specified GridFunctions are not related in a" - "Redecomp -> ParMesh relationship."); - SLIC_ERROR_ROOT_IF(strcmp(dst.FESpace()->FEColl()->Name(), - src.FESpace()->FEColl()->Name()) != 0, - "The FiniteElementCollections of the specified GridFunctions are not" - "the same."); - SLIC_ERROR_ROOT_IF(dst.FESpace()->GetVDim() != src.FESpace()->GetVDim(), - "The vdim of the FiniteElementSpaces of the specified GridFunctions are" - "not the same."); + auto redecomp = dynamic_cast( src.FESpace()->GetMesh() ); + SLIC_ERROR_ROOT_IF( redecomp == nullptr, "The Mesh of GridFunction dst must be a Redecomp mesh." ); + SLIC_ERROR_ROOT_IF( dst.ParFESpace()->GetParMesh() != &redecomp->getParent(), + "The Meshes of the specified GridFunctions are not related in a" + "Redecomp -> ParMesh relationship." ); + SLIC_ERROR_ROOT_IF( strcmp( dst.FESpace()->FEColl()->Name(), src.FESpace()->FEColl()->Name() ) != 0, + "The FiniteElementCollections of the specified GridFunctions are not" + "the same." ); + SLIC_ERROR_ROOT_IF( dst.FESpace()->GetVDim() != src.FESpace()->GetVDim(), + "The vdim of the FiniteElementSpaces of the specified GridFunctions are" + "not the same." ); // send and receive non-ghost DOF values from other ranks - auto dst_dofs = MPIArray(&redecomp->getMPIUtility()); - dst_dofs.SendRecvEach( - [redecomp, &src](int dest) - { - auto src_dofs = axom::Array(); - auto first_el = redecomp->getRedecompToParentElemOffsets()[dest]; - auto last_el = redecomp->getRedecompToParentElemOffsets()[dest+1]; - auto n_els = last_el - first_el; - if (n_els > 0) - { - auto elem_vdofs = mfem::Array(); - auto dof_vals = mfem::Vector(); - // guess the size of src_dofs based on the size of the first element - src.FESpace()->GetElementVDofs(first_el, elem_vdofs); - src_dofs.reserve(elem_vdofs.Size()*n_els); - auto vdof_ct = 0; - auto ghost_ct = 0; - for (int e{first_el}; e < last_el; ++e) - { - // skip ghost elements - if (ghost_ct < redecomp->getRedecompToParentGhostElems()[dest].size() && - redecomp->getRedecompToParentGhostElems()[dest][ghost_ct] == e) - { - ++ghost_ct; - } - else - { - src.FESpace()->GetElementVDofs(e, elem_vdofs); - src.GetSubVector(elem_vdofs, dof_vals); - src_dofs.insert(vdof_ct, dof_vals.Size(), dof_vals.GetData()); - vdof_ct += dof_vals.Size(); - } + auto dst_dofs = MPIArray( &redecomp->getMPIUtility() ); + dst_dofs.SendRecvEach( [redecomp, &src]( int dest ) { + auto src_dofs = axom::Array(); + auto first_el = redecomp->getRedecompToParentElemOffsets()[dest]; + auto last_el = redecomp->getRedecompToParentElemOffsets()[dest + 1]; + auto n_els = last_el - first_el; + if ( n_els > 0 ) { + auto elem_vdofs = mfem::Array(); + auto dof_vals = mfem::Vector(); + // guess the size of src_dofs based on the size of the first element + src.FESpace()->GetElementVDofs( first_el, elem_vdofs ); + src_dofs.reserve( elem_vdofs.Size() * n_els ); + auto vdof_ct = 0; + auto ghost_ct = 0; + for ( int e{ first_el }; e < last_el; ++e ) { + // skip ghost elements + if ( ghost_ct < redecomp->getRedecompToParentGhostElems()[dest].size() && + redecomp->getRedecompToParentGhostElems()[dest][ghost_ct] == e ) { + ++ghost_ct; + } else { + src.FESpace()->GetElementVDofs( e, elem_vdofs ); + src.GetSubVector( elem_vdofs, dof_vals ); + src_dofs.insert( vdof_ct, dof_vals.Size(), dof_vals.GetData() ); + vdof_ct += dof_vals.Size(); } } - return src_dofs; } - ); + return src_dofs; + } ); // map received non-ghost DOF values to local DOFs auto elem_vdofs = mfem::Array(); auto n_ranks = redecomp->getMPIUtility().NRanks(); - for (int r{0}; r < n_ranks; ++r) - { + for ( int r{ 0 }; r < n_ranks; ++r ) { auto vdof_ct = 0; - for (int e{0}; e < redecomp->getParentToRedecompElems().first[r].size(); ++e) - { + for ( int e{ 0 }; e < redecomp->getParentToRedecompElems().first[r].size(); ++e ) { // skip ghost elements - if (!redecomp->getParentToRedecompElems().second[r][e]) - { - dst.FESpace()->GetElementVDofs(redecomp->getParentToRedecompElems().first[r][e], elem_vdofs); - auto dof_vals = mfem::Vector(&dst_dofs[r][vdof_ct], elem_vdofs.Size()); - dst.SetSubVector(elem_vdofs, dof_vals); + if ( !redecomp->getParentToRedecompElems().second[r][e] ) { + dst.FESpace()->GetElementVDofs( redecomp->getParentToRedecompElems().first[r][e], elem_vdofs ); + auto dof_vals = mfem::Vector( &dst_dofs[r][vdof_ct], elem_vdofs.Size() ); + dst.SetSubVector( elem_vdofs, dof_vals ); vdof_ct += elem_vdofs.Size(); } } } } -} // end namespace redecomp +} // end namespace redecomp diff --git a/src/redecomp/transfer/TransferByElements.hpp b/src/redecomp/transfer/TransferByElements.hpp index d6e86f79..cfe1f416 100644 --- a/src/redecomp/transfer/TransferByElements.hpp +++ b/src/redecomp/transfer/TransferByElements.hpp @@ -8,8 +8,7 @@ #include "redecomp/transfer/GridFnTransfer.hpp" -namespace redecomp -{ +namespace redecomp { /** * @brief GridFnTransfer method using elements @@ -21,9 +20,8 @@ namespace redecomp * lists of degrees of freedom to transfer from parent to Redecomp and vice * versa and, therefore, may be faster for repeated transfers of H1 fields. */ -class TransferByElements : public GridFnTransfer -{ -public: +class TransferByElements : public GridFnTransfer { + public: /** * @brief Copies parent-based mfem::ParGridFunction values to a * RedecompMesh-based mfem::GridFunction @@ -33,10 +31,7 @@ class TransferByElements : public GridFnTransfer * @param dst A redecomp GridFunction which receives values from a parent * ParGridFunction (src) */ - void TransferToSerial( - const mfem::ParGridFunction& src, - mfem::GridFunction& dst - ) const override; + void TransferToSerial( const mfem::ParGridFunction& src, mfem::GridFunction& dst ) const override; /** * @brief Copies RedecompMesh-based mfem::GridFunction values to a @@ -47,13 +42,9 @@ class TransferByElements : public GridFnTransfer * @param dst A parent ParGridFunction which receives values from a redecomp * GridFunction (src) */ - void TransferToParallel( - const mfem::GridFunction& src, - mfem::ParGridFunction& dst - ) const override; - + void TransferToParallel( const mfem::GridFunction& src, mfem::ParGridFunction& dst ) const override; }; -} // end namespace redecomp +} // end namespace redecomp #endif /* SRC_REDECOMP_TRANSFERBYELEMENTS_HPP_ */ diff --git a/src/redecomp/transfer/TransferByNodes.cpp b/src/redecomp/transfer/TransferByNodes.cpp index f9595f31..8103e0bd 100644 --- a/src/redecomp/transfer/TransferByNodes.cpp +++ b/src/redecomp/transfer/TransferByNodes.cpp @@ -12,44 +12,34 @@ #include "redecomp/RedecompMesh.hpp" #include "redecomp/common/TypeDefs.hpp" -namespace redecomp -{ +namespace redecomp { -TransferByNodes::TransferByNodes( - const mfem::ParFiniteElementSpace& parent_fes, - const mfem::FiniteElementSpace& redecomp_fes -) -: parent_fes_ { &parent_fes }, - redecomp_fes_ { &redecomp_fes }, - redecomp_ { dynamic_cast(redecomp_fes.GetMesh()) } +TransferByNodes::TransferByNodes( const mfem::ParFiniteElementSpace& parent_fes, + const mfem::FiniteElementSpace& redecomp_fes ) + : parent_fes_{ &parent_fes }, + redecomp_fes_{ &redecomp_fes }, + redecomp_{ dynamic_cast( redecomp_fes.GetMesh() ) } { - SLIC_ERROR_ROOT_IF(redecomp_ == nullptr, - "The Redecomp mesh pointer is null. Does the redecomp_fes contain an " - "underlying Redecomp mesh?"); - SLIC_ERROR_ROOT_IF(parent_fes_->GetParMesh() != &redecomp_->getParent(), - "The ParMesh associated with both parent_fes and the redecomp mesh must match."); + SLIC_ERROR_ROOT_IF( redecomp_ == nullptr, + "The Redecomp mesh pointer is null. Does the redecomp_fes contain an " + "underlying Redecomp mesh?" ); + SLIC_ERROR_ROOT_IF( parent_fes_->GetParMesh() != &redecomp_->getParent(), + "The ParMesh associated with both parent_fes and the redecomp mesh must match." ); // p2r = parent to redecomp - p2r_nodes_ = P2RNodeList(false); + p2r_nodes_ = P2RNodeList( false ); // r2p = redecomp to parent r2p_nodes_ = R2PNodeList(); } -TransferByNodes::TransferByNodes( - const mfem::ParFiniteElementSpace& parent_fes, - const RedecompMesh& redecomp -) -: parent_fes_ { &parent_fes }, - redecomp_ { &redecomp } +TransferByNodes::TransferByNodes( const mfem::ParFiniteElementSpace& parent_fes, const RedecompMesh& redecomp ) + : parent_fes_{ &parent_fes }, redecomp_{ &redecomp } { - SLIC_ERROR_ROOT_IF(parent_fes_->GetParMesh() != &redecomp_->getParent(), - "The ParMesh associated with both parent_fes and redecomp mesh must match."); + SLIC_ERROR_ROOT_IF( parent_fes_->GetParMesh() != &redecomp_->getParent(), + "The ParMesh associated with both parent_fes and redecomp mesh must match." ); } -void TransferByNodes::TransferToSerial( - const mfem::ParGridFunction& src, - mfem::GridFunction& dst -) const +void TransferByNodes::TransferToSerial( const mfem::ParGridFunction& src, mfem::GridFunction& dst ) const { // define transfer-specific data auto src_fes = src.ParFESpace(); @@ -60,55 +50,42 @@ void TransferByNodes::TransferToSerial( const auto& dst_nodes = r2p_nodes_; // checks to make sure src and dst are valid - SLIC_ERROR_ROOT_IF(dst_fes != redecomp_fes_, - "The FiniteElementSpace of GridFunction dst must match the FiniteElementSpace " - "in TransferByNodes."); - SLIC_ERROR_ROOT_IF(src_fes != parent_fes_, - "The ParFiniteElementSpace of GridFunction src must match the ParFiniteElementSpace " - "in TransferByNodes."); + SLIC_ERROR_ROOT_IF( dst_fes != redecomp_fes_, + "The FiniteElementSpace of GridFunction dst must match the FiniteElementSpace " + "in TransferByNodes." ); + SLIC_ERROR_ROOT_IF( src_fes != parent_fes_, + "The ParFiniteElementSpace of GridFunction src must match the ParFiniteElementSpace " + "in TransferByNodes." ); // send and receive DOF values from other ranks - auto dst_dofs = MPIArray(&redecomp_->getMPIUtility()); - dst_dofs.SendRecvEach( - [&src, src_fes, &src_nodes](int dst_rank) - { - auto src_dofs = axom::Array(); - auto n_vdofs = src_fes->GetVDim(); - auto n_src_dofs = src_nodes.first[dst_rank].size(); - src_dofs.reserve(n_vdofs*n_src_dofs); - src_dofs.resize(axom::ArrayOptions::Uninitialized(), n_vdofs, n_src_dofs); - for (int d{0}; d < n_vdofs; ++d) - { - for (int j{0}; j < n_src_dofs; ++j) - { - src_dofs(d, j) = - src(src_fes->DofToVDof(src_nodes.first[dst_rank][j], d)); - } + auto dst_dofs = MPIArray( &redecomp_->getMPIUtility() ); + dst_dofs.SendRecvEach( [&src, src_fes, &src_nodes]( int dst_rank ) { + auto src_dofs = axom::Array(); + auto n_vdofs = src_fes->GetVDim(); + auto n_src_dofs = src_nodes.first[dst_rank].size(); + src_dofs.reserve( n_vdofs * n_src_dofs ); + src_dofs.resize( axom::ArrayOptions::Uninitialized(), n_vdofs, n_src_dofs ); + for ( int d{ 0 }; d < n_vdofs; ++d ) { + for ( int j{ 0 }; j < n_src_dofs; ++j ) { + src_dofs( d, j ) = src( src_fes->DofToVDof( src_nodes.first[dst_rank][j], d ) ); } - return src_dofs; } - ); + return src_dofs; + } ); // map received DOF values to local DOFs auto n_vdofs = src_fes->GetVDim(); auto n_ranks = redecomp_->getMPIUtility().NRanks(); - for (int i{0}; i < n_ranks; ++i) - { - for (int d{0}; d < n_vdofs; ++d) - { - for (int j{0}; j < dst_nodes.first[i].size(); ++j) - { - dst(dst_fes->DofToVDof(dst_nodes.first[i][j], d)) - = dst_dofs[i](d, j); + for ( int i{ 0 }; i < n_ranks; ++i ) { + for ( int d{ 0 }; d < n_vdofs; ++d ) { + for ( int j{ 0 }; j < dst_nodes.first[i].size(); ++j ) { + dst( dst_fes->DofToVDof( dst_nodes.first[i][j], d ) ) = dst_dofs[i]( d, j ); } } } } -void TransferByNodes::TransferToParallel( - const mfem::GridFunction& src, - mfem::ParGridFunction& dst -) const +void TransferByNodes::TransferToParallel( const mfem::GridFunction& src, mfem::ParGridFunction& dst ) const { // define transfer specific data auto src_fes = src.FESpace(); @@ -119,55 +96,43 @@ void TransferByNodes::TransferToParallel( const auto& dst_nodes = p2r_nodes_; // checks to make sure src and dst are valid - SLIC_ERROR_ROOT_IF(src.FESpace() != redecomp_fes_, - "The FiniteElementSpace of GridFunction src must match the FiniteElementSpace " - "in TransferByNodes."); - SLIC_ERROR_ROOT_IF(dst.ParFESpace() != parent_fes_, - "The ParFiniteElementSpace of GridFunction dst must match the ParFiniteElementSpace " - "in TransferByNodes."); + SLIC_ERROR_ROOT_IF( src.FESpace() != redecomp_fes_, + "The FiniteElementSpace of GridFunction src must match the FiniteElementSpace " + "in TransferByNodes." ); + SLIC_ERROR_ROOT_IF( dst.ParFESpace() != parent_fes_, + "The ParFiniteElementSpace of GridFunction dst must match the ParFiniteElementSpace " + "in TransferByNodes." ); // send and receive non-ghost DOF values from other ranks - auto dst_dofs = MPIArray(&redecomp_->getMPIUtility()); - dst_dofs.SendRecvEach( - [&src, src_fes, &src_nodes](int dst_rank) - { - auto src_dofs = axom::Array(); - auto n_vdofs = src_fes->GetVDim(); - auto n_src_dofs = src_nodes.first[dst_rank].size(); - src_dofs.reserve(n_vdofs*n_src_dofs); - src_dofs.resize(axom::ArrayOptions::Uninitialized(), n_vdofs, n_src_dofs); - auto dof_ct = 0; - for (int j{0}; j < n_src_dofs; ++j) - { - if (!src_nodes.second[dst_rank][j]) - { - for (int d{0}; d < n_vdofs; ++d) - { - src_dofs(d, dof_ct) = - src(src_fes->DofToVDof(src_nodes.first[dst_rank][j], d)); - } - ++dof_ct; + auto dst_dofs = MPIArray( &redecomp_->getMPIUtility() ); + dst_dofs.SendRecvEach( [&src, src_fes, &src_nodes]( int dst_rank ) { + auto src_dofs = axom::Array(); + auto n_vdofs = src_fes->GetVDim(); + auto n_src_dofs = src_nodes.first[dst_rank].size(); + src_dofs.reserve( n_vdofs * n_src_dofs ); + src_dofs.resize( axom::ArrayOptions::Uninitialized(), n_vdofs, n_src_dofs ); + auto dof_ct = 0; + for ( int j{ 0 }; j < n_src_dofs; ++j ) { + if ( !src_nodes.second[dst_rank][j] ) { + for ( int d{ 0 }; d < n_vdofs; ++d ) { + src_dofs( d, dof_ct ) = src( src_fes->DofToVDof( src_nodes.first[dst_rank][j], d ) ); } + ++dof_ct; } - src_dofs.shrink(); - return src_dofs; } - ); + src_dofs.shrink(); + return src_dofs; + } ); // map received non-ghost DOF values to dst auto n_vdofs = src_fes->GetVDim(); auto n_ranks = redecomp_->getMPIUtility().NRanks(); - for (int i{0}; i < n_ranks; ++i) - { + for ( int i{ 0 }; i < n_ranks; ++i ) { auto dof_ct = 0; - for (int j{0}; j < dst_nodes.first[i].size(); ++j) - { - if (!dst_nodes.second[i][j]) - { - for (int d{0}; d < n_vdofs; ++d) - { - dst(dst_fes->DofToVDof(dst_nodes.first[i][j], d)) - = dst_dofs[i](d, dof_ct); + for ( int j{ 0 }; j < dst_nodes.first[i].size(); ++j ) { + if ( !dst_nodes.second[i][j] ) { + for ( int d{ 0 }; d < n_vdofs; ++d ) { + dst( dst_fes->DofToVDof( dst_nodes.first[i][j], d ) ) = dst_dofs[i]( d, dof_ct ); } ++dof_ct; } @@ -175,44 +140,36 @@ void TransferByNodes::TransferToParallel( } } -EntityIndexByRank TransferByNodes::P2RNodeList(bool use_global_ids) +EntityIndexByRank TransferByNodes::P2RNodeList( bool use_global_ids ) { // p2r = parent to redecomp - auto p2r_node_idx = MPIArray(&redecomp_->getMPIUtility()); - auto p2r_node_ghost = MPIArray(&redecomp_->getMPIUtility()); + auto p2r_node_idx = MPIArray( &redecomp_->getMPIUtility() ); + auto p2r_node_ghost = MPIArray( &redecomp_->getMPIUtility() ); const auto& p2r_elem_idx = redecomp_->getParentToRedecompElems().first; const auto& p2r_elem_ghost = redecomp_->getParentToRedecompElems().second; auto n_ranks = redecomp_->getMPIUtility().NRanks(); - for (int r{0}; r < n_ranks; ++r) - { + for ( int r{ 0 }; r < n_ranks; ++r ) { auto n_els = p2r_elem_idx[r].size(); - if (n_els > 0) - { - auto n_dofs = parent_fes_->GetFE(p2r_elem_idx[r][0])->GetDof(); - p2r_node_idx[r].reserve(n_els*n_dofs); - p2r_node_ghost[r].reserve(n_els*n_dofs); + if ( n_els > 0 ) { + auto n_dofs = parent_fes_->GetFE( p2r_elem_idx[r][0] )->GetDof(); + p2r_node_idx[r].reserve( n_els * n_dofs ); + p2r_node_ghost[r].reserve( n_els * n_dofs ); auto node_idx_map = std::unordered_map(); auto dof_ct = 0; - for (int e{0}; e < p2r_elem_idx[r].size(); ++e) - { + for ( int e{ 0 }; e < p2r_elem_idx[r].size(); ++e ) { auto is_elem_ghost = p2r_elem_ghost[r][e]; auto elem_dofs = mfem::Array(); - parent_fes_->GetElementDofs(p2r_elem_idx[r][e], elem_dofs); - for (auto elem_dof : elem_dofs) - { - if (use_global_ids) - { - elem_dof = parent_fes_->GetGlobalTDofNumber(elem_dof); + parent_fes_->GetElementDofs( p2r_elem_idx[r][e], elem_dofs ); + for ( auto elem_dof : elem_dofs ) { + if ( use_global_ids ) { + elem_dof = parent_fes_->GetGlobalTDofNumber( elem_dof ); } - auto node_idx_it = node_idx_map.emplace(elem_dof, dof_ct); - if (node_idx_it.second) - { + auto node_idx_it = node_idx_map.emplace( elem_dof, dof_ct ); + if ( node_idx_it.second ) { ++dof_ct; - p2r_node_idx[r].push_back(elem_dof); - p2r_node_ghost[r].push_back(is_elem_ghost); - } - else if (!is_elem_ghost) - { + p2r_node_idx[r].push_back( elem_dof ); + p2r_node_ghost[r].push_back( is_elem_ghost ); + } else if ( !is_elem_ghost ) { p2r_node_ghost[r][node_idx_it.first->second] = false; } } @@ -221,50 +178,42 @@ EntityIndexByRank TransferByNodes::P2RNodeList(bool use_global_ids) p2r_node_ghost[r].shrink(); } } - return {std::move(p2r_node_idx), std::move(p2r_node_ghost)}; + return { std::move( p2r_node_idx ), std::move( p2r_node_ghost ) }; } EntityIndexByRank TransferByNodes::R2PNodeList() { // r2p = redecomp to parent - auto r2p_node_idx = MPIArray(&redecomp_->getMPIUtility()); - auto r2p_node_ghost = MPIArray(&redecomp_->getMPIUtility()); + auto r2p_node_idx = MPIArray( &redecomp_->getMPIUtility() ); + auto r2p_node_ghost = MPIArray( &redecomp_->getMPIUtility() ); auto n_ranks = redecomp_->getMPIUtility().NRanks(); - for (int r{0}; r < n_ranks; ++r) - { + for ( int r{ 0 }; r < n_ranks; ++r ) { auto first_el = redecomp_->getRedecompToParentElemOffsets()[r]; - auto last_el = redecomp_->getRedecompToParentElemOffsets()[r+1]; + auto last_el = redecomp_->getRedecompToParentElemOffsets()[r + 1]; auto n_els = last_el - first_el; - if (n_els > 0) - { - auto n_dofs = redecomp_fes_->GetFE(first_el)->GetDof(); - r2p_node_idx[r].reserve(n_els*n_dofs); - r2p_node_ghost[r].reserve(n_els*n_dofs); + if ( n_els > 0 ) { + auto n_dofs = redecomp_fes_->GetFE( first_el )->GetDof(); + r2p_node_idx[r].reserve( n_els * n_dofs ); + r2p_node_ghost[r].reserve( n_els * n_dofs ); auto node_idx_map = std::unordered_map(); auto dof_ct = 0; auto ghost_ct = 0; - for (int e{first_el}; e < last_el; ++e) - { + for ( int e{ first_el }; e < last_el; ++e ) { auto is_elem_ghost = false; - if (ghost_ct < redecomp_->getRedecompToParentGhostElems()[r].size() && - redecomp_->getRedecompToParentGhostElems()[r][ghost_ct] == e) - { + if ( ghost_ct < redecomp_->getRedecompToParentGhostElems()[r].size() && + redecomp_->getRedecompToParentGhostElems()[r][ghost_ct] == e ) { ++ghost_ct; is_elem_ghost = true; } auto elem_dofs = mfem::Array(); - redecomp_fes_->GetElementDofs(e, elem_dofs); - for (auto elem_dof : elem_dofs) - { - auto node_idx_it = node_idx_map.emplace(elem_dof, dof_ct); - if (node_idx_it.second) - { + redecomp_fes_->GetElementDofs( e, elem_dofs ); + for ( auto elem_dof : elem_dofs ) { + auto node_idx_it = node_idx_map.emplace( elem_dof, dof_ct ); + if ( node_idx_it.second ) { ++dof_ct; - r2p_node_idx[r].push_back(elem_dof); - r2p_node_ghost[r].push_back(is_elem_ghost); - } - else if (!is_elem_ghost) - { + r2p_node_idx[r].push_back( elem_dof ); + r2p_node_ghost[r].push_back( is_elem_ghost ); + } else if ( !is_elem_ghost ) { r2p_node_ghost[r][node_idx_it.first->second] = false; } } @@ -273,7 +222,7 @@ EntityIndexByRank TransferByNodes::R2PNodeList() r2p_node_idx[r].shrink(); } } - return {std::move(r2p_node_idx), std::move(r2p_node_ghost)}; + return { std::move( r2p_node_idx ), std::move( r2p_node_ghost ) }; } -} // end namespace redecomp +} // end namespace redecomp diff --git a/src/redecomp/transfer/TransferByNodes.hpp b/src/redecomp/transfer/TransferByNodes.hpp index 390686c3..7f67c19b 100644 --- a/src/redecomp/transfer/TransferByNodes.hpp +++ b/src/redecomp/transfer/TransferByNodes.hpp @@ -11,8 +11,7 @@ #include "redecomp/transfer/GridFnTransfer.hpp" #include "redecomp/common/TypeDefs.hpp" -namespace redecomp -{ +namespace redecomp { class RedecompMesh; @@ -27,11 +26,10 @@ class MPIUtility; * This eliminates repeated transfer of nodes on inter-element boundaries, which * occur with the related TransferByElements class. However, creating these * maps is a non-trivial operation, and therefore this class should only be used - * if multiple transfers between parent and RedecompMesh are anticipated. + * if multiple transfers between parent and RedecompMesh are anticipated. */ -class TransferByNodes : public GridFnTransfer -{ -public: +class TransferByNodes : public GridFnTransfer { + public: /** * @brief Construct a new TransferByNodes object * @@ -39,10 +37,7 @@ class TransferByNodes : public GridFnTransfer * identified in redecomp_fes * @param redecomp_fes FiniteElementSpace constructed from RedecompMesh */ - TransferByNodes( - const mfem::ParFiniteElementSpace& parent_fes, - const mfem::FiniteElementSpace& redecomp_fes - ); + TransferByNodes( const mfem::ParFiniteElementSpace& parent_fes, const mfem::FiniteElementSpace& redecomp_fes ); /** * @brief Construct a new TransferByNodes object (used by RedecompMesh) * @@ -55,10 +50,7 @@ class TransferByNodes : public GridFnTransfer * identified in redecomp * @param redecomp RedecompMesh pointer */ - TransferByNodes( - const mfem::ParFiniteElementSpace& parent_fes, - const RedecompMesh& redecomp - ); + TransferByNodes( const mfem::ParFiniteElementSpace& parent_fes, const RedecompMesh& redecomp ); /** * @brief Copies parent-based mfem::ParGridFunction values to a @@ -69,10 +61,7 @@ class TransferByNodes : public GridFnTransfer * @param dst A redecomp GridFunction which receives values from a parent * ParGridFunction (src) */ - void TransferToSerial( - const mfem::ParGridFunction& src, - mfem::GridFunction& dst - ) const override; + void TransferToSerial( const mfem::ParGridFunction& src, mfem::GridFunction& dst ) const override; /** * @brief Copies RedecompMesh-based mfem::GridFunction values to a @@ -83,10 +72,7 @@ class TransferByNodes : public GridFnTransfer * @param dst A parent ParGridFunction which receives values from a redecomp * GridFunction (src) */ - void TransferToParallel( - const mfem::GridFunction& src, - mfem::ParGridFunction& dst - ) const override; + void TransferToParallel( const mfem::GridFunction& src, mfem::ParGridFunction& dst ) const override; /** * @brief Determine list of parent nodes to send to RedecompMesh @@ -95,9 +81,9 @@ class TransferByNodes : public GridFnTransfer * @return EntityIndexByRank List of parent node ids (with ghost information) * which belong on each RedecompMesh rank */ - EntityIndexByRank P2RNodeList(bool use_global_ids); - -private: + EntityIndexByRank P2RNodeList( bool use_global_ids ); + + private: /** * @brief Determine list of RedecompMesh nodes to send to parent mesh * @@ -107,17 +93,17 @@ class TransferByNodes : public GridFnTransfer EntityIndexByRank R2PNodeList(); /** - * @brief ParFiniteElementSpace constructed from mesh identified as parent in redecomp_ + * @brief ParFiniteElementSpace constructed from mesh identified as parent in redecomp_ */ const mfem::ParFiniteElementSpace* parent_fes_; /** - * @brief FiniteElementSpace constructed from redecomp_ + * @brief FiniteElementSpace constructed from redecomp_ */ const mfem::FiniteElementSpace* redecomp_fes_; /** - * @brief Redecomp mesh + * @brief Redecomp mesh */ const RedecompMesh* redecomp_; @@ -127,11 +113,11 @@ class TransferByNodes : public GridFnTransfer EntityIndexByRank p2r_nodes_; /** - * @brief List of Redecomp nodes (with ghost information) to send to parent mesh + * @brief List of Redecomp nodes (with ghost information) to send to parent mesh */ EntityIndexByRank r2p_nodes_; }; -} // end namespace redecomp +} // end namespace redecomp #endif /* SRC_REDECOMP_TRANSFERBYNODES_HPP_ */ diff --git a/src/redecomp/utils/ArrayUtility.hpp b/src/redecomp/utils/ArrayUtility.hpp index bef866c2..39522b5d 100644 --- a/src/redecomp/utils/ArrayUtility.hpp +++ b/src/redecomp/utils/ArrayUtility.hpp @@ -10,18 +10,16 @@ #include "axom/core.hpp" -namespace redecomp -{ +namespace redecomp { /** * @brief Convenience class for generating common axom::Arrays */ -class ArrayUtility -{ -public: +class ArrayUtility { + public: /** * @brief Create an index array at compile time - * + * * @tparam T Array element type * @tparam N Size of array * @return axom::Array holding index array [0, ..., N-1] @@ -29,38 +27,39 @@ class ArrayUtility template static constexpr axom::Array IndexArray() { - return IndexArrayImpl(std::make_index_sequence{}); + return IndexArrayImpl( std::make_index_sequence{} ); } /** * @brief Create an index array at run time. - * + * * @tparam T Array element type * @param n Size of array * @return axom::Array holding index array [0, ..., n-1] */ template - static axom::Array IndexArray(size_t n) + static axom::Array IndexArray( size_t n ) { - auto index = axom::Array(n, n); - std::iota(index.begin(), index.end(), 0); + auto index = axom::Array( n, n ); + std::iota( index.begin(), index.end(), 0 ); return index; } -private: + + private: /** * @brief IndexArray implementation - * + * * @tparam T Array element type * @tparam I Indices * @return axom::Array holding the index array I */ template - static constexpr axom::Array IndexArrayImpl(std::index_sequence) + static constexpr axom::Array IndexArrayImpl( std::index_sequence ) { - return axom::Array( {I...} ); + return axom::Array( { I... } ); } }; -} // end namespace redecomp +} // end namespace redecomp #endif /* SRC_REDECOMP_UTILS_ARRAYUTILITY_HPP_ */ diff --git a/src/redecomp/utils/BisecTree.hpp b/src/redecomp/utils/BisecTree.hpp index d242f7e6..a635f5c1 100644 --- a/src/redecomp/utils/BisecTree.hpp +++ b/src/redecomp/utils/BisecTree.hpp @@ -13,8 +13,7 @@ #include "axom/core.hpp" -namespace redecomp -{ +namespace redecomp { /** * @brief Implements a nearly-complete binary tree designed for breadth-first traversal @@ -23,10 +22,8 @@ namespace redecomp * multiple levels which are not completely filled. Storage is through axom::Array. */ template -class BisecTree -{ -public: - +class BisecTree { + public: using BFSLevelIterator = typename axom::Array>::ArrayIterator; using ConstBFSLevelIterator = typename axom::Array>::ConstArrayIterator; using BFSNodeIterator = typename axom::Array::ArrayIterator; @@ -38,358 +35,300 @@ class BisecTree /** * @brief Construct a new BisecTree object - * + * * @param n_parts Number of entries at the lowest level of the tree */ - BisecTree(size_t n_parts) : - n_parts_ {n_parts}, - n_levels_ {static_cast(ceil(log2(static_cast(n_parts)-0.25)) + 0.5) + 1}, - all_nodes_ (n_levels_, n_levels_) + BisecTree( size_t n_parts ) + : n_parts_{ n_parts }, + n_levels_{ static_cast( ceil( log2( static_cast( n_parts ) - 0.25 ) ) + 0.5 ) + 1 }, + all_nodes_( n_levels_, n_levels_ ) { // populate the bisection tree with empty nodes auto n_nodes = n_parts_; - for (auto it = all_nodes_.end(); it != all_nodes_.begin(); ) - { + for ( auto it = all_nodes_.end(); it != all_nodes_.begin(); ) { --it; - *it = axom::Array(n_nodes, n_nodes); - n_nodes = (n_nodes + 1) / 2; + *it = axom::Array( n_nodes, n_nodes ); + n_nodes = ( n_nodes + 1 ) / 2; } } /** * @brief Returns the value at the given level and node - * + * * @param level Tree level (0 = top, n_levels_ = bottom) * @param node Node index (0 = left-most node) * @return T& Value at given level and node */ - T& operator()(size_t level, size_t node) - { - return all_nodes_[level][node]; - } + T& operator()( size_t level, size_t node ) { return all_nodes_[level][node]; } /** * @brief Returns the value at the bottom level and given node - * + * * @param node Node index (0 = left-most node) * @return T& Value at given node */ - T& operator()(size_t node) - { - return all_nodes_[n_levels_-1][node]; - } + T& operator()( size_t node ) { return all_nodes_[n_levels_ - 1][node]; } /** * @brief Returns an iterator to the top level - * + * * @return BFSLevelIterator pointing to the top level */ - BFSLevelIterator begin() - { - return all_nodes_.begin(); - } + BFSLevelIterator begin() { return all_nodes_.begin(); } /** * @brief Returns a const iterator to the top level * * @return ConstBFSLevelIterator pointing to the top level */ - ConstBFSLevelIterator begin() const - { - return all_nodes_.begin(); - } + ConstBFSLevelIterator begin() const { return all_nodes_.begin(); } /** * @brief Returns an iterator to the bottom level - * + * * @return BFSLevelIterator pointing to the bottom level */ - BFSLevelIterator end() - { - return all_nodes_.end(); - } + BFSLevelIterator end() { return all_nodes_.end(); } /** * @brief Returns a const iterator to the bottom level * * @return ConstBFSLevelIterator pointing to the bottom level */ - ConstBFSLevelIterator end() const - { - return all_nodes_.end(); - } + ConstBFSLevelIterator end() const { return all_nodes_.end(); } /** * @brief Returns an iterator to the left-most node of the given level - * + * * @param lvl_it Iterator pointing to a level in the BisecTree * @return BFSNodeIterator pointing to the left-most node of the level in lvl_it */ - BFSNodeIterator begin(BFSLevelIterator lvl_it) - { - return (*lvl_it).begin(); - } + BFSNodeIterator begin( BFSLevelIterator lvl_it ) { return ( *lvl_it ).begin(); } /** * @brief Returns a const iterator to the left-most node of the given level - * + * * @param lvl_it Const iterator pointing to a level in the BisecTree * @return ConstBFSNodeIterator pointing to the left-most node of the level in lvl_it */ - ConstBFSNodeIterator begin(ConstBFSLevelIterator lvl_it) const - { - return (*lvl_it).begin(); - } + ConstBFSNodeIterator begin( ConstBFSLevelIterator lvl_it ) const { return ( *lvl_it ).begin(); } /** * @brief Returns a iterator to one after the right-most node of the given level - * + * * @param lvl_it Iterator pointing to a level in the BisecTree * @return BFSNodeIterator pointing to one after the right-most node of the level in lvl_it */ - BFSNodeIterator end(BFSLevelIterator lvl_it) - { - return (*lvl_it).end(); - } + BFSNodeIterator end( BFSLevelIterator lvl_it ) { return ( *lvl_it ).end(); } /** * @brief Returns a const iterator to one after the right-most node of the given level - * + * * @param lvl_it Const iterator pointing to a level in the BisecTree * @return ConstBFSNodeIterator pointing to one after the right-most node of the level in lvl_it */ - ConstBFSNodeIterator end(ConstBFSLevelIterator lvl_it) const - { - return (*lvl_it).end(); - } + ConstBFSNodeIterator end( ConstBFSLevelIterator lvl_it ) const { return ( *lvl_it ).end(); } /** * @brief Returns the offset of the given node_it to the beginning of the level - * + * * @param lvl_it Iterator pointing to a level in the BisecTree * @param node_it Iterator pointing to a node in the level in lvl_it * @return Offset of node_it */ - size_t position(BFSLevelIterator lvl_it, BFSNodeIterator node_it) - { - return node_it - (*lvl_it).begin(); - } + size_t position( BFSLevelIterator lvl_it, BFSNodeIterator node_it ) { return node_it - ( *lvl_it ).begin(); } /** * @brief Returns the offset of the given node_it to the beginning of the level - * + * * @param lvl_it Iterator pointing to a level in the BisecTree * @param node_it Iterator pointing to a node in the level in lvl_it * @return Offset of node_it */ - size_t position(ConstBFSLevelIterator lvl_it, ConstBFSNodeIterator node_it) const + size_t position( ConstBFSLevelIterator lvl_it, ConstBFSNodeIterator node_it ) const { - return node_it - (*lvl_it).begin(); + return node_it - ( *lvl_it ).begin(); } /** * @brief Returns the offset of the given it to the beginning of the level - * + * * @param it BFSIterator pointing to a level and node in the BisecTree * @return Offset of node_it */ - size_t position(const BFSIterator& it) - { - return position(it.first, it.second); - } + size_t position( const BFSIterator& it ) { return position( it.first, it.second ); } /** * @brief Returns the offset of the given it to the beginning of the level - * + * * @param it ConstBFSIterator pointing to a level and node in the BisecTree * @return Offset of node_it */ - size_t position(const ConstBFSIterator& it) const - { - return position(it.first, it.second); - } + size_t position( const ConstBFSIterator& it ) const { return position( it.first, it.second ); } /** * @brief Returns iterator to the root node of the given level and node iterators - * + * * @param lvl_it Iterator pointing to a level in the BisecTree * @param node_it Iterator pointing to a node in the level of lvl_it * @return BFSIterator pointing to the root node */ - BFSIterator root(BFSLevelIterator lvl_it, BFSNodeIterator node_it) + BFSIterator root( BFSLevelIterator lvl_it, BFSNodeIterator node_it ) { - auto node_idx = position(lvl_it, node_it); + auto node_idx = position( lvl_it, node_it ); --lvl_it; - return std::make_pair(lvl_it, BFSNodeIterator(node_idx / 2, &(*lvl_it))); + return std::make_pair( lvl_it, BFSNodeIterator( node_idx / 2, &( *lvl_it ) ) ); } /** * @brief Returns const iterator to the root node of the given level and node const iterators - * + * * @param lvl_it Const iterator pointing to a level in the BisecTree * @param node_it Const iterator pointing to a node in the level of lvl_it * @return ConstBFSIterator pointing to the root node */ - ConstBFSIterator root(ConstBFSLevelIterator lvl_it, ConstBFSNodeIterator node_it) const + ConstBFSIterator root( ConstBFSLevelIterator lvl_it, ConstBFSNodeIterator node_it ) const { - auto node_idx = position(lvl_it, node_it); + auto node_idx = position( lvl_it, node_it ); --lvl_it; - return std::make_pair(lvl_it, ConstBFSNodeIterator(node_idx / 2, &(*lvl_it))); + return std::make_pair( lvl_it, ConstBFSNodeIterator( node_idx / 2, &( *lvl_it ) ) ); } /** * @brief Returns iterator to the root node of the given iterator - * + * * @param it Iterator pointing to a level and node in the BisecTree * @return BFSIterator pointing to the root node */ - BFSIterator root(const BFSIterator& it) - { - return root(it.first, it.second); - } + BFSIterator root( const BFSIterator& it ) { return root( it.first, it.second ); } /** * @brief Returns const iterator to the root node of the given const iterator - * + * * @param it Const iterator pointing to a level and node in the BisecTree * @return ConstBFSIterator pointing to the root node */ - ConstBFSIterator root(const ConstBFSIterator& it) const - { - return root(it.first, it.second); - } + ConstBFSIterator root( const ConstBFSIterator& it ) const { return root( it.first, it.second ); } /** * @brief Returns iterator to the left node of the given level and node iterators - * + * * @param lvl_it Iterator pointing to a level in the BisecTree * @param node_it Iterator pointing to a node in the level of lvl_it * @return BFSIterator pointing to the left node */ - BFSIterator left(BFSLevelIterator lvl_it, BFSNodeIterator node_it) + BFSIterator left( BFSLevelIterator lvl_it, BFSNodeIterator node_it ) { - auto node_idx = position(lvl_it, node_it); + auto node_idx = position( lvl_it, node_it ); ++lvl_it; - return std::make_pair(lvl_it, BFSNodeIterator(node_idx * 2, &(*lvl_it))); + return std::make_pair( lvl_it, BFSNodeIterator( node_idx * 2, &( *lvl_it ) ) ); } /** * @brief Returns const iterator to the left node of the given level and node const iterators - * + * * @param lvl_it Const iterator pointing to a level in the BisecTree * @param node_it Const iterator pointing to a node in the level of lvl_it * @return ConstBFSIterator pointing to the left node */ - ConstBFSIterator left(ConstBFSLevelIterator lvl_it, ConstBFSNodeIterator node_it) const + ConstBFSIterator left( ConstBFSLevelIterator lvl_it, ConstBFSNodeIterator node_it ) const { - auto node_idx = position(lvl_it, node_it); + auto node_idx = position( lvl_it, node_it ); ++lvl_it; - return std::make_pair(lvl_it, ConstBFSNodeIterator(node_idx * 2, &(*lvl_it))); + return std::make_pair( lvl_it, ConstBFSNodeIterator( node_idx * 2, &( *lvl_it ) ) ); } /** * @brief Returns iterator to the left node of the given iterator - * + * * @param it Iterator pointing to a level and node in the BisecTree * @return BFSIterator pointing to the left node */ - BFSIterator left(const BFSIterator& it) - { - return left(it.first, it.second); - } + BFSIterator left( const BFSIterator& it ) { return left( it.first, it.second ); } /** * @brief Returns const iterator to the left node of the given const iterator - * + * * @param it Const iterator pointing to a level and node in the BisecTree * @return ConstBFSIterator pointing to the left node */ - ConstBFSIterator left(const ConstBFSIterator& it) const - { - return left(it.first, it.second); - } + ConstBFSIterator left( const ConstBFSIterator& it ) const { return left( it.first, it.second ); } /** * @brief Returns iterator to the right node of the given level and node iterators - * + * * @param lvl_it Iterator pointing to a level in the BisecTree * @param node_it Iterator pointing to a node in the level of lvl_it * @return BFSIterator pointing to the right node */ - BFSIterator right(BFSLevelIterator lvl_it, BFSNodeIterator node_it) + BFSIterator right( BFSLevelIterator lvl_it, BFSNodeIterator node_it ) { - auto node_idx = position(lvl_it, node_it); + auto node_idx = position( lvl_it, node_it ); ++lvl_it; - return std::make_pair(lvl_it, BFSNodeIterator(node_idx * 2 + 1, &(*lvl_it))); + return std::make_pair( lvl_it, BFSNodeIterator( node_idx * 2 + 1, &( *lvl_it ) ) ); } /** * @brief Returns const iterator to the right node of the given level and node const iterators - * + * * @param lvl_it Const iterator pointing to a level in the BisecTree * @param node_it Const iterator pointing to a node in the level of lvl_it * @return ConstBFSIterator pointing to the right node */ - ConstBFSIterator right(ConstBFSLevelIterator lvl_it, ConstBFSNodeIterator node_it) const + ConstBFSIterator right( ConstBFSLevelIterator lvl_it, ConstBFSNodeIterator node_it ) const { - auto node_idx = position(lvl_it, node_it); + auto node_idx = position( lvl_it, node_it ); ++lvl_it; - return std::make_pair(lvl_it, ConstBFSNodeIterator(node_idx * 2 + 1, &(*lvl_it))); + return std::make_pair( lvl_it, ConstBFSNodeIterator( node_idx * 2 + 1, &( *lvl_it ) ) ); } /** * @brief Returns iterator to the right node of the given iterator - * + * * @param it Iterator pointing to a level and node in the BisecTree * @return BFSIterator pointing to the right node */ - BFSIterator right(const BFSIterator& it) - { - return right(it.first, it.second); - } + BFSIterator right( const BFSIterator& it ) { return right( it.first, it.second ); } /** * @brief Returns const iterator to the right node of the given const iterator - * + * * @param it Const iterator pointing to a level and node in the BisecTree * @return ConstBFSIterator pointing to the right node */ - ConstBFSIterator right(const ConstBFSIterator& it) const - { - return right(it.first, it.second); - } + ConstBFSIterator right( const ConstBFSIterator& it ) const { return right( it.first, it.second ); } /** * @brief Returns number of nodes at the last level - * + * * @return Number of nodes at the last level */ size_t NumParts() const { return n_parts_; } /** * @brief Returns number of levels in the tree - * + * * @return Number of levels in BisecTree */ size_t NumLevels() const { return n_levels_; } -private: + private: /** - * @brief Number of nodes at the last level + * @brief Number of nodes at the last level */ size_t n_parts_; /** - * @brief Number of levels in the tree + * @brief Number of levels in the tree */ size_t n_levels_; - + /** - * @brief Tree data + * @brief Tree data */ axom::Array> all_nodes_; }; -} // end namespace redecomp +} // end namespace redecomp #endif /* SRC_REDECOMP_BISECTREE_HPP_ */ diff --git a/src/redecomp/utils/MPIArray.hpp b/src/redecomp/utils/MPIArray.hpp index bb967faa..0b04da84 100644 --- a/src/redecomp/utils/MPIArray.hpp +++ b/src/redecomp/utils/MPIArray.hpp @@ -10,56 +10,51 @@ #include "redecomp/utils/MPIUtility.hpp" -namespace redecomp -{ +namespace redecomp { /** * @brief Creates and manages per-MPI-rank axom::Arrays - * + * * @tparam T Array data type * @tparam DIM Array dimension */ template -class MPIArray : public axom::Array> -{ -public: +class MPIArray : public axom::Array> { + public: /** * @brief Construct a new MPIArray object - * + * * @param mpi MPIUtility to define MPI_Comm for MPI operations * @param array Array data */ - MPIArray(const MPIUtility* mpi, const axom::Array>& array) - : axom::Array>(array), - mpi_ { mpi } + MPIArray( const MPIUtility* mpi, const axom::Array>& array ) + : axom::Array>( array ), mpi_{ mpi } { - this->reserve(mpi_->NRanks()); - this->resize(mpi_->NRanks()); + this->reserve( mpi_->NRanks() ); + this->resize( mpi_->NRanks() ); this->shrink(); } /** * @brief Construct a new MPIArray object - * + * * @param mpi MPIUtility to define MPI_Comm for MPI operations * @param array Array data */ - MPIArray(const MPIUtility* mpi, axom::Array>&& array) - : axom::Array>(std::move(array)), - mpi_ { mpi } + MPIArray( const MPIUtility* mpi, axom::Array>&& array ) + : axom::Array>( std::move( array ) ), mpi_{ mpi } { - this->reserve(mpi_->NRanks()); - this->resize(mpi_->NRanks()); + this->reserve( mpi_->NRanks() ); + this->resize( mpi_->NRanks() ); this->shrink(); } /** * @brief Construct a new MPIArray object - * + * * @param mpi MPIUtility to define MPI_Comm for MPI operations */ - MPIArray(const MPIUtility* mpi) - : MPIArray(mpi, axom::Array>(0, 0)) {} + MPIArray( const MPIUtility* mpi ) : MPIArray( mpi, axom::Array>( 0, 0 ) ) {} /** * @brief Construct an empty MPIArray object (note: object cannot be used) @@ -68,92 +63,66 @@ class MPIArray : public axom::Array> /** * @brief Returns the axom::Array at the given rank - * + * * @param rank The MPI rank of the array * @return axom::Array& holding array values at rank */ - axom::Array& at(axom::IndexType rank) - { - return this->operator[](rank); - } + axom::Array& at( axom::IndexType rank ) { return this->operator[]( rank ); } /** * @brief Returns the axom::Array at the given rank - * + * * @param rank The MPI rank of the array * @return axom::Array& holding array values at rank */ - const axom::Array& at(axom::IndexType rank) const - { - return this->operator[](rank); - } + const axom::Array& at( axom::IndexType rank ) const { return this->operator[]( rank ); } /** * @brief Sends the Array data to all other MPI ranks - * + * * @param data Data to send to other ranks */ - static void SendAll(const axom::Array& data) - { - data.mpi_.SendAll(data); - } + static void SendAll( const axom::Array& data ) { data.mpi_.SendAll( data ); } /** * @brief Receive data sent from a call to MPIArray::SendAll() - * + * * @param src The source rank of the data */ - void RecvSendAll(axom::IndexType src) - { - at(src) = mpi_->RecvSendAll(type>(), src); - } + void RecvSendAll( axom::IndexType src ) { at( src ) = mpi_->RecvSendAll( type>(), src ); } /** * @brief Sends the MPIArray data to all other MPI ranks while receiving from other ranks - * + * * @param data Data to send to other ranks */ - void SendRecvArrayEach(const MPIArray& data) + void SendRecvArrayEach( const MPIArray& data ) { mpi_->SendRecvEach( - type>(), - [data](axom::IndexType dst) - { - return data.at(dst); - }, - [this](axom::Array&& recv_data, axom::IndexType src) - { - at(src) = std::move(recv_data); - } - ); + type>(), [data]( axom::IndexType dst ) { return data.at( dst ); }, + [this]( axom::Array&& recv_data, axom::IndexType src ) { at( src ) = std::move( recv_data ); } ); } /** * @brief Create data to send to all other MPI ranks while receiving from other ranks - * + * * @param build_send A lambda which returns an axom::Array to send to the input rank */ template - void SendRecvEach(F&& build_send) + void SendRecvEach( F&& build_send ) { mpi_->SendRecvEach( - type>(), - std::forward(build_send), - [this](axom::Array&& recv_data, axom::IndexType src) - { - at(src) = std::move(recv_data); - } - ); + type>(), std::forward( build_send ), + [this]( axom::Array&& recv_data, axom::IndexType src ) { at( src ) = std::move( recv_data ); } ); } -private: + private: /** - * @brief MPIUtility associated with MPI_Comm of the MPIArray + * @brief MPIUtility associated with MPI_Comm of the MPIArray */ const MPIUtility* mpi_; - }; -} // end namespace redecomp +} // end namespace redecomp #endif /* SRC_REDECOMP_UTILS_MPIARRAY_HPP_ */ diff --git a/src/redecomp/utils/MPIUtility.cpp b/src/redecomp/utils/MPIUtility.cpp index fb368817..865d52f7 100644 --- a/src/redecomp/utils/MPIUtility.cpp +++ b/src/redecomp/utils/MPIUtility.cpp @@ -7,54 +7,51 @@ #include "redecomp/utils/ArrayUtility.hpp" -namespace redecomp -{ +namespace redecomp { -MPIUtility::MPIUtility(const MPI_Comm& comm) -: comm_ {comm} +MPIUtility::MPIUtility( const MPI_Comm& comm ) : comm_{ comm } { - MPI_Comm_size(comm, &n_ranks_); - MPI_Comm_rank(comm, &my_rank_); + MPI_Comm_size( comm, &n_ranks_ ); + MPI_Comm_rank( comm, &my_rank_ ); } -MPIUtility::Request::Request(std::unique_ptr request) -: request_ {std::move(request)} -{} +MPIUtility::Request::Request( std::unique_ptr request ) : request_{ std::move( request ) } {} -void MPIUtility::Request::Wait() -{ - MPI_Wait(request_.get(), &status_); -} +void MPIUtility::Request::Wait() { MPI_Wait( request_.get(), &status_ ); } -BisecTree MPIUtility::BuildSendTree(int rank) const +BisecTree MPIUtility::BuildSendTree( int rank ) const { - auto send_tree = BisecTree(n_ranks_); + auto send_tree = BisecTree( n_ranks_ ); auto lvl_it = --send_tree.end(); - *lvl_it = ArrayUtility::IndexArray(n_ranks_); - while (lvl_it != send_tree.begin()) - { + *lvl_it = ArrayUtility::IndexArray( n_ranks_ ); + while ( lvl_it != send_tree.begin() ) { --lvl_it; - for (auto node_it = send_tree.begin(lvl_it); - node_it != send_tree.end(lvl_it); - ++node_it) - { - auto right_node_it = send_tree.right(lvl_it, node_it); - *node_it = right_node_it.second == send_tree.end(right_node_it.first) || - *right_node_it.second != rank ? - *send_tree.left(lvl_it, node_it).second : - rank; + for ( auto node_it = send_tree.begin( lvl_it ); node_it != send_tree.end( lvl_it ); ++node_it ) { + auto right_node_it = send_tree.right( lvl_it, node_it ); + *node_it = right_node_it.second == send_tree.end( right_node_it.first ) || *right_node_it.second != rank + ? *send_tree.left( lvl_it, node_it ).second + : rank; } } return send_tree; } template <> -MPI_Datatype MPIUtility::GetMPIType() const { return MPI_C_BOOL; } +MPI_Datatype MPIUtility::GetMPIType() const +{ + return MPI_C_BOOL; +} template <> -MPI_Datatype MPIUtility::GetMPIType() const { return MPI_DOUBLE; } +MPI_Datatype MPIUtility::GetMPIType() const +{ + return MPI_DOUBLE; +} template <> -MPI_Datatype MPIUtility::GetMPIType() const { return MPI_INT; } +MPI_Datatype MPIUtility::GetMPIType() const +{ + return MPI_INT; +} -} // end namespace redecomp +} // end namespace redecomp diff --git a/src/redecomp/utils/MPIUtility.hpp b/src/redecomp/utils/MPIUtility.hpp index bee99026..7a3906cc 100644 --- a/src/redecomp/utils/MPIUtility.hpp +++ b/src/redecomp/utils/MPIUtility.hpp @@ -16,122 +16,120 @@ #include "redecomp/utils/BisecTree.hpp" -namespace redecomp -{ +namespace redecomp { template -struct type {}; +struct type { +}; /** * @brief Wrapper class for MPI functions and communication patterns used in redecomp. */ -class MPIUtility -{ -public: +class MPIUtility { + public: /** * @brief Construct a new MPIUtility object - * + * * @param comm MPI_Comm associated with the MPIUtility */ - MPIUtility(const MPI_Comm& comm); + MPIUtility( const MPI_Comm& comm ); /** * @brief Returns the MPI communicator - * + * * @return MPI Communicator */ const MPI_Comm& MPIComm() const { return comm_; } /** * @brief Returns the MPI rank of the process - * + * * @return MPI rank */ int MyRank() const { return my_rank_; } /** * @brief Returns the total number of MPI ranks - * + * * @return Number of MPI ranks */ int NRanks() const { return n_ranks_; } /** - * @brief Class to hold/query an MPI_Request + * @brief Class to hold/query an MPI_Request */ - class Request - { - public: + class Request { + public: /** * @brief Construct a new Request object * * @param request MPI_Request object associated with Request */ - Request(std::unique_ptr request); + Request( std::unique_ptr request ); /** * @brief Instructs the process to wait until the request completes */ void Wait(); - private: + private: /** * @brief MPI_Request object */ std::unique_ptr request_; /** - * @brief MPI_Status object + * @brief MPI_Status object */ MPI_Status status_; }; /** * @brief Calls MPI_Allreduce on a single value - * + * * @tparam T Type of the local value: bool, double, int currently supported * @param local_value Value on rank * @param op MPI operation to perform while reducing * @return Reduced value */ template - T AllreduceValue(T local_value, MPI_Op op) const; + T AllreduceValue( T local_value, MPI_Op op ) const; /** * @brief Calls MPI_Allreduce on an array stored in container - * + * * @tparam T Data type of the data container * @param container Stores the data to reduce; must have T::data() and T::size() methods * @param op MPI operation to perform while reducing */ template - void Allreduce(T* container, MPI_Op op) const; + void Allreduce( T* container, MPI_Op op ) const; /** * @brief Calls MPI_Allreduce on an array pointed to by data - * + * * @tparam T Data type of an element in the array: bool, double, int currently supported * @param data Pointer to first element in the array * @param size Number of elements in the array * @param op MPI operation to perform while reducing */ template - void Allreduce(T* data, int size, MPI_Op op) const; + void Allreduce( T* data, int size, MPI_Op op ) const; /** * @brief Calls MPI_Send on an array stored in container - * + * * @tparam T Data type of the data container * @param container Stores the data to send; must have T::data() and T::size() methods * @param dest Rank to send the data in container to * @param tag MPI tag for identifying the data */ template - void Send(const T& container, int dest, int tag = 0) const; + void Send( const T& container, int dest, int tag = 0 ) const; /** * @brief Calls MPI_Send on an 2D array stored in an axom::Array - * + * * @tparam T Data type of an element in the array: bool, double, int currently supported * @tparam Sp axom::MemorySpace of the axom::Array * @param container 2D axom::Array of data to send @@ -139,11 +137,11 @@ class MPIUtility * @param tag MPI tag for identifying the data */ template - void Send(const axom::Array& container, int dest, int tag = 0) const; + void Send( const axom::Array& container, int dest, int tag = 0 ) const; /** * @brief Calls MPI_Isend (non-blocking send) on an array stored in container - * + * * @tparam T Data type of the data container * @param container Stores the data to send; must have T::data() and T::size() methods * @param dest Rank to send the data in container to @@ -151,11 +149,11 @@ class MPIUtility * @return Request object to track completion of the send */ template - std::unique_ptr Isend(const T& container, int dest, int tag = 0) const; + std::unique_ptr Isend( const T& container, int dest, int tag = 0 ) const; /** * @brief Calls MPI_Isend (non-blocking send) on a 2D axom::Array - * + * * @tparam T Data type of an element in the array: bool, double, int currently supported * @tparam Sp axom::MemorySpace of the axom::Array * @param container 2D axom::Array of data to send @@ -164,22 +162,22 @@ class MPIUtility * @return Request object to track completion of the send */ template - std::unique_ptr Isend(const axom::Array& container, int dest, int tag = 0) const; + std::unique_ptr Isend( const axom::Array& container, int dest, int tag = 0 ) const; /** * @brief Calls MPI_Recv on an array stored in container - * + * * @tparam T Data type of the data container * @param source MPI rank data is coming from * @param tag MPI tag for identifying the data * @return Container of type T holding data sent */ template - T Recv(type, int source, int tag = 0) const; + T Recv( type, int source, int tag = 0 ) const; /** * @brief Calls MPI_Recv on an 2D array stored in an axom::Array - * + * * @tparam T Data type of an element in the array: bool, double, int currently supported * @tparam Sp axom::MemorySpace of the axom::Array * @param source MPI rank data is coming from @@ -187,30 +185,30 @@ class MPIUtility * @return 2D axom::Array holding the data sent */ template - axom::Array Recv(type>, int source, int tag = 0) const; + axom::Array Recv( type>, int source, int tag = 0 ) const; /** * @brief Sends the array stored in container to all other ranks - * + * * @tparam T Data type of the data container * @param container Stores the data to send; must have T::data() and T::size() methods */ template - void SendAll(const T& container) const; + void SendAll( const T& container ) const; /** * @brief Receives the array sent by SendAll - * + * * @tparam T Data type of the data container * @param rank Rank the data originated from * @return Container of type T holding data sent */ template - T RecvSendAll(type, int rank) const; + T RecvSendAll( type, int rank ) const; /** * @brief Sends and receives a different array to each rank - * + * * @tparam T Data type of the data container * @tparam F1 Lambda with destination rank as parameter returning container type T * @tparam F2 Lambda with container type T and source rank parameters @@ -218,52 +216,52 @@ class MPIUtility * @param process_recv Process the data received from another rank */ template - void SendRecvEach(type, F1&& build_send, F2&& process_recv) const; - -private: + void SendRecvEach( type, F1&& build_send, F2&& process_recv ) const; + private: /** - * @brief MPI_Comm used to facilitate MPI communications + * @brief MPI_Comm used to facilitate MPI communications */ const MPI_Comm comm_; /** - * @brief MPI rank of this process + * @brief MPI rank of this process */ int my_rank_; /** - * @brief Total number of MPI ranks + * @brief Total number of MPI ranks */ int n_ranks_; /** - * @brief MPI_Status object + * @brief MPI_Status object */ mutable MPI_Status status_; /** * @brief Builds binary tree describing the sequence of MPI communication - * + * * @param rank Rank data originates from * @return Tree used to describe where data is and needs to be sent in e.g. SendAll */ - BisecTree BuildSendTree(int rank) const; + BisecTree BuildSendTree( int rank ) const; /** * @brief Get the MPI_Datatype from a type T - * + * * @tparam T Datatype with a corresponding MPI_Datatype * @return MPI_Datatype corresponding to T */ template - MPI_Datatype GetMPIDatatype(T*) const { + MPI_Datatype GetMPIDatatype( T* ) const + { return GetMPIType::type>(); } /** * @brief Get the MPI_Datatype from a type T - * + * * @tparam T Datatype with a corresponding MPI_Datatype * @return MPI_Datatype corresponding to T */ @@ -272,7 +270,7 @@ class MPIUtility /** * @brief Sends data to other ranks once it has been received from SendAll - * + * * @tparam T Data type of the data container * @param container Stores the data to send; must have T::data() and T::size() methods * @param send_tree Bisection tree describing where data must be sent @@ -280,176 +278,156 @@ class MPIUtility * @param node_it Current node on the send_tree */ template - void SendToRest( - const T& container, - const BisecTree& send_tree, - BisecTree::ConstBFSLevelIterator lvl_it, - BisecTree::ConstBFSNodeIterator node_it) const; + void SendToRest( const T& container, const BisecTree& send_tree, BisecTree::ConstBFSLevelIterator lvl_it, + BisecTree::ConstBFSNodeIterator node_it ) const; }; template -T MPIUtility::AllreduceValue(T local_value, MPI_Op op) const +T MPIUtility::AllreduceValue( T local_value, MPI_Op op ) const { - MPI_Allreduce(MPI_IN_PLACE, &local_value, 1, GetMPIType(), op, comm_); + MPI_Allreduce( MPI_IN_PLACE, &local_value, 1, GetMPIType(), op, comm_ ); return local_value; } template -void MPIUtility::Allreduce(T* container, MPI_Op op) const +void MPIUtility::Allreduce( T* container, MPI_Op op ) const { - Allreduce(container->data(), container->size(), op); + Allreduce( container->data(), container->size(), op ); } template -void MPIUtility::Allreduce(T* data, int size, MPI_Op op) const +void MPIUtility::Allreduce( T* data, int size, MPI_Op op ) const { - MPI_Allreduce(MPI_IN_PLACE, data, size, GetMPIDatatype(data), - op, comm_); + MPI_Allreduce( MPI_IN_PLACE, data, size, GetMPIDatatype( data ), op, comm_ ); } template -void MPIUtility::Send(const T& container, int dest, int tag) const +void MPIUtility::Send( const T& container, int dest, int tag ) const { - MPI_Send(container.data(), container.size(), - GetMPIDatatype(container.data()), dest, tag, comm_); + MPI_Send( container.data(), container.size(), GetMPIDatatype( container.data() ), dest, tag, comm_ ); } template -void MPIUtility::Send(const axom::Array& container, int dest, int tag) const +void MPIUtility::Send( const axom::Array& container, int dest, int tag ) const { - MPI_Send(container.shape().m_data, 2, GetMPIType(), dest, tag, comm_); - MPI_Send(container.data(), container.size(), - GetMPIDatatype(container.data()), dest, tag, comm_); + MPI_Send( container.shape().m_data, 2, GetMPIType(), dest, tag, comm_ ); + MPI_Send( container.data(), container.size(), GetMPIDatatype( container.data() ), dest, tag, comm_ ); } template -std::unique_ptr MPIUtility::Isend(const T& container, int dest, int tag) const +std::unique_ptr MPIUtility::Isend( const T& container, int dest, int tag ) const { auto request = std::make_unique(); - MPI_Isend(container.data(), container.size(), - GetMPIDatatype(container.data()), dest, tag, comm_, request.get()); - return std::make_unique(std::move(request)); + MPI_Isend( container.data(), container.size(), GetMPIDatatype( container.data() ), dest, tag, comm_, request.get() ); + return std::make_unique( std::move( request ) ); } template -std::unique_ptr MPIUtility::Isend(const axom::Array& container, int dest, int tag) const +std::unique_ptr MPIUtility::Isend( const axom::Array& container, int dest, + int tag ) const { - MPI_Send(container.shape().m_data, 2, GetMPIType(), dest, tag, comm_); + MPI_Send( container.shape().m_data, 2, GetMPIType(), dest, tag, comm_ ); auto request = std::make_unique(); - MPI_Isend(container.data(), container.size(), - GetMPIDatatype(container.data()), dest, tag, comm_, request.get()); - return std::make_unique(std::move(request)); + MPI_Isend( container.data(), container.size(), GetMPIDatatype( container.data() ), dest, tag, comm_, request.get() ); + return std::make_unique( std::move( request ) ); } template -T MPIUtility::Recv(type, int source, int tag) const +T MPIUtility::Recv( type, int source, int tag ) const { auto container = T(); - MPI_Probe(source, tag, comm_, &status_); + MPI_Probe( source, tag, comm_, &status_ ); int count; - MPI_Get_count(&status_, GetMPIDatatype(container.data()), &count); - container.reserve(count); - container.resize(count); - MPI_Recv(container.data(), count, GetMPIDatatype(container.data()), source, tag, - comm_, &status_); + MPI_Get_count( &status_, GetMPIDatatype( container.data() ), &count ); + container.reserve( count ); + container.resize( count ); + MPI_Recv( container.data(), count, GetMPIDatatype( container.data() ), source, tag, comm_, &status_ ); return container; } template -axom::Array MPIUtility::Recv(type>, int source, int tag) const +axom::Array MPIUtility::Recv( type>, int source, int tag ) const { auto container = axom::Array(); axom::StackArray dim_size; - MPI_Recv(dim_size.m_data, 2, GetMPIDatatype(container.data()), - source, tag, comm_, &status_); - container.reserve(dim_size[0]*dim_size[1]); - container.resize(dim_size[0], dim_size[1]); - MPI_Recv(container.data(), container.size(), - GetMPIDatatype(container.data()), source, tag, comm_, &status_); + MPI_Recv( dim_size.m_data, 2, GetMPIDatatype( container.data() ), source, tag, comm_, &status_ ); + container.reserve( dim_size[0] * dim_size[1] ); + container.resize( dim_size[0], dim_size[1] ); + MPI_Recv( container.data(), container.size(), GetMPIDatatype( container.data() ), source, tag, comm_, &status_ ); return container; } template -void MPIUtility::SendAll(const T& container) const +void MPIUtility::SendAll( const T& container ) const { - const auto send_tree = BuildSendTree(my_rank_); + const auto send_tree = BuildSendTree( my_rank_ ); auto lvl_it = send_tree.begin(); - SendToRest(container, send_tree, lvl_it, send_tree.begin(lvl_it)); + SendToRest( container, send_tree, lvl_it, send_tree.begin( lvl_it ) ); } template -T MPIUtility::RecvSendAll(type, int rank) const +T MPIUtility::RecvSendAll( type, int rank ) const { - SLIC_ERROR_IF(rank == my_rank_, "Send and receive rank are the same."); - const auto send_tree = BuildSendTree(rank); + SLIC_ERROR_IF( rank == my_rank_, "Send and receive rank are the same." ); + const auto send_tree = BuildSendTree( rank ); auto lvl_it = --send_tree.end(); - auto node_it = send_tree.begin(lvl_it) + my_rank_; - auto it = send_tree.root(lvl_it, node_it); - while (*it.second == my_rank_) - { + auto node_it = send_tree.begin( lvl_it ) + my_rank_; + auto it = send_tree.root( lvl_it, node_it ); + while ( *it.second == my_rank_ ) { lvl_it = it.first; node_it = it.second; - it = send_tree.root(lvl_it, node_it); + it = send_tree.root( lvl_it, node_it ); } - auto container = Recv(type(), *it.second); - SendToRest(container, send_tree, lvl_it, node_it); + auto container = Recv( type(), *it.second ); + SendToRest( container, send_tree, lvl_it, node_it ); return container; } template -void MPIUtility::SendToRest( - const T& container, - const BisecTree& send_tree, - BisecTree::ConstBFSLevelIterator lvl_it, - BisecTree::ConstBFSNodeIterator node_it -) const +void MPIUtility::SendToRest( const T& container, const BisecTree& send_tree, + BisecTree::ConstBFSLevelIterator lvl_it, + BisecTree::ConstBFSNodeIterator node_it ) const { - auto it = std::make_pair(lvl_it, node_it); - while (it.first != --send_tree.end()) - { - auto left_it = send_tree.left(it); - auto right_it = send_tree.right(it); - if (*left_it.second == *it.second) - { + auto it = std::make_pair( lvl_it, node_it ); + while ( it.first != --send_tree.end() ) { + auto left_it = send_tree.left( it ); + auto right_it = send_tree.right( it ); + if ( *left_it.second == *it.second ) { it = left_it; - if (right_it.second != send_tree.end(right_it.first)) - { - Send(container, *right_it.second); + if ( right_it.second != send_tree.end( right_it.first ) ) { + Send( container, *right_it.second ); } - } - else - { + } else { it = right_it; - Send(container, *left_it.second); + Send( container, *left_it.second ); } } } template -void MPIUtility::SendRecvEach(type, F1&& build_send, F2&& process_recv) const +void MPIUtility::SendRecvEach( type, F1&& build_send, F2&& process_recv ) const { - for (int i{1}; i < n_ranks_; ++i) - { + for ( int i{ 1 }; i < n_ranks_; ++i ) { // compute which rank we are sending and receiving data to - auto dest = (my_rank_ + i) % n_ranks_; - auto source = (my_rank_ + n_ranks_ - i) % n_ranks_; + auto dest = ( my_rank_ + i ) % n_ranks_; + auto source = ( my_rank_ + n_ranks_ - i ) % n_ranks_; // build data (note data must be stored somewhere until the send completes) - auto data = build_send(dest); + auto data = build_send( dest ); // build and send data; return immediately - auto request = Isend(data, dest); + auto request = Isend( data, dest ); // receive data and process - process_recv(Recv(type(), source), source); + process_recv( Recv( type(), source ), source ); // wait for send to complete request->Wait(); } // process on-rank data (no communication) - process_recv(build_send(my_rank_), my_rank_); + process_recv( build_send( my_rank_ ), my_rank_ ); } -} // end namespace redecomp +} // end namespace redecomp #endif /* SRC_REDECOMP_UTILS_MPIUTILITY_HPP_ */ diff --git a/src/tests/mfem_smoke.cpp b/src/tests/mfem_smoke.cpp index 2de310ca..2611ae09 100644 --- a/src/tests/mfem_smoke.cpp +++ b/src/tests/mfem_smoke.cpp @@ -12,12 +12,11 @@ #include "mfem.hpp" #include "gtest/gtest.h" - //----------------------------------------------------------------------------- -TEST(mfem_smoke, basic_use) +TEST( mfem_smoke, basic_use ) { // Simple usage of a basic mfem type - mfem::Element* el = new mfem::Quadrilateral(0,1,2,3); + mfem::Element* el = new mfem::Quadrilateral( 0, 1, 2, 3 ); EXPECT_EQ( mfem::Element::QUADRILATERAL, el->GetType() ); EXPECT_EQ( 4, el->GetNVertices() ); diff --git a/src/tests/redecomp_massmatrix.cpp b/src/tests/redecomp_massmatrix.cpp index 64c36629..11b5e057 100644 --- a/src/tests/redecomp_massmatrix.cpp +++ b/src/tests/redecomp_massmatrix.cpp @@ -20,90 +20,83 @@ namespace redecomp { * */ class MassMatrixTest : public testing::TestWithParam> { -protected: + protected: double max_error_; void SetUp() override { auto mesh_file = GetParam().first; auto fe_order = GetParam().second; - - std::string mesh_filename = std::string(TRIBOL_REPO_DIR) + mesh_file; - mfem::Mesh serial_mesh { mesh_filename.c_str(), 1, 1, true }; + + std::string mesh_filename = std::string( TRIBOL_REPO_DIR ) + mesh_file; + mfem::Mesh serial_mesh{ mesh_filename.c_str(), 1, 1, true }; auto dim = serial_mesh.SpaceDimension(); serial_mesh.UniformRefinement(); serial_mesh.UniformRefinement(); - mfem::H1_FECollection h1_elems { fe_order, dim }; - mfem::ParMesh par_mesh { MPI_COMM_WORLD, serial_mesh }; - mfem::ParFiniteElementSpace par_fes { &par_mesh, &h1_elems, dim }; + mfem::H1_FECollection h1_elems{ fe_order, dim }; + mfem::ParMesh par_mesh{ MPI_COMM_WORLD, serial_mesh }; + mfem::ParFiniteElementSpace par_fes{ &par_mesh, &h1_elems, dim }; // compute mass matrix directly on ParMesh - mfem::ParBilinearForm par_bf { &par_fes }; - mfem::ConstantCoefficient rho0 { 1.0 }; - par_bf.AddDomainIntegrator(new mfem::VectorMassIntegrator(rho0)); + mfem::ParBilinearForm par_bf{ &par_fes }; + mfem::ConstantCoefficient rho0{ 1.0 }; + par_bf.AddDomainIntegrator( new mfem::VectorMassIntegrator( rho0 ) ); par_bf.Assemble(); par_bf.Finalize(); - std::unique_ptr par_hpm { par_bf.ParallelAssemble() }; + std::unique_ptr par_hpm{ par_bf.ParallelAssemble() }; mfem::SparseMatrix par_sm; - par_hpm->MergeDiagAndOffd(par_sm); + par_hpm->MergeDiagAndOffd( par_sm ); mfem::DenseMatrix mass_direct; - par_sm.ToDenseMatrix(mass_direct); + par_sm.ToDenseMatrix( mass_direct ); // compute mass matrix on Redecomp and transfer to ParMesh - redecomp::RedecompMesh redecomp_mesh { par_mesh }; - mfem::FiniteElementSpace redecomp_fes { &redecomp_mesh, &h1_elems, dim }; - mfem::BilinearForm redecomp_bf { &redecomp_fes }; - redecomp_bf.AddDomainIntegrator(new mfem::VectorMassIntegrator(rho0)); + redecomp::RedecompMesh redecomp_mesh{ par_mesh }; + mfem::FiniteElementSpace redecomp_fes{ &redecomp_mesh, &h1_elems, dim }; + mfem::BilinearForm redecomp_bf{ &redecomp_fes }; + redecomp_bf.AddDomainIntegrator( new mfem::VectorMassIntegrator( rho0 ) ); int n_els = redecomp_fes.GetNE(); - auto elem_idx = redecomp::ArrayUtility::IndexArray(n_els); - axom::Array elem_mats { n_els, n_els }; - for (int i{0}; i < n_els; ++i) - { - redecomp_bf.ComputeElementMatrix(i, elem_mats[i]); + auto elem_idx = redecomp::ArrayUtility::IndexArray( n_els ); + axom::Array elem_mats{ n_els, n_els }; + for ( int i{ 0 }; i < n_els; ++i ) { + redecomp_bf.ComputeElementMatrix( i, elem_mats[i] ); } - redecomp::MatrixTransfer matrix_xfer { - par_fes, - par_fes, - redecomp_fes, - redecomp_fes - }; - auto redecomp_hpm = matrix_xfer.TransferToParallel(elem_idx, elem_idx, elem_mats); + redecomp::MatrixTransfer matrix_xfer{ par_fes, par_fes, redecomp_fes, redecomp_fes }; + auto redecomp_hpm = matrix_xfer.TransferToParallel( elem_idx, elem_idx, elem_mats ); mfem::SparseMatrix redecomp_sm; - redecomp_hpm->MergeDiagAndOffd(redecomp_sm); + redecomp_hpm->MergeDiagAndOffd( redecomp_sm ); mfem::DenseMatrix mass_diff; - redecomp_sm.ToDenseMatrix(mass_diff); + redecomp_sm.ToDenseMatrix( mass_diff ); mass_diff -= mass_direct; max_error_ = mass_diff.MaxMaxNorm(); - max_error_ = redecomp_mesh.getMPIUtility().AllreduceValue(max_error_, MPI_MAX); + max_error_ = redecomp_mesh.getMPIUtility().AllreduceValue( max_error_, MPI_MAX ); } }; -TEST_P(MassMatrixTest, mass_matrix_transfer) +TEST_P( MassMatrixTest, mass_matrix_transfer ) { - EXPECT_LT(max_error_, 1.0e-13); + EXPECT_LT( max_error_, 1.0e-13 ); - MPI_Barrier(MPI_COMM_WORLD); + MPI_Barrier( MPI_COMM_WORLD ); } -INSTANTIATE_TEST_SUITE_P(redecomp, MassMatrixTest, testing::Values( - std::make_pair("/data/star.mesh", 1), - std::make_pair("/data/star.mesh", 3), - std::make_pair("/data/two_hex.mesh", 1), - std::make_pair("/data/two_hex.mesh", 3) -)); +INSTANTIATE_TEST_SUITE_P( redecomp, MassMatrixTest, + testing::Values( std::make_pair( "/data/star.mesh", 1 ), + std::make_pair( "/data/star.mesh", 3 ), + std::make_pair( "/data/two_hex.mesh", 1 ), + std::make_pair( "/data/two_hex.mesh", 3 ) ) ); } // namespace redecomp //------------------------------------------------------------------------------ #include "axom/slic/core/SimpleLogger.hpp" -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - MPI_Init(&argc, &argv); + MPI_Init( &argc, &argv ); - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); axom::slic::SimpleLogger logger; // create & initialize test logger, finalized when // exiting main scope diff --git a/src/tests/redecomp_multitransfer.cpp b/src/tests/redecomp_multitransfer.cpp index 957da759..08356e56 100644 --- a/src/tests/redecomp_multitransfer.cpp +++ b/src/tests/redecomp_multitransfer.cpp @@ -11,16 +11,17 @@ #include "tribol/config.hpp" #include "redecomp/redecomp.hpp" -namespace redecomp -{ - -enum class MeshType {TwoStarRotated, TwoHexSurface}; +namespace redecomp { -class MultiTransferTest : public testing::TestWithParam> +enum class MeshType { -protected: - struct MeshData - { + TwoStarRotated, + TwoHexSurface +}; + +class MultiTransferTest : public testing::TestWithParam> { + protected: + struct MeshData { mfem::ParMesh par_mesh; std::unique_ptr redecomp_mesh; std::unique_ptr h1_elems; @@ -36,207 +37,165 @@ class MultiTransferTest : public testing::TestWithParam std::unique_ptr xfer_quadfn; }; std::vector mesh_data_; - void CreateMeshData(std::vector&& serial_meshes) + void CreateMeshData( std::vector&& serial_meshes ) { std::vector par_meshes; - par_meshes.reserve(serial_meshes.size()); - for (auto& serial_mesh : serial_meshes) - { - par_meshes.emplace_back(MPI_COMM_WORLD, serial_mesh); + par_meshes.reserve( serial_meshes.size() ); + for ( auto& serial_mesh : serial_meshes ) { + par_meshes.emplace_back( MPI_COMM_WORLD, serial_mesh ); } - CreateMeshData(std::move(par_meshes)); + CreateMeshData( std::move( par_meshes ) ); } - void CreateMeshData(std::vector&& par_meshes) + void CreateMeshData( std::vector&& par_meshes ) { mesh_data_.clear(); - mesh_data_.reserve(par_meshes.size()); + mesh_data_.reserve( par_meshes.size() ); auto par_mesh_ptrs = std::vector(); - par_mesh_ptrs.reserve(par_meshes.size()); - for (auto& par_mesh : par_meshes) - { + par_mesh_ptrs.reserve( par_meshes.size() ); + for ( auto& par_mesh : par_meshes ) { auto mesh_data = MeshData(); - mesh_data.par_mesh = std::move(par_mesh); - mesh_data_.emplace_back(std::move(mesh_data)); - par_mesh_ptrs.push_back(&mesh_data_.back().par_mesh); + mesh_data.par_mesh = std::move( par_mesh ); + mesh_data_.emplace_back( std::move( mesh_data ) ); + par_mesh_ptrs.push_back( &mesh_data_.back().par_mesh ); } - auto multiredecomp = MultiRedecomp( - mesh_data_[0].par_mesh.SpaceDimension(), - mesh_data_[0].par_mesh.GetComm() - ); - auto redecomp_meshes = multiredecomp.createRedecompMeshes(par_mesh_ptrs); - for (size_t i{0}; i < par_meshes.size(); ++i) - { - mesh_data_[i].redecomp_mesh = std::move(redecomp_meshes[i]); + auto multiredecomp = MultiRedecomp( mesh_data_[0].par_mesh.SpaceDimension(), mesh_data_[0].par_mesh.GetComm() ); + auto redecomp_meshes = multiredecomp.createRedecompMeshes( par_mesh_ptrs ); + for ( size_t i{ 0 }; i < par_meshes.size(); ++i ) { + mesh_data_[i].redecomp_mesh = std::move( redecomp_meshes[i] ); } } void CreateRotatedStarMesh() { // open meshes auto serial_meshes = std::vector(); - serial_meshes.reserve(2); - std::string mesh_filename = std::string(TRIBOL_REPO_DIR) + "/data/star.mesh"; - serial_meshes.emplace_back(mesh_filename.c_str(), 1, 1, true); - serial_meshes.emplace_back(mesh_filename.c_str(), 1, 1, true); + serial_meshes.reserve( 2 ); + std::string mesh_filename = std::string( TRIBOL_REPO_DIR ) + "/data/star.mesh"; + serial_meshes.emplace_back( mesh_filename.c_str(), 1, 1, true ); + serial_meshes.emplace_back( mesh_filename.c_str(), 1, 1, true ); // rotate second mesh 30 degrees auto theta = 30.0; theta = theta * redecomp::pi / 180.0; - auto R = axom::Array(3, 3); - R(0, 0) = cos(theta); R(0, 1) = -sin(theta); R(0, 2) = 0.0; - R(1, 0) = sin(theta); R(1, 1) = cos(theta); R(1, 2) = 0.0; - R(2, 0) = 0.0; R(2, 1) = 0.0; R(2, 2) = 1.0; + auto R = axom::Array( 3, 3 ); + R( 0, 0 ) = cos( theta ); + R( 0, 1 ) = -sin( theta ); + R( 0, 2 ) = 0.0; + R( 1, 0 ) = sin( theta ); + R( 1, 1 ) = cos( theta ); + R( 1, 2 ) = 0.0; + R( 2, 0 ) = 0.0; + R( 2, 1 ) = 0.0; + R( 2, 2 ) = 1.0; auto dim = serial_meshes[1].Dimension(); - for (int v{0}; v < serial_meshes[1].GetNV(); ++v) - { - auto tmp_vert = axom::Array(dim, dim); - auto vert = serial_meshes[1].GetVertex(v); - for (int i{0}; i < dim; ++i) - { - for (int j{0}; j < dim; ++j) - { - tmp_vert[i] = tmp_vert[i] + R(i, j)*vert[j]; + for ( int v{ 0 }; v < serial_meshes[1].GetNV(); ++v ) { + auto tmp_vert = axom::Array( dim, dim ); + auto vert = serial_meshes[1].GetVertex( v ); + for ( int i{ 0 }; i < dim; ++i ) { + for ( int j{ 0 }; j < dim; ++j ) { + tmp_vert[i] = tmp_vert[i] + R( i, j ) * vert[j]; } } - for (int i{0}; i < dim; ++i) - { + for ( int i{ 0 }; i < dim; ++i ) { vert[i] = tmp_vert[i]; } } auto node_gf = serial_meshes[1].GetNodes(); - if (node_gf) - { + if ( node_gf ) { auto node_fes = node_gf->FESpace(); auto vdim = node_fes->GetVDim(); - for (int n{0}; n < node_fes->GetNDofs(); ++n) - { - auto tmp_node = axom::Array(vdim, vdim); - for (int i{0}; i < vdim; ++i) - { - for (int j{0}; j < vdim; ++j) - { - tmp_node[i] = tmp_node[i] + R(i, j)*(*node_gf)(node_fes->DofToVDof(n, j)); + for ( int n{ 0 }; n < node_fes->GetNDofs(); ++n ) { + auto tmp_node = axom::Array( vdim, vdim ); + for ( int i{ 0 }; i < vdim; ++i ) { + for ( int j{ 0 }; j < vdim; ++j ) { + tmp_node[i] = tmp_node[i] + R( i, j ) * ( *node_gf )( node_fes->DofToVDof( n, j ) ); } } - for (int i{0}; i < vdim; ++i) - { - (*node_gf)(node_fes->DofToVDof(n, i)) = tmp_node[i]; + for ( int i{ 0 }; i < vdim; ++i ) { + ( *node_gf )( node_fes->DofToVDof( n, i ) ) = tmp_node[i]; } } } // finish mesh setup - CreateMeshData(std::move(serial_meshes)); + CreateMeshData( std::move( serial_meshes ) ); } void CreateTwoHexMesh() { - std::string mesh_filename = std::string(TRIBOL_REPO_DIR) + "/data/two_hex.mesh"; - auto serial_mesh = mfem::Mesh(mesh_filename.c_str(), 1, 1, true); + std::string mesh_filename = std::string( TRIBOL_REPO_DIR ) + "/data/two_hex.mesh"; + auto serial_mesh = mfem::Mesh( mesh_filename.c_str(), 1, 1, true ); serial_mesh.UniformRefinement(); serial_mesh.UniformRefinement(); - auto par_mesh = mfem::ParMesh(MPI_COMM_WORLD, serial_mesh); + auto par_mesh = mfem::ParMesh( MPI_COMM_WORLD, serial_mesh ); auto par_submeshes = std::vector(); - par_submeshes.reserve(2); - auto bdry_attrib = mfem::Array(1); + par_submeshes.reserve( 2 ); + auto bdry_attrib = mfem::Array( 1 ); bdry_attrib[0] = 4; - par_submeshes.push_back(mfem::ParSubMesh::CreateFromBoundary(par_mesh, bdry_attrib)); + par_submeshes.push_back( mfem::ParSubMesh::CreateFromBoundary( par_mesh, bdry_attrib ) ); bdry_attrib[0] = 5; - par_submeshes.push_back(mfem::ParSubMesh::CreateFromBoundary(par_mesh, bdry_attrib)); - CreateMeshData(std::move(par_submeshes)); + par_submeshes.push_back( mfem::ParSubMesh::CreateFromBoundary( par_mesh, bdry_attrib ) ); + CreateMeshData( std::move( par_submeshes ) ); } void CreateGridFnData() { - for (auto& mesh_data : mesh_data_) - { + for ( auto& mesh_data : mesh_data_ ) { auto dim = mesh_data.par_mesh.Dimension(); - mesh_data.h1_elems = std::make_unique(GetParam().second, dim); - mesh_data.par_vector_space = std::make_unique( - &mesh_data.par_mesh, - mesh_data.h1_elems.get(), - dim - ); - mesh_data.orig_gridfn = - std::make_unique(mesh_data.par_vector_space.get()); - mesh_data.final_gridfn = - std::make_unique(mesh_data.par_vector_space.get()); - if (GetParam().second > 1) - { - mesh_data.par_mesh.SetNodalGridFunction(mesh_data.orig_gridfn.get(), false); - } - else - { - mesh_data.par_mesh.GetNodes(*mesh_data.orig_gridfn); + mesh_data.h1_elems = std::make_unique( GetParam().second, dim ); + mesh_data.par_vector_space = + std::make_unique( &mesh_data.par_mesh, mesh_data.h1_elems.get(), dim ); + mesh_data.orig_gridfn = std::make_unique( mesh_data.par_vector_space.get() ); + mesh_data.final_gridfn = std::make_unique( mesh_data.par_vector_space.get() ); + if ( GetParam().second > 1 ) { + mesh_data.par_mesh.SetNodalGridFunction( mesh_data.orig_gridfn.get(), false ); + } else { + mesh_data.par_mesh.GetNodes( *mesh_data.orig_gridfn ); } mesh_data.redecomp_vector_space = std::make_unique( - mesh_data.redecomp_mesh.get(), - mesh_data.h1_elems.get(), - mesh_data.redecomp_mesh->Dimension() - ); - mesh_data.xfer_gridfn = - std::make_unique(mesh_data.redecomp_vector_space.get()); + mesh_data.redecomp_mesh.get(), mesh_data.h1_elems.get(), mesh_data.redecomp_mesh->Dimension() ); + mesh_data.xfer_gridfn = std::make_unique( mesh_data.redecomp_vector_space.get() ); } } void CreateQuadFnData() { - for (auto& mesh_data : mesh_data_) - { - mesh_data.par_quad_space = std::make_unique( - &mesh_data.par_mesh, - 0 - ); - mesh_data.orig_quadfn = std::make_unique( - mesh_data.par_quad_space.get() - ); - mesh_data.final_quadfn = std::make_unique( - mesh_data.par_quad_space.get() - ); - for (int e{0}; e < mesh_data.par_mesh.GetNE(); ++e) - { + for ( auto& mesh_data : mesh_data_ ) { + mesh_data.par_quad_space = std::make_unique( &mesh_data.par_mesh, 0 ); + mesh_data.orig_quadfn = std::make_unique( mesh_data.par_quad_space.get() ); + mesh_data.final_quadfn = std::make_unique( mesh_data.par_quad_space.get() ); + for ( int e{ 0 }; e < mesh_data.par_mesh.GetNE(); ++e ) { auto quad_val = mfem::Vector(); - mesh_data.orig_quadfn->GetValues(e, quad_val); - for (int i{0}; i < quad_val.Size(); ++i) - { - quad_val[i] = static_cast(mesh_data_[i].par_mesh.GetGlobalElementNum(e)); + mesh_data.orig_quadfn->GetValues( e, quad_val ); + for ( int i{ 0 }; i < quad_val.Size(); ++i ) { + quad_val[i] = static_cast( mesh_data_[i].par_mesh.GetGlobalElementNum( e ) ); } } - mesh_data.redecomp_quad_space = std::make_unique( - mesh_data.redecomp_mesh.get(), - 0 - ); - mesh_data.xfer_quadfn = std::make_unique( - mesh_data.redecomp_quad_space.get() - ); + mesh_data.redecomp_quad_space = std::make_unique( mesh_data.redecomp_mesh.get(), 0 ); + mesh_data.xfer_quadfn = std::make_unique( mesh_data.redecomp_quad_space.get() ); } } double CalcGridFnl2Error() { auto total_error = 0.0; - for (auto& mesh_data : mesh_data_) - { - if (mesh_data.final_gridfn->FESpace()->GetNDofs() > 0) - { + for ( auto& mesh_data : mesh_data_ ) { + if ( mesh_data.final_gridfn->FESpace()->GetNDofs() > 0 ) { *mesh_data.final_gridfn -= *mesh_data.orig_gridfn; total_error += mesh_data.final_gridfn->Norml2() / mesh_data.orig_gridfn->Norml2(); } } - MPI_Allreduce(MPI_IN_PLACE, &total_error, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce( MPI_IN_PLACE, &total_error, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD ); return total_error; } double CalcQuadFnl2Error() { auto total_error = 0.0; - for (auto& mesh_data : mesh_data_) - { - if (mesh_data.final_quadfn->GetSpace()->GetSize() > 0) - { + for ( auto& mesh_data : mesh_data_ ) { + if ( mesh_data.final_quadfn->GetSpace()->GetSize() > 0 ) { *mesh_data.final_quadfn -= *mesh_data.orig_quadfn; total_error += mesh_data.final_quadfn->Norml2() / mesh_data.orig_quadfn->Norml2(); } } - MPI_Allreduce(MPI_IN_PLACE, &total_error, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce( MPI_IN_PLACE, &total_error, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD ); return total_error; } - void CreateMesh(MeshType mesh_type) + void CreateMesh( MeshType mesh_type ) { - switch (mesh_type) - { + switch ( mesh_type ) { case MeshType::TwoStarRotated: CreateRotatedStarMesh(); break; @@ -247,70 +206,63 @@ class MultiTransferTest : public testing::TestWithParam } }; -TEST_P(MultiTransferTest, element_gridfn) +TEST_P( MultiTransferTest, element_gridfn ) { - CreateMesh(GetParam().first); + CreateMesh( GetParam().first ); CreateGridFnData(); auto transfer_map = RedecompTransfer(); - for (auto& mesh_data : mesh_data_) - { - transfer_map.TransferToSerial(*mesh_data.orig_gridfn, *mesh_data.xfer_gridfn); - transfer_map.TransferToParallel(*mesh_data.xfer_gridfn, *mesh_data.final_gridfn); + for ( auto& mesh_data : mesh_data_ ) { + transfer_map.TransferToSerial( *mesh_data.orig_gridfn, *mesh_data.xfer_gridfn ); + transfer_map.TransferToParallel( *mesh_data.xfer_gridfn, *mesh_data.final_gridfn ); } - EXPECT_LT(CalcGridFnl2Error(), 1.0e-13); - MPI_Barrier(MPI_COMM_WORLD); + EXPECT_LT( CalcGridFnl2Error(), 1.0e-13 ); + MPI_Barrier( MPI_COMM_WORLD ); } -TEST_P(MultiTransferTest, element_quadfn) +TEST_P( MultiTransferTest, element_quadfn ) { - CreateMesh(GetParam().first); + CreateMesh( GetParam().first ); CreateQuadFnData(); auto transfer_map = RedecompTransfer(); - for (auto& mesh_data : mesh_data_) - { - transfer_map.TransferToSerial(*mesh_data.orig_quadfn, *mesh_data.xfer_quadfn); - transfer_map.TransferToParallel(*mesh_data.xfer_quadfn, *mesh_data.final_quadfn); + for ( auto& mesh_data : mesh_data_ ) { + transfer_map.TransferToSerial( *mesh_data.orig_quadfn, *mesh_data.xfer_quadfn ); + transfer_map.TransferToParallel( *mesh_data.xfer_quadfn, *mesh_data.final_quadfn ); } - EXPECT_LT(CalcQuadFnl2Error(), 1.0e-13); - MPI_Barrier(MPI_COMM_WORLD); + EXPECT_LT( CalcQuadFnl2Error(), 1.0e-13 ); + MPI_Barrier( MPI_COMM_WORLD ); } -TEST_P(MultiTransferTest, node_gridfn) +TEST_P( MultiTransferTest, node_gridfn ) { - CreateMesh(GetParam().first); + CreateMesh( GetParam().first ); CreateGridFnData(); - for (auto& mesh_data : mesh_data_) - { - auto transfer_map = RedecompTransfer( - *mesh_data.par_vector_space, - *mesh_data.redecomp_vector_space - ); - transfer_map.TransferToSerial(*mesh_data.orig_gridfn, *mesh_data.xfer_gridfn); - transfer_map.TransferToParallel(*mesh_data.xfer_gridfn, *mesh_data.final_gridfn); + for ( auto& mesh_data : mesh_data_ ) { + auto transfer_map = RedecompTransfer( *mesh_data.par_vector_space, *mesh_data.redecomp_vector_space ); + transfer_map.TransferToSerial( *mesh_data.orig_gridfn, *mesh_data.xfer_gridfn ); + transfer_map.TransferToParallel( *mesh_data.xfer_gridfn, *mesh_data.final_gridfn ); } - EXPECT_LT(CalcGridFnl2Error(), 1.0e-13); - MPI_Barrier(MPI_COMM_WORLD); + EXPECT_LT( CalcGridFnl2Error(), 1.0e-13 ); + MPI_Barrier( MPI_COMM_WORLD ); } -INSTANTIATE_TEST_SUITE_P(redecomp, MultiTransferTest, testing::Values( - std::make_pair(MeshType::TwoStarRotated, 1), - std::make_pair(MeshType::TwoStarRotated, 3), - std::make_pair(MeshType::TwoHexSurface, 1), - std::make_pair(MeshType::TwoHexSurface, 3) -)); +INSTANTIATE_TEST_SUITE_P( redecomp, MultiTransferTest, + testing::Values( std::make_pair( MeshType::TwoStarRotated, 1 ), + std::make_pair( MeshType::TwoStarRotated, 3 ), + std::make_pair( MeshType::TwoHexSurface, 1 ), + std::make_pair( MeshType::TwoHexSurface, 3 ) ) ); } // namespace redecomp //------------------------------------------------------------------------------ #include "axom/slic/core/SimpleLogger.hpp" -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - MPI_Init(&argc, &argv); + MPI_Init( &argc, &argv ); - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); axom::slic::SimpleLogger logger; // create & initialize test logger, finalized when // exiting main scope diff --git a/src/tests/redecomp_rectmatrix.cpp b/src/tests/redecomp_rectmatrix.cpp index 79bccb2b..b39f3ecc 100644 --- a/src/tests/redecomp_rectmatrix.cpp +++ b/src/tests/redecomp_rectmatrix.cpp @@ -21,93 +21,86 @@ namespace redecomp { * */ class RectMatrixTest : public testing::TestWithParam> { -protected: + protected: double max_error_; void SetUp() override { auto mesh_file = GetParam().first; auto fe_order = GetParam().second; - std::string mesh_filename = std::string(TRIBOL_REPO_DIR) + mesh_file; - mfem::Mesh serial_mesh { mesh_filename.c_str(), 1, 1, true }; + std::string mesh_filename = std::string( TRIBOL_REPO_DIR ) + mesh_file; + mfem::Mesh serial_mesh{ mesh_filename.c_str(), 1, 1, true }; auto dim = serial_mesh.SpaceDimension(); serial_mesh.UniformRefinement(); serial_mesh.UniformRefinement(); - mfem::H1_FECollection h1_elems1 { fe_order, dim }; - mfem::H1_FECollection h1_elems2 { 2, dim }; - mfem::ParMesh par_mesh { MPI_COMM_WORLD, serial_mesh }; - mfem::ParFiniteElementSpace par_fes1 { &par_mesh, &h1_elems1, 1 }; - mfem::ParFiniteElementSpace par_fes2 { &par_mesh, &h1_elems2, 1 }; + mfem::H1_FECollection h1_elems1{ fe_order, dim }; + mfem::H1_FECollection h1_elems2{ 2, dim }; + mfem::ParMesh par_mesh{ MPI_COMM_WORLD, serial_mesh }; + mfem::ParFiniteElementSpace par_fes1{ &par_mesh, &h1_elems1, 1 }; + mfem::ParFiniteElementSpace par_fes2{ &par_mesh, &h1_elems2, 1 }; // compute rectangular matrix directly on ParMesh - mfem::ParMixedBilinearForm par_bf { &par_fes1, &par_fes2 }; - mfem::ConstantCoefficient rho0 { 1.0 }; - par_bf.AddDomainIntegrator(new mfem::MixedScalarMassIntegrator(rho0)); + mfem::ParMixedBilinearForm par_bf{ &par_fes1, &par_fes2 }; + mfem::ConstantCoefficient rho0{ 1.0 }; + par_bf.AddDomainIntegrator( new mfem::MixedScalarMassIntegrator( rho0 ) ); par_bf.Assemble(); par_bf.Finalize(); - std::unique_ptr par_hpm { par_bf.ParallelAssemble() }; + std::unique_ptr par_hpm{ par_bf.ParallelAssemble() }; mfem::SparseMatrix par_sm; - par_hpm->MergeDiagAndOffd(par_sm); + par_hpm->MergeDiagAndOffd( par_sm ); mfem::DenseMatrix rect_direct; - par_sm.ToDenseMatrix(rect_direct); + par_sm.ToDenseMatrix( rect_direct ); // compute rectangular matrix on Redecomp and transfer to ParMesh - redecomp::RedecompMesh redecomp_mesh { par_mesh }; - mfem::FiniteElementSpace redecomp_fes1 { &redecomp_mesh, &h1_elems1, 1 }; - mfem::FiniteElementSpace redecomp_fes2 { &redecomp_mesh, &h1_elems2, 1 }; - mfem::MixedBilinearForm redecomp_bf { &redecomp_fes1, &redecomp_fes2 }; - redecomp_bf.AddDomainIntegrator(new mfem::MixedScalarMassIntegrator(rho0)); + redecomp::RedecompMesh redecomp_mesh{ par_mesh }; + mfem::FiniteElementSpace redecomp_fes1{ &redecomp_mesh, &h1_elems1, 1 }; + mfem::FiniteElementSpace redecomp_fes2{ &redecomp_mesh, &h1_elems2, 1 }; + mfem::MixedBilinearForm redecomp_bf{ &redecomp_fes1, &redecomp_fes2 }; + redecomp_bf.AddDomainIntegrator( new mfem::MixedScalarMassIntegrator( rho0 ) ); int n_els = redecomp_fes1.GetNE(); - auto elem_idx = redecomp::ArrayUtility::IndexArray(n_els); - axom::Array elem_mats { n_els, n_els }; - for (int i{0}; i < n_els; ++i) - { - redecomp_bf.ComputeElementMatrix(i, elem_mats[i]); + auto elem_idx = redecomp::ArrayUtility::IndexArray( n_els ); + axom::Array elem_mats{ n_els, n_els }; + for ( int i{ 0 }; i < n_els; ++i ) { + redecomp_bf.ComputeElementMatrix( i, elem_mats[i] ); } - redecomp::MatrixTransfer matrix_xfer { - par_fes2, - par_fes1, - redecomp_fes2, - redecomp_fes1 - }; - auto redecomp_hpm = matrix_xfer.TransferToParallel(elem_idx, elem_idx, elem_mats); + redecomp::MatrixTransfer matrix_xfer{ par_fes2, par_fes1, redecomp_fes2, redecomp_fes1 }; + auto redecomp_hpm = matrix_xfer.TransferToParallel( elem_idx, elem_idx, elem_mats ); mfem::SparseMatrix redecomp_sm; - redecomp_hpm->MergeDiagAndOffd(redecomp_sm); + redecomp_hpm->MergeDiagAndOffd( redecomp_sm ); mfem::DenseMatrix rect_diff; - redecomp_sm.ToDenseMatrix(rect_diff); + redecomp_sm.ToDenseMatrix( rect_diff ); rect_diff -= rect_direct; max_error_ = rect_diff.MaxMaxNorm(); - max_error_ = redecomp_mesh.getMPIUtility().AllreduceValue(max_error_, MPI_MAX); + max_error_ = redecomp_mesh.getMPIUtility().AllreduceValue( max_error_, MPI_MAX ); } }; -TEST_P(RectMatrixTest, rect_matrix_transfer) +TEST_P( RectMatrixTest, rect_matrix_transfer ) { - EXPECT_LT(max_error_, 1.0e-13); + EXPECT_LT( max_error_, 1.0e-13 ); - MPI_Barrier(MPI_COMM_WORLD); + MPI_Barrier( MPI_COMM_WORLD ); } -INSTANTIATE_TEST_SUITE_P(redecomp, RectMatrixTest, testing::Values( - std::make_pair("/data/star.mesh", 1), - std::make_pair("/data/star.mesh", 3), - std::make_pair("/data/two_hex.mesh", 1), - std::make_pair("/data/two_hex.mesh", 3) -)); +INSTANTIATE_TEST_SUITE_P( redecomp, RectMatrixTest, + testing::Values( std::make_pair( "/data/star.mesh", 1 ), + std::make_pair( "/data/star.mesh", 3 ), + std::make_pair( "/data/two_hex.mesh", 1 ), + std::make_pair( "/data/two_hex.mesh", 3 ) ) ); } // namespace redecomp //------------------------------------------------------------------------------ #include "axom/slic/core/SimpleLogger.hpp" -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - MPI_Init(&argc, &argv); + MPI_Init( &argc, &argv ); - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); axom::slic::SimpleLogger logger; // create & initialize test logger, finalized when // exiting main scope diff --git a/src/tests/redecomp_sparsematrix.cpp b/src/tests/redecomp_sparsematrix.cpp index f80f1108..480084cf 100644 --- a/src/tests/redecomp_sparsematrix.cpp +++ b/src/tests/redecomp_sparsematrix.cpp @@ -19,82 +19,77 @@ namespace redecomp { * @brief This test transfers a symmetric mfem::SparseMatrix whose rows and colums are associated with an order-0 L2 * field on a RedecompMesh to an mfem::HypreParMatrix on the associated parent mfem::ParMesh. The transferred matrix is * verified by applying it to a field and comparing the result to an analytic solution for the operator. - * + * * @details This test relies on using a cartesian mesh with uniform element size to obtain an analytical definition of * the filtered field. Do not change the mesh to anything other than a square without adjusting the analytical filtered * definition accordingly. * */ class SparseMatrixTest : public testing::TestWithParam> { -protected: + protected: double max_error_; void SetUp() override { - auto dim = std::get<0>(GetParam()); // spatial dimension of the mesh - auto ref_levels = std::get<1>(GetParam()); // number of times to refine the mesh - auto filter_radius = std::get<2>(GetParam()); // filter radius of the explicit density filter - - double side_length = 1.0; // side length of square domain - int n_elem_per_dim = 4; // number of elements per dimension + auto dim = std::get<0>( GetParam() ); // spatial dimension of the mesh + auto ref_levels = std::get<1>( GetParam() ); // number of times to refine the mesh + auto filter_radius = std::get<2>( GetParam() ); // filter radius of the explicit density filter + + double side_length = 1.0; // side length of square domain + int n_elem_per_dim = 4; // number of elements per dimension mfem::Mesh serial_mesh; - if (dim == 2) { - serial_mesh = mfem::Mesh::MakeCartesian2D(n_elem_per_dim, n_elem_per_dim, - mfem::Element::Type::QUADRILATERAL, false, side_length, side_length); - } - else { - serial_mesh = mfem::Mesh::MakeCartesian3D(n_elem_per_dim, n_elem_per_dim, n_elem_per_dim, - mfem::Element::Type::HEXAHEDRON, side_length, side_length, side_length); + if ( dim == 2 ) { + serial_mesh = mfem::Mesh::MakeCartesian2D( n_elem_per_dim, n_elem_per_dim, mfem::Element::Type::QUADRILATERAL, + false, side_length, side_length ); + } else { + serial_mesh = + mfem::Mesh::MakeCartesian3D( n_elem_per_dim, n_elem_per_dim, n_elem_per_dim, mfem::Element::Type::HEXAHEDRON, + side_length, side_length, side_length ); } // refine mesh "ref_levels" times - for (int i{0}; i < ref_levels; ++i){ + for ( int i{ 0 }; i < ref_levels; ++i ) { serial_mesh.UniformRefinement(); } // update the number of elements per dimension to reflect the refined mesh - n_elem_per_dim *= std::pow(2, ref_levels); + n_elem_per_dim *= std::pow( 2, ref_levels ); // compute the element side length double dx = side_length / n_elem_per_dim; // build a parallel mesh with associated 0-order, L2 finite element space - mfem::ParMesh par_mesh { MPI_COMM_WORLD, serial_mesh }; - mfem::L2_FECollection l2_elems { 0, dim }; - mfem::ParFiniteElementSpace par_fes { &par_mesh, &l2_elems }; + mfem::ParMesh par_mesh{ MPI_COMM_WORLD, serial_mesh }; + mfem::L2_FECollection l2_elems{ 0, dim }; + mfem::ParFiniteElementSpace par_fes{ &par_mesh, &l2_elems }; // create RedecompMesh and fe space and sparse matrix transfer object - redecomp::RedecompMesh redecomp_mesh { par_mesh, filter_radius, redecomp::RedecompMesh::RCB }; - mfem::FiniteElementSpace redecomp_fes { &redecomp_mesh, &l2_elems }; - redecomp::SparseMatrixTransfer matrix_xfer { - par_fes, - par_fes, - redecomp_fes, - redecomp_fes - }; + redecomp::RedecompMesh redecomp_mesh{ par_mesh, filter_radius, redecomp::RedecompMesh::RCB }; + mfem::FiniteElementSpace redecomp_fes{ &redecomp_mesh, &l2_elems }; + redecomp::SparseMatrixTransfer matrix_xfer{ par_fes, par_fes, redecomp_fes, redecomp_fes }; // compute sparse matrix on redecomp_fes (rows and col) - mfem::SparseMatrix W_redecomp { redecomp_fes.GetVSize(), redecomp_fes.GetVSize() }; + mfem::SparseMatrix W_redecomp{ redecomp_fes.GetVSize(), redecomp_fes.GetVSize() }; // define kernel of density filter (using isotropic "cone" filter here) - auto filter_kernel = [&filter_radius](const mfem::Vector &xi, const mfem::Vector &xj) { - return std::max(0.0, filter_radius - xi.DistanceTo(xj)); + auto filter_kernel = [&filter_radius]( const mfem::Vector& xi, const mfem::Vector& xj ) { + return std::max( 0.0, filter_radius - xi.DistanceTo( xj ) ); }; // intialize containers needed for filter evaluation int n_local_elem = redecomp_mesh.GetNE(); - mfem::Vector xi(dim), xj(dim); - std::vector n_row_entries(n_local_elem); + mfem::Vector xi( dim ), xj( dim ); + std::vector n_row_entries( n_local_elem ); // loop over each element to form a row of the matrix - for (int i=0; i 0.0) { - W_redecomp.Add(i, j, Wij); + if ( Wij > 0.0 ) { + W_redecomp.Add( i, j, Wij ); n_row_entries[i]++; } } @@ -104,48 +99,47 @@ class SparseMatrixTest : public testing::TestWithParamMult(x, xf_filt); + W->Mult( x, xf_filt ); // compute analytical filtered field - xf_func.ProjectCoefficient(xfCoef); + xf_func.ProjectCoefficient( xfCoef ); // compute error - mfem::ParGridFunction error = xf_filt; - error -= xf_func; - max_error_ = mfem::InnerProduct(par_mesh.GetComm(), error, error); + mfem::ParGridFunction error = xf_filt; + error -= xf_func; + max_error_ = mfem::InnerProduct( par_mesh.GetComm(), error, error ); } }; -TEST_P(SparseMatrixTest, mass_matrix_transfer) +TEST_P( SparseMatrixTest, mass_matrix_transfer ) { - EXPECT_LT(max_error_, 1.0e-13); + EXPECT_LT( max_error_, 1.0e-13 ); - MPI_Barrier(MPI_COMM_WORLD); + MPI_Barrier( MPI_COMM_WORLD ); } -INSTANTIATE_TEST_SUITE_P(redecomp, SparseMatrixTest, testing::Values( - std::make_tuple(2, 0, 0.1), - std::make_tuple(2, 0, 0.4), - std::make_tuple(2, 1, 0.1), - std::make_tuple(2, 1, 0.4), - std::make_tuple(2, 2, 0.1), - std::make_tuple(2, 2, 0.4), - std::make_tuple(2, 3, 0.1), - std::make_tuple(2, 3, 0.4), - std::make_tuple(3, 0, 0.1), - std::make_tuple(3, 0, 0.4), - std::make_tuple(3, 1, 0.1), - std::make_tuple(3, 1, 0.4), - std::make_tuple(3, 2, 0.1), - std::make_tuple(3, 2, 0.4) -)); +INSTANTIATE_TEST_SUITE_P( redecomp, SparseMatrixTest, + testing::Values( std::make_tuple( 2, 0, 0.1 ), std::make_tuple( 2, 0, 0.4 ), + std::make_tuple( 2, 1, 0.1 ), std::make_tuple( 2, 1, 0.4 ), + std::make_tuple( 2, 2, 0.1 ), std::make_tuple( 2, 2, 0.4 ), + std::make_tuple( 2, 3, 0.1 ), std::make_tuple( 2, 3, 0.4 ), + std::make_tuple( 3, 0, 0.1 ), std::make_tuple( 3, 0, 0.4 ), + std::make_tuple( 3, 1, 0.1 ), std::make_tuple( 3, 1, 0.4 ), + std::make_tuple( 3, 2, 0.1 ), std::make_tuple( 3, 2, 0.4 ) ) ); } // namespace redecomp //------------------------------------------------------------------------------ #include "axom/slic/core/SimpleLogger.hpp" -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - MPI_Init(&argc, &argv); + MPI_Init( &argc, &argv ); - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); axom::slic::SimpleLogger logger; // create & initialize test logger, finalized when // exiting main scope diff --git a/src/tests/redecomp_transfer.cpp b/src/tests/redecomp_transfer.cpp index eacb6c4e..5cc1155d 100644 --- a/src/tests/redecomp_transfer.cpp +++ b/src/tests/redecomp_transfer.cpp @@ -13,7 +13,7 @@ namespace redecomp { class TransferTest : public testing::TestWithParam> { -protected: + protected: mfem::ParMesh par_mesh_; std::unique_ptr h1_elems_; std::unique_ptr par_vector_space_; @@ -31,107 +31,99 @@ class TransferTest : public testing::TestWithParam> { auto mesh_file = GetParam().first; auto fe_order = GetParam().second; - std::string mesh_filename = std::string(TRIBOL_REPO_DIR) + mesh_file; - auto serial_mesh = mfem::Mesh(mesh_filename.c_str(), 1, 1, true); + std::string mesh_filename = std::string( TRIBOL_REPO_DIR ) + mesh_file; + auto serial_mesh = mfem::Mesh( mesh_filename.c_str(), 1, 1, true ); auto dim = serial_mesh.Dimension(); serial_mesh.UniformRefinement(); serial_mesh.UniformRefinement(); - par_mesh_ = mfem::ParMesh(MPI_COMM_WORLD, serial_mesh); + par_mesh_ = mfem::ParMesh( MPI_COMM_WORLD, serial_mesh ); // store nodal coordinate as a GridFunction - h1_elems_ = std::make_unique(fe_order, dim); - par_vector_space_ = - std::make_unique(&par_mesh_, h1_elems_.get(), dim); - orig_ = std::make_unique(par_vector_space_.get()); - final_ = std::make_unique(par_vector_space_.get()); - if (fe_order > 1) - { - par_mesh_.SetNodalGridFunction(orig_.get(), false); + h1_elems_ = std::make_unique( fe_order, dim ); + par_vector_space_ = std::make_unique( &par_mesh_, h1_elems_.get(), dim ); + orig_ = std::make_unique( par_vector_space_.get() ); + final_ = std::make_unique( par_vector_space_.get() ); + if ( fe_order > 1 ) { + par_mesh_.SetNodalGridFunction( orig_.get(), false ); + } else { + par_mesh_.GetNodes( *orig_ ); } - else - { - par_mesh_.GetNodes(*orig_); - } - redecomp_mesh_ = std::make_unique(par_mesh_); - redecomp_vector_space_ = - std::make_unique(redecomp_mesh_.get(), h1_elems_.get(), dim); - xfer_ = std::make_unique(redecomp_vector_space_.get()); + redecomp_mesh_ = std::make_unique( par_mesh_ ); + redecomp_vector_space_ = std::make_unique( redecomp_mesh_.get(), h1_elems_.get(), dim ); + xfer_ = std::make_unique( redecomp_vector_space_.get() ); // store global element number as a QuadratureFunction - par_quad_space_ = std::make_unique(&par_mesh_, 0); - orig_quad_fn_ = std::make_unique(par_quad_space_.get()); - for (int e{0}; e < par_mesh_.GetNE(); ++e) - { + par_quad_space_ = std::make_unique( &par_mesh_, 0 ); + orig_quad_fn_ = std::make_unique( par_quad_space_.get() ); + for ( int e{ 0 }; e < par_mesh_.GetNE(); ++e ) { auto quad_val = mfem::Vector(); - orig_quad_fn_->GetValues(e, quad_val); - for (int i{0}; i < quad_val.Size(); ++i) - { - quad_val[i] = static_cast(par_mesh_.GetGlobalElementNum(e)); + orig_quad_fn_->GetValues( e, quad_val ); + for ( int i{ 0 }; i < quad_val.Size(); ++i ) { + quad_val[i] = static_cast( par_mesh_.GetGlobalElementNum( e ) ); } } - redecomp_quad_space_ = std::make_unique(redecomp_mesh_.get(), 0); - xfer_quad_fn_ = std::make_unique(redecomp_quad_space_.get()); - final_quad_fn_ = std::make_unique(par_quad_space_.get()); + redecomp_quad_space_ = std::make_unique( redecomp_mesh_.get(), 0 ); + xfer_quad_fn_ = std::make_unique( redecomp_quad_space_.get() ); + final_quad_fn_ = std::make_unique( par_quad_space_.get() ); } template - double Calcl2Error(const T& orig, T& final) + double Calcl2Error( const T& orig, T& final ) { final -= orig; return final.Norml2() / orig.Norml2(); } }; -TEST_P(TransferTest, element_gridfn_transfer) +TEST_P( TransferTest, element_gridfn_transfer ) { // test grid function transfer auto transfer_map = RedecompTransfer(); - transfer_map.TransferToSerial(*orig_, *xfer_); - transfer_map.TransferToParallel(*xfer_, *final_); - EXPECT_LT(Calcl2Error(*orig_, *final_), 1.0e-13); + transfer_map.TransferToSerial( *orig_, *xfer_ ); + transfer_map.TransferToParallel( *xfer_, *final_ ); + EXPECT_LT( Calcl2Error( *orig_, *final_ ), 1.0e-13 ); - MPI_Barrier(MPI_COMM_WORLD); + MPI_Barrier( MPI_COMM_WORLD ); } -TEST_P(TransferTest, element_quadfn_transfer) +TEST_P( TransferTest, element_quadfn_transfer ) { // test quadrature function transfer auto transfer_map = RedecompTransfer(); - transfer_map.TransferToSerial(*orig_quad_fn_, *xfer_quad_fn_); - transfer_map.TransferToParallel(*xfer_quad_fn_, *final_quad_fn_); - EXPECT_LT(Calcl2Error(*orig_quad_fn_, *final_quad_fn_), 1.0e-13); + transfer_map.TransferToSerial( *orig_quad_fn_, *xfer_quad_fn_ ); + transfer_map.TransferToParallel( *xfer_quad_fn_, *final_quad_fn_ ); + EXPECT_LT( Calcl2Error( *orig_quad_fn_, *final_quad_fn_ ), 1.0e-13 ); - MPI_Barrier(MPI_COMM_WORLD); + MPI_Barrier( MPI_COMM_WORLD ); } -TEST_P(TransferTest, node_gridfn_transfer) +TEST_P( TransferTest, node_gridfn_transfer ) { - auto transfer_map = RedecompTransfer(*par_vector_space_, *redecomp_vector_space_); - transfer_map.TransferToSerial(*orig_, *xfer_); - transfer_map.TransferToParallel(*xfer_, *final_); - EXPECT_LT(Calcl2Error(*orig_, *final_), 1.0e-13); + auto transfer_map = RedecompTransfer( *par_vector_space_, *redecomp_vector_space_ ); + transfer_map.TransferToSerial( *orig_, *xfer_ ); + transfer_map.TransferToParallel( *xfer_, *final_ ); + EXPECT_LT( Calcl2Error( *orig_, *final_ ), 1.0e-13 ); - MPI_Barrier(MPI_COMM_WORLD); + MPI_Barrier( MPI_COMM_WORLD ); } -INSTANTIATE_TEST_SUITE_P(redecomp, TransferTest, testing::Values( - std::make_pair("/data/star.mesh", 1), - std::make_pair("/data/star.mesh", 3), - std::make_pair("/data/two_hex.mesh", 1), - std::make_pair("/data/two_hex.mesh", 3) -)); +INSTANTIATE_TEST_SUITE_P( redecomp, TransferTest, + testing::Values( std::make_pair( "/data/star.mesh", 1 ), + std::make_pair( "/data/star.mesh", 3 ), + std::make_pair( "/data/two_hex.mesh", 1 ), + std::make_pair( "/data/two_hex.mesh", 3 ) ) ); } // namespace redecomp //------------------------------------------------------------------------------ #include "axom/slic/core/SimpleLogger.hpp" -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - MPI_Init(&argc, &argv); + MPI_Init( &argc, &argv ); - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); axom::slic::SimpleLogger logger; // create & initialize test logger, finalized when // exiting main scope diff --git a/src/tests/tribol_check_tpl.cpp b/src/tests/tribol_check_tpl.cpp index 865613d0..3dec4c6e 100644 --- a/src/tests/tribol_check_tpl.cpp +++ b/src/tests/tribol_check_tpl.cpp @@ -28,27 +28,22 @@ TEST( tribol_check_tpl, check_axom ) EXPECT_TRUE( !axom_version.empty() ); std::ostringstream oss; - oss << "v" << AXOM_VERSION_MAJOR << "." - << AXOM_VERSION_MINOR << "." - << AXOM_VERSION_PATCH; + oss << "v" << AXOM_VERSION_MAJOR << "." << AXOM_VERSION_MINOR << "." << AXOM_VERSION_PATCH; EXPECT_EQ( axom_version, oss.str() ); } -TEST( tribol_check_tpl, print_axom_about ) -{ - axom::about(); -} +TEST( tribol_check_tpl, print_axom_about ) { axom::about(); } //------------------------------------------------------------------------------ -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); - axom::slic::SimpleLogger logger; + axom::slic::SimpleLogger logger; result = RUN_ALL_TESTS(); diff --git a/src/tests/tribol_common_plane_gap_rate.cpp b/src/tests/tribol_common_plane_gap_rate.cpp index 3c84de6a..5e588c7f 100644 --- a/src/tests/tribol_common_plane_gap_rate.cpp +++ b/src/tests/tribol_common_plane_gap_rate.cpp @@ -27,7 +27,7 @@ #include "gtest/gtest.h" // c++ includes -#include // std::abs, std::cos, std::sin +#include // std::abs, std::cos, std::sin #include #include #include @@ -35,778 +35,674 @@ using RealT = tribol::RealT; -void compareGaps( tribol::CouplingScheme const * cs, - RealT gap, const RealT tol, - const char *gapType ) +void compareGaps( tribol::CouplingScheme const* cs, RealT gap, const RealT tol, const char* gapType ) { - tribol::IndexT const numPairs = cs->getNumActivePairs(); - const auto cs_view = const_cast(cs)->getView(); - - for (tribol::IndexT cpID = 0; cpID < numPairs; ++cpID) - { - auto& plane = cs->getContactPlane(cpID); - - RealT my_gap = 0.; - if ( std::strcmp( gapType, "kinematic_penetration" ) == 0 || - std::strcmp( gapType, "kinematic_separation" ) == 0 ) - { - my_gap = plane.m_gap; - } - else - { - my_gap = plane.m_velGap; - } - - // gap tolerance per common-plane based calculation (see CouplingScheme::ViewerBase::getCommonPlaneGapTol) - RealT gap_tol = cs_view.getGapTol( plane.getCpElementId1(), plane.getCpElementId2() ); - - // check gap sense. - if ( std::strcmp( gapType, "kinematic_penetration" ) == 0 || - std::strcmp( gapType, "rate_penetration" ) == 0 ) - { - // check that the gap is less than the separation tolerance (interpenetration) - EXPECT_LE( my_gap, gap_tol ); - } - else if ( std::strcmp( gapType, "kinematic_separation" ) == 0 || - std::strcmp( gapType, "rate_separation" ) == 0 ) - { - // check that the gap is greater than the separation tolerance (separation) - EXPECT_GE( my_gap, gap_tol ); - } - else - { - SLIC_ERROR("compareGaps: invalid gapType. " << - "Acceptable types are 'kinematic_penetration', 'kinematic_separation', " << - "'rate_penetration' or 'rate_separation'." ) ;; - } - - // check diffs - RealT diff = std::abs( my_gap - gap ); - EXPECT_LE( diff, tol ); - } -} // end compareGaps() - -void checkMeshPenalties( tribol::CouplingScheme const * cs, - const RealT penalty, const RealT tol, - const char * penaltyType ) + tribol::IndexT const numPairs = cs->getNumActivePairs(); + const auto cs_view = const_cast( cs )->getView(); + + for ( tribol::IndexT cpID = 0; cpID < numPairs; ++cpID ) { + auto& plane = cs->getContactPlane( cpID ); + + RealT my_gap = 0.; + if ( std::strcmp( gapType, "kinematic_penetration" ) == 0 || std::strcmp( gapType, "kinematic_separation" ) == 0 ) { + my_gap = plane.m_gap; + } else { + my_gap = plane.m_velGap; + } + + // gap tolerance per common-plane based calculation (see CouplingScheme::ViewerBase::getCommonPlaneGapTol) + RealT gap_tol = cs_view.getGapTol( plane.getCpElementId1(), plane.getCpElementId2() ); + + // check gap sense. + if ( std::strcmp( gapType, "kinematic_penetration" ) == 0 || std::strcmp( gapType, "rate_penetration" ) == 0 ) { + // check that the gap is less than the separation tolerance (interpenetration) + EXPECT_LE( my_gap, gap_tol ); + } else if ( std::strcmp( gapType, "kinematic_separation" ) == 0 || + std::strcmp( gapType, "rate_separation" ) == 0 ) { + // check that the gap is greater than the separation tolerance (separation) + EXPECT_GE( my_gap, gap_tol ); + } else { + SLIC_ERROR( "compareGaps: invalid gapType. " + << "Acceptable types are 'kinematic_penetration', 'kinematic_separation', " + << "'rate_penetration' or 'rate_separation'." ); + ; + } + + // check diffs + RealT diff = std::abs( my_gap - gap ); + EXPECT_LE( diff, tol ); + } +} // end compareGaps() + +void checkMeshPenalties( tribol::CouplingScheme const* cs, const RealT penalty, const RealT tol, + const char* penaltyType ) { - const tribol::IndexT mesh_id1 = cs->getMeshId1(); - const tribol::IndexT mesh_id2 = cs->getMeshId2(); - - tribol::MeshManager& meshManager = tribol::MeshManager::getInstance(); - tribol::MeshData& mesh1 = meshManager.at( mesh_id1 ); - tribol::MeshData& mesh2 = meshManager.at( mesh_id2 ); - - if ( std::strcmp( penaltyType, "constant" ) == 0 ) - { - RealT penalty_diff_1 = std::abs( mesh1.getElementData().m_penalty_stiffness - penalty ); - RealT penalty_diff_2 = std::abs( mesh2.getElementData().m_penalty_stiffness - penalty ); - EXPECT_LE( penalty_diff_1, tol ); - EXPECT_LE( penalty_diff_2, tol ); - } - else if ( std::strcmp( penaltyType, "face" ) == 0 ) - { - // no-op, the face-based penalty is checked in a call to tribol::update() - } - else if ( std::strcmp( penaltyType, "constant_rate" ) == 0 ) - { - RealT penalty_diff_1 = std::abs( mesh1.getElementData().m_rate_penalty_stiffness - penalty ); - RealT penalty_diff_2 = std::abs( mesh2.getElementData().m_rate_penalty_stiffness - penalty ); - EXPECT_LE( penalty_diff_1, tol ); - EXPECT_LE( penalty_diff_2, tol ); - } - else if ( std::strcmp( penaltyType, "percent_rate" ) == 0 ) - { - RealT penalty1 = mesh1.getElementData().m_rate_percent_stiffness * mesh1.getElementData().m_penalty_stiffness; - RealT penalty2 = mesh2.getElementData().m_rate_percent_stiffness * mesh2.getElementData().m_penalty_stiffness; - RealT penalty_diff_1 = std::abs( penalty1 - penalty ); - RealT penalty_diff_2 = std::abs( penalty2 - penalty ); - EXPECT_LE( penalty_diff_1, tol ); - EXPECT_LE( penalty_diff_2, tol ); - } - else - { - SLIC_ERROR("checkMeshPenalties: invalid penaltyType. " << - "only 'constant', 'face', 'constant_rate', or 'percent_rate' accepted. " ); - } - -} // end checkMeshPenalties() - -void checkPressures( tribol::CouplingScheme const * cs, - RealT pressure, const RealT tol, const char * pressureType = "kinematic" ) + const tribol::IndexT mesh_id1 = cs->getMeshId1(); + const tribol::IndexT mesh_id2 = cs->getMeshId2(); + + tribol::MeshManager& meshManager = tribol::MeshManager::getInstance(); + tribol::MeshData& mesh1 = meshManager.at( mesh_id1 ); + tribol::MeshData& mesh2 = meshManager.at( mesh_id2 ); + + if ( std::strcmp( penaltyType, "constant" ) == 0 ) { + RealT penalty_diff_1 = std::abs( mesh1.getElementData().m_penalty_stiffness - penalty ); + RealT penalty_diff_2 = std::abs( mesh2.getElementData().m_penalty_stiffness - penalty ); + EXPECT_LE( penalty_diff_1, tol ); + EXPECT_LE( penalty_diff_2, tol ); + } else if ( std::strcmp( penaltyType, "face" ) == 0 ) { + // no-op, the face-based penalty is checked in a call to tribol::update() + } else if ( std::strcmp( penaltyType, "constant_rate" ) == 0 ) { + RealT penalty_diff_1 = std::abs( mesh1.getElementData().m_rate_penalty_stiffness - penalty ); + RealT penalty_diff_2 = std::abs( mesh2.getElementData().m_rate_penalty_stiffness - penalty ); + EXPECT_LE( penalty_diff_1, tol ); + EXPECT_LE( penalty_diff_2, tol ); + } else if ( std::strcmp( penaltyType, "percent_rate" ) == 0 ) { + RealT penalty1 = mesh1.getElementData().m_rate_percent_stiffness * mesh1.getElementData().m_penalty_stiffness; + RealT penalty2 = mesh2.getElementData().m_rate_percent_stiffness * mesh2.getElementData().m_penalty_stiffness; + RealT penalty_diff_1 = std::abs( penalty1 - penalty ); + RealT penalty_diff_2 = std::abs( penalty2 - penalty ); + EXPECT_LE( penalty_diff_1, tol ); + EXPECT_LE( penalty_diff_2, tol ); + } else { + SLIC_ERROR( "checkMeshPenalties: invalid penaltyType. " + << "only 'constant', 'face', 'constant_rate', or 'percent_rate' accepted. " ); + } + +} // end checkMeshPenalties() + +void checkPressures( tribol::CouplingScheme const* cs, RealT pressure, const RealT tol, + const char* pressureType = "kinematic" ) { - tribol::IndexT const numPairs = cs->getNumActivePairs(); - - for (tribol::IndexT cpID = 0; cpID < numPairs; ++cpID) - { - auto& plane = cs->getContactPlane(cpID); - - RealT my_pressure = 0.; - if ( std::strcmp( pressureType, "rate" ) == 0 ) - { - my_pressure = plane.m_ratePressure; - } - else if ( std::strcmp( pressureType, "kinematic" ) == 0 ) - { - my_pressure = plane.m_pressure; - } - else - { - SLIC_ERROR( "checkPressures(): invalid pressure type. Supported types are " << - "'kinematic' or 'rate'." ); - } - - // check diffs - RealT press_diff = std::abs( my_pressure - pressure ); - EXPECT_LE( press_diff, tol ); - } -} // end checkPressures() - -// problem specific routine to check the sense of the force. Note, this -// routine makes implicit use of the knowledge that the outward facing -// surface unit normals are in the +/- z-direction for mesh 1 and -// mesh 2, respectively. This is not a general routine for general + tribol::IndexT const numPairs = cs->getNumActivePairs(); + + for ( tribol::IndexT cpID = 0; cpID < numPairs; ++cpID ) { + auto& plane = cs->getContactPlane( cpID ); + + RealT my_pressure = 0.; + if ( std::strcmp( pressureType, "rate" ) == 0 ) { + my_pressure = plane.m_ratePressure; + } else if ( std::strcmp( pressureType, "kinematic" ) == 0 ) { + my_pressure = plane.m_pressure; + } else { + SLIC_ERROR( "checkPressures(): invalid pressure type. Supported types are " + << "'kinematic' or 'rate'." ); + } + + // check diffs + RealT press_diff = std::abs( my_pressure - pressure ); + EXPECT_LE( press_diff, tol ); + } +} // end checkPressures() + +// problem specific routine to check the sense of the force. Note, this +// routine makes implicit use of the knowledge that the outward facing +// surface unit normals are in the +/- z-direction for mesh 1 and +// mesh 2, respectively. This is not a general routine for general // mesh configurations. -void checkForceSense( tribol::CouplingScheme const * cs, bool isTied = false ) +void checkForceSense( tribol::CouplingScheme const* cs, bool isTied = false ) { - // TODO: get rid of const cast - const auto mesh1 = const_cast(cs)->getMesh1().getView(); - const auto mesh2 = const_cast(cs)->getMesh2().getView(); - - for (int i=0; i<2; ++i) // loop over meshes - { - auto& mesh = (i==0) ? mesh1 : mesh2; - - // loop over faces and nodes - for (tribol::IndexT kf = 0; kf < mesh.numberOfElements(); ++kf) - { - for (tribol::IndexT a = 0; a( cs )->getMesh1().getView(); + const auto mesh2 = const_cast( cs )->getMesh2().getView(); + + for ( int i = 0; i < 2; ++i ) // loop over meshes + { + auto& mesh = ( i == 0 ) ? mesh1 : mesh2; + + // loop over faces and nodes + for ( tribol::IndexT kf = 0; kf < mesh.numberOfElements(); ++kf ) { + for ( tribol::IndexT a = 0; a < mesh.numberOfNodesPerElement(); ++a ) { + int node_id = mesh.getGlobalNodeId( kf, a ); + RealT force_mag = tribol::dotProd( mesh.getResponse()[0][node_id], mesh.getResponse()[1][node_id], + mesh.getResponse()[2][node_id], mesh.getElementNormals()[0][kf], + mesh.getElementNormals()[1][kf], mesh.getElementNormals()[2][kf] ); + if ( !isTied ) { + // <= catches interpenetration AND separation + EXPECT_LE( force_mag, 0. ); + } else { + // no-op, TIED_NORMAL is a special case where we + // support all force 'sense' (i.e. tension AND compression) + } } - } -} // end checkForceSense() + } + } +} // end checkForceSense() /*! - * Test fixture class with some setup necessary to test - * the COMMON_PLANE + GAP RATE PENALTY implementation + * Test fixture class with some setup necessary to test + * the COMMON_PLANE + GAP RATE PENALTY implementation */ -class CommonPlaneTest : public ::testing::Test -{ - -public: - - tribol::TestMesh m_mesh; - -protected: - - void SetUp() override - { - } +class CommonPlaneTest : public ::testing::Test { + public: + tribol::TestMesh m_mesh; - void TearDown() override - { - this->m_mesh.clear(); - } + protected: + void SetUp() override {} -protected: + void TearDown() override { this->m_mesh.clear(); } + protected: }; TEST_F( CommonPlaneTest, constant_rate_penetration ) { - this->m_mesh.mortarMeshId = 0; - this->m_mesh.nonmortarMeshId = 1; - - int nMortarElems = 1; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 1; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - // mesh bounding box with 0.1 interpenetration gap - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.05; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 0.95; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0. ); - - // set velocities - RealT dt = 1.e-3; - RealT velX1 = 0.; - RealT velY1 = 0.; - RealT velZ1 = 1.; - RealT velX2 = 0.; - RealT velY2 = 0.; - RealT velZ2 = -1.; - this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); - this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, velZ2 ); - - // set kinematic gap and rate (velocity) gap penalty parameters - tribol::TestControlParameters parameters; - parameters.penalty_ratio = false; - parameters.const_penalty = 0.75; - parameters.percent_rate_penalty = false; // default - parameters.rate_penalty_ratio = 0.0; // default - parameters.constant_rate_penalty = true; - parameters.rate_penalty = 1.0; - parameters.dt = dt; - - int test_mesh_update_err = - this->m_mesh.tribolSetupAndUpdate( tribol::COMMON_PLANE, tribol::PENALTY, - tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); - - EXPECT_EQ( test_mesh_update_err, 0 ); - - tribol::CouplingSchemeManager& couplingSchemeManager = - tribol::CouplingSchemeManager::getInstance(); - - tribol::CouplingScheme* couplingScheme = - &couplingSchemeManager.at( 0 ); - - // check mesh rate penalties - RealT penalty = 0.; - if (parameters.constant_rate_penalty) - { - penalty = parameters.rate_penalty; - checkMeshPenalties( couplingScheme, penalty, 1.E-8, "constant_rate" ); - } - else if (parameters.percent_rate_penalty) - { - penalty = parameters.rate_penalty_ratio * parameters.const_penalty; - checkMeshPenalties( couplingScheme, penalty, 1.E-8, "percent_rate" ); - } - - // check the gaps, pressures, and force sense - //RealT gap = z_min2 - z_max1; - RealT rate_gap = velZ2 - velZ1; - RealT pressure = (rate_gap < 0.) ? penalty * rate_gap : 0.; - compareGaps( couplingScheme, rate_gap, 1.E-8, "rate_penetration" ); - checkPressures( couplingScheme, pressure, 1.E-8, "rate" ); - checkForceSense( couplingScheme ); // note: the kinematic and rate contributions are not separated - - tribol::finalize(); - -} // end test 'rate_penetration' + this->m_mesh.mortarMeshId = 0; + this->m_mesh.nonmortarMeshId = 1; + + int nMortarElems = 1; + int nElemsXM = nMortarElems; + int nElemsYM = nMortarElems; + int nElemsZM = nMortarElems; + + int nNonmortarElems = 1; + int nElemsXS = nNonmortarElems; + int nElemsYS = nNonmortarElems; + int nElemsZS = nNonmortarElems; + + // mesh bounding box with 0.1 interpenetration gap + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.05; + + RealT x_min2 = 0.; + RealT y_min2 = 0.; + RealT z_min2 = 0.95; + RealT x_max2 = 1.; + RealT y_max2 = 1.; + RealT z_max2 = 2.; + + this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., + 0. ); + + // set velocities + RealT dt = 1.e-3; + RealT velX1 = 0.; + RealT velY1 = 0.; + RealT velZ1 = 1.; + RealT velX2 = 0.; + RealT velY2 = 0.; + RealT velZ2 = -1.; + this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); + this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, velZ2 ); + + // set kinematic gap and rate (velocity) gap penalty parameters + tribol::TestControlParameters parameters; + parameters.penalty_ratio = false; + parameters.const_penalty = 0.75; + parameters.percent_rate_penalty = false; // default + parameters.rate_penalty_ratio = 0.0; // default + parameters.constant_rate_penalty = true; + parameters.rate_penalty = 1.0; + parameters.dt = dt; + + int test_mesh_update_err = this->m_mesh.tribolSetupAndUpdate( + tribol::COMMON_PLANE, tribol::PENALTY, tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); + + EXPECT_EQ( test_mesh_update_err, 0 ); + + tribol::CouplingSchemeManager& couplingSchemeManager = tribol::CouplingSchemeManager::getInstance(); + + tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); + + // check mesh rate penalties + RealT penalty = 0.; + if ( parameters.constant_rate_penalty ) { + penalty = parameters.rate_penalty; + checkMeshPenalties( couplingScheme, penalty, 1.E-8, "constant_rate" ); + } else if ( parameters.percent_rate_penalty ) { + penalty = parameters.rate_penalty_ratio * parameters.const_penalty; + checkMeshPenalties( couplingScheme, penalty, 1.E-8, "percent_rate" ); + } + + // check the gaps, pressures, and force sense + // RealT gap = z_min2 - z_max1; + RealT rate_gap = velZ2 - velZ1; + RealT pressure = ( rate_gap < 0. ) ? penalty * rate_gap : 0.; + compareGaps( couplingScheme, rate_gap, 1.E-8, "rate_penetration" ); + checkPressures( couplingScheme, pressure, 1.E-8, "rate" ); + checkForceSense( couplingScheme ); // note: the kinematic and rate contributions are not separated + + tribol::finalize(); + +} // end test 'rate_penetration' TEST_F( CommonPlaneTest, constant_rate_separation ) { - this->m_mesh.mortarMeshId = 0; - this->m_mesh.nonmortarMeshId = 1; - - int nMortarElems = 1; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 1; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - // mesh bounding box with 0.1 interpenetration kinematic gap - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.05; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 0.95; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0. ); - - // set velocities such that the blocks have equal and opposite 'separation' velocities - // Note: this should not trigger a gap-rate contribution. - RealT dt = 1.e-3; - RealT velX1 = 0.; - RealT velY1 = 0.; - RealT velZ1 = -1.; - RealT velX2 = 0.; - RealT velY2 = 0.; - RealT velZ2 = 1.; - this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); - this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, velZ2 ); - - // set kinematic gap and rate (velocity) gap penalty parameters - tribol::TestControlParameters parameters; - parameters.penalty_ratio = false; - parameters.const_penalty = 0.75; - parameters.percent_rate_penalty = false; // default - parameters.rate_penalty_ratio = 0.0; // default - parameters.constant_rate_penalty = true; - parameters.rate_penalty = 1.0; - parameters.dt = dt; - - int test_mesh_update_err = - this->m_mesh.tribolSetupAndUpdate( tribol::COMMON_PLANE, tribol::PENALTY, - tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); - - EXPECT_EQ( test_mesh_update_err, 0 ); - - tribol::CouplingSchemeManager& couplingSchemeManager = - tribol::CouplingSchemeManager::getInstance(); - - tribol::CouplingScheme* couplingScheme = - &couplingSchemeManager.at( 0 ); - - // check mesh rate penalties - RealT penalty = 0.; - if (parameters.constant_rate_penalty) - { - penalty = parameters.rate_penalty; - checkMeshPenalties( couplingScheme, penalty, 1.E-8, "constant_rate" ); - } - else if (parameters.percent_rate_penalty) - { - penalty = parameters.rate_penalty_ratio * parameters.const_penalty; - checkMeshPenalties( couplingScheme, penalty, 1.E-8, "percent_rate" ); - } - - // check the gaps, pressures and force sense - //RealT gap = z_min2 - z_max1; - RealT rate_gap = velZ2 - velZ1; - RealT pressure = (rate_gap < 0.) ? penalty * rate_gap : 0.; - compareGaps( couplingScheme, rate_gap, 1.E-8, "rate_separation" ); - checkPressures( couplingScheme, pressure, 1.E-8, "rate" ); - checkForceSense( couplingScheme ); // note: the kinematic and rate contributions aren't separated - - tribol::finalize(); - -} // end test 'rate_separation' + this->m_mesh.mortarMeshId = 0; + this->m_mesh.nonmortarMeshId = 1; + + int nMortarElems = 1; + int nElemsXM = nMortarElems; + int nElemsYM = nMortarElems; + int nElemsZM = nMortarElems; + + int nNonmortarElems = 1; + int nElemsXS = nNonmortarElems; + int nElemsYS = nNonmortarElems; + int nElemsZS = nNonmortarElems; + + // mesh bounding box with 0.1 interpenetration kinematic gap + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.05; + + RealT x_min2 = 0.; + RealT y_min2 = 0.; + RealT z_min2 = 0.95; + RealT x_max2 = 1.; + RealT y_max2 = 1.; + RealT z_max2 = 2.; + + this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., + 0. ); + + // set velocities such that the blocks have equal and opposite 'separation' velocities + // Note: this should not trigger a gap-rate contribution. + RealT dt = 1.e-3; + RealT velX1 = 0.; + RealT velY1 = 0.; + RealT velZ1 = -1.; + RealT velX2 = 0.; + RealT velY2 = 0.; + RealT velZ2 = 1.; + this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); + this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, velZ2 ); + + // set kinematic gap and rate (velocity) gap penalty parameters + tribol::TestControlParameters parameters; + parameters.penalty_ratio = false; + parameters.const_penalty = 0.75; + parameters.percent_rate_penalty = false; // default + parameters.rate_penalty_ratio = 0.0; // default + parameters.constant_rate_penalty = true; + parameters.rate_penalty = 1.0; + parameters.dt = dt; + + int test_mesh_update_err = this->m_mesh.tribolSetupAndUpdate( + tribol::COMMON_PLANE, tribol::PENALTY, tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); + + EXPECT_EQ( test_mesh_update_err, 0 ); + + tribol::CouplingSchemeManager& couplingSchemeManager = tribol::CouplingSchemeManager::getInstance(); + + tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); + + // check mesh rate penalties + RealT penalty = 0.; + if ( parameters.constant_rate_penalty ) { + penalty = parameters.rate_penalty; + checkMeshPenalties( couplingScheme, penalty, 1.E-8, "constant_rate" ); + } else if ( parameters.percent_rate_penalty ) { + penalty = parameters.rate_penalty_ratio * parameters.const_penalty; + checkMeshPenalties( couplingScheme, penalty, 1.E-8, "percent_rate" ); + } + + // check the gaps, pressures and force sense + // RealT gap = z_min2 - z_max1; + RealT rate_gap = velZ2 - velZ1; + RealT pressure = ( rate_gap < 0. ) ? penalty * rate_gap : 0.; + compareGaps( couplingScheme, rate_gap, 1.E-8, "rate_separation" ); + checkPressures( couplingScheme, pressure, 1.E-8, "rate" ); + checkForceSense( couplingScheme ); // note: the kinematic and rate contributions aren't separated + + tribol::finalize(); + +} // end test 'rate_separation' TEST_F( CommonPlaneTest, no_gap_constant_rate_penetration ) { - this->m_mesh.mortarMeshId = 0; - this->m_mesh.nonmortarMeshId = 1; - - int nMortarElems = 1; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 1; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - // mesh bounding box with zero gap. - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 1.; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0. ); - - // set velocities such that further interpenetration would occur - RealT dt = 1.e-3; - RealT velX1 = 0.; - RealT velY1 = 0.; - RealT velZ1 = 1.; - RealT velX2 = 0.; - RealT velY2 = 0.; - RealT velZ2 = -1.; - this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); - this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, velZ2 ); - - // set kinematic gap and rate (velocity) gap penalty parameters - tribol::TestControlParameters parameters; - parameters.penalty_ratio = false; - parameters.const_penalty = 0.75; - parameters.percent_rate_penalty = false; // default - parameters.rate_penalty_ratio = 0.0; // default - parameters.constant_rate_penalty = true; - parameters.rate_penalty = 1.0; - parameters.dt = dt; - - int test_mesh_update_err = - this->m_mesh.tribolSetupAndUpdate( tribol::COMMON_PLANE, tribol::PENALTY, - tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); - - EXPECT_EQ( test_mesh_update_err, 0 ); - - tribol::CouplingSchemeManager& couplingSchemeManager = - tribol::CouplingSchemeManager::getInstance(); - - tribol::CouplingScheme* couplingScheme = - &couplingSchemeManager.at( 0 ); - - // check mesh rate penalties - RealT penalty = 0.; - if (parameters.constant_rate_penalty) - { - penalty = parameters.rate_penalty; - checkMeshPenalties( couplingScheme, penalty, 1.E-8, "constant_rate" ); - } - else if (parameters.percent_rate_penalty) - { - penalty = parameters.rate_penalty_ratio * parameters.const_penalty; - checkMeshPenalties( couplingScheme, penalty, 1.E-8, "percent_rate" ); - } - - // check to make sure zero rate pressure - RealT gap = z_min2 - z_max1; - RealT rate_gap = velZ2 - velZ1; - RealT pressure = (gap < 0. && rate_gap < 0.) ? penalty * rate_gap : 0.; - checkPressures( couplingScheme, pressure, 1.E-8, "rate" ); - - tribol::finalize(); + this->m_mesh.mortarMeshId = 0; + this->m_mesh.nonmortarMeshId = 1; + + int nMortarElems = 1; + int nElemsXM = nMortarElems; + int nElemsYM = nMortarElems; + int nElemsZM = nMortarElems; + + int nNonmortarElems = 1; + int nElemsXS = nNonmortarElems; + int nElemsYS = nNonmortarElems; + int nElemsZS = nNonmortarElems; + + // mesh bounding box with zero gap. + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.; + + RealT x_min2 = 0.; + RealT y_min2 = 0.; + RealT z_min2 = 1.; + RealT x_max2 = 1.; + RealT y_max2 = 1.; + RealT z_max2 = 2.; + + this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., + 0. ); + + // set velocities such that further interpenetration would occur + RealT dt = 1.e-3; + RealT velX1 = 0.; + RealT velY1 = 0.; + RealT velZ1 = 1.; + RealT velX2 = 0.; + RealT velY2 = 0.; + RealT velZ2 = -1.; + this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); + this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, velZ2 ); + + // set kinematic gap and rate (velocity) gap penalty parameters + tribol::TestControlParameters parameters; + parameters.penalty_ratio = false; + parameters.const_penalty = 0.75; + parameters.percent_rate_penalty = false; // default + parameters.rate_penalty_ratio = 0.0; // default + parameters.constant_rate_penalty = true; + parameters.rate_penalty = 1.0; + parameters.dt = dt; + + int test_mesh_update_err = this->m_mesh.tribolSetupAndUpdate( + tribol::COMMON_PLANE, tribol::PENALTY, tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); + + EXPECT_EQ( test_mesh_update_err, 0 ); + + tribol::CouplingSchemeManager& couplingSchemeManager = tribol::CouplingSchemeManager::getInstance(); + + tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); + + // check mesh rate penalties + RealT penalty = 0.; + if ( parameters.constant_rate_penalty ) { + penalty = parameters.rate_penalty; + checkMeshPenalties( couplingScheme, penalty, 1.E-8, "constant_rate" ); + } else if ( parameters.percent_rate_penalty ) { + penalty = parameters.rate_penalty_ratio * parameters.const_penalty; + checkMeshPenalties( couplingScheme, penalty, 1.E-8, "percent_rate" ); + } + + // check to make sure zero rate pressure + RealT gap = z_min2 - z_max1; + RealT rate_gap = velZ2 - velZ1; + RealT pressure = ( gap < 0. && rate_gap < 0. ) ? penalty * rate_gap : 0.; + checkPressures( couplingScheme, pressure, 1.E-8, "rate" ); + + tribol::finalize(); } TEST_F( CommonPlaneTest, percent_rate_penetration ) { - this->m_mesh.mortarMeshId = 0; - this->m_mesh.nonmortarMeshId = 1; - - int nMortarElems = 1; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 1; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - // mesh bounding box with 0.1 interpenetration gap - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.05; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 0.95; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0. ); - - // set velocities such that further penetration would occur - RealT dt = 1.e-3; - RealT velX1 = 0.; - RealT velY1 = 0.; - RealT velZ1 = 1.; - RealT velX2 = 0.; - RealT velY2 = 0.; - RealT velZ2 = -1.; - this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); - this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, velZ2 ); - - // set kinematic gap and rate (velocity) gap penalty parameters - tribol::TestControlParameters parameters; - parameters.penalty_ratio = false; - parameters.const_penalty = 0.75; - parameters.percent_rate_penalty = true; - parameters.rate_penalty_ratio = 0.20; - parameters.constant_rate_penalty = false; - parameters.rate_penalty = 1.0; - parameters.dt = dt; - - int test_mesh_update_err = - this->m_mesh.tribolSetupAndUpdate( tribol::COMMON_PLANE, tribol::PENALTY, - tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); - - EXPECT_EQ( test_mesh_update_err, 0 ); - - tribol::CouplingSchemeManager& couplingSchemeManager = - tribol::CouplingSchemeManager::getInstance(); - - tribol::CouplingScheme* couplingScheme = - &couplingSchemeManager.at( 0 ); - - // check mesh rate penalties - RealT penalty = 0.; - if (parameters.constant_rate_penalty) - { - penalty = parameters.rate_penalty; - checkMeshPenalties( couplingScheme, penalty, 1.E-8, "constant_rate" ); - } - else if (parameters.percent_rate_penalty) - { - penalty = parameters.rate_penalty_ratio * parameters.const_penalty; - checkMeshPenalties( couplingScheme, penalty, 1.E-8, "percent_rate" ); - } - - // check the gaps, pressures, and force sense - //RealT gap = z_min2 - z_max1; - RealT rate_gap = velZ2 - velZ1; - RealT stiffness = tribol::ComputePenaltyStiffnessPerArea( penalty, penalty ); - RealT pressure = (rate_gap < 0.) ? stiffness * rate_gap : 0.; - compareGaps( couplingScheme, rate_gap, 1.E-8, "rate_penetration" ); - checkPressures( couplingScheme, pressure, 1.E-8, "rate" ); - checkForceSense( couplingScheme ); // note: the kinematic and rate contributions are not separated - - tribol::finalize(); - -} // end test 'rate_penetration' + this->m_mesh.mortarMeshId = 0; + this->m_mesh.nonmortarMeshId = 1; + + int nMortarElems = 1; + int nElemsXM = nMortarElems; + int nElemsYM = nMortarElems; + int nElemsZM = nMortarElems; + + int nNonmortarElems = 1; + int nElemsXS = nNonmortarElems; + int nElemsYS = nNonmortarElems; + int nElemsZS = nNonmortarElems; + + // mesh bounding box with 0.1 interpenetration gap + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.05; + + RealT x_min2 = 0.; + RealT y_min2 = 0.; + RealT z_min2 = 0.95; + RealT x_max2 = 1.; + RealT y_max2 = 1.; + RealT z_max2 = 2.; + + this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., + 0. ); + + // set velocities such that further penetration would occur + RealT dt = 1.e-3; + RealT velX1 = 0.; + RealT velY1 = 0.; + RealT velZ1 = 1.; + RealT velX2 = 0.; + RealT velY2 = 0.; + RealT velZ2 = -1.; + this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); + this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, velZ2 ); + + // set kinematic gap and rate (velocity) gap penalty parameters + tribol::TestControlParameters parameters; + parameters.penalty_ratio = false; + parameters.const_penalty = 0.75; + parameters.percent_rate_penalty = true; + parameters.rate_penalty_ratio = 0.20; + parameters.constant_rate_penalty = false; + parameters.rate_penalty = 1.0; + parameters.dt = dt; + + int test_mesh_update_err = this->m_mesh.tribolSetupAndUpdate( + tribol::COMMON_PLANE, tribol::PENALTY, tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); + + EXPECT_EQ( test_mesh_update_err, 0 ); + + tribol::CouplingSchemeManager& couplingSchemeManager = tribol::CouplingSchemeManager::getInstance(); + + tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); + + // check mesh rate penalties + RealT penalty = 0.; + if ( parameters.constant_rate_penalty ) { + penalty = parameters.rate_penalty; + checkMeshPenalties( couplingScheme, penalty, 1.E-8, "constant_rate" ); + } else if ( parameters.percent_rate_penalty ) { + penalty = parameters.rate_penalty_ratio * parameters.const_penalty; + checkMeshPenalties( couplingScheme, penalty, 1.E-8, "percent_rate" ); + } + + // check the gaps, pressures, and force sense + // RealT gap = z_min2 - z_max1; + RealT rate_gap = velZ2 - velZ1; + RealT stiffness = tribol::ComputePenaltyStiffnessPerArea( penalty, penalty ); + RealT pressure = ( rate_gap < 0. ) ? stiffness * rate_gap : 0.; + compareGaps( couplingScheme, rate_gap, 1.E-8, "rate_penetration" ); + checkPressures( couplingScheme, pressure, 1.E-8, "rate" ); + checkForceSense( couplingScheme ); // note: the kinematic and rate contributions are not separated + + tribol::finalize(); + +} // end test 'rate_penetration' TEST_F( CommonPlaneTest, percent_rate_separation ) { - this->m_mesh.mortarMeshId = 0; - this->m_mesh.nonmortarMeshId = 1; - - int nMortarElems = 1; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 1; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - // mesh bounding box with 0.1 interpenetration gap - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.05; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 0.95; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0. ); - - // set velocities such that equal and opposite 'separation' velocities would - // lead to eventual separation of the blocks. This should not trigger a gap-rate - // contribution. - RealT dt = 1.e-3; - RealT velX1 = 0.; - RealT velY1 = 0.; - RealT velZ1 = -1.; - RealT velX2 = 0.; - RealT velY2 = 0.; - RealT velZ2 = 1.; - this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); - this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, velZ2 ); - - // set kinematic gap and rate (velocity) gap penalty parameters - tribol::TestControlParameters parameters; - parameters.penalty_ratio = false; - parameters.const_penalty = 0.75; - parameters.percent_rate_penalty = true; - parameters.rate_penalty_ratio = 0.20; - parameters.constant_rate_penalty = false; - parameters.rate_penalty = 1.0; - parameters.dt = dt; - - int test_mesh_update_err = - this->m_mesh.tribolSetupAndUpdate( tribol::COMMON_PLANE, tribol::PENALTY, - tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); - - EXPECT_EQ( test_mesh_update_err, 0 ); - - tribol::CouplingSchemeManager& couplingSchemeManager = - tribol::CouplingSchemeManager::getInstance(); - - tribol::CouplingScheme* couplingScheme = - &couplingSchemeManager.at( 0 ); - - // check mesh rate penalties - RealT penalty = 0.; - if (parameters.constant_rate_penalty) - { - penalty = parameters.rate_penalty; - checkMeshPenalties( couplingScheme, penalty, 1.E-8, "constant_rate" ); - } - else if (parameters.percent_rate_penalty) - { - penalty = parameters.rate_penalty_ratio * parameters.const_penalty; - checkMeshPenalties( couplingScheme, penalty, 1.E-8, "percent_rate" ); - } - - // check the gaps, pressures and force sense - //RealT gap = z_min2 - z_max1; - RealT rate_gap = velZ2 - velZ1; - RealT pressure = (rate_gap < 0.) ? penalty * rate_gap : 0.; - compareGaps( couplingScheme, rate_gap, 1.E-8, "rate_separation" ); - checkPressures( couplingScheme, pressure, 1.E-8, "rate" ); - checkForceSense( couplingScheme ); // note: the kinematic and rate contributions aren't separated - - tribol::finalize(); - -} // end test 'rate_separation' + this->m_mesh.mortarMeshId = 0; + this->m_mesh.nonmortarMeshId = 1; + + int nMortarElems = 1; + int nElemsXM = nMortarElems; + int nElemsYM = nMortarElems; + int nElemsZM = nMortarElems; + + int nNonmortarElems = 1; + int nElemsXS = nNonmortarElems; + int nElemsYS = nNonmortarElems; + int nElemsZS = nNonmortarElems; + + // mesh bounding box with 0.1 interpenetration gap + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.05; + + RealT x_min2 = 0.; + RealT y_min2 = 0.; + RealT z_min2 = 0.95; + RealT x_max2 = 1.; + RealT y_max2 = 1.; + RealT z_max2 = 2.; + + this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., + 0. ); + + // set velocities such that equal and opposite 'separation' velocities would + // lead to eventual separation of the blocks. This should not trigger a gap-rate + // contribution. + RealT dt = 1.e-3; + RealT velX1 = 0.; + RealT velY1 = 0.; + RealT velZ1 = -1.; + RealT velX2 = 0.; + RealT velY2 = 0.; + RealT velZ2 = 1.; + this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); + this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, velZ2 ); + + // set kinematic gap and rate (velocity) gap penalty parameters + tribol::TestControlParameters parameters; + parameters.penalty_ratio = false; + parameters.const_penalty = 0.75; + parameters.percent_rate_penalty = true; + parameters.rate_penalty_ratio = 0.20; + parameters.constant_rate_penalty = false; + parameters.rate_penalty = 1.0; + parameters.dt = dt; + + int test_mesh_update_err = this->m_mesh.tribolSetupAndUpdate( + tribol::COMMON_PLANE, tribol::PENALTY, tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); + + EXPECT_EQ( test_mesh_update_err, 0 ); + + tribol::CouplingSchemeManager& couplingSchemeManager = tribol::CouplingSchemeManager::getInstance(); + + tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); + + // check mesh rate penalties + RealT penalty = 0.; + if ( parameters.constant_rate_penalty ) { + penalty = parameters.rate_penalty; + checkMeshPenalties( couplingScheme, penalty, 1.E-8, "constant_rate" ); + } else if ( parameters.percent_rate_penalty ) { + penalty = parameters.rate_penalty_ratio * parameters.const_penalty; + checkMeshPenalties( couplingScheme, penalty, 1.E-8, "percent_rate" ); + } + + // check the gaps, pressures and force sense + // RealT gap = z_min2 - z_max1; + RealT rate_gap = velZ2 - velZ1; + RealT pressure = ( rate_gap < 0. ) ? penalty * rate_gap : 0.; + compareGaps( couplingScheme, rate_gap, 1.E-8, "rate_separation" ); + checkPressures( couplingScheme, pressure, 1.E-8, "rate" ); + checkForceSense( couplingScheme ); // note: the kinematic and rate contributions aren't separated + + tribol::finalize(); + +} // end test 'rate_separation' TEST_F( CommonPlaneTest, no_gap_percent_rate_penetration ) { - this->m_mesh.mortarMeshId = 0; - this->m_mesh.nonmortarMeshId = 1; - - int nMortarElems = 1; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 1; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - // mesh bounding box with 0.1 interpenetration gap - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 1.; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0. ); - - // set velocities such that further interpenetration would occur - RealT dt = 1.e-3; - RealT velX1 = 0.; - RealT velY1 = 0.; - RealT velZ1 = 1.; - RealT velX2 = 0.; - RealT velY2 = 0.; - RealT velZ2 = -1.; - this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); - this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, velZ2 ); - - // set kinematic gap and rate (velocity) gap penalty parameters - tribol::TestControlParameters parameters; - parameters.penalty_ratio = false; - parameters.const_penalty = 0.75; - parameters.percent_rate_penalty = true; - parameters.rate_penalty_ratio = 0.2; - parameters.constant_rate_penalty = false; - parameters.rate_penalty = 1.0; - parameters.dt = dt; - - int test_mesh_update_err = - this->m_mesh.tribolSetupAndUpdate( tribol::COMMON_PLANE, tribol::PENALTY, - tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); - - EXPECT_EQ( test_mesh_update_err, 0 ); - - tribol::CouplingSchemeManager& couplingSchemeManager = - tribol::CouplingSchemeManager::getInstance(); - - tribol::CouplingScheme* couplingScheme = - &couplingSchemeManager.at( 0 ); - - // check mesh rate penalties - RealT penalty = 0.; - if (parameters.constant_rate_penalty) - { - penalty = parameters.rate_penalty; - checkMeshPenalties( couplingScheme, penalty, 1.E-8, "constant_rate" ); - } - else if (parameters.percent_rate_penalty) - { - penalty = parameters.rate_penalty_ratio * parameters.const_penalty; - checkMeshPenalties( couplingScheme, penalty, 1.E-8, "percent_rate" ); - } - - // check to make sure zero rate pressure - RealT gap = z_min2 - z_max1; - RealT rate_gap = velZ2 - velZ1; - RealT pressure = (gap < 0. && rate_gap < 0.) ? penalty * rate_gap : 0.; - checkPressures( couplingScheme, pressure, 1.E-8, "rate" ); - - tribol::finalize(); + this->m_mesh.mortarMeshId = 0; + this->m_mesh.nonmortarMeshId = 1; + + int nMortarElems = 1; + int nElemsXM = nMortarElems; + int nElemsYM = nMortarElems; + int nElemsZM = nMortarElems; + + int nNonmortarElems = 1; + int nElemsXS = nNonmortarElems; + int nElemsYS = nNonmortarElems; + int nElemsZS = nNonmortarElems; + + // mesh bounding box with 0.1 interpenetration gap + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.; + + RealT x_min2 = 0.; + RealT y_min2 = 0.; + RealT z_min2 = 1.; + RealT x_max2 = 1.; + RealT y_max2 = 1.; + RealT z_max2 = 2.; + + this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., + 0. ); + + // set velocities such that further interpenetration would occur + RealT dt = 1.e-3; + RealT velX1 = 0.; + RealT velY1 = 0.; + RealT velZ1 = 1.; + RealT velX2 = 0.; + RealT velY2 = 0.; + RealT velZ2 = -1.; + this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); + this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, velZ2 ); + + // set kinematic gap and rate (velocity) gap penalty parameters + tribol::TestControlParameters parameters; + parameters.penalty_ratio = false; + parameters.const_penalty = 0.75; + parameters.percent_rate_penalty = true; + parameters.rate_penalty_ratio = 0.2; + parameters.constant_rate_penalty = false; + parameters.rate_penalty = 1.0; + parameters.dt = dt; + + int test_mesh_update_err = this->m_mesh.tribolSetupAndUpdate( + tribol::COMMON_PLANE, tribol::PENALTY, tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); + + EXPECT_EQ( test_mesh_update_err, 0 ); + + tribol::CouplingSchemeManager& couplingSchemeManager = tribol::CouplingSchemeManager::getInstance(); + + tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); + + // check mesh rate penalties + RealT penalty = 0.; + if ( parameters.constant_rate_penalty ) { + penalty = parameters.rate_penalty; + checkMeshPenalties( couplingScheme, penalty, 1.E-8, "constant_rate" ); + } else if ( parameters.percent_rate_penalty ) { + penalty = parameters.rate_penalty_ratio * parameters.const_penalty; + checkMeshPenalties( couplingScheme, penalty, 1.E-8, "percent_rate" ); + } + + // check to make sure zero rate pressure + RealT gap = z_min2 - z_max1; + RealT rate_gap = velZ2 - velZ1; + RealT pressure = ( gap < 0. && rate_gap < 0. ) ? penalty * rate_gap : 0.; + checkPressures( couplingScheme, pressure, 1.E-8, "rate" ); + + tribol::finalize(); } -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); #ifdef TRIBOL_USE_UMPIRE umpire::ResourceManager::getInstance(); // initialize umpire's ResouceManager diff --git a/src/tests/tribol_common_plane_gpu.cpp b/src/tests/tribol_common_plane_gpu.cpp index e522cb9d..feba434b 100644 --- a/src/tests/tribol_common_plane_gpu.cpp +++ b/src/tests/tribol_common_plane_gpu.cpp @@ -21,7 +21,7 @@ namespace tribol { template -double runExample(int num_elems_1d); +double runExample( int num_elems_1d ); /** * @brief This tests the tribol common plane method on multiple platforms and @@ -30,29 +30,28 @@ double runExample(int num_elems_1d); * a reference result for the given mesh configuration. */ class CommonPlaneGPUTest : public testing::TestWithParam { -protected: + protected: double max_error_; void SetUp() override { int ref_level = 2; - switch(GetParam()) - { + switch ( GetParam() ) { case ExecutionMode::Sequential: - max_error_ = runExample(ref_level); + max_error_ = runExample( ref_level ); break; #ifdef TRIBOL_USE_OPENMP case ExecutionMode::OpenMP: - max_error_ = runExample(ref_level); + max_error_ = runExample( ref_level ); break; #endif #ifdef TRIBOL_USE_CUDA case ExecutionMode::Cuda: - max_error_ = runExample(ref_level); + max_error_ = runExample( ref_level ); break; #endif #ifdef TRIBOL_USE_HIP case ExecutionMode::Hip: - max_error_ = runExample(ref_level); + max_error_ = runExample( ref_level ); break; #endif default: @@ -63,57 +62,56 @@ class CommonPlaneGPUTest : public testing::TestWithParam { } }; -TEST_P(CommonPlaneGPUTest, update_test) +TEST_P( CommonPlaneGPUTest, update_test ) { - EXPECT_LT(max_error_, 1.0e-13); + EXPECT_LT( max_error_, 1.0e-13 ); - MPI_Barrier(MPI_COMM_WORLD); + MPI_Barrier( MPI_COMM_WORLD ); } -INSTANTIATE_TEST_SUITE_P(tribol, CommonPlaneGPUTest, testing::Values( - ExecutionMode::Sequential +INSTANTIATE_TEST_SUITE_P( tribol, CommonPlaneGPUTest, + testing::Values( ExecutionMode::Sequential #ifdef TRIBOL_USE_OPENMP - , ExecutionMode::OpenMP + , + ExecutionMode::OpenMP #endif #ifdef TRIBOL_USE_CUDA - , ExecutionMode::Cuda + , + ExecutionMode::Cuda #endif #ifdef TRIBOL_USE_HIP - , ExecutionMode::Hip + , + ExecutionMode::Hip #endif -)); + ) ); template -double runExample(int num_elems_1d) +double runExample( int num_elems_1d ) { - // This controls which device/programming model is targeted in MFEM. When set // to "cuda" or "hip", on device pointers point to GPU memory. mfem::Device device; - switch (MSPACE) - { + switch ( MSPACE ) { #ifdef TRIBOL_USE_CUDA case MemorySpace::Device: - device.Configure("cuda"); + device.Configure( "cuda" ); break; #endif #ifdef TRIBOL_USE_HIP case MemorySpace::Hip: - device.Configure("hip"); + device.Configure( "hip" ); break; #endif default: -#if defined(TRIBOL_USE_OPENMP) && defined(MFEM_USE_OPENMP) - if (EXEC == ExecutionMode::OpenMP) - { - device.Configure("omp"); - } - else +#if defined( TRIBOL_USE_OPENMP ) && defined( MFEM_USE_OPENMP ) + if ( EXEC == ExecutionMode::OpenMP ) { + device.Configure( "omp" ); + } else #endif // NOTE: if we specify OpenMP on this problem but MFEM doesn't support // OpenMP, we can just do all our MFEM work without it { - device.Configure("cpu"); + device.Configure( "cpu" ); } break; } @@ -122,192 +120,152 @@ double runExample(int num_elems_1d) // Creating MFEM mesh int num_contact_elems = num_elems_1d * num_elems_1d; - double elem_height = 1.0/static_cast(num_elems_1d); + double elem_height = 1.0 / static_cast( num_elems_1d ); // create top mesh - mfem::Mesh top_mesh = mfem::Mesh::MakeCartesian3D( - num_elems_1d, num_elems_1d, 1, mfem::Element::Type::HEXAHEDRON, - 1.0, 1.0, elem_height - ); + mfem::Mesh top_mesh = mfem::Mesh::MakeCartesian3D( num_elems_1d, num_elems_1d, 1, mfem::Element::Type::HEXAHEDRON, + 1.0, 1.0, elem_height ); // shift down 5% height of element (10% elem thickness interpenetration) - for (int i{0}; i < top_mesh.GetNV(); ++i) - { - top_mesh.GetVertex(i)[2] -= 0.05*elem_height; + for ( int i{ 0 }; i < top_mesh.GetNV(); ++i ) { + top_mesh.GetVertex( i )[2] -= 0.05 * elem_height; } // create bottom mesh - mfem::Mesh bottom_mesh = mfem::Mesh::MakeCartesian3D( - num_elems_1d, num_elems_1d, 1, mfem::Element::Type::HEXAHEDRON, - 1.0, 1.0, elem_height - ); + mfem::Mesh bottom_mesh = mfem::Mesh::MakeCartesian3D( num_elems_1d, num_elems_1d, 1, mfem::Element::Type::HEXAHEDRON, + 1.0, 1.0, elem_height ); // shift down 95% height of element (10% elem thickness interpenetration) - for (int i{0}; i < bottom_mesh.GetNV(); ++i) - { - bottom_mesh.GetVertex(i)[2] -= 0.95*elem_height; + for ( int i{ 0 }; i < bottom_mesh.GetNV(); ++i ) { + bottom_mesh.GetVertex( i )[2] -= 0.95 * elem_height; } // Creating MFEM grid functions - mfem::H1_FECollection top_fe_coll(1, top_mesh.SpaceDimension()); - mfem::FiniteElementSpace top_fe_space( - &top_mesh, &top_fe_coll, top_mesh.SpaceDimension() - ); - mfem::GridFunction top_coords(&top_fe_space); - top_mesh.SetNodalGridFunction(&top_coords, false); + mfem::H1_FECollection top_fe_coll( 1, top_mesh.SpaceDimension() ); + mfem::FiniteElementSpace top_fe_space( &top_mesh, &top_fe_coll, top_mesh.SpaceDimension() ); + mfem::GridFunction top_coords( &top_fe_space ); + top_mesh.SetNodalGridFunction( &top_coords, false ); auto top_coords_ptr = top_coords.Read(); - auto top_x_coords_ptr = &top_coords_ptr[top_fe_space.DofToVDof(0, 0)]; - auto top_y_coords_ptr = &top_coords_ptr[top_fe_space.DofToVDof(0, 1)]; - auto top_z_coords_ptr = &top_coords_ptr[top_fe_space.DofToVDof(0, 2)]; - - mfem::H1_FECollection bottom_fe_coll(1, bottom_mesh.SpaceDimension()); - mfem::FiniteElementSpace bottom_fe_space( - &bottom_mesh, &bottom_fe_coll, bottom_mesh.SpaceDimension() - ); - mfem::GridFunction bottom_coords(&bottom_fe_space); - bottom_mesh.SetNodalGridFunction(&bottom_coords, false); + auto top_x_coords_ptr = &top_coords_ptr[top_fe_space.DofToVDof( 0, 0 )]; + auto top_y_coords_ptr = &top_coords_ptr[top_fe_space.DofToVDof( 0, 1 )]; + auto top_z_coords_ptr = &top_coords_ptr[top_fe_space.DofToVDof( 0, 2 )]; + + mfem::H1_FECollection bottom_fe_coll( 1, bottom_mesh.SpaceDimension() ); + mfem::FiniteElementSpace bottom_fe_space( &bottom_mesh, &bottom_fe_coll, bottom_mesh.SpaceDimension() ); + mfem::GridFunction bottom_coords( &bottom_fe_space ); + bottom_mesh.SetNodalGridFunction( &bottom_coords, false ); auto bottom_coords_ptr = bottom_coords.Read(); - auto bottom_x_coords_ptr = &bottom_coords_ptr[bottom_fe_space.DofToVDof(0, 0)]; - auto bottom_y_coords_ptr = &bottom_coords_ptr[bottom_fe_space.DofToVDof(0, 1)]; - auto bottom_z_coords_ptr = &bottom_coords_ptr[bottom_fe_space.DofToVDof(0, 2)]; + auto bottom_x_coords_ptr = &bottom_coords_ptr[bottom_fe_space.DofToVDof( 0, 0 )]; + auto bottom_y_coords_ptr = &bottom_coords_ptr[bottom_fe_space.DofToVDof( 0, 1 )]; + auto bottom_z_coords_ptr = &bottom_coords_ptr[bottom_fe_space.DofToVDof( 0, 2 )]; // Creating Tribol connectivity // top mesh connectivity (build on cpu) - auto top_bdry_attrib = 1; // corresponds to bottom of top mesh - ArrayT host_top_conn( - num_contact_elems, 4 - ); + auto top_bdry_attrib = 1; // corresponds to bottom of top mesh + ArrayT host_top_conn( num_contact_elems, 4 ); int elem_ct = 0; - for (int be{0}; be < top_mesh.GetNBE(); ++be) - { - if (top_mesh.GetBdrAttribute(be) == top_bdry_attrib) - { - mfem::Array be_dofs(4);//, mfem::MemoryType::Host_UMPIRE); - top_fe_space.GetBdrElementDofs(be, be_dofs); - for (int i{0}; i < 4; ++i) - { - host_top_conn(elem_ct, i) = be_dofs[i]; + for ( int be{ 0 }; be < top_mesh.GetNBE(); ++be ) { + if ( top_mesh.GetBdrAttribute( be ) == top_bdry_attrib ) { + mfem::Array be_dofs( 4 ); //, mfem::MemoryType::Host_UMPIRE); + top_fe_space.GetBdrElementDofs( be, be_dofs ); + for ( int i{ 0 }; i < 4; ++i ) { + host_top_conn( elem_ct, i ) = be_dofs[i]; } ++elem_ct; } } // move to gpu if MSPACE is device, just (deep) copy otherwise - ArrayT top_conn(host_top_conn); + ArrayT top_conn( host_top_conn ); // bottom mesh connectivity (build on cpu) - auto bottom_bdry_attrib = 6; // corresponds to top of bottom mesh - ArrayT host_bottom_conn( - num_contact_elems, 4 - ); + auto bottom_bdry_attrib = 6; // corresponds to top of bottom mesh + ArrayT host_bottom_conn( num_contact_elems, 4 ); elem_ct = 0; - for (int be{0}; be < bottom_mesh.GetNBE(); ++be) - { - if (bottom_mesh.GetBdrAttribute(be) == bottom_bdry_attrib) - { - mfem::Array be_dofs(4);//, mfem::MemoryType::Host_UMPIRE); - bottom_fe_space.GetBdrElementDofs(be, be_dofs); - for (int i{0}; i < 4; ++i) - { - host_bottom_conn(elem_ct, i) = be_dofs[i]; + for ( int be{ 0 }; be < bottom_mesh.GetNBE(); ++be ) { + if ( bottom_mesh.GetBdrAttribute( be ) == bottom_bdry_attrib ) { + mfem::Array be_dofs( 4 ); //, mfem::MemoryType::Host_UMPIRE); + bottom_fe_space.GetBdrElementDofs( be, be_dofs ); + for ( int i{ 0 }; i < 4; ++i ) { + host_bottom_conn( elem_ct, i ) = be_dofs[i]; } ++elem_ct; } } // move to gpu if MSPACE is device, just (deep) copy otherwise - ArrayT bottom_conn(host_bottom_conn); + ArrayT bottom_conn( host_bottom_conn ); // Registering Tribol mesh data constexpr IndexT top_mesh_id = 0; - registerMesh( - top_mesh_id, num_contact_elems, top_fe_space.GetNDofs(), - top_conn.data(), LINEAR_QUAD, - top_x_coords_ptr, top_y_coords_ptr, top_z_coords_ptr, MSPACE - ); + registerMesh( top_mesh_id, num_contact_elems, top_fe_space.GetNDofs(), top_conn.data(), LINEAR_QUAD, top_x_coords_ptr, + top_y_coords_ptr, top_z_coords_ptr, MSPACE ); constexpr IndexT bottom_mesh_id = 1; - registerMesh( - bottom_mesh_id, num_contact_elems, bottom_fe_space.GetNDofs(), - bottom_conn.data(), LINEAR_QUAD, - bottom_x_coords_ptr, bottom_y_coords_ptr, bottom_z_coords_ptr, MSPACE - ); + registerMesh( bottom_mesh_id, num_contact_elems, bottom_fe_space.GetNDofs(), bottom_conn.data(), LINEAR_QUAD, + bottom_x_coords_ptr, bottom_y_coords_ptr, bottom_z_coords_ptr, MSPACE ); constexpr RealT penalty = 5000.0; - setKinematicConstantPenalty(top_mesh_id, penalty); - setKinematicConstantPenalty(bottom_mesh_id, penalty); + setKinematicConstantPenalty( top_mesh_id, penalty ); + setKinematicConstantPenalty( bottom_mesh_id, penalty ); // Creating and registering velocity and force - mfem::GridFunction top_velocity(&top_fe_space); + mfem::GridFunction top_velocity( &top_fe_space ); top_velocity = 0.0; // Get a (device, if on GPU) pointer to read velocity data auto top_velocity_ptr = top_velocity.Read(); - auto top_x_velocity_ptr = &top_velocity_ptr[top_fe_space.DofToVDof(0, 0)]; - auto top_y_velocity_ptr = &top_velocity_ptr[top_fe_space.DofToVDof(0, 1)]; - auto top_z_velocity_ptr = &top_velocity_ptr[top_fe_space.DofToVDof(0, 2)]; - registerNodalVelocities( - top_mesh_id, top_x_velocity_ptr, top_y_velocity_ptr, top_z_velocity_ptr - ); - - mfem::GridFunction bottom_velocity(&bottom_fe_space); + auto top_x_velocity_ptr = &top_velocity_ptr[top_fe_space.DofToVDof( 0, 0 )]; + auto top_y_velocity_ptr = &top_velocity_ptr[top_fe_space.DofToVDof( 0, 1 )]; + auto top_z_velocity_ptr = &top_velocity_ptr[top_fe_space.DofToVDof( 0, 2 )]; + registerNodalVelocities( top_mesh_id, top_x_velocity_ptr, top_y_velocity_ptr, top_z_velocity_ptr ); + + mfem::GridFunction bottom_velocity( &bottom_fe_space ); bottom_velocity = 0.0; // Get a (device, if on GPU) pointer to read velocity data auto bottom_velocity_ptr = bottom_velocity.Read(); - auto bottom_x_velocity_ptr = &bottom_velocity_ptr[bottom_fe_space.DofToVDof(0, 0)]; - auto bottom_y_velocity_ptr = &bottom_velocity_ptr[bottom_fe_space.DofToVDof(0, 1)]; - auto bottom_z_velocity_ptr = &bottom_velocity_ptr[bottom_fe_space.DofToVDof(0, 2)]; - registerNodalVelocities( - bottom_mesh_id, bottom_x_velocity_ptr, bottom_y_velocity_ptr, bottom_z_velocity_ptr - ); - - mfem::Vector top_force(top_fe_space.GetVSize()); + auto bottom_x_velocity_ptr = &bottom_velocity_ptr[bottom_fe_space.DofToVDof( 0, 0 )]; + auto bottom_y_velocity_ptr = &bottom_velocity_ptr[bottom_fe_space.DofToVDof( 0, 1 )]; + auto bottom_z_velocity_ptr = &bottom_velocity_ptr[bottom_fe_space.DofToVDof( 0, 2 )]; + registerNodalVelocities( bottom_mesh_id, bottom_x_velocity_ptr, bottom_y_velocity_ptr, bottom_z_velocity_ptr ); + + mfem::Vector top_force( top_fe_space.GetVSize() ); // For mfem::Vectors, the assumption is a single vector on host. Calling // UseDevice(true) creates a version on device. Note this call isn't needed // for mfem::GridFunctions, which call UseDevice(true) in the constructor. - top_force.UseDevice(true); + top_force.UseDevice( true ); top_force = 0.0; // Get a (device, if on GPU) pointer to read and write to force data auto top_force_ptr = top_force.ReadWrite(); - auto top_x_force_ptr = &top_force_ptr[top_fe_space.DofToVDof(0, 0)]; - auto top_y_force_ptr = &top_force_ptr[top_fe_space.DofToVDof(0, 1)]; - auto top_z_force_ptr = &top_force_ptr[top_fe_space.DofToVDof(0, 2)]; - registerNodalResponse( - top_mesh_id, top_x_force_ptr, top_y_force_ptr, top_z_force_ptr - ); - - mfem::Vector bottom_force(bottom_fe_space.GetVSize()); + auto top_x_force_ptr = &top_force_ptr[top_fe_space.DofToVDof( 0, 0 )]; + auto top_y_force_ptr = &top_force_ptr[top_fe_space.DofToVDof( 0, 1 )]; + auto top_z_force_ptr = &top_force_ptr[top_fe_space.DofToVDof( 0, 2 )]; + registerNodalResponse( top_mesh_id, top_x_force_ptr, top_y_force_ptr, top_z_force_ptr ); + + mfem::Vector bottom_force( bottom_fe_space.GetVSize() ); // For mfem::Vectors, the assumption is a single vector on host. Calling // UseDevice(true) creates a version on device. Note this call isn't needed // for mfem::GridFunctions, which call UseDevice(true) in the constructor. - bottom_force.UseDevice(true); + bottom_force.UseDevice( true ); bottom_force = 0.0; // Get a (device, if on GPU) pointer to read and write to force data auto bottom_force_ptr = bottom_force.ReadWrite(); - auto bottom_x_force_ptr = &bottom_force_ptr[bottom_fe_space.DofToVDof(0, 0)]; - auto bottom_y_force_ptr = &bottom_force_ptr[bottom_fe_space.DofToVDof(0, 1)]; - auto bottom_z_force_ptr = &bottom_force_ptr[bottom_fe_space.DofToVDof(0, 2)]; - registerNodalResponse( - bottom_mesh_id, bottom_x_force_ptr, bottom_y_force_ptr, bottom_z_force_ptr - ); + auto bottom_x_force_ptr = &bottom_force_ptr[bottom_fe_space.DofToVDof( 0, 0 )]; + auto bottom_y_force_ptr = &bottom_force_ptr[bottom_fe_space.DofToVDof( 0, 1 )]; + auto bottom_z_force_ptr = &bottom_force_ptr[bottom_fe_space.DofToVDof( 0, 2 )]; + registerNodalResponse( bottom_mesh_id, bottom_x_force_ptr, bottom_y_force_ptr, bottom_z_force_ptr ); // Registering Tribol coupling scheme constexpr IndexT cs_id = 0; - registerCouplingScheme(cs_id, top_mesh_id, bottom_mesh_id, - SURFACE_TO_SURFACE, - NO_CASE, - COMMON_PLANE, - FRICTIONLESS, - PENALTY, - BINNING_BVH, - EXEC); + registerCouplingScheme( cs_id, top_mesh_id, bottom_mesh_id, SURFACE_TO_SURFACE, NO_CASE, COMMON_PLANE, FRICTIONLESS, + PENALTY, BINNING_BVH, EXEC ); - setPenaltyOptions(cs_id, KINEMATIC, KINEMATIC_CONSTANT); + setPenaltyOptions( cs_id, KINEMATIC, KINEMATIC_CONSTANT ); // Calling Tribol update constexpr int cycle = 1; constexpr RealT t = 1.0; RealT dt = 1.0; - update(cycle, t, dt); + update( cycle, t, dt ); RealT tot_force = top_force.Norml1() + bottom_force.Norml1(); std::cout << "Total |force|: " << tot_force << std::endl; @@ -315,18 +273,18 @@ double runExample(int num_elems_1d) return tot_force; } -} // namespace tribol +} // namespace tribol //------------------------------------------------------------------------------ #include "axom/slic/core/SimpleLogger.hpp" -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - MPI_Init(&argc, &argv); + MPI_Init( &argc, &argv ); - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); #ifdef TRIBOL_USE_UMPIRE umpire::ResourceManager::getInstance(); // initialize umpire's ResouceManager @@ -341,4 +299,3 @@ int main(int argc, char* argv[]) return result; } - diff --git a/src/tests/tribol_common_plane_penalty.cpp b/src/tests/tribol_common_plane_penalty.cpp index b53fed4c..b6442bfe 100644 --- a/src/tests/tribol_common_plane_penalty.cpp +++ b/src/tests/tribol_common_plane_penalty.cpp @@ -27,7 +27,7 @@ #include "gtest/gtest.h" // c++ includes -#include // std::abs, std::cos, std::sin +#include // std::abs, std::cos, std::sin #include #include #include @@ -35,556 +35,477 @@ using RealT = tribol::RealT; -void compareGaps( tribol::CouplingScheme const * cs, - RealT gap, const RealT tol, - const char *gapType ) +void compareGaps( tribol::CouplingScheme const* cs, RealT gap, const RealT tol, const char* gapType ) { - tribol::IndexT const numPairs = cs->getNumActivePairs(); - // TODO: get rid of the const cast if we can - const auto cs_view = const_cast(cs)->getView(); - - for (tribol::IndexT cpID = 0; cpID < numPairs; ++cpID) - { - auto& plane = cs->getContactPlane(cpID); - - RealT my_gap = 0.; - if ( std::strcmp( gapType, "kinematic_penetration" ) == 0 || - std::strcmp( gapType, "kinematic_separation" ) == 0 ) - { - my_gap = plane.m_gap; - } - else - { - my_gap = plane.m_velGap; + tribol::IndexT const numPairs = cs->getNumActivePairs(); + // TODO: get rid of the const cast if we can + const auto cs_view = const_cast( cs )->getView(); + + for ( tribol::IndexT cpID = 0; cpID < numPairs; ++cpID ) { + auto& plane = cs->getContactPlane( cpID ); + + RealT my_gap = 0.; + if ( std::strcmp( gapType, "kinematic_penetration" ) == 0 || std::strcmp( gapType, "kinematic_separation" ) == 0 ) { + my_gap = plane.m_gap; + } else { + my_gap = plane.m_velGap; + } + + RealT gap_tol = cs_view.getGapTol( plane.getCpElementId1(), plane.getCpElementId2() ); + + // check gap sense + if ( std::strcmp( gapType, "kinematic_penetration" ) == 0 || std::strcmp( gapType, "rate_penetration" ) == 0 ) { + // check that g < gap_tol (interpenetration) + EXPECT_LE( my_gap, gap_tol ); + } else if ( std::strcmp( gapType, "kinematic_separation" ) == 0 || + std::strcmp( gapType, "rate_separation" ) == 0 ) { + // check that g > gap_tol (separation) + EXPECT_GE( my_gap, gap_tol ); + } else { + SLIC_ERROR( "compareGaps: invalid gapType. " + << "Acceptable types are 'kinematic_penetration', 'kinematic_separation', " + << "'rate_penetration' or 'rate_separation'." ); + ; + } + + // check diffs + RealT diff = std::abs( my_gap - gap ); + EXPECT_LE( diff, tol ); + } +} // end compareGaps() + +void checkMeshPenalties( tribol::CouplingScheme const* cs, const RealT penalty, const RealT tol, + const char* penaltyType ) +{ + tribol::IndexT const meshId1 = cs->getMeshId1(); + tribol::IndexT const meshId2 = cs->getMeshId2(); + + tribol::MeshManager& meshManager = tribol::MeshManager::getInstance(); + tribol::MeshData& mesh1 = meshManager.at( meshId1 ); + tribol::MeshData& mesh2 = meshManager.at( meshId2 ); + + if ( std::strcmp( penaltyType, "constant" ) == 0 ) { + RealT penalty_diff_1 = std::abs( mesh1.getElementData().m_penalty_stiffness - penalty ); + RealT penalty_diff_2 = std::abs( mesh2.getElementData().m_penalty_stiffness - penalty ); + EXPECT_LE( penalty_diff_1, tol ); + EXPECT_LE( penalty_diff_2, tol ); + } else if ( std::strcmp( penaltyType, "face" ) == 0 ) { + // no-op, the face-based penalty is checked in a call to tribol::update() + } else if ( std::strcmp( penaltyType, "constant_rate" ) == 0 ) { + RealT penalty_diff_1 = std::abs( mesh1.getElementData().m_rate_penalty_stiffness - penalty ); + RealT penalty_diff_2 = std::abs( mesh2.getElementData().m_rate_penalty_stiffness - penalty ); + EXPECT_LE( penalty_diff_1, tol ); + EXPECT_LE( penalty_diff_2, tol ); + } else if ( std::strcmp( penaltyType, "percent_rate" ) == 0 ) { + RealT penalty1 = mesh1.getElementData().m_rate_percent_stiffness * mesh1.getElementData().m_penalty_stiffness; + RealT penalty2 = mesh2.getElementData().m_rate_percent_stiffness * mesh2.getElementData().m_penalty_stiffness; + RealT penalty_diff_1 = std::abs( penalty1 - penalty ); + RealT penalty_diff_2 = std::abs( penalty2 - penalty ); + EXPECT_LE( penalty_diff_1, tol ); + EXPECT_LE( penalty_diff_2, tol ); + } else { + SLIC_ERROR( "checkMeshPenalties: invalid penaltyType. " + << "only 'constant', 'face', 'constant_rate', or 'percent_rate' accepted. " ); + } + +} // end checkMeshPenalties() + +void checkPressures( tribol::CouplingScheme const* cs, RealT pressure, const RealT tol, + const char* pressureType = "kinematic" ) +{ + tribol::IndexT const numPairs = cs->getNumActivePairs(); + + for ( tribol::IndexT cpID = 0; cpID < numPairs; ++cpID ) { + auto& plane = cs->getContactPlane( cpID ); + + RealT my_pressure = 0.; + if ( std::strcmp( pressureType, "rate" ) == 0 ) { + my_pressure = plane.m_ratePressure; + } else if ( std::strcmp( pressureType, "kinematic" ) == 0 ) { + my_pressure = plane.m_pressure; + } else { + SLIC_ERROR( "checkPressures(): invalid pressure type. Supported types are " + << "'kinematic' or 'rate'." ); + } + + // check diffs + RealT press_diff = std::abs( my_pressure - pressure ); + EXPECT_LE( press_diff, tol ); + } +} // end checkPressures() + +// problem specific routine to check the sense of the force. Note, this +// routine makes implicit use of the knowledge that the outward facing +// surface unit normals are in the +/- z-direction for mesh 1 and +// mesh 2, respectively. This is not a general routine for general +// mesh configurations. +void checkForceSense( tribol::CouplingScheme const* cs, bool isTied = false ) +{ + // TODO: get rid of const cast (if we can) + const auto mesh1 = const_cast( cs )->getMesh1().getView(); + const auto mesh2 = const_cast( cs )->getMesh2().getView(); + + for ( int i = 0; i < 2; ++i ) // loop over meshes + { + auto& mesh = ( i == 0 ) ? mesh1 : mesh2; + + // loop over faces and nodes + for ( tribol::IndexT kf = 0; kf < mesh.numberOfElements(); ++kf ) { + for ( tribol::IndexT a = 0; a < mesh.numberOfNodesPerElement(); ++a ) { + int node_id = mesh.getGlobalNodeId( kf, a ); + RealT force_mag = tribol::dotProd( mesh.getResponse()[0][node_id], mesh.getResponse()[1][node_id], + mesh.getResponse()[2][node_id], mesh.getElementNormals()[0][kf], + mesh.getElementNormals()[1][kf], mesh.getElementNormals()[2][kf] ); + if ( !isTied ) { + // <= catches interpenetration AND separation + EXPECT_LE( force_mag, 0. ); + } else { + // no-op, TIED_NORMAL is a special case where we support + // all force 'senses' (i.e. tension and compression) + } } + } + } +} // end checkForceSense() - RealT gap_tol = cs_view.getGapTol( plane.getCpElementId1(), plane.getCpElementId2() ); +/*! + * Test fixture class with some setup necessary to test + * the COMMON_PLANE + PENALTY implementation + */ +class CommonPlaneTest : public ::testing::Test { + public: + tribol::TestMesh m_mesh; - // check gap sense - if ( std::strcmp( gapType, "kinematic_penetration" ) == 0 || - std::strcmp( gapType, "rate_penetration" ) == 0 ) - { - // check that g < gap_tol (interpenetration) - EXPECT_LE( my_gap, gap_tol ); - } - else if ( std::strcmp( gapType, "kinematic_separation" ) == 0 || - std::strcmp( gapType, "rate_separation" ) == 0 ) - { - // check that g > gap_tol (separation) - EXPECT_GE( my_gap, gap_tol ); - } - else - { - SLIC_ERROR("compareGaps: invalid gapType. " << - "Acceptable types are 'kinematic_penetration', 'kinematic_separation', " << - "'rate_penetration' or 'rate_separation'." ) ;; - } + protected: + void SetUp() override {} - // check diffs - RealT diff = std::abs( my_gap - gap ); - EXPECT_LE( diff, tol ); - } -} // end compareGaps() + void TearDown() override { this->m_mesh.clear(); } -void checkMeshPenalties( tribol::CouplingScheme const * cs, - const RealT penalty, const RealT tol, - const char * penaltyType ) -{ - tribol::IndexT const meshId1 = cs->getMeshId1(); - tribol::IndexT const meshId2 = cs->getMeshId2(); - - tribol::MeshManager& meshManager = tribol::MeshManager::getInstance(); - tribol::MeshData& mesh1 = meshManager.at( meshId1 ); - tribol::MeshData& mesh2 = meshManager.at( meshId2 ); - - if ( std::strcmp( penaltyType, "constant" ) == 0 ) - { - RealT penalty_diff_1 = std::abs( mesh1.getElementData().m_penalty_stiffness - penalty ); - RealT penalty_diff_2 = std::abs( mesh2.getElementData().m_penalty_stiffness - penalty ); - EXPECT_LE( penalty_diff_1, tol ); - EXPECT_LE( penalty_diff_2, tol ); - } - else if ( std::strcmp( penaltyType, "face" ) == 0 ) - { - // no-op, the face-based penalty is checked in a call to tribol::update() - } - else if ( std::strcmp( penaltyType, "constant_rate" ) == 0 ) - { - RealT penalty_diff_1 = std::abs( mesh1.getElementData().m_rate_penalty_stiffness - penalty ); - RealT penalty_diff_2 = std::abs( mesh2.getElementData().m_rate_penalty_stiffness - penalty ); - EXPECT_LE( penalty_diff_1, tol ); - EXPECT_LE( penalty_diff_2, tol ); - } - else if ( std::strcmp( penaltyType, "percent_rate" ) == 0 ) - { - RealT penalty1 = mesh1.getElementData().m_rate_percent_stiffness * mesh1.getElementData().m_penalty_stiffness; - RealT penalty2 = mesh2.getElementData().m_rate_percent_stiffness * mesh2.getElementData().m_penalty_stiffness; - RealT penalty_diff_1 = std::abs( penalty1 - penalty ); - RealT penalty_diff_2 = std::abs( penalty2 - penalty ); - EXPECT_LE( penalty_diff_1, tol ); - EXPECT_LE( penalty_diff_2, tol ); - } - else - { - SLIC_ERROR("checkMeshPenalties: invalid penaltyType. " << - "only 'constant', 'face', 'constant_rate', or 'percent_rate' accepted. " ); - } - -} // end checkMeshPenalties() - -void checkPressures( tribol::CouplingScheme const * cs, - RealT pressure, const RealT tol, const char * pressureType = "kinematic" ) + protected: +}; + +TEST_F( CommonPlaneTest, penetration_gap_check ) { - tribol::IndexT const numPairs = cs->getNumActivePairs(); + this->m_mesh.mortarMeshId = 0; + this->m_mesh.nonmortarMeshId = 1; - for (tribol::IndexT cpID = 0; cpID < numPairs; ++cpID) - { - auto& plane = cs->getContactPlane(cpID); + int nMortarElems = 4; + int nElemsXM = nMortarElems; + int nElemsYM = nMortarElems; + int nElemsZM = nMortarElems; - RealT my_pressure = 0.; - if ( std::strcmp( pressureType, "rate" ) == 0 ) - { - my_pressure = plane.m_ratePressure; - } - else if ( std::strcmp( pressureType, "kinematic" ) == 0 ) - { - my_pressure = plane.m_pressure; - } - else - { - SLIC_ERROR( "checkPressures(): invalid pressure type. Supported types are " << - "'kinematic' or 'rate'." ); - } + int nNonmortarElems = 5; + int nElemsXS = nNonmortarElems; + int nElemsYS = nNonmortarElems; + int nElemsZS = nNonmortarElems; - // check diffs - RealT press_diff = std::abs( my_pressure - pressure ); - EXPECT_LE( press_diff, tol ); - } -} // end checkPressures() + // mesh bounding box with 0.1 interpenetration gap + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.05; -// problem specific routine to check the sense of the force. Note, this -// routine makes implicit use of the knowledge that the outward facing -// surface unit normals are in the +/- z-direction for mesh 1 and -// mesh 2, respectively. This is not a general routine for general -// mesh configurations. -void checkForceSense( tribol::CouplingScheme const * cs, bool isTied = false ) -{ - // TODO: get rid of const cast (if we can) - const auto mesh1 = const_cast(cs)->getMesh1().getView(); - const auto mesh2 = const_cast(cs)->getMesh2().getView(); - - for (int i=0; i<2; ++i) // loop over meshes - { - auto& mesh = (i==0) ? mesh1 : mesh2; - - // loop over faces and nodes - for (tribol::IndexT kf = 0; kf < mesh.numberOfElements(); ++kf) - { - for (tribol::IndexT a = 0; am_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., + 0. ); - tribol::TestMesh m_mesh; + // call tribol setup and update + tribol::TestControlParameters parameters; // struct does not hold info right now + parameters.dt = 1.e-3; + parameters.penalty_ratio = false; + parameters.const_penalty = 1.0; -protected: + int test_mesh_update_err = this->m_mesh.tribolSetupAndUpdate( + tribol::COMMON_PLANE, tribol::PENALTY, tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); - void SetUp() override - { - } + EXPECT_EQ( test_mesh_update_err, 0 ); - void TearDown() override - { - this->m_mesh.clear(); - } + tribol::CouplingSchemeManager& couplingSchemeManager = tribol::CouplingSchemeManager::getInstance(); -protected: + tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); -}; + RealT gap = z_min2 - z_max1; -TEST_F( CommonPlaneTest, penetration_gap_check ) -{ - this->m_mesh.mortarMeshId = 0; - this->m_mesh.nonmortarMeshId = 1; - - int nMortarElems = 4; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 5; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - // mesh bounding box with 0.1 interpenetration gap - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.05; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 0.95; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0. ); - - // call tribol setup and update - tribol::TestControlParameters parameters; // struct does not hold info right now - parameters.dt = 1.e-3; - parameters.penalty_ratio = false; - parameters.const_penalty = 1.0; - - int test_mesh_update_err = - this->m_mesh.tribolSetupAndUpdate( tribol::COMMON_PLANE, tribol::PENALTY, - tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); - - EXPECT_EQ( test_mesh_update_err, 0 ); - - tribol::CouplingSchemeManager& couplingSchemeManager = - tribol::CouplingSchemeManager::getInstance(); - - tribol::CouplingScheme* couplingScheme = - &couplingSchemeManager.at( 0 ); - - RealT gap = z_min2 - z_max1; - - compareGaps( couplingScheme, gap, 1.E-8, "kinematic_penetration" ); - - tribol::finalize(); + compareGaps( couplingScheme, gap, 1.E-8, "kinematic_penetration" ); + tribol::finalize(); } TEST_F( CommonPlaneTest, separation_gap_check ) { - this->m_mesh.mortarMeshId = 0; - this->m_mesh.nonmortarMeshId = 1; - - int nMortarElems = 4; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 5; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - // mesh bounding box with 0.1 separation gap - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 1.1; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0. ); - - // call tribol setup and update - tribol::TestControlParameters parameters; // struct does not hold info right now - parameters.penalty_ratio = false; - parameters.const_penalty = 1.0; - - int test_mesh_update_err = - this->m_mesh.tribolSetupAndUpdate( tribol::COMMON_PLANE, tribol::PENALTY, - tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); - - EXPECT_EQ( test_mesh_update_err, 0 ); - - tribol::CouplingSchemeManager& couplingSchemeManager = - tribol::CouplingSchemeManager::getInstance(); - - tribol::CouplingScheme* couplingScheme = - &couplingSchemeManager.at( 0 ); - - RealT gap = z_min2 - z_max1; - - compareGaps( couplingScheme, gap, 1.E-8, "kinematic_separation" ); - - tribol::finalize(); + this->m_mesh.mortarMeshId = 0; + this->m_mesh.nonmortarMeshId = 1; + + int nMortarElems = 4; + int nElemsXM = nMortarElems; + int nElemsYM = nMortarElems; + int nElemsZM = nMortarElems; + + int nNonmortarElems = 5; + int nElemsXS = nNonmortarElems; + int nElemsYS = nNonmortarElems; + int nElemsZS = nNonmortarElems; + + // mesh bounding box with 0.1 separation gap + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.; + + RealT x_min2 = 0.; + RealT y_min2 = 0.; + RealT z_min2 = 1.1; + RealT x_max2 = 1.; + RealT y_max2 = 1.; + RealT z_max2 = 2.; + + this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., + 0. ); + + // call tribol setup and update + tribol::TestControlParameters parameters; // struct does not hold info right now + parameters.penalty_ratio = false; + parameters.const_penalty = 1.0; + + int test_mesh_update_err = this->m_mesh.tribolSetupAndUpdate( + tribol::COMMON_PLANE, tribol::PENALTY, tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); + + EXPECT_EQ( test_mesh_update_err, 0 ); + + tribol::CouplingSchemeManager& couplingSchemeManager = tribol::CouplingSchemeManager::getInstance(); + + tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); + + RealT gap = z_min2 - z_max1; + + compareGaps( couplingScheme, gap, 1.E-8, "kinematic_separation" ); + + tribol::finalize(); } TEST_F( CommonPlaneTest, constant_penalty_check ) { - this->m_mesh.mortarMeshId = 0; - this->m_mesh.nonmortarMeshId = 1; - - int nMortarElems = 4; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 5; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - // mesh bounding box with 0.1 interpenetration gap - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.05; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 0.95; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0. ); - - // call tribol setup and update - tribol::TestControlParameters parameters; // struct does not hold info right now - parameters.penalty_ratio = false; - parameters.const_penalty = 0.75; - - int test_mesh_update_err = - this->m_mesh.tribolSetupAndUpdate( tribol::COMMON_PLANE, tribol::PENALTY, - tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); - - EXPECT_EQ( test_mesh_update_err, 0 ); - - tribol::CouplingSchemeManager& couplingSchemeManager = - tribol::CouplingSchemeManager::getInstance(); - - tribol::CouplingScheme* couplingScheme = - &couplingSchemeManager.at( 0 ); - - // check mesh penalties - checkMeshPenalties( couplingScheme, parameters.const_penalty, 1.E-8, "constant" ); - - // check the pressures - RealT gap = z_min2 - z_max1; - RealT pressure = tribol::ComputePenaltyStiffnessPerArea( parameters.const_penalty, parameters.const_penalty ) * gap; - checkPressures( couplingScheme, pressure, 1.E-8 ); - checkForceSense( couplingScheme ); - - tribol::finalize(); + this->m_mesh.mortarMeshId = 0; + this->m_mesh.nonmortarMeshId = 1; + + int nMortarElems = 4; + int nElemsXM = nMortarElems; + int nElemsYM = nMortarElems; + int nElemsZM = nMortarElems; + + int nNonmortarElems = 5; + int nElemsXS = nNonmortarElems; + int nElemsYS = nNonmortarElems; + int nElemsZS = nNonmortarElems; + + // mesh bounding box with 0.1 interpenetration gap + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.05; + + RealT x_min2 = 0.; + RealT y_min2 = 0.; + RealT z_min2 = 0.95; + RealT x_max2 = 1.; + RealT y_max2 = 1.; + RealT z_max2 = 2.; + + this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., + 0. ); + + // call tribol setup and update + tribol::TestControlParameters parameters; // struct does not hold info right now + parameters.penalty_ratio = false; + parameters.const_penalty = 0.75; + + int test_mesh_update_err = this->m_mesh.tribolSetupAndUpdate( + tribol::COMMON_PLANE, tribol::PENALTY, tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); + + EXPECT_EQ( test_mesh_update_err, 0 ); + + tribol::CouplingSchemeManager& couplingSchemeManager = tribol::CouplingSchemeManager::getInstance(); + + tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); + + // check mesh penalties + checkMeshPenalties( couplingScheme, parameters.const_penalty, 1.E-8, "constant" ); + + // check the pressures + RealT gap = z_min2 - z_max1; + RealT pressure = tribol::ComputePenaltyStiffnessPerArea( parameters.const_penalty, parameters.const_penalty ) * gap; + checkPressures( couplingScheme, pressure, 1.E-8 ); + checkForceSense( couplingScheme ); + + tribol::finalize(); } TEST_F( CommonPlaneTest, element_penalty_check ) { - this->m_mesh.mortarMeshId = 0; - this->m_mesh.nonmortarMeshId = 1; - - int nMortarElems = 4; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 5; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - // mesh bounding box with 0.1 interpenetration gap - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.05; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 0.95; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - // compute element thickness for each block - RealT element_thickness1 = (z_max1 - z_min1) / nElemsZM; - RealT element_thickness2 = (z_max2 - z_min2) / nElemsZS; - - this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0. ); - - RealT dt = 1.e-3; - RealT bulk_mod1 = 1.0; // something simple - RealT bulk_mod2 = 1.0; - RealT velX1 = 0.; - RealT velY1 = 0.; - RealT velZ1 = 0.; - RealT velX2 = 0.; - RealT velY2 = 0.; - RealT velZ2 = 0.; - - this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); - this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, -velZ2 ); - - // allocate and set element thickness and bulk modulus - this->m_mesh.allocateAndSetElementThickness( m_mesh.mortarMeshId, element_thickness1 ); - this->m_mesh.allocateAndSetBulkModulus( m_mesh.mortarMeshId, bulk_mod1 ); - this->m_mesh.allocateAndSetElementThickness( m_mesh.nonmortarMeshId, element_thickness2 ); - this->m_mesh.allocateAndSetBulkModulus( m_mesh.nonmortarMeshId, bulk_mod2 ); - - // call tribol setup and update - tribol::TestControlParameters parameters; - parameters.penalty_ratio = true; - parameters.const_penalty = 0.75; - parameters.dt = dt; - - int test_mesh_update_err = - this->m_mesh.tribolSetupAndUpdate( tribol::COMMON_PLANE, tribol::PENALTY, - tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); - - EXPECT_EQ( test_mesh_update_err, 0 ); - - tribol::CouplingSchemeManager& couplingSchemeManager = - tribol::CouplingSchemeManager::getInstance(); - - tribol::CouplingScheme* couplingScheme = - &couplingSchemeManager.at( 0 ); - - checkMeshPenalties( couplingScheme, parameters.const_penalty, 1.E-8, "face" ); - - ///////////////////////// - // check the pressures // - ///////////////////////// - RealT gap = z_min2 - z_max1; - - // this uses the same face-springs-in-parallel calculation as the common plane + penalty method: K1/t_1 * K2/t_2 / (K1/t_1 + K2/t_2) - RealT pressure = (bulk_mod1 / element_thickness1 * bulk_mod2 / element_thickness2) / - (bulk_mod1 / element_thickness1 + bulk_mod2 / element_thickness2) * gap; - checkPressures( couplingScheme, pressure, 1.E-8 ); - checkForceSense( couplingScheme ); - - tribol::finalize(); + this->m_mesh.mortarMeshId = 0; + this->m_mesh.nonmortarMeshId = 1; + + int nMortarElems = 4; + int nElemsXM = nMortarElems; + int nElemsYM = nMortarElems; + int nElemsZM = nMortarElems; + + int nNonmortarElems = 5; + int nElemsXS = nNonmortarElems; + int nElemsYS = nNonmortarElems; + int nElemsZS = nNonmortarElems; + + // mesh bounding box with 0.1 interpenetration gap + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.05; + + RealT x_min2 = 0.; + RealT y_min2 = 0.; + RealT z_min2 = 0.95; + RealT x_max2 = 1.; + RealT y_max2 = 1.; + RealT z_max2 = 2.; + + // compute element thickness for each block + RealT element_thickness1 = ( z_max1 - z_min1 ) / nElemsZM; + RealT element_thickness2 = ( z_max2 - z_min2 ) / nElemsZS; + + this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., + 0. ); + + RealT dt = 1.e-3; + RealT bulk_mod1 = 1.0; // something simple + RealT bulk_mod2 = 1.0; + RealT velX1 = 0.; + RealT velY1 = 0.; + RealT velZ1 = 0.; + RealT velX2 = 0.; + RealT velY2 = 0.; + RealT velZ2 = 0.; + + this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); + this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, -velZ2 ); + + // allocate and set element thickness and bulk modulus + this->m_mesh.allocateAndSetElementThickness( m_mesh.mortarMeshId, element_thickness1 ); + this->m_mesh.allocateAndSetBulkModulus( m_mesh.mortarMeshId, bulk_mod1 ); + this->m_mesh.allocateAndSetElementThickness( m_mesh.nonmortarMeshId, element_thickness2 ); + this->m_mesh.allocateAndSetBulkModulus( m_mesh.nonmortarMeshId, bulk_mod2 ); + + // call tribol setup and update + tribol::TestControlParameters parameters; + parameters.penalty_ratio = true; + parameters.const_penalty = 0.75; + parameters.dt = dt; + + int test_mesh_update_err = this->m_mesh.tribolSetupAndUpdate( + tribol::COMMON_PLANE, tribol::PENALTY, tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); + + EXPECT_EQ( test_mesh_update_err, 0 ); + + tribol::CouplingSchemeManager& couplingSchemeManager = tribol::CouplingSchemeManager::getInstance(); + + tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); + + checkMeshPenalties( couplingScheme, parameters.const_penalty, 1.E-8, "face" ); + + ///////////////////////// + // check the pressures // + ///////////////////////// + RealT gap = z_min2 - z_max1; + + // this uses the same face-springs-in-parallel calculation as the common plane + penalty method: K1/t_1 * K2/t_2 / + // (K1/t_1 + K2/t_2) + RealT pressure = ( bulk_mod1 / element_thickness1 * bulk_mod2 / element_thickness2 ) / + ( bulk_mod1 / element_thickness1 + bulk_mod2 / element_thickness2 ) * gap; + checkPressures( couplingScheme, pressure, 1.E-8 ); + checkForceSense( couplingScheme ); + + tribol::finalize(); } TEST_F( CommonPlaneTest, tied_contact_check ) { - this->m_mesh.mortarMeshId = 0; - this->m_mesh.nonmortarMeshId = 1; - - int nMortarElems = 4; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 5; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - // mesh bounding box with 0.1 separation gap - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 1.01; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0. ); - - // call tribol setup and update - tribol::TestControlParameters parameters; // struct does not hold info right now - parameters.penalty_ratio = false; - parameters.const_penalty = 0.25; - - int test_mesh_update_err = - this->m_mesh.tribolSetupAndUpdate( tribol::COMMON_PLANE, tribol::PENALTY, - tribol::FRICTIONLESS, tribol::TIED_NORMAL, false, parameters ); - - EXPECT_EQ( test_mesh_update_err, 0 ); - - tribol::CouplingSchemeManager& couplingSchemeManager = - tribol::CouplingSchemeManager::getInstance(); - - tribol::CouplingScheme* couplingScheme = - &couplingSchemeManager.at( 0 ); - - // check the pressures - RealT gap = z_min2 - z_max1; - RealT pressure = tribol::ComputePenaltyStiffnessPerArea( parameters.const_penalty, parameters.const_penalty ) * gap; - checkPressures( couplingScheme, pressure, 1.E-8 ); - checkForceSense( couplingScheme, true ); - - tribol::finalize(); + this->m_mesh.mortarMeshId = 0; + this->m_mesh.nonmortarMeshId = 1; + + int nMortarElems = 4; + int nElemsXM = nMortarElems; + int nElemsYM = nMortarElems; + int nElemsZM = nMortarElems; + + int nNonmortarElems = 5; + int nElemsXS = nNonmortarElems; + int nElemsYS = nNonmortarElems; + int nElemsZS = nNonmortarElems; + + // mesh bounding box with 0.1 separation gap + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.; + + RealT x_min2 = 0.; + RealT y_min2 = 0.; + RealT z_min2 = 1.01; + RealT x_max2 = 1.; + RealT y_max2 = 1.; + RealT z_max2 = 2.; + + this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., + 0. ); + + // call tribol setup and update + tribol::TestControlParameters parameters; // struct does not hold info right now + parameters.penalty_ratio = false; + parameters.const_penalty = 0.25; + + int test_mesh_update_err = this->m_mesh.tribolSetupAndUpdate( + tribol::COMMON_PLANE, tribol::PENALTY, tribol::FRICTIONLESS, tribol::TIED_NORMAL, false, parameters ); + + EXPECT_EQ( test_mesh_update_err, 0 ); + + tribol::CouplingSchemeManager& couplingSchemeManager = tribol::CouplingSchemeManager::getInstance(); + + tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); + + // check the pressures + RealT gap = z_min2 - z_max1; + RealT pressure = tribol::ComputePenaltyStiffnessPerArea( parameters.const_penalty, parameters.const_penalty ) * gap; + checkPressures( couplingScheme, pressure, 1.E-8 ); + checkForceSense( couplingScheme, true ); + + tribol::finalize(); } -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); #ifdef TRIBOL_USE_UMPIRE umpire::ResourceManager::getInstance(); // initialize umpire's ResouceManager diff --git a/src/tests/tribol_comp_geom.cpp b/src/tests/tribol_comp_geom.cpp index f1678f06..15aa0d63 100644 --- a/src/tests/tribol_comp_geom.cpp +++ b/src/tests/tribol_comp_geom.cpp @@ -26,7 +26,7 @@ #include "gtest/gtest.h" // c++ includes -#include // std::abs, std::cos, std::sin +#include // std::abs, std::cos, std::sin #include #include #include @@ -35,1147 +35,1070 @@ using RealT = tribol::RealT; /*! - * Test fixture class with some setup necessary to test - * the computational geometry. This test does not have a specific - * check that will make it pass or fail (yet), instead this is - * simply used to drive the computational geometry engine + * Test fixture class with some setup necessary to test + * the computational geometry. This test does not have a specific + * check that will make it pass or fail (yet), instead this is + * simply used to drive the computational geometry engine * and to interrogate SLIC output printed to screen */ -class CompGeomTest : public ::testing::Test +class CompGeomTest : public ::testing::Test { + public: + tribol::TestMesh m_mesh; + + protected: + void SetUp() override {} + + void TearDown() override + { + // call clear() on mesh object to be safe + this->m_mesh.clear(); + } + + protected: +}; + +TEST_F( CompGeomTest, common_plane_check ) { - -public: + int nMortarElems = 3; + int nElemsXM = nMortarElems; + int nElemsYM = 3; + int nElemsZM = nMortarElems; - tribol::TestMesh m_mesh; + int nNonmortarElems = 3; + int nElemsXS = nNonmortarElems; + int nElemsYS = 3; + int nElemsZS = nNonmortarElems; -protected: + int userSpecifiedNumOverlaps = 25; - void SetUp() override - { - } + // mesh bounding box with 0.1 interpenetration gap + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.05; - void TearDown() override - { - // call clear() on mesh object to be safe - this->m_mesh.clear(); - } + RealT x_min2 = -0.1; + RealT y_min2 = 0.0001; + RealT z_min2 = 0.95; + RealT x_max2 = 1.1; + RealT y_max2 = 0.9999; + RealT z_max2 = 2.; -protected: + this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., + 0. ); -}; + // call tribol setup and update + tribol::TestControlParameters parameters; // struct does not hold info right now + parameters.penalty_ratio = false; + parameters.const_penalty = 1.0; -TEST_F( CompGeomTest, common_plane_check ) -{ - int nMortarElems = 3; - int nElemsXM = nMortarElems; - int nElemsYM = 3; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 3; - int nElemsXS = nNonmortarElems; - int nElemsYS = 3; - int nElemsZS = nNonmortarElems; - - int userSpecifiedNumOverlaps = 25; - - // mesh bounding box with 0.1 interpenetration gap - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.05; - - RealT x_min2 = -0.1; - RealT y_min2 = 0.0001; - RealT z_min2 = 0.95; - RealT x_max2 = 1.1; - RealT y_max2 = 0.9999; - RealT z_max2 = 2.; - - this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0. ); - - // call tribol setup and update - tribol::TestControlParameters parameters; // struct does not hold info right now - parameters.penalty_ratio = false; - parameters.const_penalty = 1.0; - - int test_mesh_update_err = - this->m_mesh.tribolSetupAndUpdate( tribol::COMMON_PLANE, tribol::PENALTY, - tribol::FRICTIONLESS, tribol::NO_CASE, true, parameters ); - - EXPECT_EQ( test_mesh_update_err, 0 ); - - tribol::CouplingSchemeManager& couplingSchemeManager = - tribol::CouplingSchemeManager::getInstance(); - - tribol::CouplingScheme* couplingScheme = - &couplingSchemeManager.at( 0 ); - - EXPECT_EQ( userSpecifiedNumOverlaps, couplingScheme->getNumActivePairs() ); - - tribol::finalize(); + int test_mesh_update_err = this->m_mesh.tribolSetupAndUpdate( + tribol::COMMON_PLANE, tribol::PENALTY, tribol::FRICTIONLESS, tribol::NO_CASE, true, parameters ); + + EXPECT_EQ( test_mesh_update_err, 0 ); + + tribol::CouplingSchemeManager& couplingSchemeManager = tribol::CouplingSchemeManager::getInstance(); + + tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); + + EXPECT_EQ( userSpecifiedNumOverlaps, couplingScheme->getNumActivePairs() ); + + tribol::finalize(); } TEST_F( CompGeomTest, single_mortar_check ) { - int nMortarElems = 4; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 5; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - int userSpecifiedNumOverlaps = 64; - - // mesh bounding box with 0.1 interpenetration gap - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.05; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 0.95; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0. ); - - // call tribol setup and update - tribol::TestControlParameters parameters; // struct does not hold info right now - - int test_mesh_update_err = - this->m_mesh.tribolSetupAndUpdate( tribol::SINGLE_MORTAR, tribol::LAGRANGE_MULTIPLIER, - tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); - - EXPECT_EQ( test_mesh_update_err, 0 ); - - tribol::CouplingSchemeManager& couplingSchemeManager = - tribol::CouplingSchemeManager::getInstance(); - - tribol::CouplingScheme* couplingScheme = - &couplingSchemeManager.at( 0 ); - - EXPECT_EQ( userSpecifiedNumOverlaps, couplingScheme->getNumActivePairs() ); - - tribol::finalize(); + int nMortarElems = 4; + int nElemsXM = nMortarElems; + int nElemsYM = nMortarElems; + int nElemsZM = nMortarElems; + + int nNonmortarElems = 5; + int nElemsXS = nNonmortarElems; + int nElemsYS = nNonmortarElems; + int nElemsZS = nNonmortarElems; + + int userSpecifiedNumOverlaps = 64; + + // mesh bounding box with 0.1 interpenetration gap + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.05; + + RealT x_min2 = 0.; + RealT y_min2 = 0.; + RealT z_min2 = 0.95; + RealT x_max2 = 1.; + RealT y_max2 = 1.; + RealT z_max2 = 2.; + + this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., + 0. ); + + // call tribol setup and update + tribol::TestControlParameters parameters; // struct does not hold info right now + + int test_mesh_update_err = this->m_mesh.tribolSetupAndUpdate( + tribol::SINGLE_MORTAR, tribol::LAGRANGE_MULTIPLIER, tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); + + EXPECT_EQ( test_mesh_update_err, 0 ); + + tribol::CouplingSchemeManager& couplingSchemeManager = tribol::CouplingSchemeManager::getInstance(); + + tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); + + EXPECT_EQ( userSpecifiedNumOverlaps, couplingScheme->getNumActivePairs() ); + + tribol::finalize(); } TEST_F( CompGeomTest, poly_area_centroid_1 ) { - // This test checks the area centroid calculation - // vs. the vertex average centroid calculation for a - // rectangular quadrilateral. The expectation is that - // the results are the same - constexpr int dim = 3; - constexpr int numVerts = 4; - RealT x[ dim * numVerts ]; - - for (int i=0; igetNumActivePairs() ); - + // this is a configuration from testing that is/was producing an overlap for + // non-overlapping edges, which in turn produced negative basis function + // evaluations + constexpr int dim = 2; + constexpr int numVerts = 2; + RealT xy1[dim * numVerts]; + RealT xy2[dim * numVerts]; + + // this geometry has two faces that have "passed through" one another, but + // don't have a positive area of overlap. + xy1[0] = 0.324552; + xy1[1] = 0.625596; + xy1[2] = 0.16206; + xy1[3] = 0.722646; + + xy2[0] = 4.59227e-17; + xy2[1] = 0.752178; + xy2[2] = 0.161705; + xy2[3] = 0.72276; + + RealT x1[numVerts]; + RealT y1[numVerts]; + RealT x2[numVerts]; + RealT y2[numVerts]; + + for ( int i = 0; i < numVerts; ++i ) { + x1[i] = xy1[i * dim]; + y1[i] = xy1[i * dim + 1]; + x2[i] = xy2[i * dim]; + y2[i] = xy2[i * dim + 1]; + } + + tribol::IndexT conn1[2] = { 0, 1 }; + tribol::IndexT conn2[2] = { 0, 1 }; + + tribol::registerMesh( 0, 1, 2, &conn1[0], (int)( tribol::LINEAR_EDGE ), &x1[0], &y1[0], nullptr, + tribol::MemorySpace::Host ); + tribol::registerMesh( 1, 1, 2, &conn2[0], (int)( tribol::LINEAR_EDGE ), &x2[0], &y2[0], nullptr, + tribol::MemorySpace::Host ); + + RealT fx1[2] = { 0., 0. }; + RealT fy1[2] = { 0., 0. }; + RealT fx2[2] = { 0., 0. }; + RealT fy2[2] = { 0., 0. }; + + tribol::registerNodalResponse( 0, &fx1[0], &fy1[0], nullptr ); + tribol::registerNodalResponse( 1, &fx2[0], &fy2[0], nullptr ); + + tribol::setKinematicConstantPenalty( 0, 1. ); + tribol::setKinematicConstantPenalty( 1, 1. ); + + tribol::registerCouplingScheme( 0, 0, 1, tribol::SURFACE_TO_SURFACE, tribol::NO_CASE, tribol::COMMON_PLANE, + tribol::FRICTIONLESS, tribol::PENALTY, tribol::BINNING_GRID, + tribol::ExecutionMode::Sequential ); + + tribol::setPenaltyOptions( 0, tribol::KINEMATIC, tribol::KINEMATIC_CONSTANT ); + tribol::setContactAreaFrac( 0, 1.e-4 ); + + RealT dt = 1.; + int update_err = tribol::update( 1, 1., dt ); + + EXPECT_EQ( update_err, 0 ); + + tribol::CouplingSchemeManager& couplingSchemeManager = tribol::CouplingSchemeManager::getInstance(); + + tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); + + EXPECT_EQ( 0, couplingScheme->getNumActivePairs() ); } TEST_F( CompGeomTest, coincident_vertices_full_overlap ) { - constexpr int dim = 2; - constexpr int numVerts = 2; - RealT xy1[dim*numVerts]; - RealT xy2[dim*numVerts]; - - // this geometry is in contact with coincident vertices when - // projected onto the common plane. - xy1[0] = 1.0; - xy1[1] = 0.0; - xy1[2] = 0.0; - xy1[3] = 0.0; - - xy2[0] = 1.e-12; - xy2[1] = -0.1; - xy2[2] = 0.999999; - xy2[3] = -0.1; - - RealT x1[numVerts]; - RealT y1[numVerts]; - RealT x2[numVerts]; - RealT y2[numVerts]; - - for (int i=0; igetNumActivePairs() ); - - auto& plane = couplingScheme->getContactPlane(0); - RealT diff = (xy2[2]-xy2[0]) - plane.m_area; - EXPECT_LT(diff, 1.e-10); + constexpr int dim = 2; + constexpr int numVerts = 2; + RealT xy1[dim * numVerts]; + RealT xy2[dim * numVerts]; + + // this geometry is in contact with coincident vertices when + // projected onto the common plane. + xy1[0] = 1.0; + xy1[1] = 0.0; + xy1[2] = 0.0; + xy1[3] = 0.0; + + xy2[0] = 1.e-12; + xy2[1] = -0.1; + xy2[2] = 0.999999; + xy2[3] = -0.1; + + RealT x1[numVerts]; + RealT y1[numVerts]; + RealT x2[numVerts]; + RealT y2[numVerts]; + + for ( int i = 0; i < numVerts; ++i ) { + x1[i] = xy1[i * dim]; + y1[i] = xy1[i * dim + 1]; + x2[i] = xy2[i * dim]; + y2[i] = xy2[i * dim + 1]; + } + + tribol::IndexT conn1[2] = { 0, 1 }; + tribol::IndexT conn2[2] = { 0, 1 }; + + tribol::registerMesh( 0, 1, 2, &conn1[0], (int)( tribol::LINEAR_EDGE ), &x1[0], &y1[0], nullptr, + tribol::MemorySpace::Host ); + tribol::registerMesh( 1, 1, 2, &conn2[0], (int)( tribol::LINEAR_EDGE ), &x2[0], &y2[0], nullptr, + tribol::MemorySpace::Host ); + + RealT fx1[2] = { 0., 0. }; + RealT fy1[2] = { 0., 0. }; + RealT fx2[2] = { 0., 0. }; + RealT fy2[2] = { 0., 0. }; + + tribol::registerNodalResponse( 0, &fx1[0], &fy1[0], nullptr ); + tribol::registerNodalResponse( 1, &fx2[0], &fy2[0], nullptr ); + + tribol::setKinematicConstantPenalty( 0, 1. ); + tribol::setKinematicConstantPenalty( 1, 1. ); + + tribol::registerCouplingScheme( 0, 0, 1, tribol::SURFACE_TO_SURFACE, tribol::NO_CASE, tribol::COMMON_PLANE, + tribol::FRICTIONLESS, tribol::PENALTY, tribol::BINNING_GRID, + tribol::ExecutionMode::Sequential ); + + tribol::setPenaltyOptions( 0, tribol::KINEMATIC, tribol::KINEMATIC_CONSTANT ); + tribol::setContactAreaFrac( 0, 1.e-4 ); + + RealT dt = 1.; + int update_err = tribol::update( 1, 1., dt ); + EXPECT_EQ( update_err, 0 ); + + tribol::CouplingSchemeManager& couplingSchemeManager = tribol::CouplingSchemeManager::getInstance(); + + tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); + + EXPECT_EQ( 1, couplingScheme->getNumActivePairs() ); + + auto& plane = couplingScheme->getContactPlane( 0 ); + RealT diff = ( xy2[2] - xy2[0] ) - plane.m_area; + EXPECT_LT( diff, 1.e-10 ); } TEST_F( CompGeomTest, coincident_vertex_no_overlap ) { - constexpr int dim = 2; - constexpr int numVerts = 2; - RealT xy1[dim*numVerts]; - RealT xy2[dim*numVerts]; - - // this geometry has a pair of 'nearly' coincident vertices that should - // produce NO positive area of overlap. Note: the actual overlap is - // less than the contact area fraction set by tribol::setContactAreaFrac() below. - xy1[0] = 1.0; - xy1[1] = 0.0; - xy1[2] = 0.0; - xy1[3] = 0.0; - - xy2[0] = -1.; - xy2[1] = -0.1; - xy2[2] = 1.e-8; - xy2[3] = -0.1; - - RealT x1[numVerts]; - RealT y1[numVerts]; - RealT x2[numVerts]; - RealT y2[numVerts]; - - for (int i=0; igetNumActivePairs() ); - + constexpr int dim = 2; + constexpr int numVerts = 2; + RealT xy1[dim * numVerts]; + RealT xy2[dim * numVerts]; + + // this geometry has a pair of 'nearly' coincident vertices that should + // produce NO positive area of overlap. Note: the actual overlap is + // less than the contact area fraction set by tribol::setContactAreaFrac() below. + xy1[0] = 1.0; + xy1[1] = 0.0; + xy1[2] = 0.0; + xy1[3] = 0.0; + + xy2[0] = -1.; + xy2[1] = -0.1; + xy2[2] = 1.e-8; + xy2[3] = -0.1; + + RealT x1[numVerts]; + RealT y1[numVerts]; + RealT x2[numVerts]; + RealT y2[numVerts]; + + for ( int i = 0; i < numVerts; ++i ) { + x1[i] = xy1[i * dim]; + y1[i] = xy1[i * dim + 1]; + x2[i] = xy2[i * dim]; + y2[i] = xy2[i * dim + 1]; + } + + tribol::IndexT conn1[2] = { 0, 1 }; + tribol::IndexT conn2[2] = { 0, 1 }; + + tribol::registerMesh( 0, 1, 2, &conn1[0], (int)( tribol::LINEAR_EDGE ), &x1[0], &y1[0], nullptr, + tribol::MemorySpace::Host ); + tribol::registerMesh( 1, 1, 2, &conn2[0], (int)( tribol::LINEAR_EDGE ), &x2[0], &y2[0], nullptr, + tribol::MemorySpace::Host ); + + RealT fx1[2] = { 0., 0. }; + RealT fy1[2] = { 0., 0. }; + RealT fx2[2] = { 0., 0. }; + RealT fy2[2] = { 0., 0. }; + + tribol::registerNodalResponse( 0, &fx1[0], &fy1[0], nullptr ); + tribol::registerNodalResponse( 1, &fx2[0], &fy2[0], nullptr ); + + tribol::setKinematicConstantPenalty( 0, 1. ); + tribol::setKinematicConstantPenalty( 1, 1. ); + + tribol::registerCouplingScheme( 0, 0, 1, tribol::SURFACE_TO_SURFACE, tribol::NO_CASE, tribol::COMMON_PLANE, + tribol::FRICTIONLESS, tribol::PENALTY, tribol::BINNING_GRID, + tribol::ExecutionMode::Sequential ); + + tribol::setPenaltyOptions( 0, tribol::KINEMATIC, tribol::KINEMATIC_CONSTANT ); + tribol::setContactAreaFrac( 0, 1.e-4 ); + + RealT dt = 1.; + int update_err = tribol::update( 1, 1., dt ); + + EXPECT_EQ( update_err, 0 ); + + tribol::CouplingSchemeManager& couplingSchemeManager = tribol::CouplingSchemeManager::getInstance(); + + tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); + + EXPECT_EQ( 0, couplingScheme->getNumActivePairs() ); } TEST_F( CompGeomTest, nearly_coincident_vertex_pos_overlap ) { - constexpr int dim = 2; - constexpr int numVerts = 2; - RealT xy1[dim*numVerts]; - RealT xy2[dim*numVerts]; - - // This geometry has a face-pair with a set of 'nearly' coincident vertices, but a - // positive area of overlap that is greater than the contact area frac used in - // computing an overlap area threshold. As a result, this face-pair should be - // in contact - xy1[0] = 1.0; - xy1[1] = 0.0; - xy1[2] = 0.0; - xy1[3] = 0.0; - - xy2[0] = -1.; - xy2[1] = -0.1; - xy2[2] = 1.e-4; - xy2[3] = -0.1; - - RealT x1[numVerts]; - RealT y1[numVerts]; - RealT x2[numVerts]; - RealT y2[numVerts]; - - for (int i=0; igetNumActivePairs() ); - - auto& plane = couplingScheme->getContactPlane(0); - RealT diff = (xy2[2]-xy1[2]) - plane.m_area; - EXPECT_LT(diff, 1.e-10); + constexpr int dim = 2; + constexpr int numVerts = 2; + RealT xy1[dim * numVerts]; + RealT xy2[dim * numVerts]; + + // This geometry has a face-pair with a set of 'nearly' coincident vertices, but a + // positive area of overlap that is greater than the contact area frac used in + // computing an overlap area threshold. As a result, this face-pair should be + // in contact + xy1[0] = 1.0; + xy1[1] = 0.0; + xy1[2] = 0.0; + xy1[3] = 0.0; + + xy2[0] = -1.; + xy2[1] = -0.1; + xy2[2] = 1.e-4; + xy2[3] = -0.1; + + RealT x1[numVerts]; + RealT y1[numVerts]; + RealT x2[numVerts]; + RealT y2[numVerts]; + + for ( int i = 0; i < numVerts; ++i ) { + x1[i] = xy1[i * dim]; + y1[i] = xy1[i * dim + 1]; + x2[i] = xy2[i * dim]; + y2[i] = xy2[i * dim + 1]; + } + + tribol::IndexT conn1[2] = { 0, 1 }; + tribol::IndexT conn2[2] = { 0, 1 }; + + tribol::registerMesh( 0, 1, 2, &conn1[0], (int)( tribol::LINEAR_EDGE ), &x1[0], &y1[0], nullptr, + tribol::MemorySpace::Host ); + tribol::registerMesh( 1, 1, 2, &conn2[0], (int)( tribol::LINEAR_EDGE ), &x2[0], &y2[0], nullptr, + tribol::MemorySpace::Host ); + + RealT fx1[2] = { 0., 0. }; + RealT fy1[2] = { 0., 0. }; + RealT fx2[2] = { 0., 0. }; + RealT fy2[2] = { 0., 0. }; + + tribol::registerNodalResponse( 0, &fx1[0], &fy1[0], nullptr ); + tribol::registerNodalResponse( 1, &fx2[0], &fy2[0], nullptr ); + + tribol::setKinematicConstantPenalty( 0, 1. ); + tribol::setKinematicConstantPenalty( 1, 1. ); + + tribol::registerCouplingScheme( 0, 0, 1, tribol::SURFACE_TO_SURFACE, tribol::NO_CASE, tribol::COMMON_PLANE, + tribol::FRICTIONLESS, tribol::PENALTY, tribol::BINNING_GRID, + tribol::ExecutionMode::Sequential ); + + tribol::setPenaltyOptions( 0, tribol::KINEMATIC, tribol::KINEMATIC_CONSTANT ); + tribol::setContactAreaFrac( 0, 1.e-12 ); + + RealT dt = 1.; + int update_err = tribol::update( 1, 1., dt ); + + EXPECT_EQ( update_err, 0 ); + + tribol::CouplingSchemeManager& couplingSchemeManager = tribol::CouplingSchemeManager::getInstance(); + + tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); + + EXPECT_EQ( 1, couplingScheme->getNumActivePairs() ); + + auto& plane = couplingScheme->getContactPlane( 0 ); + RealT diff = ( xy2[2] - xy1[2] ) - plane.m_area; + EXPECT_LT( diff, 1.e-10 ); } TEST_F( CompGeomTest, 2d_projections_1 ) { - constexpr int dim = 2; - constexpr int numVerts = 2; - RealT xy1[dim*numVerts]; - RealT xy2[dim*numVerts]; - - // this geometry should be in contact, testing projections - xy1[0] = 0.75; - xy1[1] = 0.; - xy1[2] = 0.727322; - xy1[3] = 0.183039; - - xy2[0] = 0.72705; - xy2[1] = 0.182971; - xy2[2] = 0.75; - xy2[3] = 0.; - - // compute face normal - RealT faceNormal1[dim]; - RealT faceNormal2[dim]; - - RealT lambdaX1 = xy1[2]-xy1[0]; - RealT lambdaY1 = xy1[3]-xy1[1]; - - faceNormal1[0] = lambdaY1; - faceNormal1[1] = -lambdaX1; - - RealT lambdaX2 = xy2[2]-xy2[0]; - RealT lambdaY2 = xy2[3]-xy2[1]; - - faceNormal2[0] = lambdaY2; - faceNormal2[1] = -lambdaX2; - - RealT cxf1[3] = {0., 0., 0.}; - RealT cxf2[3] = {0., 0., 0.}; - - tribol::VertexAvgCentroid( xy1, dim, numVerts, cxf1[0], cxf1[1], cxf1[2] ); - tribol::VertexAvgCentroid( xy2, dim, numVerts, cxf2[0], cxf2[1], cxf2[2] ); - - // average the vertex averaged centroids of each face to get a pretty good - // estimate of the common plane centroid - RealT cx[dim]; - cx[0] = 0.5*(cxf1[0] + cxf2[0]); - cx[1] = 0.5*(cxf1[1] + cxf2[1]); - - RealT cxProj1[3] = {0., 0., 0.}; - RealT cxProj2[3] = {0., 0., 0.}; - - tribol::ProjectPointToSegment( cx[0], cx[1], faceNormal1[0], faceNormal1[1], - cxf1[0], cxf1[1], cxProj1[0], cxProj1[1] ); - tribol::ProjectPointToSegment( cx[0], cx[1], faceNormal2[0], faceNormal2[1], - cxf2[0], cxf2[1], cxProj2[0], cxProj2[1] ); - - RealT diffx1 = std::abs(cxProj1[0] - 0.738595); - RealT diffy1 = std::abs(cxProj1[1] - 0.0915028); - RealT diffx2 = std::abs(cxProj2[0] - 0.738591); - RealT diffy2 = std::abs(cxProj2[1] - 0.0915022); - //EXPECT_LE(diffx1, 1.e-6); - //EXPECT_LE(diffy1, 1.e-6); - //EXPECT_LE(diffx2, 1.e-6); - //EXPECT_LE(diffy2, 1.e-6); - - RealT x1[numVerts]; - RealT y1[numVerts]; - RealT x2[numVerts]; - RealT y2[numVerts]; - - for (int i=0; igetNumActivePairs(), 0 ); - - delete [] fx; - delete [] fy; - delete [] fz; - delete [] vx; - delete [] vy; - delete [] vz; + // this test ensures that faces in a given face-pair with nearly co-directional + // normals is not actually included as a contact candidate + constexpr int numVerts = 4; + constexpr int numCells = 2; + constexpr int lengthNodalData = numCells * numVerts; + RealT element_thickness[numCells]; + RealT x[lengthNodalData]; + RealT y[lengthNodalData]; + RealT z[lengthNodalData]; + + for ( int i = 0; i < numCells; ++i ) { + element_thickness[i] = 1.0; + } + + // coordinates for face 1 + x[0] = 0.; + x[1] = 1.; + x[2] = 1.; + x[3] = 0.; + + y[0] = 0.; + y[1] = 0.; + y[2] = 1.; + y[3] = 1.; + + z[0] = 0.; + z[1] = 0.; + z[2] = 0.; + z[3] = 0.; + + // coordinates for face 2 + x[4] = 0.; + x[5] = 1.; + x[6] = 1.; + x[7] = 0.; + + y[4] = 0.; + y[5] = 0.; + y[6] = 1.; + y[7] = 1.; + + // amount of interpenetration in the z-direction + z[4] = -0.300001 * element_thickness[1]; + z[5] = -0.300001 * element_thickness[1]; + z[6] = -0.300001 * element_thickness[1]; + z[7] = -0.300001 * element_thickness[1]; + + // register contact mesh + tribol::IndexT mesh_id = 0; + tribol::IndexT conn[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; // hard coded for a two face problem + tribol::registerMesh( mesh_id, numCells, lengthNodalData, &conn[0], (int)( tribol::LINEAR_QUAD ), &x[0], &y[0], &z[0], + tribol::MemorySpace::Host ); + + RealT* fx; + RealT* fy; + RealT* fz; + tribol::allocRealArray( &fx, lengthNodalData, 0. ); + tribol::allocRealArray( &fy, lengthNodalData, 0. ); + tribol::allocRealArray( &fz, lengthNodalData, 0. ); + + tribol::registerNodalResponse( mesh_id, fx, fy, fz ); + + RealT* vx; + RealT* vy; + RealT* vz; + RealT vel0 = -1.e-15; + tribol::allocRealArray( &vx, lengthNodalData, 0. ); + tribol::allocRealArray( &vy, lengthNodalData, 0. ); + tribol::allocRealArray( &vz, lengthNodalData, vel0 ); + + // set second face to impacting velocity + RealT vel2 = 1.e-15; + vz[4] = vel2; + vz[5] = vel2; + vz[6] = vel2; + vz[7] = vel2; + + tribol::registerNodalVelocities( mesh_id, vx, vy, vz ); + + RealT bulk_mod[2] = { 1.0, 1.0 }; + tribol::registerRealElementField( mesh_id, tribol::BULK_MODULUS, bulk_mod ); + tribol::registerRealElementField( mesh_id, tribol::ELEMENT_THICKNESS, element_thickness ); + + int csIndex = 0; + tribol::registerCouplingScheme( csIndex, mesh_id, mesh_id, tribol::SURFACE_TO_SURFACE, tribol::AUTO, + tribol::COMMON_PLANE, tribol::FRICTIONLESS, tribol::PENALTY, + tribol::BINNING_CARTESIAN_PRODUCT, tribol::ExecutionMode::Sequential ); + + tribol::enableTimestepVote( csIndex, true ); + + tribol::setLoggingLevel( csIndex, tribol::TRIBOL_DEBUG ); + + tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC, tribol::KINEMATIC_ELEMENT, tribol::NO_RATE_PENALTY ); + + RealT dt = 1.0; + int err = tribol::update( 1, 1., dt ); + + EXPECT_EQ( err, 0 ); + EXPECT_EQ( dt, 1.0 ); + + tribol::CouplingSchemeManager& couplingSchemeManager = tribol::CouplingSchemeManager::getInstance(); + + tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); + + EXPECT_EQ( couplingScheme->getNumActivePairs(), 0 ); + + delete[] fx; + delete[] fy; + delete[] fz; + delete[] vx; + delete[] vy; + delete[] vz; } TEST_F( CompGeomTest, auto_contact_lt_max_interpen ) { - // This test uses auto-contact and checks that the face-pair - // is included as a conatct candidate, and is in fact in contact - // when the interpenetration is less than the maximum allowable - // for auto contact - constexpr int numVerts = 4; - constexpr int numCells = 2; - constexpr int lengthNodalData = numCells * numVerts; - RealT element_thickness[numCells]; - RealT x[lengthNodalData]; - RealT y[lengthNodalData]; - RealT z[lengthNodalData]; - - for (int i=0; igetNumActivePairs(), 1 ); - - delete [] fx; - delete [] fy; - delete [] fz; - delete [] vx; - delete [] vy; - delete [] vz; + // This test uses auto-contact and checks that the face-pair + // is included as a conatct candidate, and is in fact in contact + // when the interpenetration is less than the maximum allowable + // for auto contact + constexpr int numVerts = 4; + constexpr int numCells = 2; + constexpr int lengthNodalData = numCells * numVerts; + RealT element_thickness[numCells]; + RealT x[lengthNodalData]; + RealT y[lengthNodalData]; + RealT z[lengthNodalData]; + + for ( int i = 0; i < numCells; ++i ) { + element_thickness[i] = 1.0; + } + + // coordinates for face 1 + x[0] = 0.; + x[1] = 1.; + x[2] = 1.; + x[3] = 0.; + + y[0] = 0.; + y[1] = 0.; + y[2] = 1.; + y[3] = 1.; + + z[0] = 0.; + z[1] = 0.; + z[2] = 0.; + z[3] = 0.; + + // coordinates for face 2 + x[4] = 0.; + x[5] = 1.; + x[6] = 1.; + x[7] = 0.; + + y[4] = 0.; + y[5] = 0.; + y[6] = 1.; + y[7] = 1.; + + // amount of interpenetration in the z-direction + RealT max_interpen_frac = 1.0; + RealT test_ratio = 0.90; // fraction of max interpen frac used for this test + z[4] = -test_ratio * max_interpen_frac * element_thickness[1]; + z[5] = -test_ratio * max_interpen_frac * element_thickness[1]; + z[6] = -test_ratio * max_interpen_frac * element_thickness[1]; + z[7] = -test_ratio * max_interpen_frac * element_thickness[1]; + + // register contact mesh + tribol::IndexT mesh_id = 0; + tribol::IndexT conn[8] = { 0, 1, 2, 3, 4, 7, 6, 5 }; // hard coded for a two face problem + tribol::registerMesh( mesh_id, numCells, lengthNodalData, &conn[0], (int)( tribol::LINEAR_QUAD ), &x[0], &y[0], &z[0], + tribol::MemorySpace::Host ); + + RealT* fx; + RealT* fy; + RealT* fz; + tribol::allocRealArray( &fx, lengthNodalData, 0. ); + tribol::allocRealArray( &fy, lengthNodalData, 0. ); + tribol::allocRealArray( &fz, lengthNodalData, 0. ); + + tribol::registerNodalResponse( mesh_id, fx, fy, fz ); + + RealT* vx; + RealT* vy; + RealT* vz; + RealT vel0 = -1.; + tribol::allocRealArray( &vx, lengthNodalData, 0. ); + tribol::allocRealArray( &vy, lengthNodalData, 0. ); + tribol::allocRealArray( &vz, lengthNodalData, vel0 ); + + // set second face to impacting velocity + RealT vel2 = 1.0; + vz[4] = vel2; + vz[5] = vel2; + vz[6] = vel2; + vz[7] = vel2; + + tribol::registerNodalVelocities( mesh_id, vx, vy, vz ); + + // register element thickness for use with auto contact + tribol::registerRealElementField( mesh_id, tribol::ELEMENT_THICKNESS, &element_thickness[0] ); + + int csIndex = 0; + tribol::registerCouplingScheme( csIndex, mesh_id, mesh_id, tribol::SURFACE_TO_SURFACE, tribol::AUTO, + tribol::COMMON_PLANE, tribol::FRICTIONLESS, tribol::PENALTY, + tribol::BINNING_CARTESIAN_PRODUCT, tribol::ExecutionMode::Sequential ); + + tribol::setAutoContactPenScale( csIndex, max_interpen_frac ); + + tribol::enableTimestepVote( csIndex, true ); + + tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC, tribol::KINEMATIC_CONSTANT, tribol::NO_RATE_PENALTY ); + + tribol::setKinematicConstantPenalty( mesh_id, 1.0 ); + + RealT dt = 1.0; + int err = tribol::update( 1, 1., dt ); + + EXPECT_EQ( err, 0 ); + + tribol::CouplingSchemeManager& couplingSchemeManager = tribol::CouplingSchemeManager::getInstance(); + + tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); + + EXPECT_EQ( couplingScheme->getNumActivePairs(), 1 ); + + delete[] fx; + delete[] fy; + delete[] fz; + delete[] vx; + delete[] vy; + delete[] vz; } TEST_F( CompGeomTest, auto_contact_gt_max_interpen ) { - // This test uses auto-contact and checks that the face-pair - // is included as a contact candidate, and is in fact in contact - // when the interpenetration is less than the maximum allowable - // for auto contact - constexpr int numVerts = 4; - constexpr int numCells = 2; - constexpr int lengthNodalData = numCells * numVerts; - RealT element_thickness[numCells]; - RealT x[lengthNodalData]; - RealT y[lengthNodalData]; - RealT z[lengthNodalData]; - - for (int i=0; igetNumActivePairs(), 0 ); - - delete [] fx; - delete [] fy; - delete [] fz; - delete [] vx; - delete [] vy; - delete [] vz; + // This test uses auto-contact and checks that the face-pair + // is included as a contact candidate, and is in fact in contact + // when the interpenetration is less than the maximum allowable + // for auto contact + constexpr int numVerts = 4; + constexpr int numCells = 2; + constexpr int lengthNodalData = numCells * numVerts; + RealT element_thickness[numCells]; + RealT x[lengthNodalData]; + RealT y[lengthNodalData]; + RealT z[lengthNodalData]; + + for ( int i = 0; i < numCells; ++i ) { + element_thickness[i] = 1.0; + } + + // coordinates for face 1 + x[0] = 0.; + x[1] = 1.; + x[2] = 1.; + x[3] = 0.; + + y[0] = 0.; + y[1] = 0.; + y[2] = 1.; + y[3] = 1.; + + z[0] = 0.; + z[1] = 0.; + z[2] = 0.; + z[3] = 0.; + + // coordinates for face 2 + x[4] = 0.; + x[5] = 1.; + x[6] = 1.; + x[7] = 0.; + + y[4] = 0.; + y[5] = 0.; + y[6] = 1.; + y[7] = 1.; + + // amount of interpenetration in the z-direction + RealT max_interpen_frac = 1.0; + RealT test_ratio = 1.01; // fraction of max interpen frac used for this test + z[4] = -test_ratio * max_interpen_frac * element_thickness[1]; + z[5] = -test_ratio * max_interpen_frac * element_thickness[1]; + z[6] = -test_ratio * max_interpen_frac * element_thickness[1]; + z[7] = -test_ratio * max_interpen_frac * element_thickness[1]; + + // register contact mesh + tribol::IndexT mesh_id = 0; + tribol::IndexT conn[8] = { 0, 1, 2, 3, 4, 7, 6, 5 }; // hard coded for a two face problem + tribol::registerMesh( mesh_id, numCells, lengthNodalData, &conn[0], (int)( tribol::LINEAR_QUAD ), &x[0], &y[0], &z[0], + tribol::MemorySpace::Host ); + + RealT* fx; + RealT* fy; + RealT* fz; + tribol::allocRealArray( &fx, lengthNodalData, 0. ); + tribol::allocRealArray( &fy, lengthNodalData, 0. ); + tribol::allocRealArray( &fz, lengthNodalData, 0. ); + + tribol::registerNodalResponse( mesh_id, fx, fy, fz ); + + RealT* vx; + RealT* vy; + RealT* vz; + RealT vel0 = -1.; + tribol::allocRealArray( &vx, lengthNodalData, 0. ); + tribol::allocRealArray( &vy, lengthNodalData, 0. ); + tribol::allocRealArray( &vz, lengthNodalData, vel0 ); + + // set second face to impacting velocity + RealT vel2 = 1.0; + vz[4] = vel2; + vz[5] = vel2; + vz[6] = vel2; + vz[7] = vel2; + + tribol::registerNodalVelocities( mesh_id, vx, vy, vz ); + + // register element thickness for use with auto contact + tribol::registerRealElementField( mesh_id, tribol::ELEMENT_THICKNESS, &element_thickness[0] ); + + int csIndex = 0; + tribol::registerCouplingScheme( csIndex, mesh_id, mesh_id, tribol::SURFACE_TO_SURFACE, tribol::AUTO, + tribol::COMMON_PLANE, tribol::FRICTIONLESS, tribol::PENALTY, + tribol::BINNING_CARTESIAN_PRODUCT, tribol::ExecutionMode::Sequential ); + + tribol::setAutoContactPenScale( csIndex, max_interpen_frac ); + + tribol::enableTimestepVote( csIndex, true ); + + tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC, tribol::KINEMATIC_CONSTANT, tribol::NO_RATE_PENALTY ); + + tribol::setKinematicConstantPenalty( mesh_id, 1.0 ); + + RealT dt = 1.0; + int err = tribol::update( 1, 1., dt ); + + EXPECT_EQ( err, 0 ); + + tribol::CouplingSchemeManager& couplingSchemeManager = tribol::CouplingSchemeManager::getInstance(); + + tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); + + EXPECT_EQ( couplingScheme->getNumActivePairs(), 0 ); + + delete[] fx; + delete[] fy; + delete[] fz; + delete[] vx; + delete[] vy; + delete[] vz; } -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); #ifdef TRIBOL_USE_UMPIRE - umpire::ResourceManager::getInstance(); // initialize umpire's ResouceManager + umpire::ResourceManager::getInstance(); // initialize umpire's ResouceManager #endif - axom::slic::SimpleLogger logger; // create & initialize logger, - tribol::SimpleMPIWrapper wrapper(argc, argv); // initialize and finalize MPI, when applicable + axom::slic::SimpleLogger logger; // create & initialize logger, + tribol::SimpleMPIWrapper wrapper( argc, argv ); // initialize and finalize MPI, when applicable result = RUN_ALL_TESTS(); diff --git a/src/tests/tribol_coupling_scheme.cpp b/src/tests/tribol_coupling_scheme.cpp index 8dd81df1..6cba7754 100644 --- a/src/tests/tribol_coupling_scheme.cpp +++ b/src/tests/tribol_coupling_scheme.cpp @@ -26,7 +26,7 @@ #include "gtest/gtest.h" // c++ includes -#include // std::abs, std::cos, std::sin +#include // std::abs, std::cos, std::sin #include #include #include @@ -36,1120 +36,925 @@ using RealT = tribol::RealT; /*! * Test fixture class to test valid coupling schemes. - * These tests only call the coupling scheme constructor - * and then the initialization scheme, which tests - * for valid coupling schemes. These tests are designed + * These tests only call the coupling scheme constructor + * and then the initialization scheme, which tests + * for valid coupling schemes. These tests are designed * to check for valid and invalid schemes. * - * NOTE: tests will need to be update/removed as more + * NOTE: tests will need to be update/removed as more * capabilities come online */ -class CouplingSchemeTest : public ::testing::Test -{ - -public: - - int m_numCells; - int m_lengthNodalData; - int * m_connectivity {nullptr}; - int m_elementType; - RealT* m_x {nullptr}; - RealT* m_y {nullptr}; - RealT* m_z {nullptr}; - RealT* m_fx {nullptr}; - RealT* m_fy {nullptr}; - RealT* m_fz {nullptr}; - -protected: - - void SetUp() override - { - } - - void TearDown() override - { - clear(); - } - - void registerDummy2DMesh( int mesh_id, int numCells = 1, bool set_response = true ) - { - // Single element meshes are usesd in these tests out of - // simplicity. - m_numCells = (numCells >= 1) ? 1 : 0; - m_lengthNodalData = 4; // Quad 4 - m_elementType = (int)(tribol::LINEAR_EDGE); // 1D edge for 2D mesh - - if (m_numCells > 0) - { - - m_connectivity = new int[ m_lengthNodalData ]; - m_x = new RealT[ m_lengthNodalData ]; - m_y = new RealT[ m_lengthNodalData ]; - if (set_response) - { - m_fx = new RealT[ m_lengthNodalData ]; - m_fy = new RealT[ m_lengthNodalData ]; - } - - for (int i=0; i= 1) ? 1 : 0; - m_lengthNodalData = 8; // Hex 8 - m_elementType = (int)(tribol::LINEAR_QUAD); // 2D face in 3D mesh - - if (m_numCells > 0) - { - m_connectivity = new int[ m_lengthNodalData ]; - m_x = new RealT[ m_lengthNodalData ]; - m_y = new RealT[ m_lengthNodalData ]; - m_z = new RealT[ m_lengthNodalData ]; - if (set_response) - { - m_fx = new RealT[ m_lengthNodalData ]; - m_fy = new RealT[ m_lengthNodalData ]; - m_fz = new RealT[ m_lengthNodalData ]; - } - - for (int i=0; i= 1 ) ? 1 : 0; + m_lengthNodalData = 4; // Quad 4 + m_elementType = (int)( tribol::LINEAR_EDGE ); // 1D edge for 2D mesh + + if ( m_numCells > 0 ) { + m_connectivity = new int[m_lengthNodalData]; + m_x = new RealT[m_lengthNodalData]; + m_y = new RealT[m_lengthNodalData]; + if ( set_response ) { + m_fx = new RealT[m_lengthNodalData]; + m_fy = new RealT[m_lengthNodalData]; } - tribol::registerMesh( mesh_id, - m_numCells, - m_lengthNodalData, - m_connectivity, - m_elementType, - m_x, m_y, m_z, - tribol::MemorySpace::Host ); - - tribol::registerNodalResponse( mesh_id, m_fx, m_fy, m_fz ); - - } - - void clear() - { - if (m_connectivity != nullptr) - { - delete [] m_connectivity; - m_connectivity = nullptr; - } - if (m_x != nullptr) - { - delete [] m_x; - m_x = nullptr; - } - if (m_y != nullptr) - { - delete [] m_y; - m_y = nullptr; + for ( int i = 0; i < m_lengthNodalData; ++i ) { + m_connectivity[i] = i; } - if (m_z != nullptr) - { - delete [] m_z; - m_z = nullptr; + // unit square + m_x[0] = 0.; + m_y[0] = 0.; + m_x[1] = 1.; + m_y[1] = 0.; + m_x[2] = 1.; + m_y[2] = 1.; + m_x[3] = 0.; + m_y[3] = 1.; + + if ( set_response ) { + for ( int i = 0; i < m_lengthNodalData; ++i ) { + m_fx[i] = 0; + m_fy[i] = 0; + } } - if (m_fx != nullptr) - { - delete [] m_fx; - m_fx = nullptr; + } + + tribol::registerMesh( mesh_id, m_numCells, m_lengthNodalData, m_connectivity, m_elementType, m_x, m_y, m_z, + tribol::MemorySpace::Host ); + + tribol::registerNodalResponse( mesh_id, m_fx, m_fy, m_fz ); + } + + void registerDummy3DMesh( int mesh_id, int numCells = 1, bool set_response = true ) + { + // Single element meshes are usesd in these tests out of + // simplicity. + m_numCells = ( numCells >= 1 ) ? 1 : 0; + m_lengthNodalData = 8; // Hex 8 + m_elementType = (int)( tribol::LINEAR_QUAD ); // 2D face in 3D mesh + + if ( m_numCells > 0 ) { + m_connectivity = new int[m_lengthNodalData]; + m_x = new RealT[m_lengthNodalData]; + m_y = new RealT[m_lengthNodalData]; + m_z = new RealT[m_lengthNodalData]; + if ( set_response ) { + m_fx = new RealT[m_lengthNodalData]; + m_fy = new RealT[m_lengthNodalData]; + m_fz = new RealT[m_lengthNodalData]; } - if (m_fy != nullptr) - { - delete [] m_fy; - m_fy = nullptr; + + for ( int i = 0; i < m_lengthNodalData; ++i ) { + m_connectivity[i] = i; } - if (m_fz != nullptr) - { - delete [] m_fz; - m_fz = nullptr; + // unit cube + m_x[0] = 0.; + m_y[0] = 0.; + m_z[0] = 0.; + m_x[1] = 1.; + m_y[1] = 0.; + m_z[1] = 0.; + m_x[2] = 1.; + m_y[2] = 1.; + m_z[2] = 0.; + m_x[3] = 0.; + m_y[3] = 1.; + m_z[3] = 0.; + m_x[4] = 0.; + m_y[4] = 0.; + m_z[4] = 1.; + m_x[5] = 1.; + m_y[5] = 0.; + m_z[5] = 1.; + m_x[6] = 1.; + m_y[6] = 1.; + m_z[6] = 1.; + m_x[7] = 0.; + m_y[7] = 1.; + m_z[7] = 1.; + + if ( set_response ) { + for ( int i = 0; i < m_lengthNodalData; ++i ) { + m_fx[i] = 0; + m_fy[i] = 0; + m_fz[i] = 0; + } } - } + } + + tribol::registerMesh( mesh_id, m_numCells, m_lengthNodalData, m_connectivity, m_elementType, m_x, m_y, m_z, + tribol::MemorySpace::Host ); + + tribol::registerNodalResponse( mesh_id, m_fx, m_fy, m_fz ); + } + + void clear() + { + if ( m_connectivity != nullptr ) { + delete[] m_connectivity; + m_connectivity = nullptr; + } + if ( m_x != nullptr ) { + delete[] m_x; + m_x = nullptr; + } + if ( m_y != nullptr ) { + delete[] m_y; + m_y = nullptr; + } + if ( m_z != nullptr ) { + delete[] m_z; + m_z = nullptr; + } + if ( m_fx != nullptr ) { + delete[] m_fx; + m_fx = nullptr; + } + if ( m_fy != nullptr ) { + delete[] m_fy; + m_fy = nullptr; + } + if ( m_fz != nullptr ) { + delete[] m_fz; + m_fz = nullptr; + } + } }; TEST_F( CouplingSchemeTest, single_mortar_2D ) { - // expect the coupling scheme to fail because 2D - // is not yet implemented - registerDummy2DMesh( 0 ); - registerDummy2DMesh( 1 ); - - // register dummy nodal fields so error doesn't return from field - // registration - RealT gaps[this->m_lengthNodalData]; - RealT pressures[this->m_lengthNodalData]; - - tribol::registerMortarGaps( 1, &gaps[0] ); - tribol::registerMortarPressures( 1, &pressures[0] ); - - tribol::registerCouplingScheme(0, 0, 1, - tribol::SURFACE_TO_SURFACE, - tribol::NO_CASE, - tribol::SINGLE_MORTAR, - tribol::FRICTIONLESS, - tribol::LAGRANGE_MULTIPLIER, - tribol::BINNING_GRID, + // expect the coupling scheme to fail because 2D + // is not yet implemented + registerDummy2DMesh( 0 ); + registerDummy2DMesh( 1 ); + + // register dummy nodal fields so error doesn't return from field + // registration + RealT gaps[this->m_lengthNodalData]; + RealT pressures[this->m_lengthNodalData]; + + tribol::registerMortarGaps( 1, &gaps[0] ); + tribol::registerMortarPressures( 1, &pressures[0] ); + + tribol::registerCouplingScheme( 0, 0, 1, tribol::SURFACE_TO_SURFACE, tribol::NO_CASE, tribol::SINGLE_MORTAR, + tribol::FRICTIONLESS, tribol::LAGRANGE_MULTIPLIER, tribol::BINNING_GRID, tribol::ExecutionMode::Sequential ); - tribol::setLagrangeMultiplierOptions( 0, tribol::ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN, - tribol::SparseMode::MFEM_LINKED_LIST ); + tribol::setLagrangeMultiplierOptions( 0, tribol::ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN, + tribol::SparseMode::MFEM_LINKED_LIST ); - tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - tribol::CouplingScheme* scheme = &csManager.at( 0 ); - bool isInit = scheme->init(); - - EXPECT_EQ( isInit, false ); - - tribol::finalize(); + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingScheme* scheme = &csManager.at( 0 ); + bool isInit = scheme->init(); + + EXPECT_EQ( isInit, false ); + + tribol::finalize(); } TEST_F( CouplingSchemeTest, aligned_mortar_2D ) { - registerDummy2DMesh( 0 ); - registerDummy2DMesh( 1 ); - - // register dummy nodal fields so error doesn't return from field - // registration - RealT gaps[this->m_lengthNodalData]; - RealT pressures[this->m_lengthNodalData]; - - tribol::registerMortarGaps( 1, &gaps[0] ); - tribol::registerMortarPressures( 1, &pressures[0] ); - - tribol::registerCouplingScheme(0, 0, 1, - tribol::SURFACE_TO_SURFACE, - tribol::NO_CASE, - tribol::ALIGNED_MORTAR, - tribol::FRICTIONLESS, - tribol::LAGRANGE_MULTIPLIER, - tribol::BINNING_GRID, + registerDummy2DMesh( 0 ); + registerDummy2DMesh( 1 ); + + // register dummy nodal fields so error doesn't return from field + // registration + RealT gaps[this->m_lengthNodalData]; + RealT pressures[this->m_lengthNodalData]; + + tribol::registerMortarGaps( 1, &gaps[0] ); + tribol::registerMortarPressures( 1, &pressures[0] ); + + tribol::registerCouplingScheme( 0, 0, 1, tribol::SURFACE_TO_SURFACE, tribol::NO_CASE, tribol::ALIGNED_MORTAR, + tribol::FRICTIONLESS, tribol::LAGRANGE_MULTIPLIER, tribol::BINNING_GRID, tribol::ExecutionMode::Sequential ); - tribol::setLagrangeMultiplierOptions( 0, tribol::ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN, - tribol::SparseMode::MFEM_LINKED_LIST ); + tribol::setLagrangeMultiplierOptions( 0, tribol::ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN, + tribol::SparseMode::MFEM_LINKED_LIST ); - tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - tribol::CouplingScheme* scheme = &csManager.at( 0 ); - bool isInit = scheme->init(); + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingScheme* scheme = &csManager.at( 0 ); + bool isInit = scheme->init(); - EXPECT_EQ( isInit, false ); + EXPECT_EQ( isInit, false ); - tribol::finalize(); + tribol::finalize(); } TEST_F( CouplingSchemeTest, mortar_weights_2D ) { - registerDummy2DMesh( 0 ); - registerDummy2DMesh( 1 ); - - tribol::registerCouplingScheme(0, 0, 1, - tribol::SURFACE_TO_SURFACE, - tribol::NO_CASE, - tribol::MORTAR_WEIGHTS, - tribol::FRICTIONLESS, - tribol::LAGRANGE_MULTIPLIER, - tribol::BINNING_GRID, + registerDummy2DMesh( 0 ); + registerDummy2DMesh( 1 ); + + tribol::registerCouplingScheme( 0, 0, 1, tribol::SURFACE_TO_SURFACE, tribol::NO_CASE, tribol::MORTAR_WEIGHTS, + tribol::FRICTIONLESS, tribol::LAGRANGE_MULTIPLIER, tribol::BINNING_GRID, tribol::ExecutionMode::Sequential ); - tribol::setLagrangeMultiplierOptions( 0, tribol::ImplicitEvalMode::MORTAR_WEIGHTS_EVAL, - tribol::SparseMode::MFEM_LINKED_LIST ); + tribol::setLagrangeMultiplierOptions( 0, tribol::ImplicitEvalMode::MORTAR_WEIGHTS_EVAL, + tribol::SparseMode::MFEM_LINKED_LIST ); - tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - tribol::CouplingScheme* scheme = &csManager.at( 0 ); - bool isInit = scheme->init(); + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingScheme* scheme = &csManager.at( 0 ); + bool isInit = scheme->init(); - EXPECT_EQ( isInit, false ); + EXPECT_EQ( isInit, false ); - tribol::finalize(); + tribol::finalize(); } TEST_F( CouplingSchemeTest, single_mortar_3D_penalty ) { - registerDummy3DMesh( 0 ); - registerDummy3DMesh( 1 ); - - // register dummy nodal fields so error doesn't return from field - // registration - RealT gaps[this->m_lengthNodalData]; - RealT pressures[this->m_lengthNodalData]; - - tribol::registerMortarGaps( 1, &gaps[0] ); - tribol::registerMortarPressures( 1, &pressures[0] ); - - tribol::registerCouplingScheme(0, 0, 1, - tribol::SURFACE_TO_SURFACE, - tribol::NO_CASE, - tribol::SINGLE_MORTAR, - tribol::FRICTIONLESS, - tribol::PENALTY, - tribol::BINNING_GRID, + registerDummy3DMesh( 0 ); + registerDummy3DMesh( 1 ); + + // register dummy nodal fields so error doesn't return from field + // registration + RealT gaps[this->m_lengthNodalData]; + RealT pressures[this->m_lengthNodalData]; + + tribol::registerMortarGaps( 1, &gaps[0] ); + tribol::registerMortarPressures( 1, &pressures[0] ); + + tribol::registerCouplingScheme( 0, 0, 1, tribol::SURFACE_TO_SURFACE, tribol::NO_CASE, tribol::SINGLE_MORTAR, + tribol::FRICTIONLESS, tribol::PENALTY, tribol::BINNING_GRID, tribol::ExecutionMode::Sequential ); - tribol::setPenaltyOptions( 0, tribol::KINEMATIC, - tribol::KINEMATIC_CONSTANT ); + tribol::setPenaltyOptions( 0, tribol::KINEMATIC, tribol::KINEMATIC_CONSTANT ); - tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - tribol::CouplingScheme* scheme = &csManager.at( 0 ); - bool isInit = scheme->init(); + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingScheme* scheme = &csManager.at( 0 ); + bool isInit = scheme->init(); - EXPECT_EQ( isInit, false ); + EXPECT_EQ( isInit, false ); - tribol::finalize(); + tribol::finalize(); } TEST_F( CouplingSchemeTest, common_plane_lagrange_multiplier ) { - registerDummy3DMesh( 0 ); - registerDummy3DMesh( 1 ); - - tribol::registerCouplingScheme(0, 0, 1, - tribol::SURFACE_TO_SURFACE, - tribol::NO_CASE, - tribol::COMMON_PLANE, - tribol::FRICTIONLESS, - tribol::LAGRANGE_MULTIPLIER, - tribol::BINNING_GRID, + registerDummy3DMesh( 0 ); + registerDummy3DMesh( 1 ); + + tribol::registerCouplingScheme( 0, 0, 1, tribol::SURFACE_TO_SURFACE, tribol::NO_CASE, tribol::COMMON_PLANE, + tribol::FRICTIONLESS, tribol::LAGRANGE_MULTIPLIER, tribol::BINNING_GRID, tribol::ExecutionMode::Sequential ); - tribol::setLagrangeMultiplierOptions( 0, tribol::ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN, - tribol::SparseMode::MFEM_LINKED_LIST ); + tribol::setLagrangeMultiplierOptions( 0, tribol::ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN, + tribol::SparseMode::MFEM_LINKED_LIST ); - tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - tribol::CouplingScheme* scheme = &csManager.at( 0 ); - bool isInit = scheme->init(); + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingScheme* scheme = &csManager.at( 0 ); + bool isInit = scheme->init(); - EXPECT_EQ( isInit, false ); + EXPECT_EQ( isInit, false ); } TEST_F( CouplingSchemeTest, mortar_no_nodal_gaps_or_pressures ) { - registerDummy3DMesh( 0 ); - registerDummy3DMesh( 1 ); - - tribol::registerCouplingScheme(0, 0, 1, - tribol::SURFACE_TO_SURFACE, - tribol::NO_CASE, - tribol::SINGLE_MORTAR, - tribol::FRICTIONLESS, - tribol::LAGRANGE_MULTIPLIER, - tribol::BINNING_GRID, + registerDummy3DMesh( 0 ); + registerDummy3DMesh( 1 ); + + tribol::registerCouplingScheme( 0, 0, 1, tribol::SURFACE_TO_SURFACE, tribol::NO_CASE, tribol::SINGLE_MORTAR, + tribol::FRICTIONLESS, tribol::LAGRANGE_MULTIPLIER, tribol::BINNING_GRID, tribol::ExecutionMode::Sequential ); - tribol::setLagrangeMultiplierOptions( 0, tribol::ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN, - tribol::SparseMode::MFEM_LINKED_LIST ); + tribol::setLagrangeMultiplierOptions( 0, tribol::ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN, + tribol::SparseMode::MFEM_LINKED_LIST ); - tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - tribol::CouplingScheme* scheme = &csManager.at( 0 ); - bool isInit = scheme->init(); + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingScheme* scheme = &csManager.at( 0 ); + bool isInit = scheme->init(); - EXPECT_EQ( isInit, false ); + EXPECT_EQ( isInit, false ); - tribol::finalize(); + tribol::finalize(); } TEST_F( CouplingSchemeTest, mortar_tied ) { - registerDummy3DMesh( 0 ); - registerDummy3DMesh( 1 ); - - // register dummy nodal fields so error doesn't return from field - // registration - RealT gaps[this->m_lengthNodalData]; - RealT pressures[this->m_lengthNodalData]; - - tribol::registerMortarGaps( 1, &gaps[0] ); - tribol::registerMortarPressures( 1, &pressures[0] ); - - tribol::registerCouplingScheme(0, 0, 1, - tribol::SURFACE_TO_SURFACE, - tribol::NO_CASE, - tribol::SINGLE_MORTAR, - tribol::TIED_NORMAL, - tribol::LAGRANGE_MULTIPLIER, - tribol::BINNING_GRID, + registerDummy3DMesh( 0 ); + registerDummy3DMesh( 1 ); + + // register dummy nodal fields so error doesn't return from field + // registration + RealT gaps[this->m_lengthNodalData]; + RealT pressures[this->m_lengthNodalData]; + + tribol::registerMortarGaps( 1, &gaps[0] ); + tribol::registerMortarPressures( 1, &pressures[0] ); + + tribol::registerCouplingScheme( 0, 0, 1, tribol::SURFACE_TO_SURFACE, tribol::NO_CASE, tribol::SINGLE_MORTAR, + tribol::TIED_NORMAL, tribol::LAGRANGE_MULTIPLIER, tribol::BINNING_GRID, tribol::ExecutionMode::Sequential ); - tribol::setLagrangeMultiplierOptions( 0, tribol::ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN, - tribol::SparseMode::MFEM_LINKED_LIST ); + tribol::setLagrangeMultiplierOptions( 0, tribol::ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN, + tribol::SparseMode::MFEM_LINKED_LIST ); - tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - tribol::CouplingScheme* scheme = &csManager.at( 0 ); - bool isInit = scheme->init(); + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingScheme* scheme = &csManager.at( 0 ); + bool isInit = scheme->init(); - EXPECT_EQ( isInit, false ); + EXPECT_EQ( isInit, false ); - tribol::finalize(); + tribol::finalize(); } TEST_F( CouplingSchemeTest, mortar_coulomb ) { - registerDummy3DMesh( 0 ); - registerDummy3DMesh( 1 ); - - // register dummy nodal fields so error doesn't return from field - // registration - RealT gaps[this->m_lengthNodalData]; - RealT pressures[this->m_lengthNodalData]; - - tribol::registerMortarGaps( 1, &gaps[0] ); - tribol::registerMortarPressures( 1, &pressures[0] ); - - tribol::registerCouplingScheme(0, 0, 1, - tribol::SURFACE_TO_SURFACE, - tribol::NO_CASE, - tribol::SINGLE_MORTAR, - tribol::COULOMB, - tribol::LAGRANGE_MULTIPLIER, - tribol::BINNING_GRID, + registerDummy3DMesh( 0 ); + registerDummy3DMesh( 1 ); + + // register dummy nodal fields so error doesn't return from field + // registration + RealT gaps[this->m_lengthNodalData]; + RealT pressures[this->m_lengthNodalData]; + + tribol::registerMortarGaps( 1, &gaps[0] ); + tribol::registerMortarPressures( 1, &pressures[0] ); + + tribol::registerCouplingScheme( 0, 0, 1, tribol::SURFACE_TO_SURFACE, tribol::NO_CASE, tribol::SINGLE_MORTAR, + tribol::COULOMB, tribol::LAGRANGE_MULTIPLIER, tribol::BINNING_GRID, tribol::ExecutionMode::Sequential ); - tribol::setLagrangeMultiplierOptions( 0, tribol::ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN, - tribol::SparseMode::MFEM_LINKED_LIST ); + tribol::setLagrangeMultiplierOptions( 0, tribol::ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN, + tribol::SparseMode::MFEM_LINKED_LIST ); - tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - tribol::CouplingScheme* scheme = &csManager.at( 0 ); - bool isInit = scheme->init(); + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingScheme* scheme = &csManager.at( 0 ); + bool isInit = scheme->init(); - EXPECT_EQ( isInit, false ); + EXPECT_EQ( isInit, false ); - tribol::finalize(); + tribol::finalize(); } TEST_F( CouplingSchemeTest, common_plane_tied ) { - registerDummy3DMesh( 0 ); - registerDummy3DMesh( 1 ); - - RealT penalty = 1.0; - tribol::setKinematicConstantPenalty(0, penalty); - tribol::setKinematicConstantPenalty(1, penalty); - - tribol::registerCouplingScheme(0, 0, 1, - tribol::SURFACE_TO_SURFACE, - tribol::TIED_NORMAL, - tribol::COMMON_PLANE, - tribol::FRICTIONLESS, - tribol::PENALTY, - tribol::BINNING_GRID, + registerDummy3DMesh( 0 ); + registerDummy3DMesh( 1 ); + + RealT penalty = 1.0; + tribol::setKinematicConstantPenalty( 0, penalty ); + tribol::setKinematicConstantPenalty( 1, penalty ); + + tribol::registerCouplingScheme( 0, 0, 1, tribol::SURFACE_TO_SURFACE, tribol::TIED_NORMAL, tribol::COMMON_PLANE, + tribol::FRICTIONLESS, tribol::PENALTY, tribol::BINNING_GRID, tribol::ExecutionMode::Sequential ); - tribol::setPenaltyOptions( 0, tribol::KINEMATIC, - tribol::KINEMATIC_CONSTANT ); + tribol::setPenaltyOptions( 0, tribol::KINEMATIC, tribol::KINEMATIC_CONSTANT ); - tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - tribol::CouplingScheme* scheme = &csManager.at( 0 ); - bool isInit = scheme->init(); + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingScheme* scheme = &csManager.at( 0 ); + bool isInit = scheme->init(); - EXPECT_EQ( isInit, true ); + EXPECT_EQ( isInit, true ); - tribol::finalize(); + tribol::finalize(); } TEST_F( CouplingSchemeTest, common_plane_coulomb ) { - registerDummy3DMesh( 0 ); - registerDummy3DMesh( 1 ); - - RealT penalty = 1.0; - tribol::setKinematicConstantPenalty(0, penalty); - tribol::setKinematicConstantPenalty(1, penalty); - - tribol::registerCouplingScheme(0, 0, 1, - tribol::SURFACE_TO_SURFACE, - tribol::NO_CASE, - tribol::COMMON_PLANE, - tribol::COULOMB, - tribol::PENALTY, - tribol::BINNING_GRID, + registerDummy3DMesh( 0 ); + registerDummy3DMesh( 1 ); + + RealT penalty = 1.0; + tribol::setKinematicConstantPenalty( 0, penalty ); + tribol::setKinematicConstantPenalty( 1, penalty ); + + tribol::registerCouplingScheme( 0, 0, 1, tribol::SURFACE_TO_SURFACE, tribol::NO_CASE, tribol::COMMON_PLANE, + tribol::COULOMB, tribol::PENALTY, tribol::BINNING_GRID, tribol::ExecutionMode::Sequential ); - tribol::setPenaltyOptions( 0, tribol::KINEMATIC, - tribol::KINEMATIC_CONSTANT ); + tribol::setPenaltyOptions( 0, tribol::KINEMATIC, tribol::KINEMATIC_CONSTANT ); - tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - tribol::CouplingScheme* scheme = &csManager.at( 0 ); - bool isInit = scheme->init(); + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingScheme* scheme = &csManager.at( 0 ); + bool isInit = scheme->init(); - EXPECT_EQ( isInit, false ); + EXPECT_EQ( isInit, false ); - tribol::finalize(); + tribol::finalize(); } TEST_F( CouplingSchemeTest, non_null_to_null_meshes ) { - //////////////////////////////////////////////// - // setup simple non-null contacting test mesh // - //////////////////////////////////////////////// - tribol::TestMesh mesh; - mesh.mortarMeshId = 0; - mesh.nonmortarMeshId = 1; - - int nMortarElems = 1; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 1; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - // mesh bounding box with 0.1 interpenetration gap - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.05; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 0.95; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0. ); - - // register meshes - tribol::registerMesh( mesh.mortarMeshId, - mesh.numMortarFaces, - mesh.numTotalNodes, - mesh.faceConn1, (int)(tribol::LINEAR_QUAD), - mesh.x, mesh.y, mesh.z, - tribol::MemorySpace::Host ); - - tribol::registerMesh( mesh.nonmortarMeshId, - mesh.numNonmortarFaces, - mesh.numTotalNodes, - mesh.faceConn2, (int)(tribol::LINEAR_QUAD), - mesh.x, mesh.y, mesh.z, - tribol::MemorySpace::Host ); - - // set penalty data so coupling scheme initialization passes - RealT penalty = 1.0; - tribol::setKinematicConstantPenalty( 0, penalty ); - tribol::setKinematicConstantPenalty( 1, penalty ); - - // register nodal responses (i.e. nodal forces) - tribol::allocRealArray( &mesh.fx1, mesh.numTotalNodes, 0. ); - tribol::allocRealArray( &mesh.fy1, mesh.numTotalNodes, 0. ); - tribol::allocRealArray( &mesh.fz1, mesh.numTotalNodes, 0. ); - tribol::allocRealArray( &mesh.fx2, mesh.numTotalNodes, 0. ); - tribol::allocRealArray( &mesh.fy2, mesh.numTotalNodes, 0. ); - tribol::allocRealArray( &mesh.fz2, mesh.numTotalNodes, 0. ); - - tribol::registerNodalResponse( mesh.mortarMeshId, - mesh.fx1, mesh.fy1, mesh.fz1 ); - - tribol::registerNodalResponse( mesh.nonmortarMeshId, - mesh.fx2, mesh.fy2, mesh.fz2 ); - - // register the coupling scheme - const int csIndex = 0; - tribol::registerCouplingScheme(csIndex, 0, 1, - tribol::SURFACE_TO_SURFACE, - tribol::NO_CASE, - tribol::COMMON_PLANE, - tribol::FRICTIONLESS, - tribol::PENALTY, - tribol::BINNING_GRID, + //////////////////////////////////////////////// + // setup simple non-null contacting test mesh // + //////////////////////////////////////////////// + tribol::TestMesh mesh; + mesh.mortarMeshId = 0; + mesh.nonmortarMeshId = 1; + + int nMortarElems = 1; + int nElemsXM = nMortarElems; + int nElemsYM = nMortarElems; + int nElemsZM = nMortarElems; + + int nNonmortarElems = 1; + int nElemsXS = nNonmortarElems; + int nElemsYS = nNonmortarElems; + int nElemsZS = nNonmortarElems; + + // mesh bounding box with 0.1 interpenetration gap + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.05; + + RealT x_min2 = 0.; + RealT y_min2 = 0.; + RealT z_min2 = 0.95; + RealT x_max2 = 1.; + RealT y_max2 = 1.; + RealT z_max2 = 2.; + + mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, nElemsXS, + nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., 0. ); + + // register meshes + tribol::registerMesh( mesh.mortarMeshId, mesh.numMortarFaces, mesh.numTotalNodes, mesh.faceConn1, + (int)( tribol::LINEAR_QUAD ), mesh.x, mesh.y, mesh.z, tribol::MemorySpace::Host ); + + tribol::registerMesh( mesh.nonmortarMeshId, mesh.numNonmortarFaces, mesh.numTotalNodes, mesh.faceConn2, + (int)( tribol::LINEAR_QUAD ), mesh.x, mesh.y, mesh.z, tribol::MemorySpace::Host ); + + // set penalty data so coupling scheme initialization passes + RealT penalty = 1.0; + tribol::setKinematicConstantPenalty( 0, penalty ); + tribol::setKinematicConstantPenalty( 1, penalty ); + + // register nodal responses (i.e. nodal forces) + tribol::allocRealArray( &mesh.fx1, mesh.numTotalNodes, 0. ); + tribol::allocRealArray( &mesh.fy1, mesh.numTotalNodes, 0. ); + tribol::allocRealArray( &mesh.fz1, mesh.numTotalNodes, 0. ); + tribol::allocRealArray( &mesh.fx2, mesh.numTotalNodes, 0. ); + tribol::allocRealArray( &mesh.fy2, mesh.numTotalNodes, 0. ); + tribol::allocRealArray( &mesh.fz2, mesh.numTotalNodes, 0. ); + + tribol::registerNodalResponse( mesh.mortarMeshId, mesh.fx1, mesh.fy1, mesh.fz1 ); + + tribol::registerNodalResponse( mesh.nonmortarMeshId, mesh.fx2, mesh.fy2, mesh.fz2 ); + + // register the coupling scheme + const int csIndex = 0; + tribol::registerCouplingScheme( csIndex, 0, 1, tribol::SURFACE_TO_SURFACE, tribol::NO_CASE, tribol::COMMON_PLANE, + tribol::FRICTIONLESS, tribol::PENALTY, tribol::BINNING_GRID, tribol::ExecutionMode::Sequential ); - - tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC, - tribol::KINEMATIC_CONSTANT ); - - // call update so binning on coupling scheme is performed. - RealT dt = 1.0; - EXPECT_EQ(tribol::update(1, 1., dt), 0); - - tribol::CouplingSchemeManager& csManager = - tribol::CouplingSchemeManager::getInstance(); - tribol::CouplingScheme* cs_non_null = &csManager.at(csIndex); - - // check that the total number of nodes in the coupling scheme are - // the two 8 node hexes in 3D - EXPECT_EQ(cs_non_null->getNumTotalNodes(), 16); // hard coded for simplicity - - // check that there is a single active pair - EXPECT_EQ(cs_non_null->getNumActivePairs(), 1); - - /////////////////////////////////////////////////////////////// - // Re-register Data With Same Mesh Ids and CouplingScheme Id // - /////////////////////////////////////////////////////////////// - - // register same mesh IDs as NULL meshes - int elementType = (int)(tribol::LINEAR_QUAD); - tribol::registerMesh( 0, 0, 0, nullptr, elementType, nullptr, nullptr, nullptr, tribol::MemorySpace::Host ); - tribol::registerMesh( 1, 0, 0, nullptr, elementType, nullptr, nullptr, nullptr, tribol::MemorySpace::Host ); - - // set penalty data for valid coupling scheme with penalty enforcement. - // Previous meshes and mesh associated penalty data is overwritten with - // registerMesh() calls above - tribol::setKinematicConstantPenalty( 0, penalty ); - tribol::setKinematicConstantPenalty( 1, penalty ); - - //RE-register coupling scheme 0 with null-meshes with same IDs as before - tribol::registerCouplingScheme(csIndex, 0, 1, - tribol::SURFACE_TO_SURFACE, - tribol::NO_CASE, - tribol::COMMON_PLANE, - tribol::FRICTIONLESS, - tribol::PENALTY, - tribol::BINNING_GRID, + + tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC, tribol::KINEMATIC_CONSTANT ); + + // call update so binning on coupling scheme is performed. + RealT dt = 1.0; + EXPECT_EQ( tribol::update( 1, 1., dt ), 0 ); + + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingScheme* cs_non_null = &csManager.at( csIndex ); + + // check that the total number of nodes in the coupling scheme are + // the two 8 node hexes in 3D + EXPECT_EQ( cs_non_null->getNumTotalNodes(), 16 ); // hard coded for simplicity + + // check that there is a single active pair + EXPECT_EQ( cs_non_null->getNumActivePairs(), 1 ); + + /////////////////////////////////////////////////////////////// + // Re-register Data With Same Mesh Ids and CouplingScheme Id // + /////////////////////////////////////////////////////////////// + + // register same mesh IDs as NULL meshes + int elementType = (int)( tribol::LINEAR_QUAD ); + tribol::registerMesh( 0, 0, 0, nullptr, elementType, nullptr, nullptr, nullptr, tribol::MemorySpace::Host ); + tribol::registerMesh( 1, 0, 0, nullptr, elementType, nullptr, nullptr, nullptr, tribol::MemorySpace::Host ); + + // set penalty data for valid coupling scheme with penalty enforcement. + // Previous meshes and mesh associated penalty data is overwritten with + // registerMesh() calls above + tribol::setKinematicConstantPenalty( 0, penalty ); + tribol::setKinematicConstantPenalty( 1, penalty ); + + // RE-register coupling scheme 0 with null-meshes with same IDs as before + tribol::registerCouplingScheme( csIndex, 0, 1, tribol::SURFACE_TO_SURFACE, tribol::NO_CASE, tribol::COMMON_PLANE, + tribol::FRICTIONLESS, tribol::PENALTY, tribol::BINNING_GRID, tribol::ExecutionMode::Sequential ); - tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC, - tribol::KINEMATIC_CONSTANT ); + tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC, tribol::KINEMATIC_CONSTANT ); + + tribol::CouplingScheme* cs_null = &csManager.at( csIndex ); - tribol::CouplingScheme* cs_null = &csManager.at(csIndex); - - // check that total number of nodes on the coupling scheme is zero from null meshes - EXPECT_EQ(cs_null->getNumTotalNodes(), 0); - - // check that the number of active pairs is zero from initialization and not - // carried over from previous coupling scheme registration with non-null meshes - EXPECT_EQ( cs_null->getNumActivePairs(), 0 ); + // check that total number of nodes on the coupling scheme is zero from null meshes + EXPECT_EQ( cs_null->getNumTotalNodes(), 0 ); - // call cs_null->init() to make sure the nullMeshes boolean is correctly set - cs_null->init(); - EXPECT_EQ( cs_null->nullMeshes(), true ); + // check that the number of active pairs is zero from initialization and not + // carried over from previous coupling scheme registration with non-null meshes + EXPECT_EQ( cs_null->getNumActivePairs(), 0 ); - // call update() to make sure there is a no-op for this coupling scheme - EXPECT_EQ(tribol::update(1, 1., dt), 0); - EXPECT_EQ(cs_null->getNumActivePairs(), 0); + // call cs_null->init() to make sure the nullMeshes boolean is correctly set + cs_null->init(); + EXPECT_EQ( cs_null->nullMeshes(), true ); - // check InterfacePairs data - EXPECT_EQ( cs_null->getInterfacePairs().size(), 0 ); + // call update() to make sure there is a no-op for this coupling scheme + EXPECT_EQ( tribol::update( 1, 1., dt ), 0 ); + EXPECT_EQ( cs_null->getNumActivePairs(), 0 ); - tribol::finalize(); + // check InterfacePairs data + EXPECT_EQ( cs_null->getInterfacePairs().size(), 0 ); + + tribol::finalize(); } TEST_F( CouplingSchemeTest, invalid_mesh_in_coupling_scheme ) { - // register meshes - int id1 = 0; - int id2 = 1; - int numFaces1 = 1; - int numFaces2 = 1; - constexpr int numTotalNodes1 = 2; - constexpr int numTotalNodes2 = 4; - int faceConn1[numTotalNodes1] = {0, 1}; // dummy triangle connectivity (invalid) - int faceConn2[numTotalNodes2] = {4, 5, 6, 7}; // dummy quadrilateral connectivity (valid) - RealT x1[numTotalNodes1] = {0., 0.5}; - RealT y1[numTotalNodes1] = {0., 0.}; - RealT z1[numTotalNodes1] = {0., 0.}; - RealT x2[numTotalNodes2] = {0., 0.5, 0.5, 0.}; - RealT y2[numTotalNodes2] = {0., 0., 0.5, 0.5}; - RealT z2[numTotalNodes2] = {-0.1, -0.1, -0.1, -0.1}; - RealT fx1[numTotalNodes1], fy1[numTotalNodes1], fz1[numTotalNodes1]; - RealT fx2[numTotalNodes2], fy2[numTotalNodes2], fz2[numTotalNodes2]; - - tribol::initRealArray( &fx1[0], numTotalNodes1, 0. ); - tribol::initRealArray( &fy1[0], numTotalNodes1, 0. ); - tribol::initRealArray( &fz1[0], numTotalNodes1, 0. ); - - tribol::initRealArray( &fx2[0], numTotalNodes2, 0. ); - tribol::initRealArray( &fy2[0], numTotalNodes2, 0. ); - tribol::initRealArray( &fz2[0], numTotalNodes2, 0. ); - - tribol::registerMesh( id1, numFaces1, numTotalNodes1, - &faceConn1[0], (int)(tribol::LINEAR_EDGE), - &x1[0], &y1[0], &z1[0], tribol::MemorySpace::Host ); - - tribol::registerMesh( id2, numFaces2, numTotalNodes2, - &faceConn2[0], (int)(tribol::LINEAR_QUAD), - &x2[0], &y2[0], &z2[0], tribol::MemorySpace::Host ); - - // set penalty data so coupling scheme initialization passes - RealT penalty = 1.0; - tribol::setKinematicConstantPenalty( 0, penalty ); - tribol::setKinematicConstantPenalty( 1, penalty ); - - tribol::registerNodalResponse( id1, &fx1[0], &fy1[0], &fz1[0] ); - - tribol::registerNodalResponse( id2, &fx2[0], &fy2[0], &fz2[0] ); - - // register the coupling scheme - const int csIndex = 0; - tribol::registerCouplingScheme(csIndex, 0, 1, - tribol::SURFACE_TO_SURFACE, - tribol::NO_CASE, - tribol::COMMON_PLANE, - tribol::FRICTIONLESS, - tribol::PENALTY, - tribol::BINNING_GRID, + // register meshes + int id1 = 0; + int id2 = 1; + int numFaces1 = 1; + int numFaces2 = 1; + constexpr int numTotalNodes1 = 2; + constexpr int numTotalNodes2 = 4; + int faceConn1[numTotalNodes1] = { 0, 1 }; // dummy triangle connectivity (invalid) + int faceConn2[numTotalNodes2] = { 4, 5, 6, 7 }; // dummy quadrilateral connectivity (valid) + RealT x1[numTotalNodes1] = { 0., 0.5 }; + RealT y1[numTotalNodes1] = { 0., 0. }; + RealT z1[numTotalNodes1] = { 0., 0. }; + RealT x2[numTotalNodes2] = { 0., 0.5, 0.5, 0. }; + RealT y2[numTotalNodes2] = { 0., 0., 0.5, 0.5 }; + RealT z2[numTotalNodes2] = { -0.1, -0.1, -0.1, -0.1 }; + RealT fx1[numTotalNodes1], fy1[numTotalNodes1], fz1[numTotalNodes1]; + RealT fx2[numTotalNodes2], fy2[numTotalNodes2], fz2[numTotalNodes2]; + + tribol::initRealArray( &fx1[0], numTotalNodes1, 0. ); + tribol::initRealArray( &fy1[0], numTotalNodes1, 0. ); + tribol::initRealArray( &fz1[0], numTotalNodes1, 0. ); + + tribol::initRealArray( &fx2[0], numTotalNodes2, 0. ); + tribol::initRealArray( &fy2[0], numTotalNodes2, 0. ); + tribol::initRealArray( &fz2[0], numTotalNodes2, 0. ); + + tribol::registerMesh( id1, numFaces1, numTotalNodes1, &faceConn1[0], (int)( tribol::LINEAR_EDGE ), &x1[0], &y1[0], + &z1[0], tribol::MemorySpace::Host ); + + tribol::registerMesh( id2, numFaces2, numTotalNodes2, &faceConn2[0], (int)( tribol::LINEAR_QUAD ), &x2[0], &y2[0], + &z2[0], tribol::MemorySpace::Host ); + + // set penalty data so coupling scheme initialization passes + RealT penalty = 1.0; + tribol::setKinematicConstantPenalty( 0, penalty ); + tribol::setKinematicConstantPenalty( 1, penalty ); + + tribol::registerNodalResponse( id1, &fx1[0], &fy1[0], &fz1[0] ); + + tribol::registerNodalResponse( id2, &fx2[0], &fy2[0], &fz2[0] ); + + // register the coupling scheme + const int csIndex = 0; + tribol::registerCouplingScheme( csIndex, 0, 1, tribol::SURFACE_TO_SURFACE, tribol::NO_CASE, tribol::COMMON_PLANE, + tribol::FRICTIONLESS, tribol::PENALTY, tribol::BINNING_GRID, tribol::ExecutionMode::Sequential ); - - tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC, - tribol::KINEMATIC_CONSTANT ); - tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - tribol::CouplingScheme* scheme = &csManager.at( 0 ); - bool isInit = scheme->init(); + tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC, tribol::KINEMATIC_CONSTANT ); + + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingScheme* scheme = &csManager.at( 0 ); + bool isInit = scheme->init(); - EXPECT_EQ( isInit, false ); + EXPECT_EQ( isInit, false ); - tribol::finalize(); + tribol::finalize(); } TEST_F( CouplingSchemeTest, finalize ) { - //////////////////////////////////////////////// - // setup simple non-null contacting test mesh // - //////////////////////////////////////////////// - tribol::TestMesh mesh; - mesh.mortarMeshId = 0; - mesh.nonmortarMeshId = 1; - - int nMortarElems = 1; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 1; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - // mesh bounding box with 0.1 interpenetration gap - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.05; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 0.95; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0. ); - - // register meshes - tribol::registerMesh( mesh.mortarMeshId, - mesh.numMortarFaces, - mesh.numTotalNodes, - mesh.faceConn1, (int)(tribol::LINEAR_QUAD), - mesh.x, mesh.y, mesh.z, tribol::MemorySpace::Host ); - - tribol::registerMesh( mesh.nonmortarMeshId, - mesh.numNonmortarFaces, - mesh.numTotalNodes, - mesh.faceConn2, (int)(tribol::LINEAR_QUAD), - mesh.x, mesh.y, mesh.z, tribol::MemorySpace::Host ); - - // set penalty data so coupling scheme initialization passes - RealT penalty = 1.0; - tribol::setKinematicConstantPenalty(0, penalty); - tribol::setKinematicConstantPenalty(1, penalty); - - // register nodal responses (i.e. nodal forces) - tribol::allocRealArray( &mesh.fx1, mesh.numTotalNodes, 0. ); - tribol::allocRealArray( &mesh.fy1, mesh.numTotalNodes, 0. ); - tribol::allocRealArray( &mesh.fz1, mesh.numTotalNodes, 0. ); - tribol::allocRealArray( &mesh.fx2, mesh.numTotalNodes, 0. ); - tribol::allocRealArray( &mesh.fy2, mesh.numTotalNodes, 0. ); - tribol::allocRealArray( &mesh.fz2, mesh.numTotalNodes, 0. ); - - tribol::registerNodalResponse( mesh.mortarMeshId, - mesh.fx1, mesh.fy1, mesh.fz1 ); - - tribol::registerNodalResponse( mesh.nonmortarMeshId, - mesh.fx2, mesh.fy2, mesh.fz2 ); - - // register the coupling scheme - const int csIndex = 0; - tribol::registerCouplingScheme(csIndex, 0, 1, - tribol::SURFACE_TO_SURFACE, - tribol::NO_CASE, - tribol::COMMON_PLANE, - tribol::FRICTIONLESS, - tribol::PENALTY, - tribol::BINNING_GRID, + //////////////////////////////////////////////// + // setup simple non-null contacting test mesh // + //////////////////////////////////////////////// + tribol::TestMesh mesh; + mesh.mortarMeshId = 0; + mesh.nonmortarMeshId = 1; + + int nMortarElems = 1; + int nElemsXM = nMortarElems; + int nElemsYM = nMortarElems; + int nElemsZM = nMortarElems; + + int nNonmortarElems = 1; + int nElemsXS = nNonmortarElems; + int nElemsYS = nNonmortarElems; + int nElemsZS = nNonmortarElems; + + // mesh bounding box with 0.1 interpenetration gap + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.05; + + RealT x_min2 = 0.; + RealT y_min2 = 0.; + RealT z_min2 = 0.95; + RealT x_max2 = 1.; + RealT y_max2 = 1.; + RealT z_max2 = 2.; + + mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, nElemsXS, + nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., 0. ); + + // register meshes + tribol::registerMesh( mesh.mortarMeshId, mesh.numMortarFaces, mesh.numTotalNodes, mesh.faceConn1, + (int)( tribol::LINEAR_QUAD ), mesh.x, mesh.y, mesh.z, tribol::MemorySpace::Host ); + + tribol::registerMesh( mesh.nonmortarMeshId, mesh.numNonmortarFaces, mesh.numTotalNodes, mesh.faceConn2, + (int)( tribol::LINEAR_QUAD ), mesh.x, mesh.y, mesh.z, tribol::MemorySpace::Host ); + + // set penalty data so coupling scheme initialization passes + RealT penalty = 1.0; + tribol::setKinematicConstantPenalty( 0, penalty ); + tribol::setKinematicConstantPenalty( 1, penalty ); + + // register nodal responses (i.e. nodal forces) + tribol::allocRealArray( &mesh.fx1, mesh.numTotalNodes, 0. ); + tribol::allocRealArray( &mesh.fy1, mesh.numTotalNodes, 0. ); + tribol::allocRealArray( &mesh.fz1, mesh.numTotalNodes, 0. ); + tribol::allocRealArray( &mesh.fx2, mesh.numTotalNodes, 0. ); + tribol::allocRealArray( &mesh.fy2, mesh.numTotalNodes, 0. ); + tribol::allocRealArray( &mesh.fz2, mesh.numTotalNodes, 0. ); + + tribol::registerNodalResponse( mesh.mortarMeshId, mesh.fx1, mesh.fy1, mesh.fz1 ); + + tribol::registerNodalResponse( mesh.nonmortarMeshId, mesh.fx2, mesh.fy2, mesh.fz2 ); + + // register the coupling scheme + const int csIndex = 0; + tribol::registerCouplingScheme( csIndex, 0, 1, tribol::SURFACE_TO_SURFACE, tribol::NO_CASE, tribol::COMMON_PLANE, + tribol::FRICTIONLESS, tribol::PENALTY, tribol::BINNING_GRID, tribol::ExecutionMode::Sequential ); - - tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC, - tribol::KINEMATIC_CONSTANT ); - - // call update so binning on coupling scheme is performed. - RealT dt = 1.0; - EXPECT_EQ(tribol::update(1, 1., dt), 0); - tribol::finalize(); + tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC, tribol::KINEMATIC_CONSTANT ); + + // call update so binning on coupling scheme is performed. + RealT dt = 1.0; + EXPECT_EQ( tribol::update( 1, 1., dt ), 0 ); - tribol::CouplingSchemeManager& csManager = - tribol::CouplingSchemeManager::getInstance(); - EXPECT_EQ( csManager.findData(csIndex), nullptr ); + tribol::finalize(); + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + EXPECT_EQ( csManager.findData( csIndex ), nullptr ); } TEST_F( CouplingSchemeTest, null_velocity_kinematic_penalty ) { - registerDummy3DMesh( 0 ); - registerDummy3DMesh( 1 ); - - RealT penalty = 1.0; - tribol::setKinematicConstantPenalty(0, penalty); - tribol::setKinematicConstantPenalty(1, penalty); - - tribol::registerCouplingScheme(0, 0, 1, - tribol::SURFACE_TO_SURFACE, - tribol::NO_CASE, - tribol::COMMON_PLANE, - tribol::FRICTIONLESS, - tribol::PENALTY, - tribol::BINNING_GRID, + registerDummy3DMesh( 0 ); + registerDummy3DMesh( 1 ); + + RealT penalty = 1.0; + tribol::setKinematicConstantPenalty( 0, penalty ); + tribol::setKinematicConstantPenalty( 1, penalty ); + + tribol::registerCouplingScheme( 0, 0, 1, tribol::SURFACE_TO_SURFACE, tribol::NO_CASE, tribol::COMMON_PLANE, + tribol::FRICTIONLESS, tribol::PENALTY, tribol::BINNING_GRID, tribol::ExecutionMode::Sequential ); - tribol::setPenaltyOptions( 0, tribol::KINEMATIC, - tribol::KINEMATIC_CONSTANT ); + tribol::setPenaltyOptions( 0, tribol::KINEMATIC, tribol::KINEMATIC_CONSTANT ); - // register null nodal velocity pointers. The coupling scheme - // should initialize correctly for kinematic penalty only. - RealT* v_x {nullptr}; - RealT* v_y {nullptr}; - RealT* v_z {nullptr}; - tribol::registerNodalVelocities(0, v_x, v_y, v_z); - tribol::registerNodalVelocities(1, v_x, v_y, v_z); + // register null nodal velocity pointers. The coupling scheme + // should initialize correctly for kinematic penalty only. + RealT* v_x{ nullptr }; + RealT* v_y{ nullptr }; + RealT* v_z{ nullptr }; + tribol::registerNodalVelocities( 0, v_x, v_y, v_z ); + tribol::registerNodalVelocities( 1, v_x, v_y, v_z ); - tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - tribol::CouplingScheme* scheme = &csManager.at( 0 ); - bool isInit = scheme->init(); + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingScheme* scheme = &csManager.at( 0 ); + bool isInit = scheme->init(); - EXPECT_EQ( isInit, true ); + EXPECT_EQ( isInit, true ); - tribol::finalize(); + tribol::finalize(); } TEST_F( CouplingSchemeTest, null_velocity_kinematic_and_rate_penalty ) { - registerDummy3DMesh( 0 ); - registerDummy3DMesh( 1 ); - - RealT penalty = 1.0; - tribol::setKinematicConstantPenalty(0, penalty); - tribol::setKinematicConstantPenalty(1, penalty); - - tribol::registerCouplingScheme(0, 0, 1, - tribol::SURFACE_TO_SURFACE, - tribol::NO_CASE, - tribol::COMMON_PLANE, - tribol::FRICTIONLESS, - tribol::PENALTY, - tribol::BINNING_GRID, + registerDummy3DMesh( 0 ); + registerDummy3DMesh( 1 ); + + RealT penalty = 1.0; + tribol::setKinematicConstantPenalty( 0, penalty ); + tribol::setKinematicConstantPenalty( 1, penalty ); + + tribol::registerCouplingScheme( 0, 0, 1, tribol::SURFACE_TO_SURFACE, tribol::NO_CASE, tribol::COMMON_PLANE, + tribol::FRICTIONLESS, tribol::PENALTY, tribol::BINNING_GRID, tribol::ExecutionMode::Sequential ); - tribol::setPenaltyOptions( 0, tribol::KINEMATIC_AND_RATE, - tribol::KINEMATIC_CONSTANT ); + tribol::setPenaltyOptions( 0, tribol::KINEMATIC_AND_RATE, tribol::KINEMATIC_CONSTANT ); - // register null nodal velocity pointers. The coupling scheme - // should NOT initialize correctly for kinematic-and-rate penalty. - RealT* v_x {nullptr}; - RealT* v_y {nullptr}; - RealT* v_z {nullptr}; - tribol::registerNodalVelocities(0, v_x, v_y, v_z); - tribol::registerNodalVelocities(1, v_x, v_y, v_z); + // register null nodal velocity pointers. The coupling scheme + // should NOT initialize correctly for kinematic-and-rate penalty. + RealT* v_x{ nullptr }; + RealT* v_y{ nullptr }; + RealT* v_z{ nullptr }; + tribol::registerNodalVelocities( 0, v_x, v_y, v_z ); + tribol::registerNodalVelocities( 1, v_x, v_y, v_z ); - tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - tribol::CouplingScheme* scheme = &csManager.at( 0 ); - bool isInit = scheme->init(); + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingScheme* scheme = &csManager.at( 0 ); + bool isInit = scheme->init(); - EXPECT_EQ( isInit, false ); + EXPECT_EQ( isInit, false ); - tribol::finalize(); + tribol::finalize(); } TEST_F( CouplingSchemeTest, mortar_weights_null_response_pointers ) { - bool setResponse = false; - int numCells = 1; - registerDummy3DMesh( 0, numCells, setResponse ); - registerDummy3DMesh( 1, numCells, setResponse ); - - tribol::registerCouplingScheme(0, 0, 1, - tribol::SURFACE_TO_SURFACE, - tribol::NO_CASE, - tribol::MORTAR_WEIGHTS, - tribol::FRICTIONLESS, - tribol::LAGRANGE_MULTIPLIER, - tribol::BINNING_GRID, + bool setResponse = false; + int numCells = 1; + registerDummy3DMesh( 0, numCells, setResponse ); + registerDummy3DMesh( 1, numCells, setResponse ); + + tribol::registerCouplingScheme( 0, 0, 1, tribol::SURFACE_TO_SURFACE, tribol::NO_CASE, tribol::MORTAR_WEIGHTS, + tribol::FRICTIONLESS, tribol::LAGRANGE_MULTIPLIER, tribol::BINNING_GRID, tribol::ExecutionMode::Sequential ); - tribol::setLagrangeMultiplierOptions( 0, tribol::ImplicitEvalMode::MORTAR_WEIGHTS_EVAL, - tribol::SparseMode::MFEM_LINKED_LIST ); + tribol::setLagrangeMultiplierOptions( 0, tribol::ImplicitEvalMode::MORTAR_WEIGHTS_EVAL, + tribol::SparseMode::MFEM_LINKED_LIST ); - tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - tribol::CouplingScheme* scheme = &csManager.at( 0 ); - bool isInit = scheme->init(); + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingScheme* scheme = &csManager.at( 0 ); + bool isInit = scheme->init(); - EXPECT_EQ( isInit, true ); + EXPECT_EQ( isInit, true ); - tribol::finalize(); + tribol::finalize(); } TEST_F( CouplingSchemeTest, single_mortar_null_response_pointers ) { - bool setResponse = false; - int numCells = 1; - registerDummy3DMesh( 0, numCells, setResponse ); - registerDummy3DMesh( 1, numCells, setResponse ); - - RealT gaps[this->m_lengthNodalData]; - RealT pressures[this->m_lengthNodalData]; - - tribol::registerMortarGaps( 1, &gaps[0] ); - tribol::registerMortarPressures( 1, &pressures[0] ); - - tribol::registerCouplingScheme(0, 0, 1, - tribol::SURFACE_TO_SURFACE, - tribol::NO_CASE, - tribol::SINGLE_MORTAR, - tribol::FRICTIONLESS, - tribol::LAGRANGE_MULTIPLIER, - tribol::BINNING_GRID, + bool setResponse = false; + int numCells = 1; + registerDummy3DMesh( 0, numCells, setResponse ); + registerDummy3DMesh( 1, numCells, setResponse ); + + RealT gaps[this->m_lengthNodalData]; + RealT pressures[this->m_lengthNodalData]; + + tribol::registerMortarGaps( 1, &gaps[0] ); + tribol::registerMortarPressures( 1, &pressures[0] ); + + tribol::registerCouplingScheme( 0, 0, 1, tribol::SURFACE_TO_SURFACE, tribol::NO_CASE, tribol::SINGLE_MORTAR, + tribol::FRICTIONLESS, tribol::LAGRANGE_MULTIPLIER, tribol::BINNING_GRID, tribol::ExecutionMode::Sequential ); - tribol::setLagrangeMultiplierOptions( 0, tribol::ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN, - tribol::SparseMode::MFEM_LINKED_LIST ); + tribol::setLagrangeMultiplierOptions( 0, tribol::ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN, + tribol::SparseMode::MFEM_LINKED_LIST ); - tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - tribol::CouplingScheme* scheme = &csManager.at( 0 ); - bool isInit = scheme->init(); + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingScheme* scheme = &csManager.at( 0 ); + bool isInit = scheme->init(); - EXPECT_EQ( isInit, false ); + EXPECT_EQ( isInit, false ); - tribol::finalize(); + tribol::finalize(); } TEST_F( CouplingSchemeTest, common_plane_null_response_pointers ) { - int numCells = 1; - bool setResponse = false; - registerDummy3DMesh( 0, numCells, setResponse ); - registerDummy3DMesh( 1, numCells, setResponse ); - - RealT penalty = 1.0; - tribol::setKinematicConstantPenalty(0, penalty); - tribol::setKinematicConstantPenalty(1, penalty); - - tribol::registerCouplingScheme(0, 0, 1, - tribol::SURFACE_TO_SURFACE, - tribol::NO_CASE, - tribol::COMMON_PLANE, - tribol::FRICTIONLESS, - tribol::PENALTY, - tribol::BINNING_GRID, + int numCells = 1; + bool setResponse = false; + registerDummy3DMesh( 0, numCells, setResponse ); + registerDummy3DMesh( 1, numCells, setResponse ); + + RealT penalty = 1.0; + tribol::setKinematicConstantPenalty( 0, penalty ); + tribol::setKinematicConstantPenalty( 1, penalty ); + + tribol::registerCouplingScheme( 0, 0, 1, tribol::SURFACE_TO_SURFACE, tribol::NO_CASE, tribol::COMMON_PLANE, + tribol::FRICTIONLESS, tribol::PENALTY, tribol::BINNING_GRID, tribol::ExecutionMode::Sequential ); - tribol::setPenaltyOptions( 0, tribol::KINEMATIC, - tribol::KINEMATIC_CONSTANT ); + tribol::setPenaltyOptions( 0, tribol::KINEMATIC, tribol::KINEMATIC_CONSTANT ); - tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - tribol::CouplingScheme* scheme = &csManager.at( 0 ); - bool isInit = scheme->init(); + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingScheme* scheme = &csManager.at( 0 ); + bool isInit = scheme->init(); - EXPECT_EQ( isInit, false ); + EXPECT_EQ( isInit, false ); - tribol::finalize(); + tribol::finalize(); } TEST_F( CouplingSchemeTest, null_mesh_with_null_pointers ) { - int numCells = 0; - bool setResponse = false; - registerDummy3DMesh( 0, numCells, setResponse ); - registerDummy3DMesh( 1, numCells, setResponse ); - - RealT penalty = 1.0; - tribol::setKinematicConstantPenalty(0, penalty); - tribol::setKinematicConstantPenalty(1, penalty); - - tribol::registerCouplingScheme(0, 0, 1, - tribol::SURFACE_TO_SURFACE, - tribol::NO_CASE, - tribol::COMMON_PLANE, - tribol::FRICTIONLESS, - tribol::PENALTY, - tribol::BINNING_GRID, + int numCells = 0; + bool setResponse = false; + registerDummy3DMesh( 0, numCells, setResponse ); + registerDummy3DMesh( 1, numCells, setResponse ); + + RealT penalty = 1.0; + tribol::setKinematicConstantPenalty( 0, penalty ); + tribol::setKinematicConstantPenalty( 1, penalty ); + + tribol::registerCouplingScheme( 0, 0, 1, tribol::SURFACE_TO_SURFACE, tribol::NO_CASE, tribol::COMMON_PLANE, + tribol::FRICTIONLESS, tribol::PENALTY, tribol::BINNING_GRID, tribol::ExecutionMode::Sequential ); - // register null nodal velocity pointers. The coupling scheme - // should NOT initialize correctly for kinematic-and-rate penalty. - RealT* v_x {nullptr}; - RealT* v_y {nullptr}; - RealT* v_z {nullptr}; - tribol::registerNodalVelocities(0, v_x, v_y, v_z); - tribol::registerNodalVelocities(1, v_x, v_y, v_z); + // register null nodal velocity pointers. The coupling scheme + // should NOT initialize correctly for kinematic-and-rate penalty. + RealT* v_x{ nullptr }; + RealT* v_y{ nullptr }; + RealT* v_z{ nullptr }; + tribol::registerNodalVelocities( 0, v_x, v_y, v_z ); + tribol::registerNodalVelocities( 1, v_x, v_y, v_z ); - tribol::setPenaltyOptions( 0, tribol::KINEMATIC, - tribol::KINEMATIC_CONSTANT ); + tribol::setPenaltyOptions( 0, tribol::KINEMATIC, tribol::KINEMATIC_CONSTANT ); - tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - tribol::CouplingScheme* scheme = &csManager.at( 0 ); - bool isInit = scheme->init(); + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingScheme* scheme = &csManager.at( 0 ); + bool isInit = scheme->init(); - EXPECT_EQ( isInit, true ); - EXPECT_EQ( scheme->nullMeshes(), true ); + EXPECT_EQ( isInit, true ); + EXPECT_EQ( scheme->nullMeshes(), true ); - // check the InterfacePairs member class on the coupling scheme - EXPECT_EQ( scheme->getInterfacePairs().size(), 0 ); + // check the InterfacePairs member class on the coupling scheme + EXPECT_EQ( scheme->getInterfacePairs().size(), 0 ); - tribol::finalize(); + tribol::finalize(); } TEST_F( CouplingSchemeTest, auto_common_plane_no_element_thickness ) { - tribol::IndexT mesh_id = 0; - int csId = 0; - registerDummy3DMesh( mesh_id ); - - tribol::registerCouplingScheme(csId, mesh_id, mesh_id, - tribol::SURFACE_TO_SURFACE, - tribol::AUTO, - tribol::COMMON_PLANE, - tribol::FRICTIONLESS, - tribol::PENALTY, - tribol::BINNING_GRID, + tribol::IndexT mesh_id = 0; + int csId = 0; + registerDummy3DMesh( mesh_id ); + + tribol::registerCouplingScheme( csId, mesh_id, mesh_id, tribol::SURFACE_TO_SURFACE, tribol::AUTO, + tribol::COMMON_PLANE, tribol::FRICTIONLESS, tribol::PENALTY, tribol::BINNING_GRID, tribol::ExecutionMode::Sequential ); - - tribol::setKinematicConstantPenalty( mesh_id, 1.0 ); - tribol::setPenaltyOptions( csId, tribol::KINEMATIC, - tribol::KINEMATIC_CONSTANT ); + tribol::setKinematicConstantPenalty( mesh_id, 1.0 ); + + tribol::setPenaltyOptions( csId, tribol::KINEMATIC, tribol::KINEMATIC_CONSTANT ); - tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - tribol::CouplingScheme* scheme = &csManager.at(csId); - bool isInit = scheme->init(); + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingScheme* scheme = &csManager.at( csId ); + bool isInit = scheme->init(); - EXPECT_EQ( isInit, false ); + EXPECT_EQ( isInit, false ); } TEST_F( CouplingSchemeTest, auto_common_plane_with_element_thickness ) { - tribol::IndexT mesh_id = 0; - int numElements = 1; - int csId = 0; - registerDummy3DMesh( mesh_id, numElements ); - - tribol::registerCouplingScheme(csId, mesh_id, mesh_id, - tribol::SURFACE_TO_SURFACE, - tribol::AUTO, - tribol::COMMON_PLANE, - tribol::FRICTIONLESS, - tribol::PENALTY, - tribol::BINNING_GRID, + tribol::IndexT mesh_id = 0; + int numElements = 1; + int csId = 0; + registerDummy3DMesh( mesh_id, numElements ); + + tribol::registerCouplingScheme( csId, mesh_id, mesh_id, tribol::SURFACE_TO_SURFACE, tribol::AUTO, + tribol::COMMON_PLANE, tribol::FRICTIONLESS, tribol::PENALTY, tribol::BINNING_GRID, tribol::ExecutionMode::Sequential ); - tribol::setKinematicConstantPenalty( mesh_id, 1.0 ); + tribol::setKinematicConstantPenalty( mesh_id, 1.0 ); - tribol::setPenaltyOptions( csId, tribol::KINEMATIC, - tribol::KINEMATIC_CONSTANT ); + tribol::setPenaltyOptions( csId, tribol::KINEMATIC, tribol::KINEMATIC_CONSTANT ); - RealT element_thick = 1.0; - tribol::registerRealElementField( mesh_id, tribol::ELEMENT_THICKNESS, &element_thick ); + RealT element_thick = 1.0; + tribol::registerRealElementField( mesh_id, tribol::ELEMENT_THICKNESS, &element_thick ); - tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - tribol::CouplingScheme* scheme = &csManager.at(csId); - bool isInit = scheme->init(); + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingScheme* scheme = &csManager.at( csId ); + bool isInit = scheme->init(); - EXPECT_EQ( isInit, true ); + EXPECT_EQ( isInit, true ); } -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); #ifdef TRIBOL_USE_UMPIRE umpire::ResourceManager::getInstance(); // initialize umpire's ResouceManager diff --git a/src/tests/tribol_coupling_scheme_manager.cpp b/src/tests/tribol_coupling_scheme_manager.cpp index 029b471f..a77f95d2 100644 --- a/src/tests/tribol_coupling_scheme_manager.cpp +++ b/src/tests/tribol_coupling_scheme_manager.cpp @@ -22,169 +22,149 @@ // C/C++ includes #include - /*! * Test fixture class with some setup necessary to use the * CouplingSchemeManager class */ -class CouplingSchemeManagerTest : public ::testing::Test -{ - -public: - // Simple function to generate a functioning coupling scheme - tribol::CouplingScheme generateCouplingScheme() - { - // Note: We are not testing anything about coupling schemes in this file. - // Contact parameters don't matter for this setup either. - return tribol::CouplingScheme( - 0, mesh_id[0], mesh_id[1], 0, - 0,0,0,0,tribol::DEFAULT_BINNING_METHOD); - } - +class CouplingSchemeManagerTest : public ::testing::Test { + public: + // Simple function to generate a functioning coupling scheme + tribol::CouplingScheme generateCouplingScheme() + { + // Note: We are not testing anything about coupling schemes in this file. + // Contact parameters don't matter for this setup either. + return tribol::CouplingScheme( 0, mesh_id[0], mesh_id[1], 0, 0, 0, 0, 0, tribol::DEFAULT_BINNING_METHOD ); + } -protected: + protected: + void SetUp() override + { + // Register two "dummy" meshes to be used by coupling scheme - void SetUp() override - { - // Register two "dummy" meshes to be used by coupling scheme + mesh_id[0] = 0; + tribol::registerMesh( mesh_id[0], 1, 4, &connectivity[0], 3, &x[0], &y[0], &z[0], tribol::MemorySpace::Host ); - mesh_id[0] = 0; - tribol::registerMesh(mesh_id[0], 1, 4, &connectivity[0], - 3, &x[0], &y[0], &z[0], tribol::MemorySpace::Host); + mesh_id[1] = 1; + tribol::registerMesh( mesh_id[1], 1, 4, &connectivity[0], 3, &x[0], &y[0], &z[0], tribol::MemorySpace::Host ); + } - mesh_id[1] = 1; - tribol::registerMesh(mesh_id[1], 1, 4, &connectivity[0], - 3, &x[0], &y[0], &z[0], tribol::MemorySpace::Host); - } + void TearDown() override + { + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - void TearDown() override - { - tribol::CouplingSchemeManager& csManager = - tribol::CouplingSchemeManager::getInstance(); + csManager.clear(); - csManager.clear(); + EXPECT_EQ( 0, csManager.size() ); + } - EXPECT_EQ( 0, csManager.size() ); - } + protected: + tribol::IndexT mesh_id[2]; -protected: - - tribol::IndexT mesh_id[2]; - - tribol::RealT x[4] { 0., 1., 1., 0. }; - tribol::RealT y[4] { 0., 0., 1., 1. }; - tribol::RealT z[4] { 0., 0., 0., 0. }; - - tribol::IndexT connectivity[4] { 0, 1, 2, 3 }; + tribol::RealT x[4]{ 0., 1., 1., 0. }; + tribol::RealT y[4]{ 0., 0., 1., 1. }; + tribol::RealT z[4]{ 0., 0., 0., 0. }; + tribol::IndexT connectivity[4]{ 0, 1, 2, 3 }; }; - TEST_F( CouplingSchemeManagerTest, initially_empty ) { - tribol::CouplingSchemeManager& csManager = - tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - EXPECT_EQ(0, csManager.size()); + EXPECT_EQ( 0, csManager.size() ); } TEST_F( CouplingSchemeManagerTest, add_remove_couplings ) { - tribol::CouplingSchemeManager& csManager = - tribol::CouplingSchemeManager::getInstance(); - - EXPECT_EQ(0, csManager.size()); - - // Adds a first coupling - { - constexpr tribol::IndexT cs_id = 0; - csManager.addData(cs_id, this->generateCouplingScheme()); - constexpr int expectedNumCouplings = 1; - EXPECT_EQ(expectedNumCouplings, csManager.size()); - - EXPECT_NE(nullptr, csManager.findData(cs_id)); - } - - // Adds a second coupling - { - constexpr tribol::IndexT cs_id = 1; - csManager.addData(cs_id, this->generateCouplingScheme()); - const int expectedNumCouplings = 2; - EXPECT_EQ(expectedNumCouplings, csManager.size()); - - EXPECT_NE(nullptr, csManager.findData(cs_id)); - } + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + + EXPECT_EQ( 0, csManager.size() ); + + // Adds a first coupling + { + constexpr tribol::IndexT cs_id = 0; + csManager.addData( cs_id, this->generateCouplingScheme() ); + constexpr int expectedNumCouplings = 1; + EXPECT_EQ( expectedNumCouplings, csManager.size() ); + + EXPECT_NE( nullptr, csManager.findData( cs_id ) ); + } + + // Adds a second coupling + { + constexpr tribol::IndexT cs_id = 1; + csManager.addData( cs_id, this->generateCouplingScheme() ); + const int expectedNumCouplings = 2; + EXPECT_EQ( expectedNumCouplings, csManager.size() ); + + EXPECT_NE( nullptr, csManager.findData( cs_id ) ); + } } TEST_F( CouplingSchemeManagerTest, add_couplings_at_index ) { - tribol::CouplingSchemeManager& csManager = - tribol::CouplingSchemeManager::getInstance(); - - EXPECT_EQ(0, csManager.size()); - - // Add some couplings - csManager.addData(0, this->generateCouplingScheme()); - csManager.addData(1, this->generateCouplingScheme()); - EXPECT_EQ(2, csManager.size()); + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + EXPECT_EQ( 0, csManager.size() ); - // Adds a coupling at index 4 -- note, we skipped indices 2 and 3 - { - constexpr tribol::IndexT cs_id = 4; - csManager.addData(cs_id, this->generateCouplingScheme()); + // Add some couplings + csManager.addData( 0, this->generateCouplingScheme() ); + csManager.addData( 1, this->generateCouplingScheme() ); + EXPECT_EQ( 2, csManager.size() ); - constexpr size_t expectedNumCouplings = 3; - EXPECT_EQ(expectedNumCouplings, csManager.size()); + // Adds a coupling at index 4 -- note, we skipped indices 2 and 3 + { + constexpr tribol::IndexT cs_id = 4; + csManager.addData( cs_id, this->generateCouplingScheme() ); - EXPECT_NE(nullptr, csManager.findData(cs_id)); - } + constexpr size_t expectedNumCouplings = 3; + EXPECT_EQ( expectedNumCouplings, csManager.size() ); - // After growing the manager's array, there are some nullptrs slots - { - // The existing slots should still be there - for(auto i : std::vector{0,1,4}) - { - EXPECT_NE(nullptr, csManager.findData(i)); - } + EXPECT_NE( nullptr, csManager.findData( cs_id ) ); + } - // The extra slots should be filled with zeros - for(auto i : std::vector{2,3}) - { - EXPECT_EQ(nullptr, csManager.findData(i)); - } - } + // After growing the manager's array, there are some nullptrs slots + { + // The existing slots should still be there + for ( auto i : std::vector{ 0, 1, 4 } ) { + EXPECT_NE( nullptr, csManager.findData( i ) ); + } - // Remove coupling at index 1 - { - constexpr tribol::IndexT cs_id = 1; - csManager.erase(cs_id); + // The extra slots should be filled with zeros + for ( auto i : std::vector{ 2, 3 } ) { + EXPECT_EQ( nullptr, csManager.findData( i ) ); + } + } - EXPECT_EQ(nullptr, csManager.findData(cs_id)); + // Remove coupling at index 1 + { + constexpr tribol::IndexT cs_id = 1; + csManager.erase( cs_id ); - // num couplings is now 2 - constexpr size_t expectedNumCouplings = 2; - EXPECT_EQ(expectedNumCouplings, csManager.size()); - } + EXPECT_EQ( nullptr, csManager.findData( cs_id ) ); - // Replace coupling at index 4 - { - constexpr tribol::IndexT cs_id = 4; - csManager.addData(cs_id, this->generateCouplingScheme()); + // num couplings is now 2 + constexpr size_t expectedNumCouplings = 2; + EXPECT_EQ( expectedNumCouplings, csManager.size() ); + } - constexpr size_t expectedNumCouplings = 2; - EXPECT_EQ(expectedNumCouplings, csManager.size()); + // Replace coupling at index 4 + { + constexpr tribol::IndexT cs_id = 4; + csManager.addData( cs_id, this->generateCouplingScheme() ); - EXPECT_NE(nullptr, csManager.findData(cs_id)); - } + constexpr size_t expectedNumCouplings = 2; + EXPECT_EQ( expectedNumCouplings, csManager.size() ); + EXPECT_NE( nullptr, csManager.findData( cs_id ) ); + } } -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); #ifdef TRIBOL_USE_UMPIRE umpire::ResourceManager::getInstance(); // initialize umpire's ResouceManager diff --git a/src/tests/tribol_enforcement_options.cpp b/src/tests/tribol_enforcement_options.cpp index 89b475cc..cf7a50fc 100644 --- a/src/tests/tribol_enforcement_options.cpp +++ b/src/tests/tribol_enforcement_options.cpp @@ -26,7 +26,7 @@ #include "gtest/gtest.h" // c++ includes -#include // std::abs, std::cos, std::sin +#include // std::abs, std::cos, std::sin #include #include #include @@ -36,468 +36,436 @@ using RealT = tribol::RealT; /*! * Test fixture class to test valid enforcement options - * These tests have to be written and run with registering + * These tests have to be written and run with registering * a valid coupling scheme with valid meshes. * */ -class EnforcementOptionsTest : public ::testing::Test -{ - -public: - -protected: - - void SetUp() override - { - // no-op - } - - // Setup boiler plate data and register mesh, nodal response, and coupling scheme - void SetupTest( tribol::TestMesh* mesh ) - { - //////////////////////////////////////////////// - // setup simple non-null contacting test mesh // - //////////////////////////////////////////////// - mesh->mortarMeshId = 0; - mesh->nonmortarMeshId = 1; - - int nMortarElems = 1; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 1; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - // mesh bounding box with 0.1 interpenetration gap - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.05; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 0.95; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - mesh->setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0. ); - - // register meshes - tribol::registerMesh( mesh->mortarMeshId, - mesh->numMortarFaces, - mesh->numTotalNodes, - mesh->faceConn1, 3, - mesh->x, mesh->y, mesh->z, tribol::MemorySpace::Host ); - - tribol::registerMesh( mesh->nonmortarMeshId, - mesh->numNonmortarFaces, - mesh->numTotalNodes, - mesh->faceConn2, 3, - mesh->x, mesh->y, mesh->z, tribol::MemorySpace::Host ); - - // register nodal responses (i.e. nodal forces) - tribol::allocRealArray( &mesh->fx1, mesh->numTotalNodes, 0. ); - tribol::allocRealArray( &mesh->fy1, mesh->numTotalNodes, 0. ); - tribol::allocRealArray( &mesh->fz1, mesh->numTotalNodes, 0. ); - tribol::allocRealArray( &mesh->fx2, mesh->numTotalNodes, 0. ); - tribol::allocRealArray( &mesh->fy2, mesh->numTotalNodes, 0. ); - tribol::allocRealArray( &mesh->fz2, mesh->numTotalNodes, 0. ); - - tribol::registerNodalResponse( mesh->mortarMeshId, - mesh->fx1, mesh->fy1, mesh->fz1 ); - - tribol::registerNodalResponse( mesh->nonmortarMeshId, - mesh->fx2, mesh->fy2, mesh->fz2 ); - - - // allocate velocity arrays on test mesh - RealT velX1 = 0.; - RealT velY1 = 0.; - RealT velZ1 = -1.; - RealT velX2 = 0.; - RealT velY2 = 0.; - RealT velZ2 = 1.; - mesh->allocateAndSetVelocities( mesh->mortarMeshId, velX1, velY1, velZ1 ); - mesh->allocateAndSetVelocities( mesh->nonmortarMeshId, velX2, velY2, velZ2 ); - - // register velocities with Tribol - tribol::registerNodalVelocities(mesh->mortarMeshId, mesh->vx1, mesh->vy1, mesh->vz1); - tribol::registerNodalVelocities(mesh->nonmortarMeshId, mesh->vx2, mesh->vy2, mesh->vz2); - - // register the coupling scheme - const int csIndex = 0; - tribol::registerCouplingScheme(csIndex, 0, 1, - tribol::SURFACE_TO_SURFACE, - tribol::NO_CASE, - tribol::COMMON_PLANE, - tribol::FRICTIONLESS, - tribol::PENALTY, - tribol::BINNING_GRID, - tribol::ExecutionMode::Sequential ); - - } - - void TearDown() override - { - clear(); - } - - void clear() - { - // no-op - } +class EnforcementOptionsTest : public ::testing::Test { + public: + protected: + void SetUp() override + { + // no-op + } + + // Setup boiler plate data and register mesh, nodal response, and coupling scheme + void SetupTest( tribol::TestMesh* mesh ) + { + //////////////////////////////////////////////// + // setup simple non-null contacting test mesh // + //////////////////////////////////////////////// + mesh->mortarMeshId = 0; + mesh->nonmortarMeshId = 1; + + int nMortarElems = 1; + int nElemsXM = nMortarElems; + int nElemsYM = nMortarElems; + int nElemsZM = nMortarElems; + + int nNonmortarElems = 1; + int nElemsXS = nNonmortarElems; + int nElemsYS = nNonmortarElems; + int nElemsZS = nNonmortarElems; + + // mesh bounding box with 0.1 interpenetration gap + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.05; + + RealT x_min2 = 0.; + RealT y_min2 = 0.; + RealT z_min2 = 0.95; + RealT x_max2 = 1.; + RealT y_max2 = 1.; + RealT z_max2 = 2.; + + mesh->setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, nElemsXS, + nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., 0. ); + + // register meshes + tribol::registerMesh( mesh->mortarMeshId, mesh->numMortarFaces, mesh->numTotalNodes, mesh->faceConn1, 3, mesh->x, + mesh->y, mesh->z, tribol::MemorySpace::Host ); + + tribol::registerMesh( mesh->nonmortarMeshId, mesh->numNonmortarFaces, mesh->numTotalNodes, mesh->faceConn2, 3, + mesh->x, mesh->y, mesh->z, tribol::MemorySpace::Host ); + + // register nodal responses (i.e. nodal forces) + tribol::allocRealArray( &mesh->fx1, mesh->numTotalNodes, 0. ); + tribol::allocRealArray( &mesh->fy1, mesh->numTotalNodes, 0. ); + tribol::allocRealArray( &mesh->fz1, mesh->numTotalNodes, 0. ); + tribol::allocRealArray( &mesh->fx2, mesh->numTotalNodes, 0. ); + tribol::allocRealArray( &mesh->fy2, mesh->numTotalNodes, 0. ); + tribol::allocRealArray( &mesh->fz2, mesh->numTotalNodes, 0. ); + + tribol::registerNodalResponse( mesh->mortarMeshId, mesh->fx1, mesh->fy1, mesh->fz1 ); + + tribol::registerNodalResponse( mesh->nonmortarMeshId, mesh->fx2, mesh->fy2, mesh->fz2 ); + + // allocate velocity arrays on test mesh + RealT velX1 = 0.; + RealT velY1 = 0.; + RealT velZ1 = -1.; + RealT velX2 = 0.; + RealT velY2 = 0.; + RealT velZ2 = 1.; + mesh->allocateAndSetVelocities( mesh->mortarMeshId, velX1, velY1, velZ1 ); + mesh->allocateAndSetVelocities( mesh->nonmortarMeshId, velX2, velY2, velZ2 ); + + // register velocities with Tribol + tribol::registerNodalVelocities( mesh->mortarMeshId, mesh->vx1, mesh->vy1, mesh->vz1 ); + tribol::registerNodalVelocities( mesh->nonmortarMeshId, mesh->vx2, mesh->vy2, mesh->vz2 ); + + // register the coupling scheme + const int csIndex = 0; + tribol::registerCouplingScheme( csIndex, 0, 1, tribol::SURFACE_TO_SURFACE, tribol::NO_CASE, tribol::COMMON_PLANE, + tribol::FRICTIONLESS, tribol::PENALTY, tribol::BINNING_GRID, + tribol::ExecutionMode::Sequential ); + } + + void TearDown() override { clear(); } + + void clear() + { + // no-op + } }; // TESTS1 // Coupling schemes with errors TEST_F( EnforcementOptionsTest, penalty_kinematic_constant_error ) { - // Setup boiler plate test data etc. - tribol::TestMesh* mesh = new tribol::TestMesh(); - SetupTest(mesh); + // Setup boiler plate test data etc. + tribol::TestMesh* mesh = new tribol::TestMesh(); + SetupTest( mesh ); - // set penalty data so coupling scheme initialization passes - RealT penalty = 1.0; - tribol::setKinematicConstantPenalty( 0, penalty ); - tribol::setKinematicConstantPenalty( 1, penalty ); + // set penalty data so coupling scheme initialization passes + RealT penalty = 1.0; + tribol::setKinematicConstantPenalty( 0, penalty ); + tribol::setKinematicConstantPenalty( 1, penalty ); - // incorrectly set penalty options with KINEMATIC_ELEMENT instead of the set KINEMATIC_CONSTANT - int csIndex = 0; - tribol::KinematicPenaltyCalculation wrong_calculation = tribol::KINEMATIC_ELEMENT; - tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC, wrong_calculation ); + // incorrectly set penalty options with KINEMATIC_ELEMENT instead of the set KINEMATIC_CONSTANT + int csIndex = 0; + tribol::KinematicPenaltyCalculation wrong_calculation = tribol::KINEMATIC_ELEMENT; + tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC, wrong_calculation ); - tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - tribol::CouplingScheme* scheme = &csManager.at(csIndex); - bool isInit = scheme->init(); + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingScheme* scheme = &csManager.at( csIndex ); + bool isInit = scheme->init(); - EXPECT_EQ( isInit, false ); + EXPECT_EQ( isInit, false ); - tribol::finalize(); + tribol::finalize(); - delete mesh; + delete mesh; } TEST_F( EnforcementOptionsTest, penalty_kinematic_element_error ) { - // Setup boiler plate test data etc. - tribol::TestMesh* mesh = new tribol::TestMesh(); - SetupTest(mesh); - - // set penalty data so coupling scheme initialization passes - RealT* bulk_modulus_1; - RealT* bulk_modulus_2; - RealT* element_thickness_1; - RealT* element_thickness_2; - - tribol::allocRealArray( &bulk_modulus_1, mesh->numMortarFaces, 0. ); - tribol::allocRealArray( &bulk_modulus_2, mesh->numNonmortarFaces, 0. ); - tribol::allocRealArray( &element_thickness_1, mesh->numMortarFaces, 0. ); - tribol::allocRealArray( &element_thickness_2, mesh->numNonmortarFaces, 0. ); - - tribol::setKinematicElementPenalty( mesh->mortarMeshId, bulk_modulus_1, element_thickness_1 ); - tribol::setKinematicElementPenalty( mesh->nonmortarMeshId, bulk_modulus_2, element_thickness_2 ); - - // 'incorrectly' set penalty options with KINEMATIC_CONSTANT instead of the set KINEMATIC_ELEMENT - int csIndex = 0; - tribol::KinematicPenaltyCalculation wrong_calculation = tribol::KINEMATIC_CONSTANT; - tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC, wrong_calculation ); - - tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - tribol::CouplingScheme* scheme = &csManager.at(csIndex); - bool isInit = scheme->init(); - - EXPECT_EQ( isInit, false ); - - tribol::finalize(); - - delete bulk_modulus_1; - delete bulk_modulus_2; - delete element_thickness_1; - delete element_thickness_2; - delete mesh; + // Setup boiler plate test data etc. + tribol::TestMesh* mesh = new tribol::TestMesh(); + SetupTest( mesh ); + + // set penalty data so coupling scheme initialization passes + RealT* bulk_modulus_1; + RealT* bulk_modulus_2; + RealT* element_thickness_1; + RealT* element_thickness_2; + + tribol::allocRealArray( &bulk_modulus_1, mesh->numMortarFaces, 0. ); + tribol::allocRealArray( &bulk_modulus_2, mesh->numNonmortarFaces, 0. ); + tribol::allocRealArray( &element_thickness_1, mesh->numMortarFaces, 0. ); + tribol::allocRealArray( &element_thickness_2, mesh->numNonmortarFaces, 0. ); + + tribol::setKinematicElementPenalty( mesh->mortarMeshId, bulk_modulus_1, element_thickness_1 ); + tribol::setKinematicElementPenalty( mesh->nonmortarMeshId, bulk_modulus_2, element_thickness_2 ); + + // 'incorrectly' set penalty options with KINEMATIC_CONSTANT instead of the set KINEMATIC_ELEMENT + int csIndex = 0; + tribol::KinematicPenaltyCalculation wrong_calculation = tribol::KINEMATIC_CONSTANT; + tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC, wrong_calculation ); + + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingScheme* scheme = &csManager.at( csIndex ); + bool isInit = scheme->init(); + + EXPECT_EQ( isInit, false ); + + tribol::finalize(); + + delete bulk_modulus_1; + delete bulk_modulus_2; + delete element_thickness_1; + delete element_thickness_2; + delete mesh; } TEST_F( EnforcementOptionsTest, penalty_kinematic_constant_rate_constant_error ) { - // Setup boiler plate test data etc. - tribol::TestMesh* mesh = new tribol::TestMesh(); - SetupTest(mesh); + // Setup boiler plate test data etc. + tribol::TestMesh* mesh = new tribol::TestMesh(); + SetupTest( mesh ); - // set penalty data so coupling scheme initialization passes - RealT penalty = 1.0; - tribol::setKinematicConstantPenalty( 0, penalty ); - tribol::setKinematicConstantPenalty( 1, penalty ); + // set penalty data so coupling scheme initialization passes + RealT penalty = 1.0; + tribol::setKinematicConstantPenalty( 0, penalty ); + tribol::setKinematicConstantPenalty( 1, penalty ); - RealT rate_penalty = 0.5; - tribol::setRateConstantPenalty( 0, rate_penalty ); - tribol::setRateConstantPenalty( 1, rate_penalty ); + RealT rate_penalty = 0.5; + tribol::setRateConstantPenalty( 0, rate_penalty ); + tribol::setRateConstantPenalty( 1, rate_penalty ); - // incorrectly set penalty options with RATE_PERCENT instead of the set RATE_CONSTANT - int csIndex = 0; - tribol::RatePenaltyCalculation wrong_calculation = tribol::RATE_PERCENT; - tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC_AND_RATE, - tribol::KINEMATIC_CONSTANT, wrong_calculation ); + // incorrectly set penalty options with RATE_PERCENT instead of the set RATE_CONSTANT + int csIndex = 0; + tribol::RatePenaltyCalculation wrong_calculation = tribol::RATE_PERCENT; + tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC_AND_RATE, tribol::KINEMATIC_CONSTANT, wrong_calculation ); - tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - tribol::CouplingScheme* scheme = &csManager.at(csIndex); - bool isInit = scheme->init(); + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingScheme* scheme = &csManager.at( csIndex ); + bool isInit = scheme->init(); - EXPECT_EQ( isInit, false ); + EXPECT_EQ( isInit, false ); - tribol::finalize(); + tribol::finalize(); - delete mesh; + delete mesh; } TEST_F( EnforcementOptionsTest, penalty_kinematic_constant_rate_percent_error_1 ) { - // Setup boiler plate test data etc. - tribol::TestMesh* mesh = new tribol::TestMesh(); - SetupTest(mesh); + // Setup boiler plate test data etc. + tribol::TestMesh* mesh = new tribol::TestMesh(); + SetupTest( mesh ); - // set penalty data so coupling scheme initialization passes - RealT penalty = 1.0; - tribol::setKinematicConstantPenalty( 0, penalty ); - tribol::setKinematicConstantPenalty( 1, penalty ); + // set penalty data so coupling scheme initialization passes + RealT penalty = 1.0; + tribol::setKinematicConstantPenalty( 0, penalty ); + tribol::setKinematicConstantPenalty( 1, penalty ); - RealT rate_percent = 0.5; - tribol::setRatePercentPenalty( 0, rate_percent ); - tribol::setRatePercentPenalty( 1, rate_percent ); + RealT rate_percent = 0.5; + tribol::setRatePercentPenalty( 0, rate_percent ); + tribol::setRatePercentPenalty( 1, rate_percent ); - // incorrectly set penalty options with RATE_CONSTANT instead of the set RATE_PERCENT - int csIndex = 0; - tribol::RatePenaltyCalculation wrong_calculation = tribol::RATE_CONSTANT; - tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC_AND_RATE, - tribol::KINEMATIC_CONSTANT, wrong_calculation ); + // incorrectly set penalty options with RATE_CONSTANT instead of the set RATE_PERCENT + int csIndex = 0; + tribol::RatePenaltyCalculation wrong_calculation = tribol::RATE_CONSTANT; + tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC_AND_RATE, tribol::KINEMATIC_CONSTANT, wrong_calculation ); - tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - tribol::CouplingScheme* scheme = &csManager.at(csIndex); - bool isInit = scheme->init(); + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingScheme* scheme = &csManager.at( csIndex ); + bool isInit = scheme->init(); - EXPECT_EQ( isInit, false ); + EXPECT_EQ( isInit, false ); - tribol::finalize(); + tribol::finalize(); - delete mesh; + delete mesh; } TEST_F( EnforcementOptionsTest, penalty_kinematic_constant_rate_percent_error_2 ) { - // Setup boiler plate test data etc. - tribol::TestMesh* mesh = new tribol::TestMesh(); - SetupTest(mesh); + // Setup boiler plate test data etc. + tribol::TestMesh* mesh = new tribol::TestMesh(); + SetupTest( mesh ); - // set penalty data so coupling scheme initialization passes - RealT penalty = 1.0; - tribol::setKinematicConstantPenalty( 0, penalty ); - tribol::setKinematicConstantPenalty( 1, penalty ); + // set penalty data so coupling scheme initialization passes + RealT penalty = 1.0; + tribol::setKinematicConstantPenalty( 0, penalty ); + tribol::setKinematicConstantPenalty( 1, penalty ); - // incorrectly set the rate_percent value outside of acceptable bounds - RealT rate_percent = 1.2; - tribol::setRatePercentPenalty( 0, rate_percent ); - tribol::setRatePercentPenalty( 1, rate_percent ); + // incorrectly set the rate_percent value outside of acceptable bounds + RealT rate_percent = 1.2; + tribol::setRatePercentPenalty( 0, rate_percent ); + tribol::setRatePercentPenalty( 1, rate_percent ); - // (correctly) set penalty options with KINEMATIC_CONSTANT and RATE_PERCENT - int csIndex = 0; - tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC_AND_RATE, - tribol::KINEMATIC_CONSTANT, tribol::RATE_PERCENT ); + // (correctly) set penalty options with KINEMATIC_CONSTANT and RATE_PERCENT + int csIndex = 0; + tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC_AND_RATE, tribol::KINEMATIC_CONSTANT, tribol::RATE_PERCENT ); - tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - tribol::CouplingScheme* scheme = &csManager.at(csIndex); - bool isInit = scheme->init(); + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingScheme* scheme = &csManager.at( csIndex ); + bool isInit = scheme->init(); - EXPECT_EQ( isInit, false ); + EXPECT_EQ( isInit, false ); - tribol::finalize(); + tribol::finalize(); - delete mesh; + delete mesh; } // TESTS2 // Coupling schemes with no errors TEST_F( EnforcementOptionsTest, penalty_kinematic_constant_pass ) { - // Setup boiler plate test data etc. - tribol::TestMesh* mesh = new tribol::TestMesh(); - SetupTest(mesh); + // Setup boiler plate test data etc. + tribol::TestMesh* mesh = new tribol::TestMesh(); + SetupTest( mesh ); - // set penalty data so coupling scheme initialization passes - RealT penalty = 1.0; - tribol::setKinematicConstantPenalty( 0, penalty ); - tribol::setKinematicConstantPenalty( 1, penalty ); + // set penalty data so coupling scheme initialization passes + RealT penalty = 1.0; + tribol::setKinematicConstantPenalty( 0, penalty ); + tribol::setKinematicConstantPenalty( 1, penalty ); - // set penalty options with KINEMATIC_CONSTANT - int csIndex = 0; - tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC, tribol::KINEMATIC_CONSTANT ); + // set penalty options with KINEMATIC_CONSTANT + int csIndex = 0; + tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC, tribol::KINEMATIC_CONSTANT ); - tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - tribol::CouplingScheme* scheme = &csManager.at(csIndex); - bool isInit = scheme->init(); + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingScheme* scheme = &csManager.at( csIndex ); + bool isInit = scheme->init(); - EXPECT_EQ( isInit, true ); + EXPECT_EQ( isInit, true ); - tribol::finalize(); + tribol::finalize(); - delete mesh; + delete mesh; } TEST_F( EnforcementOptionsTest, penalty_kinematic_element_pass ) { - // Setup boiler plate test data etc. - tribol::TestMesh* mesh = new tribol::TestMesh(); - SetupTest(mesh); + // Setup boiler plate test data etc. + tribol::TestMesh* mesh = new tribol::TestMesh(); + SetupTest( mesh ); - // set penalty data so coupling scheme initialization passes - RealT* bulk_modulus_1; - RealT* bulk_modulus_2; - RealT* element_thickness_1; - RealT* element_thickness_2; + // set penalty data so coupling scheme initialization passes + RealT* bulk_modulus_1; + RealT* bulk_modulus_2; + RealT* element_thickness_1; + RealT* element_thickness_2; - tribol::allocRealArray( &bulk_modulus_1, mesh->numMortarFaces, 1. ); - tribol::allocRealArray( &bulk_modulus_2, mesh->numNonmortarFaces, 1. ); - tribol::allocRealArray( &element_thickness_1, mesh->numMortarFaces, 1. ); - tribol::allocRealArray( &element_thickness_2, mesh->numNonmortarFaces, 1. ); + tribol::allocRealArray( &bulk_modulus_1, mesh->numMortarFaces, 1. ); + tribol::allocRealArray( &bulk_modulus_2, mesh->numNonmortarFaces, 1. ); + tribol::allocRealArray( &element_thickness_1, mesh->numMortarFaces, 1. ); + tribol::allocRealArray( &element_thickness_2, mesh->numNonmortarFaces, 1. ); - tribol::setKinematicElementPenalty( mesh->mortarMeshId, bulk_modulus_1, element_thickness_1 ); - tribol::setKinematicElementPenalty( mesh->nonmortarMeshId, bulk_modulus_2, element_thickness_2 ); + tribol::setKinematicElementPenalty( mesh->mortarMeshId, bulk_modulus_1, element_thickness_1 ); + tribol::setKinematicElementPenalty( mesh->nonmortarMeshId, bulk_modulus_2, element_thickness_2 ); - // set penalty options with KINEMATIC_ELEMENT - int csIndex = 0; - tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC, tribol::KINEMATIC_ELEMENT ); + // set penalty options with KINEMATIC_ELEMENT + int csIndex = 0; + tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC, tribol::KINEMATIC_ELEMENT ); - tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - tribol::CouplingScheme* scheme = &csManager.at(csIndex); - bool isInit = scheme->init(); + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingScheme* scheme = &csManager.at( csIndex ); + bool isInit = scheme->init(); - EXPECT_EQ( isInit, true ); + EXPECT_EQ( isInit, true ); - tribol::finalize(); + tribol::finalize(); - delete bulk_modulus_1; - delete bulk_modulus_2; - delete element_thickness_1; - delete element_thickness_2; - delete mesh; + delete bulk_modulus_1; + delete bulk_modulus_2; + delete element_thickness_1; + delete element_thickness_2; + delete mesh; } TEST_F( EnforcementOptionsTest, penalty_kinematic_element_invalid_element_input ) { - // Setup boiler plate test data etc. - tribol::TestMesh* mesh = new tribol::TestMesh(); - SetupTest(mesh); + // Setup boiler plate test data etc. + tribol::TestMesh* mesh = new tribol::TestMesh(); + SetupTest( mesh ); - // set penalty data so coupling scheme initialization passes - RealT* bulk_modulus_1; - RealT* bulk_modulus_2; - RealT* element_thickness_1; - RealT* element_thickness_2; + // set penalty data so coupling scheme initialization passes + RealT* bulk_modulus_1; + RealT* bulk_modulus_2; + RealT* element_thickness_1; + RealT* element_thickness_2; - tribol::allocRealArray( &bulk_modulus_1, mesh->numMortarFaces, 0. ); - tribol::allocRealArray( &bulk_modulus_2, mesh->numNonmortarFaces, 0. ); - tribol::allocRealArray( &element_thickness_1, mesh->numMortarFaces, 0. ); - tribol::allocRealArray( &element_thickness_2, mesh->numNonmortarFaces, 0. ); + tribol::allocRealArray( &bulk_modulus_1, mesh->numMortarFaces, 0. ); + tribol::allocRealArray( &bulk_modulus_2, mesh->numNonmortarFaces, 0. ); + tribol::allocRealArray( &element_thickness_1, mesh->numMortarFaces, 0. ); + tribol::allocRealArray( &element_thickness_2, mesh->numNonmortarFaces, 0. ); - tribol::setKinematicElementPenalty( mesh->mortarMeshId, bulk_modulus_1, element_thickness_1 ); - tribol::setKinematicElementPenalty( mesh->nonmortarMeshId, bulk_modulus_2, element_thickness_2 ); + tribol::setKinematicElementPenalty( mesh->mortarMeshId, bulk_modulus_1, element_thickness_1 ); + tribol::setKinematicElementPenalty( mesh->nonmortarMeshId, bulk_modulus_2, element_thickness_2 ); - // set penalty options with KINEMATIC_ELEMENT - int csIndex = 0; - tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC, tribol::KINEMATIC_ELEMENT ); + // set penalty options with KINEMATIC_ELEMENT + int csIndex = 0; + tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC, tribol::KINEMATIC_ELEMENT ); - tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - tribol::CouplingScheme* scheme = &csManager.at(csIndex); - bool isInit = scheme->init(); + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingScheme* scheme = &csManager.at( csIndex ); + bool isInit = scheme->init(); - EXPECT_EQ( isInit, false ); + EXPECT_EQ( isInit, false ); - tribol::finalize(); + tribol::finalize(); - delete bulk_modulus_1; - delete bulk_modulus_2; - delete element_thickness_1; - delete element_thickness_2; - delete mesh; + delete bulk_modulus_1; + delete bulk_modulus_2; + delete element_thickness_1; + delete element_thickness_2; + delete mesh; } TEST_F( EnforcementOptionsTest, penalty_kinematic_constant_rate_constant_pass ) { - // Setup boiler plate test data etc. - tribol::TestMesh* mesh = new tribol::TestMesh(); - SetupTest(mesh); + // Setup boiler plate test data etc. + tribol::TestMesh* mesh = new tribol::TestMesh(); + SetupTest( mesh ); - // set penalty data so coupling scheme initialization passes - RealT penalty = 1.0; - tribol::setKinematicConstantPenalty( 0, penalty ); - tribol::setKinematicConstantPenalty( 1, penalty ); + // set penalty data so coupling scheme initialization passes + RealT penalty = 1.0; + tribol::setKinematicConstantPenalty( 0, penalty ); + tribol::setKinematicConstantPenalty( 1, penalty ); - RealT rate_penalty = 0.5; - tribol::setRateConstantPenalty( 0, rate_penalty ); - tribol::setRateConstantPenalty( 1, rate_penalty ); + RealT rate_penalty = 0.5; + tribol::setRateConstantPenalty( 0, rate_penalty ); + tribol::setRateConstantPenalty( 1, rate_penalty ); - // set penalty options with KINEMATIC_CONSTANT and RATE_CONSTANT - int csIndex = 0; - tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC_AND_RATE, - tribol::KINEMATIC_CONSTANT, tribol::RATE_CONSTANT ); + // set penalty options with KINEMATIC_CONSTANT and RATE_CONSTANT + int csIndex = 0; + tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC_AND_RATE, tribol::KINEMATIC_CONSTANT, tribol::RATE_CONSTANT ); - tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - tribol::CouplingScheme* scheme = &csManager.at(csIndex); - bool isInit = scheme->init(); + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingScheme* scheme = &csManager.at( csIndex ); + bool isInit = scheme->init(); - EXPECT_EQ( isInit, true ); + EXPECT_EQ( isInit, true ); - tribol::finalize(); + tribol::finalize(); - delete mesh; + delete mesh; } TEST_F( EnforcementOptionsTest, penalty_kinematic_constant_rate_percent_pass ) { - // Setup boiler plate test data etc. - tribol::TestMesh* mesh = new tribol::TestMesh(); - SetupTest(mesh); + // Setup boiler plate test data etc. + tribol::TestMesh* mesh = new tribol::TestMesh(); + SetupTest( mesh ); - // set penalty data so coupling scheme initialization passes - RealT penalty = 1.0; - tribol::setKinematicConstantPenalty( 0, penalty ); - tribol::setKinematicConstantPenalty( 1, penalty ); + // set penalty data so coupling scheme initialization passes + RealT penalty = 1.0; + tribol::setKinematicConstantPenalty( 0, penalty ); + tribol::setKinematicConstantPenalty( 1, penalty ); - RealT rate_percent = 0.5; - tribol::setRatePercentPenalty( 0, rate_percent ); - tribol::setRatePercentPenalty( 1, rate_percent ); + RealT rate_percent = 0.5; + tribol::setRatePercentPenalty( 0, rate_percent ); + tribol::setRatePercentPenalty( 1, rate_percent ); - // set penalty options with KINEMATIC_CONSTANT and RATE_PERCENT - int csIndex = 0; - tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC_AND_RATE, - tribol::KINEMATIC_CONSTANT, tribol::RATE_PERCENT ); + // set penalty options with KINEMATIC_CONSTANT and RATE_PERCENT + int csIndex = 0; + tribol::setPenaltyOptions( csIndex, tribol::KINEMATIC_AND_RATE, tribol::KINEMATIC_CONSTANT, tribol::RATE_PERCENT ); - tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); - tribol::CouplingScheme* scheme = &csManager.at(csIndex); - bool isInit = scheme->init(); + tribol::CouplingSchemeManager& csManager = tribol::CouplingSchemeManager::getInstance(); + tribol::CouplingScheme* scheme = &csManager.at( csIndex ); + bool isInit = scheme->init(); - EXPECT_EQ( isInit, true ); + EXPECT_EQ( isInit, true ); - tribol::finalize(); + tribol::finalize(); - delete mesh; + delete mesh; } -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); #ifdef TRIBOL_USE_UMPIRE umpire::ResourceManager::getInstance(); // initialize umpire's ResouceManager diff --git a/src/tests/tribol_execution_modes.cpp b/src/tests/tribol_execution_modes.cpp index 2d246f39..a57fd959 100644 --- a/src/tests/tribol_execution_modes.cpp +++ b/src/tests/tribol_execution_modes.cpp @@ -15,7 +15,6 @@ #include "umpire/ResourceManager.hpp" #endif - namespace tribol { /** @@ -23,17 +22,16 @@ namespace tribol { * tribol::MemorySpace and a suggested tribol::ExecutionMode in a coupling * scheme. */ -class ExecutionModeTest : public testing::TestWithParam> // deduced execution mode +class ExecutionModeTest : public testing::TestWithParam> // deduced execution mode { -protected: + protected: ExecutionMode returned_mode_; - void PrintMemorySpace(MemorySpace space) const + void PrintMemorySpace( MemorySpace space ) const { - switch (space) - { + switch ( space ) { case MemorySpace::Dynamic: std::cout << "Dynamic"; break; @@ -51,10 +49,9 @@ class ExecutionModeTest : public testing::TestWithParam(GetParam())); + PrintMemorySpace( std::get<0>( GetParam() ) ); std::cout << " Given: "; - PrintExecutionMode(std::get<1>(GetParam())); + PrintExecutionMode( std::get<1>( GetParam() ) ); std::cout << " Expected: "; - PrintExecutionMode(std::get<2>(GetParam())); + PrintExecutionMode( std::get<2>( GetParam() ) ); constexpr IndexT cs_id = 0; constexpr IndexT mesh_id = 0; - registerMesh(mesh_id, 0, 0, nullptr, InterfaceElementType::LINEAR_QUAD, - nullptr, nullptr, nullptr, std::get<0>(GetParam())); - CouplingScheme cs( - cs_id, mesh_id, mesh_id, - ContactMode::SURFACE_TO_SURFACE, - ContactCase::NO_CASE, - ContactMethod::COMMON_PLANE, - ContactModel::FRICTIONLESS, - EnforcementMethod::PENALTY, - BinningMethod::BINNING_BVH, - std::get<1>(GetParam()) - ); + registerMesh( mesh_id, 0, 0, nullptr, InterfaceElementType::LINEAR_QUAD, nullptr, nullptr, nullptr, + std::get<0>( GetParam() ) ); + CouplingScheme cs( cs_id, mesh_id, mesh_id, ContactMode::SURFACE_TO_SURFACE, ContactCase::NO_CASE, + ContactMethod::COMMON_PLANE, ContactModel::FRICTIONLESS, EnforcementMethod::PENALTY, + BinningMethod::BINNING_BVH, std::get<1>( GetParam() ) ); cs.setMeshPointers(); cs.checkExecutionModeData(); returned_mode_ = cs.getExecutionMode(); std::cout << " Deduced: "; - PrintExecutionMode(returned_mode_); + PrintExecutionMode( returned_mode_ ); std::cout << std::endl; } }; -TEST_P(ExecutionModeTest, test_mode) +TEST_P( ExecutionModeTest, test_mode ) { - EXPECT_EQ(returned_mode_, std::get<2>(GetParam())); + EXPECT_EQ( returned_mode_, std::get<2>( GetParam() ) ); - MPI_Barrier(MPI_COMM_WORLD); + MPI_Barrier( MPI_COMM_WORLD ); } -INSTANTIATE_TEST_SUITE_P(tribol, ExecutionModeTest, testing::Values( - std::make_tuple(tribol::MemorySpace::Host, tribol::ExecutionMode::Sequential, tribol::ExecutionMode::Sequential) +INSTANTIATE_TEST_SUITE_P( + tribol, ExecutionModeTest, + testing::Values( + std::make_tuple( tribol::MemorySpace::Host, tribol::ExecutionMode::Sequential, + tribol::ExecutionMode::Sequential ) #ifdef TRIBOL_USE_OPENMP - , std::make_tuple(tribol::MemorySpace::Host, tribol::ExecutionMode::Dynamic, tribol::ExecutionMode::OpenMP) + , + std::make_tuple( tribol::MemorySpace::Host, tribol::ExecutionMode::Dynamic, tribol::ExecutionMode::OpenMP ) #else - , std::make_tuple(tribol::MemorySpace::Host, tribol::ExecutionMode::Dynamic, tribol::ExecutionMode::Sequential) + , + std::make_tuple( tribol::MemorySpace::Host, tribol::ExecutionMode::Dynamic, tribol::ExecutionMode::Sequential ) #endif #ifdef TRIBOL_USE_CUDA - // error:, std::make_tuple(tribol::MemorySpace::Host, tribol::ExecutionMode::Cuda, tribol::ExecutionMode::Sequential) +// error:, std::make_tuple(tribol::MemorySpace::Host, tribol::ExecutionMode::Cuda, tribol::ExecutionMode::Sequential) #endif #ifdef TRIBOL_USE_HIP - // error: , std::make_tuple(tribol::MemorySpace::Host, tribol::ExecutionMode::Hip, tribol::ExecutionMode::Sequential) +// error: , std::make_tuple(tribol::MemorySpace::Host, tribol::ExecutionMode::Hip, tribol::ExecutionMode::Sequential) #endif #ifdef TRIBOL_USE_OPENMP - , std::make_tuple(tribol::MemorySpace::Host, tribol::ExecutionMode::OpenMP, tribol::ExecutionMode::OpenMP) -#endif - , std::make_tuple(tribol::MemorySpace::Dynamic, tribol::ExecutionMode::Sequential, tribol::ExecutionMode::Sequential) - // error: , std::make_tuple(tribol::MemorySpace::Dynamic, tribol::ExecutionMode::Dynamic, tribol::ExecutionMode::Sequential) + , + std::make_tuple( tribol::MemorySpace::Host, tribol::ExecutionMode::OpenMP, tribol::ExecutionMode::OpenMP ) +#endif + , + std::make_tuple( tribol::MemorySpace::Dynamic, tribol::ExecutionMode::Sequential, + tribol::ExecutionMode::Sequential ) +// error: , std::make_tuple(tribol::MemorySpace::Dynamic, tribol::ExecutionMode::Dynamic, +// tribol::ExecutionMode::Sequential) #ifdef TRIBOL_USE_CUDA - , std::make_tuple(tribol::MemorySpace::Dynamic, tribol::ExecutionMode::Cuda, tribol::ExecutionMode::Cuda) + , + std::make_tuple( tribol::MemorySpace::Dynamic, tribol::ExecutionMode::Cuda, tribol::ExecutionMode::Cuda ) #endif #ifdef TRIBOL_USE_HIP - , std::make_tuple(tribol::MemorySpace::Dynamic, tribol::ExecutionMode::Hip, tribol::ExecutionMode::Hip) + , + std::make_tuple( tribol::MemorySpace::Dynamic, tribol::ExecutionMode::Hip, tribol::ExecutionMode::Hip ) #endif #ifdef TRIBOL_USE_OPENMP - , std::make_tuple(tribol::MemorySpace::Dynamic, tribol::ExecutionMode::OpenMP, tribol::ExecutionMode::OpenMP) + , + std::make_tuple( tribol::MemorySpace::Dynamic, tribol::ExecutionMode::OpenMP, tribol::ExecutionMode::OpenMP ) #endif #ifdef TRIBOL_USE_UMPIRE - // error: , std::make_tuple(tribol::MemorySpace::Device, tribol::ExecutionMode::Sequential, tribol::ExecutionMode::Sequential) -#if defined(TRIBOL_USE_CUDA) - , std::make_tuple(tribol::MemorySpace::Device, tribol::ExecutionMode::Dynamic, tribol::ExecutionMode::Cuda) -#elif defined(TRIBOL_USE_HIP) - , std::make_tuple(tribol::MemorySpace::Device, tribol::ExecutionMode::Dynamic, tribol::ExecutionMode::Hip) +// error: , std::make_tuple(tribol::MemorySpace::Device, tribol::ExecutionMode::Sequential, +// tribol::ExecutionMode::Sequential) +#if defined( TRIBOL_USE_CUDA ) + , + std::make_tuple( tribol::MemorySpace::Device, tribol::ExecutionMode::Dynamic, tribol::ExecutionMode::Cuda ) +#elif defined( TRIBOL_USE_HIP ) + , + std::make_tuple( tribol::MemorySpace::Device, tribol::ExecutionMode::Dynamic, tribol::ExecutionMode::Hip ) #else - // error: , std::make_tuple(tribol::MemorySpace::Device, tribol::ExecutionMode::Dynamic, tribol::ExecutionMode::Sequential) +// error: , std::make_tuple(tribol::MemorySpace::Device, tribol::ExecutionMode::Dynamic, +// tribol::ExecutionMode::Sequential) #endif #ifdef TRIBOL_USE_CUDA - , std::make_tuple(tribol::MemorySpace::Device, tribol::ExecutionMode::Cuda, tribol::ExecutionMode::Cuda) + , + std::make_tuple( tribol::MemorySpace::Device, tribol::ExecutionMode::Cuda, tribol::ExecutionMode::Cuda ) #endif #ifdef TRIBOL_USE_HIP - , std::make_tuple(tribol::MemorySpace::Device, tribol::ExecutionMode::Hip, tribol::ExecutionMode::Hip) + , + std::make_tuple( tribol::MemorySpace::Device, tribol::ExecutionMode::Hip, tribol::ExecutionMode::Hip ) #endif #ifdef TRIBOL_USE_OPENMP - // error: , std::make_tuple(tribol::MemorySpace::Device, tribol::ExecutionMode::OpenMP, tribol::ExecutionMode::Sequential) -#endif -#if defined(TRIBOL_USE_HIP) || defined(TRIBOL_USE_CUDA) - , std::make_tuple(tribol::MemorySpace::Unified, tribol::ExecutionMode::Sequential, tribol::ExecutionMode::Sequential) -#if defined(TRIBOL_USE_CUDA) - , std::make_tuple(tribol::MemorySpace::Unified, tribol::ExecutionMode::Dynamic, tribol::ExecutionMode::Cuda) -#elif defined(TRIBOL_USE_HIP) - , std::make_tuple(tribol::MemorySpace::Unified, tribol::ExecutionMode::Dynamic, tribol::ExecutionMode::Hip) -#elif defined(TRIBOL_USE_OPENMP) - , std::make_tuple(tribol::MemorySpace::Unified, tribol::ExecutionMode::Dynamic, tribol::ExecutionMode::OpenMP) +// error: , std::make_tuple(tribol::MemorySpace::Device, tribol::ExecutionMode::OpenMP, +// tribol::ExecutionMode::Sequential) +#endif +#if defined( TRIBOL_USE_HIP ) || defined( TRIBOL_USE_CUDA ) + , + std::make_tuple( tribol::MemorySpace::Unified, tribol::ExecutionMode::Sequential, + tribol::ExecutionMode::Sequential ) +#if defined( TRIBOL_USE_CUDA ) + , + std::make_tuple( tribol::MemorySpace::Unified, tribol::ExecutionMode::Dynamic, tribol::ExecutionMode::Cuda ) +#elif defined( TRIBOL_USE_HIP ) + , + std::make_tuple( tribol::MemorySpace::Unified, tribol::ExecutionMode::Dynamic, tribol::ExecutionMode::Hip ) +#elif defined( TRIBOL_USE_OPENMP ) + , + std::make_tuple( tribol::MemorySpace::Unified, tribol::ExecutionMode::Dynamic, tribol::ExecutionMode::OpenMP ) #else - , std::make_tuple(tribol::MemorySpace::Unified, tribol::ExecutionMode::Dynamic, tribol::ExecutionMode::Sequential) + , + std::make_tuple( tribol::MemorySpace::Unified, tribol::ExecutionMode::Dynamic, + tribol::ExecutionMode::Sequential ) #endif #ifdef TRIBOL_USE_CUDA - , std::make_tuple(tribol::MemorySpace::Unified, tribol::ExecutionMode::Cuda, tribol::ExecutionMode::Cuda) + , + std::make_tuple( tribol::MemorySpace::Unified, tribol::ExecutionMode::Cuda, tribol::ExecutionMode::Cuda ) #endif #ifdef TRIBOL_USE_HIP - , std::make_tuple(tribol::MemorySpace::Unified, tribol::ExecutionMode::Hip, tribol::ExecutionMode::Hip) + , + std::make_tuple( tribol::MemorySpace::Unified, tribol::ExecutionMode::Hip, tribol::ExecutionMode::Hip ) #endif #ifdef TRIBOL_USE_OPENMP - , std::make_tuple(tribol::MemorySpace::Unified, tribol::ExecutionMode::OpenMP, tribol::ExecutionMode::OpenMP) + , + std::make_tuple( tribol::MemorySpace::Unified, tribol::ExecutionMode::OpenMP, tribol::ExecutionMode::OpenMP ) #endif #endif #endif -)); + ) ); -} // namespace tribol +} // namespace tribol //------------------------------------------------------------------------------ #include "axom/slic/core/SimpleLogger.hpp" -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - MPI_Init(&argc, &argv); + MPI_Init( &argc, &argv ); - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); #ifdef TRIBOL_USE_UMPIRE umpire::ResourceManager::getInstance(); // initialize umpire's ResouceManager diff --git a/src/tests/tribol_hex_mesh.cpp b/src/tests/tribol_hex_mesh.cpp index 8c0398d2..b4c297b1 100644 --- a/src/tests/tribol_hex_mesh.cpp +++ b/src/tests/tribol_hex_mesh.cpp @@ -15,7 +15,7 @@ #include "gtest/gtest.h" // c++ includes -#include // std::abs, std::cos, std::sin +#include // std::abs, std::cos, std::sin #include #include #include @@ -24,153 +24,136 @@ using RealT = tribol::RealT; /*! - * Test fixture class with some setup necessary to test - * the TestMesh hex mesh feature + * Test fixture class with some setup necessary to test + * the TestMesh hex mesh feature */ -class HexMeshTest : public ::testing::Test -{ - -public: - - tribol::TestMesh m_mesh; - -protected: - - void SetUp() override - { - } +class HexMeshTest : public ::testing::Test { + public: + tribol::TestMesh m_mesh; - void TearDown() override - { - this->m_mesh.clear(); - } + protected: + void SetUp() override {} -protected: + void TearDown() override { this->m_mesh.clear(); } + protected: }; TEST_F( HexMeshTest, build_and_check_hex_mesh ) { - this->m_mesh.mortarMeshId = 0; - this->m_mesh.nonmortarMeshId = 1; - - int nMortarElems = 4; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 3; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - // mesh bounding box with 0.1 interpenetration gap - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.05; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 0.95; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 5., -5. ); - - // sanity checks for this specific mesh - EXPECT_EQ( this->m_mesh.mesh_constructed, true ); - EXPECT_EQ( this->m_mesh.cellType, (int)(tribol::LINEAR_QUAD) ); - EXPECT_EQ( this->m_mesh.numNodesPerFace, 4 ); - EXPECT_EQ( this->m_mesh.numNodesPerElement, 8 ); - EXPECT_EQ( this->m_mesh.numMortarElements, nMortarElems*nMortarElems*nMortarElems ); - EXPECT_EQ( this->m_mesh.numNonmortarElements, nNonmortarElems*nNonmortarElems*nNonmortarElems ); - EXPECT_EQ( this->m_mesh.numTotalElements, this->m_mesh.numMortarElements + this->m_mesh.numNonmortarElements ); - EXPECT_EQ( this->m_mesh.numMortarFaces, nMortarElems*nMortarElems ); - EXPECT_EQ( this->m_mesh.numNonmortarFaces, nNonmortarElems*nNonmortarElems ); - EXPECT_EQ( this->m_mesh.numMortarNodes, (nMortarElems+1)*(nMortarElems+1)*(nMortarElems+1) ); - EXPECT_EQ( this->m_mesh.numNonmortarNodes, (nNonmortarElems+1)*(nNonmortarElems+1)*(nNonmortarElems+1) ); - - //this->m_mesh.testMeshToVtk( "", 1, 1 ); + this->m_mesh.mortarMeshId = 0; + this->m_mesh.nonmortarMeshId = 1; + + int nMortarElems = 4; + int nElemsXM = nMortarElems; + int nElemsYM = nMortarElems; + int nElemsZM = nMortarElems; + + int nNonmortarElems = 3; + int nElemsXS = nNonmortarElems; + int nElemsYS = nNonmortarElems; + int nElemsZS = nNonmortarElems; + + // mesh bounding box with 0.1 interpenetration gap + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.05; + + RealT x_min2 = 0.; + RealT y_min2 = 0.; + RealT z_min2 = 0.95; + RealT x_max2 = 1.; + RealT y_max2 = 1.; + RealT z_max2 = 2.; + + this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 5., + -5. ); + + // sanity checks for this specific mesh + EXPECT_EQ( this->m_mesh.mesh_constructed, true ); + EXPECT_EQ( this->m_mesh.cellType, (int)( tribol::LINEAR_QUAD ) ); + EXPECT_EQ( this->m_mesh.numNodesPerFace, 4 ); + EXPECT_EQ( this->m_mesh.numNodesPerElement, 8 ); + EXPECT_EQ( this->m_mesh.numMortarElements, nMortarElems * nMortarElems * nMortarElems ); + EXPECT_EQ( this->m_mesh.numNonmortarElements, nNonmortarElems * nNonmortarElems * nNonmortarElems ); + EXPECT_EQ( this->m_mesh.numTotalElements, this->m_mesh.numMortarElements + this->m_mesh.numNonmortarElements ); + EXPECT_EQ( this->m_mesh.numMortarFaces, nMortarElems * nMortarElems ); + EXPECT_EQ( this->m_mesh.numNonmortarFaces, nNonmortarElems * nNonmortarElems ); + EXPECT_EQ( this->m_mesh.numMortarNodes, ( nMortarElems + 1 ) * ( nMortarElems + 1 ) * ( nMortarElems + 1 ) ); + EXPECT_EQ( this->m_mesh.numNonmortarNodes, + ( nNonmortarElems + 1 ) * ( nNonmortarElems + 1 ) * ( nNonmortarElems + 1 ) ); + + // this->m_mesh.testMeshToVtk( "", 1, 1 ); } TEST_F( HexMeshTest, build_and_check_mfem_hex_mesh ) { - this->m_mesh.mortarMeshId = 0; - this->m_mesh.nonmortarMeshId = 1; - - int nMortarElems = 5; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 4; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - // mesh bounding box with 0.1 interpenetration gap - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.05; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 0.95; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 5., -5. ); - - // setup mfem mesh, but don't fix orientations in order to - // check if underlying TestMesh hex mesh has orientation issues - this->m_mesh.setupMfemMesh( false ); - - // perform mfem mesh based sanity checks - EXPECT_EQ( this->m_mesh.mfem_mesh->SpaceDimension(), 3 ); - SLIC_INFO("number of elements: " << this->m_mesh.mfem_mesh->GetNE() << "."); - EXPECT_EQ( this->m_mesh.mfem_mesh->GetNE(), this->m_mesh.numMortarElements + this->m_mesh.numNonmortarElements ); - EXPECT_EQ( this->m_mesh.mfem_mesh->GetNV(), this->m_mesh.numMortarNodes + this->m_mesh.numNonmortarNodes ); - EXPECT_EQ( this->m_mesh.mfem_mesh->GetNFbyType(mfem::FaceType::Boundary), - 6 * (nMortarElems*nMortarElems + nNonmortarElems*nNonmortarElems) ); - - // check for inverted elements - int wrong_elem_orientation = -1; - int wrong_bdr_elem_orientation = -1; - bool fix_it = false; - wrong_elem_orientation = this->m_mesh.mfem_mesh->CheckElementOrientation( fix_it ); - wrong_bdr_elem_orientation = this->m_mesh.mfem_mesh->CheckBdrElementOrientation( fix_it ); - EXPECT_EQ( wrong_elem_orientation, 0 ); - EXPECT_EQ( wrong_bdr_elem_orientation, 0 ); - - //std::ofstream mfem_file("mfem_test_mesh.vtk"); - //this->m_mesh.mfem_mesh->PrintVTK(mfem_file); + this->m_mesh.mortarMeshId = 0; + this->m_mesh.nonmortarMeshId = 1; + + int nMortarElems = 5; + int nElemsXM = nMortarElems; + int nElemsYM = nMortarElems; + int nElemsZM = nMortarElems; + + int nNonmortarElems = 4; + int nElemsXS = nNonmortarElems; + int nElemsYS = nNonmortarElems; + int nElemsZS = nNonmortarElems; + + // mesh bounding box with 0.1 interpenetration gap + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.05; + + RealT x_min2 = 0.; + RealT y_min2 = 0.; + RealT z_min2 = 0.95; + RealT x_max2 = 1.; + RealT y_max2 = 1.; + RealT z_max2 = 2.; + + this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 5., + -5. ); + + // setup mfem mesh, but don't fix orientations in order to + // check if underlying TestMesh hex mesh has orientation issues + this->m_mesh.setupMfemMesh( false ); + + // perform mfem mesh based sanity checks + EXPECT_EQ( this->m_mesh.mfem_mesh->SpaceDimension(), 3 ); + SLIC_INFO( "number of elements: " << this->m_mesh.mfem_mesh->GetNE() << "." ); + EXPECT_EQ( this->m_mesh.mfem_mesh->GetNE(), this->m_mesh.numMortarElements + this->m_mesh.numNonmortarElements ); + EXPECT_EQ( this->m_mesh.mfem_mesh->GetNV(), this->m_mesh.numMortarNodes + this->m_mesh.numNonmortarNodes ); + EXPECT_EQ( this->m_mesh.mfem_mesh->GetNFbyType( mfem::FaceType::Boundary ), + 6 * ( nMortarElems * nMortarElems + nNonmortarElems * nNonmortarElems ) ); + + // check for inverted elements + int wrong_elem_orientation = -1; + int wrong_bdr_elem_orientation = -1; + bool fix_it = false; + wrong_elem_orientation = this->m_mesh.mfem_mesh->CheckElementOrientation( fix_it ); + wrong_bdr_elem_orientation = this->m_mesh.mfem_mesh->CheckBdrElementOrientation( fix_it ); + EXPECT_EQ( wrong_elem_orientation, 0 ); + EXPECT_EQ( wrong_bdr_elem_orientation, 0 ); + + // std::ofstream mfem_file("mfem_test_mesh.vtk"); + // this->m_mesh.mfem_mesh->PrintVTK(mfem_file); } -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); axom::slic::SimpleLogger logger; result = RUN_ALL_TESTS(); diff --git a/src/tests/tribol_inv_iso.cpp b/src/tests/tribol_inv_iso.cpp index e7bcf4c3..6055e68e 100644 --- a/src/tests/tribol_inv_iso.cpp +++ b/src/tests/tribol_inv_iso.cpp @@ -18,237 +18,201 @@ using RealT = tribol::RealT; /*! * Test fixture class with some setup necessary to use the - * inverse isoparametric routine + * inverse isoparametric routine */ -class InvIsoTest : public ::testing::Test -{ - -public: - int numNodes; - - RealT* getXCoords() - { - return this->x; - } - - RealT* getYCoords() - { - return this->y; - } - - RealT* getZCoords() - { - return this->z; - } - - - bool InvMap( RealT point[3], RealT const tol ) - { - - RealT x_sol[2]; - tribol::InvIso( point, this->x, this->y, this->z, 4, x_sol ); - - // test (xi,eta) obtained from inverse isoparametric - // mapping by performing forward map of that point and - // compare to original point. - RealT map_point[3] = {0., 0, 0}; - tribol::FwdMapLinQuad( x_sol, this->x, this->y, this->z, map_point ); - - bool convrg = false; - RealT res = tribol::magnitude((point[0] - map_point[0]), - (point[1] - map_point[1]), - (point[2] - map_point[2])); - - if (res < tol) - { - convrg = true; - } - - return convrg; - - } - -protected: - - void SetUp() override - { - this->numNodes = 4; - if (this->x == nullptr) - { - this->x = new RealT [this->numNodes]; - } - else - { - delete [] this->x; - this->x = new RealT [this->numNodes]; - } - - if (this->y == nullptr) - { - this->y = new RealT [this->numNodes]; - } - else - { - delete [] this->y; - this->y = new RealT [this->numNodes]; - } - - if (this->z == nullptr) - { - this->z = new RealT [this->numNodes]; - } - else - { - delete [] this->z; - this->z = new RealT [this->numNodes]; - } - } - - void TearDown() override - { - if (this->x != nullptr) - { - delete [] this->x; - this->x = nullptr; - } - if (this->y != nullptr) - { - delete [] this->y; - this->y = nullptr; - } - if (this->z != nullptr) - { - delete [] this->z; - this->z = nullptr; - } - } - -protected: - - RealT* x {nullptr}; - RealT* y {nullptr}; - RealT* z {nullptr}; - +class InvIsoTest : public ::testing::Test { + public: + int numNodes; + + RealT* getXCoords() { return this->x; } + + RealT* getYCoords() { return this->y; } + + RealT* getZCoords() { return this->z; } + + bool InvMap( RealT point[3], RealT const tol ) + { + RealT x_sol[2]; + tribol::InvIso( point, this->x, this->y, this->z, 4, x_sol ); + + // test (xi,eta) obtained from inverse isoparametric + // mapping by performing forward map of that point and + // compare to original point. + RealT map_point[3] = { 0., 0, 0 }; + tribol::FwdMapLinQuad( x_sol, this->x, this->y, this->z, map_point ); + + bool convrg = false; + RealT res = + tribol::magnitude( ( point[0] - map_point[0] ), ( point[1] - map_point[1] ), ( point[2] - map_point[2] ) ); + + if ( res < tol ) { + convrg = true; + } + + return convrg; + } + + protected: + void SetUp() override + { + this->numNodes = 4; + if ( this->x == nullptr ) { + this->x = new RealT[this->numNodes]; + } else { + delete[] this->x; + this->x = new RealT[this->numNodes]; + } + + if ( this->y == nullptr ) { + this->y = new RealT[this->numNodes]; + } else { + delete[] this->y; + this->y = new RealT[this->numNodes]; + } + + if ( this->z == nullptr ) { + this->z = new RealT[this->numNodes]; + } else { + delete[] this->z; + this->z = new RealT[this->numNodes]; + } + } + + void TearDown() override + { + if ( this->x != nullptr ) { + delete[] this->x; + this->x = nullptr; + } + if ( this->y != nullptr ) { + delete[] this->y; + this->y = nullptr; + } + if ( this->z != nullptr ) { + delete[] this->z; + this->z = nullptr; + } + } + + protected: + RealT* x{ nullptr }; + RealT* y{ nullptr }; + RealT* z{ nullptr }; }; - TEST_F( InvIsoTest, nonaffine_centroid ) { - RealT* x = this->getXCoords(); - RealT* y = this->getYCoords(); - RealT* z = this->getZCoords(); - - x[0] = -0.5; - x[1] = 0.5; - x[2] = 0.235; - x[3] = -0.35; - - y[0] = -0.25; - y[1] = -0.15; - y[2] = 0.25; - y[3] = 0.235; - - z[0] = 0.1; - z[1] = 0.1; - z[2] = 0.1; - z[3] = 0.1; - - RealT point[3]; - - // initialize physical point array - for (int i=0; i<3; ++i) - { - point[i] = 0.; - } - - // generate physical space point to be mapped as - // vertex averaged centroid of quad - for (int i=0; i<4; ++i) - { - point[0] += x[i]; - point[1] += y[i]; - point[2] += z[i]; - } - - // divide by number of nodes - point[0] /= 4; - point[1] /= 4; - point[2] /= 4; - - bool convrg = this->InvMap( point, 1.e-6 ); - - EXPECT_EQ( convrg, true ); - + RealT* x = this->getXCoords(); + RealT* y = this->getYCoords(); + RealT* z = this->getZCoords(); + + x[0] = -0.5; + x[1] = 0.5; + x[2] = 0.235; + x[3] = -0.35; + + y[0] = -0.25; + y[1] = -0.15; + y[2] = 0.25; + y[3] = 0.235; + + z[0] = 0.1; + z[1] = 0.1; + z[2] = 0.1; + z[3] = 0.1; + + RealT point[3]; + + // initialize physical point array + for ( int i = 0; i < 3; ++i ) { + point[i] = 0.; + } + + // generate physical space point to be mapped as + // vertex averaged centroid of quad + for ( int i = 0; i < 4; ++i ) { + point[0] += x[i]; + point[1] += y[i]; + point[2] += z[i]; + } + + // divide by number of nodes + point[0] /= 4; + point[1] /= 4; + point[2] /= 4; + + bool convrg = this->InvMap( point, 1.e-6 ); + + EXPECT_EQ( convrg, true ); } TEST_F( InvIsoTest, nonaffine_test_point ) { - RealT* x = this->getXCoords(); - RealT* y = this->getYCoords(); - RealT* z = this->getZCoords(); - - x[0] = -0.5; - x[1] = 0.5; - x[2] = 0.235; - x[3] = -0.35; + RealT* x = this->getXCoords(); + RealT* y = this->getYCoords(); + RealT* z = this->getZCoords(); - y[0] = -0.25; - y[1] = -0.15; - y[2] = 0.25; - y[3] = 0.235; + x[0] = -0.5; + x[1] = 0.5; + x[2] = 0.235; + x[3] = -0.35; - z[0] = 0.1; - z[1] = 0.1; - z[2] = 0.1; - z[3] = 0.1; + y[0] = -0.25; + y[1] = -0.15; + y[2] = 0.25; + y[3] = 0.235; - // hard code point - RealT point[3] = { 0.215, 0.116, 0.1 }; + z[0] = 0.1; + z[1] = 0.1; + z[2] = 0.1; + z[3] = 0.1; - bool convrg = this->InvMap( point, 1.e-6 ); + // hard code point + RealT point[3] = { 0.215, 0.116, 0.1 }; - EXPECT_EQ( convrg, true ); + bool convrg = this->InvMap( point, 1.e-6 ); + EXPECT_EQ( convrg, true ); } TEST_F( InvIsoTest, affine_test_point ) { - RealT* x = this->getXCoords(); - RealT* y = this->getYCoords(); - RealT* z = this->getZCoords(); + RealT* x = this->getXCoords(); + RealT* y = this->getYCoords(); + RealT* z = this->getZCoords(); - x[0] = -0.5; - x[1] = 0.5; - x[2] = 0.5; - x[3] = -0.5; + x[0] = -0.5; + x[1] = 0.5; + x[2] = 0.5; + x[3] = -0.5; - y[0] = -0.5; - y[1] = -0.5; - y[2] = 0.5; - y[3] = 0.5; + y[0] = -0.5; + y[1] = -0.5; + y[2] = 0.5; + y[3] = 0.5; - z[0] = 0.1; - z[1] = 0.1; - z[2] = 0.1; - z[3] = 0.1; + z[0] = 0.1; + z[1] = 0.1; + z[2] = 0.1; + z[3] = 0.1; - RealT point[3]; + RealT point[3]; - // hard-code point - point[0] = 0.25; - point[1] = 0.25; - point[2] = 0.1; + // hard-code point + point[0] = 0.25; + point[1] = 0.25; + point[2] = 0.1; - bool convrg = this->InvMap( point, 1.e-6 ); + bool convrg = this->InvMap( point, 1.e-6 ); - EXPECT_EQ( convrg, true ); + EXPECT_EQ( convrg, true ); } -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); axom::slic::SimpleLogger logger; diff --git a/src/tests/tribol_iso_integ.cpp b/src/tests/tribol_iso_integ.cpp index 331b83ba..c4648f83 100644 --- a/src/tests/tribol_iso_integ.cpp +++ b/src/tests/tribol_iso_integ.cpp @@ -17,292 +17,254 @@ #include "gtest/gtest.h" // c++ includes -#include // std::abs +#include // std::abs using RealT = tribol::RealT; /*! * Test fixture class with some setup necessary to use the - * triangular decomposition of a quadrilateral with integration - * points specified on each triangle's parent space, forward mapped - * to the physical triangle, and then mapped using the inverse - * isoparametric mapping in order to obtain (xi,eta) coordinates - * on the parent four node quad. These tests compute the area - * as calculated by summing integrals of shape functions defined + * triangular decomposition of a quadrilateral with integration + * points specified on each triangle's parent space, forward mapped + * to the physical triangle, and then mapped using the inverse + * isoparametric mapping in order to obtain (xi,eta) coordinates + * on the parent four node quad. These tests compute the area + * as calculated by summing integrals of shape functions defined * on the four node quad. */ -class IsoIntegTest : public ::testing::Test -{ - -public: - int numNodes; - int dim; - - RealT* getXCoords() - { - return x; - } - - RealT* getYCoords() - { - return y; - } - - RealT* getZCoords() - { - return z; - } - - bool integrate ( RealT const tol ) - { - RealT xyz[ this->dim * this->numNodes ]; - RealT* xy = xyz; - - RealT * x = this->x; - RealT * y = this->y; - RealT * z = this->z; - - // generate stacked coordinate array - for (int j=0; jnumNodes; ++j) - { - for (int k=0; kdim; ++k) - { - switch (k) - { - case 0: - xy[ this->dim * j + k ] = x[ j ]; - break; - case 1: - xy[ this->dim * j + k ] = y[ j ]; - break; - case 2: - xy[ this->dim * j + k ] = z[ j ]; - break; - } // end switch - } // end loop over dimension - } // end loop over nodes - - // instantiate SurfaceContactElem struct. Note, this object is instantiated - // using face 1 as face 2, but these faces are not used in this test so this - // is ok. - tribol::SurfaceContactElem elem ( this->dim, xy, xy, xy, - this->numNodes, this->numNodes, - nullptr, nullptr, 0, 0); - - // instantiate integration object - tribol::IntegPts integ; - - // generate all current configuration integration point coordinates and weights - tribol::GaussPolyIntTri( elem, integ, 2 ); - - // evaluate sum_a (integral_face (phi_a) da) with outer loop over nodes, a, and - // inner loop over number of integration points - RealT areaTest = 0.; - RealT phi = 0.; - - for (int a=0; anumNodes; ++a) - { - for (int ip=0; ipnumNodes, xi ); - tribol::LinIsoQuadShapeFunc( xi[0], xi[1], a, phi ); - - areaTest += integ.wts[ip]*phi; - - } - } - - RealT area = tribol::Area2DPolygon( x, y, this->numNodes ); - - bool convrg = (std::abs(areaTest - area) <= tol) ? true : false; - - return convrg; - - } - -protected: - - void SetUp() override - { - this->numNodes = 4; - this->dim = 3; - - if (this->x == nullptr) - { - this->x = new RealT [this->numNodes]; - } - else - { - delete [] this->x; - this->x = new RealT [this->numNodes]; - } - - if (this->y == nullptr) - { - this->y = new RealT [this->numNodes]; - } - else - { - delete [] this->y; - this->y = new RealT [this->numNodes]; +class IsoIntegTest : public ::testing::Test { + public: + int numNodes; + int dim; + + RealT* getXCoords() { return x; } + + RealT* getYCoords() { return y; } + + RealT* getZCoords() { return z; } + + bool integrate( RealT const tol ) + { + RealT xyz[this->dim * this->numNodes]; + RealT* xy = xyz; + + RealT* x = this->x; + RealT* y = this->y; + RealT* z = this->z; + + // generate stacked coordinate array + for ( int j = 0; j < this->numNodes; ++j ) { + for ( int k = 0; k < this->dim; ++k ) { + switch ( k ) { + case 0: + xy[this->dim * j + k] = x[j]; + break; + case 1: + xy[this->dim * j + k] = y[j]; + break; + case 2: + xy[this->dim * j + k] = z[j]; + break; + } // end switch + } // end loop over dimension + } // end loop over nodes + + // instantiate SurfaceContactElem struct. Note, this object is instantiated + // using face 1 as face 2, but these faces are not used in this test so this + // is ok. + tribol::SurfaceContactElem elem( this->dim, xy, xy, xy, this->numNodes, this->numNodes, nullptr, nullptr, 0, 0 ); + + // instantiate integration object + tribol::IntegPts integ; + + // generate all current configuration integration point coordinates and weights + tribol::GaussPolyIntTri( elem, integ, 2 ); + + // evaluate sum_a (integral_face (phi_a) da) with outer loop over nodes, a, and + // inner loop over number of integration points + RealT areaTest = 0.; + RealT phi = 0.; + + for ( int a = 0; a < this->numNodes; ++a ) { + for ( int ip = 0; ip < integ.numIPs; ++ip ) { + // perform inverse isoparametric mapping of current configuration + // integration point to four node quad parent space + RealT xp[3] = { integ.xy[dim * ip], integ.xy[dim * ip + 1], integ.xy[dim * ip + 2] }; + RealT xi[2] = { 0., 0. }; + tribol::InvIso( xp, x, y, z, this->numNodes, xi ); + tribol::LinIsoQuadShapeFunc( xi[0], xi[1], a, phi ); + + areaTest += integ.wts[ip] * phi; } - - if (this->z == nullptr) - { - this->z = new RealT [this->numNodes]; - } - else - { - delete [] this->z; - this->z = new RealT [this->numNodes]; - } - } - - void TearDown() override - { - if (this->x != nullptr) - { - delete [] this->x; - this->x = nullptr; - } - if (this->y != nullptr) - { - delete [] this->y; - this->y = nullptr; - } - if (this->z != nullptr) - { - delete [] this->z; - this->z = nullptr; - } - } - -protected: - - RealT* x {nullptr}; - RealT* y {nullptr}; - RealT* z {nullptr}; - + } + + RealT area = tribol::Area2DPolygon( x, y, this->numNodes ); + + bool convrg = ( std::abs( areaTest - area ) <= tol ) ? true : false; + + return convrg; + } + + protected: + void SetUp() override + { + this->numNodes = 4; + this->dim = 3; + + if ( this->x == nullptr ) { + this->x = new RealT[this->numNodes]; + } else { + delete[] this->x; + this->x = new RealT[this->numNodes]; + } + + if ( this->y == nullptr ) { + this->y = new RealT[this->numNodes]; + } else { + delete[] this->y; + this->y = new RealT[this->numNodes]; + } + + if ( this->z == nullptr ) { + this->z = new RealT[this->numNodes]; + } else { + delete[] this->z; + this->z = new RealT[this->numNodes]; + } + } + + void TearDown() override + { + if ( this->x != nullptr ) { + delete[] this->x; + this->x = nullptr; + } + if ( this->y != nullptr ) { + delete[] this->y; + this->y = nullptr; + } + if ( this->z != nullptr ) { + delete[] this->z; + this->z = nullptr; + } + } + + protected: + RealT* x{ nullptr }; + RealT* y{ nullptr }; + RealT* z{ nullptr }; }; - TEST_F( IsoIntegTest, square ) { + RealT* x = this->getXCoords(); + RealT* y = this->getYCoords(); + RealT* z = this->getZCoords(); - RealT* x = this->getXCoords(); - RealT* y = this->getYCoords(); - RealT* z = this->getZCoords(); - - x[0] = -0.5; - x[1] = 0.5; - x[2] = 0.5; - x[3] = -0.5; + x[0] = -0.5; + x[1] = 0.5; + x[2] = 0.5; + x[3] = -0.5; - y[0] = -0.5; - y[1] = -0.5; - y[2] = 0.5; - y[3] = 0.5; + y[0] = -0.5; + y[1] = -0.5; + y[2] = 0.5; + y[3] = 0.5; - z[0] = 0.1; - z[1] = 0.1; - z[2] = 0.1; - z[3] = 0.1; + z[0] = 0.1; + z[1] = 0.1; + z[2] = 0.1; + z[3] = 0.1; - bool convrg = this->integrate( 1.e-8 ); + bool convrg = this->integrate( 1.e-8 ); - EXPECT_EQ( convrg, true ); + EXPECT_EQ( convrg, true ); } TEST_F( IsoIntegTest, rect ) { + RealT* x = this->getXCoords(); + RealT* y = this->getYCoords(); + RealT* z = this->getZCoords(); - RealT* x = this->getXCoords(); - RealT* y = this->getYCoords(); - RealT* z = this->getZCoords(); - - x[0] = -0.5; - x[1] = 0.5; - x[2] = 0.5; - x[3] = -0.5; + x[0] = -0.5; + x[1] = 0.5; + x[2] = 0.5; + x[3] = -0.5; - y[0] = -0.25; - y[1] = -0.25; - y[2] = 0.25; - y[3] = 0.25; + y[0] = -0.25; + y[1] = -0.25; + y[2] = 0.25; + y[3] = 0.25; - z[0] = 0.1; - z[1] = 0.1; - z[2] = 0.1; - z[3] = 0.1; + z[0] = 0.1; + z[1] = 0.1; + z[2] = 0.1; + z[3] = 0.1; - bool convrg = this->integrate( 1.e-8 ); + bool convrg = this->integrate( 1.e-8 ); - EXPECT_EQ( convrg, true ); + EXPECT_EQ( convrg, true ); } TEST_F( IsoIntegTest, affine ) { - RealT* x = this->getXCoords(); - RealT* y = this->getYCoords(); - RealT* z = this->getZCoords(); + RealT* x = this->getXCoords(); + RealT* y = this->getYCoords(); + RealT* z = this->getZCoords(); - x[0] = -0.5; - x[1] = 0.5; - x[2] = 0.8; - x[3] = -0.2; + x[0] = -0.5; + x[1] = 0.5; + x[2] = 0.8; + x[3] = -0.2; - y[0] = -0.415; - y[1] = -0.415; - y[2] = 0.5; - y[3] = 0.5; + y[0] = -0.415; + y[1] = -0.415; + y[2] = 0.5; + y[3] = 0.5; - z[0] = 0.1; - z[1] = 0.1; - z[2] = 0.1; - z[3] = 0.1; + z[0] = 0.1; + z[1] = 0.1; + z[2] = 0.1; + z[3] = 0.1; - bool convrg = integrate( 1.e-8 ); + bool convrg = integrate( 1.e-8 ); - EXPECT_EQ( convrg, true ); + EXPECT_EQ( convrg, true ); } TEST_F( IsoIntegTest, nonaffine ) { - RealT* x = this->getXCoords(); - RealT* y = this->getYCoords(); - RealT* z = this->getZCoords(); - - x[0] = -0.5; - x[1] = 0.5; - x[2] = 0.235; - x[3] = -0.35; - - y[0] = -0.25; - y[1] = -0.15; - y[2] = 0.25; - y[3] = 0.235; - - z[0] = 0.1; - z[1] = 0.1; - z[2] = 0.1; - z[3] = 0.1; - - // note slightly lower convergence tol for nonaffinely - // mapped quad - bool convrg = integrate( 1.e-5 ); - - EXPECT_EQ( convrg, true ); + RealT* x = this->getXCoords(); + RealT* y = this->getYCoords(); + RealT* z = this->getZCoords(); + + x[0] = -0.5; + x[1] = 0.5; + x[2] = 0.235; + x[3] = -0.35; + + y[0] = -0.25; + y[1] = -0.15; + y[2] = 0.25; + y[3] = 0.235; + + z[0] = 0.1; + z[1] = 0.1; + z[2] = 0.1; + z[3] = 0.1; + + // note slightly lower convergence tol for nonaffinely + // mapped quad + bool convrg = integrate( 1.e-5 ); + + EXPECT_EQ( convrg, true ); } -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); axom::slic::SimpleLogger logger; diff --git a/src/tests/tribol_math.cpp b/src/tests/tribol_math.cpp index e2d9e2be..81da7bf2 100644 --- a/src/tests/tribol_math.cpp +++ b/src/tests/tribol_math.cpp @@ -20,234 +20,212 @@ using RealT = tribol::RealT; /*! * Test fixture class to test math functions */ -class MathTest : public ::testing::Test -{ - -public: - - void get3DVectorComps( RealT& x, RealT& y, RealT& z ) - { - x = m_vx; - y = m_vy; - z = m_vz; - } - - void set3DVectorComps( RealT vx, RealT vy, RealT vz ) - { - m_vx = vx; - m_vy = vy; - m_vz = vz; - } - - RealT get3DMag() - { - return m_3VecMag; - } - - void setSecond3DVectorComps( RealT vx, RealT vy, RealT vz ) - { - m_ux = vx; - m_uy = vy; - m_uz = vz; - } - - void getSecond3DVectorComps( RealT& x, RealT& y, RealT& z ) - { - x = m_ux; - y = m_uy; - z = m_uz; - } - - RealT getSecond3DMag() - { - return m_Second3VecMag; - } - - void get2DVectorComps( RealT& x, RealT& y ) - { - x = m_wx; - y = m_wy; - } - - void set2DVectorComps( RealT vx, RealT vy ) - { - m_wx = vx; - m_wy = vy; - } - - RealT get2DMag() - { - return m_2VecMag; - } - - void setTol( RealT tol ) - { - m_tol = tol; - } - - RealT getTol() - { - return m_tol; - } - -protected: - RealT m_tol { 1.E-8 }; - - // components of a three dimensional vector - RealT m_vx { 1.5 }; - RealT m_vy { 2.32 }; - RealT m_vz { 3.1 }; - RealT m_3VecMag { 4.152396898 }; - - // components of another three dimensional vector - RealT m_ux {2.1}; - RealT m_uy {4.23}; - RealT m_uz {5.35}; - RealT m_Second3VecMag { 7.136203472 }; - - // components of a two dimensional vector - RealT m_wx { 1.5 }; - RealT m_wy { 2.32 }; - RealT m_2VecMag { 2.762679858 }; +class MathTest : public ::testing::Test { + public: + void get3DVectorComps( RealT& x, RealT& y, RealT& z ) + { + x = m_vx; + y = m_vy; + z = m_vz; + } + + void set3DVectorComps( RealT vx, RealT vy, RealT vz ) + { + m_vx = vx; + m_vy = vy; + m_vz = vz; + } + + RealT get3DMag() { return m_3VecMag; } + + void setSecond3DVectorComps( RealT vx, RealT vy, RealT vz ) + { + m_ux = vx; + m_uy = vy; + m_uz = vz; + } + + void getSecond3DVectorComps( RealT& x, RealT& y, RealT& z ) + { + x = m_ux; + y = m_uy; + z = m_uz; + } + + RealT getSecond3DMag() { return m_Second3VecMag; } + + void get2DVectorComps( RealT& x, RealT& y ) + { + x = m_wx; + y = m_wy; + } + + void set2DVectorComps( RealT vx, RealT vy ) + { + m_wx = vx; + m_wy = vy; + } + + RealT get2DMag() { return m_2VecMag; } + + void setTol( RealT tol ) { m_tol = tol; } + + RealT getTol() { return m_tol; } + + protected: + RealT m_tol{ 1.E-8 }; + + // components of a three dimensional vector + RealT m_vx{ 1.5 }; + RealT m_vy{ 2.32 }; + RealT m_vz{ 3.1 }; + RealT m_3VecMag{ 4.152396898 }; + + // components of another three dimensional vector + RealT m_ux{ 2.1 }; + RealT m_uy{ 4.23 }; + RealT m_uz{ 5.35 }; + RealT m_Second3VecMag{ 7.136203472 }; + + // components of a two dimensional vector + RealT m_wx{ 1.5 }; + RealT m_wy{ 2.32 }; + RealT m_2VecMag{ 2.762679858 }; }; TEST_F( MathTest, magnitude ) { - RealT vx; - RealT vy; - RealT vz; - get3DVectorComps( vx, vy, vz ); + RealT vx; + RealT vy; + RealT vz; + get3DVectorComps( vx, vy, vz ); - RealT mag1 = tribol::magnitude( vx, vy, vz ); + RealT mag1 = tribol::magnitude( vx, vy, vz ); - RealT diff1 = std::abs( mag1 - get3DMag() ); + RealT diff1 = std::abs( mag1 - get3DMag() ); - EXPECT_LE( diff1, getTol() ); + EXPECT_LE( diff1, getTol() ); - RealT wx, wy; - get2DVectorComps( wx, wy ); + RealT wx, wy; + get2DVectorComps( wx, wy ); - RealT mag2 = tribol::magnitude( wx, wy ); + RealT mag2 = tribol::magnitude( wx, wy ); - RealT diff2 = std::abs( mag2 - get2DMag() ); + RealT diff2 = std::abs( mag2 - get2DMag() ); - EXPECT_LE( diff2, getTol() ); + EXPECT_LE( diff2, getTol() ); } TEST_F( MathTest, dotProd ) { - RealT vx; - RealT vy; - RealT vz; - get3DVectorComps( vx, vy, vz ); + RealT vx; + RealT vy; + RealT vz; + get3DVectorComps( vx, vy, vz ); + + RealT prod = tribol::dotProd( vx, vy, vz, vx, vy, vz ); - RealT prod = tribol::dotProd( vx, vy, vz, - vx, vy, vz ); + RealT diff = std::abs( prod - ( vx * vx + vy * vy + vz * vz ) ); - RealT diff = std::abs( prod - (vx*vx + vy*vy + vz*vz) ); + EXPECT_LE( diff, getTol() ); - EXPECT_LE( diff, getTol() ); + // test pointer input to dot product routine + RealT vec[3]{ + 0., + 0., + 0., + }; + RealT* w; + w = vec; + w[0] = vx; + w[1] = vy; + w[2] = vz; - // test pointer input to dot product routine - RealT vec[3] {0., 0., 0.,}; - RealT *w; - w = vec; - w[0] = vx; - w[1] = vy; - w[2] = vz; + RealT prod2 = tribol::dotProd( w, w, 3 ); - RealT prod2 = tribol::dotProd( w, w, 3 ); - - RealT diff2 = std::abs( prod2 - (vx*vx + vy*vy + vz*vz) ); + RealT diff2 = std::abs( prod2 - ( vx * vx + vy * vy + vz * vz ) ); - EXPECT_LE( diff2, getTol() ); + EXPECT_LE( diff2, getTol() ); } TEST_F( MathTest, crossProd ) { - RealT vx, vy, vz; - RealT ux, uy, uz; - get3DVectorComps( vx, vy, vz ); - getSecond3DVectorComps( ux, uy, uz ); - - RealT prod_x, prod_y, prod_z; - tribol::crossProd( vx, vy, vz, - ux, uy, uz, - prod_x, prod_y, prod_z ); - - RealT vec_mag = tribol::magnitude( prod_x, prod_y, prod_z ); - - RealT prod_test_i = vy*uz - vz*uy; - RealT prod_test_j = vz*ux - vx*uz; - RealT prod_test_k = vx*uy - vy*ux; - RealT mag_test = tribol::magnitude( prod_test_i, prod_test_j, prod_test_k ); - RealT diff = std::abs( vec_mag - mag_test ); - - EXPECT_LE( diff, getTol() ); - - // test pointer input to dot product routine - RealT a[3] {0., 0., 0.}; - RealT b[3] {0., 0., 0.}; - a[0] = vx; - a[1] = vy; - a[2] = vz; - b[0] = ux; - b[1] = uy; - b[2] = uz; - - vec_mag = tribol::magCrossProd( a, b ); - - diff = std::abs( vec_mag - mag_test ); - - EXPECT_LE( diff, getTol() ); + RealT vx, vy, vz; + RealT ux, uy, uz; + get3DVectorComps( vx, vy, vz ); + getSecond3DVectorComps( ux, uy, uz ); + + RealT prod_x, prod_y, prod_z; + tribol::crossProd( vx, vy, vz, ux, uy, uz, prod_x, prod_y, prod_z ); + + RealT vec_mag = tribol::magnitude( prod_x, prod_y, prod_z ); + + RealT prod_test_i = vy * uz - vz * uy; + RealT prod_test_j = vz * ux - vx * uz; + RealT prod_test_k = vx * uy - vy * ux; + RealT mag_test = tribol::magnitude( prod_test_i, prod_test_j, prod_test_k ); + RealT diff = std::abs( vec_mag - mag_test ); + + EXPECT_LE( diff, getTol() ); + + // test pointer input to dot product routine + RealT a[3]{ 0., 0., 0. }; + RealT b[3]{ 0., 0., 0. }; + a[0] = vx; + a[1] = vy; + a[2] = vz; + b[0] = ux; + b[1] = uy; + b[2] = uz; + + vec_mag = tribol::magCrossProd( a, b ); + + diff = std::abs( vec_mag - mag_test ); + + EXPECT_LE( diff, getTol() ); } TEST_F( MathTest, sort_and_search ) { - // define integer array. Note, introduce a duplicate entry - int a[6] { 1, 12, 8, 2, 7, 2 }; - - // sort array in increasing order - tribol::bubble_sort( &a[0], 6 ); - - EXPECT_EQ( a[0], 1 ); - EXPECT_EQ( a[1], 2 ); - EXPECT_EQ( a[2], 2 ); - EXPECT_EQ( a[3], 7 ); - EXPECT_EQ( a[4], 8 ); - EXPECT_EQ( a[5], 12 ); - - // search array for integer value = 7 - RealT index = tribol::binary_search( &a[0], 6, 7 ); - EXPECT_EQ( index, 3 ); - - // search array for integer value = 2 - index = tribol::binary_search( &a[0], 6, 2 ); - - // the search will find only one of the duplicate entries - if ( index == 1 ) - { - EXPECT_EQ( index, 1 ); - } - else if ( index == 2 ) - { - EXPECT_EQ( index, 2 ); - } - else - { - EXPECT_EQ( index, 1 ); // will fail if here - } - - // search for faulty value - index = tribol::binary_search( &a[0], 6, 21 ); - EXPECT_EQ( index, -1 ); + // define integer array. Note, introduce a duplicate entry + int a[6]{ 1, 12, 8, 2, 7, 2 }; + + // sort array in increasing order + tribol::bubble_sort( &a[0], 6 ); + + EXPECT_EQ( a[0], 1 ); + EXPECT_EQ( a[1], 2 ); + EXPECT_EQ( a[2], 2 ); + EXPECT_EQ( a[3], 7 ); + EXPECT_EQ( a[4], 8 ); + EXPECT_EQ( a[5], 12 ); + + // search array for integer value = 7 + RealT index = tribol::binary_search( &a[0], 6, 7 ); + EXPECT_EQ( index, 3 ); + + // search array for integer value = 2 + index = tribol::binary_search( &a[0], 6, 2 ); + + // the search will find only one of the duplicate entries + if ( index == 1 ) { + EXPECT_EQ( index, 1 ); + } else if ( index == 2 ) { + EXPECT_EQ( index, 2 ); + } else { + EXPECT_EQ( index, 1 ); // will fail if here + } + + // search for faulty value + index = tribol::binary_search( &a[0], 6, 21 ); + EXPECT_EQ( index, -1 ); } -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); axom::slic::SimpleLogger logger; diff --git a/src/tests/tribol_mfem_common_plane.cpp b/src/tests/tribol_mfem_common_plane.cpp index f3492649..37dc987e 100644 --- a/src/tests/tribol_mfem_common_plane.cpp +++ b/src/tests/tribol_mfem_common_plane.cpp @@ -38,7 +38,7 @@ * */ class MfemCommonPlaneTest : public testing::TestWithParam> { -protected: + protected: tribol::RealT max_disp_; void SetUp() override { @@ -59,104 +59,91 @@ class MfemCommonPlaneTest : public testing::TestWithParam({4}); + auto contact_surf_1 = std::set( { 4 } ); // boundary element attributes of contact surface 2 - auto contact_surf_2 = std::set({5}); + auto contact_surf_2 = std::set( { 5 } ); // boundary element attributes of fixed surface (points on z = 0, all t) - auto fixed_attrs = std::set({3}); + auto fixed_attrs = std::set( { 3 } ); // element attribute corresponding to volume elements where an initial // velocity will be applied - auto moving_attrs = std::set({2}); + auto moving_attrs = std::set( { 2 } ); // read mesh - std::unique_ptr pmesh { nullptr }; + std::unique_ptr pmesh{ nullptr }; { // read serial mesh - auto mesh = std::make_unique(mesh_file.c_str(), 1, 1); + auto mesh = std::make_unique( mesh_file.c_str(), 1, 1 ); // refine serial mesh - if (ref_levels > 0) - { - for (int i{0}; i < ref_levels; ++i) - { + if ( ref_levels > 0 ) { + for ( int i{ 0 }; i < ref_levels; ++i ) { mesh->UniformRefinement(); } } - + // create parallel mesh from serial - pmesh = std::make_unique(MPI_COMM_WORLD, *mesh); - mesh.reset(nullptr); + pmesh = std::make_unique( MPI_COMM_WORLD, *mesh ); + mesh.reset( nullptr ); // further refinement of parallel mesh { int par_ref_levels = 0; - for (int i{0}; i < par_ref_levels; ++i) - { + for ( int i{ 0 }; i < par_ref_levels; ++i ) { pmesh->UniformRefinement(); } } } // grid function for higher-order nodes - auto fe_coll = mfem::H1_FECollection(order, pmesh->SpaceDimension()); - auto par_fe_space = mfem::ParFiniteElementSpace( - pmesh.get(), &fe_coll, pmesh->SpaceDimension()); - auto coords = mfem::ParGridFunction(&par_fe_space); - if (order > 1) - { - pmesh->SetNodalGridFunction(&coords, false); - } - else - { - pmesh->GetNodes(coords); + auto fe_coll = mfem::H1_FECollection( order, pmesh->SpaceDimension() ); + auto par_fe_space = mfem::ParFiniteElementSpace( pmesh.get(), &fe_coll, pmesh->SpaceDimension() ); + auto coords = mfem::ParGridFunction( &par_fe_space ); + if ( order > 1 ) { + pmesh->SetNodalGridFunction( &coords, false ); + } else { + pmesh->GetNodes( coords ); } - mfem::ParGridFunction ref_coords { coords }; + mfem::ParGridFunction ref_coords{ coords }; // grid function for displacement - mfem::ParGridFunction displacement { &par_fe_space }; + mfem::ParGridFunction displacement{ &par_fe_space }; displacement = 0.0; // grid function for velocity - mfem::ParGridFunction v { &par_fe_space }; + mfem::ParGridFunction v{ &par_fe_space }; v = 0.0; // set initial velocity { - mfem::Array attrib_marker(pmesh->attributes.Max()); + mfem::Array attrib_marker( pmesh->attributes.Max() ); attrib_marker = 0; - for (auto moving_attr : moving_attrs) - { - attrib_marker[moving_attr-1] = 1; + for ( auto moving_attr : moving_attrs ) { + attrib_marker[moving_attr - 1] = 1; } - mfem::Array vdof_marker(par_fe_space.GetVSize()); + mfem::Array vdof_marker( par_fe_space.GetVSize() ); vdof_marker = 0; - for (int e{0}; e < pmesh->GetNE(); ++e) - { - if (attrib_marker[pmesh->GetElement(e)->GetAttribute()-1]) - { + for ( int e{ 0 }; e < pmesh->GetNE(); ++e ) { + if ( attrib_marker[pmesh->GetElement( e )->GetAttribute() - 1] ) { mfem::Array vdofs; - par_fe_space.GetElementDofs(e, vdofs); - par_fe_space.DofsToVDofs(2, vdofs); - for (auto vdof : vdofs) - { + par_fe_space.GetElementDofs( e, vdofs ); + par_fe_space.DofsToVDofs( 2, vdofs ); + for ( auto vdof : vdofs ) { vdof_marker[vdof] = 1; } } } - for (int i{0}; i < vdof_marker.Size(); ++i) - { - if (vdof_marker[i]) - { - v[i] = -std::abs(initial_v); + for ( int i{ 0 }; i < vdof_marker.Size(); ++i ) { + if ( vdof_marker[i] ) { + v[i] = -std::abs( initial_v ); } } } @@ -165,101 +152,90 @@ class MfemCommonPlaneTest : public testing::TestWithParam ess_vdof_list; { mfem::Array ess_vdof_marker; - mfem::Array ess_bdr(pmesh->bdr_attributes.Max()); + mfem::Array ess_bdr( pmesh->bdr_attributes.Max() ); ess_bdr = 0; - for (auto fixed_attr : fixed_attrs) - { - ess_bdr[fixed_attr-1] = 1; + for ( auto fixed_attr : fixed_attrs ) { + ess_bdr[fixed_attr - 1] = 1; } - par_fe_space.GetEssentialVDofs(ess_bdr, ess_vdof_marker); - mfem::FiniteElementSpace::MarkerToList(ess_vdof_marker, ess_vdof_list); + par_fe_space.GetEssentialVDofs( ess_bdr, ess_vdof_marker ); + mfem::FiniteElementSpace::MarkerToList( ess_vdof_marker, ess_vdof_list ); } // set up mfem elasticity bilinear form - mfem::ConstantCoefficient rho_coeff {rho}; - mfem::ConstantCoefficient lambda_coeff {lambda}; - mfem::ConstantCoefficient mu_coeff {mu}; - mfem_ext::ExplicitMechanics op {par_fe_space, rho_coeff, lambda_coeff, mu_coeff}; + mfem::ConstantCoefficient rho_coeff{ rho }; + mfem::ConstantCoefficient lambda_coeff{ lambda }; + mfem::ConstantCoefficient mu_coeff{ mu }; + mfem_ext::ExplicitMechanics op{ par_fe_space, rho_coeff, lambda_coeff, mu_coeff }; // set up time integrator - mfem_ext::CentralDiffSolver solver { ess_vdof_list }; - solver.Init(op); + mfem_ext::CentralDiffSolver solver{ ess_vdof_list }; + solver.Init( op ); // set up tribol int coupling_scheme_id = 0; int mesh1_id = 0; int mesh2_id = 1; - tribol::registerMfemCouplingScheme( - coupling_scheme_id, mesh1_id, mesh2_id, - *pmesh, coords, contact_surf_1, contact_surf_2, - tribol::SURFACE_TO_SURFACE, - tribol::NO_CASE, - tribol::COMMON_PLANE, - tribol::FRICTIONLESS, - tribol::PENALTY, - tribol::BINNING_GRID - ); - tribol::registerMfemVelocity(0, v); - if (GetParam().second == tribol::KINEMATIC_CONSTANT) - { - tribol::setMfemKinematicConstantPenalty(coupling_scheme_id, p_kine, p_kine); - } - else - { - mfem::Vector bulk_moduli_by_bdry_attrib(pmesh->bdr_attributes.Max()); + tribol::registerMfemCouplingScheme( coupling_scheme_id, mesh1_id, mesh2_id, *pmesh, coords, contact_surf_1, + contact_surf_2, tribol::SURFACE_TO_SURFACE, tribol::NO_CASE, + tribol::COMMON_PLANE, tribol::FRICTIONLESS, tribol::PENALTY, + tribol::BINNING_GRID ); + tribol::registerMfemVelocity( 0, v ); + if ( GetParam().second == tribol::KINEMATIC_CONSTANT ) { + tribol::setMfemKinematicConstantPenalty( coupling_scheme_id, p_kine, p_kine ); + } else { + mfem::Vector bulk_moduli_by_bdry_attrib( pmesh->bdr_attributes.Max() ); bulk_moduli_by_bdry_attrib = lambda + 2.0 / 3.0 * mu; - mfem::PWConstCoefficient mat_coeff(bulk_moduli_by_bdry_attrib); - tribol::setMfemKinematicElementPenalty(coupling_scheme_id, mat_coeff); + mfem::PWConstCoefficient mat_coeff( bulk_moduli_by_bdry_attrib ); + tribol::setMfemKinematicElementPenalty( coupling_scheme_id, mat_coeff ); } - int cycle {0}; - for (tribol::RealT t {0.0}; t < t_end; t+=dt) - { + int cycle{ 0 }; + for ( tribol::RealT t{ 0.0 }; t < t_end; t += dt ) { // build new parallel decomposed redecomp mesh and update grid functions // on each mesh tribol::updateMfemParallelDecomposition(); - tribol::update(cycle, t, dt); + tribol::update( cycle, t, dt ); op.f_ext = 0.0; - tribol::getMfemResponse(0, op.f_ext); + tribol::getMfemResponse( 0, op.f_ext ); - op.SetTime(t); - solver.Step(displacement, v, t, dt); + op.SetTime( t ); + solver.Step( displacement, v, t, dt ); - coords.Set(1.0, ref_coords); + coords.Set( 1.0, ref_coords ); coords += displacement; - if (order == 1) - { - pmesh->SetVertices(coords); + if ( order == 1 ) { + pmesh->SetVertices( coords ); } ++cycle; } max_disp_ = displacement.Max(); - MPI_Allreduce(MPI_IN_PLACE, &max_disp_, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD); + MPI_Allreduce( MPI_IN_PLACE, &max_disp_, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD ); } }; -TEST_P(MfemCommonPlaneTest, common_plane) +TEST_P( MfemCommonPlaneTest, common_plane ) { - EXPECT_LT(std::abs(max_disp_ - 0.013637427890739103), 1.5e-6); + EXPECT_LT( std::abs( max_disp_ - 0.013637427890739103 ), 1.5e-6 ); - MPI_Barrier(MPI_COMM_WORLD); + MPI_Barrier( MPI_COMM_WORLD ); } -INSTANTIATE_TEST_SUITE_P(tribol, MfemCommonPlaneTest, testing::Values(std::make_pair(1, tribol::KINEMATIC_CONSTANT), - std::make_pair(1, tribol::KINEMATIC_ELEMENT), - std::make_pair(2, tribol::KINEMATIC_CONSTANT), - std::make_pair(2, tribol::KINEMATIC_ELEMENT))); +INSTANTIATE_TEST_SUITE_P( tribol, MfemCommonPlaneTest, + testing::Values( std::make_pair( 1, tribol::KINEMATIC_CONSTANT ), + std::make_pair( 1, tribol::KINEMATIC_ELEMENT ), + std::make_pair( 2, tribol::KINEMATIC_CONSTANT ), + std::make_pair( 2, tribol::KINEMATIC_ELEMENT ) ) ); //------------------------------------------------------------------------------ -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - MPI_Init(&argc, &argv); + MPI_Init( &argc, &argv ); - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); #ifdef TRIBOL_USE_UMPIRE umpire::ResourceManager::getInstance(); // initialize umpire's ResouceManager diff --git a/src/tests/tribol_mfem_mortar_lm.cpp b/src/tests/tribol_mfem_mortar_lm.cpp index c4db72eb..d896de1b 100644 --- a/src/tests/tribol_mfem_mortar_lm.cpp +++ b/src/tests/tribol_mfem_mortar_lm.cpp @@ -33,7 +33,7 @@ * */ class MfemMortarTest : public testing::TestWithParam { -protected: + protected: tribol::RealT max_disp_; void SetUp() override { @@ -47,198 +47,177 @@ class MfemMortarTest : public testing::TestWithParam { // location of mesh file. TRIBOL_REPO_DIR is defined in tribol/config.hpp std::string mesh_file = TRIBOL_REPO_DIR "/data/two_hex_overlap.mesh"; // boundary element attributes of mortar surface - auto mortar_attrs = std::set({4}); - // boundary element attributes of nonmortar surface - auto nonmortar_attrs = std::set({5}); + auto mortar_attrs = std::set( { 4 } ); + // boundary element attributes of nonmortar surface + auto nonmortar_attrs = std::set( { 5 } ); // boundary element attributes of x-fixed surfaces (at x = 0) - auto xfixed_attrs = std::set({1}); + auto xfixed_attrs = std::set( { 1 } ); // boundary element attributes of y-fixed surfaces (at y = 0) - auto yfixed_attrs = std::set({2}); + auto yfixed_attrs = std::set( { 2 } ); // boundary element attributes of z-fixed surfaces (3: surface at z = 0, 6: surface at z = 1.99) - auto zfixed_attrs = std::set({3, 6}); + auto zfixed_attrs = std::set( { 3, 6 } ); // read mesh - std::unique_ptr pmesh { nullptr }; + std::unique_ptr pmesh{ nullptr }; { // read serial mesh - auto mesh = std::make_unique(mesh_file.c_str(), 1, 1); + auto mesh = std::make_unique( mesh_file.c_str(), 1, 1 ); // refine serial mesh - if (ref_levels > 0) - { - for (int i{0}; i < ref_levels; ++i) - { + if ( ref_levels > 0 ) { + for ( int i{ 0 }; i < ref_levels; ++i ) { mesh->UniformRefinement(); } } - + // create parallel mesh from serial - pmesh = std::make_unique(MPI_COMM_WORLD, *mesh); - mesh.reset(nullptr); + pmesh = std::make_unique( MPI_COMM_WORLD, *mesh ); + mesh.reset( nullptr ); // further refinement of parallel mesh { int par_ref_levels = 0; - for (int i{0}; i < par_ref_levels; ++i) - { + for ( int i{ 0 }; i < par_ref_levels; ++i ) { pmesh->UniformRefinement(); } } } // grid function for higher-order nodes - auto fe_coll = mfem::H1_FECollection(order, pmesh->SpaceDimension()); - auto par_fe_space = mfem::ParFiniteElementSpace( - pmesh.get(), &fe_coll, pmesh->SpaceDimension()); - auto coords = mfem::ParGridFunction(&par_fe_space); - if (order > 1) - { - pmesh->SetNodalGridFunction(&coords, false); - } - else - { - pmesh->GetNodes(coords); + auto fe_coll = mfem::H1_FECollection( order, pmesh->SpaceDimension() ); + auto par_fe_space = mfem::ParFiniteElementSpace( pmesh.get(), &fe_coll, pmesh->SpaceDimension() ); + auto coords = mfem::ParGridFunction( &par_fe_space ); + if ( order > 1 ) { + pmesh->SetNodalGridFunction( &coords, false ); + } else { + pmesh->GetNodes( coords ); } // grid function for displacement - mfem::ParGridFunction displacement { &par_fe_space }; + mfem::ParGridFunction displacement{ &par_fe_space }; displacement = 0.0; // recover dirichlet bc tdof list mfem::Array ess_tdof_list; { mfem::Array ess_vdof_marker; - mfem::Array ess_bdr(pmesh->bdr_attributes.Max()); + mfem::Array ess_bdr( pmesh->bdr_attributes.Max() ); ess_bdr = 0; - for (auto xfixed_attr : xfixed_attrs) - { - ess_bdr[xfixed_attr-1] = 1; + for ( auto xfixed_attr : xfixed_attrs ) { + ess_bdr[xfixed_attr - 1] = 1; } - par_fe_space.GetEssentialVDofs(ess_bdr, ess_vdof_marker, 0); + par_fe_space.GetEssentialVDofs( ess_bdr, ess_vdof_marker, 0 ); mfem::Array new_ess_vdof_marker; ess_bdr = 0; - for (auto yfixed_attr : yfixed_attrs) - { - ess_bdr[yfixed_attr-1] = 1; + for ( auto yfixed_attr : yfixed_attrs ) { + ess_bdr[yfixed_attr - 1] = 1; } - par_fe_space.GetEssentialVDofs(ess_bdr, new_ess_vdof_marker, 1); - for (int i{0}; i < ess_vdof_marker.Size(); ++i) - { + par_fe_space.GetEssentialVDofs( ess_bdr, new_ess_vdof_marker, 1 ); + for ( int i{ 0 }; i < ess_vdof_marker.Size(); ++i ) { ess_vdof_marker[i] = ess_vdof_marker[i] || new_ess_vdof_marker[i]; } ess_bdr = 0; - for (auto zfixed_attr : zfixed_attrs) - { - ess_bdr[zfixed_attr-1] = 1; + for ( auto zfixed_attr : zfixed_attrs ) { + ess_bdr[zfixed_attr - 1] = 1; } - par_fe_space.GetEssentialVDofs(ess_bdr, new_ess_vdof_marker, 2); - for (int i{0}; i < ess_vdof_marker.Size(); ++i) - { + par_fe_space.GetEssentialVDofs( ess_bdr, new_ess_vdof_marker, 2 ); + for ( int i{ 0 }; i < ess_vdof_marker.Size(); ++i ) { ess_vdof_marker[i] = ess_vdof_marker[i] || new_ess_vdof_marker[i]; } mfem::Array ess_tdof_marker; - par_fe_space.GetRestrictionMatrix()->BooleanMult(ess_vdof_marker, ess_tdof_marker); - mfem::FiniteElementSpace::MarkerToList(ess_tdof_marker, ess_tdof_list); + par_fe_space.GetRestrictionMatrix()->BooleanMult( ess_vdof_marker, ess_tdof_marker ); + mfem::FiniteElementSpace::MarkerToList( ess_tdof_marker, ess_tdof_list ); } // set up mfem elasticity bilinear form - mfem::ParBilinearForm a(&par_fe_space); - mfem::ConstantCoefficient lambda(50.0); - mfem::ConstantCoefficient mu(50.0); - a.AddDomainIntegrator(new mfem::ElasticityIntegrator(lambda, mu)); + mfem::ParBilinearForm a( &par_fe_space ); + mfem::ConstantCoefficient lambda( 50.0 ); + mfem::ConstantCoefficient mu( 50.0 ); + a.AddDomainIntegrator( new mfem::ElasticityIntegrator( lambda, mu ) ); a.Assemble(); // compute elasticity contribution to stiffness auto A = std::make_unique(); - a.FormSystemMatrix(ess_tdof_list, *A); + a.FormSystemMatrix( ess_tdof_list, *A ); // set up tribol int coupling_scheme_id = 0; int mesh1_id = 0; int mesh2_id = 1; - tribol::registerMfemCouplingScheme( - coupling_scheme_id, mesh1_id, mesh2_id, - *pmesh, coords, mortar_attrs, nonmortar_attrs, - tribol::SURFACE_TO_SURFACE, - tribol::NO_SLIDING, - tribol::SINGLE_MORTAR, - tribol::FRICTIONLESS, - tribol::LAGRANGE_MULTIPLIER, - tribol::BINNING_GRID - ); - tribol::setLagrangeMultiplierOptions( - 0, - tribol::ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN - ); + tribol::registerMfemCouplingScheme( coupling_scheme_id, mesh1_id, mesh2_id, *pmesh, coords, mortar_attrs, + nonmortar_attrs, tribol::SURFACE_TO_SURFACE, tribol::NO_SLIDING, + tribol::SINGLE_MORTAR, tribol::FRICTIONLESS, tribol::LAGRANGE_MULTIPLIER, + tribol::BINNING_GRID ); + tribol::setLagrangeMultiplierOptions( 0, tribol::ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN ); // update tribol (compute contact contribution to force and stiffness) tribol::updateMfemParallelDecomposition(); - tribol::RealT dt {1.0}; // time is arbitrary here (no timesteps) - tribol::update(1, 1.0, dt); + tribol::RealT dt{ 1.0 }; // time is arbitrary here (no timesteps) + tribol::update( 1, 1.0, dt ); // retrieve block stiffness matrix - auto A_blk = tribol::getMfemBlockJacobian(0); - A_blk->SetBlock(0, 0, A.release()); + auto A_blk = tribol::getMfemBlockJacobian( 0 ); + A_blk->SetBlock( 0, 0, A.release() ); // create block solution and RHS vectors - mfem::BlockVector B_blk { A_blk->ColOffsets() }; + mfem::BlockVector B_blk{ A_blk->ColOffsets() }; B_blk = 0.0; - mfem::BlockVector X_blk { A_blk->RowOffsets() }; + mfem::BlockVector X_blk{ A_blk->RowOffsets() }; X_blk = 0.0; // retrieve gap vector (RHS) from contact mfem::ParGridFunction g; - tribol::getMfemGap(0, g); + tribol::getMfemGap( 0, g ); - // prolongation transpose operator on submesh: maps dofs stored in g to tdofs stored in G + // prolongation transpose operator on submesh: maps dofs stored in g to tdofs stored in G { - auto& G = B_blk.GetBlock(1); - auto& P_submesh = *tribol::getMfemPressure(0).ParFESpace()->GetProlongationMatrix(); - P_submesh.MultTranspose(g, G); + auto& G = B_blk.GetBlock( 1 ); + auto& P_submesh = *tribol::getMfemPressure( 0 ).ParFESpace()->GetProlongationMatrix(); + P_submesh.MultTranspose( g, G ); } // solve for X_blk - mfem::MINRESSolver solver(MPI_COMM_WORLD); - solver.SetRelTol(1.0e-8); - solver.SetAbsTol(1.0e-12); - solver.SetMaxIter(5000); - solver.SetPrintLevel(3); - solver.SetOperator(*A_blk); - solver.Mult(B_blk, X_blk); + mfem::MINRESSolver solver( MPI_COMM_WORLD ); + solver.SetRelTol( 1.0e-8 ); + solver.SetAbsTol( 1.0e-12 ); + solver.SetMaxIter( 5000 ); + solver.SetPrintLevel( 3 ); + solver.SetOperator( *A_blk ); + solver.Mult( B_blk, X_blk ); // move block displacements to grid function { - auto& U = X_blk.GetBlock(0); + auto& U = X_blk.GetBlock( 0 ); auto& P = *par_fe_space.GetProlongationMatrix(); - P.Mult(U, displacement); + P.Mult( U, displacement ); } displacement.Neg(); auto local_max = displacement.Max(); max_disp_ = 0.0; - MPI_Allreduce(&local_max, &max_disp_, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD); + MPI_Allreduce( &local_max, &max_disp_, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD ); } }; -TEST_P(MfemMortarTest, mass_matrix_transfer) +TEST_P( MfemMortarTest, mass_matrix_transfer ) { - EXPECT_LT(std::abs(max_disp_ - 0.005), 1.0e-6); + EXPECT_LT( std::abs( max_disp_ - 0.005 ), 1.0e-6 ); - MPI_Barrier(MPI_COMM_WORLD); + MPI_Barrier( MPI_COMM_WORLD ); } -INSTANTIATE_TEST_SUITE_P(tribol, MfemMortarTest, testing::Values(2)); +INSTANTIATE_TEST_SUITE_P( tribol, MfemMortarTest, testing::Values( 2 ) ); //------------------------------------------------------------------------------ #include "axom/slic/core/SimpleLogger.hpp" -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - MPI_Init(&argc, &argv); + MPI_Init( &argc, &argv ); - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); #ifdef TRIBOL_USE_UMPIRE umpire::ResourceManager::getInstance(); // initialize umpire's ResouceManager diff --git a/src/tests/tribol_mortar_data_geom.cpp b/src/tests/tribol_mortar_data_geom.cpp index ead59c9b..42d295ea 100644 --- a/src/tests/tribol_mortar_data_geom.cpp +++ b/src/tests/tribol_mortar_data_geom.cpp @@ -33,7 +33,7 @@ #include "gtest/gtest.h" // c++ includes -#include // std::abs, std::cos, std::sin +#include // std::abs, std::cos, std::sin #include #include #include @@ -46,539 +46,460 @@ namespace axom_fs = axom::utilities::filesystem; * Test fixture class with some setup necessary to test * the _MORTAR_ computational geometry implementation in Tribol */ -class MortarGeomTest : public ::testing::Test -{ - -public: - mfem::Vector v_xm; - mfem::Vector v_ym; - mfem::Vector v_zm; - mfem::Vector v_xs; - mfem::Vector v_ys; - mfem::Vector v_zs; - mfem::Array v_ixm; - mfem::Array v_ixs; - - int lengthMortarConn; - int lengthNonmortarConn; - int lengthMortarNodes; - int lengthNonmortarNodes; - int numMortarCells; - int numNonmortarCells; - - tribol::TestMesh m_mesh; - -protected: - - void SetUp() override - { - } - - void TearDown() override - { - // call clear() on mesh object to be safe - this->m_mesh.clear(); - } - -protected: - +class MortarGeomTest : public ::testing::Test { + public: + mfem::Vector v_xm; + mfem::Vector v_ym; + mfem::Vector v_zm; + mfem::Vector v_xs; + mfem::Vector v_ys; + mfem::Vector v_zs; + mfem::Array v_ixm; + mfem::Array v_ixs; + + int lengthMortarConn; + int lengthNonmortarConn; + int lengthMortarNodes; + int lengthNonmortarNodes; + int numMortarCells; + int numNonmortarCells; + + tribol::TestMesh m_mesh; + + protected: + void SetUp() override {} + + void TearDown() override + { + // call clear() on mesh object to be safe + this->m_mesh.clear(); + } + + protected: }; TEST_F( MortarGeomTest, mortar_good_patch ) { - - // read data sets for mesh - std::string ixm_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_good_patch/ixm.txt"); - std::string ixs_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_good_patch/ixs.txt"); - std::string xm_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_good_patch/xm.txt"); - std::string ym_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_good_patch/ym.txt"); - std::string zm_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_good_patch/zm.txt"); - std::string xs_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_good_patch/xs.txt"); - std::string ys_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_good_patch/ys.txt"); - std::string zs_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_good_patch/zs.txt"); - - this->lengthMortarConn = 64; - this->lengthNonmortarConn = 36; - this->lengthMortarNodes = 189; - this->lengthNonmortarNodes = 189; - this->numMortarCells = 16; - this->numNonmortarCells = 9; - - std::ifstream i_ixm( ixm_file ); - std::ifstream i_ixs( ixs_file ); - std::ifstream i_xm( xm_file ); - std::ifstream i_ym( ym_file ); - std::ifstream i_zm( zm_file ); - std::ifstream i_xs( xs_file ); - std::ifstream i_ys( ys_file ); - std::ifstream i_zs( zs_file ); - - this->v_ixm.SetSize( this->lengthMortarConn ); - this->v_ixs.SetSize( this->lengthNonmortarConn ); - - this->v_ixm.Load( i_ixm, 1 ); - this->v_ixs.Load( i_ixs, 1 ); - this->v_xm.Load( i_xm, this->lengthMortarNodes ); - this->v_ym.Load( i_ym, this->lengthMortarNodes ); - this->v_zm.Load( i_zm, this->lengthMortarNodes ); - this->v_xs.Load( i_xs, this->lengthNonmortarNodes ); - this->v_ys.Load( i_ys, this->lengthNonmortarNodes ); - this->v_zs.Load( i_zs, this->lengthNonmortarNodes ); - - i_ixm.close(); - i_ixs.close(); - i_xm.close(); - i_ym.close(); - i_zm.close(); - i_xs.close(); - i_ys.close(); - i_zs.close(); - - SLIC_INFO("After loading mesh data and constructing mfem vectors."); - - // get pointers to mfem vector data - int* ixm_data = this->v_ixm.GetData(); - int* ixs_data = this->v_ixs.GetData(); - RealT* xm_data = this->v_xm.GetData(); - RealT* ym_data = this->v_ym.GetData(); - RealT* zm_data = this->v_zm.GetData(); - RealT* xs_data = this->v_xs.GetData(); - RealT* ys_data = this->v_ys.GetData(); - RealT* zs_data = this->v_zs.GetData(); - - // set gaps and pressure arrays. Note that for this test - // the length of the nonmortar nodes array is the same as the mortar, - // which means that it is the total number of nodes in the whole - // mesh - RealT* gaps, * pressures; - int numTotalNodes = this->lengthNonmortarNodes; - gaps = new RealT[ numTotalNodes ]; - pressures = new RealT[ numTotalNodes ]; - - // initialize arrays - for (int i=0; inumMortarCells, - this->lengthMortarNodes, - ixm_data, - xm_data, - ym_data, - zm_data, - this->numNonmortarCells, - this->lengthNonmortarNodes, - ixs_data, - xs_data, - ys_data, - zs_data, - 1.e-3, - gaps, - pressures); - - RealT dt = 1.0; - int err = Update( dt ); - - EXPECT_EQ(err, 0); - - tribol::CouplingSchemeManager& couplingSchemeManager = - tribol::CouplingSchemeManager::getInstance(); - - tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); - - EXPECT_EQ( couplingScheme->getNumActivePairs(), 36 ); - - delete[] gaps; - delete[] pressures; + // read data sets for mesh + std::string ixm_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_good_patch/ixm.txt" ); + std::string ixs_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_good_patch/ixs.txt" ); + std::string xm_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_good_patch/xm.txt" ); + std::string ym_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_good_patch/ym.txt" ); + std::string zm_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_good_patch/zm.txt" ); + std::string xs_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_good_patch/xs.txt" ); + std::string ys_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_good_patch/ys.txt" ); + std::string zs_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_good_patch/zs.txt" ); + + this->lengthMortarConn = 64; + this->lengthNonmortarConn = 36; + this->lengthMortarNodes = 189; + this->lengthNonmortarNodes = 189; + this->numMortarCells = 16; + this->numNonmortarCells = 9; + + std::ifstream i_ixm( ixm_file ); + std::ifstream i_ixs( ixs_file ); + std::ifstream i_xm( xm_file ); + std::ifstream i_ym( ym_file ); + std::ifstream i_zm( zm_file ); + std::ifstream i_xs( xs_file ); + std::ifstream i_ys( ys_file ); + std::ifstream i_zs( zs_file ); + + this->v_ixm.SetSize( this->lengthMortarConn ); + this->v_ixs.SetSize( this->lengthNonmortarConn ); + + this->v_ixm.Load( i_ixm, 1 ); + this->v_ixs.Load( i_ixs, 1 ); + this->v_xm.Load( i_xm, this->lengthMortarNodes ); + this->v_ym.Load( i_ym, this->lengthMortarNodes ); + this->v_zm.Load( i_zm, this->lengthMortarNodes ); + this->v_xs.Load( i_xs, this->lengthNonmortarNodes ); + this->v_ys.Load( i_ys, this->lengthNonmortarNodes ); + this->v_zs.Load( i_zs, this->lengthNonmortarNodes ); + + i_ixm.close(); + i_ixs.close(); + i_xm.close(); + i_ym.close(); + i_zm.close(); + i_xs.close(); + i_ys.close(); + i_zs.close(); + + SLIC_INFO( "After loading mesh data and constructing mfem vectors." ); + + // get pointers to mfem vector data + int* ixm_data = this->v_ixm.GetData(); + int* ixs_data = this->v_ixs.GetData(); + RealT* xm_data = this->v_xm.GetData(); + RealT* ym_data = this->v_ym.GetData(); + RealT* zm_data = this->v_zm.GetData(); + RealT* xs_data = this->v_xs.GetData(); + RealT* ys_data = this->v_ys.GetData(); + RealT* zs_data = this->v_zs.GetData(); + + // set gaps and pressure arrays. Note that for this test + // the length of the nonmortar nodes array is the same as the mortar, + // which means that it is the total number of nodes in the whole + // mesh + RealT *gaps, *pressures; + int numTotalNodes = this->lengthNonmortarNodes; + gaps = new RealT[numTotalNodes]; + pressures = new RealT[numTotalNodes]; + + // initialize arrays + for ( int i = 0; i < numTotalNodes; ++i ) { + gaps[i] = 0.; + pressures[i] = 1.; + } + + // setup simple coupling + SimpleCouplingSetup( 3, (int)( tribol::LINEAR_QUAD ), tribol::MORTAR_WEIGHTS, this->numMortarCells, + this->lengthMortarNodes, ixm_data, xm_data, ym_data, zm_data, this->numNonmortarCells, + this->lengthNonmortarNodes, ixs_data, xs_data, ys_data, zs_data, 1.e-3, gaps, pressures ); + + RealT dt = 1.0; + int err = Update( dt ); + + EXPECT_EQ( err, 0 ); + + tribol::CouplingSchemeManager& couplingSchemeManager = tribol::CouplingSchemeManager::getInstance(); + + tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); + + EXPECT_EQ( couplingScheme->getNumActivePairs(), 36 ); + + delete[] gaps; + delete[] pressures; } TEST_F( MortarGeomTest, mortar_bad_patch ) { - - // read data sets for mesh - std::string ixm_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_bad_patch/ixm.txt"); - std::string ixs_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_bad_patch/ixs.txt"); - std::string xm_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_bad_patch/xm.txt"); - std::string ym_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_bad_patch/ym.txt"); - std::string zm_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_bad_patch/zm.txt"); - std::string xs_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_bad_patch/xs.txt"); - std::string ys_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_bad_patch/ys.txt"); - std::string zs_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_bad_patch/zs.txt"); - - this->lengthMortarConn = 64; - this->lengthNonmortarConn = 36; - this->lengthMortarNodes = 189; - this->lengthNonmortarNodes = 189; - this->numMortarCells = 16; - this->numNonmortarCells = 9; - - std::ifstream i_ixm( ixm_file ); - std::ifstream i_ixs( ixs_file ); - std::ifstream i_xm( xm_file ); - std::ifstream i_ym( ym_file ); - std::ifstream i_zm( zm_file ); - std::ifstream i_xs( xs_file ); - std::ifstream i_ys( ys_file ); - std::ifstream i_zs( zs_file ); - - this->v_ixm.SetSize( this->lengthMortarConn ); - this->v_ixs.SetSize( this->lengthNonmortarConn ); - - this->v_ixm.Load( i_ixm, 1 ); - this->v_ixs.Load( i_ixs, 1 ); - this->v_xm.Load( i_xm, this->lengthMortarNodes ); - this->v_ym.Load( i_ym, this->lengthMortarNodes ); - this->v_zm.Load( i_zm, this->lengthMortarNodes ); - this->v_xs.Load( i_xs, this->lengthNonmortarNodes ); - this->v_ys.Load( i_ys, this->lengthNonmortarNodes ); - this->v_zs.Load( i_zs, this->lengthNonmortarNodes ); - - i_ixm.close(); - i_ixs.close(); - i_xm.close(); - i_ym.close(); - i_zm.close(); - i_xs.close(); - i_ys.close(); - i_zs.close(); - - SLIC_INFO("After loading mesh data and constructing mfem vectors."); - - // get pointers to mfem vector data - int* ixm_data = this->v_ixm.GetData(); - int* ixs_data = this->v_ixs.GetData(); - RealT* xm_data = this->v_xm.GetData(); - RealT* ym_data = this->v_ym.GetData(); - RealT* zm_data = this->v_zm.GetData(); - RealT* xs_data = this->v_xs.GetData(); - RealT* ys_data = this->v_ys.GetData(); - RealT* zs_data = this->v_zs.GetData(); - - // set gaps and pressure arrays. Note that for this test - // the length of the nonmortar nodes array is the same as the mortar, - // which means that it is the total number of nodes in the whole - // mesh - RealT* gaps, * pressures; - int numTotalNodes = this->lengthNonmortarNodes; - gaps = new RealT[ numTotalNodes ]; - pressures = new RealT[ numTotalNodes ]; - - // initialize arrays - for (int i=0; inumMortarCells, - this->lengthMortarNodes, - ixm_data, - xm_data, - ym_data, - zm_data, - this->numNonmortarCells, - this->lengthNonmortarNodes, - ixs_data, - xs_data, - ys_data, - zs_data, - 1.e-3, - gaps, - pressures); - - RealT dt = 1.0; - int err = Update( dt ); - - EXPECT_EQ(err, 0); - - tribol::CouplingSchemeManager& couplingSchemeManager = - tribol::CouplingSchemeManager::getInstance(); - - tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); - - EXPECT_EQ( couplingScheme->getNumActivePairs(), 36 ); - - delete[] gaps; - delete[] pressures; + // read data sets for mesh + std::string ixm_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_bad_patch/ixm.txt" ); + std::string ixs_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_bad_patch/ixs.txt" ); + std::string xm_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_bad_patch/xm.txt" ); + std::string ym_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_bad_patch/ym.txt" ); + std::string zm_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_bad_patch/zm.txt" ); + std::string xs_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_bad_patch/xs.txt" ); + std::string ys_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_bad_patch/ys.txt" ); + std::string zs_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_bad_patch/zs.txt" ); + + this->lengthMortarConn = 64; + this->lengthNonmortarConn = 36; + this->lengthMortarNodes = 189; + this->lengthNonmortarNodes = 189; + this->numMortarCells = 16; + this->numNonmortarCells = 9; + + std::ifstream i_ixm( ixm_file ); + std::ifstream i_ixs( ixs_file ); + std::ifstream i_xm( xm_file ); + std::ifstream i_ym( ym_file ); + std::ifstream i_zm( zm_file ); + std::ifstream i_xs( xs_file ); + std::ifstream i_ys( ys_file ); + std::ifstream i_zs( zs_file ); + + this->v_ixm.SetSize( this->lengthMortarConn ); + this->v_ixs.SetSize( this->lengthNonmortarConn ); + + this->v_ixm.Load( i_ixm, 1 ); + this->v_ixs.Load( i_ixs, 1 ); + this->v_xm.Load( i_xm, this->lengthMortarNodes ); + this->v_ym.Load( i_ym, this->lengthMortarNodes ); + this->v_zm.Load( i_zm, this->lengthMortarNodes ); + this->v_xs.Load( i_xs, this->lengthNonmortarNodes ); + this->v_ys.Load( i_ys, this->lengthNonmortarNodes ); + this->v_zs.Load( i_zs, this->lengthNonmortarNodes ); + + i_ixm.close(); + i_ixs.close(); + i_xm.close(); + i_ym.close(); + i_zm.close(); + i_xs.close(); + i_ys.close(); + i_zs.close(); + + SLIC_INFO( "After loading mesh data and constructing mfem vectors." ); + + // get pointers to mfem vector data + int* ixm_data = this->v_ixm.GetData(); + int* ixs_data = this->v_ixs.GetData(); + RealT* xm_data = this->v_xm.GetData(); + RealT* ym_data = this->v_ym.GetData(); + RealT* zm_data = this->v_zm.GetData(); + RealT* xs_data = this->v_xs.GetData(); + RealT* ys_data = this->v_ys.GetData(); + RealT* zs_data = this->v_zs.GetData(); + + // set gaps and pressure arrays. Note that for this test + // the length of the nonmortar nodes array is the same as the mortar, + // which means that it is the total number of nodes in the whole + // mesh + RealT *gaps, *pressures; + int numTotalNodes = this->lengthNonmortarNodes; + gaps = new RealT[numTotalNodes]; + pressures = new RealT[numTotalNodes]; + + // initialize arrays + for ( int i = 0; i < numTotalNodes; ++i ) { + gaps[i] = 0.; + pressures[i] = 1.; + } + + // setup simple coupling + SimpleCouplingSetup( 3, (int)( tribol::LINEAR_QUAD ), tribol::MORTAR_WEIGHTS, this->numMortarCells, + this->lengthMortarNodes, ixm_data, xm_data, ym_data, zm_data, this->numNonmortarCells, + this->lengthNonmortarNodes, ixs_data, xs_data, ys_data, zs_data, 1.e-3, gaps, pressures ); + + RealT dt = 1.0; + int err = Update( dt ); + + EXPECT_EQ( err, 0 ); + + tribol::CouplingSchemeManager& couplingSchemeManager = tribol::CouplingSchemeManager::getInstance(); + + tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); + + EXPECT_EQ( couplingScheme->getNumActivePairs(), 36 ); + + delete[] gaps; + delete[] pressures; } TEST_F( MortarGeomTest, mortar_ironing ) { - - // read data sets for mesh - std::string ixm_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_ironing_3/ixm.txt"); - std::string ixs_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_ironing_3/ixs.txt"); - std::string xm_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_ironing_3/xm.txt"); - std::string ym_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_ironing_3/ym.txt"); - std::string zm_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_ironing_3/zm.txt"); - std::string xs_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_ironing_3/xs.txt"); - std::string ys_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_ironing_3/ys.txt"); - std::string zs_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_ironing_3/zs.txt"); - - this->lengthMortarConn = 480; - this->lengthNonmortarConn = 160; - this->lengthMortarNodes = 696; - this->lengthNonmortarNodes = 696; - this->numMortarCells = 120; - this->numNonmortarCells = 40; - - std::ifstream i_ixm( ixm_file ); - std::ifstream i_ixs( ixs_file ); - std::ifstream i_xm( xm_file ); - std::ifstream i_ym( ym_file ); - std::ifstream i_zm( zm_file ); - std::ifstream i_xs( xs_file ); - std::ifstream i_ys( ys_file ); - std::ifstream i_zs( zs_file ); - - this->v_ixm.SetSize( this->lengthMortarConn ); - this->v_ixs.SetSize( this->lengthNonmortarConn ); - - this->v_ixm.Load( i_ixm, 1 ); - this->v_ixs.Load( i_ixs, 1 ); - this->v_xm.Load( i_xm, this->lengthMortarNodes ); - this->v_ym.Load( i_ym, this->lengthMortarNodes ); - this->v_zm.Load( i_zm, this->lengthMortarNodes ); - this->v_xs.Load( i_xs, this->lengthNonmortarNodes ); - this->v_ys.Load( i_ys, this->lengthNonmortarNodes ); - this->v_zs.Load( i_zs, this->lengthNonmortarNodes ); - - i_ixm.close(); - i_ixs.close(); - i_xm.close(); - i_ym.close(); - i_zm.close(); - i_xs.close(); - i_ys.close(); - i_zs.close(); - - SLIC_INFO("After loading mesh data and constructing mfem vectors."); - - // get pointers to mfem vector data - int* ixm_data = this->v_ixm.GetData(); - int* ixs_data = this->v_ixs.GetData(); - RealT* xm_data = this->v_xm.GetData(); - RealT* ym_data = this->v_ym.GetData(); - RealT* zm_data = this->v_zm.GetData(); - RealT* xs_data = this->v_xs.GetData(); - RealT* ys_data = this->v_ys.GetData(); - RealT* zs_data = this->v_zs.GetData(); - - // set gaps and pressure arrays. Note that for this test - // the length of the nonmortar nodes array is the same as the mortar, - // which means that it is the total number of nodes in the whole - // mesh - RealT* gaps, * pressures; - int numTotalNodes = this->lengthNonmortarNodes; - gaps = new RealT[ numTotalNodes ]; - pressures = new RealT[ numTotalNodes ]; - - // initialize arrays - for (int i=0; inumMortarCells, - this->lengthMortarNodes, - ixm_data, - xm_data, - ym_data, - zm_data, - this->numNonmortarCells, - this->lengthNonmortarNodes, - ixs_data, - xs_data, - ys_data, - zs_data, - 1.e-3, - gaps, - pressures); - - RealT dt = 1.0; - int err = Update( dt ); - - EXPECT_EQ( err, 0 ); - - tribol::CouplingSchemeManager& couplingSchemeManager = - tribol::CouplingSchemeManager::getInstance(); - - tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); - - int *I = nullptr; - int *J = nullptr; - RealT *wts = nullptr; - int nOffsets = 0; - int nNonZeros = 0; - int csr_err = GetSimpleCouplingCSR( &I, &J, &wts, &nOffsets, &nNonZeros ); - - EXPECT_EQ( csr_err, 0 ); - - numTotalNodes = static_cast( couplingScheme->getMethodData() )->m_numTotalNodes; - int num_total_active_nodes = 0; - for (int a=0; alengthMortarConn = 480; + this->lengthNonmortarConn = 160; + this->lengthMortarNodes = 696; + this->lengthNonmortarNodes = 696; + this->numMortarCells = 120; + this->numNonmortarCells = 40; + + std::ifstream i_ixm( ixm_file ); + std::ifstream i_ixs( ixs_file ); + std::ifstream i_xm( xm_file ); + std::ifstream i_ym( ym_file ); + std::ifstream i_zm( zm_file ); + std::ifstream i_xs( xs_file ); + std::ifstream i_ys( ys_file ); + std::ifstream i_zs( zs_file ); + + this->v_ixm.SetSize( this->lengthMortarConn ); + this->v_ixs.SetSize( this->lengthNonmortarConn ); + + this->v_ixm.Load( i_ixm, 1 ); + this->v_ixs.Load( i_ixs, 1 ); + this->v_xm.Load( i_xm, this->lengthMortarNodes ); + this->v_ym.Load( i_ym, this->lengthMortarNodes ); + this->v_zm.Load( i_zm, this->lengthMortarNodes ); + this->v_xs.Load( i_xs, this->lengthNonmortarNodes ); + this->v_ys.Load( i_ys, this->lengthNonmortarNodes ); + this->v_zs.Load( i_zs, this->lengthNonmortarNodes ); + + i_ixm.close(); + i_ixs.close(); + i_xm.close(); + i_ym.close(); + i_zm.close(); + i_xs.close(); + i_ys.close(); + i_zs.close(); + + SLIC_INFO( "After loading mesh data and constructing mfem vectors." ); + + // get pointers to mfem vector data + int* ixm_data = this->v_ixm.GetData(); + int* ixs_data = this->v_ixs.GetData(); + RealT* xm_data = this->v_xm.GetData(); + RealT* ym_data = this->v_ym.GetData(); + RealT* zm_data = this->v_zm.GetData(); + RealT* xs_data = this->v_xs.GetData(); + RealT* ys_data = this->v_ys.GetData(); + RealT* zs_data = this->v_zs.GetData(); + + // set gaps and pressure arrays. Note that for this test + // the length of the nonmortar nodes array is the same as the mortar, + // which means that it is the total number of nodes in the whole + // mesh + RealT *gaps, *pressures; + int numTotalNodes = this->lengthNonmortarNodes; + gaps = new RealT[numTotalNodes]; + pressures = new RealT[numTotalNodes]; + + // initialize arrays + for ( int i = 0; i < numTotalNodes; ++i ) { + gaps[i] = 0.; + pressures[i] = 1.; + } + + // setup simple coupling + SimpleCouplingSetup( 3, (int)( tribol::LINEAR_QUAD ), tribol::MORTAR_WEIGHTS, this->numMortarCells, + this->lengthMortarNodes, ixm_data, xm_data, ym_data, zm_data, this->numNonmortarCells, + this->lengthNonmortarNodes, ixs_data, xs_data, ys_data, zs_data, 1.e-3, gaps, pressures ); + + RealT dt = 1.0; + int err = Update( dt ); + + EXPECT_EQ( err, 0 ); + + tribol::CouplingSchemeManager& couplingSchemeManager = tribol::CouplingSchemeManager::getInstance(); + + tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); + + int* I = nullptr; + int* J = nullptr; + RealT* wts = nullptr; + int nOffsets = 0; + int nNonZeros = 0; + int csr_err = GetSimpleCouplingCSR( &I, &J, &wts, &nOffsets, &nNonZeros ); + + EXPECT_EQ( csr_err, 0 ); + + numTotalNodes = static_cast( couplingScheme->getMethodData() )->m_numTotalNodes; + int num_total_active_nodes = 0; + for ( int a = 0; a < numTotalNodes; ++a ) { + // loop over range of nonzero column entries + int num_active_cnt = 0; + for ( int b = I[a]; b < I[a + 1]; ++b ) { + if ( num_active_cnt == 0 ) { + ++num_active_cnt; + ++num_total_active_nodes; + } + } // end loop over nonzero columns, I[a] + } // end loop over matrix rows + + SLIC_INFO( "Total number of ACTIVE nonmortar nodes: " << num_total_active_nodes ); + + EXPECT_EQ( num_total_active_nodes, 54 ); + + delete[] gaps; + delete[] pressures; } TEST_F( MortarGeomTest, mortar_ironing_block_sub_mesh ) { - // this mesh tests if the update occurred without errors. - // Specifically, the Tribol issue was that the inverse - // isoparametric mapping for this mesh configuration was providing - // a (xi,eta) point outside the parent quadrilateral. A check - // was added to the InvIso() routine to catch this and error out. - - // read data sets for mesh - std::string ixm_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_ironing_sub_mesh/ixm.txt"); - std::string ixs_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_ironing_sub_mesh/ixs.txt"); - std::string xm_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_ironing_sub_mesh/xm.txt"); - std::string ym_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_ironing_sub_mesh/ym.txt"); - std::string zm_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_ironing_sub_mesh/zm.txt"); - std::string xs_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_ironing_sub_mesh/xs.txt"); - std::string ys_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_ironing_sub_mesh/ys.txt"); - std::string zs_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_ironing_sub_mesh/zs.txt"); - - this->lengthMortarConn = 28; - this->lengthNonmortarConn = 8; - this->lengthMortarNodes = 663; - this->lengthNonmortarNodes = 663; - this->numMortarCells = 7; - this->numNonmortarCells = 2; - - std::ifstream i_ixm( ixm_file ); - std::ifstream i_ixs( ixs_file ); - std::ifstream i_xm( xm_file ); - std::ifstream i_ym( ym_file ); - std::ifstream i_zm( zm_file ); - std::ifstream i_xs( xs_file ); - std::ifstream i_ys( ys_file ); - std::ifstream i_zs( zs_file ); - - this->v_ixm.SetSize( this->lengthMortarConn ); - this->v_ixs.SetSize( this->lengthNonmortarConn ); - - this->v_ixm.Load( i_ixm, 1 ); - this->v_ixs.Load( i_ixs, 1 ); - this->v_xm.Load( i_xm, this->lengthMortarNodes ); - this->v_ym.Load( i_ym, this->lengthMortarNodes ); - this->v_zm.Load( i_zm, this->lengthMortarNodes ); - this->v_xs.Load( i_xs, this->lengthNonmortarNodes ); - this->v_ys.Load( i_ys, this->lengthNonmortarNodes ); - this->v_zs.Load( i_zs, this->lengthNonmortarNodes ); - - i_ixm.close(); - i_ixs.close(); - i_xm.close(); - i_ym.close(); - i_zm.close(); - i_xs.close(); - i_ys.close(); - i_zs.close(); - - SLIC_INFO("After loading mesh data and constructing mfem vectors."); - - // get pointers to mfem vector data - int* ixm_data = this->v_ixm.GetData(); - int* ixs_data = this->v_ixs.GetData(); - RealT* xm_data = this->v_xm.GetData(); - RealT* ym_data = this->v_ym.GetData(); - RealT* zm_data = this->v_zm.GetData(); - RealT* xs_data = this->v_xs.GetData(); - RealT* ys_data = this->v_ys.GetData(); - RealT* zs_data = this->v_zs.GetData(); - - // set gaps and pressure arrays. Note that for this test - // the length of the nonmortar nodes array is the same as the mortar, - // which means that it is the total number of nodes in the whole - // mesh - RealT* gaps, * pressures; - int numTotalNodes = this->lengthNonmortarNodes; - gaps = new RealT[ numTotalNodes ]; - pressures = new RealT[ numTotalNodes ]; - - // initialize arrays - for (int i=0; inumMortarCells, - this->lengthMortarNodes, - ixm_data, - xm_data, - ym_data, - zm_data, - this->numNonmortarCells, - this->lengthNonmortarNodes, - ixs_data, - xs_data, - ys_data, - zs_data, - 1.e-3, - gaps, - pressures); - - RealT dt = 1.0; - int err = Update( dt ); - - EXPECT_EQ( err, 0 ); - - delete[] gaps; - delete[] pressures; + // this mesh tests if the update occurred without errors. + // Specifically, the Tribol issue was that the inverse + // isoparametric mapping for this mesh configuration was providing + // a (xi,eta) point outside the parent quadrilateral. A check + // was added to the InvIso() routine to catch this and error out. + + // read data sets for mesh + std::string ixm_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_ironing_sub_mesh/ixm.txt" ); + std::string ixs_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_ironing_sub_mesh/ixs.txt" ); + std::string xm_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_ironing_sub_mesh/xm.txt" ); + std::string ym_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_ironing_sub_mesh/ym.txt" ); + std::string zm_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_ironing_sub_mesh/zm.txt" ); + std::string xs_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_ironing_sub_mesh/xs.txt" ); + std::string ys_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_ironing_sub_mesh/ys.txt" ); + std::string zs_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_ironing_sub_mesh/zs.txt" ); + + this->lengthMortarConn = 28; + this->lengthNonmortarConn = 8; + this->lengthMortarNodes = 663; + this->lengthNonmortarNodes = 663; + this->numMortarCells = 7; + this->numNonmortarCells = 2; + + std::ifstream i_ixm( ixm_file ); + std::ifstream i_ixs( ixs_file ); + std::ifstream i_xm( xm_file ); + std::ifstream i_ym( ym_file ); + std::ifstream i_zm( zm_file ); + std::ifstream i_xs( xs_file ); + std::ifstream i_ys( ys_file ); + std::ifstream i_zs( zs_file ); + + this->v_ixm.SetSize( this->lengthMortarConn ); + this->v_ixs.SetSize( this->lengthNonmortarConn ); + + this->v_ixm.Load( i_ixm, 1 ); + this->v_ixs.Load( i_ixs, 1 ); + this->v_xm.Load( i_xm, this->lengthMortarNodes ); + this->v_ym.Load( i_ym, this->lengthMortarNodes ); + this->v_zm.Load( i_zm, this->lengthMortarNodes ); + this->v_xs.Load( i_xs, this->lengthNonmortarNodes ); + this->v_ys.Load( i_ys, this->lengthNonmortarNodes ); + this->v_zs.Load( i_zs, this->lengthNonmortarNodes ); + + i_ixm.close(); + i_ixs.close(); + i_xm.close(); + i_ym.close(); + i_zm.close(); + i_xs.close(); + i_ys.close(); + i_zs.close(); + + SLIC_INFO( "After loading mesh data and constructing mfem vectors." ); + + // get pointers to mfem vector data + int* ixm_data = this->v_ixm.GetData(); + int* ixs_data = this->v_ixs.GetData(); + RealT* xm_data = this->v_xm.GetData(); + RealT* ym_data = this->v_ym.GetData(); + RealT* zm_data = this->v_zm.GetData(); + RealT* xs_data = this->v_xs.GetData(); + RealT* ys_data = this->v_ys.GetData(); + RealT* zs_data = this->v_zs.GetData(); + + // set gaps and pressure arrays. Note that for this test + // the length of the nonmortar nodes array is the same as the mortar, + // which means that it is the total number of nodes in the whole + // mesh + RealT *gaps, *pressures; + int numTotalNodes = this->lengthNonmortarNodes; + gaps = new RealT[numTotalNodes]; + pressures = new RealT[numTotalNodes]; + + // initialize arrays + for ( int i = 0; i < numTotalNodes; ++i ) { + gaps[i] = 0.; + pressures[i] = 1.; + } + + // setup simple coupling + SimpleCouplingSetup( 3, (int)( tribol::LINEAR_QUAD ), tribol::MORTAR_WEIGHTS, this->numMortarCells, + this->lengthMortarNodes, ixm_data, xm_data, ym_data, zm_data, this->numNonmortarCells, + this->lengthNonmortarNodes, ixs_data, xs_data, ys_data, zs_data, 1.e-3, gaps, pressures ); + + RealT dt = 1.0; + int err = Update( dt ); + + EXPECT_EQ( err, 0 ); + + delete[] gaps; + delete[] pressures; } -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); // Initialize Tribol via simple Tribol interface Initialize(); #ifdef TRIBOL_USE_UMPIRE - umpire::ResourceManager::getInstance(); // initialize umpire's ResouceManager + umpire::ResourceManager::getInstance(); // initialize umpire's ResouceManager #endif - tribol::SimpleMPIWrapper wrapper(argc, argv); // initialize and finalize MPI, when applicable + tribol::SimpleMPIWrapper wrapper( argc, argv ); // initialize and finalize MPI, when applicable result = RUN_ALL_TESTS(); diff --git a/src/tests/tribol_mortar_data_weights.cpp b/src/tests/tribol_mortar_data_weights.cpp index d42eae0a..92613e89 100644 --- a/src/tests/tribol_mortar_data_weights.cpp +++ b/src/tests/tribol_mortar_data_weights.cpp @@ -33,7 +33,7 @@ #include "gtest/gtest.h" // c++ includes -#include // std::abs, std::cos, std::sin +#include // std::abs, std::cos, std::sin #include #include #include @@ -42,449 +42,386 @@ using RealT = tribol::RealT; namespace axom_fs = axom::utilities::filesystem; -void TestMortarWeights( tribol::CouplingScheme const * cs, RealT exact_area, RealT tol ) +void TestMortarWeights( tribol::CouplingScheme const* cs, RealT exact_area, RealT tol ) { - //////////////////////////////////////////////////////////////////////// - // - // Grab pointers to mesh data - // - //////////////////////////////////////////////////////////////////////// - - // get CSR weights data - int *I = nullptr; - int *J = nullptr; - RealT *wts = nullptr; - int nOffsets = 0; - int nNonZeros = 0; - int csr_err = GetSimpleCouplingCSR( &I, &J, &wts, &nOffsets, &nNonZeros ); - - EXPECT_EQ( csr_err, 0 ); - - SLIC_ERROR_IF(I==nullptr, "Mortar wts test, I is null."); - - RealT area = 0.; - int numTotalNodes = static_cast( cs->getMethodData() )->m_numTotalNodes; - for (int a=0; a= nodeOffset, "nonmortar/nonmortar weight for nonmortar node, " << a << " and mortar node, " << J[b] << "."); - - } // end loop over nonzero columns, I[a] - } // end loop over matrix rows - - area /= 2.; - - SLIC_DEBUG("area: " << area << "."); - - RealT diff = std::abs( area - exact_area ); - EXPECT_LE( diff, tol ); - -} // end TestMortarWeights() - + //////////////////////////////////////////////////////////////////////// + // + // Grab pointers to mesh data + // + //////////////////////////////////////////////////////////////////////// + + // get CSR weights data + int* I = nullptr; + int* J = nullptr; + RealT* wts = nullptr; + int nOffsets = 0; + int nNonZeros = 0; + int csr_err = GetSimpleCouplingCSR( &I, &J, &wts, &nOffsets, &nNonZeros ); + + EXPECT_EQ( csr_err, 0 ); + + SLIC_ERROR_IF( I == nullptr, "Mortar wts test, I is null." ); + + RealT area = 0.; + int numTotalNodes = static_cast( cs->getMethodData() )->m_numTotalNodes; + for ( int a = 0; a < numTotalNodes; ++a ) { + // loop over range of nonzero column entries + for ( int b = I[a]; b < I[a + 1]; ++b ) { + area += wts[b]; + + // nonmortar/mortar weight + // SLIC_DEBUG_IF(J[b] < nodeOffset, "nonmortar/mortar weight for nonmortar node, " << a << " and mortar node, " << + // J[b] << "."); nonmortar/nonmortar weight SLIC_DEBUG_IF(J[b] >= nodeOffset, "nonmortar/nonmortar weight for + // nonmortar node, " << a << " and mortar node, " << J[b] << "."); + + } // end loop over nonzero columns, I[a] + } // end loop over matrix rows + + area /= 2.; + + SLIC_DEBUG( "area: " << area << "." ); + + RealT diff = std::abs( area - exact_area ); + EXPECT_LE( diff, tol ); + +} // end TestMortarWeights() /*! * Test fixture class with some setup necessary to test * the MORTAR_WEIGHTS implementation in Tribol */ -class MortarSparseWtsTest : public ::testing::Test -{ - -public: - mfem::Vector v_xm; - mfem::Vector v_ym; - mfem::Vector v_zm; - mfem::Vector v_xs; - mfem::Vector v_ys; - mfem::Vector v_zs; - mfem::Array v_ixm; - mfem::Array v_ixs; - - int lengthMortarConn; - int lengthNonmortarConn; - int lengthMortarNodes; - int lengthNonmortarNodes; - int numMortarCells; - int numNonmortarCells; - - tribol::TestMesh m_mesh; - -protected: - - void SetUp() override - { - } - - void TearDown() override - { - // call clear() on mesh object to be safe - this->m_mesh.clear(); - } - -protected: - +class MortarSparseWtsTest : public ::testing::Test { + public: + mfem::Vector v_xm; + mfem::Vector v_ym; + mfem::Vector v_zm; + mfem::Vector v_xs; + mfem::Vector v_ys; + mfem::Vector v_zs; + mfem::Array v_ixm; + mfem::Array v_ixs; + + int lengthMortarConn; + int lengthNonmortarConn; + int lengthMortarNodes; + int lengthNonmortarNodes; + int numMortarCells; + int numNonmortarCells; + + tribol::TestMesh m_mesh; + + protected: + void SetUp() override {} + + void TearDown() override + { + // call clear() on mesh object to be safe + this->m_mesh.clear(); + } + + protected: }; TEST_F( MortarSparseWtsTest, mortar_sphere ) { - - // read data sets for mesh - std::string ixm_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_sphere/ixm.txt"); - std::string ixs_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_sphere/ixs.txt"); - std::string xm_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_sphere/xm.txt"); - std::string ym_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_sphere/ym.txt"); - std::string zm_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_sphere/zm.txt"); - std::string xs_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_sphere/xs.txt"); - std::string ys_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_sphere/ys.txt"); - std::string zs_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_sphere/zs.txt"); - - this->lengthMortarConn = 192; - this->lengthNonmortarConn = 768; - this->lengthMortarNodes = 834; - this->lengthNonmortarNodes = 834; - this->numMortarCells = 48; - this->numNonmortarCells = 192; - - std::ifstream i_ixm( ixm_file ); - std::ifstream i_ixs( ixs_file ); - std::ifstream i_xm( xm_file ); - std::ifstream i_ym( ym_file ); - std::ifstream i_zm( zm_file ); - std::ifstream i_xs( xs_file ); - std::ifstream i_ys( ys_file ); - std::ifstream i_zs( zs_file ); - - this->v_ixm.SetSize( this->lengthMortarConn ); - this->v_ixs.SetSize( this->lengthNonmortarConn ); - - this->v_ixm.Load( i_ixm, 1 ); - this->v_ixs.Load( i_ixs, 1 ); - this->v_xm.Load( i_xm, this->lengthMortarNodes ); - this->v_ym.Load( i_ym, this->lengthMortarNodes ); - this->v_zm.Load( i_zm, this->lengthMortarNodes ); - this->v_xs.Load( i_xs, this->lengthNonmortarNodes ); - this->v_ys.Load( i_ys, this->lengthNonmortarNodes ); - this->v_zs.Load( i_zs, this->lengthNonmortarNodes ); - - i_ixm.close(); - i_ixs.close(); - i_xm.close(); - i_ym.close(); - i_zm.close(); - i_xs.close(); - i_ys.close(); - i_zs.close(); - - SLIC_DEBUG("After loading mesh data and constructing mfem vectors."); - - // get pointers to mfem vector data - int* ixm_data = this->v_ixm.GetData(); - int* ixs_data = this->v_ixs.GetData(); - RealT* xm_data = this->v_xm.GetData(); - RealT* ym_data = this->v_ym.GetData(); - RealT* zm_data = this->v_zm.GetData(); - RealT* xs_data = this->v_xs.GetData(); - RealT* ys_data = this->v_ys.GetData(); - RealT* zs_data = this->v_zs.GetData(); - - // set gaps and pressure arrays. Note that for this test - // the length of the nonmortar nodes array is the same as the mortar, - // which means that it is the total number of nodes in the whole - // mesh - RealT* gaps, * pressures; - int numTotalNodes = this->lengthNonmortarNodes; - gaps = new RealT[ numTotalNodes ]; - pressures = new RealT[ numTotalNodes ]; - - // initialize arrays - for (int i=0; inumMortarCells, - this->lengthMortarNodes, - ixm_data, - xm_data, - ym_data, - zm_data, - this->numNonmortarCells, - this->lengthNonmortarNodes, - ixs_data, - xs_data, - ys_data, - zs_data, - 1.e-3, - gaps, - pressures); - - RealT dt = 1.0; - int err = Update( dt ); - - EXPECT_EQ(err, 0); - - tribol::CouplingSchemeManager& couplingSchemeManager = - tribol::CouplingSchemeManager::getInstance(); - - tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); - TestMortarWeights( couplingScheme, 2.256, 1.e-3 ); - - delete[] gaps; - delete[] pressures; + // read data sets for mesh + std::string ixm_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_sphere/ixm.txt" ); + std::string ixs_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_sphere/ixs.txt" ); + std::string xm_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_sphere/xm.txt" ); + std::string ym_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_sphere/ym.txt" ); + std::string zm_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_sphere/zm.txt" ); + std::string xs_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_sphere/xs.txt" ); + std::string ys_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_sphere/ys.txt" ); + std::string zs_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_sphere/zs.txt" ); + + this->lengthMortarConn = 192; + this->lengthNonmortarConn = 768; + this->lengthMortarNodes = 834; + this->lengthNonmortarNodes = 834; + this->numMortarCells = 48; + this->numNonmortarCells = 192; + + std::ifstream i_ixm( ixm_file ); + std::ifstream i_ixs( ixs_file ); + std::ifstream i_xm( xm_file ); + std::ifstream i_ym( ym_file ); + std::ifstream i_zm( zm_file ); + std::ifstream i_xs( xs_file ); + std::ifstream i_ys( ys_file ); + std::ifstream i_zs( zs_file ); + + this->v_ixm.SetSize( this->lengthMortarConn ); + this->v_ixs.SetSize( this->lengthNonmortarConn ); + + this->v_ixm.Load( i_ixm, 1 ); + this->v_ixs.Load( i_ixs, 1 ); + this->v_xm.Load( i_xm, this->lengthMortarNodes ); + this->v_ym.Load( i_ym, this->lengthMortarNodes ); + this->v_zm.Load( i_zm, this->lengthMortarNodes ); + this->v_xs.Load( i_xs, this->lengthNonmortarNodes ); + this->v_ys.Load( i_ys, this->lengthNonmortarNodes ); + this->v_zs.Load( i_zs, this->lengthNonmortarNodes ); + + i_ixm.close(); + i_ixs.close(); + i_xm.close(); + i_ym.close(); + i_zm.close(); + i_xs.close(); + i_ys.close(); + i_zs.close(); + + SLIC_DEBUG( "After loading mesh data and constructing mfem vectors." ); + + // get pointers to mfem vector data + int* ixm_data = this->v_ixm.GetData(); + int* ixs_data = this->v_ixs.GetData(); + RealT* xm_data = this->v_xm.GetData(); + RealT* ym_data = this->v_ym.GetData(); + RealT* zm_data = this->v_zm.GetData(); + RealT* xs_data = this->v_xs.GetData(); + RealT* ys_data = this->v_ys.GetData(); + RealT* zs_data = this->v_zs.GetData(); + + // set gaps and pressure arrays. Note that for this test + // the length of the nonmortar nodes array is the same as the mortar, + // which means that it is the total number of nodes in the whole + // mesh + RealT *gaps, *pressures; + int numTotalNodes = this->lengthNonmortarNodes; + gaps = new RealT[numTotalNodes]; + pressures = new RealT[numTotalNodes]; + + // initialize arrays + for ( int i = 0; i < numTotalNodes; ++i ) { + gaps[i] = 0.; + pressures[i] = 1.; + } + + // setup simple coupling + SimpleCouplingSetup( 3, (int)( tribol::LINEAR_QUAD ), tribol::MORTAR_WEIGHTS, this->numMortarCells, + this->lengthMortarNodes, ixm_data, xm_data, ym_data, zm_data, this->numNonmortarCells, + this->lengthNonmortarNodes, ixs_data, xs_data, ys_data, zs_data, 1.e-3, gaps, pressures ); + + RealT dt = 1.0; + int err = Update( dt ); + + EXPECT_EQ( err, 0 ); + + tribol::CouplingSchemeManager& couplingSchemeManager = tribol::CouplingSchemeManager::getInstance(); + + tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); + TestMortarWeights( couplingScheme, 2.256, 1.e-3 ); + + delete[] gaps; + delete[] pressures; } TEST_F( MortarSparseWtsTest, mortar_sphere_offset ) { - - // read data sets for mesh - std::string ixm_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_sphere_offset/ixm.txt"); - std::string ixs_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_sphere_offset/ixs.txt"); - std::string xm_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_sphere_offset/xm.txt"); - std::string ym_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_sphere_offset/ym.txt"); - std::string zm_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_sphere_offset/zm.txt"); - std::string xs_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_sphere_offset/xs.txt"); - std::string ys_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_sphere_offset/ys.txt"); - std::string zs_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_sphere_offset/zs.txt"); - - this->lengthMortarConn = 4*507; - this->lengthNonmortarConn = 4*48; - this->lengthMortarNodes = 2918; - this->lengthNonmortarNodes = 2918; - this->numMortarCells = 507; - this->numNonmortarCells = 48; - - std::ifstream i_ixm( ixm_file ); - std::ifstream i_ixs( ixs_file ); - std::ifstream i_xm( xm_file ); - std::ifstream i_ym( ym_file ); - std::ifstream i_zm( zm_file ); - std::ifstream i_xs( xs_file ); - std::ifstream i_ys( ys_file ); - std::ifstream i_zs( zs_file ); - - this->v_ixm.SetSize( this->lengthMortarConn ); - this->v_ixs.SetSize( this->lengthNonmortarConn ); - - this->v_ixm.Load( i_ixm, 1 ); - this->v_ixs.Load( i_ixs, 1 ); - this->v_xm.Load( i_xm, this->lengthMortarNodes ); - this->v_ym.Load( i_ym, this->lengthMortarNodes ); - this->v_zm.Load( i_zm, this->lengthMortarNodes ); - this->v_xs.Load( i_xs, this->lengthNonmortarNodes ); - this->v_ys.Load( i_ys, this->lengthNonmortarNodes ); - this->v_zs.Load( i_zs, this->lengthNonmortarNodes ); - - i_ixm.close(); - i_ixs.close(); - i_xm.close(); - i_ym.close(); - i_zm.close(); - i_xs.close(); - i_ys.close(); - i_zs.close(); - - SLIC_DEBUG("After loading mesh data and constructing mfem vectors."); - - // get pointers to mfem vector data - int* ixm_data = this->v_ixm.GetData(); - int* ixs_data = this->v_ixs.GetData(); - RealT* xm_data = this->v_xm.GetData(); - RealT* ym_data = this->v_ym.GetData(); - RealT* zm_data = this->v_zm.GetData(); - RealT* xs_data = this->v_xs.GetData(); - RealT* ys_data = this->v_ys.GetData(); - RealT* zs_data = this->v_zs.GetData(); - - // set gaps and pressure arrays. Note that for this test - // the length of the nonmortar nodes array is the same as the mortar, - // which means that it is the total number of nodes in the whole - // mesh - RealT* gaps, * pressures; - int numTotalNodes = this->lengthNonmortarNodes; - gaps = new RealT[ numTotalNodes ]; - pressures = new RealT[ numTotalNodes ]; - - // initialize arrays - for (int i=0; inumMortarCells, - this->lengthMortarNodes, - ixm_data, - xm_data, - ym_data, - zm_data, - this->numNonmortarCells, - this->lengthNonmortarNodes, - ixs_data, - xs_data, - ys_data, - zs_data, - 1.e-3, - gaps, - pressures); - - RealT dt = 1.0; - int err = Update( dt ); - - EXPECT_EQ(err, 0); - - tribol::CouplingSchemeManager& couplingSchemeManager = - tribol::CouplingSchemeManager::getInstance(); - - tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); - TestMortarWeights( couplingScheme, 2.260, 1.e-1 ); - - delete[] gaps; - delete[] pressures; + // read data sets for mesh + std::string ixm_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_sphere_offset/ixm.txt" ); + std::string ixs_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_sphere_offset/ixs.txt" ); + std::string xm_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_sphere_offset/xm.txt" ); + std::string ym_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_sphere_offset/ym.txt" ); + std::string zm_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_sphere_offset/zm.txt" ); + std::string xs_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_sphere_offset/xs.txt" ); + std::string ys_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_sphere_offset/ys.txt" ); + std::string zs_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_sphere_offset/zs.txt" ); + + this->lengthMortarConn = 4 * 507; + this->lengthNonmortarConn = 4 * 48; + this->lengthMortarNodes = 2918; + this->lengthNonmortarNodes = 2918; + this->numMortarCells = 507; + this->numNonmortarCells = 48; + + std::ifstream i_ixm( ixm_file ); + std::ifstream i_ixs( ixs_file ); + std::ifstream i_xm( xm_file ); + std::ifstream i_ym( ym_file ); + std::ifstream i_zm( zm_file ); + std::ifstream i_xs( xs_file ); + std::ifstream i_ys( ys_file ); + std::ifstream i_zs( zs_file ); + + this->v_ixm.SetSize( this->lengthMortarConn ); + this->v_ixs.SetSize( this->lengthNonmortarConn ); + + this->v_ixm.Load( i_ixm, 1 ); + this->v_ixs.Load( i_ixs, 1 ); + this->v_xm.Load( i_xm, this->lengthMortarNodes ); + this->v_ym.Load( i_ym, this->lengthMortarNodes ); + this->v_zm.Load( i_zm, this->lengthMortarNodes ); + this->v_xs.Load( i_xs, this->lengthNonmortarNodes ); + this->v_ys.Load( i_ys, this->lengthNonmortarNodes ); + this->v_zs.Load( i_zs, this->lengthNonmortarNodes ); + + i_ixm.close(); + i_ixs.close(); + i_xm.close(); + i_ym.close(); + i_zm.close(); + i_xs.close(); + i_ys.close(); + i_zs.close(); + + SLIC_DEBUG( "After loading mesh data and constructing mfem vectors." ); + + // get pointers to mfem vector data + int* ixm_data = this->v_ixm.GetData(); + int* ixs_data = this->v_ixs.GetData(); + RealT* xm_data = this->v_xm.GetData(); + RealT* ym_data = this->v_ym.GetData(); + RealT* zm_data = this->v_zm.GetData(); + RealT* xs_data = this->v_xs.GetData(); + RealT* ys_data = this->v_ys.GetData(); + RealT* zs_data = this->v_zs.GetData(); + + // set gaps and pressure arrays. Note that for this test + // the length of the nonmortar nodes array is the same as the mortar, + // which means that it is the total number of nodes in the whole + // mesh + RealT *gaps, *pressures; + int numTotalNodes = this->lengthNonmortarNodes; + gaps = new RealT[numTotalNodes]; + pressures = new RealT[numTotalNodes]; + + // initialize arrays + for ( int i = 0; i < numTotalNodes; ++i ) { + gaps[i] = 0.; + pressures[i] = 1.; + } + + // setup simple coupling + SimpleCouplingSetup( 3, (int)( tribol::LINEAR_QUAD ), tribol::MORTAR_WEIGHTS, this->numMortarCells, + this->lengthMortarNodes, ixm_data, xm_data, ym_data, zm_data, this->numNonmortarCells, + this->lengthNonmortarNodes, ixs_data, xs_data, ys_data, zs_data, 1.e-3, gaps, pressures ); + + RealT dt = 1.0; + int err = Update( dt ); + + EXPECT_EQ( err, 0 ); + + tribol::CouplingSchemeManager& couplingSchemeManager = tribol::CouplingSchemeManager::getInstance(); + + tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); + TestMortarWeights( couplingScheme, 2.260, 1.e-1 ); + + delete[] gaps; + delete[] pressures; } TEST_F( MortarSparseWtsTest, mortar_one_seg_rotated ) { - - // read data sets for mesh - std::string ixm_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_one_seg_rotated_square/ixm.txt"); - std::string ixs_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_one_seg_rotated_square/ixs.txt"); - std::string xm_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_one_seg_rotated_square/xm.txt"); - std::string ym_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_one_seg_rotated_square/ym.txt"); - std::string zm_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_one_seg_rotated_square/zm.txt"); - std::string xs_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_one_seg_rotated_square/xs.txt"); - std::string ys_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_one_seg_rotated_square/ys.txt"); - std::string zs_file = axom_fs::joinPath(TRIBOL_DATA_DIR,"mortar_one_seg_rotated_square/zs.txt"); - - this->lengthMortarConn = 4; - this->lengthNonmortarConn = 4; - this->lengthMortarNodes = 16; - this->lengthNonmortarNodes = 16; - this->numMortarCells = 1; - this->numNonmortarCells = 1; - - std::ifstream i_ixm( ixm_file ); - std::ifstream i_ixs( ixs_file ); - std::ifstream i_xm( xm_file ); - std::ifstream i_ym( ym_file ); - std::ifstream i_zm( zm_file ); - std::ifstream i_xs( xs_file ); - std::ifstream i_ys( ys_file ); - std::ifstream i_zs( zs_file ); - - this->v_ixm.SetSize( this->lengthMortarConn ); - this->v_ixs.SetSize( this->lengthNonmortarConn ); - - this->v_ixm.Load( i_ixm, 1 ); - this->v_ixs.Load( i_ixs, 1 ); - this->v_xm.Load( i_xm, this->lengthMortarNodes ); - this->v_ym.Load( i_ym, this->lengthMortarNodes ); - this->v_zm.Load( i_zm, this->lengthMortarNodes ); - this->v_xs.Load( i_xs, this->lengthNonmortarNodes ); - this->v_ys.Load( i_ys, this->lengthNonmortarNodes ); - this->v_zs.Load( i_zs, this->lengthNonmortarNodes ); - - i_ixm.close(); - i_ixs.close(); - i_xm.close(); - i_ym.close(); - i_zm.close(); - i_xs.close(); - i_ys.close(); - i_zs.close(); - - SLIC_DEBUG("After loading mesh data and constructing mfem vectors."); - - // get pointers to mfem vector data - int* ixm_data = this->v_ixm.GetData(); - int* ixs_data = this->v_ixs.GetData(); - RealT* xm_data = this->v_xm.GetData(); - RealT* ym_data = this->v_ym.GetData(); - RealT* zm_data = this->v_zm.GetData(); - RealT* xs_data = this->v_xs.GetData(); - RealT* ys_data = this->v_ys.GetData(); - RealT* zs_data = this->v_zs.GetData(); - - // set gaps and pressure arrays. Note that for this test - // the length of the nonmortar nodes array is the same as the mortar, - // which means that it is the total number of nodes in the whole - // mesh - RealT* gaps, * pressures; - int numTotalNodes = this->lengthNonmortarNodes; - gaps = new RealT[ numTotalNodes ]; - pressures = new RealT[ numTotalNodes ]; - - // initialize arrays - for (int i=0; inumMortarCells, - this->lengthMortarNodes, - ixm_data, - xm_data, - ym_data, - zm_data, - this->numNonmortarCells, - this->lengthNonmortarNodes, - ixs_data, - xs_data, - ys_data, - zs_data, - 1.e-3, - gaps, - pressures); - - RealT dt = 1.0; - int err = Update( dt ); - - EXPECT_EQ(err, 0); - - tribol::CouplingSchemeManager& couplingSchemeManager = - tribol::CouplingSchemeManager::getInstance(); - - tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); - TestMortarWeights( couplingScheme, 20., 1.e-3 ); - - delete[] gaps; - delete[] pressures; + // read data sets for mesh + std::string ixm_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_one_seg_rotated_square/ixm.txt" ); + std::string ixs_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_one_seg_rotated_square/ixs.txt" ); + std::string xm_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_one_seg_rotated_square/xm.txt" ); + std::string ym_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_one_seg_rotated_square/ym.txt" ); + std::string zm_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_one_seg_rotated_square/zm.txt" ); + std::string xs_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_one_seg_rotated_square/xs.txt" ); + std::string ys_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_one_seg_rotated_square/ys.txt" ); + std::string zs_file = axom_fs::joinPath( TRIBOL_DATA_DIR, "mortar_one_seg_rotated_square/zs.txt" ); + + this->lengthMortarConn = 4; + this->lengthNonmortarConn = 4; + this->lengthMortarNodes = 16; + this->lengthNonmortarNodes = 16; + this->numMortarCells = 1; + this->numNonmortarCells = 1; + + std::ifstream i_ixm( ixm_file ); + std::ifstream i_ixs( ixs_file ); + std::ifstream i_xm( xm_file ); + std::ifstream i_ym( ym_file ); + std::ifstream i_zm( zm_file ); + std::ifstream i_xs( xs_file ); + std::ifstream i_ys( ys_file ); + std::ifstream i_zs( zs_file ); + + this->v_ixm.SetSize( this->lengthMortarConn ); + this->v_ixs.SetSize( this->lengthNonmortarConn ); + + this->v_ixm.Load( i_ixm, 1 ); + this->v_ixs.Load( i_ixs, 1 ); + this->v_xm.Load( i_xm, this->lengthMortarNodes ); + this->v_ym.Load( i_ym, this->lengthMortarNodes ); + this->v_zm.Load( i_zm, this->lengthMortarNodes ); + this->v_xs.Load( i_xs, this->lengthNonmortarNodes ); + this->v_ys.Load( i_ys, this->lengthNonmortarNodes ); + this->v_zs.Load( i_zs, this->lengthNonmortarNodes ); + + i_ixm.close(); + i_ixs.close(); + i_xm.close(); + i_ym.close(); + i_zm.close(); + i_xs.close(); + i_ys.close(); + i_zs.close(); + + SLIC_DEBUG( "After loading mesh data and constructing mfem vectors." ); + + // get pointers to mfem vector data + int* ixm_data = this->v_ixm.GetData(); + int* ixs_data = this->v_ixs.GetData(); + RealT* xm_data = this->v_xm.GetData(); + RealT* ym_data = this->v_ym.GetData(); + RealT* zm_data = this->v_zm.GetData(); + RealT* xs_data = this->v_xs.GetData(); + RealT* ys_data = this->v_ys.GetData(); + RealT* zs_data = this->v_zs.GetData(); + + // set gaps and pressure arrays. Note that for this test + // the length of the nonmortar nodes array is the same as the mortar, + // which means that it is the total number of nodes in the whole + // mesh + RealT *gaps, *pressures; + int numTotalNodes = this->lengthNonmortarNodes; + gaps = new RealT[numTotalNodes]; + pressures = new RealT[numTotalNodes]; + + // initialize arrays + for ( int i = 0; i < numTotalNodes; ++i ) { + gaps[i] = 0.; + pressures[i] = 1.; + } + + // setup simple coupling + SimpleCouplingSetup( 3, (int)( tribol::LINEAR_QUAD ), tribol::MORTAR_WEIGHTS, this->numMortarCells, + this->lengthMortarNodes, ixm_data, xm_data, ym_data, zm_data, this->numNonmortarCells, + this->lengthNonmortarNodes, ixs_data, xs_data, ys_data, zs_data, 1.e-3, gaps, pressures ); + + RealT dt = 1.0; + int err = Update( dt ); + + EXPECT_EQ( err, 0 ); + + tribol::CouplingSchemeManager& couplingSchemeManager = tribol::CouplingSchemeManager::getInstance(); + + tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); + TestMortarWeights( couplingScheme, 20., 1.e-3 ); + + delete[] gaps; + delete[] pressures; } -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); // Initialize Tribol via simple Tribol interface - Initialize(3); + Initialize( 3 ); #ifdef TRIBOL_USE_UMPIRE - umpire::ResourceManager::getInstance(); // initialize umpire's ResouceManager + umpire::ResourceManager::getInstance(); // initialize umpire's ResouceManager #endif - axom::slic::SimpleLogger logger; // create & initialize logger, - tribol::SimpleMPIWrapper wrapper(argc, argv); // initialize and finalize MPI, when applicable + axom::slic::SimpleLogger logger; // create & initialize logger, + tribol::SimpleMPIWrapper wrapper( argc, argv ); // initialize and finalize MPI, when applicable result = RUN_ALL_TESTS(); diff --git a/src/tests/tribol_mortar_force.cpp b/src/tests/tribol_mortar_force.cpp index 4011279c..35ccd0bc 100644 --- a/src/tests/tribol_mortar_force.cpp +++ b/src/tests/tribol_mortar_force.cpp @@ -25,707 +25,623 @@ #include "gtest/gtest.h" // c++ includes -#include // std::abs +#include // std::abs using RealT = tribol::RealT; /*! - * Test fixture class with some setup necessary to compute - * the mortar contact forces between two parallel, but + * Test fixture class with some setup necessary to compute + * the mortar contact forces between two parallel, but * misaligned faces */ -class MortarForceTest : public ::testing::Test -{ - -public: - int numNodes; - int numFaces; - int numNodesPerFace; - int numOverlapNodes; - int dim; - - RealT* getXCoords( ) - { - return x; - } - - RealT* getYCoords( ) - { - return y; - } - - RealT* getZCoords( ) - { - return z; - } - - RealT* getXOverlapCoords() - { - return xOverlap; - } - - RealT* getYOverlapCoords() - { - return yOverlap; - } - - RealT* getZOverlapCoords() - { - return zOverlap; - } - - void checkMortarForces( int * conn1, - int * conn2, - tribol::ContactMethod method ) - { - // grab coordinate data - RealT * x = this->x; - RealT * y = this->y; - RealT * z = this->z; - - // register the mesh with tribol - int cellType = static_cast(tribol::UNDEFINED_ELEMENT); - switch (this->numNodesPerFace) - { - case 4: - { - cellType = (int)(tribol::LINEAR_QUAD); - break; - } - default: - { - SLIC_ERROR("checkMortarForces: number of nodes per face not equal to 4."); - } - } - - const int mortarMeshId = 0; - const int nonmortarMeshId = 1; - - // register mesh - tribol::registerMesh( mortarMeshId, 1, - this->numNodes, - conn1, cellType, - x, y, z, tribol::MemorySpace::Host ); - tribol::registerMesh( nonmortarMeshId, 1, - this->numNodes, - conn2, cellType, - x, y, z, tribol::MemorySpace::Host ); - - // register nodal forces - RealT *fx1, *fy1, *fz1; - RealT *fx2, *fy2, *fz2; - - RealT forceX1[ this->numNodes ]; - RealT forceY1[ this->numNodes ]; - RealT forceZ1[ this->numNodes ]; - - RealT forceX2[ this->numNodes ]; - RealT forceY2[ this->numNodes ]; - RealT forceZ2[ this->numNodes ]; - - fx1 = forceX1; - fy1 = forceY1; - fz1 = forceZ1; - - fx2 = forceX2; - fy2 = forceY2; - fz2 = forceZ2; - - // initialize force arrays - for (int i=0; inumNodes; ++i) - { - fx1[i] = 0.; - fy1[i] = 0.; - fz1[i] = 0.; - fx2[i] = 0.; - fy2[i] = 0.; - fz2[i] = 0.; - } - - tribol::registerNodalResponse( mortarMeshId, fx1, fy1, fz1 ); - tribol::registerNodalResponse( nonmortarMeshId, fx2, fy2, fz2 ); - - // register nodal pressure and nodal gap array for the nonmortar mesh - RealT *gaps, *pressures; - - gaps = new RealT [ this->numNodes ]; - pressures = new RealT [ this->numNodes ]; - - // initialize gaps and pressures. Initialize all - // nonmortar pressures to 1.0 - for (int i=0; inumNodes; ++i) - { - gaps[i] = 0.; - pressures[i] = 1.; - } - - // register nodal gaps and pressure arrays - tribol::registerMortarGaps( nonmortarMeshId, gaps ); - tribol::registerMortarPressures( nonmortarMeshId, pressures ); - - // register coupling scheme - const int csIndex = 0; - tribol::registerCouplingScheme( csIndex, - mortarMeshId, - nonmortarMeshId, - tribol::SURFACE_TO_SURFACE, - tribol::NO_CASE, - method, - tribol::FRICTIONLESS, - tribol::LAGRANGE_MULTIPLIER, - tribol::DEFAULT_BINNING_METHOD, - tribol::ExecutionMode::Sequential ); - - tribol::setLagrangeMultiplierOptions( csIndex, tribol::ImplicitEvalMode::MORTAR_RESIDUAL, - tribol::SparseMode::MFEM_LINKED_LIST ); - - // call tribol update - RealT dt = 1.0; - int tribol_update_err = tribol::update( 1, 1., dt ); - - EXPECT_EQ( tribol_update_err, 0 ); - - // diagnostics - auto& cs = tribol::CouplingSchemeManager::getInstance().at(csIndex); - const auto mortarMesh = cs.getMesh1().getView(); - const auto nonmortarMesh = cs.getMesh2().getView(); - - // compute the sum of the nodal forces - RealT fx1Sum = 0.; - RealT fy1Sum = 0.; - RealT fz1Sum = 0.; - - RealT fx2Sum = 0.; - RealT fy2Sum = 0.; - RealT fz2Sum = 0.; - for (int i=0; inumNodesPerFace; ++i) - { - int nonmortarNodeId = nonmortarMesh.getGlobalNodeId( 0, i ); - int mortarNodeId = mortarMesh.getGlobalNodeId( 0, i ); - - fx2Sum += nonmortarMesh.getResponse()[0][ nonmortarNodeId ]; - fy2Sum += nonmortarMesh.getResponse()[1][ nonmortarNodeId ]; - fz2Sum += nonmortarMesh.getResponse()[2][ nonmortarNodeId ]; - - fx1Sum += mortarMesh.getResponse()[0][ mortarNodeId ]; - fy1Sum += mortarMesh.getResponse()[1][ mortarNodeId ]; - fz1Sum += mortarMesh.getResponse()[2][ mortarNodeId ]; - } - - // sum nonmortar pressure - RealT pSum = 0.; - for (int i=0; inumNodesPerFace; ++i) - { - int nonmortarNodeId = nonmortarMesh.getGlobalNodeId( 0, i ); - pSum += nonmortarMesh.getNodalFields().m_node_pressure[ nonmortarNodeId ]; - } - - RealT diffX1 = std::abs(fx1Sum) - std::abs(pSum); - RealT diffY1 = std::abs(fy1Sum) - std::abs(pSum); - RealT diffZ1 = std::abs(fz1Sum) - std::abs(pSum); - - RealT diffX2 = std::abs(fx2Sum) - std::abs(pSum); - RealT diffY2 = std::abs(fy2Sum) - std::abs(pSum); - RealT diffZ2 = std::abs(fz2Sum) - std::abs(pSum); - - RealT tol = 1.e-8; - EXPECT_LE( diffX1, tol); - EXPECT_LE( diffY1, tol); - EXPECT_LE( diffZ1, tol); - EXPECT_LE( diffX2, tol); - EXPECT_LE( diffY2, tol); - EXPECT_LE( diffZ2, tol); - - // finalize - tribol::finalize(); - - delete [] gaps; - delete [] pressures; +class MortarForceTest : public ::testing::Test { + public: + int numNodes; + int numFaces; + int numNodesPerFace; + int numOverlapNodes; + int dim; - } // end checkMortarForces() + RealT* getXCoords() { return x; } -protected: + RealT* getYCoords() { return y; } - void SetUp() override - { - this->numNodes = 8; - this->numFaces = 2; - this->numNodesPerFace = 4; - this->numOverlapNodes = 4; - this->dim = 3; + RealT* getZCoords() { return z; } - if (this->x == nullptr) - { - this->x = new RealT [this->numNodes]; - } - else - { - delete [] this->x; - this->x = new RealT [this->numNodes]; - } + RealT* getXOverlapCoords() { return xOverlap; } - if (this->y == nullptr) - { - this->y = new RealT [this->numNodes]; - } - else - { - delete [] this->y; - this->y = new RealT [this->numNodes]; - } + RealT* getYOverlapCoords() { return yOverlap; } - if (this->z == nullptr) - { - this->z = new RealT [this->numNodes]; - } - else - { - delete [] this->z; - this->z = new RealT [this->numNodes]; - } + RealT* getZOverlapCoords() { return zOverlap; } - if (this->xOverlap == nullptr) - { - this->xOverlap = new RealT [this->numOverlapNodes]; - } - else - { - delete [] this->xOverlap; - this->xOverlap = new RealT [this->numOverlapNodes]; - } + void checkMortarForces( int* conn1, int* conn2, tribol::ContactMethod method ) + { + // grab coordinate data + RealT* x = this->x; + RealT* y = this->y; + RealT* z = this->z; - if (this->yOverlap == nullptr) - { - this->yOverlap = new RealT [this->numOverlapNodes]; - } - else - { - delete [] this->yOverlap; - this->yOverlap = new RealT [this->numOverlapNodes]; - } - if (this->zOverlap == nullptr) - { - this->zOverlap = new RealT [this->numOverlapNodes]; - } - else - { - delete [] this->zOverlap; - this->zOverlap = new RealT [this->numOverlapNodes]; - } - } - - void TearDown() override - { - if (this->x != nullptr) - { - delete [] this->x; - this->x = nullptr; + // register the mesh with tribol + int cellType = static_cast( tribol::UNDEFINED_ELEMENT ); + switch ( this->numNodesPerFace ) { + case 4: { + cellType = (int)( tribol::LINEAR_QUAD ); + break; } - if (this->y != nullptr) - { - delete [] this->y; - this->y = nullptr; + default: { + SLIC_ERROR( "checkMortarForces: number of nodes per face not equal to 4." ); } - if (this->z != nullptr) - { - delete [] this->z; - this->z = nullptr; - } - if (this->xOverlap != nullptr) - { - delete [] this->xOverlap; - this->xOverlap = nullptr; - } - if (this->yOverlap != nullptr) - { - delete [] this->yOverlap; - this->yOverlap = nullptr; - } - if (this->zOverlap != nullptr) - { - delete [] this->zOverlap; - this->zOverlap = nullptr; - } - } - -protected: - - RealT* x {nullptr}; - RealT* y {nullptr}; - RealT* z {nullptr}; - - RealT* xOverlap {nullptr}; - RealT* yOverlap {nullptr}; - RealT* zOverlap {nullptr}; - + } + + const int mortarMeshId = 0; + const int nonmortarMeshId = 1; + + // register mesh + tribol::registerMesh( mortarMeshId, 1, this->numNodes, conn1, cellType, x, y, z, tribol::MemorySpace::Host ); + tribol::registerMesh( nonmortarMeshId, 1, this->numNodes, conn2, cellType, x, y, z, tribol::MemorySpace::Host ); + + // register nodal forces + RealT *fx1, *fy1, *fz1; + RealT *fx2, *fy2, *fz2; + + RealT forceX1[this->numNodes]; + RealT forceY1[this->numNodes]; + RealT forceZ1[this->numNodes]; + + RealT forceX2[this->numNodes]; + RealT forceY2[this->numNodes]; + RealT forceZ2[this->numNodes]; + + fx1 = forceX1; + fy1 = forceY1; + fz1 = forceZ1; + + fx2 = forceX2; + fy2 = forceY2; + fz2 = forceZ2; + + // initialize force arrays + for ( int i = 0; i < this->numNodes; ++i ) { + fx1[i] = 0.; + fy1[i] = 0.; + fz1[i] = 0.; + fx2[i] = 0.; + fy2[i] = 0.; + fz2[i] = 0.; + } + + tribol::registerNodalResponse( mortarMeshId, fx1, fy1, fz1 ); + tribol::registerNodalResponse( nonmortarMeshId, fx2, fy2, fz2 ); + + // register nodal pressure and nodal gap array for the nonmortar mesh + RealT *gaps, *pressures; + + gaps = new RealT[this->numNodes]; + pressures = new RealT[this->numNodes]; + + // initialize gaps and pressures. Initialize all + // nonmortar pressures to 1.0 + for ( int i = 0; i < this->numNodes; ++i ) { + gaps[i] = 0.; + pressures[i] = 1.; + } + + // register nodal gaps and pressure arrays + tribol::registerMortarGaps( nonmortarMeshId, gaps ); + tribol::registerMortarPressures( nonmortarMeshId, pressures ); + + // register coupling scheme + const int csIndex = 0; + tribol::registerCouplingScheme( csIndex, mortarMeshId, nonmortarMeshId, tribol::SURFACE_TO_SURFACE, tribol::NO_CASE, + method, tribol::FRICTIONLESS, tribol::LAGRANGE_MULTIPLIER, + tribol::DEFAULT_BINNING_METHOD, tribol::ExecutionMode::Sequential ); + + tribol::setLagrangeMultiplierOptions( csIndex, tribol::ImplicitEvalMode::MORTAR_RESIDUAL, + tribol::SparseMode::MFEM_LINKED_LIST ); + + // call tribol update + RealT dt = 1.0; + int tribol_update_err = tribol::update( 1, 1., dt ); + + EXPECT_EQ( tribol_update_err, 0 ); + + // diagnostics + auto& cs = tribol::CouplingSchemeManager::getInstance().at( csIndex ); + const auto mortarMesh = cs.getMesh1().getView(); + const auto nonmortarMesh = cs.getMesh2().getView(); + + // compute the sum of the nodal forces + RealT fx1Sum = 0.; + RealT fy1Sum = 0.; + RealT fz1Sum = 0.; + + RealT fx2Sum = 0.; + RealT fy2Sum = 0.; + RealT fz2Sum = 0.; + for ( int i = 0; i < this->numNodesPerFace; ++i ) { + int nonmortarNodeId = nonmortarMesh.getGlobalNodeId( 0, i ); + int mortarNodeId = mortarMesh.getGlobalNodeId( 0, i ); + + fx2Sum += nonmortarMesh.getResponse()[0][nonmortarNodeId]; + fy2Sum += nonmortarMesh.getResponse()[1][nonmortarNodeId]; + fz2Sum += nonmortarMesh.getResponse()[2][nonmortarNodeId]; + + fx1Sum += mortarMesh.getResponse()[0][mortarNodeId]; + fy1Sum += mortarMesh.getResponse()[1][mortarNodeId]; + fz1Sum += mortarMesh.getResponse()[2][mortarNodeId]; + } + + // sum nonmortar pressure + RealT pSum = 0.; + for ( int i = 0; i < this->numNodesPerFace; ++i ) { + int nonmortarNodeId = nonmortarMesh.getGlobalNodeId( 0, i ); + pSum += nonmortarMesh.getNodalFields().m_node_pressure[nonmortarNodeId]; + } + + RealT diffX1 = std::abs( fx1Sum ) - std::abs( pSum ); + RealT diffY1 = std::abs( fy1Sum ) - std::abs( pSum ); + RealT diffZ1 = std::abs( fz1Sum ) - std::abs( pSum ); + + RealT diffX2 = std::abs( fx2Sum ) - std::abs( pSum ); + RealT diffY2 = std::abs( fy2Sum ) - std::abs( pSum ); + RealT diffZ2 = std::abs( fz2Sum ) - std::abs( pSum ); + + RealT tol = 1.e-8; + EXPECT_LE( diffX1, tol ); + EXPECT_LE( diffY1, tol ); + EXPECT_LE( diffZ1, tol ); + EXPECT_LE( diffX2, tol ); + EXPECT_LE( diffY2, tol ); + EXPECT_LE( diffZ2, tol ); + + // finalize + tribol::finalize(); + + delete[] gaps; + delete[] pressures; + + } // end checkMortarForces() + + protected: + void SetUp() override + { + this->numNodes = 8; + this->numFaces = 2; + this->numNodesPerFace = 4; + this->numOverlapNodes = 4; + this->dim = 3; + + if ( this->x == nullptr ) { + this->x = new RealT[this->numNodes]; + } else { + delete[] this->x; + this->x = new RealT[this->numNodes]; + } + + if ( this->y == nullptr ) { + this->y = new RealT[this->numNodes]; + } else { + delete[] this->y; + this->y = new RealT[this->numNodes]; + } + + if ( this->z == nullptr ) { + this->z = new RealT[this->numNodes]; + } else { + delete[] this->z; + this->z = new RealT[this->numNodes]; + } + + if ( this->xOverlap == nullptr ) { + this->xOverlap = new RealT[this->numOverlapNodes]; + } else { + delete[] this->xOverlap; + this->xOverlap = new RealT[this->numOverlapNodes]; + } + + if ( this->yOverlap == nullptr ) { + this->yOverlap = new RealT[this->numOverlapNodes]; + } else { + delete[] this->yOverlap; + this->yOverlap = new RealT[this->numOverlapNodes]; + } + if ( this->zOverlap == nullptr ) { + this->zOverlap = new RealT[this->numOverlapNodes]; + } else { + delete[] this->zOverlap; + this->zOverlap = new RealT[this->numOverlapNodes]; + } + } + + void TearDown() override + { + if ( this->x != nullptr ) { + delete[] this->x; + this->x = nullptr; + } + if ( this->y != nullptr ) { + delete[] this->y; + this->y = nullptr; + } + if ( this->z != nullptr ) { + delete[] this->z; + this->z = nullptr; + } + if ( this->xOverlap != nullptr ) { + delete[] this->xOverlap; + this->xOverlap = nullptr; + } + if ( this->yOverlap != nullptr ) { + delete[] this->yOverlap; + this->yOverlap = nullptr; + } + if ( this->zOverlap != nullptr ) { + delete[] this->zOverlap; + this->zOverlap = nullptr; + } + } + + protected: + RealT* x{ nullptr }; + RealT* y{ nullptr }; + RealT* z{ nullptr }; + + RealT* xOverlap{ nullptr }; + RealT* yOverlap{ nullptr }; + RealT* zOverlap{ nullptr }; }; TEST_F( MortarForceTest, parallel_misaligned ) { - - RealT* x = this->getXCoords(); - RealT* y = this->getYCoords(); - RealT* z = this->getZCoords(); - - RealT* xOvrlp = this->getXOverlapCoords(); - RealT* yOvrlp = this->getYOverlapCoords(); - RealT* zOvrlp = this->getZOverlapCoords(); - - x[0] = -1.; - x[1] = -1.; - x[2] = 1.; - x[3] = 1.; - - y[0] = 1.; - y[1] = -1.; - y[2] = -1.; - y[3] = 1.; - - z[0] = 0.1; - z[1] = 0.1; - z[2] = 0.1; - z[3] = 0.1; - - x[4] = 0.; - x[5] = 2.; - x[6] = 2.; - x[7] = 0.; - - y[4] = 0.; - y[5] = 0.; - y[6] = -2.; - y[7] = -2.; - - z[4] = 0; - z[5] = 0; - z[6] = 0; - z[7] = 0; - - xOvrlp[0] = 0.; - xOvrlp[1] = 0.; - xOvrlp[2] = 1.; - xOvrlp[3] = 1.; - - yOvrlp[0] = 0.; - yOvrlp[1] = -1.; - yOvrlp[2] = -1.; - yOvrlp[3] = 0.; - - zOvrlp[0] = 0.1; - zOvrlp[1] = 0.1; - zOvrlp[2] = 0.1; - zOvrlp[3] = 0.1; - - // register a tribol mesh for computing mortar gaps - int numNodesPerFace = 4; - int conn1[ numNodesPerFace ]; - int conn2[ numNodesPerFace ]; - - for (int i=0; icheckMortarForces( &conn1[0], &conn2[0], tribol::SINGLE_MORTAR ); - + RealT* x = this->getXCoords(); + RealT* y = this->getYCoords(); + RealT* z = this->getZCoords(); + + RealT* xOvrlp = this->getXOverlapCoords(); + RealT* yOvrlp = this->getYOverlapCoords(); + RealT* zOvrlp = this->getZOverlapCoords(); + + x[0] = -1.; + x[1] = -1.; + x[2] = 1.; + x[3] = 1.; + + y[0] = 1.; + y[1] = -1.; + y[2] = -1.; + y[3] = 1.; + + z[0] = 0.1; + z[1] = 0.1; + z[2] = 0.1; + z[3] = 0.1; + + x[4] = 0.; + x[5] = 2.; + x[6] = 2.; + x[7] = 0.; + + y[4] = 0.; + y[5] = 0.; + y[6] = -2.; + y[7] = -2.; + + z[4] = 0; + z[5] = 0; + z[6] = 0; + z[7] = 0; + + xOvrlp[0] = 0.; + xOvrlp[1] = 0.; + xOvrlp[2] = 1.; + xOvrlp[3] = 1.; + + yOvrlp[0] = 0.; + yOvrlp[1] = -1.; + yOvrlp[2] = -1.; + yOvrlp[3] = 0.; + + zOvrlp[0] = 0.1; + zOvrlp[1] = 0.1; + zOvrlp[2] = 0.1; + zOvrlp[3] = 0.1; + + // register a tribol mesh for computing mortar gaps + int numNodesPerFace = 4; + int conn1[numNodesPerFace]; + int conn2[numNodesPerFace]; + + for ( int i = 0; i < numNodesPerFace; ++i ) { + conn1[i] = i; + conn2[i] = numNodesPerFace + i; + } + + this->checkMortarForces( &conn1[0], &conn2[0], tribol::SINGLE_MORTAR ); } TEST_F( MortarForceTest, parallel_aligned ) { - - RealT* x = this->getXCoords(); - RealT* y = this->getYCoords(); - RealT* z = this->getZCoords(); - - RealT* xOvrlp = this->getXOverlapCoords(); - RealT* yOvrlp = this->getYOverlapCoords(); - RealT* zOvrlp = this->getZOverlapCoords(); - - x[0] = -1.; - x[1] = -1.; - x[2] = 1.; - x[3] = 1.; - - y[0] = 1.; - y[1] = -1.; - y[2] = -1.; - y[3] = 1.; - - z[0] = 0.1; - z[1] = 0.1; - z[2] = 0.1; - z[3] = 0.1; - - x[4] = -1.; - x[5] = 1.; - x[6] = 1.; - x[7] = -1.; - - y[4] = 1.; - y[5] = 1.; - y[6] = -1.; - y[7] = -1.; - - z[4] = 0; - z[5] = 0; - z[6] = 0; - z[7] = 0; - - xOvrlp[0] = x[0]; - xOvrlp[1] = x[1]; - xOvrlp[2] = x[2]; - xOvrlp[3] = x[3]; - - yOvrlp[0] = y[0]; - yOvrlp[1] = y[1]; - yOvrlp[2] = y[2]; - yOvrlp[3] = y[3]; - - zOvrlp[0] = z[0]; - zOvrlp[1] = z[1]; - zOvrlp[2] = z[2]; - zOvrlp[3] = z[3]; - - // register a tribol mesh for computing mortar gaps - int numNodesPerFace = 4; - int conn1[ numNodesPerFace ]; - int conn2[ numNodesPerFace ]; - - for (int i=0; icheckMortarForces( &conn1[0], &conn2[0], tribol::SINGLE_MORTAR ); - + RealT* x = this->getXCoords(); + RealT* y = this->getYCoords(); + RealT* z = this->getZCoords(); + + RealT* xOvrlp = this->getXOverlapCoords(); + RealT* yOvrlp = this->getYOverlapCoords(); + RealT* zOvrlp = this->getZOverlapCoords(); + + x[0] = -1.; + x[1] = -1.; + x[2] = 1.; + x[3] = 1.; + + y[0] = 1.; + y[1] = -1.; + y[2] = -1.; + y[3] = 1.; + + z[0] = 0.1; + z[1] = 0.1; + z[2] = 0.1; + z[3] = 0.1; + + x[4] = -1.; + x[5] = 1.; + x[6] = 1.; + x[7] = -1.; + + y[4] = 1.; + y[5] = 1.; + y[6] = -1.; + y[7] = -1.; + + z[4] = 0; + z[5] = 0; + z[6] = 0; + z[7] = 0; + + xOvrlp[0] = x[0]; + xOvrlp[1] = x[1]; + xOvrlp[2] = x[2]; + xOvrlp[3] = x[3]; + + yOvrlp[0] = y[0]; + yOvrlp[1] = y[1]; + yOvrlp[2] = y[2]; + yOvrlp[3] = y[3]; + + zOvrlp[0] = z[0]; + zOvrlp[1] = z[1]; + zOvrlp[2] = z[2]; + zOvrlp[3] = z[3]; + + // register a tribol mesh for computing mortar gaps + int numNodesPerFace = 4; + int conn1[numNodesPerFace]; + int conn2[numNodesPerFace]; + + for ( int i = 0; i < numNodesPerFace; ++i ) { + conn1[i] = i; + conn2[i] = numNodesPerFace + i; + } + + this->checkMortarForces( &conn1[0], &conn2[0], tribol::SINGLE_MORTAR ); } TEST_F( MortarForceTest, non_parallel_misaligned ) { - - RealT* x = this->getXCoords(); - RealT* y = this->getYCoords(); - RealT* z = this->getZCoords(); - - RealT* xOvrlp = this->getXOverlapCoords(); - RealT* yOvrlp = this->getYOverlapCoords(); - RealT* zOvrlp = this->getZOverlapCoords(); - - x[0] = -1.; - x[1] = -1.; - x[2] = 1.; - x[3] = 1.; - - y[0] = 1.; - y[1] = -1.; - y[2] = -1.; - y[3] = 1.; - - z[0] = 0.1; - z[1] = 0.1; - z[2] = 0.1; - z[3] = 0.1; - - x[4] = 0.; - x[5] = 2.; - x[6] = 2.; - x[7] = 0.; - - y[4] = 0.; - y[5] = 0.; - y[6] = -2.; - y[7] = -2.; - - z[4] = 0.05; - z[5] = 0.05; - z[6] = 0; - z[7] = 0; - - xOvrlp[0] = 0.; - xOvrlp[1] = 0.; - xOvrlp[2] = 1.; - xOvrlp[3] = 1.; - - yOvrlp[0] = 0.; - yOvrlp[1] = -1.; - yOvrlp[2] = -1.; - yOvrlp[3] = 0.; - - zOvrlp[0] = 0.1; - zOvrlp[1] = 0.1; - zOvrlp[2] = 0.1; - zOvrlp[3] = 0.1; - - // register a tribol mesh for computing mortar gaps - int numNodesPerFace = 4; - int conn1[ numNodesPerFace ]; - int conn2[ numNodesPerFace ]; - - for (int i=0; icheckMortarForces( &conn1[0], &conn2[0], tribol::SINGLE_MORTAR ); - + RealT* x = this->getXCoords(); + RealT* y = this->getYCoords(); + RealT* z = this->getZCoords(); + + RealT* xOvrlp = this->getXOverlapCoords(); + RealT* yOvrlp = this->getYOverlapCoords(); + RealT* zOvrlp = this->getZOverlapCoords(); + + x[0] = -1.; + x[1] = -1.; + x[2] = 1.; + x[3] = 1.; + + y[0] = 1.; + y[1] = -1.; + y[2] = -1.; + y[3] = 1.; + + z[0] = 0.1; + z[1] = 0.1; + z[2] = 0.1; + z[3] = 0.1; + + x[4] = 0.; + x[5] = 2.; + x[6] = 2.; + x[7] = 0.; + + y[4] = 0.; + y[5] = 0.; + y[6] = -2.; + y[7] = -2.; + + z[4] = 0.05; + z[5] = 0.05; + z[6] = 0; + z[7] = 0; + + xOvrlp[0] = 0.; + xOvrlp[1] = 0.; + xOvrlp[2] = 1.; + xOvrlp[3] = 1.; + + yOvrlp[0] = 0.; + yOvrlp[1] = -1.; + yOvrlp[2] = -1.; + yOvrlp[3] = 0.; + + zOvrlp[0] = 0.1; + zOvrlp[1] = 0.1; + zOvrlp[2] = 0.1; + zOvrlp[3] = 0.1; + + // register a tribol mesh for computing mortar gaps + int numNodesPerFace = 4; + int conn1[numNodesPerFace]; + int conn2[numNodesPerFace]; + + for ( int i = 0; i < numNodesPerFace; ++i ) { + conn1[i] = i; + conn2[i] = numNodesPerFace + i; + } + + this->checkMortarForces( &conn1[0], &conn2[0], tribol::SINGLE_MORTAR ); } TEST_F( MortarForceTest, non_parallel_aligned ) { - - RealT* x = this->getXCoords(); - RealT* y = this->getYCoords(); - RealT* z = this->getZCoords(); - - RealT* xOvrlp = this->getXOverlapCoords(); - RealT* yOvrlp = this->getYOverlapCoords(); - RealT* zOvrlp = this->getZOverlapCoords(); - - x[0] = -1.; - x[1] = -1.; - x[2] = 1.; - x[3] = 1.; - - y[0] = 1.; - y[1] = -1.; - y[2] = -1.; - y[3] = 1.; - - z[0] = 0.1; - z[1] = 0.1; - z[2] = 0.1; - z[3] = 0.1; - - x[4] = -1.; - x[5] = 1.; - x[6] = 1.; - x[7] = -1.; - - y[4] = 1.; - y[5] = 1.; - y[6] = -1.; - y[7] = -1.; - - z[4] = 0.05; - z[5] = 0.05; - z[6] = 0; - z[7] = 0; - - xOvrlp[0] = x[0]; - xOvrlp[1] = x[1]; - xOvrlp[2] = x[2]; - xOvrlp[3] = x[3]; - - yOvrlp[0] = y[0]; - yOvrlp[1] = y[1]; - yOvrlp[2] = y[2]; - yOvrlp[3] = y[3]; - - zOvrlp[0] = z[0]; - zOvrlp[1] = z[1]; - zOvrlp[2] = z[2]; - zOvrlp[3] = z[3]; - - // register a tribol mesh for computing mortar gaps - int numNodesPerFace = 4; - int conn1[ numNodesPerFace ]; - int conn2[ numNodesPerFace ]; - - for (int i=0; icheckMortarForces( &conn1[0], &conn2[0], tribol::SINGLE_MORTAR ); - + RealT* x = this->getXCoords(); + RealT* y = this->getYCoords(); + RealT* z = this->getZCoords(); + + RealT* xOvrlp = this->getXOverlapCoords(); + RealT* yOvrlp = this->getYOverlapCoords(); + RealT* zOvrlp = this->getZOverlapCoords(); + + x[0] = -1.; + x[1] = -1.; + x[2] = 1.; + x[3] = 1.; + + y[0] = 1.; + y[1] = -1.; + y[2] = -1.; + y[3] = 1.; + + z[0] = 0.1; + z[1] = 0.1; + z[2] = 0.1; + z[3] = 0.1; + + x[4] = -1.; + x[5] = 1.; + x[6] = 1.; + x[7] = -1.; + + y[4] = 1.; + y[5] = 1.; + y[6] = -1.; + y[7] = -1.; + + z[4] = 0.05; + z[5] = 0.05; + z[6] = 0; + z[7] = 0; + + xOvrlp[0] = x[0]; + xOvrlp[1] = x[1]; + xOvrlp[2] = x[2]; + xOvrlp[3] = x[3]; + + yOvrlp[0] = y[0]; + yOvrlp[1] = y[1]; + yOvrlp[2] = y[2]; + yOvrlp[3] = y[3]; + + zOvrlp[0] = z[0]; + zOvrlp[1] = z[1]; + zOvrlp[2] = z[2]; + zOvrlp[3] = z[3]; + + // register a tribol mesh for computing mortar gaps + int numNodesPerFace = 4; + int conn1[numNodesPerFace]; + int conn2[numNodesPerFace]; + + for ( int i = 0; i < numNodesPerFace; ++i ) { + conn1[i] = i; + conn2[i] = numNodesPerFace + i; + } + + this->checkMortarForces( &conn1[0], &conn2[0], tribol::SINGLE_MORTAR ); } TEST_F( MortarForceTest, parallel_simple_aligned ) { - - RealT* x = this->getXCoords(); - RealT* y = this->getYCoords(); - RealT* z = this->getZCoords(); - - RealT* xOvrlp = this->getXOverlapCoords(); - RealT* yOvrlp = this->getYOverlapCoords(); - RealT* zOvrlp = this->getZOverlapCoords(); - - x[0] = -1.; - x[1] = -1.; - x[2] = 1.; - x[3] = 1.; - - y[0] = 1.; - y[1] = -1.; - y[2] = -1.; - y[3] = 1.; - - z[0] = 0.1; - z[1] = 0.1; - z[2] = 0.1; - z[3] = 0.1; - - x[4] = -1.; - x[5] = 1.; - x[6] = 1.; - x[7] = -1.; - - y[4] = 1.; - y[5] = 1.; - y[6] = -1.; - y[7] = -1.; - - z[4] = 0.; - z[5] = 0.; - z[6] = 0.; - z[7] = 0.; - - xOvrlp[0] = x[0]; - xOvrlp[1] = x[1]; - xOvrlp[2] = x[2]; - xOvrlp[3] = x[3]; - - yOvrlp[0] = y[0]; - yOvrlp[1] = y[1]; - yOvrlp[2] = y[2]; - yOvrlp[3] = y[3]; - - zOvrlp[0] = z[0]; - zOvrlp[1] = z[1]; - zOvrlp[2] = z[2]; - zOvrlp[3] = z[3]; - - // register a tribol mesh for computing mortar gaps - int numNodesPerFace = 4; - int conn1[ numNodesPerFace ]; - int conn2[ numNodesPerFace ]; - - for (int i=0; icheckMortarForces( &conn1[0], &conn2[0], tribol::ALIGNED_MORTAR ); - + RealT* x = this->getXCoords(); + RealT* y = this->getYCoords(); + RealT* z = this->getZCoords(); + + RealT* xOvrlp = this->getXOverlapCoords(); + RealT* yOvrlp = this->getYOverlapCoords(); + RealT* zOvrlp = this->getZOverlapCoords(); + + x[0] = -1.; + x[1] = -1.; + x[2] = 1.; + x[3] = 1.; + + y[0] = 1.; + y[1] = -1.; + y[2] = -1.; + y[3] = 1.; + + z[0] = 0.1; + z[1] = 0.1; + z[2] = 0.1; + z[3] = 0.1; + + x[4] = -1.; + x[5] = 1.; + x[6] = 1.; + x[7] = -1.; + + y[4] = 1.; + y[5] = 1.; + y[6] = -1.; + y[7] = -1.; + + z[4] = 0.; + z[5] = 0.; + z[6] = 0.; + z[7] = 0.; + + xOvrlp[0] = x[0]; + xOvrlp[1] = x[1]; + xOvrlp[2] = x[2]; + xOvrlp[3] = x[3]; + + yOvrlp[0] = y[0]; + yOvrlp[1] = y[1]; + yOvrlp[2] = y[2]; + yOvrlp[3] = y[3]; + + zOvrlp[0] = z[0]; + zOvrlp[1] = z[1]; + zOvrlp[2] = z[2]; + zOvrlp[3] = z[3]; + + // register a tribol mesh for computing mortar gaps + int numNodesPerFace = 4; + int conn1[numNodesPerFace]; + int conn2[numNodesPerFace]; + + for ( int i = 0; i < numNodesPerFace; ++i ) { + conn1[i] = i; + conn2[i] = numNodesPerFace + i; + } + + this->checkMortarForces( &conn1[0], &conn2[0], tribol::ALIGNED_MORTAR ); } -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); #ifdef TRIBOL_USE_UMPIRE umpire::ResourceManager::getInstance(); // initialize umpire's ResouceManager #endif - axom::slic::SimpleLogger logger; // create & initialize logger, + axom::slic::SimpleLogger logger; // create & initialize logger, result = RUN_ALL_TESTS(); diff --git a/src/tests/tribol_mortar_gap.cpp b/src/tests/tribol_mortar_gap.cpp index 9b9b58b5..dec63f5b 100644 --- a/src/tests/tribol_mortar_gap.cpp +++ b/src/tests/tribol_mortar_gap.cpp @@ -26,7 +26,7 @@ #include "gtest/gtest.h" // c++ includes -#include // std::abs +#include // std::abs using RealT = tribol::RealT; @@ -34,660 +34,576 @@ using RealT = tribol::RealT; * Test fixture class with some setup necessary to compute * the mortar gap between two parallel, but misaligned faces */ -class MortarGapTest : public ::testing::Test -{ - -public: - int numNodes; - int numFaces; - int numNodesPerFace; - int numOverlapNodes; - int dim; - - tribol::ArrayT gaps; +class MortarGapTest : public ::testing::Test { + public: + int numNodes; + int numFaces; + int numNodesPerFace; + int numOverlapNodes; + int dim; + + tribol::ArrayT gaps; + + RealT* getXCoords( int id ) + { + if ( id == 0 ) { + return x1; + } else { + return x2; + } + } + + RealT* getYCoords( int id ) + { + if ( id == 0 ) { + return y1; + } else { + return y2; + } + } + + RealT* getZCoords( int id ) + { + if ( id == 0 ) { + return z1; + } else { + return z2; + } + } + + RealT* getXOverlapCoords() { return xOverlap; } + + RealT* getYOverlapCoords() { return yOverlap; } + + RealT* getZOverlapCoords() { return zOverlap; } + + void checkMortarGaps( int* conn1, int* conn2, tribol::ContactMethod method ) + { + // declare arrays to hold stacked coordinates for each + // face used in initializing a SurfaceContactElem struct + RealT xyz1[this->dim * this->numNodesPerFace]; + RealT xyz2[this->dim * this->numNodesPerFace]; + + // declare array to hold overlap vertices used for + // initializing a SurfaceContactElem struct + RealT xyzOverlap[this->dim * this->numOverlapNodes]; + + // assign pointers to arrays + RealT* xy1 = xyz1; + RealT* xy2 = xyz2; + RealT* xyOverlap = xyzOverlap; + + // grab coordinate data + RealT* x1 = this->x1; + RealT* y1 = this->y1; + RealT* z1 = this->z1; + RealT* x2 = this->x2; + RealT* y2 = this->y2; + RealT* z2 = this->z2; + RealT* xo = this->xOverlap; + RealT* yo = this->yOverlap; + RealT* zo = this->zOverlap; + + // generate stacked coordinate array + for ( int j = 0; j < this->numNodesPerFace; ++j ) { + for ( int k = 0; k < this->dim; ++k ) { + int id = this->dim * j + k; + // int id2 = this->dim * this->numNodesPerFace + id; + switch ( k ) { + case 0: + xy1[id] = x1[j]; + xy2[id] = x2[j]; + xyOverlap[id] = xo[j]; + break; + case 1: + xy1[id] = y1[j]; + xy2[id] = y2[j]; + xyOverlap[id] = yo[j]; + break; + case 2: + xy1[id] = z1[j]; + xy2[id] = z2[j]; + xyOverlap[id] = zo[j]; + break; + } // end switch + } // end loop over dimension + } // end loop over nodes - RealT* getXCoords( int id ) - { - if (id == 0) - { - return x1; + // register the mesh with tribol + int cellType = static_cast( tribol::UNDEFINED_ELEMENT ); + switch ( this->numNodesPerFace ) { + case 4: { + cellType = (int)( tribol::LINEAR_QUAD ); + break; } - else - { - return x2; + default: { + SLIC_ERROR( "checkMortarWts: number of nodes per face not equal to 4." ); } - } + } - RealT* getYCoords( int id ) - { - if (id == 0) - { - return y1; - } - else - { - return y2; - } - } + const int mortarMeshId = 0; + const int nonmortarMeshId = 1; - RealT* getZCoords( int id ) - { - if (id == 0) - { - return z1; - } - else - { - return z2; - } - } - - RealT* getXOverlapCoords() - { - return xOverlap; - } - - RealT* getYOverlapCoords() - { - return yOverlap; - } - - RealT* getZOverlapCoords() - { - return zOverlap; - } - - void checkMortarGaps( int * conn1, - int * conn2, - tribol::ContactMethod method ) - { - // declare arrays to hold stacked coordinates for each - // face used in initializing a SurfaceContactElem struct - RealT xyz1[ this->dim * this->numNodesPerFace ]; - RealT xyz2[ this->dim * this->numNodesPerFace ]; - - // declare array to hold overlap vertices used for - // initializing a SurfaceContactElem struct - RealT xyzOverlap[ this->dim * this->numOverlapNodes ]; - - // assign pointers to arrays - RealT* xy1 = xyz1; - RealT* xy2 = xyz2; - RealT* xyOverlap = xyzOverlap; - - // grab coordinate data - RealT * x1 = this->x1; - RealT * y1 = this->y1; - RealT * z1 = this->z1; - RealT * x2 = this->x2; - RealT * y2 = this->y2; - RealT * z2 = this->z2; - RealT * xo = this->xOverlap; - RealT * yo = this->yOverlap; - RealT * zo = this->zOverlap; - - // generate stacked coordinate array - for (int j=0; jnumNodesPerFace; ++j) - { - for (int k=0; kdim; ++k) - { - int id = this->dim * j + k; - //int id2 = this->dim * this->numNodesPerFace + id; - switch (k) - { - case 0: - xy1[ id ] = x1[ j ]; - xy2[ id ] = x2[ j ]; - xyOverlap[ id ] = xo[ j ]; - break; - case 1: - xy1[ id ] = y1[ j ]; - xy2[ id ] = y2[ j ]; - xyOverlap[ id ] = yo[ j ]; - break; - case 2: - xy1[ id ] = z1[ j ]; - xy2[ id ] = z2[ j ]; - xyOverlap[ id ] = zo[ j ]; - break; - } // end switch - } // end loop over dimension - } // end loop over nodes - - // register the mesh with tribol - int cellType = static_cast(tribol::UNDEFINED_ELEMENT); - switch (this->numNodesPerFace) - { - case 4: - { - cellType = (int)(tribol::LINEAR_QUAD); - break; - } - default: - { - SLIC_ERROR("checkMortarWts: number of nodes per face not equal to 4."); - } - } + tribol::registerMesh( mortarMeshId, 1, this->numNodesPerFace, conn1, cellType, x1, y1, z1, + tribol::MemorySpace::Host ); + tribol::registerMesh( nonmortarMeshId, 1, this->numNodesPerFace, conn2, cellType, x2, y2, z2, + tribol::MemorySpace::Host ); - const int mortarMeshId = 0; - const int nonmortarMeshId = 1; - - tribol::registerMesh( mortarMeshId, 1, - this->numNodesPerFace, - conn1, cellType, - x1, y1, z1, tribol::MemorySpace::Host ); - tribol::registerMesh( nonmortarMeshId, 1, - this->numNodesPerFace, - conn2, cellType, - x2, y2, z2, tribol::MemorySpace::Host ); - - // get instance of meshes to compute face data required for other calculations - tribol::MeshManager& meshManager = tribol::MeshManager::getInstance(); - tribol::MeshData& mortarMesh = meshManager.at( mortarMeshId ); - tribol::MeshData& nonmortarMesh = meshManager.at( nonmortarMeshId ); - - mortarMesh.computeFaceData(tribol::ExecutionMode::Sequential); - nonmortarMesh.computeFaceData(tribol::ExecutionMode::Sequential); - - gaps.clear(); - int size = 2*this->numNodesPerFace; - gaps.resize(size); - - for (int i=0; inumNodesPerFace; + gaps.resize( size ); + + for ( int i = 0; i < size; ++i ) { + gaps[i] = 0.; + } - tribol::registerMortarGaps( nonmortarMeshId, gaps.data() ); + tribol::registerMortarGaps( nonmortarMeshId, gaps.data() ); - nonmortarMesh.computeNodalNormals( this->dim ); + nonmortarMesh.computeNodalNormals( this->dim ); - auto mortarView = mortarMesh.getView(); - auto nonmortarView = nonmortarMesh.getView(); + auto mortarView = mortarMesh.getView(); + auto nonmortarView = nonmortarMesh.getView(); - // instantiate SurfaceContactElem struct. Note, this object is instantiated - // using face 1, face 2, and the set overlap polygon. Note, the mesh ids are set - // equal to 0, and the face ids are 0 and 1, respectively. - tribol::SurfaceContactElem elem ( this->dim, xy1, xy2, xyOverlap, - this->numNodesPerFace, this->numOverlapNodes, - &mortarView, &nonmortarView, 0, 0); + // instantiate SurfaceContactElem struct. Note, this object is instantiated + // using face 1, face 2, and the set overlap polygon. Note, the mesh ids are set + // equal to 0, and the face ids are 0 and 1, respectively. + tribol::SurfaceContactElem elem( this->dim, xy1, xy2, xyOverlap, this->numNodesPerFace, this->numOverlapNodes, + &mortarView, &nonmortarView, 0, 0 ); - // compute the mortar weights to be stored on - // the surface contact element struct. - switch (method) - { + // compute the mortar weights to be stored on + // the surface contact element struct. + switch ( method ) { case tribol::SINGLE_MORTAR: - tribol::ComputeMortarWeights( elem ); - break; + tribol::ComputeMortarWeights( elem ); + break; case tribol::ALIGNED_MORTAR: - tribol::ComputeAlignedMortarWeights( elem ); - break; + tribol::ComputeAlignedMortarWeights( elem ); + break; default: - SLIC_ERROR("Unsupported contact method"); - break; - } + SLIC_ERROR( "Unsupported contact method" ); + break; + } - switch (method) - { + switch ( method ) { case tribol::SINGLE_MORTAR: - tribol::ComputeNodalGap< tribol::SINGLE_MORTAR >( elem ); - break; + tribol::ComputeNodalGap( elem ); + break; case tribol::ALIGNED_MORTAR: - tribol::ComputeNodalGap< tribol::ALIGNED_MORTAR >( elem ); - break; + tribol::ComputeNodalGap( elem ); + break; default: - SLIC_ERROR("Unsupported contact method"); - break; - } - - tribol::finalize(); - } - -protected: - - void SetUp() override - { - this->numNodes = 8; - this->numFaces = 2; - this->numNodesPerFace = 4; - this->numOverlapNodes = 4; - this->dim = 3; - - if (this->x1 == nullptr) - { - this->x1 = new RealT [this->numNodes]; - } - else - { - delete [] this->x1; - this->x1 = new RealT [this->numNodes]; - } - - if (this->x2 == nullptr) - { - this->x2 = new RealT [this->numNodes]; - } - else - { - delete [] this->x2; - this->x2 = new RealT [this->numNodes]; - } - - if (this->y1 == nullptr) - { - this->y1 = new RealT [this->numNodes]; - } - else - { - delete [] this->y1; - this->y1 = new RealT [this->numNodes]; - } - - if (this->y2 == nullptr) - { - this->y2 = new RealT [this->numNodes]; - } - else - { - delete [] this->y2; - this->y2 = new RealT [this->numNodes]; - } - - if (this->z1 == nullptr) - { - this->z1 = new RealT [this->numNodes]; - } - else - { - delete [] this->z1; - this->z1 = new RealT [this->numNodes]; - } - - if (this->z2 == nullptr) - { - this->z2 = new RealT [this->numNodes]; - } - else - { - delete [] this->z2; - this->z2 = new RealT [this->numNodes]; - } - - if (this->xOverlap == nullptr) - { - this->xOverlap = new RealT [this->numOverlapNodes]; - } - else - { - delete [] this->xOverlap; - this->xOverlap = new RealT [this->numOverlapNodes]; - } - - if (this->yOverlap == nullptr) - { - this->yOverlap = new RealT [this->numOverlapNodes]; - } - else - { - delete [] this->yOverlap; - this->yOverlap = new RealT [this->numOverlapNodes]; - } - if (this->zOverlap == nullptr) - { - this->zOverlap = new RealT [this->numOverlapNodes]; - } - else - { - delete [] this->zOverlap; - this->zOverlap = new RealT [this->numOverlapNodes]; - } - } - - void TearDown() override - { - if (this->x1 != nullptr) - { - delete [] this->x1; - this->x1 = nullptr; - } - if (this->x2 != nullptr) - { - delete [] this->x2; - this->x2 = nullptr; - } - if (this->y1 != nullptr) - { - delete [] this->y1; - this->y1 = nullptr; - } - if (this->y2 != nullptr) - { - delete [] this->y2; - this->y2 = nullptr; - } - if (this->z1 != nullptr) - { - delete [] this->z1; - this->z1 = nullptr; - } - if (this->z2 != nullptr) - { - delete [] this->z2; - this->z2 = nullptr; - } - if (this->xOverlap != nullptr) - { - delete [] this->xOverlap; - this->xOverlap = nullptr; - } - if (this->yOverlap != nullptr) - { - delete [] this->yOverlap; - this->yOverlap = nullptr; - } - if (this->zOverlap != nullptr) - { - delete [] this->zOverlap; - this->zOverlap = nullptr; - } - } - -protected: - - RealT* x1 {nullptr}; - RealT* y1 {nullptr}; - RealT* z1 {nullptr}; - - RealT* x2 {nullptr}; - RealT* y2 {nullptr}; - RealT* z2 {nullptr}; - - RealT* xOverlap {nullptr}; - RealT* yOverlap {nullptr}; - RealT* zOverlap {nullptr}; - + SLIC_ERROR( "Unsupported contact method" ); + break; + } + + tribol::finalize(); + } + + protected: + void SetUp() override + { + this->numNodes = 8; + this->numFaces = 2; + this->numNodesPerFace = 4; + this->numOverlapNodes = 4; + this->dim = 3; + + if ( this->x1 == nullptr ) { + this->x1 = new RealT[this->numNodes]; + } else { + delete[] this->x1; + this->x1 = new RealT[this->numNodes]; + } + + if ( this->x2 == nullptr ) { + this->x2 = new RealT[this->numNodes]; + } else { + delete[] this->x2; + this->x2 = new RealT[this->numNodes]; + } + + if ( this->y1 == nullptr ) { + this->y1 = new RealT[this->numNodes]; + } else { + delete[] this->y1; + this->y1 = new RealT[this->numNodes]; + } + + if ( this->y2 == nullptr ) { + this->y2 = new RealT[this->numNodes]; + } else { + delete[] this->y2; + this->y2 = new RealT[this->numNodes]; + } + + if ( this->z1 == nullptr ) { + this->z1 = new RealT[this->numNodes]; + } else { + delete[] this->z1; + this->z1 = new RealT[this->numNodes]; + } + + if ( this->z2 == nullptr ) { + this->z2 = new RealT[this->numNodes]; + } else { + delete[] this->z2; + this->z2 = new RealT[this->numNodes]; + } + + if ( this->xOverlap == nullptr ) { + this->xOverlap = new RealT[this->numOverlapNodes]; + } else { + delete[] this->xOverlap; + this->xOverlap = new RealT[this->numOverlapNodes]; + } + + if ( this->yOverlap == nullptr ) { + this->yOverlap = new RealT[this->numOverlapNodes]; + } else { + delete[] this->yOverlap; + this->yOverlap = new RealT[this->numOverlapNodes]; + } + if ( this->zOverlap == nullptr ) { + this->zOverlap = new RealT[this->numOverlapNodes]; + } else { + delete[] this->zOverlap; + this->zOverlap = new RealT[this->numOverlapNodes]; + } + } + + void TearDown() override + { + if ( this->x1 != nullptr ) { + delete[] this->x1; + this->x1 = nullptr; + } + if ( this->x2 != nullptr ) { + delete[] this->x2; + this->x2 = nullptr; + } + if ( this->y1 != nullptr ) { + delete[] this->y1; + this->y1 = nullptr; + } + if ( this->y2 != nullptr ) { + delete[] this->y2; + this->y2 = nullptr; + } + if ( this->z1 != nullptr ) { + delete[] this->z1; + this->z1 = nullptr; + } + if ( this->z2 != nullptr ) { + delete[] this->z2; + this->z2 = nullptr; + } + if ( this->xOverlap != nullptr ) { + delete[] this->xOverlap; + this->xOverlap = nullptr; + } + if ( this->yOverlap != nullptr ) { + delete[] this->yOverlap; + this->yOverlap = nullptr; + } + if ( this->zOverlap != nullptr ) { + delete[] this->zOverlap; + this->zOverlap = nullptr; + } + } + + protected: + RealT* x1{ nullptr }; + RealT* y1{ nullptr }; + RealT* z1{ nullptr }; + + RealT* x2{ nullptr }; + RealT* y2{ nullptr }; + RealT* z2{ nullptr }; + + RealT* xOverlap{ nullptr }; + RealT* yOverlap{ nullptr }; + RealT* zOverlap{ nullptr }; }; TEST_F( MortarGapTest, parallel_misaligned ) { - - RealT* x1 = this->getXCoords(0); - RealT* y1 = this->getYCoords(0); - RealT* z1 = this->getZCoords(0); - - RealT* x2 = this->getXCoords(1); - RealT* y2 = this->getYCoords(1); - RealT* z2 = this->getZCoords(1); - - RealT* xOvrlp = this->getXOverlapCoords(); - RealT* yOvrlp = this->getYOverlapCoords(); - RealT* zOvrlp = this->getZOverlapCoords(); - - x1[0] = -1.; - x1[1] = -1.; - x1[2] = 1.; - x1[3] = 1.; - - y1[0] = 1.; - y1[1] = -1.; - y1[2] = -1.; - y1[3] = 1.; - - z1[0] = 0.1; - z1[1] = 0.1; - z1[2] = 0.1; - z1[3] = 0.1; - - x2[0] = 0.; - x2[1] = 2.; - x2[2] = 2.; - x2[3] = 0.; - - y2[0] = 0.; - y2[1] = 0.; - y2[2] = -2.; - y2[3] = -2.; - - z2[0] = 0; - z2[1] = 0; - z2[2] = 0; - z2[3] = 0; - - xOvrlp[0] = 0.; - xOvrlp[1] = 0.; - xOvrlp[2] = 1.; - xOvrlp[3] = 1.; - - yOvrlp[0] = 0.; - yOvrlp[1] = -1.; - yOvrlp[2] = -1.; - yOvrlp[3] = 0.; - - zOvrlp[0] = 0.1; - zOvrlp[1] = 0.1; - zOvrlp[2] = 0.1; - zOvrlp[3] = 0.1; - - // register a tribol mesh for computing mortar gaps - int numNodesPerFace = 4; - int conn1[ numNodesPerFace ]; - int conn2[ numNodesPerFace ]; - - for (int i=0; icheckMortarGaps( &conn1[0], &conn2[0], tribol::SINGLE_MORTAR ); - - tribol::MeshManager& meshManager = tribol::MeshManager::getInstance(); - tribol::MeshData& nonmortarMesh = meshManager.at( 1 ); - - // compute the sum of the nodal gaps - RealT gap = 0.; - for (int i=0; igetXCoords( 0 ); + RealT* y1 = this->getYCoords( 0 ); + RealT* z1 = this->getZCoords( 0 ); + + RealT* x2 = this->getXCoords( 1 ); + RealT* y2 = this->getYCoords( 1 ); + RealT* z2 = this->getZCoords( 1 ); + + RealT* xOvrlp = this->getXOverlapCoords(); + RealT* yOvrlp = this->getYOverlapCoords(); + RealT* zOvrlp = this->getZOverlapCoords(); + + x1[0] = -1.; + x1[1] = -1.; + x1[2] = 1.; + x1[3] = 1.; + + y1[0] = 1.; + y1[1] = -1.; + y1[2] = -1.; + y1[3] = 1.; + + z1[0] = 0.1; + z1[1] = 0.1; + z1[2] = 0.1; + z1[3] = 0.1; + + x2[0] = 0.; + x2[1] = 2.; + x2[2] = 2.; + x2[3] = 0.; + + y2[0] = 0.; + y2[1] = 0.; + y2[2] = -2.; + y2[3] = -2.; + + z2[0] = 0; + z2[1] = 0; + z2[2] = 0; + z2[3] = 0; + + xOvrlp[0] = 0.; + xOvrlp[1] = 0.; + xOvrlp[2] = 1.; + xOvrlp[3] = 1.; + + yOvrlp[0] = 0.; + yOvrlp[1] = -1.; + yOvrlp[2] = -1.; + yOvrlp[3] = 0.; + + zOvrlp[0] = 0.1; + zOvrlp[1] = 0.1; + zOvrlp[2] = 0.1; + zOvrlp[3] = 0.1; + + // register a tribol mesh for computing mortar gaps + int numNodesPerFace = 4; + int conn1[numNodesPerFace]; + int conn2[numNodesPerFace]; + + for ( int i = 0; i < numNodesPerFace; ++i ) { + conn1[i] = i; + conn2[i] = i; + } + + this->checkMortarGaps( &conn1[0], &conn2[0], tribol::SINGLE_MORTAR ); + + tribol::MeshManager& meshManager = tribol::MeshManager::getInstance(); + tribol::MeshData& nonmortarMesh = meshManager.at( 1 ); + + // compute the sum of the nodal gaps + RealT gap = 0.; + for ( int i = 0; i < numNodesPerFace; ++i ) { + gap += nonmortarMesh.getNodalFields().m_node_gap[i]; + } + + // note the face-gap of 0.1 is hard coded based on the + // hard-coded face coordinates in this test + RealT gapDiff = std::abs( 0.1 + gap ); + + RealT tol = 1.e-8; + EXPECT_LE( gapDiff, tol ); } TEST_F( MortarGapTest, parallel_aligned ) { - - RealT* x1 = this->getXCoords(0); - RealT* y1 = this->getYCoords(0); - RealT* z1 = this->getZCoords(0); - - RealT* x2 = this->getXCoords(1); - RealT* y2 = this->getYCoords(1); - RealT* z2 = this->getZCoords(1); - - RealT* xOvrlp = this->getXOverlapCoords(); - RealT* yOvrlp = this->getYOverlapCoords(); - RealT* zOvrlp = this->getZOverlapCoords(); - - x1[0] = 0.; //-1.; - x1[1] = 0.; //-1.; - x1[2] = 1.; - x1[3] = 1.; - - y1[0] = 1.; - y1[1] = 0.; // -1.; - y1[2] = 0.; // -1.; - y1[3] = 1.; - - z1[0] = 0.1; - z1[1] = 0.1; - z1[2] = 0.1; - z1[3] = 0.1; - - x2[0] = 0.; // -1.; - x2[1] = 1.; - x2[2] = 1.; - x2[3] = 0.; //-1.; - - y2[0] = 1.; - y2[1] = 1.; - y2[2] = 0.; // -1.; - y2[3] = 0.; //-1.; - - z2[0] = 0; - z2[1] = 0; - z2[2] = 0; - z2[3] = 0; - - xOvrlp[0] = x1[0]; - xOvrlp[1] = x1[1]; - xOvrlp[2] = x1[2]; - xOvrlp[3] = x1[3]; - - yOvrlp[0] = y1[0]; - yOvrlp[1] = y1[1]; - yOvrlp[2] = y1[2]; - yOvrlp[3] = y1[3]; - - zOvrlp[0] = z1[0]; - zOvrlp[1] = z1[1]; - zOvrlp[2] = z1[2]; - zOvrlp[3] = z1[3]; - - // register a tribol mesh for computing mortar gaps - int numNodesPerFace = 4; - int conn1[ numNodesPerFace ]; - int conn2[ numNodesPerFace ]; - - for (int i=0; icheckMortarGaps( &conn1[0], &conn2[0], tribol::SINGLE_MORTAR ); - - tribol::MeshManager& meshManager = tribol::MeshManager::getInstance(); - tribol::MeshData& nonmortarMesh = meshManager.at( 1 ); - - // compute the sum of the nodal gaps - RealT gap = 0.; - for (int i=0; igetXCoords( 0 ); + RealT* y1 = this->getYCoords( 0 ); + RealT* z1 = this->getZCoords( 0 ); + + RealT* x2 = this->getXCoords( 1 ); + RealT* y2 = this->getYCoords( 1 ); + RealT* z2 = this->getZCoords( 1 ); + + RealT* xOvrlp = this->getXOverlapCoords(); + RealT* yOvrlp = this->getYOverlapCoords(); + RealT* zOvrlp = this->getZOverlapCoords(); + + x1[0] = 0.; //-1.; + x1[1] = 0.; //-1.; + x1[2] = 1.; + x1[3] = 1.; + + y1[0] = 1.; + y1[1] = 0.; // -1.; + y1[2] = 0.; // -1.; + y1[3] = 1.; + + z1[0] = 0.1; + z1[1] = 0.1; + z1[2] = 0.1; + z1[3] = 0.1; + + x2[0] = 0.; // -1.; + x2[1] = 1.; + x2[2] = 1.; + x2[3] = 0.; //-1.; + + y2[0] = 1.; + y2[1] = 1.; + y2[2] = 0.; // -1.; + y2[3] = 0.; //-1.; + + z2[0] = 0; + z2[1] = 0; + z2[2] = 0; + z2[3] = 0; + + xOvrlp[0] = x1[0]; + xOvrlp[1] = x1[1]; + xOvrlp[2] = x1[2]; + xOvrlp[3] = x1[3]; + + yOvrlp[0] = y1[0]; + yOvrlp[1] = y1[1]; + yOvrlp[2] = y1[2]; + yOvrlp[3] = y1[3]; + + zOvrlp[0] = z1[0]; + zOvrlp[1] = z1[1]; + zOvrlp[2] = z1[2]; + zOvrlp[3] = z1[3]; + + // register a tribol mesh for computing mortar gaps + int numNodesPerFace = 4; + int conn1[numNodesPerFace]; + int conn2[numNodesPerFace]; + + for ( int i = 0; i < numNodesPerFace; ++i ) { + conn1[i] = i; + conn2[i] = i; + } + + this->checkMortarGaps( &conn1[0], &conn2[0], tribol::SINGLE_MORTAR ); + + tribol::MeshManager& meshManager = tribol::MeshManager::getInstance(); + tribol::MeshData& nonmortarMesh = meshManager.at( 1 ); + + // compute the sum of the nodal gaps + RealT gap = 0.; + for ( int i = 0; i < numNodesPerFace; ++i ) { + gap += nonmortarMesh.getNodalFields().m_node_gap[i]; + } + + // note the face-gap of 0.1 is hard coded based on the + // hard-coded face coordinates in this test + RealT gapDiff = std::abs( 0.1 + gap ); + + RealT tol = 1.e-8; + EXPECT_LE( gapDiff, tol ); } TEST_F( MortarGapTest, parallel_simple_aligned ) { - - RealT* x1 = this->getXCoords(0); - RealT* y1 = this->getYCoords(0); - RealT* z1 = this->getZCoords(0); - - RealT* x2 = this->getXCoords(1); - RealT* y2 = this->getYCoords(1); - RealT* z2 = this->getZCoords(1); - - RealT* xOvrlp = this->getXOverlapCoords(); - RealT* yOvrlp = this->getYOverlapCoords(); - RealT* zOvrlp = this->getZOverlapCoords(); - - x1[0] = -1.; - x1[1] = -1.; - x1[2] = 1.; - x1[3] = 1.; - - y1[0] = 1.; - y1[1] = -1.; - y1[2] = -1.; - y1[3] = 1.; - - z1[0] = 0.1; - z1[1] = 0.1; - z1[2] = 0.2; - z1[3] = 0.1; - - x2[0] = -1.; - x2[1] = 1.; - x2[2] = 1.; - x2[3] = -1.; - - y2[0] = 1.; - y2[1] = 1.; - y2[2] = -1.; - y2[3] = -1.; - - z2[0] = 0; - z2[1] = 0; - z2[2] = 0; - z2[3] = 0; - - xOvrlp[0] = x1[0]; - xOvrlp[1] = x1[1]; - xOvrlp[2] = x1[2]; - xOvrlp[3] = x1[3]; - - yOvrlp[0] = y1[0]; - yOvrlp[1] = y1[1]; - yOvrlp[2] = y1[2]; - yOvrlp[3] = y1[3]; - - zOvrlp[0] = z1[0]; - zOvrlp[1] = z1[1]; - zOvrlp[2] = z1[2]; - zOvrlp[3] = z1[3]; - - // register a tribol mesh for computing mortar gaps - int numNodesPerFace = 4; - int conn1[ numNodesPerFace ]; - int conn2[ numNodesPerFace ]; - - for (int i=0; icheckMortarGaps( &conn1[0], &conn2[0], tribol::ALIGNED_MORTAR ); - - tribol::MeshManager& meshManager = tribol::MeshManager::getInstance(); - tribol::MeshData& nonmortarMesh = meshManager.at( 1 ); - - // compute the sum of the nodal gaps - RealT gap = 0.; - RealT gapTest = 0; - for (int i=0; igetXCoords( 0 ); + RealT* y1 = this->getYCoords( 0 ); + RealT* z1 = this->getZCoords( 0 ); + + RealT* x2 = this->getXCoords( 1 ); + RealT* y2 = this->getYCoords( 1 ); + RealT* z2 = this->getZCoords( 1 ); + + RealT* xOvrlp = this->getXOverlapCoords(); + RealT* yOvrlp = this->getYOverlapCoords(); + RealT* zOvrlp = this->getZOverlapCoords(); + + x1[0] = -1.; + x1[1] = -1.; + x1[2] = 1.; + x1[3] = 1.; + + y1[0] = 1.; + y1[1] = -1.; + y1[2] = -1.; + y1[3] = 1.; + + z1[0] = 0.1; + z1[1] = 0.1; + z1[2] = 0.2; + z1[3] = 0.1; + + x2[0] = -1.; + x2[1] = 1.; + x2[2] = 1.; + x2[3] = -1.; + + y2[0] = 1.; + y2[1] = 1.; + y2[2] = -1.; + y2[3] = -1.; + + z2[0] = 0; + z2[1] = 0; + z2[2] = 0; + z2[3] = 0; + + xOvrlp[0] = x1[0]; + xOvrlp[1] = x1[1]; + xOvrlp[2] = x1[2]; + xOvrlp[3] = x1[3]; + + yOvrlp[0] = y1[0]; + yOvrlp[1] = y1[1]; + yOvrlp[2] = y1[2]; + yOvrlp[3] = y1[3]; + + zOvrlp[0] = z1[0]; + zOvrlp[1] = z1[1]; + zOvrlp[2] = z1[2]; + zOvrlp[3] = z1[3]; + + // register a tribol mesh for computing mortar gaps + int numNodesPerFace = 4; + int conn1[numNodesPerFace]; + int conn2[numNodesPerFace]; + + for ( int i = 0; i < numNodesPerFace; ++i ) { + conn1[i] = i; + conn2[i] = i; + } + + this->checkMortarGaps( &conn1[0], &conn2[0], tribol::ALIGNED_MORTAR ); + + tribol::MeshManager& meshManager = tribol::MeshManager::getInstance(); + tribol::MeshData& nonmortarMesh = meshManager.at( 1 ); + + // compute the sum of the nodal gaps + RealT gap = 0.; + RealT gapTest = 0; + for ( int i = 0; i < numNodesPerFace; ++i ) { + gap += nonmortarMesh.getNodalFields().m_node_gap[i]; + gapTest += z1[i] - z2[i]; + } + + // note the face-gap of 0.1 is hard coded based on the + // hard-coded face coordinates in this test + RealT gapDiff = std::abs( gapTest + gap ); + + RealT tol = 1.e-8; + EXPECT_LE( gapDiff, tol ); } -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); #ifdef TRIBOL_USE_UMPIRE umpire::ResourceManager::getInstance(); // initialize umpire's ResouceManager diff --git a/src/tests/tribol_mortar_jacobian.cpp b/src/tests/tribol_mortar_jacobian.cpp index f24166db..29c907db 100644 --- a/src/tests/tribol_mortar_jacobian.cpp +++ b/src/tests/tribol_mortar_jacobian.cpp @@ -24,7 +24,7 @@ #include "gtest/gtest.h" // c++ includes -#include // std::abs +#include // std::abs #include #include #include @@ -34,406 +34,349 @@ using RealT = tribol::RealT; /*! * Test fixture class with some setup necessary to test - * and compute the Jacobian matrix for an implicit + * and compute the Jacobian matrix for an implicit * mortar method with Lagrange multipliers */ -class MortarJacTest : public ::testing::Test -{ - -public: - int numNodes; - int numFaces; - int numNodesPerFace; - int numOverlapNodes; - int dim; - - RealT* getXCoords( ) - { - return x; - } - - RealT* getYCoords( ) - { - return y; - } - - RealT* getZCoords( ) - { - return z; - } - - void setupTribol( int * conn1, - int * conn2, - tribol::ContactMethod method ) - { - // Note, this assumes that numNodes is the total number of - // nodes encompassing the two meshes that will be registered - // with tribol, and that the conn1 and conn2 connectivity arrays - // reflect a global, contiguous index space - - // grab coordinate data - RealT * x = this->x; - RealT * y = this->y; - RealT * z = this->z; - - // register the mesh with tribol - int cellType = static_cast(tribol::UNDEFINED_ELEMENT); - switch (this->numNodesPerFace) - { - case 4: - { - cellType = (int)(tribol::LINEAR_QUAD); - break; - } - default: - { - SLIC_ERROR("setupTribol: number of nodes per face not equal to 4."); - } - } - - const int mortarMeshId = 0; - const int nonmortarMeshId = 1; - - // register mesh - tribol::registerMesh( mortarMeshId, 1, - this->numNodes, - conn1, cellType, - x, y, z, tribol::MemorySpace::Host ); - tribol::registerMesh( nonmortarMeshId, 1, - this->numNodes, - conn2, cellType, - x, y, z, tribol::MemorySpace::Host ); - - // register nodal forces. Note, I was getting a seg fault when - // registering the same pointer to a single set of force arrays - // for both calls to tribol::registerNodalResponse(). As a result, - // I created two sets of nodal force arrays with their own pointers - // to the data that are registered with tribol and there is no longer - // a seg fault. - RealT *fx1, *fy1, *fz1; - RealT *fx2, *fy2, *fz2; - - RealT forceX1[ this->numNodes ]; - RealT forceY1[ this->numNodes ]; - RealT forceZ1[ this->numNodes ]; - - RealT forceX2[ this->numNodes ]; - RealT forceY2[ this->numNodes ]; - RealT forceZ2[ this->numNodes ]; - - fx1 = forceX1; - fy1 = forceY1; - fz1 = forceZ1; - - fx2 = forceX2; - fy2 = forceY2; - fz2 = forceZ2; - - // initialize force arrays - for (int i=0; inumNodes; ++i) - { - fx1[i] = 0.; - fy1[i] = 0.; - fz1[i] = 0.; - - fx2[i] = 0.; - fy2[i] = 0.; - fz2[i] = 0.; - } - - tribol::registerNodalResponse( mortarMeshId, fx1, fy1, fz1 ); - tribol::registerNodalResponse( nonmortarMeshId, fx2, fy2, fz2 ); - - gaps = tribol::ArrayT(this->numNodes, this->numNodes); // length of total mesh to use global connectivity to index - pressures = tribol::ArrayT(this->numNodes, this->numNodes); // length of total mesh to use global connectivity to index - - // initialize gaps and pressures. Initialize all - // nonmortar pressures to 1.0 - for (int i=0; inumNodes; ++i) - { - gaps[i] = 0.; - pressures[i] = 1.; - } - - // register nodal gaps and pressures - tribol::registerMortarGaps( nonmortarMeshId, gaps.data() ); - tribol::registerMortarPressures( nonmortarMeshId, pressures.data() ); - - // register coupling scheme - const int csIndex = 0; - tribol::registerCouplingScheme( csIndex, - mortarMeshId, - nonmortarMeshId, - tribol::SURFACE_TO_SURFACE, - tribol::NO_CASE, - method, - tribol::FRICTIONLESS, - tribol::LAGRANGE_MULTIPLIER, - tribol::DEFAULT_BINNING_METHOD, - tribol::ExecutionMode::Sequential ); - - tribol::setLagrangeMultiplierOptions( csIndex, tribol::ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN, - tribol::SparseMode::MFEM_LINKED_LIST ); - - RealT dt = 1.0; - int tribol_update_err = tribol::update( 1, 1., dt ); - - EXPECT_EQ( tribol_update_err, 0 ); - - } - -protected: - - void SetUp() override - { - this->numNodes = 8; - this->numFaces = 2; - this->numNodesPerFace = 4; - this->numOverlapNodes = 4; - this->dim = 3; - - if (this->x == nullptr) - { - this->x = new RealT [this->numNodes]; - } - else - { - delete [] this->x; - this->x = new RealT [this->numNodes]; - } - - if (this->y == nullptr) - { - this->y = new RealT [this->numNodes]; - } - else - { - delete [] this->y; - this->y = new RealT [this->numNodes]; - } - - if (this->z == nullptr) - { - this->z = new RealT [this->numNodes]; +class MortarJacTest : public ::testing::Test { + public: + int numNodes; + int numFaces; + int numNodesPerFace; + int numOverlapNodes; + int dim; + + RealT* getXCoords() { return x; } + + RealT* getYCoords() { return y; } + + RealT* getZCoords() { return z; } + + void setupTribol( int* conn1, int* conn2, tribol::ContactMethod method ) + { + // Note, this assumes that numNodes is the total number of + // nodes encompassing the two meshes that will be registered + // with tribol, and that the conn1 and conn2 connectivity arrays + // reflect a global, contiguous index space + + // grab coordinate data + RealT* x = this->x; + RealT* y = this->y; + RealT* z = this->z; + + // register the mesh with tribol + int cellType = static_cast( tribol::UNDEFINED_ELEMENT ); + switch ( this->numNodesPerFace ) { + case 4: { + cellType = (int)( tribol::LINEAR_QUAD ); + break; } - else - { - delete [] this->z; - this->z = new RealT [this->numNodes]; + default: { + SLIC_ERROR( "setupTribol: number of nodes per face not equal to 4." ); } - - } - - void TearDown() override - { - if (this->x != nullptr) - { - delete [] this->x; - this->x = nullptr; - } - if (this->y != nullptr) - { - delete [] this->y; - this->y = nullptr; - } - if (this->z != nullptr) - { - delete [] this->z; - this->z = nullptr; - } - } - -protected: - - RealT* x {nullptr}; - RealT* y {nullptr}; - RealT* z {nullptr}; - tribol::ArrayT gaps; - tribol::ArrayT pressures; - + } + + const int mortarMeshId = 0; + const int nonmortarMeshId = 1; + + // register mesh + tribol::registerMesh( mortarMeshId, 1, this->numNodes, conn1, cellType, x, y, z, tribol::MemorySpace::Host ); + tribol::registerMesh( nonmortarMeshId, 1, this->numNodes, conn2, cellType, x, y, z, tribol::MemorySpace::Host ); + + // register nodal forces. Note, I was getting a seg fault when + // registering the same pointer to a single set of force arrays + // for both calls to tribol::registerNodalResponse(). As a result, + // I created two sets of nodal force arrays with their own pointers + // to the data that are registered with tribol and there is no longer + // a seg fault. + RealT *fx1, *fy1, *fz1; + RealT *fx2, *fy2, *fz2; + + RealT forceX1[this->numNodes]; + RealT forceY1[this->numNodes]; + RealT forceZ1[this->numNodes]; + + RealT forceX2[this->numNodes]; + RealT forceY2[this->numNodes]; + RealT forceZ2[this->numNodes]; + + fx1 = forceX1; + fy1 = forceY1; + fz1 = forceZ1; + + fx2 = forceX2; + fy2 = forceY2; + fz2 = forceZ2; + + // initialize force arrays + for ( int i = 0; i < this->numNodes; ++i ) { + fx1[i] = 0.; + fy1[i] = 0.; + fz1[i] = 0.; + + fx2[i] = 0.; + fy2[i] = 0.; + fz2[i] = 0.; + } + + tribol::registerNodalResponse( mortarMeshId, fx1, fy1, fz1 ); + tribol::registerNodalResponse( nonmortarMeshId, fx2, fy2, fz2 ); + + gaps = tribol::ArrayT( this->numNodes, + this->numNodes ); // length of total mesh to use global connectivity to index + pressures = tribol::ArrayT( this->numNodes, + this->numNodes ); // length of total mesh to use global connectivity to index + + // initialize gaps and pressures. Initialize all + // nonmortar pressures to 1.0 + for ( int i = 0; i < this->numNodes; ++i ) { + gaps[i] = 0.; + pressures[i] = 1.; + } + + // register nodal gaps and pressures + tribol::registerMortarGaps( nonmortarMeshId, gaps.data() ); + tribol::registerMortarPressures( nonmortarMeshId, pressures.data() ); + + // register coupling scheme + const int csIndex = 0; + tribol::registerCouplingScheme( csIndex, mortarMeshId, nonmortarMeshId, tribol::SURFACE_TO_SURFACE, tribol::NO_CASE, + method, tribol::FRICTIONLESS, tribol::LAGRANGE_MULTIPLIER, + tribol::DEFAULT_BINNING_METHOD, tribol::ExecutionMode::Sequential ); + + tribol::setLagrangeMultiplierOptions( csIndex, tribol::ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN, + tribol::SparseMode::MFEM_LINKED_LIST ); + + RealT dt = 1.0; + int tribol_update_err = tribol::update( 1, 1., dt ); + + EXPECT_EQ( tribol_update_err, 0 ); + } + + protected: + void SetUp() override + { + this->numNodes = 8; + this->numFaces = 2; + this->numNodesPerFace = 4; + this->numOverlapNodes = 4; + this->dim = 3; + + if ( this->x == nullptr ) { + this->x = new RealT[this->numNodes]; + } else { + delete[] this->x; + this->x = new RealT[this->numNodes]; + } + + if ( this->y == nullptr ) { + this->y = new RealT[this->numNodes]; + } else { + delete[] this->y; + this->y = new RealT[this->numNodes]; + } + + if ( this->z == nullptr ) { + this->z = new RealT[this->numNodes]; + } else { + delete[] this->z; + this->z = new RealT[this->numNodes]; + } + } + + void TearDown() override + { + if ( this->x != nullptr ) { + delete[] this->x; + this->x = nullptr; + } + if ( this->y != nullptr ) { + delete[] this->y; + this->y = nullptr; + } + if ( this->z != nullptr ) { + delete[] this->z; + this->z = nullptr; + } + } + + protected: + RealT* x{ nullptr }; + RealT* y{ nullptr }; + RealT* z{ nullptr }; + tribol::ArrayT gaps; + tribol::ArrayT pressures; }; TEST_F( MortarJacTest, jac_input_test ) { - - RealT* x = this->getXCoords(); - RealT* y = this->getYCoords(); - RealT* z = this->getZCoords(); - - x[0] = -1.; - x[1] = -1.; - x[2] = 1.; - x[3] = 1.; - - y[0] = 1.; - y[1] = -1.; - y[2] = -1.; - y[3] = 1.; - - z[0] = 0.1; - z[1] = 0.1; - z[2] = 0.1; - z[3] = 0.1; - - x[4] = -1.; - x[5] = 1.; - x[6] = 1.; - x[7] = -1.; - - y[4] = 1.; - y[5] = 1.; - y[6] = -1.; - y[7] = -1.; - - z[4] = 0.; - z[5] = 0.; - z[6] = 0.; - z[7] = 0.; - - // register a tribol mesh for computing mortar gaps - int numNodesPerFace = 4; - int conn1[ numNodesPerFace ]; - int conn2[ numNodesPerFace ]; - - for (int i=0; isetupTribol( &conn1[0], &conn2[0], - tribol::ALIGNED_MORTAR ); - - // check Jacobian sparse matrix - mfem::SparseMatrix * jac { nullptr }; - int sparseMatErr = tribol::getJacobianSparseMatrix( &jac, 0 ); - - EXPECT_EQ( sparseMatErr, 0 ); - - // add each diagonal entry to 1 (i.e. identity matrix) to test adding routine - for (int i=0; i<2*numNodesPerFace; ++i) - { - jac->Add(i, i, 1.); - } - - // get the number of rows and compare to expected to make sure - // sparse matrix initialization is correct - int my_num_rows = 3 * 2 * numNodesPerFace + 2 * numNodesPerFace; - int numRows = jac->NumRows(); - EXPECT_EQ( numRows, my_num_rows ); - - // get the number of columns and compare to expected - int numCols = jac->NumCols(); - EXPECT_EQ( numCols, my_num_rows ); - - tribol::finalize(); - - // delete the jacobian matrix - delete jac; - - + RealT* x = this->getXCoords(); + RealT* y = this->getYCoords(); + RealT* z = this->getZCoords(); + + x[0] = -1.; + x[1] = -1.; + x[2] = 1.; + x[3] = 1.; + + y[0] = 1.; + y[1] = -1.; + y[2] = -1.; + y[3] = 1.; + + z[0] = 0.1; + z[1] = 0.1; + z[2] = 0.1; + z[3] = 0.1; + + x[4] = -1.; + x[5] = 1.; + x[6] = 1.; + x[7] = -1.; + + y[4] = 1.; + y[5] = 1.; + y[6] = -1.; + y[7] = -1.; + + z[4] = 0.; + z[5] = 0.; + z[6] = 0.; + z[7] = 0.; + + // register a tribol mesh for computing mortar gaps + int numNodesPerFace = 4; + int conn1[numNodesPerFace]; + int conn2[numNodesPerFace]; + + for ( int i = 0; i < numNodesPerFace; ++i ) { + conn1[i] = i; + conn2[i] = numNodesPerFace + i; + } + + this->setupTribol( &conn1[0], &conn2[0], tribol::ALIGNED_MORTAR ); + + // check Jacobian sparse matrix + mfem::SparseMatrix* jac{ nullptr }; + int sparseMatErr = tribol::getJacobianSparseMatrix( &jac, 0 ); + + EXPECT_EQ( sparseMatErr, 0 ); + + // add each diagonal entry to 1 (i.e. identity matrix) to test adding routine + for ( int i = 0; i < 2 * numNodesPerFace; ++i ) { + jac->Add( i, i, 1. ); + } + + // get the number of rows and compare to expected to make sure + // sparse matrix initialization is correct + int my_num_rows = 3 * 2 * numNodesPerFace + 2 * numNodesPerFace; + int numRows = jac->NumRows(); + EXPECT_EQ( numRows, my_num_rows ); + + // get the number of columns and compare to expected + int numCols = jac->NumCols(); + EXPECT_EQ( numCols, my_num_rows ); + + tribol::finalize(); + + // delete the jacobian matrix + delete jac; } TEST_F( MortarJacTest, update_jac_test ) { - - RealT* x = this->getXCoords(); - RealT* y = this->getYCoords(); - RealT* z = this->getZCoords(); - - x[0] = -1.; - x[1] = -1.; - x[2] = 1.; - x[3] = 1.; - - y[0] = 1.; - y[1] = -1.; - y[2] = -1.; - y[3] = 1.; - - z[0] = 0.1; - z[1] = 0.1; - z[2] = 0.1; - z[3] = 0.1; - - x[4] = -1.; - x[5] = 1.; - x[6] = 1.; - x[7] = -1.; - - y[4] = 1.; - y[5] = 1.; - y[6] = -1.; - y[7] = -1.; - - z[4] = 0.; - z[5] = 0.; - z[6] = 0.; - z[7] = 0.; - - // register a tribol mesh for computing mortar gaps - int numNodesPerFace = 4; - int conn1[ numNodesPerFace ]; - int conn2[ numNodesPerFace ]; - - for (int i=0; isetupTribol( &conn1[0], &conn2[0], - tribol::ALIGNED_MORTAR ); - - // check Jacobian sparse matrix - mfem::SparseMatrix * jac { nullptr }; - int sparseMatErr = tribol::getJacobianSparseMatrix( &jac, 0 ); - - EXPECT_EQ( sparseMatErr, 0 ); - - // make sure sparse matrix is finalized to convert to CSR format - jac->Finalize(); - - int numRows = jac->NumRows(); - int numCols = numRows; - - // convert sparse matrix to dense matrix for output - mfem::DenseMatrix dJac; - jac->ToDenseMatrix(dJac); - - std::ofstream matrix; - matrix.setf(std::ios::scientific); - matrix.precision(2); - std::ostringstream suffix_matrix; - suffix_matrix << "test2" << ".txt"; - matrix.open("matrix_" + suffix_matrix.str()); - - for (int i=0; igetXCoords(); + RealT* y = this->getYCoords(); + RealT* z = this->getZCoords(); + + x[0] = -1.; + x[1] = -1.; + x[2] = 1.; + x[3] = 1.; + + y[0] = 1.; + y[1] = -1.; + y[2] = -1.; + y[3] = 1.; + + z[0] = 0.1; + z[1] = 0.1; + z[2] = 0.1; + z[3] = 0.1; + + x[4] = -1.; + x[5] = 1.; + x[6] = 1.; + x[7] = -1.; + + y[4] = 1.; + y[5] = 1.; + y[6] = -1.; + y[7] = -1.; + + z[4] = 0.; + z[5] = 0.; + z[6] = 0.; + z[7] = 0.; + + // register a tribol mesh for computing mortar gaps + int numNodesPerFace = 4; + int conn1[numNodesPerFace]; + int conn2[numNodesPerFace]; + + for ( int i = 0; i < numNodesPerFace; ++i ) { + conn1[i] = i; + conn2[i] = numNodesPerFace + i; + } + + this->setupTribol( &conn1[0], &conn2[0], tribol::ALIGNED_MORTAR ); + + // check Jacobian sparse matrix + mfem::SparseMatrix* jac{ nullptr }; + int sparseMatErr = tribol::getJacobianSparseMatrix( &jac, 0 ); + + EXPECT_EQ( sparseMatErr, 0 ); + + // make sure sparse matrix is finalized to convert to CSR format + jac->Finalize(); + + int numRows = jac->NumRows(); + int numCols = numRows; + + // convert sparse matrix to dense matrix for output + mfem::DenseMatrix dJac; + jac->ToDenseMatrix( dJac ); + + std::ofstream matrix; + matrix.setf( std::ios::scientific ); + matrix.precision( 2 ); + std::ostringstream suffix_matrix; + suffix_matrix << "test2" + << ".txt"; + matrix.open( "matrix_" + suffix_matrix.str() ); + + for ( int i = 0; i < numRows; ++i ) { + for ( int j = 0; j < numCols; ++j ) { + RealT val = dJac( i, j ); + matrix << val << " "; + } + matrix << "\n"; + } + + matrix.close(); + + tribol::finalize(); + + // delete the jacobian matrix + delete jac; } -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); - axom::slic::SimpleLogger logger; // create & initialize logger, + axom::slic::SimpleLogger logger; // create & initialize logger, result = RUN_ALL_TESTS(); diff --git a/src/tests/tribol_mortar_lm_patch_test.cpp b/src/tests/tribol_mortar_lm_patch_test.cpp index a5c7dbb4..a73a63c6 100644 --- a/src/tests/tribol_mortar_lm_patch_test.cpp +++ b/src/tests/tribol_mortar_lm_patch_test.cpp @@ -29,7 +29,7 @@ #include "gtest/gtest.h" // c++ includes -#include // std::abs, std::cos, std::sin +#include // std::abs, std::cos, std::sin #include #include #include @@ -39,753 +39,652 @@ using RealT = tribol::RealT; /*! * Test fixture class with some setup necessary to test - * the Lagrange multiplier pressure field solution for - * Tribol's simplified mortar method on a single element + * the Lagrange multiplier pressure field solution for + * Tribol's simplified mortar method on a single element * on single element contact problem */ -class MortarLMPatchTest : public ::testing::Test -{ - -public: - tribol::TestMesh m_mesh; - - void computeContactSolution( int nMortarElemsX, int nMortarElemsY, int nMortarElemsZ, - int nNonmortarElemsX, int nNonmortarElemsY, int nNonmortarElemsZ, - RealT xMin1, RealT yMin1, RealT zMin1, - RealT xMax1, RealT yMax1, RealT zMax1, - RealT xMin2, RealT yMin2, RealT zMin2, - RealT xMax2, RealT yMax2, RealT zMax2, - RealT thetaM, RealT thetaS, - tribol::ContactMethod method, - std::string test_name, - bool cntct, bool writeOutput, bool debug, bool vis, - mfem::Vector &sol, RealT &pressure_rel_error ); - -protected: - - void SetUp() override - { - } - - void TearDown() override - { - // call clear() on mesh object to be safe - this->m_mesh.clear(); - } - -protected: - +class MortarLMPatchTest : public ::testing::Test { + public: + tribol::TestMesh m_mesh; + + void computeContactSolution( int nMortarElemsX, int nMortarElemsY, int nMortarElemsZ, int nNonmortarElemsX, + int nNonmortarElemsY, int nNonmortarElemsZ, RealT xMin1, RealT yMin1, RealT zMin1, + RealT xMax1, RealT yMax1, RealT zMax1, RealT xMin2, RealT yMin2, RealT zMin2, + RealT xMax2, RealT yMax2, RealT zMax2, RealT thetaM, RealT thetaS, + tribol::ContactMethod method, std::string test_name, bool cntct, bool writeOutput, + bool debug, bool vis, mfem::Vector& sol, RealT& pressure_rel_error ); + + protected: + void SetUp() override {} + + void TearDown() override + { + // call clear() on mesh object to be safe + this->m_mesh.clear(); + } + + protected: }; void MortarLMPatchTest::computeContactSolution( int nMortarElemsX, int nMortarElemsY, int nMortarElemsZ, int nNonmortarElemsX, int nNonmortarElemsY, int nNonmortarElemsZ, - RealT xMin1, RealT yMin1, RealT zMin1, - RealT xMax1, RealT yMax1, RealT zMax1, - RealT xMin2, RealT yMin2, RealT zMin2, - RealT xMax2, RealT yMax2, RealT zMax2, - RealT thetaM, RealT thetaS, - tribol::ContactMethod method, - std::string test_name, - bool cntct, bool writeOutput, bool TRIBOL_UNUSED_PARAM(debug), bool vis, - mfem::Vector &sol, RealT &pressure_rel_error ) + RealT xMin1, RealT yMin1, RealT zMin1, RealT xMax1, RealT yMax1, + RealT zMax1, RealT xMin2, RealT yMin2, RealT zMin2, RealT xMax2, + RealT yMax2, RealT zMax2, RealT thetaM, RealT thetaS, + tribol::ContactMethod method, std::string test_name, bool cntct, + bool writeOutput, bool TRIBOL_UNUSED_PARAM( debug ), bool vis, + mfem::Vector& sol, RealT& pressure_rel_error ) { - bool inHomogeneous = false; - RealT inHomogeneousVal = 0.; - if (!cntct) - { - inHomogeneous = true; - inHomogeneousVal = 0.05; // hard coded for this example - } - - this->m_mesh.setupContactMeshHex( nMortarElemsX, nMortarElemsY, nMortarElemsZ, - xMin1, yMin1, zMin1, xMax1, yMax1, zMax1, - nNonmortarElemsX, nNonmortarElemsY, nNonmortarElemsZ, - xMin2, yMin2, zMin2, xMax2, yMax2, zMax2, - thetaM, thetaS ); - - // setup mortar boundary conditions - this->m_mesh.setupPatchTestDirichletBCs( this->m_mesh.mortarMeshId, nMortarElemsX, nMortarElemsY, nMortarElemsZ, - 0, inHomogeneous, -inHomogeneousVal ); - - // setup nonmortar boundary conditions - this->m_mesh.setupPatchTestDirichletBCs( this->m_mesh.nonmortarMeshId, nNonmortarElemsX, nNonmortarElemsY, nNonmortarElemsZ, - this->m_mesh.numMortarNodes, inHomogeneous, inHomogeneousVal ); - - // setup DUMMY MORTAR pressure dof array. Consider getting rid of - // mortar pressure dofs based on new way stiffness data is handled - // after passed back from Tribol. ALWAYS call this function with - // these arguments for the mortar block - this->m_mesh.setupPatchTestPressureDofs( this->m_mesh.mortarMeshId, nMortarElemsX, nMortarElemsY, nMortarElemsZ, - 0, false ); - - // setup NONMORTAR pressure dofs - this->m_mesh.setupPatchTestPressureDofs( this->m_mesh.nonmortarMeshId, nNonmortarElemsX, nNonmortarElemsY, nNonmortarElemsZ, - this->m_mesh.numMortarNodes, true ); - - // specify if contact is on - //bool matrixDebug = debug; // this controls whether the matrix printed is without (true) or with BCs applied - bool output = writeOutput; - bool contact = cntct; - - // register mesh, fields and coupling scheme with Tribol and call update. Note, the mfem - // mesh is not needed here. The mfem mesh is simply for the equilibrium calculations - tribol::TestControlParameters parameters; - - int test_mesh_update_err = - this->m_mesh.tribolSetupAndUpdate( method, tribol::LAGRANGE_MULTIPLIER, - tribol::FRICTIONLESS, tribol::NO_CASE, vis, parameters ); - - EXPECT_EQ( test_mesh_update_err, 0 ); - - // setup mfem mesh - this->m_mesh.setupMfemMesh( ); - - // setup a temporary stacked array of nodal coordinates for use in the instantiation - // of a reference configuration grid function. The nodal coordinates are stacked x, then - // y, then z. Also setup a local interleaved array of incremental nodal displacements. - RealT xyz[ this->m_mesh.dim * this->m_mesh.numTotalNodes ]; - RealT xyz_inc[ this->m_mesh.dim * this->m_mesh.numTotalNodes ]; - for (int i=0; im_mesh.numTotalNodes; ++i) - { - xyz[ i ] = this->m_mesh.x[i]; - xyz[ this->m_mesh.numTotalNodes + i ] = this->m_mesh.y[i]; - xyz[ 2*this->m_mesh.numTotalNodes + i ] = this->m_mesh.z[i]; - - xyz_inc[ this->m_mesh.dim * i ] = 0.; - xyz_inc[ this->m_mesh.dim * i + 1 ] = 0.; - xyz_inc[ this->m_mesh.dim * i + 2 ] = 0.; - } - - // define the FE collection and finite element space - mfem::FiniteElementSpace * fe_space { nullptr }; - int order = 1; - mfem::H1_FECollection fe_coll( order, this->m_mesh.dim ); - fe_space = new mfem::FiniteElementSpace( this->m_mesh.mfem_mesh, &fe_coll, this->m_mesh.dim ); - - // get Jacobian sparse matrix from Tribol - mfem::SparseMatrix * tribolJac { nullptr }; - int sparseMatErr = tribol::getJacobianSparseMatrix( &tribolJac, 0 ); - - EXPECT_EQ( sparseMatErr, 0 ); - - // add hex8 equilibrium contributions for linear elasticity - // define the lambda and mu constant coefficients - RealT lambda_val, mu_val, nu_val, youngs_val; - nu_val = 0.33; - youngs_val = 3.E7; - lambda_val = (youngs_val * nu_val) / ((1. + nu_val) * (1. - 2. * nu_val)); - mu_val = youngs_val / (2.*(1.+nu_val)); - mfem::ConstantCoefficient lambda(lambda_val); - mfem::ConstantCoefficient mu(mu_val); - - // instantiate elasticity integrator - mfem::ElasticityIntegrator elastInteg( lambda, mu ); - - // compute equilibrium contributions and sum into Jacobian - m_mesh.computeEquilibriumJacobian( tribolJac, &elastInteg, fe_space ); - - // make sure sparse matrix is finalized to convert to CSR format - tribolJac->Finalize(); - - // convert sparse matrix to dense matrix for output - mfem::DenseMatrix dTribolJac; - tribolJac->ToDenseMatrix(dTribolJac); - - // instantiate mfem vector for rhs vector. The length of this vector is the - // (space dimension) x (total number of mesh nodes) + (number of nonmortar nodes in contact), - // where the last addition is for the pressure lagrange multiplier field - int rhs_size = this->m_mesh.dim * this->m_mesh.numTotalNodes + - this->m_mesh.numNonmortarSurfaceNodes; - RealT b[ rhs_size ]; - - // initialize b vector - for (int i=0; im_mesh.tribolMatrixToSystemMatrix( &dTribolJac, &jac ); - - // note: this does not populate the right hand side with any contact weak form - // residual terms. That is, the initial guess for pressure is zero, and therefore - // there are no contact contributions to the equilibrium residual. Here we only - // assemble gap contributions. Recall at this point that tribol::update() is - // called again with the updated contact solution, so doing so would actually - // mess with the RHS in a negative way - if (contact) - { - this->m_mesh.getGapEvals( &b[0] ); - } - - // zero out all Dirichlet BC components for each block - this->m_mesh.enforceDirichletBCs( &jac, &rhs, contact ); - - jac.Finalize(); - mfem::DenseMatrix dJac; - jac.ToDenseMatrix(dJac); - int rank = dJac.Rank(1.e-15); - - SLIC_DEBUG( "Matrix rank: " << rank ); - - SLIC_ERROR_IF(rankm_mesh.numNonmortarFaces * this->m_mesh.numNodesPerFace; - for (int i=0; im_mesh.dim * (this->m_mesh.numMortarNodes + this->m_mesh.numNonmortarNodes); - int nonmortarOffset = this->m_mesh.numMortarNodes; - int id = this->m_mesh.faceConn2[i]; - this->m_mesh.pressures[id] = sol_data[ offset + id - nonmortarOffset ]; - } - - // zero out mortar and nonmortar nodal force contributions for equilibrium - // residual evaluation. Note, don't update the nodal coordinates! We want - // to evaluate the equilibrium residual with the current pressure - // solution and the contact overlaps (i.e. mortar weights) used to - // solve for those pressures. The updated nodal coordinates should be - // used for a gap only evaluation, which is currently not done. - for (int i=0; im_mesh.numTotalNodes; ++i) - { - this->m_mesh.fx1[i] = 0.; - this->m_mesh.fy1[i] = 0.; - this->m_mesh.fz1[i] = 0.; - this->m_mesh.fx2[i] = 0.; - this->m_mesh.fy2[i] = 0.; - this->m_mesh.fz2[i] = 0.; - } - - // call tribol update() again for residual only evaluation - // TODO check to make sure this call here after refactor works, SRW - tribol::setLagrangeMultiplierOptions( 0, tribol::ImplicitEvalMode::MORTAR_RESIDUAL, - tribol::SparseMode::MFEM_LINKED_LIST ); - - RealT dt = 1.0; - int tribol_update_err = tribol::update( 1, 1., dt ); - - EXPECT_EQ( tribol_update_err, 0 ); - - // sum the nodal force contributions on the nonmortar side - for (int i=0; im_mesh.numNonmortarSurfaceNodes; ++i) - { - int offset = this->m_mesh.numMortarNodes; - // exploit offset and contiguous node numbering in the indexing here. - nonmortarForceSum += this->m_mesh.fz2[ offset + i ]; - } - pressure_rel_error = nonmortarForceSum; - SLIC_DEBUG("NODAL FORCE SUM (NONMORTAR, TRIBOL RESIDUALS): " << nonmortarForceSum); - } - - // update nodal coordinates in separate stacked array. Keep original - // mesh array as that of the reference configuration - for (int i=0; im_mesh.numTotalNodes; ++i) - { - for (int j=0; jm_mesh.dim; ++j) - { - xyz[ this->m_mesh.numTotalNodes * j + i ] - += sol_data[ this->m_mesh.dim * i + j ]; - xyz_inc[ this->m_mesh.numTotalNodes * j + i ] - = sol_data[ this->m_mesh.dim * i + j ]; - } - } - - // compute stress update - mfem::GridFunction u( fe_space, &xyz_inc[0] ); - const int tdim = this->m_mesh.dim*(this->m_mesh.dim+1)/2; - mfem::FiniteElementSpace flux_fespace( this->m_mesh.mfem_mesh, &fe_coll, tdim ); - mfem::GridFunction stress( &flux_fespace ); - stress = 0.; - mfem::BilinearFormIntegrator *integ = &elastInteg; - - u.ComputeFlux( *integ, stress ); - - // instantiate stress33 relative error grid function - mfem::GridFunction stress33_rel_error( stress ); - - // compute pressure difference from computed contact pressure solution - // and stress solution from mfem - if (contact) - { - mfem::Vector node_vals; - stress.GetNodalValues( node_vals, 3 ); - RealT *data = stress.GetData(); - pressure_rel_error - -= data[2*this->m_mesh.numTotalNodes + this->m_mesh.faceConn2[0]]; - pressure_rel_error - /= data[2*this->m_mesh.numTotalNodes + this->m_mesh.faceConn2[0]]; - pressure_rel_error = std::abs(pressure_rel_error); - - RealT * error_data = stress33_rel_error.GetData(); - for (int i=(2*this->m_mesh.numTotalNodes); i<(2*this->m_mesh.numTotalNodes+this->m_mesh.numTotalNodes); ++i) - { - error_data[i] -= nonmortarForceSum; - error_data[i] /= nonmortarForceSum; - error_data[i] = std::abs(error_data[i]); - } - } - - if (vis) - { - // mesh output - std::ostringstream mesh_ref_name, mesh_cur_name; - mesh_ref_name << "mesh_ref_" << test_name << "." << std::setfill('0') << std::setw(6) << ".vtk"; - mesh_cur_name << "mesh_cur_" << test_name << "." << std::setfill('0') << std::setw(6) << ".vtk"; - - std::ofstream mesh_ref_ofs(mesh_ref_name.str().c_str()); - mesh_ref_ofs.precision(8); - - // print reference configuration mesh - this->m_mesh.mfem_mesh->PrintVTK( mesh_ref_ofs, 0, 0 ); - - // set the current configuration vector and mesh node grid function for output - mfem::Vector x_cur( &xyz[0], this->m_mesh.dim*this->m_mesh.numTotalNodes ); - this->m_mesh.mfem_mesh->SetNodes( x_cur ); - - // print current mesh - std::ofstream mesh_cur_ofs(mesh_cur_name.str().c_str()); - mesh_cur_ofs.precision(8); - this->m_mesh.mfem_mesh->PrintVTK( mesh_cur_ofs, 0, 0 ); - - // save stress in current mesh .vtk. Note, do this for contact and no contact - stress.SaveVTK( mesh_cur_ofs, "stress", 0 ); - stress33_rel_error.SaveVTK( mesh_cur_ofs, "stress_rel_error", 0 ); - - } - - // DEBUG: print matrix and vector output. Leave this code in here - // and guard with boolean - if (output) - { - std::ofstream matrix; - matrix.setf(std::ios::scientific); - matrix.precision(0); - std::ostringstream suffix_matrix; - suffix_matrix << "jacobian_" << test_name << ".txt"; - matrix.open("matrix_" + suffix_matrix.str()); - - std::ofstream sol_vec; - sol_vec.setf(std::ios::scientific); - sol_vec.precision(2); - std::ostringstream suffix_sol; - suffix_sol << "vector_" << test_name << ".txt"; - sol_vec.open("sol_" + suffix_sol.str()); - - std::ofstream rhs_vec; - rhs_vec.setf(std::ios::scientific); - rhs_vec.precision(2); - std::ostringstream suffix_rhs; - suffix_rhs << "vector_" << test_name << ".txt"; - rhs_vec.open("rhs_" + suffix_rhs.str()); - - for (int i=0; im_mesh.setupContactMeshHex( nMortarElemsX, nMortarElemsY, nMortarElemsZ, xMin1, yMin1, zMin1, xMax1, yMax1, + zMax1, nNonmortarElemsX, nNonmortarElemsY, nNonmortarElemsZ, xMin2, yMin2, zMin2, + xMax2, yMax2, zMax2, thetaM, thetaS ); + + // setup mortar boundary conditions + this->m_mesh.setupPatchTestDirichletBCs( this->m_mesh.mortarMeshId, nMortarElemsX, nMortarElemsY, nMortarElemsZ, 0, + inHomogeneous, -inHomogeneousVal ); + + // setup nonmortar boundary conditions + this->m_mesh.setupPatchTestDirichletBCs( this->m_mesh.nonmortarMeshId, nNonmortarElemsX, nNonmortarElemsY, + nNonmortarElemsZ, this->m_mesh.numMortarNodes, inHomogeneous, + inHomogeneousVal ); + + // setup DUMMY MORTAR pressure dof array. Consider getting rid of + // mortar pressure dofs based on new way stiffness data is handled + // after passed back from Tribol. ALWAYS call this function with + // these arguments for the mortar block + this->m_mesh.setupPatchTestPressureDofs( this->m_mesh.mortarMeshId, nMortarElemsX, nMortarElemsY, nMortarElemsZ, 0, + false ); + + // setup NONMORTAR pressure dofs + this->m_mesh.setupPatchTestPressureDofs( this->m_mesh.nonmortarMeshId, nNonmortarElemsX, nNonmortarElemsY, + nNonmortarElemsZ, this->m_mesh.numMortarNodes, true ); + + // specify if contact is on + // bool matrixDebug = debug; // this controls whether the matrix printed is without (true) or with BCs applied + bool output = writeOutput; + bool contact = cntct; + + // register mesh, fields and coupling scheme with Tribol and call update. Note, the mfem + // mesh is not needed here. The mfem mesh is simply for the equilibrium calculations + tribol::TestControlParameters parameters; + + int test_mesh_update_err = this->m_mesh.tribolSetupAndUpdate( + method, tribol::LAGRANGE_MULTIPLIER, tribol::FRICTIONLESS, tribol::NO_CASE, vis, parameters ); + + EXPECT_EQ( test_mesh_update_err, 0 ); + + // setup mfem mesh + this->m_mesh.setupMfemMesh(); + + // setup a temporary stacked array of nodal coordinates for use in the instantiation + // of a reference configuration grid function. The nodal coordinates are stacked x, then + // y, then z. Also setup a local interleaved array of incremental nodal displacements. + RealT xyz[this->m_mesh.dim * this->m_mesh.numTotalNodes]; + RealT xyz_inc[this->m_mesh.dim * this->m_mesh.numTotalNodes]; + for ( int i = 0; i < this->m_mesh.numTotalNodes; ++i ) { + xyz[i] = this->m_mesh.x[i]; + xyz[this->m_mesh.numTotalNodes + i] = this->m_mesh.y[i]; + xyz[2 * this->m_mesh.numTotalNodes + i] = this->m_mesh.z[i]; + + xyz_inc[this->m_mesh.dim * i] = 0.; + xyz_inc[this->m_mesh.dim * i + 1] = 0.; + xyz_inc[this->m_mesh.dim * i + 2] = 0.; + } + + // define the FE collection and finite element space + mfem::FiniteElementSpace* fe_space{ nullptr }; + int order = 1; + mfem::H1_FECollection fe_coll( order, this->m_mesh.dim ); + fe_space = new mfem::FiniteElementSpace( this->m_mesh.mfem_mesh, &fe_coll, this->m_mesh.dim ); + + // get Jacobian sparse matrix from Tribol + mfem::SparseMatrix* tribolJac{ nullptr }; + int sparseMatErr = tribol::getJacobianSparseMatrix( &tribolJac, 0 ); + + EXPECT_EQ( sparseMatErr, 0 ); + + // add hex8 equilibrium contributions for linear elasticity + // define the lambda and mu constant coefficients + RealT lambda_val, mu_val, nu_val, youngs_val; + nu_val = 0.33; + youngs_val = 3.E7; + lambda_val = ( youngs_val * nu_val ) / ( ( 1. + nu_val ) * ( 1. - 2. * nu_val ) ); + mu_val = youngs_val / ( 2. * ( 1. + nu_val ) ); + mfem::ConstantCoefficient lambda( lambda_val ); + mfem::ConstantCoefficient mu( mu_val ); + + // instantiate elasticity integrator + mfem::ElasticityIntegrator elastInteg( lambda, mu ); + + // compute equilibrium contributions and sum into Jacobian + m_mesh.computeEquilibriumJacobian( tribolJac, &elastInteg, fe_space ); + + // make sure sparse matrix is finalized to convert to CSR format + tribolJac->Finalize(); + + // convert sparse matrix to dense matrix for output + mfem::DenseMatrix dTribolJac; + tribolJac->ToDenseMatrix( dTribolJac ); + + // instantiate mfem vector for rhs vector. The length of this vector is the + // (space dimension) x (total number of mesh nodes) + (number of nonmortar nodes in contact), + // where the last addition is for the pressure lagrange multiplier field + int rhs_size = this->m_mesh.dim * this->m_mesh.numTotalNodes + this->m_mesh.numNonmortarSurfaceNodes; + RealT b[rhs_size]; + + // initialize b vector + for ( int i = 0; i < rhs_size; ++i ) { + b[i] = 0.; + } + + // instantiate mfem vector for right hand side + mfem::Vector rhs( &b[0], rhs_size ); + + //////////////////////////////////////////////////// + // // + // Condense the Tribol Jacobian into a system // + // that is (dim*numTotalNodes + numPressureDof). // + // Place contributions into another sparse matrix // + // sized accordingly. // + // // + //////////////////////////////////////////////////// + + int solveSize = rhs_size; + mfem::SparseMatrix jac( solveSize ); + + int numRows = jac.NumRows(); + + this->m_mesh.tribolMatrixToSystemMatrix( &dTribolJac, &jac ); + + // note: this does not populate the right hand side with any contact weak form + // residual terms. That is, the initial guess for pressure is zero, and therefore + // there are no contact contributions to the equilibrium residual. Here we only + // assemble gap contributions. Recall at this point that tribol::update() is + // called again with the updated contact solution, so doing so would actually + // mess with the RHS in a negative way + if ( contact ) { + this->m_mesh.getGapEvals( &b[0] ); + } + + // zero out all Dirichlet BC components for each block + this->m_mesh.enforceDirichletBCs( &jac, &rhs, contact ); + + jac.Finalize(); + mfem::DenseMatrix dJac; + jac.ToDenseMatrix( dJac ); + int rank = dJac.Rank( 1.e-15 ); + + SLIC_DEBUG( "Matrix rank: " << rank ); + + SLIC_ERROR_IF( rank < numRows, "Jacobian rank (" << rank << ") less than row dimension (" << numRows << ")" ); + + // instantiate mfem dense matrix inverse object and + // solution vector + mfem::DenseMatrixInverse invJ( dJac ); + + // Solve the system + invJ.Mult( rhs, sol ); + + // get solution data + RealT* sol_data = sol.GetData(); + + // update the tribol nodal pressure array. This traverses + // the connectivity array, which will populate nonmortar pressures + // more than once for nonmortar nodes shared between multiple faces, + // but this is easy for now (SRW). To do this we need a mapping + // from local nonmortar pressure dofs (in the solution vector) to + // the global mesh connectivity ids. I don't create this mapping, + // instead I exploit the fact that the nonmortar pressure nodes are + // ordered consecutively with an offset that is the number of + // mortar nodes in the mortar block + RealT nonmortarForceSum = 0.; + if ( contact ) { + int connSize = this->m_mesh.numNonmortarFaces * this->m_mesh.numNodesPerFace; + for ( int i = 0; i < connSize; ++i ) { + int offset = this->m_mesh.dim * ( this->m_mesh.numMortarNodes + this->m_mesh.numNonmortarNodes ); + int nonmortarOffset = this->m_mesh.numMortarNodes; + int id = this->m_mesh.faceConn2[i]; + this->m_mesh.pressures[id] = sol_data[offset + id - nonmortarOffset]; + } + + // zero out mortar and nonmortar nodal force contributions for equilibrium + // residual evaluation. Note, don't update the nodal coordinates! We want + // to evaluate the equilibrium residual with the current pressure + // solution and the contact overlaps (i.e. mortar weights) used to + // solve for those pressures. The updated nodal coordinates should be + // used for a gap only evaluation, which is currently not done. + for ( int i = 0; i < this->m_mesh.numTotalNodes; ++i ) { + this->m_mesh.fx1[i] = 0.; + this->m_mesh.fy1[i] = 0.; + this->m_mesh.fz1[i] = 0.; + this->m_mesh.fx2[i] = 0.; + this->m_mesh.fy2[i] = 0.; + this->m_mesh.fz2[i] = 0.; + } + + // call tribol update() again for residual only evaluation + // TODO check to make sure this call here after refactor works, SRW + tribol::setLagrangeMultiplierOptions( 0, tribol::ImplicitEvalMode::MORTAR_RESIDUAL, + tribol::SparseMode::MFEM_LINKED_LIST ); + + RealT dt = 1.0; + int tribol_update_err = tribol::update( 1, 1., dt ); + + EXPECT_EQ( tribol_update_err, 0 ); + + // sum the nodal force contributions on the nonmortar side + for ( int i = 0; i < this->m_mesh.numNonmortarSurfaceNodes; ++i ) { + int offset = this->m_mesh.numMortarNodes; + // exploit offset and contiguous node numbering in the indexing here. + nonmortarForceSum += this->m_mesh.fz2[offset + i]; + } + pressure_rel_error = nonmortarForceSum; + SLIC_DEBUG( "NODAL FORCE SUM (NONMORTAR, TRIBOL RESIDUALS): " << nonmortarForceSum ); + } + + // update nodal coordinates in separate stacked array. Keep original + // mesh array as that of the reference configuration + for ( int i = 0; i < this->m_mesh.numTotalNodes; ++i ) { + for ( int j = 0; j < this->m_mesh.dim; ++j ) { + xyz[this->m_mesh.numTotalNodes * j + i] += sol_data[this->m_mesh.dim * i + j]; + xyz_inc[this->m_mesh.numTotalNodes * j + i] = sol_data[this->m_mesh.dim * i + j]; + } + } + + // compute stress update + mfem::GridFunction u( fe_space, &xyz_inc[0] ); + const int tdim = this->m_mesh.dim * ( this->m_mesh.dim + 1 ) / 2; + mfem::FiniteElementSpace flux_fespace( this->m_mesh.mfem_mesh, &fe_coll, tdim ); + mfem::GridFunction stress( &flux_fespace ); + stress = 0.; + mfem::BilinearFormIntegrator* integ = &elastInteg; + + u.ComputeFlux( *integ, stress ); + + // instantiate stress33 relative error grid function + mfem::GridFunction stress33_rel_error( stress ); + + // compute pressure difference from computed contact pressure solution + // and stress solution from mfem + if ( contact ) { + mfem::Vector node_vals; + stress.GetNodalValues( node_vals, 3 ); + RealT* data = stress.GetData(); + pressure_rel_error -= data[2 * this->m_mesh.numTotalNodes + this->m_mesh.faceConn2[0]]; + pressure_rel_error /= data[2 * this->m_mesh.numTotalNodes + this->m_mesh.faceConn2[0]]; + pressure_rel_error = std::abs( pressure_rel_error ); + + RealT* error_data = stress33_rel_error.GetData(); + for ( int i = ( 2 * this->m_mesh.numTotalNodes ); + i < ( 2 * this->m_mesh.numTotalNodes + this->m_mesh.numTotalNodes ); ++i ) { + error_data[i] -= nonmortarForceSum; + error_data[i] /= nonmortarForceSum; + error_data[i] = std::abs( error_data[i] ); + } + } + + if ( vis ) { + // mesh output + std::ostringstream mesh_ref_name, mesh_cur_name; + mesh_ref_name << "mesh_ref_" << test_name << "." << std::setfill( '0' ) << std::setw( 6 ) << ".vtk"; + mesh_cur_name << "mesh_cur_" << test_name << "." << std::setfill( '0' ) << std::setw( 6 ) << ".vtk"; + + std::ofstream mesh_ref_ofs( mesh_ref_name.str().c_str() ); + mesh_ref_ofs.precision( 8 ); + + // print reference configuration mesh + this->m_mesh.mfem_mesh->PrintVTK( mesh_ref_ofs, 0, 0 ); + + // set the current configuration vector and mesh node grid function for output + mfem::Vector x_cur( &xyz[0], this->m_mesh.dim * this->m_mesh.numTotalNodes ); + this->m_mesh.mfem_mesh->SetNodes( x_cur ); + + // print current mesh + std::ofstream mesh_cur_ofs( mesh_cur_name.str().c_str() ); + mesh_cur_ofs.precision( 8 ); + this->m_mesh.mfem_mesh->PrintVTK( mesh_cur_ofs, 0, 0 ); + + // save stress in current mesh .vtk. Note, do this for contact and no contact + stress.SaveVTK( mesh_cur_ofs, "stress", 0 ); + stress33_rel_error.SaveVTK( mesh_cur_ofs, "stress_rel_error", 0 ); + } + + // DEBUG: print matrix and vector output. Leave this code in here + // and guard with boolean + if ( output ) { + std::ofstream matrix; + matrix.setf( std::ios::scientific ); + matrix.precision( 0 ); + std::ostringstream suffix_matrix; + suffix_matrix << "jacobian_" << test_name << ".txt"; + matrix.open( "matrix_" + suffix_matrix.str() ); + + std::ofstream sol_vec; + sol_vec.setf( std::ios::scientific ); + sol_vec.precision( 2 ); + std::ostringstream suffix_sol; + suffix_sol << "vector_" << test_name << ".txt"; + sol_vec.open( "sol_" + suffix_sol.str() ); + + std::ofstream rhs_vec; + rhs_vec.setf( std::ios::scientific ); + rhs_vec.precision( 2 ); + std::ostringstream suffix_rhs; + suffix_rhs << "vector_" << test_name << ".txt"; + rhs_vec.open( "rhs_" + suffix_rhs.str() ); + + for ( int i = 0; i < jac.NumRows(); ++i ) { + rhs_vec << b[i] << "\n"; + sol_vec << sol_data[i] << "\n"; + for ( int j = 0; j < jac.NumCols(); ++j ) { + RealT val = dJac( i, j ); + matrix << val << " "; } + matrix << "\n"; + } - sol_vec.close(); - rhs_vec.close(); - matrix.close(); - } + sol_vec.close(); + rhs_vec.close(); + matrix.close(); + } - tribol::finalize(); + tribol::finalize(); - // delete the jacobian matrix - delete fe_space; + // delete the jacobian matrix + delete fe_space; -} // end computeContactSolution +} // end computeContactSolution TEST_F( MortarLMPatchTest, single_mortar_uniform_patch ) { - - mfem::Vector xsol_base; - mfem::Vector xsol_cntct; - - int nMortarElems = 4; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = 1; - - int nNonmortarElems = 4; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = 1; - - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.05; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 0.95; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - bool output = false; // solution vector and rhs - bool contact = true; - bool debug = false; // element matrix writes to .txt files - bool visualization = false; // output of .vtk with mesh and stress output - - RealT thetaM = 0.; - RealT thetaS = 0.; - - RealT press_rel_error = 0.; - this->computeContactSolution( nElemsXM, nElemsYM, nElemsZM, - nElemsXS, nElemsYS, nElemsZS, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - thetaM, thetaS, - tribol::SINGLE_MORTAR, - "uniform_mortar_fine", - contact, output, debug, - visualization, xsol_cntct, press_rel_error ); - - this->TearDown(); - this->SetUp(); - contact = false; - output = false; - debug = false; - visualization = false; - - RealT press_rel_error_null = 0.; - - this->computeContactSolution( nElemsXM, nElemsYM, nElemsZM, - nElemsXS, nElemsYS, nElemsZS, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - thetaM, thetaS, - tribol::SINGLE_MORTAR, - "uniform_mortar_fine_exact", - contact, output, debug, - visualization, xsol_base, press_rel_error_null ); - - mfem::Vector diff(xsol_cntct.Size()); - subtract(xsol_cntct, xsol_base, diff); - - RealT tol = 5.e-4; // 1/100th of the gap - int size = this->m_mesh.dim * this->m_mesh.numTotalNodes; - for (int i=0; icomputeContactSolution( nElemsXM, nElemsYM, nElemsZM, nElemsXS, nElemsYS, nElemsZS, x_min1, y_min1, z_min1, + x_max1, y_max1, z_max1, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, thetaM, thetaS, + tribol::SINGLE_MORTAR, "uniform_mortar_fine", contact, output, debug, visualization, + xsol_cntct, press_rel_error ); + + this->TearDown(); + this->SetUp(); + contact = false; + output = false; + debug = false; + visualization = false; + + RealT press_rel_error_null = 0.; + + this->computeContactSolution( nElemsXM, nElemsYM, nElemsZM, nElemsXS, nElemsYS, nElemsZS, x_min1, y_min1, z_min1, + x_max1, y_max1, z_max1, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, thetaM, thetaS, + tribol::SINGLE_MORTAR, "uniform_mortar_fine_exact", contact, output, debug, + visualization, xsol_base, press_rel_error_null ); + + mfem::Vector diff( xsol_cntct.Size() ); + subtract( xsol_cntct, xsol_base, diff ); + + RealT tol = 5.e-4; // 1/100th of the gap + int size = this->m_mesh.dim * this->m_mesh.numTotalNodes; + for ( int i = 0; i < size; ++i ) { + EXPECT_LE( std::abs( diff( i ) ), tol ); + } + + RealT press_tol = 1.e-2; // 0.02% error in pressure + SLIC_DEBUG( "press_rel_error: " << press_rel_error ); + EXPECT_LE( std::abs( press_rel_error ), press_tol ); } TEST_F( MortarLMPatchTest, single_mortar_nonuniform_mortar_fine_patch ) { - - mfem::Vector xsol_base; - mfem::Vector xsol_cntct; - - int nMortarElems = 5; // 4; // keep for a nice non-uniform mesh - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = 1; - - int nNonmortarElems = 3; // 3; // keep for a nice non-uniform mesh - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = 1; - - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.05; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 0.95; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - bool output = false; // solution vector and rhs - bool contact = true; - bool debug = false; // element matrix writes to .txt files - bool visualization = false; // output of .vtk with mesh and stress output - - RealT thetaM = 3.; // 0.; // keep for a nice non-uniform mesh - RealT thetaS = -5.; // 3.; // keep for a nice non-uniform mesh - - RealT press_rel_error = 0.; - this->computeContactSolution( nElemsXM, nElemsYM, nElemsZM, - nElemsXS, nElemsYS, nElemsZS, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - thetaM, thetaS, - tribol::SINGLE_MORTAR, - "nonuniform_mortar_fine", - contact, output, debug, - visualization, xsol_cntct, press_rel_error ); - - this->TearDown(); - this->SetUp(); - contact = false; - output = false; - debug = false; - visualization = false; - - RealT press_rel_error_null = 0.; - - this->computeContactSolution( nElemsXM, nElemsYM, nElemsZM, - nElemsXS, nElemsYS, nElemsZS, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - thetaM, thetaS, - tribol::SINGLE_MORTAR, - "nonuniform_mortar_fine_exact", - contact, output, debug, - visualization, xsol_base, press_rel_error_null ); - - mfem::Vector diff(xsol_cntct.Size()); - subtract(xsol_cntct, xsol_base, diff); - - RealT tol = 5.e-4; // 1/100th of the gap - int size = this->m_mesh.dim * this->m_mesh.numTotalNodes; - for (int i=0; icomputeContactSolution( nElemsXM, nElemsYM, nElemsZM, nElemsXS, nElemsYS, nElemsZS, x_min1, y_min1, z_min1, + x_max1, y_max1, z_max1, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, thetaM, thetaS, + tribol::SINGLE_MORTAR, "nonuniform_mortar_fine", contact, output, debug, visualization, + xsol_cntct, press_rel_error ); + + this->TearDown(); + this->SetUp(); + contact = false; + output = false; + debug = false; + visualization = false; + + RealT press_rel_error_null = 0.; + + this->computeContactSolution( nElemsXM, nElemsYM, nElemsZM, nElemsXS, nElemsYS, nElemsZS, x_min1, y_min1, z_min1, + x_max1, y_max1, z_max1, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, thetaM, thetaS, + tribol::SINGLE_MORTAR, "nonuniform_mortar_fine_exact", contact, output, debug, + visualization, xsol_base, press_rel_error_null ); + + mfem::Vector diff( xsol_cntct.Size() ); + subtract( xsol_cntct, xsol_base, diff ); + + RealT tol = 5.e-4; // 1/100th of the gap + int size = this->m_mesh.dim * this->m_mesh.numTotalNodes; + for ( int i = 0; i < size; ++i ) { + EXPECT_LE( std::abs( diff( i ) ), tol ); + } + + RealT press_tol = 5.e-3; // 0.5% error in pressure + SLIC_DEBUG( "press_rel_error: " << press_rel_error ); + EXPECT_LE( std::abs( press_rel_error ), press_tol ); } TEST_F( MortarLMPatchTest, single_mortar_nonuniform_nonmortar_fine_patch ) { - - mfem::Vector xsol_base; - mfem::Vector xsol_cntct; - - int nMortarElems = 3; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = 1; - - int nNonmortarElems = 5; - int nElemsXS = nNonmortarElems; - int nElemsYS = 7; //nNonmortarElems; - int nElemsZS = 1; - - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.05; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 0.95; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - bool output = false; // solution vector and rhs - bool contact = true; - bool debug = false; // element matrix writes to .txt files - bool visualization = false; // output of .vtk with mesh and stress output - - RealT press_rel_error = 0.; - this->computeContactSolution( nElemsXM, nElemsYM, nElemsZM, - nElemsXS, nElemsYS, nElemsZS, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0., - tribol::SINGLE_MORTAR, - "uniform_nonmortar_fine", - contact, output, debug, - visualization, xsol_cntct, press_rel_error ); - - this->TearDown(); - this->SetUp(); - contact = false; - output = false; - debug = false; - visualization = false; - - RealT press_rel_error_null = 0.; - - this->computeContactSolution( nElemsXM, nElemsYM, nElemsZM, - nElemsXS, nElemsYS, nElemsZS, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0., - tribol::SINGLE_MORTAR, - "uniform_nonmortar_fine_exact", - contact, output, debug, - visualization, xsol_base, press_rel_error_null ); - - mfem::Vector diff(xsol_cntct.Size()); - subtract(xsol_cntct, xsol_base, diff); - - RealT tol = 1.e-10; - int size = this->m_mesh.dim * this->m_mesh.numTotalNodes; - for (int i=0; icomputeContactSolution( nElemsXM, nElemsYM, nElemsZM, nElemsXS, nElemsYS, nElemsZS, x_min1, y_min1, z_min1, + x_max1, y_max1, z_max1, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., 0., + tribol::SINGLE_MORTAR, "uniform_nonmortar_fine", contact, output, debug, visualization, + xsol_cntct, press_rel_error ); + + this->TearDown(); + this->SetUp(); + contact = false; + output = false; + debug = false; + visualization = false; + + RealT press_rel_error_null = 0.; + + this->computeContactSolution( nElemsXM, nElemsYM, nElemsZM, nElemsXS, nElemsYS, nElemsZS, x_min1, y_min1, z_min1, + x_max1, y_max1, z_max1, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., 0., + tribol::SINGLE_MORTAR, "uniform_nonmortar_fine_exact", contact, output, debug, + visualization, xsol_base, press_rel_error_null ); + + mfem::Vector diff( xsol_cntct.Size() ); + subtract( xsol_cntct, xsol_base, diff ); + + RealT tol = 1.e-10; + int size = this->m_mesh.dim * this->m_mesh.numTotalNodes; + for ( int i = 0; i < size; ++i ) { + EXPECT_LE( std::abs( diff( i ) ), tol ); + } + + RealT press_tol = 1.e-7; + SLIC_DEBUG( "press_rel_error: " << press_rel_error ); + EXPECT_LE( std::abs( press_rel_error ), press_tol ); } TEST_F( MortarLMPatchTest, aligned_mortar_patch ) { - - mfem::Vector xsol_base; - mfem::Vector xsol_cntct; - - int nMortarElems = 4; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = 1; - - int nNonmortarElems = 4; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = 1; - - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.05; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 0.95; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - bool output = false; // solution output and rhs - bool contact = true; - bool debug = false; // debug matrix writes to .txt files - bool visualization = false; // visualize mesh and stress - - RealT press_rel_error = 0.; - this->computeContactSolution( nElemsXM, nElemsYM, nElemsZM, - nElemsXS, nElemsYS, nElemsZS, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0., - tribol::ALIGNED_MORTAR, - "aligned", - contact, output, debug, - visualization, xsol_cntct, press_rel_error ); - - this->TearDown(); - this->SetUp(); - contact = false; - output = false; - debug = false; - visualization = false; - - RealT press_rel_error_null = 0.; - - this->computeContactSolution( nElemsXM, nElemsYM, nElemsZM, - nElemsXS, nElemsYS, nElemsZS, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0., - tribol::ALIGNED_MORTAR, - "aligned_exact", - contact, output, debug, - visualization, xsol_base, press_rel_error_null ); - - mfem::Vector diff(xsol_cntct.Size()); - subtract(xsol_cntct, xsol_base, diff); - - RealT tol = 1.e-10; - int size = this->m_mesh.dim * this->m_mesh.numTotalNodes; - for (int i=0; icomputeContactSolution( nElemsXM, nElemsYM, nElemsZM, nElemsXS, nElemsYS, nElemsZS, x_min1, y_min1, z_min1, + x_max1, y_max1, z_max1, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., 0., + tribol::ALIGNED_MORTAR, "aligned", contact, output, debug, visualization, xsol_cntct, + press_rel_error ); + + this->TearDown(); + this->SetUp(); + contact = false; + output = false; + debug = false; + visualization = false; + + RealT press_rel_error_null = 0.; + + this->computeContactSolution( nElemsXM, nElemsYM, nElemsZM, nElemsXS, nElemsYS, nElemsZS, x_min1, y_min1, z_min1, + x_max1, y_max1, z_max1, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., 0., + tribol::ALIGNED_MORTAR, "aligned_exact", contact, output, debug, visualization, + xsol_base, press_rel_error_null ); + + mfem::Vector diff( xsol_cntct.Size() ); + subtract( xsol_cntct, xsol_base, diff ); + + RealT tol = 1.e-10; + int size = this->m_mesh.dim * this->m_mesh.numTotalNodes; + for ( int i = 0; i < size; ++i ) { + EXPECT_LE( std::abs( diff( i ) ), tol ); + } + + RealT press_tol = 1.e-7; + SLIC_DEBUG( "press_rel_error: " << press_rel_error ); + EXPECT_LE( std::abs( press_rel_error ), press_tol ); } -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); #ifdef TRIBOL_USE_UMPIRE umpire::ResourceManager::getInstance(); // initialize umpire's ResouceManager diff --git a/src/tests/tribol_mortar_sparse_weights.cpp b/src/tests/tribol_mortar_sparse_weights.cpp index 56d38ca1..852fbc23 100644 --- a/src/tests/tribol_mortar_sparse_weights.cpp +++ b/src/tests/tribol_mortar_sparse_weights.cpp @@ -32,7 +32,7 @@ #include "gtest/gtest.h" // c++ includes -#include // std::abs, std::cos, std::sin +#include // std::abs, std::cos, std::sin #include #include #include @@ -42,427 +42,381 @@ using RealT = tribol::RealT; void computeGapsFromSparseWts( tribol::CouplingScheme* cs, RealT* gaps ) { - int const dim = cs->spatialDimension(); - - //////////////////////////////////////////////////////////////////////// - // - // Grab pointers to mesh data - // - //////////////////////////////////////////////////////////////////////// - auto mortarMesh = cs->getMesh1().getView(); - auto nonmortarMesh = cs->getMesh2().getView(); - - // get mortar weights in CSR format. Note this simple API function - // calls tribol::getCSRMatrix() so this API function in the Tribol - // namespace is getting tested. - int *I = nullptr; - int *J = nullptr; - RealT *wts = nullptr; - int nOffsets = 0; - int nNonZeros = 0; - int csr_err = GetSimpleCouplingCSR( &I, &J, &wts, &nOffsets, &nNonZeros ); - - EXPECT_EQ( csr_err, 0 ); - - SLIC_ERROR_IF(I==nullptr, "Mortar wts test, I is null."); - - // get mortar node id offset to distinguish mortar from nonmortar column contributions - // sorts unique surface node ids from connectivity - auto mortar_id = mortarMesh.meshId(); - auto sorted_surface_node_ids = tribol::MeshManager::getInstance().at(mortar_id).sortSurfaceNodeIds(); - int nodeOffset = sorted_surface_node_ids.back() + 1; - - //////////////////////////////////////////////////////////////// - // compute nonmortar gaps to determine active set of contact dofs // - //////////////////////////////////////////////////////////////// - // loop over total number of nodes. Note, only the active nonmortar "rows" should - // have nonzero contributions - int numTotalNodes = static_cast( cs->getMethodData() )->m_numTotalNodes; - for (int a=0; aspatialDimension(); + + //////////////////////////////////////////////////////////////////////// + // + // Grab pointers to mesh data + // + //////////////////////////////////////////////////////////////////////// + auto mortarMesh = cs->getMesh1().getView(); + auto nonmortarMesh = cs->getMesh2().getView(); + + // get mortar weights in CSR format. Note this simple API function + // calls tribol::getCSRMatrix() so this API function in the Tribol + // namespace is getting tested. + int* I = nullptr; + int* J = nullptr; + RealT* wts = nullptr; + int nOffsets = 0; + int nNonZeros = 0; + int csr_err = GetSimpleCouplingCSR( &I, &J, &wts, &nOffsets, &nNonZeros ); + + EXPECT_EQ( csr_err, 0 ); + + SLIC_ERROR_IF( I == nullptr, "Mortar wts test, I is null." ); + + // get mortar node id offset to distinguish mortar from nonmortar column contributions + // sorts unique surface node ids from connectivity + auto mortar_id = mortarMesh.meshId(); + auto sorted_surface_node_ids = tribol::MeshManager::getInstance().at( mortar_id ).sortSurfaceNodeIds(); + int nodeOffset = sorted_surface_node_ids.back() + 1; + + //////////////////////////////////////////////////////////////// + // compute nonmortar gaps to determine active set of contact dofs // + //////////////////////////////////////////////////////////////// + // loop over total number of nodes. Note, only the active nonmortar "rows" should + // have nonzero contributions + int numTotalNodes = static_cast( cs->getMethodData() )->m_numTotalNodes; + for ( int a = 0; a < numTotalNodes; ++a ) { + // get nonmortar nodal normal + RealT nrml_a[dim]; + nrml_a[0] = nonmortarMesh.getNodalNormals()[0][a]; // array is global length; no index out (?) + nrml_a[1] = nonmortarMesh.getNodalNormals()[1][a]; + if ( dim == 3 ) { + nrml_a[2] = nonmortarMesh.getNodalNormals()[2][a]; + } + + // loop over range of nonzero column entries + for ( int b = I[a]; b < I[a + 1]; ++b ) { + // get face coordinates for node J[b], i.e. column node id + RealT mortar_xyz[dim]; + RealT nonmortar_xyz[dim]; + + for ( int i = 0; i < dim; ++i ) { + mortar_xyz[i] = 0.; + nonmortar_xyz[i] = 0.; } - // loop over range of nonzero column entries - for (int b=I[a]; bgetMeshId2(); - tribol::MeshData& nonmortarMesh = meshManager.at( nonmortarId ); + mortar_xyz[0] = mortarMesh.getPosition()[0][J[b]]; + mortar_xyz[1] = mortarMesh.getPosition()[1][J[b]]; + if ( dim == 3 ) { + mortar_xyz[2] = mortarMesh.getPosition()[2][J[b]]; + } - int numTotalNodes = cs->getNumTotalNodes(); + gaps[a] += tribol::dotProd( &nrml_a[0], &mortar_xyz[0], dim ) * n_ab; + + } else // nonmortar/nonmortar weight + { + nonmortar_xyz[0] = nonmortarMesh.getPosition()[0][J[b]]; + nonmortar_xyz[1] = nonmortarMesh.getPosition()[1][J[b]]; + if ( dim == 3 ) { + nonmortar_xyz[2] = nonmortarMesh.getPosition()[2][J[b]]; + } + gaps[a] -= tribol::dotProd( &nrml_a[0], &nonmortar_xyz[0], dim ) * n_ab; + } // end if-block + + } // end loop over nonzero columns, I[a] + } // end loop over matrix rows + +} // end ComputeGapsFromSparseWts() + +void compareGaps( tribol::CouplingScheme const* cs, RealT* gaps, const RealT tol ) +{ + tribol::MeshManager& meshManager = tribol::MeshManager::getInstance(); + tribol::IndexT const nonmortarId = cs->getMeshId2(); + tribol::MeshData& nonmortarMesh = meshManager.at( nonmortarId ); - for (int i=0; igetNumTotalNodes(); + for ( int i = 0; i < numTotalNodes; ++i ) { + RealT diff = nonmortarMesh.getNodalFields().m_node_gap[i] - gaps[i]; + EXPECT_LE( diff, tol ); + } } /*! * Test fixture class with some setup necessary to test * the MORTAR_WEIGHTS implementation in Tribol */ -class MortarSparseWtsTest : public ::testing::Test +class MortarSparseWtsTest : public ::testing::Test { + public: + tribol::TestMesh m_mesh; + + protected: + void SetUp() override {} + + void TearDown() override + { + // call clear() on mesh object to be safe + this->m_mesh.clear(); + } + + protected: +}; + +TEST_F( MortarSparseWtsTest, mortar_weights_uniform ) { - -public: + int nMortarElems = 5; + int nElemsXM = nMortarElems; + int nElemsYM = nMortarElems; + int nElemsZM = nMortarElems; - tribol::TestMesh m_mesh; + int nNonmortarElems = 5; + int nElemsXS = nNonmortarElems; + int nElemsYS = nNonmortarElems; + int nElemsZS = nNonmortarElems; -protected: + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.05; - void SetUp() override - { - } + RealT x_min2 = 0.; + RealT y_min2 = 0.; + RealT z_min2 = 0.95; + RealT x_max2 = 1.; + RealT y_max2 = 1.; + RealT z_max2 = 2.; - void TearDown() override - { - // call clear() on mesh object to be safe - this->m_mesh.clear(); - } + this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., + 0. ); -protected: + // call tribol setup and update + tribol::TestControlParameters parameters; // struct does not hold info right now + int test_mesh_update_err = this->m_mesh.tribolSetupAndUpdate( + tribol::MORTAR_WEIGHTS, tribol::NULL_ENFORCEMENT, tribol::NULL_MODEL, tribol::NO_CASE, false, parameters ); -}; + EXPECT_EQ( test_mesh_update_err, 0 ); -TEST_F( MortarSparseWtsTest, mortar_weights_uniform ) -{ - int nMortarElems = 5; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 5; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.05; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 0.95; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0. ); - - // call tribol setup and update - tribol::TestControlParameters parameters; // struct does not hold info right now - int test_mesh_update_err = - this->m_mesh.tribolSetupAndUpdate( tribol::MORTAR_WEIGHTS, tribol::NULL_ENFORCEMENT, - tribol::NULL_MODEL, tribol::NO_CASE, false, parameters ); - - EXPECT_EQ( test_mesh_update_err, 0 ); - - tribol::CouplingSchemeManager& couplingSchemeManager = - tribol::CouplingSchemeManager::getInstance(); - - tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); - - // allocate storage for gap computations using sparse mortar weights - RealT * gaps = nullptr; - int size = static_cast( couplingScheme->getMethodData() )->m_numTotalNodes; - gaps = new RealT[ size ]; - - // initialize gap storage - for (int i=0; i( couplingScheme->getMethodData() )->m_numTotalNodes; + gaps = new RealT[size]; + + // initialize gap storage + for ( int i = 0; i < size; ++i ) { + gaps[i] = 0.; + } + + // use sparse mortar weights to compute gaps + computeGapsFromSparseWts( couplingScheme, gaps ); + + compareGaps( couplingScheme, gaps, 1.E-8 ); + + tribol::finalize(); + + delete[] gaps; } TEST_F( MortarSparseWtsTest, simple_api_mortar_weights_uniform ) { - int nMortarElems = 5; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 5; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.05; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 0.95; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0. ); - - // call tribol setup and update - tribol::TestControlParameters parameters; // struct does not hold info right now - int test_mesh_simple_update_err = - this->m_mesh.simpleTribolSetupAndUpdate( tribol::MORTAR_WEIGHTS, tribol::NULL_ENFORCEMENT, - tribol::NULL_MODEL, tribol::NO_CASE, false, parameters ); - - EXPECT_EQ( test_mesh_simple_update_err, 0 ); - - tribol::CouplingSchemeManager& couplingSchemeManager = - tribol::CouplingSchemeManager::getInstance(); - - tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); - - // allocate storage for gap computations using sparse mortar weights - RealT * gaps = nullptr; - int size = static_cast( couplingScheme->getMethodData() )->m_numTotalNodes; - gaps = new RealT[ size ]; - - // initialize gap storage - for (int i=0; im_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., + 0. ); + + // call tribol setup and update + tribol::TestControlParameters parameters; // struct does not hold info right now + int test_mesh_simple_update_err = this->m_mesh.simpleTribolSetupAndUpdate( + tribol::MORTAR_WEIGHTS, tribol::NULL_ENFORCEMENT, tribol::NULL_MODEL, tribol::NO_CASE, false, parameters ); + + EXPECT_EQ( test_mesh_simple_update_err, 0 ); + + tribol::CouplingSchemeManager& couplingSchemeManager = tribol::CouplingSchemeManager::getInstance(); + + tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); + + // allocate storage for gap computations using sparse mortar weights + RealT* gaps = nullptr; + int size = static_cast( couplingScheme->getMethodData() )->m_numTotalNodes; + gaps = new RealT[size]; + + // initialize gap storage + for ( int i = 0; i < size; ++i ) { + gaps[i] = 0.; + } + + // use sparse mortar weights to compute gaps + computeGapsFromSparseWts( couplingScheme, gaps ); + + compareGaps( couplingScheme, gaps, 1.E-8 ); + + tribol::finalize(); + + delete[] gaps; } TEST_F( MortarSparseWtsTest, mortar_weights_nonuniform_mortar_fine ) { - int nMortarElems = 5; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 4; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.05; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 0.95; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0. ); - - // call tribol setup and update - tribol::TestControlParameters parameters; // struct does not hold info right now - - int test_mesh_update_err = - this->m_mesh.tribolSetupAndUpdate( tribol::MORTAR_WEIGHTS, tribol::NULL_ENFORCEMENT, - tribol::NULL_MODEL, tribol::NO_CASE, false, parameters ); - - EXPECT_EQ( test_mesh_update_err, 0 ); - - tribol::CouplingSchemeManager& couplingSchemeManager = - tribol::CouplingSchemeManager::getInstance(); - - tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); - - // allocate storage for gap computations using sparse mortar weights - RealT * gaps = nullptr; - int size = static_cast( couplingScheme->getMethodData() )->m_numTotalNodes; - gaps = new RealT[ size ]; - - // initialize gap storage - for (int i=0; im_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., + 0. ); + + // call tribol setup and update + tribol::TestControlParameters parameters; // struct does not hold info right now + + int test_mesh_update_err = this->m_mesh.tribolSetupAndUpdate( + tribol::MORTAR_WEIGHTS, tribol::NULL_ENFORCEMENT, tribol::NULL_MODEL, tribol::NO_CASE, false, parameters ); + + EXPECT_EQ( test_mesh_update_err, 0 ); + + tribol::CouplingSchemeManager& couplingSchemeManager = tribol::CouplingSchemeManager::getInstance(); + + tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); + + // allocate storage for gap computations using sparse mortar weights + RealT* gaps = nullptr; + int size = static_cast( couplingScheme->getMethodData() )->m_numTotalNodes; + gaps = new RealT[size]; + + // initialize gap storage + for ( int i = 0; i < size; ++i ) { + gaps[i] = 0.; + } + + // use sparse mortar weights to compute gaps + computeGapsFromSparseWts( couplingScheme, gaps ); + + compareGaps( couplingScheme, gaps, 1.E-8 ); + + tribol::finalize(); + + delete[] gaps; } TEST_F( MortarSparseWtsTest, mortar_weights_nonuniform_nonmortar_fine ) { - int nMortarElems = 2; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 3; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.05; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 0.95; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0. ); - - // call tribol setup and update - tribol::TestControlParameters parameters; // struct does not hold info right now - - int test_mesh_update_err = - this->m_mesh.tribolSetupAndUpdate( tribol::MORTAR_WEIGHTS, tribol::NULL_ENFORCEMENT, - tribol::NULL_MODEL, tribol::NO_CASE, false, parameters ); - - EXPECT_EQ( test_mesh_update_err, 0 ); - - tribol::CouplingSchemeManager& couplingSchemeManager = - tribol::CouplingSchemeManager::getInstance(); - - tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); - - // allocate storage for gap computations using sparse mortar weights - RealT * gaps = nullptr; - int size = static_cast( couplingScheme->getMethodData() )->m_numTotalNodes; - gaps = new RealT[ size ]; - - // initialize gap storage - for (int i=0; im_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., + 0. ); + + // call tribol setup and update + tribol::TestControlParameters parameters; // struct does not hold info right now + + int test_mesh_update_err = this->m_mesh.tribolSetupAndUpdate( + tribol::MORTAR_WEIGHTS, tribol::NULL_ENFORCEMENT, tribol::NULL_MODEL, tribol::NO_CASE, false, parameters ); + + EXPECT_EQ( test_mesh_update_err, 0 ); + + tribol::CouplingSchemeManager& couplingSchemeManager = tribol::CouplingSchemeManager::getInstance(); + + tribol::CouplingScheme* couplingScheme = &couplingSchemeManager.at( 0 ); + + // allocate storage for gap computations using sparse mortar weights + RealT* gaps = nullptr; + int size = static_cast( couplingScheme->getMethodData() )->m_numTotalNodes; + gaps = new RealT[size]; + + // initialize gap storage + for ( int i = 0; i < size; ++i ) { + gaps[i] = 0.; + } + + // use sparse mortar weights to compute gaps + computeGapsFromSparseWts( couplingScheme, gaps ); + + compareGaps( couplingScheme, gaps, 1.E-8 ); + + tribol::finalize(); + + delete[] gaps; } -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); #ifdef TRIBOL_USE_UMPIRE - umpire::ResourceManager::getInstance(); // initialize umpire's ResouceManager + umpire::ResourceManager::getInstance(); // initialize umpire's ResouceManager #endif - axom::slic::SimpleLogger logger; // create & initialize logger, - tribol::SimpleMPIWrapper wrapper(argc, argv); // initialize and finalize MPI, when applicable + axom::slic::SimpleLogger logger; // create & initialize logger, + tribol::SimpleMPIWrapper wrapper( argc, argv ); // initialize and finalize MPI, when applicable result = RUN_ALL_TESTS(); diff --git a/src/tests/tribol_mortar_wts.cpp b/src/tests/tribol_mortar_wts.cpp index 3c714f3f..ccafafa3 100644 --- a/src/tests/tribol_mortar_wts.cpp +++ b/src/tests/tribol_mortar_wts.cpp @@ -16,412 +16,342 @@ #include "gtest/gtest.h" // c++ includes -#include // std::abs +#include // std::abs using RealT = tribol::RealT; /*! - * Test fixture class with some setup necessary to compute - * the nonmortar/nonmortar and mortar/nonmortar mortar projection weights - * in order to test a simple projection between two + * Test fixture class with some setup necessary to compute + * the nonmortar/nonmortar and mortar/nonmortar mortar projection weights + * in order to test a simple projection between two * mis-aligned faces. */ -class MortarWeightTest : public ::testing::Test -{ - -public: - int numNodes; - int numNodesPerFace; - int numOverlapNodes; - int dim; - - RealT* getXCoords( int id ) - { - if (id == 0) - { - return x1; - } - else - { - return x2; - } - } - - RealT* getYCoords( int id ) - { - if (id == 0) - { - return y1; - } - else - { - return y2; - } - } - - RealT* getZCoords( int id ) - { - if (id == 0) - { - return z1; - } - else - { - return z2; - } - } - - RealT* getXOverlapCoords() - { - return xOverlap; - } - - RealT* getYOverlapCoords() - { - return yOverlap; - } - - RealT* getZOverlapCoords() - { - return zOverlap; - } - - void checkMortarWts ( RealT pTestNonmortar[4], RealT pTestMortar[4]) - { - if (this->numNodesPerFace != 4) - { - SLIC_ERROR("checkMortarWts: number of nodes per face not equal to 4."); - } - - RealT xyz1[ this->dim * this->numNodesPerFace ]; - RealT xyz2[ this->dim * this->numNodesPerFace ]; - RealT xyzOverlap[ this->dim * this->numOverlapNodes ]; - RealT* xy1 = xyz1; - RealT* xy2 = xyz2; - RealT* xyOverlap = xyzOverlap; - - RealT * x1 = this->x1; - RealT * y1 = this->y1; - RealT * z1 = this->z1; - RealT * x2 = this->x2; - RealT * y2 = this->y2; - RealT * z2 = this->z2; - RealT * xo = this->xOverlap; - RealT * yo = this->yOverlap; - RealT * zo = this->zOverlap; - - // generate stacked coordinate array - for (int j=0; jnumNodesPerFace; ++j) - { - for (int k=0; kdim; ++k) - { - switch (k) - { - case 0: - xy1[ this->dim * j + k ] = x1[ j ]; - xy2[ this->dim * j + k ] = x2[ j ]; - xyOverlap[ this->dim * j + k ] = xo[ j ]; - break; - case 1: - xy1[ this->dim * j + k ] = y1[ j ]; - xy2[ this->dim * j + k ] = y2[ j ]; - xyOverlap[ this->dim * j + k ] = yo[ j ]; - break; - case 2: - xy1[ this->dim * j + k ] = z1[ j ]; - xy2[ this->dim * j + k ] = z2[ j ]; - xyOverlap[ this->dim * j + k ] = zo[ j ]; - break; - } // end switch - } // end loop over dimension - } // end loop over nodes - - // instantiate SurfaceContactElem struct. Note, this object is instantiated - // using face 1, face 2, and the set overlap polygon. Note, the mesh ids are set - // equal to 0, and the face ids are 0 and 1, respectively. - tribol::SurfaceContactElem elem ( this->dim, xy1, xy2, xyOverlap, - this->numNodesPerFace, this->numOverlapNodes, - 0, 0, 0, 1); - - // compute the mortar weights to be stored on - // the surface contact element struct. - tribol::ComputeMortarWeights( elem ); - - // test the projection on an arbitrary vector - RealT p[4] = {1., 1., 1., 1.}; - for (int i=0; inumNodesPerFace; ++i) - { - for (int j=0; jnumNodesPerFace; ++j) - { - int nonmortarId = this->numNodesPerFace*i+j; - int mortarId = this->numNodesPerFace * this->numNodesPerFace + - this->numNodesPerFace * i + j; - pTestNonmortar[i] += elem.mortarWts[ nonmortarId ] * p[j]; - pTestMortar[i] += elem.mortarWts[ mortarId ] * p[j]; - } - } - - } - -protected: - - void SetUp() override - { - this->numNodes = 8; - this->numNodesPerFace = 4; - this->numOverlapNodes = 4; - this->dim = 3; - - if (this->x1 == nullptr) - { - this->x1 = new RealT [this->numNodes]; - } - else - { - delete [] this->x1; - this->x1 = new RealT [this->numNodes]; - } - - if (this->x2 == nullptr) - { - this->x2 = new RealT [this->numNodes]; - } - else - { - delete [] this->x2; - this->x2 = new RealT [this->numNodes]; - } - - if (this->y1 == nullptr) - { - this->y1 = new RealT [this->numNodes]; - } - else - { - delete [] this->y1; - this->y1 = new RealT [this->numNodes]; - } - - if (this->y2 == nullptr) - { - this->y2 = new RealT [this->numNodes]; - } - else - { - delete [] this->y2; - this->y2 = new RealT [this->numNodes]; - } - - if (this->z1 == nullptr) - { - this->z1 = new RealT [this->numNodes]; - } - else - { - delete [] this->z1; - this->z1 = new RealT [this->numNodes]; - } - - if (this->z2 == nullptr) - { - this->z2 = new RealT [this->numNodes]; - } - else - { - delete [] this->z2; - this->z2 = new RealT [this->numNodes]; - } - - if (this->xOverlap == nullptr) - { - this->xOverlap = new RealT [this->numOverlapNodes]; - } - else - { - delete [] this->xOverlap; - this->xOverlap = new RealT [this->numOverlapNodes]; - } - - if (this->yOverlap == nullptr) - { - this->yOverlap = new RealT [this->numOverlapNodes]; - } - else - { - delete [] this->yOverlap; - this->yOverlap = new RealT [this->numOverlapNodes]; - } - if (this->zOverlap == nullptr) - { - this->zOverlap = new RealT [this->numOverlapNodes]; - } - else - { - delete [] this->zOverlap; - this->zOverlap = new RealT [this->numOverlapNodes]; - } - } - - void TearDown() override - { - if (this->x1 != nullptr) - { - delete [] this->x1; - this->x1 = nullptr; - } - if (this->x2 != nullptr) - { - delete [] this->x2; - this->x2 = nullptr; - } - if (this->y1 != nullptr) - { - delete [] this->y1; - this->y1 = nullptr; - } - if (this->y2 != nullptr) - { - delete [] this->y2; - this->y2 = nullptr; - } - if (this->z1 != nullptr) - { - delete [] this->z1; - this->z1 = nullptr; - } - if (this->z2 != nullptr) - { - delete [] this->z2; - this->z2 = nullptr; - } - if (this->xOverlap != nullptr) - { - delete [] this->xOverlap; - this->xOverlap = nullptr; - } - if (this->yOverlap != nullptr) - { - delete [] this->yOverlap; - this->yOverlap = nullptr; - } - if (this->zOverlap != nullptr) - { - delete [] this->zOverlap; - this->zOverlap = nullptr; - } - } - -protected: - - RealT* x1 {nullptr}; - RealT* y1 {nullptr}; - RealT* z1 {nullptr}; - - RealT* x2 {nullptr}; - RealT* y2 {nullptr}; - RealT* z2 {nullptr}; - - RealT* xOverlap {nullptr}; - RealT* yOverlap {nullptr}; - RealT* zOverlap {nullptr}; - +class MortarWeightTest : public ::testing::Test { + public: + int numNodes; + int numNodesPerFace; + int numOverlapNodes; + int dim; + + RealT* getXCoords( int id ) + { + if ( id == 0 ) { + return x1; + } else { + return x2; + } + } + + RealT* getYCoords( int id ) + { + if ( id == 0 ) { + return y1; + } else { + return y2; + } + } + + RealT* getZCoords( int id ) + { + if ( id == 0 ) { + return z1; + } else { + return z2; + } + } + + RealT* getXOverlapCoords() { return xOverlap; } + + RealT* getYOverlapCoords() { return yOverlap; } + + RealT* getZOverlapCoords() { return zOverlap; } + + void checkMortarWts( RealT pTestNonmortar[4], RealT pTestMortar[4] ) + { + if ( this->numNodesPerFace != 4 ) { + SLIC_ERROR( "checkMortarWts: number of nodes per face not equal to 4." ); + } + + RealT xyz1[this->dim * this->numNodesPerFace]; + RealT xyz2[this->dim * this->numNodesPerFace]; + RealT xyzOverlap[this->dim * this->numOverlapNodes]; + RealT* xy1 = xyz1; + RealT* xy2 = xyz2; + RealT* xyOverlap = xyzOverlap; + + RealT* x1 = this->x1; + RealT* y1 = this->y1; + RealT* z1 = this->z1; + RealT* x2 = this->x2; + RealT* y2 = this->y2; + RealT* z2 = this->z2; + RealT* xo = this->xOverlap; + RealT* yo = this->yOverlap; + RealT* zo = this->zOverlap; + + // generate stacked coordinate array + for ( int j = 0; j < this->numNodesPerFace; ++j ) { + for ( int k = 0; k < this->dim; ++k ) { + switch ( k ) { + case 0: + xy1[this->dim * j + k] = x1[j]; + xy2[this->dim * j + k] = x2[j]; + xyOverlap[this->dim * j + k] = xo[j]; + break; + case 1: + xy1[this->dim * j + k] = y1[j]; + xy2[this->dim * j + k] = y2[j]; + xyOverlap[this->dim * j + k] = yo[j]; + break; + case 2: + xy1[this->dim * j + k] = z1[j]; + xy2[this->dim * j + k] = z2[j]; + xyOverlap[this->dim * j + k] = zo[j]; + break; + } // end switch + } // end loop over dimension + } // end loop over nodes + + // instantiate SurfaceContactElem struct. Note, this object is instantiated + // using face 1, face 2, and the set overlap polygon. Note, the mesh ids are set + // equal to 0, and the face ids are 0 and 1, respectively. + tribol::SurfaceContactElem elem( this->dim, xy1, xy2, xyOverlap, this->numNodesPerFace, this->numOverlapNodes, 0, 0, + 0, 1 ); + + // compute the mortar weights to be stored on + // the surface contact element struct. + tribol::ComputeMortarWeights( elem ); + + // test the projection on an arbitrary vector + RealT p[4] = { 1., 1., 1., 1. }; + for ( int i = 0; i < this->numNodesPerFace; ++i ) { + for ( int j = 0; j < this->numNodesPerFace; ++j ) { + int nonmortarId = this->numNodesPerFace * i + j; + int mortarId = this->numNodesPerFace * this->numNodesPerFace + this->numNodesPerFace * i + j; + pTestNonmortar[i] += elem.mortarWts[nonmortarId] * p[j]; + pTestMortar[i] += elem.mortarWts[mortarId] * p[j]; + } + } + } + + protected: + void SetUp() override + { + this->numNodes = 8; + this->numNodesPerFace = 4; + this->numOverlapNodes = 4; + this->dim = 3; + + if ( this->x1 == nullptr ) { + this->x1 = new RealT[this->numNodes]; + } else { + delete[] this->x1; + this->x1 = new RealT[this->numNodes]; + } + + if ( this->x2 == nullptr ) { + this->x2 = new RealT[this->numNodes]; + } else { + delete[] this->x2; + this->x2 = new RealT[this->numNodes]; + } + + if ( this->y1 == nullptr ) { + this->y1 = new RealT[this->numNodes]; + } else { + delete[] this->y1; + this->y1 = new RealT[this->numNodes]; + } + + if ( this->y2 == nullptr ) { + this->y2 = new RealT[this->numNodes]; + } else { + delete[] this->y2; + this->y2 = new RealT[this->numNodes]; + } + + if ( this->z1 == nullptr ) { + this->z1 = new RealT[this->numNodes]; + } else { + delete[] this->z1; + this->z1 = new RealT[this->numNodes]; + } + + if ( this->z2 == nullptr ) { + this->z2 = new RealT[this->numNodes]; + } else { + delete[] this->z2; + this->z2 = new RealT[this->numNodes]; + } + + if ( this->xOverlap == nullptr ) { + this->xOverlap = new RealT[this->numOverlapNodes]; + } else { + delete[] this->xOverlap; + this->xOverlap = new RealT[this->numOverlapNodes]; + } + + if ( this->yOverlap == nullptr ) { + this->yOverlap = new RealT[this->numOverlapNodes]; + } else { + delete[] this->yOverlap; + this->yOverlap = new RealT[this->numOverlapNodes]; + } + if ( this->zOverlap == nullptr ) { + this->zOverlap = new RealT[this->numOverlapNodes]; + } else { + delete[] this->zOverlap; + this->zOverlap = new RealT[this->numOverlapNodes]; + } + } + + void TearDown() override + { + if ( this->x1 != nullptr ) { + delete[] this->x1; + this->x1 = nullptr; + } + if ( this->x2 != nullptr ) { + delete[] this->x2; + this->x2 = nullptr; + } + if ( this->y1 != nullptr ) { + delete[] this->y1; + this->y1 = nullptr; + } + if ( this->y2 != nullptr ) { + delete[] this->y2; + this->y2 = nullptr; + } + if ( this->z1 != nullptr ) { + delete[] this->z1; + this->z1 = nullptr; + } + if ( this->z2 != nullptr ) { + delete[] this->z2; + this->z2 = nullptr; + } + if ( this->xOverlap != nullptr ) { + delete[] this->xOverlap; + this->xOverlap = nullptr; + } + if ( this->yOverlap != nullptr ) { + delete[] this->yOverlap; + this->yOverlap = nullptr; + } + if ( this->zOverlap != nullptr ) { + delete[] this->zOverlap; + this->zOverlap = nullptr; + } + } + + protected: + RealT* x1{ nullptr }; + RealT* y1{ nullptr }; + RealT* z1{ nullptr }; + + RealT* x2{ nullptr }; + RealT* y2{ nullptr }; + RealT* z2{ nullptr }; + + RealT* xOverlap{ nullptr }; + RealT* yOverlap{ nullptr }; + RealT* zOverlap{ nullptr }; }; TEST_F( MortarWeightTest, simple_projection ) { - - RealT* x1 = this->getXCoords(0); - RealT* y1 = this->getYCoords(0); - RealT* z1 = this->getZCoords(0); - - RealT* x2 = this->getXCoords(1); - RealT* y2 = this->getYCoords(1); - RealT* z2 = this->getZCoords(1); - - RealT* xOvrlp = this->getXOverlapCoords(); - RealT* yOvrlp = this->getYOverlapCoords(); - RealT* zOvrlp = this->getZOverlapCoords(); - - x1[0] = -1.; - x1[1] = -1.; - x1[2] = 1.; - x1[3] = 1.; - - y1[0] = 1.; - y1[1] = -1.; - y1[2] = -1.; - y1[3] = 1.; - - z1[0] = 0.1; - z1[1] = 0.1; - z1[2] = 0.1; - z1[3] = 0.1; - - x2[0] = 0.; - x2[1] = 2.; - x2[2] = 2.; - x2[3] = 0.; - - y2[0] = 0.; - y2[1] = 0.; - y2[2] = -2.; - y2[3] = -2.; - - z2[0] = 0.1; - z2[1] = 0.1; - z2[2] = 0.1; - z2[3] = 0.1; - - xOvrlp[0] = 0.; - xOvrlp[1] = 0.; - xOvrlp[2] = 1.; - xOvrlp[3] = 1.; - - yOvrlp[0] = 0.; - yOvrlp[1] = -1.; - yOvrlp[2] = -1.; - yOvrlp[3] = 0.; - - zOvrlp[0] = 0.1; - zOvrlp[1] = 0.1; - zOvrlp[2] = 0.1; - zOvrlp[3] = 0.1; - - RealT pNonmortar[4] = {0., 0., 0., 0.}; - RealT pMortar[4] = {0., 0., 0., 0}; - this->checkMortarWts( pNonmortar, pMortar ); - - // hard-code diffs for each element in pNonmortar and pMortar. - // Note, these hard coded values DEPEND on the ordering of - // the nodes in this function (above). - RealT diffNonmortar1 = std::abs(0.5625 - pNonmortar[0]); - RealT diffNonmortar2 = std::abs(0.1875 - pNonmortar[1]); - RealT diffNonmortar3 = std::abs(0.0625 - pNonmortar[2]); - RealT diffNonmortar4 = std::abs(0.1875 - pNonmortar[3]); - - RealT diffMortar1 = std::abs(0.0625 - pMortar[0]); - RealT diffMortar2 = std::abs(0.1875 - pMortar[1]); - RealT diffMortar3 = std::abs(0.5625 - pMortar[2]); - RealT diffMortar4 = std::abs(0.1875 - pMortar[3]); - - RealT tol = 1.e-8; - EXPECT_LE( diffNonmortar1, tol ); - EXPECT_LE( diffNonmortar2, tol ); - EXPECT_LE( diffNonmortar3, tol ); - EXPECT_LE( diffNonmortar4, tol ); - - EXPECT_LE( diffMortar1, tol ); - EXPECT_LE( diffMortar2, tol ); - EXPECT_LE( diffMortar3, tol ); - EXPECT_LE( diffMortar4, tol ); - + RealT* x1 = this->getXCoords( 0 ); + RealT* y1 = this->getYCoords( 0 ); + RealT* z1 = this->getZCoords( 0 ); + + RealT* x2 = this->getXCoords( 1 ); + RealT* y2 = this->getYCoords( 1 ); + RealT* z2 = this->getZCoords( 1 ); + + RealT* xOvrlp = this->getXOverlapCoords(); + RealT* yOvrlp = this->getYOverlapCoords(); + RealT* zOvrlp = this->getZOverlapCoords(); + + x1[0] = -1.; + x1[1] = -1.; + x1[2] = 1.; + x1[3] = 1.; + + y1[0] = 1.; + y1[1] = -1.; + y1[2] = -1.; + y1[3] = 1.; + + z1[0] = 0.1; + z1[1] = 0.1; + z1[2] = 0.1; + z1[3] = 0.1; + + x2[0] = 0.; + x2[1] = 2.; + x2[2] = 2.; + x2[3] = 0.; + + y2[0] = 0.; + y2[1] = 0.; + y2[2] = -2.; + y2[3] = -2.; + + z2[0] = 0.1; + z2[1] = 0.1; + z2[2] = 0.1; + z2[3] = 0.1; + + xOvrlp[0] = 0.; + xOvrlp[1] = 0.; + xOvrlp[2] = 1.; + xOvrlp[3] = 1.; + + yOvrlp[0] = 0.; + yOvrlp[1] = -1.; + yOvrlp[2] = -1.; + yOvrlp[3] = 0.; + + zOvrlp[0] = 0.1; + zOvrlp[1] = 0.1; + zOvrlp[2] = 0.1; + zOvrlp[3] = 0.1; + + RealT pNonmortar[4] = { 0., 0., 0., 0. }; + RealT pMortar[4] = { 0., 0., 0., 0 }; + this->checkMortarWts( pNonmortar, pMortar ); + + // hard-code diffs for each element in pNonmortar and pMortar. + // Note, these hard coded values DEPEND on the ordering of + // the nodes in this function (above). + RealT diffNonmortar1 = std::abs( 0.5625 - pNonmortar[0] ); + RealT diffNonmortar2 = std::abs( 0.1875 - pNonmortar[1] ); + RealT diffNonmortar3 = std::abs( 0.0625 - pNonmortar[2] ); + RealT diffNonmortar4 = std::abs( 0.1875 - pNonmortar[3] ); + + RealT diffMortar1 = std::abs( 0.0625 - pMortar[0] ); + RealT diffMortar2 = std::abs( 0.1875 - pMortar[1] ); + RealT diffMortar3 = std::abs( 0.5625 - pMortar[2] ); + RealT diffMortar4 = std::abs( 0.1875 - pMortar[3] ); + + RealT tol = 1.e-8; + EXPECT_LE( diffNonmortar1, tol ); + EXPECT_LE( diffNonmortar2, tol ); + EXPECT_LE( diffNonmortar3, tol ); + EXPECT_LE( diffNonmortar4, tol ); + + EXPECT_LE( diffMortar1, tol ); + EXPECT_LE( diffMortar2, tol ); + EXPECT_LE( diffMortar3, tol ); + EXPECT_LE( diffMortar4, tol ); } -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); axom::slic::SimpleLogger logger; diff --git a/src/tests/tribol_nodal_nrmls.cpp b/src/tests/tribol_nodal_nrmls.cpp index 709bdab9..a89e6278 100644 --- a/src/tests/tribol_nodal_nrmls.cpp +++ b/src/tests/tribol_nodal_nrmls.cpp @@ -21,157 +21,135 @@ #include "gtest/gtest.h" // c++ includes -#include // std::abs +#include // std::abs using RealT = tribol::RealT; /*! - * Test fixture class with some setup for registering a mesh + * Test fixture class with some setup for registering a mesh * and computing nodally averaged normals as used in mortar methods */ -class NodalNormalTest : public ::testing::Test -{ - -public: - int numNodes; - int dim; - - void computeNodalNormals( int cell_type, - RealT const * const x, - RealT const * const y, - RealT const * const z, - int const * const conn, - int const numCells, - int const numNodes, - int const dim ) - { - // register the mesh with tribol - const tribol::IndexT mesh_id = 0; - tribol::registerMesh( mesh_id, numCells, numNodes, - conn, cell_type, x, y, z, tribol::MemorySpace::Host ); - +class NodalNormalTest : public ::testing::Test { + public: + int numNodes; + int dim; - // get instance of mesh in order to compute nodally averaged normals - tribol::MeshManager& meshManager = tribol::MeshManager::getInstance(); - tribol::MeshData& mesh = meshManager.at( mesh_id ); + void computeNodalNormals( int cell_type, RealT const* const x, RealT const* const y, RealT const* const z, + int const* const conn, int const numCells, int const numNodes, int const dim ) + { + // register the mesh with tribol + const tribol::IndexT mesh_id = 0; + tribol::registerMesh( mesh_id, numCells, numNodes, conn, cell_type, x, y, z, tribol::MemorySpace::Host ); - // compute the face data for this mesh - mesh.computeFaceData(tribol::ExecutionMode::Sequential); + // get instance of mesh in order to compute nodally averaged normals + tribol::MeshManager& meshManager = tribol::MeshManager::getInstance(); + tribol::MeshData& mesh = meshManager.at( mesh_id ); - mesh.computeNodalNormals( dim ); + // compute the face data for this mesh + mesh.computeFaceData( tribol::ExecutionMode::Sequential ); - return; - } + mesh.computeNodalNormals( dim ); -protected: + return; + } - void SetUp() override - { - } + protected: + void SetUp() override {} - void TearDown() override - { - } - -protected: + void TearDown() override {} + protected: }; - TEST_F( NodalNormalTest, two_quad_inverted_v ) { - - // - // This test includes two, four node quadrilaterals oriented - // in an inverted V shape that is symmetric about the Z axis. - // The two faces are oriented at a 45 degree angle with respect - // to the Z axis. - // - int numFaces = 2; - int numNodesPerFace = 4; - int cellType = (int)(tribol::LINEAR_QUAD); - int conn[ numFaces * numNodesPerFace ]; - - // setup connectivity for the two faces ensuring nodes - // are ordered consistent with an outward unit normal - for (int i=0; i // std::abs +#include // std::abs using RealT = tribol::RealT; /*! - * Test fixture class; some setup is necessary to test the evaluation of - * integrals of quad 4 shape functions to compute the area of a quadrilateral + * Test fixture class; some setup is necessary to test the evaluation of + * integrals of quad 4 shape functions to compute the area of a quadrilateral * using Gauss quadrature and an isoparametric four node quad. */ -class IsoIntegTest : public ::testing::Test -{ - -public: - int numNodes; - int dim; - - RealT* getXCoords() - { - return x; - } - - RealT* getYCoords() - { - return y; - } - - RealT* getZCoords() - { - return z; - } - - bool integrate ( RealT const tol ) - { - RealT xyz[ this->dim * this->numNodes ]; - RealT* xy = xyz; - - RealT * x = this->x; - RealT * y = this->y; - RealT * z = this->z; - - // generate stacked coordinate array - for (int j=0; jnumNodes; ++j) - { - for (int k=0; kdim; ++k) - { - switch (k) - { - case 0: - xy[ this->dim * j + k ] = x[ j ]; - break; - case 1: - xy[ this->dim * j + k ] = y[ j ]; - break; - case 2: - xy[ this->dim * j + k ] = z[ j ]; - break; - } // end switch - } // end loop over dimension - } // end loop over nodes - - // instantiate SurfaceContactElem struct. Note, this object is instantiated - // using face 1 as face 2, but these faces are not used in this test so this - // is ok. - tribol::SurfaceContactElem elem ( this->dim, xy, xy, xy, - this->numNodes, this->numNodes, - nullptr, nullptr, 0, 0); - - // instantiate integration object - tribol::IntegPts integ; - - // generate all current configuration integration point coordinates and weights - tribol::GaussPolyIntQuad( elem, integ, 2 ); - - // evaluate sum_a (integral_face (phi_a) da) with outer loop over nodes, a, and - // inner loop over number of integration points - RealT areaTest = 0.; - RealT phi = 0.; - - for (int a=0; anumNodes; ++a) - { - for (int ip=0; ipnumNodes ); - - bool convrg = (std::abs(areaTest - area) <= tol) ? true : false; - - return convrg; - - } - -protected: - - void SetUp() override - { - this->numNodes = 4; - this->dim = 3; - - if (this->x == nullptr) - { - this->x = new RealT [this->numNodes]; - } - else - { - delete [] this->x; - this->x = new RealT [this->numNodes]; - } - - if (this->y == nullptr) - { - this->y = new RealT [this->numNodes]; - } - else - { - delete [] this->y; - this->y = new RealT [this->numNodes]; +class IsoIntegTest : public ::testing::Test { + public: + int numNodes; + int dim; + + RealT* getXCoords() { return x; } + + RealT* getYCoords() { return y; } + + RealT* getZCoords() { return z; } + + bool integrate( RealT const tol ) + { + RealT xyz[this->dim * this->numNodes]; + RealT* xy = xyz; + + RealT* x = this->x; + RealT* y = this->y; + RealT* z = this->z; + + // generate stacked coordinate array + for ( int j = 0; j < this->numNodes; ++j ) { + for ( int k = 0; k < this->dim; ++k ) { + switch ( k ) { + case 0: + xy[this->dim * j + k] = x[j]; + break; + case 1: + xy[this->dim * j + k] = y[j]; + break; + case 2: + xy[this->dim * j + k] = z[j]; + break; + } // end switch + } // end loop over dimension + } // end loop over nodes + + // instantiate SurfaceContactElem struct. Note, this object is instantiated + // using face 1 as face 2, but these faces are not used in this test so this + // is ok. + tribol::SurfaceContactElem elem( this->dim, xy, xy, xy, this->numNodes, this->numNodes, nullptr, nullptr, 0, 0 ); + + // instantiate integration object + tribol::IntegPts integ; + + // generate all current configuration integration point coordinates and weights + tribol::GaussPolyIntQuad( elem, integ, 2 ); + + // evaluate sum_a (integral_face (phi_a) da) with outer loop over nodes, a, and + // inner loop over number of integration points + RealT areaTest = 0.; + RealT phi = 0.; + + for ( int a = 0; a < this->numNodes; ++a ) { + for ( int ip = 0; ip < integ.numIPs; ++ip ) { + RealT xi[2]; + + // access (xi,eta) coordinates. Note that the integration point coordinates + // for this method are not using a zeta=0 component. That is, the stride is 2, + // not 3. + xi[0] = integ.xy[integ.ipDim * ip]; + xi[1] = integ.xy[integ.ipDim * ip + 1]; + + tribol::LinIsoQuadShapeFunc( xi[0], xi[1], a, phi ); + + // compute determinant of the Jacobian of the transformation + RealT dJ; + tribol::DetJQuad( xi[0], xi[1], elem.overlapCoords, elem.dim, dJ ); + + areaTest += integ.wts[ip] * phi * dJ; } - - if (this->z == nullptr) - { - this->z = new RealT [this->numNodes]; - } - else - { - delete [] this->z; - this->z = new RealT [this->numNodes]; - } - } - - void TearDown() override - { - if (this->x != nullptr) - { - delete [] this->x; - this->x = nullptr; - } - if (this->y != nullptr) - { - delete [] this->y; - this->y = nullptr; - } - if (this->z != nullptr) - { - delete [] this->z; - this->z = nullptr; - } - } - -protected: - - RealT* x {nullptr}; - RealT* y {nullptr}; - RealT* z {nullptr}; - + } + + RealT area = tribol::Area2DPolygon( x, y, this->numNodes ); + + bool convrg = ( std::abs( areaTest - area ) <= tol ) ? true : false; + + return convrg; + } + + protected: + void SetUp() override + { + this->numNodes = 4; + this->dim = 3; + + if ( this->x == nullptr ) { + this->x = new RealT[this->numNodes]; + } else { + delete[] this->x; + this->x = new RealT[this->numNodes]; + } + + if ( this->y == nullptr ) { + this->y = new RealT[this->numNodes]; + } else { + delete[] this->y; + this->y = new RealT[this->numNodes]; + } + + if ( this->z == nullptr ) { + this->z = new RealT[this->numNodes]; + } else { + delete[] this->z; + this->z = new RealT[this->numNodes]; + } + } + + void TearDown() override + { + if ( this->x != nullptr ) { + delete[] this->x; + this->x = nullptr; + } + if ( this->y != nullptr ) { + delete[] this->y; + this->y = nullptr; + } + if ( this->z != nullptr ) { + delete[] this->z; + this->z = nullptr; + } + } + + protected: + RealT* x{ nullptr }; + RealT* y{ nullptr }; + RealT* z{ nullptr }; }; - TEST_F( IsoIntegTest, square ) { + RealT* x = this->getXCoords(); + RealT* y = this->getYCoords(); + RealT* z = this->getZCoords(); - RealT* x = this->getXCoords(); - RealT* y = this->getYCoords(); - RealT* z = this->getZCoords(); - - x[0] = -0.5; - x[1] = 0.5; - x[2] = 0.5; - x[3] = -0.5; + x[0] = -0.5; + x[1] = 0.5; + x[2] = 0.5; + x[3] = -0.5; - y[0] = -0.5; - y[1] = -0.5; - y[2] = 0.5; - y[3] = 0.5; + y[0] = -0.5; + y[1] = -0.5; + y[2] = 0.5; + y[3] = 0.5; - z[0] = 0.1; - z[1] = 0.1; - z[2] = 0.1; - z[3] = 0.1; + z[0] = 0.1; + z[1] = 0.1; + z[2] = 0.1; + z[3] = 0.1; - bool convrg = this->integrate( 1.e-8 ); + bool convrg = this->integrate( 1.e-8 ); - EXPECT_EQ( convrg, true ); + EXPECT_EQ( convrg, true ); } TEST_F( IsoIntegTest, rect ) { + RealT* x = this->getXCoords(); + RealT* y = this->getYCoords(); + RealT* z = this->getZCoords(); - RealT* x = this->getXCoords(); - RealT* y = this->getYCoords(); - RealT* z = this->getZCoords(); - - x[0] = -0.5; - x[1] = 0.5; - x[2] = 0.5; - x[3] = -0.5; + x[0] = -0.5; + x[1] = 0.5; + x[2] = 0.5; + x[3] = -0.5; - y[0] = -0.25; - y[1] = -0.25; - y[2] = 0.25; - y[3] = 0.25; + y[0] = -0.25; + y[1] = -0.25; + y[2] = 0.25; + y[3] = 0.25; - z[0] = 0.1; - z[1] = 0.1; - z[2] = 0.1; - z[3] = 0.1; + z[0] = 0.1; + z[1] = 0.1; + z[2] = 0.1; + z[3] = 0.1; - bool convrg = this->integrate( 1.e-8 ); + bool convrg = this->integrate( 1.e-8 ); - EXPECT_EQ( convrg, true ); + EXPECT_EQ( convrg, true ); } TEST_F( IsoIntegTest, affine ) { - RealT* x = this->getXCoords(); - RealT* y = this->getYCoords(); - RealT* z = this->getZCoords(); + RealT* x = this->getXCoords(); + RealT* y = this->getYCoords(); + RealT* z = this->getZCoords(); - x[0] = -0.5; - x[1] = 0.5; - x[2] = 0.8; - x[3] = -0.2; + x[0] = -0.5; + x[1] = 0.5; + x[2] = 0.8; + x[3] = -0.2; - y[0] = -0.415; - y[1] = -0.415; - y[2] = 0.5; - y[3] = 0.5; + y[0] = -0.415; + y[1] = -0.415; + y[2] = 0.5; + y[3] = 0.5; - z[0] = 0.1; - z[1] = 0.1; - z[2] = 0.1; - z[3] = 0.1; + z[0] = 0.1; + z[1] = 0.1; + z[2] = 0.1; + z[3] = 0.1; - bool convrg = integrate( 1.e-5 ); + bool convrg = integrate( 1.e-5 ); - EXPECT_EQ( convrg, true ); + EXPECT_EQ( convrg, true ); } TEST_F( IsoIntegTest, nonaffine ) { - RealT* x = this->getXCoords(); - RealT* y = this->getYCoords(); - RealT* z = this->getZCoords(); + RealT* x = this->getXCoords(); + RealT* y = this->getYCoords(); + RealT* z = this->getZCoords(); - x[0] = -0.5; - x[1] = 0.5; - x[2] = 0.235; - x[3] = -0.35; + x[0] = -0.5; + x[1] = 0.5; + x[2] = 0.235; + x[3] = -0.35; - y[0] = -0.25; - y[1] = -0.15; - y[2] = 0.25; - y[3] = 0.235; + y[0] = -0.25; + y[1] = -0.15; + y[2] = 0.25; + y[3] = 0.235; - z[0] = 0.1; - z[1] = 0.1; - z[2] = 0.1; - z[3] = 0.1; + z[0] = 0.1; + z[1] = 0.1; + z[2] = 0.1; + z[3] = 0.1; - bool convrg = integrate( 1.e-8 ); + bool convrg = integrate( 1.e-8 ); - EXPECT_EQ( convrg, true ); + EXPECT_EQ( convrg, true ); } -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); axom::slic::SimpleLogger logger; diff --git a/src/tests/tribol_tet_mesh.cpp b/src/tests/tribol_tet_mesh.cpp index ecc4164c..9cf5cc84 100644 --- a/src/tests/tribol_tet_mesh.cpp +++ b/src/tests/tribol_tet_mesh.cpp @@ -15,7 +15,7 @@ #include "gtest/gtest.h" // c++ includes -#include // std::abs, std::cos, std::sin +#include // std::abs, std::cos, std::sin #include #include #include @@ -24,153 +24,136 @@ using RealT = tribol::RealT; /*! - * Test fixture class with some setup necessary to test - * the TestMesh tet mesh feature + * Test fixture class with some setup necessary to test + * the TestMesh tet mesh feature */ -class TetMeshTest : public ::testing::Test -{ - -public: - - tribol::TestMesh m_mesh; - -protected: - - void SetUp() override - { - } +class TetMeshTest : public ::testing::Test { + public: + tribol::TestMesh m_mesh; - void TearDown() override - { - this->m_mesh.clear(); - } + protected: + void SetUp() override {} -protected: + void TearDown() override { this->m_mesh.clear(); } + protected: }; TEST_F( TetMeshTest, build_and_check_tet_mesh ) { - this->m_mesh.mortarMeshId = 0; - this->m_mesh.nonmortarMeshId = 1; - - int nMortarElems = 4; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 3; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - // mesh bounding box with 0.1 interpenetration gap - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.05; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 0.95; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - this->m_mesh.setupContactMeshTet( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 5., -5. ); - - // sanity checks for this specific mesh - EXPECT_EQ( this->m_mesh.mesh_constructed, true ); - EXPECT_EQ( this->m_mesh.cellType, (int)(tribol::LINEAR_TRIANGLE) ); - EXPECT_EQ( this->m_mesh.numNodesPerFace, 3 ); - EXPECT_EQ( this->m_mesh.numNodesPerElement, 4 ); - EXPECT_EQ( this->m_mesh.numMortarElements, 6 * nMortarElems*nMortarElems*nMortarElems ); - EXPECT_EQ( this->m_mesh.numNonmortarElements, 6 * nNonmortarElems*nNonmortarElems*nNonmortarElems ); - EXPECT_EQ( this->m_mesh.numTotalElements, this->m_mesh.numMortarElements + this->m_mesh.numNonmortarElements ); - EXPECT_EQ( this->m_mesh.numMortarFaces, 2 * nMortarElems*nMortarElems ); - EXPECT_EQ( this->m_mesh.numNonmortarFaces, 2 * nNonmortarElems*nNonmortarElems ); - EXPECT_EQ( this->m_mesh.numMortarNodes, (nMortarElems+1)*(nMortarElems+1)*(nMortarElems+1) ); - EXPECT_EQ( this->m_mesh.numNonmortarNodes, (nNonmortarElems+1)*(nNonmortarElems+1)*(nNonmortarElems+1) ); - - //this->m_mesh.testMeshToVtk( "", 1, 1 ); + this->m_mesh.mortarMeshId = 0; + this->m_mesh.nonmortarMeshId = 1; + + int nMortarElems = 4; + int nElemsXM = nMortarElems; + int nElemsYM = nMortarElems; + int nElemsZM = nMortarElems; + + int nNonmortarElems = 3; + int nElemsXS = nNonmortarElems; + int nElemsYS = nNonmortarElems; + int nElemsZS = nNonmortarElems; + + // mesh bounding box with 0.1 interpenetration gap + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.05; + + RealT x_min2 = 0.; + RealT y_min2 = 0.; + RealT z_min2 = 0.95; + RealT x_max2 = 1.; + RealT y_max2 = 1.; + RealT z_max2 = 2.; + + this->m_mesh.setupContactMeshTet( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 5., + -5. ); + + // sanity checks for this specific mesh + EXPECT_EQ( this->m_mesh.mesh_constructed, true ); + EXPECT_EQ( this->m_mesh.cellType, (int)( tribol::LINEAR_TRIANGLE ) ); + EXPECT_EQ( this->m_mesh.numNodesPerFace, 3 ); + EXPECT_EQ( this->m_mesh.numNodesPerElement, 4 ); + EXPECT_EQ( this->m_mesh.numMortarElements, 6 * nMortarElems * nMortarElems * nMortarElems ); + EXPECT_EQ( this->m_mesh.numNonmortarElements, 6 * nNonmortarElems * nNonmortarElems * nNonmortarElems ); + EXPECT_EQ( this->m_mesh.numTotalElements, this->m_mesh.numMortarElements + this->m_mesh.numNonmortarElements ); + EXPECT_EQ( this->m_mesh.numMortarFaces, 2 * nMortarElems * nMortarElems ); + EXPECT_EQ( this->m_mesh.numNonmortarFaces, 2 * nNonmortarElems * nNonmortarElems ); + EXPECT_EQ( this->m_mesh.numMortarNodes, ( nMortarElems + 1 ) * ( nMortarElems + 1 ) * ( nMortarElems + 1 ) ); + EXPECT_EQ( this->m_mesh.numNonmortarNodes, + ( nNonmortarElems + 1 ) * ( nNonmortarElems + 1 ) * ( nNonmortarElems + 1 ) ); + + // this->m_mesh.testMeshToVtk( "", 1, 1 ); } TEST_F( TetMeshTest, build_and_check_mfem_tet_mesh ) { - this->m_mesh.mortarMeshId = 0; - this->m_mesh.nonmortarMeshId = 1; - - int nMortarElems = 5; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 4; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - // mesh bounding box with 0.1 interpenetration gap - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.05; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 0.95; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - this->m_mesh.setupContactMeshTet( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 5., -5. ); - - // setup mfem mesh, but don't fix orientations in order to - // check if underlying TestMesh tet mesh has orientation issues - this->m_mesh.setupMfemMesh( false ); - - // perform mfem mesh based sanity checks - EXPECT_EQ( this->m_mesh.mfem_mesh->SpaceDimension(), 3 ); - SLIC_DEBUG("number of elements: " << this->m_mesh.mfem_mesh->GetNE() << "."); - EXPECT_EQ( this->m_mesh.mfem_mesh->GetNE(), this->m_mesh.numMortarElements + this->m_mesh.numNonmortarElements ); - EXPECT_EQ( this->m_mesh.mfem_mesh->GetNV(), this->m_mesh.numMortarNodes + this->m_mesh.numNonmortarNodes ); - EXPECT_EQ( this->m_mesh.mfem_mesh->GetNFbyType(mfem::FaceType::Boundary), - 2 * 6 * (nMortarElems*nMortarElems + nNonmortarElems*nNonmortarElems) ); - - // check for inverted elements - int wrong_elem_orientation = -1; - int wrong_bdr_elem_orientation = -1; - bool fix_it = false; - wrong_elem_orientation = this->m_mesh.mfem_mesh->CheckElementOrientation( fix_it ); - wrong_bdr_elem_orientation = this->m_mesh.mfem_mesh->CheckBdrElementOrientation( fix_it ); - EXPECT_EQ( wrong_elem_orientation, 0 ); - EXPECT_EQ( wrong_bdr_elem_orientation, 0 ); - - //std::ofstream mfem_file("mfem_test_mesh.vtk"); - //this->m_mesh.mfem_mesh->PrintVTK(mfem_file); + this->m_mesh.mortarMeshId = 0; + this->m_mesh.nonmortarMeshId = 1; + + int nMortarElems = 5; + int nElemsXM = nMortarElems; + int nElemsYM = nMortarElems; + int nElemsZM = nMortarElems; + + int nNonmortarElems = 4; + int nElemsXS = nNonmortarElems; + int nElemsYS = nNonmortarElems; + int nElemsZS = nNonmortarElems; + + // mesh bounding box with 0.1 interpenetration gap + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.05; + + RealT x_min2 = 0.; + RealT y_min2 = 0.; + RealT z_min2 = 0.95; + RealT x_max2 = 1.; + RealT y_max2 = 1.; + RealT z_max2 = 2.; + + this->m_mesh.setupContactMeshTet( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 5., + -5. ); + + // setup mfem mesh, but don't fix orientations in order to + // check if underlying TestMesh tet mesh has orientation issues + this->m_mesh.setupMfemMesh( false ); + + // perform mfem mesh based sanity checks + EXPECT_EQ( this->m_mesh.mfem_mesh->SpaceDimension(), 3 ); + SLIC_DEBUG( "number of elements: " << this->m_mesh.mfem_mesh->GetNE() << "." ); + EXPECT_EQ( this->m_mesh.mfem_mesh->GetNE(), this->m_mesh.numMortarElements + this->m_mesh.numNonmortarElements ); + EXPECT_EQ( this->m_mesh.mfem_mesh->GetNV(), this->m_mesh.numMortarNodes + this->m_mesh.numNonmortarNodes ); + EXPECT_EQ( this->m_mesh.mfem_mesh->GetNFbyType( mfem::FaceType::Boundary ), + 2 * 6 * ( nMortarElems * nMortarElems + nNonmortarElems * nNonmortarElems ) ); + + // check for inverted elements + int wrong_elem_orientation = -1; + int wrong_bdr_elem_orientation = -1; + bool fix_it = false; + wrong_elem_orientation = this->m_mesh.mfem_mesh->CheckElementOrientation( fix_it ); + wrong_bdr_elem_orientation = this->m_mesh.mfem_mesh->CheckBdrElementOrientation( fix_it ); + EXPECT_EQ( wrong_elem_orientation, 0 ); + EXPECT_EQ( wrong_bdr_elem_orientation, 0 ); + + // std::ofstream mfem_file("mfem_test_mesh.vtk"); + // this->m_mesh.mfem_mesh->PrintVTK(mfem_file); } -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); axom::slic::SimpleLogger logger; result = RUN_ALL_TESTS(); diff --git a/src/tests/tribol_timestep_vote.cpp b/src/tests/tribol_timestep_vote.cpp index ad46fc5b..3c663276 100644 --- a/src/tests/tribol_timestep_vote.cpp +++ b/src/tests/tribol_timestep_vote.cpp @@ -27,7 +27,7 @@ #include "gtest/gtest.h" // c++ includes -#include // std::abs, std::cos, std::sin +#include // std::abs, std::cos, std::sin #include #include #include @@ -36,954 +36,896 @@ using RealT = tribol::RealT; /*! - * Test fixture class with some setup necessary to test - * the COMMON_PLANE + PENALTY implementation with registered + * Test fixture class with some setup necessary to test + * the COMMON_PLANE + PENALTY implementation with registered * velocities and timestep vote */ -class CommonPlaneTest : public ::testing::Test -{ - -public: - - tribol::TestMesh m_mesh; - -protected: - - void SetUp() override - { - } +class CommonPlaneTest : public ::testing::Test { + public: + tribol::TestMesh m_mesh; - void TearDown() override - { - // call clear() on mesh object to be safe - this->m_mesh.clear(); - } + protected: + void SetUp() override {} -protected: + void TearDown() override + { + // call clear() on mesh object to be safe + this->m_mesh.clear(); + } + protected: }; TEST_F( CommonPlaneTest, zero_velocity_small_gap ) { - // This test uses a small gap with zero velocity to test both the - // timestep vote gap check and velocity check with no resulting - // change to dt - this->m_mesh.mortarMeshId = 0; - this->m_mesh.nonmortarMeshId = 1; - - int nMortarElems = 4; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 5; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - // mesh bounding box with 'small' (<30% element thickness) interpenetration gap - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.005; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 0.95; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - // compute element thickness for each block - RealT element_thickness1 = (z_max1 - z_min1) / nElemsZM; - RealT element_thickness2 = (z_max2 - z_min2) / nElemsZS; - - // setup mesh - this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0. ); - - // specify dt and component velocities for each block. - // Large velocity in the z-direction will incite a change in the - // timestep. This velocity is computed on the high side using the - // hardcoded rule that one face cannot interpen the other - // exceeding 30% of the other's element thickness. - RealT dt = 1.0; - RealT bulk_mod1 = 1.0; // something simple - RealT bulk_mod2 = 1.0; - RealT velX1 = 0.; - RealT velY1 = 0.; - RealT velZ1 = 0.; - RealT velX2 = 0.; - RealT velY2 = 0.; - RealT velZ2 = 0.; - - this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); - this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, -velZ2 ); - - // allocate and set element thickness and bulk modulus - this->m_mesh.allocateAndSetElementThickness( m_mesh.mortarMeshId, element_thickness1 ); - this->m_mesh.allocateAndSetBulkModulus( m_mesh.mortarMeshId, bulk_mod1 ); - this->m_mesh.allocateAndSetElementThickness( m_mesh.nonmortarMeshId, element_thickness2 ); - this->m_mesh.allocateAndSetBulkModulus( m_mesh.nonmortarMeshId, bulk_mod2 ); - - // call tribol setup and update - tribol::TestControlParameters parameters; - parameters.penalty_ratio = true; - parameters.const_penalty = 0.75; - parameters.dt = dt; - parameters.enable_timestep_vote = true; - parameters.timestep_pen_frac = 0.3; - - int test_mesh_update_err = - this->m_mesh.tribolSetupAndUpdate( tribol::COMMON_PLANE, tribol::PENALTY, - tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); - - EXPECT_EQ( test_mesh_update_err, 0 ); - EXPECT_EQ( parameters.dt, dt ); - - tribol::finalize(); + // This test uses a small gap with zero velocity to test both the + // timestep vote gap check and velocity check with no resulting + // change to dt + this->m_mesh.mortarMeshId = 0; + this->m_mesh.nonmortarMeshId = 1; + + int nMortarElems = 4; + int nElemsXM = nMortarElems; + int nElemsYM = nMortarElems; + int nElemsZM = nMortarElems; + + int nNonmortarElems = 5; + int nElemsXS = nNonmortarElems; + int nElemsYS = nNonmortarElems; + int nElemsZS = nNonmortarElems; + + // mesh bounding box with 'small' (<30% element thickness) interpenetration gap + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.005; + + RealT x_min2 = 0.; + RealT y_min2 = 0.; + RealT z_min2 = 0.95; + RealT x_max2 = 1.; + RealT y_max2 = 1.; + RealT z_max2 = 2.; + + // compute element thickness for each block + RealT element_thickness1 = ( z_max1 - z_min1 ) / nElemsZM; + RealT element_thickness2 = ( z_max2 - z_min2 ) / nElemsZS; + + // setup mesh + this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., + 0. ); + + // specify dt and component velocities for each block. + // Large velocity in the z-direction will incite a change in the + // timestep. This velocity is computed on the high side using the + // hardcoded rule that one face cannot interpen the other + // exceeding 30% of the other's element thickness. + RealT dt = 1.0; + RealT bulk_mod1 = 1.0; // something simple + RealT bulk_mod2 = 1.0; + RealT velX1 = 0.; + RealT velY1 = 0.; + RealT velZ1 = 0.; + RealT velX2 = 0.; + RealT velY2 = 0.; + RealT velZ2 = 0.; + + this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); + this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, -velZ2 ); + + // allocate and set element thickness and bulk modulus + this->m_mesh.allocateAndSetElementThickness( m_mesh.mortarMeshId, element_thickness1 ); + this->m_mesh.allocateAndSetBulkModulus( m_mesh.mortarMeshId, bulk_mod1 ); + this->m_mesh.allocateAndSetElementThickness( m_mesh.nonmortarMeshId, element_thickness2 ); + this->m_mesh.allocateAndSetBulkModulus( m_mesh.nonmortarMeshId, bulk_mod2 ); + + // call tribol setup and update + tribol::TestControlParameters parameters; + parameters.penalty_ratio = true; + parameters.const_penalty = 0.75; + parameters.dt = dt; + parameters.enable_timestep_vote = true; + parameters.timestep_pen_frac = 0.3; + + int test_mesh_update_err = this->m_mesh.tribolSetupAndUpdate( + tribol::COMMON_PLANE, tribol::PENALTY, tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); + + EXPECT_EQ( test_mesh_update_err, 0 ); + EXPECT_EQ( parameters.dt, dt ); + + tribol::finalize(); } TEST_F( CommonPlaneTest, large_velocity_small_gap_dt_not_enabled ) { - // This test has a small gap and large interpen velocity, but does not - // call the API function to return a timestep vote. As such, we expect - // the timestep to be unchanged - this->m_mesh.mortarMeshId = 0; - this->m_mesh.nonmortarMeshId = 1; - - int nMortarElems = 4; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 5; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - // mesh bounding box with 'small' (0.055) interpenetration gap - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.005; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 0.95; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - // compute element thickness for each block - RealT element_thickness1 = (z_max1 - z_min1) / nElemsZM; - RealT element_thickness2 = (z_max2 - z_min2) / nElemsZS; - - // setup mesh - this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0. ); - - // specify dt and component velocities for each block. - // Large velocity in the z-direction will incite a change in the - // timestep. This velocity is computed on the high side using the - // hardcoded rule that one face cannot interpen the other - // exceeding 30% of the other's element thickness. - RealT dt = 1.0; - RealT bulk_mod1 = 1.0; - RealT bulk_mod2 = 1.0; - RealT vel_factor = 100.; - RealT velX1 = 0.; - RealT velY1 = 0.; - RealT velZ1 = vel_factor * 0.3 * element_thickness1 / dt; - RealT velX2 = 0.; - RealT velY2 = 0.; - RealT velZ2 = vel_factor * 0.3 * element_thickness2 / dt; - - this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); - this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, -velZ2 ); - - // allocate and set element thickness and bulk modulus - this->m_mesh.allocateAndSetElementThickness( m_mesh.mortarMeshId, element_thickness1 ); - this->m_mesh.allocateAndSetBulkModulus( m_mesh.mortarMeshId, bulk_mod1 ); - this->m_mesh.allocateAndSetElementThickness( m_mesh.nonmortarMeshId, element_thickness2 ); - this->m_mesh.allocateAndSetBulkModulus( m_mesh.nonmortarMeshId, bulk_mod2 ); - - // call tribol setup and update - tribol::TestControlParameters parameters; - parameters.penalty_ratio = true; - parameters.const_penalty = 0.75; - parameters.dt = dt; - parameters.enable_timestep_vote = false; - parameters.timestep_pen_frac = 0.3; - - int test_mesh_update_err = - this->m_mesh.tribolSetupAndUpdate( tribol::COMMON_PLANE, tribol::PENALTY, - tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); - - EXPECT_EQ( test_mesh_update_err, 0 ); - EXPECT_EQ( parameters.dt, dt); - - - tribol::finalize(); + // This test has a small gap and large interpen velocity, but does not + // call the API function to return a timestep vote. As such, we expect + // the timestep to be unchanged + this->m_mesh.mortarMeshId = 0; + this->m_mesh.nonmortarMeshId = 1; + + int nMortarElems = 4; + int nElemsXM = nMortarElems; + int nElemsYM = nMortarElems; + int nElemsZM = nMortarElems; + + int nNonmortarElems = 5; + int nElemsXS = nNonmortarElems; + int nElemsYS = nNonmortarElems; + int nElemsZS = nNonmortarElems; + + // mesh bounding box with 'small' (0.055) interpenetration gap + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.005; + + RealT x_min2 = 0.; + RealT y_min2 = 0.; + RealT z_min2 = 0.95; + RealT x_max2 = 1.; + RealT y_max2 = 1.; + RealT z_max2 = 2.; + + // compute element thickness for each block + RealT element_thickness1 = ( z_max1 - z_min1 ) / nElemsZM; + RealT element_thickness2 = ( z_max2 - z_min2 ) / nElemsZS; + + // setup mesh + this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., + 0. ); + + // specify dt and component velocities for each block. + // Large velocity in the z-direction will incite a change in the + // timestep. This velocity is computed on the high side using the + // hardcoded rule that one face cannot interpen the other + // exceeding 30% of the other's element thickness. + RealT dt = 1.0; + RealT bulk_mod1 = 1.0; + RealT bulk_mod2 = 1.0; + RealT vel_factor = 100.; + RealT velX1 = 0.; + RealT velY1 = 0.; + RealT velZ1 = vel_factor * 0.3 * element_thickness1 / dt; + RealT velX2 = 0.; + RealT velY2 = 0.; + RealT velZ2 = vel_factor * 0.3 * element_thickness2 / dt; + + this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); + this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, -velZ2 ); + + // allocate and set element thickness and bulk modulus + this->m_mesh.allocateAndSetElementThickness( m_mesh.mortarMeshId, element_thickness1 ); + this->m_mesh.allocateAndSetBulkModulus( m_mesh.mortarMeshId, bulk_mod1 ); + this->m_mesh.allocateAndSetElementThickness( m_mesh.nonmortarMeshId, element_thickness2 ); + this->m_mesh.allocateAndSetBulkModulus( m_mesh.nonmortarMeshId, bulk_mod2 ); + + // call tribol setup and update + tribol::TestControlParameters parameters; + parameters.penalty_ratio = true; + parameters.const_penalty = 0.75; + parameters.dt = dt; + parameters.enable_timestep_vote = false; + parameters.timestep_pen_frac = 0.3; + + int test_mesh_update_err = this->m_mesh.tribolSetupAndUpdate( + tribol::COMMON_PLANE, tribol::PENALTY, tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); + + EXPECT_EQ( test_mesh_update_err, 0 ); + EXPECT_EQ( parameters.dt, dt ); + + tribol::finalize(); } TEST_F( CommonPlaneTest, numerically_zero_velocity_small_gap ) { - // this test is meant to test tolerancing in the timestep calculation - // when numerically zero velocities are present. This test caught a - // negative dt estimate bug that has since been fixed. - // - // Additionally, this test checks the case with 'zero' velocity and - // a small gap resulting in no timestep change - this->m_mesh.mortarMeshId = 0; - this->m_mesh.nonmortarMeshId = 1; - - int nMortarElems = 4; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 5; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - // mesh bounding box with 'small' (0.055) interpenetration gap - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.005; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 0.95; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - // compute element thickness for each block - RealT element_thickness1 = (z_max1 - z_min1) / nElemsZM; - RealT element_thickness2 = (z_max2 - z_min2) / nElemsZS; - - // setup mesh - this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0. ); - - // specify dt and component velocities for each block. - // Large velocity in the z-direction will incite a change in the - // timestep. This velocity is computed on the high side using the - // hardcoded rule that one face cannot interpen the other - // exceeding 30% of the other's element thickness. - RealT dt = 1.0; - RealT bulk_mod1 = 1.0; // something simple - RealT bulk_mod2 = 1.0; - RealT velX1 = 1.e-12; - RealT velY1 = 1.e-12; - RealT velZ1 = 1.e-12; - RealT velX2 = -1.e-12; - RealT velY2 = -1.e-12; - RealT velZ2 = -1.e-12; - - this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); - this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, -velZ2 ); - - // allocate and set element thickness and bulk modulus - this->m_mesh.allocateAndSetElementThickness( m_mesh.mortarMeshId, element_thickness1 ); - this->m_mesh.allocateAndSetBulkModulus( m_mesh.mortarMeshId, bulk_mod1 ); - this->m_mesh.allocateAndSetElementThickness( m_mesh.nonmortarMeshId, element_thickness2 ); - this->m_mesh.allocateAndSetBulkModulus( m_mesh.nonmortarMeshId, bulk_mod2 ); - - // call tribol setup and update - tribol::TestControlParameters parameters; - parameters.penalty_ratio = true; - parameters.const_penalty = 0.75; - parameters.dt = dt; - parameters.enable_timestep_vote = true; - parameters.timestep_pen_frac = 0.3; - - int test_mesh_update_err = - this->m_mesh.tribolSetupAndUpdate( tribol::COMMON_PLANE, tribol::PENALTY, - tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); - - EXPECT_EQ( test_mesh_update_err, 0 ); - - // expect no change in dt - EXPECT_EQ( parameters.dt, dt ); - - tribol::finalize(); + // this test is meant to test tolerancing in the timestep calculation + // when numerically zero velocities are present. This test caught a + // negative dt estimate bug that has since been fixed. + // + // Additionally, this test checks the case with 'zero' velocity and + // a small gap resulting in no timestep change + this->m_mesh.mortarMeshId = 0; + this->m_mesh.nonmortarMeshId = 1; + + int nMortarElems = 4; + int nElemsXM = nMortarElems; + int nElemsYM = nMortarElems; + int nElemsZM = nMortarElems; + + int nNonmortarElems = 5; + int nElemsXS = nNonmortarElems; + int nElemsYS = nNonmortarElems; + int nElemsZS = nNonmortarElems; + + // mesh bounding box with 'small' (0.055) interpenetration gap + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.005; + + RealT x_min2 = 0.; + RealT y_min2 = 0.; + RealT z_min2 = 0.95; + RealT x_max2 = 1.; + RealT y_max2 = 1.; + RealT z_max2 = 2.; + + // compute element thickness for each block + RealT element_thickness1 = ( z_max1 - z_min1 ) / nElemsZM; + RealT element_thickness2 = ( z_max2 - z_min2 ) / nElemsZS; + + // setup mesh + this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., + 0. ); + + // specify dt and component velocities for each block. + // Large velocity in the z-direction will incite a change in the + // timestep. This velocity is computed on the high side using the + // hardcoded rule that one face cannot interpen the other + // exceeding 30% of the other's element thickness. + RealT dt = 1.0; + RealT bulk_mod1 = 1.0; // something simple + RealT bulk_mod2 = 1.0; + RealT velX1 = 1.e-12; + RealT velY1 = 1.e-12; + RealT velZ1 = 1.e-12; + RealT velX2 = -1.e-12; + RealT velY2 = -1.e-12; + RealT velZ2 = -1.e-12; + + this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); + this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, -velZ2 ); + + // allocate and set element thickness and bulk modulus + this->m_mesh.allocateAndSetElementThickness( m_mesh.mortarMeshId, element_thickness1 ); + this->m_mesh.allocateAndSetBulkModulus( m_mesh.mortarMeshId, bulk_mod1 ); + this->m_mesh.allocateAndSetElementThickness( m_mesh.nonmortarMeshId, element_thickness2 ); + this->m_mesh.allocateAndSetBulkModulus( m_mesh.nonmortarMeshId, bulk_mod2 ); + + // call tribol setup and update + tribol::TestControlParameters parameters; + parameters.penalty_ratio = true; + parameters.const_penalty = 0.75; + parameters.dt = dt; + parameters.enable_timestep_vote = true; + parameters.timestep_pen_frac = 0.3; + + int test_mesh_update_err = this->m_mesh.tribolSetupAndUpdate( + tribol::COMMON_PLANE, tribol::PENALTY, tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); + + EXPECT_EQ( test_mesh_update_err, 0 ); + + // expect no change in dt + EXPECT_EQ( parameters.dt, dt ); + + tribol::finalize(); } TEST_F( CommonPlaneTest, zero_velocity_large_gap ) { - // This is a tricky test where even in the presence of a large gap, there - // is no change to the timestep because with zero velocity, no reduction in the - // timestep via a contact timestep vote could reduce the amount of interpen - this->m_mesh.mortarMeshId = 0; - this->m_mesh.nonmortarMeshId = 1; - - int nMortarElems = 4; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 4; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - // mesh bounding box with >= 30% element thickness interpenetration gap - RealT interpen_gap = 0.3 * 1.0 / nMortarElems; - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.0 + 1.05*interpen_gap; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 1.0 - 1.05*interpen_gap; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - // compute element thickness for each block - RealT element_thickness1 = (z_max1 - z_min1) / nElemsZM; - RealT element_thickness2 = (z_max2 - z_min2) / nElemsZS; - - // setup mesh - this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0. ); - - // specify dt and component velocities for each block. - // Large velocity in the z-direction will incite a change in the - // timestep. This velocity is computed on the high side using the - // hardcoded rule that one face cannot interpen the other - // exceeding 30% of the other's element thickness. - RealT dt = 1.0; - RealT bulk_mod1 = 1.0; - RealT bulk_mod2 = 1.0; - RealT velX1 = 0.; - RealT velY1 = 0.; - RealT velZ1 = 0.; - RealT velX2 = 0.; - RealT velY2 = 0.; - RealT velZ2 = 0.; - - this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); - this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, -velZ2 ); - - // allocate and set element thickness and bulk modulus - this->m_mesh.allocateAndSetElementThickness( m_mesh.mortarMeshId, element_thickness1 ); - this->m_mesh.allocateAndSetBulkModulus( m_mesh.mortarMeshId, bulk_mod1 ); - this->m_mesh.allocateAndSetElementThickness( m_mesh.nonmortarMeshId, element_thickness2 ); - this->m_mesh.allocateAndSetBulkModulus( m_mesh.nonmortarMeshId, bulk_mod2 ); - - // call tribol setup and update - tribol::TestControlParameters parameters; - parameters.penalty_ratio = true; - parameters.const_penalty = 0.75; - parameters.dt = dt; - parameters.enable_timestep_vote = true; - parameters.timestep_pen_frac = 0.3; - - int test_mesh_update_err = - this->m_mesh.tribolSetupAndUpdate( tribol::COMMON_PLANE, tribol::PENALTY, - tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); - - EXPECT_EQ( test_mesh_update_err, 0 ); - // note that with very small velocity, the dt estimate will be - // very large, and won't change the timestep, even if the gap is large. This - // allows for a soft contact response in the presence of a small velocity that - // won't actually correct too much interpen with a contact dt vote - EXPECT_EQ( parameters.dt, dt ); - - tribol::finalize(); + // This is a tricky test where even in the presence of a large gap, there + // is no change to the timestep because with zero velocity, no reduction in the + // timestep via a contact timestep vote could reduce the amount of interpen + this->m_mesh.mortarMeshId = 0; + this->m_mesh.nonmortarMeshId = 1; + + int nMortarElems = 4; + int nElemsXM = nMortarElems; + int nElemsYM = nMortarElems; + int nElemsZM = nMortarElems; + + int nNonmortarElems = 4; + int nElemsXS = nNonmortarElems; + int nElemsYS = nNonmortarElems; + int nElemsZS = nNonmortarElems; + + // mesh bounding box with >= 30% element thickness interpenetration gap + RealT interpen_gap = 0.3 * 1.0 / nMortarElems; + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.0 + 1.05 * interpen_gap; + + RealT x_min2 = 0.; + RealT y_min2 = 0.; + RealT z_min2 = 1.0 - 1.05 * interpen_gap; + RealT x_max2 = 1.; + RealT y_max2 = 1.; + RealT z_max2 = 2.; + + // compute element thickness for each block + RealT element_thickness1 = ( z_max1 - z_min1 ) / nElemsZM; + RealT element_thickness2 = ( z_max2 - z_min2 ) / nElemsZS; + + // setup mesh + this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., + 0. ); + + // specify dt and component velocities for each block. + // Large velocity in the z-direction will incite a change in the + // timestep. This velocity is computed on the high side using the + // hardcoded rule that one face cannot interpen the other + // exceeding 30% of the other's element thickness. + RealT dt = 1.0; + RealT bulk_mod1 = 1.0; + RealT bulk_mod2 = 1.0; + RealT velX1 = 0.; + RealT velY1 = 0.; + RealT velZ1 = 0.; + RealT velX2 = 0.; + RealT velY2 = 0.; + RealT velZ2 = 0.; + + this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); + this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, -velZ2 ); + + // allocate and set element thickness and bulk modulus + this->m_mesh.allocateAndSetElementThickness( m_mesh.mortarMeshId, element_thickness1 ); + this->m_mesh.allocateAndSetBulkModulus( m_mesh.mortarMeshId, bulk_mod1 ); + this->m_mesh.allocateAndSetElementThickness( m_mesh.nonmortarMeshId, element_thickness2 ); + this->m_mesh.allocateAndSetBulkModulus( m_mesh.nonmortarMeshId, bulk_mod2 ); + + // call tribol setup and update + tribol::TestControlParameters parameters; + parameters.penalty_ratio = true; + parameters.const_penalty = 0.75; + parameters.dt = dt; + parameters.enable_timestep_vote = true; + parameters.timestep_pen_frac = 0.3; + + int test_mesh_update_err = this->m_mesh.tribolSetupAndUpdate( + tribol::COMMON_PLANE, tribol::PENALTY, tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); + + EXPECT_EQ( test_mesh_update_err, 0 ); + // note that with very small velocity, the dt estimate will be + // very large, and won't change the timestep, even if the gap is large. This + // allows for a soft contact response in the presence of a small velocity that + // won't actually correct too much interpen with a contact dt vote + EXPECT_EQ( parameters.dt, dt ); + + tribol::finalize(); } TEST_F( CommonPlaneTest, large_velocity_small_gap ) { - // This test does call the timestep enabling API function and uses a large velocity - // and small gap to check that the timestep velocity check triggers a change in dt - this->m_mesh.mortarMeshId = 0; - this->m_mesh.nonmortarMeshId = 1; - - int nMortarElems = 4; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 4; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - // mesh bounding box with 'small' (0.055) interpenetration gap - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.005; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 0.95; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - // compute element thickness for each block - RealT element_thickness1 = (z_max1 - z_min1) / nElemsZM; - RealT element_thickness2 = (z_max2 - z_min2) / nElemsZS; - - // setup mesh - this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0. ); - - // specify dt and component velocities for each block. - // Large velocity in the z-direction will incite a change in the - // timestep. This velocity is computed on the high side using the - // hardcoded rule that one face cannot interpen the other - // exceeding 30% of the other's element thickness. - RealT dt = 1.0; - RealT bulk_mod1 = 1.0; - RealT bulk_mod2 = 1.0; - RealT vel_factor = 100.; - RealT velX1 = 0.; - RealT velY1 = 0.; - RealT velZ1 = vel_factor * 0.3 * element_thickness1 / dt; - RealT velX2 = 0.; - RealT velY2 = 0.; - RealT velZ2 = vel_factor * 0.3 * element_thickness2 / dt; - - this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); - this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, -velZ2 ); - - // allocate and set element thickness and bulk modulus - this->m_mesh.allocateAndSetElementThickness( m_mesh.mortarMeshId, element_thickness1 ); - this->m_mesh.allocateAndSetBulkModulus( m_mesh.mortarMeshId, bulk_mod1 ); - this->m_mesh.allocateAndSetElementThickness( m_mesh.nonmortarMeshId, element_thickness2 ); - this->m_mesh.allocateAndSetBulkModulus( m_mesh.nonmortarMeshId, bulk_mod2 ); - - // call tribol setup and update - tribol::TestControlParameters parameters; - parameters.penalty_ratio = true; - parameters.const_penalty = 0.75; - parameters.dt = dt; - parameters.enable_timestep_vote = true; - parameters.timestep_pen_frac = 0.3; - - int test_mesh_update_err = - this->m_mesh.tribolSetupAndUpdate( tribol::COMMON_PLANE, tribol::PENALTY, - tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); - - EXPECT_EQ( test_mesh_update_err, 0 ); - - RealT dt_vote = parameters.timestep_pen_frac*element_thickness1/velZ1; - RealT dt_diff = std::abs(parameters.dt - dt_vote); - RealT dt_tol = 1.e-8; - EXPECT_LT( dt_diff, dt_tol ); - - tribol::finalize(); + // This test does call the timestep enabling API function and uses a large velocity + // and small gap to check that the timestep velocity check triggers a change in dt + this->m_mesh.mortarMeshId = 0; + this->m_mesh.nonmortarMeshId = 1; + + int nMortarElems = 4; + int nElemsXM = nMortarElems; + int nElemsYM = nMortarElems; + int nElemsZM = nMortarElems; + + int nNonmortarElems = 4; + int nElemsXS = nNonmortarElems; + int nElemsYS = nNonmortarElems; + int nElemsZS = nNonmortarElems; + + // mesh bounding box with 'small' (0.055) interpenetration gap + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.005; + + RealT x_min2 = 0.; + RealT y_min2 = 0.; + RealT z_min2 = 0.95; + RealT x_max2 = 1.; + RealT y_max2 = 1.; + RealT z_max2 = 2.; + + // compute element thickness for each block + RealT element_thickness1 = ( z_max1 - z_min1 ) / nElemsZM; + RealT element_thickness2 = ( z_max2 - z_min2 ) / nElemsZS; + + // setup mesh + this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., + 0. ); + + // specify dt and component velocities for each block. + // Large velocity in the z-direction will incite a change in the + // timestep. This velocity is computed on the high side using the + // hardcoded rule that one face cannot interpen the other + // exceeding 30% of the other's element thickness. + RealT dt = 1.0; + RealT bulk_mod1 = 1.0; + RealT bulk_mod2 = 1.0; + RealT vel_factor = 100.; + RealT velX1 = 0.; + RealT velY1 = 0.; + RealT velZ1 = vel_factor * 0.3 * element_thickness1 / dt; + RealT velX2 = 0.; + RealT velY2 = 0.; + RealT velZ2 = vel_factor * 0.3 * element_thickness2 / dt; + + this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); + this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, -velZ2 ); + + // allocate and set element thickness and bulk modulus + this->m_mesh.allocateAndSetElementThickness( m_mesh.mortarMeshId, element_thickness1 ); + this->m_mesh.allocateAndSetBulkModulus( m_mesh.mortarMeshId, bulk_mod1 ); + this->m_mesh.allocateAndSetElementThickness( m_mesh.nonmortarMeshId, element_thickness2 ); + this->m_mesh.allocateAndSetBulkModulus( m_mesh.nonmortarMeshId, bulk_mod2 ); + + // call tribol setup and update + tribol::TestControlParameters parameters; + parameters.penalty_ratio = true; + parameters.const_penalty = 0.75; + parameters.dt = dt; + parameters.enable_timestep_vote = true; + parameters.timestep_pen_frac = 0.3; + + int test_mesh_update_err = this->m_mesh.tribolSetupAndUpdate( + tribol::COMMON_PLANE, tribol::PENALTY, tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); + + EXPECT_EQ( test_mesh_update_err, 0 ); + + RealT dt_vote = parameters.timestep_pen_frac * element_thickness1 / velZ1; + RealT dt_diff = std::abs( parameters.dt - dt_vote ); + RealT dt_tol = 1.e-8; + EXPECT_LT( dt_diff, dt_tol ); + + tribol::finalize(); } TEST_F( CommonPlaneTest, large_velocity_large_gap ) { - // This test uses a large velocity AND large gap to ensure that - // the minimum of both timestep checks modify the dt. If the gap - // governs, then a finite velocity means that a gap dt can control - this->m_mesh.mortarMeshId = 0; - this->m_mesh.nonmortarMeshId = 1; - - int nMortarElems = 4; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 4; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - // mesh bounding box with >= 30% element thickness interpenetration gap - RealT interpen_gap = 0.3 * 1.0 / nMortarElems; - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.0 + 1.05*interpen_gap; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 1.0 - 1.05*interpen_gap; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - // compute element thickness for each block - RealT element_thickness1 = (z_max1 - z_min1) / nElemsZM; - RealT element_thickness2 = (z_max2 - z_min2) / nElemsZS; - - // setup mesh - this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0. ); - - // specify dt and component velocities for each block. - // Large velocity in the z-direction will incite a change in the - // timestep. This velocity is computed on the high side using the - // hardcoded rule that one face cannot interpen the other - // exceeding 30% of the other's element thickness. - RealT dt = 1.0; - RealT bulk_mod1 = 1.0; - RealT bulk_mod2 = 1.0; - RealT vel_factor = 100.; - RealT velX1 = 0.; - RealT velY1 = 0.; - RealT velZ1 = vel_factor * 0.3 * element_thickness1 / dt; - RealT velX2 = 0.; - RealT velY2 = 0.; - RealT velZ2 = vel_factor * 0.3 * element_thickness2 / dt; - - this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); - this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, -velZ2 ); - - // allocate and set element thickness and bulk modulus - this->m_mesh.allocateAndSetElementThickness( m_mesh.mortarMeshId, element_thickness1 ); - this->m_mesh.allocateAndSetBulkModulus( m_mesh.mortarMeshId, bulk_mod1 ); - this->m_mesh.allocateAndSetElementThickness( m_mesh.nonmortarMeshId, element_thickness2 ); - this->m_mesh.allocateAndSetBulkModulus( m_mesh.nonmortarMeshId, bulk_mod2 ); - - // call tribol setup and update - tribol::TestControlParameters parameters; - parameters.penalty_ratio = true; - parameters.const_penalty = 0.75; - parameters.dt = dt; - parameters.enable_timestep_vote = true; - parameters.timestep_pen_frac = 0.3; - - int test_mesh_update_err = - this->m_mesh.tribolSetupAndUpdate( tribol::COMMON_PLANE, tribol::PENALTY, - tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); - - EXPECT_EQ( test_mesh_update_err, 0 ); - - RealT dt_vote = parameters.timestep_pen_frac*element_thickness1/velZ1; - RealT dt_diff = std::abs(parameters.dt - dt_vote); - RealT dt_tol = 1.e-8; - EXPECT_LT( dt_diff, dt_tol ); - - tribol::finalize(); + // This test uses a large velocity AND large gap to ensure that + // the minimum of both timestep checks modify the dt. If the gap + // governs, then a finite velocity means that a gap dt can control + this->m_mesh.mortarMeshId = 0; + this->m_mesh.nonmortarMeshId = 1; + + int nMortarElems = 4; + int nElemsXM = nMortarElems; + int nElemsYM = nMortarElems; + int nElemsZM = nMortarElems; + + int nNonmortarElems = 4; + int nElemsXS = nNonmortarElems; + int nElemsYS = nNonmortarElems; + int nElemsZS = nNonmortarElems; + + // mesh bounding box with >= 30% element thickness interpenetration gap + RealT interpen_gap = 0.3 * 1.0 / nMortarElems; + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.0 + 1.05 * interpen_gap; + + RealT x_min2 = 0.; + RealT y_min2 = 0.; + RealT z_min2 = 1.0 - 1.05 * interpen_gap; + RealT x_max2 = 1.; + RealT y_max2 = 1.; + RealT z_max2 = 2.; + + // compute element thickness for each block + RealT element_thickness1 = ( z_max1 - z_min1 ) / nElemsZM; + RealT element_thickness2 = ( z_max2 - z_min2 ) / nElemsZS; + + // setup mesh + this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., + 0. ); + + // specify dt and component velocities for each block. + // Large velocity in the z-direction will incite a change in the + // timestep. This velocity is computed on the high side using the + // hardcoded rule that one face cannot interpen the other + // exceeding 30% of the other's element thickness. + RealT dt = 1.0; + RealT bulk_mod1 = 1.0; + RealT bulk_mod2 = 1.0; + RealT vel_factor = 100.; + RealT velX1 = 0.; + RealT velY1 = 0.; + RealT velZ1 = vel_factor * 0.3 * element_thickness1 / dt; + RealT velX2 = 0.; + RealT velY2 = 0.; + RealT velZ2 = vel_factor * 0.3 * element_thickness2 / dt; + + this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); + this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, -velZ2 ); + + // allocate and set element thickness and bulk modulus + this->m_mesh.allocateAndSetElementThickness( m_mesh.mortarMeshId, element_thickness1 ); + this->m_mesh.allocateAndSetBulkModulus( m_mesh.mortarMeshId, bulk_mod1 ); + this->m_mesh.allocateAndSetElementThickness( m_mesh.nonmortarMeshId, element_thickness2 ); + this->m_mesh.allocateAndSetBulkModulus( m_mesh.nonmortarMeshId, bulk_mod2 ); + + // call tribol setup and update + tribol::TestControlParameters parameters; + parameters.penalty_ratio = true; + parameters.const_penalty = 0.75; + parameters.dt = dt; + parameters.enable_timestep_vote = true; + parameters.timestep_pen_frac = 0.3; + + int test_mesh_update_err = this->m_mesh.tribolSetupAndUpdate( + tribol::COMMON_PLANE, tribol::PENALTY, tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); + + EXPECT_EQ( test_mesh_update_err, 0 ); + + RealT dt_vote = parameters.timestep_pen_frac * element_thickness1 / velZ1; + RealT dt_diff = std::abs( parameters.dt - dt_vote ); + RealT dt_tol = 1.e-8; + EXPECT_LT( dt_diff, dt_tol ); + + tribol::finalize(); } TEST_F( CommonPlaneTest, separation_velocity_small_gap ) { - // This test makes sure that there is no change to dt in - // the presence of a separation velocity with small gap - this->m_mesh.mortarMeshId = 0; - this->m_mesh.nonmortarMeshId = 1; - - int nMortarElems = 4; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 5; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - // mesh bounding box with 0.055 interpenetration gap - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.005; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 0.95; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.; - - // compute element thickness for each block - RealT element_thickness1 = (z_max1 - z_min1) / nElemsZM; - RealT element_thickness2 = (z_max2 - z_min2) / nElemsZS; - - // setup mesh - this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0. ); - - // specify dt and component velocities for each block. - // Large velocity in the z-direction will incite a change in the - // timestep. This velocity is computed on the high side using the - // hardcoded rule that one face cannot interpen the other - // exceeding 30% of the other's element thickness. - RealT dt = 1.0; - RealT bulk_mod1 = 1.0; - RealT bulk_mod2 = 1.0; - RealT vel_factor = 100.; - RealT velX1 = 0.; - RealT velY1 = 0.; - RealT velZ1 = vel_factor * 0.3 * element_thickness1 / dt; - RealT velX2 = 0.; - RealT velY2 = 0.; - RealT velZ2 = vel_factor * 0.3 * element_thickness2 / dt; - - this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, -velZ1 ); - this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, velZ2 ); - - // allocate and set element thickness and bulk modulus - this->m_mesh.allocateAndSetElementThickness( m_mesh.mortarMeshId, element_thickness1 ); - this->m_mesh.allocateAndSetBulkModulus( m_mesh.mortarMeshId, bulk_mod1 ); - this->m_mesh.allocateAndSetElementThickness( m_mesh.nonmortarMeshId, element_thickness2 ); - this->m_mesh.allocateAndSetBulkModulus( m_mesh.nonmortarMeshId, bulk_mod2 ); - - // call tribol setup and update - tribol::TestControlParameters parameters; - parameters.penalty_ratio = true; - parameters.const_penalty = 0.75; - parameters.dt = dt; - parameters.enable_timestep_vote = true; - parameters.timestep_pen_frac = 0.3; - - int test_mesh_update_err = - this->m_mesh.tribolSetupAndUpdate( tribol::COMMON_PLANE, tribol::PENALTY, - tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); - - EXPECT_EQ( test_mesh_update_err, 0 ); - - // no change in dt because of separation velocities - EXPECT_EQ( parameters.dt, dt ); - - tribol::finalize(); + // This test makes sure that there is no change to dt in + // the presence of a separation velocity with small gap + this->m_mesh.mortarMeshId = 0; + this->m_mesh.nonmortarMeshId = 1; + + int nMortarElems = 4; + int nElemsXM = nMortarElems; + int nElemsYM = nMortarElems; + int nElemsZM = nMortarElems; + + int nNonmortarElems = 5; + int nElemsXS = nNonmortarElems; + int nElemsYS = nNonmortarElems; + int nElemsZS = nNonmortarElems; + + // mesh bounding box with 0.055 interpenetration gap + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.005; + + RealT x_min2 = 0.; + RealT y_min2 = 0.; + RealT z_min2 = 0.95; + RealT x_max2 = 1.; + RealT y_max2 = 1.; + RealT z_max2 = 2.; + + // compute element thickness for each block + RealT element_thickness1 = ( z_max1 - z_min1 ) / nElemsZM; + RealT element_thickness2 = ( z_max2 - z_min2 ) / nElemsZS; + + // setup mesh + this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., + 0. ); + + // specify dt and component velocities for each block. + // Large velocity in the z-direction will incite a change in the + // timestep. This velocity is computed on the high side using the + // hardcoded rule that one face cannot interpen the other + // exceeding 30% of the other's element thickness. + RealT dt = 1.0; + RealT bulk_mod1 = 1.0; + RealT bulk_mod2 = 1.0; + RealT vel_factor = 100.; + RealT velX1 = 0.; + RealT velY1 = 0.; + RealT velZ1 = vel_factor * 0.3 * element_thickness1 / dt; + RealT velX2 = 0.; + RealT velY2 = 0.; + RealT velZ2 = vel_factor * 0.3 * element_thickness2 / dt; + + this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, -velZ1 ); + this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, velZ2 ); + + // allocate and set element thickness and bulk modulus + this->m_mesh.allocateAndSetElementThickness( m_mesh.mortarMeshId, element_thickness1 ); + this->m_mesh.allocateAndSetBulkModulus( m_mesh.mortarMeshId, bulk_mod1 ); + this->m_mesh.allocateAndSetElementThickness( m_mesh.nonmortarMeshId, element_thickness2 ); + this->m_mesh.allocateAndSetBulkModulus( m_mesh.nonmortarMeshId, bulk_mod2 ); + + // call tribol setup and update + tribol::TestControlParameters parameters; + parameters.penalty_ratio = true; + parameters.const_penalty = 0.75; + parameters.dt = dt; + parameters.enable_timestep_vote = true; + parameters.timestep_pen_frac = 0.3; + + int test_mesh_update_err = this->m_mesh.tribolSetupAndUpdate( + tribol::COMMON_PLANE, tribol::PENALTY, tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); + + EXPECT_EQ( test_mesh_update_err, 0 ); + + // no change in dt because of separation velocities + EXPECT_EQ( parameters.dt, dt ); + + tribol::finalize(); } TEST_F( CommonPlaneTest, large_velocity_large_separation ) { - // This test uses two blocks with a large initial separation, and an interpen - // velocity small enough that it should not trigger a timestep vote from the - // velocity projection check - this->m_mesh.mortarMeshId = 0; - this->m_mesh.nonmortarMeshId = 1; - - int nMortarElems = 4; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 5; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - // mesh bounding box with initial separation - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 2.1; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 3.1; - - // compute element thickness for each block - RealT element_thickness1 = (z_max1 - z_min1) / nElemsZM; - RealT element_thickness2 = (z_max2 - z_min2) / nElemsZS; - - // setup mesh - this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0. ); - - // specify dt and component velocities for each block. - // Large velocity in the z-direction will incite a change in the - // timestep. This velocity is computed on the high side using the - // hardcoded rule that one face cannot interpen the other - // exceeding 30% of the other's element thickness. - RealT dt = 1.0; - RealT bulk_mod1 = 1.0; - RealT bulk_mod2 = 1.0; - RealT vel_factor = 10.; - RealT velX1 = 0.; - RealT velY1 = 0.; - RealT velZ1 = vel_factor * 0.3 * element_thickness1 / dt; - RealT velX2 = 0.; - RealT velY2 = 0.; - RealT velZ2 = vel_factor * 0.3 * element_thickness2 / dt; - - this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); - this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, -velZ2 ); - - // allocate and set element thickness and bulk modulus - this->m_mesh.allocateAndSetElementThickness( m_mesh.mortarMeshId, element_thickness1 ); - this->m_mesh.allocateAndSetBulkModulus( m_mesh.mortarMeshId, bulk_mod1 ); - this->m_mesh.allocateAndSetElementThickness( m_mesh.nonmortarMeshId, element_thickness2 ); - this->m_mesh.allocateAndSetBulkModulus( m_mesh.nonmortarMeshId, bulk_mod2 ); - - // call tribol setup and update - tribol::TestControlParameters parameters; - parameters.penalty_ratio = true; - parameters.const_penalty = 0.75; - parameters.dt = dt; - parameters.enable_timestep_vote = true; - parameters.timestep_pen_frac = 0.3; - - int test_mesh_update_err = - this->m_mesh.tribolSetupAndUpdate( tribol::COMMON_PLANE, tribol::PENALTY, - tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); - - EXPECT_EQ( test_mesh_update_err, 0 ); - - // no change in dt due to separation velocities - EXPECT_EQ( parameters.dt, dt ); - - tribol::finalize(); + // This test uses two blocks with a large initial separation, and an interpen + // velocity small enough that it should not trigger a timestep vote from the + // velocity projection check + this->m_mesh.mortarMeshId = 0; + this->m_mesh.nonmortarMeshId = 1; + + int nMortarElems = 4; + int nElemsXM = nMortarElems; + int nElemsYM = nMortarElems; + int nElemsZM = nMortarElems; + + int nNonmortarElems = 5; + int nElemsXS = nNonmortarElems; + int nElemsYS = nNonmortarElems; + int nElemsZS = nNonmortarElems; + + // mesh bounding box with initial separation + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.; + + RealT x_min2 = 0.; + RealT y_min2 = 0.; + RealT z_min2 = 2.1; + RealT x_max2 = 1.; + RealT y_max2 = 1.; + RealT z_max2 = 3.1; + + // compute element thickness for each block + RealT element_thickness1 = ( z_max1 - z_min1 ) / nElemsZM; + RealT element_thickness2 = ( z_max2 - z_min2 ) / nElemsZS; + + // setup mesh + this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., + 0. ); + + // specify dt and component velocities for each block. + // Large velocity in the z-direction will incite a change in the + // timestep. This velocity is computed on the high side using the + // hardcoded rule that one face cannot interpen the other + // exceeding 30% of the other's element thickness. + RealT dt = 1.0; + RealT bulk_mod1 = 1.0; + RealT bulk_mod2 = 1.0; + RealT vel_factor = 10.; + RealT velX1 = 0.; + RealT velY1 = 0.; + RealT velZ1 = vel_factor * 0.3 * element_thickness1 / dt; + RealT velX2 = 0.; + RealT velY2 = 0.; + RealT velZ2 = vel_factor * 0.3 * element_thickness2 / dt; + + this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); + this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, -velZ2 ); + + // allocate and set element thickness and bulk modulus + this->m_mesh.allocateAndSetElementThickness( m_mesh.mortarMeshId, element_thickness1 ); + this->m_mesh.allocateAndSetBulkModulus( m_mesh.mortarMeshId, bulk_mod1 ); + this->m_mesh.allocateAndSetElementThickness( m_mesh.nonmortarMeshId, element_thickness2 ); + this->m_mesh.allocateAndSetBulkModulus( m_mesh.nonmortarMeshId, bulk_mod2 ); + + // call tribol setup and update + tribol::TestControlParameters parameters; + parameters.penalty_ratio = true; + parameters.const_penalty = 0.75; + parameters.dt = dt; + parameters.enable_timestep_vote = true; + parameters.timestep_pen_frac = 0.3; + + int test_mesh_update_err = this->m_mesh.tribolSetupAndUpdate( + tribol::COMMON_PLANE, tribol::PENALTY, tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); + + EXPECT_EQ( test_mesh_update_err, 0 ); + + // no change in dt due to separation velocities + EXPECT_EQ( parameters.dt, dt ); + + tribol::finalize(); } TEST_F( CommonPlaneTest, large_velocity_small_separation ) { - // this test checks the two blocks with a small initial separation - // and a large velocity. This should trigger a velocity projection - // timestep vote with the face pairs marked as contact candidates - this->m_mesh.mortarMeshId = 0; - this->m_mesh.nonmortarMeshId = 1; - - int nMortarElems = 4; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 4; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - // mesh bounding box with initial separation - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 1.0001; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.0001; - - // compute element thickness for each block - RealT element_thickness1 = (z_max1 - z_min1) / nElemsZM; - RealT element_thickness2 = (z_max2 - z_min2) / nElemsZS; - - // setup mesh - this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0. ); - - // specify dt and component velocities for each block. - // Large velocity in the z-direction will incite a change in the - // timestep. This velocity is computed on the high side using the - // hardcoded rule that one face cannot interpen the other - // exceeding 30% of the other's element thickness. - RealT dt = 1.0; - RealT bulk_mod1 = 1.0; - RealT bulk_mod2 = 1.0; - RealT vel_factor = 1.e6; - RealT velX1 = 0.; - RealT velY1 = 0.; - RealT velZ1 = vel_factor * 0.3 * element_thickness1 / dt; - RealT velX2 = 0.; - RealT velY2 = 0.; - RealT velZ2 = vel_factor * 0.3 * element_thickness2 / dt; - - this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); - this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, -velZ2 ); - - // allocate and set element thickness and bulk modulus - this->m_mesh.allocateAndSetElementThickness( m_mesh.mortarMeshId, element_thickness1 ); - this->m_mesh.allocateAndSetBulkModulus( m_mesh.mortarMeshId, bulk_mod1 ); - this->m_mesh.allocateAndSetElementThickness( m_mesh.nonmortarMeshId, element_thickness2 ); - this->m_mesh.allocateAndSetBulkModulus( m_mesh.nonmortarMeshId, bulk_mod2 ); - - // call tribol setup and update - tribol::TestControlParameters parameters; - parameters.penalty_ratio = true; - parameters.const_penalty = 0.75; - parameters.dt = dt; - parameters.enable_timestep_vote = true; - parameters.timestep_pen_frac = 0.3; - - int test_mesh_update_err = - this->m_mesh.tribolSetupAndUpdate( tribol::COMMON_PLANE, tribol::PENALTY, - tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); - - EXPECT_EQ( test_mesh_update_err, 0 ); - RealT dt_vote = parameters.timestep_pen_frac*element_thickness1/velZ1; - RealT dt_diff = std::abs(parameters.dt - dt_vote); - RealT dt_tol = 1.e-8; - EXPECT_LT( dt_diff, dt_tol ); - - tribol::finalize(); + // this test checks the two blocks with a small initial separation + // and a large velocity. This should trigger a velocity projection + // timestep vote with the face pairs marked as contact candidates + this->m_mesh.mortarMeshId = 0; + this->m_mesh.nonmortarMeshId = 1; + + int nMortarElems = 4; + int nElemsXM = nMortarElems; + int nElemsYM = nMortarElems; + int nElemsZM = nMortarElems; + + int nNonmortarElems = 4; + int nElemsXS = nNonmortarElems; + int nElemsYS = nNonmortarElems; + int nElemsZS = nNonmortarElems; + + // mesh bounding box with initial separation + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.; + + RealT x_min2 = 0.; + RealT y_min2 = 0.; + RealT z_min2 = 1.0001; + RealT x_max2 = 1.; + RealT y_max2 = 1.; + RealT z_max2 = 2.0001; + + // compute element thickness for each block + RealT element_thickness1 = ( z_max1 - z_min1 ) / nElemsZM; + RealT element_thickness2 = ( z_max2 - z_min2 ) / nElemsZS; + + // setup mesh + this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., + 0. ); + + // specify dt and component velocities for each block. + // Large velocity in the z-direction will incite a change in the + // timestep. This velocity is computed on the high side using the + // hardcoded rule that one face cannot interpen the other + // exceeding 30% of the other's element thickness. + RealT dt = 1.0; + RealT bulk_mod1 = 1.0; + RealT bulk_mod2 = 1.0; + RealT vel_factor = 1.e6; + RealT velX1 = 0.; + RealT velY1 = 0.; + RealT velZ1 = vel_factor * 0.3 * element_thickness1 / dt; + RealT velX2 = 0.; + RealT velY2 = 0.; + RealT velZ2 = vel_factor * 0.3 * element_thickness2 / dt; + + this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); + this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, -velZ2 ); + + // allocate and set element thickness and bulk modulus + this->m_mesh.allocateAndSetElementThickness( m_mesh.mortarMeshId, element_thickness1 ); + this->m_mesh.allocateAndSetBulkModulus( m_mesh.mortarMeshId, bulk_mod1 ); + this->m_mesh.allocateAndSetElementThickness( m_mesh.nonmortarMeshId, element_thickness2 ); + this->m_mesh.allocateAndSetBulkModulus( m_mesh.nonmortarMeshId, bulk_mod2 ); + + // call tribol setup and update + tribol::TestControlParameters parameters; + parameters.penalty_ratio = true; + parameters.const_penalty = 0.75; + parameters.dt = dt; + parameters.enable_timestep_vote = true; + parameters.timestep_pen_frac = 0.3; + + int test_mesh_update_err = this->m_mesh.tribolSetupAndUpdate( + tribol::COMMON_PLANE, tribol::PENALTY, tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); + + EXPECT_EQ( test_mesh_update_err, 0 ); + RealT dt_vote = parameters.timestep_pen_frac * element_thickness1 / velZ1; + RealT dt_diff = std::abs( parameters.dt - dt_vote ); + RealT dt_tol = 1.e-8; + EXPECT_LT( dt_diff, dt_tol ); + + tribol::finalize(); } TEST_F( CommonPlaneTest, large_velocity_small_separation_set_alpha ) { - // this test checks the two blocks with a small initial separation - // and a large velocity. This should trigger a velocity projection - // timestep vote with the face pairs marked as contact candidates - this->m_mesh.mortarMeshId = 0; - this->m_mesh.nonmortarMeshId = 1; - - int nMortarElems = 4; - int nElemsXM = nMortarElems; - int nElemsYM = nMortarElems; - int nElemsZM = nMortarElems; - - int nNonmortarElems = 4; - int nElemsXS = nNonmortarElems; - int nElemsYS = nNonmortarElems; - int nElemsZS = nNonmortarElems; - - // mesh bounding box with initial separation - RealT x_min1 = 0.; - RealT y_min1 = 0.; - RealT z_min1 = 0.; - RealT x_max1 = 1.; - RealT y_max1 = 1.; - RealT z_max1 = 1.; - - RealT x_min2 = 0.; - RealT y_min2 = 0.; - RealT z_min2 = 1.0001; - RealT x_max2 = 1.; - RealT y_max2 = 1.; - RealT z_max2 = 2.0001; - - // compute element thickness for each block - RealT element_thickness1 = (z_max1 - z_min1) / nElemsZM; - RealT element_thickness2 = (z_max2 - z_min2) / nElemsZS; - - // setup mesh - this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, - x_min1, y_min1, z_min1, - x_max1, y_max1, z_max1, - nElemsXS, nElemsYS, nElemsZS, - x_min2, y_min2, z_min2, - x_max2, y_max2, z_max2, - 0., 0. ); - - // specify dt and component velocities for each block. - // Large velocity in the z-direction will incite a change in the - // timestep. This velocity is computed on the high side using the - // hardcoded rule that one face cannot interpen the other - // exceeding 30% of the other's element thickness. - RealT dt = 1.0; - RealT bulk_mod1 = 1.0; - RealT bulk_mod2 = 1.0; - RealT vel_factor = 1.e6; - RealT velX1 = 0.; - RealT velY1 = 0.; - RealT velZ1 = vel_factor * 0.3 * element_thickness1 / dt; - RealT velX2 = 0.; - RealT velY2 = 0.; - RealT velZ2 = vel_factor * 0.3 * element_thickness2 / dt; - - this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); - this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, -velZ2 ); - - // allocate and set element thickness and bulk modulus - this->m_mesh.allocateAndSetElementThickness( m_mesh.mortarMeshId, element_thickness1 ); - this->m_mesh.allocateAndSetBulkModulus( m_mesh.mortarMeshId, bulk_mod1 ); - this->m_mesh.allocateAndSetElementThickness( m_mesh.nonmortarMeshId, element_thickness2 ); - this->m_mesh.allocateAndSetBulkModulus( m_mesh.nonmortarMeshId, bulk_mod2 ); - - // call tribol setup and update - tribol::TestControlParameters parameters; - parameters.penalty_ratio = true; - parameters.const_penalty = 0.75; - parameters.dt = dt; - parameters.enable_timestep_vote = true; - parameters.timestep_pen_frac = 0.3; - parameters.timestep_scale = 0.5; - - int test_mesh_update_err = - this->m_mesh.tribolSetupAndUpdate( tribol::COMMON_PLANE, tribol::PENALTY, - tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); - - EXPECT_EQ( test_mesh_update_err, 0 ); - RealT dt_vote = parameters.timestep_scale*parameters.timestep_pen_frac*element_thickness1/velZ1; - RealT dt_diff = std::abs(parameters.dt - dt_vote); - RealT dt_tol = 1.e-8; - EXPECT_LT( dt_diff, dt_tol ); - - tribol::finalize(); + // this test checks the two blocks with a small initial separation + // and a large velocity. This should trigger a velocity projection + // timestep vote with the face pairs marked as contact candidates + this->m_mesh.mortarMeshId = 0; + this->m_mesh.nonmortarMeshId = 1; + + int nMortarElems = 4; + int nElemsXM = nMortarElems; + int nElemsYM = nMortarElems; + int nElemsZM = nMortarElems; + + int nNonmortarElems = 4; + int nElemsXS = nNonmortarElems; + int nElemsYS = nNonmortarElems; + int nElemsZS = nNonmortarElems; + + // mesh bounding box with initial separation + RealT x_min1 = 0.; + RealT y_min1 = 0.; + RealT z_min1 = 0.; + RealT x_max1 = 1.; + RealT y_max1 = 1.; + RealT z_max1 = 1.; + + RealT x_min2 = 0.; + RealT y_min2 = 0.; + RealT z_min2 = 1.0001; + RealT x_max2 = 1.; + RealT y_max2 = 1.; + RealT z_max2 = 2.0001; + + // compute element thickness for each block + RealT element_thickness1 = ( z_max1 - z_min1 ) / nElemsZM; + RealT element_thickness2 = ( z_max2 - z_min2 ) / nElemsZS; + + // setup mesh + this->m_mesh.setupContactMeshHex( nElemsXM, nElemsYM, nElemsZM, x_min1, y_min1, z_min1, x_max1, y_max1, z_max1, + nElemsXS, nElemsYS, nElemsZS, x_min2, y_min2, z_min2, x_max2, y_max2, z_max2, 0., + 0. ); + + // specify dt and component velocities for each block. + // Large velocity in the z-direction will incite a change in the + // timestep. This velocity is computed on the high side using the + // hardcoded rule that one face cannot interpen the other + // exceeding 30% of the other's element thickness. + RealT dt = 1.0; + RealT bulk_mod1 = 1.0; + RealT bulk_mod2 = 1.0; + RealT vel_factor = 1.e6; + RealT velX1 = 0.; + RealT velY1 = 0.; + RealT velZ1 = vel_factor * 0.3 * element_thickness1 / dt; + RealT velX2 = 0.; + RealT velY2 = 0.; + RealT velZ2 = vel_factor * 0.3 * element_thickness2 / dt; + + this->m_mesh.allocateAndSetVelocities( m_mesh.mortarMeshId, velX1, velY1, velZ1 ); + this->m_mesh.allocateAndSetVelocities( m_mesh.nonmortarMeshId, velX2, velY2, -velZ2 ); + + // allocate and set element thickness and bulk modulus + this->m_mesh.allocateAndSetElementThickness( m_mesh.mortarMeshId, element_thickness1 ); + this->m_mesh.allocateAndSetBulkModulus( m_mesh.mortarMeshId, bulk_mod1 ); + this->m_mesh.allocateAndSetElementThickness( m_mesh.nonmortarMeshId, element_thickness2 ); + this->m_mesh.allocateAndSetBulkModulus( m_mesh.nonmortarMeshId, bulk_mod2 ); + + // call tribol setup and update + tribol::TestControlParameters parameters; + parameters.penalty_ratio = true; + parameters.const_penalty = 0.75; + parameters.dt = dt; + parameters.enable_timestep_vote = true; + parameters.timestep_pen_frac = 0.3; + parameters.timestep_scale = 0.5; + + int test_mesh_update_err = this->m_mesh.tribolSetupAndUpdate( + tribol::COMMON_PLANE, tribol::PENALTY, tribol::FRICTIONLESS, tribol::NO_CASE, false, parameters ); + + EXPECT_EQ( test_mesh_update_err, 0 ); + RealT dt_vote = parameters.timestep_scale * parameters.timestep_pen_frac * element_thickness1 / velZ1; + RealT dt_diff = std::abs( parameters.dt - dt_vote ); + RealT dt_tol = 1.e-8; + EXPECT_LT( dt_diff, dt_tol ); + + tribol::finalize(); } -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); #ifdef TRIBOL_USE_UMPIRE umpire::ResourceManager::getInstance(); // initialize umpire's ResouceManager diff --git a/src/tests/tribol_twb_integ.cpp b/src/tests/tribol_twb_integ.cpp index 772ef3b3..c513e875 100644 --- a/src/tests/tribol_twb_integ.cpp +++ b/src/tests/tribol_twb_integ.cpp @@ -17,256 +17,217 @@ #include "gtest/gtest.h" // c++ includes -#include // std::abs +#include // std::abs using RealT = tribol::RealT; /*! * Test fixture class with some setup necessary to use the - * Taylor-Wingate-Bos integration with either Wachspress basis - * function evaluations or standard Lagrange basis function + * Taylor-Wingate-Bos integration with either Wachspress basis + * function evaluations or standard Lagrange basis function * evaluations */ -class TWBIntegTest : public ::testing::Test -{ - -public: - - int numNodes; - int dim; - - RealT* getXCoords() - { - return x; - } - - RealT* getYCoords() - { - return y; - } - - RealT* getZCoords() - { - return z; - } - - bool integrate( RealT const tol ) - { - RealT xyz[ this->dim * this->numNodes ]; - RealT* xy = xyz; - - RealT* x = this->x; - RealT* y = this->y; - RealT* z = this->z; - - // generate stacked coordinate array - for (int j=0; jnumNodes; ++j) - { - for (int k=0; kdim; ++k) - { - switch (k) - { - case 0: - xy[ this->dim * j + k ] = x[ j ]; - break; - case 1: - xy[ this->dim * j + k ] = y[ j ]; - break; - case 2: - xy[ this->dim * j + k ] = z[ j ]; - break; - } // end switch - } // end loop over dimension - } // end loop over nodes - - // instantiate SurfaceContactElem struct. Note, this object is instantiated - // using face 1 as face 2, but these faces are not used in this test so this - // is ok. - tribol::SurfaceContactElem elem (this->dim, xy, xy, xy, - this->numNodes, this->numNodes, - nullptr, nullptr, 0, 0); - - // instantiate integration object - tribol::IntegPts integ; - - // generate all current configuration integration point coordinates and weights - tribol::TWBPolyInt( elem, integ, 2 ); - - // evaluate sum_a (integral_face (phi_a) da) with outer loop over nodes, a, and - // inner loop over number of integration points - RealT areaTest = 0.; - RealT phi = 0.; - - for (int a=0; anumNodes; ++a) - { - for (int ip=0; ipnumNodes, a, phi ); - - areaTest += integ.wts[ip]*phi; - } - } - - RealT area = tribol::Area2DPolygon( x, y, this->numNodes ); - - bool convrg = (std::abs(areaTest - area) <= tol) ? true : false; - - return convrg; - } -protected: - - void SetUp() override - { - this->numNodes = 4; - this->dim = 3; - - if (this->x == nullptr) - { - this->x = new RealT [this->numNodes]; - } - else - { - delete [] this->x; - this->x = new RealT [this->numNodes]; - } - - if (this->y == nullptr) - { - this->y = new RealT [this->numNodes]; - } - else - { - delete [] this->y; - this->y = new RealT [this->numNodes]; - } - - if (this->z == nullptr) - { - this->z = new RealT [this->numNodes]; - } - else - { - delete [] this->z; - this->z = new RealT [this->numNodes]; +class TWBIntegTest : public ::testing::Test { + public: + int numNodes; + int dim; + + RealT* getXCoords() { return x; } + + RealT* getYCoords() { return y; } + + RealT* getZCoords() { return z; } + + bool integrate( RealT const tol ) + { + RealT xyz[this->dim * this->numNodes]; + RealT* xy = xyz; + + RealT* x = this->x; + RealT* y = this->y; + RealT* z = this->z; + + // generate stacked coordinate array + for ( int j = 0; j < this->numNodes; ++j ) { + for ( int k = 0; k < this->dim; ++k ) { + switch ( k ) { + case 0: + xy[this->dim * j + k] = x[j]; + break; + case 1: + xy[this->dim * j + k] = y[j]; + break; + case 2: + xy[this->dim * j + k] = z[j]; + break; + } // end switch + } // end loop over dimension + } // end loop over nodes + + // instantiate SurfaceContactElem struct. Note, this object is instantiated + // using face 1 as face 2, but these faces are not used in this test so this + // is ok. + tribol::SurfaceContactElem elem( this->dim, xy, xy, xy, this->numNodes, this->numNodes, nullptr, nullptr, 0, 0 ); + + // instantiate integration object + tribol::IntegPts integ; + + // generate all current configuration integration point coordinates and weights + tribol::TWBPolyInt( elem, integ, 2 ); + + // evaluate sum_a (integral_face (phi_a) da) with outer loop over nodes, a, and + // inner loop over number of integration points + RealT areaTest = 0.; + RealT phi = 0.; + + for ( int a = 0; a < this->numNodes; ++a ) { + for ( int ip = 0; ip < integ.numIPs; ++ip ) { + tribol::WachspressBasis( xy, integ.xy[dim * ip], integ.xy[dim * ip + 1], integ.xy[dim * ip + 2], this->numNodes, + a, phi ); + + areaTest += integ.wts[ip] * phi; } - } - - void TearDown() override - { - if (this->x != nullptr) - { - delete [] this->x; - this->x = nullptr; - } - if (this->y != nullptr) - { - delete [] this->y; - this->y = nullptr; - } - if (this->z != nullptr) - { - delete [] this->z; - this->z = nullptr; - } - } - -protected: - - RealT* x {nullptr}; - RealT* y {nullptr}; - RealT* z {nullptr}; - + } + + RealT area = tribol::Area2DPolygon( x, y, this->numNodes ); + + bool convrg = ( std::abs( areaTest - area ) <= tol ) ? true : false; + + return convrg; + } + + protected: + void SetUp() override + { + this->numNodes = 4; + this->dim = 3; + + if ( this->x == nullptr ) { + this->x = new RealT[this->numNodes]; + } else { + delete[] this->x; + this->x = new RealT[this->numNodes]; + } + + if ( this->y == nullptr ) { + this->y = new RealT[this->numNodes]; + } else { + delete[] this->y; + this->y = new RealT[this->numNodes]; + } + + if ( this->z == nullptr ) { + this->z = new RealT[this->numNodes]; + } else { + delete[] this->z; + this->z = new RealT[this->numNodes]; + } + } + + void TearDown() override + { + if ( this->x != nullptr ) { + delete[] this->x; + this->x = nullptr; + } + if ( this->y != nullptr ) { + delete[] this->y; + this->y = nullptr; + } + if ( this->z != nullptr ) { + delete[] this->z; + this->z = nullptr; + } + } + + protected: + RealT* x{ nullptr }; + RealT* y{ nullptr }; + RealT* z{ nullptr }; }; - TEST_F( TWBIntegTest, square_wachspress_basis ) { + RealT* x = this->getXCoords(); + RealT* y = this->getYCoords(); + RealT* z = this->getZCoords(); - RealT* x = this->getXCoords(); - RealT* y = this->getYCoords(); - RealT* z = this->getZCoords(); + x[0] = -0.5; + x[1] = 0.5; + x[2] = 0.5; + x[3] = -0.5; - x[0] = -0.5; - x[1] = 0.5; - x[2] = 0.5; - x[3] = -0.5; + y[0] = -0.5; + y[1] = -0.5; + y[2] = 0.5; + y[3] = 0.5; - y[0] = -0.5; - y[1] = -0.5; - y[2] = 0.5; - y[3] = 0.5; + z[0] = 0.1; + z[1] = 0.1; + z[2] = 0.1; + z[3] = 0.1; - z[0] = 0.1; - z[1] = 0.1; - z[2] = 0.1; - z[3] = 0.1; + bool convrg = this->integrate( 1.e-8 ); - bool convrg = this->integrate( 1.e-8 ); - - EXPECT_EQ( convrg, true ); + EXPECT_EQ( convrg, true ); } TEST_F( TWBIntegTest, rect_wachspress_basis ) { - - RealT* x = this->getXCoords(); - RealT* y = this->getYCoords(); - RealT* z = this->getZCoords(); - - x[0] = -0.5; - x[1] = 0.5; - x[2] = 0.5; - x[3] = -0.5; - - y[0] = -0.25; - y[1] = -0.25; - y[2] = 0.25; - y[3] = 0.25; - - z[0] = 0.1; - z[1] = 0.1; - z[2] = 0.1; - z[3] = 0.1; - - bool convrg = this->integrate( 1.e-8 ); - EXPECT_EQ( convrg, true ); + RealT* x = this->getXCoords(); + RealT* y = this->getYCoords(); + RealT* z = this->getZCoords(); + + x[0] = -0.5; + x[1] = 0.5; + x[2] = 0.5; + x[3] = -0.5; + + y[0] = -0.25; + y[1] = -0.25; + y[2] = 0.25; + y[3] = 0.25; + + z[0] = 0.1; + z[1] = 0.1; + z[2] = 0.1; + z[3] = 0.1; + + bool convrg = this->integrate( 1.e-8 ); + EXPECT_EQ( convrg, true ); } TEST_F( TWBIntegTest, nonaffine_wachspress_basis ) { + RealT* x = this->getXCoords(); + RealT* y = this->getYCoords(); + RealT* z = this->getZCoords(); - RealT* x = this->getXCoords(); - RealT* y = this->getYCoords(); - RealT* z = this->getZCoords(); - - x[0] = -0.5; - x[1] = 0.5; - x[2] = 0.235; - x[3] = -0.35; + x[0] = -0.5; + x[1] = 0.5; + x[2] = 0.235; + x[3] = -0.35; - y[0] = -0.25; - y[1] = -0.15; - y[2] = 0.25; - y[3] = 0.235; + y[0] = -0.25; + y[1] = -0.15; + y[2] = 0.25; + y[3] = 0.235; - z[0] = 0.1; - z[1] = 0.1; - z[2] = 0.1; - z[3] = 0.1; + z[0] = 0.1; + z[1] = 0.1; + z[2] = 0.1; + z[3] = 0.1; - bool convrg = this->integrate( 1.e-8 ); + bool convrg = this->integrate( 1.e-8 ); - EXPECT_EQ( convrg, true ); + EXPECT_EQ( convrg, true ); } -int main(int argc, char* argv[]) +int main( int argc, char* argv[] ) { int result = 0; - ::testing::InitGoogleTest(&argc, argv); + ::testing::InitGoogleTest( &argc, argv ); axom::slic::SimpleLogger logger; diff --git a/src/tribol/common/ArrayTypes.hpp b/src/tribol/common/ArrayTypes.hpp index 07c164e9..b5533784 100644 --- a/src/tribol/common/ArrayTypes.hpp +++ b/src/tribol/common/ArrayTypes.hpp @@ -12,8 +12,7 @@ #include "axom/core/Array.hpp" #include "axom/core/ArrayView.hpp" -namespace tribol -{ +namespace tribol { /** * @brief Array allocated on the free store, i.e. the heap @@ -69,6 +68,6 @@ using MultiArrayView = ArrayT, 1, SPACE>; template using MultiViewArrayView = ArrayViewT, 1, SPACE>; -} // namespace tribol +} // namespace tribol #endif /* TRIBOL_COMMON_ARRAYTYPES_HPP_ */ diff --git a/src/tribol/common/BasicTypes.hpp b/src/tribol/common/BasicTypes.hpp index f4b79c04..4da0a7f7 100644 --- a/src/tribol/common/BasicTypes.hpp +++ b/src/tribol/common/BasicTypes.hpp @@ -17,8 +17,7 @@ #include #endif -namespace tribol -{ +namespace tribol { #ifdef TRIBOL_USE_MPI @@ -52,22 +51,22 @@ using RealT = double; #define TRIBOL_UNUSED_PARAM AXOM_UNUSED_PARAM // Execution space specifiers -#if defined(TRIBOL_USE_CUDA) || defined(TRIBOL_USE_HIP) - #ifndef __device__ - #error "TRIBOL_USE_CUDA or TRIBOL_USE_HIP but __device__ is undefined. Check include files" - #endif - #define TRIBOL_DEVICE __device__ - #define TRIBOL_HOST_DEVICE __host__ __device__ +#if defined( TRIBOL_USE_CUDA ) || defined( TRIBOL_USE_HIP ) +#ifndef __device__ +#error "TRIBOL_USE_CUDA or TRIBOL_USE_HIP but __device__ is undefined. Check include files" +#endif +#define TRIBOL_DEVICE __device__ +#define TRIBOL_HOST_DEVICE __host__ __device__ #else - #define TRIBOL_DEVICE - #define TRIBOL_HOST_DEVICE +#define TRIBOL_DEVICE +#define TRIBOL_HOST_DEVICE #endif // Define variable when loops are computed on host -#if !(defined(TRIBOL_USE_CUDA) || defined(TRIBOL_USE_HIP)) - #define TRIBOL_USE_HOST +#if !( defined( TRIBOL_USE_CUDA ) || defined( TRIBOL_USE_HIP ) ) +#define TRIBOL_USE_HOST #endif -} // namespace tribol +} // namespace tribol #endif /* TRIBOL_COMMON_BASICTYPES_HPP_ */ diff --git a/src/tribol/common/Containers.hpp b/src/tribol/common/Containers.hpp index ee4819c4..b53d8391 100644 --- a/src/tribol/common/Containers.hpp +++ b/src/tribol/common/Containers.hpp @@ -9,64 +9,48 @@ // Tribol includes #include "tribol/common/BasicTypes.hpp" -namespace tribol -{ +namespace tribol { /** * @brief Storage base class for device-compatible free store (heap) allocated * arrays - * + * * @tparam T Datatype stored in array */ template -class DeviceArrayData -{ -protected: - TRIBOL_HOST_DEVICE DeviceArrayData() - : size_ {0}, - data_ {nullptr} - {} - TRIBOL_HOST_DEVICE DeviceArrayData(IndexT size) - : size_ {size}, - data_ {new T[size]} - {} - TRIBOL_HOST_DEVICE virtual ~DeviceArrayData() - { - deleteData(); - } +class DeviceArrayData { + protected: + TRIBOL_HOST_DEVICE DeviceArrayData() : size_{ 0 }, data_{ nullptr } {} + TRIBOL_HOST_DEVICE DeviceArrayData( IndexT size ) : size_{ size }, data_{ new T[size] } {} + TRIBOL_HOST_DEVICE virtual ~DeviceArrayData() { deleteData(); } - TRIBOL_HOST_DEVICE DeviceArrayData(const DeviceArrayData& other) - : DeviceArrayData(other.size_) + TRIBOL_HOST_DEVICE DeviceArrayData( const DeviceArrayData& other ) : DeviceArrayData( other.size_ ) { // deep copy data - for (IndexT i{0}; i < size_; ++i) - { + for ( IndexT i{ 0 }; i < size_; ++i ) { data_[i] = other.data_[i]; } } - TRIBOL_HOST_DEVICE DeviceArrayData(DeviceArrayData&& other) - : size_ {other.size_}, - data_ {other.data_} + TRIBOL_HOST_DEVICE DeviceArrayData( DeviceArrayData&& other ) : size_{ other.size_ }, data_{ other.data_ } { // reset other other.size_ = 0; other.data_ = nullptr; } - TRIBOL_HOST_DEVICE DeviceArrayData& operator=(const DeviceArrayData& other) + TRIBOL_HOST_DEVICE DeviceArrayData& operator=( const DeviceArrayData& other ) { deleteData(); size_ = other.size_; data_ = new T[size_]; // deep copy data - for (IndexT i{0}; i < size_; ++i) - { + for ( IndexT i{ 0 }; i < size_; ++i ) { data_[i] = other.data_[i]; } return *this; } - TRIBOL_HOST_DEVICE DeviceArrayData& operator=(DeviceArrayData&& other) + TRIBOL_HOST_DEVICE DeviceArrayData& operator=( DeviceArrayData&& other ) { deleteData(); size_ = other.size_; @@ -79,11 +63,10 @@ class DeviceArrayData IndexT size_; T* data_; -private: + private: TRIBOL_HOST_DEVICE void deleteData() { - if (data_ != nullptr) - { + if ( data_ != nullptr ) { delete[] data_; size_ = 0; data_ = nullptr; @@ -93,49 +76,34 @@ class DeviceArrayData /** * @brief Simple free store (heap) allocated array that can be created on device - * + * * @tparam T Datatype stored in array */ template -class DeviceArray : public DeviceArrayData -{ -public: - TRIBOL_HOST_DEVICE DeviceArray() - : DeviceArrayData() - {} - TRIBOL_HOST_DEVICE DeviceArray(IndexT size) - : DeviceArrayData(size) - {} +class DeviceArray : public DeviceArrayData { + public: + TRIBOL_HOST_DEVICE DeviceArray() : DeviceArrayData() {} + TRIBOL_HOST_DEVICE DeviceArray( IndexT size ) : DeviceArrayData( size ) {} TRIBOL_HOST_DEVICE ~DeviceArray() = default; - TRIBOL_HOST_DEVICE DeviceArray(const DeviceArray& other) - : DeviceArrayData(other) - {} - TRIBOL_HOST_DEVICE DeviceArray(DeviceArray&& other) - : DeviceArrayData(std::move(other)) - {} + TRIBOL_HOST_DEVICE DeviceArray( const DeviceArray& other ) : DeviceArrayData( other ) {} + TRIBOL_HOST_DEVICE DeviceArray( DeviceArray&& other ) : DeviceArrayData( std::move( other ) ) {} - TRIBOL_HOST_DEVICE DeviceArray& operator=(const DeviceArray& other) + TRIBOL_HOST_DEVICE DeviceArray& operator=( const DeviceArray& other ) { - DeviceArrayData::operator=(other); + DeviceArrayData::operator=( other ); return *this; } - TRIBOL_HOST_DEVICE DeviceArray& operator=(DeviceArray&& other) + TRIBOL_HOST_DEVICE DeviceArray& operator=( DeviceArray&& other ) { - DeviceArrayData::operator=(std::move(other)); + DeviceArrayData::operator=( std::move( other ) ); return *this; } - TRIBOL_HOST_DEVICE T& operator[](IndexT i) - { - return DeviceArrayData::data_[i]; - } + TRIBOL_HOST_DEVICE T& operator[]( IndexT i ) { return DeviceArrayData::data_[i]; } - TRIBOL_HOST_DEVICE const T& operator[](IndexT i) const - { - return DeviceArrayData::data_[i]; - } + TRIBOL_HOST_DEVICE const T& operator[]( IndexT i ) const { return DeviceArrayData::data_[i]; } TRIBOL_HOST_DEVICE IndexT size() const { return DeviceArrayData::size_; } @@ -145,47 +113,39 @@ class DeviceArray : public DeviceArrayData /** * @brief Simple free store (heap) allocated two-dimensional array that can be * created on device - * + * * @tparam T Datatype stored in array */ template -class DeviceArray2D : public DeviceArrayData -{ -public: - TRIBOL_HOST_DEVICE DeviceArray2D() - : DeviceArrayData(), - height_ {0}, - width_ {0} - {} - TRIBOL_HOST_DEVICE DeviceArray2D(IndexT height, IndexT width) - : DeviceArrayData(width * height), - height_ {height}, - width_ {width} - {} +class DeviceArray2D : public DeviceArrayData { + public: + TRIBOL_HOST_DEVICE DeviceArray2D() : DeviceArrayData(), height_{ 0 }, width_{ 0 } {} + TRIBOL_HOST_DEVICE DeviceArray2D( IndexT height, IndexT width ) + : DeviceArrayData( width * height ), height_{ height }, width_{ width } + { + } TRIBOL_HOST_DEVICE ~DeviceArray2D() = default; - TRIBOL_HOST_DEVICE DeviceArray2D(const DeviceArray2D& other) - : DeviceArrayData(other), - height_ {other.height_}, - width_ {other.width_} - {} - TRIBOL_HOST_DEVICE DeviceArray2D(DeviceArray2D&& other) - : DeviceArrayData(std::move(other)), - height_ {other.height_}, - width_ {other.width_} - {} - - TRIBOL_HOST_DEVICE DeviceArray2D& operator=(const DeviceArray2D& other) + TRIBOL_HOST_DEVICE DeviceArray2D( const DeviceArray2D& other ) + : DeviceArrayData( other ), height_{ other.height_ }, width_{ other.width_ } + { + } + TRIBOL_HOST_DEVICE DeviceArray2D( DeviceArray2D&& other ) + : DeviceArrayData( std::move( other ) ), height_{ other.height_ }, width_{ other.width_ } + { + } + + TRIBOL_HOST_DEVICE DeviceArray2D& operator=( const DeviceArray2D& other ) { - DeviceArrayData::operator=(other); + DeviceArrayData::operator=( other ); height_ = other.height_; width_ = other.width_; return *this; } - TRIBOL_HOST_DEVICE DeviceArray2D& operator=(DeviceArray2D&& other) + TRIBOL_HOST_DEVICE DeviceArray2D& operator=( DeviceArray2D&& other ) { - DeviceArrayData::operator=(std::move(other)); + DeviceArrayData::operator=( std::move( other ) ); height_ = other.height_; width_ = other.width_; other.width_ = 0; @@ -193,22 +153,13 @@ class DeviceArray2D : public DeviceArrayData return *this; } - TRIBOL_HOST_DEVICE T& operator[](IndexT i) - { - return DeviceArrayData::data_[i]; - } + TRIBOL_HOST_DEVICE T& operator[]( IndexT i ) { return DeviceArrayData::data_[i]; } - TRIBOL_HOST_DEVICE const T& operator[](IndexT i) const - { - return DeviceArrayData::data_[i]; - } + TRIBOL_HOST_DEVICE const T& operator[]( IndexT i ) const { return DeviceArrayData::data_[i]; } - TRIBOL_HOST_DEVICE T& operator()(IndexT i, IndexT j) - { - return DeviceArrayData::data_[i + j * height_]; - } + TRIBOL_HOST_DEVICE T& operator()( IndexT i, IndexT j ) { return DeviceArrayData::data_[i + j * height_]; } - TRIBOL_HOST_DEVICE const T& operator()(IndexT i, IndexT j) const + TRIBOL_HOST_DEVICE const T& operator()( IndexT i, IndexT j ) const { return DeviceArrayData::data_[i + j * height_]; } @@ -216,20 +167,19 @@ class DeviceArray2D : public DeviceArrayData TRIBOL_HOST_DEVICE IndexT size() const { return DeviceArrayData::size_; } TRIBOL_HOST_DEVICE T* data() const { return DeviceArrayData::data_; } - + TRIBOL_HOST_DEVICE IndexT height() const { return height_; } - + TRIBOL_HOST_DEVICE IndexT width() const { return width_; } - TRIBOL_HOST_DEVICE void fill(T value) + TRIBOL_HOST_DEVICE void fill( T value ) { - for (int i{0}; i < size(); ++i) - { + for ( int i{ 0 }; i < size(); ++i ) { data()[i] = value; } } -private: + private: IndexT height_; IndexT width_; }; @@ -239,47 +189,33 @@ class DeviceArray2D : public DeviceArrayData * created on device */ template -class StackArray -{ -public: +class StackArray { + public: TRIBOL_HOST_DEVICE StackArray() = default; - TRIBOL_HOST_DEVICE StackArray(IndexT width) - : width_ {width} - {} + TRIBOL_HOST_DEVICE StackArray( IndexT width ) : width_{ width } {} TRIBOL_HOST_DEVICE ~StackArray() = default; - TRIBOL_HOST_DEVICE StackArray(const StackArray& other) = default; - TRIBOL_HOST_DEVICE StackArray(StackArray&& other) = default; + TRIBOL_HOST_DEVICE StackArray( const StackArray& other ) = default; + TRIBOL_HOST_DEVICE StackArray( StackArray&& other ) = default; - TRIBOL_HOST_DEVICE StackArray& operator=(const StackArray& other) = default; - TRIBOL_HOST_DEVICE StackArray& operator=(StackArray&& other) = default; + TRIBOL_HOST_DEVICE StackArray& operator=( const StackArray& other ) = default; + TRIBOL_HOST_DEVICE StackArray& operator=( StackArray&& other ) = default; TRIBOL_HOST_DEVICE operator T*() noexcept { return &data_[0]; } TRIBOL_HOST_DEVICE operator const T*() const noexcept { return &data_[0]; } - TRIBOL_HOST_DEVICE T& operator[](IndexT i) - { - return data_[i]; - } + TRIBOL_HOST_DEVICE T& operator[]( IndexT i ) { return data_[i]; } - TRIBOL_HOST_DEVICE const T& operator[](IndexT i) const - { - return data_[i]; - } + TRIBOL_HOST_DEVICE const T& operator[]( IndexT i ) const { return data_[i]; } - TRIBOL_HOST_DEVICE T& operator()(IndexT i, IndexT j) - { - return data_[i * width_ + j]; - } + TRIBOL_HOST_DEVICE T& operator()( IndexT i, IndexT j ) { return data_[i * width_ + j]; } - TRIBOL_HOST_DEVICE const T& operator()(IndexT i, IndexT j) const - { - return data_[i * width_ + j]; - } -private: + TRIBOL_HOST_DEVICE const T& operator()( IndexT i, IndexT j ) const { return data_[i * width_ + j]; } + + private: T data_[N]; IndexT width_; }; -} +} // namespace tribol #endif /* TRIBOL_COMMON_CONTAINERS_HPP_ */ diff --git a/src/tribol/common/ExecModel.hpp b/src/tribol/common/ExecModel.hpp index f0077653..5dab5ec1 100644 --- a/src/tribol/common/ExecModel.hpp +++ b/src/tribol/common/ExecModel.hpp @@ -2,7 +2,7 @@ // other Tribol Project Developers. See the top-level LICENSE file for details. // // SPDX-License-Identifier: (MIT) - + #ifndef SRC_COMMON_EXECMODEL_HPP_ #define SRC_COMMON_EXECMODEL_HPP_ @@ -13,8 +13,7 @@ #include "axom/core/memory_management.hpp" #include "axom/slic.hpp" -namespace tribol -{ +namespace tribol { /** * @brief A MemorySpace ties a resource to an associated pointer @@ -54,32 +53,28 @@ enum class ExecutionMode /** * @brief SFINAE struct to deduce axom memory space from a Tribol memory space * at compile time - * - * @tparam MSPACE + * + * @tparam MSPACE */ template -struct toAxomMemorySpace -{ +struct toAxomMemorySpace { static constexpr axom::MemorySpace value = axom::MemorySpace::Dynamic; }; #ifdef TRIBOL_USE_UMPIRE template <> -struct toAxomMemorySpace -{ +struct toAxomMemorySpace { static constexpr axom::MemorySpace value = axom::MemorySpace::Host; }; template <> -struct toAxomMemorySpace -{ +struct toAxomMemorySpace { static constexpr axom::MemorySpace value = axom::MemorySpace::Device; }; template <> -struct toAxomMemorySpace -{ +struct toAxomMemorySpace { static constexpr axom::MemorySpace value = axom::MemorySpace::Unified; }; @@ -91,10 +86,9 @@ struct toAxomMemorySpace #ifdef TRIBOL_USE_UMPIRE -inline umpire::resource::MemoryResourceType toUmpireMemoryType(MemorySpace mem_space) +inline umpire::resource::MemoryResourceType toUmpireMemoryType( MemorySpace mem_space ) { - switch (mem_space) - { + switch ( mem_space ) { case MemorySpace::Host: return umpire::resource::MemoryResourceType::Host; case MemorySpace::Device: @@ -108,28 +102,26 @@ inline umpire::resource::MemoryResourceType toUmpireMemoryType(MemorySpace mem_s #endif -inline int getResourceAllocatorID(MemorySpace mem_space) +inline int getResourceAllocatorID( MemorySpace mem_space ) { int allocator_id = axom::getDefaultAllocatorID(); #ifdef TRIBOL_USE_UMPIRE - if (mem_space != MemorySpace::Dynamic) - { - allocator_id = axom::getUmpireResourceAllocatorID(toUmpireMemoryType(mem_space)); + if ( mem_space != MemorySpace::Dynamic ) { + allocator_id = axom::getUmpireResourceAllocatorID( toUmpireMemoryType( mem_space ) ); } #else - TRIBOL_UNUSED_VAR(mem_space); + TRIBOL_UNUSED_VAR( mem_space ); #endif return allocator_id; } -inline bool isOnDevice(ExecutionMode exec) +inline bool isOnDevice( ExecutionMode exec ) { - switch (exec) - { -#if defined(TRIBOL_USE_CUDA) + switch ( exec ) { +#if defined( TRIBOL_USE_CUDA ) case ExecutionMode::Cuda: return true; -#elif defined(TRIBOL_USE_HIP) +#elif defined( TRIBOL_USE_HIP ) case ExecutionMode::Hip: return true; #endif @@ -138,17 +130,16 @@ inline bool isOnDevice(ExecutionMode exec) return false; #endif case ExecutionMode::Dynamic: - SLIC_ERROR_ROOT("Dynamic execution mode does not define a memory space location."); + SLIC_ERROR_ROOT( "Dynamic execution mode does not define a memory space location." ); return false; case ExecutionMode::Sequential: return false; default: - SLIC_ERROR_ROOT("Unknown execution mode."); + SLIC_ERROR_ROOT( "Unknown execution mode." ); return false; } } -} // namespace tribol - +} // namespace tribol #endif /* SRC_COMMON_EXECMODEL_HPP_ */ diff --git a/src/tribol/common/LoopExec.hpp b/src/tribol/common/LoopExec.hpp index eecbb8a8..21ccf06d 100644 --- a/src/tribol/common/LoopExec.hpp +++ b/src/tribol/common/LoopExec.hpp @@ -2,7 +2,7 @@ // other Tribol Project Developers. See the top-level LICENSE file for details. // // SPDX-License-Identifier: (MIT) - + #ifndef SRC_COMMON_LOOPEXEC_HPP_ #define SRC_COMMON_LOOPEXEC_HPP_ @@ -20,121 +20,119 @@ #include "RAJA/RAJA.hpp" #endif -namespace tribol -{ +namespace tribol { // Check Tribol has RAJA if we are using CUDA, HIP, or OpenMP. -#if defined(TRIBOL_USE_CUDA) && !defined(TRIBOL_USE_RAJA) +#if defined( TRIBOL_USE_CUDA ) && !defined( TRIBOL_USE_RAJA ) #error "RAJA is required for CUDA support in tribol." -#endif +#endif -#if defined(TRIBOL_USE_HIP) && !defined(TRIBOL_USE_RAJA) +#if defined( TRIBOL_USE_HIP ) && !defined( TRIBOL_USE_RAJA ) #error "RAJA is required for HIP support in tribol." -#endif +#endif -#if defined(TRIBOL_USE_OPENMP) && !defined(TRIBOL_USE_RAJA) +#if defined( TRIBOL_USE_OPENMP ) && !defined( TRIBOL_USE_RAJA ) #error "RAJA is required for OpenMP support in tribol." -#endif +#endif // Check for compatibility with RAJA build configuration. // RAJA_ENABLE_CUDA, RAJA_ENABLE_HIP, and RAJA_ENABLE_OPENMP are defined in RAJA/config.hpp. -#if defined(TRIBOL_USE_CUDA) && !defined(RAJA_ENABLE_CUDA) +#if defined( TRIBOL_USE_CUDA ) && !defined( RAJA_ENABLE_CUDA ) #error "ENABLE_CUDA was specified for tribol, but RAJA was built without RAJA_ENABLE_CUDA." -#endif +#endif -#if defined(TRIBOL_USE_HIP) && !defined(RAJA_ENABLE_HIP) +#if defined( TRIBOL_USE_HIP ) && !defined( RAJA_ENABLE_HIP ) #error "ENABLE_HIP was specified for tribol, but RAJA was built without RAJA_ENABLE_HIP." -#endif +#endif -#if defined(TRIBOL_USE_OPENMP) && !defined(RAJA_ENABLE_OPENMP) +#if defined( TRIBOL_USE_OPENMP ) && !defined( RAJA_ENABLE_OPENMP ) #error "ENABLE_OPENMP was specified for tribol, but RAJA was built without RAJA_ENABLE_OPENMP." -#endif +#endif + +namespace detail { +// SFINAE type for choosing correct RAJA::forall policy at compile time +template +struct forAllType { +}; -namespace detail +template +void forAllImpl( forAllType, IndexT, BODY&& ) { - // SFINAE type for choosing correct RAJA::forall policy at compile time - template - struct forAllType {}; - - template - void forAllImpl(forAllType, IndexT, BODY&&) - { - SLIC_ERROR_ROOT("forAllExec not defined for the given ExecutionMode."); - } + SLIC_ERROR_ROOT( "forAllExec not defined for the given ExecutionMode." ); +} - template - void forAllImpl(forAllType, IndexT, BODY&&) - { - SLIC_ERROR_ROOT("tribol::forAllExec requires an execution mode besides Dynamic."); - } +template +void forAllImpl( forAllType, IndexT, BODY&& ) +{ + SLIC_ERROR_ROOT( "tribol::forAllExec requires an execution mode besides Dynamic." ); +} - template - void forAllImpl(forAllType, IndexT N, BODY&& body) - { +template +void forAllImpl( forAllType, IndexT N, BODY&& body ) +{ #ifdef TRIBOL_USE_RAJA - RAJA::forall(RAJA::TypedRangeSegment(0, N), std::move(body)); + RAJA::forall( RAJA::TypedRangeSegment( 0, N ), std::move( body ) ); #else - for (IndexT i{0}; i < N; ++i) - { - body(i); - } -#endif + for ( IndexT i{ 0 }; i < N; ++i ) { + body( i ); } +#endif +} #ifdef TRIBOL_USE_CUDA - template - typename std::enable_if_t forAllCudaImpl(IndexT N, BODY&& body) - { - RAJA::forall>(RAJA::TypedRangeSegment(0, N), std::move(body)); - } +template +typename std::enable_if_t forAllCudaImpl( IndexT N, BODY&& body ) +{ + RAJA::forall>( RAJA::TypedRangeSegment( 0, N ), std::move( body ) ); +} - template - typename std::enable_if_t forAllCudaImpl(IndexT N, BODY&& body) - { - RAJA::forall>(RAJA::TypedRangeSegment(0, N), std::move(body)); - } +template +typename std::enable_if_t forAllCudaImpl( IndexT N, BODY&& body ) +{ + RAJA::forall>( RAJA::TypedRangeSegment( 0, N ), std::move( body ) ); +} - template - void forAllImpl(forAllType, IndexT N, BODY&& body) - { - forAllCudaImpl(N, std::move(body)); - } +template +void forAllImpl( forAllType, IndexT N, BODY&& body ) +{ + forAllCudaImpl( N, std::move( body ) ); +} #endif #ifdef TRIBOL_USE_HIP - template - typename std::enable_if_t forAllHipImpl(IndexT N, BODY&& body) - { - RAJA::forall>(RAJA::TypedRangeSegment(0, N), std::move(body)); - } +template +typename std::enable_if_t forAllHipImpl( IndexT N, BODY&& body ) +{ + RAJA::forall>( RAJA::TypedRangeSegment( 0, N ), std::move( body ) ); +} - template - typename std::enable_if_t forAllHipImpl(IndexT N, BODY&& body) - { - RAJA::forall>(RAJA::TypedRangeSegment(0, N), std::move(body)); - } +template +typename std::enable_if_t forAllHipImpl( IndexT N, BODY&& body ) +{ + RAJA::forall>( RAJA::TypedRangeSegment( 0, N ), std::move( body ) ); +} - template - void forAllImpl(forAllType, IndexT N, BODY&& body) - { - forAllHipImpl(N, std::move(body)); - } +template +void forAllImpl( forAllType, IndexT N, BODY&& body ) +{ + forAllHipImpl( N, std::move( body ) ); +} #endif #ifdef TRIBOL_USE_OPENMP - template - void forAllImpl(forAllType, IndexT N, BODY&& body) - { - RAJA::forall(RAJA::TypedRangeSegment(0, N), std::move(body)); - } -#endif +template +void forAllImpl( forAllType, IndexT N, BODY&& body ) +{ + RAJA::forall( RAJA::TypedRangeSegment( 0, N ), std::move( body ) ); } +#endif +} // namespace detail #define TRIBOL_BLOCK_SIZE 256 /** * @brief Call a RAJA forall loop with the execution mode known at compile time - * + * * @tparam EXEC Execution mode for loop * @tparam BODY Functor type defining what to do inside the loop * @tparam ASYNC Can the loops be run asynchroniously? @@ -143,14 +141,14 @@ namespace detail * @param body Functor body defining what to do inside the loop */ template -void forAllExec(IndexT N, BODY&& body) +void forAllExec( IndexT N, BODY&& body ) { - detail::forAllImpl(detail::forAllType(), N, std::move(body)); + detail::forAllImpl( detail::forAllType(), N, std::move( body ) ); } /** * @brief Call a RAJA forall loop with the execution mode determined at run time - * + * * @tparam ASYNC Can the loops be run asynchroniously? * @tparam BLOCK_SIZE Block size for kernel (if applicable) * @tparam BODY Functor type defining what to do inside the loop @@ -159,35 +157,33 @@ void forAllExec(IndexT N, BODY&& body) * @param body Functor body defining what to do inside the loop */ template -void forAllExec(ExecutionMode exec_mode, IndexT N, BODY&& body) +void forAllExec( ExecutionMode exec_mode, IndexT N, BODY&& body ) { - switch (exec_mode) - { + switch ( exec_mode ) { case ExecutionMode::Sequential: - return detail::forAllImpl( - detail::forAllType(), N, std::move(body)); + return detail::forAllImpl( detail::forAllType(), N, + std::move( body ) ); #ifdef TRIBOL_USE_OPENMP case ExecutionMode::OpenMP: - return detail::forAllImpl( - detail::forAllType(), N, std::move(body)); + return detail::forAllImpl( detail::forAllType(), N, + std::move( body ) ); #endif #ifdef TRIBOL_USE_CUDA case ExecutionMode::Cuda: - return detail::forAllImpl( - detail::forAllType(), N, std::move(body)); + return detail::forAllImpl( detail::forAllType(), N, + std::move( body ) ); #endif #ifdef TRIBOL_USE_HIP case ExecutionMode::Hip: - return detail::forAllImpl( - detail::forAllType(), N, std::move(body)); + return detail::forAllImpl( detail::forAllType(), N, + std::move( body ) ); #endif default: - SLIC_ERROR_ROOT("Unsupported execution mode in a forAllExec loop."); + SLIC_ERROR_ROOT( "Unsupported execution mode in a forAllExec loop." ); return; } } -} // namespace tribol - +} // namespace tribol #endif /* SRC_COMMON_LOOPEXEC_HPP_ */ diff --git a/src/tribol/common/Parameters.hpp b/src/tribol/common/Parameters.hpp index 527024ac..61ce0f52 100644 --- a/src/tribol/common/Parameters.hpp +++ b/src/tribol/common/Parameters.hpp @@ -2,7 +2,7 @@ // other Tribol Project Developers. See the top-level LICENSE file for details. // // SPDX-License-Identifier: (MIT) - + #ifndef TRIBOL_PARAMETERS_HPP_ #define TRIBOL_PARAMETERS_HPP_ @@ -11,21 +11,19 @@ #include -namespace tribol -{ +namespace tribol { // Internal Helper Method for Enums -namespace -{ +namespace { //------------------------------------------------------------------------------ inline bool in_range( int target, int N ) { // NOTE: assumes indexing starts from 0 - return( (target >= 0) && ( target < N ) ); + return ( ( target >= 0 ) && ( target < N ) ); } -} // end anonymous namespace +} // end anonymous namespace constexpr int ANY_MESH = -1; @@ -34,26 +32,26 @@ constexpr int ANY_MESH = -1; */ enum LoggingLevel { - TRIBOL_UNDEFINED, ///! Undefined - TRIBOL_DEBUG, ///! Debug and higher - TRIBOL_INFO, ///! Info and higher - TRIBOL_WARNING, ///! Warning and higher - TRIBOL_ERROR, ///! Errors only - NUM_LOGGING_LEVELS = TRIBOL_ERROR + TRIBOL_UNDEFINED, ///! Undefined + TRIBOL_DEBUG, ///! Debug and higher + TRIBOL_INFO, ///! Info and higher + TRIBOL_WARNING, ///! Warning and higher + TRIBOL_ERROR, ///! Errors only + NUM_LOGGING_LEVELS = TRIBOL_ERROR }; /*! - * \brief Enumerates the interface element types + * \brief Enumerates the interface element types */ enum InterfaceElementType { - UNDEFINED_ELEMENT, ///! Undefined - LINEAR_EDGE, ///! 1D linear edge - LINEAR_TRIANGLE, ///! 2D linear triangle - LINEAR_QUAD, ///! 2D linear quadrilateral - LINEAR_TET, ///! 3D linear tetrahedron (volume methods and test mesh class) - LINEAR_HEX, ///! 3D linear hexahedron (volume methods and test mesh class) - NUM_CONTACT_ELEMENTS = LINEAR_HEX + UNDEFINED_ELEMENT, ///! Undefined + LINEAR_EDGE, ///! 1D linear edge + LINEAR_TRIANGLE, ///! 2D linear triangle + LINEAR_QUAD, ///! 2D linear quadrilateral + LINEAR_TET, ///! 3D linear tetrahedron (volume methods and test mesh class) + LINEAR_HEX, ///! 3D linear hexahedron (volume methods and test mesh class) + NUM_CONTACT_ELEMENTS = LINEAR_HEX }; /*! @@ -61,39 +59,39 @@ enum InterfaceElementType */ enum VisType { - UNDEFINED_VIS, ///! Undefined - VIS_MESH, ///! Print registered mesh(es) - VIS_FACES, ///! Print active interface-faces (method specific) - VIS_OVERLAPS, ///! Print interface face-face overlaps - VIS_MESH_AND_OVERLAPS, ///! Print registered mesh(es) and interface overlaps - VIS_FACES_AND_OVERLAPS, ///! Print active interface-faces and face-face overlaps - VIS_MESH_FACES_AND_OVERLAPS, ///! Print registered mesh(es), active faces, and overlaps - NUM_VIS_TYPES = VIS_MESH_FACES_AND_OVERLAPS + UNDEFINED_VIS, ///! Undefined + VIS_MESH, ///! Print registered mesh(es) + VIS_FACES, ///! Print active interface-faces (method specific) + VIS_OVERLAPS, ///! Print interface face-face overlaps + VIS_MESH_AND_OVERLAPS, ///! Print registered mesh(es) and interface overlaps + VIS_FACES_AND_OVERLAPS, ///! Print active interface-faces and face-face overlaps + VIS_MESH_FACES_AND_OVERLAPS, ///! Print registered mesh(es), active faces, and overlaps + NUM_VIS_TYPES = VIS_MESH_FACES_AND_OVERLAPS }; /*! * \brief Enumerates the contact modes that specify paired topologies in an interaction * * - * The contact mode enumerates the two-sided pairing of mesh entities - * (element topologies) in an interaction. These may be combinations + * The contact mode enumerates the two-sided pairing of mesh entities + * (element topologies) in an interaction. These may be combinations * of surface and volume interactions. */ enum ContactMode { - SURFACE_TO_SURFACE, ///! surface-to-surface interaction - SURFACE_TO_SURFACE_CONFORMING, ///! conforming surface-to-surface interaction - SURFACE_TO_VOLUME, ///! surface-to-volume interaction - VOLUME_TO_VOLUME, ///! volume-to-volume interaction + SURFACE_TO_SURFACE, ///! surface-to-surface interaction + SURFACE_TO_SURFACE_CONFORMING, ///! conforming surface-to-surface interaction + SURFACE_TO_VOLUME, ///! surface-to-volume interaction + VOLUME_TO_VOLUME, ///! volume-to-volume interaction NUM_CONTACT_MODES }; /*! - * \brief Enumerates the available contact cases + * \brief Enumerates the available contact cases * * The contact case enumerates specializations, or Tribol use-cases that require * special algorithmic considerations beyond standard Lagrangian contact. Note, - * the use of auto-contact cannot be used with the tied contact variants. This + * the use of auto-contact cannot be used with the tied contact variants. This * may be a limitation down the road, but for now, the very use case of TIED_* * implies that a host-code knows what two surfaces are tied in a given interaction, * and is able to explicitly specify these when registering the meshes, coupling schemes, @@ -101,11 +99,11 @@ enum ContactMode */ enum ContactCase { - NO_CASE, ///! No case specified for chosen mode and/or method - AUTO, ///! Auto contact - TIED_NORMAL, ///! Tied in the surface normal direction - TIED_FULL, ///! Tied in the surface normal and tangential directions - NO_SLIDING, ///! User may specify no sliding, simplifying search update + NO_CASE, ///! No case specified for chosen mode and/or method + AUTO, ///! Auto contact + TIED_NORMAL, ///! Tied in the surface normal direction + TIED_FULL, ///! Tied in the surface normal and tangential directions + NO_SLIDING, ///! User may specify no sliding, simplifying search update NUM_CONTACT_CASES }; @@ -115,12 +113,12 @@ enum ContactCase * The contact method is the numerical method used to discretize the * contact surface in order to integrate the weak form contact integrals. */ -enum ContactMethod // all mortar methods go first +enum ContactMethod // all mortar methods go first { - SINGLE_MORTAR, ///! Single mortar per Puso 2003 - ALIGNED_MORTAR, ///! Aligned mortar to be used with ContactCase = NO_SLIDING - MORTAR_WEIGHTS, ///! Method that only returns mortar weights per single mortar method - COMMON_PLANE, ///! Common plane method, currently with single integration point + SINGLE_MORTAR, ///! Single mortar per Puso 2003 + ALIGNED_MORTAR, ///! Aligned mortar to be used with ContactCase = NO_SLIDING + MORTAR_WEIGHTS, ///! Method that only returns mortar weights per single mortar method + COMMON_PLANE, ///! Common plane method, currently with single integration point NUM_CONTACT_METHODS }; @@ -133,11 +131,12 @@ enum ContactMethod // all mortar methods go first */ enum ContactModel { - NO_CONTACT, ///! No contact - FRICTIONLESS, ///! Frictionless, normal contact only - COULOMB, ///! Coulomb friction model, not supported - ADHESION_SEPARATION_SCALAR_LAW, ///! Scalar pressure law for the separation of adhered surfaces (Used with tied contact) - NULL_MODEL, ///! Null model, for use with ContactMethod = MORTAR_WEIGHTS + NO_CONTACT, ///! No contact + FRICTIONLESS, ///! Frictionless, normal contact only + COULOMB, ///! Coulomb friction model, not supported + ADHESION_SEPARATION_SCALAR_LAW, ///! Scalar pressure law for the separation of adhered surfaces (Used with tied + /// contact) + NULL_MODEL, ///! Null model, for use with ContactMethod = MORTAR_WEIGHTS NUM_CONTACT_MODELS }; @@ -149,9 +148,9 @@ enum ContactModel */ enum EnforcementMethod { - PENALTY, ///! Penalty enforcement method for gap only - LAGRANGE_MULTIPLIER, ///! Lagrange multiplier with system output - NULL_ENFORCEMENT, ///! Null enforcement, for use with ContactMethod = MORTAR_WEIGHTS + PENALTY, ///! Penalty enforcement method for gap only + LAGRANGE_MULTIPLIER, ///! Lagrange multiplier with system output + NULL_ENFORCEMENT, ///! Null enforcement, for use with ContactMethod = MORTAR_WEIGHTS NUM_ENFORCEMENT_METHODS }; @@ -168,13 +167,13 @@ enum BinningMethod }; /*! - * \brief Enumerates the available penalty enforcement options + * \brief Enumerates the available penalty enforcement options */ enum PenaltyConstraintType { - KINEMATIC, - KINEMATIC_AND_RATE, - NUM_PENALTY_OPTIONS + KINEMATIC, + KINEMATIC_AND_RATE, + NUM_PENALTY_OPTIONS }; /*! @@ -182,9 +181,9 @@ enum PenaltyConstraintType */ enum KinematicPenaltyCalculation { - KINEMATIC_CONSTANT, ///! Constant penalty stiffness applied to all contacting face-pairs - KINEMATIC_ELEMENT, ///! Element-wise penalty stiffness calculation - NUM_KINEMATIC_PENALTY_CALCULATION + KINEMATIC_CONSTANT, ///! Constant penalty stiffness applied to all contacting face-pairs + KINEMATIC_ELEMENT, ///! Element-wise penalty stiffness calculation + NUM_KINEMATIC_PENALTY_CALCULATION }; /*! @@ -192,10 +191,10 @@ enum KinematicPenaltyCalculation */ enum RatePenaltyCalculation { - NO_RATE_PENALTY, - RATE_CONSTANT, ///! Constant rate penalty stiffness - RATE_PERCENT, ///! Rate penalty stiffness as a percentage of the kinematic penalty stiffness - NUM_RATE_PENALTY_CALCULATION + NO_RATE_PENALTY, + RATE_CONSTANT, ///! Constant rate penalty stiffness + RATE_PERCENT, ///! Rate penalty stiffness as a percentage of the kinematic penalty stiffness + NUM_RATE_PENALTY_CALCULATION }; /*! @@ -203,14 +202,14 @@ enum RatePenaltyCalculation */ enum RealElementFields { - UNDEFINED_REAL_ELEMENT_FIELD, - KINEMATIC_CONSTANT_STIFFNESS, ///! Constant kinematic penalty stiffness - RATE_CONSTANT_STIFFNESS, ///! Constant rate penalty stiffness - RATE_PERCENT_STIFFNESS, ///! Percent rate penalty stiffness - BULK_MODULUS, ///! Element bulk modulus - YOUNGS_MODULUS, ///! Element Young's modulus - ELEMENT_THICKNESS, ///! Element thickness in contact normal direction - NUM_REAL_ELEMENT_FIELDS = ELEMENT_THICKNESS + UNDEFINED_REAL_ELEMENT_FIELD, + KINEMATIC_CONSTANT_STIFFNESS, ///! Constant kinematic penalty stiffness + RATE_CONSTANT_STIFFNESS, ///! Constant rate penalty stiffness + RATE_PERCENT_STIFFNESS, ///! Percent rate penalty stiffness + BULK_MODULUS, ///! Element bulk modulus + YOUNGS_MODULUS, ///! Element Young's modulus + ELEMENT_THICKNESS, ///! Element thickness in contact normal direction + NUM_REAL_ELEMENT_FIELDS = ELEMENT_THICKNESS }; /*! @@ -218,7 +217,7 @@ enum RealElementFields */ enum IntElementFields { - UNDEFINED_INT_ELEMENT_FIELD + UNDEFINED_INT_ELEMENT_FIELD }; /*! @@ -226,7 +225,7 @@ enum IntElementFields */ enum IntNodalFields { - UNDEFINED_INT_NODAL_FIELD + UNDEFINED_INT_NODAL_FIELD }; /*! @@ -234,8 +233,8 @@ enum IntNodalFields */ enum PolyInteg { - SINGLE_POINT, ///! Single point integration at centroid of polygon - FULL_TRI_DECOMP, ///! Full integration using triangular decomposition + SINGLE_POINT, ///! Single point integration at centroid of polygon + FULL_TRI_DECOMP, ///! Full integration using triangular decomposition NUM_INTEG_RULES }; @@ -244,10 +243,10 @@ enum PolyInteg */ enum IntegMethod { - TWB, ///! Taylor-Wingate-Bos integration on low order triangles - TRIBOL_INV_ISO, ///! Inverse isoparametric mapping of integration points to parent space - MFEM, ///! MFEM integration rule and integration methods - NUM_INTEG_METHODS + TWB, ///! Taylor-Wingate-Bos integration on low order triangles + TRIBOL_INV_ISO, ///! Inverse isoparametric mapping of integration points to parent space + MFEM, ///! MFEM integration rule and integration methods + NUM_INTEG_METHODS }; /*! @@ -255,10 +254,10 @@ enum IntegMethod */ enum class BlockSpace { - MORTAR, ///! The coordinate space on the mortar contact surface - NONMORTAR, ///! The coordinate space on the nonmortar contact surface - LAGRANGE_MULTIPLIER, ///! The Lagrange multiplier space - NUM_BLOCK_SPACES + MORTAR, ///! The coordinate space on the mortar contact surface + NONMORTAR, ///! The coordinate space on the nonmortar contact surface + LAGRANGE_MULTIPLIER, ///! The Lagrange multiplier space + NUM_BLOCK_SPACES }; /*! @@ -266,11 +265,11 @@ enum class BlockSpace */ enum class ImplicitEvalMode { - MORTAR_JACOBIAN, ///! Contact Jacobian evaluation only - MORTAR_RESIDUAL, ///! Contact residual evaluation only - MORTAR_RESIDUAL_JACOBIAN, ///! Contact residual AND Jacobian evaluation - MORTAR_GAP, ///! Contact gap evaluation only - MORTAR_WEIGHTS_EVAL ///! Mortar weight evaluation only + MORTAR_JACOBIAN, ///! Contact Jacobian evaluation only + MORTAR_RESIDUAL, ///! Contact residual evaluation only + MORTAR_RESIDUAL_JACOBIAN, ///! Contact residual AND Jacobian evaluation + MORTAR_GAP, ///! Contact gap evaluation only + MORTAR_WEIGHTS_EVAL ///! Mortar weight evaluation only }; /*! @@ -278,9 +277,9 @@ enum class ImplicitEvalMode */ enum class SparseMode { - MFEM_INDEX_SET, ///! initialize mfem sparse matrix with I, J, and data - MFEM_LINKED_LIST, ///! initialize mfem sparse matrix with flexible, linked list option - MFEM_ELEMENT_DENSE ///! Stores element Jacobian contributions in an ArrayT of mfem::DenseMatrixs + MFEM_INDEX_SET, ///! initialize mfem sparse matrix with I, J, and data + MFEM_LINKED_LIST, ///! initialize mfem sparse matrix with flexible, linked list option + MFEM_ELEMENT_DENSE ///! Stores element Jacobian contributions in an ArrayT of mfem::DenseMatrixs }; /*! @@ -288,12 +287,12 @@ enum class SparseMode */ enum FaceOrderType { - LINEAR, ///! Linear Lagrange (default) - QUADRATIC_LAGRANGE, ///! Quadratic 9-node Lagrange face - QUADRATIC_SERENDIPITY, ///! Quadratic 8-node serendipity face - CUBIC_LAGRANGE, ///! Cubic Lagrange face - NUM_ORDERS_TYPES, - UNDEFINED_ORDER_TYPE = NUM_ORDERS_TYPES + LINEAR, ///! Linear Lagrange (default) + QUADRATIC_LAGRANGE, ///! Quadratic 9-node Lagrange face + QUADRATIC_SERENDIPITY, ///! Quadratic 8-node serendipity face + CUBIC_LAGRANGE, ///! Cubic Lagrange face + NUM_ORDERS_TYPES, + UNDEFINED_ORDER_TYPE = NUM_ORDERS_TYPES }; /*! @@ -301,10 +300,10 @@ enum FaceOrderType */ enum BasisEvalType { - UNDEFINED_BASIS_EVAL_TYPE, - PARENT, ///! Evaluate basis in parent space - PHYSICAL, ///! Evaluate basis in physical space - NUM_BASIS_EVAL_TYPES = PHYSICAL + UNDEFINED_BASIS_EVAL_TYPE, + PARENT, ///! Evaluate basis in parent space + PHYSICAL, ///! Evaluate basis in physical space + NUM_BASIS_EVAL_TYPES = PHYSICAL }; /*! @@ -312,12 +311,13 @@ enum BasisEvalType */ enum FaceGeomError { - NO_FACE_GEOM_ERROR, ///! No face geometry error - FACE_ORIENTATION, ///! Face vertices not ordered consistent with outward unit normal - INVALID_FACE_INPUT, ///! Invalid input - DEGENERATE_OVERLAP, ///! Issues with overlap calculation resulting in degenerate overlap - FACE_VERTEX_INDEX_EXCEEDS_OVERLAP_VERTICES, ///! Very specific debug indexing error where face vertex count exceeds overlap vertex count in cg routine - NUM_FACE_GEOM_ERRORS + NO_FACE_GEOM_ERROR, ///! No face geometry error + FACE_ORIENTATION, ///! Face vertices not ordered consistent with outward unit normal + INVALID_FACE_INPUT, ///! Invalid input + DEGENERATE_OVERLAP, ///! Issues with overlap calculation resulting in degenerate overlap + FACE_VERTEX_INDEX_EXCEEDS_OVERLAP_VERTICES, ///! Very specific debug indexing error where face vertex count exceeds + /// overlap vertex count in cg routine + NUM_FACE_GEOM_ERRORS }; /*! @@ -325,10 +325,10 @@ enum FaceGeomError */ enum ModeError { - INVALID_MODE, - NO_MODE_IMPLEMENTATION, - NO_MODE_ERROR, - NUM_MODE_ERRORS + INVALID_MODE, + NO_MODE_IMPLEMENTATION, + NO_MODE_ERROR, + NUM_MODE_ERRORS }; /*! @@ -336,11 +336,11 @@ enum ModeError */ enum CaseError { - INVALID_CASE, - NO_CASE_IMPLEMENTATION, - INVALID_CASE_DATA, - NO_CASE_ERROR, - NUM_CASE_ERRORS + INVALID_CASE, + NO_CASE_IMPLEMENTATION, + INVALID_CASE_DATA, + NO_CASE_ERROR, + NUM_CASE_ERRORS }; /*! @@ -348,16 +348,16 @@ enum CaseError */ enum MethodError { - INVALID_ELEMENT_TYPE, - INVALID_METHOD, - NO_METHOD_IMPLEMENTATION, - DIFFERENT_FACE_TYPES, - SAME_MESH_IDS, - SAME_MESH_IDS_INVALID_DIM, - INVALID_DIM, - NULL_NODAL_RESPONSE, - NO_METHOD_ERROR, - NUM_METHOD_ERRORS + INVALID_ELEMENT_TYPE, + INVALID_METHOD, + NO_METHOD_IMPLEMENTATION, + DIFFERENT_FACE_TYPES, + SAME_MESH_IDS, + SAME_MESH_IDS_INVALID_DIM, + INVALID_DIM, + NULL_NODAL_RESPONSE, + NO_METHOD_ERROR, + NUM_METHOD_ERRORS }; /*! @@ -365,11 +365,11 @@ enum MethodError */ enum ModelError { - INVALID_MODEL, - NO_MODEL_IMPLEMENTATION, - NO_MODEL_IMPLEMENTATION_FOR_REGISTERED_METHOD, - NO_MODEL_ERROR, - NUM_MODEL_ERRORS + INVALID_MODEL, + NO_MODEL_IMPLEMENTATION, + NO_MODEL_IMPLEMENTATION_FOR_REGISTERED_METHOD, + NO_MODEL_ERROR, + NUM_MODEL_ERRORS }; /*! @@ -377,15 +377,15 @@ enum ModelError */ enum EnforcementError { - INVALID_ENFORCEMENT, - INVALID_ENFORCEMENT_FOR_REGISTERED_METHOD, - INVALID_ENFORCEMENT_OPTION, - OPTIONS_NOT_SET, - NO_ENFORCEMENT_IMPLEMENTATION, - NO_ENFORCEMENT_IMPLEMENTATION_FOR_REGISTERED_METHOD, - NO_ENFORCEMENT_IMPLEMENTATION_FOR_REGISTERED_OPTION, - NO_ENFORCEMENT_ERROR, - NUM_ENFORCEMENT_ERRORS + INVALID_ENFORCEMENT, + INVALID_ENFORCEMENT_FOR_REGISTERED_METHOD, + INVALID_ENFORCEMENT_OPTION, + OPTIONS_NOT_SET, + NO_ENFORCEMENT_IMPLEMENTATION, + NO_ENFORCEMENT_IMPLEMENTATION_FOR_REGISTERED_METHOD, + NO_ENFORCEMENT_IMPLEMENTATION_FOR_REGISTERED_OPTION, + NO_ENFORCEMENT_ERROR, + NUM_ENFORCEMENT_ERRORS }; /*! @@ -393,9 +393,9 @@ enum EnforcementError */ enum EnforcementDataErrors { - ERROR_IN_REGISTERED_ENFORCEMENT_DATA, - NO_ENFORCEMENT_DATA_ERROR, - NUM_ENFORCEMENT_DATA_ERRORS + ERROR_IN_REGISTERED_ENFORCEMENT_DATA, + NO_ENFORCEMENT_DATA_ERROR, + NUM_ENFORCEMENT_DATA_ERRORS }; /*! @@ -403,11 +403,11 @@ enum EnforcementDataErrors */ enum CaseInfo { - SPECIFYING_NO_SLIDING_WITH_REGISTERED_MODE, - SPECIFYING_NO_SLIDING_WITH_REGISTERED_METHOD, - SPECIFYING_NONE_WITH_REGISTERED_METHOD, - NO_CASE_INFO, - NUM_CASE_INFO + SPECIFYING_NO_SLIDING_WITH_REGISTERED_MODE, + SPECIFYING_NO_SLIDING_WITH_REGISTERED_METHOD, + SPECIFYING_NONE_WITH_REGISTERED_METHOD, + NO_CASE_INFO, + NUM_CASE_INFO }; /*! @@ -415,88 +415,84 @@ enum CaseInfo */ enum EnforcementInfo { - SPECIFYING_NULL_ENFORCEMENT_WITH_REGISTERED_METHOD, - NO_ENFORCEMENT_INFO, - NUM_ENFORCEMENT_INFO + SPECIFYING_NULL_ENFORCEMENT_WITH_REGISTERED_METHOD, + NO_ENFORCEMENT_INFO, + NUM_ENFORCEMENT_INFO }; /*! * \brief Struct to hold Lagrange multiplier enforcement and implicit evaluation options */ -struct LagrangeMultiplierImplicitOptions -{ -public: - bool enforcement_option_set {false}; - - ImplicitEvalMode eval_mode; ///! Implicit evaluation mode for residual, jacobian and gaps - SparseMode sparse_mode; ///! Mode for assembling sparse matrix contributions +struct LagrangeMultiplierImplicitOptions { + public: + bool enforcement_option_set{ false }; + + ImplicitEvalMode eval_mode; ///! Implicit evaluation mode for residual, jacobian and gaps + SparseMode sparse_mode; ///! Mode for assembling sparse matrix contributions }; /*! * \brief Struct to hold penalty enforcement options */ -struct PenaltyEnforcementOptions -{ -public: - PenaltyConstraintType constraint_type; - KinematicPenaltyCalculation kinematic_calculation; - RatePenaltyCalculation rate_calculation; - - bool constraint_type_set {false}; - bool kinematic_calc_set {false}; - bool rate_calc_set {false}; +struct PenaltyEnforcementOptions { + public: + PenaltyConstraintType constraint_type; + KinematicPenaltyCalculation kinematic_calculation; + RatePenaltyCalculation rate_calculation; + + bool constraint_type_set{ false }; + bool kinematic_calc_set{ false }; + bool rate_calc_set{ false }; - RealT tiny_length {1.e-12}; ///! Small length to avoid division by zero - RealT tiny_penalty {1.e-12}; ///! Small penalty to avoid division by zero + RealT tiny_length{ 1.e-12 }; ///! Small length to avoid division by zero + RealT tiny_penalty{ 1.e-12 }; ///! Small penalty to avoid division by zero }; /*! * \brief Struct wrapping constraint enforcement options */ -struct EnforcementOptions -{ -public: - PenaltyEnforcementOptions penalty_options; - LagrangeMultiplierImplicitOptions lm_implicit_options; +struct EnforcementOptions { + public: + PenaltyEnforcementOptions penalty_options; + LagrangeMultiplierImplicitOptions lm_implicit_options; }; - /*! - * \brief Coupling scheme parameters struct - */ - struct Parameters - { - CommT problem_comm = TRIBOL_COMM_WORLD; ///! MPI communicator for the problem +/*! + * \brief Coupling scheme parameters struct + */ +struct Parameters { + CommT problem_comm = TRIBOL_COMM_WORLD; ///! MPI communicator for the problem - RealT overlap_area_frac = 1.0e-8; ///! Ratio of overlap area to largest face area for contact inclusion - RealT gap_tol_ratio = 1.0e-12; ///! Ratio for determining tolerance for active contact gaps - RealT gap_separation_ratio = 0.75; ///! Ratio for determining allowable separation in geometric filtering - RealT gap_tied_tol = 0.1; ///! Ratio for determining max separation tied contact can support - RealT len_collapse_ratio = 1.0e-8; ///! Ratio of face length providing topology collapse length tolerance - RealT projection_ratio = 1.0e-10; ///! Ratio for defining nonzero projections - RealT auto_contact_pen_frac = 0.95; ///! Max allowable interpenetration as percent of element thickness for contact candidacy - RealT timestep_pen_frac = 3.0e-1; ///! Max allowable interpenetration as percent of element thickness prior to triggering timestep vote - RealT timestep_scale = 1.0; ///! Scale factor (>0) applied to the timestep vote giving users some control over the vote + RealT overlap_area_frac = 1.0e-8; ///! Ratio of overlap area to largest face area for contact inclusion + RealT gap_tol_ratio = 1.0e-12; ///! Ratio for determining tolerance for active contact gaps + RealT gap_separation_ratio = 0.75; ///! Ratio for determining allowable separation in geometric filtering + RealT gap_tied_tol = 0.1; ///! Ratio for determining max separation tied contact can support + RealT len_collapse_ratio = 1.0e-8; ///! Ratio of face length providing topology collapse length tolerance + RealT projection_ratio = 1.0e-10; ///! Ratio for defining nonzero projections + RealT auto_contact_pen_frac = + 0.95; ///! Max allowable interpenetration as percent of element thickness for contact candidacy + RealT timestep_pen_frac = + 3.0e-1; ///! Max allowable interpenetration as percent of element thickness prior to triggering timestep vote + RealT timestep_scale = + 1.0; ///! Scale factor (>0) applied to the timestep vote giving users some control over the vote - int vis_cycle_incr = 100; ///! Frequency for visualizations dumps - VisType vis_type = VIS_OVERLAPS; ///! Type of interface physics visualization output - bool enable_timestep_vote = false; ///! True if host-code desires the timestep vote to be calculated and returned + int vis_cycle_incr = 100; ///! Frequency for visualizations dumps + VisType vis_type = VIS_OVERLAPS; ///! Type of interface physics visualization output + bool enable_timestep_vote = false; ///! True if host-code desires the timestep vote to be calculated and returned - bool auto_interpen_check = false; ///! True if the auto-contact interpenetration check is used for full-overlap pairs + bool auto_interpen_check = false; ///! True if the auto-contact interpenetration check is used for full-overlap pairs - double auto_contact_len_scale_factor; ///! Scale factor applied to element thickness for auto contact length scale - - // Interpenetration check for auto-contact. If true, this will check a full-overlap - // face-pair configuration in the computational geoemtry routines to preclude - // auto-contact of opposite sides of thin structures/plates. If the full-overlap - // interpenetration kinematic gap is more than the smallest thickness of the - // constituent face elements, then we don't consider the face-pair a contact candidate. - // Note, auto-contact will require registration of element thicknesses. - bool auto_contact_check = false; ///! True if auto-contact checks should be enabled - }; + double auto_contact_len_scale_factor; ///! Scale factor applied to element thickness for auto contact length scale -} // namespace tribol + // Interpenetration check for auto-contact. If true, this will check a full-overlap + // face-pair configuration in the computational geoemtry routines to preclude + // auto-contact of opposite sides of thin structures/plates. If the full-overlap + // interpenetration kinematic gap is more than the smallest thickness of the + // constituent face elements, then we don't consider the face-pair a contact candidate. + // Note, auto-contact will require registration of element thicknesses. + bool auto_contact_check = false; ///! True if auto-contact checks should be enabled +}; +} // namespace tribol #endif /* TRIBOL_PARAMETERS_HPP_ */ - - diff --git a/src/tribol/config.hpp.in b/src/tribol/config.hpp.in index ca22f881..3c1ee9be 100644 --- a/src/tribol/config.hpp.in +++ b/src/tribol/config.hpp.in @@ -11,14 +11,13 @@ * since it might be included from a C file */ - /* * Version Information */ -#define TRIBOL_VERSION_MAJOR @TRIBOL_VERSION_MAJOR@ -#define TRIBOL_VERSION_MINOR @TRIBOL_VERSION_MINOR@ -#define TRIBOL_VERSION_PATCH @TRIBOL_VERSION_PATCH@ -#define TRIBOL_VERSION_FULL "@TRIBOL_VERSION_FULL@" +#define TRIBOL_VERSION_MAJOR @TRIBOL_VERSION_MAJOR @ +#define TRIBOL_VERSION_MINOR @TRIBOL_VERSION_MINOR @ +#define TRIBOL_VERSION_PATCH @TRIBOL_VERSION_PATCH @ +#define TRIBOL_VERSION_FULL "@TRIBOL_VERSION_FULL@" /* * Directories diff --git a/src/tribol/geom/ContactPlane.cpp b/src/tribol/geom/ContactPlane.cpp index b9710927..03d7ed3b 100644 --- a/src/tribol/geom/ContactPlane.cpp +++ b/src/tribol/geom/ContactPlane.cpp @@ -11,7 +11,7 @@ #include "tribol/mesh/CouplingScheme.hpp" #include "tribol/utils/Math.hpp" -#include "axom/core.hpp" +#include "axom/core.hpp" #include "axom/slic.hpp" #include @@ -19,94 +19,71 @@ #include #include -namespace tribol -{ +namespace tribol { //------------------------------------------------------------------------------ // free functions //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE FaceGeomError CheckInterfacePair( InterfacePair& pair, - const MeshData::Viewer& mesh1, - const MeshData::Viewer& mesh2, - const Parameters& params, - ContactMethod cMethod, - ContactCase TRIBOL_UNUSED_PARAM(cCase), - bool& isInteracting, - ArrayViewT& planes_2d, - ArrayViewT& planes_3d, - IndexT* plane_ct ) +TRIBOL_HOST_DEVICE FaceGeomError CheckInterfacePair( InterfacePair& pair, const MeshData::Viewer& mesh1, + const MeshData::Viewer& mesh2, const Parameters& params, + ContactMethod cMethod, ContactCase TRIBOL_UNUSED_PARAM( cCase ), + bool& isInteracting, ArrayViewT& planes_2d, + ArrayViewT& planes_3d, IndexT* plane_ct ) { isInteracting = false; - // note: will likely need the ContactCase for specialized + // note: will likely need the ContactCase for specialized // geometry check(s)/routine(s) - switch (cMethod) - { + switch ( cMethod ) { case SINGLE_MORTAR: case MORTAR_WEIGHTS: - case COMMON_PLANE: - { - // set whether full overlap is to be used or not. Note: SINGLE_MORTAR and + case COMMON_PLANE: { + // set whether full overlap is to be used or not. Note: SINGLE_MORTAR and // MORTAR_WEIGHTS drop into this 'case', so the method still needs to be checked - const bool full = (cMethod == COMMON_PLANE) ? false : true; - const bool interpenOverlap = (full) ? false : true; - const bool intermediatePlane = (cMethod == COMMON_PLANE) ? true : false; + const bool full = ( cMethod == COMMON_PLANE ) ? false : true; + const bool interpenOverlap = ( full ) ? false : true; + const bool intermediatePlane = ( cMethod == COMMON_PLANE ) ? true : false; // Perform contact plane specific computations (2D and 3D) - if (mesh1.spatialDimension() == 3) - { - - ContactPlane3D cpTemp( &pair, params.overlap_area_frac, interpenOverlap, intermediatePlane); + if ( mesh1.spatialDimension() == 3 ) { + ContactPlane3D cpTemp( &pair, params.overlap_area_frac, interpenOverlap, intermediatePlane ); FaceGeomError face_err = CheckFacePair( cpTemp, mesh1, mesh2, params, full ); - - if (face_err != NO_FACE_GEOM_ERROR) - { - isInteracting = false; + if ( face_err != NO_FACE_GEOM_ERROR ) { + isInteracting = false; #ifdef TRIBOL_USE_HOST - SLIC_DEBUG("face_err: " << face_err ); + SLIC_DEBUG( "face_err: " << face_err ); #endif - } - else if (cpTemp.m_inContact) - { + } else if ( cpTemp.m_inContact ) { #ifdef TRIBOL_USE_RAJA - auto idx = RAJA::atomicInc(plane_ct); + auto idx = RAJA::atomicInc( plane_ct ); #else auto idx = *plane_ct; - ++(*plane_ct); + ++( *plane_ct ); #endif - planes_3d[idx] = std::move(cpTemp); + planes_3d[idx] = std::move( cpTemp ); isInteracting = true; - } - else - { + } else { isInteracting = false; } return face_err; - } - else - { - ContactPlane2D cpTemp( &pair, params.overlap_area_frac, interpenOverlap, intermediatePlane); + } else { + ContactPlane2D cpTemp( &pair, params.overlap_area_frac, interpenOverlap, intermediatePlane ); FaceGeomError edge_err = CheckEdgePair( cpTemp, mesh1, mesh2, params, full ); - if (edge_err != NO_FACE_GEOM_ERROR) - { + if ( edge_err != NO_FACE_GEOM_ERROR ) { isInteracting = false; - } - else if (cpTemp.m_inContact) - { + } else if ( cpTemp.m_inContact ) { #ifdef TRIBOL_USE_RAJA - auto idx = RAJA::atomicInc(plane_ct); + auto idx = RAJA::atomicInc( plane_ct ); #else auto idx = *plane_ct; - ++(*plane_ct); + ++( *plane_ct ); #endif - planes_2d[idx] = std::move(cpTemp); + planes_2d[idx] = std::move( cpTemp ); isInteracting = true; - } - else - { + } else { isInteracting = false; } return edge_err; @@ -114,2183 +91,1966 @@ TRIBOL_HOST_DEVICE FaceGeomError CheckInterfacePair( InterfacePair& pair, break; } - case ALIGNED_MORTAR: - { + case ALIGNED_MORTAR: { // Note: this is checked by CouplingScheme::isValidMethod() - if (mesh1.spatialDimension() == 3) - { + if ( mesh1.spatialDimension() == 3 ) { // TODO refactor to make consistent with CheckFacePair, SRW ContactPlane3D cpTemp = CheckAlignedFacePair( pair, mesh1, mesh2, params ); - if (cpTemp.m_inContact) - { + if ( cpTemp.m_inContact ) { #ifdef TRIBOL_USE_RAJA - auto idx = RAJA::atomicInc(plane_ct); + auto idx = RAJA::atomicInc( plane_ct ); #else - auto idx = (*plane_ct); - ++(*plane_ct); + auto idx = ( *plane_ct ); + ++( *plane_ct ); #endif - planes_3d[idx] = std::move(cpTemp); + planes_3d[idx] = std::move( cpTemp ); isInteracting = true; - } - else - { + } else { isInteracting = false; } return NO_FACE_GEOM_ERROR; - } - else - { + } else { // Note: this is checked by CouplingScheme::isValidMethod() // SLIC_ERROR_IF(true, "2D ALIGNED_MORTAR not yet implemented."); } break; } - default: - { + default: { // don't do anything break; } } - return NO_FACE_GEOM_ERROR; // quiet compiler + return NO_FACE_GEOM_ERROR; // quiet compiler -} // end CheckInterfacePair() +} // end CheckInterfacePair() //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE bool FaceInterCheck( const MeshData::Viewer& mesh1, const MeshData::Viewer& mesh2, - int fId1, int fId2, RealT tol, bool& allVerts ) +TRIBOL_HOST_DEVICE bool FaceInterCheck( const MeshData::Viewer& mesh1, const MeshData::Viewer& mesh2, int fId1, + int fId2, RealT tol, bool& allVerts ) { - bool check = false; - allVerts = false; - - // loop over vertices on face 2 - int k = 0; - for (int i=0; i -tol) - { - check = true; - ++k; - } + bool check = false; + allVerts = false; + + // loop over vertices on face 2 + int k = 0; + for ( int i = 0; i < mesh2.numberOfNodesPerElement(); ++i ) { + // get ith face 2 node id + const int f2NodeId = mesh2.getGlobalNodeId( fId2, i ); + + // compute components of vector between face 1 center and face 2 vertex + RealT vX = mesh1.getElementCentroids()[0][fId1] - mesh2.getPosition()[0][f2NodeId]; + RealT vY = mesh1.getElementCentroids()[1][fId1] - mesh2.getPosition()[1][f2NodeId]; + RealT vZ = mesh1.getElementCentroids()[2][fId1] - mesh2.getPosition()[2][f2NodeId]; + + // project the vector onto face 1 normal + RealT proj = vX * mesh1.getElementNormals()[0][fId1] + vY * mesh1.getElementNormals()[1][fId1] + + vZ * mesh1.getElementNormals()[2][fId1]; + + // if a node of face 2 is on the other side of the plane defined by face 1 the + // projection will be positive. If a node on face 2 lies on face 1 the projection + // will be zero. If a node lies just outside of face 1 then the projection will + // be a small negative number. + if ( proj > -tol ) { + check = true; + ++k; + } - } // end loop over nodes - - // check to see if all nodes are on the other side - if (k == mesh2.numberOfNodesPerElement()) - { - allVerts = true; - } - - // at this point, if the check is false then there is deemed no interaction. The - // faces are on the non-contacting sides of one another. Return. - if (check == false) - { - return check; - } - - // Added 10/19/18 by SRW to catch the case where one face is entirely on the other - // side of the other (for which ordering of face 1 vs. 2 in this routine matters) - // and may not cross the contact plane (adjusted later outside of this routine). - // For the cases where check == true, we know that some of the vertices of face - // 1 pass through the plane defined by face 2. We want to know if they all do and - // trigger the allVerts boolean, which will later trigger the correct full - // overlap computation - - // loop over vertices on face 1 - k = 0; - for (int i=0; i -tol) { - check = true; // check will be true from the first loop - ++k; - } + } // end loop over nodes + + // check to see if all nodes are on the other side + if ( k == mesh2.numberOfNodesPerElement() ) { + allVerts = true; + } + + // at this point, if the check is false then there is deemed no interaction. The + // faces are on the non-contacting sides of one another. Return. + if ( check == false ) { + return check; + } + + // Added 10/19/18 by SRW to catch the case where one face is entirely on the other + // side of the other (for which ordering of face 1 vs. 2 in this routine matters) + // and may not cross the contact plane (adjusted later outside of this routine). + // For the cases where check == true, we know that some of the vertices of face + // 1 pass through the plane defined by face 2. We want to know if they all do and + // trigger the allVerts boolean, which will later trigger the correct full + // overlap computation + + // loop over vertices on face 1 + k = 0; + for ( int i = 0; i < mesh1.numberOfNodesPerElement(); ++i ) { + // get ith face 1 node id + const int f1NodeId = mesh1.getGlobalNodeId( fId1, i ); + + // compute the components of vector between face 2 center and face 1 vertex + RealT vX = mesh2.getElementCentroids()[0][fId2] - mesh1.getPosition()[0][f1NodeId]; + RealT vY = mesh2.getElementCentroids()[1][fId2] - mesh1.getPosition()[1][f1NodeId]; + RealT vZ = mesh2.getElementCentroids()[2][fId2] - mesh1.getPosition()[2][f1NodeId]; + + // project the vector onto face 2 normal + RealT proj = vX * mesh2.getElementNormals()[0][fId2] + vY * mesh2.getElementNormals()[1][fId2] + + vZ * mesh2.getElementNormals()[2][fId2]; + + // if a node of face 1 is on the other side of the plane defined by face 2 the + // projection will be positive. If a node on face 1 lies on face 2 the projection + // will be zero. If a node lies just outside of face 2 then the projection will + // be a small negative number. + if ( proj > -tol ) { + check = true; // check will be true from the first loop + ++k; + } - } // end loop over nodes + } // end loop over nodes - // check to see if all nodes are on the other side - if (k == mesh1.numberOfNodesPerElement()) - { - allVerts = true; - } + // check to see if all nodes are on the other side + if ( k == mesh1.numberOfNodesPerElement() ) { + allVerts = true; + } - return check; + return check; -} // end FaceInterCheck() +} // end FaceInterCheck() //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE bool EdgeInterCheck( const MeshData::Viewer& mesh1, const MeshData::Viewer& mesh2, - int eId1, int eId2, RealT tol, bool& allVerts ) +TRIBOL_HOST_DEVICE bool EdgeInterCheck( const MeshData::Viewer& mesh1, const MeshData::Viewer& mesh2, int eId1, + int eId2, RealT tol, bool& allVerts ) { - bool check = false; - allVerts = false; - - // loop over vertices on edge 2 - int k = 0; - for (int i=0; i -tol) - { - check = true; - ++k; - } - } // end loop over edge2 vertices - - // check to see if all vertices are on the other side - if (k == mesh2.numberOfNodesPerElement()) allVerts = true; - - if (check == false) return check; - - // loop over vertices on edge 1 to catch the case where edge 1 lies - // entirely on the other side of edge 2 triggering a full overlap - // computation - k = 0; - for (int i=0; i -tol) - { - check = true; // check will be true from the first loop - ++k; - } - } // end loop over edge1 vertices + bool check = false; + allVerts = false; + + // loop over vertices on edge 2 + int k = 0; + for ( int i = 0; i < mesh2.numberOfNodesPerElement(); ++i ) { + // get edge 2 ith vertex id + const int e2vId = mesh2.getGlobalNodeId( eId2, i ); + + // compute components of vector between edge 1 center and edge 2 vertex + RealT vX = mesh1.getElementCentroids()[0][eId1] - mesh2.getPosition()[0][e2vId]; + RealT vY = mesh1.getElementCentroids()[1][eId1] - mesh2.getPosition()[1][e2vId]; + + // project the vector onto edge1 normal + RealT proj = vX * mesh1.getElementNormals()[0][eId1] + vY * mesh1.getElementNormals()[1][eId1]; + + // check projection against tolerance + if ( proj > -tol ) { + check = true; + ++k; + } + } // end loop over edge2 vertices + + // check to see if all vertices are on the other side + if ( k == mesh2.numberOfNodesPerElement() ) allVerts = true; + + if ( check == false ) return check; + + // loop over vertices on edge 1 to catch the case where edge 1 lies + // entirely on the other side of edge 2 triggering a full overlap + // computation + k = 0; + for ( int i = 0; i < mesh1.numberOfNodesPerElement(); ++i ) { + // get edge 1 ith vertex id + const int e1vId = mesh1.getGlobalNodeId( eId1, i ); + + // compute components of vector between edge 2 center and edge 1 vertex + RealT vX = mesh2.getElementCentroids()[0][eId2] - mesh1.getPosition()[0][e1vId]; + RealT vY = mesh2.getElementCentroids()[1][eId2] - mesh1.getPosition()[1][e1vId]; + + // project the vector onto edge2 normal + RealT proj = vX * mesh2.getElementNormals()[0][eId2] + vY * mesh2.getElementNormals()[1][eId2]; + + // check projection against tolerance + if ( proj > -tol ) { + check = true; // check will be true from the first loop + ++k; + } + } // end loop over edge1 vertices - // check to see if all vertices are on the other side - if (k == mesh1.numberOfNodesPerElement()) allVerts = true; + // check to see if all vertices are on the other side + if ( k == mesh1.numberOfNodesPerElement() ) allVerts = true; - return check; + return check; -} // end EdgeInterCheck() +} // end EdgeInterCheck() //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE bool ExceedsMaxAutoInterpen( const MeshData::Viewer& mesh1, - const MeshData::Viewer& mesh2, - const int faceId1, - const int faceId2, - const Parameters& params, +TRIBOL_HOST_DEVICE bool ExceedsMaxAutoInterpen( const MeshData::Viewer& mesh1, const MeshData::Viewer& mesh2, + const int faceId1, const int faceId2, const Parameters& params, const RealT gap ) { - if (params.auto_contact_check) - { - RealT max_interpen = -1. * params.auto_contact_pen_frac * - axom::utilities::min( mesh1.getElementData().m_thickness[ faceId1 ], - mesh2.getElementData().m_thickness[ faceId2 ] ); - if (gap < max_interpen) - { - return true; - } - } - return false; + if ( params.auto_contact_check ) { + RealT max_interpen = -1. * params.auto_contact_pen_frac * + axom::utilities::min( mesh1.getElementData().m_thickness[faceId1], + mesh2.getElementData().m_thickness[faceId2] ); + if ( gap < max_interpen ) { + return true; + } + } + return false; } //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE void ProjectFaceNodesToPlane( const MeshData::Viewer& mesh, int faceId, - RealT nrmlX, RealT nrmlY, RealT nrmlZ, - RealT cX, RealT cY, RealT cZ, - RealT* pX, RealT* pY, RealT* pZ ) +TRIBOL_HOST_DEVICE void ProjectFaceNodesToPlane( const MeshData::Viewer& mesh, int faceId, RealT nrmlX, RealT nrmlY, + RealT nrmlZ, RealT cX, RealT cY, RealT cZ, RealT* pX, RealT* pY, + RealT* pZ ) { + // loop over nodes and project onto the plane defined by the point-normal + // input arguments + for ( int i = 0; i < mesh.numberOfNodesPerElement(); ++i ) { + const int nodeId = mesh.getGlobalNodeId( faceId, i ); + ProjectPointToPlane( mesh.getPosition()[0][nodeId], mesh.getPosition()[1][nodeId], mesh.getPosition()[2][nodeId], + nrmlX, nrmlY, nrmlZ, cX, cY, cZ, pX[i], pY[i], pZ[i] ); + } - // loop over nodes and project onto the plane defined by the point-normal - // input arguments - for (int i=0; i cp.m_gapTol) - { + // check polygonal vertex ordering with common plane normal + PolyReorderWithNormal( cp.m_polyX, cp.m_polyY, cp.m_polyZ, cp.m_numPolyVert, cp.m_nX, cp.m_nY, cp.m_nZ ); + + // transform local interpenetration overlaps to global coords for the + // current polygonal overlap + if ( interpenOverlap ) { + for ( int i = 0; i < cp.m_numInterpenPoly1Vert; ++i ) { + Local2DToGlobalCoords( cp.m_interpenPoly1X[i], cp.m_interpenPoly1Y[i], cp.m_e1X, cp.m_e1Y, cp.m_e1Z, cp.m_e2X, + cp.m_e2Y, cp.m_e2Z, cp.m_cX, cp.m_cY, cp.m_cZ, cp.m_interpenG1X[i], cp.m_interpenG1Y[i], + cp.m_interpenG1Z[i] ); + } + + for ( int i = 0; i < cp.m_numInterpenPoly2Vert; ++i ) { + Local2DToGlobalCoords( cp.m_interpenPoly2X[i], cp.m_interpenPoly2Y[i], cp.m_e1X, cp.m_e1Y, cp.m_e1Z, cp.m_e2X, + cp.m_e2Y, cp.m_e2Z, cp.m_cX, cp.m_cY, cp.m_cZ, cp.m_interpenG2X[i], cp.m_interpenG2Y[i], + cp.m_interpenG2Z[i] ); + } + } + + // Now that all local-to-global projections have occurred, + // relocate the contact plane based on the most up-to-date + // contact plane centroid and recompute the gap. For interpenOverlap, + // the contact plane is updated and this just amounts to a gap + // computation. For the fullOverlap case, this may relocate + // the contact plane in space. For Mortar methods this routine + // only computes the gap based on the current plane point and + // normal. + // + // Warning: + // Make sure that any local to global transformations have + // occurred prior to this call. This does not need to be done + // for mortar methods. We should just do a gap computation if + // needed. + cp.planePointAndCentroidGap( + mesh1, mesh2, + 2. * axom::utilities::max( mesh1.getFaceRadius()[element_id1], mesh2.getFaceRadius()[element_id2] ) ); + + // The gap tolerance allows separation up to the separation ratio of the + // largest face-radius. This is conservative and allows for possible + // over-inclusion. This is done for the mortar method per testing. + cp.m_gapTol = params.gap_separation_ratio * + axom::utilities::max( mesh1.getFaceRadius()[element_id1], mesh2.getFaceRadius()[element_id2] ); + + if ( cp.m_gap > cp.m_gapTol ) { + cp.m_inContact = false; + return NO_FACE_GEOM_ERROR; + } + + // for auto-contact, remove contact candidacy for full-overlap + // face-pairs with interpenetration exceeding contact penetration fraction. + // Note, this check is solely meant to exclude face-pairs composed of faces + // on opposite sides of thin structures/plates + // + // Recall that interpen gaps are negative + if ( fullOverlap ) { + if ( ExceedsMaxAutoInterpen( mesh1, mesh2, element_id1, element_id2, params, cp.m_gap ) ) { cp.m_inContact = false; return NO_FACE_GEOM_ERROR; - } - - // for auto-contact, remove contact candidacy for full-overlap - // face-pairs with interpenetration exceeding contact penetration fraction. - // Note, this check is solely meant to exclude face-pairs composed of faces - // on opposite sides of thin structures/plates - // - // Recall that interpen gaps are negative - if (fullOverlap) - { - if (ExceedsMaxAutoInterpen( mesh1, mesh2, element_id1, element_id2, - params, cp.m_gap )) - { - cp.m_inContact = false; - return NO_FACE_GEOM_ERROR; - } - } - - // if fullOverlap is used, REPROJECT the overlapping polygon - // onto the new contact plane - if (fullOverlap) - { - for (int i=0; i(*this); - auto xloc = cp3.m_overlapCX; - auto yloc = cp3.m_overlapCY; - xcg = xloc * cp3.m_e1X + yloc * cp3.m_e2X +cp3.m_cX; - ycg = xloc * cp3.m_e1Y + yloc * cp3.m_e2Y +cp3.m_cY; - zcg = xloc * cp3.m_e1Z + yloc * cp3.m_e2Z +cp3.m_cZ; - } - - // find where the overlap centroid (plane point) intersects each face - - // set the line segment's first vertex at the contact plane centroid scaled - // in the direction opposite the contact plane normal - - RealT xA = xcg + m_nX * scale; - RealT yA = ycg + m_nY * scale; - RealT zA = 0.0; - if (m_dim == 3) - { - zA = zcg + m_nZ * scale; - } - - // use the contact plane normal as the segment directional vector scale in - // the direction of the contact plane - RealT xB = xcg - m_nX * scale; - RealT yB = ycg - m_nY * scale; - RealT zB = 0.0; - if (m_dim == 3) - { - zB = zcg - m_nZ * scale; - } - - auto fId1 = getCpElementId1(); - auto fId2 = getCpElementId2(); - RealT c1_z = 0.0; - RealT n1_z = 0.0; - RealT c2_z = 0.0; - RealT n2_z = 0.0; - if (m_dim == 3) - { + // project the overlap centroid back to each face using a + // line-plane intersection method + RealT xc1 = 0.; + RealT yc1 = 0.; + RealT zc1 = 0.; + RealT xc2 = 0.; + RealT yc2 = 0.; + RealT zc2 = 0.; + + RealT xcg = m_cX; + RealT ycg = m_cY; + RealT zcg = 0.0; + + // first project the projected area of overlap's centroid in local + // coordinates to global coordinates + if ( m_dim == 3 ) { + auto& cp3 = static_cast( *this ); + auto xloc = cp3.m_overlapCX; + auto yloc = cp3.m_overlapCY; + xcg = xloc * cp3.m_e1X + yloc * cp3.m_e2X + cp3.m_cX; + ycg = xloc * cp3.m_e1Y + yloc * cp3.m_e2Y + cp3.m_cY; + zcg = xloc * cp3.m_e1Z + yloc * cp3.m_e2Z + cp3.m_cZ; + } + + // find where the overlap centroid (plane point) intersects each face + + // set the line segment's first vertex at the contact plane centroid scaled + // in the direction opposite the contact plane normal + + RealT xA = xcg + m_nX * scale; + RealT yA = ycg + m_nY * scale; + RealT zA = 0.0; + if ( m_dim == 3 ) { + zA = zcg + m_nZ * scale; + } + + // use the contact plane normal as the segment directional vector scale in + // the direction of the contact plane + RealT xB = xcg - m_nX * scale; + RealT yB = ycg - m_nY * scale; + RealT zB = 0.0; + if ( m_dim == 3 ) { + zB = zcg - m_nZ * scale; + } + + auto fId1 = getCpElementId1(); + auto fId2 = getCpElementId2(); + RealT c1_z = 0.0; + RealT n1_z = 0.0; + RealT c2_z = 0.0; + RealT n2_z = 0.0; + if ( m_dim == 3 ) { c1_z = m1.getElementCentroids()[2][fId1]; n1_z = m1.getElementNormals()[2][fId1]; c2_z = m2.getElementCentroids()[2][fId2]; n2_z = m2.getElementNormals()[2][fId2]; - } - bool inPlane = false; - LinePlaneIntersection( xA, yA, zA, xB, yB, zB, - m1.getElementCentroids()[0][fId1], m1.getElementCentroids()[1][fId1], c1_z, - m1.getElementNormals()[0][fId1], m1.getElementNormals()[1][fId1], n1_z, - xc1, yc1, zc1, inPlane ); - - LinePlaneIntersection( xA, yA, zA, xB, yB, zB, - m2.getElementCentroids()[0][fId2], m2.getElementCentroids()[1][fId2], c2_z, - m2.getElementNormals()[0][fId2], m2.getElementNormals()[1][fId2], n2_z, - xc2, yc2, zc2, inPlane ); - - // for intermediate, or common plane methods, average the two contact plane - // centroid-to-plane intersections and use this as the new point data for the - // contact plane (do not do for mortar methods, or is redundant). - if ( m_dim == 2 || m_intermediatePlane ) - { - m_cX = 0.5 * (xc1 + xc2); - m_cY = 0.5 * (yc1 + yc2); - m_cZ = 0.5 * (zc1 + zc2); - } - - // compute normal gap magnitude (x1 - x2 for positive gap in separation - // and negative gap in penetration) - m_gap = (xc1 - xc2) * m_nX + (yc1 - yc2) * m_nY + (zc1 - zc2) * m_nZ; - - // store the two face points corresponding to the contact plane centroid projection/intersection - - m_cXf1 = xc1; - m_cYf1 = yc1; - m_cZf1 = zc1; - - m_cXf2 = xc2; - m_cYf2 = yc2; - m_cZf2 = zc2; + } + bool inPlane = false; + LinePlaneIntersection( xA, yA, zA, xB, yB, zB, m1.getElementCentroids()[0][fId1], m1.getElementCentroids()[1][fId1], + c1_z, m1.getElementNormals()[0][fId1], m1.getElementNormals()[1][fId1], n1_z, xc1, yc1, zc1, + inPlane ); + + LinePlaneIntersection( xA, yA, zA, xB, yB, zB, m2.getElementCentroids()[0][fId2], m2.getElementCentroids()[1][fId2], + c2_z, m2.getElementNormals()[0][fId2], m2.getElementNormals()[1][fId2], n2_z, xc2, yc2, zc2, + inPlane ); + + // for intermediate, or common plane methods, average the two contact plane + // centroid-to-plane intersections and use this as the new point data for the + // contact plane (do not do for mortar methods, or is redundant). + if ( m_dim == 2 || m_intermediatePlane ) { + m_cX = 0.5 * ( xc1 + xc2 ); + m_cY = 0.5 * ( yc1 + yc2 ); + m_cZ = 0.5 * ( zc1 + zc2 ); + } + + // compute normal gap magnitude (x1 - x2 for positive gap in separation + // and negative gap in penetration) + m_gap = ( xc1 - xc2 ) * m_nX + ( yc1 - yc2 ) * m_nY + ( zc1 - zc2 ) * m_nZ; + + // store the two face points corresponding to the contact plane centroid projection/intersection + + m_cXf1 = xc1; + m_cYf1 = yc1; + m_cZf1 = zc1; + + m_cXf2 = xc2; + m_cYf2 = yc2; + m_cZf2 = zc2; } //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE ContactPlane3D CheckAlignedFacePair( InterfacePair& pair, - const MeshData::Viewer& mesh1, - const MeshData::Viewer& mesh2, - const Parameters& params ) +TRIBOL_HOST_DEVICE ContactPlane3D CheckAlignedFacePair( InterfacePair& pair, const MeshData::Viewer& mesh1, + const MeshData::Viewer& mesh2, const Parameters& params ) { - // Note: Checks #1-#4 are done in the binning - - // get fraction of largest face we keep for overlap area - RealT areaFrac = params.overlap_area_frac; - - // alias variables off the InterfacePair - IndexT element_id1 = pair.m_element_id1; - IndexT element_id2 = pair.m_element_id2; - - // instantiate temporary contact plane to be returned by this routine - bool interpenOverlap = false; - bool intermediatePlane = false; - ContactPlane3D cp( &pair, areaFrac, interpenOverlap, intermediatePlane); - - // TODO should probably stay consistent with the mortar convention and change - // the plane point and normal to the nonmortar surface. These calculations are only - // geometry calculations intended to determine if the face-pair should be included - // so there isn't much consequence to choosing until we talk about integration. - // If the mortar data is switched to nonmortar data, the calculations must be chased - // through to make sure contacting face pairs are included. - - // set the common plane "point" to the mortar face vertex averaged centroid - cp.m_cX = mesh1.getElementCentroids()[0][ element_id1 ]; - cp.m_cY = mesh1.getElementCentroids()[1][ element_id1 ]; - cp.m_cZ = mesh1.getElementCentroids()[2][ element_id1 ]; - - // set the common plane "normal" to the mortar outward unit normal - cp.m_nX = mesh1.getElementNormals()[0][ element_id1 ]; - cp.m_nY = mesh1.getElementNormals()[1][ element_id1 ]; - cp.m_nZ = mesh1.getElementNormals()[2][ element_id1 ]; - - // set the gap tolerance inclusive for separation up to m_gapTol - cp.m_gapTol = params.gap_separation_ratio * - axom::utilities::max( mesh1.getFaceRadius()[ element_id1 ], - mesh2.getFaceRadius()[ element_id2 ] ); - - // set the area fraction - cp.m_areaFrac = params.overlap_area_frac; - - // set the minimum area - cp.m_areaMin = cp.m_areaFrac * - axom::utilities::min( mesh1.getElementAreas()[ element_id1 ], - mesh2.getElementAreas()[ element_id2 ] ); - - // compute the vector centroid gap and scalar centroid gap to - // check the alignment criterion AND gap - RealT gapVecX = mesh2.getElementCentroids()[0][element_id2] - mesh1.getElementCentroids()[0][element_id1]; - RealT gapVecY = mesh2.getElementCentroids()[1][element_id2] - mesh1.getElementCentroids()[1][element_id1]; - RealT gapVecZ = mesh2.getElementCentroids()[2][element_id2] - mesh1.getElementCentroids()[2][element_id1]; - - RealT scalarGap = ( mesh2.getElementCentroids()[0][element_id2] - mesh1.getElementCentroids()[0][element_id1] ) * cp.m_nX + - ( mesh2.getElementCentroids()[1][element_id2] - mesh1.getElementCentroids()[1][element_id1] ) * cp.m_nY + - ( mesh2.getElementCentroids()[2][element_id2] - mesh1.getElementCentroids()[2][element_id1] ) * cp.m_nZ; - - RealT gapVecMag = magnitude( gapVecX, gapVecY, gapVecZ ); - - if (gapVecMag > 1.1*std::abs(scalarGap)) - { - cp.m_inContact = false; - return cp; - } + // Note: Checks #1-#4 are done in the binning + + // get fraction of largest face we keep for overlap area + RealT areaFrac = params.overlap_area_frac; + + // alias variables off the InterfacePair + IndexT element_id1 = pair.m_element_id1; + IndexT element_id2 = pair.m_element_id2; + + // instantiate temporary contact plane to be returned by this routine + bool interpenOverlap = false; + bool intermediatePlane = false; + ContactPlane3D cp( &pair, areaFrac, interpenOverlap, intermediatePlane ); + + // TODO should probably stay consistent with the mortar convention and change + // the plane point and normal to the nonmortar surface. These calculations are only + // geometry calculations intended to determine if the face-pair should be included + // so there isn't much consequence to choosing until we talk about integration. + // If the mortar data is switched to nonmortar data, the calculations must be chased + // through to make sure contacting face pairs are included. + + // set the common plane "point" to the mortar face vertex averaged centroid + cp.m_cX = mesh1.getElementCentroids()[0][element_id1]; + cp.m_cY = mesh1.getElementCentroids()[1][element_id1]; + cp.m_cZ = mesh1.getElementCentroids()[2][element_id1]; + + // set the common plane "normal" to the mortar outward unit normal + cp.m_nX = mesh1.getElementNormals()[0][element_id1]; + cp.m_nY = mesh1.getElementNormals()[1][element_id1]; + cp.m_nZ = mesh1.getElementNormals()[2][element_id1]; + + // set the gap tolerance inclusive for separation up to m_gapTol + cp.m_gapTol = params.gap_separation_ratio * + axom::utilities::max( mesh1.getFaceRadius()[element_id1], mesh2.getFaceRadius()[element_id2] ); + + // set the area fraction + cp.m_areaFrac = params.overlap_area_frac; + + // set the minimum area + cp.m_areaMin = cp.m_areaFrac * + axom::utilities::min( mesh1.getElementAreas()[element_id1], mesh2.getElementAreas()[element_id2] ); + + // compute the vector centroid gap and scalar centroid gap to + // check the alignment criterion AND gap + RealT gapVecX = mesh2.getElementCentroids()[0][element_id2] - mesh1.getElementCentroids()[0][element_id1]; + RealT gapVecY = mesh2.getElementCentroids()[1][element_id2] - mesh1.getElementCentroids()[1][element_id1]; + RealT gapVecZ = mesh2.getElementCentroids()[2][element_id2] - mesh1.getElementCentroids()[2][element_id1]; + + RealT scalarGap = + ( mesh2.getElementCentroids()[0][element_id2] - mesh1.getElementCentroids()[0][element_id1] ) * cp.m_nX + + ( mesh2.getElementCentroids()[1][element_id2] - mesh1.getElementCentroids()[1][element_id1] ) * cp.m_nY + + ( mesh2.getElementCentroids()[2][element_id2] - mesh1.getElementCentroids()[2][element_id1] ) * cp.m_nZ; + + RealT gapVecMag = magnitude( gapVecX, gapVecY, gapVecZ ); + + if ( gapVecMag > 1.1 * std::abs( scalarGap ) ) { + cp.m_inContact = false; + return cp; + } - // perform gap check - if (scalarGap > cp.m_gapTol) - { - cp.m_inContact = false; - return cp; - } - - // for auto-contact, remove contact candidacy for face-pairs with - // interpenetration exceeding contact penetration fraction. - // Note, this check is solely meant to exclude face-pairs composed of faces - // on opposite sides of thin structures/plates - // - // Recall that interpen gaps are negative - if (ExceedsMaxAutoInterpen( mesh1, mesh2, element_id1, element_id2, - params, scalarGap )) - { - cp.m_inContact = false; - return cp; - } + // perform gap check + if ( scalarGap > cp.m_gapTol ) { + cp.m_inContact = false; + return cp; + } + + // for auto-contact, remove contact candidacy for face-pairs with + // interpenetration exceeding contact penetration fraction. + // Note, this check is solely meant to exclude face-pairs composed of faces + // on opposite sides of thin structures/plates + // + // Recall that interpen gaps are negative + if ( ExceedsMaxAutoInterpen( mesh1, mesh2, element_id1, element_id2, params, scalarGap ) ) { + cp.m_inContact = false; + return cp; + } - // if we are here we have contact between two aligned faces - cp.m_numPolyVert = mesh1.numberOfNodesPerElement(); + // if we are here we have contact between two aligned faces + cp.m_numPolyVert = mesh1.numberOfNodesPerElement(); - for (int a=0; am_element_id1; - IndexT fId2 = m_pair->m_element_id2; - - if (m_intermediatePlane) - { - // INTERMEDIATE (I.E. COMMON) PLANE normal calculation: - // compute the cp normal as the average of the two face normals, and in - // the direction such that the dot product between the cp normal and - // the normal of face 2 is positive. This is the default method of - // computing the cp normal - m_nX = 0.5 * ( m2.getElementNormals()[0][ fId2 ] - m1.getElementNormals()[0][ fId1 ] ); - m_nY = 0.5 * ( m2.getElementNormals()[1][ fId2 ] - m1.getElementNormals()[1][ fId1 ] ); - m_nZ = 0.5 * ( m2.getElementNormals()[2][ fId2 ] - m1.getElementNormals()[2][ fId1 ] ); - } - else // for mortar - { - // the projection plane is the nonmortar (i.e. mesh id 2) surface so - // we use the outward normal for face 2 on mesh 2 - m_nX = m2.getElementNormals()[0][ fId2 ]; - m_nY = m2.getElementNormals()[1][ fId2 ]; - m_nZ = m2.getElementNormals()[2][ fId2 ]; - } - - // normalize the cp normal - RealT mag = magnitude( m_nX, m_nY, m_nZ ); - RealT invMag = 1.0 / mag; - - m_nX *= invMag; - m_nY *= invMag; - m_nZ *= invMag; - - return; - -} // end ContactPlane3D::computeNormal() + IndexT fId1 = m_pair->m_element_id1; + IndexT fId2 = m_pair->m_element_id2; + + if ( m_intermediatePlane ) { + // INTERMEDIATE (I.E. COMMON) PLANE normal calculation: + // compute the cp normal as the average of the two face normals, and in + // the direction such that the dot product between the cp normal and + // the normal of face 2 is positive. This is the default method of + // computing the cp normal + m_nX = 0.5 * ( m2.getElementNormals()[0][fId2] - m1.getElementNormals()[0][fId1] ); + m_nY = 0.5 * ( m2.getElementNormals()[1][fId2] - m1.getElementNormals()[1][fId1] ); + m_nZ = 0.5 * ( m2.getElementNormals()[2][fId2] - m1.getElementNormals()[2][fId1] ); + } else // for mortar + { + // the projection plane is the nonmortar (i.e. mesh id 2) surface so + // we use the outward normal for face 2 on mesh 2 + m_nX = m2.getElementNormals()[0][fId2]; + m_nY = m2.getElementNormals()[1][fId2]; + m_nZ = m2.getElementNormals()[2][fId2]; + } + + // normalize the cp normal + RealT mag = magnitude( m_nX, m_nY, m_nZ ); + RealT invMag = 1.0 / mag; + + m_nX *= invMag; + m_nY *= invMag; + m_nZ *= invMag; + + return; + +} // end ContactPlane3D::computeNormal() //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE void ContactPlane3D::computePlanePoint( const MeshData::Viewer& m1, - const MeshData::Viewer& m2 ) +TRIBOL_HOST_DEVICE void ContactPlane3D::computePlanePoint( const MeshData::Viewer& m1, const MeshData::Viewer& m2 ) { - // compute the cp centroid as the average of the two face's centers. - // This is the default method of computing the cp centroid - IndexT fId1 = m_pair->m_element_id1; - IndexT fId2 = m_pair->m_element_id2; - - // INTERMEDIATE (I.E. COMMON) PLANE point calculation: - // average two face vertex averaged centroids - if (m_intermediatePlane) - { - m_cX = 0.5 * ( m1.getElementCentroids()[0][fId1] + m2.getElementCentroids()[0][fId2] ); - m_cY = 0.5 * ( m1.getElementCentroids()[1][fId1] + m2.getElementCentroids()[1][fId2] ); - m_cZ = 0.5 * ( m1.getElementCentroids()[2][fId1] + m2.getElementCentroids()[2][fId2] ); - } - // ELSE: MORTAR calculation using the vertex averaged - // centroid of the nonmortar face - else - { - m_cX = m2.getElementCentroids()[0][ fId2 ]; - m_cY = m2.getElementCentroids()[1][ fId2 ]; - m_cZ = m2.getElementCentroids()[2][ fId2 ]; - } - - return; - -} // end ContactPlane3D::computePlanePoint() + // compute the cp centroid as the average of the two face's centers. + // This is the default method of computing the cp centroid + IndexT fId1 = m_pair->m_element_id1; + IndexT fId2 = m_pair->m_element_id2; + + // INTERMEDIATE (I.E. COMMON) PLANE point calculation: + // average two face vertex averaged centroids + if ( m_intermediatePlane ) { + m_cX = 0.5 * ( m1.getElementCentroids()[0][fId1] + m2.getElementCentroids()[0][fId2] ); + m_cY = 0.5 * ( m1.getElementCentroids()[1][fId1] + m2.getElementCentroids()[1][fId2] ); + m_cZ = 0.5 * ( m1.getElementCentroids()[2][fId1] + m2.getElementCentroids()[2][fId2] ); + } + // ELSE: MORTAR calculation using the vertex averaged + // centroid of the nonmortar face + else { + m_cX = m2.getElementCentroids()[0][fId2]; + m_cY = m2.getElementCentroids()[1][fId2]; + m_cZ = m2.getElementCentroids()[2][fId2]; + } + + return; + +} // end ContactPlane3D::computePlanePoint() //------------------------------------------------------------------------------ TRIBOL_HOST_DEVICE void ContactPlane3D::computeLocalBasis( const MeshData::Viewer& m1 ) { - // somewhat arbitrarily set the first local basis vector to be - // between contact plane centroid and first node on first face as - // projected onto the contact plane - const int nodeId = m1.getGlobalNodeId(m_pair->m_element_id1, 0); - - // project to plane - RealT pX, pY, pZ; - ProjectPointToPlane( m1.getPosition()[0][nodeId], - m1.getPosition()[1][nodeId], - m1.getPosition()[2][nodeId], - m_nX, m_nY, m_nZ, m_cX, - m_cY, m_cZ, pX, pY, pZ ); - - // check the square of the magnitude of the first basis vector to - // catch the case where pX = m_cX and so on. - RealT sqrMag = m_e1X * m_e1X + m_e1Y * m_e1Y + m_e1Z * m_e1Z; - - if (sqrMag < 1.E-12) // note: tolerance on the square of the magnitude - { - // translate projected first node by face radius - RealT radius = m1.getFaceRadius()[ m_pair->m_element_id1 ]; - RealT scale = 1.0 * radius; - - RealT pNewX = pX + scale; - RealT pNewY = pY + scale; - RealT pNewZ = pZ + scale; - - // project point onto contact plane - ProjectPointToPlane( pNewX, pNewY, pNewZ, - m_nX, m_nY, m_nZ, - m_cX, m_cY, m_cZ, - pX, pY, pZ ); - - m_e1X = pX - m_cX; - m_e1Y = pY - m_cY; - m_e1Z = pZ - m_cZ; - } - - // recompute the magnitude - RealT mag = magnitude( m_e1X, m_e1Y, m_e1Z ); - RealT invMag = 1.0 / mag; - - // normalize the first basis vector - m_e1X *= invMag; - m_e1Y *= invMag; - m_e1Z *= invMag; - - // compute the second, and orthogonal, in-plane basis vector as the - // cross product between the cp normal and e1. This will be unit because - // the cp normal and e1 are unit. - m_e2X = 0.0; - m_e2Y = 0.0; - m_e2Z = 0.0; - - m_e2X += (m_nY * m_e1Z) - (m_nZ * m_e1Y); - m_e2Y += (m_nZ * m_e1X) - (m_nX * m_e1Z); - m_e2Z += (m_nX * m_e1Y) - (m_nY * m_e1X); - - // normalize second vector - mag = magnitude( m_e2X, m_e2Y, m_e2Z ); - invMag = 1.0 / mag; - - m_e2X *= invMag; - m_e2Y *= invMag; - m_e2Z *= invMag; - - return; - -} // end ContactPlane3D::computeLocalBasis() + // somewhat arbitrarily set the first local basis vector to be + // between contact plane centroid and first node on first face as + // projected onto the contact plane + const int nodeId = m1.getGlobalNodeId( m_pair->m_element_id1, 0 ); -//------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE void ContactPlane3D::globalTo2DLocalCoords( RealT* pX, RealT* pY, - RealT* pZ, RealT* pLX, - RealT* pLY, int size ) -{ + // project to plane + RealT pX, pY, pZ; + ProjectPointToPlane( m1.getPosition()[0][nodeId], m1.getPosition()[1][nodeId], m1.getPosition()[2][nodeId], m_nX, + m_nY, m_nZ, m_cX, m_cY, m_cZ, pX, pY, pZ ); + + // check the square of the magnitude of the first basis vector to + // catch the case where pX = m_cX and so on. + RealT sqrMag = m_e1X * m_e1X + m_e1Y * m_e1Y + m_e1Z * m_e1Z; + + if ( sqrMag < 1.E-12 ) // note: tolerance on the square of the magnitude + { + // translate projected first node by face radius + RealT radius = m1.getFaceRadius()[m_pair->m_element_id1]; + RealT scale = 1.0 * radius; + + RealT pNewX = pX + scale; + RealT pNewY = pY + scale; + RealT pNewZ = pZ + scale; + + // project point onto contact plane + ProjectPointToPlane( pNewX, pNewY, pNewZ, m_nX, m_nY, m_nZ, m_cX, m_cY, m_cZ, pX, pY, pZ ); + + m_e1X = pX - m_cX; + m_e1Y = pY - m_cY; + m_e1Z = pZ - m_cZ; + } - // loop over projected nodes - for (int i=0; im_element_id1 ], - m2.getElementAreas()[ m_pair->m_element_id2 ] ); + m_areaMin = m_areaFrac * axom::utilities::min( m1.getElementAreas()[m_pair->m_element_id1], + m2.getElementAreas()[m_pair->m_element_id2] ); - return; + return; -} // end ContactPlane3D::computeAreaTol() +} // end ContactPlane3D::computeAreaTol() //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE void ContactPlane3D::checkPolyOverlap( const MeshData::Viewer& m1, - const MeshData::Viewer& m2, - RealT* projLocX1, RealT* projLocY1, - RealT* projLocX2, RealT* projLocY2, - const int isym ) +TRIBOL_HOST_DEVICE void ContactPlane3D::checkPolyOverlap( const MeshData::Viewer& m1, const MeshData::Viewer& m2, + RealT* projLocX1, RealT* projLocY1, RealT* projLocX2, + RealT* projLocY2, const int isym ) { - // change the vertex ordering of one of the faces so that the two match - constexpr int max_nodes_per_elem = 4; - RealT x2Temp[ max_nodes_per_elem ]; - RealT y2Temp[ max_nodes_per_elem ]; - - // set first vertex coordinates the same - x2Temp[0] = projLocX2[0]; - y2Temp[0] = projLocY2[0]; - - // reorder - int k = 1; - for (int i=(m2.numberOfNodesPerElement()-1); i>0; --i) - { - x2Temp[k] = projLocX2[i]; - y2Temp[k] = projLocY2[i]; - ++k; - } + // change the vertex ordering of one of the faces so that the two match + constexpr int max_nodes_per_elem = 4; + RealT x2Temp[max_nodes_per_elem]; + RealT y2Temp[max_nodes_per_elem]; + + // set first vertex coordinates the same + x2Temp[0] = projLocX2[0]; + y2Temp[0] = projLocY2[0]; + + // reorder + int k = 1; + for ( int i = ( m2.numberOfNodesPerElement() - 1 ); i > 0; --i ) { + x2Temp[k] = projLocX2[i]; + y2Temp[k] = projLocY2[i]; + ++k; + } - PolyInterYCentroid( m1.numberOfNodesPerElement(), projLocX1, projLocY1, m2.numberOfNodesPerElement(), - x2Temp, y2Temp, isym, m_area, m_overlapCY ); - PolyInterYCentroid( m1.numberOfNodesPerElement(), projLocY1, projLocX1, m2.numberOfNodesPerElement(), - y2Temp, x2Temp, isym, m_area, m_overlapCX ); + PolyInterYCentroid( m1.numberOfNodesPerElement(), projLocX1, projLocY1, m2.numberOfNodesPerElement(), x2Temp, y2Temp, + isym, m_area, m_overlapCY ); + PolyInterYCentroid( m1.numberOfNodesPerElement(), projLocY1, projLocX1, m2.numberOfNodesPerElement(), y2Temp, x2Temp, + isym, m_area, m_overlapCX ); - return; + return; -} // end ContactPlane3D::checkPolyOverlap() +} // end ContactPlane3D::checkPolyOverlap() //------------------------------------------------------------------------------ void ContactPlane3D::computeIntegralGap() { - // TODO implement this routine + // TODO implement this routine - // this may be contact method specific + // this may be contact method specific - return; + return; -} // end ContactPlane3D::computeIntegralGap() +} // end ContactPlane3D::computeIntegralGap() //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE void ContactPlane3D::local2DToGlobalCoords( RealT xloc, RealT yloc, - RealT& xg, RealT& yg, RealT& zg ) +TRIBOL_HOST_DEVICE void ContactPlane3D::local2DToGlobalCoords( RealT xloc, RealT yloc, RealT& xg, RealT& yg, RealT& zg ) { - // This projection takes the two input local vector components and uses - // them as coefficients in a linear combination of local basis vectors. - // This gives a 3-vector with origin at the contact plane centroid. - RealT vx = xloc * m_e1X + yloc * m_e2X; - RealT vy = xloc * m_e1Y + yloc * m_e2Y; - RealT vz = xloc * m_e1Z + yloc * m_e2Z; + // This projection takes the two input local vector components and uses + // them as coefficients in a linear combination of local basis vectors. + // This gives a 3-vector with origin at the contact plane centroid. + RealT vx = xloc * m_e1X + yloc * m_e2X; + RealT vy = xloc * m_e1Y + yloc * m_e2Y; + RealT vz = xloc * m_e1Z + yloc * m_e2Z; - // the vector in the global coordinate system requires the addition of the - // contact plane point vector (in global Cartesian basis) to the previously - // computed vector - xg = vx + m_cX; - yg = vy + m_cY; - zg = vz + m_cZ; + // the vector in the global coordinate system requires the addition of the + // contact plane point vector (in global Cartesian basis) to the previously + // computed vector + xg = vx + m_cX; + yg = vy + m_cY; + zg = vz + m_cZ; - return; + return; -} // end ContactPlane3D::local2DToGlobalCoords() +} // end ContactPlane3D::local2DToGlobalCoords() //------------------------------------------------------------------------------ -void ContactPlane3D::centroidGap( const MeshData::Viewer& m1, - const MeshData::Viewer& m2, - RealT scale ) +void ContactPlane3D::centroidGap( const MeshData::Viewer& m1, const MeshData::Viewer& m2, RealT scale ) { - // project the overlap centroid back to each face using a - // line-plane intersection method - RealT xc1 = 0.; - RealT yc1 = 0.; - RealT zc1 = 0.; - RealT xc2 = 0.; - RealT yc2 = 0.; - RealT zc2 = 0.; - - RealT xcg = 0.; - RealT ycg = 0.; - RealT zcg = 0.; - - // first project the projected area of overlap's centroid in local - // coordinates to global coordinates - local2DToGlobalCoords(m_overlapCX, m_overlapCY, xcg, ycg, zcg); - - // find where the overlap centroid (plane point) intersects each face - - // set the line segment's first vertex at the contact plane centroid scaled - // in the direction opposite the contact plane normal - RealT xA = xcg + m_nX * scale; - RealT yA = ycg + m_nY * scale; - RealT zA = zcg + m_nZ * scale; - - // use the contact plane normal as the segment directional vector scale in - // the direction of the contact plane - RealT xB = xcg - m_nX * scale; - RealT yB = ycg - m_nY * scale; - RealT zB = zcg - m_nZ * scale; - - bool inPlane = false; - IndexT fId1 = m_pair->m_element_id1; - IndexT fId2 = m_pair->m_element_id2; - - bool intersect1 = LinePlaneIntersection( xA, yA, zA, xB, yB, zB, - m1.getElementCentroids()[0][fId1], m1.getElementCentroids()[1][fId1], m1.getElementCentroids()[2][fId1], - m1.getElementNormals()[0][fId1], m1.getElementNormals()[1][fId1], m1.getElementNormals()[2][fId1], - xc1, yc1, zc1, inPlane ); - - bool intersect2 = LinePlaneIntersection( xA, yA, zA, xB, yB, zB, - m2.getElementCentroids()[0][fId2], m2.getElementCentroids()[1][fId2], m2.getElementCentroids()[2][fId2], - m2.getElementNormals()[0][fId2], m2.getElementNormals()[1][fId2], m2.getElementNormals()[2][fId2], - xc2, yc2, zc2, inPlane ); - TRIBOL_UNUSED_VAR(intersect1); // We don't currently use these bool variabeles - TRIBOL_UNUSED_VAR(intersect2); // but the above function calls modify some parameters - - - // compute normal gap magnitude (x1 - x2 for positive gap in separation - // and negative gap in penetration) - m_gap = (xc1 - xc2) * m_nX + (yc1 - yc2) * m_nY + (zc1 - zc2) * m_nZ; - - // store the two face points corresponding to the contact plane centroid projection/intersection - m_cXf1 = xc1; - m_cYf1 = yc1; - m_cZf1 = zc1; - - m_cXf2 = xc2; - m_cYf2 = yc2; - m_cZf2 = zc2; - - return; - -} // end ContactPlane3D::centroidGap() + // project the overlap centroid back to each face using a + // line-plane intersection method + RealT xc1 = 0.; + RealT yc1 = 0.; + RealT zc1 = 0.; + RealT xc2 = 0.; + RealT yc2 = 0.; + RealT zc2 = 0.; + + RealT xcg = 0.; + RealT ycg = 0.; + RealT zcg = 0.; + + // first project the projected area of overlap's centroid in local + // coordinates to global coordinates + local2DToGlobalCoords( m_overlapCX, m_overlapCY, xcg, ycg, zcg ); + + // find where the overlap centroid (plane point) intersects each face + + // set the line segment's first vertex at the contact plane centroid scaled + // in the direction opposite the contact plane normal + RealT xA = xcg + m_nX * scale; + RealT yA = ycg + m_nY * scale; + RealT zA = zcg + m_nZ * scale; + + // use the contact plane normal as the segment directional vector scale in + // the direction of the contact plane + RealT xB = xcg - m_nX * scale; + RealT yB = ycg - m_nY * scale; + RealT zB = zcg - m_nZ * scale; + + bool inPlane = false; + IndexT fId1 = m_pair->m_element_id1; + IndexT fId2 = m_pair->m_element_id2; + + bool intersect1 = LinePlaneIntersection( xA, yA, zA, xB, yB, zB, m1.getElementCentroids()[0][fId1], + m1.getElementCentroids()[1][fId1], m1.getElementCentroids()[2][fId1], + m1.getElementNormals()[0][fId1], m1.getElementNormals()[1][fId1], + m1.getElementNormals()[2][fId1], xc1, yc1, zc1, inPlane ); + + bool intersect2 = LinePlaneIntersection( xA, yA, zA, xB, yB, zB, m2.getElementCentroids()[0][fId2], + m2.getElementCentroids()[1][fId2], m2.getElementCentroids()[2][fId2], + m2.getElementNormals()[0][fId2], m2.getElementNormals()[1][fId2], + m2.getElementNormals()[2][fId2], xc2, yc2, zc2, inPlane ); + TRIBOL_UNUSED_VAR( intersect1 ); // We don't currently use these bool variabeles + TRIBOL_UNUSED_VAR( intersect2 ); // but the above function calls modify some parameters + + // compute normal gap magnitude (x1 - x2 for positive gap in separation + // and negative gap in penetration) + m_gap = ( xc1 - xc2 ) * m_nX + ( yc1 - yc2 ) * m_nY + ( zc1 - zc2 ) * m_nZ; + + // store the two face points corresponding to the contact plane centroid projection/intersection + m_cXf1 = xc1; + m_cYf1 = yc1; + m_cZf1 = zc1; + + m_cXf2 = xc2; + m_cYf2 = yc2; + m_cZf2 = zc2; + + return; + +} // end ContactPlane3D::centroidGap() //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE FaceGeomError ContactPlane3D::computeLocalInterpenOverlap( - const MeshData::Viewer& m1, const MeshData::Viewer& m2, - const Parameters& params, bool& interpen ) +TRIBOL_HOST_DEVICE FaceGeomError ContactPlane3D::computeLocalInterpenOverlap( const MeshData::Viewer& m1, + const MeshData::Viewer& m2, + const Parameters& params, bool& interpen ) { - // for each face, loop over current configuration segments and - // determine the two (there should be at most two, or in the odd - // case zero if one plane lies completely on one side of the - // contact plane) that intersect the contact plane. These two new - // vertices in addition to the face vertices that "penetrate" the - // contact plane define the two new polygons whose intersection we - // seek. - - interpen = false; - - RealT xInter[4]; - RealT yInter[4]; - RealT zInter[4]; - bool inPlane = false; - int numV[2]; - - // set up vertex id arrays to indicate which vertices pass through - // contact plane - StackArrayT mesh({&m1, &m2}); - constexpr int max_nodes_per_elem = 4; - int interpenVertex1[ max_nodes_per_elem ]; - int interpenVertex2[ max_nodes_per_elem ]; - - StackArrayT element_id({getCpElementId1(), getCpElementId2()}); - - for (int i=0; i<2; ++i) // loop over two constituent faces - { - // initialize output intersection point array - xInter[ 2*i ] = 0.; - xInter[ 2*i+1 ] = 0.; - yInter[ 2*i ] = 0.; - yInter[ 2*i+1 ] = 0.; - zInter[ 2*i ] = 0.; - zInter[ 2*i+1 ] = 0.; - - // declare array to hold vertex id for all vertices that interpenetrate - // the contact plane - int interpenVertex[ max_nodes_per_elem ]; - - int k = 0; - for (int j=0; jnumberOfNodesPerElement(); ++j) // loop over face segments - { - // initialize current entry in the vertex id list - interpenVertex[j] = -1; + // for each face, loop over current configuration segments and + // determine the two (there should be at most two, or in the odd + // case zero if one plane lies completely on one side of the + // contact plane) that intersect the contact plane. These two new + // vertices in addition to the face vertices that "penetrate" the + // contact plane define the two new polygons whose intersection we + // seek. + + interpen = false; + + RealT xInter[4]; + RealT yInter[4]; + RealT zInter[4]; + bool inPlane = false; + int numV[2]; + + // set up vertex id arrays to indicate which vertices pass through + // contact plane + StackArrayT mesh( { &m1, &m2 } ); + constexpr int max_nodes_per_elem = 4; + int interpenVertex1[max_nodes_per_elem]; + int interpenVertex2[max_nodes_per_elem]; + + StackArrayT element_id( { getCpElementId1(), getCpElementId2() } ); + + for ( int i = 0; i < 2; ++i ) // loop over two constituent faces + { + // initialize output intersection point array + xInter[2 * i] = 0.; + xInter[2 * i + 1] = 0.; + yInter[2 * i] = 0.; + yInter[2 * i + 1] = 0.; + zInter[2 * i] = 0.; + zInter[2 * i + 1] = 0.; + + // declare array to hold vertex id for all vertices that interpenetrate + // the contact plane + int interpenVertex[max_nodes_per_elem]; + + int k = 0; + for ( int j = 0; j < mesh[i]->numberOfNodesPerElement(); ++j ) // loop over face segments + { + // initialize current entry in the vertex id list + interpenVertex[j] = -1; - // determine segment vertex ids - int ja = j; - int jb = (j == (mesh[i]->numberOfNodesPerElement()-1)) ? 0 : (j+1); + // determine segment vertex ids + int ja = j; + int jb = ( j == ( mesh[i]->numberOfNodesPerElement() - 1 ) ) ? 0 : ( j + 1 ); - const int& fNodeIdA = mesh[i]->getGlobalNodeId(element_id[i], ja); - const RealT& x1 = mesh[i]->getPosition()[0][fNodeIdA]; - const RealT& y1 = mesh[i]->getPosition()[1][fNodeIdA]; - const RealT& z1 = mesh[i]->getPosition()[2][fNodeIdA]; + const int& fNodeIdA = mesh[i]->getGlobalNodeId( element_id[i], ja ); + const RealT& x1 = mesh[i]->getPosition()[0][fNodeIdA]; + const RealT& y1 = mesh[i]->getPosition()[1][fNodeIdA]; + const RealT& z1 = mesh[i]->getPosition()[2][fNodeIdA]; - const int& fNodeIdB = mesh[i]->getGlobalNodeId(element_id[i], jb); - const RealT& x2 = mesh[i]->getPosition()[0][fNodeIdB]; - const RealT& y2 = mesh[i]->getPosition()[1][fNodeIdB]; - const RealT& z2 = mesh[i]->getPosition()[2][fNodeIdB]; + const int& fNodeIdB = mesh[i]->getGlobalNodeId( element_id[i], jb ); + const RealT& x2 = mesh[i]->getPosition()[0][fNodeIdB]; + const RealT& y2 = mesh[i]->getPosition()[1][fNodeIdB]; + const RealT& z2 = mesh[i]->getPosition()[2][fNodeIdB]; - if (k > 2) - { + if ( k > 2 ) { #ifdef TRIBOL_USE_HOST - SLIC_DEBUG("ContactPlane3D::computeInterpenOverlap(): too many segment-plane intersections; " << - "check for degenerate face " << m_pair->m_element_id1 << "on mesh " << mesh[i]->meshId() << "."); + SLIC_DEBUG( "ContactPlane3D::computeInterpenOverlap(): too many segment-plane intersections; " + << "check for degenerate face " << m_pair->m_element_id1 << "on mesh " << mesh[i]->meshId() + << "." ); #endif - interpen = false; - return DEGENERATE_OVERLAP; - } - - // call segment-to-plane intersection routine - if (k < 2) // we haven't found both intersection points yet - { - bool inter = LinePlaneIntersection( x1, y1, z1, x2, y2, z2, - m_cX, m_cY, m_cZ, - m_nX, m_nY, m_nZ, - xInter[2*i+k], yInter[2*i+k], - zInter[2*i+k], inPlane ); - - if (inter) ++k; - } - - // we now have the two vertices for the ith face that represent segment-plane intersections. - // Now determine the existing current configuration face vertices that lie - // "on the other side" of the contact plane. - RealT vX = x1 - m_cX; - RealT vY = y1 - m_cY; - RealT vZ = z1 - m_cZ; - - // project the vector onto the contact plane normal - RealT proj = vX*m_nX + vY*m_nY + vZ*m_nZ; - - // if the projection for face 1 vertices is positive then that vertex crosses - // (i.e. interpenetrates) the contact plane. if the projection for face 2 vertices - // is negative then that vertex crosses the contact plane - interpenVertex[ja] = (i == 0 && proj > 0) ? ja : -1; - interpenVertex[ja] = (i == 1 && proj < 0) ? ja : interpenVertex[ja]; - - } // end loop over nodes - - // if we haven't found intersection points, the planes are either separated or coplanar. - // return - - if (k < 2) - { - interpen = false; - return NO_FACE_GEOM_ERROR; - } - - // count the number of vertices for the clipped portion of the i^th face that - // interpenetrates the contact plane. - numV[i] = k; - for (int vid=0; vidnumberOfNodesPerElement(); ++vid) - { - if (interpenVertex[vid] == vid) ++numV[i]; - - // populate the face specific id array - if (i == 0) - { - interpenVertex1[vid] = interpenVertex[vid]; - } - else - { - interpenVertex2[vid] = interpenVertex[vid]; - } + interpen = false; + return DEGENERATE_OVERLAP; } - } // end loop over faces - - // allocate arrays to store new clipped vertices for the current face - constexpr int max_nodes_per_overlap = 8; - RealT cfx1[ max_nodes_per_overlap ]; // cfx = clipped face x-coordinate - RealT cfy1[ max_nodes_per_overlap ]; - RealT cfz1[ max_nodes_per_overlap ]; - - RealT cfx2[ max_nodes_per_overlap ]; // cfx = clipped face x-coordinate - RealT cfy2[ max_nodes_per_overlap ]; - RealT cfz2[ max_nodes_per_overlap ]; - - // populate arrays - for (int m=0; m<2; ++m) // populate segment-contact-plane intersection vertices - { - cfx1[ m ] = xInter[ m ]; - cfy1[ m ] = yInter[ m ]; - cfz1[ m ] = zInter[ m ]; - - cfx2[ m ] = xInter[ 2+m ]; - cfy2[ m ] = yInter[ 2+m ]; - cfz2[ m ] = zInter[ 2+m ]; - } - - // populate the face 1 vertices that cross the contact plane - int k = 2; - for (int m=0; mnumberOfNodesPerElement(); ++m) - { - if (interpenVertex1[m] != -1) + // call segment-to-plane intersection routine + if ( k < 2 ) // we haven't found both intersection points yet { - int fNodeId = mesh[0]->getGlobalNodeId(getCpElementId1(), interpenVertex1[m]); - cfx1[ k ] = mesh[0]->getPosition()[0][ fNodeId ]; - cfy1[ k ] = mesh[0]->getPosition()[1][ fNodeId ]; - cfz1[ k ] = mesh[0]->getPosition()[2][ fNodeId ]; - ++k; - } - } + bool inter = LinePlaneIntersection( x1, y1, z1, x2, y2, z2, m_cX, m_cY, m_cZ, m_nX, m_nY, m_nZ, + xInter[2 * i + k], yInter[2 * i + k], zInter[2 * i + k], inPlane ); - // populate the face 2 vertices that cross the contact plane - k = 2; - for (int m=0; mnumberOfNodesPerElement(); ++m) - { - if (interpenVertex2[m] != -1) - { - int fNodeId = mesh[1]->getGlobalNodeId(getCpElementId2(), interpenVertex2[m]); - cfx2[ k ] = mesh[1]->getPosition()[0][ fNodeId ]; - cfy2[ k ] = mesh[1]->getPosition()[1][ fNodeId ]; - cfz2[ k ] = mesh[1]->getPosition()[2][ fNodeId ]; - ++k; + if ( inter ) ++k; } - } - - // declare projected coordinate arrays - RealT cfx1_proj[ max_nodes_per_overlap ]; - RealT cfy1_proj[ max_nodes_per_overlap ]; - RealT cfz1_proj[ max_nodes_per_overlap ]; - - RealT cfx2_proj[ max_nodes_per_overlap ]; - RealT cfy2_proj[ max_nodes_per_overlap ]; - RealT cfz2_proj[ max_nodes_per_overlap ]; - - // project clipped face coordinates to contact plane - for (int i=0; igetFaceRadius()[ getCpElementId1() ], - mesh[1]->getFaceRadius()[ getCpElementId2() ] ); - RealT len_tol = pos_tol; - FaceGeomError inter_err = Intersection2DPolygon( cfx1_loc, cfy1_loc, numV[0], - cfx2_loc, cfy2_loc, numV[1], - pos_tol, len_tol, m_polyLocX, - m_polyLocY, m_numPolyVert, - m_interpenArea, true ); - - if (inter_err != NO_FACE_GEOM_ERROR) - { + + // we now have the two vertices for the ith face that represent segment-plane intersections. + // Now determine the existing current configuration face vertices that lie + // "on the other side" of the contact plane. + RealT vX = x1 - m_cX; + RealT vY = y1 - m_cY; + RealT vZ = z1 - m_cZ; + + // project the vector onto the contact plane normal + RealT proj = vX * m_nX + vY * m_nY + vZ * m_nZ; + + // if the projection for face 1 vertices is positive then that vertex crosses + // (i.e. interpenetrates) the contact plane. if the projection for face 2 vertices + // is negative then that vertex crosses the contact plane + interpenVertex[ja] = ( i == 0 && proj > 0 ) ? ja : -1; + interpenVertex[ja] = ( i == 1 && proj < 0 ) ? ja : interpenVertex[ja]; + + } // end loop over nodes + + // if we haven't found intersection points, the planes are either separated or coplanar. + // return + + if ( k < 2 ) { interpen = false; - return inter_err; - } + return NO_FACE_GEOM_ERROR; + } + + // count the number of vertices for the clipped portion of the i^th face that + // interpenetrates the contact plane. + numV[i] = k; + for ( int vid = 0; vid < mesh[i]->numberOfNodesPerElement(); ++vid ) { + if ( interpenVertex[vid] == vid ) ++numV[i]; + + // populate the face specific id array + if ( i == 0 ) { + interpenVertex1[vid] = interpenVertex[vid]; + } else { + interpenVertex2[vid] = interpenVertex[vid]; + } + } + + } // end loop over faces + + // allocate arrays to store new clipped vertices for the current face + constexpr int max_nodes_per_overlap = 8; + RealT cfx1[max_nodes_per_overlap]; // cfx = clipped face x-coordinate + RealT cfy1[max_nodes_per_overlap]; + RealT cfz1[max_nodes_per_overlap]; + + RealT cfx2[max_nodes_per_overlap]; // cfx = clipped face x-coordinate + RealT cfy2[max_nodes_per_overlap]; + RealT cfz2[max_nodes_per_overlap]; + + // populate arrays + for ( int m = 0; m < 2; ++m ) // populate segment-contact-plane intersection vertices + { + cfx1[m] = xInter[m]; + cfy1[m] = yInter[m]; + cfz1[m] = zInter[m]; + + cfx2[m] = xInter[2 + m]; + cfy2[m] = yInter[2 + m]; + cfz2[m] = zInter[2 + m]; + } + + // populate the face 1 vertices that cross the contact plane + int k = 2; + for ( int m = 0; m < mesh[0]->numberOfNodesPerElement(); ++m ) { + if ( interpenVertex1[m] != -1 ) { + int fNodeId = mesh[0]->getGlobalNodeId( getCpElementId1(), interpenVertex1[m] ); + cfx1[k] = mesh[0]->getPosition()[0][fNodeId]; + cfy1[k] = mesh[0]->getPosition()[1][fNodeId]; + cfz1[k] = mesh[0]->getPosition()[2][fNodeId]; + ++k; + } + } + + // populate the face 2 vertices that cross the contact plane + k = 2; + for ( int m = 0; m < mesh[1]->numberOfNodesPerElement(); ++m ) { + if ( interpenVertex2[m] != -1 ) { + int fNodeId = mesh[1]->getGlobalNodeId( getCpElementId2(), interpenVertex2[m] ); + cfx2[k] = mesh[1]->getPosition()[0][fNodeId]; + cfy2[k] = mesh[1]->getPosition()[1][fNodeId]; + cfz2[k] = mesh[1]->getPosition()[2][fNodeId]; + ++k; + } + } + + // declare projected coordinate arrays + RealT cfx1_proj[max_nodes_per_overlap]; + RealT cfy1_proj[max_nodes_per_overlap]; + RealT cfz1_proj[max_nodes_per_overlap]; + + RealT cfx2_proj[max_nodes_per_overlap]; + RealT cfy2_proj[max_nodes_per_overlap]; + RealT cfz2_proj[max_nodes_per_overlap]; + + // project clipped face coordinates to contact plane + for ( int i = 0; i < numV[0]; ++i ) { + ProjectPointToPlane( cfx1[i], cfy1[i], cfz1[i], m_nX, m_nY, m_nZ, m_cX, m_cY, m_cZ, cfx1_proj[i], cfy1_proj[i], + cfz1_proj[i] ); + } + + for ( int i = 0; i < numV[1]; ++i ) { + ProjectPointToPlane( cfx2[i], cfy2[i], cfz2[i], m_nX, m_nY, m_nZ, m_cX, m_cY, m_cZ, cfx2_proj[i], cfy2_proj[i], + cfz2_proj[i] ); + } + + // declare local coordinate pointers + RealT cfx1_loc[max_nodes_per_overlap]; + RealT cfy1_loc[max_nodes_per_overlap]; + + RealT cfx2_loc[max_nodes_per_overlap]; + RealT cfy2_loc[max_nodes_per_overlap]; - // store the local intersection polygons on the contact plane object, - // primarily for visualization + // convert global coords to local contact plane coordinates + GlobalTo2DLocalCoords( cfx1_proj, cfy1_proj, cfz1_proj, m_e1X, m_e1Y, m_e1Z, m_e2X, m_e2Y, m_e2Z, m_cX, m_cY, m_cZ, + cfx1_loc, cfy1_loc, numV[0] ); - m_numInterpenPoly1Vert = numV[0]; - m_numInterpenPoly2Vert = numV[1]; + GlobalTo2DLocalCoords( cfx2_proj, cfy2_proj, cfz2_proj, m_e1X, m_e1Y, m_e1Z, m_e2X, m_e2Y, m_e2Z, m_cX, m_cY, m_cZ, + cfx2_loc, cfy2_loc, numV[1] ); - for (int i=0; igetFaceRadius()[getCpElementId1()], + mesh[1]->getFaceRadius()[getCpElementId2()] ); + RealT len_tol = pos_tol; + FaceGeomError inter_err = + Intersection2DPolygon( cfx1_loc, cfy1_loc, numV[0], cfx2_loc, cfy2_loc, numV[1], pos_tol, len_tol, m_polyLocX, + m_polyLocY, m_numPolyVert, m_interpenArea, true ); - interpen = true; - return NO_FACE_GEOM_ERROR; - -} // end ContactPlane3D::computeLocalInterpenOverlap() + if ( inter_err != NO_FACE_GEOM_ERROR ) { + interpen = false; + return inter_err; + } + + // store the local intersection polygons on the contact plane object, + // primarily for visualization + + m_numInterpenPoly1Vert = numV[0]; + m_numInterpenPoly2Vert = numV[1]; + + for ( int i = 0; i < numV[0]; ++i ) { + m_interpenPoly1X[i] = cfx1_loc[i]; + m_interpenPoly1Y[i] = cfy1_loc[i]; + } + + for ( int i = 0; i < numV[1]; ++i ) { + m_interpenPoly2X[i] = cfx2_loc[i]; + m_interpenPoly2Y[i] = cfy2_loc[i]; + } + + interpen = true; + return NO_FACE_GEOM_ERROR; + +} // end ContactPlane3D::computeLocalInterpenOverlap() //----------------------------------------------------------------------------- // Contact Plane 2D routines //----------------------------------------------------------------------------- -TRIBOL_HOST_DEVICE ContactPlane2D::ContactPlane2D( InterfacePair* pair, - RealT lenFrac, - bool interpenOverlap, +TRIBOL_HOST_DEVICE ContactPlane2D::ContactPlane2D( InterfacePair* pair, RealT lenFrac, bool interpenOverlap, bool interPlane ) - : ContactPlane( pair, lenFrac, interpenOverlap, interPlane, 2 ) + : ContactPlane( pair, lenFrac, interpenOverlap, interPlane, 2 ) { - for (int i=0; i<2; ++i) - { - m_segX[i] = 0.0; - m_segY[i] = 0.0; - } -} // end ContactPlane2D::ContactPlane2D() + for ( int i = 0; i < 2; ++i ) { + m_segX[i] = 0.0; + m_segY[i] = 0.0; + } +} // end ContactPlane2D::ContactPlane2D() //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE ContactPlane2D::ContactPlane2D() - : ContactPlane2D( nullptr, 0.0, true, false ) -{} +TRIBOL_HOST_DEVICE ContactPlane2D::ContactPlane2D() : ContactPlane2D( nullptr, 0.0, true, false ) {} //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE FaceGeomError CheckEdgePair( ContactPlane2D& cp, - const MeshData::Viewer& mesh1, - const MeshData::Viewer& mesh2, - const Parameters& params, +TRIBOL_HOST_DEVICE FaceGeomError CheckEdgePair( ContactPlane2D& cp, const MeshData::Viewer& mesh1, + const MeshData::Viewer& mesh2, const Parameters& params, bool fullOverlap ) { - // Note: Checks #1-#4 are done in the binning - - // alias variables off the InterfacePair - IndexT edgeId1 = cp.getCpElementId1(); - IndexT edgeId2 = cp.getCpElementId2(); - - // instantiate temporary contact plane to be returned by this routine - bool interpenOverlap = (!fullOverlap) ? true : false; - - // CHECK #5: check if edge2 vertices have passed through edge1, and - // vice-versa. If both vertices have done so for either edge, we trigger - // a fullOverlap computation, but we don't know if there is a positive - // length of overlap and will have to construct the contact plane - // (contact segment) and perform this check. Note, this tolerance is - // inclusive up to a separation of a fraction of the edge-radius. - // This is done for the mortar method per 3D testing. - RealT separationTol = params.gap_separation_ratio * - axom::utilities::max( mesh1.getFaceRadius()[ edgeId1 ], - mesh2.getFaceRadius()[ edgeId2 ] ); - bool all = false; - bool ls = EdgeInterCheck( mesh1, mesh2, edgeId1, edgeId2, separationTol, all ); - if (!ls) - { + // Note: Checks #1-#4 are done in the binning + + // alias variables off the InterfacePair + IndexT edgeId1 = cp.getCpElementId1(); + IndexT edgeId2 = cp.getCpElementId2(); + + // instantiate temporary contact plane to be returned by this routine + bool interpenOverlap = ( !fullOverlap ) ? true : false; + + // CHECK #5: check if edge2 vertices have passed through edge1, and + // vice-versa. If both vertices have done so for either edge, we trigger + // a fullOverlap computation, but we don't know if there is a positive + // length of overlap and will have to construct the contact plane + // (contact segment) and perform this check. Note, this tolerance is + // inclusive up to a separation of a fraction of the edge-radius. + // This is done for the mortar method per 3D testing. + RealT separationTol = params.gap_separation_ratio * + axom::utilities::max( mesh1.getFaceRadius()[edgeId1], mesh2.getFaceRadius()[edgeId2] ); + bool all = false; + bool ls = EdgeInterCheck( mesh1, mesh2, edgeId1, edgeId2, separationTol, all ); + if ( !ls ) { + cp.m_inContact = false; + return NO_FACE_GEOM_ERROR; + } + + // if all the vertices lie on the other side of edge1, then use full + // projection + if ( all ) { + fullOverlap = true; + interpenOverlap = false; + cp.m_interpenOverlap = interpenOverlap; + } + + // CHECK #6: compute the projected length of overlap on the contact plane. + // At this point the edges are proximate and likely have a positive + // projected length of overlap. + + cp.computeNormal( mesh1, mesh2 ); + cp.computePlanePoint( mesh1, mesh2 ); + + // project each edge's nodes onto the contact segment. + constexpr int max_nodes_per_elem = 2; + RealT projX1[max_nodes_per_elem]; + RealT projY1[max_nodes_per_elem]; + RealT projX2[max_nodes_per_elem]; + RealT projY2[max_nodes_per_elem]; + + ProjectEdgeNodesToSegment( mesh1, edgeId1, cp.m_nX, cp.m_nY, cp.m_cX, cp.m_cY, &projX1[0], &projY1[0] ); + ProjectEdgeNodesToSegment( mesh2, edgeId2, cp.m_nX, cp.m_nY, cp.m_cX, cp.m_cY, &projX2[0], &projY2[0] ); + + // compute the full overlap. Even if we are using the interpenetration + // overlap, we have to compute the full overlap in order to properly + // locate the contact plane (segment) for the interpenetration calculation + cp.checkSegOverlap( &projX1[0], &projY1[0], &projX2[0], &projY2[0], mesh1.numberOfNodesPerElement(), + mesh2.numberOfNodesPerElement() ); + + // compute the overlap length tolerance + cp.computeAreaTol( mesh1, mesh2, params ); + + // check the contact plane length against the minimum length. + // In general the interpen length is going to be less than + // the full overlap length so we can do this check prior to + // any interpenetration overlap calculation + if ( cp.m_area < cp.m_areaMin ) { + cp.m_inContact = false; + return NO_FACE_GEOM_ERROR; + } + + if ( interpenOverlap ) { + // properly locate the contact plane (segment) + cp.planePointAndCentroidGap( + mesh1, mesh2, 2. * axom::utilities::max( mesh1.getFaceRadius()[edgeId1], mesh2.getFaceRadius()[edgeId2] ) ); + bool interpen = false; + FaceGeomError interpen_err = cp.computeLocalInterpenOverlap( mesh1, mesh2, params, interpen ); + if ( interpen_err != NO_FACE_GEOM_ERROR ) { cp.m_inContact = false; - return NO_FACE_GEOM_ERROR; - } - - // if all the vertices lie on the other side of edge1, then use full - // projection - if (all) - { - fullOverlap = true; - interpenOverlap = false; - cp.m_interpenOverlap = interpenOverlap; - } - - // CHECK #6: compute the projected length of overlap on the contact plane. - // At this point the edges are proximate and likely have a positive - // projected length of overlap. - - cp.computeNormal(mesh1, mesh2); - cp.computePlanePoint(mesh1, mesh2); - - // project each edge's nodes onto the contact segment. - constexpr int max_nodes_per_elem = 2; - RealT projX1[max_nodes_per_elem]; - RealT projY1[max_nodes_per_elem]; - RealT projX2[max_nodes_per_elem]; - RealT projY2[max_nodes_per_elem]; - - ProjectEdgeNodesToSegment( mesh1, edgeId1, cp.m_nX, cp.m_nY, - cp.m_cX, cp.m_cY, &projX1[0], &projY1[0] ); - ProjectEdgeNodesToSegment( mesh2, edgeId2, cp.m_nX, cp.m_nY, - cp.m_cX, cp.m_cY, &projX2[0], &projY2[0] ); - - // compute the full overlap. Even if we are using the interpenetration - // overlap, we have to compute the full overlap in order to properly - // locate the contact plane (segment) for the interpenetration calculation - cp.checkSegOverlap( &projX1[0], &projY1[0], &projX2[0], &projY2[0], - mesh1.numberOfNodesPerElement(), mesh2.numberOfNodesPerElement() ); - - // compute the overlap length tolerance - cp.computeAreaTol( mesh1, mesh2, params ); - - // check the contact plane length against the minimum length. - // In general the interpen length is going to be less than - // the full overlap length so we can do this check prior to - // any interpenetration overlap calculation - if (cp.m_area < cp.m_areaMin) - { + return interpen_err; + } else if ( !interpen ) { cp.m_inContact = false; return NO_FACE_GEOM_ERROR; - } - - if (interpenOverlap) - { - // properly locate the contact plane (segment) - cp.planePointAndCentroidGap( mesh1, mesh2, 2. * - axom::utilities::max( mesh1.getFaceRadius()[ edgeId1 ], - mesh2.getFaceRadius()[ edgeId2 ] )); - bool interpen = false; - FaceGeomError interpen_err = cp.computeLocalInterpenOverlap( - mesh1, mesh2, params, interpen ); - if (interpen_err != NO_FACE_GEOM_ERROR) - { - cp.m_inContact = false; - return interpen_err; - } - else if (!interpen) - { - cp.m_inContact = false; - return NO_FACE_GEOM_ERROR; - } - } - - // recompute the plane point and centroid gap. For the full overlap - // the centroid (i.e. plane point) of the contact plane has been modified - // based on the computed segment overlap. This routine will relocate the - // contact plane and compute the centroid gap. For the interpenetration - // overlap, this ought to only amount to a centroid gap calculation as the - // contact plane was properly located wrt the two edges, but the contact - // plane point moved (in-contact segment) due to the interpen overlap - // segment calc - cp.planePointAndCentroidGap( mesh1, mesh2, 2. * - axom::utilities::max( mesh1.getFaceRadius()[ edgeId1 ], - mesh2.getFaceRadius()[ edgeId2 ] )); - - // Per 3D mortar testing, allow for separation up to the edge-radius - cp.m_gapTol = params.gap_separation_ratio * - axom::utilities::max( mesh1.getFaceRadius()[ edgeId1 ], - mesh2.getFaceRadius()[ edgeId2 ] ); - if (cp.m_gap > cp.m_gapTol) - { + } + } + + // recompute the plane point and centroid gap. For the full overlap + // the centroid (i.e. plane point) of the contact plane has been modified + // based on the computed segment overlap. This routine will relocate the + // contact plane and compute the centroid gap. For the interpenetration + // overlap, this ought to only amount to a centroid gap calculation as the + // contact plane was properly located wrt the two edges, but the contact + // plane point moved (in-contact segment) due to the interpen overlap + // segment calc + cp.planePointAndCentroidGap( + mesh1, mesh2, 2. * axom::utilities::max( mesh1.getFaceRadius()[edgeId1], mesh2.getFaceRadius()[edgeId2] ) ); + + // Per 3D mortar testing, allow for separation up to the edge-radius + cp.m_gapTol = params.gap_separation_ratio * + axom::utilities::max( mesh1.getFaceRadius()[edgeId1], mesh2.getFaceRadius()[edgeId2] ); + if ( cp.m_gap > cp.m_gapTol ) { + cp.m_inContact = false; + return NO_FACE_GEOM_ERROR; + } + + // for auto-contact, remove contact candidacy for full-overlap + // face-pairs with interpenetration exceeding contact penetration fraction. + // Note, this check is solely meant to exclude face-pairs composed of faces + // on opposite sides of thin structures/plates + // + // Recall that interpen gaps are negative + if ( fullOverlap ) { + if ( ExceedsMaxAutoInterpen( mesh1, mesh2, edgeId1, edgeId2, params, cp.m_gap ) ) { cp.m_inContact = false; return NO_FACE_GEOM_ERROR; - } - - // for auto-contact, remove contact candidacy for full-overlap - // face-pairs with interpenetration exceeding contact penetration fraction. - // Note, this check is solely meant to exclude face-pairs composed of faces - // on opposite sides of thin structures/plates - // - // Recall that interpen gaps are negative - if (fullOverlap) - { - if (ExceedsMaxAutoInterpen( mesh1, mesh2, edgeId1, edgeId2, - params, cp.m_gap )) - { - cp.m_inContact = false; - return NO_FACE_GEOM_ERROR; - } - } - - // for the full overlap case we need to project the overlap segment - // onto the updated contact plane - if (fullOverlap) - { - // allocate dummy space for the interpen topology so adding the - // contact plane to the contact plane manager doesn't seg fault. - // Fix this later... - cp.m_numInterpenPoly1Vert = 2; - cp.m_numInterpenPoly2Vert = 2; - - for (int i=0; i<2; ++i) - { - RealT xproj, yproj; - ProjectPointToSegment( cp.m_segX[i], cp.m_segY[i], - cp.m_nX, cp.m_nY, - cp.m_cX, cp.m_cY, xproj, yproj ); - cp.m_segX[i] = xproj; - cp.m_segY[i] = yproj; - - // set the interpen vertices to the full overlap vertices - cp.m_interpenG1X[i] = 0.0; - cp.m_interpenG1Y[i] = 0.0; - cp.m_interpenG2X[i] = 0.0; - cp.m_interpenG2Y[i] = 0.0; - } - - } + } + } + + // for the full overlap case we need to project the overlap segment + // onto the updated contact plane + if ( fullOverlap ) { + // allocate dummy space for the interpen topology so adding the + // contact plane to the contact plane manager doesn't seg fault. + // Fix this later... + cp.m_numInterpenPoly1Vert = 2; + cp.m_numInterpenPoly2Vert = 2; + + for ( int i = 0; i < 2; ++i ) { + RealT xproj, yproj; + ProjectPointToSegment( cp.m_segX[i], cp.m_segY[i], cp.m_nX, cp.m_nY, cp.m_cX, cp.m_cY, xproj, yproj ); + cp.m_segX[i] = xproj; + cp.m_segY[i] = yproj; + + // set the interpen vertices to the full overlap vertices + cp.m_interpenG1X[i] = 0.0; + cp.m_interpenG1Y[i] = 0.0; + cp.m_interpenG2X[i] = 0.0; + cp.m_interpenG2Y[i] = 0.0; + } + } - cp.m_inContact = true; - return NO_FACE_GEOM_ERROR; + cp.m_inContact = true; + return NO_FACE_GEOM_ERROR; -} // end CheckEdgePair() +} // end CheckEdgePair() //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE void ContactPlane2D::computeNormal( const MeshData::Viewer& m1, - const MeshData::Viewer& m2 ) +TRIBOL_HOST_DEVICE void ContactPlane2D::computeNormal( const MeshData::Viewer& m1, const MeshData::Viewer& m2 ) { - if (m_intermediatePlane) - { - // COMMON_PLANE normal calculation: - // compute the cp normal as the average of the two face normals, and in - // the direction such that the dot product between the cp normal and - // the normal of face 2 is positive. - m_nX = 0.5 * (m2.getElementNormals()[0][ m_pair->m_element_id2 ] - m1.getElementNormals()[0][ m_pair->m_element_id1 ]); - m_nY = 0.5 * (m2.getElementNormals()[1][ m_pair->m_element_id2 ] - m1.getElementNormals()[1][ m_pair->m_element_id1 ]); - m_nZ = 0.0; // zero out the third component of the normal - } - else - { - // MORTAR normal calculation. This is the normal of the nonmortar surface - m_nX = m2.getElementNormals()[0][ m_pair->m_element_id2 ]; - m_nY = m2.getElementNormals()[1][ m_pair->m_element_id2 ]; - m_nZ = 0.; - } - - // normalize the cp normal - RealT mag; - RealT invMag; - - mag = magnitude( m_nX, m_nY ); - invMag = 1.0 / mag; - - m_nX *= invMag; - m_nY *= invMag; - - return; - -} // end ContactPlane2D::computeNormal() + if ( m_intermediatePlane ) { + // COMMON_PLANE normal calculation: + // compute the cp normal as the average of the two face normals, and in + // the direction such that the dot product between the cp normal and + // the normal of face 2 is positive. + m_nX = + 0.5 * ( m2.getElementNormals()[0][m_pair->m_element_id2] - m1.getElementNormals()[0][m_pair->m_element_id1] ); + m_nY = + 0.5 * ( m2.getElementNormals()[1][m_pair->m_element_id2] - m1.getElementNormals()[1][m_pair->m_element_id1] ); + m_nZ = 0.0; // zero out the third component of the normal + } else { + // MORTAR normal calculation. This is the normal of the nonmortar surface + m_nX = m2.getElementNormals()[0][m_pair->m_element_id2]; + m_nY = m2.getElementNormals()[1][m_pair->m_element_id2]; + m_nZ = 0.; + } + + // normalize the cp normal + RealT mag; + RealT invMag; + + mag = magnitude( m_nX, m_nY ); + invMag = 1.0 / mag; + + m_nX *= invMag; + m_nY *= invMag; + + return; + +} // end ContactPlane2D::computeNormal() //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE void ContactPlane2D::computePlanePoint( const MeshData::Viewer& m1, - const MeshData::Viewer& m2 ) +TRIBOL_HOST_DEVICE void ContactPlane2D::computePlanePoint( const MeshData::Viewer& m1, const MeshData::Viewer& m2 ) { - // compute the cp centroid as the average of - // the two face's centers. This is the default - // method of compute the cp centroid - m_cX = 0.5 * ( m1.getElementCentroids()[0][m_pair->m_element_id1] + m2.getElementCentroids()[0][m_pair->m_element_id2] ); - m_cY = 0.5 * ( m1.getElementCentroids()[1][m_pair->m_element_id1] + m2.getElementCentroids()[1][m_pair->m_element_id2] ); - m_cZ = 0.0; - return; - -} // end ContactPlane2D::computePlanePoint() + // compute the cp centroid as the average of + // the two face's centers. This is the default + // method of compute the cp centroid + m_cX = + 0.5 * ( m1.getElementCentroids()[0][m_pair->m_element_id1] + m2.getElementCentroids()[0][m_pair->m_element_id2] ); + m_cY = + 0.5 * ( m1.getElementCentroids()[1][m_pair->m_element_id1] + m2.getElementCentroids()[1][m_pair->m_element_id2] ); + m_cZ = 0.0; + return; + +} // end ContactPlane2D::computePlanePoint() //------------------------------------------------------------------------------ void ContactPlane2D::computeIntegralGap() { - // TODO implement this routine - // This will be contact method dependent - return; + // TODO implement this routine + // This will be contact method dependent + return; -} // end ContactPlane2D::computeIntegralGap() +} // end ContactPlane2D::computeIntegralGap() //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE void ContactPlane2D::computeAreaTol( const MeshData::Viewer& m1, - const MeshData::Viewer& m2, +TRIBOL_HOST_DEVICE void ContactPlane2D::computeAreaTol( const MeshData::Viewer& m1, const MeshData::Viewer& m2, const Parameters& params ) { - if (m_areaFrac < params.overlap_area_frac) - { + if ( m_areaFrac < params.overlap_area_frac ) { #ifdef TRIBOL_USE_HOST - SLIC_DEBUG( "ContactPlane2D::computeAreaTol() the overlap area fraction too small or negative; " << - "setting to overlap_area_frac parameter." ); + SLIC_DEBUG( "ContactPlane2D::computeAreaTol() the overlap area fraction too small or negative; " + << "setting to overlap_area_frac parameter." ); #endif - m_areaFrac = params.overlap_area_frac; - } + m_areaFrac = params.overlap_area_frac; + } - m_areaMin = m_areaFrac * - axom::utilities::min( m1.getElementAreas()[ m_pair->m_element_id1 ], - m2.getElementAreas()[ m_pair->m_element_id2 ] ); - return; + m_areaMin = m_areaFrac * axom::utilities::min( m1.getElementAreas()[m_pair->m_element_id1], + m2.getElementAreas()[m_pair->m_element_id2] ); + return; -} // ContactPlane2D::computeAreaTol() +} // ContactPlane2D::computeAreaTol() //------------------------------------------------------------------------------ -void ContactPlane2D::centroidGap( const MeshData::Viewer& m1, - const MeshData::Viewer& m2, - RealT scale ) +void ContactPlane2D::centroidGap( const MeshData::Viewer& m1, const MeshData::Viewer& m2, RealT scale ) { - // project the overlap centroid, which is taken to be the point data - // (i.e. centroid) of the contact plane, back to each edge using the - // line-plane intersection method where each edge is imagined to be - // within a plane defined by the edge's centroid and normal - RealT xc1 = 0.; - RealT yc1 = 0.; - RealT zc1 = 0.; - RealT xc2 = 0.; - RealT yc2 = 0.; - RealT zc2 = 0.; - - // find where the overlap centroid (plane point) intersects each face. - // The following variables store the end vertices of - // a fictitious line co-directional with the contact plane normal - // passing through each edge - - // set the line segment's first vertex at the contact plane centroid, - // scaled in the direction opposite the contact plane normal - RealT xA = m_cX + m_nX * scale; - RealT yA = m_cY + m_nY * scale; - RealT zA = 0.0; - - // use the contact plane normal as the directional vector scale - // in the direction of the contact plane - RealT xB = m_cX - m_nX * scale; - RealT yB = m_cY - m_nY * scale; - RealT zB = 0.0; - - bool inPlane = false; - IndexT fId1 = m_pair->m_element_id1; - IndexT fId2 = m_pair->m_element_id2; - bool intersect1 = LinePlaneIntersection( xA, yA, zA, xB, yB, zB, - m1.getElementCentroids()[0][fId1], m1.getElementCentroids()[1][fId1], 0.0, - m1.getElementNormals()[0][fId1], m1.getElementNormals()[1][fId1], 0.0, - xc1, yc1, zc1, inPlane ); - - bool intersect2 = LinePlaneIntersection( xA, yA, zA, xB, yB, zB, - m2.getElementCentroids()[0][fId2], m2.getElementCentroids()[1][fId2], 0.0, - m2.getElementNormals()[0][fId2], m2.getElementNormals()[1][fId2], 0.0, - xc2, yc2, zc2, inPlane ); - TRIBOL_UNUSED_VAR(intersect1); // We don't currently use these bool variabeles - TRIBOL_UNUSED_VAR(intersect2); // but the above function calls modify some parameters - - // compute the normal gap magnitude (x1 - x2 for positive gap in separation - // and negative gap in penetration). - m_gap = (xc1 - xc2) * m_nX + (yc1 - yc2) * m_nY; - - // store the two edge points corresponding to the contact plane centroid - // projection/intersection - m_cXf1 = xc1; - m_cYf1 = yc1; - m_cZf1 = 0.0; - - m_cXf2 = xc2; - m_cYf2 = yc2; - m_cZf2 = 0.0; - - return; - -} // end ContactPlane2D::centroidGap() + // project the overlap centroid, which is taken to be the point data + // (i.e. centroid) of the contact plane, back to each edge using the + // line-plane intersection method where each edge is imagined to be + // within a plane defined by the edge's centroid and normal + RealT xc1 = 0.; + RealT yc1 = 0.; + RealT zc1 = 0.; + RealT xc2 = 0.; + RealT yc2 = 0.; + RealT zc2 = 0.; + + // find where the overlap centroid (plane point) intersects each face. + // The following variables store the end vertices of + // a fictitious line co-directional with the contact plane normal + // passing through each edge + + // set the line segment's first vertex at the contact plane centroid, + // scaled in the direction opposite the contact plane normal + RealT xA = m_cX + m_nX * scale; + RealT yA = m_cY + m_nY * scale; + RealT zA = 0.0; + + // use the contact plane normal as the directional vector scale + // in the direction of the contact plane + RealT xB = m_cX - m_nX * scale; + RealT yB = m_cY - m_nY * scale; + RealT zB = 0.0; + + bool inPlane = false; + IndexT fId1 = m_pair->m_element_id1; + IndexT fId2 = m_pair->m_element_id2; + bool intersect1 = LinePlaneIntersection( xA, yA, zA, xB, yB, zB, m1.getElementCentroids()[0][fId1], + m1.getElementCentroids()[1][fId1], 0.0, m1.getElementNormals()[0][fId1], + m1.getElementNormals()[1][fId1], 0.0, xc1, yc1, zc1, inPlane ); + + bool intersect2 = LinePlaneIntersection( xA, yA, zA, xB, yB, zB, m2.getElementCentroids()[0][fId2], + m2.getElementCentroids()[1][fId2], 0.0, m2.getElementNormals()[0][fId2], + m2.getElementNormals()[1][fId2], 0.0, xc2, yc2, zc2, inPlane ); + TRIBOL_UNUSED_VAR( intersect1 ); // We don't currently use these bool variabeles + TRIBOL_UNUSED_VAR( intersect2 ); // but the above function calls modify some parameters + + // compute the normal gap magnitude (x1 - x2 for positive gap in separation + // and negative gap in penetration). + m_gap = ( xc1 - xc2 ) * m_nX + ( yc1 - yc2 ) * m_nY; + + // store the two edge points corresponding to the contact plane centroid + // projection/intersection + m_cXf1 = xc1; + m_cYf1 = yc1; + m_cZf1 = 0.0; + + m_cXf2 = xc2; + m_cYf2 = yc2; + m_cZf2 = 0.0; + + return; + +} // end ContactPlane2D::centroidGap() //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE void ContactPlane2D::checkSegOverlap( const RealT* const pX1, const RealT* const pY1, - const RealT* const pX2, const RealT* const pY2, - const int nV1, const int nV2 ) +TRIBOL_HOST_DEVICE void ContactPlane2D::checkSegOverlap( const RealT* const pX1, const RealT* const pY1, + const RealT* const pX2, const RealT* const pY2, const int nV1, + const int nV2 ) { - // TODO: Re-write in a way where the assert isn't needed + // TODO: Re-write in a way where the assert isn't needed #ifdef TRIBOL_USE_CUDA - assert(nV1 == 2); - assert(nV2 == 2); + assert( nV1 == 2 ); + assert( nV2 == 2 ); #else - SLIC_ASSERT( nV1 == 2 ); - SLIC_ASSERT( nV2 == 2 ); + SLIC_ASSERT( nV1 == 2 ); + SLIC_ASSERT( nV2 == 2 ); #endif - // define the edge 1 non-unit directional vector between vertices - // 2 and 1 - RealT lvx1 = pX1[1] - pX1[0]; - RealT lvy1 = pY1[1] - pY1[0]; - - RealT e1_len = magnitude( lvx1, lvy1 ); - - // define the edge 2 non-unit directional vector between vertices - // 2 and 1 - RealT lvx2 = pX2[1] - pX2[0]; - RealT lvy2 = pY2[1] - pY2[0]; - - RealT e2_len = magnitude( lvx2, lvy2 ); - - // - // perform the all-in-1 check - // - - // compute vector between each edge 2 vertex and vertex 1 on edge 1. - // Then dot that vector with the directional vector of edge 1 to see - // if they are codirectional (projection > 0 indicating edge 2 vertex - // lies within or beyond edge 1. If so, check, that this vector length is - // less than edge 1 length indicating that the vertex lies within edge 1 - int inter2 = 0; - int twoInOneId = -1; - for (int i=0; i; if vLen < some tolerance we have a - // coincident node - RealT vLen = magnitude( vx, vy ); - - // check for >= 0 projections and vector lengths <= edge 1 length. This - // indicates an edge 2 vertex interior to edge 1, or coincident vertices in - // the case of projection = 0 or vector length is equal to edge 1 length - if (proj >= 0 && vLen <= e1_len) // interior vertex - { - twoInOneId = i; - ++inter2; - } - } - - // if both vertices pass the above criteria than 2 is in 1 - if (inter2 == 2) - { - // set the contact plane (segment) length - m_area = e2_len; - - // set the vertices of the overlap segment - m_segX[0] = pX2[0]; - m_segY[0] = pY2[0]; - - m_segX[1] = pX2[1]; - m_segY[1] = pY2[1]; - - // relocate the centroid within the currently defined contact - // segment - m_cX = 0.5 * (m_segX[0] + m_segX[1]); - m_cY = 0.5 * (m_segY[0] + m_segY[1]); - m_cZ = 0.0; - return; - } - - // - // perform the all-in-2 check - // - - // compute vector between each edge 1 vertex and vertex 1 on edge 2. - // Then dot that vector with the directional vector of edge 2 to see - // if they are codirectional. If so, check, that this vector length is - // less than edge 2 length indicating that the vertex is within edge 2 - int inter1 = 0; - int oneInTwoId = -1; - for (int i=0; i - RealT vLen = magnitude( vx, vy ); - - // check for >= 0 projections and vector lengths <= edge 2 length. This - // indicates an edge 1 vertex interior to edge 2 or is coincident if the - // projection is zero or vector length is equal to edge 2 length - if (proj >= 0. && vLen <= e2_len) // interior vertex - { - oneInTwoId = i; - ++inter1; - } - } - - // if both vertices pass the above criteria then 1 is in 2. - if (inter1 == 2) - { - // set the contact plane (segment) length - m_area = e1_len; - - // set the overlap segment vertices on the contact plane object - m_segX[0] = pX1[0]; - m_segY[0] = pY1[0]; - - m_segX[1] = pX1[1]; - m_segY[1] = pY1[1]; - - // relocate the centroid within the currently defined contact - // segment - m_cX = 0.5 * (m_segX[0] + m_segX[1]); - m_cY = 0.5 * (m_segY[0] + m_segY[1]); - m_cZ = 0.0; - return; - } - - // if inter1 == 0 and inter2 == 0 then there is no overlap - if (inter1 == 0 && inter2 == 0) - { - m_area = 0.0; - m_cX = m_cY = m_cZ = 0.0; - return; - } - - // there is a chance that oneInTowId or twoInOneId is not actually set, - // in which case we don't have an overlap. - if (oneInTwoId == -1 || twoInOneId == -1) - { - m_area = 0.0; - m_cX = m_cY = m_cZ = 0.0; - return; - } - - // if we are here, we have ruled out all-in-1 and all-in-2 overlaps, - // and non-overlapping edges, but have the case where edge 1 and - // edge 2 overlap some finite distance that is less than either of their - // lengths. We have vertex information from the all-in-one checks - // indicating which vertices on one edge are within the other edge - - // set the segment vertices - m_segX[0] = pX1[ oneInTwoId ]; - m_segY[0] = pY1[ oneInTwoId ]; - m_segX[1] = pX2[ twoInOneId ]; - m_segY[1] = pY2[ twoInOneId ]; - - // compute vector between "inter"-vertices - RealT vecX = m_segX[1] - m_segX[0]; - RealT vecY = m_segY[1] - m_segY[0]; - - // compute the length of the overlapping segment - m_area = magnitude( vecX, vecY ); - - // compute the overlap centroid - m_cX = 0.5 * (m_segX[0] + m_segX[1]); - m_cY = 0.5 * (m_segY[0] + m_segY[1]); - m_cZ = 0.0; - - return; - -} // end ContactPlane2D::checkSegOverlap() + // define the edge 1 non-unit directional vector between vertices + // 2 and 1 + RealT lvx1 = pX1[1] - pX1[0]; + RealT lvy1 = pY1[1] - pY1[0]; + + RealT e1_len = magnitude( lvx1, lvy1 ); + + // define the edge 2 non-unit directional vector between vertices + // 2 and 1 + RealT lvx2 = pX2[1] - pX2[0]; + RealT lvy2 = pY2[1] - pY2[0]; + + RealT e2_len = magnitude( lvx2, lvy2 ); + + // + // perform the all-in-1 check + // + + // compute vector between each edge 2 vertex and vertex 1 on edge 1. + // Then dot that vector with the directional vector of edge 1 to see + // if they are codirectional (projection > 0 indicating edge 2 vertex + // lies within or beyond edge 1. If so, check, that this vector length is + // less than edge 1 length indicating that the vertex lies within edge 1 + int inter2 = 0; + int twoInOneId = -1; + for ( int i = 0; i < nV2; ++i ) { + RealT vx = pX2[i] - pX1[0]; + RealT vy = pY2[i] - pY1[0]; + + // compute projection onto edge 1 directional vector. (Positive if codirectional, + // negative otherwise. Only positive projections will be potential overlap vertex candidates + RealT proj = vx * lvx1 + vy * lvy1; + + // compute length of ; if vLen < some tolerance we have a + // coincident node + RealT vLen = magnitude( vx, vy ); + + // check for >= 0 projections and vector lengths <= edge 1 length. This + // indicates an edge 2 vertex interior to edge 1, or coincident vertices in + // the case of projection = 0 or vector length is equal to edge 1 length + if ( proj >= 0 && vLen <= e1_len ) // interior vertex + { + twoInOneId = i; + ++inter2; + } + } + + // if both vertices pass the above criteria than 2 is in 1 + if ( inter2 == 2 ) { + // set the contact plane (segment) length + m_area = e2_len; + + // set the vertices of the overlap segment + m_segX[0] = pX2[0]; + m_segY[0] = pY2[0]; + + m_segX[1] = pX2[1]; + m_segY[1] = pY2[1]; + + // relocate the centroid within the currently defined contact + // segment + m_cX = 0.5 * ( m_segX[0] + m_segX[1] ); + m_cY = 0.5 * ( m_segY[0] + m_segY[1] ); + m_cZ = 0.0; + return; + } + + // + // perform the all-in-2 check + // + + // compute vector between each edge 1 vertex and vertex 1 on edge 2. + // Then dot that vector with the directional vector of edge 2 to see + // if they are codirectional. If so, check, that this vector length is + // less than edge 2 length indicating that the vertex is within edge 2 + int inter1 = 0; + int oneInTwoId = -1; + for ( int i = 0; i < nV1; ++i ) { + RealT vx = pX1[i] - pX2[0]; + RealT vy = pY1[i] - pY2[0]; + + // compute projection onto edge 2 directional vector + RealT proj = vx * lvx2 + vy * lvy2; + + // compute length of + RealT vLen = magnitude( vx, vy ); + + // check for >= 0 projections and vector lengths <= edge 2 length. This + // indicates an edge 1 vertex interior to edge 2 or is coincident if the + // projection is zero or vector length is equal to edge 2 length + if ( proj >= 0. && vLen <= e2_len ) // interior vertex + { + oneInTwoId = i; + ++inter1; + } + } + + // if both vertices pass the above criteria then 1 is in 2. + if ( inter1 == 2 ) { + // set the contact plane (segment) length + m_area = e1_len; + + // set the overlap segment vertices on the contact plane object + m_segX[0] = pX1[0]; + m_segY[0] = pY1[0]; + + m_segX[1] = pX1[1]; + m_segY[1] = pY1[1]; + + // relocate the centroid within the currently defined contact + // segment + m_cX = 0.5 * ( m_segX[0] + m_segX[1] ); + m_cY = 0.5 * ( m_segY[0] + m_segY[1] ); + m_cZ = 0.0; + return; + } + + // if inter1 == 0 and inter2 == 0 then there is no overlap + if ( inter1 == 0 && inter2 == 0 ) { + m_area = 0.0; + m_cX = m_cY = m_cZ = 0.0; + return; + } + + // there is a chance that oneInTowId or twoInOneId is not actually set, + // in which case we don't have an overlap. + if ( oneInTwoId == -1 || twoInOneId == -1 ) { + m_area = 0.0; + m_cX = m_cY = m_cZ = 0.0; + return; + } + + // if we are here, we have ruled out all-in-1 and all-in-2 overlaps, + // and non-overlapping edges, but have the case where edge 1 and + // edge 2 overlap some finite distance that is less than either of their + // lengths. We have vertex information from the all-in-one checks + // indicating which vertices on one edge are within the other edge + + // set the segment vertices + m_segX[0] = pX1[oneInTwoId]; + m_segY[0] = pY1[oneInTwoId]; + m_segX[1] = pX2[twoInOneId]; + m_segY[1] = pY2[twoInOneId]; + + // compute vector between "inter"-vertices + RealT vecX = m_segX[1] - m_segX[0]; + RealT vecY = m_segY[1] - m_segY[0]; + + // compute the length of the overlapping segment + m_area = magnitude( vecX, vecY ); + + // compute the overlap centroid + m_cX = 0.5 * ( m_segX[0] + m_segX[1] ); + m_cY = 0.5 * ( m_segY[0] + m_segY[1] ); + m_cZ = 0.0; + + return; + +} // end ContactPlane2D::checkSegOverlap() //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE FaceGeomError ContactPlane2D::computeLocalInterpenOverlap( - const MeshData::Viewer& m1, const MeshData::Viewer& m2, - const Parameters& params, bool& interpen ) +TRIBOL_HOST_DEVICE FaceGeomError ContactPlane2D::computeLocalInterpenOverlap( const MeshData::Viewer& m1, + const MeshData::Viewer& m2, + const Parameters& params, bool& interpen ) { - // - // Note: the contact plane has to be properly located prior to calling - // this routine. - // - - interpen = false; - - // all edge-edge interactions suitable for an interpenetration overlap - // calculation are edges that intersect at a single point - int edgeId1 = getCpElementId1(); - int edgeId2 = getCpElementId2(); - int nodeA1 = m1.getGlobalNodeId( edgeId1, 0 ); - int nodeB1 = m1.getGlobalNodeId( edgeId1, 1 ); - int nodeA2 = m2.getGlobalNodeId( edgeId2, 0 ); - int nodeB2 = m2.getGlobalNodeId( edgeId2, 1 ); - - RealT xposA1 = m1.getPosition()[0][ nodeA1 ]; - RealT yposA1 = m1.getPosition()[1][ nodeA1 ]; - RealT xposB1 = m1.getPosition()[0][ nodeB1 ]; - RealT yposB1 = m1.getPosition()[1][ nodeB1 ]; - - RealT xposA2 = m2.getPosition()[0][ nodeA2 ]; - RealT yposA2 = m2.getPosition()[1][ nodeA2 ]; - RealT xposB2 = m2.getPosition()[0][ nodeB2 ]; - RealT yposB2 = m2.getPosition()[1][ nodeB2 ]; - - RealT xInter, yInter; - bool duplicatePoint = false; - - // check if the segments intersect - RealT len_tol = params.len_collapse_ratio * - axom::utilities::max( m1.getFaceRadius()[ edgeId1 ], - m2.getFaceRadius()[ edgeId2 ] ); - - bool edgeIntersect = SegmentIntersection2D( xposA1, yposA1, xposB1, yposB1, - xposA2, yposA2, xposB2, yposB2, - nullptr, xInter, yInter, - duplicatePoint, len_tol ); - - // check to make sure the edges are actually intersecting. Note - // that an intersection point within the specified tolerance of - // an edge vertex is collapsed to that vertex point and duplicatePoint - // is marked true, but the SegmentIntersection2D returns false - if (!edgeIntersect && !duplicatePoint) - { - m_interpenArea = 0.0; - interpen = false; - return NO_FACE_GEOM_ERROR; - } - - // check if a duplicate point (i.e. vertex) was registered. - // That is, if the intersection point is at an edge vertex, - // in which case we don't register the interaction - if (duplicatePoint) - { - m_interpenArea = 0.0; - interpen = false; - return NO_FACE_GEOM_ERROR; - } - - // project unique intersection point to the contact plane. - // The contact plane should have been properly located prior - // to this subroutine, in which case the intersection point lies - // on the contact plane (segment). We can still do this projection - // to be safe and the routine will handle a point that is already - // on the plane - RealT xInterProj, yInterProj; - ProjectPointToSegment( xInter, yInter, m_nX, m_nY, - m_cX, m_cY, xInterProj, yInterProj ); - - // now isolate which vertex on edge 1 and which vertex on edge 2 lie - // on the "wrong" side of the contact plane. - - // define vectors between an edge vertex and the contact plane centroid; - int interId1 = -1; - int interId2 = -1; - int k = 0; - for (int i=0; i 0.0) - { - interId1 = i; - ++k; - } - if (proj2 < 0.0) - { - interId2 = i; - ++k; - } - } + // + // Note: the contact plane has to be properly located prior to calling + // this routine. + // + + interpen = false; + + // all edge-edge interactions suitable for an interpenetration overlap + // calculation are edges that intersect at a single point + int edgeId1 = getCpElementId1(); + int edgeId2 = getCpElementId2(); + int nodeA1 = m1.getGlobalNodeId( edgeId1, 0 ); + int nodeB1 = m1.getGlobalNodeId( edgeId1, 1 ); + int nodeA2 = m2.getGlobalNodeId( edgeId2, 0 ); + int nodeB2 = m2.getGlobalNodeId( edgeId2, 1 ); + + RealT xposA1 = m1.getPosition()[0][nodeA1]; + RealT yposA1 = m1.getPosition()[1][nodeA1]; + RealT xposB1 = m1.getPosition()[0][nodeB1]; + RealT yposB1 = m1.getPosition()[1][nodeB1]; + + RealT xposA2 = m2.getPosition()[0][nodeA2]; + RealT yposA2 = m2.getPosition()[1][nodeA2]; + RealT xposB2 = m2.getPosition()[0][nodeB2]; + RealT yposB2 = m2.getPosition()[1][nodeB2]; + + RealT xInter, yInter; + bool duplicatePoint = false; + + // check if the segments intersect + RealT len_tol = + params.len_collapse_ratio * axom::utilities::max( m1.getFaceRadius()[edgeId1], m2.getFaceRadius()[edgeId2] ); + + bool edgeIntersect = SegmentIntersection2D( xposA1, yposA1, xposB1, yposB1, xposA2, yposA2, xposB2, yposB2, nullptr, + xInter, yInter, duplicatePoint, len_tol ); + + // check to make sure the edges are actually intersecting. Note + // that an intersection point within the specified tolerance of + // an edge vertex is collapsed to that vertex point and duplicatePoint + // is marked true, but the SegmentIntersection2D returns false + if ( !edgeIntersect && !duplicatePoint ) { + m_interpenArea = 0.0; + interpen = false; + return NO_FACE_GEOM_ERROR; + } + + // check if a duplicate point (i.e. vertex) was registered. + // That is, if the intersection point is at an edge vertex, + // in which case we don't register the interaction + if ( duplicatePoint ) { + m_interpenArea = 0.0; + interpen = false; + return NO_FACE_GEOM_ERROR; + } - // Debug check the number of interpenetrating vertices - if (k > 2) - { + // project unique intersection point to the contact plane. + // The contact plane should have been properly located prior + // to this subroutine, in which case the intersection point lies + // on the contact plane (segment). We can still do this projection + // to be safe and the routine will handle a point that is already + // on the plane + RealT xInterProj, yInterProj; + ProjectPointToSegment( xInter, yInter, m_nX, m_nY, m_cX, m_cY, xInterProj, yInterProj ); + + // now isolate which vertex on edge 1 and which vertex on edge 2 lie + // on the "wrong" side of the contact plane. + + // define vectors between an edge vertex and the contact plane centroid; + int interId1 = -1; + int interId2 = -1; + int k = 0; + for ( int i = 0; i < m1.numberOfNodesPerElement(); ++i ) { + int nodeId1 = m1.getGlobalNodeId( edgeId1, i ); + int nodeId2 = m2.getGlobalNodeId( edgeId2, i ); + RealT lvx1 = m1.getPosition()[0][nodeId1] - m_cX; + RealT lvy1 = m1.getPosition()[1][nodeId1] - m_cY; + RealT lvx2 = m2.getPosition()[0][nodeId2] - m_cX; + RealT lvy2 = m2.getPosition()[1][nodeId2] - m_cY; + + // dot each vector with the contact plane normal + RealT proj1 = lvx1 * m_nX + lvy1 * m_nY; + RealT proj2 = lvx2 * m_nX + lvy2 * m_nY; + + // check the projection to detect interpenetration and + // mark the node id if true + if ( proj1 > 0.0 ) { + interId1 = i; + ++k; + } + if ( proj2 < 0.0 ) { + interId2 = i; + ++k; + } + } + + // Debug check the number of interpenetrating vertices + if ( k > 2 ) { #ifdef TRIBOL_USE_HOST - SLIC_DEBUG("ContactPlane2D::computeLocalInterpenOverlap() more than 2 interpenetrating vertices detected; " << - "check for degenerate geometry for edges (" << edgeId1 << ", " << edgeId2 << ") on meshes (" << - m1.meshId() << ", " << m2.meshId() << ")."); + SLIC_DEBUG( "ContactPlane2D::computeLocalInterpenOverlap() more than 2 interpenetrating vertices detected; " + << "check for degenerate geometry for edges (" << edgeId1 << ", " << edgeId2 << ") on meshes (" + << m1.meshId() << ", " << m2.meshId() << ")." ); #endif - interpen = false; - return DEGENERATE_OVERLAP; - } - - // now that we have marked the interpenetrating vertex of each edge, - // compute the distance between the interpenetrating vertex and the - // edge intersection point - int nodeInter1 = m1.getGlobalNodeId( edgeId1, interId1 ); - int nodeInter2 = m2.getGlobalNodeId( edgeId2, interId2 ); - - RealT vix1 = m1.getPosition()[0][ nodeInter1 ] - xInterProj; - RealT viy1 = m1.getPosition()[1][ nodeInter1 ] - yInterProj; - RealT vix2 = m2.getPosition()[0][ nodeInter2 ] - xInterProj; - RealT viy2 = m2.getPosition()[1][ nodeInter2 ] - yInterProj; - - // determine magnitude of each vector - RealT mag1 = magnitude( vix1, viy1 ); - RealT mag2 = magnitude( vix2, viy2 ); - - // the interpenetration overlap length is the minimum of the above - // vectors - m_interpenArea = (mag1 <= mag2) ? mag1 : mag2; - - if (m_interpenArea > m_areaMin) - { - // determine the edge vertex that forms the overlap segment along - // with the intersection point previously computed - RealT vx1 = (mag1 <= mag2) ? m1.getPosition()[0][ nodeInter1 ] - : m2.getPosition()[0][ nodeInter2 ]; - - RealT vy1 = (mag1 <= mag2) ? m1.getPosition()[1][ nodeInter1 ] - : m2.getPosition()[1][ nodeInter2 ]; - - RealT vx2 = xInterProj; - RealT vy2 = yInterProj; - - // allocate space to store the interpen vertices for visualization - // (stored on contact plane base class) - m_numInterpenPoly1Vert = 2; - m_numInterpenPoly2Vert = 2; - - m_interpenG1X[0] = vix1; - m_interpenG1Y[0] = viy1; - m_interpenG1X[1] = xInter; - m_interpenG1Y[1] = yInter; - - m_interpenG2X[0] = vix2; - m_interpenG2Y[0] = viy2; - m_interpenG2X[1] = xInter; - m_interpenG2Y[1] = yInter; - - // project these points to the contact plane - ProjectPointToSegment(vx1, vy1, m_nX, m_nY, m_cX, m_cY, - m_segX[0], m_segY[0]); - - ProjectPointToSegment(vx2, vy2, m_nX, m_nY, m_cX, m_cY, - m_segX[1], m_segY[1]); - - // compute the new contact plane overlap centroid (segment point) - m_cX = 0.5 * (m_segX[0] + m_segX[1]); - m_cY = 0.5 * (m_segY[0] + m_segY[1]); - - interpen = true; - return NO_FACE_GEOM_ERROR; - } + interpen = false; + return DEGENERATE_OVERLAP; + } + + // now that we have marked the interpenetrating vertex of each edge, + // compute the distance between the interpenetrating vertex and the + // edge intersection point + int nodeInter1 = m1.getGlobalNodeId( edgeId1, interId1 ); + int nodeInter2 = m2.getGlobalNodeId( edgeId2, interId2 ); + + RealT vix1 = m1.getPosition()[0][nodeInter1] - xInterProj; + RealT viy1 = m1.getPosition()[1][nodeInter1] - yInterProj; + RealT vix2 = m2.getPosition()[0][nodeInter2] - xInterProj; + RealT viy2 = m2.getPosition()[1][nodeInter2] - yInterProj; + + // determine magnitude of each vector + RealT mag1 = magnitude( vix1, viy1 ); + RealT mag2 = magnitude( vix2, viy2 ); + + // the interpenetration overlap length is the minimum of the above + // vectors + m_interpenArea = ( mag1 <= mag2 ) ? mag1 : mag2; + + if ( m_interpenArea > m_areaMin ) { + // determine the edge vertex that forms the overlap segment along + // with the intersection point previously computed + RealT vx1 = ( mag1 <= mag2 ) ? m1.getPosition()[0][nodeInter1] : m2.getPosition()[0][nodeInter2]; + + RealT vy1 = ( mag1 <= mag2 ) ? m1.getPosition()[1][nodeInter1] : m2.getPosition()[1][nodeInter2]; + + RealT vx2 = xInterProj; + RealT vy2 = yInterProj; + + // allocate space to store the interpen vertices for visualization + // (stored on contact plane base class) + m_numInterpenPoly1Vert = 2; + m_numInterpenPoly2Vert = 2; + + m_interpenG1X[0] = vix1; + m_interpenG1Y[0] = viy1; + m_interpenG1X[1] = xInter; + m_interpenG1Y[1] = yInter; + + m_interpenG2X[0] = vix2; + m_interpenG2Y[0] = viy2; + m_interpenG2X[1] = xInter; + m_interpenG2Y[1] = yInter; + + // project these points to the contact plane + ProjectPointToSegment( vx1, vy1, m_nX, m_nY, m_cX, m_cY, m_segX[0], m_segY[0] ); + + ProjectPointToSegment( vx2, vy2, m_nX, m_nY, m_cX, m_cY, m_segX[1], m_segY[1] ); + + // compute the new contact plane overlap centroid (segment point) + m_cX = 0.5 * ( m_segX[0] + m_segX[1] ); + m_cY = 0.5 * ( m_segY[0] + m_segY[1] ); + + interpen = true; + return NO_FACE_GEOM_ERROR; + } - interpen = false; - return NO_FACE_GEOM_ERROR; + interpen = false; + return NO_FACE_GEOM_ERROR; -} // end ContactPlane2D::computeLocalInterpenOverlap() +} // end ContactPlane2D::computeLocalInterpenOverlap() //------------------------------------------------------------------------------ -} // end of namespace +} // namespace tribol diff --git a/src/tribol/geom/ContactPlane.hpp b/src/tribol/geom/ContactPlane.hpp index 20b35fa5..4a170d34 100644 --- a/src/tribol/geom/ContactPlane.hpp +++ b/src/tribol/geom/ContactPlane.hpp @@ -9,16 +9,15 @@ #include "tribol/mesh/MeshData.hpp" #include "tribol/mesh/InterfacePairs.hpp" #include "tribol/common/Parameters.hpp" -#include "axom/slic.hpp" +#include "axom/slic.hpp" #include -namespace tribol -{ +namespace tribol { /*! * - * \brief projects all the nodes (vertices) of a given FE face to a + * \brief projects all the nodes (vertices) of a given FE face to a * specified plane * * \param [in] mesh mesh data viewer @@ -35,15 +34,13 @@ namespace tribol * * \pre length(pX), length(pY), length(pZ) >= number of nodes on face */ -TRIBOL_HOST_DEVICE void ProjectFaceNodesToPlane( const MeshData::Viewer& mesh, int faceId, - RealT nrmlX, RealT nrmlY, RealT nrmlZ, - RealT cX, RealT cY, RealT cZ, - RealT* pX, RealT* pY, - RealT* pZ ); +TRIBOL_HOST_DEVICE void ProjectFaceNodesToPlane( const MeshData::Viewer& mesh, int faceId, RealT nrmlX, RealT nrmlY, + RealT nrmlZ, RealT cX, RealT cY, RealT cZ, RealT* pX, RealT* pY, + RealT* pZ ); /*! * - * \brief projects nodes belonging to a surface edge to a contact segment + * \brief projects nodes belonging to a surface edge to a contact segment * * \param [in] mesh mesh data viewer * \param [in] edgeId edge id @@ -55,13 +52,11 @@ TRIBOL_HOST_DEVICE void ProjectFaceNodesToPlane( const MeshData::Viewer& mesh, i * \param [in,out] pY pointer to array of projected nodal y-coordinates * */ -TRIBOL_HOST_DEVICE void ProjectEdgeNodesToSegment( const MeshData::Viewer& mesh, int edgeId, - RealT nrmlX, RealT nrmlY, RealT cX, - RealT cY, RealT* pX, - RealT* pY ); +TRIBOL_HOST_DEVICE void ProjectEdgeNodesToSegment( const MeshData::Viewer& mesh, int edgeId, RealT nrmlX, RealT nrmlY, + RealT cX, RealT cY, RealT* pX, RealT* pY ); /*! - * \brief checks if the vertices on face2 have interpenetrated the level set + * \brief checks if the vertices on face2 have interpenetrated the level set * defined by face 1 * * \param [in] mesh1 mesh data viewer for mesh 1 to which face 1 belongs @@ -73,15 +68,15 @@ TRIBOL_HOST_DEVICE void ProjectEdgeNodesToSegment( const MeshData::Viewer& mesh, * * \return true if face 2 intersects the level set of face 1, otherwise false * - * This uses face1 as a level set and checks the projection - * of the vector defined by differencing the face1 center and a face2 - * node onto the face1 normal. If this projection is positive then - * interpenetration has occured and face2 intersects the plane defined - * by face1 (i.e. the zero level set). - * + * This uses face1 as a level set and checks the projection + * of the vector defined by differencing the face1 center and a face2 + * node onto the face1 normal. If this projection is positive then + * interpenetration has occured and face2 intersects the plane defined + * by face1 (i.e. the zero level set). + * */ -TRIBOL_HOST_DEVICE bool FaceInterCheck( const MeshData::Viewer& mesh1, const MeshData::Viewer& mesh2, - int fId1, int fId2, RealT tol, bool& allVerts ); +TRIBOL_HOST_DEVICE bool FaceInterCheck( const MeshData::Viewer& mesh1, const MeshData::Viewer& mesh2, int fId1, + int fId2, RealT tol, bool& allVerts ); /*! * @@ -92,18 +87,18 @@ TRIBOL_HOST_DEVICE bool FaceInterCheck( const MeshData::Viewer& mesh1, const Mes * \param [in] eId1 edge id for edge belonging to mesh 1 * \param [in] eId2 edge id for edge belonging to mesh 2 * \param [in] tol interpenetration tolerance - * \param [in,out] allVerts true if all of edge2 has interpenetrated - * edge 1 + * \param [in,out] allVerts true if all of edge2 has interpenetrated + * edge 1 * * \return true if edge 2 interpenetrates the level set defined by edge 1 * */ -TRIBOL_HOST_DEVICE bool EdgeInterCheck( const MeshData::Viewer& mesh1, const MeshData::Viewer& mesh2, - int eId1, int eId2, RealT tol, bool& allVerts ); +TRIBOL_HOST_DEVICE bool EdgeInterCheck( const MeshData::Viewer& mesh1, const MeshData::Viewer& mesh2, int eId1, + int eId2, RealT tol, bool& allVerts ); /*! * - * \brief checks the contact plane gap against the maximum allowable interpenetration + * \brief checks the contact plane gap against the maximum allowable interpenetration * * \param [in] mesh1 mesh data viewer for mesh 1 * \param [in] mesh2 mesh data viewer for mesh 2 @@ -118,530 +113,475 @@ TRIBOL_HOST_DEVICE bool EdgeInterCheck( const MeshData::Viewer& mesh1, const Mes * sides of thin structures/plates * */ -TRIBOL_HOST_DEVICE bool ExceedsMaxAutoInterpen( const MeshData::Viewer& mesh1, - const MeshData::Viewer& mesh2, - const int faceId1, - const int faceId2, - RealT auto_contact_pen_frac, +TRIBOL_HOST_DEVICE bool ExceedsMaxAutoInterpen( const MeshData::Viewer& mesh1, const MeshData::Viewer& mesh2, + const int faceId1, const int faceId2, RealT auto_contact_pen_frac, const RealT gap ); //----------------------------------------------------------------------------- // Contact Plane base class //----------------------------------------------------------------------------- -class ContactPlane -{ -protected: - InterfacePair* m_pair; ///< Face-pair struct for two constituent faces - - /** - * @brief Constructs a contact plane - * - * @param pair Proximate candidate interface pair - * @param areaFrac Sets the minimum allowable area for an overlap - * @param interpenOverlap If true, overlap includes only parts of face where constraint is violated - * @param interPlane If true, a common plane is used; if false, a mortar plane is used - * @param dim Plane dimension - */ - TRIBOL_HOST_DEVICE ContactPlane( InterfacePair* pair, - RealT areaFrac, - bool interpenOverlap, - bool interPlane, - int dim ); - - TRIBOL_HOST_DEVICE ContactPlane(); - - virtual ~ContactPlane() = default; - - static constexpr int max_nodes_per_overlap {8}; - -public: - - int m_dim; ///< Problem dimension - int m_numFaces; ///< Number of constituent faces - - bool m_intermediatePlane; ///< True if intermediate plane is used - bool m_inContact; ///< True if face-pair is in contact - bool m_interpenOverlap; ///< True if using interpenetration overlap algorithm - - RealT m_cX; ///< Contact plane point global x-coordinate - RealT m_cY; ///< Contact plane point global y-coordinate - RealT m_cZ; ///< Contact plane point global z-coordinate (zero out for 2D) - - RealT m_cXf1; ///< Global x-coordinate of contact plane centroid projected to face 1 - RealT m_cYf1; ///< Global y-coordinate of contact plane centroid projected to face 1 - RealT m_cZf1; ///< Global z-coordinate of contact plane centroid projected to face 1 - - RealT m_cXf2; ///< global x-coordinate of contact plane centroid projected to face 2 - RealT m_cYf2; ///< global y-coordinate of contact plane centroid projected to face 2 - RealT m_cZf2; ///< global z-coordinate of contact plane centroid projected to face 2 - - int m_numInterpenPoly1Vert; ///< Number of vertices on face 1 interpenetrating polygon - RealT m_interpenG1X[max_nodes_per_overlap]; ///< Global x-coordinate of face 1 interpenetrating polygon - RealT m_interpenG1Y[max_nodes_per_overlap]; ///< Global y-coordinate of face 1 interpenetrating polygon - RealT m_interpenG1Z[max_nodes_per_overlap]; ///< Global z-coordinate of face 1 interpenetrating polygon - - int m_numInterpenPoly2Vert; ///< Number of vertices on face 2 interpenetrating polygon - RealT m_interpenG2X[max_nodes_per_overlap]; ///< Global x-coordinate of face 2 interpenetrating polygon - RealT m_interpenG2Y[max_nodes_per_overlap]; ///< Global y-coordinate of face 2 interpenetrating polygon - RealT m_interpenG2Z[max_nodes_per_overlap]; ///< Global z-coordinate of face 2 interpenetrating polygon - - RealT m_nX; ///< Global x-component of contact plane unit normal - RealT m_nY; ///< Global y-component of contact plane unit normal - RealT m_nZ; ///< Global z-component of contact plane unit normal (zero out for 2D) - - RealT m_gap; ///< Face-pair gap - RealT m_gapTol; ///< Face-pair gap tolerance - - // cp area - RealT m_areaFrac; ///< Face area fraction used to determine overlap area cutoff - RealT m_areaMin; ///< Minimum overlap area for inclusion into the active set - RealT m_area; ///< Overlap area - RealT m_interpenArea; ///< Interpenetrating overlap area - - RealT m_velGap; - RealT m_ratePressure; - - RealT m_pressure; - - /// \name Contact plane routines - /// @{ - - /*! - * \brief Compute the contact plane normal - * - * \param [in] m1 mesh data viewer for mesh 1 - * \param [in] m2 mesh data viewer for mesh 2 - */ - TRIBOL_HOST_DEVICE virtual void computeNormal( const MeshData::Viewer& m1, - const MeshData::Viewer& m2 ) = 0 ; - - /*! - * \brief Compute the contact plane point - * - * \param [in] m1 mesh data viewer for mesh 1 - * \param [in] m2 mesh data viewer for mesh 2 - */ - TRIBOL_HOST_DEVICE virtual void computePlanePoint( const MeshData::Viewer& m1, - const MeshData::Viewer& m2 ) = 0 ; - - - /*! - * \brief Recomputes the reference point that locates the plane in 3-space - * and the gap between the projected intersection poly centroids - * - * \note This projects the projected area of overlap's centroid (from the - * polygon intersection routine) back to each face that are used to form - * the contact plane and then averages these projected points. - * - * \param [in] m1 mesh data viewer for mesh 1 - * \param [in] m2 mesh data viewer for mesh 2 - * \param [in] scale Scale to help find the centroid-to-face projections - */ - TRIBOL_HOST_DEVICE void planePointAndCentroidGap( const MeshData::Viewer& m1, - const MeshData::Viewer& m2, - RealT scale ); - - /*! - * \brief Compute the contact plane integral gap expression - */ - virtual void computeIntegralGap() = 0 ; - - /*! - * \brief Compute the contact plane area tolerance - * - * \param [in] m1 mesh data viewer for mesh 1 - * \param [in] m2 mesh data viewer for mesh 2 - * \param [in] params Coupling scheme-dependent parameters - */ - TRIBOL_HOST_DEVICE virtual void computeAreaTol( const MeshData::Viewer& m1, - const MeshData::Viewer& m2, - const Parameters& params ) = 0 ; - - /*! - * \brief Compute the contact plane centroid gap - * - * \param [in] m1 mesh data viewer for mesh 1 - * \param [in] m2 mesh data viewer for mesh 2 - * \param [in] scale Scale to help find centroid-to-face projections - */ - virtual void centroidGap( const MeshData::Viewer& m1, - const MeshData::Viewer& m2, - RealT scale ) = 0 ; - - /*! - * \brief Compute the contact plane integral gap expression - * - * \param [in] m1 mesh data viewer for mesh 1 - * \param [in] m2 mesh data viewer for mesh 2 - * \param [in] params Coupling scheme-dependent parameters - * \param [in,out] interpen true if the two faces interpenetrate - * - * \return 0 if no error, non-zero (via FaceGeomError enum) otherwise - */ - TRIBOL_HOST_DEVICE virtual FaceGeomError computeLocalInterpenOverlap( - const MeshData::Viewer& m1, const MeshData::Viewer& m2, - const Parameters& params, bool& interpen ) = 0; - - /// @} - - - /// \name Getters and setters - /// @{ - - /*! - * \brief Get the id of the first element that forms the contact plane - * - * \return Face id - */ - TRIBOL_HOST_DEVICE int getCpElementId1() const { return m_pair->m_element_id1; } - - /*! - * \brief Get the id of the second element that forms the contact plane - * - * \return Face id - */ - TRIBOL_HOST_DEVICE int getCpElementId2() const { return m_pair->m_element_id2; } - - /*! - * \brief Get the number of faces used to form the contact plane - * - * \return Number of faces - * - * \note Number of faces should typically be 2 - * - */ - int getCpNumFaces() const - { - return m_numFaces; - } - - /*! - * \brief Set the first contact plane element id - * - * \param [in] element_id element id - */ - void setCpElementId1( IndexT element_id ) { m_pair->m_element_id1 = element_id; } - - /*! - * \brief Set the second contact plane element id - * - * \param [in] element_id element id - */ - void setCpElementId2( IndexT element_id ) { m_pair->m_element_id2 = element_id; } - - /*! - * \brief Set the number of faces involved in forming the contact plane - * - * \param [in] num Number of faces - */ - void setCpNumFaces( int num ) - { - m_numFaces = num; - } - - /// @} +class ContactPlane { + protected: + InterfacePair* m_pair; ///< Face-pair struct for two constituent faces + + /** + * @brief Constructs a contact plane + * + * @param pair Proximate candidate interface pair + * @param areaFrac Sets the minimum allowable area for an overlap + * @param interpenOverlap If true, overlap includes only parts of face where constraint is violated + * @param interPlane If true, a common plane is used; if false, a mortar plane is used + * @param dim Plane dimension + */ + TRIBOL_HOST_DEVICE ContactPlane( InterfacePair* pair, RealT areaFrac, bool interpenOverlap, bool interPlane, + int dim ); + + TRIBOL_HOST_DEVICE ContactPlane(); + + virtual ~ContactPlane() = default; + + static constexpr int max_nodes_per_overlap{ 8 }; + + public: + int m_dim; ///< Problem dimension + int m_numFaces; ///< Number of constituent faces + + bool m_intermediatePlane; ///< True if intermediate plane is used + bool m_inContact; ///< True if face-pair is in contact + bool m_interpenOverlap; ///< True if using interpenetration overlap algorithm + + RealT m_cX; ///< Contact plane point global x-coordinate + RealT m_cY; ///< Contact plane point global y-coordinate + RealT m_cZ; ///< Contact plane point global z-coordinate (zero out for 2D) + + RealT m_cXf1; ///< Global x-coordinate of contact plane centroid projected to face 1 + RealT m_cYf1; ///< Global y-coordinate of contact plane centroid projected to face 1 + RealT m_cZf1; ///< Global z-coordinate of contact plane centroid projected to face 1 + + RealT m_cXf2; ///< global x-coordinate of contact plane centroid projected to face 2 + RealT m_cYf2; ///< global y-coordinate of contact plane centroid projected to face 2 + RealT m_cZf2; ///< global z-coordinate of contact plane centroid projected to face 2 + + int m_numInterpenPoly1Vert; ///< Number of vertices on face 1 interpenetrating polygon + RealT m_interpenG1X[max_nodes_per_overlap]; ///< Global x-coordinate of face 1 interpenetrating polygon + RealT m_interpenG1Y[max_nodes_per_overlap]; ///< Global y-coordinate of face 1 interpenetrating polygon + RealT m_interpenG1Z[max_nodes_per_overlap]; ///< Global z-coordinate of face 1 interpenetrating polygon + + int m_numInterpenPoly2Vert; ///< Number of vertices on face 2 interpenetrating polygon + RealT m_interpenG2X[max_nodes_per_overlap]; ///< Global x-coordinate of face 2 interpenetrating polygon + RealT m_interpenG2Y[max_nodes_per_overlap]; ///< Global y-coordinate of face 2 interpenetrating polygon + RealT m_interpenG2Z[max_nodes_per_overlap]; ///< Global z-coordinate of face 2 interpenetrating polygon + + RealT m_nX; ///< Global x-component of contact plane unit normal + RealT m_nY; ///< Global y-component of contact plane unit normal + RealT m_nZ; ///< Global z-component of contact plane unit normal (zero out for 2D) + + RealT m_gap; ///< Face-pair gap + RealT m_gapTol; ///< Face-pair gap tolerance + + // cp area + RealT m_areaFrac; ///< Face area fraction used to determine overlap area cutoff + RealT m_areaMin; ///< Minimum overlap area for inclusion into the active set + RealT m_area; ///< Overlap area + RealT m_interpenArea; ///< Interpenetrating overlap area + + RealT m_velGap; + RealT m_ratePressure; + + RealT m_pressure; + + /// \name Contact plane routines + /// @{ + + /*! + * \brief Compute the contact plane normal + * + * \param [in] m1 mesh data viewer for mesh 1 + * \param [in] m2 mesh data viewer for mesh 2 + */ + TRIBOL_HOST_DEVICE virtual void computeNormal( const MeshData::Viewer& m1, const MeshData::Viewer& m2 ) = 0; + + /*! + * \brief Compute the contact plane point + * + * \param [in] m1 mesh data viewer for mesh 1 + * \param [in] m2 mesh data viewer for mesh 2 + */ + TRIBOL_HOST_DEVICE virtual void computePlanePoint( const MeshData::Viewer& m1, const MeshData::Viewer& m2 ) = 0; + + /*! + * \brief Recomputes the reference point that locates the plane in 3-space + * and the gap between the projected intersection poly centroids + * + * \note This projects the projected area of overlap's centroid (from the + * polygon intersection routine) back to each face that are used to form + * the contact plane and then averages these projected points. + * + * \param [in] m1 mesh data viewer for mesh 1 + * \param [in] m2 mesh data viewer for mesh 2 + * \param [in] scale Scale to help find the centroid-to-face projections + */ + TRIBOL_HOST_DEVICE void planePointAndCentroidGap( const MeshData::Viewer& m1, const MeshData::Viewer& m2, + RealT scale ); + + /*! + * \brief Compute the contact plane integral gap expression + */ + virtual void computeIntegralGap() = 0; + + /*! + * \brief Compute the contact plane area tolerance + * + * \param [in] m1 mesh data viewer for mesh 1 + * \param [in] m2 mesh data viewer for mesh 2 + * \param [in] params Coupling scheme-dependent parameters + */ + TRIBOL_HOST_DEVICE virtual void computeAreaTol( const MeshData::Viewer& m1, const MeshData::Viewer& m2, + const Parameters& params ) = 0; + + /*! + * \brief Compute the contact plane centroid gap + * + * \param [in] m1 mesh data viewer for mesh 1 + * \param [in] m2 mesh data viewer for mesh 2 + * \param [in] scale Scale to help find centroid-to-face projections + */ + virtual void centroidGap( const MeshData::Viewer& m1, const MeshData::Viewer& m2, RealT scale ) = 0; + + /*! + * \brief Compute the contact plane integral gap expression + * + * \param [in] m1 mesh data viewer for mesh 1 + * \param [in] m2 mesh data viewer for mesh 2 + * \param [in] params Coupling scheme-dependent parameters + * \param [in,out] interpen true if the two faces interpenetrate + * + * \return 0 if no error, non-zero (via FaceGeomError enum) otherwise + */ + TRIBOL_HOST_DEVICE virtual FaceGeomError computeLocalInterpenOverlap( const MeshData::Viewer& m1, + const MeshData::Viewer& m2, + const Parameters& params, bool& interpen ) = 0; + + /// @} + + /// \name Getters and setters + /// @{ + + /*! + * \brief Get the id of the first element that forms the contact plane + * + * \return Face id + */ + TRIBOL_HOST_DEVICE int getCpElementId1() const { return m_pair->m_element_id1; } + + /*! + * \brief Get the id of the second element that forms the contact plane + * + * \return Face id + */ + TRIBOL_HOST_DEVICE int getCpElementId2() const { return m_pair->m_element_id2; } + + /*! + * \brief Get the number of faces used to form the contact plane + * + * \return Number of faces + * + * \note Number of faces should typically be 2 + * + */ + int getCpNumFaces() const { return m_numFaces; } + + /*! + * \brief Set the first contact plane element id + * + * \param [in] element_id element id + */ + void setCpElementId1( IndexT element_id ) { m_pair->m_element_id1 = element_id; } + + /*! + * \brief Set the second contact plane element id + * + * \param [in] element_id element id + */ + void setCpElementId2( IndexT element_id ) { m_pair->m_element_id2 = element_id; } + + /*! + * \brief Set the number of faces involved in forming the contact plane + * + * \param [in] num Number of faces + */ + void setCpNumFaces( int num ) { m_numFaces = num; } + + /// @} }; //----------------------------------------------------------------------------- // Contact Plane 3D class //----------------------------------------------------------------------------- -class ContactPlane3D : public ContactPlane -{ -public: - - /*! - * @brief Constructs a 3D contact plane - * - * @param pair Proximate candidate interface pair - * @param areaFrac Sets the minimum allowable area for an overlap - * @param interpenOverlap If true, overlap includes only parts of face where constraint is violated - * @param interPlane If true, a common plane is used; if false, a mortar plane is used - */ - TRIBOL_HOST_DEVICE ContactPlane3D( InterfacePair* pair, - RealT areaFrac, - bool interpenOverlap, - bool interPlane ); - - /*! - * Overload constructor with no argument list - * - */ - TRIBOL_HOST_DEVICE ContactPlane3D(); - - RealT m_e1X; ///< Global x-component of first in-plane basis vector - RealT m_e1Y; ///< Global y-component of first in-plane basis vector - RealT m_e1Z; ///< Global z-component of first in-plane basis vector - - RealT m_e2X; ///< Global x-component of second in-plane basis vector - RealT m_e2Y; ///< Global y-component of second in-plane basis vector - RealT m_e2Z; ///< Global z-component of second in-plane basis vector - - RealT m_polyLocX[max_nodes_per_overlap]; ///< Pointer to local x-components of overlap polygon's vertices - RealT m_polyLocY[max_nodes_per_overlap]; ///< Pointer to local y-components of overlap polygon's vertices - - RealT m_polyX[max_nodes_per_overlap]; ///< Global x-components of overlap polygon's vertices - RealT m_polyY[max_nodes_per_overlap]; ///< Global y-components of overlap polygon's vertices - RealT m_polyZ[max_nodes_per_overlap]; ///< Global z-components of overlap polygon's vertices - - int m_numPolyVert; ///< Number of vertices in overlapping polygon - - RealT m_overlapCX; ///< Local x-coordinate of overlap centroid - RealT m_overlapCY; ///< Local y-coordinate of overlap centroid - - RealT m_interpenPoly1X[max_nodes_per_overlap]; ///< Local x-coordinates of face 1 interpenetrating overlap - RealT m_interpenPoly1Y[max_nodes_per_overlap]; ///< Local y-coordinates of face 1 interpenetrating overlap - - RealT m_interpenPoly2X[max_nodes_per_overlap]; ///< Local x-coordinates of face 2 interpenetrating overlap - RealT m_interpenPoly2Y[max_nodes_per_overlap]; ///< Local y-coordinates of face 2 interpenetrating overlap - - /*! - * \brief Compute the unit normal that defines the contact plane - * \param [in] m1 mesh data viewer for mesh 1 - * \param [in] m2 mesh data viewer for mesh 2 - */ - TRIBOL_HOST_DEVICE void computeNormal( const MeshData::Viewer& m1, - const MeshData::Viewer& m2 ) override; - - /*! - * \brief Computes a reference point on the plane locating it in 3-space - * - * \param [in] m1 mesh data viewer for mesh 1 - * \param [in] m2 mesh data viewer for mesh 2 - * - * \note This is taken as the average of the vertex averaged centroids of - * the two faces that are used to define a local contact plane - */ - TRIBOL_HOST_DEVICE void computePlanePoint( const MeshData::Viewer& m1, - const MeshData::Viewer& m2 ) override; - - /*! - * \brief Compute a local basis on the contact plane - * - * \param [in] m1 mesh data viewer for mesh 1 - */ - TRIBOL_HOST_DEVICE void computeLocalBasis( const MeshData::Viewer& m1 ); - - /*! - * \brief Compute the weak (integral form) gap between the two faces. - */ - void computeIntegralGap() override; - - /*! - * \brief Compute the local 2D coordinates of an array of points on the - * contact plane - * - * \param [in] pX array of global x coordinates for input points - * \param [in] pY array of global y coordinates for input points - * \param [in] pZ array of global z coordinates for input points - * \param [in,out] pLX array of local x coordinates of transformed points - * \param [in,out] pLY array of local y coordinates of transformed points - * \param [in] size number of points in arrays - * - * \pre length(pX), length(pY), length(pZ) >= size - * \pre length(pLX), length(pLY) >= size - */ - TRIBOL_HOST_DEVICE void globalTo2DLocalCoords( RealT* pX, RealT* pY, - RealT* pZ, RealT* pLX, - RealT* pLY, int size ); - - /*! - * \brief Compute the local 2D coordinates of a point on the contact plane - * - * \param [in] pX global x coordinate of point - * \param [in] pY global y coordinate of point - * \param [in] pZ global z coordinate of point - * \param [in,out] pLX local x coordinate of point on contact plane - * \param [in,out] pLY local y coordinate of point on contact plane - * - * \note Overloaded member function to compute local coordinates of - * a single point on the contact plane - */ - void globalTo2DLocalCoords( RealT pX, RealT pY, RealT pZ, - RealT& pLX, RealT& pLY, int size ); - - /*! - * \brief Computes the area tolerance for accepting a face pair - * - * \param [in] m1 mesh data viewer for mesh 1 - * \param [in] m2 mesh data viewer for mesh 2 - * \param [in] params Coupling scheme-dependent parameters - */ - TRIBOL_HOST_DEVICE void computeAreaTol( const MeshData::Viewer& m1, - const MeshData::Viewer& m2, - const Parameters& params ) override; - - /*! - * \brief Check whether two polygons (faces) have a positive area of overlap - * - * \note Wrapper routine that calls the polygon intersection routine. That routine - * does not return vertices, just overlap area. - * - * \param [in] m1 mesh data viewer for mesh 1 - * \param [in] m2 mesh data viewer for mesh 2 - * \param [in] projLocX1 2D x-coordinates of projected element 1 vertices - * \param [in] projLocY1 2D y-coordinates of projected element 1 vertices - * \param [in] projLocX2 2D x-coordinates of projected element 2 vertices - * \param [in] projLocY2 2D y-coordinates of projected element 2 vertices - * \param [in] isym 0 for planar symmetry, 1 for axial symmetry - */ - TRIBOL_HOST_DEVICE void checkPolyOverlap( const MeshData::Viewer& m1, - const MeshData::Viewer& m2, - RealT* projLocX1, RealT* projLocY1, - RealT* projLocX2, RealT* projLocY2, - const int isym); - - /*! - * \brief Transform a local 2D point on the contact plane to global 3D - * coordinates - * - * \param [in] xloc local x coordinate of point - * \param [in] yloc local y coordinate of point - * \param [in,out] xg global x coordinate of point - * \param [in,out] yg global y coordinate of point - * \param [in,out] zg global z coordinate of point - * - */ - TRIBOL_HOST_DEVICE void local2DToGlobalCoords( RealT xloc, RealT yloc, RealT& xg, RealT& yg, RealT& zg ); - - /*! - * \brief Computes the gap between the two projections of the contact - * plane centroid onto each constituent face. - * - * \param [in] m1 mesh data viewer for mesh 1 - * \param [in] m2 mesh data viewer for mesh 2 - * \param [in] scale Scale to help find centroid-to-face projections - */ - void centroidGap( const MeshData::Viewer& m1, - const MeshData::Viewer& m2, - RealT scale ) override; - - /*! - * \brief Compute the contact plane integral gap expression - * - * \param [in] m1 mesh data viewer for mesh 1 - * \param [in] m2 mesh data viewer for mesh 2 - * \param [in] params Coupling scheme-dependent parameters - * \param [in,out] interpen true if the two faces interpenetrate - * - * \return 0 if no error, non-zero (via FaceGeomError enum) otherwise - */ - TRIBOL_HOST_DEVICE FaceGeomError computeLocalInterpenOverlap( - const MeshData::Viewer& m1, const MeshData::Viewer& m2, - const Parameters& params, bool& interpen ) override; - +class ContactPlane3D : public ContactPlane { + public: + /*! + * @brief Constructs a 3D contact plane + * + * @param pair Proximate candidate interface pair + * @param areaFrac Sets the minimum allowable area for an overlap + * @param interpenOverlap If true, overlap includes only parts of face where constraint is violated + * @param interPlane If true, a common plane is used; if false, a mortar plane is used + */ + TRIBOL_HOST_DEVICE ContactPlane3D( InterfacePair* pair, RealT areaFrac, bool interpenOverlap, bool interPlane ); + + /*! + * Overload constructor with no argument list + * + */ + TRIBOL_HOST_DEVICE ContactPlane3D(); + + RealT m_e1X; ///< Global x-component of first in-plane basis vector + RealT m_e1Y; ///< Global y-component of first in-plane basis vector + RealT m_e1Z; ///< Global z-component of first in-plane basis vector + + RealT m_e2X; ///< Global x-component of second in-plane basis vector + RealT m_e2Y; ///< Global y-component of second in-plane basis vector + RealT m_e2Z; ///< Global z-component of second in-plane basis vector + + RealT m_polyLocX[max_nodes_per_overlap]; ///< Pointer to local x-components of overlap polygon's vertices + RealT m_polyLocY[max_nodes_per_overlap]; ///< Pointer to local y-components of overlap polygon's vertices + + RealT m_polyX[max_nodes_per_overlap]; ///< Global x-components of overlap polygon's vertices + RealT m_polyY[max_nodes_per_overlap]; ///< Global y-components of overlap polygon's vertices + RealT m_polyZ[max_nodes_per_overlap]; ///< Global z-components of overlap polygon's vertices + + int m_numPolyVert; ///< Number of vertices in overlapping polygon + + RealT m_overlapCX; ///< Local x-coordinate of overlap centroid + RealT m_overlapCY; ///< Local y-coordinate of overlap centroid + + RealT m_interpenPoly1X[max_nodes_per_overlap]; ///< Local x-coordinates of face 1 interpenetrating overlap + RealT m_interpenPoly1Y[max_nodes_per_overlap]; ///< Local y-coordinates of face 1 interpenetrating overlap + + RealT m_interpenPoly2X[max_nodes_per_overlap]; ///< Local x-coordinates of face 2 interpenetrating overlap + RealT m_interpenPoly2Y[max_nodes_per_overlap]; ///< Local y-coordinates of face 2 interpenetrating overlap + + /*! + * \brief Compute the unit normal that defines the contact plane + * \param [in] m1 mesh data viewer for mesh 1 + * \param [in] m2 mesh data viewer for mesh 2 + */ + TRIBOL_HOST_DEVICE void computeNormal( const MeshData::Viewer& m1, const MeshData::Viewer& m2 ) override; + + /*! + * \brief Computes a reference point on the plane locating it in 3-space + * + * \param [in] m1 mesh data viewer for mesh 1 + * \param [in] m2 mesh data viewer for mesh 2 + * + * \note This is taken as the average of the vertex averaged centroids of + * the two faces that are used to define a local contact plane + */ + TRIBOL_HOST_DEVICE void computePlanePoint( const MeshData::Viewer& m1, const MeshData::Viewer& m2 ) override; + + /*! + * \brief Compute a local basis on the contact plane + * + * \param [in] m1 mesh data viewer for mesh 1 + */ + TRIBOL_HOST_DEVICE void computeLocalBasis( const MeshData::Viewer& m1 ); + + /*! + * \brief Compute the weak (integral form) gap between the two faces. + */ + void computeIntegralGap() override; + + /*! + * \brief Compute the local 2D coordinates of an array of points on the + * contact plane + * + * \param [in] pX array of global x coordinates for input points + * \param [in] pY array of global y coordinates for input points + * \param [in] pZ array of global z coordinates for input points + * \param [in,out] pLX array of local x coordinates of transformed points + * \param [in,out] pLY array of local y coordinates of transformed points + * \param [in] size number of points in arrays + * + * \pre length(pX), length(pY), length(pZ) >= size + * \pre length(pLX), length(pLY) >= size + */ + TRIBOL_HOST_DEVICE void globalTo2DLocalCoords( RealT* pX, RealT* pY, RealT* pZ, RealT* pLX, RealT* pLY, int size ); + + /*! + * \brief Compute the local 2D coordinates of a point on the contact plane + * + * \param [in] pX global x coordinate of point + * \param [in] pY global y coordinate of point + * \param [in] pZ global z coordinate of point + * \param [in,out] pLX local x coordinate of point on contact plane + * \param [in,out] pLY local y coordinate of point on contact plane + * + * \note Overloaded member function to compute local coordinates of + * a single point on the contact plane + */ + void globalTo2DLocalCoords( RealT pX, RealT pY, RealT pZ, RealT& pLX, RealT& pLY, int size ); + + /*! + * \brief Computes the area tolerance for accepting a face pair + * + * \param [in] m1 mesh data viewer for mesh 1 + * \param [in] m2 mesh data viewer for mesh 2 + * \param [in] params Coupling scheme-dependent parameters + */ + TRIBOL_HOST_DEVICE void computeAreaTol( const MeshData::Viewer& m1, const MeshData::Viewer& m2, + const Parameters& params ) override; + + /*! + * \brief Check whether two polygons (faces) have a positive area of overlap + * + * \note Wrapper routine that calls the polygon intersection routine. That routine + * does not return vertices, just overlap area. + * + * \param [in] m1 mesh data viewer for mesh 1 + * \param [in] m2 mesh data viewer for mesh 2 + * \param [in] projLocX1 2D x-coordinates of projected element 1 vertices + * \param [in] projLocY1 2D y-coordinates of projected element 1 vertices + * \param [in] projLocX2 2D x-coordinates of projected element 2 vertices + * \param [in] projLocY2 2D y-coordinates of projected element 2 vertices + * \param [in] isym 0 for planar symmetry, 1 for axial symmetry + */ + TRIBOL_HOST_DEVICE void checkPolyOverlap( const MeshData::Viewer& m1, const MeshData::Viewer& m2, RealT* projLocX1, + RealT* projLocY1, RealT* projLocX2, RealT* projLocY2, const int isym ); + + /*! + * \brief Transform a local 2D point on the contact plane to global 3D + * coordinates + * + * \param [in] xloc local x coordinate of point + * \param [in] yloc local y coordinate of point + * \param [in,out] xg global x coordinate of point + * \param [in,out] yg global y coordinate of point + * \param [in,out] zg global z coordinate of point + * + */ + TRIBOL_HOST_DEVICE void local2DToGlobalCoords( RealT xloc, RealT yloc, RealT& xg, RealT& yg, RealT& zg ); + + /*! + * \brief Computes the gap between the two projections of the contact + * plane centroid onto each constituent face. + * + * \param [in] m1 mesh data viewer for mesh 1 + * \param [in] m2 mesh data viewer for mesh 2 + * \param [in] scale Scale to help find centroid-to-face projections + */ + void centroidGap( const MeshData::Viewer& m1, const MeshData::Viewer& m2, RealT scale ) override; + + /*! + * \brief Compute the contact plane integral gap expression + * + * \param [in] m1 mesh data viewer for mesh 1 + * \param [in] m2 mesh data viewer for mesh 2 + * \param [in] params Coupling scheme-dependent parameters + * \param [in,out] interpen true if the two faces interpenetrate + * + * \return 0 if no error, non-zero (via FaceGeomError enum) otherwise + */ + TRIBOL_HOST_DEVICE FaceGeomError computeLocalInterpenOverlap( const MeshData::Viewer& m1, const MeshData::Viewer& m2, + const Parameters& params, bool& interpen ) override; }; - //----------------------------------------------------------------------------- // Contact Plane 2D class //----------------------------------------------------------------------------- -class ContactPlane2D : public ContactPlane -{ -public: - - RealT m_segX[2]; ///< Global x-components of overlap segment vertices - RealT m_segY[2]; ///< Global y-components of overlap segment vertices - -public: - - /*! - * \brief Constructor - * - * \param [in] pair InterfacePair struct - * \param [in] lenFrac Length fraction used for overlap segment cutoff - * \param [in] interpenOverlap True if using interpenetration overlap algorithm - * \param [in] interPlane True if intermediate (i.e. common) plane is used - * \param [in] dimension Dimension of problem - */ - TRIBOL_HOST_DEVICE ContactPlane2D( InterfacePair* pair, - RealT lenFrac, - bool interpenOverlap, - bool interPlane ) ; - - /*! - * \brief Overloaded constructor with no arguments - * - */ - TRIBOL_HOST_DEVICE ContactPlane2D(); - - /*! - * \brief Destructor - * - */ - ~ContactPlane2D() = default; - - /*! - * \brief Compute the unit normal that defines the contact plane - * \param [in] m1 mesh data viewer for mesh 1 - * \param [in] m2 mesh data viewer for mesh 2 - */ - TRIBOL_HOST_DEVICE void computeNormal( const MeshData::Viewer& m1, - const MeshData::Viewer& m2 ) override; - - /*! - * \brief Computes a reference point on the plane locating it in 3-space - * \param [in] m1 mesh data viewer for mesh 1 - * \param [in] m2 mesh data viewer for mesh 2 - * - * \note This is taken as the average of the vertex averaged centroids of - * the two faces that are used to define a local contact plane - */ - TRIBOL_HOST_DEVICE void computePlanePoint( const MeshData::Viewer& m1, - const MeshData::Viewer& m2 ) override; - - /*! - * \brief Compute the weak (integral form) gap between the two faces. - */ - void computeIntegralGap() override; - - /*! - * \brief Computes the area tolerance for accepting a face pair - * - * \param [in] m1 mesh data viewer for mesh 1 - * \param [in] m2 mesh data viewer for mesh 2 - * \param [in] params Coupling scheme-dependent parameters - */ - TRIBOL_HOST_DEVICE void computeAreaTol( const MeshData::Viewer& m1, - const MeshData::Viewer& m2, - const Parameters& params ) override; - - /*! - * \brief Computes the gap between the two projections of the contact - * plane centroid onto each constituent face. - * - * \param [in] m1 mesh data viewer for mesh 1 - * \param [in] m2 mesh data viewer for mesh 2 - * \param [in] scale Scale to help find centroid-to-face projections - */ - void centroidGap( const MeshData::Viewer& m1, - const MeshData::Viewer& m2, - RealT scale ) override; - - /*! - * \brief Check whether two segments have a positive length of overlap - * - */ - TRIBOL_HOST_DEVICE void checkSegOverlap( const RealT* const pX1, const RealT* const pY1, - const RealT* const pX2, const RealT* const pY2, - const int nV1, const int nV2 ); - - /*! - * \brief Compute the contact plane integral gap expression - * - * \param [in] m1 mesh data viewer for mesh 1 - * \param [in] m2 mesh data viewer for mesh 2 - * \param [in] params Coupling scheme-dependent parameters - * \param [in,out] interpen true if the two faces interpenetrate - * - * \return 0 if no error, non-zero (via FaceGeomError enum) otherwise - */ - TRIBOL_HOST_DEVICE FaceGeomError computeLocalInterpenOverlap( - const MeshData::Viewer& m1, const MeshData::Viewer& m2, - const Parameters& params, bool& interpen ) override; - +class ContactPlane2D : public ContactPlane { + public: + RealT m_segX[2]; ///< Global x-components of overlap segment vertices + RealT m_segY[2]; ///< Global y-components of overlap segment vertices + + public: + /*! + * \brief Constructor + * + * \param [in] pair InterfacePair struct + * \param [in] lenFrac Length fraction used for overlap segment cutoff + * \param [in] interpenOverlap True if using interpenetration overlap algorithm + * \param [in] interPlane True if intermediate (i.e. common) plane is used + * \param [in] dimension Dimension of problem + */ + TRIBOL_HOST_DEVICE ContactPlane2D( InterfacePair* pair, RealT lenFrac, bool interpenOverlap, bool interPlane ); + + /*! + * \brief Overloaded constructor with no arguments + * + */ + TRIBOL_HOST_DEVICE ContactPlane2D(); + + /*! + * \brief Destructor + * + */ + ~ContactPlane2D() = default; + + /*! + * \brief Compute the unit normal that defines the contact plane + * \param [in] m1 mesh data viewer for mesh 1 + * \param [in] m2 mesh data viewer for mesh 2 + */ + TRIBOL_HOST_DEVICE void computeNormal( const MeshData::Viewer& m1, const MeshData::Viewer& m2 ) override; + + /*! + * \brief Computes a reference point on the plane locating it in 3-space + * \param [in] m1 mesh data viewer for mesh 1 + * \param [in] m2 mesh data viewer for mesh 2 + * + * \note This is taken as the average of the vertex averaged centroids of + * the two faces that are used to define a local contact plane + */ + TRIBOL_HOST_DEVICE void computePlanePoint( const MeshData::Viewer& m1, const MeshData::Viewer& m2 ) override; + + /*! + * \brief Compute the weak (integral form) gap between the two faces. + */ + void computeIntegralGap() override; + + /*! + * \brief Computes the area tolerance for accepting a face pair + * + * \param [in] m1 mesh data viewer for mesh 1 + * \param [in] m2 mesh data viewer for mesh 2 + * \param [in] params Coupling scheme-dependent parameters + */ + TRIBOL_HOST_DEVICE void computeAreaTol( const MeshData::Viewer& m1, const MeshData::Viewer& m2, + const Parameters& params ) override; + + /*! + * \brief Computes the gap between the two projections of the contact + * plane centroid onto each constituent face. + * + * \param [in] m1 mesh data viewer for mesh 1 + * \param [in] m2 mesh data viewer for mesh 2 + * \param [in] scale Scale to help find centroid-to-face projections + */ + void centroidGap( const MeshData::Viewer& m1, const MeshData::Viewer& m2, RealT scale ) override; + + /*! + * \brief Check whether two segments have a positive length of overlap + * + */ + TRIBOL_HOST_DEVICE void checkSegOverlap( const RealT* const pX1, const RealT* const pY1, const RealT* const pX2, + const RealT* const pY2, const int nV1, const int nV2 ); + + /*! + * \brief Compute the contact plane integral gap expression + * + * \param [in] m1 mesh data viewer for mesh 1 + * \param [in] m2 mesh data viewer for mesh 2 + * \param [in] params Coupling scheme-dependent parameters + * \param [in,out] interpen true if the two faces interpenetrate + * + * \return 0 if no error, non-zero (via FaceGeomError enum) otherwise + */ + TRIBOL_HOST_DEVICE FaceGeomError computeLocalInterpenOverlap( const MeshData::Viewer& m1, const MeshData::Viewer& m2, + const Parameters& params, bool& interpen ) override; }; //----------------------------------------------------------------------------- @@ -661,7 +601,7 @@ class ContactPlane2D : public ContactPlane * \param [in,out] planes_3d array view of 3D contact planes * \param [in,out] plane_ct number of contact planes in the array views * - * \note isInteracting is true indicating a contact candidate for intersecting or + * \note isInteracting is true indicating a contact candidate for intersecting or * nearly intersecting face-pairs with a positive area of overlap * * \return 0 if no error, non-zero (via FaceGeomError enum) otherwise @@ -669,17 +609,11 @@ class ContactPlane2D : public ContactPlane * \note will need the contact case for specialized geometry checks * */ -TRIBOL_HOST_DEVICE FaceGeomError CheckInterfacePair( InterfacePair& pair, - const MeshData::Viewer& mesh1, - const MeshData::Viewer& mesh2, - const Parameters& params, - ContactMethod const cMethod, - ContactCase const cCase, - bool& isInteracting, - ArrayViewT& planes_2d, - ArrayViewT& planes_3d, - IndexT* plane_ct ); - +TRIBOL_HOST_DEVICE FaceGeomError CheckInterfacePair( InterfacePair& pair, const MeshData::Viewer& mesh1, + const MeshData::Viewer& mesh2, const Parameters& params, + ContactMethod const cMethod, ContactCase const cCase, + bool& isInteracting, ArrayViewT& planes_2d, + ArrayViewT& planes_3d, IndexT* plane_ct ); //----------------------------------------------------------------------------- // Free functions returning Contact Plane objects @@ -695,12 +629,10 @@ TRIBOL_HOST_DEVICE FaceGeomError CheckInterfacePair( InterfacePair& pair, * \param [in] fullOverlap True if full overlap calculation is used, false if interpenetration calculation is used * * \return 0 if no error, non-zero (via FaceGeomError enum) otherwise - * + * */ -TRIBOL_HOST_DEVICE FaceGeomError CheckFacePair( ContactPlane3D& cp, - const MeshData::Viewer& mesh1, - const MeshData::Viewer& mesh2, - const Parameters& params, +TRIBOL_HOST_DEVICE FaceGeomError CheckFacePair( ContactPlane3D& cp, const MeshData::Viewer& mesh1, + const MeshData::Viewer& mesh2, const Parameters& params, bool fullOverlap ); /*! @@ -712,12 +644,10 @@ TRIBOL_HOST_DEVICE FaceGeomError CheckFacePair( ContactPlane3D& cp, * \param [in] params coupling-scheme specific parameters * * \return 3D contact plane object with boolean indicating if face-pair form a local contact interaction - * + * */ -TRIBOL_HOST_DEVICE ContactPlane3D CheckAlignedFacePair( InterfacePair& pair, - const MeshData::Viewer& mesh1, - const MeshData::Viewer& mesh2, - const Parameters& params ); +TRIBOL_HOST_DEVICE ContactPlane3D CheckAlignedFacePair( InterfacePair& pair, const MeshData::Viewer& mesh1, + const MeshData::Viewer& mesh2, const Parameters& params ); /*! * \brief Checks if 2D edge-pair candidate is actual local contact interaction. @@ -729,13 +659,11 @@ TRIBOL_HOST_DEVICE ContactPlane3D CheckAlignedFacePair( InterfacePair& pair, * \param [in] fullOverlap True if full overlap calculation is used, false if interpenetration calculation is used * * \return 0 if no error, non-zero (via FaceGeomError enum) otherwise - * + * */ -TRIBOL_HOST_DEVICE FaceGeomError CheckEdgePair( ContactPlane2D& cp, - const MeshData::Viewer& mesh1, - const MeshData::Viewer& mesh2, - const Parameters& params, +TRIBOL_HOST_DEVICE FaceGeomError CheckEdgePair( ContactPlane2D& cp, const MeshData::Viewer& mesh1, + const MeshData::Viewer& mesh2, const Parameters& params, bool fullOverlap ); -} +} // namespace tribol #endif /* SRC_GEOM_CONTACTPLANE_HPP_ */ diff --git a/src/tribol/geom/GeomUtilities.cpp b/src/tribol/geom/GeomUtilities.cpp index 519477cc..e5022c8a 100644 --- a/src/tribol/geom/GeomUtilities.cpp +++ b/src/tribol/geom/GeomUtilities.cpp @@ -7,1705 +7,1537 @@ #include "ContactPlane.hpp" #include "tribol/utils/Math.hpp" -#include "axom/core.hpp" -#include "axom/slic.hpp" +#include "axom/core.hpp" +#include "axom/slic.hpp" -#include +#include #include -#include +#include -namespace tribol -{ +namespace tribol { -TRIBOL_HOST_DEVICE void ProjectPointToPlane( const RealT x, const RealT y, const RealT z, - const RealT nx, const RealT ny, const RealT nz, - const RealT ox, const RealT oy, const RealT oz, - RealT& px, RealT& py, RealT& pz ) +TRIBOL_HOST_DEVICE void ProjectPointToPlane( const RealT x, const RealT y, const RealT z, const RealT nx, + const RealT ny, const RealT nz, const RealT ox, const RealT oy, + const RealT oz, RealT& px, RealT& py, RealT& pz ) { - // compute the vector from input point to be projected to - // the origin point on the plane - RealT vx = x - ox; - RealT vy = y - oy; - RealT vz = z - oz; + // compute the vector from input point to be projected to + // the origin point on the plane + RealT vx = x - ox; + RealT vy = y - oy; + RealT vz = z - oz; - // compute the projection onto the plane normal - RealT dist = vx * nx + vy * ny + vz * nz; + // compute the projection onto the plane normal + RealT dist = vx * nx + vy * ny + vz * nz; - // compute the projected coordinates of the input point - px = x - dist*nx; - py = y - dist*ny; - pz = z - dist*nz; + // compute the projected coordinates of the input point + px = x - dist * nx; + py = y - dist * ny; + pz = z - dist * nz; - return; + return; -} // end ProjectPointToPlane() +} // end ProjectPointToPlane() //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE void ProjectPointToSegment( const RealT x, const RealT y, - const RealT nx, const RealT ny, - const RealT ox, const RealT oy, - RealT& px, RealT& py ) +TRIBOL_HOST_DEVICE void ProjectPointToSegment( const RealT x, const RealT y, const RealT nx, const RealT ny, + const RealT ox, const RealT oy, RealT& px, RealT& py ) { - // compute the vector from input point to be projected to - // the origin point on the plane - RealT vx = x - ox; - RealT vy = y - oy; + // compute the vector from input point to be projected to + // the origin point on the plane + RealT vx = x - ox; + RealT vy = y - oy; - // compute the projection onto the plane normal - RealT dist = vx * nx + vy * ny; + // compute the projection onto the plane normal + RealT dist = vx * nx + vy * ny; - // compute the projected coordinates of the input point - px = x - dist * nx; - py = y - dist * ny; + // compute the projected coordinates of the input point + px = x - dist * nx; + py = y - dist * ny; - return; + return; -} // end ProjectPointToSegment() +} // end ProjectPointToSegment() //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE void PolyInterYCentroid( const int namax, - const RealT* const xa, - const RealT* const ya, - const int nbmax, - const RealT* const xb, - const RealT* const yb, - const int isym, - RealT & area, - RealT & ycent ) +TRIBOL_HOST_DEVICE void PolyInterYCentroid( const int namax, const RealT* const xa, const RealT* const ya, + const int nbmax, const RealT* const xb, const RealT* const yb, + const int isym, RealT& area, RealT& ycent ) { + RealT vol; + + // calculate origin shift to avoid roundoff errors + RealT xorg = FLT_MAX; + RealT yorg = FLT_MAX; + RealT xa_min = FLT_MAX; + RealT xa_max = -FLT_MAX; + RealT ya_min = FLT_MAX; + RealT ya_max = -FLT_MAX; + RealT xb_min = FLT_MAX; + RealT xb_max = -FLT_MAX; + RealT yb_min = FLT_MAX; + RealT yb_max = -FLT_MAX; + + RealT qy = 0.0; + + if ( nbmax < 1 || namax < 1 ) { + area = 0.0; + vol = 0.0; + ycent = 0.0; + return; + } - RealT vol; - - // calculate origin shift to avoid roundoff errors - RealT xorg = FLT_MAX; - RealT yorg = FLT_MAX; - RealT xa_min = FLT_MAX; - RealT xa_max = -FLT_MAX; - RealT ya_min = FLT_MAX; - RealT ya_max = -FLT_MAX; - RealT xb_min = FLT_MAX; - RealT xb_max = -FLT_MAX; - RealT yb_min = FLT_MAX; - RealT yb_max = -FLT_MAX; + for ( int na = 0; na < namax; ++na ) { + if ( xa[na] < xa_min ) { + xa_min = xa[na]; + } + if ( ya[na] < ya_min ) { + ya_min = ya[na]; + } + if ( xa[na] > xa_max ) { + xa_max = xa[na]; + } + if ( ya[na] > ya_max ) { + ya_max = ya[na]; + } + xorg = axom::utilities::min( xorg, xa[na] ); + yorg = axom::utilities::min( yorg, ya[na] ); + } + for ( int nb = 0; nb < nbmax; ++nb ) { + if ( xb[nb] < xb_min ) { + xb_min = xb[nb]; + } + if ( yb[nb] < yb_min ) { + yb_min = yb[nb]; + } + if ( xb[nb] > xb_max ) { + xb_max = xb[nb]; + } + if ( yb[nb] > yb_max ) { + yb_max = yb[nb]; + } + xorg = axom::utilities::min( xorg, xb[nb] ); + yorg = axom::utilities::min( yorg, yb[nb] ); + } + if ( isym == 1 ) { + yorg = axom::utilities::max( yorg, 0.0 ); + } - RealT qy = 0.0; + area = 0.0; + vol = 0.0; + ycent = 0.0; + if ( xa_min > xb_max ) { + return; + } + if ( xb_min > xa_max ) { + return; + } + if ( ya_min > yb_max ) { + return; + } + if ( yb_min > ya_max ) { + return; + } - if (nbmax < 1 || namax < 1) { - area = 0.0; - vol = 0.0; - ycent = 0.0; - return; - } - - for (int na = 0 ; na < namax ; ++na) { - if (xa[na]xa_max) { - xa_max=xa[na]; + } + RealT dxa = xa2 - xa1; + if ( dxa == 0.0 ) { + continue; + } + RealT dya = ya2 - ya1; + RealT slopea = dya / dxa; + + // loop over faces of polygon b + for ( int nb = 0; nb < nbmax; ++nb ) { + int nbp = ( nb + 1 ) % nbmax; + RealT xb1 = xb[nb] - xorg; + RealT yb1 = yb[nb] - yorg; + RealT xb2 = xb[nbp] - xorg; + RealT yb2 = yb[nbp] - yorg; + if ( isym == 1 ) { + if ( yb[nb] < 0.0 && yb[nbp] < 0.0 ) { + continue; + } + if ( yb[nb] < 0.0 ) { + if ( yb1 != yb2 ) { + xb1 = xb1 - ( yb1 + yorg ) * ( xb2 - xb1 ) / ( yb2 - yb1 ); + } + yb1 = -yorg; + } else if ( yb[nbp] < 0.0 ) { + if ( yb1 != yb2 ) { + xb2 = xb2 - ( yb2 + yorg ) * ( xb1 - xb2 ) / ( yb1 - yb2 ); + } + yb2 = -yorg; + } } - if (ya[na]>ya_max) { - ya_max=ya[na]; + RealT dxb = xb2 - xb1; + if ( dxb == 0.0 ) { + continue; } - xorg = axom::utilities::min(xorg, xa[na]); - yorg = axom::utilities::min(yorg, ya[na]); - } - for (int nb = 0 ; nb < nbmax ; ++nb) { - if (xb[nb]xb_max) { - xb_max=xb[nb]; - } - if (yb[nb]>yb_max) { - yb_max=yb[nb]; - } - xorg = axom::utilities::min(xorg, xb[nb]); - yorg = axom::utilities::min(yorg, yb[nb]); - } - if (isym==1) { - yorg = axom::utilities::max(yorg, 0.0); - } - - area = 0.0; - vol = 0.0; - ycent = 0.0; - if (xa_min>xb_max) { - return; - } - if (xb_min>xa_max) { - return; - } - if (ya_min>yb_max) { - return; - } - if (yb_min>ya_max) { - return; - } - - // loop over faces of polygon a - for (int na = 0 ; na < namax ; ++na) { - int nap = (na+1)%namax; - RealT xa1 = xa[na] - xorg; - RealT ya1 = ya[na] - yorg; - RealT xa2 = xa[nap] - xorg; - RealT ya2 = ya[nap] - yorg; - if (isym==1) { - if (ya[na]<0.0 && ya[nap]<0.0) { - continue; - } - if (ya[na]<0.0) { - if (ya1!=ya2) { - xa1 = xa1 - (ya1+yorg)*(xa2-xa1)/(ya2-ya1); - } - ya1 = -yorg; - } - else if (ya[nap]<0.0) { - if (ya1!=ya2) { - xa2 = xa2 - (ya2+yorg)*(xa1-xa2)/(ya1-ya2); - } - ya2 = -yorg; - } - } - RealT dxa = xa2 - xa1; - if (dxa==0.0) { - continue; + RealT dyb = yb2 - yb1; + RealT slopeb = dyb / dxb; + + // determine sign of volume of intersection + RealT s = dxa * dxb; + + // calculate left and right coordinates of overlap + RealT xl = axom::utilities::max( axom::utilities::min( xa1, xa2 ), axom::utilities::min( xb1, xb2 ) ); + RealT xr = axom::utilities::min( axom::utilities::max( xa1, xa2 ), axom::utilities::max( xb1, xb2 ) ); + if ( xl >= xr ) { + continue; } - RealT dya = ya2 - ya1; - RealT slopea = dya/dxa; - - // loop over faces of polygon b - for (int nb = 0 ; nb < nbmax ; ++nb) { - int nbp = (nb+1)%nbmax; - RealT xb1 = xb[nb] - xorg; - RealT yb1 = yb[nb] - yorg; - RealT xb2 = xb[nbp] - xorg; - RealT yb2 = yb[nbp] - yorg; - if (isym==1) { - if (yb[nb]<0.0 && yb[nbp]<0.0) { - continue; - } - if (yb[nb]<0.0) { - if (yb1!=yb2) { - xb1 = xb1 - (yb1+yorg)*(xb2-xb1)/(yb2-yb1); - } - yb1 = -yorg; - } - else if (yb[nbp]<0.0) { - if (yb1!=yb2) { - xb2 = xb2 - (yb2+yorg)*(xb1-xb2)/(yb1-yb2); - } - yb2 = -yorg; - } - } - RealT dxb = xb2 - xb1; - if (dxb==0.0) { - continue; - } - RealT dyb = yb2 - yb1; - RealT slopeb = dyb/dxb; - - // determine sign of volume of intersection - RealT s = dxa * dxb; - - // calculate left and right coordinates of overlap - RealT xl = axom::utilities::max(axom::utilities::min(xa1, xa2), axom::utilities::min(xb1, xb2) ); - RealT xr = axom::utilities::min(axom::utilities::max(xa1, xa2), axom::utilities::max(xb1, xb2) ); - if (xl>=xr) { - continue; - } - RealT yla = ya1 + (xl-xa1)*slopea; - RealT ylb = yb1 + (xl-xb1)*slopeb; - RealT yra = ya1 + (xr-xa1)*slopea; - RealT yrb = yb1 + (xr-xb1)*slopeb; - RealT yl = axom::utilities::min(yla, ylb); - RealT yr = axom::utilities::min(yra, yrb); - - RealT area1; - RealT qy1; - RealT ym; - - // check if lines intersect - RealT dslope = slopea - slopeb; - if (dslope!=0.0) { - RealT xm = (yb1 - ya1 + slopea*xa1 - slopeb*xb1)/dslope; - ym = ya1 + slopea*(xm-xa1); - if (xm>xl && xm0) { - qy1 = 1.0/3.0*(ym+yl*yl/(yl+ym))*area1; - qy = qy + qy1; - } - if (ym+yr>0) { - RealT qy2 = 1.0/3.0*(yr+ym*ym/(ym+yr))*area2; - qy = qy + qy2; - } - - if (isym==1) { - yl = yl + yorg; - ym = ym + yorg; - yr = yr + yorg; - vol = vol + copysign( (xm-xl)*(yl*yl+yl*ym+ym*ym) - + (xr-xm)*(ym*ym+ym*yr+yr*yr), s) / 3.0; - } - continue; - } - } - - // lines do not intersect, case i - area1 = 0.5 * copysign( (xr-xl)*(yr+yl), s); - area = area + area1; - if (yl+yr>0) { - qy1 = 1./3.0*(yr+yl*yl/(yl+yr))*area1; - qy = qy + qy1; - } - - if (isym==1) { + RealT yla = ya1 + ( xl - xa1 ) * slopea; + RealT ylb = yb1 + ( xl - xb1 ) * slopeb; + RealT yra = ya1 + ( xr - xa1 ) * slopea; + RealT yrb = yb1 + ( xr - xb1 ) * slopeb; + RealT yl = axom::utilities::min( yla, ylb ); + RealT yr = axom::utilities::min( yra, yrb ); + + RealT area1; + RealT qy1; + RealT ym; + + // check if lines intersect + RealT dslope = slopea - slopeb; + if ( dslope != 0.0 ) { + RealT xm = ( yb1 - ya1 + slopea * xa1 - slopeb * xb1 ) / dslope; + ym = ya1 + slopea * ( xm - xa1 ); + if ( xm > xl && xm < xr ) { + // lines intersect, case ii + area1 = 0.5 * copysign( ( yl + ym ) * ( xm - xl ), s ); + RealT area2 = 0.5 * copysign( ( ym + yr ) * ( xr - xm ), s ); + area = area + area1 + area2; + + if ( yl + ym > 0 ) { + qy1 = 1.0 / 3.0 * ( ym + yl * yl / ( yl + ym ) ) * area1; + qy = qy + qy1; + } + if ( ym + yr > 0 ) { + RealT qy2 = 1.0 / 3.0 * ( yr + ym * ym / ( ym + yr ) ) * area2; + qy = qy + qy2; + } + + if ( isym == 1 ) { yl = yl + yorg; ym = ym + yorg; yr = yr + yorg; - vol = vol + copysign( (xr-xl)*(yl*yl+yl*yr+yr*yr), s) / 3.0; - } + vol = vol + copysign( ( xm - xl ) * ( yl * yl + yl * ym + ym * ym ) + + ( xr - xm ) * ( ym * ym + ym * yr + yr * yr ), + s ) / + 3.0; + } + continue; + } } - } - if (area != 0.0) { - ycent = qy/area + yorg; - } + // lines do not intersect, case i + area1 = 0.5 * copysign( ( xr - xl ) * ( yr + yl ), s ); + area = area + area1; + if ( yl + yr > 0 ) { + qy1 = 1. / 3.0 * ( yr + yl * yl / ( yl + yr ) ) * area1; + qy = qy + qy1; + } - if (isym==0) { - vol = area; - } - - return; + if ( isym == 1 ) { + yl = yl + yorg; + ym = ym + yorg; + yr = yr + yorg; + vol = vol + copysign( ( xr - xl ) * ( yl * yl + yl * yr + yr * yr ), s ) / 3.0; + } + } + } -} // end PolyInterYCentroid() + if ( area != 0.0 ) { + ycent = qy / area + yorg; + } + + if ( isym == 0 ) { + vol = area; + } + + return; + +} // end PolyInterYCentroid() //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE void Local2DToGlobalCoords( RealT xloc, RealT yloc, - RealT e1X, RealT e1Y, RealT e1Z, - RealT e2X, RealT e2Y, RealT e2Z, - RealT cX, RealT cY, RealT cZ, - RealT& xg, RealT& yg, RealT& zg ) +TRIBOL_HOST_DEVICE void Local2DToGlobalCoords( RealT xloc, RealT yloc, RealT e1X, RealT e1Y, RealT e1Z, RealT e2X, + RealT e2Y, RealT e2Z, RealT cX, RealT cY, RealT cZ, RealT& xg, RealT& yg, + RealT& zg ) { + // This projection takes the two input local vector components and uses + // them as coefficients in a linear combination of local basis vectors. + // This gives a 3-vector with origin at the common plane centroid. + RealT vx = xloc * e1X + yloc * e2X; + RealT vy = xloc * e1Y + yloc * e2Y; + RealT vz = xloc * e1Z + yloc * e2Z; + + // the vector in the global coordinate system requires the addition of the + // plane point vector (global Cartesian coordinate basis) to the previously + // computed vector + xg = vx + cX; + yg = vy + cY; + zg = vz + cZ; - // This projection takes the two input local vector components and uses - // them as coefficients in a linear combination of local basis vectors. - // This gives a 3-vector with origin at the common plane centroid. - RealT vx = xloc * e1X + yloc * e2X; - RealT vy = xloc * e1Y + yloc * e2Y; - RealT vz = xloc * e1Z + yloc * e2Z; - - // the vector in the global coordinate system requires the addition of the - // plane point vector (global Cartesian coordinate basis) to the previously - // computed vector - xg = vx + cX; - yg = vy + cY; - zg = vz + cZ; - - return; + return; -} // end Local2DToGlobalCoords() +} // end Local2DToGlobalCoords() //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE void GlobalTo2DLocalCoords( const RealT* const pX, - const RealT* const pY, - const RealT* const pZ, - RealT e1X, RealT e1Y, RealT e1Z, - RealT e2X, RealT e2Y, RealT e2Z, - RealT cX, RealT cY, RealT cZ, - RealT* const pLX, - RealT* const pLY, int size ) +TRIBOL_HOST_DEVICE void GlobalTo2DLocalCoords( const RealT* const pX, const RealT* const pY, const RealT* const pZ, + RealT e1X, RealT e1Y, RealT e1Z, RealT e2X, RealT e2Y, RealT e2Z, + RealT cX, RealT cY, RealT cZ, RealT* const pLX, RealT* const pLY, + int size ) { #ifdef TRIBOL_USE_HOST - SLIC_ERROR_IF(size > 0 && (pLX == nullptr || pLY == nullptr), - "GlobalTo2DLocalCoords: local coordinate pointers are null"); + SLIC_ERROR_IF( size > 0 && ( pLX == nullptr || pLY == nullptr ), + "GlobalTo2DLocalCoords: local coordinate pointers are null" ); #endif - // loop over projected nodes - for (int i=0; i 2) - { - cZ += x[dim*i+2]; - } - } + if ( numVert == 0 ) { + return false; + } + + // (re)initialize the input/output centroid components + cX = 0.0; + cY = 0.0; + cZ = 0.0; + + // loop over nodes adding the position components + RealT fac = 1.0 / numVert; + for ( int i = 0; i < numVert; ++i ) { + cX += x[dim * i]; + cY += x[dim * i + 1]; + if ( dim > 2 ) { + cZ += x[dim * i + 2]; + } + } - // divide by the number of nodes to compute average - cX *= fac; - cY *= fac; - cZ *= fac; + // divide by the number of nodes to compute average + cX *= fac; + cY *= fac; + cZ *= fac; - return true; + return true; -} // end VertexAvgCentroid() +} // end VertexAvgCentroid() //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE bool PolyAreaCentroid( const RealT* const x, - const int dim, - const int numVert, - RealT& cX, RealT& cY, RealT& cZ ) +TRIBOL_HOST_DEVICE bool PolyAreaCentroid( const RealT* const x, const int dim, const int numVert, RealT& cX, RealT& cY, + RealT& cZ ) { #ifdef TRIBOL_USE_HOST - SLIC_ERROR_IF(dim != 3, "PolyAreaCentroid: Only compatible with dim = 3."); - SLIC_ERROR_IF(numVert==0, "PolyAreaCentroid: numVert = 0."); + SLIC_ERROR_IF( dim != 3, "PolyAreaCentroid: Only compatible with dim = 3." ); + SLIC_ERROR_IF( numVert == 0, "PolyAreaCentroid: numVert = 0." ); #endif - if (numVert == 0) - { - return false; - } - - // (re)initialize the input/output centroid components - cX = 0.0; - cY = 0.0; - cZ = 0.0; - - // compute the vertex average centroid of the polygon in - // order to break it up into triangles - RealT cX_poly, cY_poly, cZ_poly; - VertexAvgCentroid( x, dim, numVert, cX_poly, cY_poly, cZ_poly); - - // loop over triangles formed from adjacent polygon vertices - // and the vertex averaged centroid - RealT xTri[3] = { 0., 0., 0. }; - RealT yTri[3] = { 0., 0., 0. }; - RealT zTri[3] = { 0., 0., 0. }; - - // assign all of the last triangle coordinates to the - // polygon's vertex average centroid - xTri[2] = cX_poly; - yTri[2] = cY_poly; - zTri[2] = cZ_poly; - RealT area_sum = 0.; - for (int i=0; i= max_intersections) - { + bool interior[4]; + + // initialize the interX and interY entries + initRealArray( interX, max_intersections, 0. ); + initRealArray( interY, max_intersections, 0. ); + initBoolArray( intersect, max_intersections, false ); + dupl = false; + + // loop over segment-segment intersections to find the rest of the + // intersecting vertices. This is O(n^2), but segments defined by two + // nodes interior to the other polygon will be skipped. This will catch + // outlier cases. + int interId = 0; + + // loop over A segments + for ( int ia = 0; ia < numVertexA; ++ia ) { + int vAID1 = ia; + int vAID2 = ( ia == ( numVertexA - 1 ) ) ? 0 : ( ia + 1 ); + + // set boolean indicating which nodes on segment A are interior + interior[0] = ( interiorVAId[vAID1] != -1 ) ? true : false; + interior[1] = ( interiorVAId[vAID2] != -1 ) ? true : false; + // bool checkA = (interior[0] == -1 && interior[1] == -1) ? true : false; + bool checkA = true; + + // loop over B segments + for ( int jb = 0; jb < numVertexB; ++jb ) { + int vBID1 = jb; + int vBID2 = ( jb == ( numVertexB - 1 ) ) ? 0 : ( jb + 1 ); + interior[2] = ( interiorVBId[vBID1] != -1 ) ? true : false; + interior[3] = ( interiorVBId[vBID2] != -1 ) ? true : false; + // bool checkB = (interior[2] == -1 && interior[3] == -1) ? true : false; + bool checkB = true; + + // if both segments are not defined by nodes interior to the other polygon + if ( checkA && checkB ) { + if ( interId >= max_intersections ) { #ifdef TRIBOL_USE_HOST - SLIC_DEBUG("Intersection2DPolygon: number of segment/segment intersections exceeds precomputed maximum; " << - "check for degenerate overlap."); + SLIC_DEBUG( "Intersection2DPolygon: number of segment/segment intersections exceeds precomputed maximum; " + << "check for degenerate overlap." ); #endif - return DEGENERATE_OVERLAP; - } - - intersect[interId] = SegmentIntersection2D( xA[vAID1], yA[vAID1], xA[vAID2], yA[vAID2], - xB[vBID1], yB[vBID1], xB[vBID2], yB[vBID2], - interior, interX[interId], interY[interId], - dupl, posTol ); - ++interId; - } - } // end loop over A segments - } // end loop over B segments - - // count the number of segment-segment intersections - int numSegInter = 0; - for (int i=0; i max_identified_points) - { + } // end loop over A segments + } // end loop over B segments + + // count the number of segment-segment intersections + int numSegInter = 0; + for ( int i = 0; i < interId; ++i ) { + if ( intersect[i] ) ++numSegInter; + } + + // add check for case where there are no interior vertices or + // intersection vertices + if ( numSegInter == 0 && numVBI == 0 && numVAI == 0 ) { + area = 0.0; + return NO_FACE_GEOM_ERROR; + } + + // allocate temp intersection polygon vertex coordinate arrays to consist + // of segment-segment intersections and number of interior points in A and B + numPolyVert = numSegInter + numVAI + numVBI; + // maximum number of vertices between the two polygons. assumes convex elements. + constexpr int max_nodes_per_overlap = 2 * max_nodes_per_element; + constexpr int max_identified_points = max_nodes_per_overlap + 2 * max_nodes_per_element; + RealT polyXTemp[max_identified_points]; + RealT polyYTemp[max_identified_points]; + + // fill polyXTemp and polyYTemp with the intersection points + int k = 0; + for ( int i = 0; i < interId; ++i ) { + if ( intersect[i] ) { + polyXTemp[k] = interX[i]; + polyYTemp[k] = interY[i]; + ++k; + } + } + + // fill polyX and polyY with the vertices on A that lie in B + for ( int i = 0; i < numVertexA; ++i ) { + if ( interiorVAId[i] != -1 ) { + // debug + if ( k > max_identified_points ) { #ifdef TRIBOL_USE_HOST - SLIC_DEBUG("Intersection2DPolygon(): number of A vertices interior to B " << - "polygon exceeds total number of overlap vertices. Check interior vertex id values."); + SLIC_DEBUG( "Intersection2DPolygon(): number of A vertices interior to B " + << "polygon exceeds total number of overlap vertices. Check interior vertex id values." ); #endif - return FACE_VERTEX_INDEX_EXCEEDS_OVERLAP_VERTICES; - } - - polyXTemp[k] = xA[i]; - polyYTemp[k] = yA[i]; - ++k; + return FACE_VERTEX_INDEX_EXCEEDS_OVERLAP_VERTICES; } - } - - for (int i=0; i max_identified_points) - { + + polyXTemp[k] = xA[i]; + polyYTemp[k] = yA[i]; + ++k; + } + } + + for ( int i = 0; i < numVertexB; ++i ) { + if ( interiorVBId[i] != -1 ) { + // debug + if ( k > max_identified_points ) { #ifdef TRIBOL_USE_HOST - SLIC_DEBUG("Intersection2DPolygon(): number of B vertices interior to A " << - "polygon exceeds total number of overlap vertices. Check interior vertex id values."); + SLIC_DEBUG( "Intersection2DPolygon(): number of B vertices interior to A " + << "polygon exceeds total number of overlap vertices. Check interior vertex id values." ); #endif - return FACE_VERTEX_INDEX_EXCEEDS_OVERLAP_VERTICES; - } - - polyXTemp[k] = xB[i]; - polyYTemp[k] = yB[i]; - ++k; - } - } - - // reorder the unordered vertices and check segment length against tolerance for edge collapse. - // Only do this for overlaps with 3 or more vertices. We skip any overlap that degenerates to <3 vertices - if (numPolyVert>2) - { - // order the unordered vertices (in counter clockwise fashion) - PolyReorder( polyXTemp, polyYTemp, numPolyVert ); - - // check length of segs against tolerance and collapse short segments if necessary - // This is where polyX and polyY get allocated for any overlap that remains with - // > 3 vertices - int numFinalVert = 0; - - FaceGeomError segErr = CheckPolySegs( polyXTemp, polyYTemp, numPolyVert, - lenTol, polyX, polyY, numFinalVert ); - - // check for an error in the segment check routine - if (segErr != 0) - { - return segErr; - } - - // check to see if the overlap was degenerated to have 2 or less vertices. - if (numFinalVert<3) - { - area = 0.0; - return NO_FACE_GEOM_ERROR; // punt on degenerated or collapsed overlaps + return FACE_VERTEX_INDEX_EXCEEDS_OVERLAP_VERTICES; } - numPolyVert = numFinalVert; - } - else - { + polyXTemp[k] = xB[i]; + polyYTemp[k] = yB[i]; + ++k; + } + } + + // reorder the unordered vertices and check segment length against tolerance for edge collapse. + // Only do this for overlaps with 3 or more vertices. We skip any overlap that degenerates to <3 vertices + if ( numPolyVert > 2 ) { + // order the unordered vertices (in counter clockwise fashion) + PolyReorder( polyXTemp, polyYTemp, numPolyVert ); + + // check length of segs against tolerance and collapse short segments if necessary + // This is where polyX and polyY get allocated for any overlap that remains with + // > 3 vertices + int numFinalVert = 0; + + FaceGeomError segErr = CheckPolySegs( polyXTemp, polyYTemp, numPolyVert, lenTol, polyX, polyY, numFinalVert ); + + // check for an error in the segment check routine + if ( segErr != 0 ) { + return segErr; + } + + // check to see if the overlap was degenerated to have 2 or less vertices. + if ( numFinalVert < 3 ) { area = 0.0; - return NO_FACE_GEOM_ERROR; // don't return error here. We should tolerate 'collapsed' (zero area) overlaps - } + return NO_FACE_GEOM_ERROR; // punt on degenerated or collapsed overlaps + } - // compute the area of the polygon - area = Area2DPolygon( polyX, polyY, numPolyVert ); + numPolyVert = numFinalVert; + } else { + area = 0.0; + return NO_FACE_GEOM_ERROR; // don't return error here. We should tolerate 'collapsed' (zero area) overlaps + } - return NO_FACE_GEOM_ERROR; + // compute the area of the polygon + area = Area2DPolygon( polyX, polyY, numPolyVert ); -} // end Intersection2DPolygon() + return NO_FACE_GEOM_ERROR; + +} // end Intersection2DPolygon() //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE bool CheckPolyOrientation( const RealT* const x, - const RealT* const y, - const int numVertex ) +TRIBOL_HOST_DEVICE bool CheckPolyOrientation( const RealT* const x, const RealT* const y, const int numVertex ) { - bool check = true; - for (int i=0; i= 0) && (v >= 0) && (u + v <= 1)) - { - inside = true; - } + if ( ( u >= 0 ) && ( v >= 0 ) && ( u + v <= 1 ) ) { + inside = true; + } - return inside; + return inside; -} // end Point2DInTri() +} // end Point2DInTri() //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE RealT Area2DPolygon( const RealT* const x, - const RealT* const y, - const int numPolyVert ) +TRIBOL_HOST_DEVICE RealT Area2DPolygon( const RealT* const x, const RealT* const y, const int numPolyVert ) { + RealT area = 0.; - RealT area = 0.; - - // compute vertex-averaged centroid to construct a triangle between segment - // vertices and centroid - RealT* z = nullptr; - RealT xc, yc, zc; - VertexAvgCentroid(x, y, z, numPolyVert, xc, yc, zc); + // compute vertex-averaged centroid to construct a triangle between segment + // vertices and centroid + RealT* z = nullptr; + RealT xc, yc, zc; + VertexAvgCentroid( x, y, z, numPolyVert, xc, yc, zc ); - for (int i=0; i -detTol && det < detTol) - { - x = 0.; - y = 0.; - duplicate = false; - return false; - } - - // compute intersection - RealT invDet = 1.0 / det; - RealT rX = xA1 - xA2; - RealT rY = yA1 - yA2; - RealT tA = invDet * (rX * lambdaY2 - rY * lambdaX2); - RealT tB = invDet * (rX * lambdaY1 - rY * lambdaX1); - - // if tA and tB don't lie between [0,1] then return false. - if ((tA < 0. || tA > 1.) || (tB < 0. || tB > 1.)) - { - // no intersection - x = 0.; - y = 0.; - duplicate = false; - return false; - } - - // TODO refine how these debug calculations are guarded - { - // debug check to make sure the intersection coordinates derived from - // each segment equation (scaled with tA and tB) are the same to some - // tolerance - RealT xTest1 = xA1 + lambdaX1 * tA; - RealT yTest1 = yA1 + lambdaY1 * tA; - RealT xTest2 = xA2 + lambdaX2 * tB; - RealT yTest2 = yA2 + lambdaY2 * tB; - - RealT xDiff = xTest1 - xTest2; - RealT yDiff = yTest1 - yTest2; - - // make sure the differences are positive - xDiff = (xDiff < 0.) ? -1.0 * xDiff : xDiff; - yDiff = (yDiff < 0.) ? -1.0 * yDiff : yDiff; - - RealT diffTol = 1.0E-3; + // note 1: this routine computes a unique segment-segment intersection, where two + // segments are assumed to intersect at a single point. A segment-segment overlap + // is a different computation and is not accounted for here. In the context of the + // use of this routine in the tribol polygon-polygon intersection calculation, + // two overlapping segments will have already registered the vertices that form + // the bounds of the overlapping length as vertices interior to the other polygon + // and therefore will be in the list of overlapping polygon vertices prior to this + // routine. + // + // note 2: any segment-segment intersection that occurs at a vertex of either segment + // will pass back the intersection coordinates, but will note a duplicate vertex. + // This is because that any vertex of polygon A that lies on a segment of polygon B + // will be caught and registered as a vertex interior to the other polygon and will + // be in the list of overlapping polygon vertices prior to calling this routine. + + // compute segment vectors + RealT lambdaX1 = xB1 - xA1; + RealT lambdaY1 = yB1 - yA1; + + RealT lambdaX2 = xB2 - xA2; + RealT lambdaY2 = yB2 - yA2; + + RealT seg1Mag = magnitude( lambdaX1, lambdaY1 ); + RealT seg2Mag = magnitude( lambdaX2, lambdaY2 ); + + // compute determinant of the lambda matrix, [ -lx1 -ly1, lx2 ly2 ] + RealT det = -lambdaX1 * lambdaY2 + lambdaX2 * lambdaY1; + + // return false if det = 0. Check for numerically zero determinant + RealT detTol = 1.E-12; + if ( det > -detTol && det < detTol ) { + x = 0.; + y = 0.; + duplicate = false; + return false; + } + + // compute intersection + RealT invDet = 1.0 / det; + RealT rX = xA1 - xA2; + RealT rY = yA1 - yA2; + RealT tA = invDet * ( rX * lambdaY2 - rY * lambdaX2 ); + RealT tB = invDet * ( rX * lambdaY1 - rY * lambdaX1 ); + + // if tA and tB don't lie between [0,1] then return false. + if ( ( tA < 0. || tA > 1. ) || ( tB < 0. || tB > 1. ) ) { + // no intersection + x = 0.; + y = 0.; + duplicate = false; + return false; + } + + // TODO refine how these debug calculations are guarded + { + // debug check to make sure the intersection coordinates derived from + // each segment equation (scaled with tA and tB) are the same to some + // tolerance + RealT xTest1 = xA1 + lambdaX1 * tA; + RealT yTest1 = yA1 + lambdaY1 * tA; + RealT xTest2 = xA2 + lambdaX2 * tB; + RealT yTest2 = yA2 + lambdaY2 * tB; + + RealT xDiff = xTest1 - xTest2; + RealT yDiff = yTest1 - yTest2; + + // make sure the differences are positive + xDiff = ( xDiff < 0. ) ? -1.0 * xDiff : xDiff; + yDiff = ( yDiff < 0. ) ? -1.0 * yDiff : yDiff; + + RealT diffTol = 1.0E-3; #ifdef TRIBOL_USE_HOST - SLIC_DEBUG_IF( xDiff > diffTol || yDiff > diffTol, - "SegmentIntersection2D(): Intersection coordinates are not equally derived." ); + SLIC_DEBUG_IF( xDiff > diffTol || yDiff > diffTol, + "SegmentIntersection2D(): Intersection coordinates are not equally derived." ); #endif - } - - // if we get here then it means we have an intersection point. - // Find the minimum distance of the intersection point to any of the segment - // vertices. - x = xA1 + lambdaX1 * tA; - y = yA1 + lambdaY1 * tA; - - // for convenience, define an array of pointers that point to the - // input coordinates - RealT xVert[4]; - RealT yVert[4]; - - xVert[0] = xA1; - xVert[1] = xB1; - xVert[2] = xA2; - xVert[3] = xB2; - - yVert[0] = yA1; - yVert[1] = yB1; - yVert[2] = yA2; - yVert[3] = yB2; - - RealT distX[4]; - RealT distY[4]; - RealT distMag[4]; - - for (int i=0; i<4; ++i) - { - distX[i] = x - xVert[i]; - distY[i] = y - yVert[i]; - distMag[i] = magnitude( distX[i], distY[i] ); - } - - RealT distMin = (seg1Mag > seg2Mag) ? seg1Mag: seg2Mag; - int idMin; - RealT xMinVert; - RealT yMinVert; - - for (int i=0; i<4; ++i) - { - if (distMag[i] < distMin) - { - distMin = distMag[i]; - idMin = i; - xMinVert = xVert[i]; - yMinVert = yVert[i]; - } - } - - // check to see if the minimum distance is less than the position tolerance for - // the segments - RealT distRatio = (idMin == 0 || idMin == 1) ? (distMin / seg1Mag) : (distMin / seg2Mag); - - // if the distRatio is less than the tolerance, or percentage cutoff of the original - // segment that we would like to keep, then check to see if the segment vertex closest - // to the computed intersection point is an interior point. If this is true, then collapse - // the computed intersection point to the interior point and mark the duplicate boolean. - // Also do this for the argument, interior, set to nullptr - if (distRatio < tol) - { - if (interior == nullptr || interior[idMin]) - { - x = xMinVert; - y = yMinVert; - duplicate = true; - return false; - } - } + } + + // if we get here then it means we have an intersection point. + // Find the minimum distance of the intersection point to any of the segment + // vertices. + x = xA1 + lambdaX1 * tA; + y = yA1 + lambdaY1 * tA; + + // for convenience, define an array of pointers that point to the + // input coordinates + RealT xVert[4]; + RealT yVert[4]; + + xVert[0] = xA1; + xVert[1] = xB1; + xVert[2] = xA2; + xVert[3] = xB2; + + yVert[0] = yA1; + yVert[1] = yB1; + yVert[2] = yA2; + yVert[3] = yB2; + + RealT distX[4]; + RealT distY[4]; + RealT distMag[4]; + + for ( int i = 0; i < 4; ++i ) { + distX[i] = x - xVert[i]; + distY[i] = y - yVert[i]; + distMag[i] = magnitude( distX[i], distY[i] ); + } - // if we are here we are ready to return the true intersection point - duplicate = false; - return true; + RealT distMin = ( seg1Mag > seg2Mag ) ? seg1Mag : seg2Mag; + int idMin; + RealT xMinVert; + RealT yMinVert; + + for ( int i = 0; i < 4; ++i ) { + if ( distMag[i] < distMin ) { + distMin = distMag[i]; + idMin = i; + xMinVert = xVert[i]; + yMinVert = yVert[i]; + } + } + + // check to see if the minimum distance is less than the position tolerance for + // the segments + RealT distRatio = ( idMin == 0 || idMin == 1 ) ? ( distMin / seg1Mag ) : ( distMin / seg2Mag ); + + // if the distRatio is less than the tolerance, or percentage cutoff of the original + // segment that we would like to keep, then check to see if the segment vertex closest + // to the computed intersection point is an interior point. If this is true, then collapse + // the computed intersection point to the interior point and mark the duplicate boolean. + // Also do this for the argument, interior, set to nullptr + if ( distRatio < tol ) { + if ( interior == nullptr || interior[idMin] ) { + x = xMinVert; + y = yMinVert; + duplicate = true; + return false; + } + } + + // if we are here we are ready to return the true intersection point + duplicate = false; + return true; -} // end SegmentIntersection2D() +} // end SegmentIntersection2D() //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE FaceGeomError CheckPolySegs( const RealT* const x, const RealT* const y, - const int numPoints, const RealT tol, - RealT* xnew, RealT* ynew, - int& numNewPoints ) +TRIBOL_HOST_DEVICE FaceGeomError CheckPolySegs( const RealT* const x, const RealT* const y, const int numPoints, + const RealT tol, RealT* xnew, RealT* ynew, int& numNewPoints ) { - constexpr int max_nodes_per_overlap = 8; - RealT newIDs[ max_nodes_per_overlap ]; - - // set newIDs[i] to original local ordering - for (int i=0; i= 3 for valid overlap polygons prior - // to memory allocation - if (numNewPoints < 3) - { - // return and degenerated polygon will be skipped over. - return NO_FACE_GEOM_ERROR; - } - - // set the coordinates in xnew and ynew - int k = 0; - for (int i=0; i numNewPoints) - { + constexpr int max_nodes_per_overlap = 8; + RealT newIDs[max_nodes_per_overlap]; + + // set newIDs[i] to original local ordering + for ( int i = 0; i < numPoints; ++i ) { + newIDs[i] = i; + } + + for ( int i = 0; i < numPoints; ++i ) { + // determine vertex indices of the segment + int ia = i; + int ib = ( i == ( numPoints - 1 ) ) ? 0 : ( i + 1 ); + + // compute segment vector magnitude + RealT lambdaX = x[ib] - x[ia]; + RealT lambdaY = y[ib] - y[ia]; + RealT lambdaMag = magnitude( lambdaX, lambdaY ); + + // check segment length against tolerance + if ( lambdaMag < tol ) { + // collapse second vertex to the first vertex of the current segment + newIDs[ib] = i; + } + } + + // determine the number of new points + numNewPoints = 0; + for ( int i = 0; i < numPoints; ++i ) { + if ( newIDs[i] == i ) { + ++numNewPoints; + } + } + + // check to make sure numNewPoints >= 3 for valid overlap polygons prior + // to memory allocation + if ( numNewPoints < 3 ) { + // return and degenerated polygon will be skipped over. + return NO_FACE_GEOM_ERROR; + } + + // set the coordinates in xnew and ynew + int k = 0; + for ( int i = 0; i < numPoints; ++i ) { + if ( newIDs[i] == i ) { + if ( k > numNewPoints ) { #ifdef TRIBOL_USE_HOST - SLIC_DEBUG("checkPolySegs(): index into polyX/polyY exceeds allocated space"); + SLIC_DEBUG( "checkPolySegs(): index into polyX/polyY exceeds allocated space" ); #endif - return FACE_VERTEX_INDEX_EXCEEDS_OVERLAP_VERTICES; - } - - xnew[k] = x[i]; - ynew[k] = y[i]; - ++k; + return FACE_VERTEX_INDEX_EXCEEDS_OVERLAP_VERTICES; } - } - return NO_FACE_GEOM_ERROR; + xnew[k] = x[i]; + ynew[k] = y[i]; + ++k; + } + } + + return NO_FACE_GEOM_ERROR; -} // end CheckPolySegs() +} // end CheckPolySegs() //------------------------------------------------------------------------------ TRIBOL_HOST_DEVICE bool PolyReorder( RealT* const x, RealT* const y, const int numPoints ) { - - if (numPoints<3) - { + if ( numPoints < 3 ) { #ifdef TRIBOL_USE_HOST - SLIC_DEBUG("PolyReorder: numPoints (" << numPoints << ") < 3."); + SLIC_DEBUG( "PolyReorder: numPoints (" << numPoints << ") < 3." ); #endif - return false; - } - - RealT xC, yC, zC; - RealT * z = nullptr; - constexpr int max_nodes_per_overlap = 8 + 2*4; - RealT proj [max_nodes_per_overlap - 2]; - - int newIDs[ max_nodes_per_overlap ]; - - // initialize newIDs array to local ordering, 0,1,2,...,numPoints-1 - for (int i=0; i 0. ) ? true : false; } - // check if all points are on one side of line defined by segment - // (pk at this point should be equal to numPoints - 2) - bool neg = false; - bool pos = false; - for (int ip=0; ip 0.) ? true : false; - } - - if (neg && pos) - { - break; - } + if ( neg && pos ) { + break; + } + } + + // if one of the booleans is false then all points are on one side + // of line defined by i-j segment. + if ( !neg || !pos ) { + // check the orientation of the nodes to make sure we have the correct + // one of two segments that will pass the previous test. + // Check the dot product between the normal and the vector + // between the centroid and first (0th) vertex + RealT vx = xC - x[id0]; + RealT vy = yC - y[id0]; + + RealT prod = nrmlx * vx + nrmly * vy; + + // check if the two vertices are a segment on the convex hull and oriented CCW. + // CCW orientation has prod > 0 + if ( prod > 0 ) { + id1 = j; + break; } + } + + } // end loop over j + + // swap ids + if ( id1 != -1 ) { + newIDs[1] = id1; + newIDs[id1] = 1; + } + + // given the first (current) reference segment, compute the link vector between the jth vertex + // (j cannot be a vertex belonging to the reference segment) and the first vertex of + // the given reference segment. The next reference segment is between the second vertex of + // the current reference segment and the jth vertex whose link vector has the smallest + // dot product with the current reference segment. - // if one of the booleans is false then all points are on one side - // of line defined by i-j segment. - if (!neg || !pos) - { - // check the orientation of the nodes to make sure we have the correct - // one of two segments that will pass the previous test. - // Check the dot product between the normal and the vector - // between the centroid and first (0th) vertex - RealT vx = xC - x[id0]; - RealT vy = yC - y[id0]; - - RealT prod = nrmlx * vx + nrmly * vy; - - // check if the two vertices are a segment on the convex hull and oriented CCW. - // CCW orientation has prod > 0 - if (prod > 0) - { - id1 = j; - break; - } + for ( int i = 0; i < ( numPoints - 3 ); ++i ) // increment to (numPoints - 3) or (numPoints - 2)? + { + int jID; + RealT cosThetaMax = -1.; // this handles angles up to 180 degrees. Not possible for convex polygons + RealT cosTheta; + RealT refMag, linkMag; + + // compute reference vector; + RealT refx, refy; + refx = x[newIDs[i + 1]] - x[newIDs[i]]; + refy = y[newIDs[i + 1]] - y[newIDs[i]]; + refMag = magnitude( refx, refy ); + + // SLIC_ERROR_IF(refMag < 1.E-12, "PolyReorder: reference segment for link vector check is nearly zero + // length"); + + // loop over link vectors of unassigned vertices + int nextVertexID = 2 + i; + for ( int j = nextVertexID; j < numPoints; ++j ) { + RealT lx, ly; + + lx = x[newIDs[j]] - x[newIDs[i]]; + ly = y[newIDs[j]] - y[newIDs[i]]; + linkMag = magnitude( lx, ly ); + + cosTheta = ( lx * refx + ly * refy ) / ( refMag * linkMag ); + if ( cosTheta > cosThetaMax ) { + cosThetaMax = cosTheta; + jID = j; } - } // end loop over j - - // swap ids - if (id1 != -1) - { - newIDs[1] = id1; - newIDs[id1] = 1; - } - - // given the first (current) reference segment, compute the link vector between the jth vertex - // (j cannot be a vertex belonging to the reference segment) and the first vertex of - // the given reference segment. The next reference segment is between the second vertex of - // the current reference segment and the jth vertex whose link vector has the smallest - // dot product with the current reference segment. - - for (int i=0; i<(numPoints-3); ++i) // increment to (numPoints - 3) or (numPoints - 2)? - { - int jID; - RealT cosThetaMax = -1.; // this handles angles up to 180 degrees. Not possible for convex polygons - RealT cosTheta; - RealT refMag, linkMag; - - // compute reference vector; - RealT refx, refy; - refx = x[newIDs[i+1]] - x[newIDs[i]]; - refy = y[newIDs[i+1]] - y[newIDs[i]]; - refMag = magnitude( refx, refy ); - -// SLIC_ERROR_IF(refMag < 1.E-12, "PolyReorder: reference segment for link vector check is nearly zero length"); - - // loop over link vectors of unassigned vertices - int nextVertexID = 2+i; - for (int j=nextVertexID; j cosThetaMax) - { - cosThetaMax = cosTheta; - jID = j; - } - - } // end loop over j - - // we have found the minimum angle and the corresponding local vertex id. - // swap ids - int swapID = newIDs[ nextVertexID ]; - newIDs[ nextVertexID ] = newIDs[jID]; - newIDs[jID] = swapID; - - } // end loop over i - - // reorder x and y coordinate arrays based on newIDs id-array - RealT xtemp[ max_nodes_per_overlap ]; - RealT ytemp[ max_nodes_per_overlap ]; - for (int i=0; i0; --i) - { - x[k] = xtemp[i]; - y[k] = ytemp[i]; - ++k; - } + constexpr int max_nodes_per_elem = 4; + RealT xtemp[max_nodes_per_elem]; + RealT ytemp[max_nodes_per_elem]; + for ( int i = 0; i < numPoints; ++i ) { + xtemp[i] = x[i]; + ytemp[i] = y[i]; + } + + int k = 1; + for ( int i = ( numPoints - 1 ); i > 0; --i ) { + x[k] = xtemp[i]; + y[k] = ytemp[i]; + ++k; + } } //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE void PolyReorderWithNormal( RealT* const x, - RealT* const y, - RealT* const z, - const int numPoints, - const RealT nX, - const RealT nY, - const RealT nZ ) +TRIBOL_HOST_DEVICE void PolyReorderWithNormal( RealT* const x, RealT* const y, RealT* const z, const int numPoints, + const RealT nX, const RealT nY, const RealT nZ ) { - // form link vectors between second and first vertex and third and first - // vertex - RealT lv10X = x[1] - x[0]; - RealT lv10Y = y[1] - y[0]; - RealT lv10Z = z[1] - z[0]; - - RealT lv20X = x[2] - x[0]; - RealT lv20Y = y[2] - y[0]; - RealT lv20Z = z[2] - z[0]; - - // take the cross product of the vectors to get the normal - RealT pNrmlX, pNrmlY, pNrmlZ; - crossProd( lv10X, lv10Y, lv10Z, - lv20X, lv20Y, lv20Z, - pNrmlX, pNrmlY, pNrmlZ ); - - // dot the computed plane normal based on vertex ordering with the - // input normal - RealT v = dotProd( pNrmlX, pNrmlY, pNrmlZ, nX, nY, nZ ); - - // check to see if v is negative. If so, reorient the vertices - constexpr int max_nodes_per_overlap = 8; - if (v < 0) - { - RealT xTemp[ max_nodes_per_overlap ]; - RealT yTemp[ max_nodes_per_overlap ]; - RealT zTemp[ max_nodes_per_overlap ]; - - xTemp[0] = x[0]; - yTemp[0] = y[0]; - zTemp[0] = z[0]; - - for (int i=1; i and the plane normal - RealT prodV = vX * nX + vY * nY + vZ * nZ; - - // compute the line segment parameter, t, and check to see if it is - // between 0 and 1, inclusive - RealT t = prodV / prod; - - if (t >= 0 && t <= 1) - { - x = xA + lambdaX * t; - y = yA + lambdaY * t; - z = zA + lambdaZ * t; - inPlane = false; - return true; - } - else - { - x = 0.; - y = 0.; - z = 0.; - inPlane = false; - return false; - } + // compute vector difference between point on plane + // and first vertex on segment + RealT vX = xP - xA; + RealT vY = yP - yA; + RealT vZ = zP - zA; + + // compute dot product between and the plane normal + RealT prodV = vX * nX + vY * nY + vZ * nZ; + + // compute the line segment parameter, t, and check to see if it is + // between 0 and 1, inclusive + RealT t = prodV / prod; + + if ( t >= 0 && t <= 1 ) { + x = xA + lambdaX * t; + y = yA + lambdaY * t; + z = zA + lambdaZ * t; + inPlane = false; + return true; + } else { + x = 0.; + y = 0.; + z = 0.; + inPlane = false; + return false; + } -} // end LinePlaneIntersection() +} // end LinePlaneIntersection() //------------------------------------------------------------------------------ -bool PlanePlaneIntersection( const RealT x1, const RealT y1, const RealT z1, - const RealT x2, const RealT y2, const RealT z2, - const RealT nX1, const RealT nY1, const RealT nZ1, - const RealT nX2, const RealT nY2, const RealT nZ2, - RealT& x, RealT& y, RealT& z ) +bool PlanePlaneIntersection( const RealT x1, const RealT y1, const RealT z1, const RealT x2, const RealT y2, + const RealT z2, const RealT nX1, const RealT nY1, const RealT nZ1, const RealT nX2, + const RealT nY2, const RealT nZ2, RealT& x, RealT& y, RealT& z ) { + // note: this routine has not been tested - // note: this routine has not been tested - - // check dot product between two normals for coplanarity - RealT coProd = nX1 * nX2 + nY1 * nY2 + nZ1 * nZ2; + // check dot product between two normals for coplanarity + RealT coProd = nX1 * nX2 + nY1 * nY2 + nZ1 * nZ2; - if (axom::utilities::isNearlyEqual(coProd, 1.0, 1.e-8)) - { - x = 0.; - y = 0.; - z = 0.; - return false; - } + if ( axom::utilities::isNearlyEqual( coProd, 1.0, 1.e-8 ) ) { + x = 0.; + y = 0.; + z = 0.; + return false; + } - // compute dot products between each plane's reference point and the normal - RealT prod1 = nX1 * x1 + nY1 * y1 + nZ1 * z1; - RealT prod2 = nX2 * x2 + nY2 * y2 + nZ2 * z2; + // compute dot products between each plane's reference point and the normal + RealT prod1 = nX1 * x1 + nY1 * y1 + nZ1 * z1; + RealT prod2 = nX2 * x2 + nY2 * y2 + nZ2 * z2; - // form matrix of dot products between normals - RealT A11 = nX1 * nX1 + nY1 * nY1 + nZ1 * nZ1; - RealT A12 = nX1 * nX2 + nY1 * nY2 + nZ1 * nZ2; - RealT A22 = nX2 * nX2 + nY2 * nY2 + nZ2 * nZ2; + // form matrix of dot products between normals + RealT A11 = nX1 * nX1 + nY1 * nY1 + nZ1 * nZ1; + RealT A12 = nX1 * nX2 + nY1 * nY2 + nZ1 * nZ2; + RealT A22 = nX2 * nX2 + nY2 * nY2 + nZ2 * nZ2; - // form determinant and inverse determinant of 2x2 matrix - RealT detA = A11 * A22 - A12 * A12; - RealT invDetA = 1.0 / detA; + // form determinant and inverse determinant of 2x2 matrix + RealT detA = A11 * A22 - A12 * A12; + RealT invDetA = 1.0 / detA; - // form inverse matrix components - RealT invA11 = A22; - RealT invA12 = -A12; - RealT invA22 = A11; + // form inverse matrix components + RealT invA11 = A22; + RealT invA12 = -A12; + RealT invA22 = A11; - // compute two parameters for point on line of intersection - RealT s1 = invDetA * (prod1 * invA11 + prod2 * invA12); - RealT s2 = invDetA * (prod1 * invA12 + prod2 * invA22); + // compute two parameters for point on line of intersection + RealT s1 = invDetA * ( prod1 * invA11 + prod2 * invA12 ); + RealT s2 = invDetA * ( prod1 * invA12 + prod2 * invA22 ); - // compute the point on the line of intersection - x = s1 * nX1 + s2 * nX2; - y = s1 * nY1 + s2 * nY2; - z = s1 * nZ1 + s2 * nZ2; + // compute the point on the line of intersection + x = s1 * nX1 + s2 * nX2; + y = s1 * nY1 + s2 * nY2; + z = s1 * nZ1 + s2 * nZ2; - return true; + return true; -} // end PlanePlaneIntersection() +} // end PlanePlaneIntersection() //------------------------------------------------------------------------------ -void Vertex2DOrderToCCW( const RealT* const x, const RealT* const y, - RealT* xTemp, RealT* yTemp, - const int numVert ) +void Vertex2DOrderToCCW( const RealT* const x, const RealT* const y, RealT* xTemp, RealT* yTemp, const int numVert ) { - if (numVert <= 0) - { - SLIC_DEBUG("Vertex2DOrderToCCW: numVert <= 0; returning."); - return; - } + if ( numVert <= 0 ) { + SLIC_DEBUG( "Vertex2DOrderToCCW: numVert <= 0; returning." ); + return; + } - SLIC_ERROR_IF(x == nullptr || y == nullptr || xTemp == nullptr || yTemp == nullptr, - "Vertex2DOrderToCCW: must set pointers prior to call to routine."); + SLIC_ERROR_IF( x == nullptr || y == nullptr || xTemp == nullptr || yTemp == nullptr, + "Vertex2DOrderToCCW: must set pointers prior to call to routine." ); - xTemp[0] = x[0]; - yTemp[0] = y[0]; + xTemp[0] = x[0]; + yTemp[0] = y[0]; int k = 1; - for (int i=numVert; i>0; --i) - { - xTemp[k] = x[i]; - yTemp[k] = y[i]; - ++k; + for ( int i = numVert; i > 0; --i ) { + xTemp[k] = x[i]; + yTemp[k] = y[i]; + ++k; } return; -} // end Vertex2DOrderToCCW() +} // end Vertex2DOrderToCCW() //------------------------------------------------------------------------------ -} // end namespace tribol +} // end namespace tribol diff --git a/src/tribol/geom/GeomUtilities.hpp b/src/tribol/geom/GeomUtilities.hpp index 45d27747..81441680 100644 --- a/src/tribol/geom/GeomUtilities.hpp +++ b/src/tribol/geom/GeomUtilities.hpp @@ -8,14 +8,13 @@ #include "tribol/common/Parameters.hpp" -namespace tribol -{ +namespace tribol { /*! * * \brief Projects a point in 3-space to a plane. * - * General method to project a point to a plane based on point normal data for that + * General method to project a point to a plane based on point normal data for that * plane and the input point in three dimensions. * * \param [in] x coordinate of point to be projected @@ -33,10 +32,9 @@ namespace tribol * \param [in,out] pz z coordinate of projected point * */ -TRIBOL_HOST_DEVICE void ProjectPointToPlane( const RealT x, const RealT y, const RealT z, - const RealT nx, const RealT ny, const RealT nz, - const RealT ox, const RealT oy, const RealT oz, - RealT& px, RealT& py, RealT& pz ); +TRIBOL_HOST_DEVICE void ProjectPointToPlane( const RealT x, const RealT y, const RealT z, const RealT nx, + const RealT ny, const RealT nz, const RealT ox, const RealT oy, + const RealT oz, RealT& px, RealT& py, RealT& pz ); /*! * @@ -53,19 +51,17 @@ TRIBOL_HOST_DEVICE void ProjectPointToPlane( const RealT x, const RealT y, const * \param [in,out] py y coordinate of projected point * */ -TRIBOL_HOST_DEVICE void ProjectPointToSegment( const RealT x, const RealT y, - const RealT nx, const RealT ny, - const RealT ox, const RealT oy, - RealT& px, RealT& py ); +TRIBOL_HOST_DEVICE void ProjectPointToSegment( const RealT x, const RealT y, const RealT nx, const RealT ny, + const RealT ox, const RealT oy, RealT& px, RealT& py ); /*! * - * \brief Method to find the intersection area between two polygons and - * the local y-coordinate of the centroid + * \brief Method to find the intersection area between two polygons and + * the local y-coordinate of the centroid * * \param [in] namax number of vertices in polygon a * \param [in] xa array of local x coordinates of polygon a vertices - * \param [in] ya array of local y coordinates of polygon b vertices + * \param [in] ya array of local y coordinates of polygon b vertices * \param [in] nbmax number of vertices in polygon b * \param [in] xb array of local x coordintes of polygon b * \param [in] yb array of local y coordinates of polygon b @@ -75,23 +71,17 @@ TRIBOL_HOST_DEVICE void ProjectPointToSegment( const RealT x, const RealT y, * \pre length(xa), length(ya) >= namax * \pre length(xb), length(yb) >= nbmax * - * \note method to determine area of overlap of two polygons and local - * centroid y-coordinate. Swap input (xa,ya)->(ya,xa) and (xb,yb)->(yb,xb) + * \note method to determine area of overlap of two polygons and local + * centroid y-coordinate. Swap input (xa,ya)->(ya,xa) and (xb,yb)->(yb,xb) * to get centroid x-coordinate. */ -TRIBOL_HOST_DEVICE void PolyInterYCentroid( const int namax, - const RealT* const xa, - const RealT* const ya, - const int nbmax, - const RealT* const xb, - const RealT* const yb, - const int isym, - RealT & area, - RealT & ycent ); +TRIBOL_HOST_DEVICE void PolyInterYCentroid( const int namax, const RealT* const xa, const RealT* const ya, + const int nbmax, const RealT* const xb, const RealT* const yb, + const int isym, RealT& area, RealT& ycent ); /*! * - * \brief converts a point in a local 2D coordinate system to a + * \brief converts a point in a local 2D coordinate system to a * point in the global 3D coordinate system * * \param [in] xloc local x coordinate in (e1,e2) frame @@ -102,26 +92,24 @@ TRIBOL_HOST_DEVICE void PolyInterYCentroid( const int namax, * \param [in] e2X x component of second local basis vector * \param [in] e2Y y component of second local basis vector * \param [in] e2Z z component of second local basis vector - * \param [in] cX global x coordinate of local basis shift + * \param [in] cX global x coordinate of local basis shift * \param [in] cY global y coordinate of local basis shift * \param [in] cZ global z coordinate of local basis shift * \param [in,out] xg global x coordinate * \param [in,out] yg global y coordinate * \param [in,out] zg global z coordinate * - * \note this is used to convert a point on a plane in a local + * \note this is used to convert a point on a plane in a local * 2D coordinate basis to a point in the 3D global coordinate system * */ -TRIBOL_HOST_DEVICE void Local2DToGlobalCoords( RealT xloc, RealT yloc, - RealT e1X, RealT e1Y, RealT e1Z, - RealT e2X, RealT e2Y, RealT e2Z, - RealT cX, RealT cY, RealT cZ, - RealT& xg, RealT& yg, RealT& zg ); +TRIBOL_HOST_DEVICE void Local2DToGlobalCoords( RealT xloc, RealT yloc, RealT e1X, RealT e1Y, RealT e1Z, RealT e2X, + RealT e2Y, RealT e2Z, RealT cX, RealT cY, RealT cZ, RealT& xg, RealT& yg, + RealT& zg ); /*! * - * \brief converts an array of points in the global coordinate system to a 2D + * \brief converts an array of points in the global coordinate system to a 2D * local basis * * \param [in] pX array of x coordinates of points in global coordinate system @@ -139,28 +127,23 @@ TRIBOL_HOST_DEVICE void Local2DToGlobalCoords( RealT xloc, RealT yloc, * \param [in,out] pLX array of local x coordinates of input points * \param [in,out] pLY array of local y coordinates of input points * - * \pre length(pX) >= size - * \pre length(pY) >= size + * \pre length(pX) >= size + * \pre length(pY) >= size * \pre length(pZ) >= size - * \pre length(pLX) >= size + * \pre length(pLX) >= size * \pre length(pLY) >= size * - * \note this assumes that the point lies in the plane defined by the - * 2D local basis vectors. + * \note this assumes that the point lies in the plane defined by the + * 2D local basis vectors. */ -TRIBOL_HOST_DEVICE void GlobalTo2DLocalCoords( const RealT* const pX, - const RealT* const pY, - const RealT* const pZ, - RealT e1X, RealT e1Y, RealT e1Z, - RealT e2X, RealT e2Y, RealT e2Z, - RealT cX, RealT cY, RealT cZ, - RealT* const pLX, - RealT* const pLY, +TRIBOL_HOST_DEVICE void GlobalTo2DLocalCoords( const RealT* const pX, const RealT* const pY, const RealT* const pZ, + RealT e1X, RealT e1Y, RealT e1Z, RealT e2X, RealT e2Y, RealT e2Z, + RealT cX, RealT cY, RealT cZ, RealT* const pLX, RealT* const pLY, int size ); /*! * - * \brief converts a point in the global coordinate system to a 2D + * \brief converts a point in the global coordinate system to a 2D * local basis * * \param [in] pX x coordinate of point in global coordinate system @@ -178,14 +161,11 @@ TRIBOL_HOST_DEVICE void GlobalTo2DLocalCoords( const RealT* const pX, * \param [in,out] pLX local x coordinate of input point * \param [in,out] pLY local y coordinate of input point * - * \note this assumes that the point lies in the plane defined by the - * 2D local basis vectors. + * \note this assumes that the point lies in the plane defined by the + * 2D local basis vectors. */ -void GlobalTo2DLocalCoords( RealT pX, RealT pY, RealT pZ, - RealT e1X, RealT e1Y, RealT e1Z, - RealT e2X, RealT e2Y, RealT e2Z, - RealT cX, RealT cY, RealT cZ, - RealT& pLX, RealT& pLY ); +void GlobalTo2DLocalCoords( RealT pX, RealT pY, RealT pZ, RealT e1X, RealT e1Y, RealT e1Z, RealT e2X, RealT e2Y, + RealT e2Z, RealT cX, RealT cY, RealT cZ, RealT& pLX, RealT& pLY ); /*! * * \brief computes the vertex averaged centroid of a point set @@ -197,7 +177,7 @@ void GlobalTo2DLocalCoords( RealT pX, RealT pY, RealT pZ, * \param [in,out] cX x coordinate of vertex averaged centroid * \param [in,out] cY y coordinate of vertex averaged centroid * \param [in,out] cZ z coordinate of vertex averaged centroid - * + * * \return true if calculation successful, false if an error occurred * * \pre length(x) >= numVert @@ -205,11 +185,8 @@ void GlobalTo2DLocalCoords( RealT pX, RealT pY, RealT pZ, * \pre length(z) >= numVert * */ -TRIBOL_HOST_DEVICE bool VertexAvgCentroid( const RealT* const x, - const RealT* const y, - const RealT* const z, - const int numVert, - RealT& cX, RealT& cY, RealT& cZ ); +TRIBOL_HOST_DEVICE bool VertexAvgCentroid( const RealT* const x, const RealT* const y, const RealT* const z, + const int numVert, RealT& cX, RealT& cY, RealT& cZ ); /*! * @@ -221,16 +198,14 @@ TRIBOL_HOST_DEVICE bool VertexAvgCentroid( const RealT* const x, * \param [in,out] cX x coordinate of vertex averaged centroid * \param [in,out] cY y coordinate of vertex averaged centroid * \param [in,out] cZ z coordinate of vertex averaged centroid - * + * * \return true if calculation successful, false if an error occurred * * \pre length(x) >= numVert * */ -TRIBOL_HOST_DEVICE bool VertexAvgCentroid( const RealT* const x, - const int dim, - const int numVert, - RealT& cX, RealT& cY, RealT& cZ ); +TRIBOL_HOST_DEVICE bool VertexAvgCentroid( const RealT* const x, const int dim, const int numVert, RealT& cX, RealT& cY, + RealT& cZ ); /*! * @@ -242,16 +217,14 @@ TRIBOL_HOST_DEVICE bool VertexAvgCentroid( const RealT* const x, * \param [in,out] cX x coordinate of vertex averaged centroid * \param [in,out] cY y coordinate of vertex averaged centroid * \param [in,out] cZ z coordinate of vertex averaged centroid - * + * * \return true if calculation successful, false if an error occurred * * \pre length(x) >= numVert * */ -TRIBOL_HOST_DEVICE bool PolyAreaCentroid( const RealT* const x, - const int dim, - const int numVert, - RealT& cX, RealT& cY, RealT& cZ ); +TRIBOL_HOST_DEVICE bool PolyAreaCentroid( const RealT* const x, const int dim, const int numVert, RealT& cX, RealT& cY, + RealT& cZ ); /*! * @@ -266,10 +239,7 @@ TRIBOL_HOST_DEVICE bool PolyAreaCentroid( const RealT* const x, * \pre length(x) >= numVert * */ -void PolyCentroid( const RealT* const x, - const RealT* const y, - const int numVert, - RealT& cX, RealT& cY ); +void PolyCentroid( const RealT* const x, const RealT* const y, const int numVert, RealT& cX, RealT& cY ); /*! * @@ -281,7 +251,7 @@ void PolyCentroid( const RealT* const x, * \param [in] xB array of local x coordinates of polygon B * \param [in] yB array of local y coordinates of polygon B * \param [in] numVertexB number of vertices in polygon B - * \param [in] posTol position tolerance to collapse segment-segment intersection points + * \param [in] posTol position tolerance to collapse segment-segment intersection points * \param [in] lenTol length tolerance to collapse short intersection edges * \param [in,out] polyX array of x coordinates of intersection polygon * \param [in,out] polyY array of y coordinates of intersection polygon @@ -297,18 +267,12 @@ void PolyCentroid( const RealT* const x, * of points for the intersection polygon * */ -TRIBOL_HOST_DEVICE FaceGeomError Intersection2DPolygon( const RealT* const xA, - const RealT* const yA, - const int numVertexA, - const RealT* const xB, - const RealT* const yB, - const int numVertexB, - RealT posTol, RealT lenTol, - RealT* polyX, - RealT* polyY, - int& numPolyVert, RealT& area, - bool orientCheck=true ); - +TRIBOL_HOST_DEVICE FaceGeomError Intersection2DPolygon( const RealT* const xA, const RealT* const yA, + const int numVertexA, const RealT* const xB, + const RealT* const yB, const int numVertexB, RealT posTol, + RealT lenTol, RealT* polyX, RealT* polyY, int& numPolyVert, + RealT& area, bool orientCheck = true ); + /*! * * \brief check to confirm orientation of polygon vertices are counter clockwise (CCW) @@ -316,13 +280,11 @@ TRIBOL_HOST_DEVICE FaceGeomError Intersection2DPolygon( const RealT* const xA, * \param [in] x array of local x coordinates * \param [in] y array of local y coordinates * \param [in] numVertex number of vertices - * + * * \return true if CCW orientation, false otherwise * */ -TRIBOL_HOST_DEVICE bool CheckPolyOrientation( const RealT* const x, - const RealT* const y, - const int numVertex ); +TRIBOL_HOST_DEVICE bool CheckPolyOrientation( const RealT* const x, const RealT* const y, const int numVertex ); /*! * @@ -340,18 +302,16 @@ TRIBOL_HOST_DEVICE bool CheckPolyOrientation( const RealT* const x, * * \pre length(xPoly), length(yPoly) >= numPolyVert * - * \note this routine assumes a star convex polygon. It starts by checking - * which polygon vertex the input point is closest to, defined as the reference - * vertex. Two triangles are then constructed, each using the reference vertex as - * the first point, the second vertex is the vertex belonging to one of two edge - * segments that share the reference vertex, and the third vertex is the input - * vertex averaged centroid. This routine then calls a routine to check if the + * \note this routine assumes a star convex polygon. It starts by checking + * which polygon vertex the input point is closest to, defined as the reference + * vertex. Two triangles are then constructed, each using the reference vertex as + * the first point, the second vertex is the vertex belonging to one of two edge + * segments that share the reference vertex, and the third vertex is the input + * vertex averaged centroid. This routine then calls a routine to check if the * point lies in either of those two triangles. */ -TRIBOL_HOST_DEVICE bool Point2DInFace( const RealT xPoint, const RealT yPoint, - const RealT* const xPoly, - const RealT* const yPoly, - const RealT xC, const RealT yC, +TRIBOL_HOST_DEVICE bool Point2DInFace( const RealT xPoint, const RealT yPoint, const RealT* const xPoly, + const RealT* const yPoly, const RealT xC, const RealT yC, const int numPolyVert ); /*! @@ -367,12 +327,11 @@ TRIBOL_HOST_DEVICE bool Point2DInFace( const RealT xPoint, const RealT yPoint, * * \pre length(xTri), length(yTri) >= 3 * - * \note this routine finds the two barycentric coordinates of the triangle and - * determines if those coordinates are inside or out + * \note this routine finds the two barycentric coordinates of the triangle and + * determines if those coordinates are inside or out * (http://blackpawn.com/texts/pointinpoly/default.html); */ -TRIBOL_HOST_DEVICE bool Point2DInTri( const RealT xp, const RealT yp, - const RealT* const xTri, +TRIBOL_HOST_DEVICE bool Point2DInTri( const RealT xp, const RealT yp, const RealT* const xTri, const RealT* const yTri ); /*! @@ -386,23 +345,19 @@ TRIBOL_HOST_DEVICE bool Point2DInTri( const RealT xp, const RealT yp, * * \note breaks the polygon into triangles and sums the areas of the triangles */ -TRIBOL_HOST_DEVICE RealT Area2DPolygon( const RealT* const x, - const RealT* const y, - const int numPolyVert ); +TRIBOL_HOST_DEVICE RealT Area2DPolygon( const RealT* const x, const RealT* const y, const int numPolyVert ); /*! * \brief computes the area of a triangle given 3D vertex coordinates * - * \param [in] x array of x coordinates of the three vertices + * \param [in] x array of x coordinates of the three vertices * \param [in] y array of y coordinates of the three vertices * \param [in] z array of z coordinates of the three vertices * * \return area of triangle * */ -TRIBOL_HOST_DEVICE RealT Area3DTri( const RealT* const x, - const RealT* const y, - const RealT* const z ); +TRIBOL_HOST_DEVICE RealT Area3DTri( const RealT* const x, const RealT* const y, const RealT* const z ); /*! * @@ -410,40 +365,40 @@ TRIBOL_HOST_DEVICE RealT Area3DTri( const RealT* const x, * polygon-polygon intersection calculation * * \param [in] xA1 local x coordinate of first vertex (A) of segment 1 - * \param [in] yA1 local y coordinate of first vertex (A) of segment 1 + * \param [in] yA1 local y coordinate of first vertex (A) of segment 1 * \param [in] xB1 local x coordinate of second vertex (B) of segment 1 * \param [in] yB1 local y coordinate of second vertex (B) of segment 1 * \param [in] xA2 local x coordinate of first vertex (A) of segment 2 * \param [in] yA2 local y coordinate of first vertex (A) of segment 2 * \param [in] xB2 local x coordinate of second vertex (B) of segment 2 * \param [in] yB2 local y coordinate of second vertex (B) of segment 2 - * \param [in] interior array where each element is set to true if the associated + * \param [in] interior array where each element is set to true if the associated * vertex is interior to the polygon to which the other segment belongs * \param [in,out] x local coordinate of the intersection point * \param [in,out] y local coordinate of the intersection point - * \param [in,out] duplicate true if intersection point is computed as duplicate polygon + * \param [in,out] duplicate true if intersection point is computed as duplicate polygon * intersection point * \param [in] tol length tolerance for collapsing intersection points to interior points * * \return true if the segments intersect at a non-duplicate point, false otherwise * - * \note this routine returns true or false for an intersection point that we are going - * to use to define an overlapping polygon. In this sense, this is a routine specific to - * the tribol problem. If this routine returns false, but the boolean duplicate is true, - * then the x,y coordinates are the coordinates of a segment vertex that is interior - * to one of the polygons that is the solution to the intersection problem. The solution - * may arise due to the the segments intersecting at a vertex tagged as interior to one - * of the polygons, or due to collapsing the true intersection point to an interior - * vertex based on the position tolerance input argument. For intersection points that - * are within the position tolerance to a non-interior segment vertex, nothing is done - * because we want to retain this intersection point. Collapsing intersection points to - * vertices tagged as interior to one of the polygons may render a degenerate overlap + * \note this routine returns true or false for an intersection point that we are going + * to use to define an overlapping polygon. In this sense, this is a routine specific to + * the tribol problem. If this routine returns false, but the boolean duplicate is true, + * then the x,y coordinates are the coordinates of a segment vertex that is interior + * to one of the polygons that is the solution to the intersection problem. The solution + * may arise due to the the segments intersecting at a vertex tagged as interior to one + * of the polygons, or due to collapsing the true intersection point to an interior + * vertex based on the position tolerance input argument. For intersection points that + * are within the position tolerance to a non-interior segment vertex, nothing is done + * because we want to retain this intersection point. Collapsing intersection points to + * vertices tagged as interior to one of the polygons may render a degenerate overlap * polygon and must be checked. */ TRIBOL_HOST_DEVICE bool SegmentIntersection2D( const RealT xA1, const RealT yA1, const RealT xB1, const RealT yB1, const RealT xA2, const RealT yA2, const RealT xB2, const RealT yB2, - const bool* const interior, RealT& x, RealT& y, - bool& duplicate, const RealT tol ); + const bool* const interior, RealT& x, RealT& y, bool& duplicate, + const RealT tol ); /*! * @@ -461,33 +416,31 @@ TRIBOL_HOST_DEVICE bool SegmentIntersection2D( const RealT xA1, const RealT yA1, * * \pre length(x), length(y) >= numPoints * - * /note this routine checks the overlapping polygon segments. We may have two adjacent - * intersection points (vertices derived from two different segment-segment - * intersection calculations) that produce a very small polygon edge. We may want - * to collapse these segments. Multiple collapses may produce a degenerate polygon, - * which needs to be checked. For cases where there is no collapse of segments, then - * xnew and ynew values are set to x and y, respectively, and numNewPoints + * /note this routine checks the overlapping polygon segments. We may have two adjacent + * intersection points (vertices derived from two different segment-segment + * intersection calculations) that produce a very small polygon edge. We may want + * to collapse these segments. Multiple collapses may produce a degenerate polygon, + * which needs to be checked. For cases where there is no collapse of segments, then + * xnew and ynew values are set to x and y, respectively, and numNewPoints * equals numPoints. */ -TRIBOL_HOST_DEVICE FaceGeomError CheckPolySegs( const RealT* const x, const RealT* const y, - const int numPoints, const RealT tol, - RealT* xnew, RealT* ynew, - int& numNewPoints ); +TRIBOL_HOST_DEVICE FaceGeomError CheckPolySegs( const RealT* const x, const RealT* const y, const int numPoints, + const RealT tol, RealT* xnew, RealT* ynew, int& numNewPoints ); /*! * - * \brief reorders a set of unordered vertices associated with a star convex polygon in + * \brief reorders a set of unordered vertices associated with a star convex polygon in * counter clockwise orientation * * \param [in,out] x array of local x vertex coordinates * \param [in,out] y array of local y vertex coordinates * \param [in] numPoints number of vertices - * + * * \return true if calculation successful, false if an error occurred * * \pre length(x), length(y) >= numPoints * - * \note This routine takes the unordered set of vertex coordinates of a star convex + * \note This routine takes the unordered set of vertex coordinates of a star convex * polygon and orders the vertices in counter-clockwise orientation. */ TRIBOL_HOST_DEVICE bool PolyReorder( RealT* const x, RealT* const y, const int numPoints ); @@ -507,7 +460,7 @@ TRIBOL_HOST_DEVICE void ElemReverse( RealT* const x, RealT* const y, const int n /*! * - * \brief reorders, if necessary, an ordered set of polygonal vertices such that the + * \brief reorders, if necessary, an ordered set of polygonal vertices such that the * ordering obeys the right hand rule per the provided normal * * \param [in,out] x array of global x vertex coordinates @@ -521,13 +474,8 @@ TRIBOL_HOST_DEVICE void ElemReverse( RealT* const x, RealT* const y, const int n * \pre length(x), length(y), length(z) >= numPoints * */ -TRIBOL_HOST_DEVICE void PolyReorderWithNormal( RealT* const x, - RealT* const y, - RealT* const z, - const int numPoints, - const RealT nX, - const RealT nY, - const RealT nZ ); +TRIBOL_HOST_DEVICE void PolyReorderWithNormal( RealT* const x, RealT* const y, RealT* const z, const int numPoints, + const RealT nX, const RealT nY, const RealT nZ ); /*! * \brief computes the intersection point between a line and plane @@ -550,15 +498,14 @@ TRIBOL_HOST_DEVICE void PolyReorderWithNormal( RealT* const x, * \param[in,out] inPlane true if segment lies in the plane * */ -TRIBOL_HOST_DEVICE bool LinePlaneIntersection( const RealT xA, const RealT yA, const RealT zA, - const RealT xB, const RealT yB, const RealT zB, - const RealT xP, const RealT yP, const RealT zP, - const RealT nX, const RealT nY, const RealT nZ, - RealT& x, RealT& y, RealT& z, bool& inPlane ); +TRIBOL_HOST_DEVICE bool LinePlaneIntersection( const RealT xA, const RealT yA, const RealT zA, const RealT xB, + const RealT yB, const RealT zB, const RealT xP, const RealT yP, + const RealT zP, const RealT nX, const RealT nY, const RealT nZ, RealT& x, + RealT& y, RealT& z, bool& inPlane ); /*! - * \brief computes the line segment that is the intersection between two - * planes. + * \brief computes the line segment that is the intersection between two + * planes. * * \param[in] x1 x-coordinate of reference point on plane 1 * \param[in] y1 y-coordinate of reference point on plane 1 @@ -576,25 +523,23 @@ TRIBOL_HOST_DEVICE bool LinePlaneIntersection( const RealT xA, const RealT yA, c * \param[in,out] y y-component of point on intersection line * \param[in,out] z z-component of point on intersection line * - * \note the line segment is described by the output point (x,y,z), which - * locates the segment vector, which is n1 x n2, where n1 is the - * unit normal of plane 1 and n2 is the unit normal of plane 2. - * Internal to this routine, this point is the intersection of a third - * plane where a point on this plane is described in terms of a linear - * combination of the two original plane's unit normals. This coordinate - * description is plugged into the equation describing each of the two - * original planes and the two parameters scaling the third plane's - * linear combination are solved for. The three planes intersect at - * this point, which is along the line segment of the line of intersection - * of the first two planes. Where this point lies on the intersection line + * \note the line segment is described by the output point (x,y,z), which + * locates the segment vector, which is n1 x n2, where n1 is the + * unit normal of plane 1 and n2 is the unit normal of plane 2. + * Internal to this routine, this point is the intersection of a third + * plane where a point on this plane is described in terms of a linear + * combination of the two original plane's unit normals. This coordinate + * description is plugged into the equation describing each of the two + * original planes and the two parameters scaling the third plane's + * linear combination are solved for. The three planes intersect at + * this point, which is along the line segment of the line of intersection + * of the first two planes. Where this point lies on the intersection line * segment is controlled by each plane's input reference points. * */ -bool PlanePlaneIntersection( const RealT x1, const RealT y1, const RealT z1, - const RealT x2, const RealT y2, const RealT z2, - const RealT nX1, const RealT nY1, const RealT nZ1, - const RealT nX2, const RealT nY2, const RealT nZ2, - RealT& x, RealT& y, RealT& z ); +bool PlanePlaneIntersection( const RealT x1, const RealT y1, const RealT z1, const RealT x2, const RealT y2, + const RealT z2, const RealT nX1, const RealT nY1, const RealT nZ1, const RealT nX2, + const RealT nY2, const RealT nZ2, RealT& x, RealT& y, RealT& z ); /*! * @@ -602,17 +547,15 @@ bool PlanePlaneIntersection( const RealT x1, const RealT y1, const RealT z1, * * \param [in] x x-component coordinates * \param [in] y y-component coordinates - * \param [in,out] xTemp reordered x-component coordinates + * \param [in,out] xTemp reordered x-component coordinates * \param [in,out] yTemp reordered y-component coordinates * \param [in] numVert number of vertices * * \pre this routine assumes that the original coordinates are in clockwise ordering * */ -void Vertex2DOrderToCCW( const RealT* const x, const RealT* const y, - RealT* xTemp, RealT* yTemp, - const int numVert ); +void Vertex2DOrderToCCW( const RealT* const x, const RealT* const y, RealT* xTemp, RealT* yTemp, const int numVert ); -} +} // namespace tribol #endif /* SRC_GEOM_GEOMUTILITIES_HPP_ */ diff --git a/src/tribol/integ/FE.cpp b/src/tribol/integ/FE.cpp index 4313872a..f4b7063e 100644 --- a/src/tribol/integ/FE.cpp +++ b/src/tribol/integ/FE.cpp @@ -7,536 +7,469 @@ #include "tribol/integ/FE.hpp" #include "tribol/geom/GeomUtilities.hpp" -#include "axom/slic.hpp" +#include "axom/slic.hpp" #include #include -namespace tribol -{ +namespace tribol { TRIBOL_HOST_DEVICE int GetNumFaceNodes( int dim, FaceOrderType order_type ) { - // SRW consider consolidating this to take a tribol topology and - // order for consistency - int numNodes = 0; - switch (order_type) - { - case LINEAR : - numNodes = (dim == 3) ? 4 : 2; // segments and quads - break; - default : + // SRW consider consolidating this to take a tribol topology and + // order for consistency + int numNodes = 0; + switch ( order_type ) { + case LINEAR: + numNodes = ( dim == 3 ) ? 4 : 2; // segments and quads + break; + default: #ifdef TRIBOL_USE_HOST - SLIC_ERROR("GetNumFaceNodes(): order_type not supported."); + SLIC_ERROR( "GetNumFaceNodes(): order_type not supported." ); #endif - break; - } - return numNodes; + break; + } + return numNodes; } -TRIBOL_HOST_DEVICE void GalerkinEval( const RealT* const x, - const RealT pX, const RealT pY, const RealT pZ, - FaceOrderType order_type, BasisEvalType basis_type, - int dim, int galerkinDim, RealT* nodeVals, RealT* galerkinVal ) +TRIBOL_HOST_DEVICE void GalerkinEval( const RealT* const x, const RealT pX, const RealT pY, const RealT pZ, + FaceOrderType order_type, BasisEvalType basis_type, int dim, int galerkinDim, + RealT* nodeVals, RealT* galerkinVal ) { #ifdef TRIBOL_USE_HOST - SLIC_ERROR_IF(x==nullptr, "GalerkinEval(): input pointer, x, is NULL."); - SLIC_ERROR_IF(nodeVals==nullptr, "GalerkinEval(): input pointer, nodeVals, is NULL."); - SLIC_ERROR_IF(galerkinVal==nullptr, "GalerkinEval(): input/output pointer, galerkinVal, is NULL."); - SLIC_ERROR_IF(galerkinDim<1, "GalerkinEval(): scalar approximations not yet supported." ); + SLIC_ERROR_IF( x == nullptr, "GalerkinEval(): input pointer, x, is NULL." ); + SLIC_ERROR_IF( nodeVals == nullptr, "GalerkinEval(): input pointer, nodeVals, is NULL." ); + SLIC_ERROR_IF( galerkinVal == nullptr, "GalerkinEval(): input/output pointer, galerkinVal, is NULL." ); + SLIC_ERROR_IF( galerkinDim < 1, "GalerkinEval(): scalar approximations not yet supported." ); #endif - int numNodes = GetNumFaceNodes( dim, order_type ); - switch (basis_type) - { - case PHYSICAL : - for (int nd=0; nd 2) - { - WachspressBasis( x, pX, pY, pZ, numPoints, vertexId, phi ); - } - else if (numPoints == 2) - { - SegmentBasis( x, pX, pY, vertexId, phi ); - } - else - { + if ( numPoints > 2 ) { + WachspressBasis( x, pX, pY, pZ, numPoints, vertexId, phi ); + } else if ( numPoints == 2 ) { + SegmentBasis( x, pX, pY, vertexId, phi ); + } else { #ifdef TRIBOL_USE_HOST - SLIC_ERROR("EvalBasis: invalid numPoints argument."); + SLIC_ERROR( "EvalBasis: invalid numPoints argument." ); #endif - } - return; + } + return; } //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE void SegmentBasis( const RealT* const x, - const RealT pX, const RealT pY, - const int vertexId, RealT& phi ) +TRIBOL_HOST_DEVICE void SegmentBasis( const RealT* const x, const RealT pX, const RealT pY, const int vertexId, + RealT& phi ) { #ifdef TRIBOL_USE_HOST - // note, vertexId is the index, 0 or 1. - SLIC_ERROR_IF(vertexId != 0 && vertexId != 1, "SegmentBasis: vertexId is " << vertexId << - " but should be 0 or 1."); + // note, vertexId is the index, 0 or 1. + SLIC_ERROR_IF( vertexId != 0 && vertexId != 1, "SegmentBasis: vertexId is " << vertexId << " but should be 0 or 1." ); #endif - const int dim=2; + const int dim = 2; - // compute length of segment - RealT vx = x[dim*1] - x[dim*0]; - RealT vy = x[dim*1+1] - x[dim*0+1]; - RealT lambda = magnitude( vx, vy ); + // compute length of segment + RealT vx = x[dim * 1] - x[dim * 0]; + RealT vy = x[dim * 1 + 1] - x[dim * 0 + 1]; + RealT lambda = magnitude( vx, vy ); - // compute the magnitude of the vector - - RealT wx = pX - x[ dim*vertexId ]; - RealT wy = pY - x[ dim*vertexId+1 ]; + // compute the magnitude of the vector - + RealT wx = pX - x[dim * vertexId]; + RealT wy = pY - x[dim * vertexId + 1]; - RealT magW = magnitude( wx, wy ); + RealT magW = magnitude( wx, wy ); - phi = 1.0 / lambda * (lambda - magW); + phi = 1.0 / lambda * ( lambda - magW ); - if (phi > 1.0 || phi < 0.0) - { - SLIC_DEBUG("SegmentBasis: phi is " << phi << " not between 0. and 1 for vertex " << vertexId << "." ); - SLIC_DEBUG("(x0,y0) and (x1,y1): " << "(" << x[0] << ", " << x[1] << "), " << "(" << x[2] << ", " << x[3] << ")."); - SLIC_DEBUG("(px,py): " << "(" << pX << ", " << pY << ")"); - } + if ( phi > 1.0 || phi < 0.0 ) { + SLIC_DEBUG( "SegmentBasis: phi is " << phi << " not between 0. and 1 for vertex " << vertexId << "." ); + SLIC_DEBUG( "(x0,y0) and (x1,y1): " + << "(" << x[0] << ", " << x[1] << "), " + << "(" << x[2] << ", " << x[3] << ")." ); + SLIC_DEBUG( "(px,py): " + << "(" << pX << ", " << pY << ")" ); + } - return; + return; } //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE void WachspressBasis( const RealT* const x, - const RealT pX, const RealT pY, const RealT pZ, +TRIBOL_HOST_DEVICE void WachspressBasis( const RealT* const x, const RealT pX, const RealT pY, const RealT pZ, const int numPoints, const int vertexId, RealT& phi ) { #ifdef TRIBOL_USE_HOST - SLIC_ERROR_IF(numPoints<3, "WachspressBasis: numPoints < 3."); + SLIC_ERROR_IF( numPoints < 3, "WachspressBasis: numPoints < 3." ); #endif - // first compute the areas of all the triangles formed by the i-1,i,i+1 vertices. - // These consist of all the numerators in the Wachspress formulation - // NOTE: this limits the routine to 4 noded quadrilaterals - constexpr int max_nodes_per_elem = 4; - RealT triVertArea[ max_nodes_per_elem ]; - for (int i=0; i 1.) - { - SLIC_ERROR("Wachspress Basis: phi is not between 0 and 1."); - } + if ( phi <= 0. || phi > 1. ) { + SLIC_ERROR( "Wachspress Basis: phi is not between 0 and 1." ); + } #endif - return; + return; } //------------------------------------------------------------------------------ -void InvIso( const RealT x[3], - const RealT* xA, - const RealT* yA, - const RealT* zA, - const int numNodes, - RealT xi[2] ) +void InvIso( const RealT x[3], const RealT* xA, const RealT* yA, const RealT* zA, const int numNodes, RealT xi[2] ) { - - SLIC_ERROR_IF(numNodes!=4, "InvIso: routine only for 4 node quads."); - - bool convrg = false; - int kmax = 15; - RealT xtol = 1.E-12; - - RealT x_sol[2] = {0., 0.}; - - // derivatives of the Jacobian wrt (xi,eta) - RealT djde_11 = 0.; - RealT djde_x_12 = 0.25 * (xA[0] - xA[1] + xA[2] - xA[3]); - RealT djde_y_12 = 0.25 * (yA[0] - yA[1] + yA[2] - yA[3]); - RealT djde_z_12 = 0.25 * (zA[0] - zA[1] + zA[2] - zA[3]); - RealT djde_22 = 0.; - - // loop over newton iterations - for (int k = 0; k < kmax; ++k) - { - // evaluate Jacobian - RealT j_x_1 = 0.25 * (xA[0] * (1. + x_sol[1]) - xA[1] * - (1. + x_sol[1]) - xA[2] * (1. - x_sol[1]) + - xA[3] * (1. - x_sol[1])); - - RealT j_y_1 = 0.25 * (yA[0] * (1. + x_sol[1]) - yA[1] * - (1. + x_sol[1]) - yA[2] * (1. - x_sol[1]) + - yA[3] * (1. - x_sol[1])); - - RealT j_z_1 = 0.25 * (zA[0] * (1. + x_sol[1]) - zA[1] * - (1. + x_sol[1]) - zA[2] * (1. - x_sol[1]) + - zA[3] * (1. - x_sol[1])); - - RealT j_x_2 = 0.25 * (xA[0] * (1. + x_sol[0]) + xA[1] * - (1. - x_sol[0]) - xA[2] * (1. - x_sol[0]) - - xA[3] * (1. + x_sol[0])); - - RealT j_y_2 = 0.25 * (yA[0] * (1. + x_sol[0]) + yA[1] * - (1. - x_sol[0]) - yA[2] * (1. - x_sol[0]) - - yA[3] * (1. + x_sol[0])); - - RealT j_z_2 = 0.25 * (zA[0] * (1. + x_sol[0]) + zA[1] * - (1. - x_sol[0]) - zA[2] * (1. - x_sol[0]) - - zA[3] * (1. + x_sol[0])); - - // evaluate the residual - RealT f_x = x[0] - 0.25 * ((1. + x_sol[0]) * (1. + x_sol[1]) * xA[0] - + (1. - x_sol[0]) * (1. + x_sol[1]) * xA[1] - + (1. - x_sol[0]) * (1. - x_sol[1]) * xA[2] - + (1. + x_sol[0]) * (1. - x_sol[1]) * xA[3]); - - RealT f_y = x[1] - 0.25 * ((1. + x_sol[0]) * (1. + x_sol[1]) * yA[0] - + (1. - x_sol[0]) * (1. + x_sol[1]) * yA[1] - + (1. - x_sol[0]) * (1. - x_sol[1]) * yA[2] - + (1. + x_sol[0]) * (1. - x_sol[1]) * yA[3]); - - RealT f_z = x[2] - 0.25 * ((1. + x_sol[0]) * (1. + x_sol[1]) * zA[0] - + (1. - x_sol[0]) * (1. + x_sol[1]) * zA[1] - + (1. - x_sol[0]) * (1. - x_sol[1]) * zA[2] - + (1. + x_sol[0]) * (1. - x_sol[1]) * zA[3]); - - // compute J' * J - RealT JTJ_11 = j_x_1 * j_x_1 + j_y_1 * j_y_1 + j_z_1 * j_z_1; - RealT JTJ_12 = j_x_1 * j_x_2 + j_y_1 * j_y_2 + j_z_1 * j_z_2; - //RealT JTJ_21 = JTJ_12; - RealT JTJ_22 = j_x_2 * j_x_2 + j_y_2 * j_y_2 + j_z_2 * j_z_2;; - - // compute J' * F - RealT JTF_1 = j_x_1 * f_x + j_y_1 * f_y + j_z_1 * f_z; - RealT JTF_2 = j_x_2 * f_x + j_y_2 * f_y + j_z_2 * f_z; - - // for first few steps don't do exact Newton. - RealT cm_11 = JTJ_11; //- (djde_11 * f_x + djde_11 * f_y + djde_11 * f_z); - RealT cm_12 = JTJ_12; //- (djde_x_12 * f_x + djde_y_12 * f_y + djde_z_12 * f_z); - RealT cm_21 = cm_12; - RealT cm_22 = JTJ_22; //- (djde_22 * f_x + djde_22 * f_y + djde_22 * f_z); - - // do exact Newton for steps beyond first few - if (k > 2) // set to 2 per mortar method testing - { - cm_11 += - (djde_11 * f_x + djde_11 * f_y + djde_11 * f_z); - cm_12 += - (djde_x_12 * f_x + djde_y_12 * f_y + djde_z_12 * f_z); - cm_21 = cm_12; - cm_22 += - (djde_22 * f_x + djde_22 * f_y + djde_22 * f_z); + SLIC_ERROR_IF( numNodes != 4, "InvIso: routine only for 4 node quads." ); + + bool convrg = false; + int kmax = 15; + RealT xtol = 1.E-12; + + RealT x_sol[2] = { 0., 0. }; + + // derivatives of the Jacobian wrt (xi,eta) + RealT djde_11 = 0.; + RealT djde_x_12 = 0.25 * ( xA[0] - xA[1] + xA[2] - xA[3] ); + RealT djde_y_12 = 0.25 * ( yA[0] - yA[1] + yA[2] - yA[3] ); + RealT djde_z_12 = 0.25 * ( zA[0] - zA[1] + zA[2] - zA[3] ); + RealT djde_22 = 0.; + + // loop over newton iterations + for ( int k = 0; k < kmax; ++k ) { + // evaluate Jacobian + RealT j_x_1 = 0.25 * ( xA[0] * ( 1. + x_sol[1] ) - xA[1] * ( 1. + x_sol[1] ) - xA[2] * ( 1. - x_sol[1] ) + + xA[3] * ( 1. - x_sol[1] ) ); + + RealT j_y_1 = 0.25 * ( yA[0] * ( 1. + x_sol[1] ) - yA[1] * ( 1. + x_sol[1] ) - yA[2] * ( 1. - x_sol[1] ) + + yA[3] * ( 1. - x_sol[1] ) ); + + RealT j_z_1 = 0.25 * ( zA[0] * ( 1. + x_sol[1] ) - zA[1] * ( 1. + x_sol[1] ) - zA[2] * ( 1. - x_sol[1] ) + + zA[3] * ( 1. - x_sol[1] ) ); + + RealT j_x_2 = 0.25 * ( xA[0] * ( 1. + x_sol[0] ) + xA[1] * ( 1. - x_sol[0] ) - xA[2] * ( 1. - x_sol[0] ) - + xA[3] * ( 1. + x_sol[0] ) ); + + RealT j_y_2 = 0.25 * ( yA[0] * ( 1. + x_sol[0] ) + yA[1] * ( 1. - x_sol[0] ) - yA[2] * ( 1. - x_sol[0] ) - + yA[3] * ( 1. + x_sol[0] ) ); + + RealT j_z_2 = 0.25 * ( zA[0] * ( 1. + x_sol[0] ) + zA[1] * ( 1. - x_sol[0] ) - zA[2] * ( 1. - x_sol[0] ) - + zA[3] * ( 1. + x_sol[0] ) ); + + // evaluate the residual + RealT f_x = + x[0] - 0.25 * ( ( 1. + x_sol[0] ) * ( 1. + x_sol[1] ) * xA[0] + ( 1. - x_sol[0] ) * ( 1. + x_sol[1] ) * xA[1] + + ( 1. - x_sol[0] ) * ( 1. - x_sol[1] ) * xA[2] + ( 1. + x_sol[0] ) * ( 1. - x_sol[1] ) * xA[3] ); + + RealT f_y = + x[1] - 0.25 * ( ( 1. + x_sol[0] ) * ( 1. + x_sol[1] ) * yA[0] + ( 1. - x_sol[0] ) * ( 1. + x_sol[1] ) * yA[1] + + ( 1. - x_sol[0] ) * ( 1. - x_sol[1] ) * yA[2] + ( 1. + x_sol[0] ) * ( 1. - x_sol[1] ) * yA[3] ); + + RealT f_z = + x[2] - 0.25 * ( ( 1. + x_sol[0] ) * ( 1. + x_sol[1] ) * zA[0] + ( 1. - x_sol[0] ) * ( 1. + x_sol[1] ) * zA[1] + + ( 1. - x_sol[0] ) * ( 1. - x_sol[1] ) * zA[2] + ( 1. + x_sol[0] ) * ( 1. - x_sol[1] ) * zA[3] ); + + // compute J' * J + RealT JTJ_11 = j_x_1 * j_x_1 + j_y_1 * j_y_1 + j_z_1 * j_z_1; + RealT JTJ_12 = j_x_1 * j_x_2 + j_y_1 * j_y_2 + j_z_1 * j_z_2; + // RealT JTJ_21 = JTJ_12; + RealT JTJ_22 = j_x_2 * j_x_2 + j_y_2 * j_y_2 + j_z_2 * j_z_2; + ; + + // compute J' * F + RealT JTF_1 = j_x_1 * f_x + j_y_1 * f_y + j_z_1 * f_z; + RealT JTF_2 = j_x_2 * f_x + j_y_2 * f_y + j_z_2 * f_z; + + // for first few steps don't do exact Newton. + RealT cm_11 = JTJ_11; //- (djde_11 * f_x + djde_11 * f_y + djde_11 * f_z); + RealT cm_12 = JTJ_12; //- (djde_x_12 * f_x + djde_y_12 * f_y + djde_z_12 * f_z); + RealT cm_21 = cm_12; + RealT cm_22 = JTJ_22; //- (djde_22 * f_x + djde_22 * f_y + djde_22 * f_z); + + // do exact Newton for steps beyond first few + if ( k > 2 ) // set to 2 per mortar method testing + { + cm_11 += -( djde_11 * f_x + djde_11 * f_y + djde_11 * f_z ); + cm_12 += -( djde_x_12 * f_x + djde_y_12 * f_y + djde_z_12 * f_z ); + cm_21 = cm_12; + cm_22 += -( djde_22 * f_x + djde_22 * f_y + djde_22 * f_z ); + } + + RealT detI = 1. / ( cm_11 * cm_22 - cm_12 * cm_21 ); + + RealT cmi_11 = cm_22 * detI; + RealT cmi_22 = cm_11 * detI; + RealT cmi_12 = -cm_12 * detI; + RealT cmi_21 = -cm_21 * detI; + + RealT dxi_1 = cmi_11 * JTF_1 + cmi_12 * JTF_2; + RealT dxi_2 = cmi_21 * JTF_1 + cmi_22 * JTF_2; + + x_sol[0] += dxi_1; + x_sol[1] += dxi_2; + + RealT abs_dxi_1 = std::abs( dxi_1 ); + RealT abs_dxi_2 = std::abs( dxi_2 ); + + if ( abs_dxi_1 <= xtol && abs_dxi_2 <= xtol ) { + convrg = true; + xi[0] = x_sol[0]; + xi[1] = x_sol[1]; + + // check to make sure point is inside isoparametric quad + bool in_quad = true; + if ( std::abs( xi[0] ) > 1. || std::abs( xi[1] ) > 1. ) { + if ( std::abs( xi[0] ) > 1. + 100 * xtol || + std::abs( xi[1] ) > 1. + 100 * xtol ) // should have some tolerance dependent conv tol? + { + in_quad = false; + } else { + xi[0] = std::min( xi[0], 1. ); + xi[1] = std::min( xi[1], 1. ); + xi[0] = std::max( xi[0], -1. ); + xi[1] = std::max( xi[1], -1. ); + } } - RealT detI = 1. / (cm_11 * cm_22 - cm_12 * cm_21); - - RealT cmi_11 = cm_22 * detI; - RealT cmi_22 = cm_11 * detI; - RealT cmi_12 = -cm_12 * detI; - RealT cmi_21 = -cm_21 * detI; - - RealT dxi_1 = cmi_11 * JTF_1 + cmi_12 * JTF_2; - RealT dxi_2 = cmi_21 * JTF_1 + cmi_22 * JTF_2; - - x_sol[0] += dxi_1; - x_sol[1] += dxi_2; - - RealT abs_dxi_1 = std::abs(dxi_1); - RealT abs_dxi_2 = std::abs(dxi_2); - - if (abs_dxi_1 <= xtol && abs_dxi_2 <= xtol) - { - convrg = true; - xi[0] = x_sol[0]; - xi[1] = x_sol[1]; - -// check to make sure point is inside isoparametric quad - bool in_quad = true; - if(std::abs(xi[0]) > 1. || std::abs(xi[1]) > 1.) - { - if(std::abs(xi[0]) > 1.+100*xtol || std::abs(xi[1]) > 1.+100*xtol) // should have some tolerance dependent conv tol? - { - in_quad = false; - } - else - { - xi[0] = std::min(xi[0],1.); - xi[1] = std::min(xi[1],1.); - xi[0] = std::max(xi[0],-1.); - xi[1] = std::max(xi[1],-1.); - } - } - - SLIC_ERROR_IF(!in_quad, "InvIso(): (xi,eta) coordinate does not lie " << - "inside isoparametric quad."); - - return; - } + SLIC_ERROR_IF( !in_quad, "InvIso(): (xi,eta) coordinate does not lie " + << "inside isoparametric quad." ); - } + return; + } + } - SLIC_ERROR_IF(!convrg, "InvIso: Newtons method did not converge."); + SLIC_ERROR_IF( !convrg, "InvIso: Newtons method did not converge." ); - return; + return; } //------------------------------------------------------------------------------ -void FwdMapLinQuad( const RealT xi[2], - RealT xa[4], - RealT ya[4], - RealT za[4], - RealT x[3] ) +void FwdMapLinQuad( const RealT xi[2], RealT xa[4], RealT ya[4], RealT za[4], RealT x[3] ) { - // initialize output array - initRealArray( &x[0], 3, 0. ); - - // obtain shape function evaluations at (xi,eta) - RealT phi[4] = { 0., 0., 0., 0. }; - LinIsoQuadShapeFunc( xi[0], xi[1], 0, phi[0] ); - LinIsoQuadShapeFunc( xi[0], xi[1], 1, phi[1] ); - LinIsoQuadShapeFunc( xi[0], xi[1], 2, phi[2] ); - LinIsoQuadShapeFunc( xi[0], xi[1], 3, phi[3] ); - - for (int j=0; j<4; ++j) - { - x[0] += xa[j] * phi[j]; - x[1] += ya[j] * phi[j]; - x[2] += za[j] * phi[j]; - } - return; + // initialize output array + initRealArray( &x[0], 3, 0. ); + + // obtain shape function evaluations at (xi,eta) + RealT phi[4] = { 0., 0., 0., 0. }; + LinIsoQuadShapeFunc( xi[0], xi[1], 0, phi[0] ); + LinIsoQuadShapeFunc( xi[0], xi[1], 1, phi[1] ); + LinIsoQuadShapeFunc( xi[0], xi[1], 2, phi[2] ); + LinIsoQuadShapeFunc( xi[0], xi[1], 3, phi[3] ); + + for ( int j = 0; j < 4; ++j ) { + x[0] += xa[j] * phi[j]; + x[1] += ya[j] * phi[j]; + x[2] += za[j] * phi[j]; + } + return; } //------------------------------------------------------------------------------ -void FwdMapLinTri( const RealT xi[2], - RealT xa[3], - RealT ya[3], - RealT za[3], - RealT x[3] ) +void FwdMapLinTri( const RealT xi[2], RealT xa[3], RealT ya[3], RealT za[3], RealT x[3] ) { - // initialize output array - initRealArray( &x[0], 3, 0. ); - - // obtain shape function evaluations at (xi,eta) - RealT phi[3] = { 0., 0., 0. }; - LinIsoTriShapeFunc( xi[0], xi[1], 0, phi[0] ); - LinIsoTriShapeFunc( xi[0], xi[1], 1, phi[1] ); - LinIsoTriShapeFunc( xi[0], xi[1], 2, phi[2] ); - - for (int j=0; j<3; ++j) - { - x[0] += xa[j] * phi[j]; - x[1] += ya[j] * phi[j]; - x[2] += za[j] * phi[j]; - } - return; + // initialize output array + initRealArray( &x[0], 3, 0. ); + + // obtain shape function evaluations at (xi,eta) + RealT phi[3] = { 0., 0., 0. }; + LinIsoTriShapeFunc( xi[0], xi[1], 0, phi[0] ); + LinIsoTriShapeFunc( xi[0], xi[1], 1, phi[1] ); + LinIsoTriShapeFunc( xi[0], xi[1], 2, phi[2] ); + + for ( int j = 0; j < 3; ++j ) { + x[0] += xa[j] * phi[j]; + x[1] += ya[j] * phi[j]; + x[2] += za[j] * phi[j]; + } + return; } //------------------------------------------------------------------------------ -void LinIsoTriShapeFunc( const RealT xi, - const RealT eta, - const int a, - RealT& phi ) +void LinIsoTriShapeFunc( const RealT xi, const RealT eta, const int a, RealT& phi ) { - switch (a) - { - case 0: - phi = 1 - xi - eta; - break; - case 1: - phi = xi; - break; - case 2: - phi = eta; - break; - default: - SLIC_ERROR("LinIsoTriShapeFunc: node id is not between 0 and 2."); - break; - } - - return; + switch ( a ) { + case 0: + phi = 1 - xi - eta; + break; + case 1: + phi = xi; + break; + case 2: + phi = eta; + break; + default: + SLIC_ERROR( "LinIsoTriShapeFunc: node id is not between 0 and 2." ); + break; + } + + return; } //------------------------------------------------------------------------------ -void LinIsoQuadShapeFunc( const RealT xi, - const RealT eta, - const int a, - RealT& phi ) +void LinIsoQuadShapeFunc( const RealT xi, const RealT eta, const int a, RealT& phi ) { - RealT xi_node, eta_node; - switch (a) - { - case 0: - xi_node = 1.; - eta_node = 1.; - break; - case 1: - xi_node = -1.; - eta_node = 1.; - break; - case 2: - xi_node = -1.; - eta_node = -1.; - break; - case 3: - xi_node = 1.; - eta_node = -1.; - break; - default: - SLIC_ERROR("LinIsoQuadShapeFunc: node id is not between 0 and 3."); - return; - } - - phi = 0.25 * (1. + xi_node * xi) * ( 1. + eta_node * eta); - - SLIC_ERROR_IF(phi > 1.0 || phi < 0.0, "LinIsoQuadShapeFunc: phi is " << phi << " not between 0. and 1." ); - - return; + RealT xi_node, eta_node; + switch ( a ) { + case 0: + xi_node = 1.; + eta_node = 1.; + break; + case 1: + xi_node = -1.; + eta_node = 1.; + break; + case 2: + xi_node = -1.; + eta_node = -1.; + break; + case 3: + xi_node = 1.; + eta_node = -1.; + break; + default: + SLIC_ERROR( "LinIsoQuadShapeFunc: node id is not between 0 and 3." ); + return; + } + + phi = 0.25 * ( 1. + xi_node * xi ) * ( 1. + eta_node * eta ); + + SLIC_ERROR_IF( phi > 1.0 || phi < 0.0, "LinIsoQuadShapeFunc: phi is " << phi << " not between 0. and 1." ); + + return; } //------------------------------------------------------------------------------ -void DetJQuad( const RealT xi, - const RealT eta, - const RealT* x, - const int dim, - RealT& detJ ) +void DetJQuad( const RealT xi, const RealT eta, const RealT* x, const int dim, RealT& detJ ) { - - RealT J[4] = { 0., 0., 0., 0. }; // column major ordering - - // loop over nodes - for (int a=0; a<4; ++a) - { - // determine (xi,eta) coord of node a - RealT xi_node, eta_node; - switch (a) - { - case 0: - xi_node = 1.; - eta_node = 1.; - break; - case 1: - xi_node = -1.; - eta_node = 1.; - break; - case 2: - xi_node = -1.; - eta_node = -1.; - break; - case 3: - xi_node = 1.; - eta_node = -1.; - break; - } - - // loop over 2D coords - for (int j=0; j<2; ++j) - { - J[0+j] += 0.25 * x[dim*a+j] * xi_node * (1. + eta_node * eta); - J[2+j] += 0.25 * x[dim*a+j] * eta_node * (1. + xi_node * xi); - } - } + RealT J[4] = { 0., 0., 0., 0. }; // column major ordering - detJ = J[0] * J[3] - J[2] * J[1]; - - // this is a hack, but I can't guarantee the correct orientation of the - // overlap vertices with respect to some global notion of up. This - // calculation will calculate the correct absolute value. - detJ = (detJ <= 0) ? -detJ : detJ; - - return; + // loop over nodes + for ( int a = 0; a < 4; ++a ) { + // determine (xi,eta) coord of node a + RealT xi_node, eta_node; + switch ( a ) { + case 0: + xi_node = 1.; + eta_node = 1.; + break; + case 1: + xi_node = -1.; + eta_node = 1.; + break; + case 2: + xi_node = -1.; + eta_node = -1.; + break; + case 3: + xi_node = 1.; + eta_node = -1.; + break; + } + + // loop over 2D coords + for ( int j = 0; j < 2; ++j ) { + J[0 + j] += 0.25 * x[dim * a + j] * xi_node * ( 1. + eta_node * eta ); + J[2 + j] += 0.25 * x[dim * a + j] * eta_node * ( 1. + xi_node * xi ); + } + } + + detJ = J[0] * J[3] - J[2] * J[1]; + + // this is a hack, but I can't guarantee the correct orientation of the + // overlap vertices with respect to some global notion of up. This + // calculation will calculate the correct absolute value. + detJ = ( detJ <= 0 ) ? -detJ : detJ; + + return; } //------------------------------------------------------------------------------ -} // end of namespace "tribol" +} // namespace tribol diff --git a/src/tribol/integ/FE.hpp b/src/tribol/integ/FE.hpp index 10527a8f..8de21e56 100644 --- a/src/tribol/integ/FE.hpp +++ b/src/tribol/integ/FE.hpp @@ -8,12 +8,11 @@ #include "tribol/common/Parameters.hpp" -namespace tribol -{ +namespace tribol { /*! * - * \brief wrapper routine for evaluation of 2D or 3D shape functions on projected + * \brief wrapper routine for evaluation of 2D or 3D shape functions on projected * surface element topologies * * \param [in] x pointer to array of stacked (xyz) coordinates for 2D or 3D surface face/edge vertices @@ -30,14 +29,13 @@ namespace tribol * \pre z is nullptr for 2D * */ -TRIBOL_HOST_DEVICE void GalerkinEval( const RealT* const x, - const RealT pX, const RealT pY, const RealT pZ, - FaceOrderType order_type, BasisEvalType basis_type, - int dim, int galerkinDim, RealT* nodeVals, RealT* galerkinVal ); +TRIBOL_HOST_DEVICE void GalerkinEval( const RealT* const x, const RealT pX, const RealT pY, const RealT pZ, + FaceOrderType order_type, BasisEvalType basis_type, int dim, int galerkinDim, + RealT* nodeVals, RealT* galerkinVal ); /*! * - * \brief wrapper routine for evaluation of 2D or 3D shape functions on projected + * \brief wrapper routine for evaluation of 2D or 3D shape functions on projected * surface element topologies * * \param [in] x pointer to array of stacked (xyz) coordinates for 2D or 3D surface face/edge vertices @@ -51,10 +49,8 @@ TRIBOL_HOST_DEVICE void GalerkinEval( const RealT* const x, * \pre z is nullptr for 2D * */ -TRIBOL_HOST_DEVICE void EvalBasis( const RealT* const x, - const RealT pX, const RealT pY, const RealT pZ, - const int numPoints, const int vertexId, - RealT& phi ); +TRIBOL_HOST_DEVICE void EvalBasis( const RealT* const x, const RealT pX, const RealT pY, const RealT pZ, + const int numPoints, const int vertexId, RealT& phi ); /*! * @@ -73,8 +69,7 @@ TRIBOL_HOST_DEVICE void EvalBasis( const RealT* const x, * \note This is implicitly a 3D routine * */ -TRIBOL_HOST_DEVICE void WachspressBasis( const RealT* const x, - const RealT pX, const RealT pY, const RealT pZ, +TRIBOL_HOST_DEVICE void WachspressBasis( const RealT* const x, const RealT pX, const RealT pY, const RealT pZ, const int numPoints, const int vertexId, RealT& phi ); /*! @@ -90,13 +85,12 @@ TRIBOL_HOST_DEVICE void WachspressBasis( const RealT* const x, * \note This is implicitly a 2D routine * */ -TRIBOL_HOST_DEVICE void SegmentBasis( const RealT* const x, - const RealT pX, const RealT pY, - const int vertexId, RealT& phi ); +TRIBOL_HOST_DEVICE void SegmentBasis( const RealT* const x, const RealT pX, const RealT pY, const int vertexId, + RealT& phi ); /*! * - * \brief performs the inverse isoparametric mapping to obtain a (xi,eta) + * \brief performs the inverse isoparametric mapping to obtain a (xi,eta) * coordinate in parent space associated with a point in physical space * * \param [in] x array of (x,y,z) coordinates of a point in physical space @@ -104,20 +98,15 @@ TRIBOL_HOST_DEVICE void SegmentBasis( const RealT* const x, * \param [in] yA pointer to array of stacked nodal y-coordinates * \param [in] zA pointer to array of stacked nodal z-coordinates * \param [in] numNodes number of nodes for a given finite element - * \param [in,out] xi (xi,eta) coordinates in parent space + * \param [in,out] xi (xi,eta) coordinates in parent space * * \pre xA, yA, and zA are pointer to arrays of length, numNodes * - * \note This routine works in 2D or 3D. In 2D, zA is a nullptr and + * \note This routine works in 2D or 3D. In 2D, zA is a nullptr and * x[2] is equal to 0. * */ -void InvIso( const RealT x[3], - const RealT* xA, - const RealT* yA, - const RealT* zA, - const int numNodes, - RealT xi[2] ); +void InvIso( const RealT x[3], const RealT* xA, const RealT* yA, const RealT* zA, const int numNodes, RealT xi[2] ); /*! * @@ -131,15 +120,11 @@ void InvIso( const RealT x[3], * * */ -void FwdMapLinQuad( const RealT xi[2], - RealT xa[4], - RealT ya[4], - RealT za[4], - RealT x[3] ); +void FwdMapLinQuad( const RealT xi[2], RealT xa[4], RealT ya[4], RealT za[4], RealT x[3] ); /*! * - * \brief performs a foward linear map for a linear, three node triangle + * \brief performs a foward linear map for a linear, three node triangle * * \param [in] xi (xi,eta) point in parent space * \param [in] xa nodal x-coordinates @@ -149,11 +134,7 @@ void FwdMapLinQuad( const RealT xi[2], * * */ -void FwdMapLinTri( const RealT xi[2], - RealT xa[3], - RealT ya[3], - RealT za[3], - RealT x[3] ); +void FwdMapLinTri( const RealT xi[2], RealT xa[3], RealT ya[3], RealT za[3], RealT x[3] ); /*! * @@ -169,10 +150,7 @@ void FwdMapLinTri( const RealT xi[2], * * */ -void LinIsoQuadShapeFunc( const RealT xi, - const RealT eta, - const int a, - RealT& phi ); +void LinIsoQuadShapeFunc( const RealT xi, const RealT eta, const int a, RealT& phi ); /*! * @@ -186,19 +164,16 @@ void LinIsoQuadShapeFunc( const RealT xi, * * \pre input argument, a, ranges from 0-2. * - * \note this routine uses shape functions generated from - * collapsing a four node quadrilateral. The parent coordinates + * \note this routine uses shape functions generated from + * collapsing a four node quadrilateral. The parent coordinates * of each node are as follows (-1,-1), (1,-1), (0,1). * */ -void LinIsoTriShapeFunc( const RealT xi, - const RealT eta, - const int a, - RealT& phi ); +void LinIsoTriShapeFunc( const RealT xi, const RealT eta, const int a, RealT& phi ); /*! * - * \brief returns the determinant of the Jacobian for a four node + * \brief returns the determinant of the Jacobian for a four node * quadrilateral * * \param [in] xi first parent coordinate of evaluation point @@ -207,19 +182,15 @@ void LinIsoTriShapeFunc( const RealT xi, * \param [in] dim dimension of the coordinate data * \param [in,out] detJ determinant of the Jacobian of transformation * - * \note The input argument x may be stacked 2D or 3D coordinates, + * \note The input argument x may be stacked 2D or 3D coordinates, * indicated by dim, respectively. - * This routine ignores the z-dimension, assuming that the - * four node quad is planar, which is the case for contact + * This routine ignores the z-dimension, assuming that the + * four node quad is planar, which is the case for contact * integrals * */ -void DetJQuad( const RealT xi, - const RealT eta, - const RealT* x, - const int dim, - RealT& detJ ); +void DetJQuad( const RealT xi, const RealT eta, const RealT* x, const int dim, RealT& detJ ); -} // end of namespace "tribol" +} // namespace tribol #endif /* SRC_INTEG_FE_HPP_ */ diff --git a/src/tribol/integ/Integration.cpp b/src/tribol/integ/Integration.cpp index 66530f1d..fb4efe3b 100644 --- a/src/tribol/integ/Integration.cpp +++ b/src/tribol/integ/Integration.cpp @@ -11,762 +11,700 @@ #include "tribol/geom/GeomUtilities.hpp" // axom includes -#include "axom/slic.hpp" +#include "axom/slic.hpp" // C++ includes #include -namespace tribol +namespace tribol { + +template <> +TRIBOL_HOST_DEVICE void EvalWeakFormIntegral( SurfaceContactElem const& elem, + RealT* const integ1, RealT* const integ2 ) { + // compute the area centroid of the overlap polygon, + // or vertex avg. centroid of the overlap segment, which + // serves as the single integration point + RealT cx[3] = { 0., 0., 0. }; + if ( elem.dim == 2 ) { + VertexAvgCentroid( elem.overlapCoords, elem.dim, elem.numPolyVert, cx[0], cx[1], cx[2] ); + } else { + PolyAreaCentroid( elem.overlapCoords, elem.dim, elem.numPolyVert, cx[0], cx[1], cx[2] ); + } + + // debug: leave commented out so we don't enter loop + { + // SLIC_DEBUG("Integration point: " << cx[0] << ", " << cx[1]); + + // SLIC_DEBUG("Overlap area: " << elem.overlapArea); + // SLIC_DEBUG("Overlap coords: "); + // for (int i=0; inumberOfNodesPerElement(); ++i ) { + const int nodeId1 = elem.m_mesh1->getGlobalNodeId( elem.faceId1, i ); + ProjectPointToPlane( elem.m_mesh1->getPosition()[0][nodeId1], elem.m_mesh1->getPosition()[1][nodeId1], + elem.m_mesh1->getPosition()[2][nodeId1], elem.overlapNormal[0], elem.overlapNormal[1], + elem.overlapNormal[2], cx[0], cx[1], cx[2], projX1[elem.dim * i], projX1[elem.dim * i + 1], + projX1[elem.dim * i + 2] ); + + SLIC_DEBUG( "face 1 projected vertex " << i << ": " << elem.m_mesh1->getPosition()[0][nodeId1] << ", " + << elem.m_mesh1->getPosition()[1][nodeId1] + << elem.m_mesh1->getPosition()[2][nodeId1] ); + + const int nodeId2 = elem.m_mesh2->getGlobalNodeId( elem.faceId2, i ); + ProjectPointToPlane( elem.m_mesh2->getPosition()[0][nodeId2], elem.m_mesh2->getPosition()[1][nodeId2], + elem.m_mesh2->getPosition()[2][nodeId2], elem.overlapNormal[0], elem.overlapNormal[1], + elem.overlapNormal[2], cx[0], cx[1], cx[2], projX2[elem.dim * i], projX2[elem.dim * i + 1], + projX2[elem.dim * i + 2] ); + + SLIC_DEBUG( "face 2 projected vertex " << i << ": " << elem.m_mesh2->getPosition()[0][nodeId2] << ", " + << elem.m_mesh2->getPosition()[1][nodeId2] + << elem.m_mesh2->getPosition()[2][nodeId2] ); + } + } else { + // loop over number of nodes per edge (same for each mesh) and project nodes to common plane. + // Can use the integration point as the point in the point-normal data. + for ( int i = 0; i < elem.m_mesh1->numberOfNodesPerElement(); ++i ) { + const int nodeId1 = elem.m_mesh1->getGlobalNodeId( elem.faceId1, i ); + + ProjectPointToSegment( elem.m_mesh1->getPosition()[0][nodeId1], elem.m_mesh1->getPosition()[1][nodeId1], + elem.overlapNormal[0], elem.overlapNormal[1], cx[0], cx[1], projX1[elem.dim * i], + projX1[elem.dim * i + 1] ); + + SLIC_DEBUG( "edge 1 projected vertex " << i << ": " << elem.m_mesh1->getPosition()[0][nodeId1] << ", " + << elem.m_mesh1->getPosition()[1][nodeId1] ); + + const int nodeId2 = elem.m_mesh2->getGlobalNodeId( elem.faceId2, i ); + ProjectPointToSegment( elem.m_mesh2->getPosition()[0][nodeId2], elem.m_mesh2->getPosition()[1][nodeId2], + elem.overlapNormal[0], elem.overlapNormal[1], cx[0], cx[1], projX2[elem.dim * i], + projX2[elem.dim * i + 1] ); + + SLIC_DEBUG( "edge 2 projected vertex " << i << ": " << elem.m_mesh2->getPosition()[0][nodeId2] << ", " + << elem.m_mesh2->getPosition()[1][nodeId2] ); + } + } + + // loop over nodes and compute nodal force integral + // contributions + for ( int a = 0; a < elem.numFaceVert; ++a ) { + EvalBasis( &projX1[0], cx[0], cx[1], cx[2], elem.numFaceVert, a, integ1[a] ); + EvalBasis( &projX2[0], cx[0], cx[1], cx[2], elem.numFaceVert, a, integ2[a] ); + } + + return; +} -template< > -TRIBOL_HOST_DEVICE void EvalWeakFormIntegral< COMMON_PLANE, SINGLE_POINT > - ( SurfaceContactElem const & elem, - RealT * const integ1, - RealT * const integ2 ) +//------------------------------------------------------------------------------ +void TWBPolyInt( SurfaceContactElem const& elem, IntegPts& integ, int k ) { - // compute the area centroid of the overlap polygon, - // or vertex avg. centroid of the overlap segment, which - // serves as the single integration point - RealT cx[3] = {0., 0., 0.}; - if (elem.dim == 2) - { - VertexAvgCentroid( elem.overlapCoords, elem.dim, elem.numPolyVert, - cx[0], cx[1], cx[2] ); - } else { - PolyAreaCentroid( elem.overlapCoords, elem.dim, elem.numPolyVert, - cx[0], cx[1], cx[2] ); - } - - // debug: leave commented out so we don't enter loop - { - //SLIC_DEBUG("Integration point: " << cx[0] << ", " << cx[1]); - - //SLIC_DEBUG("Overlap area: " << elem.overlapArea); - //SLIC_DEBUG("Overlap coords: "); - //for (int i=0; inumberOfNodesPerElement(); ++i) - { - const int nodeId1 = elem.m_mesh1->getGlobalNodeId(elem.faceId1, i); - ProjectPointToPlane( elem.m_mesh1->getPosition()[0][nodeId1], elem.m_mesh1->getPosition()[1][nodeId1], - elem.m_mesh1->getPosition()[2][nodeId1], elem.overlapNormal[0], elem.overlapNormal[1], - elem.overlapNormal[2], cx[0], cx[1], cx[2], projX1[elem.dim*i], projX1[elem.dim*i+1], - projX1[elem.dim*i+2] ); - - SLIC_DEBUG("face 1 projected vertex " << i << ": " << elem.m_mesh1->getPosition()[0][nodeId1] << ", " << elem.m_mesh1->getPosition()[1][nodeId1] << - elem.m_mesh1->getPosition()[2][nodeId1]); - - const int nodeId2 = elem.m_mesh2->getGlobalNodeId(elem.faceId2, i); - ProjectPointToPlane( elem.m_mesh2->getPosition()[0][nodeId2], elem.m_mesh2->getPosition()[1][nodeId2], - elem.m_mesh2->getPosition()[2][nodeId2], elem.overlapNormal[0], elem.overlapNormal[1], - elem.overlapNormal[2], cx[0], cx[1], cx[2], projX2[elem.dim*i], projX2[elem.dim*i+1], - projX2[elem.dim*i+2] ); - - SLIC_DEBUG("face 2 projected vertex " << i << ": " << elem.m_mesh2->getPosition()[0][nodeId2] << ", " << elem.m_mesh2->getPosition()[1][nodeId2] << - elem.m_mesh2->getPosition()[2][nodeId2]); + // check that the order, k, is either 2 or 3 + if ( k != 2 && k != 3 ) { + SLIC_ERROR( "TWBPolyInt: input argument, k, must be 2 or 3." ); + return; + } + + // determine number of TWB integration points for current overlap + int numTotalPoints, numTriPoints; + numTotalPoints = NumTWBPointsPoly( elem, k ); + numTriPoints = NumTWBPointsPerTri( k ); + + integ.initialize( 3, numTotalPoints ); + + // declare local array to hold barycentric coordinates for each + // triangle + RealT bary[elem.dim * numTriPoints]; + + switch ( k ) { + case 2: + + for ( int i = 0; i < numTotalPoints; ++i ) { + integ.wts[i] = 0.6666666666667; } - } - else - { - // loop over number of nodes per edge (same for each mesh) and project nodes to common plane. - // Can use the integration point as the point in the point-normal data. - for (int i=0; inumberOfNodesPerElement(); ++i) - { - const int nodeId1 = elem.m_mesh1->getGlobalNodeId(elem.faceId1, i); - - ProjectPointToSegment( elem.m_mesh1->getPosition()[0][nodeId1], elem.m_mesh1->getPosition()[1][nodeId1], - elem.overlapNormal[0], elem.overlapNormal[1], cx[0], cx[1], - projX1[elem.dim*i], projX1[elem.dim*i+1] ); - - SLIC_DEBUG("edge 1 projected vertex " << i << ": " << elem.m_mesh1->getPosition()[0][nodeId1] << ", " << elem.m_mesh1->getPosition()[1][nodeId1] ); - - const int nodeId2 = elem.m_mesh2->getGlobalNodeId(elem.faceId2, i); - ProjectPointToSegment( elem.m_mesh2->getPosition()[0][nodeId2], elem.m_mesh2->getPosition()[1][nodeId2], - elem.overlapNormal[0], elem.overlapNormal[1], cx[0], cx[1], - projX2[elem.dim*i], projX2[elem.dim*i+1] ); - - SLIC_DEBUG("edge 2 projected vertex " << i << ": " << elem.m_mesh2->getPosition()[0][nodeId2] << ", " << elem.m_mesh2->getPosition()[1][nodeId2] ); + + // first barycentric point + bary[0] = 0.1666666666667; + bary[1] = 0.6666666666667; + bary[2] = 1. - bary[0] - bary[1]; + // second barycentric point + bary[3] = 0.6666666666667; + bary[4] = 0.1666666666667; + bary[5] = 1. - bary[3] - bary[4]; + // third barycentric point + bary[6] = 0.1666666666667; + bary[7] = 0.1666666666667; + bary[8] = 1. - bary[6] - bary[7]; + break; + + case 3: + + // populate first three wts for first triangle + for ( int i = 0; i < 3; ++i ) { + integ.wts[i] = 0.2199034873106; } - } - // loop over nodes and compute nodal force integral - // contributions - for (int a=0; aipDim = dim; + this->numIPs = numTotalIPs; + if ( this->xy == nullptr ) { + this->xy = new RealT[dim * numTotalIPs]; + } else { + delete[] this->xy; + this->xy = new RealT[dim * numTotalIPs]; } + if ( this->wts == nullptr ) { + this->wts = new RealT[numTotalIPs]; + } else { + delete[] this->wts; + this->wts = new RealT[numTotalIPs]; + } + } - /// Initialization function - void initialize( int const dim, int const numTotalIPs ) - { - this->ipDim = dim; - this->numIPs = numTotalIPs; - if (this->xy == nullptr) - { - this->xy = new RealT [dim * numTotalIPs]; - } - else - { - delete [] this->xy; - this->xy = new RealT [dim * numTotalIPs]; - } - if (this->wts == nullptr) - { - this->wts = new RealT [numTotalIPs]; - } - else - { - delete [] this->wts; - this->wts = new RealT [numTotalIPs]; - } - } - - // member variables - int numIPs; ///< number of integration points on entire overlap - int ipDim; ///< coordinate dimension of the integration points - RealT* xy; ///< coordinates of ALL integration points - RealT* wts; ///< integration point weights + // member variables + int numIPs; ///< number of integration points on entire overlap + int ipDim; ///< coordinate dimension of the integration points + RealT* xy; ///< coordinates of ALL integration points + RealT* wts; ///< integration point weights }; /*! * - * \brief Templated function with explicit specialization evaluating the - * weak form contact integral, typically involving the integration - * of shape functions or product of shape functions over contact + * \brief Templated function with explicit specialization evaluating the + * weak form contact integral, typically involving the integration + * of shape functions or product of shape functions over contact * overlap patches for surface-to-surface contact methods. * - * \param [in] elem surface contact element struct + * \param [in] elem surface contact element struct * \param [out] integ1 scalar integral evaluation for face 1 at node nodeEvalId * \param [out] integ2 scalar integral evaluation for face 2 at node nodeEvalId * * \pre The local node id, nodeEvalId, ranges from 0-3 for a four node quad face. * */ -template< ContactMethod M, PolyInteg I > -TRIBOL_HOST_DEVICE void EvalWeakFormIntegral( SurfaceContactElem const & elem, - RealT * const integ1, - RealT * const integ2 ); - +template +TRIBOL_HOST_DEVICE void EvalWeakFormIntegral( SurfaceContactElem const& elem, RealT* const integ1, + RealT* const integ2 ); + /*! * * \brief Populates the integration points and weights on the IntegPts object - * for all integration points per Taylor-Wingate-Bos integration rule + * for all integration points per Taylor-Wingate-Bos integration rule * of order k. - * - * \note Integration per M. Taylor, B. Wingate, L. Bos. Several new quadrature - * formulas for polynomial integration in the triangle. + * + * \note Integration per M. Taylor, B. Wingate, L. Bos. Several new quadrature + * formulas for polynomial integration in the triangle. * arXiv:math/0501496, 2007. * * \param [in] elem SurfaceContactElem object containing dimension and overlap vertices @@ -113,55 +102,49 @@ TRIBOL_HOST_DEVICE void EvalWeakFormIntegral( SurfaceContactElem const & elem, * \param [in] k order of TWB integration * * \pre order 2 <= k <= 3 - * \pre integ IntegPts object can be instantiated with no-op constructor. This routine + * \pre integ IntegPts object can be instantiated with no-op constructor. This routine * will allocate and populate necessary data. * */ -void TWBPolyInt( SurfaceContactElem const & elem, - IntegPts & integ, - int k ); +void TWBPolyInt( SurfaceContactElem const& elem, IntegPts& integ, int k ); /*! * * \brief Populates the integration points and weights on the IntegPts object - * for all integration points per symmetric Gauss integration rule + * for all integration points per symmetric Gauss integration rule * of order k on triangles - * + * * \param [in] elem SurfaceContactElem object containing dimension and overlap vertices * \param [in,out] integ IntegPts object holding integration points and weights * \param [in] k order of integration * * \pre order 2 <= k <= 3 - * \pre integ IntegPts object can be instantiated with no-op constructor. This routine + * \pre integ IntegPts object can be instantiated with no-op constructor. This routine * will allocate and populate necessary data. * */ -void GaussPolyIntTri( SurfaceContactElem const & elem, - IntegPts & integ, - int k ); +void GaussPolyIntTri( SurfaceContactElem const& elem, IntegPts& integ, int k ); /*! * * \brief Populates the integration points and weights on the IntegPts object - * for all integration points per symmetric Gauss integration rule - * of order k on quadrilaterals - * + * for all integration points per symmetric Gauss integration rule + * of order k on quadrilaterals + * * \param [in] elem SurfaceContactElem object containing dimension and overlap vertices * \param [in,out] integ IntegPts object holding integration points and weights * \param [in] k order of integration * * \pre order 2 <= k <= 3 - * \pre integ IntegPts object can be instantiated with no-op constructor. This routine + * \pre integ IntegPts object can be instantiated with no-op constructor. This routine * will allocate and populate necessary data. * */ -void GaussPolyIntQuad( SurfaceContactElem const & elem, - IntegPts & integ, - int k ); +void GaussPolyIntQuad( SurfaceContactElem const& elem, IntegPts& integ, int k ); /*! * - * \brief returns the number of TWB integration points for polygonal overlap + * \brief returns the number of TWB integration points for polygonal overlap * for integration rule of order k * * \param [in] elem SurfaceContactElem object containing dimension and overlap vertices @@ -170,12 +153,11 @@ void GaussPolyIntQuad( SurfaceContactElem const & elem, * \pre order 2 <= k <= 3 * */ -int NumTWBPointsPoly( SurfaceContactElem const & elem, - int k ); +int NumTWBPointsPoly( SurfaceContactElem const& elem, int k ); /*! * - * \brief returns the number of TWB integration points on a triangle per + * \brief returns the number of TWB integration points on a triangle per * the integration rule of order k * * \param [in] order order of polynomial that TWB integration rule will exactly integrate @@ -185,5 +167,5 @@ int NumTWBPointsPoly( SurfaceContactElem const & elem, */ int NumTWBPointsPerTri( int order ); -} // end namespace tribol +} // end namespace tribol #endif /* SRC_INTEG_INTEGRATION_HPP_ */ diff --git a/src/tribol/interface/c_fortran/wrapTRIBOL_SIMPLE.cpp b/src/tribol/interface/c_fortran/wrapTRIBOL_SIMPLE.cpp index ad11c155..aa401f12 100644 --- a/src/tribol/interface/c_fortran/wrapTRIBOL_SIMPLE.cpp +++ b/src/tribol/interface/c_fortran/wrapTRIBOL_SIMPLE.cpp @@ -19,101 +19,112 @@ extern "C" { // splicer begin C_definitions // splicer end C_definitions -int TRIBOL_SIMPLE_initialize_0(const int dim) +int TRIBOL_SIMPLE_initialize_0( const int dim ) { - // splicer begin function.initialize_0 - int SHC_rv = Initialize(dim); - return SHC_rv; - // splicer end function.initialize_0 + // splicer begin function.initialize_0 + int SHC_rv = Initialize( dim ); + return SHC_rv; + // splicer end function.initialize_0 } -int TRIBOL_SIMPLE_initialize_1(const int dim, bool init_slic) +int TRIBOL_SIMPLE_initialize_1( const int dim, bool init_slic ) { - // splicer begin function.initialize_1 - int SHC_rv = Initialize(dim, init_slic); - return SHC_rv; - // splicer end function.initialize_1 + // splicer begin function.initialize_1 + int SHC_rv = Initialize( dim, init_slic ); + return SHC_rv; + // splicer end function.initialize_1 } -int TRIBOL_SIMPLE_finalize_0(void) +int TRIBOL_SIMPLE_finalize_0( void ) { - // splicer begin function.finalize_0 - int SHC_rv = Finalize(); - return SHC_rv; - // splicer end function.finalize_0 + // splicer begin function.finalize_0 + int SHC_rv = Finalize(); + return SHC_rv; + // splicer end function.finalize_0 } -int TRIBOL_SIMPLE_finalize_1(bool finalize_slic) +int TRIBOL_SIMPLE_finalize_1( bool finalize_slic ) { - // splicer begin function.finalize_1 - int SHC_rv = Finalize(finalize_slic); - return SHC_rv; - // splicer end function.finalize_1 + // splicer begin function.finalize_1 + int SHC_rv = Finalize( finalize_slic ); + return SHC_rv; + // splicer end function.finalize_1 } -void TRIBOL_SIMPLE_simple_coupling_setup(const int dim, int cell_type, int contact_method, int mortar_numCells, int mortar_lengthNodalData, const int * mortar_connectivity, const double * mortar_x, const double * mortar_y, const double * mortar_z, int nonmortar_numCells, int nonmortar_lengthNodalData, const int * nonmortar_connectivity, const double * nonmortar_x, const double * nonmortar_y, const double * nonmortar_z, const double area_frac, double * mortar_gaps, double * mortar_pressures) +void TRIBOL_SIMPLE_simple_coupling_setup( const int dim, int cell_type, int contact_method, int mortar_numCells, + int mortar_lengthNodalData, const int* mortar_connectivity, + const double* mortar_x, const double* mortar_y, const double* mortar_z, + int nonmortar_numCells, int nonmortar_lengthNodalData, + const int* nonmortar_connectivity, const double* nonmortar_x, + const double* nonmortar_y, const double* nonmortar_z, const double area_frac, + double* mortar_gaps, double* mortar_pressures ) { - // splicer begin function.simple_coupling_setup - SimpleCouplingSetup(dim, cell_type, contact_method, mortar_numCells, mortar_lengthNodalData, mortar_connectivity, mortar_x, mortar_y, mortar_z, nonmortar_numCells, nonmortar_lengthNodalData, nonmortar_connectivity, nonmortar_x, nonmortar_y, nonmortar_z, area_frac, mortar_gaps, mortar_pressures); - // splicer end function.simple_coupling_setup + // splicer begin function.simple_coupling_setup + SimpleCouplingSetup( dim, cell_type, contact_method, mortar_numCells, mortar_lengthNodalData, mortar_connectivity, + mortar_x, mortar_y, mortar_z, nonmortar_numCells, nonmortar_lengthNodalData, + nonmortar_connectivity, nonmortar_x, nonmortar_y, nonmortar_z, area_frac, mortar_gaps, + mortar_pressures ); + // splicer end function.simple_coupling_setup } -int TRIBOL_SIMPLE_update(double dt) +int TRIBOL_SIMPLE_update( double dt ) { - // splicer begin function.update - int SHC_rv = Update(dt); - return SHC_rv; - // splicer end function.update + // splicer begin function.update + int SHC_rv = Update( dt ); + return SHC_rv; + // splicer end function.update } -int TRIBOL_SIMPLE_get_simple_coupling_csr(int * * I, int * * J, double * * vals, int * n_offsets, int * n_nonzeros) +int TRIBOL_SIMPLE_get_simple_coupling_csr( int** I, int** J, double** vals, int* n_offsets, int* n_nonzeros ) { - // splicer begin function.get_simple_coupling_csr - int SHC_rv = GetSimpleCouplingCSR(I, J, vals, n_offsets, n_nonzeros); - return SHC_rv; - // splicer end function.get_simple_coupling_csr + // splicer begin function.get_simple_coupling_csr + int SHC_rv = GetSimpleCouplingCSR( I, J, vals, n_offsets, n_nonzeros ); + return SHC_rv; + // splicer end function.get_simple_coupling_csr } -int TRIBOL_SIMPLE_get_simple_coupling_csr_bufferify(TRIBOL_SIMPLE_SHROUD_array *DI, TRIBOL_SIMPLE_SHROUD_array *DJ, TRIBOL_SIMPLE_SHROUD_array *Dvals, int * n_offsets, int * n_nonzeros) +int TRIBOL_SIMPLE_get_simple_coupling_csr_bufferify( TRIBOL_SIMPLE_SHROUD_array* DI, TRIBOL_SIMPLE_SHROUD_array* DJ, + TRIBOL_SIMPLE_SHROUD_array* Dvals, int* n_offsets, + int* n_nonzeros ) { - // splicer begin function.get_simple_coupling_csr_bufferify - int *I; - int *J; - double *vals; - int SHC_rv = GetSimpleCouplingCSR(&I, &J, &vals, n_offsets, n_nonzeros); - DI->cxx.addr = I; - DI->cxx.idtor = 0; - DI->addr.base = I; - DI->type = SH_TYPE_INT; - DI->elem_len = sizeof(int); - DI->rank = 1; - DI->shape[0] = *n_offsets; - DI->size = DI->shape[0]; - DJ->cxx.addr = J; - DJ->cxx.idtor = 0; - DJ->addr.base = J; - DJ->type = SH_TYPE_INT; - DJ->elem_len = sizeof(int); - DJ->rank = 1; - DJ->shape[0] = *n_nonzeros; - DJ->size = DJ->shape[0]; - Dvals->cxx.addr = vals; - Dvals->cxx.idtor = 0; - Dvals->addr.base = vals; - Dvals->type = SH_TYPE_DOUBLE; - Dvals->elem_len = sizeof(double); - Dvals->rank = 1; - Dvals->shape[0] = *n_nonzeros; - Dvals->size = Dvals->shape[0]; - return SHC_rv; - // splicer end function.get_simple_coupling_csr_bufferify + // splicer begin function.get_simple_coupling_csr_bufferify + int* I; + int* J; + double* vals; + int SHC_rv = GetSimpleCouplingCSR( &I, &J, &vals, n_offsets, n_nonzeros ); + DI->cxx.addr = I; + DI->cxx.idtor = 0; + DI->addr.base = I; + DI->type = SH_TYPE_INT; + DI->elem_len = sizeof( int ); + DI->rank = 1; + DI->shape[0] = *n_offsets; + DI->size = DI->shape[0]; + DJ->cxx.addr = J; + DJ->cxx.idtor = 0; + DJ->addr.base = J; + DJ->type = SH_TYPE_INT; + DJ->elem_len = sizeof( int ); + DJ->rank = 1; + DJ->shape[0] = *n_nonzeros; + DJ->size = DJ->shape[0]; + Dvals->cxx.addr = vals; + Dvals->cxx.idtor = 0; + Dvals->addr.base = vals; + Dvals->type = SH_TYPE_DOUBLE; + Dvals->elem_len = sizeof( double ); + Dvals->rank = 1; + Dvals->shape[0] = *n_nonzeros; + Dvals->size = Dvals->shape[0]; + return SHC_rv; + // splicer end function.get_simple_coupling_csr_bufferify } // Release library allocated memory. -void TRIBOL_SIMPLE_SHROUD_memory_destructor(TRIBOL_SIMPLE_SHROUD_capsule_data *cap) +void TRIBOL_SIMPLE_SHROUD_memory_destructor( TRIBOL_SIMPLE_SHROUD_capsule_data* cap ) { - cap->addr = nullptr; - cap->idtor = 0; // avoid deleting again + cap->addr = nullptr; + cap->idtor = 0; // avoid deleting again } } // extern "C" diff --git a/src/tribol/interface/c_fortran/wrapTRIBOL_TEST_MESH.cpp b/src/tribol/interface/c_fortran/wrapTRIBOL_TEST_MESH.cpp index 4a08fb2e..d4611b53 100644 --- a/src/tribol/interface/c_fortran/wrapTRIBOL_TEST_MESH.cpp +++ b/src/tribol/interface/c_fortran/wrapTRIBOL_TEST_MESH.cpp @@ -19,29 +19,28 @@ extern "C" { // splicer end C_definitions // Release library allocated memory. -void TRIBOL_TEST_MESH_SHROUD_memory_destructor(TRIBOL_TEST_MESH_SHROUD_capsule_data *cap) +void TRIBOL_TEST_MESH_SHROUD_memory_destructor( TRIBOL_TEST_MESH_SHROUD_capsule_data* cap ) { - void *ptr = cap->addr; - switch (cap->idtor) { - case 0: // --none-- + void* ptr = cap->addr; + switch ( cap->idtor ) { + case 0: // --none-- { - // Nothing to delete - break; + // Nothing to delete + break; } - case 1: // tribol::TestMesh + case 1: // tribol::TestMesh { - tribol::TestMesh *cxx_ptr = reinterpret_cast(ptr); - delete cxx_ptr; - break; - } - default: - { - // Unexpected case in destructor - break; + tribol::TestMesh* cxx_ptr = reinterpret_cast( ptr ); + delete cxx_ptr; + break; } + default: { + // Unexpected case in destructor + break; } - cap->addr = nullptr; - cap->idtor = 0; // avoid deleting again + } + cap->addr = nullptr; + cap->idtor = 0; // avoid deleting again } } // extern "C" diff --git a/src/tribol/interface/c_fortran/wrapTestMesh.cpp b/src/tribol/interface/c_fortran/wrapTestMesh.cpp index d7a9fc4a..b0650a22 100644 --- a/src/tribol/interface/c_fortran/wrapTestMesh.cpp +++ b/src/tribol/interface/c_fortran/wrapTestMesh.cpp @@ -18,203 +18,215 @@ extern "C" { // splicer begin class.TestMesh.C_definitions // splicer end class.TestMesh.C_definitions -TRIBOL_TEST_MESH_TestMesh * TRIBOL_TEST_MESH_TestMesh_new(TRIBOL_TEST_MESH_TestMesh * SHC_rv) +TRIBOL_TEST_MESH_TestMesh* TRIBOL_TEST_MESH_TestMesh_new( TRIBOL_TEST_MESH_TestMesh* SHC_rv ) { - // splicer begin class.TestMesh.method.new - tribol::TestMesh *SHCXX_rv = new tribol::TestMesh(); - SHC_rv->addr = static_cast(SHCXX_rv); - SHC_rv->idtor = 1; - return SHC_rv; - // splicer end class.TestMesh.method.new -} - -void TRIBOL_TEST_MESH_TestMesh_delete(TRIBOL_TEST_MESH_TestMesh * self) + // splicer begin class.TestMesh.method.new + tribol::TestMesh* SHCXX_rv = new tribol::TestMesh(); + SHC_rv->addr = static_cast( SHCXX_rv ); + SHC_rv->idtor = 1; + return SHC_rv; + // splicer end class.TestMesh.method.new +} + +void TRIBOL_TEST_MESH_TestMesh_delete( TRIBOL_TEST_MESH_TestMesh* self ) +{ + tribol::TestMesh* SH_this = static_cast( self->addr ); + // splicer begin class.TestMesh.method.delete + delete SH_this; + self->addr = nullptr; + // splicer end class.TestMesh.method.delete +} + +void TRIBOL_TEST_MESH_TestMesh_setup_contact_mesh_hex( TRIBOL_TEST_MESH_TestMesh* self, int numElemsX1, int numElemsY1, + int numElemsZ1, double xMin1, double yMin1, double zMin1, + double xMax1, double yMax1, double zMax1, int numElemsX2, + int numElemsY2, int numElemsZ2, double xMin2, double yMin2, + double zMin2, double xMax2, double yMax2, double zMax2, + double thetaMortar, double thetaNonmortar ) { - tribol::TestMesh *SH_this = static_cast(self->addr); - // splicer begin class.TestMesh.method.delete - delete SH_this; - self->addr = nullptr; - // splicer end class.TestMesh.method.delete + tribol::TestMesh* SH_this = static_cast( self->addr ); + // splicer begin class.TestMesh.method.setup_contact_mesh_hex + SH_this->setupContactMeshHex( numElemsX1, numElemsY1, numElemsZ1, xMin1, yMin1, zMin1, xMax1, yMax1, zMax1, + numElemsX2, numElemsY2, numElemsZ2, xMin2, yMin2, zMin2, xMax2, yMax2, zMax2, + thetaMortar, thetaNonmortar ); + // splicer end class.TestMesh.method.setup_contact_mesh_hex } -void TRIBOL_TEST_MESH_TestMesh_setup_contact_mesh_hex(TRIBOL_TEST_MESH_TestMesh * self, int numElemsX1, int numElemsY1, int numElemsZ1, double xMin1, double yMin1, double zMin1, double xMax1, double yMax1, double zMax1, int numElemsX2, int numElemsY2, int numElemsZ2, double xMin2, double yMin2, double zMin2, double xMax2, double yMax2, double zMax2, double thetaMortar, double thetaNonmortar) +double* TRIBOL_TEST_MESH_TestMesh_get_x( const TRIBOL_TEST_MESH_TestMesh* self ) { - tribol::TestMesh *SH_this = static_cast(self->addr); - // splicer begin class.TestMesh.method.setup_contact_mesh_hex - SH_this->setupContactMeshHex(numElemsX1, numElemsY1, numElemsZ1, xMin1, yMin1, zMin1, xMax1, yMax1, zMax1, numElemsX2, numElemsY2, numElemsZ2, xMin2, yMin2, zMin2, xMax2, yMax2, zMax2, thetaMortar, thetaNonmortar); - // splicer end class.TestMesh.method.setup_contact_mesh_hex -} - -double * TRIBOL_TEST_MESH_TestMesh_get_x(const TRIBOL_TEST_MESH_TestMesh * self) + const tribol::TestMesh* SH_this = static_cast( self->addr ); + // splicer begin class.TestMesh.method.get_x + double* SHC_rv = SH_this->getX(); + return SHC_rv; + // splicer end class.TestMesh.method.get_x +} + +double* TRIBOL_TEST_MESH_TestMesh_get_x_bufferify( const TRIBOL_TEST_MESH_TestMesh* self, + TRIBOL_TEST_MESH_SHROUD_array* DSHC_rv ) +{ + const tribol::TestMesh* SH_this = static_cast( self->addr ); + // splicer begin class.TestMesh.method.get_x_bufferify + double* SHC_rv = SH_this->getX(); + DSHC_rv->cxx.addr = SHC_rv; + DSHC_rv->cxx.idtor = 0; + DSHC_rv->addr.base = SHC_rv; + DSHC_rv->type = SH_TYPE_DOUBLE; + DSHC_rv->elem_len = sizeof( double ); + DSHC_rv->rank = 1; + DSHC_rv->shape[0] = SH_this->numTotalNodes; + DSHC_rv->size = DSHC_rv->shape[0]; + return SHC_rv; + // splicer end class.TestMesh.method.get_x_bufferify +} + +double* TRIBOL_TEST_MESH_TestMesh_get_y( const TRIBOL_TEST_MESH_TestMesh* self ) { - const tribol::TestMesh *SH_this = static_cast(self->addr); - // splicer begin class.TestMesh.method.get_x - double * SHC_rv = SH_this->getX(); - return SHC_rv; - // splicer end class.TestMesh.method.get_x + const tribol::TestMesh* SH_this = static_cast( self->addr ); + // splicer begin class.TestMesh.method.get_y + double* SHC_rv = SH_this->getY(); + return SHC_rv; + // splicer end class.TestMesh.method.get_y } -double * TRIBOL_TEST_MESH_TestMesh_get_x_bufferify(const TRIBOL_TEST_MESH_TestMesh * self, TRIBOL_TEST_MESH_SHROUD_array *DSHC_rv) +double* TRIBOL_TEST_MESH_TestMesh_get_y_bufferify( const TRIBOL_TEST_MESH_TestMesh* self, + TRIBOL_TEST_MESH_SHROUD_array* DSHC_rv ) { - const tribol::TestMesh *SH_this = static_cast(self->addr); - // splicer begin class.TestMesh.method.get_x_bufferify - double * SHC_rv = SH_this->getX(); - DSHC_rv->cxx.addr = SHC_rv; - DSHC_rv->cxx.idtor = 0; - DSHC_rv->addr.base = SHC_rv; - DSHC_rv->type = SH_TYPE_DOUBLE; - DSHC_rv->elem_len = sizeof(double); - DSHC_rv->rank = 1; - DSHC_rv->shape[0] = SH_this->numTotalNodes; - DSHC_rv->size = DSHC_rv->shape[0]; - return SHC_rv; - // splicer end class.TestMesh.method.get_x_bufferify -} + const tribol::TestMesh* SH_this = static_cast( self->addr ); + // splicer begin class.TestMesh.method.get_y_bufferify + double* SHC_rv = SH_this->getY(); + DSHC_rv->cxx.addr = SHC_rv; + DSHC_rv->cxx.idtor = 0; + DSHC_rv->addr.base = SHC_rv; + DSHC_rv->type = SH_TYPE_DOUBLE; + DSHC_rv->elem_len = sizeof( double ); + DSHC_rv->rank = 1; + DSHC_rv->shape[0] = SH_this->numTotalNodes; + DSHC_rv->size = DSHC_rv->shape[0]; + return SHC_rv; + // splicer end class.TestMesh.method.get_y_bufferify +} + +double* TRIBOL_TEST_MESH_TestMesh_get_z( const TRIBOL_TEST_MESH_TestMesh* self ) +{ + const tribol::TestMesh* SH_this = static_cast( self->addr ); + // splicer begin class.TestMesh.method.get_z + double* SHC_rv = SH_this->getZ(); + return SHC_rv; + // splicer end class.TestMesh.method.get_z +} -double * TRIBOL_TEST_MESH_TestMesh_get_y(const TRIBOL_TEST_MESH_TestMesh * self) -{ - const tribol::TestMesh *SH_this = static_cast(self->addr); - // splicer begin class.TestMesh.method.get_y - double * SHC_rv = SH_this->getY(); - return SHC_rv; - // splicer end class.TestMesh.method.get_y -} +double* TRIBOL_TEST_MESH_TestMesh_get_z_bufferify( const TRIBOL_TEST_MESH_TestMesh* self, + TRIBOL_TEST_MESH_SHROUD_array* DSHC_rv ) +{ + const tribol::TestMesh* SH_this = static_cast( self->addr ); + // splicer begin class.TestMesh.method.get_z_bufferify + double* SHC_rv = SH_this->getZ(); + DSHC_rv->cxx.addr = SHC_rv; + DSHC_rv->cxx.idtor = 0; + DSHC_rv->addr.base = SHC_rv; + DSHC_rv->type = SH_TYPE_DOUBLE; + DSHC_rv->elem_len = sizeof( double ); + DSHC_rv->rank = 1; + DSHC_rv->shape[0] = SH_this->numTotalNodes; + DSHC_rv->size = DSHC_rv->shape[0]; + return SHC_rv; + // splicer end class.TestMesh.method.get_z_bufferify +} -double * TRIBOL_TEST_MESH_TestMesh_get_y_bufferify(const TRIBOL_TEST_MESH_TestMesh * self, TRIBOL_TEST_MESH_SHROUD_array *DSHC_rv) +int TRIBOL_TEST_MESH_TestMesh_get_mortar_face_connectivity_size( const TRIBOL_TEST_MESH_TestMesh* self ) { - const tribol::TestMesh *SH_this = static_cast(self->addr); - // splicer begin class.TestMesh.method.get_y_bufferify - double * SHC_rv = SH_this->getY(); - DSHC_rv->cxx.addr = SHC_rv; - DSHC_rv->cxx.idtor = 0; - DSHC_rv->addr.base = SHC_rv; - DSHC_rv->type = SH_TYPE_DOUBLE; - DSHC_rv->elem_len = sizeof(double); - DSHC_rv->rank = 1; - DSHC_rv->shape[0] = SH_this->numTotalNodes; - DSHC_rv->size = DSHC_rv->shape[0]; - return SHC_rv; - // splicer end class.TestMesh.method.get_y_bufferify + const tribol::TestMesh* SH_this = static_cast( self->addr ); + // splicer begin class.TestMesh.method.get_mortar_face_connectivity_size + int SHC_rv = SH_this->getMortarFaceConnectivitySize(); + return SHC_rv; + // splicer end class.TestMesh.method.get_mortar_face_connectivity_size } -double * TRIBOL_TEST_MESH_TestMesh_get_z(const TRIBOL_TEST_MESH_TestMesh * self) +int* TRIBOL_TEST_MESH_TestMesh_get_mortar_face_connectivity( const TRIBOL_TEST_MESH_TestMesh* self ) { - const tribol::TestMesh *SH_this = static_cast(self->addr); - // splicer begin class.TestMesh.method.get_z - double * SHC_rv = SH_this->getZ(); - return SHC_rv; - // splicer end class.TestMesh.method.get_z + const tribol::TestMesh* SH_this = static_cast( self->addr ); + // splicer begin class.TestMesh.method.get_mortar_face_connectivity + int* SHC_rv = SH_this->getMortarFaceConnectivity(); + return SHC_rv; + // splicer end class.TestMesh.method.get_mortar_face_connectivity } -double * TRIBOL_TEST_MESH_TestMesh_get_z_bufferify(const TRIBOL_TEST_MESH_TestMesh * self, TRIBOL_TEST_MESH_SHROUD_array *DSHC_rv) +int* TRIBOL_TEST_MESH_TestMesh_get_mortar_face_connectivity_bufferify( const TRIBOL_TEST_MESH_TestMesh* self, + TRIBOL_TEST_MESH_SHROUD_array* DSHC_rv ) { - const tribol::TestMesh *SH_this = static_cast(self->addr); - // splicer begin class.TestMesh.method.get_z_bufferify - double * SHC_rv = SH_this->getZ(); - DSHC_rv->cxx.addr = SHC_rv; - DSHC_rv->cxx.idtor = 0; - DSHC_rv->addr.base = SHC_rv; - DSHC_rv->type = SH_TYPE_DOUBLE; - DSHC_rv->elem_len = sizeof(double); - DSHC_rv->rank = 1; - DSHC_rv->shape[0] = SH_this->numTotalNodes; - DSHC_rv->size = DSHC_rv->shape[0]; - return SHC_rv; - // splicer end class.TestMesh.method.get_z_bufferify -} - -int TRIBOL_TEST_MESH_TestMesh_get_mortar_face_connectivity_size(const TRIBOL_TEST_MESH_TestMesh * self) -{ - const tribol::TestMesh *SH_this = static_cast(self->addr); - // splicer begin class.TestMesh.method.get_mortar_face_connectivity_size - int SHC_rv = SH_this->getMortarFaceConnectivitySize(); - return SHC_rv; - // splicer end class.TestMesh.method.get_mortar_face_connectivity_size -} - -int * TRIBOL_TEST_MESH_TestMesh_get_mortar_face_connectivity(const TRIBOL_TEST_MESH_TestMesh * self) -{ - const tribol::TestMesh *SH_this = static_cast(self->addr); - // splicer begin class.TestMesh.method.get_mortar_face_connectivity - int * SHC_rv = SH_this->getMortarFaceConnectivity(); - return SHC_rv; - // splicer end class.TestMesh.method.get_mortar_face_connectivity -} - -int * TRIBOL_TEST_MESH_TestMesh_get_mortar_face_connectivity_bufferify(const TRIBOL_TEST_MESH_TestMesh * self, TRIBOL_TEST_MESH_SHROUD_array *DSHC_rv) -{ - const tribol::TestMesh *SH_this = static_cast(self->addr); - // splicer begin class.TestMesh.method.get_mortar_face_connectivity_bufferify - int * SHC_rv = SH_this->getMortarFaceConnectivity(); - DSHC_rv->cxx.addr = SHC_rv; - DSHC_rv->cxx.idtor = 0; - DSHC_rv->addr.base = SHC_rv; - DSHC_rv->type = SH_TYPE_INT; - DSHC_rv->elem_len = sizeof(int); - DSHC_rv->rank = 1; - DSHC_rv->shape[0] = SH_this->getMortarFaceConnectivitySize(); - DSHC_rv->size = DSHC_rv->shape[0]; - return SHC_rv; - // splicer end class.TestMesh.method.get_mortar_face_connectivity_bufferify -} + const tribol::TestMesh* SH_this = static_cast( self->addr ); + // splicer begin class.TestMesh.method.get_mortar_face_connectivity_bufferify + int* SHC_rv = SH_this->getMortarFaceConnectivity(); + DSHC_rv->cxx.addr = SHC_rv; + DSHC_rv->cxx.idtor = 0; + DSHC_rv->addr.base = SHC_rv; + DSHC_rv->type = SH_TYPE_INT; + DSHC_rv->elem_len = sizeof( int ); + DSHC_rv->rank = 1; + DSHC_rv->shape[0] = SH_this->getMortarFaceConnectivitySize(); + DSHC_rv->size = DSHC_rv->shape[0]; + return SHC_rv; + // splicer end class.TestMesh.method.get_mortar_face_connectivity_bufferify +} -int TRIBOL_TEST_MESH_TestMesh_get_nonmortar_face_connectivity_size(const TRIBOL_TEST_MESH_TestMesh * self) +int TRIBOL_TEST_MESH_TestMesh_get_nonmortar_face_connectivity_size( const TRIBOL_TEST_MESH_TestMesh* self ) { - const tribol::TestMesh *SH_this = static_cast(self->addr); - // splicer begin class.TestMesh.method.get_nonmortar_face_connectivity_size - int SHC_rv = SH_this->getNonmortarFaceConnectivitySize(); - return SHC_rv; - // splicer end class.TestMesh.method.get_nonmortar_face_connectivity_size + const tribol::TestMesh* SH_this = static_cast( self->addr ); + // splicer begin class.TestMesh.method.get_nonmortar_face_connectivity_size + int SHC_rv = SH_this->getNonmortarFaceConnectivitySize(); + return SHC_rv; + // splicer end class.TestMesh.method.get_nonmortar_face_connectivity_size } -int * TRIBOL_TEST_MESH_TestMesh_get_nonmortar_face_connectivity(const TRIBOL_TEST_MESH_TestMesh * self) +int* TRIBOL_TEST_MESH_TestMesh_get_nonmortar_face_connectivity( const TRIBOL_TEST_MESH_TestMesh* self ) { - const tribol::TestMesh *SH_this = static_cast(self->addr); - // splicer begin class.TestMesh.method.get_nonmortar_face_connectivity - int * SHC_rv = SH_this->getNonmortarFaceConnectivity(); - return SHC_rv; - // splicer end class.TestMesh.method.get_nonmortar_face_connectivity + const tribol::TestMesh* SH_this = static_cast( self->addr ); + // splicer begin class.TestMesh.method.get_nonmortar_face_connectivity + int* SHC_rv = SH_this->getNonmortarFaceConnectivity(); + return SHC_rv; + // splicer end class.TestMesh.method.get_nonmortar_face_connectivity } -int * TRIBOL_TEST_MESH_TestMesh_get_nonmortar_face_connectivity_bufferify(const TRIBOL_TEST_MESH_TestMesh * self, TRIBOL_TEST_MESH_SHROUD_array *DSHC_rv) +int* TRIBOL_TEST_MESH_TestMesh_get_nonmortar_face_connectivity_bufferify( const TRIBOL_TEST_MESH_TestMesh* self, + TRIBOL_TEST_MESH_SHROUD_array* DSHC_rv ) { - const tribol::TestMesh *SH_this = static_cast(self->addr); - // splicer begin class.TestMesh.method.get_nonmortar_face_connectivity_bufferify - int * SHC_rv = SH_this->getNonmortarFaceConnectivity(); - DSHC_rv->cxx.addr = SHC_rv; - DSHC_rv->cxx.idtor = 0; - DSHC_rv->addr.base = SHC_rv; - DSHC_rv->type = SH_TYPE_INT; - DSHC_rv->elem_len = sizeof(int); - DSHC_rv->rank = 1; - DSHC_rv->shape[0] = SH_this->getNonmortarFaceConnectivitySize(); - DSHC_rv->size = DSHC_rv->shape[0]; - return SHC_rv; - // splicer end class.TestMesh.method.get_nonmortar_face_connectivity_bufferify + const tribol::TestMesh* SH_this = static_cast( self->addr ); + // splicer begin class.TestMesh.method.get_nonmortar_face_connectivity_bufferify + int* SHC_rv = SH_this->getNonmortarFaceConnectivity(); + DSHC_rv->cxx.addr = SHC_rv; + DSHC_rv->cxx.idtor = 0; + DSHC_rv->addr.base = SHC_rv; + DSHC_rv->type = SH_TYPE_INT; + DSHC_rv->elem_len = sizeof( int ); + DSHC_rv->rank = 1; + DSHC_rv->shape[0] = SH_this->getNonmortarFaceConnectivitySize(); + DSHC_rv->size = DSHC_rv->shape[0]; + return SHC_rv; + // splicer end class.TestMesh.method.get_nonmortar_face_connectivity_bufferify } -int TRIBOL_TEST_MESH_TestMesh_get_numtotalnodes(TRIBOL_TEST_MESH_TestMesh * self) +int TRIBOL_TEST_MESH_TestMesh_get_numtotalnodes( TRIBOL_TEST_MESH_TestMesh* self ) { - tribol::TestMesh *SH_this = static_cast(self->addr); - // splicer begin class.TestMesh.method.get_numtotalnodes - return SH_this->numTotalNodes; - // splicer end class.TestMesh.method.get_numtotalnodes + tribol::TestMesh* SH_this = static_cast( self->addr ); + // splicer begin class.TestMesh.method.get_numtotalnodes + return SH_this->numTotalNodes; + // splicer end class.TestMesh.method.get_numtotalnodes } -int TRIBOL_TEST_MESH_TestMesh_get_nummortarfaces(TRIBOL_TEST_MESH_TestMesh * self) +int TRIBOL_TEST_MESH_TestMesh_get_nummortarfaces( TRIBOL_TEST_MESH_TestMesh* self ) { - tribol::TestMesh *SH_this = static_cast(self->addr); - // splicer begin class.TestMesh.method.get_nummortarfaces - return SH_this->numMortarFaces; - // splicer end class.TestMesh.method.get_nummortarfaces + tribol::TestMesh* SH_this = static_cast( self->addr ); + // splicer begin class.TestMesh.method.get_nummortarfaces + return SH_this->numMortarFaces; + // splicer end class.TestMesh.method.get_nummortarfaces } -int TRIBOL_TEST_MESH_TestMesh_get_numnonmortarfaces(TRIBOL_TEST_MESH_TestMesh * self) +int TRIBOL_TEST_MESH_TestMesh_get_numnonmortarfaces( TRIBOL_TEST_MESH_TestMesh* self ) { - tribol::TestMesh *SH_this = static_cast(self->addr); - // splicer begin class.TestMesh.method.get_numnonmortarfaces - return SH_this->numNonmortarFaces; - // splicer end class.TestMesh.method.get_numnonmortarfaces + tribol::TestMesh* SH_this = static_cast( self->addr ); + // splicer begin class.TestMesh.method.get_numnonmortarfaces + return SH_this->numNonmortarFaces; + // splicer end class.TestMesh.method.get_numnonmortarfaces } } // extern "C" diff --git a/src/tribol/interface/mfem_tribol.cpp b/src/tribol/interface/mfem_tribol.cpp index d3fee74e..38fb564c 100644 --- a/src/tribol/interface/mfem_tribol.cpp +++ b/src/tribol/interface/mfem_tribol.cpp @@ -12,524 +12,405 @@ #include "tribol.hpp" #include "tribol/mesh/CouplingScheme.hpp" -namespace tribol -{ +namespace tribol { -void registerMfemCouplingScheme( IndexT cs_id, - int mesh_id_1, - int mesh_id_2, - const mfem::ParMesh& mesh, - const mfem::ParGridFunction& current_coords, - std::set b_attributes_1, - std::set b_attributes_2, - ContactMode contact_mode, - ContactCase contact_case, - ContactMethod contact_method, - ContactModel contact_model, - EnforcementMethod enforcement_method, - BinningMethod binning_method) +void registerMfemCouplingScheme( IndexT cs_id, int mesh_id_1, int mesh_id_2, const mfem::ParMesh& mesh, + const mfem::ParGridFunction& current_coords, std::set b_attributes_1, + std::set b_attributes_2, ContactMode contact_mode, ContactCase contact_case, + ContactMethod contact_method, ContactModel contact_model, + EnforcementMethod enforcement_method, BinningMethod binning_method ) { - // create transfer operators from parent mesh to redecomp mesh - auto mfem_data = std::make_unique( - mesh_id_1, - mesh_id_2, - mesh, - current_coords, - std::move(b_attributes_1), - std::move(b_attributes_2) - ); - // register empty meshes so the coupling scheme is valid - registerMesh( - mesh_id_1, 0, 0, nullptr, 1, nullptr, nullptr, nullptr, MemorySpace::Host); - registerMesh( - mesh_id_2, 0, 0, nullptr, 1, nullptr, nullptr, nullptr, MemorySpace::Host); - registerCouplingScheme( - cs_id, - mesh_id_1, - mesh_id_2, - contact_mode, - contact_case, - contact_method, - contact_model, - enforcement_method, - binning_method, - ExecutionMode::Sequential - ); - auto& coupling_scheme = CouplingSchemeManager::getInstance().at(cs_id); - coupling_scheme.setMPIComm(mesh.GetComm()); - - // Set data required for use with Lagrange multiplier enforcement option. - // Coupling scheme validity will be checked later, but here some initial - // data is created/initialized for use with LMs. - if (enforcement_method == LAGRANGE_MULTIPLIER) - { - std::unique_ptr pressure_fec - = std::make_unique( - current_coords.FESpace()->FEColl()->GetOrder(), - mesh.SpaceDimension() - ); - int pressure_vdim = 0; - if (contact_model == FRICTIONLESS) // only contact model supported with Lagrange multipliers now - { - pressure_vdim = 1; - } - // TODO add the following if they are implemented with Lagrange multipliers: - // - // 1) contact_model == COULOMB - // 2) contact_case == TIED_NORMAL - // 3) contact_case == TIED_FULL - // - // and set pressure_vdim = mesh.SpaceDimension(); - else - { - SLIC_ERROR_ROOT("Unsupported contact model. " - "Only FRICTIONLESS is supported with Lagrange multipliers."); - } - // create pressure field on the parent-linked boundary submesh and - // transfer operators to the redecomp level - coupling_scheme.setMfemSubmeshData( - std::make_unique( - mfem_data->GetSubmesh(), - mfem_data->GetLORMesh(), - std::move(pressure_fec), - pressure_vdim - ) - ); - // set up Jacobian transfer if the coupling scheme requires it - auto lm_options = coupling_scheme.getEnforcementOptions().lm_implicit_options; - if ( - lm_options.enforcement_option_set && - ( - lm_options.eval_mode == ImplicitEvalMode::MORTAR_JACOBIAN || - lm_options.eval_mode == ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN - ) - ) - { - // create matrix transfer operator between redecomp and - // parent/parent-linked boundary submesh - coupling_scheme.setMfemJacobianData(std::make_unique( - *mfem_data, - *coupling_scheme.getMfemSubmeshData(), - contact_method - )); - } - } - coupling_scheme.setMfemMeshData(std::move(mfem_data)); + // create transfer operators from parent mesh to redecomp mesh + auto mfem_data = std::make_unique( mesh_id_1, mesh_id_2, mesh, current_coords, + std::move( b_attributes_1 ), std::move( b_attributes_2 ) ); + // register empty meshes so the coupling scheme is valid + registerMesh( mesh_id_1, 0, 0, nullptr, 1, nullptr, nullptr, nullptr, MemorySpace::Host ); + registerMesh( mesh_id_2, 0, 0, nullptr, 1, nullptr, nullptr, nullptr, MemorySpace::Host ); + registerCouplingScheme( cs_id, mesh_id_1, mesh_id_2, contact_mode, contact_case, contact_method, contact_model, + enforcement_method, binning_method, ExecutionMode::Sequential ); + auto& coupling_scheme = CouplingSchemeManager::getInstance().at( cs_id ); + coupling_scheme.setMPIComm( mesh.GetComm() ); + // Set data required for use with Lagrange multiplier enforcement option. + // Coupling scheme validity will be checked later, but here some initial + // data is created/initialized for use with LMs. + if ( enforcement_method == LAGRANGE_MULTIPLIER ) { + std::unique_ptr pressure_fec = std::make_unique( + current_coords.FESpace()->FEColl()->GetOrder(), mesh.SpaceDimension() ); + int pressure_vdim = 0; + if ( contact_model == FRICTIONLESS ) // only contact model supported with Lagrange multipliers now + { + pressure_vdim = 1; + } + // TODO add the following if they are implemented with Lagrange multipliers: + // + // 1) contact_model == COULOMB + // 2) contact_case == TIED_NORMAL + // 3) contact_case == TIED_FULL + // + // and set pressure_vdim = mesh.SpaceDimension(); + else { + SLIC_ERROR_ROOT( + "Unsupported contact model. " + "Only FRICTIONLESS is supported with Lagrange multipliers." ); + } + // create pressure field on the parent-linked boundary submesh and + // transfer operators to the redecomp level + coupling_scheme.setMfemSubmeshData( std::make_unique( + mfem_data->GetSubmesh(), mfem_data->GetLORMesh(), std::move( pressure_fec ), pressure_vdim ) ); + // set up Jacobian transfer if the coupling scheme requires it + auto lm_options = coupling_scheme.getEnforcementOptions().lm_implicit_options; + if ( lm_options.enforcement_option_set && ( lm_options.eval_mode == ImplicitEvalMode::MORTAR_JACOBIAN || + lm_options.eval_mode == ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN ) ) { + // create matrix transfer operator between redecomp and + // parent/parent-linked boundary submesh + coupling_scheme.setMfemJacobianData( + std::make_unique( *mfem_data, *coupling_scheme.getMfemSubmeshData(), contact_method ) ); + } + } + coupling_scheme.setMfemMeshData( std::move( mfem_data ) ); } void setMfemLORFactor( IndexT cs_id, int lor_factor ) { - auto coupling_scheme = CouplingSchemeManager::getInstance().findData(cs_id); - SLIC_ERROR_ROOT_IF( !coupling_scheme, - axom::fmt::format("Coupling scheme cs_id={0} does not exist. Call tribol::registerMfemCouplingScheme() " - "to create a coupling scheme with this cs_id.", cs_id) ); - SLIC_ERROR_ROOT_IF( - !coupling_scheme->hasMfemData(), - "Coupling scheme does not contain MFEM data. " - "Create the coupling scheme using registerMfemCouplingScheme() to set the LOR factor." - ); - coupling_scheme->getMfemMeshData()->SetLORFactor(lor_factor); + auto coupling_scheme = CouplingSchemeManager::getInstance().findData( cs_id ); + SLIC_ERROR_ROOT_IF( + !coupling_scheme, + axom::fmt::format( "Coupling scheme cs_id={0} does not exist. Call tribol::registerMfemCouplingScheme() " + "to create a coupling scheme with this cs_id.", + cs_id ) ); + SLIC_ERROR_ROOT_IF( !coupling_scheme->hasMfemData(), + "Coupling scheme does not contain MFEM data. " + "Create the coupling scheme using registerMfemCouplingScheme() to set the LOR factor." ); + coupling_scheme->getMfemMeshData()->SetLORFactor( lor_factor ); } -void setMfemKinematicConstantPenalty( IndexT cs_id, - RealT mesh1_penalty, - RealT mesh2_penalty ) +void setMfemKinematicConstantPenalty( IndexT cs_id, RealT mesh1_penalty, RealT mesh2_penalty ) { - auto coupling_scheme = CouplingSchemeManager::getInstance().findData(cs_id); - SLIC_ERROR_ROOT_IF( !coupling_scheme, - axom::fmt::format("Coupling scheme cs_id={0} does not exist. Call tribol::registerMfemCouplingScheme() " - "to create a coupling scheme with this cs_id.", cs_id) ); - SLIC_ERROR_ROOT_IF( - !coupling_scheme->hasMfemData(), - "Coupling scheme does not contain MFEM data. " - "Create the coupling scheme using registerMfemCouplingScheme() to set the penalty." - ); - setPenaltyOptions(cs_id, KINEMATIC, KINEMATIC_CONSTANT); - coupling_scheme->getMfemMeshData()->ClearAllPenaltyData(); - coupling_scheme->getMfemMeshData()->SetMesh1KinematicConstantPenalty(mesh1_penalty); - coupling_scheme->getMfemMeshData()->SetMesh2KinematicConstantPenalty(mesh2_penalty); + auto coupling_scheme = CouplingSchemeManager::getInstance().findData( cs_id ); + SLIC_ERROR_ROOT_IF( + !coupling_scheme, + axom::fmt::format( "Coupling scheme cs_id={0} does not exist. Call tribol::registerMfemCouplingScheme() " + "to create a coupling scheme with this cs_id.", + cs_id ) ); + SLIC_ERROR_ROOT_IF( !coupling_scheme->hasMfemData(), + "Coupling scheme does not contain MFEM data. " + "Create the coupling scheme using registerMfemCouplingScheme() to set the penalty." ); + setPenaltyOptions( cs_id, KINEMATIC, KINEMATIC_CONSTANT ); + coupling_scheme->getMfemMeshData()->ClearAllPenaltyData(); + coupling_scheme->getMfemMeshData()->SetMesh1KinematicConstantPenalty( mesh1_penalty ); + coupling_scheme->getMfemMeshData()->SetMesh2KinematicConstantPenalty( mesh2_penalty ); } -void setMfemKinematicElementPenalty( IndexT cs_id, - mfem::Coefficient& modulus_coefficient ) +void setMfemKinematicElementPenalty( IndexT cs_id, mfem::Coefficient& modulus_coefficient ) { - auto coupling_scheme = CouplingSchemeManager::getInstance().findData(cs_id); - SLIC_ERROR_ROOT_IF( !coupling_scheme, - axom::fmt::format("Coupling scheme cs_id={0} does not exist. Call tribol::registerMfemCouplingScheme() " - "to create a coupling scheme with this cs_id.", cs_id) ); - SLIC_ERROR_ROOT_IF( - !coupling_scheme->hasMfemData(), - "Coupling scheme does not contain MFEM data. " - "Create the coupling scheme using registerMfemCouplingScheme() to set the penalty." - ); - setPenaltyOptions(cs_id, KINEMATIC, KINEMATIC_ELEMENT); - coupling_scheme->getMfemMeshData()->ClearAllPenaltyData(); - coupling_scheme->getMfemMeshData()->ComputeElementThicknesses(); - coupling_scheme->getMfemMeshData()->SetMaterialModulus(modulus_coefficient); + auto coupling_scheme = CouplingSchemeManager::getInstance().findData( cs_id ); + SLIC_ERROR_ROOT_IF( + !coupling_scheme, + axom::fmt::format( "Coupling scheme cs_id={0} does not exist. Call tribol::registerMfemCouplingScheme() " + "to create a coupling scheme with this cs_id.", + cs_id ) ); + SLIC_ERROR_ROOT_IF( !coupling_scheme->hasMfemData(), + "Coupling scheme does not contain MFEM data. " + "Create the coupling scheme using registerMfemCouplingScheme() to set the penalty." ); + setPenaltyOptions( cs_id, KINEMATIC, KINEMATIC_ELEMENT ); + coupling_scheme->getMfemMeshData()->ClearAllPenaltyData(); + coupling_scheme->getMfemMeshData()->ComputeElementThicknesses(); + coupling_scheme->getMfemMeshData()->SetMaterialModulus( modulus_coefficient ); } -void setMfemRateConstantPenalty( IndexT cs_id, - RealT mesh1_penalty, - RealT mesh2_penalty ) +void setMfemRateConstantPenalty( IndexT cs_id, RealT mesh1_penalty, RealT mesh2_penalty ) { - auto coupling_scheme = CouplingSchemeManager::getInstance().findData(cs_id); - SLIC_ERROR_ROOT_IF( !coupling_scheme, - axom::fmt::format("Coupling scheme cs_id={0} does not exist. Call tribol::registerMfemCouplingScheme() " - "to create a coupling scheme with this cs_id.", cs_id) ); - SLIC_ERROR_ROOT_IF( - !coupling_scheme->hasMfemData(), - "Coupling scheme does not contain MFEM data. " - "Create the coupling scheme using registerMfemCouplingScheme() to set the penalty." - ); - auto penalty_opts = coupling_scheme->getEnforcementOptions().penalty_options; - SLIC_ERROR_ROOT_IF( - !penalty_opts.kinematic_calc_set, - "No kinematic enforcement method set. Call setMfemKinematicConstantPenalty() or " - "setMfemKinematicElementPenalty() first." - ); - setPenaltyOptions( - cs_id, KINEMATIC_AND_RATE, penalty_opts.kinematic_calculation, RATE_CONSTANT); - coupling_scheme->getMfemMeshData()->ClearRatePenaltyData(); - coupling_scheme->getMfemMeshData()->SetMesh1RateConstantPenalty(mesh1_penalty); - coupling_scheme->getMfemMeshData()->SetMesh2RateConstantPenalty(mesh2_penalty); + auto coupling_scheme = CouplingSchemeManager::getInstance().findData( cs_id ); + SLIC_ERROR_ROOT_IF( + !coupling_scheme, + axom::fmt::format( "Coupling scheme cs_id={0} does not exist. Call tribol::registerMfemCouplingScheme() " + "to create a coupling scheme with this cs_id.", + cs_id ) ); + SLIC_ERROR_ROOT_IF( !coupling_scheme->hasMfemData(), + "Coupling scheme does not contain MFEM data. " + "Create the coupling scheme using registerMfemCouplingScheme() to set the penalty." ); + auto penalty_opts = coupling_scheme->getEnforcementOptions().penalty_options; + SLIC_ERROR_ROOT_IF( !penalty_opts.kinematic_calc_set, + "No kinematic enforcement method set. Call setMfemKinematicConstantPenalty() or " + "setMfemKinematicElementPenalty() first." ); + setPenaltyOptions( cs_id, KINEMATIC_AND_RATE, penalty_opts.kinematic_calculation, RATE_CONSTANT ); + coupling_scheme->getMfemMeshData()->ClearRatePenaltyData(); + coupling_scheme->getMfemMeshData()->SetMesh1RateConstantPenalty( mesh1_penalty ); + coupling_scheme->getMfemMeshData()->SetMesh2RateConstantPenalty( mesh2_penalty ); } -void setMfemRatePercentPenalty( IndexT cs_id, - RealT mesh1_ratio, - RealT mesh2_ratio ) +void setMfemRatePercentPenalty( IndexT cs_id, RealT mesh1_ratio, RealT mesh2_ratio ) { - auto coupling_scheme = CouplingSchemeManager::getInstance().findData(cs_id); - SLIC_ERROR_ROOT_IF( !coupling_scheme, - axom::fmt::format("Coupling scheme cs_id={0} does not exist. Call tribol::registerMfemCouplingScheme() " - "to create a coupling scheme with this cs_id.", cs_id) ); - SLIC_ERROR_ROOT_IF( - !coupling_scheme->hasMfemData(), - "Coupling scheme does not contain MFEM data. " - "Create the coupling scheme using registerMfemCouplingScheme() to set the penalty." - ); - auto penalty_opts = coupling_scheme->getEnforcementOptions().penalty_options; - SLIC_ERROR_ROOT_IF( - !penalty_opts.kinematic_calc_set, - "No kinematic enforcement method set. Call setMfemKinematicConstantPenalty() or " - "setMfemKinematicElementPenalty() first." - ); - setPenaltyOptions( - cs_id, KINEMATIC_AND_RATE, penalty_opts.kinematic_calculation, RATE_PERCENT); - coupling_scheme->getMfemMeshData()->ClearRatePenaltyData(); - coupling_scheme->getMfemMeshData()->SetMesh1RatePercentPenalty(mesh1_ratio); - coupling_scheme->getMfemMeshData()->SetMesh2RatePercentPenalty(mesh2_ratio); + auto coupling_scheme = CouplingSchemeManager::getInstance().findData( cs_id ); + SLIC_ERROR_ROOT_IF( + !coupling_scheme, + axom::fmt::format( "Coupling scheme cs_id={0} does not exist. Call tribol::registerMfemCouplingScheme() " + "to create a coupling scheme with this cs_id.", + cs_id ) ); + SLIC_ERROR_ROOT_IF( !coupling_scheme->hasMfemData(), + "Coupling scheme does not contain MFEM data. " + "Create the coupling scheme using registerMfemCouplingScheme() to set the penalty." ); + auto penalty_opts = coupling_scheme->getEnforcementOptions().penalty_options; + SLIC_ERROR_ROOT_IF( !penalty_opts.kinematic_calc_set, + "No kinematic enforcement method set. Call setMfemKinematicConstantPenalty() or " + "setMfemKinematicElementPenalty() first." ); + setPenaltyOptions( cs_id, KINEMATIC_AND_RATE, penalty_opts.kinematic_calculation, RATE_PERCENT ); + coupling_scheme->getMfemMeshData()->ClearRatePenaltyData(); + coupling_scheme->getMfemMeshData()->SetMesh1RatePercentPenalty( mesh1_ratio ); + coupling_scheme->getMfemMeshData()->SetMesh2RatePercentPenalty( mesh2_ratio ); } void setMfemKinematicPenaltyScale( IndexT cs_id, RealT mesh1_scale, RealT mesh2_scale ) { - auto coupling_scheme = CouplingSchemeManager::getInstance().findData(cs_id); - SLIC_ERROR_ROOT_IF( !coupling_scheme, - axom::fmt::format("Coupling scheme cs_id={0} does not exist. Call tribol::registerMfemCouplingScheme() " - "to create a coupling scheme with this cs_id.", cs_id) ); - SLIC_ERROR_ROOT_IF( - !coupling_scheme->hasMfemData(), - "Coupling scheme does not contain MFEM data. " - "Create the coupling scheme using registerMfemCouplingScheme() to set the penalty." - ); - auto penalty_opts = coupling_scheme->getEnforcementOptions().penalty_options; - SLIC_ERROR_ROOT_IF( - !penalty_opts.kinematic_calc_set, - "No kinematic enforcement method set. Call setMfemKinematicConstantPenalty() or " - "setMfemKinematicElementPenalty() first." - ); - coupling_scheme->getMfemMeshData()->SetMesh1KinematicPenaltyScale(mesh1_scale); - coupling_scheme->getMfemMeshData()->SetMesh2KinematicPenaltyScale(mesh2_scale); + auto coupling_scheme = CouplingSchemeManager::getInstance().findData( cs_id ); + SLIC_ERROR_ROOT_IF( + !coupling_scheme, + axom::fmt::format( "Coupling scheme cs_id={0} does not exist. Call tribol::registerMfemCouplingScheme() " + "to create a coupling scheme with this cs_id.", + cs_id ) ); + SLIC_ERROR_ROOT_IF( !coupling_scheme->hasMfemData(), + "Coupling scheme does not contain MFEM data. " + "Create the coupling scheme using registerMfemCouplingScheme() to set the penalty." ); + auto penalty_opts = coupling_scheme->getEnforcementOptions().penalty_options; + SLIC_ERROR_ROOT_IF( !penalty_opts.kinematic_calc_set, + "No kinematic enforcement method set. Call setMfemKinematicConstantPenalty() or " + "setMfemKinematicElementPenalty() first." ); + coupling_scheme->getMfemMeshData()->SetMesh1KinematicPenaltyScale( mesh1_scale ); + coupling_scheme->getMfemMeshData()->SetMesh2KinematicPenaltyScale( mesh2_scale ); } void updateMfemElemThickness( IndexT cs_id ) { - auto coupling_scheme = CouplingSchemeManager::getInstance().findData(cs_id); - SLIC_ERROR_ROOT_IF( !coupling_scheme, - axom::fmt::format("Coupling scheme cs_id={0} does not exist. Call tribol::registerMfemCouplingScheme() " - "to create a coupling scheme with this cs_id.", cs_id) ); - SLIC_ERROR_ROOT_IF( - !coupling_scheme->hasMfemData(), - "Coupling scheme does not contain MFEM data. " - "Create the coupling scheme using registerMfemCouplingScheme() to set the penalty." - ); - auto penalty_opts = coupling_scheme->getEnforcementOptions().penalty_options; - SLIC_ERROR_ROOT_IF( + auto coupling_scheme = CouplingSchemeManager::getInstance().findData( cs_id ); + SLIC_ERROR_ROOT_IF( + !coupling_scheme, + axom::fmt::format( "Coupling scheme cs_id={0} does not exist. Call tribol::registerMfemCouplingScheme() " + "to create a coupling scheme with this cs_id.", + cs_id ) ); + SLIC_ERROR_ROOT_IF( !coupling_scheme->hasMfemData(), + "Coupling scheme does not contain MFEM data. " + "Create the coupling scheme using registerMfemCouplingScheme() to set the penalty." ); + auto penalty_opts = coupling_scheme->getEnforcementOptions().penalty_options; + SLIC_ERROR_ROOT_IF( !penalty_opts.kinematic_calc_set && penalty_opts.kinematic_calculation != KINEMATIC_ELEMENT, - "Thickness can only be updated when kinematic penalty has been set using setMfemKinematicElementPenalty()." - ); - coupling_scheme->getMfemMeshData()->ComputeElementThicknesses(); + "Thickness can only be updated when kinematic penalty has been set using setMfemKinematicElementPenalty()." ); + coupling_scheme->getMfemMeshData()->ComputeElementThicknesses(); } void updateMfemMaterialModulus( IndexT cs_id, mfem::Coefficient& modulus_coefficient ) { - auto coupling_scheme = CouplingSchemeManager::getInstance().findData(cs_id); - SLIC_ERROR_ROOT_IF( !coupling_scheme, - axom::fmt::format("Coupling scheme cs_id={0} does not exist. Call tribol::registerMfemCouplingScheme() " - "to create a coupling scheme with this cs_id.", cs_id) ); - SLIC_ERROR_ROOT_IF( - !coupling_scheme->hasMfemData(), - "Coupling scheme does not contain MFEM data. " - "Create the coupling scheme using registerMfemCouplingScheme() to set the penalty." - ); - auto penalty_opts = coupling_scheme->getEnforcementOptions().penalty_options; - SLIC_ERROR_ROOT_IF( - !penalty_opts.kinematic_calc_set && penalty_opts.kinematic_calculation != KINEMATIC_ELEMENT, - "Material modulus can only be updated when kinematic penalty has been set using setMfemKinematicElementPenalty()." - ); - coupling_scheme->getMfemMeshData()->SetMaterialModulus(modulus_coefficient); + auto coupling_scheme = CouplingSchemeManager::getInstance().findData( cs_id ); + SLIC_ERROR_ROOT_IF( + !coupling_scheme, + axom::fmt::format( "Coupling scheme cs_id={0} does not exist. Call tribol::registerMfemCouplingScheme() " + "to create a coupling scheme with this cs_id.", + cs_id ) ); + SLIC_ERROR_ROOT_IF( !coupling_scheme->hasMfemData(), + "Coupling scheme does not contain MFEM data. " + "Create the coupling scheme using registerMfemCouplingScheme() to set the penalty." ); + auto penalty_opts = coupling_scheme->getEnforcementOptions().penalty_options; + SLIC_ERROR_ROOT_IF( !penalty_opts.kinematic_calc_set && penalty_opts.kinematic_calculation != KINEMATIC_ELEMENT, + "Material modulus can only be updated when kinematic penalty has been set using " + "setMfemKinematicElementPenalty()." ); + coupling_scheme->getMfemMeshData()->SetMaterialModulus( modulus_coefficient ); } void registerMfemVelocity( IndexT cs_id, const mfem::ParGridFunction& v ) { - auto coupling_scheme = CouplingSchemeManager::getInstance().findData(cs_id); - SLIC_ERROR_ROOT_IF( !coupling_scheme, - axom::fmt::format("Coupling scheme cs_id={0} does not exist. Call tribol::registerMfemCouplingScheme() " - "to create a coupling scheme with this cs_id.", cs_id) ); - SLIC_ERROR_ROOT_IF( - !coupling_scheme->hasMfemData(), - "Coupling scheme does not contain MFEM data. " - "Create the coupling scheme using registerMfemCouplingScheme() to register a velocity." - ); - coupling_scheme->getMfemMeshData()->SetParentVelocity(v); + auto coupling_scheme = CouplingSchemeManager::getInstance().findData( cs_id ); + SLIC_ERROR_ROOT_IF( + !coupling_scheme, + axom::fmt::format( "Coupling scheme cs_id={0} does not exist. Call tribol::registerMfemCouplingScheme() " + "to create a coupling scheme with this cs_id.", + cs_id ) ); + SLIC_ERROR_ROOT_IF( !coupling_scheme->hasMfemData(), + "Coupling scheme does not contain MFEM data. " + "Create the coupling scheme using registerMfemCouplingScheme() to register a velocity." ); + coupling_scheme->getMfemMeshData()->SetParentVelocity( v ); } void getMfemResponse( IndexT cs_id, mfem::Vector& r ) { - auto coupling_scheme = CouplingSchemeManager::getInstance().findData(cs_id); - SLIC_ERROR_ROOT_IF( !coupling_scheme, - axom::fmt::format("Coupling scheme cs_id={0} does not exist. Call tribol::registerMfemCouplingScheme() " - "to create a coupling scheme with this cs_id.", cs_id) ); - SLIC_ERROR_ROOT_IF( - !coupling_scheme->hasMfemData(), - "Coupling scheme does not contain MFEM data. " - "Create the coupling scheme using registerMfemCouplingScheme() to return a response vector." - ); - coupling_scheme->getMfemMeshData()->GetParentResponse(r); + auto coupling_scheme = CouplingSchemeManager::getInstance().findData( cs_id ); + SLIC_ERROR_ROOT_IF( + !coupling_scheme, + axom::fmt::format( "Coupling scheme cs_id={0} does not exist. Call tribol::registerMfemCouplingScheme() " + "to create a coupling scheme with this cs_id.", + cs_id ) ); + SLIC_ERROR_ROOT_IF( !coupling_scheme->hasMfemData(), + "Coupling scheme does not contain MFEM data. " + "Create the coupling scheme using registerMfemCouplingScheme() to return a response vector." ); + coupling_scheme->getMfemMeshData()->GetParentResponse( r ); } std::unique_ptr getMfemBlockJacobian( IndexT cs_id ) { - CouplingScheme* coupling_scheme = CouplingSchemeManager::getInstance().findData(cs_id); - SLIC_ERROR_ROOT_IF( !coupling_scheme, - axom::fmt::format("Coupling scheme cs_id={0} does not exist. Call tribol::registerMfemCouplingScheme() " - "to create a coupling scheme with this cs_id.", cs_id) ); - SparseMode sparse_mode = coupling_scheme - ->getEnforcementOptions().lm_implicit_options.sparse_mode; - if (sparse_mode != SparseMode::MFEM_ELEMENT_DENSE) - { - SLIC_ERROR_ROOT("Jacobian is assembled and can be accessed by " - "getMfemSparseMatrix() or getCSRMatrix(). For (unassembled) element " - "Jacobian contributions, call setLagrangeMultiplierOptions() with " - "SparseMode::MFEM_ELEMENT_DENSE before calling update()."); - } - SLIC_ERROR_ROOT_IF( - !coupling_scheme->hasMfemData(), - axom::fmt::format("Coupling scheme cs_id={0} does not contain MFEM data." - "Create the coupling scheme using registerMfemCouplingScheme() to return a MFEM block Jacobian.", cs_id) - ); - // creates a block Jacobian on the parent mesh/parent-linked boundary submesh - // based on the element Jacobians stored in the coupling scheme's method data - return coupling_scheme->getMfemJacobianData()->GetMfemBlockJacobian( - coupling_scheme->getMethodData() - ); + CouplingScheme* coupling_scheme = CouplingSchemeManager::getInstance().findData( cs_id ); + SLIC_ERROR_ROOT_IF( + !coupling_scheme, + axom::fmt::format( "Coupling scheme cs_id={0} does not exist. Call tribol::registerMfemCouplingScheme() " + "to create a coupling scheme with this cs_id.", + cs_id ) ); + SparseMode sparse_mode = coupling_scheme->getEnforcementOptions().lm_implicit_options.sparse_mode; + if ( sparse_mode != SparseMode::MFEM_ELEMENT_DENSE ) { + SLIC_ERROR_ROOT( + "Jacobian is assembled and can be accessed by " + "getMfemSparseMatrix() or getCSRMatrix(). For (unassembled) element " + "Jacobian contributions, call setLagrangeMultiplierOptions() with " + "SparseMode::MFEM_ELEMENT_DENSE before calling update()." ); + } + SLIC_ERROR_ROOT_IF( + !coupling_scheme->hasMfemData(), + axom::fmt::format( + "Coupling scheme cs_id={0} does not contain MFEM data." + "Create the coupling scheme using registerMfemCouplingScheme() to return a MFEM block Jacobian.", + cs_id ) ); + // creates a block Jacobian on the parent mesh/parent-linked boundary submesh + // based on the element Jacobians stored in the coupling scheme's method data + return coupling_scheme->getMfemJacobianData()->GetMfemBlockJacobian( coupling_scheme->getMethodData() ); } void getMfemGap( IndexT cs_id, mfem::Vector& g ) { - auto coupling_scheme = CouplingSchemeManager::getInstance().findData(cs_id); - SLIC_ERROR_ROOT_IF( !coupling_scheme, - axom::fmt::format("Coupling scheme cs_id={0} does not exist. Call tribol::registerMfemCouplingScheme() " - "to create a coupling scheme with this cs_id.", cs_id) ); - SLIC_ERROR_ROOT_IF( - !coupling_scheme->hasMfemSubmeshData(), - axom::fmt::format("Coupling scheme cs_id={0} does not contain MFEM pressure field data. " - "Create the coupling scheme using registerMfemCouplingScheme() and set the " - "enforcement_method to LAGRANGE_MULTIPLIER to set the gap vector.", cs_id) - ); - coupling_scheme->getMfemSubmeshData()->GetSubmeshGap(g); + auto coupling_scheme = CouplingSchemeManager::getInstance().findData( cs_id ); + SLIC_ERROR_ROOT_IF( + !coupling_scheme, + axom::fmt::format( "Coupling scheme cs_id={0} does not exist. Call tribol::registerMfemCouplingScheme() " + "to create a coupling scheme with this cs_id.", + cs_id ) ); + SLIC_ERROR_ROOT_IF( !coupling_scheme->hasMfemSubmeshData(), + axom::fmt::format( "Coupling scheme cs_id={0} does not contain MFEM pressure field data. " + "Create the coupling scheme using registerMfemCouplingScheme() and set the " + "enforcement_method to LAGRANGE_MULTIPLIER to set the gap vector.", + cs_id ) ); + coupling_scheme->getMfemSubmeshData()->GetSubmeshGap( g ); } mfem::ParGridFunction& getMfemPressure( IndexT cs_id ) { - auto coupling_scheme = CouplingSchemeManager::getInstance().findData(cs_id); - SLIC_ERROR_ROOT_IF( !coupling_scheme, - axom::fmt::format("Coupling scheme cs_id={0} does not exist. Call tribol::registerMfemCouplingScheme() " - "to create a coupling scheme with this cs_id.", cs_id) ); - SLIC_ERROR_ROOT_IF( - !coupling_scheme->hasMfemSubmeshData(), - axom::fmt::format("Coupling scheme cs_id={0} does not contain MFEM pressure field data. " - "Create the coupling scheme using registerMfemCouplingScheme() and set the " - "enforcement_method to LAGRANGE_MULTIPLIER to access the pressure field.", cs_id) - ); - return coupling_scheme->getMfemSubmeshData()->GetSubmeshPressure(); + auto coupling_scheme = CouplingSchemeManager::getInstance().findData( cs_id ); + SLIC_ERROR_ROOT_IF( + !coupling_scheme, + axom::fmt::format( "Coupling scheme cs_id={0} does not exist. Call tribol::registerMfemCouplingScheme() " + "to create a coupling scheme with this cs_id.", + cs_id ) ); + SLIC_ERROR_ROOT_IF( !coupling_scheme->hasMfemSubmeshData(), + axom::fmt::format( "Coupling scheme cs_id={0} does not contain MFEM pressure field data. " + "Create the coupling scheme using registerMfemCouplingScheme() and set the " + "enforcement_method to LAGRANGE_MULTIPLIER to access the pressure field.", + cs_id ) ); + return coupling_scheme->getMfemSubmeshData()->GetSubmeshPressure(); } void updateMfemParallelDecomposition() { - for (auto& cs_pair : CouplingSchemeManager::getInstance()) - { - auto& coupling_scheme = cs_pair.second; + for ( auto& cs_pair : CouplingSchemeManager::getInstance() ) { + auto& coupling_scheme = cs_pair.second; - // update redecomp meshes if supplied mfem data - if (coupling_scheme.hasMfemData()) - { - auto mfem_data = coupling_scheme.getMfemMeshData(); - ArrayT mesh_ids {2, 2}; - mesh_ids[0] = mfem_data->GetMesh1ID(); - mesh_ids[1] = mfem_data->GetMesh2ID(); - // creates a new redecomp mesh based on updated coordinates and updates - // transfer operators and displacement, velocity, and response grid - // functions based on new redecomp mesh - mfem_data->UpdateMfemMeshData(); - auto coord_ptrs = mfem_data->GetRedecompCoordsPtrs(); + // update redecomp meshes if supplied mfem data + if ( coupling_scheme.hasMfemData() ) { + auto mfem_data = coupling_scheme.getMfemMeshData(); + ArrayT mesh_ids{ 2, 2 }; + mesh_ids[0] = mfem_data->GetMesh1ID(); + mesh_ids[1] = mfem_data->GetMesh2ID(); + // creates a new redecomp mesh based on updated coordinates and updates + // transfer operators and displacement, velocity, and response grid + // functions based on new redecomp mesh + mfem_data->UpdateMfemMeshData(); + auto coord_ptrs = mfem_data->GetRedecompCoordsPtrs(); - registerMesh( - mesh_ids[0], - mfem_data->GetMesh1NE(), - mfem_data->GetNV(), - mfem_data->GetMesh1Conn(), - mfem_data->GetElemType(), - coord_ptrs[0], - coord_ptrs[1], - coord_ptrs[2], - MemorySpace::Host - ); - registerMesh( - mesh_ids[1], - mfem_data->GetMesh2NE(), - mfem_data->GetNV(), - mfem_data->GetMesh2Conn(), - mfem_data->GetElemType(), - coord_ptrs[0], - coord_ptrs[1], - coord_ptrs[2], - MemorySpace::Host - ); + registerMesh( mesh_ids[0], mfem_data->GetMesh1NE(), mfem_data->GetNV(), mfem_data->GetMesh1Conn(), + mfem_data->GetElemType(), coord_ptrs[0], coord_ptrs[1], coord_ptrs[2], MemorySpace::Host ); + registerMesh( mesh_ids[1], mfem_data->GetMesh2NE(), mfem_data->GetNV(), mfem_data->GetMesh2Conn(), + mfem_data->GetElemType(), coord_ptrs[0], coord_ptrs[1], coord_ptrs[2], MemorySpace::Host ); - auto f_ptrs = mfem_data->GetRedecompResponsePtrs(); - registerNodalResponse( - mesh_ids[0], f_ptrs[0], f_ptrs[1], f_ptrs[2]); - registerNodalResponse( - mesh_ids[1], f_ptrs[0], f_ptrs[1], f_ptrs[2]); - if (mfem_data->HasVelocity()) - { - auto v_ptrs = mfem_data->GetRedecompVelocityPtrs(); - registerNodalVelocities( - mesh_ids[0], v_ptrs[0], v_ptrs[1], v_ptrs[2]); - registerNodalVelocities( - mesh_ids[1], v_ptrs[0], v_ptrs[1], v_ptrs[2]); - } - if (coupling_scheme.getEnforcementMethod() == LAGRANGE_MULTIPLIER) - { - SLIC_ERROR_ROOT_IF(coupling_scheme.getContactModel() != FRICTIONLESS, - "Only frictionless contact is supported."); - SLIC_ERROR_ROOT_IF(coupling_scheme.getContactMethod() != SINGLE_MORTAR, - "Only single mortar contact is supported."); - auto submesh_data = coupling_scheme.getMfemSubmeshData(); - // updates submesh-native grid functions and transfer operators on - // the new redecomp mesh - submesh_data->UpdateMfemSubmeshData(mfem_data->GetRedecompMesh()); - auto g_ptrs = submesh_data->GetRedecompGapPtrs(); - registerMortarGaps(mesh_ids[1], g_ptrs[0]); - auto p_ptrs = submesh_data->GetRedecompPressurePtrs(); - registerMortarPressures(mesh_ids[1], p_ptrs[0]); - if (coupling_scheme.hasMfemJacobianData()) - { - // updates Jacobian transfer operator for new redecomp mesh - coupling_scheme.getMfemJacobianData()->UpdateJacobianXfer(); - } - } - auto& penalty_opts = coupling_scheme.getEnforcementOptions().penalty_options; - if (penalty_opts.kinematic_calc_set) - { - if (penalty_opts.kinematic_calculation == KINEMATIC_ELEMENT) - { - SLIC_ERROR_ROOT_IF( - !mfem_data->GetRedecompElemThickness1() || !mfem_data->GetRedecompElemThickness2(), - "No element thickness data available. Call setMfemKinematicElementPenalty()." - ); - SLIC_ERROR_ROOT_IF( - !mfem_data->GetRedecompMaterialModulus1() || !mfem_data->GetRedecompMaterialModulus2(), - "Material modulus data has not been registered. Call setMfemKinematicElementPenalty()." - ); - setKinematicElementPenalty( - mesh_ids[0], - mfem_data->GetRedecompMaterialModulus1(), - mfem_data->GetRedecompElemThickness1() - ); - setKinematicElementPenalty( - mesh_ids[1], - mfem_data->GetRedecompMaterialModulus2(), - mfem_data->GetRedecompElemThickness2() - ); - } - else if (penalty_opts.kinematic_calculation == KINEMATIC_CONSTANT) - { - SLIC_ERROR_ROOT_IF( - !mfem_data->GetMesh1KinematicConstantPenalty() || !mfem_data->GetMesh2KinematicConstantPenalty(), - "Penalty parameters have not been set. Call setMfemKinematicConstantPenalty()." - ); - setKinematicConstantPenalty(mesh_ids[0], *mfem_data->GetMesh1KinematicConstantPenalty()); - setKinematicConstantPenalty(mesh_ids[1], *mfem_data->GetMesh2KinematicConstantPenalty()); - } - if (mfem_data->GetMesh1KinematicPenaltyScale()) - { - setPenaltyScale(mesh_ids[0], *mfem_data->GetMesh1KinematicPenaltyScale()); - } - if (mfem_data->GetMesh2KinematicPenaltyScale()) - { - setPenaltyScale(mesh_ids[1], *mfem_data->GetMesh2KinematicPenaltyScale()); - } - } - if (penalty_opts.rate_calc_set) - { - if (penalty_opts.rate_calculation == RATE_CONSTANT) - { - SLIC_ERROR_ROOT_IF( - !mfem_data->GetMesh1RateConstantPenalty() || !mfem_data->GetMesh2RateConstantPenalty(), - "Rate penalty values have not been set. Call setMfemRateConstantPenalty()." - ); - setRateConstantPenalty(mesh_ids[0], *mfem_data->GetMesh1RateConstantPenalty()); - setRateConstantPenalty(mesh_ids[1], *mfem_data->GetMesh2RateConstantPenalty()); - } - else if (penalty_opts.rate_calculation == RATE_PERCENT) - { - SLIC_ERROR_ROOT_IF( - !mfem_data->GetMesh1RatePercentPenalty() || !mfem_data->GetMesh2RatePercentPenalty(), - "Rate penalty values have not been set. Call setMfemRatePercentPenalty()." - ); - setRatePercentPenalty(mesh_ids[0], *mfem_data->GetMesh1RatePercentPenalty()); - setRatePercentPenalty(mesh_ids[1], *mfem_data->GetMesh2RatePercentPenalty()); - } - } + auto f_ptrs = mfem_data->GetRedecompResponsePtrs(); + registerNodalResponse( mesh_ids[0], f_ptrs[0], f_ptrs[1], f_ptrs[2] ); + registerNodalResponse( mesh_ids[1], f_ptrs[0], f_ptrs[1], f_ptrs[2] ); + if ( mfem_data->HasVelocity() ) { + auto v_ptrs = mfem_data->GetRedecompVelocityPtrs(); + registerNodalVelocities( mesh_ids[0], v_ptrs[0], v_ptrs[1], v_ptrs[2] ); + registerNodalVelocities( mesh_ids[1], v_ptrs[0], v_ptrs[1], v_ptrs[2] ); } - - } - + if ( coupling_scheme.getEnforcementMethod() == LAGRANGE_MULTIPLIER ) { + SLIC_ERROR_ROOT_IF( coupling_scheme.getContactModel() != FRICTIONLESS, + "Only frictionless contact is supported." ); + SLIC_ERROR_ROOT_IF( coupling_scheme.getContactMethod() != SINGLE_MORTAR, + "Only single mortar contact is supported." ); + auto submesh_data = coupling_scheme.getMfemSubmeshData(); + // updates submesh-native grid functions and transfer operators on + // the new redecomp mesh + submesh_data->UpdateMfemSubmeshData( mfem_data->GetRedecompMesh() ); + auto g_ptrs = submesh_data->GetRedecompGapPtrs(); + registerMortarGaps( mesh_ids[1], g_ptrs[0] ); + auto p_ptrs = submesh_data->GetRedecompPressurePtrs(); + registerMortarPressures( mesh_ids[1], p_ptrs[0] ); + if ( coupling_scheme.hasMfemJacobianData() ) { + // updates Jacobian transfer operator for new redecomp mesh + coupling_scheme.getMfemJacobianData()->UpdateJacobianXfer(); + } + } + auto& penalty_opts = coupling_scheme.getEnforcementOptions().penalty_options; + if ( penalty_opts.kinematic_calc_set ) { + if ( penalty_opts.kinematic_calculation == KINEMATIC_ELEMENT ) { + SLIC_ERROR_ROOT_IF( !mfem_data->GetRedecompElemThickness1() || !mfem_data->GetRedecompElemThickness2(), + "No element thickness data available. Call setMfemKinematicElementPenalty()." ); + SLIC_ERROR_ROOT_IF( + !mfem_data->GetRedecompMaterialModulus1() || !mfem_data->GetRedecompMaterialModulus2(), + "Material modulus data has not been registered. Call setMfemKinematicElementPenalty()." ); + setKinematicElementPenalty( mesh_ids[0], mfem_data->GetRedecompMaterialModulus1(), + mfem_data->GetRedecompElemThickness1() ); + setKinematicElementPenalty( mesh_ids[1], mfem_data->GetRedecompMaterialModulus2(), + mfem_data->GetRedecompElemThickness2() ); + } else if ( penalty_opts.kinematic_calculation == KINEMATIC_CONSTANT ) { + SLIC_ERROR_ROOT_IF( + !mfem_data->GetMesh1KinematicConstantPenalty() || !mfem_data->GetMesh2KinematicConstantPenalty(), + "Penalty parameters have not been set. Call setMfemKinematicConstantPenalty()." ); + setKinematicConstantPenalty( mesh_ids[0], *mfem_data->GetMesh1KinematicConstantPenalty() ); + setKinematicConstantPenalty( mesh_ids[1], *mfem_data->GetMesh2KinematicConstantPenalty() ); + } + if ( mfem_data->GetMesh1KinematicPenaltyScale() ) { + setPenaltyScale( mesh_ids[0], *mfem_data->GetMesh1KinematicPenaltyScale() ); + } + if ( mfem_data->GetMesh2KinematicPenaltyScale() ) { + setPenaltyScale( mesh_ids[1], *mfem_data->GetMesh2KinematicPenaltyScale() ); + } + } + if ( penalty_opts.rate_calc_set ) { + if ( penalty_opts.rate_calculation == RATE_CONSTANT ) { + SLIC_ERROR_ROOT_IF( !mfem_data->GetMesh1RateConstantPenalty() || !mfem_data->GetMesh2RateConstantPenalty(), + "Rate penalty values have not been set. Call setMfemRateConstantPenalty()." ); + setRateConstantPenalty( mesh_ids[0], *mfem_data->GetMesh1RateConstantPenalty() ); + setRateConstantPenalty( mesh_ids[1], *mfem_data->GetMesh2RateConstantPenalty() ); + } else if ( penalty_opts.rate_calculation == RATE_PERCENT ) { + SLIC_ERROR_ROOT_IF( !mfem_data->GetMesh1RatePercentPenalty() || !mfem_data->GetMesh2RatePercentPenalty(), + "Rate penalty values have not been set. Call setMfemRatePercentPenalty()." ); + setRatePercentPenalty( mesh_ids[0], *mfem_data->GetMesh1RatePercentPenalty() ); + setRatePercentPenalty( mesh_ids[1], *mfem_data->GetMesh2RatePercentPenalty() ); + } + } + } + } } void saveRedecompMesh( int output_id ) { - for (auto& cs_pair : CouplingSchemeManager::getInstance()) - { - auto& coupling_scheme = cs_pair.second; - - if (coupling_scheme.hasMfemData()) - { - auto mfem_data = coupling_scheme.getMfemMeshData(); - auto& redecomp_mesh = mfem_data->GetRedecompMesh(); - std::string dc_name("redecomp_cs" + std::to_string(cs_pair.first) + "_id" - + std::to_string(output_id) + "_rank" - + std::to_string(redecomp_mesh.getMPIUtility().MyRank())); - mfem::VisItDataCollection visit_datacoll(dc_name, &redecomp_mesh); - visit_datacoll.RegisterField("pos", redecomp_mesh.GetNodes()); - visit_datacoll.Save(); - } - } + for ( auto& cs_pair : CouplingSchemeManager::getInstance() ) { + auto& coupling_scheme = cs_pair.second; + if ( coupling_scheme.hasMfemData() ) { + auto mfem_data = coupling_scheme.getMfemMeshData(); + auto& redecomp_mesh = mfem_data->GetRedecompMesh(); + std::string dc_name( "redecomp_cs" + std::to_string( cs_pair.first ) + "_id" + std::to_string( output_id ) + + "_rank" + std::to_string( redecomp_mesh.getMPIUtility().MyRank() ) ); + mfem::VisItDataCollection visit_datacoll( dc_name, &redecomp_mesh ); + visit_datacoll.RegisterField( "pos", redecomp_mesh.GetNodes() ); + visit_datacoll.Save(); + } + } } -} // end tribol namespace +} // namespace tribol #endif /* BUILD_REDECOMP */ diff --git a/src/tribol/interface/mfem_tribol.hpp b/src/tribol/interface/mfem_tribol.hpp index ef95d79f..7648afde 100644 --- a/src/tribol/interface/mfem_tribol.hpp +++ b/src/tribol/interface/mfem_tribol.hpp @@ -15,8 +15,7 @@ // MFEM includes #include "mfem.hpp" -namespace tribol -{ +namespace tribol { /** * @brief Define and register a coupling scheme over an MFEM mesh @@ -44,24 +43,17 @@ namespace tribol * @param [in] current_coords Coordinates associated with the MFEM volume mesh * @param [in] b_attributes_1 Boundary attributes defining the first mesh * @param [in] b_attributes_2 Boundary attributes defining the second mesh - * @param [in] contact_mode - * @param [in] contact_case - * @param [in] contact_method - * @param [in] contact_model - * @param [in] enforcement_method - * @param [in] binning_method + * @param [in] contact_mode + * @param [in] contact_case + * @param [in] contact_method + * @param [in] contact_model + * @param [in] enforcement_method + * @param [in] binning_method */ -void registerMfemCouplingScheme( IndexT cs_id, - int mesh_id_1, - int mesh_id_2, - const mfem::ParMesh& mesh, - const mfem::ParGridFunction& current_coords, - std::set b_attributes_1, - std::set b_attributes_2, - ContactMode contact_mode, - ContactCase contact_case, - ContactMethod contact_method, - ContactModel contact_model, +void registerMfemCouplingScheme( IndexT cs_id, int mesh_id_1, int mesh_id_2, const mfem::ParMesh& mesh, + const mfem::ParGridFunction& current_coords, std::set b_attributes_1, + std::set b_attributes_2, ContactMode contact_mode, ContactCase contact_case, + ContactMethod contact_method, ContactModel contact_model, EnforcementMethod enforcement_method, BinningMethod binning_method = DEFAULT_BINNING_METHOD ); @@ -263,7 +255,7 @@ void getMfemGap( IndexT cs_id, mfem::Vector& g ); * @pre Coupling scheme cs_id must be registered using * registerMfemCouplingScheme() * - * @param cs_id Coupling scheme id with a registered MFEM mesh + * @param cs_id Coupling scheme id with a registered MFEM mesh * @return mfem::ParGridFunction& Nodal pressure vector defined on the * parent-linked boundary submesh */ @@ -282,7 +274,7 @@ void updateMfemParallelDecomposition(); * * @pre Coupling schemes must be registered using registerMfemCouplingScheme() * @pre Redecomp mesh must be created and up to date by calling updateMfemParallelDecomposition() - * + * * @param output_id Unique identifier in the saved file name (usually cycle number) */ void saveRedecompMesh( int output_id ); diff --git a/src/tribol/interface/simple_tribol.cpp b/src/tribol/interface/simple_tribol.cpp index f2cefbb9..f5b6429f 100644 --- a/src/tribol/interface/simple_tribol.cpp +++ b/src/tribol/interface/simple_tribol.cpp @@ -27,127 +27,98 @@ // free functions for simple API usage //------------------------------------------------------------------------------ -int Initialize(bool init_slic) +int Initialize( bool init_slic ) { - // initialize slic - if (init_slic) - { - axom::slic::finalize(); - axom::slic::initialize(); - std::string format = "[]: \n"; - axom::slic::setLoggingMsgLevel( axom::slic::message::Info ); - - axom::slic::addStreamToAllMsgLevels( - new axom::slic::GenericOutputStream( &std::cout,format ) ); - } - - return 0; + // initialize slic + if ( init_slic ) { + axom::slic::finalize(); + axom::slic::initialize(); + std::string format = "[]: \n"; + axom::slic::setLoggingMsgLevel( axom::slic::message::Info ); + + axom::slic::addStreamToAllMsgLevels( new axom::slic::GenericOutputStream( &std::cout, format ) ); + } + + return 0; } -int Finalize(bool finalize_slic) +int Finalize( bool finalize_slic ) { - // finalize tribol - tribol::finalize(); - - // finalize slic - if(finalize_slic) - { - axom::slic::finalize(); - } - - return 0; + // finalize tribol + tribol::finalize(); + + // finalize slic + if ( finalize_slic ) { + axom::slic::finalize(); + } + + return 0; } -void SimpleCouplingSetup( const int dim, - int cell_type, - int contact_method, - int mortar_numCells, - int mortar_lengthNodalData, - const int* mortar_connectivity, - const double* mortar_x, - const double* mortar_y, - const double* mortar_z, - int nonmortar_numCells, - int nonmortar_lengthNodalData, - const int* nonmortar_connectivity, - const double* nonmortar_x, - const double* nonmortar_y, - const double* nonmortar_z, - const double area_frac, - double* mortar_gaps, - double* mortar_pressures) +void SimpleCouplingSetup( const int dim, int cell_type, int contact_method, int mortar_numCells, + int mortar_lengthNodalData, const int* mortar_connectivity, const double* mortar_x, + const double* mortar_y, const double* mortar_z, int nonmortar_numCells, + int nonmortar_lengthNodalData, const int* nonmortar_connectivity, const double* nonmortar_x, + const double* nonmortar_y, const double* nonmortar_z, const double area_frac, + double* mortar_gaps, double* mortar_pressures ) { - (void)dim; // quiet compiler - - if (contact_method != tribol::MORTAR_WEIGHTS) - { - SLIC_ERROR( "SimpleCouplingSetup: simple API only works " << - "for MORTAR_WEIGHTS method." ); - } - - // register mortar mesh - int mortarMeshId = 0; - tribol::registerMesh( mortarMeshId, mortar_numCells, - mortar_lengthNodalData, - mortar_connectivity, cell_type, - mortar_x, mortar_y, mortar_z, tribol::MemorySpace::Host ); - - // register nonmortar mesh - int nonmortarMeshId = 1; - tribol::registerMesh( nonmortarMeshId, nonmortar_numCells, - nonmortar_lengthNodalData, - nonmortar_connectivity, cell_type, - nonmortar_x, nonmortar_y, nonmortar_z, tribol::MemorySpace::Host ); - - // Register mortar gaps and pressures, if provided - if( mortar_gaps != nullptr) - { - tribol::registerMortarGaps( nonmortarMeshId, mortar_gaps); - } - if( mortar_pressures != nullptr) - { - tribol::registerMortarPressures( nonmortarMeshId, mortar_pressures); - } - - // note the use of NULL_ENFORCEMENT reflects that this routine is used - // to initially setup tests for MORTAR_WEIGHTS only! - tribol::registerCouplingScheme( 0, mortarMeshId, nonmortarMeshId, - tribol::SURFACE_TO_SURFACE, - tribol::AUTO, - contact_method, - tribol::NULL_MODEL, - tribol::NULL_ENFORCEMENT, - tribol::DEFAULT_BINNING_METHOD, - tribol::ExecutionMode::Sequential ); - - // set contact area fraction - tribol::setContactAreaFrac( 0, area_frac ); - tribol::setPlotCycleIncrement( 0, 1 ); - - // set enforcement options for MORTAR_WEIGHTS - tribol::setLagrangeMultiplierOptions( 0, tribol::ImplicitEvalMode::MORTAR_WEIGHTS_EVAL, - tribol::SparseMode::MFEM_LINKED_LIST ); - - axom::slic::flushStreams(); - - return; + (void)dim; // quiet compiler + + if ( contact_method != tribol::MORTAR_WEIGHTS ) { + SLIC_ERROR( "SimpleCouplingSetup: simple API only works " + << "for MORTAR_WEIGHTS method." ); + } + + // register mortar mesh + int mortarMeshId = 0; + tribol::registerMesh( mortarMeshId, mortar_numCells, mortar_lengthNodalData, mortar_connectivity, cell_type, mortar_x, + mortar_y, mortar_z, tribol::MemorySpace::Host ); + + // register nonmortar mesh + int nonmortarMeshId = 1; + tribol::registerMesh( nonmortarMeshId, nonmortar_numCells, nonmortar_lengthNodalData, nonmortar_connectivity, + cell_type, nonmortar_x, nonmortar_y, nonmortar_z, tribol::MemorySpace::Host ); + + // Register mortar gaps and pressures, if provided + if ( mortar_gaps != nullptr ) { + tribol::registerMortarGaps( nonmortarMeshId, mortar_gaps ); + } + if ( mortar_pressures != nullptr ) { + tribol::registerMortarPressures( nonmortarMeshId, mortar_pressures ); + } + + // note the use of NULL_ENFORCEMENT reflects that this routine is used + // to initially setup tests for MORTAR_WEIGHTS only! + tribol::registerCouplingScheme( 0, mortarMeshId, nonmortarMeshId, tribol::SURFACE_TO_SURFACE, tribol::AUTO, + contact_method, tribol::NULL_MODEL, tribol::NULL_ENFORCEMENT, + tribol::DEFAULT_BINNING_METHOD, tribol::ExecutionMode::Sequential ); + + // set contact area fraction + tribol::setContactAreaFrac( 0, area_frac ); + tribol::setPlotCycleIncrement( 0, 1 ); + + // set enforcement options for MORTAR_WEIGHTS + tribol::setLagrangeMultiplierOptions( 0, tribol::ImplicitEvalMode::MORTAR_WEIGHTS_EVAL, + tribol::SparseMode::MFEM_LINKED_LIST ); + + axom::slic::flushStreams(); + + return; } //------------------------------------------------------------------------------ -int Update( double &dt ) +int Update( double& dt ) { - int err = tribol::update( 1, 1., dt ); + int err = tribol::update( 1, 1., dt ); - axom::slic::flushStreams(); + axom::slic::flushStreams(); - return err; + return err; } //------------------------------------------------------------------------------ -int GetSimpleCouplingCSR( int** I, int** J, double** vals, - int* n_offsets, int* n_nonzeros ) +int GetSimpleCouplingCSR( int** I, int** J, double** vals, int* n_offsets, int* n_nonzeros ) { - int err = tribol::getJacobianCSRMatrix( I, J, vals, 0, n_offsets, n_nonzeros ); - return err; + int err = tribol::getJacobianCSRMatrix( I, J, vals, 0, n_offsets, n_nonzeros ); + return err; } - diff --git a/src/tribol/interface/simple_tribol.hpp b/src/tribol/interface/simple_tribol.hpp index 5b1912fb..4130388d 100644 --- a/src/tribol/interface/simple_tribol.hpp +++ b/src/tribol/interface/simple_tribol.hpp @@ -10,8 +10,6 @@ #include - - //------------------------------------------------------------------------------ // free functions for simple API usage //------------------------------------------------------------------------------ @@ -22,20 +20,19 @@ * \brief Initializes tribol and optionally initializes slic (logging library) * * \param [in] init_slic indicates if we should initialize slic - * \return 0 if no error has occurred + * \return 0 if no error has occurred * */ -int Initialize(bool init_slic = true); +int Initialize( bool init_slic = true ); /*! * \brief Finalizes tribol and optionally finalizes slic (logging library) * * \param [in] finalize_slic indicates if we should finalize slic - * \return 0 if no error has occurred + * \return 0 if no error has occurred * */ -int Finalize(bool finalize_slic = true); - +int Finalize( bool finalize_slic = true ); /*! * \brief Simple coupling setup @@ -59,25 +56,12 @@ int Finalize(bool finalize_slic = true); * \param [in] mortar_gaps (optional) pointer to nodal mortar gap scalar field * \param [in] mortar_pressures (optional) pointer to nodal mortar pressures scalar field */ -void SimpleCouplingSetup( const int dim, - int cell_type, - int contact_method, - int mortar_numCells, - int mortar_lengthNodalData, - const int* mortar_connectivity, - const double* mortar_x, - const double* mortar_y, - const double* mortar_z, - int nonmortar_numCells, - int nonmortar_lengthNodalData, - const int* nonmortar_connectivity, - const double* nonmortar_x, - const double* nonmortar_y, - const double* nonmortar_z, - const double area_frac = 1.e-3, - double* mortar_gaps = nullptr, - double* mortar_pressures = nullptr - ); +void SimpleCouplingSetup( const int dim, int cell_type, int contact_method, int mortar_numCells, + int mortar_lengthNodalData, const int* mortar_connectivity, const double* mortar_x, + const double* mortar_y, const double* mortar_z, int nonmortar_numCells, + int nonmortar_lengthNodalData, const int* nonmortar_connectivity, const double* nonmortar_x, + const double* nonmortar_y, const double* nonmortar_z, const double area_frac = 1.e-3, + double* mortar_gaps = nullptr, double* mortar_pressures = nullptr ); /*! * \brief Update per registered contact method @@ -87,7 +71,7 @@ void SimpleCouplingSetup( const int dim, * \return 0 if no error has occurred and any update to timestep * */ -int Update( double &dt); +int Update( double& dt ); /*! * \brief Gets the CSR data from the coupling scheme @@ -101,8 +85,7 @@ int Update( double &dt); * \return 0 for success, 1 for failure * */ -int GetSimpleCouplingCSR( int** I, int** J, double** vals, - int* n_offsets, int* n_nonzeros ); +int GetSimpleCouplingCSR( int** I, int** J, double** vals, int* n_offsets, int* n_nonzeros ); /// @} diff --git a/src/tribol/interface/tribol.cpp b/src/tribol/interface/tribol.cpp index 19464286..8ee49278 100644 --- a/src/tribol/interface/tribol.cpp +++ b/src/tribol/interface/tribol.cpp @@ -33,958 +33,779 @@ //------------------------------------------------------------------------------ // Interface Implementation //------------------------------------------------------------------------------ -namespace tribol -{ +namespace tribol { //------------------------------------------------------------------------------ void initialize( int, CommT ) { - SLIC_WARNING_ROOT("Initialization of Tribol is no longer needed. Dimension\n" - "is set by registered meshes and MPI communicator is stored on the\n" - "coupling scheme (see setMPIComm())."); + SLIC_WARNING_ROOT( + "Initialization of Tribol is no longer needed. Dimension\n" + "is set by registered meshes and MPI communicator is stored on the\n" + "coupling scheme (see setMPIComm())." ); } //------------------------------------------------------------------------------ void setMPIComm( IndexT cs_id, CommT comm ) { - auto cs = CouplingSchemeManager::getInstance().findData(cs_id); - - // check to see if coupling scheme exists - SLIC_ERROR_ROOT_IF( !cs, - "tribol::setMPIComm(): call tribol::registerCouplingScheme() " << - "prior to calling this routine." ); - - cs->setMPIComm(comm); + auto cs = CouplingSchemeManager::getInstance().findData( cs_id ); + + // check to see if coupling scheme exists + SLIC_ERROR_ROOT_IF( !cs, "tribol::setMPIComm(): call tribol::registerCouplingScheme() " + << "prior to calling this routine." ); + + cs->setMPIComm( comm ); } //------------------------------------------------------------------------------ void setPenaltyOptions( IndexT cs_id, PenaltyConstraintType pen_enfrc_option, - KinematicPenaltyCalculation kinematic_calc, - RatePenaltyCalculation rate_calc ) -{ - auto cs = CouplingSchemeManager::getInstance().findData(cs_id); - - // check to see if coupling scheme exists - SLIC_ERROR_ROOT_IF( !cs, - "tribol::setPenaltyOptions(): call tribol::registerCouplingScheme() " << - "prior to calling this routine." ); - - // get access to struct on coupling scheme holding penalty options - EnforcementOptions& enforcement_options = cs->getEnforcementOptions(); - PenaltyEnforcementOptions& penalty_options = enforcement_options.penalty_options; - - // check that penalty enforcement option is valid - if ( !in_range(pen_enfrc_option, NUM_PENALTY_OPTIONS) ) - { - SLIC_WARNING_ROOT( "tribol::setPenaltyOptions(): penalty enforcement option not available." ); - } - else - { - penalty_options.constraint_type = pen_enfrc_option; - penalty_options.constraint_type_set = true; - } - - // check that kinematic penalty calculation is valid - if ( !in_range(kinematic_calc, NUM_KINEMATIC_PENALTY_CALCULATION) ) - { - SLIC_WARNING_ROOT( "tribol::setPenaltyOptions(): kinematic penalty calculation not available." ); - } - else - { - penalty_options.kinematic_calculation = kinematic_calc; - penalty_options.kinematic_calc_set = true; - } - - // check that the rate penalty calculation is valid - if ( !in_range(rate_calc, NUM_RATE_PENALTY_CALCULATION) ) - { - SLIC_WARNING_ROOT( "tribol::setPenaltyOptions(): rate penalty calculation not available." ); - } - else - { - penalty_options.rate_calculation = rate_calc; - penalty_options.rate_calc_set = true; - } - -} // end setPenaltyOptions() + KinematicPenaltyCalculation kinematic_calc, RatePenaltyCalculation rate_calc ) +{ + auto cs = CouplingSchemeManager::getInstance().findData( cs_id ); + + // check to see if coupling scheme exists + SLIC_ERROR_ROOT_IF( !cs, "tribol::setPenaltyOptions(): call tribol::registerCouplingScheme() " + << "prior to calling this routine." ); + + // get access to struct on coupling scheme holding penalty options + EnforcementOptions& enforcement_options = cs->getEnforcementOptions(); + PenaltyEnforcementOptions& penalty_options = enforcement_options.penalty_options; + + // check that penalty enforcement option is valid + if ( !in_range( pen_enfrc_option, NUM_PENALTY_OPTIONS ) ) { + SLIC_WARNING_ROOT( "tribol::setPenaltyOptions(): penalty enforcement option not available." ); + } else { + penalty_options.constraint_type = pen_enfrc_option; + penalty_options.constraint_type_set = true; + } + + // check that kinematic penalty calculation is valid + if ( !in_range( kinematic_calc, NUM_KINEMATIC_PENALTY_CALCULATION ) ) { + SLIC_WARNING_ROOT( "tribol::setPenaltyOptions(): kinematic penalty calculation not available." ); + } else { + penalty_options.kinematic_calculation = kinematic_calc; + penalty_options.kinematic_calc_set = true; + } + + // check that the rate penalty calculation is valid + if ( !in_range( rate_calc, NUM_RATE_PENALTY_CALCULATION ) ) { + SLIC_WARNING_ROOT( "tribol::setPenaltyOptions(): rate penalty calculation not available." ); + } else { + penalty_options.rate_calculation = rate_calc; + penalty_options.rate_calc_set = true; + } + +} // end setPenaltyOptions() //------------------------------------------------------------------------------ void setKinematicConstantPenalty( IndexT mesh_id, RealT k ) { - // note, error checking done in the following registration routine - registerRealElementField( mesh_id, KINEMATIC_CONSTANT_STIFFNESS, &k ); + // note, error checking done in the following registration routine + registerRealElementField( mesh_id, KINEMATIC_CONSTANT_STIFFNESS, &k ); -} // end setKinematicConstantPenalty() +} // end setKinematicConstantPenalty() //------------------------------------------------------------------------------ -void setKinematicElementPenalty( IndexT mesh_id, - const RealT *material_modulus, - const RealT *element_thickness ) +void setKinematicElementPenalty( IndexT mesh_id, const RealT* material_modulus, const RealT* element_thickness ) { - // note, error checking done in the following registration routine - registerRealElementField( mesh_id, BULK_MODULUS, material_modulus ); - registerRealElementField( mesh_id, ELEMENT_THICKNESS, element_thickness ); + // note, error checking done in the following registration routine + registerRealElementField( mesh_id, BULK_MODULUS, material_modulus ); + registerRealElementField( mesh_id, ELEMENT_THICKNESS, element_thickness ); -} // end setKinematicElementPenalty() +} // end setKinematicElementPenalty() //------------------------------------------------------------------------------ void setRateConstantPenalty( IndexT mesh_id, RealT r_k ) { - // note, error checking done in the following registration routine - registerRealElementField( mesh_id, RATE_CONSTANT_STIFFNESS, &r_k ); + // note, error checking done in the following registration routine + registerRealElementField( mesh_id, RATE_CONSTANT_STIFFNESS, &r_k ); -} // end setRateConstantPenalty() +} // end setRateConstantPenalty() //------------------------------------------------------------------------------ void setRatePercentPenalty( IndexT mesh_id, RealT r_p ) { - // note, error checking done in the following registration routine - registerRealElementField( mesh_id, RATE_PERCENT_STIFFNESS, &r_p ); + // note, error checking done in the following registration routine + registerRealElementField( mesh_id, RATE_PERCENT_STIFFNESS, &r_p ); -} // end setRatePercentPenalty() +} // end setRatePercentPenalty() //------------------------------------------------------------------------------ void setAutoContactPenScale( IndexT cs_id, RealT scale ) { - auto cs = CouplingSchemeManager::getInstance().findData(cs_id); - - // check to see if coupling scheme exists - SLIC_ERROR_ROOT_IF( !cs, - "tribol::setAutoContactPenScale(): call tribol::registerCouplingScheme() " << - "prior to calling this routine." ); + auto cs = CouplingSchemeManager::getInstance().findData( cs_id ); + + // check to see if coupling scheme exists + SLIC_ERROR_ROOT_IF( !cs, "tribol::setAutoContactPenScale(): call tribol::registerCouplingScheme() " + << "prior to calling this routine." ); - // check for strict positivity of the input parameter - SLIC_WARNING_ROOT_IF(scale<0., "tribol::setAutoContactPenScale(): " << - "input for the auto-contact length scale factor must be positive."); + // check for strict positivity of the input parameter + SLIC_WARNING_ROOT_IF( scale < 0., "tribol::setAutoContactPenScale(): " + << "input for the auto-contact length scale factor must be positive." ); - cs->getParameters().auto_contact_pen_frac = scale; + cs->getParameters().auto_contact_pen_frac = scale; -} // end setAutoContactPenScale() +} // end setAutoContactPenScale() //------------------------------------------------------------------------------ void setTimestepPenFrac( IndexT cs_id, RealT frac ) { - auto cs = CouplingSchemeManager::getInstance().findData(cs_id); - - // check to see if coupling scheme exists - SLIC_ERROR_ROOT_IF( !cs, - "tribol::setTimestepPenFrac(): call tribol::registerCouplingScheme() " << - "prior to calling this routine." ); + auto cs = CouplingSchemeManager::getInstance().findData( cs_id ); + + // check to see if coupling scheme exists + SLIC_ERROR_ROOT_IF( !cs, "tribol::setTimestepPenFrac(): call tribol::registerCouplingScheme() " + << "prior to calling this routine." ); - if (frac <= 0.) - { - // Don't set the timestep_pen_frac. This will use default - return; - } + if ( frac <= 0. ) { + // Don't set the timestep_pen_frac. This will use default + return; + } - cs->getParameters().timestep_pen_frac = frac; + cs->getParameters().timestep_pen_frac = frac; -} // end setTimestepPenFrac() +} // end setTimestepPenFrac() //------------------------------------------------------------------------------ void setTimestepScale( IndexT cs_id, RealT scale ) { - if (scale <= 0.) - { - // Don't set the timestep_scale. This will use default - return; - } + if ( scale <= 0. ) { + // Don't set the timestep_scale. This will use default + return; + } - auto cs = CouplingSchemeManager::getInstance().findData(cs_id); - - // check to see if coupling scheme exists - SLIC_ERROR_ROOT_IF( !cs, - "tribol::setTimestepScale(): call tribol::registerCouplingScheme() " << - "prior to calling this routine." ); + auto cs = CouplingSchemeManager::getInstance().findData( cs_id ); - cs->getParameters().timestep_scale = scale; + // check to see if coupling scheme exists + SLIC_ERROR_ROOT_IF( !cs, "tribol::setTimestepScale(): call tribol::registerCouplingScheme() " + << "prior to calling this routine." ); + + cs->getParameters().timestep_scale = scale; } //------------------------------------------------------------------------------ void setContactAreaFrac( IndexT cs_id, RealT frac ) { - auto cs = CouplingSchemeManager::getInstance().findData(cs_id); - - // check to see if coupling scheme exists - SLIC_ERROR_ROOT_IF( !cs, - "tribol::setContactAreaFrac(): call tribol::registerCouplingScheme() " << - "prior to calling this routine." ); + auto cs = CouplingSchemeManager::getInstance().findData( cs_id ); + + // check to see if coupling scheme exists + SLIC_ERROR_ROOT_IF( !cs, "tribol::setContactAreaFrac(): call tribol::registerCouplingScheme() " + << "prior to calling this routine." ); - if (frac <= 0.0) - { - SLIC_DEBUG_ROOT("tribol::setContactAreaFrac(): area fraction <= 0.0; " << - "setting to default 1.e-8."); - frac = 1.e-8; - } - cs->getParameters().overlap_area_frac = frac; + if ( frac <= 0.0 ) { + SLIC_DEBUG_ROOT( "tribol::setContactAreaFrac(): area fraction <= 0.0; " + << "setting to default 1.e-8." ); + frac = 1.e-8; + } + cs->getParameters().overlap_area_frac = frac; -} // end setPenaltyScale() +} // end setPenaltyScale() //------------------------------------------------------------------------------ void setPenaltyScale( IndexT mesh_id, RealT scale ) { - auto mesh = MeshManager::getInstance().findData(mesh_id); + auto mesh = MeshManager::getInstance().findData( mesh_id ); - SLIC_ERROR_ROOT_IF(!mesh, - "tribol::setPenaltyScale(): " << - "no mesh with id, " << mesh_id << "exists."); + SLIC_ERROR_ROOT_IF( !mesh, "tribol::setPenaltyScale(): " + << "no mesh with id, " << mesh_id << "exists." ); - if (scale > 1.e-6) - { - mesh->getElementData().m_penalty_scale = scale; - } - else - { - // still set small penalty to allow for zeroing out kinematic penalty - // enforcement allowing for rate only enforcement - mesh->getElementData().m_penalty_scale = scale; - SLIC_WARNING_ROOT("tribol::setPenaltyScale(): input scale factor is " << - "close to zero or negative; kinematic contact may " << - "not be properly enforced."); - } + if ( scale > 1.e-6 ) { + mesh->getElementData().m_penalty_scale = scale; + } else { + // still set small penalty to allow for zeroing out kinematic penalty + // enforcement allowing for rate only enforcement + mesh->getElementData().m_penalty_scale = scale; + SLIC_WARNING_ROOT( "tribol::setPenaltyScale(): input scale factor is " + << "close to zero or negative; kinematic contact may " + << "not be properly enforced." ); + } -} // end setPenaltyScale() +} // end setPenaltyScale() //------------------------------------------------------------------------------ -void setLagrangeMultiplierOptions( IndexT cs_id, ImplicitEvalMode evalMode, - SparseMode sparseMode ) +void setLagrangeMultiplierOptions( IndexT cs_id, ImplicitEvalMode evalMode, SparseMode sparseMode ) { - // get access to coupling scheme - auto cs = CouplingSchemeManager::getInstance().findData(cs_id); + // get access to coupling scheme + auto cs = CouplingSchemeManager::getInstance().findData( cs_id ); - SLIC_ERROR_ROOT_IF( !cs, - "tribol::setLagrangeMultiplierOptions(): call tribol::registerCouplingScheme() " << - "prior to calling this routine." ); + SLIC_ERROR_ROOT_IF( !cs, "tribol::setLagrangeMultiplierOptions(): call tribol::registerCouplingScheme() " + << "prior to calling this routine." ); - // get access to struct on coupling scheme holding penalty options - EnforcementOptions& enforcement_options = cs->getEnforcementOptions(); - LagrangeMultiplierImplicitOptions& lm_options = enforcement_options.lm_implicit_options; + // get access to struct on coupling scheme holding penalty options + EnforcementOptions& enforcement_options = cs->getEnforcementOptions(); + LagrangeMultiplierImplicitOptions& lm_options = enforcement_options.lm_implicit_options; - lm_options.eval_mode = evalMode; - lm_options.sparse_mode = sparseMode; + lm_options.eval_mode = evalMode; + lm_options.sparse_mode = sparseMode; #ifdef BUILD_REDECOMP - if (cs->hasMfemData()) - { - // MFEM_ELEMENT_DENSE is required to use the MFEM interface - lm_options.sparse_mode = SparseMode::MFEM_ELEMENT_DENSE; - if ( - !cs->hasMfemJacobianData() && ( - lm_options.eval_mode == ImplicitEvalMode::MORTAR_JACOBIAN || - lm_options.eval_mode == ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN - ) - ) - { - cs->setMfemJacobianData(std::make_unique( - *cs->getMfemMeshData(), - *cs->getMfemSubmeshData(), - cs->getContactMethod() - )); - } - } + if ( cs->hasMfemData() ) { + // MFEM_ELEMENT_DENSE is required to use the MFEM interface + lm_options.sparse_mode = SparseMode::MFEM_ELEMENT_DENSE; + if ( !cs->hasMfemJacobianData() && ( lm_options.eval_mode == ImplicitEvalMode::MORTAR_JACOBIAN || + lm_options.eval_mode == ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN ) ) { + cs->setMfemJacobianData( std::make_unique( *cs->getMfemMeshData(), *cs->getMfemSubmeshData(), + cs->getContactMethod() ) ); + } + } #endif /* BUILD_REDECOMP */ - lm_options.enforcement_option_set = true; + lm_options.enforcement_option_set = true; -} // end setLagrangeMultiplierOptions() +} // end setLagrangeMultiplierOptions() //------------------------------------------------------------------------------ void setPlotCycleIncrement( IndexT cs_id, int incr ) { - auto cs = CouplingSchemeManager::getInstance().findData(cs_id); - - // check to see if coupling scheme exists - SLIC_ERROR_ROOT_IF( !cs, - "tribol::setPlotCycleIncrement(): call tribol::registerCouplingScheme() " << - "prior to calling this routine." ); + auto cs = CouplingSchemeManager::getInstance().findData( cs_id ); - cs->getParameters().vis_cycle_incr = incr; + // check to see if coupling scheme exists + SLIC_ERROR_ROOT_IF( !cs, "tribol::setPlotCycleIncrement(): call tribol::registerCouplingScheme() " + << "prior to calling this routine." ); -} // end setPlotCycleIncrement() + cs->getParameters().vis_cycle_incr = incr; + +} // end setPlotCycleIncrement() //------------------------------------------------------------------------------ void setPlotOptions( IndexT cs_id, enum VisType v_type ) { - auto cs = CouplingSchemeManager::getInstance().findData(cs_id); - - // check to see if coupling scheme exists - SLIC_ERROR_ROOT_IF( !cs, - "tribol::setPlotOptions(): call tribol::registerCouplingScheme() " << - "prior to calling this routine." ); + auto cs = CouplingSchemeManager::getInstance().findData( cs_id ); + + // check to see if coupling scheme exists + SLIC_ERROR_ROOT_IF( !cs, "tribol::setPlotOptions(): call tribol::registerCouplingScheme() " + << "prior to calling this routine." ); - cs->getParameters().vis_type = v_type; + cs->getParameters().vis_type = v_type; -} // end setPlotOptions() +} // end setPlotOptions() //------------------------------------------------------------------------------ -void setOutputDirectory( IndexT cs_id, const std::string& dir) +void setOutputDirectory( IndexT cs_id, const std::string& dir ) { - auto cs = CouplingSchemeManager::getInstance().findData(cs_id); - - // check to see if coupling scheme exists - SLIC_ERROR_ROOT_IF( !cs, - "tribol::setOutputDirectory(): call tribol::registerCouplingScheme() " << - "prior to calling this routine." ); + auto cs = CouplingSchemeManager::getInstance().findData( cs_id ); + + // check to see if coupling scheme exists + SLIC_ERROR_ROOT_IF( !cs, "tribol::setOutputDirectory(): call tribol::registerCouplingScheme() " + << "prior to calling this routine." ); - // Create path if it doesn't already exist - if(! axom::utilities::filesystem::pathExists(dir) ) - { - SLIC_INFO_ROOT("Creating output path '" << dir << "'"); - axom::utilities::filesystem::makeDirsForPath(dir); - } + // Create path if it doesn't already exist + if ( !axom::utilities::filesystem::pathExists( dir ) ) { + SLIC_INFO_ROOT( "Creating output path '" << dir << "'" ); + axom::utilities::filesystem::makeDirsForPath( dir ); + } - cs->setOutputDirectory(dir); + cs->setOutputDirectory( dir ); -} // end setOutputDirectory() +} // end setOutputDirectory() //------------------------------------------------------------------------------ void setLoggingLevel( IndexT cs_id, LoggingLevel log_level ) { - // get access to coupling scheme - auto cs = CouplingSchemeManager::getInstance().findData(cs_id); + // get access to coupling scheme + auto cs = CouplingSchemeManager::getInstance().findData( cs_id ); - SLIC_ERROR_IF(!cs, "tribol::setLoggingLevel(): " << - "invalid CouplingScheme id."); + SLIC_ERROR_IF( !cs, "tribol::setLoggingLevel(): " + << "invalid CouplingScheme id." ); - if ( !in_range(static_cast(log_level), - static_cast(tribol::NUM_LOGGING_LEVELS)) ) - { - SLIC_INFO_ROOT("tribol::setLoggingLevel(): Logging level not an option; " << - "using 'warning' level."); - cs->setLoggingLevel( tribol::TRIBOL_WARNING ); - } - else - { - cs->setLoggingLevel( log_level ); - } + if ( !in_range( static_cast( log_level ), static_cast( tribol::NUM_LOGGING_LEVELS ) ) ) { + SLIC_INFO_ROOT( "tribol::setLoggingLevel(): Logging level not an option; " + << "using 'warning' level." ); + cs->setLoggingLevel( tribol::TRIBOL_WARNING ); + } else { + cs->setLoggingLevel( log_level ); + } -} // end setLoggingLevel() +} // end setLoggingLevel() //------------------------------------------------------------------------------ void enableTimestepVote( IndexT cs_id, const bool enable ) { - auto cs = CouplingSchemeManager::getInstance().findData(cs_id); - - // check to see if coupling scheme exists - SLIC_ERROR_ROOT_IF( !cs, - "tribol::enableTimestepVote(): call tribol::registerCouplingScheme() " << - "prior to calling this routine." ); + auto cs = CouplingSchemeManager::getInstance().findData( cs_id ); - cs->getParameters().enable_timestep_vote= enable; + // check to see if coupling scheme exists + SLIC_ERROR_ROOT_IF( !cs, "tribol::enableTimestepVote(): call tribol::registerCouplingScheme() " + << "prior to calling this routine." ); -} // end enableTimestepVote() + cs->getParameters().enable_timestep_vote = enable; + +} // end enableTimestepVote() //------------------------------------------------------------------------------ -void registerMesh( IndexT mesh_id, - IndexT num_elements, - IndexT num_nodes, - const IndexT* connectivity, - int element_type, - const RealT* x, - const RealT* y, - const RealT* z, - MemorySpace mem_space ) +void registerMesh( IndexT mesh_id, IndexT num_elements, IndexT num_nodes, const IndexT* connectivity, int element_type, + const RealT* x, const RealT* y, const RealT* z, MemorySpace mem_space ) { - MeshManager::getInstance().addData(mesh_id, MeshData( - mesh_id, num_elements, num_nodes, connectivity, - static_cast(element_type), x, y, z, mem_space)); -} // end registerMesh() + MeshManager::getInstance().addData( + mesh_id, MeshData( mesh_id, num_elements, num_nodes, connectivity, + static_cast( element_type ), x, y, z, mem_space ) ); +} // end registerMesh() //------------------------------------------------------------------------------ -void registerNodalDisplacements( IndexT mesh_id, - const RealT* dx, - const RealT* dy, - const RealT* dz ) +void registerNodalDisplacements( IndexT mesh_id, const RealT* dx, const RealT* dy, const RealT* dz ) { - auto mesh = MeshManager::getInstance().findData(mesh_id); + auto mesh = MeshManager::getInstance().findData( mesh_id ); - SLIC_ERROR_ROOT_IF(!mesh, "tribol::registerNodalDisplacements(): " << - "no mesh with id, " << mesh_id << "exists."); + SLIC_ERROR_ROOT_IF( !mesh, "tribol::registerNodalDisplacements(): " + << "no mesh with id, " << mesh_id << "exists." ); - mesh->getNodalFields().m_is_nodal_displacement_set = true; + mesh->getNodalFields().m_is_nodal_displacement_set = true; - if (dx == nullptr || dy == nullptr) - { - mesh->getNodalFields().m_is_nodal_displacement_set = false; - } + if ( dx == nullptr || dy == nullptr ) { + mesh->getNodalFields().m_is_nodal_displacement_set = false; + } - if (mesh->spatialDimension() == 3) - { - if (dz == nullptr) - { - mesh->getNodalFields().m_is_nodal_displacement_set = false; - } - } + if ( mesh->spatialDimension() == 3 ) { + if ( dz == nullptr ) { + mesh->getNodalFields().m_is_nodal_displacement_set = false; + } + } - mesh->setDisplacement(dx, dy, dz); + mesh->setDisplacement( dx, dy, dz ); -} // end registerNodalDisplacements() +} // end registerNodalDisplacements() //------------------------------------------------------------------------------ -void registerNodalVelocities( IndexT mesh_id, - const RealT* vx, - const RealT* vy, - const RealT* vz ) +void registerNodalVelocities( IndexT mesh_id, const RealT* vx, const RealT* vy, const RealT* vz ) { - auto mesh = MeshManager::getInstance().findData(mesh_id); + auto mesh = MeshManager::getInstance().findData( mesh_id ); + + SLIC_ERROR_ROOT_IF( !mesh, "tribol::registerNodalVelocities(): " + << "no mesh with id, " << mesh_id << "exists." ); - SLIC_ERROR_ROOT_IF(!mesh, "tribol::registerNodalVelocities(): " << - "no mesh with id, " << mesh_id << "exists."); + mesh->getNodalFields().m_is_velocity_set = true; - mesh->getNodalFields().m_is_velocity_set = true; + if ( vx == nullptr || vy == nullptr ) { + mesh->getNodalFields().m_is_velocity_set = false; + } - if (vx == nullptr || vy == nullptr) - { + if ( mesh->spatialDimension() == 3 ) { + if ( vz == nullptr ) { mesh->getNodalFields().m_is_velocity_set = false; - } - - if (mesh->spatialDimension() == 3) - { - if (vz == nullptr) - { - mesh->getNodalFields().m_is_velocity_set = false; - } - } + } + } - mesh->setVelocity(vx, vy, vz); + mesh->setVelocity( vx, vy, vz ); -} // end registerNodalVelocities() +} // end registerNodalVelocities() //------------------------------------------------------------------------------ -void registerNodalResponse( IndexT mesh_id, - RealT* rx, - RealT* ry, - RealT* rz ) +void registerNodalResponse( IndexT mesh_id, RealT* rx, RealT* ry, RealT* rz ) { - auto mesh = MeshManager::getInstance().findData(mesh_id); + auto mesh = MeshManager::getInstance().findData( mesh_id ); - SLIC_ERROR_ROOT_IF(!mesh, "tribol::registerNodalResponse(): " << - "no mesh with id, " << mesh_id << "exists."); + SLIC_ERROR_ROOT_IF( !mesh, "tribol::registerNodalResponse(): " + << "no mesh with id, " << mesh_id << "exists." ); - mesh->getNodalFields().m_is_nodal_response_set = true; - if (rx == nullptr || ry == nullptr) - { - mesh->getNodalFields().m_is_nodal_response_set = false; - } + mesh->getNodalFields().m_is_nodal_response_set = true; + if ( rx == nullptr || ry == nullptr ) { + mesh->getNodalFields().m_is_nodal_response_set = false; + } - if (mesh->spatialDimension() == 3) - { - if (rz == nullptr) - { - mesh->getNodalFields().m_is_nodal_response_set = false; - } - } + if ( mesh->spatialDimension() == 3 ) { + if ( rz == nullptr ) { + mesh->getNodalFields().m_is_nodal_response_set = false; + } + } - mesh->setResponse(rx, ry, rz); + mesh->setResponse( rx, ry, rz ); -} // end registerNodalResponse() +} // end registerNodalResponse() //------------------------------------------------------------------------------ -int getJacobianSparseMatrix( mfem::SparseMatrix ** sMat, IndexT cs_id ) +int getJacobianSparseMatrix( mfem::SparseMatrix** sMat, IndexT cs_id ) { - // note, SLIC_ERROR_ROOT_IF is not used here because it's possible not all ranks - // will have method (i.e. mortar) data. - SLIC_ERROR_IF(*sMat!=nullptr, "tribol::getJacobianSparseMatrix(): " << - "sparse matrix pointer not null."); + // note, SLIC_ERROR_ROOT_IF is not used here because it's possible not all ranks + // will have method (i.e. mortar) data. + SLIC_ERROR_IF( *sMat != nullptr, "tribol::getJacobianSparseMatrix(): " + << "sparse matrix pointer not null." ); - // get access to coupling scheme - auto cs = CouplingSchemeManager::getInstance().findData(cs_id); + // get access to coupling scheme + auto cs = CouplingSchemeManager::getInstance().findData( cs_id ); - SLIC_ERROR_IF(!cs, "tribol::getJacobianSparseMatrix(): " << - "invalid CouplingScheme id."); + SLIC_ERROR_IF( !cs, "tribol::getJacobianSparseMatrix(): " + << "invalid CouplingScheme id." ); - switch (cs->getContactMethod()) - { - case MORTAR_WEIGHTS: - case ALIGNED_MORTAR: - case SINGLE_MORTAR: - { - *sMat = static_cast( cs->getMethodData() )->getMfemSparseMatrix(); - return 0; - } - default: - { - SLIC_WARNING("tribol::getJacobianSparseMatrix(): interface method does not return matrix data."); - return 1; - } - } + switch ( cs->getContactMethod() ) { + case MORTAR_WEIGHTS: + case ALIGNED_MORTAR: + case SINGLE_MORTAR: { + *sMat = static_cast( cs->getMethodData() )->getMfemSparseMatrix(); + return 0; + } + default: { + SLIC_WARNING( "tribol::getJacobianSparseMatrix(): interface method does not return matrix data." ); + return 1; + } + } -} // end getJacobianSparseMatrix() +} // end getJacobianSparseMatrix() //------------------------------------------------------------------------------ -int getJacobianCSRMatrix( int** I, int** J, RealT** vals, IndexT cs_id, - int* n_offsets, int* n_nonzero ) +int getJacobianCSRMatrix( int** I, int** J, RealT** vals, IndexT cs_id, int* n_offsets, int* n_nonzero ) { - // check to make sure input pointers are null - if ( *I != nullptr || *J != nullptr || *vals != nullptr ) - { - SLIC_WARNING("tribol::getJacobianCSRMatrix(): input pointers must be null."); + // check to make sure input pointers are null + if ( *I != nullptr || *J != nullptr || *vals != nullptr ) { + SLIC_WARNING( "tribol::getJacobianCSRMatrix(): input pointers must be null." ); + return 1; + } + + // get access to coupling scheme + auto cs = CouplingSchemeManager::getInstance().findData( cs_id ); + + // Note, SLIC_<>_ROOT macros are not here because it's possible not all ranks will have + // method data. + SLIC_ERROR_IF( !cs, "tribol::getJacobianCSRMatrix(): invalid " + << "CouplingScheme id." ); + + switch ( cs->getContactMethod() ) { + case ALIGNED_MORTAR: { + SLIC_WARNING( "tribol::getJacobianCSRMatrix(): CSR format not currently implemented with " + << "ALIGNED_MORTAR. Use MFEM sparse matrix registration." ); return 1; - } - - // get access to coupling scheme - auto cs = CouplingSchemeManager::getInstance().findData(cs_id); - - // Note, SLIC_<>_ROOT macros are not here because it's possible not all ranks will have - // method data. - SLIC_ERROR_IF(!cs, "tribol::getJacobianCSRMatrix(): invalid " << - "CouplingScheme id."); - - switch (cs->getContactMethod()) - { - case ALIGNED_MORTAR: - { - SLIC_WARNING("tribol::getJacobianCSRMatrix(): CSR format not currently implemented with " << - "ALIGNED_MORTAR. Use MFEM sparse matrix registration."); - return 1; - } - case MORTAR_WEIGHTS: - { - static_cast( cs->getMethodData() )->getCSRArrays( I, J, vals, n_offsets, n_nonzero ); - return 0; - } - case SINGLE_MORTAR: - { - SLIC_WARNING("tribol::getJacobianCSRMatrix(): CSR format not currently implemented with " - "SINGLE_MORTAR. Use MFEM sparse matrix registration."); - return 1; - } - default: - { - SLIC_WARNING("tribol::getJacobianCSRMatrix(): method does not return matrix data; " << - "invalid call."); - return 1; - } - } + } + case MORTAR_WEIGHTS: { + static_cast( cs->getMethodData() )->getCSRArrays( I, J, vals, n_offsets, n_nonzero ); + return 0; + } + case SINGLE_MORTAR: { + SLIC_WARNING( + "tribol::getJacobianCSRMatrix(): CSR format not currently implemented with " + "SINGLE_MORTAR. Use MFEM sparse matrix registration." ); + return 1; + } + default: { + SLIC_WARNING( "tribol::getJacobianCSRMatrix(): method does not return matrix data; " + << "invalid call." ); + return 1; + } + } -} // end getJacobianCSRMatrix() +} // end getJacobianCSRMatrix() //------------------------------------------------------------------------------ -int getElementBlockJacobians( IndexT cs_id, - BlockSpace row_block, - BlockSpace col_block, - const axom::Array** row_elem_idx, - const axom::Array** col_elem_idx, +int getElementBlockJacobians( IndexT cs_id, BlockSpace row_block, BlockSpace col_block, + const axom::Array** row_elem_idx, const axom::Array** col_elem_idx, const axom::Array** jacobians ) { - // get access to coupling scheme - auto cs = CouplingSchemeManager::getInstance().findData(cs_id); - - // Note, SLIC_<>_ROOT macros are not here because it's possible not all ranks will have - // method data. - SLIC_ERROR_IF(!cs, "tribol::getElementBlockJacobians(): invalid " << - "CouplingScheme id."); - - SparseMode sparse_mode = cs->getEnforcementOptions().lm_implicit_options.sparse_mode; - if (sparse_mode != SparseMode::MFEM_ELEMENT_DENSE) - { - SLIC_WARNING("Jacobian is assembled and can be accessed by " - "getMfemSparseMatrix() or getCSRMatrix(). For (unassembled) element " - "Jacobian contributions, call setLagrangeMultiplierOptions() with " - "SparseMode::MFEM_ELEMENT_DENSE before calling update()."); - return 1; - } - MethodData* method_data = cs->getMethodData(); - *row_elem_idx = &method_data->getBlockJElementIds()[static_cast(row_block)]; - *col_elem_idx = &method_data->getBlockJElementIds()[static_cast(col_block)]; - *jacobians = &method_data->getBlockJ()( - static_cast(row_block), - static_cast(col_block) - ); - return 0; - -} // end getElementBlockJacobians() - -//------------------------------------------------------------------------------ -void registerMortarGaps( IndexT mesh_id, - RealT * gaps ) -{ - auto mesh = MeshManager::getInstance().findData(mesh_id); - - SLIC_ERROR_ROOT_IF(!mesh, "tribol::registerMortarGaps(): " << - "no mesh with id " << mesh_id << " exists."); - - if (gaps == nullptr && mesh->numberOfElements() > 0) - { - SLIC_WARNING( "tribol::registerMortarGaps(): null pointer to gap data " << - "on non-null mesh " << mesh_id << "."); - mesh->isMeshValid() = false; - } - else - { - mesh->getNodalFields().m_node_gap = ArrayViewT( - gaps, mesh->numberOfNodes()); - mesh->getNodalFields().m_is_node_gap_set = true; - } - -} // end registerMortarGaps() - -//------------------------------------------------------------------------------ -void registerMortarPressures( IndexT mesh_id, - const RealT * pressures ) -{ - auto mesh = MeshManager::getInstance().findData(mesh_id); - - SLIC_ERROR_ROOT_IF(!mesh, "tribol::registerMortarPressures(): " << - "no mesh with id " << mesh_id << " exists."); - - if (pressures == nullptr && mesh->numberOfElements() > 0) - { - SLIC_WARNING( "tribol::registerMortarPressures(): null pointer to pressure data " << - "on non-null mesh " << mesh_id << "."); - mesh->isMeshValid() = false; - } - else - { - mesh->getNodalFields().m_node_pressure = ArrayViewT( - pressures, mesh->numberOfNodes()); - mesh->getNodalFields().m_is_node_pressure_set = true; - } - -} // end registerMortarPressures() - -//------------------------------------------------------------------------------ -void registerIntNodalField( IndexT mesh_id, - const IntNodalFields field, - int * TRIBOL_UNUSED_PARAM(fieldVariable) ) -{ - auto mesh = MeshManager::getInstance().findData(mesh_id); - - SLIC_ERROR_ROOT_IF(!mesh, "tribol::registerIntNodalField(): " << - "no mesh with id " << mesh_id << " exists."); - - switch (field) - { - case UNDEFINED_INT_NODAL_FIELD: - default: - SLIC_ERROR_ROOT("tribol::registerIntNodalField() not yet implemented."); - } // end switch over field - -} // end registerIntNodalField() - -//------------------------------------------------------------------------------ -void registerRealElementField( IndexT mesh_id, - const RealElementFields field, - const RealT * fieldVariable ) -{ - auto mesh = MeshManager::getInstance().findData(mesh_id); - - SLIC_ERROR_IF(!mesh, "tribol::registerRealElementField(): " << - "no mesh with id " << mesh_id << " exists."); - - switch (field) - { - case KINEMATIC_CONSTANT_STIFFNESS: - { - if (fieldVariable==nullptr) - { - if (mesh->numberOfElements()>0) - { - SLIC_ERROR( "tribol::registerRealElementField(): null pointer to data for " << - "'KINEMATIC_CONSTANT_STIFFNESS' on mesh " << mesh_id << "."); - mesh->getElementData().m_is_kinematic_constant_penalty_set = false; - } - else - { - mesh->getElementData().m_is_kinematic_constant_penalty_set = true; - } - } - else - { - mesh->getElementData().m_penalty_stiffness = *fieldVariable; - mesh->getElementData().m_is_kinematic_constant_penalty_set = true; - } - break; - } - case RATE_CONSTANT_STIFFNESS: - { - if (fieldVariable==nullptr) - { - if (mesh->numberOfElements()>0) - { - SLIC_ERROR( "tribol::registerRealElementField(): null pointer to data for " << - "'RATE_CONSTANT_STIFFNESS' on mesh " << mesh_id << "."); - mesh->getElementData().m_is_rate_constant_penalty_set = false; - } - else - { - mesh->getElementData().m_is_rate_constant_penalty_set = true; - } - } - else - { - mesh->getElementData().m_rate_penalty_stiffness = *fieldVariable; - mesh->getElementData().m_is_rate_constant_penalty_set = true; - } - break; + // get access to coupling scheme + auto cs = CouplingSchemeManager::getInstance().findData( cs_id ); + + // Note, SLIC_<>_ROOT macros are not here because it's possible not all ranks will have + // method data. + SLIC_ERROR_IF( !cs, "tribol::getElementBlockJacobians(): invalid " + << "CouplingScheme id." ); + + SparseMode sparse_mode = cs->getEnforcementOptions().lm_implicit_options.sparse_mode; + if ( sparse_mode != SparseMode::MFEM_ELEMENT_DENSE ) { + SLIC_WARNING( + "Jacobian is assembled and can be accessed by " + "getMfemSparseMatrix() or getCSRMatrix(). For (unassembled) element " + "Jacobian contributions, call setLagrangeMultiplierOptions() with " + "SparseMode::MFEM_ELEMENT_DENSE before calling update()." ); + return 1; + } + MethodData* method_data = cs->getMethodData(); + *row_elem_idx = &method_data->getBlockJElementIds()[static_cast( row_block )]; + *col_elem_idx = &method_data->getBlockJElementIds()[static_cast( col_block )]; + *jacobians = &method_data->getBlockJ()( static_cast( row_block ), static_cast( col_block ) ); + return 0; + +} // end getElementBlockJacobians() + +//------------------------------------------------------------------------------ +void registerMortarGaps( IndexT mesh_id, RealT* gaps ) +{ + auto mesh = MeshManager::getInstance().findData( mesh_id ); + + SLIC_ERROR_ROOT_IF( !mesh, "tribol::registerMortarGaps(): " + << "no mesh with id " << mesh_id << " exists." ); + + if ( gaps == nullptr && mesh->numberOfElements() > 0 ) { + SLIC_WARNING( "tribol::registerMortarGaps(): null pointer to gap data " + << "on non-null mesh " << mesh_id << "." ); + mesh->isMeshValid() = false; + } else { + mesh->getNodalFields().m_node_gap = ArrayViewT( gaps, mesh->numberOfNodes() ); + mesh->getNodalFields().m_is_node_gap_set = true; + } + +} // end registerMortarGaps() + +//------------------------------------------------------------------------------ +void registerMortarPressures( IndexT mesh_id, const RealT* pressures ) +{ + auto mesh = MeshManager::getInstance().findData( mesh_id ); + + SLIC_ERROR_ROOT_IF( !mesh, "tribol::registerMortarPressures(): " + << "no mesh with id " << mesh_id << " exists." ); + + if ( pressures == nullptr && mesh->numberOfElements() > 0 ) { + SLIC_WARNING( "tribol::registerMortarPressures(): null pointer to pressure data " + << "on non-null mesh " << mesh_id << "." ); + mesh->isMeshValid() = false; + } else { + mesh->getNodalFields().m_node_pressure = ArrayViewT( pressures, mesh->numberOfNodes() ); + mesh->getNodalFields().m_is_node_pressure_set = true; + } + +} // end registerMortarPressures() + +//------------------------------------------------------------------------------ +void registerIntNodalField( IndexT mesh_id, const IntNodalFields field, int* TRIBOL_UNUSED_PARAM( fieldVariable ) ) +{ + auto mesh = MeshManager::getInstance().findData( mesh_id ); + + SLIC_ERROR_ROOT_IF( !mesh, "tribol::registerIntNodalField(): " + << "no mesh with id " << mesh_id << " exists." ); + + switch ( field ) { + case UNDEFINED_INT_NODAL_FIELD: + default: + SLIC_ERROR_ROOT( "tribol::registerIntNodalField() not yet implemented." ); + } // end switch over field + +} // end registerIntNodalField() + +//------------------------------------------------------------------------------ +void registerRealElementField( IndexT mesh_id, const RealElementFields field, const RealT* fieldVariable ) +{ + auto mesh = MeshManager::getInstance().findData( mesh_id ); + + SLIC_ERROR_IF( !mesh, "tribol::registerRealElementField(): " + << "no mesh with id " << mesh_id << " exists." ); + + switch ( field ) { + case KINEMATIC_CONSTANT_STIFFNESS: { + if ( fieldVariable == nullptr ) { + if ( mesh->numberOfElements() > 0 ) { + SLIC_ERROR( "tribol::registerRealElementField(): null pointer to data for " + << "'KINEMATIC_CONSTANT_STIFFNESS' on mesh " << mesh_id << "." ); + mesh->getElementData().m_is_kinematic_constant_penalty_set = false; + } else { + mesh->getElementData().m_is_kinematic_constant_penalty_set = true; + } + } else { + mesh->getElementData().m_penalty_stiffness = *fieldVariable; + mesh->getElementData().m_is_kinematic_constant_penalty_set = true; } - case RATE_PERCENT_STIFFNESS: - { - if (fieldVariable==nullptr) - { - if (mesh->numberOfElements()>0) - { - SLIC_ERROR( "tribol::registerRealElementField(): null pointer to data for " << - "'RATE_PERCENT_STIFFNESS' on mesh " << mesh_id << "."); - mesh->getElementData().m_is_rate_percent_penalty_set = false; - } - else - { - mesh->getElementData().m_is_rate_percent_penalty_set = true; - } - } - else - { - mesh->getElementData().m_rate_percent_stiffness = *fieldVariable; - mesh->getElementData().m_is_rate_percent_penalty_set = true; - } - break; + break; + } + case RATE_CONSTANT_STIFFNESS: { + if ( fieldVariable == nullptr ) { + if ( mesh->numberOfElements() > 0 ) { + SLIC_ERROR( "tribol::registerRealElementField(): null pointer to data for " + << "'RATE_CONSTANT_STIFFNESS' on mesh " << mesh_id << "." ); + mesh->getElementData().m_is_rate_constant_penalty_set = false; + } else { + mesh->getElementData().m_is_rate_constant_penalty_set = true; + } + } else { + mesh->getElementData().m_rate_penalty_stiffness = *fieldVariable; + mesh->getElementData().m_is_rate_constant_penalty_set = true; } - case BULK_MODULUS: - { - if (fieldVariable==nullptr) - { - if (mesh->numberOfElements()>0) - { - SLIC_ERROR( "tribol::registerRealElementField(): null pointer to data for " << - "'BULK_MODULUS' on mesh " << mesh_id << "."); - mesh->getElementData().m_is_kinematic_element_penalty_set = false; - } - else - { - // set boolean to true for zero element meshes (acceptable registration) - mesh->getElementData().m_is_kinematic_element_penalty_set = true; - } - } - else - { - mesh->getElementData().m_mat_mod = ArrayViewT(fieldVariable, mesh->numberOfElements()); - - // Only set boolean to true if the element thickness has been registered - // for nonzero element meshes. This will be true if the element thickness - // was registered first (need both). - if (!mesh->getElementData().m_thickness.empty()) - { - mesh->getElementData().m_is_kinematic_element_penalty_set = true; - } - } - - break; + break; + } + case RATE_PERCENT_STIFFNESS: { + if ( fieldVariable == nullptr ) { + if ( mesh->numberOfElements() > 0 ) { + SLIC_ERROR( "tribol::registerRealElementField(): null pointer to data for " + << "'RATE_PERCENT_STIFFNESS' on mesh " << mesh_id << "." ); + mesh->getElementData().m_is_rate_percent_penalty_set = false; + } else { + mesh->getElementData().m_is_rate_percent_penalty_set = true; + } + } else { + mesh->getElementData().m_rate_percent_stiffness = *fieldVariable; + mesh->getElementData().m_is_rate_percent_penalty_set = true; } - case YOUNGS_MODULUS: - { - if (fieldVariable==nullptr) - { - if (mesh->numberOfElements()>0) - { - SLIC_ERROR( "tribol::registerRealElementField(): null pointer to data for " << - "'YOUNGS_MODULUS' on mesh " << mesh_id << "."); - mesh->getElementData().m_is_kinematic_element_penalty_set = false; - } - else - { - // set boolean to true for zero element meshes (acceptable registration) - mesh->getElementData().m_is_kinematic_element_penalty_set = true; - } - } - else - { - mesh->getElementData().m_mat_mod = ArrayViewT(fieldVariable, mesh->numberOfElements()); - - // Only set boolean to true if the element thickness has been registered - // for nonzero element meshes. This will be true if the element thickness - // was registered first (need both). - if (!mesh->getElementData().m_thickness.empty()) - { - mesh->getElementData().m_is_kinematic_element_penalty_set = true; - } - } - - break; + break; + } + case BULK_MODULUS: { + if ( fieldVariable == nullptr ) { + if ( mesh->numberOfElements() > 0 ) { + SLIC_ERROR( "tribol::registerRealElementField(): null pointer to data for " + << "'BULK_MODULUS' on mesh " << mesh_id << "." ); + mesh->getElementData().m_is_kinematic_element_penalty_set = false; + } else { + // set boolean to true for zero element meshes (acceptable registration) + mesh->getElementData().m_is_kinematic_element_penalty_set = true; + } + } else { + mesh->getElementData().m_mat_mod = ArrayViewT( fieldVariable, mesh->numberOfElements() ); + + // Only set boolean to true if the element thickness has been registered + // for nonzero element meshes. This will be true if the element thickness + // was registered first (need both). + if ( !mesh->getElementData().m_thickness.empty() ) { + mesh->getElementData().m_is_kinematic_element_penalty_set = true; + } } - case ELEMENT_THICKNESS: - { - if (fieldVariable==nullptr) - { - if (mesh->numberOfElements()>0) - { - SLIC_ERROR( "tribol::registerRealElementField(): null pointer to data for " << - "'ELEMENT_THICKNESS' on mesh " << mesh_id << "."); - mesh->getElementData().m_is_kinematic_element_penalty_set = false; - } - else - { - // set booleans to true for zero element meshes (acceptable registration) - mesh->getElementData().m_is_kinematic_element_penalty_set = true; - mesh->getElementData().m_is_element_thickness_set = true; - } - } - else - { - mesh->getElementData().m_thickness = ArrayViewT(fieldVariable, mesh->numberOfElements()); - mesh->getElementData().m_is_element_thickness_set = true; - - // Only set boolean to true if the material modulus has been registered for - // nonzero element meshes. This will set to true if the material modulus was - // registered first (need both). - if (!mesh->getElementData().m_mat_mod.empty()) - { - mesh->getElementData().m_is_kinematic_element_penalty_set = true; - } - } - - break; + + break; + } + case YOUNGS_MODULUS: { + if ( fieldVariable == nullptr ) { + if ( mesh->numberOfElements() > 0 ) { + SLIC_ERROR( "tribol::registerRealElementField(): null pointer to data for " + << "'YOUNGS_MODULUS' on mesh " << mesh_id << "." ); + mesh->getElementData().m_is_kinematic_element_penalty_set = false; + } else { + // set boolean to true for zero element meshes (acceptable registration) + mesh->getElementData().m_is_kinematic_element_penalty_set = true; + } + } else { + mesh->getElementData().m_mat_mod = ArrayViewT( fieldVariable, mesh->numberOfElements() ); + + // Only set boolean to true if the element thickness has been registered + // for nonzero element meshes. This will be true if the element thickness + // was registered first (need both). + if ( !mesh->getElementData().m_thickness.empty() ) { + mesh->getElementData().m_is_kinematic_element_penalty_set = true; + } } - default: - { - SLIC_ERROR( "tribol::registerRealElementField(): the field argument " << - "on mesh " << mesh_id << " is not an accepted tribol real element field." ); + + break; + } + case ELEMENT_THICKNESS: { + if ( fieldVariable == nullptr ) { + if ( mesh->numberOfElements() > 0 ) { + SLIC_ERROR( "tribol::registerRealElementField(): null pointer to data for " + << "'ELEMENT_THICKNESS' on mesh " << mesh_id << "." ); + mesh->getElementData().m_is_kinematic_element_penalty_set = false; + } else { + // set booleans to true for zero element meshes (acceptable registration) + mesh->getElementData().m_is_kinematic_element_penalty_set = true; + mesh->getElementData().m_is_element_thickness_set = true; + } + } else { + mesh->getElementData().m_thickness = ArrayViewT( fieldVariable, mesh->numberOfElements() ); + mesh->getElementData().m_is_element_thickness_set = true; + + // Only set boolean to true if the material modulus has been registered for + // nonzero element meshes. This will set to true if the material modulus was + // registered first (need both). + if ( !mesh->getElementData().m_mat_mod.empty() ) { + mesh->getElementData().m_is_kinematic_element_penalty_set = true; + } } - } // end switch over field -} // end registerRealElementField() + break; + } + default: { + SLIC_ERROR( "tribol::registerRealElementField(): the field argument " + << "on mesh " << mesh_id << " is not an accepted tribol real element field." ); + } + } // end switch over field + +} // end registerRealElementField() //------------------------------------------------------------------------------ -void registerIntElementField( IndexT mesh_id, - const IntElementFields field, - int * TRIBOL_UNUSED_PARAM(fieldVariable) ) +void registerIntElementField( IndexT mesh_id, const IntElementFields field, int* TRIBOL_UNUSED_PARAM( fieldVariable ) ) { - auto mesh = MeshManager::getInstance().findData(mesh_id); + auto mesh = MeshManager::getInstance().findData( mesh_id ); - SLIC_ERROR_ROOT_IF(!mesh, "tribol::registerIntElementField(): " << - "no mesh with id " << mesh_id << " exists."); + SLIC_ERROR_ROOT_IF( !mesh, "tribol::registerIntElementField(): " + << "no mesh with id " << mesh_id << " exists." ); - switch (field) - { - case UNDEFINED_INT_ELEMENT_FIELD: - default: - SLIC_ERROR_ROOT("tribol::registerIntElementField() not yet implemented."); - } // end switch over field + switch ( field ) { + case UNDEFINED_INT_ELEMENT_FIELD: + default: + SLIC_ERROR_ROOT( "tribol::registerIntElementField() not yet implemented." ); + } // end switch over field -} // end registerIntElementField() +} // end registerIntElementField() //------------------------------------------------------------------------------ -void registerCouplingScheme( IndexT cs_id, - IndexT mesh_id1, - IndexT mesh_id2, - int contact_mode, - int contact_case, - int contact_method, - int contact_model, - int enforcement_method, - int binning_method, +void registerCouplingScheme( IndexT cs_id, IndexT mesh_id1, IndexT mesh_id2, int contact_mode, int contact_case, + int contact_method, int contact_model, int enforcement_method, int binning_method, ExecutionMode given_exec_mode ) { - CouplingScheme scheme (cs_id, - mesh_id1, - mesh_id2, - contact_mode, - contact_case, - contact_method, - contact_model, - enforcement_method, - binning_method, - given_exec_mode); - - // add coupling scheme to manager. Validity checks are performed in - // tribol::update() when each coupling scheme is initialized. - CouplingSchemeManager::getInstance().addData(cs_id, std::move(scheme)); - -} // end registerCouplingScheme() - -//------------------------------------------------------------------------------ -void setInterfacePairs( IndexT cs_id, - IndexT numPairs, - IndexT const * const pairIndex1, - IndexT const * const pairIndex2 ) -{ - // get access to coupling scheme - auto cs = CouplingSchemeManager::getInstance().findData(cs_id); - - SLIC_ERROR_ROOT_IF(!cs, - "tribol::setInterfacePairs(): invalid coupling scheme index."); - - auto& pairs = cs->getInterfacePairs(); - auto& mesh1 = cs->getMesh1(); - auto& mesh2 = cs->getMesh2(); - - pairs.clear(); - pairs.reserve(numPairs); - - // copy the interaction pairs - for(int i=0; i< numPairs; ++i) - { - ContactMode mode = cs->getContactMode(); - - // perform initial face-pair validity checks to add valid face-pairs - // to interface pair manager. Note, further computational geometry - // filtering will be performed on each face-pair indendifying - // contact candidates. - if (geomFilter( pairIndex1[i], pairIndex2[i], mesh1, mesh2, mode, cs->getParameters().auto_contact_check )) - { - pairs.emplace_back(pairIndex1[i], pairIndex2[i], true); - } - } - - // Disable per-cycle rebinning - cs->setFixedBinning(true); - -} // end setInterfacePairs() - -//------------------------------------------------------------------------------ -int update( int cycle, RealT t, RealT &dt ) -{ - bool err_cs = false; - - ///////////////////////////////////////////////////////////////////////// - // // - // Loop over coupling schemes. // - // // - // Note, numCouplings is always 1 greater than the highest registered // - // coupling index. This allows for non-contiguous coupling scheme ids, // - // which may arise from host-code registration or from skipped schemes // - // // - ///////////////////////////////////////////////////////////////////////// - for (auto& cs_pair : CouplingSchemeManager::getInstance()) - { - auto& cs = cs_pair.second; - - // initialize and check for valid coupling scheme. If not valid, the coupling - // scheme will not be valid across all ranks and we will skip this coupling scheme - if (!cs.init()) - { - SLIC_WARNING_ROOT("tribol::update(): skipping invalid CouplingScheme " << - cs_pair.first << "Please see warnings."); - continue; - } + CouplingScheme scheme( cs_id, mesh_id1, mesh_id2, contact_mode, contact_case, contact_method, contact_model, + enforcement_method, binning_method, given_exec_mode ); - // perform binning between meshes on the coupling scheme - // Note, this routine is guarded against null meshes - cs.performBinning(); + // add coupling scheme to manager. Validity checks are performed in + // tribol::update() when each coupling scheme is initialized. + CouplingSchemeManager::getInstance().addData( cs_id, std::move( scheme ) ); - // apply the coupling scheme. Note, there are appropriate guards against zero - // element meshes, or null-mesh coupling schemes - err_cs = cs.apply( cycle, t, dt ); +} // end registerCouplingScheme() - if ( err_cs != 0 ) - { - SLIC_WARNING("tribol::update(): coupling scheme " << cs_pair.first << - " returned with an error."); - } +//------------------------------------------------------------------------------ +void setInterfacePairs( IndexT cs_id, IndexT numPairs, IndexT const* const pairIndex1, IndexT const* const pairIndex2 ) +{ + // get access to coupling scheme + auto cs = CouplingSchemeManager::getInstance().findData( cs_id ); + + SLIC_ERROR_ROOT_IF( !cs, "tribol::setInterfacePairs(): invalid coupling scheme index." ); + + auto& pairs = cs->getInterfacePairs(); + auto& mesh1 = cs->getMesh1(); + auto& mesh2 = cs->getMesh2(); + + pairs.clear(); + pairs.reserve( numPairs ); - } // end of coupling scheme loop + // copy the interaction pairs + for ( int i = 0; i < numPairs; ++i ) { + ContactMode mode = cs->getContactMode(); - return err_cs; + // perform initial face-pair validity checks to add valid face-pairs + // to interface pair manager. Note, further computational geometry + // filtering will be performed on each face-pair indendifying + // contact candidates. + if ( geomFilter( pairIndex1[i], pairIndex2[i], mesh1, mesh2, mode, cs->getParameters().auto_contact_check ) ) { + pairs.emplace_back( pairIndex1[i], pairIndex2[i], true ); + } + } -} // end update() + // Disable per-cycle rebinning + cs->setFixedBinning( true ); + +} // end setInterfacePairs() //------------------------------------------------------------------------------ -void finalize() +int update( int cycle, RealT t, RealT& dt ) { - CouplingSchemeManager::getInstance().clear(); -} + bool err_cs = false; + + ///////////////////////////////////////////////////////////////////////// + // // + // Loop over coupling schemes. // + // // + // Note, numCouplings is always 1 greater than the highest registered // + // coupling index. This allows for non-contiguous coupling scheme ids, // + // which may arise from host-code registration or from skipped schemes // + // // + ///////////////////////////////////////////////////////////////////////// + for ( auto& cs_pair : CouplingSchemeManager::getInstance() ) { + auto& cs = cs_pair.second; + + // initialize and check for valid coupling scheme. If not valid, the coupling + // scheme will not be valid across all ranks and we will skip this coupling scheme + if ( !cs.init() ) { + SLIC_WARNING_ROOT( "tribol::update(): skipping invalid CouplingScheme " << cs_pair.first + << "Please see warnings." ); + continue; + } + + // perform binning between meshes on the coupling scheme + // Note, this routine is guarded against null meshes + cs.performBinning(); + + // apply the coupling scheme. Note, there are appropriate guards against zero + // element meshes, or null-mesh coupling schemes + err_cs = cs.apply( cycle, t, dt ); + + if ( err_cs != 0 ) { + SLIC_WARNING( "tribol::update(): coupling scheme " << cs_pair.first << " returned with an error." ); + } + + } // end of coupling scheme loop + + return err_cs; + +} // end update() //------------------------------------------------------------------------------ -} // end tribol namespace +void finalize() { CouplingSchemeManager::getInstance().clear(); } +//------------------------------------------------------------------------------ +} // namespace tribol diff --git a/src/tribol/interface/tribol.hpp b/src/tribol/interface/tribol.hpp index fec0dd60..9bafed1d 100644 --- a/src/tribol/interface/tribol.hpp +++ b/src/tribol/interface/tribol.hpp @@ -15,9 +15,7 @@ // MFEM includes #include "mfem.hpp" - -namespace tribol -{ +namespace tribol { /// \name Contact Library Initialization methods /// @{ @@ -40,7 +38,7 @@ void initialize( int dimension, CommT comm ); /** * @brief Sets the MPI communicator for a coupling scheme - * + * * @param cs_id coupling scheme id * @param comm MPI communicator * \pre MPI-enabled Tribol @@ -59,41 +57,38 @@ void setMPIComm( IndexT cs_id, CommT comm ); * \param [in] rate_calc rate penalty stiffness calculation option * \pre user must register coupling scheme prior to setting penalty enforcement options for that scheme */ -void setPenaltyOptions( IndexT cs_id, - PenaltyConstraintType pen_enfrc_option, +void setPenaltyOptions( IndexT cs_id, PenaltyConstraintType pen_enfrc_option, KinematicPenaltyCalculation kinematic_calc, - RatePenaltyCalculation rate_calc=NO_RATE_PENALTY ); + RatePenaltyCalculation rate_calc = NO_RATE_PENALTY ); /*! * \brief Sets the constant kinematic penalty stiffness - * \param [in] mesh_id mesh id for penalty stiffness + * \param [in] mesh_id mesh id for penalty stiffness * \param [in] k constant kinematic penalty stiffness */ void setKinematicConstantPenalty( IndexT mesh_id, RealT k ); /*! * \brief Sets the kinematic element penalty stiffness data - * \param [in] mesh_id mesh id for penalty stiffness + * \param [in] mesh_id mesh id for penalty stiffness * \param [in] material_modulus pointer to element array of bulk or Young's moduli * \param [in] element_thickness pointer to element array of through element thicknesses * * \note the length of the arrays that material_modulus and element_thickness point to * is the number of contact faces registered for mesh with id, \p mesh_id. */ -void setKinematicElementPenalty( IndexT mesh_id, - const RealT *material_modulus, - const RealT *element_thickness ); +void setKinematicElementPenalty( IndexT mesh_id, const RealT* material_modulus, const RealT* element_thickness ); /*! * \brief Sets the constant rate penalty stiffness - * \param [in] mesh_id mesh id for penalty stiffness + * \param [in] mesh_id mesh id for penalty stiffness * \param [in] r_k constant rate penalty stiffness */ void setRateConstantPenalty( IndexT mesh_id, RealT r_k ); /*! * \brief Sets the percent rate penalty stiffness - * \param [in] mesh_id mesh id for penalty stiffness + * \param [in] mesh_id mesh id for penalty stiffness * \param [in] r_p rate penalty as percent of kinematic penalty */ void setRatePercentPenalty( IndexT mesh_id, RealT r_p ); @@ -103,11 +98,11 @@ void setRatePercentPenalty( IndexT mesh_id, RealT r_p ); * \brief sets the auto-contact interpen fraction on the parameters struct * * \param [in] cs_id coupling scheme id - * \param [in] scale the scale applied to the element thickness to determine the auto-contact length scale + * \param [in] scale the scale applied to the element thickness to determine the auto-contact length scale * - * \note this is only used for common-plane with penalty enforcement. A sacle < 1.0 may + * \note this is only used for common-plane with penalty enforcement. A sacle < 1.0 may * result in missed contact face-pairs in softer contact responses - * + * * */ void setAutoContactPenScale( IndexT cs_id, RealT scale ); @@ -119,7 +114,7 @@ void setAutoContactPenScale( IndexT cs_id, RealT scale ); * \param [in] cs_id coupling scheme id * \param [in] frac the maximum allowable interpenetration factor triggering a timestep vote * - * \note this is only used for common-plane with penalty enforcement. This is the + * \note this is only used for common-plane with penalty enforcement. This is the * fraction of the element thickness that is allowed prior to triggering a timestep vote. * */ @@ -127,10 +122,10 @@ void setTimestepPenFrac( IndexT cs_id, RealT frac ); /*! * - * \brief sets the timestep scale factor applied to the timestep vote + * \brief sets the timestep scale factor applied to the timestep vote * * \param [in] cs_id coupling scheme id - * \param [in] scale the scale factor applied to the timestep vote + * \param [in] scale the scale factor applied to the timestep vote * * \pre scale > 0 * @@ -138,13 +133,13 @@ void setTimestepPenFrac( IndexT cs_id, RealT frac ); void setTimestepScale( IndexT cs_id, RealT scale ); /*! - * \brief Sets the area fraction for inclusion of a contact overlap + * \brief Sets the area fraction for inclusion of a contact overlap * * \param [in] cs_id coupling scheme id * \param [in] frac area fraction tolerance * - * \note the area fraction under consideration is the ratio of the - * contact overlap with the largest of the two consituent + * \note the area fraction under consideration is the ratio of the + * contact overlap with the largest of the two consituent * faces. A default ratio is provided by Tribol. */ void setContactAreaFrac( IndexT cs_id, RealT frac ); @@ -166,7 +161,7 @@ void setPenaltyScale( IndexT mesh_id, RealT scale ); * */ void setLagrangeMultiplierOptions( IndexT cs_id, ImplicitEvalMode evalMode, - SparseMode sparseMode=SparseMode::MFEM_LINKED_LIST ); + SparseMode sparseMode = SparseMode::MFEM_LINKED_LIST ); /*! * \brief Sets the plot cycle increment for visualization @@ -177,7 +172,7 @@ void setLagrangeMultiplierOptions( IndexT cs_id, ImplicitEvalMode evalMode, void setPlotCycleIncrement( IndexT cs_id, int incr ); /*! - * \brief Sets the plot options for interface visualization + * \brief Sets the plot options for interface visualization * * \param [in] cs_id coupling scheme id * \param [in] v_type visualization option @@ -195,14 +190,14 @@ void setOutputDirectory( IndexT cs_id, const std::string& dir ); /*! * \brief Optionally sets the logging level per coupling scheme * \param [in] cs_id coupling scheme id - * \param [in] log_level the desired logging level + * \param [in] log_level the desired logging level * * \note this overrides the logging level set in initialize(). */ void setLoggingLevel( IndexT cs_id, LoggingLevel log_level ); /*! - * \brief Enable the contact timestep vote + * \brief Enable the contact timestep vote * * \param [in] cs_id coupling scheme id * \param [in] enable the timestep vote will be calculated and returned if true @@ -238,15 +233,8 @@ void enableTimestepVote( IndexT cs_id, const bool enable ); * \note connectivity is a 2D array with num_elements rows and num_nodes columns * with row-major ordering */ -void registerMesh( IndexT mesh_id, - IndexT num_elements, - IndexT num_nodes, - const IndexT* connectivity, - int element_type, - const RealT* x, - const RealT* y, - const RealT* z = nullptr, - MemorySpace m_space = MemorySpace::Host ); +void registerMesh( IndexT mesh_id, IndexT num_elements, IndexT num_nodes, const IndexT* connectivity, int element_type, + const RealT* x, const RealT* y, const RealT* z = nullptr, MemorySpace m_space = MemorySpace::Host ); /*! * \brief Registers nodal displacements on the contact surface. @@ -263,10 +251,7 @@ void registerMesh( IndexT mesh_id, * \note A mesh for the given contact surface must have already been registered * prior to calling this method via registerMesh() */ -void registerNodalDisplacements( IndexT mesh_id, - const RealT* dx, - const RealT* dy, - const RealT* dz=nullptr ); +void registerNodalDisplacements( IndexT mesh_id, const RealT* dx, const RealT* dy, const RealT* dz = nullptr ); /*! * \brief Registers nodal velocities on the contact surface. @@ -283,10 +268,7 @@ void registerNodalDisplacements( IndexT mesh_id, * \note A mesh for the given contact surface must have already been registered * prior to calling this method] via registerMesh() */ -void registerNodalVelocities( IndexT mesh_id, - const RealT* vx, - const RealT* vy, - const RealT* vz=nullptr ); +void registerNodalVelocities( IndexT mesh_id, const RealT* vx, const RealT* vy, const RealT* vz = nullptr ); /*! * \brief Registers nodal response buffers. @@ -303,28 +285,25 @@ void registerNodalVelocities( IndexT mesh_id, * \note A mesh for the given contact surface must have already been registered * prior to calling this method. */ -void registerNodalResponse( IndexT mesh_id, - RealT* rx, - RealT* ry, - RealT* rz=nullptr ); +void registerNodalResponse( IndexT mesh_id, RealT* rx, RealT* ry, RealT* rz = nullptr ); /*! - * \brief Get mfem sparse matrix for method specific Jacobian matrix output + * \brief Get mfem sparse matrix for method specific Jacobian matrix output * * \param [in,out] sMat double pointer to mfem sparse matrix object * \param [in] cs_id coupling scheme id * - * \return 0 for success, nonzero for failure + * \return 0 for success, nonzero for failure * * \pre *sMat = nullptr * - * \note Mortar Method: The sizing of the sparse matrix assumes that all - * nonmortar and mortar nodes may have a Lagrange multiplier associated - * with them. This allows us to use the global connectivity array, - * which assumes contiguous and unique node ids between mortar and + * \note Mortar Method: The sizing of the sparse matrix assumes that all + * nonmortar and mortar nodes may have a Lagrange multiplier associated + * with them. This allows us to use the global connectivity array, + * which assumes contiguous and unique node ids between mortar and * nonmortar meshes registered in a given coupling scheme. */ -int getJacobianSparseMatrix( mfem::SparseMatrix ** sMat, IndexT cs_id ); +int getJacobianSparseMatrix( mfem::SparseMatrix** sMat, IndexT cs_id ); /*! * \brief Gets CSR storage arrays for method specific Jacobian matrix output @@ -334,7 +313,7 @@ int getJacobianSparseMatrix( mfem::SparseMatrix ** sMat, IndexT cs_id ); * \param [out] vals pointer to nonzero value array * \param [in] cs_id coupling scheme id * \param [out] n_offsets optional pointer to the number of offsets (size of I array) - * \param [out] n_nonzero optional pointer to the number of non zeros + * \param [out] n_nonzero optional pointer to the number of non zeros * (size of J and vals arrays) * * \pre I == nullptr @@ -343,15 +322,11 @@ int getJacobianSparseMatrix( mfem::SparseMatrix ** sMat, IndexT cs_id ); * * \post n_offsets will store the number of offsets, if a non-nullptr was passed in * \post n_nonzero will store the number of non-zeros, if a non-nullptr was passed in - * + * * \return 0 success (if CSR data exists and pointed to), nonzero for failure * */ -int getJacobianCSRMatrix( int** I, - int** J, - RealT** vals, - IndexT cs_id, - int* n_offsets = nullptr, +int getJacobianCSRMatrix( int** I, int** J, RealT** vals, IndexT cs_id, int* n_offsets = nullptr, int* n_nonzero = nullptr ); /*! @@ -378,16 +353,16 @@ int getJacobianCSRMatrix( int** I, * For example, requesting row_block = BlockSpace::MORTAR and col_block = * BlockSpace::LAGRANGE_MULTIPLIER will return the block Jacobians in position * 02. row_elem_idx will be an array of mortar element indices and col_elem_idx - * will be an array of Lagrange multiplier element indices. The length of + * will be an array of Lagrange multiplier element indices. The length of * row_elem_idx, col_elem_idx, and jacobians will match. Each entry in the * array corresponds to a single coupled element pair, so element indices will * not be unique, in general. For instance, if a nonmortar face interacts with * multiple mortar faces and vice-versa. * * \param [in] cs_id coupling scheme id - * \param [in] row_block Row Jacobian block (MORTAR, NONMORTAR, or + * \param [in] row_block Row Jacobian block (MORTAR, NONMORTAR, or * LAGRANGE_MULTIPLIER) - * \param [in] col_block Column Jacobian block (MORTAR, NONMORTAR, or + * \param [in] col_block Column Jacobian block (MORTAR, NONMORTAR, or * LAGRANGE_MULTIPLIER) * \param [out] row_elem_idx Pointer to pointer to array of element indices for * the row block @@ -395,16 +370,13 @@ int getJacobianCSRMatrix( int** I, * the column block * \param [out] jacobians Pointer to pointer to array of Jacobian dense matrices * - * @note The second pointer of the double pointer is updated by this function to + * @note The second pointer of the double pointer is updated by this function to * point to internally stored arrays of indices and Jacobian values. * * \return 0 success (if Jacobians exist), nonzero for failure */ -int getElementBlockJacobians( IndexT cs_id, - BlockSpace row_block, - BlockSpace col_block, - const ArrayT** row_elem_idx, - const ArrayT** col_elem_idx, +int getElementBlockJacobians( IndexT cs_id, BlockSpace row_block, BlockSpace col_block, + const ArrayT** row_elem_idx, const ArrayT** col_elem_idx, const ArrayT** jacobians ); /*! @@ -415,8 +387,7 @@ int getElementBlockJacobians( IndexT cs_id, * \param gaps Array of degree-of-freedom values on the nodes of the mesh * representing the scalar gap field */ -void registerMortarGaps( IndexT mesh_id, - RealT * gaps ); +void registerMortarGaps( IndexT mesh_id, RealT* gaps ); /*! * \brief Register pressure field on a nonmortar surface mesh associated with @@ -426,23 +397,16 @@ void registerMortarGaps( IndexT mesh_id, * \param gaps Array of degree-of-freedom values on the nodes of the mesh * representing the scalar pressure field */ -void registerMortarPressures( IndexT mesh_id, - const RealT * pressures ); +void registerMortarPressures( IndexT mesh_id, const RealT* pressures ); /// register an integer nodal field -void registerIntNodalField( IndexT mesh_id, - const IntNodalFields field, - int * fieldVariable ); +void registerIntNodalField( IndexT mesh_id, const IntNodalFields field, int* fieldVariable ); -/// register a real element field or parameter -void registerRealElementField( IndexT mesh_id, - const RealElementFields field, - const RealT * fieldVariable ); +/// register a real element field or parameter +void registerRealElementField( IndexT mesh_id, const RealElementFields field, const RealT* fieldVariable ); /// register an integer element field -void registerIntElementField( IndexT mesh_id, - const IntElementFields field, - int * fieldVariable ); +void registerIntElementField( IndexT mesh_id, const IntElementFields field, int* fieldVariable ); /// @} @@ -466,24 +430,17 @@ void registerIntElementField( IndexT mesh_id, * \note A mesh for the given contact surface must have already been registered * prior to calling this method. */ -void registerCouplingScheme( IndexT cs_id, - IndexT mesh_id1, - IndexT mesh_id2, - int contact_mode, - int contact_case, - int contact_method, - int contact_model, - int enforcement_method, +void registerCouplingScheme( IndexT cs_id, IndexT mesh_id1, IndexT mesh_id2, int contact_mode, int contact_case, + int contact_method, int contact_model, int enforcement_method, int binning_method = DEFAULT_BINNING_METHOD, - ExecutionMode exec_mode = ExecutionMode::Dynamic); + ExecutionMode exec_mode = ExecutionMode::Dynamic ); /// @} - /*! * \brief Sets the interacting cell-pairs manually. * * \param [in] cs_id coupling scheme id - * \param [in] numPairs number of cell-pairs to be registered + * \param [in] numPairs number of cell-pairs to be registered * \param [in] mesh_id1 mesh id of the first cell in the pair list * \param [in] pairType1 cell type of the first cell in the pair list * \param [in] pairIndex1 index of the first cell in the pair list @@ -492,15 +449,9 @@ void registerCouplingScheme( IndexT cs_id, * \param [in] pairIndex2 index of the second cell in the pair list * */ -void setInterfacePairs( IndexT cs_id, - IndexT numPairs, - IndexT const * mesh_id1, - IndexT const * pairType1, - IndexT const * pairIndex1, - IndexT const * mesh_id2, - IndexT const * pairType2, - IndexT const * pairIndex2 ); - +void setInterfacePairs( IndexT cs_id, IndexT numPairs, IndexT const* mesh_id1, IndexT const* pairType1, + IndexT const* pairIndex1, IndexT const* mesh_id2, IndexT const* pairType2, + IndexT const* pairIndex2 ); /*! * \brief Computes the contact response at the given cycle. @@ -511,7 +462,7 @@ void setInterfacePairs( IndexT cs_id, * * \return rc return code, a non-zero return code indicates an error. */ -int update( int cycle, RealT t, RealT &dt ); +int update( int cycle, RealT t, RealT& dt ); /// \name Contact Library finalization methods /// @{ @@ -525,5 +476,4 @@ void finalize(); } /* namespace tribol */ - #endif /* TRIBOL_HPP_ */ diff --git a/src/tribol/mesh/CouplingScheme.cpp b/src/tribol/mesh/CouplingScheme.cpp index 6de87c0a..259dd6ae 100644 --- a/src/tribol/mesh/CouplingScheme.cpp +++ b/src/tribol/mesh/CouplingScheme.cpp @@ -26,20 +26,18 @@ // C++ includes #include -namespace tribol -{ +namespace tribol { //------------------------------------------------------------------------------ // INTERNAL HELPER METHODS //------------------------------------------------------------------------------ -namespace -{ +namespace { //------------------------------------------------------------------------------ inline bool validMeshID( IndexT mesh_id ) { - MeshManager & meshManager = MeshManager::getInstance(); - return (mesh_id==ANY_MESH) || meshManager.findData( mesh_id ); + MeshManager& meshManager = MeshManager::getInstance(); + return ( mesh_id == ANY_MESH ) || meshManager.findData( mesh_id ); } } /* end anonymous namespace */ @@ -49,340 +47,280 @@ inline bool validMeshID( IndexT mesh_id ) //------------------------------------------------------------------------------ void CouplingSchemeErrors::printModeErrors() { - switch(this->cs_mode_error) - { - case INVALID_MODE: - { - SLIC_WARNING_ROOT("The specified ContactMode is invalid."); - break; - } - case NO_MODE_IMPLEMENTATION: - { - SLIC_WARNING_ROOT("The specified ContactMode has no implementation."); - break; - } - case NO_MODE_ERROR: - { - break; - } - default: - break; - } // end switch over mode errors -} // end CouplingSchemeErrors::printModeErrors() + switch ( this->cs_mode_error ) { + case INVALID_MODE: { + SLIC_WARNING_ROOT( "The specified ContactMode is invalid." ); + break; + } + case NO_MODE_IMPLEMENTATION: { + SLIC_WARNING_ROOT( "The specified ContactMode has no implementation." ); + break; + } + case NO_MODE_ERROR: { + break; + } + default: + break; + } // end switch over mode errors +} // end CouplingSchemeErrors::printModeErrors() //------------------------------------------------------------------------------ void CouplingSchemeErrors::printCaseErrors() { - switch(this->cs_case_error) - { - case INVALID_CASE: - { - SLIC_WARNING_ROOT("The specified ContactCase is invalid."); - break; - } - case NO_CASE_IMPLEMENTATION: - { - SLIC_WARNING_ROOT("The specified ContactCase has no implementation."); - break; - } - case INVALID_CASE_DATA: - { - SLIC_WARNING_ROOT("The specified ContactCase has invalid data. " << - "AUTO contact requires element thickness registration."); - break; - } - case NO_CASE_ERROR: - { - break; - } - default: - break; - } // end switch over case errors -} // end CouplingSchemeErrors::printCaseErrors() + switch ( this->cs_case_error ) { + case INVALID_CASE: { + SLIC_WARNING_ROOT( "The specified ContactCase is invalid." ); + break; + } + case NO_CASE_IMPLEMENTATION: { + SLIC_WARNING_ROOT( "The specified ContactCase has no implementation." ); + break; + } + case INVALID_CASE_DATA: { + SLIC_WARNING_ROOT( "The specified ContactCase has invalid data. " + << "AUTO contact requires element thickness registration." ); + break; + } + case NO_CASE_ERROR: { + break; + } + default: + break; + } // end switch over case errors +} // end CouplingSchemeErrors::printCaseErrors() //------------------------------------------------------------------------------ void CouplingSchemeErrors::printMethodErrors() { - switch(this->cs_method_error) - { - case INVALID_ELEMENT_TYPE: - { - SLIC_WARNING_ROOT("The specified element type is invalid."); - break; - } - case INVALID_METHOD: - { - SLIC_WARNING_ROOT("The specified ContactMethod is invalid."); - break; - } - case NO_METHOD_IMPLEMENTATION: - { - SLIC_WARNING_ROOT("The specified ContactMethod has no implementation."); - break; - } - case DIFFERENT_FACE_TYPES: - { - SLIC_WARNING_ROOT("The specified ContactMethod does not support different face types."); - break; - } - case SAME_MESH_IDS: - { - SLIC_WARNING_ROOT("The specified ContactMethod cannot be used in coupling schemes with identical mesh IDs."); - break; - } - case SAME_MESH_IDS_INVALID_DIM: - { - SLIC_WARNING_ROOT("The specified ContactMethod is not implemented for the problem dimension and " << - "cannot be used in coupling schemes with identical mesh IDs."); - break; - } - case INVALID_DIM: - { - SLIC_WARNING_ROOT("The specified ContactMethod is not implemented for the problem dimension."); - break; - } - case NULL_NODAL_RESPONSE: - { - SLIC_WARNING_ROOT("User must call tribol::registerNodalResponse() for each mesh to use this ContactMethod."); - break; - } - case NO_METHOD_ERROR: - { - break; - } - default: - break; - } // end switch over method errors -} // end CouplingSchemeErrors::printMethodErrors() + switch ( this->cs_method_error ) { + case INVALID_ELEMENT_TYPE: { + SLIC_WARNING_ROOT( "The specified element type is invalid." ); + break; + } + case INVALID_METHOD: { + SLIC_WARNING_ROOT( "The specified ContactMethod is invalid." ); + break; + } + case NO_METHOD_IMPLEMENTATION: { + SLIC_WARNING_ROOT( "The specified ContactMethod has no implementation." ); + break; + } + case DIFFERENT_FACE_TYPES: { + SLIC_WARNING_ROOT( "The specified ContactMethod does not support different face types." ); + break; + } + case SAME_MESH_IDS: { + SLIC_WARNING_ROOT( "The specified ContactMethod cannot be used in coupling schemes with identical mesh IDs." ); + break; + } + case SAME_MESH_IDS_INVALID_DIM: { + SLIC_WARNING_ROOT( "The specified ContactMethod is not implemented for the problem dimension and " + << "cannot be used in coupling schemes with identical mesh IDs." ); + break; + } + case INVALID_DIM: { + SLIC_WARNING_ROOT( "The specified ContactMethod is not implemented for the problem dimension." ); + break; + } + case NULL_NODAL_RESPONSE: { + SLIC_WARNING_ROOT( "User must call tribol::registerNodalResponse() for each mesh to use this ContactMethod." ); + break; + } + case NO_METHOD_ERROR: { + break; + } + default: + break; + } // end switch over method errors +} // end CouplingSchemeErrors::printMethodErrors() //------------------------------------------------------------------------------ void CouplingSchemeErrors::printModelErrors() { - switch(this->cs_model_error) - { - case INVALID_MODEL: - { - SLIC_WARNING_ROOT("The specified ContactModel is invalid."); - break; - } - case NO_MODEL_IMPLEMENTATION: - { - SLIC_WARNING_ROOT("The specified ContactModel has no implementation."); - break; - } - case NO_MODEL_IMPLEMENTATION_FOR_REGISTERED_METHOD: - { - SLIC_WARNING_ROOT("The specified ContactModel has no implementation for the registered ContactMethod."); - break; - } - case NO_MODEL_ERROR: - { - break; - } - default: - break; - } // end switch over model errors -} // end CouplingSchemeErrors::printModelErrors() + switch ( this->cs_model_error ) { + case INVALID_MODEL: { + SLIC_WARNING_ROOT( "The specified ContactModel is invalid." ); + break; + } + case NO_MODEL_IMPLEMENTATION: { + SLIC_WARNING_ROOT( "The specified ContactModel has no implementation." ); + break; + } + case NO_MODEL_IMPLEMENTATION_FOR_REGISTERED_METHOD: { + SLIC_WARNING_ROOT( "The specified ContactModel has no implementation for the registered ContactMethod." ); + break; + } + case NO_MODEL_ERROR: { + break; + } + default: + break; + } // end switch over model errors +} // end CouplingSchemeErrors::printModelErrors() //------------------------------------------------------------------------------ void CouplingSchemeErrors::printEnforcementErrors() { - switch(this->cs_enforcement_error) - { - case INVALID_ENFORCEMENT: - { - SLIC_WARNING_ROOT("The specified EnforcementMethod is invalid."); - break; - } - case INVALID_ENFORCEMENT_FOR_REGISTERED_METHOD: - { - SLIC_WARNING_ROOT("The specified EnforcementMethod is invalid for the registered ContactMethod."); - break; - } - case INVALID_ENFORCEMENT_OPTION: - { - SLIC_WARNING_ROOT("The specified enforcement option is invalid."); - break; - } - case OPTIONS_NOT_SET: - { - SLIC_WARNING_ROOT("User must call 'tribol::setOptions(..)' to set options for " << - "registered EnforcementMethod."); - break; - } - case NO_ENFORCEMENT_IMPLEMENTATION: - { - SLIC_WARNING_ROOT("The specified enforcement option has no implementation."); - break; - } - case NO_ENFORCEMENT_IMPLEMENTATION_FOR_REGISTERED_METHOD: - { - SLIC_WARNING_ROOT("The specified enforcement option has no implementation for the registered ContactMethod."); - break; - } - case NO_ENFORCEMENT_IMPLEMENTATION_FOR_REGISTERED_OPTION: - { - SLIC_WARNING_ROOT("The specified enforcement option has no implementation for the specified EnforcementMethod."); - break; - } - case NO_ENFORCEMENT_ERROR: - { - break; - } - default: - break; - } // end switch over enforcement errors -} // end CouplingSchemeErrors::printEnforcementErrors() + switch ( this->cs_enforcement_error ) { + case INVALID_ENFORCEMENT: { + SLIC_WARNING_ROOT( "The specified EnforcementMethod is invalid." ); + break; + } + case INVALID_ENFORCEMENT_FOR_REGISTERED_METHOD: { + SLIC_WARNING_ROOT( "The specified EnforcementMethod is invalid for the registered ContactMethod." ); + break; + } + case INVALID_ENFORCEMENT_OPTION: { + SLIC_WARNING_ROOT( "The specified enforcement option is invalid." ); + break; + } + case OPTIONS_NOT_SET: { + SLIC_WARNING_ROOT( "User must call 'tribol::setOptions(..)' to set options for " + << "registered EnforcementMethod." ); + break; + } + case NO_ENFORCEMENT_IMPLEMENTATION: { + SLIC_WARNING_ROOT( "The specified enforcement option has no implementation." ); + break; + } + case NO_ENFORCEMENT_IMPLEMENTATION_FOR_REGISTERED_METHOD: { + SLIC_WARNING_ROOT( "The specified enforcement option has no implementation for the registered ContactMethod." ); + break; + } + case NO_ENFORCEMENT_IMPLEMENTATION_FOR_REGISTERED_OPTION: { + SLIC_WARNING_ROOT( + "The specified enforcement option has no implementation for the specified EnforcementMethod." ); + break; + } + case NO_ENFORCEMENT_ERROR: { + break; + } + default: + break; + } // end switch over enforcement errors +} // end CouplingSchemeErrors::printEnforcementErrors() //------------------------------------------------------------------------------ void CouplingSchemeErrors::printEnforcementDataErrors() { - switch(this->cs_enforcement_data_error) - { - case ERROR_IN_REGISTERED_ENFORCEMENT_DATA: - { - SLIC_WARNING_ROOT("Error in registered enforcement data; see warnings."); - break; - } - case NO_ENFORCEMENT_DATA_ERROR: - { - break; - } - default: - break; - } // end switch over enforcement data errors -} // end CouplingSchemeErrors::printEnforcementDataErrors() + switch ( this->cs_enforcement_data_error ) { + case ERROR_IN_REGISTERED_ENFORCEMENT_DATA: { + SLIC_WARNING_ROOT( "Error in registered enforcement data; see warnings." ); + break; + } + case NO_ENFORCEMENT_DATA_ERROR: { + break; + } + default: + break; + } // end switch over enforcement data errors +} // end CouplingSchemeErrors::printEnforcementDataErrors() //------------------------------------------------------------------------------ void CouplingSchemeErrors::printExecutionModeErrors() { - switch(this->cs_execution_mode_error) - { - case ExecutionModeError::NON_MATCHING_MEMORY_SPACE: - { - SLIC_WARNING_ROOT("Memory spaces for meshes do not match; see warnings."); - break; - } - case ExecutionModeError::BAD_MODE_FOR_MEMORY_SPACE: - { - SLIC_WARNING_ROOT("Memory space is not compatible with execution mode; see warnings."); - break; - } - case ExecutionModeError::INCOMPATIBLE_METHOD: - { - SLIC_WARNING_ROOT("Contact method not compatible with execution mode; see warnings."); - break; - } - default: - break; - } // end switch over execution mode errors -} // end CouplingSchemeErrors::printExecutionModeErrors() + switch ( this->cs_execution_mode_error ) { + case ExecutionModeError::NON_MATCHING_MEMORY_SPACE: { + SLIC_WARNING_ROOT( "Memory spaces for meshes do not match; see warnings." ); + break; + } + case ExecutionModeError::BAD_MODE_FOR_MEMORY_SPACE: { + SLIC_WARNING_ROOT( "Memory space is not compatible with execution mode; see warnings." ); + break; + } + case ExecutionModeError::INCOMPATIBLE_METHOD: { + SLIC_WARNING_ROOT( "Contact method not compatible with execution mode; see warnings." ); + break; + } + default: + break; + } // end switch over execution mode errors +} // end CouplingSchemeErrors::printExecutionModeErrors() //------------------------------------------------------------------------------ // Struct implementation for CouplingSchemeInfo //------------------------------------------------------------------------------ void CouplingSchemeInfo::printCaseInfo() { - switch(this->cs_case_info) - { - case SPECIFYING_NO_SLIDING_WITH_REGISTERED_MODE: - { - SLIC_DEBUG_ROOT("Overriding with ContactCase=NO_SLIDING with registered ContactMode."); - break; - } - case SPECIFYING_NO_SLIDING_WITH_REGISTERED_METHOD: - { - SLIC_DEBUG_ROOT("Overriding with ContactCase=NO_SLIDING with registered ContactMethod."); - break; - } - case SPECIFYING_NONE_WITH_REGISTERED_METHOD: - { - SLIC_DEBUG_ROOT("Overriding with ContactCase=NO_CASE with registered ContactMethod."); - break; - } - case NO_CASE_INFO: - { - break; - } - default: - break; - } // end switch over case info -} // end CouplingSchemeInfo::printCaseInfo() + switch ( this->cs_case_info ) { + case SPECIFYING_NO_SLIDING_WITH_REGISTERED_MODE: { + SLIC_DEBUG_ROOT( "Overriding with ContactCase=NO_SLIDING with registered ContactMode." ); + break; + } + case SPECIFYING_NO_SLIDING_WITH_REGISTERED_METHOD: { + SLIC_DEBUG_ROOT( "Overriding with ContactCase=NO_SLIDING with registered ContactMethod." ); + break; + } + case SPECIFYING_NONE_WITH_REGISTERED_METHOD: { + SLIC_DEBUG_ROOT( "Overriding with ContactCase=NO_CASE with registered ContactMethod." ); + break; + } + case NO_CASE_INFO: { + break; + } + default: + break; + } // end switch over case info +} // end CouplingSchemeInfo::printCaseInfo() //------------------------------------------------------------------------------ void CouplingSchemeInfo::printExecutionModeInfo() { - switch(this->cs_execution_mode_info) - { - case ExecutionModeInfo::NONOPTIMAL_MODE_FOR_MEMORY_SPACE: - { - SLIC_WARNING_ROOT("Execution mode is not optimal for memory space; see warnings."); - break; - } - default: - break; - } // end switch over execution mode info -} // end CouplingSchemeInfo::printExecutionModeInfo() + switch ( this->cs_execution_mode_info ) { + case ExecutionModeInfo::NONOPTIMAL_MODE_FOR_MEMORY_SPACE: { + SLIC_WARNING_ROOT( "Execution mode is not optimal for memory space; see warnings." ); + break; + } + default: + break; + } // end switch over execution mode info +} // end CouplingSchemeInfo::printExecutionModeInfo() //------------------------------------------------------------------------------ void CouplingSchemeInfo::printEnforcementInfo() { - switch(this->cs_enforcement_info) - { - case SPECIFYING_NULL_ENFORCEMENT_WITH_REGISTERED_METHOD: - { - SLIC_DEBUG_ROOT("Overriding with EnforcementMethod=NULL_ENFORCEMENT with registered ContactMethod."); - break; - } - case NO_ENFORCEMENT_INFO: - { - break; - } - default: - break; - } // end switch over enforcement info -} // end CouplingSchemeInfo::printEnforcementInfo() + switch ( this->cs_enforcement_info ) { + case SPECIFYING_NULL_ENFORCEMENT_WITH_REGISTERED_METHOD: { + SLIC_DEBUG_ROOT( "Overriding with EnforcementMethod=NULL_ENFORCEMENT with registered ContactMethod." ); + break; + } + case NO_ENFORCEMENT_INFO: { + break; + } + default: + break; + } // end switch over enforcement info +} // end CouplingSchemeInfo::printEnforcementInfo() //------------------------------------------------------------------------------ // CouplingScheme class implementation //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ -CouplingScheme::CouplingScheme( IndexT cs_id, - IndexT mesh_id1, - IndexT mesh_id2, - int contact_mode, - int contact_case, - int contact_method, - int contact_model, - int enforcement_method, - int binning_method, +CouplingScheme::CouplingScheme( IndexT cs_id, IndexT mesh_id1, IndexT mesh_id2, int contact_mode, int contact_case, + int contact_method, int contact_model, int enforcement_method, int binning_method, ExecutionMode given_exec_mode ) - : m_id ( cs_id ) - , m_mesh_id1 ( mesh_id1 ) - , m_mesh_id2 ( mesh_id2 ) - , m_given_exec_mode ( given_exec_mode ) - , m_numTotalNodes ( 0 ) - , m_fixedBinning ( false ) - , m_isBinned ( false ) - , m_isTied ( false ) - , m_methodData ( nullptr ) + : m_id( cs_id ), + m_mesh_id1( mesh_id1 ), + m_mesh_id2( mesh_id2 ), + m_given_exec_mode( given_exec_mode ), + m_numTotalNodes( 0 ), + m_fixedBinning( false ), + m_isBinned( false ), + m_isTied( false ), + m_methodData( nullptr ) { // error sanity checks - SLIC_ERROR_ROOT_IF( mesh_id1==ANY_MESH, "mesh_id1 cannot be set to ANY_MESH" ); + SLIC_ERROR_ROOT_IF( mesh_id1 == ANY_MESH, "mesh_id1 cannot be set to ANY_MESH" ); SLIC_ERROR_ROOT_IF( !validMeshID( m_mesh_id1 ), "invalid mesh_id1=" << mesh_id1 ); SLIC_ERROR_ROOT_IF( !validMeshID( m_mesh_id2 ), "invalid mesh_id2=" << mesh_id2 ); - SLIC_ERROR_ROOT_IF( !in_range( contact_mode, NUM_CONTACT_MODES ), - "invalid contact_mode=" << contact_mode ); - SLIC_ERROR_ROOT_IF( !in_range( contact_method, NUM_CONTACT_METHODS ), - "invalid contact_method=" << contact_method ); - SLIC_ERROR_ROOT_IF( !in_range( contact_model, NUM_CONTACT_MODELS ), - "invalid contact_model=" << contact_model ); + SLIC_ERROR_ROOT_IF( !in_range( contact_mode, NUM_CONTACT_MODES ), "invalid contact_mode=" << contact_mode ); + SLIC_ERROR_ROOT_IF( !in_range( contact_method, NUM_CONTACT_METHODS ), "invalid contact_method=" << contact_method ); + SLIC_ERROR_ROOT_IF( !in_range( contact_model, NUM_CONTACT_MODELS ), "invalid contact_model=" << contact_model ); SLIC_ERROR_ROOT_IF( !in_range( enforcement_method, NUM_ENFORCEMENT_METHODS ), "invalid enforcement_method=" << enforcement_method ); - SLIC_ERROR_ROOT_IF( !in_range( binning_method, NUM_BINNING_METHODS ), - "invalid binning_method=" << binning_method ); + SLIC_ERROR_ROOT_IF( !in_range( binning_method, NUM_BINNING_METHODS ), "invalid binning_method=" << binning_method ); m_contactMode = static_cast( contact_mode ); m_contactCase = static_cast( contact_case ); @@ -391,28 +329,25 @@ CouplingScheme::CouplingScheme( IndexT cs_id, m_enforcementMethod = static_cast( enforcement_method ); m_binningMethod = static_cast( binning_method ); - m_couplingSchemeErrors.cs_mode_error = NO_MODE_ERROR; - m_couplingSchemeErrors.cs_case_error = NO_CASE_ERROR; - m_couplingSchemeErrors.cs_method_error = NO_METHOD_ERROR; - m_couplingSchemeErrors.cs_model_error = NO_MODEL_ERROR; + m_couplingSchemeErrors.cs_mode_error = NO_MODE_ERROR; + m_couplingSchemeErrors.cs_case_error = NO_CASE_ERROR; + m_couplingSchemeErrors.cs_method_error = NO_METHOD_ERROR; + m_couplingSchemeErrors.cs_model_error = NO_MODEL_ERROR; m_couplingSchemeErrors.cs_enforcement_error = NO_ENFORCEMENT_ERROR; - m_couplingSchemeInfo.cs_case_info = NO_CASE_INFO; + m_couplingSchemeInfo.cs_case_info = NO_CASE_INFO; m_couplingSchemeInfo.cs_enforcement_info = NO_ENFORCEMENT_INFO; m_loggingLevel = TRIBOL_UNDEFINED; -} // end CouplingScheme::CouplingScheme() +} // end CouplingScheme::CouplingScheme() //------------------------------------------------------------------------------ const ContactPlane& CouplingScheme::getContactPlane( IndexT id ) const { - if (spatialDimension() == 2) - { + if ( spatialDimension() == 2 ) { return m_contact_plane2d[id]; - } - else - { + } else { return m_contact_plane3d[id]; } } @@ -420,581 +355,472 @@ const ContactPlane& CouplingScheme::getContactPlane( IndexT id ) const //------------------------------------------------------------------------------ bool CouplingScheme::setMeshPointers() { - // verify meshes exist and set pointers to meshes - MeshManager & meshManager = MeshManager::getInstance(); - if (!meshManager.findData(this->m_mesh_id1) || !meshManager.findData(this->m_mesh_id2)) - { - SLIC_WARNING_ROOT("Please register meshes for coupling scheme, " << this->m_id << "."); - return false; - } + // verify meshes exist and set pointers to meshes + MeshManager& meshManager = MeshManager::getInstance(); + if ( !meshManager.findData( this->m_mesh_id1 ) || !meshManager.findData( this->m_mesh_id2 ) ) { + SLIC_WARNING_ROOT( "Please register meshes for coupling scheme, " << this->m_id << "." ); + return false; + } - this->m_mesh1 = &MeshManager::getInstance().at( this->m_mesh_id1 ); - this->m_mesh2 = &MeshManager::getInstance().at( this->m_mesh_id2 ); + this->m_mesh1 = &MeshManager::getInstance().at( this->m_mesh_id1 ); + this->m_mesh2 = &MeshManager::getInstance().at( this->m_mesh_id2 ); - return true; + return true; } //------------------------------------------------------------------------------ bool CouplingScheme::isValidCouplingScheme() { - bool valid {true}; + bool valid{ true }; - if (!setMeshPointers()) - { - return false; - } - - // set boolean for null meshes - this->m_nullMeshes = this->m_mesh1->numberOfElements() <= 0 || this->m_mesh2->numberOfElements() <= 0; - - // check for invalid mesh topology matches in a coupling scheme - if (this->m_mesh1->getElementType() != this->m_mesh2->getElementType()) - { - SLIC_WARNING_ROOT("Coupling scheme " << this->m_id << " does not support meshes with " << - "different surface element types."); - this->m_mesh1->isMeshValid() = false; - this->m_mesh2->isMeshValid() = false; - } - - // check for invalid meshes. A mesh could be deemed invalid when registered. - if (!this->m_mesh1->isMeshValid() || !this->m_mesh2->isMeshValid()) - { - return false; - } + if ( !setMeshPointers() ) { + return false; + } - // check valid contact mode. Not all modes have an implementation - if (!this->isValidMode()) - { - this->m_couplingSchemeErrors.printModeErrors(); - valid = false; - } - - // TODO check whether info should be printed before - // errors in case AUTO needs to be change to NO_CASE - // and the check on element thickness needs to be modified - if (!this->isValidCase()) - { - this->m_couplingSchemeErrors.printCaseErrors(); - valid = false; - } - else - { - // print reasons why case may have been modified - this->m_couplingSchemeInfo.printCaseInfo(); - } - - if (!this->isValidMethod()) - { - this->m_couplingSchemeErrors.printMethodErrors(); - valid = false; - } + // set boolean for null meshes + this->m_nullMeshes = this->m_mesh1->numberOfElements() <= 0 || this->m_mesh2->numberOfElements() <= 0; - if (!this->isValidModel()) - { - this->m_couplingSchemeErrors.printModelErrors(); - valid = false; - } + // check for invalid mesh topology matches in a coupling scheme + if ( this->m_mesh1->getElementType() != this->m_mesh2->getElementType() ) { + SLIC_WARNING_ROOT( "Coupling scheme " << this->m_id << " does not support meshes with " + << "different surface element types." ); + this->m_mesh1->isMeshValid() = false; + this->m_mesh2->isMeshValid() = false; + } - if (!this->isValidEnforcement()) - { - this->m_couplingSchemeErrors.printEnforcementErrors(); - valid = false; - } - else if (this->checkEnforcementData() != 0) - { - this->m_couplingSchemeErrors.printEnforcementDataErrors(); + // check for invalid meshes. A mesh could be deemed invalid when registered. + if ( !this->m_mesh1->isMeshValid() || !this->m_mesh2->isMeshValid() ) { + return false; + } + + // check valid contact mode. Not all modes have an implementation + if ( !this->isValidMode() ) { + this->m_couplingSchemeErrors.printModeErrors(); + valid = false; + } + + // TODO check whether info should be printed before + // errors in case AUTO needs to be change to NO_CASE + // and the check on element thickness needs to be modified + if ( !this->isValidCase() ) { + this->m_couplingSchemeErrors.printCaseErrors(); + valid = false; + } else { + // print reasons why case may have been modified + this->m_couplingSchemeInfo.printCaseInfo(); + } + + if ( !this->isValidMethod() ) { + this->m_couplingSchemeErrors.printMethodErrors(); + valid = false; + } + + if ( !this->isValidModel() ) { + this->m_couplingSchemeErrors.printModelErrors(); + valid = false; + } + + if ( !this->isValidEnforcement() ) { + this->m_couplingSchemeErrors.printEnforcementErrors(); + valid = false; + } else if ( this->checkEnforcementData() != 0 ) { + this->m_couplingSchemeErrors.printEnforcementDataErrors(); + valid = false; + } + + switch ( this->checkExecutionModeData() ) { + case 1: + this->m_couplingSchemeErrors.printExecutionModeErrors(); valid = false; - } - - switch (this->checkExecutionModeData()) - { - case 1: - this->m_couplingSchemeErrors.printExecutionModeErrors(); - valid = false; - break; - case 2: - this->m_couplingSchemeInfo.printExecutionModeInfo(); - break; - default: - // no info or error messages - break; - } + break; + case 2: + this->m_couplingSchemeInfo.printExecutionModeInfo(); + break; + default: + // no info or error messages + break; + } - return valid; -} // end CouplingScheme::isValidCouplingScheme() + return valid; +} // end CouplingScheme::isValidCouplingScheme() //------------------------------------------------------------------------------ bool CouplingScheme::isValidMode() { - // check if contactMode is not an existing option - if ( !in_range(this->m_contactMode, NUM_CONTACT_MODES) ) - { - this->m_couplingSchemeErrors.cs_mode_error = INVALID_MODE; - return false; - } - else if (this->m_contactMode != SURFACE_TO_SURFACE && - this->m_contactMode != SURFACE_TO_SURFACE_CONFORMING) - { - this->m_couplingSchemeErrors.cs_mode_error = NO_MODE_IMPLEMENTATION; - return false; - } - else - { - this->m_couplingSchemeErrors.cs_mode_error = NO_MODE_ERROR; - } - return true; -} // end CouplingScheme::isValidMode() + // check if contactMode is not an existing option + if ( !in_range( this->m_contactMode, NUM_CONTACT_MODES ) ) { + this->m_couplingSchemeErrors.cs_mode_error = INVALID_MODE; + return false; + } else if ( this->m_contactMode != SURFACE_TO_SURFACE && this->m_contactMode != SURFACE_TO_SURFACE_CONFORMING ) { + this->m_couplingSchemeErrors.cs_mode_error = NO_MODE_IMPLEMENTATION; + return false; + } else { + this->m_couplingSchemeErrors.cs_mode_error = NO_MODE_ERROR; + } + return true; +} // end CouplingScheme::isValidMode() //------------------------------------------------------------------------------ bool CouplingScheme::isValidCase() { - // set to no error until otherwise noted - this->m_couplingSchemeErrors.cs_case_error = NO_CASE_ERROR; - bool isValid = true; // pre-set to a valid case - - // check if contactCase is not an existing option - if ( !in_range(this->m_contactCase, NUM_CONTACT_CASES) ) - { - this->m_couplingSchemeErrors.cs_case_error = INVALID_CASE; - isValid = false; - } - - // modify incompatible case with SURFACE_TO_SURFACE_CONFORMING to - // NO_SLIDING - if (this->m_contactMode == SURFACE_TO_SURFACE_CONFORMING && - this->m_contactCase != NO_SLIDING) - { - this->m_couplingSchemeInfo.cs_case_info = SPECIFYING_NO_SLIDING_WITH_REGISTERED_MODE; - this->m_contactCase = NO_SLIDING; - } - - // make sure NO_SLIDING case is specified with ALIGNED_MORTAR - if (this->m_contactMethod == ALIGNED_MORTAR && - this->m_contactCase != NO_SLIDING) - { - this->m_couplingSchemeInfo.cs_case_info = SPECIFYING_NO_SLIDING_WITH_REGISTERED_METHOD; - this->m_contactCase = NO_SLIDING; - } - - // catch invalid case with SINGLE_MORTAR and MORTAR_WEIGHTS and switch - // case to NONE (no case required). - if ((this->m_contactMethod == SINGLE_MORTAR || - this->m_contactMethod == MORTAR_WEIGHTS) && - (this->m_contactCase != NO_CASE && this->m_contactCase != NO_SLIDING)) - { - this->m_couplingSchemeInfo.cs_case_info = SPECIFYING_NONE_WITH_REGISTERED_METHOD; - this->m_contactCase = NO_CASE; - } - - if (this->m_contactMethod == COMMON_PLANE) - { - switch (this->m_contactCase) - { - case AUTO: - { - // enable auto-contact specific checks through boolean, and check to - // make sure element thicknesses have been registered for auto-contact - // length scale calculations - this->m_parameters.auto_contact_check = true; - - if (!this->m_mesh1->getElementData().m_is_element_thickness_set || - !this->m_mesh2->getElementData().m_is_element_thickness_set) - { - this->m_couplingSchemeErrors.cs_case_error = INVALID_CASE_DATA; - isValid = false; - } - break; - } - case TIED_FULL: - { - // uncomment when there is an implementation - //this->m_parameters.auto_contact_check = false; - this->m_couplingSchemeErrors.cs_case_error = NO_CASE_IMPLEMENTATION; - isValid = false; - break; - } - default: - this->m_parameters.auto_contact_check = false; - } // end switch on case - } // end if check on common-plane - - return isValid; -} // end CouplingScheme::isValidCase() + // set to no error until otherwise noted + this->m_couplingSchemeErrors.cs_case_error = NO_CASE_ERROR; + bool isValid = true; // pre-set to a valid case + + // check if contactCase is not an existing option + if ( !in_range( this->m_contactCase, NUM_CONTACT_CASES ) ) { + this->m_couplingSchemeErrors.cs_case_error = INVALID_CASE; + isValid = false; + } + + // modify incompatible case with SURFACE_TO_SURFACE_CONFORMING to + // NO_SLIDING + if ( this->m_contactMode == SURFACE_TO_SURFACE_CONFORMING && this->m_contactCase != NO_SLIDING ) { + this->m_couplingSchemeInfo.cs_case_info = SPECIFYING_NO_SLIDING_WITH_REGISTERED_MODE; + this->m_contactCase = NO_SLIDING; + } + + // make sure NO_SLIDING case is specified with ALIGNED_MORTAR + if ( this->m_contactMethod == ALIGNED_MORTAR && this->m_contactCase != NO_SLIDING ) { + this->m_couplingSchemeInfo.cs_case_info = SPECIFYING_NO_SLIDING_WITH_REGISTERED_METHOD; + this->m_contactCase = NO_SLIDING; + } + + // catch invalid case with SINGLE_MORTAR and MORTAR_WEIGHTS and switch + // case to NONE (no case required). + if ( ( this->m_contactMethod == SINGLE_MORTAR || this->m_contactMethod == MORTAR_WEIGHTS ) && + ( this->m_contactCase != NO_CASE && this->m_contactCase != NO_SLIDING ) ) { + this->m_couplingSchemeInfo.cs_case_info = SPECIFYING_NONE_WITH_REGISTERED_METHOD; + this->m_contactCase = NO_CASE; + } + + if ( this->m_contactMethod == COMMON_PLANE ) { + switch ( this->m_contactCase ) { + case AUTO: { + // enable auto-contact specific checks through boolean, and check to + // make sure element thicknesses have been registered for auto-contact + // length scale calculations + this->m_parameters.auto_contact_check = true; + + if ( !this->m_mesh1->getElementData().m_is_element_thickness_set || + !this->m_mesh2->getElementData().m_is_element_thickness_set ) { + this->m_couplingSchemeErrors.cs_case_error = INVALID_CASE_DATA; + isValid = false; + } + break; + } + case TIED_FULL: { + // uncomment when there is an implementation + // this->m_parameters.auto_contact_check = false; + this->m_couplingSchemeErrors.cs_case_error = NO_CASE_IMPLEMENTATION; + isValid = false; + break; + } + default: + this->m_parameters.auto_contact_check = false; + } // end switch on case + } // end if check on common-plane + + return isValid; +} // end CouplingScheme::isValidCase() //------------------------------------------------------------------------------ bool CouplingScheme::isValidMethod() { - //////////////////////// - // NOTE // - //////////////////////// - // Any new method has to be added as a case in the switch statement, even - // if there are no specific checks, otherwise Tribol will error out assuming - // that there is no implementation for a method in the ContactMethod enum list - - // check if contactMethod is not an existing option - if ( !in_range(this->m_contactMethod, NUM_CONTACT_METHODS) ) - { - this->m_couplingSchemeErrors.cs_method_error = INVALID_METHOD; + //////////////////////// + // NOTE // + //////////////////////// + // Any new method has to be added as a case in the switch statement, even + // if there are no specific checks, otherwise Tribol will error out assuming + // that there is no implementation for a method in the ContactMethod enum list + + // check if contactMethod is not an existing option + if ( !in_range( this->m_contactMethod, NUM_CONTACT_METHODS ) ) { + this->m_couplingSchemeErrors.cs_method_error = INVALID_METHOD; + return false; + } + + int dim = this->spatialDimension(); + + // check all methods for basic validity issues for non-null meshes + if ( !this->m_nullMeshes ) { + if ( ( this->m_mesh1->getElementType() != LINEAR_EDGE && this->m_mesh1->getElementType() != LINEAR_QUAD ) || + ( this->m_mesh2->getElementType() != LINEAR_EDGE && this->m_mesh2->getElementType() != LINEAR_QUAD ) ) { + this->m_couplingSchemeErrors.cs_method_error = INVALID_ELEMENT_TYPE; return false; - } - - int dim = this->spatialDimension(); - - // check all methods for basic validity issues for non-null meshes - if (!this->m_nullMeshes) - { - if ((this->m_mesh1->getElementType() != LINEAR_EDGE && - this->m_mesh1->getElementType() != LINEAR_QUAD) || - (this->m_mesh2->getElementType() != LINEAR_EDGE && - this->m_mesh2->getElementType() != LINEAR_QUAD)) - { - this->m_couplingSchemeErrors.cs_method_error = INVALID_ELEMENT_TYPE; - return false; + } + + if ( this->m_contactMethod == ALIGNED_MORTAR || this->m_contactMethod == MORTAR_WEIGHTS || + this->m_contactMethod == SINGLE_MORTAR ) { + if ( this->m_mesh1->numberOfNodesPerElement() != this->m_mesh2->numberOfNodesPerElement() ) { + this->m_couplingSchemeErrors.cs_method_error = DIFFERENT_FACE_TYPES; + return false; + } + if ( this->m_mesh_id1 == this->m_mesh_id2 ) { + this->m_couplingSchemeErrors.cs_method_error = SAME_MESH_IDS; + if ( dim != 3 ) { + this->m_couplingSchemeErrors.cs_method_error = SAME_MESH_IDS_INVALID_DIM; + } + return false; } - if ( this->m_contactMethod == ALIGNED_MORTAR || - this->m_contactMethod == MORTAR_WEIGHTS || - this->m_contactMethod == SINGLE_MORTAR ) - { - if (this->m_mesh1->numberOfNodesPerElement() != this->m_mesh2->numberOfNodesPerElement()) - { - this->m_couplingSchemeErrors.cs_method_error = DIFFERENT_FACE_TYPES; - return false; - } - if( this->m_mesh_id1 == this->m_mesh_id2 ) - { - this->m_couplingSchemeErrors.cs_method_error = SAME_MESH_IDS; - if (dim != 3) - { - this->m_couplingSchemeErrors.cs_method_error = SAME_MESH_IDS_INVALID_DIM; - } - return false; - } - - if (dim != 3) - { - this->m_couplingSchemeErrors.cs_method_error = INVALID_DIM; - return false; - } + if ( dim != 3 ) { + this->m_couplingSchemeErrors.cs_method_error = INVALID_DIM; + return false; } - else if ( this->m_contactMethod == COMMON_PLANE ) - { - // check for different face types. This is not yet supported - if (this->m_mesh1->numberOfNodesPerElement() != this->m_mesh2->numberOfNodesPerElement()) - { - this->m_couplingSchemeErrors.cs_method_error = DIFFERENT_FACE_TYPES; - return false; - } - } // end switch on contact method - else - { - // if we are here there may be a method with no implementation. - // See note at top of routine. - this->m_couplingSchemeErrors.cs_method_error = NO_METHOD_IMPLEMENTATION; - return false; + } else if ( this->m_contactMethod == COMMON_PLANE ) { + // check for different face types. This is not yet supported + if ( this->m_mesh1->numberOfNodesPerElement() != this->m_mesh2->numberOfNodesPerElement() ) { + this->m_couplingSchemeErrors.cs_method_error = DIFFERENT_FACE_TYPES; + return false; } + } // end switch on contact method + else { + // if we are here there may be a method with no implementation. + // See note at top of routine. + this->m_couplingSchemeErrors.cs_method_error = NO_METHOD_IMPLEMENTATION; + return false; + } - if ( this->m_contactMethod == ALIGNED_MORTAR || - this->m_contactMethod == SINGLE_MORTAR || - this->m_contactMethod == COMMON_PLANE ) - { - if ( this->m_mesh1->numberOfElements() > 0 && !this->m_mesh1->getNodalFields().m_is_nodal_response_set ) - { - this->m_couplingSchemeErrors.cs_method_error = NULL_NODAL_RESPONSE; - return false; - } - - if ( this->m_mesh2->numberOfElements() > 0 && !this->m_mesh2->getNodalFields().m_is_nodal_response_set ) - { - this->m_couplingSchemeErrors.cs_method_error = NULL_NODAL_RESPONSE; - return false; - } - + if ( this->m_contactMethod == ALIGNED_MORTAR || this->m_contactMethod == SINGLE_MORTAR || + this->m_contactMethod == COMMON_PLANE ) { + if ( this->m_mesh1->numberOfElements() > 0 && !this->m_mesh1->getNodalFields().m_is_nodal_response_set ) { + this->m_couplingSchemeErrors.cs_method_error = NULL_NODAL_RESPONSE; + return false; } - } // end if-check on non-null meshes - // TODO check for nodal displacements for methods that require this data + if ( this->m_mesh2->numberOfElements() > 0 && !this->m_mesh2->getNodalFields().m_is_nodal_response_set ) { + this->m_couplingSchemeErrors.cs_method_error = NULL_NODAL_RESPONSE; + return false; + } + } + } // end if-check on non-null meshes + + // TODO check for nodal displacements for methods that require this data - // no method error if here - this->m_couplingSchemeErrors.cs_method_error = NO_METHOD_ERROR; - return true; + // no method error if here + this->m_couplingSchemeErrors.cs_method_error = NO_METHOD_ERROR; + return true; -} // end CouplingScheme::isValidMethod() +} // end CouplingScheme::isValidMethod() //------------------------------------------------------------------------------ bool CouplingScheme::isValidModel() { - // Note: add a method check for compatible models when implementing a new - // method in Tribol + // Note: add a method check for compatible models when implementing a new + // method in Tribol - // check if the m_contactModel is not an existing option - if ( !in_range(this->m_contactModel, NUM_CONTACT_MODELS) ) - { - this->m_couplingSchemeErrors.cs_model_error = INVALID_MODEL; - return false; - } + // check if the m_contactModel is not an existing option + if ( !in_range( this->m_contactModel, NUM_CONTACT_MODELS ) ) { + this->m_couplingSchemeErrors.cs_model_error = INVALID_MODEL; + return false; + } - // check for model and method compatibility issues - switch (this->m_contactMethod) - { - case SINGLE_MORTAR: - case ALIGNED_MORTAR: - case MORTAR_WEIGHTS: - { - if ( this->m_contactModel != FRICTIONLESS && this->m_contactModel != NULL_MODEL ) - { - this->m_couplingSchemeErrors.cs_model_error = NO_MODEL_IMPLEMENTATION_FOR_REGISTERED_METHOD; - return false; - } - break; + // check for model and method compatibility issues + switch ( this->m_contactMethod ) { + case SINGLE_MORTAR: + case ALIGNED_MORTAR: + case MORTAR_WEIGHTS: { + if ( this->m_contactModel != FRICTIONLESS && this->m_contactModel != NULL_MODEL ) { + this->m_couplingSchemeErrors.cs_model_error = NO_MODEL_IMPLEMENTATION_FOR_REGISTERED_METHOD; + return false; } + break; + } - case COMMON_PLANE: - { - if ( this->m_contactModel != FRICTIONLESS && - this->m_contactModel != NULL_MODEL ) - { - this->m_couplingSchemeErrors.cs_model_error = NO_MODEL_IMPLEMENTATION_FOR_REGISTERED_METHOD; - return false; - } - if ( this->m_contactCase == TIED_NORMAL && this->m_contactModel == ADHESION_SEPARATION_SCALAR_LAW ) - { - this->m_couplingSchemeErrors.cs_model_error = NO_MODEL_IMPLEMENTATION; - return false; - } - break; + case COMMON_PLANE: { + if ( this->m_contactModel != FRICTIONLESS && this->m_contactModel != NULL_MODEL ) { + this->m_couplingSchemeErrors.cs_model_error = NO_MODEL_IMPLEMENTATION_FOR_REGISTERED_METHOD; + return false; } - - default: - { - // Don't need to add default error/info. Compatibility is driven by existing - // method implementations, which are checked in isValidMethod() - break; + if ( this->m_contactCase == TIED_NORMAL && this->m_contactModel == ADHESION_SEPARATION_SCALAR_LAW ) { + this->m_couplingSchemeErrors.cs_model_error = NO_MODEL_IMPLEMENTATION; + return false; } - } // end switch + break; + } + + default: { + // Don't need to add default error/info. Compatibility is driven by existing + // method implementations, which are checked in isValidMethod() + break; + } + } // end switch - this->m_couplingSchemeErrors.cs_model_error = NO_MODEL_ERROR; - return true; -} // end isValidModel() + this->m_couplingSchemeErrors.cs_model_error = NO_MODEL_ERROR; + return true; +} // end isValidModel() //------------------------------------------------------------------------------ bool CouplingScheme::isValidEnforcement() { - // NOTE: Add a method check here for compatible enforcement when adding a - // new method to Tribol + // NOTE: Add a method check here for compatible enforcement when adding a + // new method to Tribol - // check if the enforcementMethod is not an existing option - if ( !in_range(this->m_enforcementMethod, NUM_ENFORCEMENT_METHODS) ) - { - this->m_couplingSchemeErrors.cs_enforcement_error = INVALID_ENFORCEMENT; - return false; - } - - // check for invalid method/enforcement compatibility - switch (this->m_contactMethod) - { - case MORTAR_WEIGHTS: - { - // force NULL_ENFORCEMENT for MORTAR_WEIGHTS. Only possible choice - if (this->m_enforcementMethod != NULL_ENFORCEMENT) - { - this->m_couplingSchemeInfo.cs_enforcement_info = - SPECIFYING_NULL_ENFORCEMENT_WITH_REGISTERED_METHOD; - this->m_enforcementMethod = NULL_ENFORCEMENT; - // don't return - } - if ( this->m_enforcementOptions.lm_implicit_options.eval_mode != ImplicitEvalMode::MORTAR_WEIGHTS_EVAL ) - { - // Note, not adding a cs_enforcement_info note here since MORTAR_WEIGHTS only - // works with this eval mode. This is simply protecting a user from specifying - // something that doesn't make sense for this specialized 'method'. This does - // not affect requirements on registered data or output for the user. - this->m_enforcementOptions.lm_implicit_options.eval_mode = ImplicitEvalMode::MORTAR_WEIGHTS_EVAL; - // don't return - } - if ( this->m_enforcementOptions.lm_implicit_options.sparse_mode != SparseMode::MFEM_LINKED_LIST ) - { - this->m_couplingSchemeErrors.cs_enforcement_error = - NO_ENFORCEMENT_IMPLEMENTATION_FOR_REGISTERED_OPTION; - return false; - } - break; - } // end case MORTAR_WEIGHTS - - case ALIGNED_MORTAR: - case SINGLE_MORTAR: - { - if ( this->m_enforcementMethod == PENALTY ) - { - this->m_couplingSchemeErrors.cs_enforcement_error = - NO_ENFORCEMENT_IMPLEMENTATION_FOR_REGISTERED_METHOD; - return false; - } - else if ( this->m_enforcementMethod != LAGRANGE_MULTIPLIER ) - { - // Don't change to valid enforcement method. Data required - // for valid method likely not registered - this->m_couplingSchemeErrors.cs_enforcement_error = - INVALID_ENFORCEMENT_FOR_REGISTERED_METHOD; - return false; - } - else if ( this->m_enforcementMethod == LAGRANGE_MULTIPLIER ) - { - if ( !this->m_enforcementOptions.lm_implicit_options.enforcement_option_set ) - { - this->m_couplingSchemeErrors.cs_enforcement_error = - OPTIONS_NOT_SET; - return false; - } - else if ( - this->m_enforcementOptions.lm_implicit_options.sparse_mode != SparseMode::MFEM_LINKED_LIST && - this->m_enforcementOptions.lm_implicit_options.sparse_mode != - SparseMode::MFEM_ELEMENT_DENSE ) - { - this->m_couplingSchemeErrors.cs_enforcement_error = - NO_ENFORCEMENT_IMPLEMENTATION_FOR_REGISTERED_OPTION; - return false; - } - else if ( this->m_enforcementOptions.lm_implicit_options.eval_mode == ImplicitEvalMode::MORTAR_WEIGHTS_EVAL ) - { - this->m_couplingSchemeErrors.cs_enforcement_error = - NO_ENFORCEMENT_IMPLEMENTATION_FOR_REGISTERED_OPTION; - return false; - } - } - break; - } // end case SINGLE_MORTAR + // check if the enforcementMethod is not an existing option + if ( !in_range( this->m_enforcementMethod, NUM_ENFORCEMENT_METHODS ) ) { + this->m_couplingSchemeErrors.cs_enforcement_error = INVALID_ENFORCEMENT; + return false; + } - case COMMON_PLANE: - { - // check if PENALTY is not chosen. This is the only possible (and foreseeable) - // choice for COMMON_PLANE - if ( this->m_enforcementMethod != PENALTY ) - { - this->m_couplingSchemeErrors.cs_enforcement_error = - INVALID_ENFORCEMENT_FOR_REGISTERED_METHOD; - return false; - } - else if ( !this->m_enforcementOptions.penalty_options.constraint_type_set ) - { - this->m_couplingSchemeErrors.cs_enforcement_error = - OPTIONS_NOT_SET; - return false; - } - break; + // check for invalid method/enforcement compatibility + switch ( this->m_contactMethod ) { + case MORTAR_WEIGHTS: { + // force NULL_ENFORCEMENT for MORTAR_WEIGHTS. Only possible choice + if ( this->m_enforcementMethod != NULL_ENFORCEMENT ) { + this->m_couplingSchemeInfo.cs_enforcement_info = SPECIFYING_NULL_ENFORCEMENT_WITH_REGISTERED_METHOD; + this->m_enforcementMethod = NULL_ENFORCEMENT; + // don't return + } + if ( this->m_enforcementOptions.lm_implicit_options.eval_mode != ImplicitEvalMode::MORTAR_WEIGHTS_EVAL ) { + // Note, not adding a cs_enforcement_info note here since MORTAR_WEIGHTS only + // works with this eval mode. This is simply protecting a user from specifying + // something that doesn't make sense for this specialized 'method'. This does + // not affect requirements on registered data or output for the user. + this->m_enforcementOptions.lm_implicit_options.eval_mode = ImplicitEvalMode::MORTAR_WEIGHTS_EVAL; + // don't return + } + if ( this->m_enforcementOptions.lm_implicit_options.sparse_mode != SparseMode::MFEM_LINKED_LIST ) { + this->m_couplingSchemeErrors.cs_enforcement_error = NO_ENFORCEMENT_IMPLEMENTATION_FOR_REGISTERED_OPTION; + return false; } - - default: - { - // no default check. These are method driven and method checks are performed - // in isValidMethod(). - break; + break; + } // end case MORTAR_WEIGHTS + + case ALIGNED_MORTAR: + case SINGLE_MORTAR: { + if ( this->m_enforcementMethod == PENALTY ) { + this->m_couplingSchemeErrors.cs_enforcement_error = NO_ENFORCEMENT_IMPLEMENTATION_FOR_REGISTERED_METHOD; + return false; + } else if ( this->m_enforcementMethod != LAGRANGE_MULTIPLIER ) { + // Don't change to valid enforcement method. Data required + // for valid method likely not registered + this->m_couplingSchemeErrors.cs_enforcement_error = INVALID_ENFORCEMENT_FOR_REGISTERED_METHOD; + return false; + } else if ( this->m_enforcementMethod == LAGRANGE_MULTIPLIER ) { + if ( !this->m_enforcementOptions.lm_implicit_options.enforcement_option_set ) { + this->m_couplingSchemeErrors.cs_enforcement_error = OPTIONS_NOT_SET; + return false; + } else if ( this->m_enforcementOptions.lm_implicit_options.sparse_mode != SparseMode::MFEM_LINKED_LIST && + this->m_enforcementOptions.lm_implicit_options.sparse_mode != SparseMode::MFEM_ELEMENT_DENSE ) { + this->m_couplingSchemeErrors.cs_enforcement_error = NO_ENFORCEMENT_IMPLEMENTATION_FOR_REGISTERED_OPTION; + return false; + } else if ( this->m_enforcementOptions.lm_implicit_options.eval_mode == + ImplicitEvalMode::MORTAR_WEIGHTS_EVAL ) { + this->m_couplingSchemeErrors.cs_enforcement_error = NO_ENFORCEMENT_IMPLEMENTATION_FOR_REGISTERED_OPTION; + return false; + } } - } // end switch - - this->m_couplingSchemeErrors.cs_enforcement_error = NO_ENFORCEMENT_ERROR; - return true; -} // end CouplingScheme::isValidEnforcement() + break; + } // end case SINGLE_MORTAR + + case COMMON_PLANE: { + // check if PENALTY is not chosen. This is the only possible (and foreseeable) + // choice for COMMON_PLANE + if ( this->m_enforcementMethod != PENALTY ) { + this->m_couplingSchemeErrors.cs_enforcement_error = INVALID_ENFORCEMENT_FOR_REGISTERED_METHOD; + return false; + } else if ( !this->m_enforcementOptions.penalty_options.constraint_type_set ) { + this->m_couplingSchemeErrors.cs_enforcement_error = OPTIONS_NOT_SET; + return false; + } + break; + } + + default: { + // no default check. These are method driven and method checks are performed + // in isValidMethod(). + break; + } + } // end switch + + this->m_couplingSchemeErrors.cs_enforcement_error = NO_ENFORCEMENT_ERROR; + return true; +} // end CouplingScheme::isValidEnforcement() //------------------------------------------------------------------------------ int CouplingScheme::checkEnforcementData() { - this->m_couplingSchemeErrors.cs_enforcement_data_error - = NO_ENFORCEMENT_DATA_ERROR; + this->m_couplingSchemeErrors.cs_enforcement_data_error = NO_ENFORCEMENT_DATA_ERROR; - int err = 0; - switch (this->m_contactMethod) - { - case MORTAR_WEIGHTS: - // no-op for now - break; - case ALIGNED_MORTAR: - // don't break - case SINGLE_MORTAR: - { - switch (this->m_enforcementMethod) - { - case LAGRANGE_MULTIPLIER: - { - // check LM data. Note, this routine is guarded against null-meshes - if (this->m_mesh2->checkLagrangeMultiplierData() != 0) // nonmortar side only - { - this->m_couplingSchemeErrors.cs_enforcement_data_error = ERROR_IN_REGISTERED_ENFORCEMENT_DATA; - err = 1; - } - break; - } // end case LAGRANGE_MULTIPLIER - default: - // no-op - break; - } // end switch over enforcement method - break; - } // end case SINGLE_MORTAR - case COMMON_PLANE: - { - switch (this->m_enforcementMethod) - { - case PENALTY: - { - // check penalty data. Note, this routine is guarded against null-meshes - PenaltyEnforcementOptions& pen_enfrc_options = this->m_enforcementOptions.penalty_options; - if (this->m_mesh1->checkPenaltyData( pen_enfrc_options ) != 0 || - this->m_mesh2->checkPenaltyData( pen_enfrc_options ) != 0) - { - this->m_couplingSchemeErrors.cs_enforcement_data_error - = ERROR_IN_REGISTERED_ENFORCEMENT_DATA; - err = 1; - } - break; - } // end case PENALTY - default: - // no-op - break; - } // end switch over enforcement method - } // end case COMMON_PLANE - default: - // no-op - break; - } // end switch on method + int err = 0; + switch ( this->m_contactMethod ) { + case MORTAR_WEIGHTS: + // no-op for now + break; + case ALIGNED_MORTAR: + // don't break + case SINGLE_MORTAR: { + switch ( this->m_enforcementMethod ) { + case LAGRANGE_MULTIPLIER: { + // check LM data. Note, this routine is guarded against null-meshes + if ( this->m_mesh2->checkLagrangeMultiplierData() != 0 ) // nonmortar side only + { + this->m_couplingSchemeErrors.cs_enforcement_data_error = ERROR_IN_REGISTERED_ENFORCEMENT_DATA; + err = 1; + } + break; + } // end case LAGRANGE_MULTIPLIER + default: + // no-op + break; + } // end switch over enforcement method + break; + } // end case SINGLE_MORTAR + case COMMON_PLANE: { + switch ( this->m_enforcementMethod ) { + case PENALTY: { + // check penalty data. Note, this routine is guarded against null-meshes + PenaltyEnforcementOptions& pen_enfrc_options = this->m_enforcementOptions.penalty_options; + if ( this->m_mesh1->checkPenaltyData( pen_enfrc_options ) != 0 || + this->m_mesh2->checkPenaltyData( pen_enfrc_options ) != 0 ) { + this->m_couplingSchemeErrors.cs_enforcement_data_error = ERROR_IN_REGISTERED_ENFORCEMENT_DATA; + err = 1; + } + break; + } // end case PENALTY + default: + // no-op + break; + } // end switch over enforcement method + } // end case COMMON_PLANE + default: + // no-op + break; + } // end switch on method - return err; + return err; -} // end CouplingScheme::checkEnforcementData() +} // end CouplingScheme::checkEnforcementData() //------------------------------------------------------------------------------ int CouplingScheme::checkExecutionModeData() { int err = 0; this->m_exec_mode = ExecutionMode::Sequential; - this->m_couplingSchemeErrors.cs_execution_mode_error = - ExecutionModeError::NO_ERROR; - this->m_couplingSchemeInfo.cs_execution_mode_info = - ExecutionModeInfo::NO_INFO; - - if (this->m_mesh1->getMemorySpace() != this->m_mesh2->getMemorySpace()) - { - SLIC_WARNING_ROOT("Coupling scheme " << this->m_id << ": Paired meshes reside in " << - "different memory spaces."); + this->m_couplingSchemeErrors.cs_execution_mode_error = ExecutionModeError::NO_ERROR; + this->m_couplingSchemeInfo.cs_execution_mode_info = ExecutionModeInfo::NO_INFO; + + if ( this->m_mesh1->getMemorySpace() != this->m_mesh2->getMemorySpace() ) { + SLIC_WARNING_ROOT( "Coupling scheme " << this->m_id << ": Paired meshes reside in " + << "different memory spaces." ); this->m_mesh1->isMeshValid() = false; this->m_mesh2->isMeshValid() = false; - this->m_couplingSchemeErrors.cs_execution_mode_error = - ExecutionModeError::NON_MATCHING_MEMORY_SPACE; + this->m_couplingSchemeErrors.cs_execution_mode_error = ExecutionModeError::NON_MATCHING_MEMORY_SPACE; err = 1; return err; } - switch (this->m_mesh1->getMemorySpace()) - { + switch ( this->m_mesh1->getMemorySpace() ) { case MemorySpace::Dynamic: #ifdef TRIBOL_USE_UMPIRE // trust the user here... this->m_exec_mode = this->m_given_exec_mode; - if (this->m_exec_mode == ExecutionMode::Dynamic) - { - SLIC_WARNING_ROOT("Dynamic execution with dynamic memory space. " - "Unable to determine execution mode."); - this->m_couplingSchemeErrors.cs_execution_mode_error = - ExecutionModeError::BAD_MODE_FOR_MEMORY_SPACE; + if ( this->m_exec_mode == ExecutionMode::Dynamic ) { + SLIC_WARNING_ROOT( + "Dynamic execution with dynamic memory space. " + "Unable to determine execution mode." ); + this->m_couplingSchemeErrors.cs_execution_mode_error = ExecutionModeError::BAD_MODE_FOR_MEMORY_SPACE; err = 1; } #else @@ -1006,39 +832,39 @@ int CouplingScheme::checkExecutionModeData() case MemorySpace::Unified: // this should be able to run anywhere. let the user decide. this->m_exec_mode = this->m_given_exec_mode; - if (this->m_exec_mode == ExecutionMode::Dynamic) - { - #if defined(TRIBOL_USE_CUDA) - SLIC_INFO_ROOT("Dynamic execution with unified memory space. " - "Assuming CUDA parallel execution."); + if ( this->m_exec_mode == ExecutionMode::Dynamic ) { +#if defined( TRIBOL_USE_CUDA ) + SLIC_INFO_ROOT( + "Dynamic execution with unified memory space. " + "Assuming CUDA parallel execution." ); this->m_exec_mode = ExecutionMode::Cuda; - #elif defined(TRIBOL_USE_HIP) - SLIC_INFO_ROOT("Dynamic execution with unified memory space. " - "Assuming HIP parallel execution."); +#elif defined( TRIBOL_USE_HIP ) + SLIC_INFO_ROOT( + "Dynamic execution with unified memory space. " + "Assuming HIP parallel execution." ); this->m_exec_mode = ExecutionMode::Hip; - #elif defined(TRIBOL_USE_OPENMP) - SLIC_INFO_ROOT("Dynamic execution with unified memory space. " - "Tribol not built with CUDA/HIP support. " - "Assuming OpenMP parallel execution."); +#elif defined( TRIBOL_USE_OPENMP ) + SLIC_INFO_ROOT( + "Dynamic execution with unified memory space. " + "Tribol not built with CUDA/HIP support. " + "Assuming OpenMP parallel execution." ); this->m_exec_mode = ExecutionMode::OpenMP; - this->m_couplingSchemeInfo.cs_execution_mode_info = - ExecutionModeInfo::NONOPTIMAL_MODE_FOR_MEMORY_SPACE; + this->m_couplingSchemeInfo.cs_execution_mode_info = ExecutionModeInfo::NONOPTIMAL_MODE_FOR_MEMORY_SPACE; err = 2; - #else - SLIC_INFO_ROOT("Dynamic execution with unified memory space. " - "Tribol not built with CUDA/HIP/OpenMP support. " - "Assuming sequential execution."); +#else + SLIC_INFO_ROOT( + "Dynamic execution with unified memory space. " + "Tribol not built with CUDA/HIP/OpenMP support. " + "Assuming sequential execution." ); this->m_exec_mode = ExecutionMode::Sequential; - this->m_couplingSchemeInfo.cs_execution_mode_info = - ExecutionModeInfo::NONOPTIMAL_MODE_FOR_MEMORY_SPACE; + this->m_couplingSchemeInfo.cs_execution_mode_info = ExecutionModeInfo::NONOPTIMAL_MODE_FOR_MEMORY_SPACE; err = 2; - #endif +#endif } break; #endif case MemorySpace::Host: - switch (this->m_given_exec_mode) - { + switch ( this->m_given_exec_mode ) { case ExecutionMode::Sequential: #ifdef TRIBOL_USE_OPENMP case ExecutionMode::OpenMP: @@ -1047,80 +873,79 @@ int CouplingScheme::checkExecutionModeData() break; case ExecutionMode::Dynamic: #ifdef TRIBOL_USE_OPENMP - SLIC_INFO_ROOT("Dynamic execution with a host memory space. " - "Assuming OpenMP parallel execution."); + SLIC_INFO_ROOT( + "Dynamic execution with a host memory space. " + "Assuming OpenMP parallel execution." ); this->m_exec_mode = ExecutionMode::OpenMP; #else - SLIC_INFO_ROOT("Dynamic execution with a host memory space. " - "Assuming sequential execution."); + SLIC_INFO_ROOT( + "Dynamic execution with a host memory space. " + "Assuming sequential execution." ); this->m_exec_mode = ExecutionMode::Sequential; #endif break; default: - SLIC_WARNING_ROOT("Unsupported execution mode for host memory. " - "Unable to determine execution mode."); - this->m_couplingSchemeErrors.cs_execution_mode_error = - ExecutionModeError::BAD_MODE_FOR_MEMORY_SPACE; + SLIC_WARNING_ROOT( + "Unsupported execution mode for host memory. " + "Unable to determine execution mode." ); + this->m_couplingSchemeErrors.cs_execution_mode_error = ExecutionModeError::BAD_MODE_FOR_MEMORY_SPACE; err = 1; break; } break; #ifdef TRIBOL_USE_UMPIRE case MemorySpace::Device: - switch (this->m_given_exec_mode) - { - #if defined(TRIBOL_USE_CUDA) || defined(TRIBOL_USE_HIP) - #ifdef TRIBOL_USE_CUDA + switch ( this->m_given_exec_mode ) { +#if defined( TRIBOL_USE_CUDA ) || defined( TRIBOL_USE_HIP ) +#ifdef TRIBOL_USE_CUDA case ExecutionMode::Cuda: - #endif - #ifdef TRIBOL_USE_HIP +#endif +#ifdef TRIBOL_USE_HIP case ExecutionMode::Hip: - #endif +#endif this->m_exec_mode = this->m_given_exec_mode; break; - #endif +#endif case ExecutionMode::Dynamic: - #if defined(TRIBOL_USE_CUDA) +#if defined( TRIBOL_USE_CUDA ) this->m_exec_mode = ExecutionMode::Cuda; break; - #elif defined(TRIBOL_USE_HIP) +#elif defined( TRIBOL_USE_HIP ) this->m_exec_mode = ExecutionMode::Hip; break; - #endif +#endif default: - SLIC_ERROR_ROOT("Unknown execution mode for device memory. " - "Unable to determine execution mode."); - this->m_couplingSchemeErrors.cs_execution_mode_error = - ExecutionModeError::BAD_MODE_FOR_MEMORY_SPACE; + SLIC_ERROR_ROOT( + "Unknown execution mode for device memory. " + "Unable to determine execution mode." ); + this->m_couplingSchemeErrors.cs_execution_mode_error = ExecutionModeError::BAD_MODE_FOR_MEMORY_SPACE; err = 1; break; } #endif } - + // Update memory spaces of mesh data which are originally set as dynamic // (ensures data owned by MeshData is in the right memory space) - if (this->m_mesh1->getMemorySpace() == MemorySpace::Dynamic) - { - switch (this->m_exec_mode) - { + if ( this->m_mesh1->getMemorySpace() == MemorySpace::Dynamic ) { + switch ( this->m_exec_mode ) { case ExecutionMode::Sequential: #ifdef TRIBOL_USE_OPENMP case ExecutionMode::OpenMP: #endif - this->m_mesh1->updateAllocatorId(getResourceAllocatorID(MemorySpace::Host)); - this->m_mesh2->updateAllocatorId(getResourceAllocatorID(MemorySpace::Host)); + this->m_mesh1->updateAllocatorId( getResourceAllocatorID( MemorySpace::Host ) ); + this->m_mesh2->updateAllocatorId( getResourceAllocatorID( MemorySpace::Host ) ); break; #ifdef TRIBOL_USE_CUDA case ExecutionMode::Cuda: - this->m_mesh1->updateAllocatorId(getResourceAllocatorID(MemorySpace::Device)); - this->m_mesh2->updateAllocatorId(getResourceAllocatorID(MemorySpace::Device)); + this->m_mesh1->updateAllocatorId( getResourceAllocatorID( MemorySpace::Device ) ); + this->m_mesh2->updateAllocatorId( getResourceAllocatorID( MemorySpace::Device ) ); break; #endif #ifdef TRIBOL_USE_HIP case ExecutionMode::Hip: - this->m_mesh1->updateAllocatorId(getResourceAllocatorID(MemorySpace::Device)); - this->m_mesh2->updateAllocatorId(getResourceAllocatorID(MemorySpace::Device)); + this->m_mesh1->updateAllocatorId( getResourceAllocatorID( MemorySpace::Device ) ); + this->m_mesh2->updateAllocatorId( getResourceAllocatorID( MemorySpace::Device ) ); break; #endif default: @@ -1130,14 +955,12 @@ int CouplingScheme::checkExecutionModeData() } this->m_allocator_id = this->m_mesh1->getAllocatorId(); - if (m_contactMethod != COMMON_PLANE) - { - if (m_exec_mode != ExecutionMode::Sequential) - { - SLIC_WARNING_ROOT("Only sequential execution on host supported for contact methods " - "other than COMMON_PLANE."); - this->m_couplingSchemeErrors.cs_execution_mode_error = - ExecutionModeError::INCOMPATIBLE_METHOD; + if ( m_contactMethod != COMMON_PLANE ) { + if ( m_exec_mode != ExecutionMode::Sequential ) { + SLIC_WARNING_ROOT( + "Only sequential execution on host supported for contact methods " + "other than COMMON_PLANE." ); + this->m_couplingSchemeErrors.cs_execution_mode_error = ExecutionModeError::INCOMPATIBLE_METHOD; err = 1; } } @@ -1148,346 +971,302 @@ int CouplingScheme::checkExecutionModeData() //------------------------------------------------------------------------------ void CouplingScheme::performBinning() { - // Find the interacting pairs for this coupling scheme. Will not use - // binning if setInterfacePairs has been called. - if( !this->hasFixedBinning() ) - { - // create interface pairs based on allocator id - m_interface_pairs = ArrayT(0, 0, m_allocator_id); - - InterfacePairFinder finder(this); - finder.initialize(); - finder.findInterfacePairs(); - - // For Cartesian binning, we only need to compute the binning once - if(this->getBinningMethod() == BINNING_CARTESIAN_PRODUCT) - { - this->setFixedBinning(true); - } + // Find the interacting pairs for this coupling scheme. Will not use + // binning if setInterfacePairs has been called. + if ( !this->hasFixedBinning() ) { + // create interface pairs based on allocator id + m_interface_pairs = ArrayT( 0, 0, m_allocator_id ); + + InterfacePairFinder finder( this ); + finder.initialize(); + finder.findInterfacePairs(); + + // For Cartesian binning, we only need to compute the binning once + if ( this->getBinningMethod() == BINNING_CARTESIAN_PRODUCT ) { + this->setFixedBinning( true ); + } - // set fixed binning depending on contact case, - // e.g. NO_SLIDING - this->setFixedBinningPerCase(); - } - return; + // set fixed binning depending on contact case, + // e.g. NO_SLIDING + this->setFixedBinningPerCase(); + } + return; } //------------------------------------------------------------------------------ -int CouplingScheme::apply( int cycle, RealT t, RealT &dt ) +int CouplingScheme::apply( int cycle, RealT t, RealT& dt ) { auto& params = m_parameters; - + // loop over number of interface pairs IndexT numPairs = m_interface_pairs.size(); - SLIC_DEBUG("Coupling scheme " << m_id << " has " << numPairs << " pairs."); + SLIC_DEBUG( "Coupling scheme " << m_id << " has " << numPairs << " pairs." ); // loop over all pairs and perform geometry checks to see if they are // interacting auto pairs = getInterfacePairs().view(); auto contact_method = m_contactMethod; auto contact_case = m_contactCase; - ArrayT pair_err_data(1, 1, getAllocatorId()); + ArrayT pair_err_data( 1, 1, getAllocatorId() ); auto pair_err = pair_err_data.view(); // clear contact planes to be populated/allocated anew for this cycle. // initially allocate array of numPairs size, then shrink to the actual number of pairs - if (spatialDimension() == 2) - { - m_contact_plane2d = ArrayT(numPairs, numPairs, getAllocatorId()); - m_contact_plane3d = ArrayT(0, 1, getAllocatorId()); - } - else - { - m_contact_plane2d = ArrayT(0, 1, getAllocatorId()); - m_contact_plane3d = ArrayT(numPairs, numPairs, getAllocatorId()); + if ( spatialDimension() == 2 ) { + m_contact_plane2d = ArrayT( numPairs, numPairs, getAllocatorId() ); + m_contact_plane3d = ArrayT( 0, 1, getAllocatorId() ); + } else { + m_contact_plane2d = ArrayT( 0, 1, getAllocatorId() ); + m_contact_plane3d = ArrayT( numPairs, numPairs, getAllocatorId() ); } auto planes_2d = m_contact_plane2d.view(); auto planes_3d = m_contact_plane3d.view(); auto mesh1 = getMesh1().getView(); auto mesh2 = getMesh2().getView(); // array of size one for counting number of planes on device - ArrayT planes_ct_data(1, 1, getAllocatorId()); + ArrayT planes_ct_data( 1, 1, getAllocatorId() ); auto planes_ct = planes_ct_data.view(); - forAllExec(getExecutionMode(), numPairs, - [pairs, mesh1, mesh2, params, contact_method, contact_case, planes_2d, - planes_3d, planes_ct, pair_err] TRIBOL_HOST_DEVICE (IndexT i) mutable - { - auto& pair = pairs[i]; - - // call wrapper around the contact method/case specific - // geometry checks to determine whether to include a pair - // in the active set - bool interact = false; - FaceGeomError interact_err = CheckInterfacePair( - pair, mesh1, mesh2, params, contact_method, contact_case, - interact, planes_2d, planes_3d, planes_ct.data()); - - // // Update pair reporting data for this coupling scheme - // this->updatePairReportingData( interact_err ); - - // TODO refine how these errors are handled. Here we skip over face-pairs with errors. That is, - // they are not registered for contact, but we don't error out. - if (interact_err != NO_FACE_GEOM_ERROR) - { - pair_err[0] = 1; - pair.m_is_contact_candidate = false; - // TODO consider printing offending face(s) coordinates for debugging - // SLIC_DEBUG("Face geometry error, " << static_cast(interact_err) << "for pair, " << kp << "."); - // continue; // TODO SRW why do we need this? Seems like we want to update interface pair below if-statements - } - else if (!interact) - { - pair.m_is_contact_candidate = false; - } - else - { - pair.m_is_contact_candidate = true; - } - } - ); - - ArrayT planes_ct_host(planes_ct_data); + forAllExec( getExecutionMode(), numPairs, + [pairs, mesh1, mesh2, params, contact_method, contact_case, planes_2d, planes_3d, planes_ct, + pair_err] TRIBOL_HOST_DEVICE( IndexT i ) mutable { + auto& pair = pairs[i]; + + // call wrapper around the contact method/case specific + // geometry checks to determine whether to include a pair + // in the active set + bool interact = false; + FaceGeomError interact_err = + CheckInterfacePair( pair, mesh1, mesh2, params, contact_method, contact_case, interact, planes_2d, + planes_3d, planes_ct.data() ); + + // // Update pair reporting data for this coupling scheme + // this->updatePairReportingData( interact_err ); + + // TODO refine how these errors are handled. Here we skip over face-pairs with errors. That is, + // they are not registered for contact, but we don't error out. + if ( interact_err != NO_FACE_GEOM_ERROR ) { + pair_err[0] = 1; + pair.m_is_contact_candidate = false; + // TODO consider printing offending face(s) coordinates for debugging + // SLIC_DEBUG("Face geometry error, " << static_cast(interact_err) << "for pair, " << kp << "."); + // continue; // TODO SRW why do we need this? Seems like we want to update interface pair below + // if-statements + } else if ( !interact ) { + pair.m_is_contact_candidate = false; + } else { + pair.m_is_contact_candidate = true; + } + } ); + + ArrayT planes_ct_host( planes_ct_data ); // shrink array to actual number of contact planes - if (spatialDimension() == 2) - { - m_contact_plane2d.resize(planes_ct_host[0]); - } - else - { - m_contact_plane3d.resize(planes_ct_host[0]); + if ( spatialDimension() == 2 ) { + m_contact_plane2d.resize( planes_ct_host[0] ); + } else { + m_contact_plane3d.resize( planes_ct_host[0] ); } - + // Here, the pair_err is checked, which detects an issue with a face-pair geometry // (which has been skipped over for contact eligibility) and reports this warning. - // This is intended to indicate to a user that there may be bad geometry, or issues with + // This is intended to indicate to a user that there may be bad geometry, or issues with // complex cg calculations that need debugging. // - // This is complex because a host-code may have unavoidable 'bad' geometry and wish - // to continue the simulation. In this case, we may 'punt' on those face-pairs, which - // may be reasonable and not an error. Alternatively, this warning may indicate a bug + // This is complex because a host-code may have unavoidable 'bad' geometry and wish + // to continue the simulation. In this case, we may 'punt' on those face-pairs, which + // may be reasonable and not an error. Alternatively, this warning may indicate a bug // or issue in the cg that a host-code does desire to have resolved. For this reason, this // message is kept at the warning level. - ArrayT pair_err_host(pair_err_data); - SLIC_INFO_IF( pair_err_host[0]!=0, "CouplingScheme::apply(): possible issues with orientation, " << - "input, or invalid overlaps in CheckInterfacePair()." ); + ArrayT pair_err_host( pair_err_data ); + SLIC_INFO_IF( pair_err_host[0] != 0, "CouplingScheme::apply(): possible issues with orientation, " + << "input, or invalid overlaps in CheckInterfacePair()." ); // aggregate across ranks for this coupling scheme? SRW - SLIC_DEBUG("Number of active interface pairs: " << getNumActivePairs()); + SLIC_DEBUG( "Number of active interface pairs: " << getNumActivePairs() ); - // wrapper around contact method, case, and - // enforcement to apply the interface physics in both - // normal and tangential directions. This function loops - // over the pairs on the coupling scheme and applies the + // wrapper around contact method, case, and + // enforcement to apply the interface physics in both + // normal and tangential directions. This function loops + // over the pairs on the coupling scheme and applies the // appropriate physics in the normal and tangential directions. int err = ApplyInterfacePhysics( this, cycle, t ); - SLIC_WARNING_IF(err!=0, "CouplingScheme::apply(): error in ApplyInterfacePhysics for " << - "coupling scheme, " << this->m_id << "."); + SLIC_WARNING_IF( err != 0, "CouplingScheme::apply(): error in ApplyInterfacePhysics for " + << "coupling scheme, " << this->m_id << "." ); // compute Tribol timestep vote on the coupling scheme - if (err == 0 && getNumActivePairs() > 0) - { - computeTimeStep(dt); + if ( err == 0 && getNumActivePairs() > 0 ) { + computeTimeStep( dt ); } // write output - writeInterfaceOutput( m_output_directory, - params.vis_type, - cycle, t ); - - if (err != 0) - { - return 1; - } - else - { - // here we don't have any error in the application of interface physics, - // but may have face-pair data reporting skipped pair statistics for debug print - this->printPairReportingData(); - return 0; - } - -} // end CouplingScheme::apply() + writeInterfaceOutput( m_output_directory, params.vis_type, cycle, t ); + + if ( err != 0 ) { + return 1; + } else { + // here we don't have any error in the application of interface physics, + // but may have face-pair data reporting skipped pair statistics for debug print + this->printPairReportingData(); + return 0; + } + +} // end CouplingScheme::apply() //------------------------------------------------------------------------------ bool CouplingScheme::init() { - // check for valid coupling scheme only for non-null-meshes - this->m_isValid = this->isValidCouplingScheme(); - - if (this->m_isValid) - { - // set individual coupling scheme logging level - this->setSlicLoggingLevel(); - - // compute the face data - this->m_mesh1->computeFaceData(this->m_exec_mode); - if (this->m_mesh_id2 != this->m_mesh_id1) - { - this->m_mesh2->computeFaceData(this->m_exec_mode); - } - - this->allocateMethodData(); + // check for valid coupling scheme only for non-null-meshes + this->m_isValid = this->isValidCouplingScheme(); - return true; - } - else - { - return false; - } + if ( this->m_isValid ) { + // set individual coupling scheme logging level + this->setSlicLoggingLevel(); + + // compute the face data + this->m_mesh1->computeFaceData( this->m_exec_mode ); + if ( this->m_mesh_id2 != this->m_mesh_id1 ) { + this->m_mesh2->computeFaceData( this->m_exec_mode ); + } + + this->allocateMethodData(); + + return true; + } else { + return false; + } } //------------------------------------------------------------------------------ void CouplingScheme::setSlicLoggingLevel() { - // set slic logging level for coupling schemes that have API modified logging levels - if (this->m_loggingLevel != TRIBOL_UNDEFINED) - { - switch (this->m_loggingLevel) - { - case TRIBOL_DEBUG: - { - axom::slic::setLoggingMsgLevel( axom::slic::message::Debug ); - break; - } - case TRIBOL_INFO: - { - axom::slic::setLoggingMsgLevel( axom::slic::message::Info ); - break; - } - case TRIBOL_WARNING: - { - axom::slic::setLoggingMsgLevel( axom::slic::message::Warning ); - break; - } - case TRIBOL_ERROR: - { - axom::slic::setLoggingMsgLevel( axom::slic::message::Error ); - break; - } - default: - { - axom::slic::setLoggingMsgLevel( axom::slic::message::Warning ); - break; - } - } // end switch - } // end if + // set slic logging level for coupling schemes that have API modified logging levels + if ( this->m_loggingLevel != TRIBOL_UNDEFINED ) { + switch ( this->m_loggingLevel ) { + case TRIBOL_DEBUG: { + axom::slic::setLoggingMsgLevel( axom::slic::message::Debug ); + break; + } + case TRIBOL_INFO: { + axom::slic::setLoggingMsgLevel( axom::slic::message::Info ); + break; + } + case TRIBOL_WARNING: { + axom::slic::setLoggingMsgLevel( axom::slic::message::Warning ); + break; + } + case TRIBOL_ERROR: { + axom::slic::setLoggingMsgLevel( axom::slic::message::Error ); + break; + } + default: { + axom::slic::setLoggingMsgLevel( axom::slic::message::Warning ); + break; + } + } // end switch + } // end if } //------------------------------------------------------------------------------ void CouplingScheme::allocateMethodData() { - auto& mesh1 = MeshManager::getInstance().getData(m_mesh_id1); - auto& mesh2 = MeshManager::getInstance().getData(m_mesh_id2); - // check for valid coupling schemes for those with non-null meshes. - // Note: keep if-block for non-null meshes here. A valid coupling scheme - // may have null meshes, but we don't want to allocate unnecessary memory here. - if (mesh1.numberOfElements() > 0 && mesh2.numberOfElements() > 0) - { - this->m_numTotalNodes = mesh1.numberOfNodes(); - - // dynamically allocate method data object for mortar method - switch (this->m_contactMethod) - { - case ALIGNED_MORTAR: - case MORTAR_WEIGHTS: - case SINGLE_MORTAR: - { - // dynamically allocate method data object - this->m_methodData = new MortarData; - static_cast( m_methodData )->m_numTotalNodes = this->m_numTotalNodes; - break; - } // end case SINGLE_MORTAR - default: - { - this->m_methodData = nullptr; - break; - } - } // end if on non-null meshes - - } // end if on non-null-meshes -} // end CouplingScheme::allocateMethodData() + auto& mesh1 = MeshManager::getInstance().getData( m_mesh_id1 ); + auto& mesh2 = MeshManager::getInstance().getData( m_mesh_id2 ); + // check for valid coupling schemes for those with non-null meshes. + // Note: keep if-block for non-null meshes here. A valid coupling scheme + // may have null meshes, but we don't want to allocate unnecessary memory here. + if ( mesh1.numberOfElements() > 0 && mesh2.numberOfElements() > 0 ) { + this->m_numTotalNodes = mesh1.numberOfNodes(); + + // dynamically allocate method data object for mortar method + switch ( this->m_contactMethod ) { + case ALIGNED_MORTAR: + case MORTAR_WEIGHTS: + case SINGLE_MORTAR: { + // dynamically allocate method data object + this->m_methodData = new MortarData; + static_cast( m_methodData )->m_numTotalNodes = this->m_numTotalNodes; + break; + } // end case SINGLE_MORTAR + default: { + this->m_methodData = nullptr; + break; + } + } // end if on non-null meshes + + } // end if on non-null-meshes +} // end CouplingScheme::allocateMethodData() //------------------------------------------------------------------------------ -void CouplingScheme::computeTimeStep(RealT &dt) +void CouplingScheme::computeTimeStep( RealT& dt ) { - if (dt < 1.e-8) - { - // current timestep too small for Tribol vote. Leave unchanged and return + if ( dt < 1.e-8 ) { + // current timestep too small for Tribol vote. Leave unchanged and return + return; + } + + // make sure velocities are registered + if ( !m_mesh1->hasVelocity() || !m_mesh2->hasVelocity() ) { + if ( m_mesh1->numberOfElements() > 0 && m_mesh2->numberOfElements() > 0 ) { + // invalid registration of nodal velocities for non-null meshes + dt = -1.0; + return; + } else { + // at least one null mesh with allowable null velocities; don't modify dt return; - } - - // make sure velocities are registered - if (!m_mesh1->hasVelocity() || !m_mesh2->hasVelocity()) - { - if (m_mesh1->numberOfElements() > 0 && m_mesh2->numberOfElements() > 0) - { - // invalid registration of nodal velocities for non-null meshes - dt = -1.0; - return; + } + } + + // if we are here we have registered velocities for non-null meshes + // and can compute the timestep vote + switch ( m_contactMethod ) { + case SINGLE_MORTAR: + // no-op + break; + case ALIGNED_MORTAR: + // no-op + break; + case MORTAR_WEIGHTS: + // no-op + break; + case COMMON_PLANE: + if ( m_enforcementMethod == PENALTY ) { + if ( m_parameters.enable_timestep_vote ) { + this->computeCommonPlaneTimeStep( dt ); + } } - else - { - // at least one null mesh with allowable null velocities; don't modify dt - return; - } - } - - // if we are here we have registered velocities for non-null meshes - // and can compute the timestep vote - switch( m_contactMethod ) { - case SINGLE_MORTAR : - // no-op - break; - case ALIGNED_MORTAR : - // no-op - break; - case MORTAR_WEIGHTS : - // no-op - break; - case COMMON_PLANE : - if ( m_enforcementMethod == PENALTY ) - { - if (m_parameters.enable_timestep_vote) - { - this->computeCommonPlaneTimeStep( dt ); - } - } - break; - default : - break; - } // end-switch + break; + default: + break; + } // end-switch } //------------------------------------------------------------------------------ -void CouplingScheme::computeCommonPlaneTimeStep(RealT &dt) +void CouplingScheme::computeCommonPlaneTimeStep( RealT& dt ) { // note: the timestep vote is based on a maximum allowable interpenetration // approach checking current gaps and then performing a velocity projection. - // This timestep vote is not derived from a stability analysis and does not - // account for the spring stiffness in a CFL-like timestep constraint. - // The timestep vote in this routine is intended to avoid missing contact or - // inadequately resolving contact by detecting 'too much' interpenetration. - // To do this, the first check is a 'gap check' where the current gap is - // checked against a fraction of the element-thicknesses. The second check - // assumes zero gap, and then does a velocity projection followed by the + // This timestep vote is not derived from a stability analysis and does not + // account for the spring stiffness in a CFL-like timestep constraint. + // The timestep vote in this routine is intended to avoid missing contact or + // inadequately resolving contact by detecting 'too much' interpenetration. + // To do this, the first check is a 'gap check' where the current gap is + // checked against a fraction of the element-thicknesses. The second check + // assumes zero gap, and then does a velocity projection followed by the // 'gap check' using this new, projected gap. auto& mesh1 = getMesh1(); auto& mesh2 = getMesh2(); - + // check if the element thicknesses have been set. This is now // regardless of exact penalty calculation type, since element // thicknesses are required for auto contact even if a constant // penalty is used - if ( !mesh1.getElementData().m_is_element_thickness_set || - !mesh2.getElementData().m_is_element_thickness_set ) - { - return; + if ( !mesh1.getElementData().m_is_element_thickness_set || !mesh2.getElementData().m_is_element_thickness_set ) { + return; } RealT proj_ratio = m_parameters.timestep_pen_frac; - //int num_sides = 2; // always 2 sides in a single coupling scheme + // int num_sides = 2; // always 2 sides in a single coupling scheme int dim = spatialDimension(); // Loop over each contact plane. Even if pair is not in contact, we still do a @@ -1497,472 +1276,448 @@ void CouplingScheme::computeCommonPlaneTimeStep(RealT &dt) // all candidates, not necessarily ones that are deemed to be in contact per // the gap constraint. auto cs_view = getView(); - ArrayT dt_temp_data({dt, dt}, getAllocatorId()); + ArrayT dt_temp_data( { dt, dt }, getAllocatorId() ); ArrayViewT dt_temp = dt_temp_data; // [0]: exceed_max_gap1, [1]: exceed_max_gap2, [2]: neg_dt_gap_msg, [3]: neg_dt_vel_proj_msg - ArrayT msg_data({false, false, false, false}, getAllocatorId()); + ArrayT msg_data( { false, false, false, false }, getAllocatorId() ); ArrayViewT msg = msg_data; - forAllExec(getExecutionMode(), getNumActivePairs(), - [cs_view, dim, proj_ratio, msg, dt_temp, dt] TRIBOL_HOST_DEVICE (IndexT i) - { - auto& plane = cs_view.getContactPlane(i); - - auto& mesh1 = cs_view.getMesh1View(); - auto& mesh2 = cs_view.getMesh2View(); - - // get pair indices - IndexT index1 = plane.getCpElementId1(); - IndexT index2 = plane.getCpElementId2(); - - constexpr int max_dim = 3; - constexpr int max_nodes_per_elem = 4; - StackArrayT x1; - StackArrayT v1; - mesh1.getFaceCoords( index1, x1 ); - mesh1.getFaceVelocities( index1, v1 ); - - StackArrayT x2; - StackArrayT v2; - mesh2.getFaceCoords( index2, x2 ); - mesh2.getFaceVelocities( index2, v2 ); - - ///////////////////////////////////////////////////////////// - // calculate face velocities at projected overlap centroid // - ///////////////////////////////////////////////////////////// - StackArrayT vel_f1; - StackArrayT vel_f2; - initRealArray( vel_f1, dim, 0.0 ); - initRealArray( vel_f2, dim, 0.0 ); - - // interpolate nodal velocity at overlap centroid as projected - // onto face 1 - RealT cXf1 = plane.m_cXf1; - RealT cYf1 = plane.m_cYf1; - RealT cZf1 = (dim == 3) ? plane.m_cZf1 : 0.; - GalerkinEval( x1, cXf1, cYf1, cZf1, - LINEAR, PHYSICAL, dim, dim, - v1, vel_f1 ); - // interpolate nodal velocity at overlap centroid as projected - // onto face 2 - RealT cXf2 = plane.m_cXf2; - RealT cYf2 = plane.m_cYf2; - RealT cZf2 = (dim == 3) ? plane.m_cZf2 : 0.; - GalerkinEval( x2, cXf2, cYf2, cZf2, - LINEAR, PHYSICAL, dim, dim, - v2, vel_f2 ); - - //////////////////////////////////////////////// - // // - // Compute Timestep Vote Based on a Few Cases // - // // - //////////////////////////////////////////////// - - /////////////////////////////////////////////// - // compute data common to all timestep votes // - /////////////////////////////////////////////// - - // compute velocity projections: - // compute the dot product between the face velocities - // at the overlap-centroid-to-face projected centroid and each - // face's outward unit normal AND the overlap normal. - RealT v1_dot_n, v2_dot_n, v1_dot_n1, v2_dot_n2; - RealT overlapNormal[max_dim]; - overlapNormal[0] = plane.m_nX; - overlapNormal[1] = plane.m_nY; - if (dim == 3) - { - overlapNormal[2] = plane.m_nZ; - } - - // get face normals - RealT fn1[max_dim], fn2[max_dim]; - mesh1.getFaceNormal( index1, fn1 ); - mesh2.getFaceNormal( index2, fn2 ); - - // compute projections - v1_dot_n = dotProd( vel_f1, overlapNormal, dim ); - v2_dot_n = dotProd( vel_f2, overlapNormal, dim ); - v1_dot_n1 = dotProd( vel_f1, fn1, dim ); - v2_dot_n2 = dotProd( vel_f2, fn2, dim ); - - // Keep debug print statements. This routine is still in the testing phase - //std::cout << "face 1 normal: " << fn1[0] << ", " << fn1[1] << ", " << fn1[2] << std::endl; - //std::cout << "face 2 normal: " << fn2[0] << ", " << fn2[1] << ", " << fn2[2] << std::endl; - //std::cout << " " << std::endl; - //std::cout << "face 1 vel: " << vel_f1[0] << ", " << vel_f1[1] << ", " << vel_f1[2] << std::endl; - //std::cout << "face 2 vel: " << vel_f2[0] << ", " << vel_f2[1] << ", " << vel_f2[2] << std::endl; - //std::cout << " " << std::endl; - //std::cout << "First v1_dot_n1 calc: " << v1_dot_n1 << std::endl; - //std::cout << "First v2_dot_n2 calc: " << v2_dot_n2 << std::endl; - //std::cout << "First v1_dot_n: " << v1_dot_n << std::endl; - //std::cout << "First v2_dot_n: " << v2_dot_n << std::endl; - - // add tiny amount to velocity projections to avoid division by zero. - // Note that if these projections are close to zero, there may be - // stationary interactions or tangential motion. In this case, any - // timestep estimate will be very large, and not control the simulation - RealT tiny = 1.e-12; - RealT tiny1 = (v1_dot_n >= 0.) ? tiny : -1.*tiny; - RealT tiny2 = (v2_dot_n >= 0.) ? tiny : -1.*tiny; - v1_dot_n += tiny1; - v2_dot_n += tiny2; - // reset tiny velocity based on face normal projections. - tiny1 = (v1_dot_n1 >= 0.) ? tiny : -1.*tiny; - tiny2 = (v2_dot_n2 >= 0.) ? tiny : -1.*tiny; - v1_dot_n1 += tiny1; - v2_dot_n2 += tiny2; - - // Keep debug print statements. This routine is still in the testing phase - //std::cout << "Second v1_dot_n1 calc: " << v1_dot_n1 << std::endl; - //std::cout << "Second v2_dot_n2 calc: " << v2_dot_n2 << std::endl; - //std::cout << "Second v1_dot_n: " << v1_dot_n << std::endl; - //std::cout << "Second v2_dot_n: " << v2_dot_n << std::endl; - - // get volume element thicknesses associated with each face in this pair - RealT t1 = mesh1.getElementData().m_thickness[index1]; - RealT t2 = mesh2.getElementData().m_thickness[index2]; - - // compute the existing gap vector (recall gap is x1-x2 by convention) - RealT gapVec[max_dim]; - gapVec[0] = plane.m_cXf1 - plane.m_cXf2; - gapVec[1] = plane.m_cYf1 - plane.m_cYf2; - if (dim == 3) - { - gapVec[2] = plane.m_cZf1 - plane.m_cZf2; - } - - // compute the dot product between gap vector and the outward unit face normals. - RealT gap_f1_n1 = dotProd( gapVec, fn1, dim ); - RealT gap_f2_n2 = dotProd( gapVec, fn2, dim ); - - RealT dt1 = 1.e6; // initialize as large number - RealT dt2 = 1.e6; // initialize as large number - RealT alpha = cs_view.getTimestepScale(); // multiplier on timestep estimate - bool dt1_check1 = false; - bool dt2_check1 = false; - bool dt1_vel_check = false; - bool dt2_vel_check = false; - - // maximum allowable interpenetration in the normal direction of each element - RealT max_delta1 = proj_ratio * t1; - RealT max_delta2 = proj_ratio * t2; - - // Separation or interpenetration trigger for check 1 and 2: - // check if there is further interpen or separation based on the - // velocity projection in the direction of the common-plane normal, - // which is in the direction of face-2 normal. - // The two cases are: - // if v1*n < 0 there is interpen - // if v2*n > 0 there is interpen - // - // Note: we compare strictly to 0. here since a 'tiny' value was - // appropriately added to the velocity projections, which is akin - // to some tolerancing effect - dt1_vel_check = (v1_dot_n < 0.) ? true : false; - dt2_vel_check = (v2_dot_n > 0.) ? true : false; - - ////////////////////////////////////////////////////////////////////////// - // Check 1. Current interpenetration gap exceeds max allowable interpen // - ////////////////////////////////////////////////////////////////////////// - - // check if face-pair is in contact (i.e. gap < gap_tol), which is determined - // in Common Plane ApplyNormal<>() routine - if (plane.m_inContact) - { - - // compute the difference between the 'face-gaps' and the max allowable - // interpen as a function of element thickness. Note, we have to use the - // gap projected onto the outward unit face-normal to check against the - // max allowable gap as a factor of the thickness in the element normal - // direction - RealT delta1 = max_delta1 - gap_f1_n1; // >0 not exceeding max allowable - RealT delta2 = max_delta2 + gap_f2_n2; // >0 not exceeding max allowable - - auto exceed_max_gap1 = (delta1 < 0.) ? true : false; - auto exceed_max_gap2 = (delta2 < 0.) ? true : false; - - // if velocity projection indicates further interpenetration, and the gaps - // EXCEED max allowable, then compute time step estimates to reduce overlap - dt1_check1 = (dt1_vel_check) ? exceed_max_gap1 : false; - dt2_check1 = (dt2_vel_check) ? exceed_max_gap2 : false; - - msg[0] = exceed_max_gap1; - msg[1] = exceed_max_gap2; - - // compute dt for face 1 and 2 based on the velocity and gap projections onto - // the face-normals for faces where currect gap exceeds max allowable gap. - // - // NOTE: - // - // This calculation RESETS the current gap to be g = 0, and computes a timestep - // such that the velocity projection of the overlap-to-face projected overlap - // centroid does not exceed the max allowable gap. - // - // This avoid a timestep crash in the case that the current gap barely exceeds - // the max allowable and also allows a soft contact response with interpen - // in excess of the max allowable gap without causing timestep crashes. - // - // v1_dot_n1 > 0 and v2_dot_n2 > 0 for further interpen - dt1 = (dt1_check1) ? alpha * max_delta1 / v1_dot_n1 : dt1; - dt2 = (dt2_check1) ? alpha * max_delta2 / v2_dot_n2 : dt2; - - // Keep debug print statements. This routine is still in the testing phase - //std::cout << "dt1_check1, delta1 and v1_dot_n1: " << dt1_check1 << ", " << max_delta1 << ", " << v1_dot_n1 << std::endl; - //std::cout << "dt2_check1, delta2 and v2_dot_n2: " << dt2_check1 << ", " << max_delta2 << ", " << v2_dot_n2 << std::endl; - //std::cout << "dt1 and dt2: " << dt1 << ", " << dt2 << std::endl; - - // update dt_temp1 only for positive dt1 and/or dt2 - if (dt1 > 0.) - { + forAllExec( getExecutionMode(), getNumActivePairs(), + [cs_view, dim, proj_ratio, msg, dt_temp, dt] TRIBOL_HOST_DEVICE( IndexT i ) { + auto& plane = cs_view.getContactPlane( i ); + + auto& mesh1 = cs_view.getMesh1View(); + auto& mesh2 = cs_view.getMesh2View(); + + // get pair indices + IndexT index1 = plane.getCpElementId1(); + IndexT index2 = plane.getCpElementId2(); + + constexpr int max_dim = 3; + constexpr int max_nodes_per_elem = 4; + StackArrayT x1; + StackArrayT v1; + mesh1.getFaceCoords( index1, x1 ); + mesh1.getFaceVelocities( index1, v1 ); + + StackArrayT x2; + StackArrayT v2; + mesh2.getFaceCoords( index2, x2 ); + mesh2.getFaceVelocities( index2, v2 ); + + ///////////////////////////////////////////////////////////// + // calculate face velocities at projected overlap centroid // + ///////////////////////////////////////////////////////////// + StackArrayT vel_f1; + StackArrayT vel_f2; + initRealArray( vel_f1, dim, 0.0 ); + initRealArray( vel_f2, dim, 0.0 ); + + // interpolate nodal velocity at overlap centroid as projected + // onto face 1 + RealT cXf1 = plane.m_cXf1; + RealT cYf1 = plane.m_cYf1; + RealT cZf1 = ( dim == 3 ) ? plane.m_cZf1 : 0.; + GalerkinEval( x1, cXf1, cYf1, cZf1, LINEAR, PHYSICAL, dim, dim, v1, vel_f1 ); + // interpolate nodal velocity at overlap centroid as projected + // onto face 2 + RealT cXf2 = plane.m_cXf2; + RealT cYf2 = plane.m_cYf2; + RealT cZf2 = ( dim == 3 ) ? plane.m_cZf2 : 0.; + GalerkinEval( x2, cXf2, cYf2, cZf2, LINEAR, PHYSICAL, dim, dim, v2, vel_f2 ); + + //////////////////////////////////////////////// + // // + // Compute Timestep Vote Based on a Few Cases // + // // + //////////////////////////////////////////////// + + /////////////////////////////////////////////// + // compute data common to all timestep votes // + /////////////////////////////////////////////// + + // compute velocity projections: + // compute the dot product between the face velocities + // at the overlap-centroid-to-face projected centroid and each + // face's outward unit normal AND the overlap normal. + RealT v1_dot_n, v2_dot_n, v1_dot_n1, v2_dot_n2; + RealT overlapNormal[max_dim]; + overlapNormal[0] = plane.m_nX; + overlapNormal[1] = plane.m_nY; + if ( dim == 3 ) { + overlapNormal[2] = plane.m_nZ; + } + + // get face normals + RealT fn1[max_dim], fn2[max_dim]; + mesh1.getFaceNormal( index1, fn1 ); + mesh2.getFaceNormal( index2, fn2 ); + + // compute projections + v1_dot_n = dotProd( vel_f1, overlapNormal, dim ); + v2_dot_n = dotProd( vel_f2, overlapNormal, dim ); + v1_dot_n1 = dotProd( vel_f1, fn1, dim ); + v2_dot_n2 = dotProd( vel_f2, fn2, dim ); + + // Keep debug print statements. This routine is still in the testing phase + // std::cout << "face 1 normal: " << fn1[0] << ", " << fn1[1] << ", " << fn1[2] << std::endl; + // std::cout << "face 2 normal: " << fn2[0] << ", " << fn2[1] << ", " << fn2[2] << std::endl; + // std::cout << " " << std::endl; + // std::cout << "face 1 vel: " << vel_f1[0] << ", " << vel_f1[1] << ", " << vel_f1[2] << std::endl; + // std::cout << "face 2 vel: " << vel_f2[0] << ", " << vel_f2[1] << ", " << vel_f2[2] << std::endl; + // std::cout << " " << std::endl; + // std::cout << "First v1_dot_n1 calc: " << v1_dot_n1 << std::endl; + // std::cout << "First v2_dot_n2 calc: " << v2_dot_n2 << std::endl; + // std::cout << "First v1_dot_n: " << v1_dot_n << std::endl; + // std::cout << "First v2_dot_n: " << v2_dot_n << std::endl; + + // add tiny amount to velocity projections to avoid division by zero. + // Note that if these projections are close to zero, there may be + // stationary interactions or tangential motion. In this case, any + // timestep estimate will be very large, and not control the simulation + RealT tiny = 1.e-12; + RealT tiny1 = ( v1_dot_n >= 0. ) ? tiny : -1. * tiny; + RealT tiny2 = ( v2_dot_n >= 0. ) ? tiny : -1. * tiny; + v1_dot_n += tiny1; + v2_dot_n += tiny2; + // reset tiny velocity based on face normal projections. + tiny1 = ( v1_dot_n1 >= 0. ) ? tiny : -1. * tiny; + tiny2 = ( v2_dot_n2 >= 0. ) ? tiny : -1. * tiny; + v1_dot_n1 += tiny1; + v2_dot_n2 += tiny2; + + // Keep debug print statements. This routine is still in the testing phase + // std::cout << "Second v1_dot_n1 calc: " << v1_dot_n1 << std::endl; + // std::cout << "Second v2_dot_n2 calc: " << v2_dot_n2 << std::endl; + // std::cout << "Second v1_dot_n: " << v1_dot_n << std::endl; + // std::cout << "Second v2_dot_n: " << v2_dot_n << std::endl; + + // get volume element thicknesses associated with each face in this pair + RealT t1 = mesh1.getElementData().m_thickness[index1]; + RealT t2 = mesh2.getElementData().m_thickness[index2]; + + // compute the existing gap vector (recall gap is x1-x2 by convention) + RealT gapVec[max_dim]; + gapVec[0] = plane.m_cXf1 - plane.m_cXf2; + gapVec[1] = plane.m_cYf1 - plane.m_cYf2; + if ( dim == 3 ) { + gapVec[2] = plane.m_cZf1 - plane.m_cZf2; + } + + // compute the dot product between gap vector and the outward unit face normals. + RealT gap_f1_n1 = dotProd( gapVec, fn1, dim ); + RealT gap_f2_n2 = dotProd( gapVec, fn2, dim ); + + RealT dt1 = 1.e6; // initialize as large number + RealT dt2 = 1.e6; // initialize as large number + RealT alpha = cs_view.getTimestepScale(); // multiplier on timestep estimate + bool dt1_check1 = false; + bool dt2_check1 = false; + bool dt1_vel_check = false; + bool dt2_vel_check = false; + + // maximum allowable interpenetration in the normal direction of each element + RealT max_delta1 = proj_ratio * t1; + RealT max_delta2 = proj_ratio * t2; + + // Separation or interpenetration trigger for check 1 and 2: + // check if there is further interpen or separation based on the + // velocity projection in the direction of the common-plane normal, + // which is in the direction of face-2 normal. + // The two cases are: + // if v1*n < 0 there is interpen + // if v2*n > 0 there is interpen + // + // Note: we compare strictly to 0. here since a 'tiny' value was + // appropriately added to the velocity projections, which is akin + // to some tolerancing effect + dt1_vel_check = ( v1_dot_n < 0. ) ? true : false; + dt2_vel_check = ( v2_dot_n > 0. ) ? true : false; + + ////////////////////////////////////////////////////////////////////////// + // Check 1. Current interpenetration gap exceeds max allowable interpen // + ////////////////////////////////////////////////////////////////////////// + + // check if face-pair is in contact (i.e. gap < gap_tol), which is determined + // in Common Plane ApplyNormal<>() routine + if ( plane.m_inContact ) { + // compute the difference between the 'face-gaps' and the max allowable + // interpen as a function of element thickness. Note, we have to use the + // gap projected onto the outward unit face-normal to check against the + // max allowable gap as a factor of the thickness in the element normal + // direction + RealT delta1 = max_delta1 - gap_f1_n1; // >0 not exceeding max allowable + RealT delta2 = max_delta2 + gap_f2_n2; // >0 not exceeding max allowable + + auto exceed_max_gap1 = ( delta1 < 0. ) ? true : false; + auto exceed_max_gap2 = ( delta2 < 0. ) ? true : false; + + // if velocity projection indicates further interpenetration, and the gaps + // EXCEED max allowable, then compute time step estimates to reduce overlap + dt1_check1 = ( dt1_vel_check ) ? exceed_max_gap1 : false; + dt2_check1 = ( dt2_vel_check ) ? exceed_max_gap2 : false; + + msg[0] = exceed_max_gap1; + msg[1] = exceed_max_gap2; + + // compute dt for face 1 and 2 based on the velocity and gap projections onto + // the face-normals for faces where currect gap exceeds max allowable gap. + // + // NOTE: + // + // This calculation RESETS the current gap to be g = 0, and computes a timestep + // such that the velocity projection of the overlap-to-face projected overlap + // centroid does not exceed the max allowable gap. + // + // This avoid a timestep crash in the case that the current gap barely exceeds + // the max allowable and also allows a soft contact response with interpen + // in excess of the max allowable gap without causing timestep crashes. + // + // v1_dot_n1 > 0 and v2_dot_n2 > 0 for further interpen + dt1 = ( dt1_check1 ) ? alpha * max_delta1 / v1_dot_n1 : dt1; + dt2 = ( dt2_check1 ) ? alpha * max_delta2 / v2_dot_n2 : dt2; + + // Keep debug print statements. This routine is still in the testing phase + // std::cout << "dt1_check1, delta1 and v1_dot_n1: " << dt1_check1 << ", " << max_delta1 << ", " << + // v1_dot_n1 + // << std::endl; std::cout << "dt2_check1, delta2 and v2_dot_n2: " << dt2_check1 << ", " << max_delta2 + // << ", " + // << v2_dot_n2 << std::endl; std::cout << "dt1 and dt2: " << dt1 << ", " << dt2 << std::endl; + + // update dt_temp1 only for positive dt1 and/or dt2 + if ( dt1 > 0. ) { #ifdef TRIBOL_USE_RAJA - RAJA::atomicMin( &dt_temp[0], - axom::utilities::min(dt1, 1.e6) ); + RAJA::atomicMin( &dt_temp[0], axom::utilities::min( dt1, 1.e6 ) ); #else - dt_temp[0] = axom::utilities::min(dt_temp[0], axom::utilities::min(dt1, 1.e6)); + dt_temp[0] = axom::utilities::min(dt_temp[0], axom::utilities::min(dt1, 1.e6)); #endif - } - if (dt2 > 0.) - { + } + if ( dt2 > 0. ) { #ifdef TRIBOL_USE_RAJA - RAJA::atomicMin( &dt_temp[0], - axom::utilities::min(1.e6, dt2) ); + RAJA::atomicMin( &dt_temp[0], axom::utilities::min( 1.e6, dt2 ) ); #else - dt_temp[0] = axom::utilities::min(dt_temp[0], axom::utilities::min(1.e6, dt2)); + dt_temp[0] = axom::utilities::min(dt_temp[0], axom::utilities::min(1.e6, dt2)); #endif - } - - if (dt1 < 0. || dt2 < 0.) - { - msg[2] = true; - } - - } // end case 1 - - //////////////////////////////////////////////////////////////////////// - // 2. Velocity projection exceeds max interpenetration // - // // - // Note: This is performed for all contact candidates even if they // - // are not 'in contact' per the common-plane method. Every // - // contact candidate has a contact plane // - //////////////////////////////////////////////////////////////////////// - - { - // compute delta between velocity projection of face-projected - // overlap centroid and the OTHER face's face-projected overlap - // centroid - RealT proj_delta_x1 = plane.m_cXf1 + dt * vel_f1[0] - plane.m_cXf2; - RealT proj_delta_y1 = plane.m_cYf1 + dt * vel_f1[1] - plane.m_cYf2; - RealT proj_delta_z1 = 0.; - - RealT proj_delta_x2 = plane.m_cXf2 + dt * vel_f2[0] - plane.m_cXf1; - RealT proj_delta_y2 = plane.m_cYf2 + dt * vel_f2[1] - plane.m_cYf1; - RealT proj_delta_z2 = 0.; - - // compute the dot product between each face's delta and the OTHER - // face's outward unit normal. This is the magnitude of interpenetration - // of one face's projected overlap-centroid in the 'thickness-direction' - // of the other face (with whom in may be in contact currently, or in - // a velocity projected sense). - RealT proj_delta_n_1 = proj_delta_x1 * fn2[0] + proj_delta_y1 * fn2[1]; - RealT proj_delta_n_2 = proj_delta_x2 * fn1[0] + proj_delta_y2 * fn1[1]; - - if (dim == 3) - { - proj_delta_z1 = plane.m_cZf1 + dt * vel_f1[2] - plane.m_cZf2; - proj_delta_z2 = plane.m_cZf2 + dt * vel_f2[2] - plane.m_cZf1; - - proj_delta_n_1 += proj_delta_z1 * fn2[2]; - proj_delta_n_2 += proj_delta_z2 * fn1[2]; - } - - // Reset the dt velocity check only for faces with continued interpen that exceeds the - // max allowable gap AND where the current gap did NOT exceed that face's max allowable - // gap per check 1 (would result in same dt calc). - // - // Note: - // If proj_delta_n_i < 0, (i=1,2) there is interpen from the velocity projection. - // Check this interpen against the maximum allowable to determine if a velocity projection - // timestep estimate is still required. - if (dt1_vel_check && !dt1_check1) // continued interpen - { - dt1_vel_check = (proj_delta_n_1 < 0.) ? ((std::abs(proj_delta_n_1) > max_delta1) ? true : false) : false; - } - - if (dt2_vel_check && !dt2_check1) // continued interpen - { - dt2_vel_check = (proj_delta_n_2 < 0.) ? ((std::abs(proj_delta_n_2) > max_delta2) ? true : false) : false; - } - - // compute velocity projection based dt (check 2) using a RESET gap (g=0) such that - // the velocity projected gap does not exceed the max allowable gap. This avoid timestep - // crashes for velocity projected gaps slightly in excess of the max allowable and still - // allows for a soft contact response without a timestep crash. - // - // v1_dot_n1 > 0 and v2_dot_n2 > 0 for further interpen - dt1 = (dt1_vel_check) ? alpha * max_delta1 / v1_dot_n1 : dt1; - dt2 = (dt2_vel_check) ? alpha * max_delta2 / v2_dot_n2 : dt2; - - // Keep debug print statements. This routine is still in the testing phase - //std::cout << "dt1_vel_check, (proj_delta_n_1+max_delta1), v1_dot_n1: " << dt1_vel_check << ", " - // << proj_delta_n_1+max_delta1 << ", " << v1_dot_n1 << std::endl; - //std::cout << "dt2_vel_check, (proj_delta_n_2+max_delta2), v2_dot_n2: " << dt2_vel_check << ", " - // << proj_delta_n_2+max_delta2 << ", " << v2_dot_n2 << std::endl; - //std::cout << "dt1 and dt2: " << dt1 << ", " << dt2 << std::endl; - - // update dt_temp2 only for positive dt1 and/or dt2 - if (dt1 > 0.) - { + } + + if ( dt1 < 0. || dt2 < 0. ) { + msg[2] = true; + } + + } // end case 1 + + //////////////////////////////////////////////////////////////////////// + // 2. Velocity projection exceeds max interpenetration // + // // + // Note: This is performed for all contact candidates even if they // + // are not 'in contact' per the common-plane method. Every // + // contact candidate has a contact plane // + //////////////////////////////////////////////////////////////////////// + + { + // compute delta between velocity projection of face-projected + // overlap centroid and the OTHER face's face-projected overlap + // centroid + RealT proj_delta_x1 = plane.m_cXf1 + dt * vel_f1[0] - plane.m_cXf2; + RealT proj_delta_y1 = plane.m_cYf1 + dt * vel_f1[1] - plane.m_cYf2; + RealT proj_delta_z1 = 0.; + + RealT proj_delta_x2 = plane.m_cXf2 + dt * vel_f2[0] - plane.m_cXf1; + RealT proj_delta_y2 = plane.m_cYf2 + dt * vel_f2[1] - plane.m_cYf1; + RealT proj_delta_z2 = 0.; + + // compute the dot product between each face's delta and the OTHER + // face's outward unit normal. This is the magnitude of interpenetration + // of one face's projected overlap-centroid in the 'thickness-direction' + // of the other face (with whom in may be in contact currently, or in + // a velocity projected sense). + RealT proj_delta_n_1 = proj_delta_x1 * fn2[0] + proj_delta_y1 * fn2[1]; + RealT proj_delta_n_2 = proj_delta_x2 * fn1[0] + proj_delta_y2 * fn1[1]; + + if ( dim == 3 ) { + proj_delta_z1 = plane.m_cZf1 + dt * vel_f1[2] - plane.m_cZf2; + proj_delta_z2 = plane.m_cZf2 + dt * vel_f2[2] - plane.m_cZf1; + + proj_delta_n_1 += proj_delta_z1 * fn2[2]; + proj_delta_n_2 += proj_delta_z2 * fn1[2]; + } + + // Reset the dt velocity check only for faces with continued interpen that exceeds the + // max allowable gap AND where the current gap did NOT exceed that face's max allowable + // gap per check 1 (would result in same dt calc). + // + // Note: + // If proj_delta_n_i < 0, (i=1,2) there is interpen from the velocity projection. + // Check this interpen against the maximum allowable to determine if a velocity projection + // timestep estimate is still required. + if ( dt1_vel_check && !dt1_check1 ) // continued interpen + { + dt1_vel_check = ( proj_delta_n_1 < 0. ) + ? ( ( std::abs( proj_delta_n_1 ) > max_delta1 ) ? true : false ) + : false; + } + + if ( dt2_vel_check && !dt2_check1 ) // continued interpen + { + dt2_vel_check = ( proj_delta_n_2 < 0. ) + ? ( ( std::abs( proj_delta_n_2 ) > max_delta2 ) ? true : false ) + : false; + } + + // compute velocity projection based dt (check 2) using a RESET gap (g=0) such that + // the velocity projected gap does not exceed the max allowable gap. This avoid timestep + // crashes for velocity projected gaps slightly in excess of the max allowable and still + // allows for a soft contact response without a timestep crash. + // + // v1_dot_n1 > 0 and v2_dot_n2 > 0 for further interpen + dt1 = ( dt1_vel_check ) ? alpha * max_delta1 / v1_dot_n1 : dt1; + dt2 = ( dt2_vel_check ) ? alpha * max_delta2 / v2_dot_n2 : dt2; + + // Keep debug print statements. This routine is still in the testing phase + // std::cout << "dt1_vel_check, (proj_delta_n_1+max_delta1), v1_dot_n1: " << dt1_vel_check << ", " + // << proj_delta_n_1+max_delta1 << ", " << v1_dot_n1 << std::endl; + // std::cout << "dt2_vel_check, (proj_delta_n_2+max_delta2), v2_dot_n2: " << dt2_vel_check << ", " + // << proj_delta_n_2+max_delta2 << ", " << v2_dot_n2 << std::endl; + // std::cout << "dt1 and dt2: " << dt1 << ", " << dt2 << std::endl; + + // update dt_temp2 only for positive dt1 and/or dt2 + if ( dt1 > 0. ) { #ifdef TRIBOL_USE_RAJA - RAJA::atomicMin( &dt_temp[1], - axom::utilities::min(dt1, 1.e6) ); + RAJA::atomicMin( &dt_temp[1], axom::utilities::min( dt1, 1.e6 ) ); #else - dt_temp[1] = axom::utilities::min(dt_temp[1], axom::utilities::min(dt1, 1.e6)); + dt_temp[1] = axom::utilities::min(dt_temp[1], axom::utilities::min(dt1, 1.e6)); #endif - } - if (dt2 > 0.) - { + } + if ( dt2 > 0. ) { #ifdef TRIBOL_USE_RAJA - RAJA::atomicMin( &dt_temp[1], - axom::utilities::min(1.e6, dt2) ); + RAJA::atomicMin( &dt_temp[1], axom::utilities::min( 1.e6, dt2 ) ); #else - dt_temp[1] = axom::utilities::min(dt_temp[1], axom::utilities::min(1.e6, dt2)); + dt_temp[1] = axom::utilities::min(dt_temp[1], axom::utilities::min(1.e6, dt2)); #endif - } - if (dt1 < 0. || dt2 < 0.) - { - msg[3] = true; - } + } + if ( dt1 < 0. || dt2 < 0. ) { + msg[3] = true; + } - } // end check 2 - } - ); + } // end check 2 + } ); // print general messages once // Can we output this message on root? SRW - ArrayT msg_host(msg_data); - SLIC_DEBUG_IF(msg_host[0] || msg_host[1], "tribol::computeCommonPlaneTimeStep(): " << - "there are locations where mesh overlap may be too large. " << - "Cannot provide timestep vote. Reduce timestep and/or increase " << - "penalty."); - - SLIC_DEBUG_IF(msg_host[2], "tribol::computeCommonPlaneTimeStep(): " << - "one or more face-pairs have a negative timestep vote based on " << - "maximum gap check." ); - - SLIC_DEBUG_IF(msg_host[3], "tribol::computeCommonPlaneTimeStep(): " << - "one or more face-pairs have a negative timestep vote based on " << - "velocity projection calculation." ); - - ArrayT dt_temp_host(dt_temp_data); - dt = axom::utilities::min(dt_temp_host[0], dt_temp_host[1]); + ArrayT msg_host( msg_data ); + SLIC_DEBUG_IF( msg_host[0] || msg_host[1], "tribol::computeCommonPlaneTimeStep(): " + << "there are locations where mesh overlap may be too large. " + << "Cannot provide timestep vote. Reduce timestep and/or increase " + << "penalty." ); + + SLIC_DEBUG_IF( msg_host[2], "tribol::computeCommonPlaneTimeStep(): " + << "one or more face-pairs have a negative timestep vote based on " + << "maximum gap check." ); + + SLIC_DEBUG_IF( msg_host[3], "tribol::computeCommonPlaneTimeStep(): " + << "one or more face-pairs have a negative timestep vote based on " + << "velocity projection calculation." ); + + ArrayT dt_temp_host( dt_temp_data ); + dt = axom::utilities::min( dt_temp_host[0], dt_temp_host[1] ); } //------------------------------------------------------------------------------ -void CouplingScheme::writeInterfaceOutput( const std::string& dir, - const VisType v_type, - const int cycle, +void CouplingScheme::writeInterfaceOutput( const std::string& dir, const VisType v_type, const int cycle, const RealT t ) { - int dim = this->spatialDimension(); - if ( m_parameters.vis_cycle_incr > 0 - && !(cycle % m_parameters.vis_cycle_incr) ) - { - switch( m_contactMethod ) { - case SINGLE_MORTAR : - case ALIGNED_MORTAR : - case MORTAR_WEIGHTS : - case COMMON_PLANE : - WriteContactPlaneMeshToVtk( dir, v_type, m_id, m_mesh_id1, m_mesh_id2, - dim, cycle, t ); - break; - default : - // Can this be called on root? SRW - SLIC_INFO( "CouplingScheme::writeInterfaceOutput(): " << - "output routine not yet written for interface method. " ); - break; - } // end-switch - } // end-if - return; + int dim = this->spatialDimension(); + if ( m_parameters.vis_cycle_incr > 0 && !( cycle % m_parameters.vis_cycle_incr ) ) { + switch ( m_contactMethod ) { + case SINGLE_MORTAR: + case ALIGNED_MORTAR: + case MORTAR_WEIGHTS: + case COMMON_PLANE: + WriteContactPlaneMeshToVtk( dir, v_type, m_id, m_mesh_id1, m_mesh_id2, dim, cycle, t ); + break; + default: + // Can this be called on root? SRW + SLIC_INFO( "CouplingScheme::writeInterfaceOutput(): " + << "output routine not yet written for interface method. " ); + break; + } // end-switch + } // end-if + return; } //------------------------------------------------------------------------------ void CouplingScheme::updatePairReportingData( const FaceGeomError face_error ) { - switch (face_error) - { - case NO_FACE_GEOM_ERROR: - { - // no-op - break; - } - case FACE_ORIENTATION: - { - ++this->m_pairReportingData.numBadOrientation; - break; - } - case INVALID_FACE_INPUT: - { - ++this->m_pairReportingData.numBadFaceGeometry; - break; - } - case DEGENERATE_OVERLAP: - { - ++this->m_pairReportingData.numBadOverlaps; - break; - } - case FACE_VERTEX_INDEX_EXCEEDS_OVERLAP_VERTICES: - { - // no-op; this is a very specific, in-the-weeds computational geometry - // debug print and does not indicate an issue with the host-code mesh - break; - } - default: break; - } // end switch + switch ( face_error ) { + case NO_FACE_GEOM_ERROR: { + // no-op + break; + } + case FACE_ORIENTATION: { + ++this->m_pairReportingData.numBadOrientation; + break; + } + case INVALID_FACE_INPUT: { + ++this->m_pairReportingData.numBadFaceGeometry; + break; + } + case DEGENERATE_OVERLAP: { + ++this->m_pairReportingData.numBadOverlaps; + break; + } + case FACE_VERTEX_INDEX_EXCEEDS_OVERLAP_VERTICES: { + // no-op; this is a very specific, in-the-weeds computational geometry + // debug print and does not indicate an issue with the host-code mesh + break; + } + default: + break; + } // end switch } //------------------------------------------------------------------------------ void CouplingScheme::printPairReportingData() { - SLIC_DEBUG_IF(getInterfacePairs().size() > 0, this->getNumActivePairs()*100./getInterfacePairs().size() << - "% of binned interface pairs are active contact candidates."); - - SLIC_DEBUG_IF(this->m_pairReportingData.numBadOrientation>0, - "Number of bad orientations is " << this->m_pairReportingData.numBadOrientation << - " equaling " << this->m_pairReportingData.numBadOrientation*100./getInterfacePairs().size() << - "% of total number of binned interface pairs."); - - SLIC_DEBUG_IF(this->m_pairReportingData.numBadFaceGeometry>0, - "Number of bad face geometries is " << this->m_pairReportingData.numBadFaceGeometry << - " equaling " << this->m_pairReportingData.numBadFaceGeometry*100./getInterfacePairs().size() << - "% of total number of binned interface pairs."); - - SLIC_DEBUG_IF(this->m_pairReportingData.numBadOverlaps>0, - "Number of bad contact overlaps is " << this->m_pairReportingData.numBadOverlaps << - " equaling " << this->m_pairReportingData.numBadOverlaps*100./getInterfacePairs().size() << - "% of total number of binned interface pairs."); + SLIC_DEBUG_IF( getInterfacePairs().size() > 0, this->getNumActivePairs() * 100. / getInterfacePairs().size() + << "% of binned interface pairs are active contact candidates." ); + + SLIC_DEBUG_IF( this->m_pairReportingData.numBadOrientation > 0, + "Number of bad orientations is " + << this->m_pairReportingData.numBadOrientation << " equaling " + << this->m_pairReportingData.numBadOrientation * 100. / getInterfacePairs().size() + << "% of total number of binned interface pairs." ); + + SLIC_DEBUG_IF( this->m_pairReportingData.numBadFaceGeometry > 0, + "Number of bad face geometries is " + << this->m_pairReportingData.numBadFaceGeometry << " equaling " + << this->m_pairReportingData.numBadFaceGeometry * 100. / getInterfacePairs().size() + << "% of total number of binned interface pairs." ); + + SLIC_DEBUG_IF( this->m_pairReportingData.numBadOverlaps > 0, + "Number of bad contact overlaps is " + << this->m_pairReportingData.numBadOverlaps << " equaling " + << this->m_pairReportingData.numBadOverlaps * 100. / getInterfacePairs().size() + << "% of total number of binned interface pairs." ); } //------------------------------------------------------------------------------ CouplingScheme::Viewer::Viewer( CouplingScheme& cs ) - : m_parameters( cs.m_parameters ) - , m_contact_case( cs.m_contactCase ) - , m_contact_method( cs.m_contactMethod ) - , m_enforcement_options( cs.m_enforcementOptions ) - , m_mesh1( cs.getMesh1().getView() ) - , m_mesh2( cs.getMesh2().getView() ) - , m_contact_plane2d( cs.m_contact_plane2d ) - , m_contact_plane3d( cs.m_contact_plane3d ) -{} + : m_parameters( cs.m_parameters ), + m_contact_case( cs.m_contactCase ), + m_contact_method( cs.m_contactMethod ), + m_enforcement_options( cs.m_enforcementOptions ), + m_mesh1( cs.getMesh1().getView() ), + m_mesh2( cs.getMesh2().getView() ), + m_contact_plane2d( cs.m_contact_plane2d ), + m_contact_plane3d( cs.m_contact_plane3d ) +{ +} //------------------------------------------------------------------------------ TRIBOL_HOST_DEVICE ContactPlane& CouplingScheme::Viewer::getContactPlane( IndexT id ) const { - if (spatialDimension() == 2) - { + if ( spatialDimension() == 2 ) { return m_contact_plane2d[id]; - } - else - { + } else { return m_contact_plane3d[id]; } } @@ -1970,56 +1725,52 @@ TRIBOL_HOST_DEVICE ContactPlane& CouplingScheme::Viewer::getContactPlane( IndexT //------------------------------------------------------------------------------ TRIBOL_HOST_DEVICE RealT CouplingScheme::Viewer::getGapTol( int fid1, int fid2 ) const { - RealT gap_tol = 0.; - // add debug warning if this routine is called for interface methods - // that do not require gap tolerances - switch ( m_contact_method ) { - - case SINGLE_MORTAR : + RealT gap_tol = 0.; + // add debug warning if this routine is called for interface methods + // that do not require gap tolerances + switch ( m_contact_method ) { + case SINGLE_MORTAR: #ifdef TRIBOL_USE_HOST - SLIC_WARNING("CouplingScheme::getGapTol(): 'SINGLE_MORTAR' " << - "method does not require use of a gap tolerance." ); + SLIC_WARNING( "CouplingScheme::getGapTol(): 'SINGLE_MORTAR' " + << "method does not require use of a gap tolerance." ); #endif - break; + break; - case ALIGNED_MORTAR : + case ALIGNED_MORTAR: #ifdef TRIBOL_USE_HOST - SLIC_WARNING("CouplingScheme::getGapTol(): 'ALIGNED_MORTAR' " << - "method does not require use of a gap tolerance." ); + SLIC_WARNING( "CouplingScheme::getGapTol(): 'ALIGNED_MORTAR' " + << "method does not require use of a gap tolerance." ); #endif - break; + break; - case MORTAR_WEIGHTS : + case MORTAR_WEIGHTS: #ifdef TRIBOL_USE_HOST - SLIC_WARNING("CouplingScheme::getGapTol(): 'MORTAR_WEIGHTS' " << - "method does not require use of a gap tolerance." ); + SLIC_WARNING( "CouplingScheme::getGapTol(): 'MORTAR_WEIGHTS' " + << "method does not require use of a gap tolerance." ); #endif - break; + break; - case COMMON_PLANE : + case COMMON_PLANE: - switch ( m_contact_case ) { + switch ( m_contact_case ) { + case TIED_NORMAL: + gap_tol = m_parameters.gap_tied_tol * + axom::utilities::max( m_mesh1.getFaceRadius()[fid1], m_mesh2.getFaceRadius()[fid2] ); + break; - case TIED_NORMAL : - gap_tol = m_parameters.gap_tied_tol * - axom::utilities::max( m_mesh1.getFaceRadius()[fid1], - m_mesh2.getFaceRadius()[fid2] ); - break; + default: + gap_tol = -1. * m_parameters.gap_tol_ratio * + axom::utilities::max( m_mesh1.getFaceRadius()[fid1], m_mesh2.getFaceRadius()[fid2] ); + break; - default : - gap_tol = -1. * m_parameters.gap_tol_ratio * - axom::utilities::max( m_mesh1.getFaceRadius()[fid1], - m_mesh2.getFaceRadius()[fid2] ); - break; + } // end switch over m_contactModel + break; - } // end switch over m_contactModel - break; + default: + break; + } // end switch over m_contactMethod - default : - break; - } // end switch over m_contactMethod - - return gap_tol; + return gap_tol; } } /* namespace tribol */ diff --git a/src/tribol/mesh/CouplingScheme.hpp b/src/tribol/mesh/CouplingScheme.hpp index f73935d5..ad3a692c 100644 --- a/src/tribol/mesh/CouplingScheme.hpp +++ b/src/tribol/mesh/CouplingScheme.hpp @@ -18,21 +18,18 @@ // Axom includes #include "axom/core.hpp" -namespace tribol -{ +namespace tribol { // Struct to hold on-rank coupling scheme face-pair reporting data // generated from computational geometry issues -struct PairReportingData -{ -public: - - int numBadOrientation {0}; - int numBadOverlaps {0}; - int numBadFaceGeometry {0}; +struct PairReportingData { + public: + int numBadOrientation{ 0 }; + int numBadOverlaps{ 0 }; + int numBadFaceGeometry{ 0 }; }; /** - * @brief Enumerates execution mode errors + * @brief Enumerates execution mode errors */ enum class ExecutionModeError { @@ -44,25 +41,23 @@ enum class ExecutionModeError }; // Helper struct to handle coupling scheme errors -struct CouplingSchemeErrors -{ -public: - - ModeError cs_mode_error; - CaseError cs_case_error; - MethodError cs_method_error; - ModelError cs_model_error; - EnforcementError cs_enforcement_error; - EnforcementDataErrors cs_enforcement_data_error; - ExecutionModeError cs_execution_mode_error; - - void printModeErrors(); - void printCaseErrors(); - void printMethodErrors(); - void printModelErrors(); - void printEnforcementErrors(); - void printEnforcementDataErrors(); - void printExecutionModeErrors(); +struct CouplingSchemeErrors { + public: + ModeError cs_mode_error; + CaseError cs_case_error; + MethodError cs_method_error; + ModelError cs_model_error; + EnforcementError cs_enforcement_error; + EnforcementDataErrors cs_enforcement_data_error; + ExecutionModeError cs_execution_mode_error; + + void printModeErrors(); + void printCaseErrors(); + void printMethodErrors(); + void printModelErrors(); + void printEnforcementErrors(); + void printEnforcementDataErrors(); + void printExecutionModeErrors(); }; /** @@ -75,18 +70,16 @@ enum class ExecutionModeInfo }; // Helper struct to handle coupling scheme infomational messages -struct CouplingSchemeInfo -{ -public: - - // Add info enums as needed - CaseInfo cs_case_info; - EnforcementInfo cs_enforcement_info; - ExecutionModeInfo cs_execution_mode_info; - - void printCaseInfo(); - void printEnforcementInfo(); - void printExecutionModeInfo(); +struct CouplingSchemeInfo { + public: + // Add info enums as needed + CaseInfo cs_case_info; + EnforcementInfo cs_enforcement_info; + ExecutionModeInfo cs_execution_mode_info; + + void printCaseInfo(); + void printEnforcementInfo(); + void printExecutionModeInfo(); }; // forward declaration @@ -109,53 +102,51 @@ class MethodData; * \see BinningMethod * \see CouplingSchemeManager */ -class CouplingScheme -{ -public: +class CouplingScheme { + public: /** * @brief Nested class for holding views (non-owned, shallow copies) of coupling scheme data */ - class Viewer - { - public: + class Viewer { + public: /** * @brief Construct a new CouplingScheme::Viewer object - * + * * @param cs CouplingScheme to create a view of */ Viewer( CouplingScheme& cs ); /** * @brief Spatial dimension of the meshes in the coupling scheme - * + * * @return spatial dimension */ TRIBOL_HOST_DEVICE int spatialDimension() const { return m_mesh1.spatialDimension(); } /** * @brief Return a view of the first mesh in the coupling scheme - * + * * @return view of first mesh */ TRIBOL_HOST_DEVICE const MeshData::Viewer& getMesh1View() const { return m_mesh1; } /** * @brief Return a view of the second mesh in the coupling scheme - * + * * @return view of second mesh */ TRIBOL_HOST_DEVICE const MeshData::Viewer& getMesh2View() const { return m_mesh2; } /** * @brief Get the struct defining enforcement options - * + * * @return const reference to EnforcementOptions struct */ TRIBOL_HOST_DEVICE const EnforcementOptions& getEnforcementOptions() const { return m_enforcement_options; } /** * @brief Return the contact plane given by id - * + * * @param id identifier for a contact plane * @return contact plane object */ @@ -163,7 +154,7 @@ class CouplingScheme /** * @brief Get the timestep scale - * + * * @return timestep scale */ TRIBOL_HOST_DEVICE RealT getTimestepScale() const { return m_parameters.timestep_scale; } @@ -175,7 +166,7 @@ class CouplingScheme */ TRIBOL_HOST_DEVICE RealT getGapTol( int fid1, int fid2 ) const; - private: + private: /// Struct holding parameters for the coupling scheme Parameters m_parameters; @@ -200,7 +191,7 @@ class CouplingScheme /// Array view of 3D contact planes ArrayViewT m_contact_plane3d; - }; // end class CouplingScheme::Viewer + }; // end class CouplingScheme::Viewer /** * @brief Default constructor. Disabled. @@ -212,7 +203,8 @@ class CouplingScheme * * @param [in] cs_id coupling scheme id * @param [in] mesh_id1 id of the first contact surface mesh (corresponds to mortar surface with a mortar method) - * @param [in] mesh_id2 id of the second contact surface mesh (corresponds to nonmortar surface with a mortar method), or ANY_MESH for multiple meshes + * @param [in] mesh_id2 id of the second contact surface mesh (corresponds to nonmortar surface with a mortar method), + * or ANY_MESH for multiple meshes * @param [in] contact_mode the type of contact, e.g. SURFACE_TO_SURFACE * @param [in] contact_case the specific case of contact application, e.g. auto * @param [in] contact_method the contact method, e.g. SINGLE_MORTAR @@ -222,82 +214,75 @@ class CouplingScheme * * Per-cycle rebinning is enabled by default. */ - CouplingScheme( IndexT cs_id, - IndexT mesh_id1, - IndexT mesh_id2, - int contact_mode, - int contact_case, - int contact_method, - int contact_model, - int enforcement_method, - int binning_method, + CouplingScheme( IndexT cs_id, IndexT mesh_id1, IndexT mesh_id2, int contact_mode, int contact_case, + int contact_method, int contact_model, int enforcement_method, int binning_method, ExecutionMode given_exec_mode = ExecutionMode::Dynamic ); // Prevent copying - CouplingScheme(const CouplingScheme& other) = delete; - CouplingScheme& operator=(const CouplingScheme& other) = delete; + CouplingScheme( const CouplingScheme& other ) = delete; + CouplingScheme& operator=( const CouplingScheme& other ) = delete; // Enable moving - CouplingScheme(CouplingScheme&& other) = default; - CouplingScheme& operator=(CouplingScheme&& other) = default; + CouplingScheme( CouplingScheme&& other ) = default; + CouplingScheme& operator=( CouplingScheme&& other ) = default; /** * @brief Get the ID of the coupling scheme - * + * * @return unique coupling scheme ID */ int getId() const { return m_id; } /** * @brief Get the integer ID of the first mesh - * + * * @return unique ID of the first mesh */ int getMeshId1() const { return m_mesh_id1; } - + /** * @brief Get the integer ID of the second mesh - * + * * @return unique ID of the second mesh */ int getMeshId2() const { return m_mesh_id2; } /** * @brief Get the Parameters struct - * + * * @return reference to the Parameters struct */ Parameters& getParameters() { return m_parameters; } /** * @brief Get a reference to the first mesh - * + * * @return MeshData reference */ MeshData& getMesh1() { return *m_mesh1; } /// @overload const MeshData& getMesh1() const { return *m_mesh1; } - + /** * @brief Get a reference to the second mesh - * + * * @return MeshData reference */ MeshData& getMesh2() { return *m_mesh2; } - + /// @overload const MeshData& getMesh2() const { return *m_mesh2; } /** * @brief Get the execution mode for the coupling scheme - * - * @return ExecutionMode + * + * @return ExecutionMode */ ExecutionMode getExecutionMode() const { return m_exec_mode; } /** * @brief Get the Umpire allocator ID for mesh data (zero if built without Umpire) - * + * * @return allocator ID */ int getAllocatorId() const { return m_allocator_id; } @@ -306,63 +291,63 @@ class CouplingScheme /** * @brief Get the contact mode (pairing of mesh types) - * - * @return ContactMode + * + * @return ContactMode */ - ContactMode getContactMode() const { return m_contactMode; } + ContactMode getContactMode() const { return m_contactMode; } /** * @brief Get the contact case (special algorithmic considerations for method) - * - * @return ContactCase + * + * @return ContactCase */ - ContactCase getContactCase() const { return m_contactCase; } + ContactCase getContactCase() const { return m_contactCase; } /** * @brief Get the contact method (algorithm to integrate contact weak form term) - * - * @return ContactMethod + * + * @return ContactMethod */ - ContactMethod getContactMethod() const { return m_contactMethod; } + ContactMethod getContactMethod() const { return m_contactMethod; } /** * @brief Get the contact model (constitutive modeling options) - * - * @return ContactModel + * + * @return ContactModel */ ContactModel getContactModel() const { return m_contactModel; } /** * @brief Get the enforcement method (defines enforcement scheme for contact constraints) - * - * @return EnforcementMethod + * + * @return EnforcementMethod */ EnforcementMethod getEnforcementMethod() const { return m_enforcementMethod; } /** * @brief Get the spatial binning method - * - * @return BinningMethod + * + * @return BinningMethod */ BinningMethod getBinningMethod() const { return m_binningMethod; } /** * @brief Set the spatial binning method - * + * * @param binningMethod new enum value */ - void setBinningMethod(BinningMethod binningMethod) { m_binningMethod = binningMethod; } + void setBinningMethod( BinningMethod binningMethod ) { m_binningMethod = binningMethod; } /** * @brief Get the method data for the contact method - * + * * @return MethodData pointer */ MethodData* getMethodData() const { return m_methodData; } /** * @brief Get the enforcement options for the enforcement method - * + * * @return reference to the EnforcementOptions struct */ EnforcementOptions& getEnforcementOptions() { return m_enforcementOptions; } @@ -372,28 +357,28 @@ class CouplingScheme /** * @brief Get struct holding errors during coupling scheme initialization - * + * * @return CouplingSchemeErrors reference */ CouplingSchemeErrors& getCouplingSchemeErrors() { return m_couplingSchemeErrors; } /** * @brief Get struct holding informational messages that happened during coupling scheme initialization - * + * * @return CouplingSchemeInfo& reference */ - CouplingSchemeInfo& getCouplingSchemeInfo() { return m_couplingSchemeInfo; } + CouplingSchemeInfo& getCouplingSchemeInfo() { return m_couplingSchemeInfo; } /** * @brief Construct a non-owned, shallow copy of the CouplingScheme - * + * * @return CouplingScheme::Viewer type */ CouplingScheme::Viewer getView() { return *this; } /** * @brief Spatial dimension of the mesh - * + * * @return spatial dimension */ int spatialDimension() const @@ -408,21 +393,22 @@ class CouplingScheme * * @param [in] pred True to disable rebinning, false otherwise */ - void setFixedBinning(bool pred) { m_fixedBinning = pred; } + void setFixedBinning( bool pred ) { m_fixedBinning = pred; } /** * @brief Disable/Enable per-cycle rebinning of interface pairs based on * contact mode */ - void setFixedBinningPerCase() { - if (m_isBinned && m_contactCase == NO_SLIDING) { - m_fixedBinning = true; - } + void setFixedBinningPerCase() + { + if ( m_isBinned && m_contactCase == NO_SLIDING ) { + m_fixedBinning = true; + } } /** * @brief Set the MPI communicator for the coupling scheme - * + * * @param comm MPI communicator */ void setMPIComm( CommT comm ) { m_parameters.problem_comm = comm; } @@ -466,18 +452,15 @@ class CouplingScheme * * @return number of active interface pairs */ - int getNumActivePairs( ) const - { - return std::max(m_contact_plane2d.size(), m_contact_plane3d.size()); - } + int getNumActivePairs() const { return std::max( m_contact_plane2d.size(), m_contact_plane3d.size() ); } /** * @brief Return the contact plane given by id - * + * * @param id identifier for a contact plane * @return contact plane object */ - const ContactPlane& getContactPlane(IndexT id) const; + const ContactPlane& getContactPlane( IndexT id ) const; /** * @brief Returns a reference to the 3D contact planes @@ -489,13 +472,13 @@ class CouplingScheme /** * @brief Set whether the coupling scheme has been binned * - * @param [in] pred True to indicate binning has occurred + * @param [in] pred True to indicate binning has occurred */ - void setBinned(bool pred) { m_isBinned = pred; } - + void setBinned( bool pred ) { m_isBinned = pred; } + /** * @brief Set the stored mesh pointers based on the two mesh IDs provided - * + * * @return True if meshes are registered; false otherwise */ bool setMeshPointers(); @@ -508,7 +491,7 @@ class CouplingScheme bool isValidCouplingScheme(); /** - * @brief Returns true if one or both meshes are zero-element, null meshes + * @brief Returns true if one or both meshes are zero-element, null meshes * * @return true if one or both null meshes in coupling scheme */ @@ -577,7 +560,7 @@ class CouplingScheme void allocateMethodData(); /** - * @brief Performs the binning between mesh 1 and mesh 2 + * @brief Performs the binning between mesh 1 and mesh 2 */ void performBinning(); @@ -590,24 +573,21 @@ class CouplingScheme * * @return 0 if successful apply */ - int apply( int cycle, RealT t, RealT &dt ); + int apply( int cycle, RealT t, RealT& dt ); /** - * @brief Wrapper around method specific calculation of the Tribol timestep vote + * @brief Wrapper around method specific calculation of the Tribol timestep vote * * @param [in/out] dt simulation timestep at given cycle */ - void computeTimeStep( RealT &dt ); + void computeTimeStep( RealT& dt ); /** * @brief Set the output directory for file output - * + * * @param directory string giving a file system path */ - void setOutputDirectory( const std::string& directory ) - { - m_output_directory = directory; - } + void setOutputDirectory( const std::string& directory ) { m_output_directory = directory; } /** * @brief Wrapper to call method specific visualization output routines @@ -617,20 +597,17 @@ class CouplingScheme * @param [in] cycle simulation cycle * @param [in] t simulation time at given cycle */ - void writeInterfaceOutput( const std::string& dir, - const VisType v_type, - const int cycle, - const RealT t ); + void writeInterfaceOutput( const std::string& dir, const VisType v_type, const int cycle, const RealT t ); /** - * @brief Sets the coupling scheme logging level member variable + * @brief Sets the coupling scheme logging level member variable * * @param [in] log_level the LoggingLevel enum value */ void setLoggingLevel( const LoggingLevel log_level ) { m_loggingLevel = log_level; } /** - * @brief Sets the SLIC logging level per the coupling scheme logging level + * @brief Sets the SLIC logging level per the coupling scheme logging level * * @pre must call setLoggingLevel() first */ @@ -638,13 +615,13 @@ class CouplingScheme /** * @brief Get the SLIC logging level active for the coupling scheme - * + * * @return LoggingLevel */ LoggingLevel getLoggingLevel() const { return m_loggingLevel; } /** - * @brief This updates the total number of types of face geometry errors + * @brief This updates the total number of types of face geometry errors * * @pre The face_error is generated by calling CheckInterfacePair() */ @@ -676,23 +653,20 @@ class CouplingScheme * mesh data from the MFEM volume mesh to the Tribol surface mesh, and mesh * data such as displacement, velocity, and force (response). * - * @return MfemMeshData* + * @return MfemMeshData* */ MfemMeshData* getMfemMeshData() { return m_mfemMeshData.get(); } - + /** * @brief Get the MFEM mesh data object (const overload) * * MFEM mesh data includes the MFEM volume mesh, transfer operators to move mesh data * from the MFEM volume mesh to the Tribol surface mesh, and mesh data such as * displacement, velocity, and force (response). - * - * @return const MfemMeshData* + * + * @return const MfemMeshData* */ - const MfemMeshData* getMfemMeshData() const - { - return m_mfemMeshData.get(); - } + const MfemMeshData* getMfemMeshData() const { return m_mfemMeshData.get(); } /** * @brief Sets the MFEM mesh data object @@ -703,10 +677,7 @@ class CouplingScheme * * @param mfemMeshData Unique pointer to MFEM mesh data */ - void setMfemMeshData(std::unique_ptr mfemMeshData) - { - m_mfemMeshData = std::move(mfemMeshData); - } + void setMfemMeshData( std::unique_ptr mfemMeshData ) { m_mfemMeshData = std::move( mfemMeshData ); } /** * @brief Check if coupling scheme has MFEM submesh field data @@ -726,24 +697,21 @@ class CouplingScheme * MFEM submesh field data includes a parent-linked boundary mfem::ParSubMesh, * transfer operators to move mesh data from the boundary submesh to the * Tribol surface mesh, and submesh data such as gap and pressure. - * - * @return MfemSubmeshData* + * + * @return MfemSubmeshData* */ MfemSubmeshData* getMfemSubmeshData() { return m_mfemSubmeshData.get(); } - + /** * @brief Get the MFEM submesh field data object (const overload) * * MFEM submesh field data includes a parent-linked boundary mfem::ParSubMesh, * transfer operators to move mesh data from the boundary submesh to the * Tribol surface mesh, and submesh data such as gap and pressure. - * - * @return const MfemSubmeshData* + * + * @return const MfemSubmeshData* */ - const MfemSubmeshData* getMfemSubmeshData() const - { - return m_mfemSubmeshData.get(); - } + const MfemSubmeshData* getMfemSubmeshData() const { return m_mfemSubmeshData.get(); } /** * @brief Sets the MFEM submesh field data object @@ -751,12 +719,12 @@ class CouplingScheme * MFEM submesh field data includes a parent-linked boundary mfem::ParSubMesh, * transfer operators to move mesh data from the boundary submesh to the * Tribol surface mesh, and submesh data such as gap and pressure. - * + * * @param MfemSubmeshData Unique pointer to MFEM submesh field data */ - void setMfemSubmeshData(std::unique_ptr mfemSubmeshData) + void setMfemSubmeshData( std::unique_ptr mfemSubmeshData ) { - m_mfemSubmeshData = std::move(mfemSubmeshData); + m_mfemSubmeshData = std::move( mfemSubmeshData ); } /** @@ -770,34 +738,28 @@ class CouplingScheme * @return false: MFEM Jacobian data does not exist */ bool hasMfemJacobianData() const { return m_mfemJacobianData != nullptr; } - + /** * @brief Get the MFEM Jacobian data object * * MFEM Jacobian data includes transfer operators to move Jacobian * contributions from the Tribol surface mesh to the MFEM parent mesh and * parent-linked boundary submesh. - * - * @return MfemJacobianData* + * + * @return MfemJacobianData* */ - MfemJacobianData* getMfemJacobianData() - { - return m_mfemJacobianData.get(); - } - + MfemJacobianData* getMfemJacobianData() { return m_mfemJacobianData.get(); } + /** * @brief Get the MFEM jacobian data object (const overload) * * MFEM jacobian data includes transfer operators to move Jacobian * contributions from the Tribol surface mesh to the MFEM parent mesh and * parent-linked boundary submesh. - * - * @return MfemJacobianData* + * + * @return MfemJacobianData* */ - const MfemJacobianData* getMfemJacobianData() const - { - return m_mfemJacobianData.get(); - } + const MfemJacobianData* getMfemJacobianData() const { return m_mfemJacobianData.get(); } /** * @brief Sets the MFEM jacobian data object @@ -805,12 +767,12 @@ class CouplingScheme * MFEM jacobian data includes transfer operators to move Jacobian * contributions from the Tribol surface mesh to the MFEM parent mesh and * parent-linked boundary submesh. - * + * * @param mfemJacobianData Unique pointer to MFEM jacobian data */ - void setMfemJacobianData(std::unique_ptr mfemJacobianData) + void setMfemJacobianData( std::unique_ptr mfemJacobianData ) { - m_mfemJacobianData = std::move(mfemJacobianData); + m_mfemJacobianData = std::move( mfemJacobianData ); } #endif /* BUILD_REDECOMP */ @@ -820,56 +782,55 @@ class CouplingScheme * * @param [in/out] dt simulation timestep at given cycle */ - void computeCommonPlaneTimeStep( RealT &dt ); - -private: + void computeCommonPlaneTimeStep( RealT& dt ); - IndexT m_id; ///< Coupling Scheme id + private: + IndexT m_id; ///< Coupling Scheme id - IndexT m_mesh_id1; ///< Integer id for mesh 1 - IndexT m_mesh_id2; ///< Integer id for mesh 2 + IndexT m_mesh_id1; ///< Integer id for mesh 1 + IndexT m_mesh_id2; ///< Integer id for mesh 2 - MeshData* m_mesh1; ///< Pointer to mesh 1 (reset every time init() is called) - MeshData* m_mesh2; ///< Pointer to mesh 2 (reset every time init() is called) + MeshData* m_mesh1; ///< Pointer to mesh 1 (reset every time init() is called) + MeshData* m_mesh2; ///< Pointer to mesh 2 (reset every time init() is called) - ExecutionMode m_given_exec_mode; ///< User preferred execution mode (set by constructor) + ExecutionMode m_given_exec_mode; ///< User preferred execution mode (set by constructor) - ExecutionMode m_exec_mode; ///< Execution mode for kernels (set when init() is called) - int m_allocator_id; ///< Allocator for arrays used in kernels (set when init() is called) + ExecutionMode m_exec_mode; ///< Execution mode for kernels (set when init() is called) + int m_allocator_id; ///< Allocator for arrays used in kernels (set when init() is called) - Parameters m_parameters; ///< Struct holding coupling scheme parameters - std::string m_output_directory = ""; ///< Output directory for visualization dumps + Parameters m_parameters; ///< Struct holding coupling scheme parameters + std::string m_output_directory = ""; ///< Output directory for visualization dumps - bool m_nullMeshes {false}; ///< True if one or both meshes are zero-element (null) meshes - bool m_isValid {true}; ///< False if the coupling scheme is not valid per call to init() + bool m_nullMeshes{ false }; ///< True if one or both meshes are zero-element (null) meshes + bool m_isValid{ true }; ///< False if the coupling scheme is not valid per call to init() - int m_numTotalNodes; ///< Total number of nodes in the coupling scheme + int m_numTotalNodes; ///< Total number of nodes in the coupling scheme - ContactMode m_contactMode; ///< Contact mode - ContactCase m_contactCase; ///< Contact case - ContactMethod m_contactMethod; ///< Contact method - ContactModel m_contactModel; ///< Contact model - EnforcementMethod m_enforcementMethod; ///< Contact enforcement method - BinningMethod m_binningMethod; ///< Contact binning method + ContactMode m_contactMode; ///< Contact mode + ContactCase m_contactCase; ///< Contact case + ContactMethod m_contactMethod; ///< Contact method + ContactModel m_contactModel; ///< Contact model + EnforcementMethod m_enforcementMethod; ///< Contact enforcement method + BinningMethod m_binningMethod; ///< Contact binning method - LoggingLevel m_loggingLevel; ///< logging level enum for coupling scheme + LoggingLevel m_loggingLevel; ///< logging level enum for coupling scheme - bool m_fixedBinning; ///< True if using fixed binning for all cycles - bool m_isBinned; ///< True if binning has occured - bool m_isTied; ///< True if surfaces have been "tied" (Tied contact only) + bool m_fixedBinning; ///< True if using fixed binning for all cycles + bool m_isBinned; ///< True if binning has occured + bool m_isTied; ///< True if surfaces have been "tied" (Tied contact only) - ArrayT m_interface_pairs; ///< List of interface pairs + ArrayT m_interface_pairs; ///< List of interface pairs - ArrayT m_contact_plane2d; ///< List of 2D contact planes - ArrayT m_contact_plane3d; ///< List of 3D contact planes + ArrayT m_contact_plane2d; ///< List of 2D contact planes + ArrayT m_contact_plane3d; ///< List of 3D contact planes - MethodData* m_methodData; ///< method object holding required interface method data + MethodData* m_methodData; ///< method object holding required interface method data - EnforcementOptions m_enforcementOptions; ///< struct with options underneath chosen enforcement - CouplingSchemeErrors m_couplingSchemeErrors; ///< struct handling coupling scheme errors - CouplingSchemeInfo m_couplingSchemeInfo; ///< struct handling info to be printed + EnforcementOptions m_enforcementOptions; ///< struct with options underneath chosen enforcement + CouplingSchemeErrors m_couplingSchemeErrors; ///< struct handling coupling scheme errors + CouplingSchemeInfo m_couplingSchemeInfo; ///< struct handling info to be printed - PairReportingData m_pairReportingData; ///< struct handling on-rank pair reporting data from computational geometry + PairReportingData m_pairReportingData; ///< struct handling on-rank pair reporting data from computational geometry #ifdef BUILD_REDECOMP @@ -879,7 +840,7 @@ class CouplingScheme #endif /* BUILD_REDECOMP */ -}; // end class CouplingScheme +}; // end class CouplingScheme using CouplingSchemeManager = DataManager; diff --git a/src/tribol/mesh/InterfacePairs.cpp b/src/tribol/mesh/InterfacePairs.cpp index 1a03eb5e..54a97dfc 100644 --- a/src/tribol/mesh/InterfacePairs.cpp +++ b/src/tribol/mesh/InterfacePairs.cpp @@ -5,23 +5,16 @@ #include "tribol/mesh/InterfacePairs.hpp" -namespace tribol -{ +namespace tribol { -TRIBOL_HOST_DEVICE InterfacePair::InterfacePair( IndexT element_id1, - IndexT element_id2, - bool is_contact_candidate ) - : m_element_id1 ( element_id1 ) - , m_element_id2 ( element_id2 ) - , m_is_contact_candidate ( is_contact_candidate ) -{} +TRIBOL_HOST_DEVICE InterfacePair::InterfacePair( IndexT element_id1, IndexT element_id2, bool is_contact_candidate ) + : m_element_id1( element_id1 ), m_element_id2( element_id2 ), m_is_contact_candidate( is_contact_candidate ) +{ +} TRIBOL_HOST_DEVICE InterfacePair::InterfacePair() - : m_element_id1 ( -1 ) - , m_element_id2 ( -1 ) - , m_is_contact_candidate ( true ) -{} - -} // namespace tribol - + : m_element_id1( -1 ), m_element_id2( -1 ), m_is_contact_candidate( true ) +{ +} +} // namespace tribol diff --git a/src/tribol/mesh/InterfacePairs.hpp b/src/tribol/mesh/InterfacePairs.hpp index 5f9323f9..396549bf 100644 --- a/src/tribol/mesh/InterfacePairs.hpp +++ b/src/tribol/mesh/InterfacePairs.hpp @@ -8,33 +8,29 @@ #include "tribol/common/BasicTypes.hpp" -namespace tribol -{ +namespace tribol { -struct InterfacePair -{ - TRIBOL_HOST_DEVICE InterfacePair( IndexT element_id1, - IndexT element_id2, - bool is_contact_candidate = true ); +struct InterfacePair { + TRIBOL_HOST_DEVICE InterfacePair( IndexT element_id1, IndexT element_id2, bool is_contact_candidate = true ); // overload constructor to handle zero input arguments TRIBOL_HOST_DEVICE InterfacePair(); - // Element id for face 1 - IndexT m_element_id1; - - // Element id for face 2 - IndexT m_element_id2; - - // boolean indicating if a binned pair is a contact candidate. - // A contact candidate is defined as a face-pair that is deemed geometrically proximate - // by the binning coarse search, and one that passes the finer computational geometry - // (CG) filter/checks. These finer checks identify face-pairs that are intersecting or - // nearly intersecting with positive areas of overlap. These checks do not indicate - // whether a face-pair is contacting, since the definition of 'contacting' is specific - // to a particular contact method and its enforced contstraints. Rather, the CG filter - // identifies contact candidacy, or face-pairs likely in contact. - bool m_is_contact_candidate; + // Element id for face 1 + IndexT m_element_id1; + + // Element id for face 2 + IndexT m_element_id2; + + // boolean indicating if a binned pair is a contact candidate. + // A contact candidate is defined as a face-pair that is deemed geometrically proximate + // by the binning coarse search, and one that passes the finer computational geometry + // (CG) filter/checks. These finer checks identify face-pairs that are intersecting or + // nearly intersecting with positive areas of overlap. These checks do not indicate + // whether a face-pair is contacting, since the definition of 'contacting' is specific + // to a particular contact method and its enforced contstraints. Rather, the CG filter + // identifies contact candidacy, or face-pairs likely in contact. + bool m_is_contact_candidate; }; } /* namespace tribol */ diff --git a/src/tribol/mesh/MeshData.cpp b/src/tribol/mesh/MeshData.cpp index fbf868f1..37a6dba4 100644 --- a/src/tribol/mesh/MeshData.cpp +++ b/src/tribol/mesh/MeshData.cpp @@ -7,8 +7,8 @@ #include "tribol/common/ExecModel.hpp" #include "tribol/utils/Math.hpp" -#include -#include +#include +#include #include #include #include @@ -16,8 +16,7 @@ #include "axom/slic.hpp" #include "axom/fmt.hpp" -namespace tribol -{ +namespace tribol { //------------------------------------------------------------------------------ bool MeshElemData::isValidKinematicPenalty( PenaltyEnforcementOptions& pen_options ) @@ -26,166 +25,138 @@ bool MeshElemData::isValidKinematicPenalty( PenaltyEnforcementOptions& pen_optio KinematicPenaltyCalculation kin_calc = pen_options.kinematic_calculation; // check kinematic penalty calculation data - if ( !in_range( kin_calc, NUM_KINEMATIC_PENALTY_CALCULATION) ) - { - // warning already issued when penalty options were set. If penalty options + if ( !in_range( kin_calc, NUM_KINEMATIC_PENALTY_CALCULATION ) ) { + // warning already issued when penalty options were set. If penalty options // were not set, the following else-if will catch this return false; } - // a kinematic penalty should always be set. Right now Tribol does not support + // a kinematic penalty should always be set. Right now Tribol does not support // rate only enforcement - else if ( !pen_options.kinematic_calc_set ) - { - SLIC_WARNING( "MeshElemData::isValidKinematic(): kinematic penalty calculation data not set; " << - "call tribol::setPenaltyOptions()." ); + else if ( !pen_options.kinematic_calc_set ) { + SLIC_WARNING( "MeshElemData::isValidKinematic(): kinematic penalty calculation data not set; " + << "call tribol::setPenaltyOptions()." ); return false; } - switch (kin_calc) - { - case KINEMATIC_CONSTANT: - { - if ( !this->m_is_kinematic_constant_penalty_set ) - { - SLIC_WARNING("MeshElemData::isValidKinematicPenalty(): " << - "single stiffness penalty not set."); + switch ( kin_calc ) { + case KINEMATIC_CONSTANT: { + if ( !this->m_is_kinematic_constant_penalty_set ) { + SLIC_WARNING( "MeshElemData::isValidKinematicPenalty(): " + << "single stiffness penalty not set." ); return false; - } - else if ( this->m_penalty_stiffness < pen_options.tiny_penalty ) - { - SLIC_WARNING("MeshElemData::isValidKinematicPenalty(): " << - "single stiffness penalty less than threshold (" << - pen_options.tiny_penalty << "). Consider increasing " << - "for your problem."); + } else if ( this->m_penalty_stiffness < pen_options.tiny_penalty ) { + SLIC_WARNING( "MeshElemData::isValidKinematicPenalty(): " + << "single stiffness penalty less than threshold (" << pen_options.tiny_penalty + << "). Consider increasing " + << "for your problem." ); return false; } break; - } // end case KINEMATIC_CONSTANT - case KINEMATIC_ELEMENT: - { - if ( !this->m_is_kinematic_element_penalty_set ) - { - SLIC_WARNING("MeshElemData::isValidKinematicPenalty(): " << - "element-wise penalty data not set."); + } // end case KINEMATIC_CONSTANT + case KINEMATIC_ELEMENT: { + if ( !this->m_is_kinematic_element_penalty_set ) { + SLIC_WARNING( "MeshElemData::isValidKinematicPenalty(): " + << "element-wise penalty data not set." ); return false; } // check for positive material modulus and thickness values bool isValidMatMod = true; bool isValidElemThickness = true; - for (int i=0; im_num_cells; ++i) - { - if (this->m_mat_mod[i] <= 0.) - { + for ( int i = 0; i < this->m_num_cells; ++i ) { + if ( this->m_mat_mod[i] <= 0. ) { isValidMatMod = false; } - if (this->m_thickness[i] <= 0.) - { + if ( this->m_thickness[i] <= 0. ) { isValidElemThickness = false; } - SLIC_WARNING_IF(!isValidMatMod, "MeshElemData::isValidKinematicPenalty(): " << - "invalid nonpositive element material modulus encountered."); + SLIC_WARNING_IF( !isValidMatMod, "MeshElemData::isValidKinematicPenalty(): " + << "invalid nonpositive element material modulus encountered." ); - SLIC_WARNING_IF(!isValidElemThickness, "MeshElemData::isValidKinematicPenalty(): " << - "invalid nonpositive element thickness encountered."); + SLIC_WARNING_IF( !isValidElemThickness, "MeshElemData::isValidKinematicPenalty(): " + << "invalid nonpositive element thickness encountered." ); - if (!isValidMatMod || !isValidElemThickness) - { + if ( !isValidMatMod || !isValidElemThickness ) { return false; } - } // end for loop over elements + } // end for loop over elements break; - } // end case KINEMATIC_ELEMENT + } // end case KINEMATIC_ELEMENT default: // no-op, quiet compiler break; - } // end switch over kinematic calculation + } // end switch over kinematic calculation return true; - -} // end MeshElemData::isValidKinematicPenalty() + +} // end MeshElemData::isValidKinematicPenalty() //------------------------------------------------------------------------------ bool MeshElemData::isValidRatePenalty( PenaltyEnforcementOptions& pen_options ) { // Note, this method is and should only be called for non-null meshes - RatePenaltyCalculation rate_calc = pen_options.rate_calculation; + RatePenaltyCalculation rate_calc = pen_options.rate_calculation; // check rate penalty calculation data - if ( !in_range( rate_calc, NUM_RATE_PENALTY_CALCULATION) ) - { - // warning already issued when penalty options were set. If penalty options + if ( !in_range( rate_calc, NUM_RATE_PENALTY_CALCULATION ) ) { + // warning already issued when penalty options were set. If penalty options // were not set, the following else-if will catch this return false; } // the rate_calc could be set to NONE and this boolean will be true - else if ( !pen_options.rate_calc_set ) - { - SLIC_WARNING( "MeshElemData::isValidRatePenalty(): rate penalty calculation data not set. " << - "call tribol::setPenaltyOptions()." ); + else if ( !pen_options.rate_calc_set ) { + SLIC_WARNING( "MeshElemData::isValidRatePenalty(): rate penalty calculation data not set. " + << "call tribol::setPenaltyOptions()." ); return false; } - switch (rate_calc) - { - case NO_RATE_PENALTY: - { - // double check that mesh booleans are consistent with rate penalty + switch ( rate_calc ) { + case NO_RATE_PENALTY: { + // double check that mesh booleans are consistent with rate penalty // calculation option - if (this->m_is_rate_constant_penalty_set) - { + if ( this->m_is_rate_constant_penalty_set ) { this->m_is_rate_constant_penalty_set = false; - } - else if (this->m_is_rate_percent_penalty_set) - { + } else if ( this->m_is_rate_percent_penalty_set ) { this->m_is_rate_percent_penalty_set = false; } break; - } // end case NONE - case RATE_CONSTANT: - { - if ( !this->m_is_rate_constant_penalty_set ) - { - SLIC_WARNING("MeshElemData::isValidRatePenalty(): " << - "constant rate penalty data not set."); + } // end case NONE + case RATE_CONSTANT: { + if ( !this->m_is_rate_constant_penalty_set ) { + SLIC_WARNING( "MeshElemData::isValidRatePenalty(): " + << "constant rate penalty data not set." ); return false; - } - else if ( this->m_rate_penalty_stiffness < pen_options.tiny_penalty ) - { - SLIC_WARNING("MeshElemData::isValidRatePenalty(): " << - "constant rate penalty less than threshold (" << - pen_options.tiny_penalty << "). Consider increasing " << - "for your problem."); + } else if ( this->m_rate_penalty_stiffness < pen_options.tiny_penalty ) { + SLIC_WARNING( "MeshElemData::isValidRatePenalty(): " + << "constant rate penalty less than threshold (" << pen_options.tiny_penalty + << "). Consider increasing " + << "for your problem." ); return false; } break; - } // end case RATE_CONSTANT - case RATE_PERCENT: - { - if ( !this->m_is_rate_percent_penalty_set ) - { - SLIC_WARNING("MeshElemData::isValidRatePenalty(): " << - "percent rate penalty data not set."); + } // end case RATE_CONSTANT + case RATE_PERCENT: { + if ( !this->m_is_rate_percent_penalty_set ) { + SLIC_WARNING( "MeshElemData::isValidRatePenalty(): " + << "percent rate penalty data not set." ); return false; - } - else if ( this->m_rate_percent_stiffness < pen_options.tiny_penalty || - this->m_rate_percent_stiffness > (1.-pen_options.tiny_penalty) ) - { - SLIC_WARNING("MeshElemData::isValidRatePenalty(): " << - "rate percent penalty not in (0,1)."); + } else if ( this->m_rate_percent_stiffness < pen_options.tiny_penalty || + this->m_rate_percent_stiffness > ( 1. - pen_options.tiny_penalty ) ) { + SLIC_WARNING( "MeshElemData::isValidRatePenalty(): " + << "rate percent penalty not in (0,1)." ); return false; } break; - } // end case RATE_PERCENT + } // end case RATE_PERCENT default: // no-op, quiet compiler break; - } // end switch on rate calculation + } // end switch on rate calculation return true; -} // end MeshElemData::isValidRatePenalty() +} // end MeshElemData::isValidRatePenalty() //------------------------------------------------------------------------------ @@ -194,41 +165,35 @@ bool MeshElemData::isValidRatePenalty( PenaltyEnforcementOptions& pen_options ) // Routines for MeshData class // // // ///////////////////////////////// -MeshData::MeshData( IndexT mesh_id, IndexT num_elements, IndexT num_nodes, - const IndexT* connectivity, InterfaceElementType element_type, - const RealT* x, const RealT* y, const RealT* z, +MeshData::MeshData( IndexT mesh_id, IndexT num_elements, IndexT num_nodes, const IndexT* connectivity, + InterfaceElementType element_type, const RealT* x, const RealT* y, const RealT* z, MemorySpace mem_space ) - : m_mesh_id( mesh_id ) - , m_element_type( element_type ) - , m_dim( getDimFromElementType() ) - , m_num_nodes( num_nodes ) - , m_mem_space( mem_space ) - , m_allocator_id( getResourceAllocatorID(mem_space) ) - , m_is_valid( true ) - , m_position( createNodalVector(x, y, z) ) - , m_connectivity( createConnectivity(num_elements, connectivity) ) + : m_mesh_id( mesh_id ), + m_element_type( element_type ), + m_dim( getDimFromElementType() ), + m_num_nodes( num_nodes ), + m_mem_space( mem_space ), + m_allocator_id( getResourceAllocatorID( mem_space ) ), + m_is_valid( true ), + m_position( createNodalVector( x, y, z ) ), + m_connectivity( createConnectivity( num_elements, connectivity ) ) { // mesh verification - if (num_elements > 0) - { - if (m_dim == 2 && (x == nullptr || y == nullptr)) - { - SLIC_WARNING_ROOT("tribol::MeshData(): pointer to x and/or y-component " << - "mesh coordinate array is a null pointer " << - "for mesh id " << m_mesh_id << "."); + if ( num_elements > 0 ) { + if ( m_dim == 2 && ( x == nullptr || y == nullptr ) ) { + SLIC_WARNING_ROOT( "tribol::MeshData(): pointer to x and/or y-component " + << "mesh coordinate array is a null pointer " + << "for mesh id " << m_mesh_id << "." ); m_is_valid = false; - } - else if (m_dim == 3 && (x == nullptr || y == nullptr || z == nullptr)) - { - SLIC_WARNING_ROOT("tribol::MeshData(): pointer to x, y, and/or z-component " << - "mesh coordinate array is a null pointer " << - "for mesh id " << m_mesh_id << "."); + } else if ( m_dim == 3 && ( x == nullptr || y == nullptr || z == nullptr ) ) { + SLIC_WARNING_ROOT( "tribol::MeshData(): pointer to x, y, and/or z-component " + << "mesh coordinate array is a null pointer " + << "for mesh id " << m_mesh_id << "." ); m_is_valid = false; } - if (connectivity == nullptr) - { - SLIC_WARNING_ROOT("tribol::MeshData(): pointer to mesh connectivity is " << - "a null pointer for mesh id " << m_mesh_id << "."); + if ( connectivity == nullptr ) { + SLIC_WARNING_ROOT( "tribol::MeshData(): pointer to mesh connectivity is " + << "a null pointer for mesh id " << m_mesh_id << "." ); } } @@ -237,80 +202,60 @@ MeshData::MeshData( IndexT mesh_id, IndexT num_elements, IndexT num_nodes, } //------------------------------------------------------------------------------ -void MeshData::setPosition( const RealT* x, - const RealT* y, - const RealT* z ) +void MeshData::setPosition( const RealT* x, const RealT* y, const RealT* z ) { - m_position = createNodalVector(x, y, z); + m_position = createNodalVector( x, y, z ); } //------------------------------------------------------------------------------ -void MeshData::setDisplacement( const RealT* ux, - const RealT* uy, - const RealT* uz ) +void MeshData::setDisplacement( const RealT* ux, const RealT* uy, const RealT* uz ) { - m_disp = createNodalVector(ux, uy, uz); + m_disp = createNodalVector( ux, uy, uz ); } //------------------------------------------------------------------------------ -void MeshData::setVelocity( const RealT* vx, - const RealT* vy, - const RealT* vz ) +void MeshData::setVelocity( const RealT* vx, const RealT* vy, const RealT* vz ) { - m_vel = createNodalVector(vx, vy, vz); + m_vel = createNodalVector( vx, vy, vz ); } //------------------------------------------------------------------------------ -void MeshData::setResponse( RealT* rx, - RealT* ry, - RealT* rz ) -{ - m_response = createNodalVector(rx, ry, rz); -} +void MeshData::setResponse( RealT* rx, RealT* ry, RealT* rz ) { m_response = createNodalVector( rx, ry, rz ); } //------------------------------------------------------------------------------ int MeshData::getDimFromElementType() const { - switch (m_element_type) - { - case LINEAR_EDGE: - { + switch ( m_element_type ) { + case LINEAR_EDGE: { return 2; } case LINEAR_TRIANGLE: - case LINEAR_QUAD: - { + case LINEAR_QUAD: { return 3; } - default: - { - SLIC_ERROR_ROOT("Unsupported element type for a contact mesh."); + default: { + SLIC_ERROR_ROOT( "Unsupported element type for a contact mesh." ); return 0; } } } //------------------------------------------------------------------------------ -ArrayViewT MeshData::createConnectivity(IndexT num_elements, const IndexT* connectivity) +ArrayViewT MeshData::createConnectivity( IndexT num_elements, const IndexT* connectivity ) { - switch (m_element_type) - { - case LINEAR_EDGE: - { - return ArrayViewT(connectivity, {num_elements, 2}); + switch ( m_element_type ) { + case LINEAR_EDGE: { + return ArrayViewT( connectivity, { num_elements, 2 } ); } - case LINEAR_TRIANGLE: - { - return ArrayViewT(connectivity, {num_elements, 3}); + case LINEAR_TRIANGLE: { + return ArrayViewT( connectivity, { num_elements, 3 } ); } - case LINEAR_QUAD: - { - return ArrayViewT(connectivity, {num_elements, 4}); + case LINEAR_QUAD: { + return ArrayViewT( connectivity, { num_elements, 4 } ); } - default: - { - SLIC_ERROR_ROOT("Unsupported element type for a contact mesh."); - return ArrayViewT(connectivity, {num_elements, 0}); + default: { + SLIC_ERROR_ROOT( "Unsupported element type for a contact mesh." ); + return ArrayViewT( connectivity, { num_elements, 0 } ); } } } @@ -318,20 +263,17 @@ ArrayViewT MeshData::createConnectivity(IndexT num_elements, co //------------------------------------------------------------------------------ Array1D MeshData::sortSurfaceNodeIds() { - ArrayT sorted_conn(0, m_connectivity.size()); - for (auto node_id : m_connectivity) - { - sorted_conn.push_back(node_id); + ArrayT sorted_conn( 0, m_connectivity.size() ); + for ( auto node_id : m_connectivity ) { + sorted_conn.push_back( node_id ); } - bubble_sort(sorted_conn.data(), sorted_conn.size()); + bubble_sort( sorted_conn.data(), sorted_conn.size() ); // count number of duplicate entries int num_dup = 0; - for (IndexT i{1}; i < sorted_conn.size(); ++i) - { - if (sorted_conn[i] == sorted_conn[i-1]) - { + for ( IndexT i{ 1 }; i < sorted_conn.size(); ++i ) { + if ( sorted_conn[i] == sorted_conn[i - 1] ) { ++num_dup; } } @@ -339,48 +281,46 @@ Array1D MeshData::sortSurfaceNodeIds() // compute number of unique integer ids int unique_size = sorted_conn.size() - num_dup; - SLIC_ERROR_IF(unique_size <= 0, "MeshData::sortSurfaceNodeIds(): " << - "invalid connectivity array; " << - "only single unique id in connectivity array."); + SLIC_ERROR_IF( unique_size <= 0, "MeshData::sortSurfaceNodeIds(): " + << "invalid connectivity array; " + << "only single unique id in connectivity array." ); // allocate array to store unique, sorted node ids on mesh object - auto sorted_surface_node_ids = ArrayT(0, unique_size); + auto sorted_surface_node_ids = ArrayT( 0, unique_size ); // populate sorted node id list - sorted_surface_node_ids.push_back(sorted_conn[0]); - for (IndexT i{1}; i < sorted_conn.size(); ++i) - { - if ( sorted_conn[i] != sorted_conn[i-1] ) - { - sorted_surface_node_ids.push_back(sorted_conn[i]); + sorted_surface_node_ids.push_back( sorted_conn[0] ); + for ( IndexT i{ 1 }; i < sorted_conn.size(); ++i ) { + if ( sorted_conn[i] != sorted_conn[i - 1] ) { + sorted_surface_node_ids.push_back( sorted_conn[i] ); } } return sorted_surface_node_ids; -} // end MeshData::sortSurfaceNodeIds() +} // end MeshData::sortSurfaceNodeIds() //------------------------------------------------------------------------------ -bool MeshData::computeFaceData(ExecutionMode exec_mode) +bool MeshData::computeFaceData( ExecutionMode exec_mode ) { constexpr RealT nrml_mag_tol = 1.0e-15; // allocate m_c - m_c = Array2D({m_dim, numberOfElements()}, m_allocator_id); + m_c = Array2D( { m_dim, numberOfElements() }, m_allocator_id ); // initialize m_c - m_c.fill(0.0); + m_c.fill( 0.0 ); // allocate m_n - m_n = Array2D({m_dim, numberOfElements()}, m_allocator_id); + m_n = Array2D( { m_dim, numberOfElements() }, m_allocator_id ); // initialize m_n - m_n.fill(0.0); + m_n.fill( 0.0 ); // allocate m_area (initialized to 0.0) - m_area = Array1D(numberOfElements(), numberOfElements(), m_allocator_id); + m_area = Array1D( numberOfElements(), numberOfElements(), m_allocator_id ); // allocate face_radius (initialized to 0.0) - m_face_radius = Array1D(numberOfElements(), numberOfElements(), m_allocator_id); - - ArrayT face_data_ok_data({static_cast(true)}, m_allocator_id); + m_face_radius = Array1D( numberOfElements(), numberOfElements(), m_allocator_id ); + + ArrayT face_data_ok_data( { static_cast( true ) }, m_allocator_id ); // loop over all elements in the mesh Array2DView c = m_c; @@ -391,429 +331,392 @@ bool MeshData::computeFaceData(ExecutionMode exec_mode) auto dim = m_dim; auto conn = m_connectivity; ArrayViewT face_data_ok = face_data_ok_data; - forAllExec(exec_mode, numberOfElements(), - [c, x, n, area, radius, dim, conn, face_data_ok] TRIBOL_HOST_DEVICE (IndexT i) { - - // compute the vertex average centroid. This will lie in the - // plane of the face for planar faces, and will be used as - // an approximate centroid for warped faces, both in 3D. - - // loop over the nodes per element - auto num_nodes_per_elem = conn.shape()[1]; - for (int j=0; j sqr_radius) { - sqr_radius = sqr_link_mag; - } - } - radius[i] = sqrt(sqr_radius); - - // compute the outward facing normal - if (dim == 2) { - - // the 2D calculation over a 2-node, 1D segment assumes a - // counter-clockwise ordering of the quad4 area element - // to which the 1D line segment belongs. This is to properly - // orient the normal outward - auto node_id = conn(i, 0); - auto next_node_id = conn(i, 1); - RealT lambdaX = x[0][ next_node_id ] - x[0][ node_id ]; - RealT lambdaY = x[1][ next_node_id ] - x[1][ node_id ]; - - n[0][i] = lambdaY; - n[1][i] = -lambdaX; - - // compute the length of the segment - area[i] = magnitude( lambdaX, lambdaY ); - - // normalize normal vector - auto mag = magnitude( n[0][i], n[1][i] ); - auto inv_mag = nrml_mag_tol; - if (mag >= nrml_mag_tol) { - inv_mag = 1.0 / mag; - } else { - face_data_ok[0] = static_cast(false); - } - n[0][i] *= inv_mag; - n[1][i] *= inv_mag; - - } - else if (dim == 3) { - - // this method of computing an outward unit normal breaks the - // face into triangular pallets by connecting two consecutive - // nodes with the approximate centroid. - // The average outward unit normal for the face is the average of - // those of the pallets. This is exact for non-warped faces. To - // compute the pallet normal, you only need edge vectors for the - // pallet. These are constructed from the face centroid and the face - // edge's first node and the face edge's two nodes - - // loop over num_nodes_per_elem-1 element edges and compute pallet - // normal - for (int j=0; j= nrml_mag_tol) { - inv_mag = 1.0 / mag; - } else { - face_data_ok[0] = static_cast(false); - } - - // normalize the average normal - n[0][i] *= inv_mag; - n[1][i] *= inv_mag; - n[2][i] *= inv_mag; - - } // end if (dim == 3) - - }); // end element loop - - ArrayT face_data_ok_host(face_data_ok_data); - SLIC_WARNING_IF(!face_data_ok_host[0], - axom::fmt::format("There are faces with a normal magnitude less than tolerance ({:e}).", nrml_mag_tol)); + forAllExec( exec_mode, numberOfElements(), + [c, x, n, area, radius, dim, conn, face_data_ok] TRIBOL_HOST_DEVICE( IndexT i ) { + // compute the vertex average centroid. This will lie in the + // plane of the face for planar faces, and will be used as + // an approximate centroid for warped faces, both in 3D. + + // loop over the nodes per element + auto num_nodes_per_elem = conn.shape()[1]; + for ( int j = 0; j < num_nodes_per_elem; ++j ) { + auto node_id = conn( i, j ); + for ( IndexT d{ 0 }; d < dim; ++d ) { + c[d][i] += x[d][node_id]; + } + } // end loop over nodes + + RealT fac = 1.0 / num_nodes_per_elem; + for ( IndexT d{ 0 }; d < dim; ++d ) { + c[d][i] = fac * c[d][i]; + } + + // compute face radius for both 2D and 3D. For 2D, this is a duplicate of + // the length, but allows for some uniformity in accessing this value + // for tolerance ratios + // loop over nodes of the face and determine the maximum + // "link" vector from the ith node to the face center + RealT sqr_radius = 0.0; + for ( int j = 0; j < num_nodes_per_elem; ++j ) { + const int node_id = conn( i, j ); + RealT sqr_link_mag = 0.0; + for ( IndexT d{ 0 }; d < dim; ++d ) { + RealT lv = x[d][node_id] - c[d][i]; + sqr_link_mag += lv * lv; + } + if ( sqr_link_mag > sqr_radius ) { + sqr_radius = sqr_link_mag; + } + } + radius[i] = sqrt( sqr_radius ); + + // compute the outward facing normal + if ( dim == 2 ) { + // the 2D calculation over a 2-node, 1D segment assumes a + // counter-clockwise ordering of the quad4 area element + // to which the 1D line segment belongs. This is to properly + // orient the normal outward + auto node_id = conn( i, 0 ); + auto next_node_id = conn( i, 1 ); + RealT lambdaX = x[0][next_node_id] - x[0][node_id]; + RealT lambdaY = x[1][next_node_id] - x[1][node_id]; + + n[0][i] = lambdaY; + n[1][i] = -lambdaX; + + // compute the length of the segment + area[i] = magnitude( lambdaX, lambdaY ); + + // normalize normal vector + auto mag = magnitude( n[0][i], n[1][i] ); + auto inv_mag = nrml_mag_tol; + if ( mag >= nrml_mag_tol ) { + inv_mag = 1.0 / mag; + } else { + face_data_ok[0] = static_cast( false ); + } + n[0][i] *= inv_mag; + n[1][i] *= inv_mag; + + } else if ( dim == 3 ) { + // this method of computing an outward unit normal breaks the + // face into triangular pallets by connecting two consecutive + // nodes with the approximate centroid. + // The average outward unit normal for the face is the average of + // those of the pallets. This is exact for non-warped faces. To + // compute the pallet normal, you only need edge vectors for the + // pallet. These are constructed from the face centroid and the face + // edge's first node and the face edge's two nodes + + // loop over num_nodes_per_elem-1 element edges and compute pallet + // normal + for ( int j = 0; j < num_nodes_per_elem; ++j ) { + auto node_id = conn( i, j ); + auto next_node_id = conn( i, 0 ); + if ( j < num_nodes_per_elem - 1 ) { + next_node_id = conn( i, j + 1 ); + } + // first triangle edge vector between the face's two edge nodes + auto vX1 = x[0][next_node_id] - x[0][node_id]; + auto vY1 = x[1][next_node_id] - x[1][node_id]; + auto vZ1 = x[2][next_node_id] - x[2][node_id]; + + // second triangle edge vector between the face centroid + // and the face edge's first node + auto vX2 = c[0][i] - x[0][node_id]; + auto vY2 = c[1][i] - x[1][node_id]; + auto vZ2 = c[2][i] - x[2][node_id]; + + // compute the contribution to the pallet normal as v1 x v2. Sum these + // into the face normal component variables stored on the mesh data + // object + auto nX = ( vY1 * vZ2 ) - ( vZ1 * vY2 ); + auto nY = ( vZ1 * vX2 ) - ( vX1 * vZ2 ); + auto nZ = ( vX1 * vY2 ) - ( vY1 * vX2 ); + + // sum the normal component contributions into the component variables + n[0][i] += nX; + n[1][i] += nY; + n[2][i] += nZ; + + // half the magnitude of the computed normal is the pallet area. Note: + // this is exact for planar faces and approximate for warped faces. + // Face areas are used in a general sense to create a face-overlap + // tolerance + area[i] += 0.5 * magnitude( nX, nY, nZ ); + } + + // multiply the pallet normal components by fac to obtain avg. + n[0][i] = fac * n[0][i]; + n[1][i] = fac * n[1][i]; + n[2][i] = fac * n[2][i]; + + // compute the magnitude of the average pallet normal + auto mag = magnitude( n[0][i], n[1][i], n[2][i] ); + auto inv_mag = nrml_mag_tol; + if ( mag >= nrml_mag_tol ) { + inv_mag = 1.0 / mag; + } else { + face_data_ok[0] = static_cast( false ); + } + + // normalize the average normal + n[0][i] *= inv_mag; + n[1][i] *= inv_mag; + n[2][i] *= inv_mag; + + } // end if (dim == 3) + } ); // end element loop + + ArrayT face_data_ok_host( face_data_ok_data ); + SLIC_WARNING_IF( + !face_data_ok_host[0], + axom::fmt::format( "There are faces with a normal magnitude less than tolerance ({:e}).", nrml_mag_tol ) ); return face_data_ok_host[0]; -} // end MeshData::computeFaceData() +} // end MeshData::computeFaceData() //------------------------------------------------------------------------------ -RealT MeshData::computeEdgeLength( int faceId ) +RealT MeshData::computeEdgeLength( int faceId ) { - // compute the length of the edge as the magnitude of - // the vector defined between the two edge vertices - const int nodeId1 = getGlobalNodeId( faceId, 0 ); - const int nodeId2 = getGlobalNodeId( faceId, 1 ); - RealT lvx = m_position[0][nodeId2] - m_position[0][nodeId1]; - RealT lvy = m_position[1][nodeId2] - m_position[1][nodeId1]; + // compute the length of the edge as the magnitude of + // the vector defined between the two edge vertices + const int nodeId1 = getGlobalNodeId( faceId, 0 ); + const int nodeId2 = getGlobalNodeId( faceId, 1 ); + RealT lvx = m_position[0][nodeId2] - m_position[0][nodeId1]; + RealT lvy = m_position[1][nodeId2] - m_position[1][nodeId1]; - RealT len = magnitude( lvx, lvy ); + RealT len = magnitude( lvx, lvy ); - return len; + return len; -} // end MeshData::computeEdgeLength() +} // end MeshData::computeEdgeLength() //------------------------------------------------------------------------------ void MeshData::computeNodalNormals( int const dim ) { - int * numFaceNrmlsToNodes; - if (this->numberOfElements() > 0) - { - // check to make sure face normals have been computed with - // a call to computeFaceData - if (m_n.empty()) - { - SLIC_ERROR("MeshData::computeNodalNormals: required face normals not computed."); + int* numFaceNrmlsToNodes; + if ( this->numberOfElements() > 0 ) { + // check to make sure face normals have been computed with + // a call to computeFaceData + if ( m_n.empty() ) { + SLIC_ERROR( "MeshData::computeNodalNormals: required face normals not computed." ); + } + + m_node_n = Array2D( { m_dim, m_num_nodes }, m_allocator_id ); + m_node_n.fill( 0.0 ); + + // allocate space for nodal normal array + int size = m_num_nodes; + // allocate scratch array to hold number of faces whose + // normals contribute to a given node. Most of the time + // this will be four face normals contributing to an + // averaged nodal normal for linear quad elements, but + // we want to handle arbitrary meshes and edge cases + allocIntArray( &numFaceNrmlsToNodes, size, 0 ); + } // end if-check on null mesh + + // loop over elements + for ( int i = 0; i < this->numberOfElements(); ++i ) { + // loop over element nodes + for ( int j = 0; j < this->numberOfNodesPerElement(); ++j ) { + // SRW: note the connectivity array must be local to the mesh for indexing into + // the mesh nodal normal array. If it is not, then nodeId will access some other + // piece of memory and there may be a memory issue when numFaceNrmlsToNodes is deleted + // at the end of this routine. + int nodeId = getGlobalNodeId( i, j ); + for ( int d = 0; d < this->spatialDimension(); ++d ) { + m_node_n( d, nodeId ) += this->m_n( d, i ); // m_n(d, i) is the ith face normal d-component } - m_node_n = Array2D({m_dim, m_num_nodes}, m_allocator_id); - m_node_n.fill(0.0); - - // allocate space for nodal normal array - int size = m_num_nodes; - // allocate scratch array to hold number of faces whose - // normals contribute to a given node. Most of the time - // this will be four face normals contributing to an - // averaged nodal normal for linear quad elements, but - // we want to handle arbitrary meshes and edge cases - allocIntArray( &numFaceNrmlsToNodes, size, 0 ); - } // end if-check on null mesh - - // loop over elements - for (int i=0; inumberOfElements(); ++i) - { - // loop over element nodes - for (int j=0; jnumberOfNodesPerElement(); ++j) - { - - // SRW: note the connectivity array must be local to the mesh for indexing into - // the mesh nodal normal array. If it is not, then nodeId will access some other - // piece of memory and there may be a memory issue when numFaceNrmlsToNodes is deleted - // at the end of this routine. - int nodeId = getGlobalNodeId(i, j); - for (int d=0; dspatialDimension(); ++d) - { - m_node_n(d, nodeId) += this->m_n(d, i); // m_n(d, i) is the ith face normal d-component - } - - // increment face-normal-to-node contribution counter - ++numFaceNrmlsToNodes[ nodeId ]; - - } // end loop over element nodes - - } // end loop over elements - - // average the nodal normals - if (this->numberOfElements() > 0) - { - for (int i=0; inumberOfElements() > 0) - { - if (dim == 3) - { - for (int i=0; inumberOfElements() > 0 ) { + for ( int i = 0; i < m_num_nodes; ++i ) { + m_node_n[0][i] /= numFaceNrmlsToNodes[i]; + m_node_n[1][i] /= numFaceNrmlsToNodes[i]; + if ( dim == 3 ) { + m_node_n[2][i] /= numFaceNrmlsToNodes[i]; } - else - { - for (int i=0; inumberOfElements() > 0 ) { + if ( dim == 3 ) { + for ( int i = 0; i < m_num_nodes; ++i ) { + RealT mag = magnitude( m_node_n[0][i], m_node_n[1][i], m_node_n[2][i] ); + m_node_n[0][i] /= mag; + m_node_n[1][i] /= mag; + m_node_n[2][i] /= mag; } + } else { + for ( int i = 0; i < m_num_nodes; ++i ) { + RealT mag = magnitude( m_node_n[0][i], m_node_n[1][i] ); + m_node_n[0][i] /= mag; + m_node_n[1][i] /= mag; + } + } - delete [] numFaceNrmlsToNodes; - } // end if-check on null mesh + delete[] numFaceNrmlsToNodes; + } // end if-check on null mesh - return; -} // end MeshData::computeNodalNormals() + return; +} // end MeshData::computeNodalNormals() //------------------------------------------------------------------------------ int MeshData::checkLagrangeMultiplierData() { - int err = 0; - if (this->numberOfElements()>0) - { - if (!m_nodal_fields.m_is_node_gap_set || - !m_nodal_fields.m_is_node_pressure_set) - { - err = 1; - } - } // end if-non-null mesh - return err; + int err = 0; + if ( this->numberOfElements() > 0 ) { + if ( !m_nodal_fields.m_is_node_gap_set || !m_nodal_fields.m_is_node_pressure_set ) { + err = 1; + } + } // end if-non-null mesh + return err; } //------------------------------------------------------------------------------ int MeshData::checkPenaltyData( PenaltyEnforcementOptions& p_enfrc_options ) { - int err = 0; - if (this->numberOfElements()>0) - { - PenaltyConstraintType constraint_type = p_enfrc_options.constraint_type; - // switch over penalty enforcement options and check for required data - switch (constraint_type) - { - case KINEMATIC: - { - if (!m_element_data.isValidKinematicPenalty( p_enfrc_options )) - { - err = 1; - } - break; - } // end KINEMATIC case - - case KINEMATIC_AND_RATE: - { - if (!m_element_data.isValidKinematicPenalty( p_enfrc_options )) - { - err = 1; - } - if (!m_element_data.isValidRatePenalty( p_enfrc_options )) - { - err = 1; - } - if (!m_nodal_fields.m_is_velocity_set) - { - SLIC_WARNING("Nodal velocities not set or null pointers; please set for " << - "use with gap rate penalty enforcement."); - err = 1; - } - break; - } // end case KINEMATIC_AND_RATE - default: - // no-op, quiet compiler - break; - } // end switch over constraint types - } // end if-non-null mesh - - return err; -} // end MeshData::checkPenaltyData() + int err = 0; + if ( this->numberOfElements() > 0 ) { + PenaltyConstraintType constraint_type = p_enfrc_options.constraint_type; + // switch over penalty enforcement options and check for required data + switch ( constraint_type ) { + case KINEMATIC: { + if ( !m_element_data.isValidKinematicPenalty( p_enfrc_options ) ) { + err = 1; + } + break; + } // end KINEMATIC case + + case KINEMATIC_AND_RATE: { + if ( !m_element_data.isValidKinematicPenalty( p_enfrc_options ) ) { + err = 1; + } + if ( !m_element_data.isValidRatePenalty( p_enfrc_options ) ) { + err = 1; + } + if ( !m_nodal_fields.m_is_velocity_set ) { + SLIC_WARNING( "Nodal velocities not set or null pointers; please set for " + << "use with gap rate penalty enforcement." ); + err = 1; + } + break; + } // end case KINEMATIC_AND_RATE + default: + // no-op, quiet compiler + break; + } // end switch over constraint types + } // end if-non-null mesh + + return err; +} // end MeshData::checkPenaltyData() //------------------------------------------------------------------------------ -void MeshData::print(std::ostream& os) const +void MeshData::print( std::ostream& os ) const { - const int num_verts = m_num_nodes; - const int num_elem = numberOfElements(); - - if(num_verts <= 0) - { - os << "{}"; - return; - } - - os << "{\n"; - os << axom::fmt::format(" verts ({}) {{",num_verts); - // positions - os << axom::fmt::format("\n\tx: {}", axom::fmt::join(m_position[0].data(), m_position[0].data()+num_verts, ", ")); - os << axom::fmt::format("\n\ty: {}", axom::fmt::join(m_position[1].data(), m_position[1].data()+num_verts, ", ")); - if(m_dim == 3) - { - os << axom::fmt::format("\n\tz: {}", axom::fmt::join(m_position[2].data(), m_position[2].data()+num_verts, ", ")); - } - // contact response (force) - if( !m_response.empty() ) - { - os << axom::fmt::format("\n\tfx: {}", axom::fmt::join(m_response[0].data(), m_response[0].data()+num_verts, ", ")); - os << axom::fmt::format("\n\tfy: {}", axom::fmt::join(m_response[1].data(), m_response[1].data()+num_verts, ", ")); - if(m_dim == 3) - { - os << axom::fmt::format("\n\tfz: {}", axom::fmt::join(m_response[2].data(), m_response[2].data()+num_verts, ", ")); - } - } - os << "\n }"; - - os << axom::fmt::format("\n elems ({}) {{", num_elem); - - if( !m_connectivity.empty() ) - { - os << axom::fmt::format("\n\tconnectivity: {{ {} }}", axom::fmt::join(m_connectivity.data(), m_connectivity.data()+(num_elem*numberOfNodesPerElement()), ", ")); - } - - // normals - if( !m_n.empty() ) - { - os << "\n\tnx: " << m_n(0, 0); - for (int e{1}; e < num_elem; ++e) - { - os << ", " << m_n(e, 0); - } - os << "\n\tny: " << m_n(0, 1); - for (int e{1}; e < num_elem; ++e) - { - os << ", " << m_n(e, 1); - } - if(m_dim == 3) - { - os << "\n\tnz: " << m_n(0, 2); - for (int e{1}; e < num_elem; ++e) - { - os << ", " << m_n(e, 2); - } + const int num_verts = m_num_nodes; + const int num_elem = numberOfElements(); + + if ( num_verts <= 0 ) { + os << "{}"; + return; + } + + os << "{\n"; + os << axom::fmt::format( " verts ({}) {{", num_verts ); + // positions + os << axom::fmt::format( "\n\tx: {}", + axom::fmt::join( m_position[0].data(), m_position[0].data() + num_verts, ", " ) ); + os << axom::fmt::format( "\n\ty: {}", + axom::fmt::join( m_position[1].data(), m_position[1].data() + num_verts, ", " ) ); + if ( m_dim == 3 ) { + os << axom::fmt::format( "\n\tz: {}", + axom::fmt::join( m_position[2].data(), m_position[2].data() + num_verts, ", " ) ); + } + // contact response (force) + if ( !m_response.empty() ) { + os << axom::fmt::format( "\n\tfx: {}", + axom::fmt::join( m_response[0].data(), m_response[0].data() + num_verts, ", " ) ); + os << axom::fmt::format( "\n\tfy: {}", + axom::fmt::join( m_response[1].data(), m_response[1].data() + num_verts, ", " ) ); + if ( m_dim == 3 ) { + os << axom::fmt::format( "\n\tfz: {}", + axom::fmt::join( m_response[2].data(), m_response[2].data() + num_verts, ", " ) ); + } + } + os << "\n }"; + + os << axom::fmt::format( "\n elems ({}) {{", num_elem ); + + if ( !m_connectivity.empty() ) { + os << axom::fmt::format( + "\n\tconnectivity: {{ {} }}", + axom::fmt::join( m_connectivity.data(), m_connectivity.data() + ( num_elem * numberOfNodesPerElement() ), + ", " ) ); + } + + // normals + if ( !m_n.empty() ) { + os << "\n\tnx: " << m_n( 0, 0 ); + for ( int e{ 1 }; e < num_elem; ++e ) { + os << ", " << m_n( e, 0 ); + } + os << "\n\tny: " << m_n( 0, 1 ); + for ( int e{ 1 }; e < num_elem; ++e ) { + os << ", " << m_n( e, 1 ); + } + if ( m_dim == 3 ) { + os << "\n\tnz: " << m_n( 0, 2 ); + for ( int e{ 1 }; e < num_elem; ++e ) { + os << ", " << m_n( e, 2 ); } - } - os << "\n }"; + } + } + os << "\n }"; - os << "\n}"; + os << "\n}"; } //------------------------------------------------------------------------------ -MeshData::Viewer::Viewer(MeshData& mesh) -: m_mesh_id( mesh.m_mesh_id ) -, m_element_type( mesh.m_element_type ) -, m_num_nodes( mesh.m_num_nodes ) -, m_mem_space( mesh.m_mem_space ) -, m_allocator_id( mesh.m_allocator_id ) -, m_position( mesh.m_position ) -, m_disp( mesh.m_disp ) -, m_vel( mesh.m_vel ) -, m_response( mesh.m_response ) -, m_node_n( mesh.m_node_n ) -, m_connectivity( mesh.m_connectivity ) -, m_c( mesh.m_c ) -, m_n( mesh.m_n ) -, m_face_radius( mesh.m_face_radius ) -, m_area( mesh.m_area ) -, m_nodal_fields( mesh.m_nodal_fields ) -, m_element_data( mesh.m_element_data ) -{} +MeshData::Viewer::Viewer( MeshData& mesh ) + : m_mesh_id( mesh.m_mesh_id ), + m_element_type( mesh.m_element_type ), + m_num_nodes( mesh.m_num_nodes ), + m_mem_space( mesh.m_mem_space ), + m_allocator_id( mesh.m_allocator_id ), + m_position( mesh.m_position ), + m_disp( mesh.m_disp ), + m_vel( mesh.m_vel ), + m_response( mesh.m_response ), + m_node_n( mesh.m_node_n ), + m_connectivity( mesh.m_connectivity ), + m_c( mesh.m_c ), + m_n( mesh.m_n ), + m_face_radius( mesh.m_face_radius ), + m_area( mesh.m_area ), + m_nodal_fields( mesh.m_nodal_fields ), + m_element_data( mesh.m_element_data ) +{ +} //------------------------------------------------------------------------------ TRIBOL_HOST_DEVICE void MeshData::Viewer::getFaceCoords( IndexT face_id, RealT* coords ) const { auto dim = spatialDimension(); - for (IndexT a{0}; a < numberOfNodesPerElement(); ++a) - { - IndexT node_id = getGlobalNodeId(face_id, a); - for (int d{0}; d < dim; ++d) - { - coords[dim*a + d] = m_position[d][node_id]; + for ( IndexT a{ 0 }; a < numberOfNodesPerElement(); ++a ) { + IndexT node_id = getGlobalNodeId( face_id, a ); + for ( int d{ 0 }; d < dim; ++d ) { + coords[dim * a + d] = m_position[d][node_id]; } } @@ -826,12 +729,10 @@ TRIBOL_HOST_DEVICE void MeshData::Viewer::getFaceVelocities( IndexT face_id, Rea { auto dim = spatialDimension(); - for (IndexT a{0}; a < numberOfNodesPerElement(); ++a) - { - IndexT node_id = getGlobalNodeId(face_id, a); - for (int d{0}; d < dim; ++d) - { - vels[dim*a + d] = m_vel[d][node_id]; + for ( IndexT a{ 0 }; a < numberOfNodesPerElement(); ++a ) { + IndexT node_id = getGlobalNodeId( face_id, a ); + for ( int d{ 0 }; d < dim; ++d ) { + vels[dim * a + d] = m_vel[d][node_id]; } } @@ -840,20 +741,19 @@ TRIBOL_HOST_DEVICE void MeshData::Viewer::getFaceVelocities( IndexT face_id, Rea } // end MeshData::Viewer::getFaceVelocities() //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE void MeshData::Viewer::getFaceNormal( int const face_id, RealT * nrml ) const +TRIBOL_HOST_DEVICE void MeshData::Viewer::getFaceNormal( int const face_id, RealT* nrml ) const { - for (int d{0}; d < spatialDimension(); ++d) - { + for ( int d{ 0 }; d < spatialDimension(); ++d ) { nrml[d] = m_n[d][face_id]; } return; -} // end MeshData::getFaceNormal() +} // end MeshData::getFaceNormal() -} // end tribol namespace +} // namespace tribol -std::ostream& operator<<(std::ostream& os, const tribol::MeshData& md) +std::ostream& operator<<( std::ostream& os, const tribol::MeshData& md ) { - md.print(os); - return os; + md.print( os ); + return os; } diff --git a/src/tribol/mesh/MeshData.hpp b/src/tribol/mesh/MeshData.hpp index ca3fd5bd..22c11b05 100644 --- a/src/tribol/mesh/MeshData.hpp +++ b/src/tribol/mesh/MeshData.hpp @@ -13,96 +13,90 @@ #include "tribol/common/Parameters.hpp" #include "tribol/utils/DataManager.hpp" -namespace tribol -{ +namespace tribol { /*! * \brief Struct to hold method specific nodal fields */ -struct MeshNodalData -{ +struct MeshNodalData { int m_num_nodes; ///////////////////////// // MORTAR NODAL FIELDS // ///////////////////////// - ArrayViewT m_node_gap; ///< scalar nodal gap (used on nonmortar mesh) - ArrayViewT m_node_pressure; ///< scalar nodal pressure (used on nonmortar mesh) + ArrayViewT m_node_gap; ///< scalar nodal gap (used on nonmortar mesh) + ArrayViewT m_node_pressure; ///< scalar nodal pressure (used on nonmortar mesh) - bool m_is_node_gap_set {false}; ///< true if nodal gap field is set - bool m_is_node_pressure_set {false}; ///< true if nodal pressure field is set + bool m_is_node_gap_set{ false }; ///< true if nodal gap field is set + bool m_is_node_pressure_set{ false }; ///< true if nodal pressure field is set ///////////////////////// - bool m_is_velocity_set {false}; ///< true if nodal velocities have been registered - bool m_is_nodal_displacement_set {false}; ///< true if nodal displacements have been registered - bool m_is_nodal_response_set {false}; ///< true if the nodal responses have been registered + bool m_is_velocity_set{ false }; ///< true if nodal velocities have been registered + bool m_is_nodal_displacement_set{ false }; ///< true if nodal displacements have been registered + bool m_is_nodal_response_set{ false }; ///< true if the nodal responses have been registered -}; // end of struct MeshNodalData +}; // end of struct MeshNodalData /*! * \brief Struct to hold method specific element data */ -struct MeshElemData -{ +struct MeshElemData { int m_num_cells; ////////////////////////////////////// // PENALTY ENFORCEMENT ELEMENT DATA // ////////////////////////////////////// - ArrayViewT m_mat_mod; ///< Bulk/Young's modulus for contact faces - ArrayViewT m_thickness; ///< Volume element thickness associated with each contact face + ArrayViewT m_mat_mod; ///< Bulk/Young's modulus for contact faces + ArrayViewT m_thickness; ///< Volume element thickness associated with each contact face - RealT m_penalty_stiffness {0.}; ///< single scalar kinematic penalty stiffness for each mesh - RealT m_penalty_scale {1.}; ///< scale factor applied to kinematic penalty only - RealT m_rate_penalty_stiffness {0.}; ///< single scalar rate penalty stiffness for each mesh - RealT m_rate_percent_stiffness {0.}; ///< rate penalty is percentage of gap penalty + RealT m_penalty_stiffness{ 0. }; ///< single scalar kinematic penalty stiffness for each mesh + RealT m_penalty_scale{ 1. }; ///< scale factor applied to kinematic penalty only + RealT m_rate_penalty_stiffness{ 0. }; ///< single scalar rate penalty stiffness for each mesh + RealT m_rate_percent_stiffness{ 0. }; ///< rate penalty is percentage of gap penalty - bool m_is_kinematic_constant_penalty_set {false}; ///< True if single kinematic constant penalty is set - bool m_is_kinematic_element_penalty_set {false}; ///< True if the element-wise kinematic penalty is set - bool m_is_rate_constant_penalty_set {false}; ///< True if the constant rate penalty is set - bool m_is_rate_percent_penalty_set {false}; ///< True if the rate percent penalty is set + bool m_is_kinematic_constant_penalty_set{ false }; ///< True if single kinematic constant penalty is set + bool m_is_kinematic_element_penalty_set{ false }; ///< True if the element-wise kinematic penalty is set + bool m_is_rate_constant_penalty_set{ false }; ///< True if the constant rate penalty is set + bool m_is_rate_percent_penalty_set{ false }; ///< True if the rate percent penalty is set - bool m_is_element_thickness_set {false}; ///< True if element thickness is set + bool m_is_element_thickness_set{ false }; ///< True if element thickness is set /*! - * \brief Checks if the kinematic penalty data is valid - * - * \param [in] pen_enfrc penalty enforcement option struct - * - * \return true if the kinematic penalty option has valid data - */ + * \brief Checks if the kinematic penalty data is valid + * + * \param [in] pen_enfrc penalty enforcement option struct + * + * \return true if the kinematic penalty option has valid data + */ bool isValidKinematicPenalty( PenaltyEnforcementOptions& pen_options ); /*! - * \brief Checks if the rate penalty data is valid - * - * \param [in] pen_enfrc to penalty enforcement option struct - * - * \return true if the rate penalty option has valid data - */ - bool isValidRatePenalty( PenaltyEnforcementOptions& pen_options ); ///< True if rate penalty option is valid - + * \brief Checks if the rate penalty data is valid + * + * \param [in] pen_enfrc to penalty enforcement option struct + * + * \return true if the rate penalty option has valid data + */ + bool isValidRatePenalty( PenaltyEnforcementOptions& pen_options ); ///< True if rate penalty option is valid }; -class MeshData -{ -public: +class MeshData { + public: /** * @brief Nested class for holding views (non-owned, shallow copies) of mesh data */ - class Viewer - { - public: + class Viewer { + public: /** * @brief Construct a new MeshData::Viewer object - * + * * @param mesh MeshData to create a view of */ - Viewer(MeshData& mesh); + Viewer( MeshData& mesh ); /** * @brief Obtain the mesh ID for the current mesh view - * + * * @return mesh ID */ TRIBOL_HOST_DEVICE IndexT meshId() const { return m_mesh_id; } @@ -111,14 +105,14 @@ class MeshData * @brief Get the element type for the current mesh view * * @note Tribol supports a single element type for each mesh - * + * * @return element type */ TRIBOL_HOST_DEVICE InterfaceElementType getElementType() const { return m_element_type; } /** * @brief Get the memory space of the data for the current mesh view - * + * * @return memory space */ TRIBOL_HOST_DEVICE MemorySpace getMemorySpace() const { return m_mem_space; } @@ -128,14 +122,14 @@ class MeshData * * @note Corresponds to an umpire allocator ID if Tribol is built with * Umpire; zero otherwise - * + * * @return allocator ID */ TRIBOL_HOST_DEVICE int getAllocatorId() const { return m_allocator_id; } /** * @brief Get the mesh nodal field data - * + * * @return nodal data for the mesh */ TRIBOL_HOST_DEVICE MeshNodalData& getNodalFields() { return m_nodal_fields; } @@ -145,7 +139,7 @@ class MeshData /** * @brief Get the mesh element data - * + * * @return element data for the mesh */ TRIBOL_HOST_DEVICE MeshElemData& getElementData() { return m_element_data; } @@ -155,231 +149,201 @@ class MeshData /** * @brief Spatial dimension of the mesh - * + * * @return spatial dimension */ TRIBOL_HOST_DEVICE int spatialDimension() const { return m_position.size(); } /** * @brief Number of nodes in the mesh - * + * * @return node count */ TRIBOL_HOST_DEVICE IndexT numberOfNodes() const { return m_num_nodes; } /** * @brief Number of elements in the mesh - * + * * @return element count */ TRIBOL_HOST_DEVICE IndexT numberOfElements() const { return m_connectivity.shape()[0]; } /** * @brief Number of nodes in each element of the mesh - * + * * @return nodes per element */ TRIBOL_HOST_DEVICE IndexT numberOfNodesPerElement() const { return m_connectivity.shape()[1]; } /** * @brief Get the global node ID - * + * * @param element_id which element the node belongs to * @param local_node_id node ID for the local element * @return global node ID */ - TRIBOL_HOST_DEVICE IndexT getGlobalNodeId(IndexT element_id, IndexT local_node_id) const + TRIBOL_HOST_DEVICE IndexT getGlobalNodeId( IndexT element_id, IndexT local_node_id ) const { - return m_connectivity(element_id, local_node_id); + return m_connectivity( element_id, local_node_id ); } /** * @brief Get the nodal position array views - * + * * @return array view of the nodal position arrays */ - TRIBOL_HOST_DEVICE const MultiViewArrayView& getPosition() const - { - return m_position; - } - + TRIBOL_HOST_DEVICE const MultiViewArrayView& getPosition() const { return m_position; } + /** * @brief Is the displacement vector populated? - * + * * @return true if non-empty; false otherwise */ TRIBOL_HOST_DEVICE bool hasDisplacement() const { return !m_disp.empty(); } /** * @brief Get the nodal displacement array views - * + * * @return array view of the nodal displacement arrays */ - TRIBOL_HOST_DEVICE const MultiViewArrayView& getDisplacement() const - { - return m_disp; - } + TRIBOL_HOST_DEVICE const MultiViewArrayView& getDisplacement() const { return m_disp; } /** * @brief Is the velocity vector populated? - * + * * @return true if non-empty; false otherwise */ TRIBOL_HOST_DEVICE bool hasVelocity() const { return !m_vel.empty(); } /** * @brief Get the nodal velocity array views - * + * * @return array view of the nodal velocity arrays */ - TRIBOL_HOST_DEVICE const MultiViewArrayView& getVelocity() const - { - return m_vel; - } - + TRIBOL_HOST_DEVICE const MultiViewArrayView& getVelocity() const { return m_vel; } + /** * @brief Is the nodal response vector populated? - * + * * @return true if non-empty; false otherwise */ TRIBOL_HOST_DEVICE bool hasResponse() const { return !m_response.empty(); } /** * @brief Get the nodal response array views - * + * * @return array view of the nodal response arrays */ - TRIBOL_HOST_DEVICE const MultiViewArrayView& getResponse() const - { - return m_response; - } - + TRIBOL_HOST_DEVICE const MultiViewArrayView& getResponse() const { return m_response; } + /** * @brief Is the nodal normal vector populated? - * + * * @return true if non-empty; false otherwise */ TRIBOL_HOST_DEVICE bool hasNodalNormals() const { return !m_node_n.empty(); } /** * @brief Get an array view of the nodal normals - * + * * @return array view of the nodal normals */ - TRIBOL_HOST_DEVICE const Array2DView& getNodalNormals() const - { - return m_node_n; - } + TRIBOL_HOST_DEVICE const Array2DView& getNodalNormals() const { return m_node_n; } /** * @brief Is the element centroids vector populated? - * + * * @return true if non-empty; false otherwise */ TRIBOL_HOST_DEVICE bool hasElementCentroids() const { return !m_c.empty(); } /** * @brief Get an array view of the element centroids - * + * * @return array view of element centroids */ - TRIBOL_HOST_DEVICE const Array2DView& getElementCentroids() const - { - return m_c; - } + TRIBOL_HOST_DEVICE const Array2DView& getElementCentroids() const { return m_c; } /** * @brief Is the element normals vector populated? - * + * * @return true if non-empty; false otherwise */ TRIBOL_HOST_DEVICE bool hasElementNormals() const { return !m_n.empty(); } /** * @brief Get an array view of the element normals - * + * * @return array view of the element normals */ - TRIBOL_HOST_DEVICE const Array2DView& getElementNormals() const - { - return m_n; - } + TRIBOL_HOST_DEVICE const Array2DView& getElementNormals() const { return m_n; } /** * @brief Is the element face radii vector populated? - * + * * @return true if non-empty; false otherwise */ TRIBOL_HOST_DEVICE bool hasFaceRadius() const { return !m_face_radius.empty(); } /** * @brief Get an array view of the element face radii - * + * * @return array view of the element face radii */ - TRIBOL_HOST_DEVICE const Array1DView& getFaceRadius() const - { - return m_face_radius; - } + TRIBOL_HOST_DEVICE const Array1DView& getFaceRadius() const { return m_face_radius; } /** * @brief Is the element area vector populated? - * + * * @return true if non-empty; false otherwise */ TRIBOL_HOST_DEVICE bool hasElementAreas() const { return !m_area.empty(); } /** * @brief Get an array view of the element areas - * + * * @return array view of the element areas */ - TRIBOL_HOST_DEVICE const Array1DView& getElementAreas() const - { - return m_area; - } + TRIBOL_HOST_DEVICE const Array1DView& getElementAreas() const { return m_area; } /** * @brief Get an array view of the element connectivity - * + * * @return array view of element connectivity */ - TRIBOL_HOST_DEVICE const Array2DView& getConnectivity() const - { - return m_connectivity; - } - + TRIBOL_HOST_DEVICE const Array2DView& getConnectivity() const { return m_connectivity; } + /*! - * - * \brief returns pointer to array of stacked nodal coordinates for given face - * - * \param [in] face_id integer id of face - * \param [in/out] coords pointer to an array of stacked (x,y,z) nodal coordinates - * - */ + * + * \brief returns pointer to array of stacked nodal coordinates for given face + * + * \param [in] face_id integer id of face + * \param [in/out] coords pointer to an array of stacked (x,y,z) nodal coordinates + * + */ TRIBOL_HOST_DEVICE void getFaceCoords( IndexT face_id, RealT* coords ) const; /*! - * - * \brief returns pointer to array of stacked nodal velocities for given face - * - * \param [in] face_id integer id of face - * \param [in/out] nodalVel pointer to an array of stacked (x,y,z) nodal velocities - * - */ + * + * \brief returns pointer to array of stacked nodal velocities for given face + * + * \param [in] face_id integer id of face + * \param [in/out] nodalVel pointer to an array of stacked (x,y,z) nodal velocities + * + */ TRIBOL_HOST_DEVICE void getFaceVelocities( IndexT face_id, RealT* vels ) const; /*! - * - * \brief returns pointer to array of stacked normal components - * - * \param [in] face_id integer id of face - * \param [in/out] nrml pointer to array of stacked components of the face normal vector - * - */ + * + * \brief returns pointer to array of stacked normal components + * + * \param [in] face_id integer id of face + * \param [in/out] nrml pointer to array of stacked components of the face normal vector + * + */ TRIBOL_HOST_DEVICE void getFaceNormal( IndexT face_id, RealT* nrml ) const; - - private: + + private: /// Unique mesh ID const IndexT m_mesh_id; @@ -397,7 +361,7 @@ class MeshData /// Array of views of nodal position data const MultiViewArrayView m_position; - + /// Array of views of nodal displacement data const MultiViewArrayView m_disp; @@ -424,15 +388,15 @@ class MeshData /// Array view of element area data const ArrayViewT m_area; - - MeshNodalData m_nodal_fields; ///< method specific nodal fields - MeshElemData m_element_data; ///< method/enforcement specific element data - }; // end class MeshData::Viewer + MeshNodalData m_nodal_fields; ///< method specific nodal fields + MeshElemData m_element_data; ///< method/enforcement specific element data + + }; // end class MeshData::Viewer /** * @brief Construct a new MeshData object - * + * * \param [in] mesh_id the ID of the contact surface * \param [in] num_elements the number of elements on the contact surface * \param [in] num_nodes length of the data arrays being registered @@ -451,35 +415,28 @@ class MeshData * \note connectivity is a 2D array with num_elements rows and num_nodes * columns with row-major ordering */ - MeshData( IndexT mesh_id, - IndexT num_elements, - IndexT num_nodes, - const IndexT* connectivity, - InterfaceElementType element_type, - const RealT* x, - const RealT* y, - const RealT* z, - MemorySpace mem_space ); + MeshData( IndexT mesh_id, IndexT num_elements, IndexT num_nodes, const IndexT* connectivity, + InterfaceElementType element_type, const RealT* x, const RealT* y, const RealT* z, MemorySpace mem_space ); /** * @brief Get the element type * * @note Tribol supports a single element type for each mesh - * + * * @return element type */ InterfaceElementType getElementType() const { return m_element_type; } - + /** * @brief Spatial dimension of the mesh - * + * * @return spatial dimension */ int spatialDimension() const { return m_dim; } /** * @brief Get the memory space of nodal/element data stored in the mesh - * + * * @return memory space */ MemorySpace getMemorySpace() const { return m_mem_space; } @@ -489,17 +446,17 @@ class MeshData * * @note Corresponds to an umpire allocator ID if Tribol is built with * Umpire; zero otherwise - * + * * @return allocator ID */ int getAllocatorId() const { return m_allocator_id; } /** * @brief Set the allocator ID of the nodal/element data stored in the mesh - * + * * @param allocator_id Umpire allocator ID (if built with Umpire; zero otherwise) */ - void updateAllocatorId(int allocator_id ) { m_allocator_id = allocator_id; } + void updateAllocatorId( int allocator_id ) { m_allocator_id = allocator_id; } /** * @brief Marker which can indicate mesh validity @@ -509,7 +466,7 @@ class MeshData * * @warning The marker can be updated outside MeshData so the definition of a * valid mesh may not be consistent. - * + * * @return true the marker is set to true * @return false the marker is set to false */ @@ -517,90 +474,84 @@ class MeshData /** * @brief Get the mesh nodal field data - * + * * @return nodal data for the mesh */ MeshNodalData& getNodalFields() { return m_nodal_fields; } - + /** * @brief Get the mesh element data - * + * * @return element data for the mesh */ MeshElemData& getElementData() { return m_element_data; } /// @overload const MeshElemData& getElementData() const { return m_element_data; } - + /** * @brief Number of nodes in the mesh - * + * * @return node count */ IndexT numberOfNodes() const { return m_num_nodes; } /** * @brief Number of elements in the mesh - * + * * @return element count */ IndexT numberOfElements() const { return m_connectivity.shape()[0]; } /** * @brief Number of nodes in each element of the mesh - * + * * @return nodes per element */ IndexT numberOfNodesPerElement() const { return m_connectivity.shape()[1]; } /** * @brief Get the global node ID - * + * * @param element_id which element the node belongs to * @param local_node_id node ID for the local element * @return global node ID */ - IndexT getGlobalNodeId(IndexT element_id, IndexT local_node_id) const + IndexT getGlobalNodeId( IndexT element_id, IndexT local_node_id ) const { - return m_connectivity(element_id, local_node_id); + return m_connectivity( element_id, local_node_id ); } /** * @brief Set the pointers to the nodal position data - * + * * @param x array of x-components of the nodal position * @param y array of y-components of the nodal position * @param z array of z-components of the nodal position */ - void setPosition( const RealT* x, - const RealT* y, - const RealT* z ); + void setPosition( const RealT* x, const RealT* y, const RealT* z ); /** * @brief Set the pointers to the nodal displacement data - * + * * @param ux array of x-components of the nodal displacement * @param uy array of y-components of the nodal displacement * @param uz array of z-components of the nodal displacement */ - void setDisplacement( const RealT* ux, - const RealT* uy, - const RealT* uz ); + void setDisplacement( const RealT* ux, const RealT* uy, const RealT* uz ); /** * @brief Set the pointers to the nodal velocity data - * + * * @param vx array of x-components of the nodal velocity * @param vy array of y-components of the nodal velocity * @param vz array of z-components of the nodal velocity */ - void setVelocity( const RealT* vx, - const RealT* vy, - const RealT* vz ); - + void setVelocity( const RealT* vx, const RealT* vy, const RealT* vz ); + /** * @brief Is the velocity vector populated? - * + * * @return true vector is non-empty * @return false vector is empty */ @@ -608,7 +559,7 @@ class MeshData /** * @brief Set the pointers to the nodal response data - * + * * @param rx array of x-components of the nodal response * @param ry array of y-components of the nodal response * @param rz array of z-components of the nodal response @@ -617,7 +568,7 @@ class MeshData /** * @brief Construct a non-owned, shallow copy of the MeshData - * + * * @return MeshData::Viewer type */ Viewer getView() { return *this; } @@ -625,10 +576,10 @@ class MeshData /// sorts unique surface node ids from connectivity and stores them on the mesh object in ascending order Array1D sortSurfaceNodeIds(); -private: + private: /** * @brief Converts a Tribol element type to a mesh spatial dimension - * + * * @return spatial dimension */ int getDimFromElementType() const; @@ -636,7 +587,7 @@ class MeshData /** * @brief Converts pointers to components of a vector to views of the vector * components - * + * * @tparam T underlying type of the components * @param x pointer to array of x-components * @param y pointer to array of y-components @@ -644,118 +595,113 @@ class MeshData * @return Array of array views of vector components */ template - MultiArrayView createNodalVector( T* x, - T* y, - T* z ) const; + MultiArrayView createNodalVector( T* x, T* y, T* z ) const; /** * @brief Converts pointer to element connectivity to an array view - * + * * @param num_elements element count; number of rows in connectivity array * @param connectivity pointer to array of connectivity data * @return Array view of element connectivity */ - Array2DView createConnectivity( IndexT num_elements, - const IndexT* connectivity ); + Array2DView createConnectivity( IndexT num_elements, const IndexT* connectivity ); - IndexT m_mesh_id; ///< Mesh Id associated with this data - InterfaceElementType m_element_type; ///< Type of interface element in mesh - int m_dim; ///< Spatial dimension of the mesh coordinates + IndexT m_mesh_id; ///< Mesh Id associated with this data + InterfaceElementType m_element_type; ///< Type of interface element in mesh + int m_dim; ///< Spatial dimension of the mesh coordinates IndexT m_num_nodes; - MemorySpace m_mem_space; ///< Memory space for mesh data - int m_allocator_id; ///< Allocator for mesh data memory - - bool m_is_valid; ///< True if the mesh is valid + MemorySpace m_mem_space; ///< Memory space for mesh data + int m_allocator_id; ///< Allocator for mesh data memory + + bool m_is_valid; ///< True if the mesh is valid - MeshNodalData m_nodal_fields; ///< method specific nodal fields - MeshElemData m_element_data; ///< method/enforcement specific element data + MeshNodalData m_nodal_fields; ///< method specific nodal fields + MeshElemData m_element_data; ///< method/enforcement specific element data // Nodal field data - MultiArrayView m_position; ///< Coordinates of nodes in mesh - MultiArrayView m_disp; ///< Nodal displacements - MultiArrayView m_vel; ///< Nodal velocity - MultiArrayView m_response; ///< Nodal responses (forces) + MultiArrayView m_position; ///< Coordinates of nodes in mesh + MultiArrayView m_disp; ///< Nodal displacements + MultiArrayView m_vel; ///< Nodal velocity + MultiArrayView m_response; ///< Nodal responses (forces) - Array2D m_node_n; ///< Outward unit node normals + Array2D m_node_n; ///< Outward unit node normals // Element field data Array2DView m_connectivity; ///< Element connectivity arrays - Array2D m_c; ///< Vertex averaged element centroids - Array2D m_n; ///< Outward unit element normals - Array1D m_face_radius; ///< Face radius used in low level proximity check - Array1D m_area; ///< Element areas - -public: + Array2D m_c; ///< Vertex averaged element centroids + Array2D m_n; ///< Outward unit element normals + Array1D m_face_radius; ///< Face radius used in low level proximity check + Array1D m_area; ///< Element areas + public: /*! - * \brief Checks for valid Lagrange multiplier enforcement data - * - */ + * \brief Checks for valid Lagrange multiplier enforcement data + * + */ int checkLagrangeMultiplierData(); /*! - * \brief Checks for valid penalty enforcement data - * - * \param [in] p_enfrc_options penalty enforcement options guiding check - */ + * \brief Checks for valid penalty enforcement data + * + * \param [in] p_enfrc_options penalty enforcement options guiding check + */ int checkPenaltyData( PenaltyEnforcementOptions& p_enfrc_options ); /*! - * \brief Computes the face normals and centroids for all faces in the mesh - * - * \param [in] exec_mode defines where loops should be executed - * \return true if face calculations do not encounter errors or warnings - * - * This routine accounts for warped faces by computing an average normal. - */ - bool computeFaceData(ExecutionMode exec_mode); - + * \brief Computes the face normals and centroids for all faces in the mesh + * + * \param [in] exec_mode defines where loops should be executed + * \return true if face calculations do not encounter errors or warnings + * + * This routine accounts for warped faces by computing an average normal. + */ + bool computeFaceData( ExecutionMode exec_mode ); + /*! - * \brief Computes average nodal normals for use with mortar methods - * - * \note this routine computes average nodal normals for all nodes in the mesh. - * - * \param [in] dim Dimension of the problem - */ + * \brief Computes average nodal normals for use with mortar methods + * + * \note this routine computes average nodal normals for all nodes in the mesh. + * + * \param [in] dim Dimension of the problem + */ void computeNodalNormals( int const dim ); - + /*! - * - * \brief compute the surface edge/segment length - * - * \param [in] edgeId edge id - * - * \return edge length - * - */ + * + * \brief compute the surface edge/segment length + * + * \param [in] edgeId edge id + * + * \return edge length + * + */ RealT computeEdgeLength( int edgeId ); /// Prints information associated with this mesh to \a os - void print(std::ostream& os) const; + void print( std::ostream& os ) const; -}; // end class MeshData +}; // end class MeshData //------------------------------------------------------------------------------ template MultiArrayView MeshData::createNodalVector( T* x, T* y, T* z ) const { - MultiArrayView host_nodal_vector(m_dim, m_dim); - host_nodal_vector[0] = ArrayViewT(x, m_num_nodes); - host_nodal_vector[1] = ArrayViewT(y, m_num_nodes); - if (m_dim == 3) - { - host_nodal_vector[2] = ArrayViewT(z, m_num_nodes); + MultiArrayView host_nodal_vector( m_dim, m_dim ); + host_nodal_vector[0] = ArrayViewT( x, m_num_nodes ); + host_nodal_vector[1] = ArrayViewT( y, m_num_nodes ); + if ( m_dim == 3 ) { + host_nodal_vector[2] = ArrayViewT( z, m_num_nodes ); } - return MultiArrayView(host_nodal_vector, m_allocator_id); + return MultiArrayView( host_nodal_vector, m_allocator_id ); } using MeshManager = DataManager; -} // end namespace tribol +} // end namespace tribol /// \a ostream operator to print a \a MeshData instance to \a os -std::ostream& operator<<(std::ostream& os, const tribol::MeshData& md); +std::ostream& operator<<( std::ostream& os, const tribol::MeshData& md ); #endif /* SRC_MESH_MESHDATA_HPP_ */ diff --git a/src/tribol/mesh/MethodCouplingData.cpp b/src/tribol/mesh/MethodCouplingData.cpp index 94862555..ddb15a8c 100644 --- a/src/tribol/mesh/MethodCouplingData.cpp +++ b/src/tribol/mesh/MethodCouplingData.cpp @@ -11,227 +11,190 @@ // Axom includes #include "axom/slic.hpp" -#include +#include #include #include #include -namespace tribol -{ +namespace tribol { //////////////////////////////////////////////// // // // Routines for the SurfaceContactElem struct // // // //////////////////////////////////////////////// -void SurfaceContactElem::allocateMortarWts( ) +void SurfaceContactElem::allocateMortarWts() { - // We store wts, n_aa and n_ab, which are nonmortar/nonmortar and nonmortar/mortar - // products of shape functions - this->numWts = 2 * this->numFaceVert * this->numFaceVert; - if (this->mortarWts != nullptr) - { - delete [] this->mortarWts; - this->mortarWts = new RealT [ this->numWts ]; - } - else - { - this->mortarWts = new RealT [ this->numWts ]; - } - - this->initializeMortarWts(); + // We store wts, n_aa and n_ab, which are nonmortar/nonmortar and nonmortar/mortar + // products of shape functions + this->numWts = 2 * this->numFaceVert * this->numFaceVert; + if ( this->mortarWts != nullptr ) { + delete[] this->mortarWts; + this->mortarWts = new RealT[this->numWts]; + } else { + this->mortarWts = new RealT[this->numWts]; + } + + this->initializeMortarWts(); } //------------------------------------------------------------------------------ -void SurfaceContactElem::initializeMortarWts( ) +void SurfaceContactElem::initializeMortarWts() { - for (int i=0; inumWts; ++i) - { - mortarWts[i] = 0.; - } + for ( int i = 0; i < this->numWts; ++i ) { + mortarWts[i] = 0.; + } } //------------------------------------------------------------------------------ void SurfaceContactElem::allocateBlockJ( EnforcementMethod enf ) { - // number of element displacement degrees of freedom - int nPrimal = this->dim * this->numFaceVert; - for (int i{}; i < 2; ++i) - { - for (int j{}; j < 2; ++j) - { - this->blockJ(i, j) = DeviceArray2D(nPrimal, nPrimal); - this->blockJ(i, j).fill(0.0); - } - } - - if (enf == LAGRANGE_MULTIPLIER) - { - // number of element Lagrange multiplier degrees of freedom - int nDual = this->numFaceVert; - for (int i{}; i < 2; ++i) - { - this->blockJ(i, 2) = DeviceArray2D(nPrimal, nDual); - this->blockJ(i, 2).fill(0.0); - // transpose - this->blockJ(2, i) = DeviceArray2D(nDual, nPrimal); - this->blockJ(2, i).fill(0.0); - } - this->blockJ(2, 2) = DeviceArray2D(nDual, nDual); - this->blockJ(2, 2).fill(0.0); - } + // number of element displacement degrees of freedom + int nPrimal = this->dim * this->numFaceVert; + for ( int i{}; i < 2; ++i ) { + for ( int j{}; j < 2; ++j ) { + this->blockJ( i, j ) = DeviceArray2D( nPrimal, nPrimal ); + this->blockJ( i, j ).fill( 0.0 ); + } + } + + if ( enf == LAGRANGE_MULTIPLIER ) { + // number of element Lagrange multiplier degrees of freedom + int nDual = this->numFaceVert; + for ( int i{}; i < 2; ++i ) { + this->blockJ( i, 2 ) = DeviceArray2D( nPrimal, nDual ); + this->blockJ( i, 2 ).fill( 0.0 ); + // transpose + this->blockJ( 2, i ) = DeviceArray2D( nDual, nPrimal ); + this->blockJ( 2, i ).fill( 0.0 ); + } + this->blockJ( 2, 2 ) = DeviceArray2D( nDual, nDual ); + this->blockJ( 2, 2 ).fill( 0.0 ); + } } //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE void SurfaceContactElem::deallocateElem( ) +TRIBOL_HOST_DEVICE void SurfaceContactElem::deallocateElem() { - if (this->mortarWts != nullptr) - { - delete [] this->mortarWts; - this->mortarWts = nullptr; - } + if ( this->mortarWts != nullptr ) { + delete[] this->mortarWts; + this->mortarWts = nullptr; + } } //------------------------------------------------------------------------------ RealT SurfaceContactElem::getMortarNonmortarWt( const int a, const int b ) { - // note: the mortar wts are stored in a stacked array with nonmortar-nonmortar - // wts followed by mortar-nonmortar weights in mortar-nonmortar ordering + // note: the mortar wts are stored in a stacked array with nonmortar-nonmortar + // wts followed by mortar-nonmortar weights in mortar-nonmortar ordering - int mortarNonmortarId = this->numFaceVert * this->numFaceVert + - this->numFaceVert * a + b; + int mortarNonmortarId = this->numFaceVert * this->numFaceVert + this->numFaceVert * a + b; - RealT wt = this->mortarWts[ mortarNonmortarId ]; - return wt; + RealT wt = this->mortarWts[mortarNonmortarId]; + return wt; } //------------------------------------------------------------------------------ RealT SurfaceContactElem::getNonmortarMortarWt( const int a, const int b ) { - // note: the mortar wts are stored in a stacked array with nonmortar-nonmortar - // wts followed by mortar-nonmortar weights in mortar-nonmortar ordering. - // Therefore, the nonmortarMortar id is the transpose of how it is - // stored + // note: the mortar wts are stored in a stacked array with nonmortar-nonmortar + // wts followed by mortar-nonmortar weights in mortar-nonmortar ordering. + // Therefore, the nonmortarMortar id is the transpose of how it is + // stored - int nonmortarMortarId = this->numFaceVert * this->numFaceVert + - this->numFaceVert * b + a; + int nonmortarMortarId = this->numFaceVert * this->numFaceVert + this->numFaceVert * b + a; - RealT wt = this->mortarWts[ nonmortarMortarId ]; - return wt; + RealT wt = this->mortarWts[nonmortarMortarId]; + return wt; } //------------------------------------------------------------------------------ RealT SurfaceContactElem::getNonmortarNonmortarWt( const int a, const int b ) { - // note: the mortar wts are stored in a stacked array with nonmortar-nonmortar - // wts followed by mortar-nonmortar weights in mortar-nonmortar ordering + // note: the mortar wts are stored in a stacked array with nonmortar-nonmortar + // wts followed by mortar-nonmortar weights in mortar-nonmortar ordering - int nonmortarNonmortarId = this->numFaceVert * a + b; + int nonmortarNonmortarId = this->numFaceVert * a + b; - RealT wt = this->mortarWts[ nonmortarNonmortarId ]; - return wt; + RealT wt = this->mortarWts[nonmortarNonmortarId]; + return wt; } //------------------------------------------------------------------------------ -int SurfaceContactElem::getJacobianIndex( JacBlock block, - const int a, - const int b ) const +int SurfaceContactElem::getJacobianIndex( JacBlock block, const int a, const int b ) const { - if (block == JguBlock) - { - return a + this->numFaceVert * b; - } + if ( block == JguBlock ) { + return a + this->numFaceVert * b; + } - if (block == JrpBlock) - { - return a + this->dim * this->numFaceVert * b; - } + if ( block == JrpBlock ) { + return a + this->dim * this->numFaceVert * b; + } - return -1; + return -1; } //------------------------------------------------------------------------------ int SurfaceContactElem::getJacobianDimOffset( JacBlock block ) const { - if (block == JguBlock) - { - return this->numFaceVert * this->numFaceVert; - } + if ( block == JguBlock ) { + return this->numFaceVert * this->numFaceVert; + } - if (block == JrpBlock) - { - return this->numFaceVert; - } + if ( block == JrpBlock ) { + return this->numFaceVert; + } - return -1; + return -1; } //------------------------------------------------------------------------------ MethodData::MethodData() -: m_blockJElemIds ( - static_cast(BlockSpace::NUM_BLOCK_SPACES), - static_cast(BlockSpace::NUM_BLOCK_SPACES) - ) , - m_blockJ ( - static_cast(BlockSpace::NUM_BLOCK_SPACES), - static_cast(BlockSpace::NUM_BLOCK_SPACES) - ) + : m_blockJElemIds( static_cast( BlockSpace::NUM_BLOCK_SPACES ), + static_cast( BlockSpace::NUM_BLOCK_SPACES ) ), + m_blockJ( static_cast( BlockSpace::NUM_BLOCK_SPACES ), + static_cast( BlockSpace::NUM_BLOCK_SPACES ) ) { - m_blockJ.shrink(); + m_blockJ.shrink(); } //------------------------------------------------------------------------------ -void MethodData::reserveBlockJ( - ArrayT&& blockJSpaces, - int nPairs -) +void MethodData::reserveBlockJ( ArrayT&& blockJSpaces, int nPairs ) { - IndexT pairFraction = static_cast(0.5 * nPairs); - - m_blockJSpaces = std::move(blockJSpaces); - m_blockJElemIds.fill(ArrayT(0, 0)); - for (auto blockJSpace : m_blockJSpaces) - { - m_blockJElemIds[static_cast(blockJSpace)].reserve(pairFraction); - } - m_blockJ.fill(ArrayT(0, 0)); - for (auto blockJSpaceI : m_blockJSpaces) - { - for (auto blockJSpaceJ : m_blockJSpaces) - { - m_blockJ( - static_cast(blockJSpaceI), - static_cast(blockJSpaceJ) - ).reserve(pairFraction); - } - } + IndexT pairFraction = static_cast( 0.5 * nPairs ); + + m_blockJSpaces = std::move( blockJSpaces ); + m_blockJElemIds.fill( ArrayT( 0, 0 ) ); + for ( auto blockJSpace : m_blockJSpaces ) { + m_blockJElemIds[static_cast( blockJSpace )].reserve( pairFraction ); + } + m_blockJ.fill( ArrayT( 0, 0 ) ); + for ( auto blockJSpaceI : m_blockJSpaces ) { + for ( auto blockJSpaceJ : m_blockJSpaces ) { + m_blockJ( static_cast( blockJSpaceI ), static_cast( blockJSpaceJ ) ).reserve( pairFraction ); + } + } } //------------------------------------------------------------------------------ -void MethodData::storeElemBlockJ( - ArrayT&& blockJElemIds, - const StackArray, 9>& blockJ -) +void MethodData::storeElemBlockJ( ArrayT&& blockJElemIds, const StackArray, 9>& blockJ ) { - SLIC_ASSERT_MSG(blockJElemIds.size() == getNSpaces(), - "Number of element ID vectors does not match the number of Jacobian spaces."); - for (IndexT i{}; i < getNSpaces(); ++i) - { - IndexT blockIdxI = static_cast(m_blockJSpaces[i]); - m_blockJElemIds[blockIdxI].push_back(blockJElemIds[i]); - for (IndexT j{}; j < getNSpaces(); ++j) - { - IndexT blockIdxJ = static_cast(m_blockJSpaces[j]); - // convert to mfem::DenseMatrix - auto& block = blockJ(i, j); - // this DenseMatrix is a "view" of block - mfem::DenseMatrix block_densemat(block.data(), block.height(), block.width()); - // deep copy should happen here - m_blockJ(blockIdxI, blockIdxJ).push_back(block_densemat); - } - } -} - + SLIC_ASSERT_MSG( blockJElemIds.size() == getNSpaces(), + "Number of element ID vectors does not match the number of Jacobian spaces." ); + for ( IndexT i{}; i < getNSpaces(); ++i ) { + IndexT blockIdxI = static_cast( m_blockJSpaces[i] ); + m_blockJElemIds[blockIdxI].push_back( blockJElemIds[i] ); + for ( IndexT j{}; j < getNSpaces(); ++j ) { + IndexT blockIdxJ = static_cast( m_blockJSpaces[j] ); + // convert to mfem::DenseMatrix + auto& block = blockJ( i, j ); + // this DenseMatrix is a "view" of block + mfem::DenseMatrix block_densemat( block.data(), block.height(), block.width() ); + // deep copy should happen here + m_blockJ( blockIdxI, blockIdxJ ).push_back( block_densemat ); + } + } +} /////////////////////////////////////// // // @@ -243,261 +206,243 @@ void MethodData::storeElemBlockJ( MortarData::MortarData() { // set null pointers - this->m_smat = nullptr; + this->m_smat = nullptr; } //------------------------------------------------------------------------------ // Destructor MortarData::~MortarData() { - if (this->m_smat != nullptr) - { - delete this->m_smat; - this->m_smat = nullptr; + if ( this->m_smat != nullptr ) { + delete this->m_smat; + this->m_smat = nullptr; } } //------------------------------------------------------------------------------ -void MortarData::assembleJacobian( SurfaceContactElem & elem, SparseMode s_mode ) const +void MortarData::assembleJacobian( SurfaceContactElem& elem, SparseMode s_mode ) const { - (void) s_mode; // will be used at a later point - // grab the two meshes in this coupling scheme - auto& mortarMesh = *elem.m_mesh1; - auto& nonmortarMesh = *elem.m_mesh2; - - // compute the pressure dof offset. - // Recall that the "equilibrium" - // block is the problem_dimension x total_number_of_coupling_scheme_nodes, - // which is the sum of mortar and nonmortar mesh nodes registered - // by the host code. The node ids between the two are assumed to - // be unique and contiguous. There is space to store a pressure - // dof for ALL nonmortar AND mortar nodes (done for ease of indexing - // using connectivity arrays registered by the host code), but we - // will pass back an array indicating the active pressure dofs for - // both mortar and nonmortar meshes in the case of LM implementations - - // TODO: fix tests where we now assume the number of nodes on the - // registered nonmortar and mortar mesh is ONLY the number of nonmortar - // nodes and mortar nodes, respectively. - int numNodes = this->m_numTotalNodes; - int presOffset = elem.dim * numNodes; - - // loop over contact element nodes and assemble the four block - // contributions stored in arrays on the SurfaceContactElem struct - - // loop over face nodes "a" (general loop, don't distinguish mortar/nonmortar - // as that changes for Jrp and Jgu contributions) - for (int a = 0; am_smat->Add( i, j, elem.blockJ( - static_cast(BlockSpace::MORTAR), - static_cast(BlockSpace::LAGRANGE_MULTIPLIER) - )[ localId ]); - this->m_smat->Add( i+1, j, elem.blockJ( - static_cast(BlockSpace::MORTAR), - static_cast(BlockSpace::LAGRANGE_MULTIPLIER) - )[ localId + dimOffset ]); - this->m_smat->Add( i+2, j, elem.blockJ( - static_cast(BlockSpace::MORTAR), - static_cast(BlockSpace::LAGRANGE_MULTIPLIER) - )[ localId + 2*dimOffset ]); // assume 3D for now - - // Nonmortar-Lagrange Multiplier block (1, 2) - i = elem.dim * nonmortarNodeIdA; // nonmortar row contributions - this->m_smat->Add( i, j, elem.blockJ( - static_cast(BlockSpace::NONMORTAR), - static_cast(BlockSpace::LAGRANGE_MULTIPLIER) - )[ localId ]); - this->m_smat->Add( i+1, j, elem.blockJ( - static_cast(BlockSpace::NONMORTAR), - static_cast(BlockSpace::LAGRANGE_MULTIPLIER) - )[ localId + dimOffset ]); - this->m_smat->Add( i+2, j, elem.blockJ( - static_cast(BlockSpace::NONMORTAR), - static_cast(BlockSpace::LAGRANGE_MULTIPLIER) - )[ localId + 2*dimOffset ]); // assume 3D for now - - //////////////////////////////////////////////////////////////// - // Assemble Jgu contributions (lower-left off-diagonal block) // - //////////////////////////////////////////////////////////////// - - // note b and a are swapped. index a loops over displacement DOFs (i.e. the - // columns of Jgu) and index b loops over pressure DOFs (rows of Jgu) - localId = elem.getJacobianIndex(SurfaceContactElem::JguBlock, b, a ); - - i = presOffset + nonmortarNodeIdB; // nonmortar rows always - j = elem.dim * mortarNodeIdA; // mortar column contributions - dimOffset = elem.getJacobianDimOffset(SurfaceContactElem::JguBlock); - - // Lagrange-multiplier-mortar block (2, 0) - this->m_smat->Add( i, j, elem.blockJ( - static_cast(BlockSpace::LAGRANGE_MULTIPLIER), - static_cast(BlockSpace::MORTAR) - )[ localId ]); - this->m_smat->Add( i, j+1, elem.blockJ( - static_cast(BlockSpace::LAGRANGE_MULTIPLIER), - static_cast(BlockSpace::MORTAR) - )[ localId + dimOffset ]); - this->m_smat->Add( i, j+2, elem.blockJ( - static_cast(BlockSpace::LAGRANGE_MULTIPLIER), - static_cast(BlockSpace::MORTAR) - )[ localId + 2*dimOffset ]); // assume 3D for now - - // Lagrange multiplier-nonmortar block (2, 1) - j = elem.dim * nonmortarNodeIdA; // nonmortar column contributions - this->m_smat->Add( i, j, elem.blockJ( - static_cast(BlockSpace::LAGRANGE_MULTIPLIER), - static_cast(BlockSpace::NONMORTAR) - )[ localId ]); - this->m_smat->Add( i, j+1, elem.blockJ( - static_cast(BlockSpace::LAGRANGE_MULTIPLIER), - static_cast(BlockSpace::NONMORTAR) - )[ localId + dimOffset ]); - this->m_smat->Add( i, j+2, elem.blockJ( - static_cast(BlockSpace::LAGRANGE_MULTIPLIER), - static_cast(BlockSpace::NONMORTAR) - )[ localId + 2*dimOffset ]); // assume 3D for now - - ////////////////////////////////////////////////// - // Assemble Jru contributions (matrix 11-block) // - ////////////////////////////////////////////////// - - // TODO implement the assembly procedure if we want to add a consistent - // Jacobian - - ////////////////////////////////////////////////// - // Assemble Jgp contributions (matrix 22-block) // - ////////////////////////////////////////////////// - - // TODO implement the assembly procedure if we want to add a consistent - // Jacobian - - } // end loop over b nodes - } // end loop over a nodes - - return; -} // end of MortarData::assembleJacobian() + (void)s_mode; // will be used at a later point + // grab the two meshes in this coupling scheme + auto& mortarMesh = *elem.m_mesh1; + auto& nonmortarMesh = *elem.m_mesh2; + + // compute the pressure dof offset. + // Recall that the "equilibrium" + // block is the problem_dimension x total_number_of_coupling_scheme_nodes, + // which is the sum of mortar and nonmortar mesh nodes registered + // by the host code. The node ids between the two are assumed to + // be unique and contiguous. There is space to store a pressure + // dof for ALL nonmortar AND mortar nodes (done for ease of indexing + // using connectivity arrays registered by the host code), but we + // will pass back an array indicating the active pressure dofs for + // both mortar and nonmortar meshes in the case of LM implementations + + // TODO: fix tests where we now assume the number of nodes on the + // registered nonmortar and mortar mesh is ONLY the number of nonmortar + // nodes and mortar nodes, respectively. + int numNodes = this->m_numTotalNodes; + int presOffset = elem.dim * numNodes; + + // loop over contact element nodes and assemble the four block + // contributions stored in arrays on the SurfaceContactElem struct + + // loop over face nodes "a" (general loop, don't distinguish mortar/nonmortar + // as that changes for Jrp and Jgu contributions) + for ( int a = 0; a < elem.numFaceVert; ++a ) { + // get mortar and nonmortar node ids from connectivity + int mortarNodeIdA = mortarMesh.getGlobalNodeId( elem.faceId1, a ); + int nonmortarNodeIdA = nonmortarMesh.getGlobalNodeId( elem.faceId2, a ); + + // loop over face nodes "b" (general loop, don't distinguish mortar/nonmortar + // as that changes for Jrp and Jgu contributions) + for ( int b = 0; b < elem.numFaceVert; ++b ) { + // get nonmortar node id from connectivity + int nonmortarNodeIdB = nonmortarMesh.getGlobalNodeId( elem.faceId2, b ); + + // We don't exclude nonmortar nodes that are in separation. NOTE: Per + // testing, we include ALL nonmortar contributions for faces that + // have positive areas of overlap regardless of gap evaluation. Contact + // activity is determined from gaps AND pressure solution per KKT constraint + // equations + + ///////////////////////////////////////////////////////////////// + // Assemble Jrp contributions (upper-right off-diagonal block) // + ///////////////////////////////////////////////////////////////// + + // get local ids into contact element Jacobian arrays + int localId = elem.getJacobianIndex( SurfaceContactElem::JrpBlock, a, b ); + + int i = elem.dim * mortarNodeIdA; // mortar row contributions + int j = presOffset + nonmortarNodeIdB; // nonmortar column contributions always + int dimOffset = elem.getJacobianDimOffset( SurfaceContactElem::JrpBlock ); + + // Add() will "set" if a nonzero entry hasn't been + // introduced at the (i,j) element + // Mortar-Lagrange multiplier block (0, 2) + this->m_smat->Add( i, j, + elem.blockJ( static_cast( BlockSpace::MORTAR ), + static_cast( BlockSpace::LAGRANGE_MULTIPLIER ) )[localId] ); + this->m_smat->Add( i + 1, j, + elem.blockJ( static_cast( BlockSpace::MORTAR ), + static_cast( BlockSpace::LAGRANGE_MULTIPLIER ) )[localId + dimOffset] ); + this->m_smat->Add( + i + 2, j, + elem.blockJ( + static_cast( BlockSpace::MORTAR ), + static_cast( BlockSpace::LAGRANGE_MULTIPLIER ) )[localId + 2 * dimOffset] ); // assume 3D for now + + // Nonmortar-Lagrange Multiplier block (1, 2) + i = elem.dim * nonmortarNodeIdA; // nonmortar row contributions + this->m_smat->Add( i, j, + elem.blockJ( static_cast( BlockSpace::NONMORTAR ), + static_cast( BlockSpace::LAGRANGE_MULTIPLIER ) )[localId] ); + this->m_smat->Add( i + 1, j, + elem.blockJ( static_cast( BlockSpace::NONMORTAR ), + static_cast( BlockSpace::LAGRANGE_MULTIPLIER ) )[localId + dimOffset] ); + this->m_smat->Add( + i + 2, j, + elem.blockJ( + static_cast( BlockSpace::NONMORTAR ), + static_cast( BlockSpace::LAGRANGE_MULTIPLIER ) )[localId + 2 * dimOffset] ); // assume 3D for now + + //////////////////////////////////////////////////////////////// + // Assemble Jgu contributions (lower-left off-diagonal block) // + //////////////////////////////////////////////////////////////// + + // note b and a are swapped. index a loops over displacement DOFs (i.e. the + // columns of Jgu) and index b loops over pressure DOFs (rows of Jgu) + localId = elem.getJacobianIndex( SurfaceContactElem::JguBlock, b, a ); + + i = presOffset + nonmortarNodeIdB; // nonmortar rows always + j = elem.dim * mortarNodeIdA; // mortar column contributions + dimOffset = elem.getJacobianDimOffset( SurfaceContactElem::JguBlock ); + + // Lagrange-multiplier-mortar block (2, 0) + this->m_smat->Add( i, j, + elem.blockJ( static_cast( BlockSpace::LAGRANGE_MULTIPLIER ), + static_cast( BlockSpace::MORTAR ) )[localId] ); + this->m_smat->Add( i, j + 1, + elem.blockJ( static_cast( BlockSpace::LAGRANGE_MULTIPLIER ), + static_cast( BlockSpace::MORTAR ) )[localId + dimOffset] ); + this->m_smat->Add( + i, j + 2, + elem.blockJ( static_cast( BlockSpace::LAGRANGE_MULTIPLIER ), + static_cast( BlockSpace::MORTAR ) )[localId + 2 * dimOffset] ); // assume 3D for now + + // Lagrange multiplier-nonmortar block (2, 1) + j = elem.dim * nonmortarNodeIdA; // nonmortar column contributions + this->m_smat->Add( i, j, + elem.blockJ( static_cast( BlockSpace::LAGRANGE_MULTIPLIER ), + static_cast( BlockSpace::NONMORTAR ) )[localId] ); + this->m_smat->Add( i, j + 1, + elem.blockJ( static_cast( BlockSpace::LAGRANGE_MULTIPLIER ), + static_cast( BlockSpace::NONMORTAR ) )[localId + dimOffset] ); + this->m_smat->Add( + i, j + 2, + elem.blockJ( static_cast( BlockSpace::LAGRANGE_MULTIPLIER ), + static_cast( BlockSpace::NONMORTAR ) )[localId + 2 * dimOffset] ); // assume 3D for now + + ////////////////////////////////////////////////// + // Assemble Jru contributions (matrix 11-block) // + ////////////////////////////////////////////////// + + // TODO implement the assembly procedure if we want to add a consistent + // Jacobian + + ////////////////////////////////////////////////// + // Assemble Jgp contributions (matrix 22-block) // + ////////////////////////////////////////////////// + + // TODO implement the assembly procedure if we want to add a consistent + // Jacobian + + } // end loop over b nodes + } // end loop over a nodes + + return; +} // end of MortarData::assembleJacobian() //------------------------------------------------------------------------------ -void MortarData::assembleMortarWts( SurfaceContactElem & elem, SparseMode s_mode ) const +void MortarData::assembleMortarWts( SurfaceContactElem& elem, SparseMode s_mode ) const { - // Note: check for active gaps here - // grab the two meshes in this coupling scheme - auto& mortarMesh = *elem.m_mesh1; - auto& nonmortarMesh = *elem.m_mesh2; - - // Note: The node ids between the two are assumed to - // be unique and contiguous using the int ids in the - // mortar and nonmortar mesh connectivity arrays with the number - // of nodes specified for each mesh being the number of mortar - // and the number of nonmortar nodes for each mesh, respectively. - //int numNodes = this->m_numTotalNodes; - - // loop over contact element nodes and assemble the local weight - // contributions - - // loop over nonmortar ROWS - for (int a = 0; am_smat->Add( nonmortarNodeIdA, mortarNodeIdB, elem.getNonmortarMortarWt( a, b ) ); - this->m_smat->Add( nonmortarNodeIdA, nonmortarNodeIdB, elem.getNonmortarNonmortarWt( a, b ) ); - } - else if (s_mode == SparseMode::MFEM_INDEX_SET) - { - SLIC_ERROR("MortarData::assembleMortarWts() MFEM_INDEX_SET " << - "not implemented."); - } - - } // end loop over b nodes (columns) - } // end loop over a nodes (rows) - - return; -} // end of MortarData::assembleMortarWts() + // Note: check for active gaps here + // grab the two meshes in this coupling scheme + auto& mortarMesh = *elem.m_mesh1; + auto& nonmortarMesh = *elem.m_mesh2; + + // Note: The node ids between the two are assumed to + // be unique and contiguous using the int ids in the + // mortar and nonmortar mesh connectivity arrays with the number + // of nodes specified for each mesh being the number of mortar + // and the number of nonmortar nodes for each mesh, respectively. + // int numNodes = this->m_numTotalNodes; + + // loop over contact element nodes and assemble the local weight + // contributions + + // loop over nonmortar ROWS + for ( int a = 0; a < elem.numFaceVert; ++a ) { + // weights will be mortar/nonmortar (a,b) or nonmortar/nonmortar (a,b) + + // get nonmortar node ROW id from connectivity + int nonmortarNodeIdA = nonmortarMesh.getGlobalNodeId( elem.faceId2, a ); + + // We include ALL nonmortar nodes, even if the nodal gap is in separation. + // NOTE: Per testing, we include ALL nonmortar node + // contributions for faces that have positive areas of overlap per the + // geometric filtering. Contact activity is not determined per + // gap interrogation, but rather gap AND pressure solution. For MORTAR_WEIGHTS, + // we just pass back all nonmortar node weights. + + // loop over mortar and nonmortar columns + for ( int b = 0; b < elem.numFaceVert; ++b ) { + // get mortar COL id based on connectivity array + int mortarNodeIdB = mortarMesh.getGlobalNodeId( elem.faceId1, b ); + + // get nonmortar COL id based on connectivity array + int nonmortarNodeIdB = nonmortarMesh.getGlobalNodeId( elem.faceId2, b ); + + // Add() will "set" if a nonzero entry hasn't been + // introduced at the (i,j) element + if ( s_mode == SparseMode::MFEM_LINKED_LIST ) { + // note: if we are here, the mfem sparse object, m_smat, has been allocated + this->m_smat->Add( nonmortarNodeIdA, mortarNodeIdB, elem.getNonmortarMortarWt( a, b ) ); + this->m_smat->Add( nonmortarNodeIdA, nonmortarNodeIdB, elem.getNonmortarNonmortarWt( a, b ) ); + } else if ( s_mode == SparseMode::MFEM_INDEX_SET ) { + SLIC_ERROR( "MortarData::assembleMortarWts() MFEM_INDEX_SET " + << "not implemented." ); + } + + } // end loop over b nodes (columns) + } // end loop over a nodes (rows) + + return; +} // end of MortarData::assembleMortarWts() //------------------------------------------------------------------------------ -void MortarData::getCSRArrays( int** I, int** J, RealT** vals, - int* n_offsets, int* n_nonzero) +void MortarData::getCSRArrays( int** I, int** J, RealT** vals, int* n_offsets, int* n_nonzero ) { - if (this->m_smat == nullptr) - { - SLIC_ERROR("getCSRArrays: method data get routine has m_smat == nullptr."); - } - - this->m_smat->Finalize(); - *I = this->m_smat->GetI(); - *J = this->m_smat->GetJ(); - *vals = this->m_smat->GetData(); - - // Set the sizes - if(n_offsets != nullptr) - { - *n_offsets = this->m_smat->GetMemoryI().Capacity(); - } - if(n_nonzero != nullptr) - { - *n_nonzero = this->m_smat->GetMemoryJ().Capacity(); - } - - return; + if ( this->m_smat == nullptr ) { + SLIC_ERROR( "getCSRArrays: method data get routine has m_smat == nullptr." ); + } + + this->m_smat->Finalize(); + *I = this->m_smat->GetI(); + *J = this->m_smat->GetJ(); + *vals = this->m_smat->GetData(); + + // Set the sizes + if ( n_offsets != nullptr ) { + *n_offsets = this->m_smat->GetMemoryI().Capacity(); + } + if ( n_nonzero != nullptr ) { + *n_nonzero = this->m_smat->GetMemoryJ().Capacity(); + } + + return; } //------------------------------------------------------------------------------ -} // end of namespace tribol +} // end of namespace tribol diff --git a/src/tribol/mesh/MethodCouplingData.hpp b/src/tribol/mesh/MethodCouplingData.hpp index 4613e488..6067edab 100644 --- a/src/tribol/mesh/MethodCouplingData.hpp +++ b/src/tribol/mesh/MethodCouplingData.hpp @@ -18,90 +18,90 @@ // MFEM includes #include "mfem.hpp" -namespace tribol -{ +namespace tribol { // Forward Declarations class InterfacePairs; //------------------------------------------------------------------------------ /*! - * \brief Struct to hold data associated with a surface + * \brief Struct to hold data associated with a surface * contact element */ -struct SurfaceContactElem -{ - enum JacBlock { JguBlock, JrpBlock }; - - /// Default constructor - SurfaceContactElem( ); - - /// Overloaded Constructor - TRIBOL_HOST_DEVICE SurfaceContactElem( int dimension, ///< [in] Dimension of the problem - RealT * x1, ///< [in] Vertex coordinates of first face - RealT * x2, ///< [in] Vertex coordinates of second face - RealT * xOverlap, ///< [in] Vertex coordinates of overlap - int nFV, ///< [in] Number of face vertices - int nPV, ///< [in] Number of overlap vertices - const MeshData::Viewer* mesh1, ///< [in] View of mesh 1 - const MeshData::Viewer* mesh2, ///< [in] View of mesh 2 - int fId1, ///< [in] Id for face 1 - int fId2 ///< [in] Id for face 2 - ) - : dim(dimension) - , m_mesh1(mesh1) - , m_mesh2(mesh2) - , faceId1(fId1) - , faceId2(fId2) - , faceCoords1(x1) - , faceCoords2(x2) - , faceNormal1(nullptr) - , faceNormal2(nullptr) - , overlapCoords(xOverlap) - , overlapNormal(nullptr) - , numFaceVert(nFV) - , numPolyVert(nPV) - , overlapArea(0.) - , mortarWts(nullptr) - , numWts(0) - , numActiveGaps(0) - , blockJ(3) - - { } - - /// Destructor - TRIBOL_HOST_DEVICE ~SurfaceContactElem() - { - this->deallocateElem(); - } - - int dim; ///< Problem dimension - const MeshData::Viewer* m_mesh1; ///< Mesh view for face 1 (mortar) - const MeshData::Viewer* m_mesh2; ///< Mesh view for face 2 (nonmortar) - int faceId1; ///< Face Id for face 1 (mortar) - int faceId2; ///< Face Id for face 2 (nonmortar) - RealT * faceCoords1; ///< Coordinates of face 1 in 3D - RealT * faceCoords2; ///< Coordinates of face 2 in 3D - RealT * faceNormal1; ///< Components of face 1 normal - RealT * faceNormal2; ///< Components of face 2 normal - RealT * overlapCoords; ///< Coordinates of overlap vertices in 3D - RealT * overlapNormal; ///< Components of overlap normal - int numFaceVert; ///< Number of face vertices/nodes - int numPolyVert; ///< Number of overlap vertices - RealT overlapArea; ///< Area of polygonal overlap - - RealT * mortarWts; ///< Stacked array of mortar wts for mortar methods - int numWts; ///< Number of mortar weights - - int numActiveGaps; ///< Number of local face-pair active gaps - - StackArray, 9> blockJ; ///< Block element Jacobian contributions - - /// routine to allocate space to store mortar weights - void allocateMortarWts(); - - /// routine to initialize mortar weights - void initializeMortarWts( ); +struct SurfaceContactElem { + enum JacBlock + { + JguBlock, + JrpBlock + }; + + /// Default constructor + SurfaceContactElem(); + + /// Overloaded Constructor + TRIBOL_HOST_DEVICE SurfaceContactElem( int dimension, ///< [in] Dimension of the problem + RealT* x1, ///< [in] Vertex coordinates of first face + RealT* x2, ///< [in] Vertex coordinates of second face + RealT* xOverlap, ///< [in] Vertex coordinates of overlap + int nFV, ///< [in] Number of face vertices + int nPV, ///< [in] Number of overlap vertices + const MeshData::Viewer* mesh1, ///< [in] View of mesh 1 + const MeshData::Viewer* mesh2, ///< [in] View of mesh 2 + int fId1, ///< [in] Id for face 1 + int fId2 ///< [in] Id for face 2 + ) + : dim( dimension ), + m_mesh1( mesh1 ), + m_mesh2( mesh2 ), + faceId1( fId1 ), + faceId2( fId2 ), + faceCoords1( x1 ), + faceCoords2( x2 ), + faceNormal1( nullptr ), + faceNormal2( nullptr ), + overlapCoords( xOverlap ), + overlapNormal( nullptr ), + numFaceVert( nFV ), + numPolyVert( nPV ), + overlapArea( 0. ), + mortarWts( nullptr ), + numWts( 0 ), + numActiveGaps( 0 ), + blockJ( 3 ) + + { + } + + /// Destructor + TRIBOL_HOST_DEVICE ~SurfaceContactElem() { this->deallocateElem(); } + + int dim; ///< Problem dimension + const MeshData::Viewer* m_mesh1; ///< Mesh view for face 1 (mortar) + const MeshData::Viewer* m_mesh2; ///< Mesh view for face 2 (nonmortar) + int faceId1; ///< Face Id for face 1 (mortar) + int faceId2; ///< Face Id for face 2 (nonmortar) + RealT* faceCoords1; ///< Coordinates of face 1 in 3D + RealT* faceCoords2; ///< Coordinates of face 2 in 3D + RealT* faceNormal1; ///< Components of face 1 normal + RealT* faceNormal2; ///< Components of face 2 normal + RealT* overlapCoords; ///< Coordinates of overlap vertices in 3D + RealT* overlapNormal; ///< Components of overlap normal + int numFaceVert; ///< Number of face vertices/nodes + int numPolyVert; ///< Number of overlap vertices + RealT overlapArea; ///< Area of polygonal overlap + + RealT* mortarWts; ///< Stacked array of mortar wts for mortar methods + int numWts; ///< Number of mortar weights + + int numActiveGaps; ///< Number of local face-pair active gaps + + StackArray, 9> blockJ; ///< Block element Jacobian contributions + + /// routine to allocate space to store mortar weights + void allocateMortarWts(); + + /// routine to initialize mortar weights + void initializeMortarWts(); /*! * \brief routine to return mortar-nonmortar mortar weight @@ -110,7 +110,7 @@ struct SurfaceContactElem * \param [in] b NONMORTAR node id * */ - RealT getMortarNonmortarWt( const int a, const int b ); + RealT getMortarNonmortarWt( const int a, const int b ); /*! * \brief routine to return nonmortar-mortar mortar weights @@ -119,7 +119,7 @@ struct SurfaceContactElem * \param [in] b MORTAR node id * */ - RealT getNonmortarMortarWt( const int a, const int b ); + RealT getNonmortarMortarWt( const int a, const int b ); /*! * \brief routine to return nonmortar-nonmortar mortar weight @@ -128,7 +128,7 @@ struct SurfaceContactElem * \param [in] b NONMORTAR node id * */ - RealT getNonmortarNonmortarWt( const int a, const int b ); + RealT getNonmortarNonmortarWt( const int a, const int b ); /*! * \brief get array index for x-dimension face-pair Jacobian contribution @@ -156,9 +156,7 @@ struct SurfaceContactElem * getJacobianDimOffset() * */ - int getJacobianIndex( JacBlock block, - const int a, - const int b ) const; + int getJacobianIndex( JacBlock block, const int a, const int b ) const; /*! * \brief get element-pair Jacobian array offset due to incrementing the @@ -179,7 +177,7 @@ struct SurfaceContactElem * x-dimension given by getJacobianIndex() * */ - int getJacobianDimOffset( JacBlock block ) const; + int getJacobianDimOffset( JacBlock block ) const; /*! * \brief routine to allocate space to store contact element Jacobians @@ -187,57 +185,53 @@ struct SurfaceContactElem * \param [in] method contact method * */ - void allocateBlockJ( EnforcementMethod enf ); + void allocateBlockJ( EnforcementMethod enf ); - /// delete routine - TRIBOL_HOST_DEVICE void deallocateElem( ); + /// delete routine + TRIBOL_HOST_DEVICE void deallocateElem(); -} ; // end of SurfaceContactElem definition +}; // end of SurfaceContactElem definition //------------------------------------------------------------------------------ -class MethodData -{ -public: - /*! - * \brief Constructor - */ - MethodData(); - - /*! - * \brief Destructor - */ - ~MethodData() { }; - - /*! - * \brief allocate element Jacobian matrix storage - * - * \param [in] blockJSpaces list of block spaces used in the Jacobian matrix - * \param [in] nPairs approximate number of contacting face-pairs (used to - * allocate memory in the ArrayT) - */ - void reserveBlockJ( ArrayT&& blockJSpaces, int nPairs ); - - /*! - * \brief store an element contribution to all blocks of the Jacobian matrix - * - * \param [in] blockJElemIds list of element ids on each block space - ^ - * \param [in] blockJ 2D array of element Jacobian contributions (each array - * entry corresponds to a block of the Jacobian matrix) - */ - void storeElemBlockJ( - ArrayT&& blockJElemIds, - const StackArray, 9>& blockJ - ); - - /*! - * \brief Returns the number of blocks in the Jacobian matrix - * - * See @ref getElementBlockJacobians for a definition of the blocks. - */ - int getNSpaces() const { return m_blockJSpaces.size(); } - - /*! +class MethodData { + public: + /*! + * \brief Constructor + */ + MethodData(); + + /*! + * \brief Destructor + */ + ~MethodData(){}; + + /*! + * \brief allocate element Jacobian matrix storage + * + * \param [in] blockJSpaces list of block spaces used in the Jacobian matrix + * \param [in] nPairs approximate number of contacting face-pairs (used to + * allocate memory in the ArrayT) + */ + void reserveBlockJ( ArrayT&& blockJSpaces, int nPairs ); + + /*! + * \brief store an element contribution to all blocks of the Jacobian matrix + * + * \param [in] blockJElemIds list of element ids on each block space + ^ + * \param [in] blockJ 2D array of element Jacobian contributions (each array + * entry corresponds to a block of the Jacobian matrix) + */ + void storeElemBlockJ( ArrayT&& blockJElemIds, const StackArray, 9>& blockJ ); + + /*! + * \brief Returns the number of blocks in the Jacobian matrix + * + * See @ref getElementBlockJacobians for a definition of the blocks. + */ + int getNSpaces() const { return m_blockJSpaces.size(); } + + /*! * \brief Get the element ids for each entry of the getBlockJ 2D ArrayT * sorted by block space * @@ -249,12 +243,9 @@ class MethodData * * \return nested array identifying element ids for a given block space */ - const ArrayT>& getBlockJElementIds() const - { - return m_blockJElemIds; - } + const ArrayT>& getBlockJElementIds() const { return m_blockJElemIds; } - /*! + /*! * \brief Get element Jacobian contributions sorted by block space and element * * \note Method returns a nested array. With getBlockJ()(i,j)[k], the index i @@ -266,106 +257,94 @@ class MethodData * \return nested array identifying element Jacobian contributions for given * test and trial block spaces */ - const ArrayT, 2>& getBlockJ() const - { - return m_blockJ; - } - -private: + const ArrayT, 2>& getBlockJ() const { return m_blockJ; } - ArrayT m_blockJSpaces; ///< list of Jacobian blocks in use - ArrayT> m_blockJElemIds; ///< element ids for element Jacobian contributions - ArrayT, 2> m_blockJ; ///< element Jacobian contributions by block + private: + ArrayT m_blockJSpaces; ///< list of Jacobian blocks in use + ArrayT> m_blockJElemIds; ///< element ids for element Jacobian contributions + ArrayT, 2> m_blockJ; ///< element Jacobian contributions by block }; //------------------------------------------------------------------------------ -class MortarData : public MethodData -{ -public: - /*! - * \brief Constructor - */ - MortarData(); - - /*! - * \brief Destructor - */ - ~MortarData(); - - int m_numTotalNodes; - - /*! - * \brief allocate object's mfem sparse matrix - * - * \param [in] numRows number of rows in matrix - * - * \note number of columns is same as number of rows - * - */ - void allocateMfemSparseMatrix( const int numRows ) - { - if (this->m_smat != nullptr) - { - delete this->m_smat; - this->m_smat = nullptr; - this->m_smat = new mfem::SparseMatrix( numRows, numRows ); - } +class MortarData : public MethodData { + public: + /*! + * \brief Constructor + */ + MortarData(); + + /*! + * \brief Destructor + */ + ~MortarData(); + + int m_numTotalNodes; + /*! + * \brief allocate object's mfem sparse matrix + * + * \param [in] numRows number of rows in matrix + * + * \note number of columns is same as number of rows + * + */ + void allocateMfemSparseMatrix( const int numRows ) + { + if ( this->m_smat != nullptr ) { + delete this->m_smat; + this->m_smat = nullptr; this->m_smat = new mfem::SparseMatrix( numRows, numRows ); - } - - /// get mfem sparse matrix object - mfem::SparseMatrix * getMfemSparseMatrix() const { return m_smat; } - - /*! - * \brief get the underlying CSR arrays of mfem sparse matrix object - * - * \param [out] I offsets array - * \param [out] J column index array for each nonzero value - * \param [out] vals nonzero values array - * \param [out] n_offsets pointer to the number of offsets (size of I array) - * \param [out] n_nonzero pointer to the number of non zeros - * (size of J and vals arrays) - * - * \post n_offsets will store the number of offsets, if a non-nullptr was passed in - * \post n_nonzero will store the number of non-zeros, if a non-nullptr was passed in - */ - void getCSRArrays( int** I, - int** J, - RealT** vals, - int* n_offsets = nullptr, - int* n_nonzero = nullptr ); - - /*! - * \brief Assembles local contact element Jacobian contributions into - * MFEM sparse matrix on the coupling scheme object - * - * \param [in] elem surface contact element struct with Jacobian data - * \param [in] sparse mode for assembly - * - */ - void assembleJacobian( SurfaceContactElem & elem, - SparseMode s_mode ) const; - - /*! - * \brief Assembles local contact element mortar weights into - * MFEM sparse matrix on the coupling scheme object - * - * \param [in] elem surface contact element struct with Jacobian data - * \param [in] s_mode sparse mode option - * - */ - void assembleMortarWts( SurfaceContactElem & elem, SparseMode s_mode ) const; - -private: - // mfem sparse matrix for Jacobian contributions for both meshes - // involved in a coupling scheme or for mortar weights - mutable mfem::SparseMatrix * m_smat; ///< mfem sparse matrix for Jacobian or weights storage - - DISABLE_COPY_AND_ASSIGNMENT( MortarData ); - DISABLE_MOVE_AND_ASSIGNMENT( MortarData ); + } + + this->m_smat = new mfem::SparseMatrix( numRows, numRows ); + } + + /// get mfem sparse matrix object + mfem::SparseMatrix* getMfemSparseMatrix() const { return m_smat; } + + /*! + * \brief get the underlying CSR arrays of mfem sparse matrix object + * + * \param [out] I offsets array + * \param [out] J column index array for each nonzero value + * \param [out] vals nonzero values array + * \param [out] n_offsets pointer to the number of offsets (size of I array) + * \param [out] n_nonzero pointer to the number of non zeros + * (size of J and vals arrays) + * + * \post n_offsets will store the number of offsets, if a non-nullptr was passed in + * \post n_nonzero will store the number of non-zeros, if a non-nullptr was passed in + */ + void getCSRArrays( int** I, int** J, RealT** vals, int* n_offsets = nullptr, int* n_nonzero = nullptr ); + + /*! + * \brief Assembles local contact element Jacobian contributions into + * MFEM sparse matrix on the coupling scheme object + * + * \param [in] elem surface contact element struct with Jacobian data + * \param [in] sparse mode for assembly + * + */ + void assembleJacobian( SurfaceContactElem& elem, SparseMode s_mode ) const; + + /*! + * \brief Assembles local contact element mortar weights into + * MFEM sparse matrix on the coupling scheme object + * + * \param [in] elem surface contact element struct with Jacobian data + * \param [in] s_mode sparse mode option + * + */ + void assembleMortarWts( SurfaceContactElem& elem, SparseMode s_mode ) const; + + private: + // mfem sparse matrix for Jacobian contributions for both meshes + // involved in a coupling scheme or for mortar weights + mutable mfem::SparseMatrix* m_smat; ///< mfem sparse matrix for Jacobian or weights storage + DISABLE_COPY_AND_ASSIGNMENT( MortarData ); + DISABLE_MOVE_AND_ASSIGNMENT( MortarData ); }; -} // end namespace tribol +} // end namespace tribol #endif /* SRC_MESH_METHODCOUPLINGDATA_HPP_ */ diff --git a/src/tribol/mesh/MfemData.cpp b/src/tribol/mesh/MfemData.cpp index ab98542f..c7690861 100644 --- a/src/tribol/mesh/MfemData.cpp +++ b/src/tribol/mesh/MfemData.cpp @@ -3,255 +3,185 @@ // // SPDX-License-Identifier: (MIT) - #include "tribol/mesh/MfemData.hpp" #ifdef BUILD_REDECOMP #include "axom/slic.hpp" -namespace tribol +namespace tribol { + +SubmeshLORTransfer::SubmeshLORTransfer( mfem::ParFiniteElementSpace& submesh_fes, mfem::ParMesh& lor_mesh ) + : lor_gridfn_{ CreateLORGridFunction( + lor_mesh, std::make_unique( 1, lor_mesh.SpaceDimension() ), submesh_fes.GetVDim() ) }, + lor_xfer_{ submesh_fes, *lor_gridfn_->ParFESpace() } { +} -SubmeshLORTransfer::SubmeshLORTransfer( - mfem::ParFiniteElementSpace& submesh_fes, - mfem::ParMesh& lor_mesh -) -: lor_gridfn_ { CreateLORGridFunction( - lor_mesh, - std::make_unique(1, lor_mesh.SpaceDimension()), - submesh_fes.GetVDim() - ) }, - lor_xfer_ { submesh_fes, *lor_gridfn_->ParFESpace() } -{} - -void SubmeshLORTransfer::TransferToLORGridFn( - const mfem::ParGridFunction& submesh_src -) +void SubmeshLORTransfer::TransferToLORGridFn( const mfem::ParGridFunction& submesh_src ) { - SubmeshToLOR(submesh_src, *lor_gridfn_); + SubmeshToLOR( submesh_src, *lor_gridfn_ ); } -void SubmeshLORTransfer::TransferFromLORVector( - mfem::Vector& submesh_dst -) const +void SubmeshLORTransfer::TransferFromLORVector( mfem::Vector& submesh_dst ) const { - lor_xfer_.ForwardOperator().MultTranspose(*lor_gridfn_, submesh_dst); + lor_xfer_.ForwardOperator().MultTranspose( *lor_gridfn_, submesh_dst ); } -void SubmeshLORTransfer::SubmeshToLOR( - const mfem::ParGridFunction& submesh_src, - mfem::ParGridFunction& lor_dst -) +void SubmeshLORTransfer::SubmeshToLOR( const mfem::ParGridFunction& submesh_src, mfem::ParGridFunction& lor_dst ) { - lor_xfer_.ForwardOperator().Mult(submesh_src, lor_dst); + lor_xfer_.ForwardOperator().Mult( submesh_src, lor_dst ); } std::unique_ptr SubmeshLORTransfer::CreateLORGridFunction( - mfem::ParMesh& lor_mesh, - std::unique_ptr lor_fec, - int vdim -) + mfem::ParMesh& lor_mesh, std::unique_ptr lor_fec, int vdim ) { - auto lor_gridfn = std::make_unique( - new mfem::ParFiniteElementSpace( - &lor_mesh, - lor_fec.get(), - vdim, - mfem::Ordering::byNODES - ) - ); - lor_gridfn->MakeOwner(lor_fec.release()); + auto lor_gridfn = std::make_unique( + new mfem::ParFiniteElementSpace( &lor_mesh, lor_fec.get(), vdim, mfem::Ordering::byNODES ) ); + lor_gridfn->MakeOwner( lor_fec.release() ); return lor_gridfn; } -SubmeshRedecompTransfer::SubmeshRedecompTransfer( - mfem::ParFiniteElementSpace& submesh_fes, - SubmeshLORTransfer* submesh_lor_xfer, - redecomp::RedecompMesh& redecomp_mesh -) -: submesh_fes_ { submesh_fes }, - redecomp_fes_ { submesh_lor_xfer ? - CreateRedecompFESpace(redecomp_mesh, *submesh_lor_xfer->GetLORGridFn().ParFESpace()) : - CreateRedecompFESpace(redecomp_mesh, submesh_fes_) }, - submesh_lor_xfer_ { submesh_lor_xfer }, - redecomp_xfer_ { } // default (element transfer) constructor +SubmeshRedecompTransfer::SubmeshRedecompTransfer( mfem::ParFiniteElementSpace& submesh_fes, + SubmeshLORTransfer* submesh_lor_xfer, + redecomp::RedecompMesh& redecomp_mesh ) + : submesh_fes_{ submesh_fes }, + redecomp_fes_{ submesh_lor_xfer + ? CreateRedecompFESpace( redecomp_mesh, *submesh_lor_xfer->GetLORGridFn().ParFESpace() ) + : CreateRedecompFESpace( redecomp_mesh, submesh_fes_ ) }, + submesh_lor_xfer_{ submesh_lor_xfer }, + redecomp_xfer_{} // default (element transfer) constructor { // make sure submesh_fes is a submesh and redecomp's parent is submesh_fes's // submesh + SLIC_ERROR_ROOT_IF( !mfem::ParSubMesh::IsParSubMesh( submesh_fes_.GetParMesh() ), + "submesh_fes must be on a ParSubMesh." ); + SLIC_ERROR_ROOT_IF( !submesh_lor_xfer && &redecomp_mesh.getParent() != submesh_fes_.GetParMesh(), + "redecomp's parent must match the submesh_fes ParMesh." ); SLIC_ERROR_ROOT_IF( - !mfem::ParSubMesh::IsParSubMesh(submesh_fes_.GetParMesh()), - "submesh_fes must be on a ParSubMesh." - ); - SLIC_ERROR_ROOT_IF( - !submesh_lor_xfer && - &redecomp_mesh.getParent() != submesh_fes_.GetParMesh(), - "redecomp's parent must match the submesh_fes ParMesh." - ); - SLIC_ERROR_ROOT_IF( - submesh_lor_xfer && - &redecomp_mesh.getParent() != submesh_lor_xfer->GetLORGridFn().ParFESpace()->GetParMesh(), - "redecomp's parent must match the submesh_fes ParMesh." - ); + submesh_lor_xfer && &redecomp_mesh.getParent() != submesh_lor_xfer->GetLORGridFn().ParFESpace()->GetParMesh(), + "redecomp's parent must match the submesh_fes ParMesh." ); } -void SubmeshRedecompTransfer::SubmeshToRedecomp( - const mfem::ParGridFunction& submesh_src, - mfem::GridFunction& redecomp_dst -) const +void SubmeshRedecompTransfer::SubmeshToRedecomp( const mfem::ParGridFunction& submesh_src, + mfem::GridFunction& redecomp_dst ) const { auto src_ptr = &submesh_src; - if (submesh_lor_xfer_) - { + if ( submesh_lor_xfer_ ) { submesh_lor_xfer_->GetLORGridFn() = 0.0; - submesh_lor_xfer_->TransferToLORGridFn(submesh_src); + submesh_lor_xfer_->TransferToLORGridFn( submesh_src ); src_ptr = &submesh_lor_xfer_->GetLORGridFn(); } - redecomp_xfer_.TransferToSerial(*src_ptr, redecomp_dst); + redecomp_xfer_.TransferToSerial( *src_ptr, redecomp_dst ); } -void SubmeshRedecompTransfer::RedecompToSubmesh( - const mfem::GridFunction& redecomp_src, - mfem::Vector& submesh_dst -) const +void SubmeshRedecompTransfer::RedecompToSubmesh( const mfem::GridFunction& redecomp_src, + mfem::Vector& submesh_dst ) const { auto dst_ptr = &submesh_dst; auto dst_fespace_ptr = &submesh_fes_; // first initialize LOR grid function (if using LOR) - if (submesh_lor_xfer_) - { + if ( submesh_lor_xfer_ ) { submesh_lor_xfer_->GetLORVector() = 0.0; dst_ptr = &submesh_lor_xfer_->GetLORVector(); dst_fespace_ptr = submesh_lor_xfer_->GetLORGridFn().ParFESpace(); } // transfer data from redecomp mesh - mfem::ParGridFunction dst_gridfn(dst_fespace_ptr, *dst_ptr); - redecomp_xfer_.TransferToParallel(redecomp_src, dst_gridfn); + mfem::ParGridFunction dst_gridfn( dst_fespace_ptr, *dst_ptr ); + redecomp_xfer_.TransferToParallel( redecomp_src, dst_gridfn ); // using redecomp, shared dof values are set equal (i.e. a ParGridFunction), but we want the sum of shared dof values // to equal the actual dof value when transferring dual fields (i.e. force and gap) back to the parallel mesh // following MFEMs convention. set non-owned DOF values to zero. - + // P_I is the row index vector on the MFEM prolongation matrix. If there are no column entries for the row, then the // DOF is owned by another rank. auto P_I = dst_fespace_ptr->Dof_TrueDof_Matrix()->GetDiagMemoryI(); - HYPRE_Int tdof_ct {0}; - for (int i{0}; i < dst_fespace_ptr->GetVSize(); ++i) - { - if (P_I[i+1] != tdof_ct) - { + HYPRE_Int tdof_ct{ 0 }; + for ( int i{ 0 }; i < dst_fespace_ptr->GetVSize(); ++i ) { + if ( P_I[i + 1] != tdof_ct ) { ++tdof_ct; - } - else - { - (*dst_ptr)[i] = 0.0; + } else { + ( *dst_ptr )[i] = 0.0; } } // if using LOR, transfer data from LOR mesh to submesh - if (submesh_lor_xfer_) - { - submesh_lor_xfer_->TransferFromLORVector(submesh_dst); + if ( submesh_lor_xfer_ ) { + submesh_lor_xfer_->TransferFromLORVector( submesh_dst ); } } std::unique_ptr SubmeshRedecompTransfer::CreateRedecompFESpace( - redecomp::RedecompMesh& redecomp_mesh, - mfem::ParFiniteElementSpace& submesh_fes -) + redecomp::RedecompMesh& redecomp_mesh, mfem::ParFiniteElementSpace& submesh_fes ) { - return std::make_unique( - &redecomp_mesh, - submesh_fes.FEColl(), - submesh_fes.GetVDim(), - mfem::Ordering::byNODES - ); + return std::make_unique( &redecomp_mesh, submesh_fes.FEColl(), submesh_fes.GetVDim(), + mfem::Ordering::byNODES ); } -ParentRedecompTransfer::ParentRedecompTransfer( - const mfem::ParFiniteElementSpace& parent_fes, - mfem::ParGridFunction& submesh_gridfn, - SubmeshLORTransfer* submesh_lor_xfer, - redecomp::RedecompMesh& redecomp_mesh -) -: parent_fes_ { parent_fes }, - submesh_gridfn_ { submesh_gridfn }, - submesh_redecomp_xfer_ { - *submesh_gridfn_.ParFESpace(), - submesh_lor_xfer, - redecomp_mesh - } +ParentRedecompTransfer::ParentRedecompTransfer( const mfem::ParFiniteElementSpace& parent_fes, + mfem::ParGridFunction& submesh_gridfn, + SubmeshLORTransfer* submesh_lor_xfer, + redecomp::RedecompMesh& redecomp_mesh ) + : parent_fes_{ parent_fes }, + submesh_gridfn_{ submesh_gridfn }, + submesh_redecomp_xfer_{ *submesh_gridfn_.ParFESpace(), submesh_lor_xfer, redecomp_mesh } { // Note: this is checked in the SubmeshRedecompTransfer constructor // SLIC_ERROR_ROOT_IF( // !mfem::ParSubMesh::IsParSubMesh(submesh_gridfn_.ParFESpace()->GetParMesh()), // "submesh_gridfn_ must be associated with an mfem::ParSubMesh." // ); - SLIC_ERROR_ROOT_IF( - submesh_redecomp_xfer_.GetSubmesh().GetParent() != parent_fes_.GetParMesh(), - "submesh_gridfn's parent mesh must match the parent_fes ParMesh." - ); + SLIC_ERROR_ROOT_IF( submesh_redecomp_xfer_.GetSubmesh().GetParent() != parent_fes_.GetParMesh(), + "submesh_gridfn's parent mesh must match the parent_fes ParMesh." ); } -void ParentRedecompTransfer::ParentToRedecomp( - const mfem::ParGridFunction& parent_src, - mfem::GridFunction& redecomp_dst -) const +void ParentRedecompTransfer::ParentToRedecomp( const mfem::ParGridFunction& parent_src, + mfem::GridFunction& redecomp_dst ) const { submesh_gridfn_ = 0.0; - submesh_redecomp_xfer_.GetSubmesh().Transfer(parent_src, submesh_gridfn_); - submesh_redecomp_xfer_.SubmeshToRedecomp(submesh_gridfn_, redecomp_dst); + submesh_redecomp_xfer_.GetSubmesh().Transfer( parent_src, submesh_gridfn_ ); + submesh_redecomp_xfer_.SubmeshToRedecomp( submesh_gridfn_, redecomp_dst ); } -void ParentRedecompTransfer::RedecompToParent( - const mfem::GridFunction& redecomp_src, - mfem::Vector& parent_dst -) const +void ParentRedecompTransfer::RedecompToParent( const mfem::GridFunction& redecomp_src, mfem::Vector& parent_dst ) const { submesh_gridfn_ = 0.0; - submesh_redecomp_xfer_.RedecompToSubmesh(redecomp_src, submesh_gridfn_); + submesh_redecomp_xfer_.RedecompToSubmesh( redecomp_src, submesh_gridfn_ ); // submesh transfer requires a grid function. create one using parent_dst's data - mfem::ParGridFunction parent_gridfn(&parent_fes_, parent_dst); - submesh_redecomp_xfer_.GetSubmesh().Transfer(submesh_gridfn_, parent_gridfn); + mfem::ParGridFunction parent_gridfn( &parent_fes_, parent_dst ); + submesh_redecomp_xfer_.GetSubmesh().Transfer( submesh_gridfn_, parent_gridfn ); } -ParentField::ParentField( - const mfem::ParGridFunction& parent_gridfn -) -: parent_gridfn_ { parent_gridfn } -{} +ParentField::ParentField( const mfem::ParGridFunction& parent_gridfn ) : parent_gridfn_{ parent_gridfn } {} -void ParentField::SetParentGridFn(const mfem::ParGridFunction& parent_gridfn) +void ParentField::SetParentGridFn( const mfem::ParGridFunction& parent_gridfn ) { parent_gridfn_ = parent_gridfn; - update_data_.reset(nullptr); + update_data_.reset( nullptr ); } -void ParentField::UpdateField(ParentRedecompTransfer& parent_redecomp_xfer) +void ParentField::UpdateField( ParentRedecompTransfer& parent_redecomp_xfer ) { - update_data_ = std::make_unique(parent_redecomp_xfer, parent_gridfn_); + update_data_ = std::make_unique( parent_redecomp_xfer, parent_gridfn_ ); } std::vector ParentField::GetRedecompFieldPtrs() const { - auto data_ptrs = std::vector(3, nullptr); - if (GetRedecompGridFn().FESpace()->GetNDofs() > 0) - { - for (size_t i{}; i < static_cast(GetRedecompGridFn().FESpace()->GetVDim()); ++i) - { - data_ptrs[i] = &GetRedecompGridFn()(GetRedecompGridFn().FESpace()->DofToVDof(0, i)); + auto data_ptrs = std::vector( 3, nullptr ); + if ( GetRedecompGridFn().FESpace()->GetNDofs() > 0 ) { + for ( size_t i{}; i < static_cast( GetRedecompGridFn().FESpace()->GetVDim() ); ++i ) { + data_ptrs[i] = &GetRedecompGridFn()( GetRedecompGridFn().FESpace()->DofToVDof( 0, i ) ); } } return data_ptrs; } -std::vector ParentField::GetRedecompFieldPtrs(mfem::GridFunction& redecomp_gridfn) +std::vector ParentField::GetRedecompFieldPtrs( mfem::GridFunction& redecomp_gridfn ) { - auto data_ptrs = std::vector(3, nullptr); - if (redecomp_gridfn.FESpace()->GetNDofs() > 0) - { - for (size_t i{}; i < static_cast(redecomp_gridfn.FESpace()->GetVDim()); ++i) - { - data_ptrs[i] = &redecomp_gridfn(redecomp_gridfn.FESpace()->DofToVDof(0, i)); + auto data_ptrs = std::vector( 3, nullptr ); + if ( redecomp_gridfn.FESpace()->GetNDofs() > 0 ) { + for ( size_t i{}; i < static_cast( redecomp_gridfn.FESpace()->GetVDim() ); ++i ) { + data_ptrs[i] = &redecomp_gridfn( redecomp_gridfn.FESpace()->DofToVDof( 0, i ) ); } } return data_ptrs; @@ -259,71 +189,54 @@ std::vector ParentField::GetRedecompFieldPtrs(mfem::GridFunction& redeco ParentField::UpdateData& ParentField::GetUpdateData() { - SLIC_ERROR_ROOT_IF( - update_data_ == nullptr, - "UpdateField() must be called to generate UpdateData." - ); + SLIC_ERROR_ROOT_IF( update_data_ == nullptr, "UpdateField() must be called to generate UpdateData." ); return *update_data_; } const ParentField::UpdateData& ParentField::GetUpdateData() const { - SLIC_ERROR_ROOT_IF( - update_data_ == nullptr, - "UpdateField() must be called to generate UpdateData." - ); + SLIC_ERROR_ROOT_IF( update_data_ == nullptr, "UpdateField() must be called to generate UpdateData." ); return *update_data_; } -ParentField::UpdateData::UpdateData( - ParentRedecompTransfer& parent_redecomp_xfer, - const mfem::ParGridFunction& parent_gridfn -) -: parent_redecomp_xfer_ { parent_redecomp_xfer }, - redecomp_gridfn_ { &parent_redecomp_xfer.GetRedecompFESpace() } +ParentField::UpdateData::UpdateData( ParentRedecompTransfer& parent_redecomp_xfer, + const mfem::ParGridFunction& parent_gridfn ) + : parent_redecomp_xfer_{ parent_redecomp_xfer }, redecomp_gridfn_{ &parent_redecomp_xfer.GetRedecompFESpace() } { redecomp_gridfn_ = 0.0; - parent_redecomp_xfer_.ParentToRedecomp(parent_gridfn, redecomp_gridfn_); + parent_redecomp_xfer_.ParentToRedecomp( parent_gridfn, redecomp_gridfn_ ); } -PressureField::PressureField( - const mfem::ParGridFunction& submesh_gridfn -) -: submesh_gridfn_ { submesh_gridfn } -{} +PressureField::PressureField( const mfem::ParGridFunction& submesh_gridfn ) : submesh_gridfn_{ submesh_gridfn } {} -void PressureField::SetSubmeshField(const mfem::ParGridFunction& submesh_gridfn) +void PressureField::SetSubmeshField( const mfem::ParGridFunction& submesh_gridfn ) { submesh_gridfn_ = submesh_gridfn; - update_data_.reset(nullptr); + update_data_.reset( nullptr ); } -void PressureField::UpdateField(SubmeshRedecompTransfer& submesh_redecomp_xfer) +void PressureField::UpdateField( SubmeshRedecompTransfer& submesh_redecomp_xfer ) { - update_data_ = std::make_unique(submesh_redecomp_xfer, submesh_gridfn_); + update_data_ = std::make_unique( submesh_redecomp_xfer, submesh_gridfn_ ); } std::vector PressureField::GetRedecompFieldPtrs() const { - auto data_ptrs = std::vector(3, nullptr); - if (GetRedecompGridFn().FESpace()->GetNDofs() > 0) - { - for (size_t i{}; i < static_cast(GetRedecompGridFn().FESpace()->GetVDim()); ++i) - { - data_ptrs[i] = &GetRedecompGridFn()(GetRedecompGridFn().FESpace()->DofToVDof(0, i)); + auto data_ptrs = std::vector( 3, nullptr ); + if ( GetRedecompGridFn().FESpace()->GetNDofs() > 0 ) { + for ( size_t i{}; i < static_cast( GetRedecompGridFn().FESpace()->GetVDim() ); ++i ) { + data_ptrs[i] = &GetRedecompGridFn()( GetRedecompGridFn().FESpace()->DofToVDof( 0, i ) ); } } return data_ptrs; } -std::vector PressureField::GetRedecompFieldPtrs(mfem::GridFunction& redecomp_gridfn) +std::vector PressureField::GetRedecompFieldPtrs( mfem::GridFunction& redecomp_gridfn ) { - auto data_ptrs = std::vector(3, nullptr); - if (redecomp_gridfn.FESpace()->GetNDofs() > 0) - { - for (size_t i{}; i < static_cast(redecomp_gridfn.FESpace()->GetVDim()); ++i) - { - data_ptrs[i] = &redecomp_gridfn(redecomp_gridfn.FESpace()->DofToVDof(0, i)); + auto data_ptrs = std::vector( 3, nullptr ); + if ( redecomp_gridfn.FESpace()->GetNDofs() > 0 ) { + for ( size_t i{}; i < static_cast( redecomp_gridfn.FESpace()->GetVDim() ); ++i ) { + data_ptrs[i] = &redecomp_gridfn( redecomp_gridfn.FESpace()->DofToVDof( 0, i ) ); } } return data_ptrs; @@ -331,241 +244,188 @@ std::vector PressureField::GetRedecompFieldPtrs(mfem::GridFunction& rede PressureField::UpdateData& PressureField::GetUpdateData() { - SLIC_ERROR_ROOT_IF( - update_data_ == nullptr, - "UpdateField() must be called to generate UpdateData." - ); + SLIC_ERROR_ROOT_IF( update_data_ == nullptr, "UpdateField() must be called to generate UpdateData." ); return *update_data_; } const PressureField::UpdateData& PressureField::GetUpdateData() const { - SLIC_ERROR_ROOT_IF( - update_data_ == nullptr, - "UpdateField() must be called to generate UpdateData." - ); + SLIC_ERROR_ROOT_IF( update_data_ == nullptr, "UpdateField() must be called to generate UpdateData." ); return *update_data_; } -PressureField::UpdateData::UpdateData( - SubmeshRedecompTransfer& submesh_redecomp_xfer, - const mfem::ParGridFunction& submesh_gridfn -) -: submesh_redecomp_xfer_ { submesh_redecomp_xfer }, - redecomp_gridfn_ { &submesh_redecomp_xfer.GetRedecompFESpace() } +PressureField::UpdateData::UpdateData( SubmeshRedecompTransfer& submesh_redecomp_xfer, + const mfem::ParGridFunction& submesh_gridfn ) + : submesh_redecomp_xfer_{ submesh_redecomp_xfer }, redecomp_gridfn_{ &submesh_redecomp_xfer.GetRedecompFESpace() } { redecomp_gridfn_ = 0.0; - submesh_redecomp_xfer_.SubmeshToRedecomp(submesh_gridfn, redecomp_gridfn_); + submesh_redecomp_xfer_.SubmeshToRedecomp( submesh_gridfn, redecomp_gridfn_ ); } -MfemMeshData::MfemMeshData( - IndexT mesh_id_1, - IndexT mesh_id_2, - const mfem::ParMesh& parent_mesh, - const mfem::ParGridFunction& current_coords, - std::set&& attributes_1, - std::set&& attributes_2 -) -: mesh_id_1_ { mesh_id_1 }, - mesh_id_2_ { mesh_id_2 }, - parent_mesh_ { parent_mesh }, - attributes_1_ { std::move(attributes_1) }, - attributes_2_ { std::move(attributes_2) }, - submesh_ { CreateSubmesh(parent_mesh_, attributes_1_, attributes_2_) }, - coords_ { current_coords }, - lor_factor_ { 0 } +MfemMeshData::MfemMeshData( IndexT mesh_id_1, IndexT mesh_id_2, const mfem::ParMesh& parent_mesh, + const mfem::ParGridFunction& current_coords, std::set&& attributes_1, + std::set&& attributes_2 ) + : mesh_id_1_{ mesh_id_1 }, + mesh_id_2_{ mesh_id_2 }, + parent_mesh_{ parent_mesh }, + attributes_1_{ std::move( attributes_1 ) }, + attributes_2_{ std::move( attributes_2 ) }, + submesh_{ CreateSubmesh( parent_mesh_, attributes_1_, attributes_2_ ) }, + coords_{ current_coords }, + lor_factor_{ 0 } { // make sure a grid function exists on the submesh submesh_.EnsureNodes(); // create submesh grid function - std::unique_ptr submesh_fec { - current_coords.ParFESpace()->FEColl()->Clone( - current_coords.ParFESpace()->FEColl()->GetOrder() - ) - }; - submesh_xfer_gridfn_.SetSpace( - new mfem::ParFiniteElementSpace( - &submesh_, - submesh_fec.get(), - current_coords.ParFESpace()->GetVDim(), - mfem::Ordering::byNODES - ) - ); - submesh_xfer_gridfn_.MakeOwner(submesh_fec.release()); + std::unique_ptr submesh_fec{ + current_coords.ParFESpace()->FEColl()->Clone( current_coords.ParFESpace()->FEColl()->GetOrder() ) }; + submesh_xfer_gridfn_.SetSpace( new mfem::ParFiniteElementSpace( + &submesh_, submesh_fec.get(), current_coords.ParFESpace()->GetVDim(), mfem::Ordering::byNODES ) ); + submesh_xfer_gridfn_.MakeOwner( submesh_fec.release() ); // build LOR submesh - if (current_coords.FESpace()->FEColl()->GetOrder() > 1) - { - SetLORFactor(current_coords.FESpace()->FEColl()->GetOrder()); + if ( current_coords.FESpace()->FEColl()->GetOrder() > 1 ) { + SetLORFactor( current_coords.FESpace()->FEColl()->GetOrder() ); } } -void MfemMeshData::SetParentCoords(const mfem::ParGridFunction& current_coords) +void MfemMeshData::SetParentCoords( const mfem::ParGridFunction& current_coords ) { - coords_.SetParentGridFn(current_coords); + coords_.SetParentGridFn( current_coords ); } void MfemMeshData::UpdateMfemMeshData() { // update coordinates of submesh and LOR mesh - auto submesh_nodes = dynamic_cast(submesh_.GetNodes()); - SLIC_ERROR_ROOT_IF(!submesh_nodes, "submesh_ Nodes is not a ParGridFunction."); - submesh_.Transfer(coords_.GetParentGridFn(), *submesh_nodes); - if (lor_mesh_.get()) - { - auto lor_nodes = dynamic_cast(lor_mesh_->GetNodes()); - SLIC_ERROR_ROOT_IF(!lor_nodes, "lor_mesh_ Nodes is not a ParGridFunction."); - submesh_lor_xfer_->SubmeshToLOR(*submesh_nodes, *lor_nodes); - } - update_data_ = std::make_unique( - submesh_, - lor_mesh_.get(), - *coords_.GetParentGridFn().ParFESpace(), - submesh_xfer_gridfn_, - submesh_lor_xfer_.get(), - attributes_1_, - attributes_2_ - ); - coords_.UpdateField(update_data_->vector_xfer_); - redecomp_response_.SetSpace(coords_.GetRedecompGridFn().FESpace()); + auto submesh_nodes = dynamic_cast( submesh_.GetNodes() ); + SLIC_ERROR_ROOT_IF( !submesh_nodes, "submesh_ Nodes is not a ParGridFunction." ); + submesh_.Transfer( coords_.GetParentGridFn(), *submesh_nodes ); + if ( lor_mesh_.get() ) { + auto lor_nodes = dynamic_cast( lor_mesh_->GetNodes() ); + SLIC_ERROR_ROOT_IF( !lor_nodes, "lor_mesh_ Nodes is not a ParGridFunction." ); + submesh_lor_xfer_->SubmeshToLOR( *submesh_nodes, *lor_nodes ); + } + update_data_ = + std::make_unique( submesh_, lor_mesh_.get(), *coords_.GetParentGridFn().ParFESpace(), + submesh_xfer_gridfn_, submesh_lor_xfer_.get(), attributes_1_, attributes_2_ ); + coords_.UpdateField( update_data_->vector_xfer_ ); + redecomp_response_.SetSpace( coords_.GetRedecompGridFn().FESpace() ); redecomp_response_ = 0.0; - if (velocity_) - { - velocity_->UpdateField(update_data_->vector_xfer_); - } - if (elem_thickness_) - { - if (!material_modulus_) - { - SLIC_ERROR_ROOT("Kinematic element penalty requires material modulus information. " - "Call registerMfemMaterialModulus() to set this."); + if ( velocity_ ) { + velocity_->UpdateField( update_data_->vector_xfer_ ); + } + if ( elem_thickness_ ) { + if ( !material_modulus_ ) { + SLIC_ERROR_ROOT( + "Kinematic element penalty requires material modulus information. " + "Call registerMfemMaterialModulus() to set this." ); } redecomp::RedecompTransfer redecomp_xfer; // set element thickness on redecomp mesh - redecomp_elem_thickness_ = std::make_unique( - new mfem::QuadratureSpace(&GetRedecompMesh(), 0) - ); - redecomp_elem_thickness_->SetOwnsSpace(true); + redecomp_elem_thickness_ = + std::make_unique( new mfem::QuadratureSpace( &GetRedecompMesh(), 0 ) ); + redecomp_elem_thickness_->SetOwnsSpace( true ); *redecomp_elem_thickness_ = 0.0; - redecomp_xfer.TransferToSerial(*elem_thickness_, *redecomp_elem_thickness_); + redecomp_xfer.TransferToSerial( *elem_thickness_, *redecomp_elem_thickness_ ); // set element thickness on tribol mesh - tribol_elem_thickness_1_ = std::make_unique>( - 0, GetElemMap1().empty() ? 1 : GetElemMap1().size()); - for (auto redecomp_e : GetElemMap1()) - { + tribol_elem_thickness_1_ = std::make_unique>( 0, GetElemMap1().empty() ? 1 : GetElemMap1().size() ); + for ( auto redecomp_e : GetElemMap1() ) { mfem::Vector quad_val; - redecomp_elem_thickness_->GetValues(redecomp_e, quad_val); - tribol_elem_thickness_1_->push_back(quad_val[0]); + redecomp_elem_thickness_->GetValues( redecomp_e, quad_val ); + tribol_elem_thickness_1_->push_back( quad_val[0] ); } - tribol_elem_thickness_2_ = std::make_unique>( - 0, GetElemMap2().empty() ? 1 : GetElemMap2().size()); - for (auto redecomp_e : GetElemMap2()) - { + tribol_elem_thickness_2_ = std::make_unique>( 0, GetElemMap2().empty() ? 1 : GetElemMap2().size() ); + for ( auto redecomp_e : GetElemMap2() ) { mfem::Vector quad_val; - redecomp_elem_thickness_->GetValues(redecomp_e, quad_val); - tribol_elem_thickness_2_->push_back(quad_val[0]); + redecomp_elem_thickness_->GetValues( redecomp_e, quad_val ); + tribol_elem_thickness_2_->push_back( quad_val[0] ); } // set material modulus on redecomp mesh - redecomp_material_modulus_ = std::make_unique( - new mfem::QuadratureSpace(&GetRedecompMesh(), 0) - ); - redecomp_material_modulus_->SetOwnsSpace(true); + redecomp_material_modulus_ = + std::make_unique( new mfem::QuadratureSpace( &GetRedecompMesh(), 0 ) ); + redecomp_material_modulus_->SetOwnsSpace( true ); *redecomp_material_modulus_ = 0.0; - redecomp_xfer.TransferToSerial(*material_modulus_, *redecomp_material_modulus_); + redecomp_xfer.TransferToSerial( *material_modulus_, *redecomp_material_modulus_ ); // set material modulus on tribol mesh - tribol_material_modulus_1_ = std::make_unique>( - 0, GetElemMap1().empty() ? 1 : GetElemMap1().size()); - for (auto redecomp_e : GetElemMap1()) - { + tribol_material_modulus_1_ = std::make_unique>( 0, GetElemMap1().empty() ? 1 : GetElemMap1().size() ); + for ( auto redecomp_e : GetElemMap1() ) { mfem::Vector quad_val; - redecomp_material_modulus_->GetValues(redecomp_e, quad_val); - tribol_material_modulus_1_->push_back(quad_val[0]); + redecomp_material_modulus_->GetValues( redecomp_e, quad_val ); + tribol_material_modulus_1_->push_back( quad_val[0] ); } - tribol_material_modulus_2_ = std::make_unique>( - 0, GetElemMap2().empty() ? 1 : GetElemMap2().size()); - for (auto redecomp_e : GetElemMap2()) - { + tribol_material_modulus_2_ = std::make_unique>( 0, GetElemMap2().empty() ? 1 : GetElemMap2().size() ); + for ( auto redecomp_e : GetElemMap2() ) { mfem::Vector quad_val; - redecomp_material_modulus_->GetValues(redecomp_e, quad_val); - tribol_material_modulus_2_->push_back(quad_val[0]); + redecomp_material_modulus_->GetValues( redecomp_e, quad_val ); + tribol_material_modulus_2_->push_back( quad_val[0] ); } } } -void MfemMeshData::GetParentResponse(mfem::Vector& r) const +void MfemMeshData::GetParentResponse( mfem::Vector& r ) const { - GetParentRedecompTransfer().RedecompToParent(redecomp_response_, r); + GetParentRedecompTransfer().RedecompToParent( redecomp_response_, r ); } -void MfemMeshData::SetParentVelocity(const mfem::ParGridFunction& velocity) +void MfemMeshData::SetParentVelocity( const mfem::ParGridFunction& velocity ) { - if (velocity_) - { - velocity_->SetParentGridFn(velocity); - } - else - { - velocity_ = std::make_unique(velocity); + if ( velocity_ ) { + velocity_->SetParentGridFn( velocity ); + } else { + velocity_ = std::make_unique( velocity ); } } void MfemMeshData::ClearAllPenaltyData() { ClearRatePenaltyData(); - kinematic_constant_penalty_1_.reset(nullptr); - kinematic_constant_penalty_2_.reset(nullptr); - kinematic_penalty_scale_1_.reset(nullptr); - kinematic_penalty_scale_2_.reset(nullptr); - elem_thickness_.reset(nullptr); - redecomp_elem_thickness_.reset(nullptr); - tribol_elem_thickness_1_.reset(nullptr); - tribol_elem_thickness_2_.reset(nullptr); - material_modulus_.reset(nullptr); - redecomp_material_modulus_.reset(nullptr); - tribol_material_modulus_1_.reset(nullptr); - tribol_material_modulus_2_.reset(nullptr); + kinematic_constant_penalty_1_.reset( nullptr ); + kinematic_constant_penalty_2_.reset( nullptr ); + kinematic_penalty_scale_1_.reset( nullptr ); + kinematic_penalty_scale_2_.reset( nullptr ); + elem_thickness_.reset( nullptr ); + redecomp_elem_thickness_.reset( nullptr ); + tribol_elem_thickness_1_.reset( nullptr ); + tribol_elem_thickness_2_.reset( nullptr ); + material_modulus_.reset( nullptr ); + redecomp_material_modulus_.reset( nullptr ); + tribol_material_modulus_1_.reset( nullptr ); + tribol_material_modulus_2_.reset( nullptr ); } void MfemMeshData::ClearRatePenaltyData() { - rate_constant_penalty_1_.reset(nullptr); - rate_constant_penalty_2_.reset(nullptr); - rate_percent_ratio_1_.reset(nullptr); - rate_percent_ratio_2_.reset(nullptr); + rate_constant_penalty_1_.reset( nullptr ); + rate_constant_penalty_2_.reset( nullptr ); + rate_percent_ratio_1_.reset( nullptr ); + rate_percent_ratio_2_.reset( nullptr ); } -void MfemMeshData::SetLORFactor(int lor_factor) +void MfemMeshData::SetLORFactor( int lor_factor ) { - if (lor_factor <= 1) - { - SLIC_WARNING_ROOT("lor_factor must be an integer > 1. LOR factor not changed."); + if ( lor_factor <= 1 ) { + SLIC_WARNING_ROOT( "lor_factor must be an integer > 1. LOR factor not changed." ); return; } - if (coords_.GetParentGridFn().FESpace()->FEColl()->GetOrder() <= 1) - { - SLIC_WARNING_ROOT("lor_factor is only applicable to higher order geometry. " - "LOR factor not changed."); + if ( coords_.GetParentGridFn().FESpace()->FEColl()->GetOrder() <= 1 ) { + SLIC_WARNING_ROOT( + "lor_factor is only applicable to higher order geometry. " + "LOR factor not changed." ); return; } lor_factor_ = lor_factor; // note: calls ParMesh's move ctor - lor_mesh_ = std::make_unique(mfem::ParMesh::MakeRefined( - submesh_, lor_factor, mfem::BasisType::ClosedUniform - )); + lor_mesh_ = std::make_unique( + mfem::ParMesh::MakeRefined( submesh_, lor_factor, mfem::BasisType::ClosedUniform ) ); lor_mesh_->EnsureNodes(); - submesh_lor_xfer_ = std::make_unique( - *submesh_xfer_gridfn_.ParFESpace(), - *lor_mesh_ - ); + submesh_lor_xfer_ = std::make_unique( *submesh_xfer_gridfn_.ParFESpace(), *lor_mesh_ ); } void MfemMeshData::ComputeElementThicknesses() { - auto submesh_thickness = std::make_unique( - new mfem::QuadratureSpace(&submesh_, 0) - ); - submesh_thickness->SetOwnsSpace(true); + auto submesh_thickness = std::make_unique( new mfem::QuadratureSpace( &submesh_, 0 ) ); + submesh_thickness->SetOwnsSpace( true ); // All the elements in the submesh are on the contact surface. The algorithm // works as follows: // 1) For each submesh element, find the corresponding parent volume element @@ -575,141 +435,112 @@ void MfemMeshData::ComputeElementThicknesses() // 4) If there is an LOR mesh, use the CoarseFineTransformation to find the // LOR elements linked to the HO mesh and store the thickness of the HO // element on all of its linked LOR elements. - for (int submesh_e{0}; submesh_e < submesh_.GetNE(); ++submesh_e) - { + for ( int submesh_e{ 0 }; submesh_e < submesh_.GetNE(); ++submesh_e ) { // Step 1 auto parent_bdr_e = submesh_.GetParentElementIDMap()[submesh_e]; - auto& parent_mesh = const_cast(parent_mesh_); - auto& face_el_tr = *parent_mesh.GetBdrFaceTransformations(parent_bdr_e); + auto& parent_mesh = const_cast( parent_mesh_ ); + auto& face_el_tr = *parent_mesh.GetBdrFaceTransformations( parent_bdr_e ); auto mask = face_el_tr.GetConfigurationMask(); - auto parent_e = (mask & mfem::FaceElementTransformations::HAVE_ELEM1) ? - face_el_tr.Elem1No : - face_el_tr.Elem2No; - - // Step 2 + auto parent_e = ( mask & mfem::FaceElementTransformations::HAVE_ELEM1 ) ? face_el_tr.Elem1No : face_el_tr.Elem2No; + + // Step 2 // normal = (dx/dxi x dx/deta) / || dx/dxi x dx/deta || on parent volume boundary element centroid auto& parent_fes = *coords_.GetParentGridFn().ParFESpace(); mfem::Array be_dofs; - parent_fes.GetBdrElementDofs(parent_bdr_e, be_dofs); - mfem::DenseMatrix elem_coords(parent_mesh_.Dimension(), be_dofs.Size()); - for (int d{0}; d < parent_mesh_.Dimension(); ++d) - { - mfem::Array be_vdofs(be_dofs); - parent_fes.DofsToVDofs(d, be_vdofs); - mfem::Vector elemvect(be_dofs.Size()); - coords_.GetParentGridFn().GetSubVector(be_vdofs, elemvect); - elem_coords.SetRow(d, elemvect); + parent_fes.GetBdrElementDofs( parent_bdr_e, be_dofs ); + mfem::DenseMatrix elem_coords( parent_mesh_.Dimension(), be_dofs.Size() ); + for ( int d{ 0 }; d < parent_mesh_.Dimension(); ++d ) { + mfem::Array be_vdofs( be_dofs ); + parent_fes.DofsToVDofs( d, be_vdofs ); + mfem::Vector elemvect( be_dofs.Size() ); + coords_.GetParentGridFn().GetSubVector( be_vdofs, elemvect ); + elem_coords.SetRow( d, elemvect ); } - auto& be = *parent_fes.GetBE(parent_bdr_e); + auto& be = *parent_fes.GetBE( parent_bdr_e ); // create an integration point at the element centroid mfem::IntegrationPoint ip; - ip.Init(0); - mfem::DenseMatrix dshape(be_dofs.Size(), submesh_.Dimension()); + ip.Init( 0 ); + mfem::DenseMatrix dshape( be_dofs.Size(), submesh_.Dimension() ); // calculate shape function derivatives at the surface element centroid - be.CalcDShape(ip, dshape); - mfem::DenseMatrix dxdxi_mat(parent_mesh_.Dimension(), submesh_.Dimension()); - mfem::Mult(elem_coords, dshape, dxdxi_mat); - mfem::Vector norm(parent_mesh_.Dimension()); - mfem::CalcOrtho(dxdxi_mat, norm); - double h = parent_mesh.GetElementSize(parent_e, norm); + be.CalcDShape( ip, dshape ); + mfem::DenseMatrix dxdxi_mat( parent_mesh_.Dimension(), submesh_.Dimension() ); + mfem::Mult( elem_coords, dshape, dxdxi_mat ); + mfem::Vector norm( parent_mesh_.Dimension() ); + mfem::CalcOrtho( dxdxi_mat, norm ); + double h = parent_mesh.GetElementSize( parent_e, norm ); // Step 3 mfem::Vector quad_val; - submesh_thickness->GetValues(submesh_e, quad_val); + submesh_thickness->GetValues( submesh_e, quad_val ); quad_val[0] = h; } // Step 4 - if (GetLORMesh()) - { - elem_thickness_ = std::make_unique( - new mfem::QuadratureSpace(GetLORMesh(), 0) - ); - elem_thickness_->SetOwnsSpace(true); - for (int lor_e{0}; lor_e < GetLORMesh()->GetNE(); ++lor_e) - { - auto submesh_e = GetLORMesh()->GetRefinementTransforms() - .embeddings[lor_e].parent; + if ( GetLORMesh() ) { + elem_thickness_ = std::make_unique( new mfem::QuadratureSpace( GetLORMesh(), 0 ) ); + elem_thickness_->SetOwnsSpace( true ); + for ( int lor_e{ 0 }; lor_e < GetLORMesh()->GetNE(); ++lor_e ) { + auto submesh_e = GetLORMesh()->GetRefinementTransforms().embeddings[lor_e].parent; mfem::Vector submesh_val; - submesh_thickness->GetValues(submesh_e, submesh_val); + submesh_thickness->GetValues( submesh_e, submesh_val ); mfem::Vector lor_val; - elem_thickness_->GetValues(lor_e, lor_val); + elem_thickness_->GetValues( lor_e, lor_val ); lor_val[0] = submesh_val[0]; } - } - else - { - elem_thickness_ = std::move(submesh_thickness); + } else { + elem_thickness_ = std::move( submesh_thickness ); } } -void MfemMeshData::SetMaterialModulus(mfem::Coefficient& modulus_field) +void MfemMeshData::SetMaterialModulus( mfem::Coefficient& modulus_field ) { material_modulus_ = std::make_unique( - new mfem::QuadratureSpace(GetLORMesh() ? GetLORMesh() : &submesh_, 0) - ); - material_modulus_->SetOwnsSpace(true); + new mfem::QuadratureSpace( GetLORMesh() ? GetLORMesh() : &submesh_, 0 ) ); + material_modulus_->SetOwnsSpace( true ); // TODO: why isn't Project() const? - modulus_field.Project(*material_modulus_); + modulus_field.Project( *material_modulus_ ); } -MfemMeshData::UpdateData::UpdateData( - mfem::ParSubMesh& submesh, - mfem::ParMesh* lor_mesh, - const mfem::ParFiniteElementSpace& parent_fes, - mfem::ParGridFunction& submesh_gridfn, - SubmeshLORTransfer* submesh_lor_xfer, - const std::set& attributes_1, - const std::set& attributes_2 -) -: redecomp_mesh_ { lor_mesh ? - redecomp::RedecompMesh(*lor_mesh) : - redecomp::RedecompMesh(submesh) - }, - vector_xfer_ { parent_fes, submesh_gridfn, submesh_lor_xfer, redecomp_mesh_ } +MfemMeshData::UpdateData::UpdateData( mfem::ParSubMesh& submesh, mfem::ParMesh* lor_mesh, + const mfem::ParFiniteElementSpace& parent_fes, + mfem::ParGridFunction& submesh_gridfn, SubmeshLORTransfer* submesh_lor_xfer, + const std::set& attributes_1, const std::set& attributes_2 ) + : redecomp_mesh_{ lor_mesh ? redecomp::RedecompMesh( *lor_mesh ) : redecomp::RedecompMesh( submesh ) }, + vector_xfer_{ parent_fes, submesh_gridfn, submesh_lor_xfer, redecomp_mesh_ } { // set element type based on redecomp mesh SetElementData(); // updates the connectivity of the tribol surface mesh - UpdateConnectivity(attributes_1, attributes_2); + UpdateConnectivity( attributes_1, attributes_2 ); } -void MfemMeshData::UpdateData::UpdateConnectivity( - const std::set& attributes_1, - const std::set& attributes_2 -) +void MfemMeshData::UpdateData::UpdateConnectivity( const std::set& attributes_1, + const std::set& attributes_2 ) { - conn_1_.reserve(redecomp_mesh_.GetNE() * num_verts_per_elem_); - conn_2_.reserve(redecomp_mesh_.GetNE() * num_verts_per_elem_); - elem_map_1_.reserve(static_cast(redecomp_mesh_.GetNE())); - elem_map_2_.reserve(static_cast(redecomp_mesh_.GetNE())); - for (int e{}; e < redecomp_mesh_.GetNE(); ++e) - { - auto elem_attrib = redecomp_mesh_.GetAttribute(e); + conn_1_.reserve( redecomp_mesh_.GetNE() * num_verts_per_elem_ ); + conn_2_.reserve( redecomp_mesh_.GetNE() * num_verts_per_elem_ ); + elem_map_1_.reserve( static_cast( redecomp_mesh_.GetNE() ) ); + elem_map_2_.reserve( static_cast( redecomp_mesh_.GetNE() ) ); + for ( int e{}; e < redecomp_mesh_.GetNE(); ++e ) { + auto elem_attrib = redecomp_mesh_.GetAttribute( e ); auto elem_conn = mfem::Array(); - redecomp_mesh_.GetElementVertices(e, elem_conn); - for (auto attribute_1 : attributes_1) - { - if (attribute_1 == elem_attrib) - { - elem_map_1_.push_back(e); - conn_1_.resize(elem_map_1_.size(), num_verts_per_elem_); - for (int v{}; v < num_verts_per_elem_; ++v) - { - conn_1_(elem_map_1_.size() - 1, v) = elem_conn[v]; + redecomp_mesh_.GetElementVertices( e, elem_conn ); + for ( auto attribute_1 : attributes_1 ) { + if ( attribute_1 == elem_attrib ) { + elem_map_1_.push_back( e ); + conn_1_.resize( elem_map_1_.size(), num_verts_per_elem_ ); + for ( int v{}; v < num_verts_per_elem_; ++v ) { + conn_1_( elem_map_1_.size() - 1, v ) = elem_conn[v]; } break; } } - for (auto attribute_2 : attributes_2) - { - if (attribute_2 == elem_attrib) - { - elem_map_2_.push_back(e); - conn_2_.resize(elem_map_2_.size(), num_verts_per_elem_); - for (int v{}; v < num_verts_per_elem_; ++v) - { - conn_2_(elem_map_2_.size() - 1, v) = elem_conn[v]; + for ( auto attribute_2 : attributes_2 ) { + if ( attribute_2 == elem_attrib ) { + elem_map_2_.push_back( e ); + conn_2_.resize( elem_map_2_.size(), num_verts_per_elem_ ); + for ( int v{}; v < num_verts_per_elem_; ++v ) { + conn_2_( elem_map_2_.size() - 1, v ) = elem_conn[v]; } break; } @@ -723,48 +554,33 @@ void MfemMeshData::UpdateData::UpdateConnectivity( MfemMeshData::UpdateData& MfemMeshData::GetUpdateData() { - SLIC_ERROR_ROOT_IF( - update_data_ == nullptr, - "UpdateField() must be called to generate UpdateData." - ); + SLIC_ERROR_ROOT_IF( update_data_ == nullptr, "UpdateField() must be called to generate UpdateData." ); return *update_data_; } const MfemMeshData::UpdateData& MfemMeshData::GetUpdateData() const { - SLIC_ERROR_ROOT_IF( - update_data_ == nullptr, - "UpdateField() must be called to generate UpdateData." - ); + SLIC_ERROR_ROOT_IF( update_data_ == nullptr, "UpdateField() must be called to generate UpdateData." ); return *update_data_; } -mfem::ParSubMesh MfemMeshData::CreateSubmesh( - const mfem::ParMesh& parent_mesh, - const std::set& attributes_1, - const std::set& attributes_2 -) +mfem::ParSubMesh MfemMeshData::CreateSubmesh( const mfem::ParMesh& parent_mesh, const std::set& attributes_1, + const std::set& attributes_2 ) { // TODO: Create PR for mfem::ParSubMesh::CreateFromBoundary taking a const // reference to attributes. Then we can construct submesh_ in the initializer // list without this function (because CreateFromBoundary will be willing to // take an rvalue for attributes) - auto attributes_array - = arrayFromSet(mergeContainers(attributes_1, attributes_2)); - return mfem::ParSubMesh::CreateFromBoundary( - parent_mesh, - attributes_array - ); + auto attributes_array = arrayFromSet( mergeContainers( attributes_1, attributes_2 ) ); + return mfem::ParSubMesh::CreateFromBoundary( parent_mesh, attributes_array ); } void MfemMeshData::UpdateData::SetElementData() { - if (redecomp_mesh_.GetNE() > 0) - { - auto element_type = redecomp_mesh_.GetElementType(0); + if ( redecomp_mesh_.GetNE() > 0 ) { + auto element_type = redecomp_mesh_.GetElementType( 0 ); - switch (element_type) - { + switch ( element_type ) { case mfem::Element::SEGMENT: elem_type_ = LINEAR_EDGE; break; @@ -782,132 +598,90 @@ void MfemMeshData::UpdateData::SetElementData() break; case mfem::Element::POINT: - SLIC_ERROR_ROOT("Unsupported element type!"); + SLIC_ERROR_ROOT( "Unsupported element type!" ); break; default: - SLIC_ERROR_ROOT("Unknown element type!"); + SLIC_ERROR_ROOT( "Unknown element type!" ); break; } num_verts_per_elem_ = mfem::Geometry::NumVerts[element_type]; - } - else - { + } else { // just put something here so Tribol will not give a warning for zero element meshes elem_type_ = LINEAR_EDGE; num_verts_per_elem_ = 2; } } -MfemSubmeshData::MfemSubmeshData( - mfem::ParSubMesh& submesh, - mfem::ParMesh* lor_mesh, - std::unique_ptr pressure_fec, - int pressure_vdim -) -: submesh_pressure_ { - new mfem::ParFiniteElementSpace( - &submesh, - pressure_fec.get(), - pressure_vdim - ) - }, - pressure_ { submesh_pressure_ }, - submesh_lor_xfer_ { lor_mesh ? - std::make_unique( - *submesh_pressure_.ParFESpace(), - *lor_mesh - ) : - nullptr - } +MfemSubmeshData::MfemSubmeshData( mfem::ParSubMesh& submesh, mfem::ParMesh* lor_mesh, + std::unique_ptr pressure_fec, int pressure_vdim ) + : submesh_pressure_{ new mfem::ParFiniteElementSpace( &submesh, pressure_fec.get(), pressure_vdim ) }, + pressure_{ submesh_pressure_ }, + submesh_lor_xfer_{ lor_mesh ? std::make_unique( *submesh_pressure_.ParFESpace(), *lor_mesh ) + : nullptr } { - submesh_pressure_.MakeOwner(pressure_fec.release()); + submesh_pressure_.MakeOwner( pressure_fec.release() ); submesh_pressure_ = 0.0; } -void MfemSubmeshData::UpdateMfemSubmeshData(redecomp::RedecompMesh& redecomp_mesh) +void MfemSubmeshData::UpdateMfemSubmeshData( redecomp::RedecompMesh& redecomp_mesh ) { - update_data_ = std::make_unique( - *submesh_pressure_.ParFESpace(), - submesh_lor_xfer_.get(), - redecomp_mesh - ); - pressure_.UpdateField(update_data_->pressure_xfer_); - redecomp_gap_.SetSpace(pressure_.GetRedecompGridFn().FESpace()); + update_data_ = + std::make_unique( *submesh_pressure_.ParFESpace(), submesh_lor_xfer_.get(), redecomp_mesh ); + pressure_.UpdateField( update_data_->pressure_xfer_ ); + redecomp_gap_.SetSpace( pressure_.GetRedecompGridFn().FESpace() ); redecomp_gap_ = 0.0; } -void MfemSubmeshData::GetSubmeshGap(mfem::Vector& g) const +void MfemSubmeshData::GetSubmeshGap( mfem::Vector& g ) const { - g.SetSize(submesh_pressure_.ParFESpace()->GetVSize()); + g.SetSize( submesh_pressure_.ParFESpace()->GetVSize() ); g = 0.0; - GetPressureTransfer().RedecompToSubmesh(redecomp_gap_, g); + GetPressureTransfer().RedecompToSubmesh( redecomp_gap_, g ); } -MfemSubmeshData::UpdateData::UpdateData( - mfem::ParFiniteElementSpace& submesh_fes, - SubmeshLORTransfer* submesh_lor_xfer, - redecomp::RedecompMesh& redecomp_mesh -) -: pressure_xfer_ { submesh_fes, submesh_lor_xfer, redecomp_mesh } -{} +MfemSubmeshData::UpdateData::UpdateData( mfem::ParFiniteElementSpace& submesh_fes, SubmeshLORTransfer* submesh_lor_xfer, + redecomp::RedecompMesh& redecomp_mesh ) + : pressure_xfer_{ submesh_fes, submesh_lor_xfer, redecomp_mesh } +{ +} MfemSubmeshData::UpdateData& MfemSubmeshData::GetUpdateData() { - SLIC_ERROR_ROOT_IF( - update_data_ == nullptr, - "UpdateField() must be called to generate UpdateData." - ); + SLIC_ERROR_ROOT_IF( update_data_ == nullptr, "UpdateField() must be called to generate UpdateData." ); return *update_data_; } const MfemSubmeshData::UpdateData& MfemSubmeshData::GetUpdateData() const { - SLIC_ERROR_ROOT_IF( - update_data_ == nullptr, - "UpdateField() must be called to generate UpdateData." - ); + SLIC_ERROR_ROOT_IF( update_data_ == nullptr, "UpdateField() must be called to generate UpdateData." ); return *update_data_; } -MfemJacobianData::MfemJacobianData( - const MfemMeshData& parent_data, - const MfemSubmeshData& submesh_data, - ContactMethod contact_method -) -: parent_data_ { parent_data }, - submesh_data_ { submesh_data }, - block_offsets_ { 3 } +MfemJacobianData::MfemJacobianData( const MfemMeshData& parent_data, const MfemSubmeshData& submesh_data, + ContactMethod contact_method ) + : parent_data_{ parent_data }, submesh_data_{ submesh_data }, block_offsets_{ 3 } { - SLIC_ERROR_ROOT_IF( - parent_data.GetParentCoords().ParFESpace()->FEColl()->GetOrder() > 1, - "Higher order meshes not yet supported for Jacobian matrices." - ); + SLIC_ERROR_ROOT_IF( parent_data.GetParentCoords().ParFESpace()->FEColl()->GetOrder() > 1, + "Higher order meshes not yet supported for Jacobian matrices." ); // NOTE: Looks like GetFrom() should be const in MFEM - mfem::SubMeshUtils::BuildVdofToVdofMap( - parent_data_.GetSubmeshFESpace(), - *parent_data_.GetParentCoords().FESpace(), - const_cast(parent_data_.GetSubmesh()).GetFrom(), - parent_data_.GetSubmesh().GetParentElementIDMap(), - submesh2parent_vdof_list_ - ); - - int my_rank {0}; - MPI_Comm_rank(TRIBOL_COMM_WORLD, &my_rank); + mfem::SubMeshUtils::BuildVdofToVdofMap( parent_data_.GetSubmeshFESpace(), *parent_data_.GetParentCoords().FESpace(), + const_cast( parent_data_.GetSubmesh() ).GetFrom(), + parent_data_.GetSubmesh().GetParentElementIDMap(), + submesh2parent_vdof_list_ ); + + int my_rank{ 0 }; + MPI_Comm_rank( TRIBOL_COMM_WORLD, &my_rank ); auto offset_idx = HYPRE_AssumedPartitionCheck() ? 0 : my_rank; - auto dof_offset = parent_data_.GetParentCoords().ParFESpace()-> - GetDofOffsets()[offset_idx]; - for (auto& vdof : submesh2parent_vdof_list_) - { + auto dof_offset = parent_data_.GetParentCoords().ParFESpace()->GetDofOffsets()[offset_idx]; + for ( auto& vdof : submesh2parent_vdof_list_ ) { vdof = vdof + dof_offset; } - auto disp_size = parent_data_.GetParentCoords().ParFESpace() - ->GetTrueVSize(); - auto lm_size = submesh_data_.GetSubmeshPressure().ParFESpace() - ->GetTrueVSize(); + auto disp_size = parent_data_.GetParentCoords().ParFESpace()->GetTrueVSize(); + auto lm_size = submesh_data_.GetSubmeshPressure().ParFESpace()->GetTrueVSize(); block_offsets_[0] = 0; block_offsets_[1] = disp_size; block_offsets_[2] = disp_size + lm_size; @@ -915,51 +689,46 @@ MfemJacobianData::MfemJacobianData( // Rows/columns of pressure/gap DOFs only on the mortar surface need to be // eliminated from the Jacobian when using single mortar. The code in this // block creates a list of the true DOFs only on the mortar surface. - if (contact_method == SINGLE_MORTAR) - { + if ( contact_method == SINGLE_MORTAR ) { // Get submesh auto& submesh_fe_space = submesh_data_.GetSubmeshFESpace(); auto& submesh = parent_data_.GetSubmesh(); // Create marker of attributes for faster querying - mfem::Array attr_marker(submesh.attributes.Max()); + mfem::Array attr_marker( submesh.attributes.Max() ); attr_marker = 0; - for (auto nonmortar_attr : parent_data_.GetBoundaryAttribs2()) - { + for ( auto nonmortar_attr : parent_data_.GetBoundaryAttribs2() ) { attr_marker[nonmortar_attr - 1] = 1; } // Create marker of dofs only on mortar surface - mfem::Array mortar_dof_marker(submesh_fe_space.GetVSize()); + mfem::Array mortar_dof_marker( submesh_fe_space.GetVSize() ); mortar_dof_marker = 1; - for (int e{0}; e < submesh.GetNE(); ++e) - { - if (attr_marker[submesh_fe_space.GetAttribute(e) - 1]) - { + for ( int e{ 0 }; e < submesh.GetNE(); ++e ) { + if ( attr_marker[submesh_fe_space.GetAttribute( e ) - 1] ) { mfem::Array vdofs; - submesh_fe_space.GetElementVDofs(e, vdofs); - for (int d{0}; d < vdofs.Size(); ++d) - { + submesh_fe_space.GetElementVDofs( e, vdofs ); + for ( int d{ 0 }; d < vdofs.Size(); ++d ) { int k = vdofs[d]; - if (k < 0) { k = -1 - k; } + if ( k < 0 ) { + k = -1 - k; + } mortar_dof_marker[k] = 0; } } } // Convert marker of dofs to marker of tdofs - mfem::Array mortar_tdof_marker(submesh_fe_space.GetTrueVSize()); - submesh_fe_space.GetRestrictionMatrix()->BooleanMult(mortar_dof_marker, mortar_tdof_marker); + mfem::Array mortar_tdof_marker( submesh_fe_space.GetTrueVSize() ); + submesh_fe_space.GetRestrictionMatrix()->BooleanMult( mortar_dof_marker, mortar_tdof_marker ); // Convert markers of tdofs only on mortar surface to a list - mfem::FiniteElementSpace::MarkerToList(mortar_tdof_marker, mortar_tdof_list_); + mfem::FiniteElementSpace::MarkerToList( mortar_tdof_marker, mortar_tdof_list_ ); } } void MfemJacobianData::UpdateJacobianXfer() { - update_data_ = std::make_unique(parent_data_, submesh_data_); + update_data_ = std::make_unique( parent_data_, submesh_data_ ); } -std::unique_ptr MfemJacobianData::GetMfemBlockJacobian( - const MethodData* method_data -) const +std::unique_ptr MfemJacobianData::GetMfemBlockJacobian( const MethodData* method_data ) const { // 0 = displacement DOFs, 1 = lagrange multiplier DOFs // (0,0) block is empty (for now using SINGLE_MORTAR with approximate tangent) @@ -969,240 +738,177 @@ std::unique_ptr MfemJacobianData::GetMfemBlockJacobian( const auto& elem_map_2 = parent_data_.GetElemMap2(); // empty data structures are needed even when no meshes are on rank since TransferToParallelSparse() needs to be // called on all ranks (even those without data) - auto mortar_elems = ArrayT(0, 0); - auto nonmortar_elems = ArrayT(0, 0); - auto lm_elems = ArrayT(0, 0); - auto elem_J_1_ptr = std::make_unique>(0, 0); - auto elem_J_2_ptr = std::make_unique>(0, 0); + auto mortar_elems = ArrayT( 0, 0 ); + auto nonmortar_elems = ArrayT( 0, 0 ); + auto lm_elems = ArrayT( 0, 0 ); + auto elem_J_1_ptr = std::make_unique>( 0, 0 ); + auto elem_J_2_ptr = std::make_unique>( 0, 0 ); const ArrayT* elem_J_1 = elem_J_1_ptr.get(); const ArrayT* elem_J_2 = elem_J_2_ptr.get(); // this means both of the meshes exist - if (method_data != nullptr && !elem_map_1.empty() && !elem_map_2.empty()) - { - mortar_elems = method_data - ->getBlockJElementIds()[static_cast(BlockSpace::MORTAR)]; - for (auto& mortar_elem : mortar_elems) - { - mortar_elem = elem_map_1[static_cast(mortar_elem)]; + if ( method_data != nullptr && !elem_map_1.empty() && !elem_map_2.empty() ) { + mortar_elems = method_data->getBlockJElementIds()[static_cast( BlockSpace::MORTAR )]; + for ( auto& mortar_elem : mortar_elems ) { + mortar_elem = elem_map_1[static_cast( mortar_elem )]; } - nonmortar_elems = method_data - ->getBlockJElementIds()[static_cast(BlockSpace::NONMORTAR)]; - for (auto& nonmortar_elem : nonmortar_elems) - { - nonmortar_elem = elem_map_2[static_cast(nonmortar_elem)]; + nonmortar_elems = method_data->getBlockJElementIds()[static_cast( BlockSpace::NONMORTAR )]; + for ( auto& nonmortar_elem : nonmortar_elems ) { + nonmortar_elem = elem_map_2[static_cast( nonmortar_elem )]; } - lm_elems = method_data - ->getBlockJElementIds()[static_cast(BlockSpace::LAGRANGE_MULTIPLIER)]; - for (auto& lm_elem : lm_elems) - { - lm_elem = elem_map_2[static_cast(lm_elem)]; + lm_elems = method_data->getBlockJElementIds()[static_cast( BlockSpace::LAGRANGE_MULTIPLIER )]; + for ( auto& lm_elem : lm_elems ) { + lm_elem = elem_map_2[static_cast( lm_elem )]; } // get (1,0) block - elem_J_1 = &method_data->getBlockJ()( - static_cast(BlockSpace::LAGRANGE_MULTIPLIER), - static_cast(BlockSpace::MORTAR) - ); - elem_J_2 = &method_data->getBlockJ()( - static_cast(BlockSpace::LAGRANGE_MULTIPLIER), - static_cast(BlockSpace::NONMORTAR) - ); + elem_J_1 = &method_data->getBlockJ()( static_cast( BlockSpace::LAGRANGE_MULTIPLIER ), + static_cast( BlockSpace::MORTAR ) ); + elem_J_2 = &method_data->getBlockJ()( static_cast( BlockSpace::LAGRANGE_MULTIPLIER ), + static_cast( BlockSpace::NONMORTAR ) ); } // move to submesh level - auto submesh_J = GetUpdateData().submesh_redecomp_xfer_->TransferToParallelSparse( - lm_elems, - mortar_elems, - *elem_J_1 - ); - submesh_J += GetUpdateData().submesh_redecomp_xfer_->TransferToParallelSparse( - lm_elems, - nonmortar_elems, - *elem_J_2 - ); + auto submesh_J = + GetUpdateData().submesh_redecomp_xfer_->TransferToParallelSparse( lm_elems, mortar_elems, *elem_J_1 ); + submesh_J += GetUpdateData().submesh_redecomp_xfer_->TransferToParallelSparse( lm_elems, nonmortar_elems, *elem_J_2 ); submesh_J.Finalize(); // transform J values from submesh to parent mesh auto J = submesh_J.GetJ(); auto submesh_vector_fes = parent_data_.GetSubmeshFESpace(); - auto mpi = redecomp::MPIUtility(submesh_vector_fes.GetComm()); - auto submesh_dof_offsets = ArrayT(mpi.NRanks() + 1, mpi.NRanks() + 1); + auto mpi = redecomp::MPIUtility( submesh_vector_fes.GetComm() ); + auto submesh_dof_offsets = ArrayT( mpi.NRanks() + 1, mpi.NRanks() + 1 ); // we need the dof offsets of each rank. check if mfem stores this or if we // need to create it. - if (HYPRE_AssumedPartitionCheck()) - { - submesh_dof_offsets[mpi.MyRank()+1] = submesh_vector_fes.GetDofOffsets()[1]; - mpi.Allreduce(&submesh_dof_offsets, MPI_SUM); - } - else - { - for (int i{0}; i < mpi.NRanks(); ++i) - { - submesh_dof_offsets[i] = submesh_vector_fes.GetDofOffsets()[i]; + if ( HYPRE_AssumedPartitionCheck() ) { + submesh_dof_offsets[mpi.MyRank() + 1] = submesh_vector_fes.GetDofOffsets()[1]; + mpi.Allreduce( &submesh_dof_offsets, MPI_SUM ); + } else { + for ( int i{ 0 }; i < mpi.NRanks(); ++i ) { + submesh_dof_offsets[i] = submesh_vector_fes.GetDofOffsets()[i]; } - } // the submesh to parent vdof map only exists for vdofs on rank, so J values // not on rank will need to be transferred to the rank that the vdof exists on // to query the map. the steps are laid out below. - + // step 1) query J values on rank for their parent vdof and package J values // not on rank to send - auto send_J_by_rank = redecomp::MPIArray(&mpi); - auto J_idx = redecomp::MPIArray(&mpi); + auto send_J_by_rank = redecomp::MPIArray( &mpi ); + auto J_idx = redecomp::MPIArray( &mpi ); auto est_num_J = submesh_J.NumNonZeroElems() / mpi.NRanks(); - for (int r{}; r < mpi.NRanks(); ++r) - { - if (r == mpi.MyRank()) - { - send_J_by_rank[r].shrink(); - J_idx[r].shrink(); - } - else - { - send_J_by_rank[r].reserve(est_num_J); - J_idx[r].reserve(est_num_J); + for ( int r{}; r < mpi.NRanks(); ++r ) { + if ( r == mpi.MyRank() ) { + send_J_by_rank[r].shrink(); + J_idx[r].shrink(); + } else { + send_J_by_rank[r].reserve( est_num_J ); + J_idx[r].reserve( est_num_J ); } - } - for (int j{}; j < submesh_J.NumNonZeroElems(); ++j) - { - if (J[j] >= submesh_dof_offsets[mpi.MyRank()] - && J[j] < submesh_dof_offsets[mpi.MyRank() + 1]) - { - J[j] = submesh2parent_vdof_list_[J[j] - submesh_dof_offsets[mpi.MyRank()]]; - } - else - { - for (int r{}; r < mpi.NRanks(); ++r) - { - if (J[j] >= submesh_dof_offsets[r] && J[j] < submesh_dof_offsets[r + 1]) - { - send_J_by_rank[r].push_back(J[j] - submesh_dof_offsets[r]); - J_idx[r].push_back(j); + for ( int j{}; j < submesh_J.NumNonZeroElems(); ++j ) { + if ( J[j] >= submesh_dof_offsets[mpi.MyRank()] && J[j] < submesh_dof_offsets[mpi.MyRank() + 1] ) { + J[j] = submesh2parent_vdof_list_[J[j] - submesh_dof_offsets[mpi.MyRank()]]; + } else { + for ( int r{}; r < mpi.NRanks(); ++r ) { + if ( J[j] >= submesh_dof_offsets[r] && J[j] < submesh_dof_offsets[r + 1] ) { + send_J_by_rank[r].push_back( J[j] - submesh_dof_offsets[r] ); + J_idx[r].push_back( j ); break; } - } + } } } // step 2) sends the J values to the ranks that own them - auto recv_J_by_rank = redecomp::MPIArray(&mpi); - recv_J_by_rank.SendRecvArrayEach(send_J_by_rank); + auto recv_J_by_rank = redecomp::MPIArray( &mpi ); + recv_J_by_rank.SendRecvArrayEach( send_J_by_rank ); // step 3) query the on-rank map to recover J values - for (int r{}; r < mpi.NRanks(); ++r) - { - for (auto& recv_J : recv_J_by_rank[r]) - { - recv_J = submesh2parent_vdof_list_[recv_J]; + for ( int r{}; r < mpi.NRanks(); ++r ) { + for ( auto& recv_J : recv_J_by_rank[r] ) { + recv_J = submesh2parent_vdof_list_[recv_J]; } } // step 4) send the updated parent J values back and update the J vector - send_J_by_rank.SendRecvArrayEach(recv_J_by_rank); - for (int r{}; r < mpi.NRanks(); ++r) - { - for (int j{}; j < send_J_by_rank[r].size(); ++j) - { - J[J_idx[r][j]] = send_J_by_rank[r][j]; + send_J_by_rank.SendRecvArrayEach( recv_J_by_rank ); + for ( int r{}; r < mpi.NRanks(); ++r ) { + for ( int j{}; j < send_J_by_rank[r].size(); ++j ) { + J[J_idx[r][j]] = send_J_by_rank[r][j]; } } // create block operator - auto block_J = std::make_unique(block_offsets_); + auto block_J = std::make_unique( block_offsets_ ); block_J->owns_blocks = 1; // fill block operator auto& submesh_fes = submesh_data_.GetSubmeshFESpace(); auto& parent_trial_fes = *parent_data_.GetParentCoords().ParFESpace(); - auto J_full = std::make_unique( - mpi.MPIComm(), submesh_fes.GetVSize(), - submesh_fes.GlobalVSize(), parent_trial_fes.GlobalVSize(), - submesh_J.GetI(), submesh_J.GetJ(), submesh_J.GetData(), - submesh_fes.GetDofOffsets(), parent_trial_fes.GetDofOffsets() - ); - auto J_true = std::unique_ptr(mfem::RAP( - submesh_fes.Dof_TrueDof_Matrix(), - J_full.get(), - parent_trial_fes.Dof_TrueDof_Matrix() - )); - + auto J_full = std::make_unique( mpi.MPIComm(), submesh_fes.GetVSize(), + submesh_fes.GlobalVSize(), parent_trial_fes.GlobalVSize(), + submesh_J.GetI(), submesh_J.GetJ(), submesh_J.GetData(), + submesh_fes.GetDofOffsets(), parent_trial_fes.GetDofOffsets() ); + auto J_true = std::unique_ptr( + mfem::RAP( submesh_fes.Dof_TrueDof_Matrix(), J_full.get(), parent_trial_fes.Dof_TrueDof_Matrix() ) ); + // Create ones on diagonal of eliminated mortar tdofs (CSR sparse matrix -> HypreParMatrix) // I vector - mfem::Array rows(submesh_fes.GetTrueVSize() + 1); + mfem::Array rows( submesh_fes.GetTrueVSize() + 1 ); rows = 0; auto mortar_tdofs_ct = 0; - for (int i{0}; i < submesh_fes.GetTrueVSize(); ++i) - { - if (mortar_tdofs_ct < mortar_tdof_list_.Size() && mortar_tdof_list_[mortar_tdofs_ct] == i) - { + for ( int i{ 0 }; i < submesh_fes.GetTrueVSize(); ++i ) { + if ( mortar_tdofs_ct < mortar_tdof_list_.Size() && mortar_tdof_list_[mortar_tdofs_ct] == i ) { ++mortar_tdofs_ct; } rows[i + 1] = mortar_tdofs_ct; } // J vector - mfem::Array mortar_tdofs(mortar_tdof_list_); + mfem::Array mortar_tdofs( mortar_tdof_list_ ); // data vector - mfem::Vector ones(mortar_tdofs_ct); + mfem::Vector ones( mortar_tdofs_ct ); ones = 1.0; - mfem::SparseMatrix inactive_sm( - rows.GetData(), mortar_tdofs.GetData(), ones.GetData(), - submesh_fes.GetTrueVSize(), submesh_fes.GetTrueVSize(), - false, false, true - ); - auto inactive_hpm = std::make_unique( - J_true->GetComm(), J_true->GetGlobalNumRows(), J_true->GetRowStarts(), &inactive_sm - ); + mfem::SparseMatrix inactive_sm( rows.GetData(), mortar_tdofs.GetData(), ones.GetData(), submesh_fes.GetTrueVSize(), + submesh_fes.GetTrueVSize(), false, false, true ); + auto inactive_hpm = std::make_unique( J_true->GetComm(), J_true->GetGlobalNumRows(), + J_true->GetRowStarts(), &inactive_sm ); // Have the mfem::HypreParMatrix manage the data pointers - rows.GetMemory().SetHostPtrOwner(false); - mortar_tdofs.GetMemory().SetHostPtrOwner(false); - ones.GetMemory().SetHostPtrOwner(false); - inactive_sm.SetDataOwner(false); - inactive_hpm->SetOwnerFlags(3, 3, 1); + rows.GetMemory().SetHostPtrOwner( false ); + mortar_tdofs.GetMemory().SetHostPtrOwner( false ); + ones.GetMemory().SetHostPtrOwner( false ); + inactive_sm.SetDataOwner( false ); + inactive_hpm->SetOwnerFlags( 3, 3, 1 ); - block_J->SetBlock(0, 1, J_true->Transpose()); - block_J->SetBlock(1, 0, J_true.release()); - block_J->SetBlock(1, 1, inactive_hpm.release()); + block_J->SetBlock( 0, 1, J_true->Transpose() ); + block_J->SetBlock( 1, 0, J_true.release() ); + block_J->SetBlock( 1, 1, inactive_hpm.release() ); return block_J; } -MfemJacobianData::UpdateData::UpdateData( - const MfemMeshData& parent_data, - const MfemSubmeshData& submesh_data -) +MfemJacobianData::UpdateData::UpdateData( const MfemMeshData& parent_data, const MfemSubmeshData& submesh_data ) { auto dual_submesh_fes = &submesh_data.GetSubmeshFESpace(); auto primal_submesh_fes = &parent_data.GetSubmeshFESpace(); - if (parent_data.GetLORMesh()) - { + if ( parent_data.GetLORMesh() ) { dual_submesh_fes = submesh_data.GetLORMeshFESpace(); primal_submesh_fes = parent_data.GetLORMeshFESpace(); } // create a matrix transfer operator for moving data from redecomp to the // submesh - submesh_redecomp_xfer_ = std::make_unique( - *dual_submesh_fes, - *primal_submesh_fes, - *submesh_data.GetRedecompGap().FESpace(), - *parent_data.GetRedecompResponse().FESpace() - ); + submesh_redecomp_xfer_ = std::make_unique( *dual_submesh_fes, *primal_submesh_fes, + *submesh_data.GetRedecompGap().FESpace(), + *parent_data.GetRedecompResponse().FESpace() ); } MfemJacobianData::UpdateData& MfemJacobianData::GetUpdateData() { - SLIC_ERROR_ROOT_IF( - update_data_ == nullptr, - "UpdateField() must be called to generate UpdateData." - ); + SLIC_ERROR_ROOT_IF( update_data_ == nullptr, "UpdateField() must be called to generate UpdateData." ); return *update_data_; } const MfemJacobianData::UpdateData& MfemJacobianData::GetUpdateData() const { - SLIC_ERROR_ROOT_IF( - update_data_ == nullptr, - "UpdateField() must be called to generate UpdateData." - ); + SLIC_ERROR_ROOT_IF( update_data_ == nullptr, "UpdateField() must be called to generate UpdateData." ); return *update_data_; } -} // end tribol namespace +} // namespace tribol #endif /* BUILD_REDECOMP */ diff --git a/src/tribol/mesh/MfemData.hpp b/src/tribol/mesh/MfemData.hpp index cfcd020e..8790cc82 100644 --- a/src/tribol/mesh/MfemData.hpp +++ b/src/tribol/mesh/MfemData.hpp @@ -21,8 +21,7 @@ #include "tribol/common/Parameters.hpp" #include "tribol/mesh/MethodCouplingData.hpp" -namespace tribol -{ +namespace tribol { /** * @brief Facilitates transfer of fields to/from parent-linked boundary submesh @@ -38,9 +37,8 @@ namespace tribol * Field data on the LOR mesh are stored internally in this class, and accessed * through the GetLORGridFn() method. */ -class SubmeshLORTransfer -{ -public: +class SubmeshLORTransfer { + public: /** * @brief Construct a new SubmeshLORTransfer object * @@ -48,10 +46,7 @@ class SubmeshLORTransfer * boundary submesh * @param lor_mesh LOR mesh */ - SubmeshLORTransfer( - mfem::ParFiniteElementSpace& submesh_fes, - mfem::ParMesh& lor_mesh - ); + SubmeshLORTransfer( mfem::ParFiniteElementSpace& submesh_fes, mfem::ParMesh& lor_mesh ); /** * @brief Transfers data from a higher-order grid function on a parent-linked @@ -63,7 +58,7 @@ class SubmeshLORTransfer * @param submesh_src Source higher-order grid function on the parent-linked * boundary submesh */ - void TransferToLORGridFn(const mfem::ParGridFunction& submesh_src); + void TransferToLORGridFn( const mfem::ParGridFunction& submesh_src ); /** * @brief Transfers data to a higher-order vector on a parent-linked boundary submesh @@ -72,7 +67,7 @@ class SubmeshLORTransfer * * @param submesh_dst Destination higher-order vector on the parent-linked boundary submesh */ - void TransferFromLORVector(mfem::Vector& submesh_dst) const; + void TransferFromLORVector( mfem::Vector& submesh_dst ) const; /** * @brief Transfer grid function on parent-linked boundary submesh to grid @@ -81,56 +76,50 @@ class SubmeshLORTransfer * @param [in] submesh_src Grid function on parent-linked boundary submesh * @param [out] lor_dst Zero-valued grid function on LOR mesh */ - void SubmeshToLOR( - const mfem::ParGridFunction& submesh_src, - mfem::ParGridFunction& lor_dst - ); + void SubmeshToLOR( const mfem::ParGridFunction& submesh_src, mfem::ParGridFunction& lor_dst ); /** * @brief Access the local low-order grid function on the LOR mesh - * - * @return mfem::ParGridFunction& + * + * @return mfem::ParGridFunction& */ mfem::ParGridFunction& GetLORGridFn() { return *lor_gridfn_; } /** * @brief Access the local low-order grid function on the LOR mesh - * - * @return mfem::ParGridFunction& + * + * @return mfem::ParGridFunction& */ const mfem::ParGridFunction& GetLORGridFn() const { return *lor_gridfn_; } /** * @brief Access the local low-order vector on the LOR mesh - * - * @return mfem::Vector& + * + * @return mfem::Vector& */ mfem::Vector& GetLORVector() { return *lor_gridfn_; } /** * @brief Access the local low-order vector on the LOR mesh - * - * @return const mfem::Vector& + * + * @return const mfem::Vector& */ const mfem::Vector& GetLORVector() const { return *lor_gridfn_; } -private: + private: /** - * @brief Create low-order grid function on the LOR mesh - * - * @param lor_mesh LOR mesh - * @param lor_fec Finite element collection to apply to grid function - * @param vdim Vector dimension of the grid function - * @return mfem::ParGridFunction on lor_mesh, with lor_fec and vdim specified - */ + * @brief Create low-order grid function on the LOR mesh + * + * @param lor_mesh LOR mesh + * @param lor_fec Finite element collection to apply to grid function + * @param vdim Vector dimension of the grid function + * @return mfem::ParGridFunction on lor_mesh, with lor_fec and vdim specified + */ static std::unique_ptr CreateLORGridFunction( - mfem::ParMesh& lor_mesh, - std::unique_ptr lor_fec, - int vdim - ); + mfem::ParMesh& lor_mesh, std::unique_ptr lor_fec, int vdim ); /** - * @brief Local low-order grid function on the LOR mesh + * @brief Local low-order grid function on the LOR mesh */ std::unique_ptr lor_gridfn_; @@ -155,9 +144,8 @@ class SubmeshLORTransfer * @note This is used to transfer variables defined at the mfem::ParSubMesh * level (e.g. pressure and gap). */ -class SubmeshRedecompTransfer -{ -public: +class SubmeshRedecompTransfer { + public: /** * @brief Construct a new SubmeshRedecompTransfer object * @@ -167,11 +155,8 @@ class SubmeshRedecompTransfer * using LOR; nullptr otherwise) * @param redecomp_mesh RedecompMesh of the redecomposed contact surface mesh */ - SubmeshRedecompTransfer( - mfem::ParFiniteElementSpace& submesh_fes, - SubmeshLORTransfer* submesh_lor_xfer, - redecomp::RedecompMesh& redecomp_mesh - ); + SubmeshRedecompTransfer( mfem::ParFiniteElementSpace& submesh_fes, SubmeshLORTransfer* submesh_lor_xfer, + redecomp::RedecompMesh& redecomp_mesh ); /** * @brief Transfer grid function on parent-linked boundary submesh to grid @@ -180,10 +165,7 @@ class SubmeshRedecompTransfer * @param [in] submesh_src Grid function on parent-linked boundary submesh * @param [out] redecomp_dst Zero-valued grid function on redecomp mesh */ - void SubmeshToRedecomp( - const mfem::ParGridFunction& submesh_src, - mfem::GridFunction& redecomp_dst - ) const; + void SubmeshToRedecomp( const mfem::ParGridFunction& submesh_src, mfem::GridFunction& redecomp_dst ) const; /** * @brief Transfer grid function on redecomp mesh to vector on parent-linked boundary submesh @@ -195,49 +177,41 @@ class SubmeshRedecompTransfer * @param redecomp_src Grid function on redecomp mesh * @param submesh_dst Zero-valued vector on parent-linked boundary submesh */ - void RedecompToSubmesh( - const mfem::GridFunction& redecomp_src, - mfem::Vector& submesh_dst - ) const; + void RedecompToSubmesh( const mfem::GridFunction& redecomp_src, mfem::Vector& submesh_dst ) const; /** * @brief Get the parent-linked boundary submesh associated with the * SubmeshRedecompTransfer object * - * @return const mfem::ParSubMesh& + * @return const mfem::ParSubMesh& */ - const mfem::ParSubMesh& GetSubmesh() const - { - return static_cast(*submesh_fes_.GetParMesh()); + const mfem::ParSubMesh& GetSubmesh() const + { + return static_cast( *submesh_fes_.GetParMesh() ); } /** * @brief Returns finite element space on the redecomp mesh associated with * this transfer object * - * @return mfem::FiniteElementSpace& + * @return mfem::FiniteElementSpace& */ - mfem::FiniteElementSpace& GetRedecompFESpace() - { - return *redecomp_fes_; - } + mfem::FiniteElementSpace& GetRedecompFESpace() { return *redecomp_fes_; } -private: + private: /** * @brief Create a finite element space on the redecomp mesh * * @param redecomp_mesh RedecompMesh of the redecomposed contact surface mesh * @param submesh_fes Finite element space on the parent-linked boundary * submesh - * @return std::unique_ptr + * @return std::unique_ptr */ - static std::unique_ptr CreateRedecompFESpace( - redecomp::RedecompMesh& redecomp_mesh, - mfem::ParFiniteElementSpace& submesh_fes - ); + static std::unique_ptr CreateRedecompFESpace( redecomp::RedecompMesh& redecomp_mesh, + mfem::ParFiniteElementSpace& submesh_fes ); /** - * @brief Finite element space on the parent-linked boundary submesh + * @brief Finite element space on the parent-linked boundary submesh */ mfem::ParFiniteElementSpace& submesh_fes_; @@ -272,9 +246,8 @@ class SubmeshRedecompTransfer * \---------------------------------------------------/ * handled through SubmeshRedecompTransfer member variable */ -class ParentRedecompTransfer -{ -public: +class ParentRedecompTransfer { + public: /** * @brief Construct a new ParentRedecompTransfer object * @@ -285,12 +258,8 @@ class ParentRedecompTransfer * using LOR; nullptr otherwise) * @param redecomp_mesh RedecompMesh of the redecomposed contact surface mesh */ - ParentRedecompTransfer( - const mfem::ParFiniteElementSpace& parent_fes, - mfem::ParGridFunction& submesh_gridfn, - SubmeshLORTransfer* submesh_lor_xfer, - redecomp::RedecompMesh& redecomp_mesh - ); + ParentRedecompTransfer( const mfem::ParFiniteElementSpace& parent_fes, mfem::ParGridFunction& submesh_gridfn, + SubmeshLORTransfer* submesh_lor_xfer, redecomp::RedecompMesh& redecomp_mesh ); /** * @brief Transfer grid function on parent mesh to grid function on redecomp @@ -299,11 +268,8 @@ class ParentRedecompTransfer * @param [in] parent_src Grid function on parent mesh * @param [out] redecomp_dst Zero-valued grid function on redecomp mesh */ - void ParentToRedecomp( - const mfem::ParGridFunction& parent_src, - mfem::GridFunction& redecomp_dst - ) const; - + void ParentToRedecomp( const mfem::ParGridFunction& parent_src, mfem::GridFunction& redecomp_dst ) const; + /** * @brief Transfer grid function on redecomp mesh to vector on parent mesh * @@ -314,31 +280,25 @@ class ParentRedecompTransfer * @param [in] redecomp_src Grid function on RedecompMesh * @param [out] parent_dst Zero-valued vector on parent mesh */ - void RedecompToParent(const mfem::GridFunction& redecomp_src, mfem::Vector& parent_dst) const; + void RedecompToParent( const mfem::GridFunction& redecomp_src, mfem::Vector& parent_dst ) const; /** * @brief Get the parent-linked boundary submesh finite element space * associated with this transfer object * - * @return const mfem::ParFiniteElementSpace& + * @return const mfem::ParFiniteElementSpace& */ - const mfem::ParFiniteElementSpace& GetSubmeshFESpace() const - { - return *submesh_gridfn_.ParFESpace(); - } + const mfem::ParFiniteElementSpace& GetSubmeshFESpace() const { return *submesh_gridfn_.ParFESpace(); } /** * @brief Returns finite element space on the redecomp mesh associated with * this transfer object * - * @return mfem::FiniteElementSpace& + * @return mfem::FiniteElementSpace& */ - mfem::FiniteElementSpace& GetRedecompFESpace() - { - return submesh_redecomp_xfer_.GetRedecompFESpace(); - } + mfem::FiniteElementSpace& GetRedecompFESpace() { return submesh_redecomp_xfer_.GetRedecompFESpace(); } -private: + private: /** * @brief Finite element space on the parent mesh */ @@ -365,56 +325,49 @@ class ParentRedecompTransfer * * @note Example parent vector fields include displacement and velocity. */ -class ParentField -{ -public: +class ParentField { + public: /** * @brief Construct a new ParentField object - * + * * @param parent Grid function on the parent mesh */ - ParentField(const mfem::ParGridFunction& parent_gridfn); - + ParentField( const mfem::ParGridFunction& parent_gridfn ); + /** * @brief Set a new grid function on the parent mesh - * + * * @param parent Grid function on the parent mesh */ - void SetParentGridFn(const mfem::ParGridFunction& parent_gridfn); + void SetParentGridFn( const mfem::ParGridFunction& parent_gridfn ); /** * @brief Set a new transfer object when the redecomp mesh has been updated * * @param xfer Updated parent mesh to redecomp mesh transfer object */ - void UpdateField(ParentRedecompTransfer& parent_redecomp_xfer); + void UpdateField( ParentRedecompTransfer& parent_redecomp_xfer ); /** * @brief Get the parent grid function - * - * @return const mfem::ParGridFunction& + * + * @return const mfem::ParGridFunction& */ - const mfem::ParGridFunction& GetParentGridFn() const - { - return parent_gridfn_; - } + const mfem::ParGridFunction& GetParentGridFn() const { return parent_gridfn_; } /** * @brief Get the redecomp mesh grid function - * - * @return mfem::GridFunction& + * + * @return mfem::GridFunction& */ mfem::GridFunction& GetRedecompGridFn() { return GetUpdateData().redecomp_gridfn_; } /** * @brief Get the redecomp mesh grid function - * - * @return const mfem::GridFunction& + * + * @return const mfem::GridFunction& */ - const mfem::GridFunction& GetRedecompGridFn() const - { - return GetUpdateData().redecomp_gridfn_; - } + const mfem::GridFunction& GetRedecompGridFn() const { return GetUpdateData().redecomp_gridfn_; } /** * @brief Get pointers to component arrays of the redecomp mesh grid function @@ -424,35 +377,30 @@ class ParentField * @note The third entry is nullptr in two dimensions */ std::vector GetRedecompFieldPtrs() const; - + /** * @brief Get pointers to component arrays of the redecomp mesh grid function - * + * * @param redecomp_gridfn Redecomp mesh grid function * @return std::vector of length 3 * * @note The third entry is nullptr in two dimensions */ - static std::vector GetRedecompFieldPtrs(mfem::GridFunction& redecomp_gridfn); - -private: + static std::vector GetRedecompFieldPtrs( mfem::GridFunction& redecomp_gridfn ); + private: /** * @brief Creates and stores data that changes when the redecomp mesh is * updated */ - struct UpdateData - { + struct UpdateData { /** * @brief Construct a new UpdateData object - * + * * @param parent_redecomp_xfer Parent to redecomp field transfer object * @param parent_gridfn Grid function on the original, parent mesh */ - UpdateData( - ParentRedecompTransfer& parent_redecomp_xfer, - const mfem::ParGridFunction& parent_gridfn - ); + UpdateData( ParentRedecompTransfer& parent_redecomp_xfer, const mfem::ParGridFunction& parent_gridfn ); /** * @brief Parent to redecomp field transfer object @@ -467,15 +415,15 @@ class ParentField /** * @brief Get the UpdateData object - * - * @return UpdateData& + * + * @return UpdateData& */ UpdateData& GetUpdateData(); /** * @brief Get the UpdateData object - * - * @return const UpdateData& + * + * @return const UpdateData& */ const UpdateData& GetUpdateData() const; @@ -499,57 +447,50 @@ class ParentField * This class handles transferring pressure field data to/from representations * used by MFEM from/to representations used by Tribol. */ -class PressureField -{ -public: +class PressureField { + public: /** * @brief Construct a new PressureField object * * @param submesh_gridfn Grid function on the parent-linked boundary submesh */ - PressureField(const mfem::ParGridFunction& submesh_gridfn); + PressureField( const mfem::ParGridFunction& submesh_gridfn ); /** * @brief Sets a new grid function on the parent-linked boundary submesh - * + * * @param submesh_gridfn Grid function on the parent-linked boundary submesh */ - void SetSubmeshField(const mfem::ParGridFunction& submesh_gridfn); + void SetSubmeshField( const mfem::ParGridFunction& submesh_gridfn ); /** * @brief Sets a new transfer object when the redecomp mesh has been updated - * + * * @param xfer Updated submesh to redecomp transfer object */ - void UpdateField(SubmeshRedecompTransfer& submesh_redecomp_xfer); + void UpdateField( SubmeshRedecompTransfer& submesh_redecomp_xfer ); /** * @brief Get the parent-linked boundary submesh grid function - * - * @return const mfem::ParGridFunction& + * + * @return const mfem::ParGridFunction& */ - const mfem::ParGridFunction& GetSubmeshGridFn() const - { - return submesh_gridfn_; - } + const mfem::ParGridFunction& GetSubmeshGridFn() const { return submesh_gridfn_; } /** * @brief Get the redecomp mesh grid function - * - * @return mfem::GridFunction& + * + * @return mfem::GridFunction& */ mfem::GridFunction& GetRedecompGridFn() { return GetUpdateData().redecomp_gridfn_; } /** * @brief Get the redecomp mesh grid function - * - * @return const mfem::GridFunction& + * + * @return const mfem::GridFunction& */ - const mfem::GridFunction& GetRedecompGridFn() const - { - return GetUpdateData().redecomp_gridfn_; - } - + const mfem::GridFunction& GetRedecompGridFn() const { return GetUpdateData().redecomp_gridfn_; } + /** * @brief Get pointers to component arrays of the redecomp mesh grid function * @@ -559,7 +500,7 @@ class PressureField * frictionless contact. */ std::vector GetRedecompFieldPtrs() const; - + /** * @brief Get pointers to component arrays of a redecomp mesh grid function * @@ -569,26 +510,22 @@ class PressureField * @note Unused entries are nullptr. Only the first entry is used with * frictionless contact. */ - static std::vector GetRedecompFieldPtrs(mfem::GridFunction& redecomp_gridfn); + static std::vector GetRedecompFieldPtrs( mfem::GridFunction& redecomp_gridfn ); -private: + private: /** * @brief Creates and stores data that changes when the redecomp mesh is * updated */ - struct UpdateData - { + struct UpdateData { /** * @brief Construct a new UpdateData object * * @param submesh_redecomp_xfer Submesh to redecomp field transfer object * @param submesh_gridfn Grid function on the parent-linked boundary submesh */ - UpdateData( - SubmeshRedecompTransfer& submesh_redecomp_xfer, - const mfem::ParGridFunction& submesh_gridfn - ); - + UpdateData( SubmeshRedecompTransfer& submesh_redecomp_xfer, const mfem::ParGridFunction& submesh_gridfn ); + /** * @brief Submesh to redecomp field transfer object */ @@ -599,18 +536,18 @@ class PressureField */ mfem::GridFunction redecomp_gridfn_; }; - + /** * @brief Get the UpdateData object - * - * @return UpdateData& + * + * @return UpdateData& */ UpdateData& GetUpdateData(); /** * @brief Get the UpdateData object - * - * @return const UpdateData& + * + * @return const UpdateData& */ const UpdateData& GetUpdateData() const; @@ -631,9 +568,8 @@ class PressureField * @brief Stores MFEM and transfer data associated with parent vector fields * (displacement and velocity) */ -class MfemMeshData -{ -public: +class MfemMeshData { + public: /** * @brief Construct a new MfemMeshData object * @@ -647,31 +583,23 @@ class MfemMeshData * @param attributes_2 Mesh boundary attributes identifying surface elements * in the second Tribol registered mesh */ - MfemMeshData( - IndexT mesh_id_1, - IndexT mesh_id_2, - const mfem::ParMesh& parent_mesh, - const mfem::ParGridFunction& current_coords, - std::set&& attributes_1, - std::set&& attributes_2 - ); + MfemMeshData( IndexT mesh_id_1, IndexT mesh_id_2, const mfem::ParMesh& parent_mesh, + const mfem::ParGridFunction& current_coords, std::set&& attributes_1, + std::set&& attributes_2 ); /** * @brief Get coordinate grid function on the parent mesh - * - * @return const mfem::ParGridFunction& + * + * @return const mfem::ParGridFunction& */ - const mfem::ParGridFunction& GetParentCoords() const - { - return coords_.GetParentGridFn(); - } + const mfem::ParGridFunction& GetParentCoords() const { return coords_.GetParentGridFn(); } /** * @brief Sets a new coordinate grid function on the parent mesh - * + * * @param current_coords Coordinate grid function on the parent mesh */ - void SetParentCoords(const mfem::ParGridFunction& current_coords); + void SetParentCoords( const mfem::ParGridFunction& current_coords ); /** * @brief Build a new redecomp mesh and update grid functions on the redecomp @@ -699,49 +627,37 @@ class MfemMeshData /** * @brief Get the number of elements in the first Tribol registered mesh * - * @return int + * @return int */ - int GetMesh1NE() const - { - return GetUpdateData().conn_1_.size() / GetUpdateData().num_verts_per_elem_; - } + int GetMesh1NE() const { return GetUpdateData().conn_1_.size() / GetUpdateData().num_verts_per_elem_; } /** * @brief Get the number of elements in the second Tribol registered mesh * - * @return int + * @return int */ - int GetMesh2NE() const - { - return GetUpdateData().conn_2_.size() / GetUpdateData().num_verts_per_elem_; - } + int GetMesh2NE() const { return GetUpdateData().conn_2_.size() / GetUpdateData().num_verts_per_elem_; } /** * @brief Get the total number of vertices in both Tribol registered meshes * - * @return int + * @return int */ int GetNV() const { return GetUpdateData().redecomp_mesh_.GetNV(); } /** * @brief Get the connectivity for the first Tribol registered mesh * - * @return const IndexType* + * @return const IndexType* */ - const IndexT* GetMesh1Conn() const - { - return GetUpdateData().conn_1_.data(); - } + const IndexT* GetMesh1Conn() const { return GetUpdateData().conn_1_.data(); } /** * @brief Get the connectivity for the second Tribol registered mesh * - * @return const IndexType* + * @return const IndexType* */ - const IndexT* GetMesh2Conn() const - { - return GetUpdateData().conn_2_.data(); - } + const IndexT* GetMesh2Conn() const { return GetUpdateData().conn_2_.data(); } /** * @brief Get the element type for both Tribol registered meshes @@ -758,10 +674,7 @@ class MfemMeshData * * @note The third entry is nullptr in two dimensions */ - std::vector GetRedecompCoordsPtrs() const - { - return coords_.GetRedecompFieldPtrs(); - } + std::vector GetRedecompCoordsPtrs() const { return coords_.GetRedecompFieldPtrs(); } /** * @brief Get pointers to component arrays of the nodal response on the @@ -771,20 +684,14 @@ class MfemMeshData * * @note The third entry is nullptr in two dimensions */ - std::vector GetRedecompResponsePtrs() - { - return ParentField::GetRedecompFieldPtrs(redecomp_response_); - } + std::vector GetRedecompResponsePtrs() { return ParentField::GetRedecompFieldPtrs( redecomp_response_ ); } /** * @brief Get the nodal response grid function on the redecomp mesh - * - * @return const mfem::GridFunction& + * + * @return const mfem::GridFunction& */ - const mfem::GridFunction& GetRedecompResponse() const - { - return redecomp_response_; - } + const mfem::GridFunction& GetRedecompResponse() const { return redecomp_response_; } /** * @brief Get the nodal response vector on the parent mesh @@ -794,28 +701,25 @@ class MfemMeshData * * @param [out] r Pre-allocated, initialized mfem::Vector to which response vector is added */ - void GetParentResponse(mfem::Vector& r) const; + void GetParentResponse( mfem::Vector& r ) const; /** * @brief Get the parent to redecomp grid function transfer object - * - * @return const ParentRedecompTransfer& + * + * @return const ParentRedecompTransfer& */ - const ParentRedecompTransfer& GetParentRedecompTransfer() const - { - return GetUpdateData().vector_xfer_; - } + const ParentRedecompTransfer& GetParentRedecompTransfer() const { return GetUpdateData().vector_xfer_; } /** * @brief Add/replace the parent velocity grid function - * + * * @param velocity Velocity grid function on the parent mesh */ - void SetParentVelocity(const mfem::ParGridFunction& velocity); + void SetParentVelocity( const mfem::ParGridFunction& velocity ); /** * @brief Determine if a velocity grid function has been set - * + * * @return true: Velocity grid function has been set * @return false: Velocity grid function has not been set */ @@ -823,15 +727,12 @@ class MfemMeshData /** * @brief Get pointers to component arrays of the velocity on the RedecompMesh - * + * * @return std::vector of length 3 * * @note The third entry is nullptr in two dimensions */ - std::vector GetRedecompVelocityPtrs() const - { - return velocity_->GetRedecompFieldPtrs(); - } + std::vector GetRedecompVelocityPtrs() const { return velocity_->GetRedecompFieldPtrs(); } /** * @brief Clears all kinematic and rate penalty data @@ -845,103 +746,91 @@ class MfemMeshData /** * @brief Sets the kinematic constant penalty parameter for the first registered Tribol mesh - * + * * @param penalty Penalty value for the first registered Tribol mesh */ - void SetMesh1KinematicConstantPenalty(RealT penalty) - { - kinematic_constant_penalty_1_ = std::make_unique(penalty); + void SetMesh1KinematicConstantPenalty( RealT penalty ) + { + kinematic_constant_penalty_1_ = std::make_unique( penalty ); } /** * @brief Sets the kinematic constant penalty parameter for the second registered Tribol mesh - * + * * @param penalty Penalty value for the second registered Tribol mesh */ - void SetMesh2KinematicConstantPenalty(RealT penalty) - { - kinematic_constant_penalty_2_ = std::make_unique(penalty); + void SetMesh2KinematicConstantPenalty( RealT penalty ) + { + kinematic_constant_penalty_2_ = std::make_unique( penalty ); } /** * @brief Get the kinematic constant penalty parameter for the first registered Tribol mesh * - * @return const RealT* + * @return const RealT* */ - const RealT* GetMesh1KinematicConstantPenalty() const - { - return kinematic_constant_penalty_1_.get(); - } + const RealT* GetMesh1KinematicConstantPenalty() const { return kinematic_constant_penalty_1_.get(); } /** * @brief Get the kinematic constant penalty parameter for the second registered Tribol mesh * - * @return const RealT* + * @return const RealT* */ - const RealT* GetMesh2KinematicConstantPenalty() const - { - return kinematic_constant_penalty_2_.get(); - } + const RealT* GetMesh2KinematicConstantPenalty() const { return kinematic_constant_penalty_2_.get(); } /** * @brief Sets the kinematic penalty scale for the first registered Tribol mesh - * + * * @param scale Penalty scale value for the first registered Tribol mesh */ - void SetMesh1KinematicPenaltyScale(RealT scale) - { - kinematic_penalty_scale_1_ = std::make_unique(scale); - } + void SetMesh1KinematicPenaltyScale( RealT scale ) { kinematic_penalty_scale_1_ = std::make_unique( scale ); } /** * @brief Sets the kinematic penalty scale for the second registered Tribol mesh - * + * * @param scale Penalty scale value for the second registered Tribol mesh */ - void SetMesh2KinematicPenaltyScale(RealT scale) - { - kinematic_penalty_scale_2_ = std::make_unique(scale); - } + void SetMesh2KinematicPenaltyScale( RealT scale ) { kinematic_penalty_scale_2_ = std::make_unique( scale ); } /** * @brief Get the kinematic penalty scale for the first registered Tribol mesh * - * @return const RealT* + * @return const RealT* */ const RealT* GetMesh1KinematicPenaltyScale() const { return kinematic_penalty_scale_1_.get(); } /** * @brief Get the kinematic penalty scale for the second registered Tribol mesh * - * @return const RealT* + * @return const RealT* */ const RealT* GetMesh2KinematicPenaltyScale() const { return kinematic_penalty_scale_2_.get(); } /** * @brief Sets the rate constant penalty for the first registered Tribol mesh - * + * * @param penalty Rate constant penalty value for the first registered Tribol mesh */ - void SetMesh1RateConstantPenalty(RealT penalty) { rate_constant_penalty_1_ = std::make_unique(penalty); } + void SetMesh1RateConstantPenalty( RealT penalty ) { rate_constant_penalty_1_ = std::make_unique( penalty ); } /** * @brief Sets the rate constant penalty for the second registered Tribol mesh - * + * * @param penalty Rate penalty value for the second registered Tribol mesh */ - void SetMesh2RateConstantPenalty(RealT penalty) { rate_constant_penalty_2_ = std::make_unique(penalty); } + void SetMesh2RateConstantPenalty( RealT penalty ) { rate_constant_penalty_2_ = std::make_unique( penalty ); } /** * @brief Get the rate constant penalty for the first registered Tribol mesh * - * @return const RealT* + * @return const RealT* */ const RealT* GetMesh1RateConstantPenalty() const { return rate_constant_penalty_1_.get(); } /** * @brief Get the rate constant penalty for the second registered Tribol mesh * - * @return const RealT* + * @return const RealT* */ const RealT* GetMesh2RateConstantPenalty() const { return rate_constant_penalty_2_.get(); } @@ -951,27 +840,27 @@ class MfemMeshData * * @param ratio Rate ratio for the first registered Tribol mesh */ - void SetMesh1RatePercentPenalty(RealT ratio) { rate_percent_ratio_1_ = std::make_unique(ratio); } + void SetMesh1RatePercentPenalty( RealT ratio ) { rate_percent_ratio_1_ = std::make_unique( ratio ); } /** * @brief Sets the rate penalty as a ratio of the kinematic penalty for the * second registered Tribol mesh - * + * * @param ratio Rate ratio for the second registered Tribol mesh */ - void SetMesh2RatePercentPenalty(RealT ratio) { rate_percent_ratio_2_ = std::make_unique(ratio); } + void SetMesh2RatePercentPenalty( RealT ratio ) { rate_percent_ratio_2_ = std::make_unique( ratio ); } /** * @brief Get the rate penalty ratio for the first registered Tribol mesh * - * @return const RealT* + * @return const RealT* */ const RealT* GetMesh1RatePercentPenalty() const { return rate_percent_ratio_1_.get(); } /** * @brief Get the rate penalty ratio for the second registered Tribol mesh * - * @return const RealT* + * @return const RealT* */ const RealT* GetMesh2RatePercentPenalty() const { return rate_percent_ratio_2_.get(); } @@ -979,73 +868,55 @@ class MfemMeshData * @brief Get a pointer to the element thickness array for the first Tribol * registered mesh * - * @return const RealT* + * @return const RealT* */ - const RealT* GetRedecompElemThickness1() const - { - return tribol_elem_thickness_1_->data(); - } + const RealT* GetRedecompElemThickness1() const { return tribol_elem_thickness_1_->data(); } /** * @brief Get a pointer to the element thickness array for the second Tribol * registered mesh * - * @return const RealT* + * @return const RealT* */ - const RealT* GetRedecompElemThickness2() const - { - return tribol_elem_thickness_2_->data(); - } + const RealT* GetRedecompElemThickness2() const { return tribol_elem_thickness_2_->data(); } /** * @brief Get a pointer to the material modulus array for the first Tribol * registered mesh * - * @return const RealT* + * @return const RealT* */ - const RealT* GetRedecompMaterialModulus1() const - { - return tribol_material_modulus_1_->data(); - } + const RealT* GetRedecompMaterialModulus1() const { return tribol_material_modulus_1_->data(); } /** * @brief Get a pointer to the material modulus array for the second Tribol * registered mesh * - * @return const RealT* + * @return const RealT* */ - const RealT* GetRedecompMaterialModulus2() const - { - return tribol_material_modulus_2_->data(); - } + const RealT* GetRedecompMaterialModulus2() const { return tribol_material_modulus_2_->data(); } /** * @brief Get the map from Tribol registered mesh 1 element indices to * redecomp mesh element indices * - * @return const std::vector& + * @return const std::vector& */ - const std::vector& GetElemMap1() const - { - return GetUpdateData().elem_map_1_; - } + const std::vector& GetElemMap1() const { return GetUpdateData().elem_map_1_; } /** * @brief Get the map from Tribol registered mesh 2 element indices to * redecomp mesh element indices * - * @return const std::vector& + * @return const std::vector& */ - const std::vector& GetElemMap2() const - { - return GetUpdateData().elem_map_2_; - } + const std::vector& GetElemMap2() const { return GetUpdateData().elem_map_2_; } /** * @brief Get the parent-linked boundary submesh containing both contact * surfaces * - * @return mfem::ParSubMesh& + * @return mfem::ParSubMesh& */ mfem::ParSubMesh& GetSubmesh() { return submesh_; } @@ -1053,13 +924,13 @@ class MfemMeshData * @brief Get the parent-linked boundary submesh containing both contact * surfaces * - * @return const mfem::ParSubMesh& + * @return const mfem::ParSubMesh& */ const mfem::ParSubMesh& GetSubmesh() const { return submesh_; } /** * @brief Get the LOR mesh containing both contact surfaces - * + * * @return mfem::ParMesh* * * @note nullptr if no refined mesh exists (polynomial order of parent is 1) @@ -1077,14 +948,11 @@ class MfemMeshData /** * @brief Get the redecomp mesh containing redecomposed contact surfaces - * - * @return redecomp::RedecompMesh& + * + * @return redecomp::RedecompMesh& */ - redecomp::RedecompMesh& GetRedecompMesh() - { - return GetUpdateData().redecomp_mesh_; - } - + redecomp::RedecompMesh& GetRedecompMesh() { return GetUpdateData().redecomp_mesh_; } + /** * @brief Get the set of boundary attributes on the parent mesh corresponding * to surface elements contained in the first Tribol registered mesh @@ -1104,17 +972,14 @@ class MfemMeshData /** * @brief Get the finite element space on the parent-linked boundary submesh * - * @return const mfem::ParFiniteElementSpace& + * @return const mfem::ParFiniteElementSpace& */ - const mfem::ParFiniteElementSpace& GetSubmeshFESpace() const - { - return *submesh_xfer_gridfn_.ParFESpace(); - } + const mfem::ParFiniteElementSpace& GetSubmeshFESpace() const { return *submesh_xfer_gridfn_.ParFESpace(); } /** * @brief Get the finite element space on the LOR mesh * - * @return const mfem::ParFiniteElementSpace* + * @return const mfem::ParFiniteElementSpace* * * @note nullptr if no LOR mesh exists (polynomial order of parent is 1) */ @@ -1128,7 +993,7 @@ class MfemMeshData * * @param lor_factor Number of element subdivisions per dimension */ - void SetLORFactor(int lor_factor); + void SetLORFactor( int lor_factor ); /** * @brief Computes element thicknesses for volume elements attached to the contact surface @@ -1140,15 +1005,14 @@ class MfemMeshData * * @param modulus_field An mfem::Coefficient which spatially evaluates to the material modulus value */ - void SetMaterialModulus(mfem::Coefficient& modulus_field); + void SetMaterialModulus( mfem::Coefficient& modulus_field ); -private: + private: /** * @brief Creates and stores data that changes when the RedecompMesh is * updated */ - struct UpdateData - { + struct UpdateData { /** * @brief Construct a new UpdateData object * @@ -1165,15 +1029,9 @@ class MfemMeshData * @param attributes_2 Set of boundary attributes identifying elements in * the second Tribol registered mesh */ - UpdateData( - mfem::ParSubMesh& submesh, - mfem::ParMesh* lor_mesh, - const mfem::ParFiniteElementSpace& parent_fes, - mfem::ParGridFunction& submesh_gridfn, - SubmeshLORTransfer* submesh_lor_xfer, - const std::set& attributes_1, - const std::set& attributes_2 - ); + UpdateData( mfem::ParSubMesh& submesh, mfem::ParMesh* lor_mesh, const mfem::ParFiniteElementSpace& parent_fes, + mfem::ParGridFunction& submesh_gridfn, SubmeshLORTransfer* submesh_lor_xfer, + const std::set& attributes_1, const std::set& attributes_2 ); /** * @brief Redecomposed boundary element mesh @@ -1210,16 +1068,16 @@ class MfemMeshData std::vector elem_map_2_; /** - * @brief Type of elements on the contact meshes - */ + * @brief Type of elements on the contact meshes + */ InterfaceElementType elem_type_; /** - * @brief Number of vertices on each element in the contact meshes - */ + * @brief Number of vertices on each element in the contact meshes + */ int num_verts_per_elem_; - private: + private: /** * @brief Builds connectivity arrays and redecomp mesh to Tribol registered * mesh element maps @@ -1229,28 +1087,25 @@ class MfemMeshData * @param attributes_2 Set of boundary attributes for the second Tribol * registered mesh */ - void UpdateConnectivity( - const std::set& attributes_1, - const std::set& attributes_2 - ); + void UpdateConnectivity( const std::set& attributes_1, const std::set& attributes_2 ); /** * @brief Sets the number of vertices per element and the element type for the redecomp mesh */ void SetElementData(); }; - + /** * @brief Get the UpdateData object - * - * @return UpdateData& + * + * @return UpdateData& */ UpdateData& GetUpdateData(); /** * @brief Get the UpdateData object - * - * @return const UpdateData& + * + * @return const UpdateData& */ const UpdateData& GetUpdateData() const; @@ -1262,13 +1117,10 @@ class MfemMeshData * in the first Tribol registered mesh * @param attributes_2 Mesh boundary attributes identifying surface elements * in the second Tribol registered mesh - * @return mfem::ParSubMesh + * @return mfem::ParSubMesh */ - static mfem::ParSubMesh CreateSubmesh( - const mfem::ParMesh& parent_mesh, - const std::set& attributes_1, - const std::set& attributes_2 - ); + static mfem::ParSubMesh CreateSubmesh( const mfem::ParMesh& parent_mesh, const std::set& attributes_1, + const std::set& attributes_2 ); /** * @brief First mesh identifier @@ -1429,35 +1281,34 @@ class MfemMeshData /** * @brief Merges two STL containers - * + * * @tparam T container type * @param container_1 First container * @param container_2 Second container * @return T merged container */ template - static T mergeContainers(T container_1, T container_2) + static T mergeContainers( T container_1, T container_2 ) { auto merged = container_1; - merged.insert(container_2.begin(), container_2.end()); + merged.insert( container_2.begin(), container_2.end() ); return merged; } /** * @brief Converts a std::set to an mfem::Array - * + * * @tparam T type held in the set and array * @param orig original set * @return mfem::Array output array holding entries in orig */ template - static mfem::Array arrayFromSet(std::set orig) + static mfem::Array arrayFromSet( std::set orig ) { auto array = mfem::Array(); - array.Reserve(static_cast(orig.size())); - for (const auto& val : orig) - { - array.Append(val); + array.Reserve( static_cast( orig.size() ) ); + for ( const auto& val : orig ) { + array.Append( val ); } return array; } @@ -1465,11 +1316,10 @@ class MfemMeshData /** * @brief Stores MFEM and transfer data associated with parent-linked boundary - * submesh pressure and gap fields + * submesh pressure and gap fields */ -class MfemSubmeshData -{ -public: +class MfemSubmeshData { + public: /** * @brief Construct a new MfemSubmeshData object * @@ -1479,12 +1329,8 @@ class MfemSubmeshData * @param pressure_fec Finite element collection of the pressure field * @param pressure_vdim Vector dimension of the pressure field */ - MfemSubmeshData( - mfem::ParSubMesh& submesh, - mfem::ParMesh* lor_mesh, - std::unique_ptr pressure_fec, - int pressure_vdim - ); + MfemSubmeshData( mfem::ParSubMesh& submesh, mfem::ParMesh* lor_mesh, + std::unique_ptr pressure_fec, int pressure_vdim ); /** * @brief Build a new transfer operator and update redecomp-level grid @@ -1492,7 +1338,7 @@ class MfemSubmeshData * * @param redecomp_mesh Updated redecomp mesh */ - void UpdateMfemSubmeshData(redecomp::RedecompMesh& redecomp_mesh); + void UpdateMfemSubmeshData( redecomp::RedecompMesh& redecomp_mesh ); /** * @brief Get pointers to component arrays of the pressure on the redecomp @@ -1503,53 +1349,38 @@ class MfemSubmeshData * @note Unused entries are nullptr. Only the first entry is used with * frictionless contact. */ - std::vector GetRedecompPressurePtrs() const - { - return pressure_.GetRedecompFieldPtrs(); - } + std::vector GetRedecompPressurePtrs() const { return pressure_.GetRedecompFieldPtrs(); } /** * @brief Get the parent-linked boundary submesh pressure grid function - * - * @return mfem::ParGridFunction& + * + * @return mfem::ParGridFunction& */ - mfem::ParGridFunction& GetSubmeshPressure() - { - return submesh_pressure_; - } + mfem::ParGridFunction& GetSubmeshPressure() { return submesh_pressure_; } /** * @brief Get the parent-linked boundary submesh pressure grid function - * - * @return const mfem::ParGridFunction& + * + * @return const mfem::ParGridFunction& */ - const mfem::ParGridFunction& GetSubmeshPressure() const - { - return submesh_pressure_; - } + const mfem::ParGridFunction& GetSubmeshPressure() const { return submesh_pressure_; } /** * @brief Get pointers to component arrays of the gap on the redecomp mesh - * + * * @return std::vector of length 3 * * @note Unused entries are nullptr. Only the first entry is used with * frictionless contact. */ - std::vector GetRedecompGapPtrs() - { - return PressureField::GetRedecompFieldPtrs(redecomp_gap_); - } + std::vector GetRedecompGapPtrs() { return PressureField::GetRedecompFieldPtrs( redecomp_gap_ ); } /** * @brief Get the gap grid function on the redecomp mesh - * - * @return const mfem::GridFunction& + * + * @return const mfem::GridFunction& */ - const mfem::GridFunction& GetRedecompGap() const - { - return redecomp_gap_; - } + const mfem::GridFunction& GetRedecompGap() const { return redecomp_gap_; } /** * @brief Get the gap vector on the parent-linked boundary submesh @@ -1559,28 +1390,22 @@ class MfemSubmeshData * * @param [out] g Un-initialized mfem::Vector holding the nodal gap values */ - void GetSubmeshGap(mfem::Vector& g) const; + void GetSubmeshGap( mfem::Vector& g ) const; /** * @brief Get the parent-linked boundary submesh to redecomp mesh pressure * transfer object * - * @return const SubmeshRedecompTransfer& + * @return const SubmeshRedecompTransfer& */ - const SubmeshRedecompTransfer& GetPressureTransfer() const - { - return GetUpdateData().pressure_xfer_; - } + const SubmeshRedecompTransfer& GetPressureTransfer() const { return GetUpdateData().pressure_xfer_; } /** * @brief Get the finite element space on the parent-linked boundary submesh * - * @return const mfem::ParFiniteElementSpace& + * @return const mfem::ParFiniteElementSpace& */ - const mfem::ParFiniteElementSpace& GetSubmeshFESpace() const - { - return *submesh_pressure_.ParFESpace(); - } + const mfem::ParFiniteElementSpace& GetSubmeshFESpace() const { return *submesh_pressure_.ParFESpace(); } /** * @brief Get the finite element space on the LOR mesh @@ -1593,13 +1418,12 @@ class MfemSubmeshData return submesh_lor_xfer_ ? submesh_lor_xfer_->GetLORGridFn().ParFESpace() : nullptr; } -private: + private: /** * @brief Creates and stores data that changes when the redecomp mesh is * updated */ - struct UpdateData - { + struct UpdateData { /** * @brief Construct a new UpdateData object * @@ -1609,11 +1433,8 @@ class MfemSubmeshData * using LOR; nullptr otherwise) * @param redecomp Redecomp mesh */ - UpdateData( - mfem::ParFiniteElementSpace& submesh_fes, - SubmeshLORTransfer* submesh_lor_xfer, - redecomp::RedecompMesh& redecomp_mesh - ); + UpdateData( mfem::ParFiniteElementSpace& submesh_fes, SubmeshLORTransfer* submesh_lor_xfer, + redecomp::RedecompMesh& redecomp_mesh ); /** * @brief Parent-linked boundary submesh to redecomp mesh field transfer @@ -1624,15 +1445,15 @@ class MfemSubmeshData /** * @brief Get the UpdateData object - * - * @return UpdateData& + * + * @return UpdateData& */ UpdateData& GetUpdateData(); /** * @brief Get the UpdateData object - * - * @return const UpdateData& + * + * @return const UpdateData& */ const UpdateData& GetUpdateData() const; @@ -1666,20 +1487,16 @@ class MfemSubmeshData /** * @brief Simplifies transfer of Jacobian matrix data between MFEM and Tribol */ -class MfemJacobianData -{ -public: +class MfemJacobianData { + public: /** * @brief Construct a new MfemJacobianData object - * + * * @param parent_data MFEM data associated with displacement and velocity * @param submesh_data MFEM data associated with pressure and gap */ - MfemJacobianData( - const MfemMeshData& parent_data, - const MfemSubmeshData& submesh_data, - ContactMethod contact_method - ); + MfemJacobianData( const MfemMeshData& parent_data, const MfemSubmeshData& submesh_data, + ContactMethod contact_method ); /** * @brief Builds new transfer data after a new redecomp mesh has been built @@ -1688,31 +1505,25 @@ class MfemJacobianData /** * @brief Returns Jacobian contributions as an mfem::BlockOperator - * + * * @param method_data Method data holding element Jacobians - * @return std::unique_ptr + * @return std::unique_ptr */ - std::unique_ptr GetMfemBlockJacobian( - const MethodData* method_data - ) const; + std::unique_ptr GetMfemBlockJacobian( const MethodData* method_data ) const; -private: + private: /** * @brief Creates and stores data that changes when the redecomp mesh is * updated */ - struct UpdateData - { + struct UpdateData { /** * @brief Construct a new UpdateData object - * + * * @param parent_data MFEM data associated with displacement and velocity * @param submesh_data MFEM data associated with pressure and gap */ - UpdateData( - const MfemMeshData& parent_data, - const MfemSubmeshData& submesh_data - ); + UpdateData( const MfemMeshData& parent_data, const MfemSubmeshData& submesh_data ); /** * @brief Redecomp to parent-linked boundary submesh transfer operator @@ -1722,15 +1533,15 @@ class MfemJacobianData /** * @brief Get the UpdateData object - * - * @return UpdateData& + * + * @return UpdateData& */ UpdateData& GetUpdateData(); /** * @brief Get the UpdateData object - * - * @return const UpdateData& + * + * @return const UpdateData& */ const UpdateData& GetUpdateData() const; @@ -1766,7 +1577,7 @@ class MfemJacobianData std::unique_ptr update_data_; }; -} // end namespace tribol +} // end namespace tribol #endif /* BUILD_REDECOMP */ diff --git a/src/tribol/physics/AlignedMortar.cpp b/src/tribol/physics/AlignedMortar.cpp index 1cbfe636..a46c07d7 100644 --- a/src/tribol/physics/AlignedMortar.cpp +++ b/src/tribol/physics/AlignedMortar.cpp @@ -21,481 +21,439 @@ #include #include -namespace tribol -{ +namespace tribol { -void ComputeAlignedMortarWeights( SurfaceContactElem & elem ) +void ComputeAlignedMortarWeights( SurfaceContactElem& elem ) { - // NOTE: The aligned case may not need to make use of the - // full-up mortar weights. - - // instantiate integration object - IntegPts integ; - - // call Gauss quadrature integration rule on quads - GaussPolyIntQuad( elem, integ, 2 ); - - // allocate mortar weights array on SurfaceContactElem object. This routine - // also initializes the array - elem.allocateMortarWts(); - - RealT phiNonmortarA, phiNonmortarB, phiMortarA; - - // loop over nodes "a", where node "a" can be a nonmortar node or a mortar node - for (int a=0; a elem.numWts || mortarNonmortarId > elem.numWts, - "ComputeAlignedMortarWeights: integer ids for weights exceed elem.numWts"); - - // compute nonmortar/nonmortar mortar weight - elem.mortarWts[ nonmortarNonmortarId ] += integ.wts[ip] * phiNonmortarA * phiNonmortarB; - - // compute nonmortar/mortar mortar weight - elem.mortarWts[ mortarNonmortarId ] += integ.wts[ip] * phiMortarA * phiNonmortarB; - - } // end loop over integration points - } // end loop over nodes on side 2 - } // end loop over nodes on side 1 - -} // end ComputeAlignedMortarWeights() + // NOTE: The aligned case may not need to make use of the + // full-up mortar weights. + + // instantiate integration object + IntegPts integ; + + // call Gauss quadrature integration rule on quads + GaussPolyIntQuad( elem, integ, 2 ); + + // allocate mortar weights array on SurfaceContactElem object. This routine + // also initializes the array + elem.allocateMortarWts(); + + RealT phiNonmortarA, phiNonmortarB, phiMortarA; + + // loop over nodes "a", where node "a" can be a nonmortar node or a mortar node + for ( int a = 0; a < elem.numFaceVert; ++a ) { + // loop over nonmortar nodes + for ( int b = 0; b < elem.numFaceVert; ++b ) { + // loop over number of integration points + for ( int ip = 0; ip < integ.numIPs; ++ip ) { + RealT xi[2]; + xi[0] = integ.xy[integ.ipDim * ip]; + xi[1] = integ.xy[integ.ipDim * ip + 1]; + LinIsoQuadShapeFunc( xi[0], xi[1], a, phiMortarA ); + LinIsoQuadShapeFunc( xi[0], xi[1], a, phiNonmortarA ); + LinIsoQuadShapeFunc( xi[0], xi[1], b, phiNonmortarB ); + + // set nonmortar/nonmortar and nonmortar/mortar ids + int nonmortarNonmortarId = elem.numFaceVert * a + b; + int mortarNonmortarId = elem.numFaceVert * elem.numFaceVert + elem.numFaceVert * a + b; + + SLIC_ERROR_IF( nonmortarNonmortarId > elem.numWts || mortarNonmortarId > elem.numWts, + "ComputeAlignedMortarWeights: integer ids for weights exceed elem.numWts" ); + + // compute nonmortar/nonmortar mortar weight + elem.mortarWts[nonmortarNonmortarId] += integ.wts[ip] * phiNonmortarA * phiNonmortarB; + + // compute nonmortar/mortar mortar weight + elem.mortarWts[mortarNonmortarId] += integ.wts[ip] * phiMortarA * phiNonmortarB; + + } // end loop over integration points + } // end loop over nodes on side 2 + } // end loop over nodes on side 1 + +} // end ComputeAlignedMortarWeights() //------------------------------------------------------------------------------ -template< > -void ComputeNodalGap< ALIGNED_MORTAR >( SurfaceContactElem & elem ) +template <> +void ComputeNodalGap( SurfaceContactElem& elem ) { - // get pointer to mesh view to store gaps on mesh data object - auto& nonmortarMesh = *elem.m_mesh2; - const IndexT * const nonmortarConn = nonmortarMesh.getConnectivity().data(); - - SLIC_ERROR_IF(nonmortarMesh.getNodalFields().m_node_gap.empty(), - "ComputeNodalGap< ALIGNED_MORTAR >: allocate gaps on mesh data object."); - - // compute gap contributions associated with face 2 on the SurfaceContactElem - // (i.e. nonmortar surface) - - // set the distance magnitude tolerance as the longest edge of - // the mortar face - RealT magTol; - RealT magTest = 0.; - for (int k=0; k magTest) ? mag : magTest; - magTest = mag; - } - - // loop over nodes on nonmortar side - for (int a=0; a: allocate gaps on mesh data object." ); + + // compute gap contributions associated with face 2 on the SurfaceContactElem + // (i.e. nonmortar surface) + + // set the distance magnitude tolerance as the longest edge of + // the mortar face + RealT magTol; + RealT magTest = 0.; + for ( int k = 0; k < elem.numFaceVert; ++k ) { + int idPlus = ( k == ( elem.numFaceVert - 1 ) ) ? 0 : k + 1; + RealT dx = elem.faceCoords1[elem.dim * idPlus] - elem.faceCoords1[elem.dim * k]; + RealT dy = elem.faceCoords1[elem.dim * idPlus + 1] - elem.faceCoords1[elem.dim * k + 1]; + RealT dz = elem.faceCoords1[elem.dim * idPlus + 2] - elem.faceCoords1[elem.dim * k + 2]; + + RealT mag = magnitude( dx, dy, dz ); + + magTol = ( mag > magTest ) ? mag : magTest; + magTest = mag; + } + + // loop over nodes on nonmortar side + for ( int a = 0; a < elem.numFaceVert; ++a ) { + // get global nonmortar node number from connectivity + RealT nrml_a[elem.dim]; + int glbId = nonmortarConn[elem.numFaceVert * elem.faceId2 + a]; + nrml_a[0] = nonmortarMesh.getNodalNormals()[0][glbId]; + nrml_a[1] = nonmortarMesh.getNodalNormals()[1][glbId]; + if ( elem.dim == 3 ) { + nrml_a[2] = nonmortarMesh.getNodalNormals()[2][glbId]; + } + + ////////////////////////////////////////////// + // determine which mortar node is aligned with + // nonmortar node "a" + ////////////////////////////////////////////// + int mortarNodeId; + RealT v[3] = { 0., 0., 0. }; + RealT magTest = magTol; + // loop over nodes on the mortar side + for ( int b = 0; b < elem.numFaceVert; ++b ) { + v[0] = elem.faceCoords1[elem.dim * b] - elem.faceCoords2[elem.dim * a]; + v[1] = elem.faceCoords1[elem.dim * b + 1] - elem.faceCoords2[elem.dim * a + 1]; + v[2] = elem.faceCoords1[elem.dim * b + 2] - elem.faceCoords2[elem.dim * a + 2]; + + RealT magV = magnitude( v[0], v[1], v[2] ); + + if ( magV < magTest ) { + mortarNodeId = b; + magTest = magV; } + } - ////////////////////////////////////////////// - // determine which mortar node is aligned with - // nonmortar node "a" - ////////////////////////////////////////////// - int mortarNodeId; - RealT v[3] = {0., 0., 0.}; - RealT magTest = magTol; - // loop over nodes on the mortar side - for (int b=0; b() +} // end of ComputeNodalGap<>() //------------------------------------------------------------------------------ void ComputeAlignedMortarGaps( CouplingScheme* cs ) { - MeshManager& meshManager = MeshManager::getInstance(); - MeshData& nonmortarMeshData = meshManager.at( cs->getMeshId2() ); - int const dim = cs->spatialDimension(); - // compute nodal normals (do this outside the element loop) - // This routine is guarded against a null mesh - nonmortarMeshData.computeNodalNormals( dim ); - - auto pairs = cs->getInterfacePairs(); - const IndexT numPairs = pairs.size(); - auto planes = cs->get3DContactPlanes(); - - - //////////////////////////////////////////////////////////////////////// - // - // Grab mesh views - // - //////////////////////////////////////////////////////////////////////// - auto mortarMesh = cs->getMesh1().getView(); - auto nonmortarMesh = cs->getMesh2().getView(); - - const IndexT numNodesPerFace = mortarMesh.numberOfNodesPerElement(); - - RealT const * const x1 = mortarMesh.getPosition()[0].data(); - RealT const * const y1 = mortarMesh.getPosition()[1].data(); - RealT const * const z1 = mortarMesh.getPosition()[2].data(); - const IndexT * const mortarConn= mortarMesh.getConnectivity().data(); - - RealT const * const x2 = nonmortarMesh.getPosition()[0].data(); - RealT const * const y2 = nonmortarMesh.getPosition()[1].data(); - RealT const * const z2 = nonmortarMesh.getPosition()[2].data(); - const IndexT * nonmortarConn = nonmortarMesh.getConnectivity().data(); - - - // declare local variables to hold face nodal coordinates - // and overlap vertex coordinates - RealT mortarX[ dim * numNodesPerFace ]; - RealT nonmortarX[ dim * numNodesPerFace ]; - - //////////////////////////// - // compute nonmortar gaps // - //////////////////////////// - int cpID = 0; - for (IndexT kp = 0; kp < numPairs; ++kp) - { - auto& pair = pairs[kp]; - - if (!pair.m_is_contact_candidate) - { - continue; - } + MeshManager& meshManager = MeshManager::getInstance(); + MeshData& nonmortarMeshData = meshManager.at( cs->getMeshId2() ); + int const dim = cs->spatialDimension(); + // compute nodal normals (do this outside the element loop) + // This routine is guarded against a null mesh + nonmortarMeshData.computeNodalNormals( dim ); + + auto pairs = cs->getInterfacePairs(); + const IndexT numPairs = pairs.size(); + auto planes = cs->get3DContactPlanes(); + + //////////////////////////////////////////////////////////////////////// + // + // Grab mesh views + // + //////////////////////////////////////////////////////////////////////// + auto mortarMesh = cs->getMesh1().getView(); + auto nonmortarMesh = cs->getMesh2().getView(); + + const IndexT numNodesPerFace = mortarMesh.numberOfNodesPerElement(); + + RealT const* const x1 = mortarMesh.getPosition()[0].data(); + RealT const* const y1 = mortarMesh.getPosition()[1].data(); + RealT const* const z1 = mortarMesh.getPosition()[2].data(); + const IndexT* const mortarConn = mortarMesh.getConnectivity().data(); + + RealT const* const x2 = nonmortarMesh.getPosition()[0].data(); + RealT const* const y2 = nonmortarMesh.getPosition()[1].data(); + RealT const* const z2 = nonmortarMesh.getPosition()[2].data(); + const IndexT* nonmortarConn = nonmortarMesh.getConnectivity().data(); + + // declare local variables to hold face nodal coordinates + // and overlap vertex coordinates + RealT mortarX[dim * numNodesPerFace]; + RealT nonmortarX[dim * numNodesPerFace]; + + //////////////////////////// + // compute nonmortar gaps // + //////////////////////////// + int cpID = 0; + for ( IndexT kp = 0; kp < numPairs; ++kp ) { + auto& pair = pairs[kp]; + + if ( !pair.m_is_contact_candidate ) { + continue; + } + + auto& plane = planes[cpID]; + + // get pair indices + IndexT index1 = pair.m_element_id1; + IndexT index2 = pair.m_element_id2; + + // populate nodal coordinates array. For the aligned mortar + // gap equations, the nodal coordinates are NOT to be projected + // onto the common plane, since the aligned mortar gap + // calculation uses the current configuration nodal coordinates + // themselves + for ( int i = 0; i < numNodesPerFace; ++i ) { + int id = dim * i; + mortarX[id] = x1[mortarConn[numNodesPerFace * index1 + i]]; + mortarX[id + 1] = y1[mortarConn[numNodesPerFace * index1 + i]]; + mortarX[id + 2] = z1[mortarConn[numNodesPerFace * index1 + i]]; + nonmortarX[id] = x2[nonmortarConn[numNodesPerFace * index2 + i]]; + nonmortarX[id + 1] = y2[nonmortarConn[numNodesPerFace * index2 + i]]; + nonmortarX[id + 2] = z2[nonmortarConn[numNodesPerFace * index2 + i]]; + } + + // construct array of polygon overlap vertex coordinates + ArrayT overlapX( plane.m_numPolyVert, dim ); + for ( IndexT i{ 0 }; i < plane.m_numPolyVert; ++i ) { + overlapX( i, 0 ) = plane.m_polyX[i]; + overlapX( i, 1 ) = plane.m_polyY[i]; + overlapX( i, 2 ) = plane.m_polyZ[i]; + } + + // instantiate SurfaceContactElem struct. Note, this is done with + // the projected area of overlap, but with the actual current + // configuration face coordinates. We need the current + // configuration face coordinates here in order to correctly + // compute the mortar gaps. + SurfaceContactElem elem_for_gap( dim, mortarX, nonmortarX, overlapX.data(), numNodesPerFace, plane.m_numPolyVert, + &mortarMesh, &nonmortarMesh, index1, index2 ); + + ///////////////////////// + // compute mortar gaps // + ///////////////////////// + ComputeNodalGap( elem_for_gap ); + + // HAVE TO set the number of active constraints. For now set to + // all nonmortar face nodes. + elem_for_gap.numActiveGaps = numNodesPerFace; + + ++cpID; + + } // end loop over pairs for nonmortar gap calculations + +} // end ComputeAlignedMortarGaps() +//------------------------------------------------------------------------------ +template <> +int ApplyNormal( CouplingScheme* cs ) +{ + /////////////////////////////////////////////////////// + // // + // compute single mortar gaps // + // // + // Note, this routine is guarded against a null mesh // + /////////////////////////////////////////////////////// + ComputeAlignedMortarGaps( cs ); + + auto pairs = cs->getInterfacePairs(); + const IndexT numPairs = pairs.size(); + auto planes = cs->get3DContactPlanes(); + + int const dim = cs->spatialDimension(); + + //////////////////////////////////////////////////////////////////////// + // + // Grab mesh views + // + //////////////////////////////////////////////////////////////////////// + auto mortarMesh = cs->getMesh1().getView(); + auto nonmortarMesh = cs->getMesh2().getView(); + + const IndexT numNodesPerFace = mortarMesh.numberOfNodesPerElement(); + + RealT* const fx1 = mortarMesh.getResponse()[0].data(); + RealT* const fy1 = mortarMesh.getResponse()[1].data(); + RealT* const fz1 = mortarMesh.getResponse()[2].data(); + const IndexT* const mortarConn = mortarMesh.getConnectivity().data(); + + RealT* const fx2 = nonmortarMesh.getResponse()[0].data(); + RealT* const fy2 = nonmortarMesh.getResponse()[1].data(); + RealT* const fz2 = nonmortarMesh.getResponse()[2].data(); + const IndexT* nonmortarConn = nonmortarMesh.getConnectivity().data(); + + int numTotalNodes; + int numRows; + if ( !cs->nullMeshes() ) { + numTotalNodes = cs->getNumTotalNodes(); + numRows = dim * numTotalNodes + numTotalNodes; + static_cast( cs->getMethodData() )->allocateMfemSparseMatrix( numRows ); + } + + //////////////////////////////////////////////////////////////// + // compute equilibrium residual and/or Jacobian contributions // + //////////////////////////////////////////////////////////////// + int cpID = 0; + for ( IndexT kp = 0; kp < numPairs; ++kp ) { + auto& pair = pairs[kp]; + + if ( !pair.m_is_contact_candidate ) { + continue; + } + + // get pair indices + IndexT index1 = pair.m_element_id1; + IndexT index2 = pair.m_element_id2; + + ////////////////////////////////// + // compute equilibrium residual // + ////////////////////////////////// + + // loop over face nodes (BOTH MORTAR and NONMORTAR + // contributions). NOTE: mortar weights aren't required for + // the residual contributions for ALIGNED_MORTAR. As a result, + // a SurfaceContactElem is not required + for ( int a = 0; a < numNodesPerFace; ++a ) { + int mortarIdA = mortarConn[index1 * numNodesPerFace + a]; + int nonmortarIdA = nonmortarConn[index2 * numNodesPerFace + a]; + + // Note, we assemble residual for all nonmortar nodes, even if the gap + // is in separation. NOTE: Per testing, we + // assemble ALL nonmortar node contributions for faces with positive + // areas of overlap that have passed the geometric filtering. We judge + // contact activity by gaps AND the pressure solution. + + // note: we don't have to interpolate the nodal pressure for mortar and nonmortar + // sides for the aligned mortar case (i.e. no interpolation necessary for coincident + // mortar and nonmortar nodes). + RealT forceX = nonmortarMesh.getNodalFields().m_node_pressure[nonmortarIdA] * + nonmortarMesh.getNodalNormals()[0][nonmortarIdA]; + RealT forceY = nonmortarMesh.getNodalFields().m_node_pressure[nonmortarIdA] * + nonmortarMesh.getNodalNormals()[1][nonmortarIdA]; + RealT forceZ = nonmortarMesh.getNodalFields().m_node_pressure[nonmortarIdA] * + nonmortarMesh.getNodalNormals()[2][nonmortarIdA]; + + fx2[nonmortarIdA] -= forceX; + fy2[nonmortarIdA] -= forceY; + fz2[nonmortarIdA] -= forceZ; + + fx1[mortarIdA] += forceX; + fy1[mortarIdA] += forceY; + fz1[mortarIdA] += forceZ; + + } // end outer loop over nonmortar and mortar nodes + + ///////////////////////////////////////////////////////////// + // compute tangent stiffness contributions; Note, the // + // Jacobian of the weak form contact nodal force term // + // with respect to the primal kinematic field is 0. There // + // are only contributions from the Jacobian of the contact // + // nodal force term wrt nodal pressures and the Jacobian // + // of the gap constraints wrt the primal kinematic field // + ///////////////////////////////////////////////////////////// + + const EnforcementOptions& enforcement_options = const_cast( cs->getEnforcementOptions() ); + const LagrangeMultiplierImplicitOptions& lm_options = enforcement_options.lm_implicit_options; + if ( lm_options.eval_mode == ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN || + lm_options.eval_mode == ImplicitEvalMode::MORTAR_JACOBIAN ) { auto& plane = planes[cpID]; - // get pair indices - IndexT index1 = pair.m_element_id1; - IndexT index2 = pair.m_element_id2; - - // populate nodal coordinates array. For the aligned mortar - // gap equations, the nodal coordinates are NOT to be projected - // onto the common plane, since the aligned mortar gap - // calculation uses the current configuration nodal coordinates - // themselves - for (int i=0; i mortarX( numNodesPerFace, dim ); + ArrayT nonmortarX( numNodesPerFace, dim ); + // stores projected coordinates in column-major format + ArrayT mortarXT( dim, numNodesPerFace ); + ArrayT nonmortarXT( dim, numNodesPerFace ); + ProjectFaceNodesToPlane( mortarMesh, index1, plane.m_nX, plane.m_nY, plane.m_nZ, plane.m_cX, plane.m_cY, + plane.m_cZ, &mortarXT( 0, 0 ), &mortarXT( 1, 0 ), &mortarXT( 2, 0 ) ); + ProjectFaceNodesToPlane( nonmortarMesh, index2, plane.m_nX, plane.m_nY, plane.m_nZ, plane.m_cX, plane.m_cY, + plane.m_cZ, &nonmortarXT( 0, 0 ), &nonmortarXT( 1, 0 ), &nonmortarXT( 2, 0 ) ); + // populate row-major projected coordinates for the purpose of sending to + // the SurfaceContactElem struct + algorithm::transpose( mortarXT, mortarX ); + algorithm::transpose( nonmortarXT, nonmortarX ); // construct array of polygon overlap vertex coordinates - ArrayT overlapX(plane.m_numPolyVert, dim); - for (IndexT i{0}; i < plane.m_numPolyVert; ++i) - { - overlapX(i, 0) = plane.m_polyX[i]; - overlapX(i, 1) = plane.m_polyY[i]; - overlapX(i, 2) = plane.m_polyZ[i]; + ArrayT overlapX( plane.m_numPolyVert, dim ); + for ( IndexT i{ 0 }; i < plane.m_numPolyVert; ++i ) { + overlapX( i, 0 ) = plane.m_polyX[i]; + overlapX( i, 1 ) = plane.m_polyY[i]; + overlapX( i, 2 ) = plane.m_polyZ[i]; } - // instantiate SurfaceContactElem struct. Note, this is done with - // the projected area of overlap, but with the actual current - // configuration face coordinates. We need the current - // configuration face coordinates here in order to correctly - // compute the mortar gaps. - SurfaceContactElem elem_for_gap( dim, mortarX, nonmortarX, - overlapX.data(), - numNodesPerFace, - plane.m_numPolyVert, - &mortarMesh, &nonmortarMesh, index1, index2 ); - - ///////////////////////// - // compute mortar gaps // - ///////////////////////// - ComputeNodalGap< ALIGNED_MORTAR >( elem_for_gap ); - - // HAVE TO set the number of active constraints. For now set to + // instantiate a new surface contact element with projected face + // coordinates + SurfaceContactElem elem_for_jac( dim, mortarX.data(), nonmortarX.data(), overlapX.data(), numNodesPerFace, + plane.m_numPolyVert, &mortarMesh, &nonmortarMesh, index1, index2 ); + + // HAVE TO set the number of active constraints. For now set to // all nonmortar face nodes. - elem_for_gap.numActiveGaps = numNodesPerFace; + elem_for_jac.numActiveGaps = numNodesPerFace; + + // we need mortar weights for Jacobian contribution calculations + ComputeAlignedMortarWeights( elem_for_jac ); + ComputeAlignedMortarJacobian( elem_for_jac ); + static_cast( cs->getMethodData() )->assembleJacobian( elem_for_jac, lm_options.sparse_mode ); ++cpID; - } // end loop over pairs for nonmortar gap calculations + } // end if-jacobian related output -} // end ComputeAlignedMortarGaps() + } // end of loop over interface pairs computing residual/Jacobian contributions -//------------------------------------------------------------------------------ -template< > -int ApplyNormal< ALIGNED_MORTAR, LAGRANGE_MULTIPLIER >( CouplingScheme* cs ) -{ - /////////////////////////////////////////////////////// - // // - // compute single mortar gaps // - // // - // Note, this routine is guarded against a null mesh // - /////////////////////////////////////////////////////// - ComputeAlignedMortarGaps( cs ); - - auto pairs = cs->getInterfacePairs(); - const IndexT numPairs = pairs.size(); - auto planes = cs->get3DContactPlanes(); - - int const dim = cs->spatialDimension(); - - //////////////////////////////////////////////////////////////////////// - // - // Grab mesh views - // - //////////////////////////////////////////////////////////////////////// - auto mortarMesh = cs->getMesh1().getView(); - auto nonmortarMesh = cs->getMesh2().getView(); - - const IndexT numNodesPerFace = mortarMesh.numberOfNodesPerElement(); - - RealT * const fx1 = mortarMesh.getResponse()[0].data(); - RealT * const fy1 = mortarMesh.getResponse()[1].data(); - RealT * const fz1 = mortarMesh.getResponse()[2].data(); - const IndexT * const mortarConn= mortarMesh.getConnectivity().data(); - - RealT * const fx2 = nonmortarMesh.getResponse()[0].data(); - RealT * const fy2 = nonmortarMesh.getResponse()[1].data(); - RealT * const fz2 = nonmortarMesh.getResponse()[2].data(); - const IndexT * nonmortarConn = nonmortarMesh.getConnectivity().data(); - - int numTotalNodes; - int numRows; - if (!cs->nullMeshes()) - { - numTotalNodes = cs->getNumTotalNodes(); - numRows = dim * numTotalNodes + numTotalNodes; - static_cast( cs->getMethodData() )->allocateMfemSparseMatrix( numRows ); - } - - //////////////////////////////////////////////////////////////// - // compute equilibrium residual and/or Jacobian contributions // - //////////////////////////////////////////////////////////////// - int cpID = 0; - for (IndexT kp = 0; kp < numPairs; ++kp) - { - auto& pair = pairs[kp]; - - if (!pair.m_is_contact_candidate) - { - continue; - } + return 0; - // get pair indices - IndexT index1 = pair.m_element_id1; - IndexT index2 = pair.m_element_id2; - - ////////////////////////////////// - // compute equilibrium residual // - ////////////////////////////////// - - // loop over face nodes (BOTH MORTAR and NONMORTAR - // contributions). NOTE: mortar weights aren't required for - // the residual contributions for ALIGNED_MORTAR. As a result, - // a SurfaceContactElem is not required - for (int a=0; a(cs->getEnforcementOptions()); - const LagrangeMultiplierImplicitOptions& lm_options = enforcement_options.lm_implicit_options; - if ( lm_options.eval_mode == ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN || - lm_options.eval_mode == ImplicitEvalMode::MORTAR_JACOBIAN ) - { - auto& plane = planes[cpID]; - - // stores projected coordinates in row-major format - ArrayT mortarX(numNodesPerFace, dim); - ArrayT nonmortarX(numNodesPerFace, dim); - // stores projected coordinates in column-major format - ArrayT mortarXT(dim, numNodesPerFace); - ArrayT nonmortarXT(dim, numNodesPerFace); - ProjectFaceNodesToPlane( mortarMesh, index1, - plane.m_nX, plane.m_nY, plane.m_nZ, - plane.m_cX, plane.m_cY, plane.m_cZ, - &mortarXT(0, 0), - &mortarXT(1, 0), - &mortarXT(2, 0) ); - ProjectFaceNodesToPlane( nonmortarMesh, index2, - plane.m_nX, plane.m_nY, plane.m_nZ, - plane.m_cX, plane.m_cY, plane.m_cZ, - &nonmortarXT(0, 0), - &nonmortarXT(1, 0), - &nonmortarXT(2, 0) ); - // populate row-major projected coordinates for the purpose of sending to - // the SurfaceContactElem struct - algorithm::transpose(mortarXT, mortarX); - algorithm::transpose(nonmortarXT, nonmortarX); - - // construct array of polygon overlap vertex coordinates - ArrayT overlapX(plane.m_numPolyVert, dim); - for (IndexT i{0}; i < plane.m_numPolyVert; ++i) - { - overlapX(i, 0) = plane.m_polyX[i]; - overlapX(i, 1) = plane.m_polyY[i]; - overlapX(i, 2) = plane.m_polyZ[i]; - } - - // instantiate a new surface contact element with projected face - // coordinates - SurfaceContactElem elem_for_jac( dim, mortarX.data(), nonmortarX.data(), - overlapX.data(), - numNodesPerFace, - plane.m_numPolyVert, - &mortarMesh, &nonmortarMesh, index1, index2 ); - - // HAVE TO set the number of active constraints. For now set to - // all nonmortar face nodes. - elem_for_jac.numActiveGaps = numNodesPerFace; - - // we need mortar weights for Jacobian contribution calculations - ComputeAlignedMortarWeights( elem_for_jac ); - ComputeAlignedMortarJacobian( elem_for_jac ); - static_cast( cs->getMethodData() )->assembleJacobian( elem_for_jac, lm_options.sparse_mode ); - - ++cpID; - - } // end if-jacobian related output - - } // end of loop over interface pairs computing residual/Jacobian contributions - - return 0; - -} // end ApplyNormal<>() +} // end ApplyNormal<>() //------------------------------------------------------------------------------ -template< > -void ComputeResidualJacobian< ALIGNED_MORTAR, PRIMAL >( SurfaceContactElem & TRIBOL_UNUSED_PARAM(elem) ) +template <> +void ComputeResidualJacobian( SurfaceContactElem& TRIBOL_UNUSED_PARAM( elem ) ) { - // There is no Jacobian contribution for this block. Be safe and zero out... - return; + // There is no Jacobian contribution for this block. Be safe and zero out... + return; } //------------------------------------------------------------------------------ -template< > -void ComputeResidualJacobian< ALIGNED_MORTAR, DUAL >( SurfaceContactElem & elem ) +template <> +void ComputeResidualJacobian( SurfaceContactElem& elem ) { - ComputeResidualJacobian< SINGLE_MORTAR, DUAL >( elem ); -} // end ComputeResidualJacobian<>() + ComputeResidualJacobian( elem ); +} // end ComputeResidualJacobian<>() //------------------------------------------------------------------------------ -template< > -void ComputeConstraintJacobian< ALIGNED_MORTAR, PRIMAL >( SurfaceContactElem & elem ) +template <> +void ComputeConstraintJacobian( SurfaceContactElem& elem ) { - ComputeConstraintJacobian< SINGLE_MORTAR, PRIMAL >( elem ); -} // end ComputeConstraintJacobian + ComputeConstraintJacobian( elem ); +} // end ComputeConstraintJacobian //------------------------------------------------------------------------------ -template< > -void ComputeConstraintJacobian< ALIGNED_MORTAR, DUAL >( SurfaceContactElem & TRIBOL_UNUSED_PARAM(elem) ) +template <> +void ComputeConstraintJacobian( SurfaceContactElem& TRIBOL_UNUSED_PARAM( elem ) ) { - // unless we end up solving the complementarity equation, there is - // no Jacobian contribtion for this block. Zero out to be safe... - return; + // unless we end up solving the complementarity equation, there is + // no Jacobian contribtion for this block. Zero out to be safe... + return; } //------------------------------------------------------------------------------ -void ComputeAlignedMortarJacobian( SurfaceContactElem & elem ) +void ComputeAlignedMortarJacobian( SurfaceContactElem& elem ) { - elem.allocateBlockJ( LAGRANGE_MULTIPLIER ); - - ComputeResidualJacobian < ALIGNED_MORTAR, PRIMAL >( elem ); + elem.allocateBlockJ( LAGRANGE_MULTIPLIER ); - ComputeResidualJacobian < ALIGNED_MORTAR, DUAL >( elem ); + ComputeResidualJacobian( elem ); - ComputeConstraintJacobian< ALIGNED_MORTAR, PRIMAL >( elem ); + ComputeResidualJacobian( elem ); - ComputeConstraintJacobian< ALIGNED_MORTAR, DUAL >( elem ); + ComputeConstraintJacobian( elem ); - return; + ComputeConstraintJacobian( elem ); + return; } //------------------------------------------------------------------------------ -} // end namespace Tribol +} // namespace tribol diff --git a/src/tribol/physics/AlignedMortar.hpp b/src/tribol/physics/AlignedMortar.hpp index e31aa467..ea0536bf 100644 --- a/src/tribol/physics/AlignedMortar.hpp +++ b/src/tribol/physics/AlignedMortar.hpp @@ -9,8 +9,7 @@ #include "Mortar.hpp" #include "Physics.hpp" -namespace tribol -{ +namespace tribol { // forward declarations struct SurfaceContactElem; @@ -27,18 +26,18 @@ void ComputeAlignedMortarGaps( CouplingScheme* cs ); /*! * - * \brief computes the integral of (phi_a * phi_b) over a contact - * overlap for all (a,b) combinations. + * \brief computes the integral of (phi_a * phi_b) over a contact + * overlap for all (a,b) combinations. * * \note the mortar weights are stored on the SurfaceContactElem object * * \param [in] elem surface contact element object for contact face-pair * - * \note this is a specialized case for aligned faces using Gauss + * \note this is a specialized case for aligned faces using Gauss * quadrature on a four node quad. * */ -void ComputeAlignedMortarWeights( SurfaceContactElem & elem ); +void ComputeAlignedMortarWeights( SurfaceContactElem& elem ); /*! * @@ -47,8 +46,8 @@ void ComputeAlignedMortarWeights( SurfaceContactElem & elem ); * \param [in] cs pointer to the coupling scheme * */ -template< ContactMethod M > -void ComputeNodalGap( SurfaceContactElem & elem ); +template +void ComputeNodalGap( SurfaceContactElem& elem ); /*! * @@ -59,68 +58,68 @@ void ComputeNodalGap( SurfaceContactElem & elem ); * \param [in] cs pointer to the coupling scheme * */ -template< > -void ComputeNodalGap< ALIGNED_MORTAR >( SurfaceContactElem & elem ); +template <> +void ComputeNodalGap( SurfaceContactElem& elem ); /*! * - * \brief routine to apply interface physics in the direction normal to the interface + * \brief routine to apply interface physics in the direction normal to the interface * * \param [in] cs pointer to the coupling scheme * * \return 0 if no error * */ -template< > -int ApplyNormal< ALIGNED_MORTAR, LAGRANGE_MULTIPLIER >( CouplingScheme* cs ); +template <> +int ApplyNormal( CouplingScheme* cs ); /*! * - * \brief explicit specialization of method to compute the Jacobian contributions of - * the contact residual term with respect to the primal variable for a single + * \brief explicit specialization of method to compute the Jacobian contributions of + * the contact residual term with respect to the primal variable for a single * contact face-pair. * * \param [in] elem surface contact element struct * */ -template< > -void ComputeResidualJacobian< ALIGNED_MORTAR, PRIMAL >( SurfaceContactElem & elem ); +template <> +void ComputeResidualJacobian( SurfaceContactElem& elem ); /*! * - * \brief explicit specialization of method to compute the Jacobian contributions of - * the contact residual term with respect to the dual variable for a single + * \brief explicit specialization of method to compute the Jacobian contributions of + * the contact residual term with respect to the dual variable for a single * contact face-pair. * * \param [in] elem surface contact element struct * */ -template< > -void ComputeResidualJacobian< ALIGNED_MORTAR, DUAL >( SurfaceContactElem & elem ); +template <> +void ComputeResidualJacobian( SurfaceContactElem& elem ); /*! * - * \brief explicit specialization of method to compute the Jacobian contributions of - * the contact gap constraint with respect to the primal variable for a single + * \brief explicit specialization of method to compute the Jacobian contributions of + * the contact gap constraint with respect to the primal variable for a single * contact face-pair. * * \param [in] elem surface contact element struct * */ -template< > -void ComputeConstraintJacobian< ALIGNED_MORTAR, PRIMAL >( SurfaceContactElem & elem ); +template <> +void ComputeConstraintJacobian( SurfaceContactElem& elem ); /*! * - * \brief explicit specialization of method to compute the Jacobian contributions of - * the contact gap constraint with respect to the dual variable for a single + * \brief explicit specialization of method to compute the Jacobian contributions of + * the contact gap constraint with respect to the dual variable for a single * contact face-pair. * * \param [in] elem surface contact element struct * */ -template< > -void ComputeConstraintJacobian< ALIGNED_MORTAR, DUAL >( SurfaceContactElem & elem ); +template <> +void ComputeConstraintJacobian( SurfaceContactElem& elem ); /*! * @@ -129,8 +128,8 @@ void ComputeConstraintJacobian< ALIGNED_MORTAR, DUAL >( SurfaceContactElem & ele * \param [in] elem surface contact element struct * */ -void ComputeAlignedMortarJacobian( SurfaceContactElem & elem ); +void ComputeAlignedMortarJacobian( SurfaceContactElem& elem ); -} // end of namespace Tribol +} // namespace tribol #endif /* SRC_PHYSICS_ALIGNEDMORTAR_HPP_ */ diff --git a/src/tribol/physics/CommonPlane.cpp b/src/tribol/physics/CommonPlane.cpp index 6fa40740..d4f3500e 100644 --- a/src/tribol/physics/CommonPlane.cpp +++ b/src/tribol/physics/CommonPlane.cpp @@ -16,429 +16,391 @@ #include "tribol/utils/ContactPlaneOutput.hpp" #include "tribol/utils/Math.hpp" -namespace tribol -{ +namespace tribol { -TRIBOL_HOST_DEVICE RealT ComputePenaltyStiffnessPerArea( const RealT K1_over_t1, - const RealT K2_over_t2 ) +TRIBOL_HOST_DEVICE RealT ComputePenaltyStiffnessPerArea( const RealT K1_over_t1, const RealT K2_over_t2 ) { - // compute face-pair specific penalty stiffness per unit area. - // Note: This assumes that each face has a spring stiffness - // equal to that side's material Bulk modulus, K, over the - // thickness of the volume element to which that face belongs, - // times the overlap area. That is, K1_over_t1 * A and K2_over_t2 * A. We - // then assume the two springs are in series and compute an - // equivalent spring stiffness as, - // k_eq = A*(K1_over_t1)*(K2_over_t2) / ((K1_over_t1)+(K2_over_t2). - // Note, the host code registers each face's (K/t) as a penalty scale. - // - // UNITS: we multiply k_eq above by the overlap area A, to get a - // stiffness per unit area. This will make the force calculations - // commensurate with the previous calculations using only the - // constant registered penalty scale. - - return K1_over_t1 * K2_over_t2 / (K1_over_t1 + K2_over_t2); - -} // end ComputePenaltyStiffnessPerArea + // compute face-pair specific penalty stiffness per unit area. + // Note: This assumes that each face has a spring stiffness + // equal to that side's material Bulk modulus, K, over the + // thickness of the volume element to which that face belongs, + // times the overlap area. That is, K1_over_t1 * A and K2_over_t2 * A. We + // then assume the two springs are in series and compute an + // equivalent spring stiffness as, + // k_eq = A*(K1_over_t1)*(K2_over_t2) / ((K1_over_t1)+(K2_over_t2). + // Note, the host code registers each face's (K/t) as a penalty scale. + // + // UNITS: we multiply k_eq above by the overlap area A, to get a + // stiffness per unit area. This will make the force calculations + // commensurate with the previous calculations using only the + // constant registered penalty scale. + + return K1_over_t1 * K2_over_t2 / ( K1_over_t1 + K2_over_t2 ); + +} // end ComputePenaltyStiffnessPerArea //------------------------------------------------------------------------------ -TRIBOL_HOST_DEVICE RealT ComputeGapRatePressure( ContactPlane& plane, - const MeshData::Viewer& m1, - const MeshData::Viewer& m2, - RealT element_penalty, +TRIBOL_HOST_DEVICE RealT ComputeGapRatePressure( ContactPlane& plane, const MeshData::Viewer& m1, + const MeshData::Viewer& m2, RealT element_penalty, RatePenaltyCalculation rate_calc ) { - auto fId1 = plane.getCpElementId1(); - auto fId2 = plane.getCpElementId2(); - - const auto dim = plane.m_dim; - - // compute the correct rate_penalty - RealT rate_penalty = 0.; - switch (rate_calc) - { - case NO_RATE_PENALTY: - { - return 0.; - } - case RATE_CONSTANT: - { - rate_penalty = 0.5 * (m1.getElementData().m_rate_penalty_stiffness + - m2.getElementData().m_rate_penalty_stiffness); - break; - } - case RATE_PERCENT: - { - rate_penalty = element_penalty * 0.5 * - (m1.getElementData().m_rate_percent_stiffness + - m2.getElementData().m_rate_percent_stiffness); - break; - } - default: - // no-op, quiet compiler - break; - } // end switch on rate_calc - - // compute the velocity gap and pressure contribution - constexpr int max_dim = 3; - constexpr int max_nodes_per_elem = 4; - - StackArrayT x1; - StackArrayT v1; - m1.getFaceCoords( fId1, x1 ); - m1.getFaceVelocities( fId1, v1 ); - - StackArrayT x2; - StackArrayT v2; - m2.getFaceCoords( fId2, x2 ); - m2.getFaceVelocities( fId2, v2 ); - - ////////////////////////////////////////////////////////// - // compute velocity Galerkin approximation at projected // - // overlap centroid // - ////////////////////////////////////////////////////////// - RealT vel_f1[max_dim]; - RealT vel_f2[max_dim]; - initRealArray( vel_f1, dim, 0. ); - initRealArray( vel_f2, dim, 0. ); - - // interpolate nodal velocity at overlap centroid as projected - // onto face 1 - RealT cXf1 = plane.m_cXf1; - RealT cYf1 = plane.m_cYf1; - RealT cZf1 = (dim == 3) ? plane.m_cZf1 : 0.; - GalerkinEval( x1, cXf1, cYf1, cZf1, - LINEAR, PHYSICAL, dim, dim, - v1, vel_f1 ); - - // interpolate nodal velocity at overlap centroid as projected - // onto face 2 - RealT cXf2 = plane.m_cXf2; - RealT cYf2 = plane.m_cYf2; - RealT cZf2 = (dim == 3) ? plane.m_cZf2 : 0.; - GalerkinEval( x2, cXf2, cYf2, cZf2, - LINEAR, PHYSICAL, dim, dim, - v2, vel_f2 ); - - // compute velocity gap vector - RealT velGap[max_dim]; - velGap[0] = vel_f1[0] - vel_f2[0]; - velGap[1] = vel_f1[1] - vel_f2[1]; - if (dim == 3) - { - velGap[2] = vel_f1[2] - vel_f2[2]; - } - - // compute velocity gap scalar - plane.m_velGap = 0.; - plane.m_velGap += velGap[0] * plane.m_nX; - plane.m_velGap += velGap[1] * plane.m_nY; - if (dim == 3) - { - plane.m_velGap += velGap[2] * plane.m_nZ; - } - - // check the gap rate sense. - // (v1-v2) * \nu < 0 : velocities lead to more interpenetration; - // note, \nu is in direction of face_2 outward unit normal - // TODO consider a velocity gap tolerance. Checking this against - // 0. actually smoothed out contact behavior in contact problem 1 - // for certain percent rate penalties. - if (plane.m_velGap <= 0.) // TODO do we want = or just x1; + StackArrayT v1; + m1.getFaceCoords( fId1, x1 ); + m1.getFaceVelocities( fId1, v1 ); + + StackArrayT x2; + StackArrayT v2; + m2.getFaceCoords( fId2, x2 ); + m2.getFaceVelocities( fId2, v2 ); + + ////////////////////////////////////////////////////////// + // compute velocity Galerkin approximation at projected // + // overlap centroid // + ////////////////////////////////////////////////////////// + RealT vel_f1[max_dim]; + RealT vel_f2[max_dim]; + initRealArray( vel_f1, dim, 0. ); + initRealArray( vel_f2, dim, 0. ); + + // interpolate nodal velocity at overlap centroid as projected + // onto face 1 + RealT cXf1 = plane.m_cXf1; + RealT cYf1 = plane.m_cYf1; + RealT cZf1 = ( dim == 3 ) ? plane.m_cZf1 : 0.; + GalerkinEval( x1, cXf1, cYf1, cZf1, LINEAR, PHYSICAL, dim, dim, v1, vel_f1 ); + + // interpolate nodal velocity at overlap centroid as projected + // onto face 2 + RealT cXf2 = plane.m_cXf2; + RealT cYf2 = plane.m_cYf2; + RealT cZf2 = ( dim == 3 ) ? plane.m_cZf2 : 0.; + GalerkinEval( x2, cXf2, cYf2, cZf2, LINEAR, PHYSICAL, dim, dim, v2, vel_f2 ); + + // compute velocity gap vector + RealT velGap[max_dim]; + velGap[0] = vel_f1[0] - vel_f2[0]; + velGap[1] = vel_f1[1] - vel_f2[1]; + if ( dim == 3 ) { + velGap[2] = vel_f1[2] - vel_f2[2]; + } + + // compute velocity gap scalar + plane.m_velGap = 0.; + plane.m_velGap += velGap[0] * plane.m_nX; + plane.m_velGap += velGap[1] * plane.m_nY; + if ( dim == 3 ) { + plane.m_velGap += velGap[2] * plane.m_nZ; + } + + // check the gap rate sense. + // (v1-v2) * \nu < 0 : velocities lead to more interpenetration; + // note, \nu is in direction of face_2 outward unit normal + // TODO consider a velocity gap tolerance. Checking this against + // 0. actually smoothed out contact behavior in contact problem 1 + // for certain percent rate penalties. + if ( plane.m_velGap <= 0. ) // TODO do we want = or just -int ApplyNormal< COMMON_PLANE, PENALTY >( CouplingScheme* cs ) +template <> +int ApplyNormal( CouplingScheme* cs ) { - /////////////////////////////// - // loop over interface pairs // - /////////////////////////////// - ArrayT err_data({0}, cs->getAllocatorId()); - ArrayViewT err = err_data; - ArrayT neg_thickness_data({false}, cs->getAllocatorId()); - ArrayViewT neg_thickness = neg_thickness_data; - auto cs_view = cs->getView(); - const auto num_pairs = cs->getNumActivePairs(); - forAllExec(cs->getExecutionMode(), num_pairs, - [cs_view, err, neg_thickness] TRIBOL_HOST_DEVICE (IndexT i) - { - auto& plane = cs_view.getContactPlane(i); - - auto& mesh1 = cs_view.getMesh1View(); - auto& mesh2 = cs_view.getMesh2View(); - - // get pair indices - IndexT index1 = plane.getCpElementId1(); - IndexT index2 = plane.getCpElementId2(); - - RealT gap = plane.m_gap; - RealT A = plane.m_area; // face-pair overlap area - - // don't proceed for gaps that don't violate the constraints. This check - // allows for numerically zero interpenetration. - RealT gap_tol = cs_view.getGapTol( index1, index2 ); - - if ( gap > gap_tol ) - { - // We are here if we have a pair that passes ALL geometric - // filter checks, BUT does not actually violate this method's - // gap constraint. - plane.m_inContact = false; - return; - } - - // debug force sums - // RealT dbg_sum_force1 {0.}; - // RealT dbg_sum_force2 {0.}; - ///////////////////////////////////////////// - // kinematic penalty stiffness calculation // - ///////////////////////////////////////////// - RealT penalty_stiff_per_area {0.}; - auto& enforcement_options = cs_view.getEnforcementOptions(); - const PenaltyEnforcementOptions& pen_enfrc_options = enforcement_options.penalty_options; - RealT pen_scale1 = mesh1.getElementData().m_penalty_scale; - RealT pen_scale2 = mesh2.getElementData().m_penalty_scale; - switch (pen_enfrc_options.kinematic_calculation) - { - case KINEMATIC_CONSTANT: - { - // pre-multiply each spring stiffness by each mesh's penalty scale - auto stiffness1 = pen_scale1 * mesh1.getElementData().m_penalty_stiffness; - auto stiffness2 = pen_scale2 * mesh2.getElementData().m_penalty_stiffness; - // compute the equivalent contact penalty spring stiffness per area - penalty_stiff_per_area = ComputePenaltyStiffnessPerArea( stiffness1, stiffness2 ); - break; - } - case KINEMATIC_ELEMENT: - { - // add tiny_length to element thickness to avoid division by zero - auto t1 = mesh1.getElementData().m_thickness[ index1 ] + pen_enfrc_options.tiny_length; - auto t2 = mesh2.getElementData().m_thickness[ index2 ] + pen_enfrc_options.tiny_length; - - if (t1 < 0. || t2 < 0.) - { - neg_thickness[0] = true; - err[0] = 1; - } - - // compute each element spring stiffness. Pre-multiply the material modulus - // (i.e. material stiffness) by each mesh's penalty scale - auto stiffness1 = pen_scale1 * mesh1.getElementData().m_mat_mod[ index1 ] / t1; - auto stiffness2 = pen_scale2 * mesh2.getElementData().m_mat_mod[ index2 ] / t2; - // compute the equivalent contact penalty spring stiffness per area - penalty_stiff_per_area = ComputePenaltyStiffnessPerArea( stiffness1, stiffness2 ); - break; - } - default: - // no-op, quiet compiler - break; - } // end switch on kinematic penalty calculation option - - //////////////////////////////////////////////////// - // Compute contact pressure(s) on current overlap // - //////////////////////////////////////////////////// - - // compute total pressure based on constraint type - RealT totalPressure = 0.; - plane.m_pressure = gap * penalty_stiff_per_area; // kinematic contribution - switch(pen_enfrc_options.constraint_type) - { - case KINEMATIC_AND_RATE: - { - // kinematic contribution - totalPressure += plane.m_pressure; - // add gap-rate contribution - totalPressure += - ComputeGapRatePressure( plane, mesh1, mesh2, penalty_stiff_per_area, - pen_enfrc_options.rate_calculation ); - break; - } - case KINEMATIC: - // kinematic gap pressure contribution only - totalPressure += plane.m_pressure; - break; - default: - // no-op - break; - } // end switch on registered penalty enforcement option - - // debug prints. Comment out for now, but keep for future common plane - // debugging - // SLIC_DEBUG("gap: " << gap); - // SLIC_DEBUG("area: " << A); - // SLIC_DEBUG("penalty stiffness: " << penalty_stiff_per_area); - // SLIC_DEBUG("pressure: " << cpManager.m_pressure[ cpID ]); - - /////////////////////////////////////////// - // create surface contact element struct // - /////////////////////////////////////////// - - // construct array of nodal coordinates - constexpr int max_dim = 3; - constexpr int max_nodes_per_face = 4; - constexpr int max_nodes_per_overlap = 8; - RealT xf1[ max_dim * max_nodes_per_face ]; - RealT xf2[ max_dim * max_nodes_per_face ]; - RealT xVert[ max_dim * max_nodes_per_overlap ]; - int dim = cs_view.spatialDimension(); - int num_nodes_per_face = mesh1.numberOfNodesPerElement(); - initRealArray( xf1, dim * num_nodes_per_face, 0. ); - initRealArray( xf2, dim * num_nodes_per_face, 0. ); - // initialize assuming 2d - auto xVert_size = 4; - auto numPolyVert = 2; - // update if we are in 3d - if (dim == 3) - { - auto& cp3 = static_cast(plane); - numPolyVert = cp3.m_numPolyVert; - xVert_size = 3 * numPolyVert; - } - initRealArray( xVert, xVert_size, 0. ); - - // // get projected face coordinates - // cpManager.getProjectedFaceCoords( cpID, 0, &xf1[0] ); // face 0 = first face - // cpManager.getProjectedFaceCoords( cpID, 1, &xf2[0] ); // face 1 = second face - - // get current configuration, physical coordinates of each face - mesh1.getFaceCoords( index1, &xf1[0] ); - mesh2.getFaceCoords( index2, &xf2[0] ); - - // construct array of polygon overlap vertex coordinates - if (dim == 2) - { - auto& cp2 = static_cast(plane); - for (IndexT j{0}; j < numPolyVert; ++j) - { - xVert[dim*j] = cp2.m_segX[j]; - xVert[dim*j + 1] = cp2.m_segY[j]; - } - } - else - { - auto& cp3 = static_cast(plane); - for (IndexT j{0}; j < numPolyVert; ++j) - { - xVert[dim*j] = cp3.m_polyX[j]; - xVert[dim*j + 1] = cp3.m_polyY[j]; - xVert[dim*j + 2] = cp3.m_polyZ[j]; - } - } - - // instantiate surface contact element struct. Note, this is done with current - // configuration face coordinates (i.e. NOT on the contact plane) and overlap - // coordinates ON the contact plane. The surface contact element does not need - // to be used this way, but the developer should do the book-keeping. - SurfaceContactElem cntctElem( dim, xf1, xf2, xVert, - num_nodes_per_face, numPolyVert, - &mesh1, &mesh2, index1, index2 ); - - // set SurfaceContactElem face normals and overlap normal - RealT faceNormal1[max_dim]; - RealT faceNormal2[max_dim]; - RealT overlapNormal[max_dim]; - - mesh1.getFaceNormal( index1, faceNormal1 ); - mesh2.getFaceNormal( index2, faceNormal2 ); - overlapNormal[0] = plane.m_nX; - overlapNormal[1] = plane.m_nY; - if (dim == 3) - { - overlapNormal[2] = plane.m_nZ; - } - - cntctElem.faceNormal1 = faceNormal1; - cntctElem.faceNormal2 = faceNormal2; - cntctElem.overlapNormal = overlapNormal; - cntctElem.overlapArea = plane.m_area; - - // create arrays to hold nodal residual weak form integral evaluations - RealT phi1[max_nodes_per_face]; - RealT phi2[max_nodes_per_face]; - initRealArray( phi1, num_nodes_per_face, 0. ); - initRealArray( phi2, num_nodes_per_face, 0. ); - - //////////////////////////////////////////////////////////////////////// - // Integration of contact integrals: integral of shape functions over // - // contact overlap patch // - //////////////////////////////////////////////////////////////////////// - EvalWeakFormIntegral< COMMON_PLANE, SINGLE_POINT > - ( cntctElem, phi1, phi2 ); - - /////////////////////////////////////////////////////////////////////// - // Computation of full contact nodal force contributions // - // (i.e. premultiplication of contact integrals by normal component, // - // contact pressure, and overlap area) // - /////////////////////////////////////////////////////////////////////// - - // RealT phi_sum_1 = 0.; - // RealT phi_sum_2 = 0.; - - // compute contact force (spring force) - RealT contact_force = totalPressure * A; - RealT force_x = overlapNormal[0] * contact_force; - RealT force_y = overlapNormal[1] * contact_force; - RealT force_z = 0.; - if (dim == 3) - { - force_z = overlapNormal[2] * contact_force; + /////////////////////////////// + // loop over interface pairs // + /////////////////////////////// + ArrayT err_data( { 0 }, cs->getAllocatorId() ); + ArrayViewT err = err_data; + ArrayT neg_thickness_data( { false }, cs->getAllocatorId() ); + ArrayViewT neg_thickness = neg_thickness_data; + auto cs_view = cs->getView(); + const auto num_pairs = cs->getNumActivePairs(); + forAllExec( cs->getExecutionMode(), num_pairs, [cs_view, err, neg_thickness] TRIBOL_HOST_DEVICE( IndexT i ) { + auto& plane = cs_view.getContactPlane( i ); + + auto& mesh1 = cs_view.getMesh1View(); + auto& mesh2 = cs_view.getMesh2View(); + + // get pair indices + IndexT index1 = plane.getCpElementId1(); + IndexT index2 = plane.getCpElementId2(); + + RealT gap = plane.m_gap; + RealT A = plane.m_area; // face-pair overlap area + + // don't proceed for gaps that don't violate the constraints. This check + // allows for numerically zero interpenetration. + RealT gap_tol = cs_view.getGapTol( index1, index2 ); + + if ( gap > gap_tol ) { + // We are here if we have a pair that passes ALL geometric + // filter checks, BUT does not actually violate this method's + // gap constraint. + plane.m_inContact = false; + return; + } + + // debug force sums + // RealT dbg_sum_force1 {0.}; + // RealT dbg_sum_force2 {0.}; + ///////////////////////////////////////////// + // kinematic penalty stiffness calculation // + ///////////////////////////////////////////// + RealT penalty_stiff_per_area{ 0. }; + auto& enforcement_options = cs_view.getEnforcementOptions(); + const PenaltyEnforcementOptions& pen_enfrc_options = enforcement_options.penalty_options; + RealT pen_scale1 = mesh1.getElementData().m_penalty_scale; + RealT pen_scale2 = mesh2.getElementData().m_penalty_scale; + switch ( pen_enfrc_options.kinematic_calculation ) { + case KINEMATIC_CONSTANT: { + // pre-multiply each spring stiffness by each mesh's penalty scale + auto stiffness1 = pen_scale1 * mesh1.getElementData().m_penalty_stiffness; + auto stiffness2 = pen_scale2 * mesh2.getElementData().m_penalty_stiffness; + // compute the equivalent contact penalty spring stiffness per area + penalty_stiff_per_area = ComputePenaltyStiffnessPerArea( stiffness1, stiffness2 ); + break; + } + case KINEMATIC_ELEMENT: { + // add tiny_length to element thickness to avoid division by zero + auto t1 = mesh1.getElementData().m_thickness[index1] + pen_enfrc_options.tiny_length; + auto t2 = mesh2.getElementData().m_thickness[index2] + pen_enfrc_options.tiny_length; + + if ( t1 < 0. || t2 < 0. ) { + neg_thickness[0] = true; + err[0] = 1; } - ////////////////////////////////////////////////////// - // loop over nodes and compute contact nodal forces // - ////////////////////////////////////////////////////// - for( IndexT a=0 ; a < num_nodes_per_face ; ++a ) - { - - IndexT node0 = mesh1.getGlobalNodeId(index1, a); - IndexT node1 = mesh2.getGlobalNodeId(index2, a); - - // if (logLevel == TRIBOL_DEBUG) - // { - // phi_sum_1 += phi1[a]; - // phi_sum_2 += phi2[a]; - // } - - const RealT nodal_force_x1 = force_x * phi1[a]; - const RealT nodal_force_y1 = force_y * phi1[a]; - const RealT nodal_force_z1 = force_z * phi1[a]; - - const RealT nodal_force_x2 = force_x * phi2[a]; - const RealT nodal_force_y2 = force_y * phi2[a]; - const RealT nodal_force_z2 = force_z * phi2[a]; - - // if (logLevel == TRIBOL_DEBUG) - // { - // dbg_sum_force1 += magnitude( nodal_force_x1, - // nodal_force_y1, - // nodal_force_z1 ); - // dbg_sum_force2 += magnitude( nodal_force_x2, - // nodal_force_y2, - // nodal_force_z2 ); - // } - - // accumulate contributions in host code's registered nodal force arrays + // compute each element spring stiffness. Pre-multiply the material modulus + // (i.e. material stiffness) by each mesh's penalty scale + auto stiffness1 = pen_scale1 * mesh1.getElementData().m_mat_mod[index1] / t1; + auto stiffness2 = pen_scale2 * mesh2.getElementData().m_mat_mod[index2] / t2; + // compute the equivalent contact penalty spring stiffness per area + penalty_stiff_per_area = ComputePenaltyStiffnessPerArea( stiffness1, stiffness2 ); + break; + } + default: + // no-op, quiet compiler + break; + } // end switch on kinematic penalty calculation option + + //////////////////////////////////////////////////// + // Compute contact pressure(s) on current overlap // + //////////////////////////////////////////////////// + + // compute total pressure based on constraint type + RealT totalPressure = 0.; + plane.m_pressure = gap * penalty_stiff_per_area; // kinematic contribution + switch ( pen_enfrc_options.constraint_type ) { + case KINEMATIC_AND_RATE: { + // kinematic contribution + totalPressure += plane.m_pressure; + // add gap-rate contribution + totalPressure += + ComputeGapRatePressure( plane, mesh1, mesh2, penalty_stiff_per_area, pen_enfrc_options.rate_calculation ); + break; + } + case KINEMATIC: + // kinematic gap pressure contribution only + totalPressure += plane.m_pressure; + break; + default: + // no-op + break; + } // end switch on registered penalty enforcement option + + // debug prints. Comment out for now, but keep for future common plane + // debugging + // SLIC_DEBUG("gap: " << gap); + // SLIC_DEBUG("area: " << A); + // SLIC_DEBUG("penalty stiffness: " << penalty_stiff_per_area); + // SLIC_DEBUG("pressure: " << cpManager.m_pressure[ cpID ]); + + /////////////////////////////////////////// + // create surface contact element struct // + /////////////////////////////////////////// + + // construct array of nodal coordinates + constexpr int max_dim = 3; + constexpr int max_nodes_per_face = 4; + constexpr int max_nodes_per_overlap = 8; + RealT xf1[max_dim * max_nodes_per_face]; + RealT xf2[max_dim * max_nodes_per_face]; + RealT xVert[max_dim * max_nodes_per_overlap]; + int dim = cs_view.spatialDimension(); + int num_nodes_per_face = mesh1.numberOfNodesPerElement(); + initRealArray( xf1, dim * num_nodes_per_face, 0. ); + initRealArray( xf2, dim * num_nodes_per_face, 0. ); + // initialize assuming 2d + auto xVert_size = 4; + auto numPolyVert = 2; + // update if we are in 3d + if ( dim == 3 ) { + auto& cp3 = static_cast( plane ); + numPolyVert = cp3.m_numPolyVert; + xVert_size = 3 * numPolyVert; + } + initRealArray( xVert, xVert_size, 0. ); + + // // get projected face coordinates + // cpManager.getProjectedFaceCoords( cpID, 0, &xf1[0] ); // face 0 = first face + // cpManager.getProjectedFaceCoords( cpID, 1, &xf2[0] ); // face 1 = second face + + // get current configuration, physical coordinates of each face + mesh1.getFaceCoords( index1, &xf1[0] ); + mesh2.getFaceCoords( index2, &xf2[0] ); + + // construct array of polygon overlap vertex coordinates + if ( dim == 2 ) { + auto& cp2 = static_cast( plane ); + for ( IndexT j{ 0 }; j < numPolyVert; ++j ) { + xVert[dim * j] = cp2.m_segX[j]; + xVert[dim * j + 1] = cp2.m_segY[j]; + } + } else { + auto& cp3 = static_cast( plane ); + for ( IndexT j{ 0 }; j < numPolyVert; ++j ) { + xVert[dim * j] = cp3.m_polyX[j]; + xVert[dim * j + 1] = cp3.m_polyY[j]; + xVert[dim * j + 2] = cp3.m_polyZ[j]; + } + } + + // instantiate surface contact element struct. Note, this is done with current + // configuration face coordinates (i.e. NOT on the contact plane) and overlap + // coordinates ON the contact plane. The surface contact element does not need + // to be used this way, but the developer should do the book-keeping. + SurfaceContactElem cntctElem( dim, xf1, xf2, xVert, num_nodes_per_face, numPolyVert, &mesh1, &mesh2, index1, + index2 ); + + // set SurfaceContactElem face normals and overlap normal + RealT faceNormal1[max_dim]; + RealT faceNormal2[max_dim]; + RealT overlapNormal[max_dim]; + + mesh1.getFaceNormal( index1, faceNormal1 ); + mesh2.getFaceNormal( index2, faceNormal2 ); + overlapNormal[0] = plane.m_nX; + overlapNormal[1] = plane.m_nY; + if ( dim == 3 ) { + overlapNormal[2] = plane.m_nZ; + } + + cntctElem.faceNormal1 = faceNormal1; + cntctElem.faceNormal2 = faceNormal2; + cntctElem.overlapNormal = overlapNormal; + cntctElem.overlapArea = plane.m_area; + + // create arrays to hold nodal residual weak form integral evaluations + RealT phi1[max_nodes_per_face]; + RealT phi2[max_nodes_per_face]; + initRealArray( phi1, num_nodes_per_face, 0. ); + initRealArray( phi2, num_nodes_per_face, 0. ); + + //////////////////////////////////////////////////////////////////////// + // Integration of contact integrals: integral of shape functions over // + // contact overlap patch // + //////////////////////////////////////////////////////////////////////// + EvalWeakFormIntegral( cntctElem, phi1, phi2 ); + + /////////////////////////////////////////////////////////////////////// + // Computation of full contact nodal force contributions // + // (i.e. premultiplication of contact integrals by normal component, // + // contact pressure, and overlap area) // + /////////////////////////////////////////////////////////////////////// + + // RealT phi_sum_1 = 0.; + // RealT phi_sum_2 = 0.; + + // compute contact force (spring force) + RealT contact_force = totalPressure * A; + RealT force_x = overlapNormal[0] * contact_force; + RealT force_y = overlapNormal[1] * contact_force; + RealT force_z = 0.; + if ( dim == 3 ) { + force_z = overlapNormal[2] * contact_force; + } + + ////////////////////////////////////////////////////// + // loop over nodes and compute contact nodal forces // + ////////////////////////////////////////////////////// + for ( IndexT a = 0; a < num_nodes_per_face; ++a ) { + IndexT node0 = mesh1.getGlobalNodeId( index1, a ); + IndexT node1 = mesh2.getGlobalNodeId( index2, a ); + + // if (logLevel == TRIBOL_DEBUG) + // { + // phi_sum_1 += phi1[a]; + // phi_sum_2 += phi2[a]; + // } + + const RealT nodal_force_x1 = force_x * phi1[a]; + const RealT nodal_force_y1 = force_y * phi1[a]; + const RealT nodal_force_z1 = force_z * phi1[a]; + + const RealT nodal_force_x2 = force_x * phi2[a]; + const RealT nodal_force_y2 = force_y * phi2[a]; + const RealT nodal_force_z2 = force_z * phi2[a]; + + // if (logLevel == TRIBOL_DEBUG) + // { + // dbg_sum_force1 += magnitude( nodal_force_x1, + // nodal_force_y1, + // nodal_force_z1 ); + // dbg_sum_force2 += magnitude( nodal_force_x2, + // nodal_force_y2, + // nodal_force_z2 ); + // } + + // accumulate contributions in host code's registered nodal force arrays #ifdef TRIBOL_USE_RAJA - RAJA::atomicAdd(&mesh1.getResponse()[0][node0], -nodal_force_x1); - RAJA::atomicAdd(&mesh2.getResponse()[0][node1], nodal_force_x2); + RAJA::atomicAdd( &mesh1.getResponse()[0][node0], -nodal_force_x1 ); + RAJA::atomicAdd( &mesh2.getResponse()[0][node1], nodal_force_x2 ); - RAJA::atomicAdd(&mesh1.getResponse()[1][node0], -nodal_force_y1); - RAJA::atomicAdd(&mesh2.getResponse()[1][node1], nodal_force_y2); + RAJA::atomicAdd( &mesh1.getResponse()[1][node0], -nodal_force_y1 ); + RAJA::atomicAdd( &mesh2.getResponse()[1][node1], nodal_force_y2 ); - // there is no z component for 2D - if (dim == 3) - { - RAJA::atomicAdd(&mesh1.getResponse()[2][node0], -nodal_force_z1); - RAJA::atomicAdd(&mesh2.getResponse()[2][node1], nodal_force_z2); - } + // there is no z component for 2D + if ( dim == 3 ) { + RAJA::atomicAdd( &mesh1.getResponse()[2][node0], -nodal_force_z1 ); + RAJA::atomicAdd( &mesh2.getResponse()[2][node1], nodal_force_z2 ); + } #else mesh1.getResponse()[0][node0] -= nodal_force_x1; mesh2.getResponse()[0][node1] += nodal_force_x2; @@ -453,23 +415,23 @@ int ApplyNormal< COMMON_PLANE, PENALTY >( CouplingScheme* cs ) mesh2.getResponse()[2][node1] += nodal_force_z2; } #endif - } // end for loop over face nodes - - // comment out debug logs; too much output during tests. Keep for easy - // debugging if needed - //SLIC_DEBUG("force sum, side 1, pair " << kp << ": " << -dbg_sum_force1 ); - //SLIC_DEBUG("force sum, side 2, pair " << kp << ": " << dbg_sum_force2 ); - //SLIC_DEBUG("phi 1 sum: " << phi_sum_1 ); - //SLIC_DEBUG("phi 2 sum: " << phi_sum_2 ); - } - ); - - ArrayT neg_thickness_host(neg_thickness_data); - SLIC_DEBUG_IF(neg_thickness_host[0], "ApplyNormal: negative element thicknesses encountered."); + } // end for loop over face nodes + + // comment out debug logs; too much output during tests. Keep for easy + // debugging if needed + // SLIC_DEBUG("force sum, side 1, pair " << kp << ": " << -dbg_sum_force1 ); + // SLIC_DEBUG("force sum, side 2, pair " << kp << ": " << dbg_sum_force2 ); + // SLIC_DEBUG("phi 1 sum: " << phi_sum_1 ); + // SLIC_DEBUG("phi 2 sum: " << phi_sum_2 ); + } ); + + ArrayT neg_thickness_host( neg_thickness_data ); + SLIC_DEBUG_IF( neg_thickness_host[0], + "ApplyNormal: negative element thicknesses encountered." ); - ArrayT err_host(err_data); - return err_host[0]; + ArrayT err_host( err_data ); + return err_host[0]; -} // end ApplyNormal() +} // end ApplyNormal() -} +} // namespace tribol diff --git a/src/tribol/physics/CommonPlane.hpp b/src/tribol/physics/CommonPlane.hpp index c1b848af..5f187611 100644 --- a/src/tribol/physics/CommonPlane.hpp +++ b/src/tribol/physics/CommonPlane.hpp @@ -8,23 +8,21 @@ #include "Physics.hpp" -namespace tribol -{ +namespace tribol { /*! * - * \brief computes penalty stiffness for Common Plane + Penalty + * \brief computes penalty stiffness for Common Plane + Penalty * * \param [in] K1/t1 contact spring stiffness for face 1 (bulk_modulus/element_thickness for face 1) * \param [in] K2/t2 contact spring stiffness for face 2 (bulk_modulus/element_thickness for face 2) * - * \return face-pair based, element-wise penalty stiffness per area - * + * \return face-pair based, element-wise penalty stiffness per area * - * \pre Bulk modulus and element thickness arrays are registered by host code + * + * \pre Bulk modulus and element thickness arrays are registered by host code * */ -TRIBOL_HOST_DEVICE RealT ComputePenaltyStiffnessPerArea( const RealT K1_over_t1, - const RealT K2_over_t2 ); +TRIBOL_HOST_DEVICE RealT ComputePenaltyStiffnessPerArea( const RealT K1_over_t1, const RealT K2_over_t2 ); /*! * @@ -35,9 +33,9 @@ TRIBOL_HOST_DEVICE RealT ComputePenaltyStiffnessPerArea( const RealT K1_over_t1, * \return 0 if no error * */ -template< > -int ApplyNormal< COMMON_PLANE, PENALTY >( CouplingScheme* cs ); +template <> +int ApplyNormal( CouplingScheme* cs ); -} // end namespace tribol +} // end namespace tribol #endif /* SRC_PHYSICS_COMMONPLANE_HPP_ */ diff --git a/src/tribol/physics/Mortar.cpp b/src/tribol/physics/Mortar.cpp index 3aa1cccc..41bd2a0e 100644 --- a/src/tribol/physics/Mortar.cpp +++ b/src/tribol/physics/Mortar.cpp @@ -22,812 +22,715 @@ #include -namespace tribol -{ +namespace tribol { -void ComputeMortarWeights( SurfaceContactElem & elem ) +void ComputeMortarWeights( SurfaceContactElem& elem ) { - // instantiate integration object - IntegPts integ; - - // Debug: leave code in for now to call Gauss quadrature on triangle rule - GaussPolyIntTri( elem, integ, 3 ); - - // call Taylor-Wingate-Bos integation rule. NOTE: this is not - // working. The correct gaps are not being computed. -// TWBPolyInt( elem, integ, 3 ); - - // get individual arrays of coordinates for each face - RealT x1[elem.numFaceVert]; - RealT y1[elem.numFaceVert]; - RealT z1[elem.numFaceVert]; - RealT x2[elem.numFaceVert]; - RealT y2[elem.numFaceVert]; - RealT z2[elem.numFaceVert]; - - for (int i=0; i elem.numWts || mortarNonmortarId > elem.numWts, - "ComputeMortarWts: integer ids for weights exceed elem.numWts"); - - // compute nonmortar/nonmortar mortar weight - elem.mortarWts[ nonmortarNonmortarId ] += integ.wts[ip] * phiNonmortarA * phiNonmortarB; - - - // compute mortar/nonmortar mortar weight - elem.mortarWts[ mortarNonmortarId ] += integ.wts[ip] * phiMortarA * phiNonmortarB; - - } // end loop over integration points - - } // end loop over nodes on side 2 - - } // end loop over nodes on side 1 - -} // end ComputeMortarWeights() + // instantiate integration object + IntegPts integ; -//------------------------------------------------------------------------------ -template< > -void ComputeNodalGap< SINGLE_MORTAR >( SurfaceContactElem & elem ) -{ - // check to make sure mortar weights have been computed locally - // for the SurfaceContactElem object - SLIC_ERROR_IF(elem.mortarWts==nullptr, "ComputeNodalGap< SINGLE_MORTAR >: compute local weights on input struct first."); - - // get mesh instance to store gaps on mesh data object - auto& nonmortarMesh = *elem.m_mesh2; - IndexT const * const nonmortarConn = nonmortarMesh.getConnectivity().data(); - - // will populate local gaps on nonmortar face on nonmortar mesh data object - SLIC_ERROR_IF(nonmortarMesh.getNodalFields().m_node_gap.empty(), - "ComputeNodalGap< SINGLE_MORTAR >: allocate gaps on mesh data object."); - - SLIC_ERROR_IF(!nonmortarMesh.hasNodalNormals(), - "ComputeNodalGap< SINGLE_MORTAR >: allocate and compute nodal normals on mesh data object."); - - // compute gap contributions associated with face 2 on the SurfaceContactElem - // (i.e. nonmortar surface) - - // loop over nodes on nonmortar side - for (int a=0; a() + // allocate mortar weights array on SurfaceContactElem object. This routine + // also initializes the array + elem.allocateMortarWts(); -//------------------------------------------------------------------------------ -void ComputeSingleMortarGaps( CouplingScheme* cs ) -{ - MeshManager& meshManager = MeshManager::getInstance(); - MeshData& nonmortarMeshData = meshManager.at( cs->getMeshId2() ); - // compute nodal normals (do this outside the element loop) - // Note, this is guarded against zero element meshes - int const dim = cs->spatialDimension(); - nonmortarMeshData.computeNodalNormals( dim ); - - auto pairs = cs->getInterfacePairs(); - const IndexT numPairs = pairs.size(); - auto planes = cs->get3DContactPlanes(); - - //////////////////////////////////////////////////////////////////////// - // - // Grab mesh views - // - //////////////////////////////////////////////////////////////////////// - auto mortarMesh = cs->getMesh1().getView(); - auto nonmortarMesh = cs->getMesh2().getView(); - - IndexT const numNodesPerFace = mortarMesh.numberOfNodesPerElement(); - - RealT const * const x1 = mortarMesh.getPosition()[0].data(); - RealT const * const y1 = mortarMesh.getPosition()[1].data(); - RealT const * const z1 = mortarMesh.getPosition()[2].data(); - IndexT const * const mortarConn= mortarMesh.getConnectivity().data(); - - RealT const * const x2 = nonmortarMesh.getPosition()[0].data(); - RealT const * const y2 = nonmortarMesh.getPosition()[1].data(); - RealT const * const z2 = nonmortarMesh.getPosition()[2].data(); - IndexT const * nonmortarConn = nonmortarMesh.getConnectivity().data(); - - // declare local variables to hold face nodal coordinates - // and overlap vertex coordinates - IndexT size = dim * numNodesPerFace; - RealT mortarX[ size ]; - RealT nonmortarX[ size ]; - - //////////////////////////////////////////////////////////////////// - // compute nonmortar gaps to determine active set of contact dofs // - //////////////////////////////////////////////////////////////////// - int cpID = 0; - for (IndexT kp = 0; kp < numPairs; ++kp) - { - auto& pair = pairs[kp]; - - if (!pair.m_is_contact_candidate) - { - continue; - } + RealT phiNonmortarA, phiNonmortarB, phiMortarA; - auto& plane = planes[cpID]; - - // get pair indices - IndexT index1 = pair.m_element_id1; - IndexT index2 = pair.m_element_id2; - - // populate the current configuration nodal coordinates for the - // two faces - for (int i=0; i mortarX_bar(numNodesPerFace, dim); - ArrayT nonmortarX_bar(numNodesPerFace, dim); - // stores projected coordinates in column-major format - ArrayT mortarX_barT(dim, numNodesPerFace); - ArrayT nonmortarX_barT(dim, numNodesPerFace); - ProjectFaceNodesToPlane( mortarMesh, index1, - plane.m_nX, plane.m_nY, plane.m_nZ, - plane.m_cX, plane.m_cY, plane.m_cZ, - &mortarX_barT(0, 0), - &mortarX_barT(1, 0), - &mortarX_barT(2, 0) ); - ProjectFaceNodesToPlane( nonmortarMesh, index2, - plane.m_nX, plane.m_nY, plane.m_nZ, - plane.m_cX, plane.m_cY, plane.m_cZ, - &nonmortarX_barT(0, 0), - &nonmortarX_barT(1, 0), - &nonmortarX_barT(2, 0) ); - // populate row-major projected coordinates for the purpose of sending to - // the SurfaceContactElem struct - algorithm::transpose(mortarX_barT, mortarX_bar); - algorithm::transpose(nonmortarX_barT, nonmortarX_bar); - - // construct array of polygon overlap vertex coordinates - ArrayT overlapX(plane.m_numPolyVert, dim); - for (IndexT i{0}; i < plane.m_numPolyVert; ++i) - { - overlapX(i, 0) = plane.m_polyX[i]; - overlapX(i, 1) = plane.m_polyY[i]; - overlapX(i, 2) = plane.m_polyZ[i]; - } + for ( int a = 0; a < elem.numFaceVert; ++a ) { + // loop over number of nodes on nonmortar side + for ( int b = 0; b < elem.numFaceVert; ++b ) { + // set nonmortar/nonmortar and mortar/nonmortar ids...Don't change these ids + int nonmortarNonmortarId = elem.numFaceVert * a + b; + int mortarNonmortarId = elem.numFaceVert * elem.numFaceVert + elem.numFaceVert * a + b; - // instantiate contact surface element for purposes of computing - // mortar weights. Note, this uses projected face coords - SurfaceContactElem elem( dim, mortarX_bar.data(), nonmortarX_bar.data(), - overlapX.data(), - numNodesPerFace, - plane.m_numPolyVert, - &mortarMesh, &nonmortarMesh, index1, index2 ); + // loop over number of integration points + for ( int ip = 0; ip < integ.numIPs; ++ip ) { + // The integration method for computing weights uses + // the inverse isoparametric mapping of a current configuration + // integration point (as projected onto the current configuration + // face) to obtain a (xi,eta) coordinate pair in parent space + // for the evaluation of Lagrange shape functions + RealT xp[3] = { integ.xy[elem.dim * ip], integ.xy[elem.dim * ip + 1], integ.xy[elem.dim * ip + 2] }; + RealT xi[2] = { 0., 0. }; - // compute the mortar weights to be stored on the surface - // contact element struct. This must be done prior to computing nodal gaps - elem.overlapArea = plane.m_area; - ComputeMortarWeights( elem ); + InvIso( xp, x1, y1, z1, elem.numFaceVert, xi ); + LinIsoQuadShapeFunc( xi[0], xi[1], a, phiMortarA ); - // compute mortar gaps. Note, we have to now use current configuration - // nodal coordinates on the contact element - elem.faceCoords1 = &mortarX[0]; - elem.faceCoords2 = &nonmortarX[0]; + InvIso( xp, x2, y2, z2, elem.numFaceVert, xi ); + LinIsoQuadShapeFunc( xi[0], xi[1], a, phiNonmortarA ); + LinIsoQuadShapeFunc( xi[0], xi[1], b, phiNonmortarB ); - ComputeNodalGap< SINGLE_MORTAR >( elem ); + SLIC_ERROR_IF( nonmortarNonmortarId > elem.numWts || mortarNonmortarId > elem.numWts, + "ComputeMortarWts: integer ids for weights exceed elem.numWts" ); - // TODO: fix this to register the actual number of active nonmortar gaps. - // This is not the appropriate data structure to put this information in - // as the SurfaceContactElem goes out of scope when we exit the loop. - // HAVE TO set the number of active constraints. For now set to - // all nonmortar face nodes. - elem.numActiveGaps = numNodesPerFace; + // compute nonmortar/nonmortar mortar weight + elem.mortarWts[nonmortarNonmortarId] += integ.wts[ip] * phiNonmortarA * phiNonmortarB; - ++cpID; + // compute mortar/nonmortar mortar weight + elem.mortarWts[mortarNonmortarId] += integ.wts[ip] * phiMortarA * phiNonmortarB; - } // end loop over pairs to compute nodal gaps + } // end loop over integration points -} // end ComputeSingleMortarGaps() + } // end loop over nodes on side 2 + + } // end loop over nodes on side 1 + +} // end ComputeMortarWeights() //------------------------------------------------------------------------------ -template< > -int ApplyNormal< SINGLE_MORTAR, LAGRANGE_MULTIPLIER >( CouplingScheme* cs ) +template <> +void ComputeNodalGap( SurfaceContactElem& elem ) { - /////////////////////////////////////////////////////// - // // - // compute single mortar gaps // - // // - // Note, this routine is guarded against null meshes // - /////////////////////////////////////////////////////// - ComputeSingleMortarGaps( cs ); - - auto pairs = cs->getInterfacePairs(); - const IndexT numPairs = pairs.size(); - auto planes = cs->get3DContactPlanes(); - - int const dim = cs->spatialDimension(); - - //////////////////////////////////////////////////////////////////////// - // - // Grab mesh views - // - //////////////////////////////////////////////////////////////////////// - auto mortarMesh = cs->getMesh1().getView(); - auto nonmortarMesh = cs->getMesh2().getView(); - - IndexT const numNodesPerFace = mortarMesh.numberOfNodesPerElement(); - - RealT * const fx1 = mortarMesh.getResponse()[0].data(); - RealT * const fy1 = mortarMesh.getResponse()[1].data(); - RealT * const fz1 = mortarMesh.getResponse()[2].data(); - IndexT const * const mortarConn= mortarMesh.getConnectivity().data(); - - RealT * const fx2 = nonmortarMesh.getResponse()[0].data(); - RealT * const fy2 = nonmortarMesh.getResponse()[1].data(); - RealT * const fz2 = nonmortarMesh.getResponse()[2].data(); - IndexT const * nonmortarConn = nonmortarMesh.getConnectivity().data(); - - int numTotalNodes = cs->getNumTotalNodes(); - int numRows = dim * numTotalNodes + numTotalNodes; - const EnforcementOptions& enforcement_options = const_cast(cs->getEnforcementOptions()); - const LagrangeMultiplierImplicitOptions& lm_options = enforcement_options.lm_implicit_options; - if (!cs->nullMeshes()) - { - if ( lm_options.sparse_mode == SparseMode::MFEM_ELEMENT_DENSE ) - { - static_cast( cs->getMethodData() )->reserveBlockJ( - {BlockSpace::MORTAR, BlockSpace::NONMORTAR, BlockSpace::LAGRANGE_MULTIPLIER}, - numPairs - ); - } - else if ( lm_options.sparse_mode == SparseMode::MFEM_INDEX_SET || - lm_options.sparse_mode == SparseMode::MFEM_LINKED_LIST ) - { - static_cast( cs->getMethodData() )->allocateMfemSparseMatrix( numRows ); - } - else - { - SLIC_WARNING("Unsupported Jacobian storage method."); - return 1; - } - } - - //////////////////////////////////////////////////////////////// - // // - // compute equilibrium residual and/or Jacobian contributions // - // // - //////////////////////////////////////////////////////////////// - int cpID = 0; - for (IndexT kp = 0; kp < numPairs; ++kp) - { - auto& pair = pairs[kp]; - - if (!pair.m_is_contact_candidate) - { - continue; - } + // check to make sure mortar weights have been computed locally + // for the SurfaceContactElem object + SLIC_ERROR_IF( elem.mortarWts == nullptr, + "ComputeNodalGap< SINGLE_MORTAR >: compute local weights on input struct first." ); + + // get mesh instance to store gaps on mesh data object + auto& nonmortarMesh = *elem.m_mesh2; + IndexT const* const nonmortarConn = nonmortarMesh.getConnectivity().data(); + + // will populate local gaps on nonmortar face on nonmortar mesh data object + SLIC_ERROR_IF( nonmortarMesh.getNodalFields().m_node_gap.empty(), + "ComputeNodalGap< SINGLE_MORTAR >: allocate gaps on mesh data object." ); + + SLIC_ERROR_IF( !nonmortarMesh.hasNodalNormals(), + "ComputeNodalGap< SINGLE_MORTAR >: allocate and compute nodal normals on mesh data object." ); + + // compute gap contributions associated with face 2 on the SurfaceContactElem + // (i.e. nonmortar surface) + + // loop over nodes on nonmortar side + for ( int a = 0; a < elem.numFaceVert; ++a ) { + // initialize gap1 and gap2 terms + RealT g1 = 0.; + RealT g2 = 0.; + + // get global nonmortar node number from connectivity + RealT nrml_a[elem.dim]; + int glbId = nonmortarConn[elem.numFaceVert * elem.faceId2 + a]; + nrml_a[0] = nonmortarMesh.getNodalNormals()[0][glbId]; + nrml_a[1] = nonmortarMesh.getNodalNormals()[1][glbId]; + if ( elem.dim == 3 ) { + nrml_a[2] = nonmortarMesh.getNodalNormals()[2][glbId]; + } + + // sum contributions from both sides + for ( int b = 0; b < elem.numFaceVert; ++b ) { + // compute nonmortar-mortar and nonmortar-nonmortar ids. Note, n_ab is + // the stored mortar weight. For mortar-nonmortar mortar weights, + // a = mortar node and b = nonmortar node, BUT FOR THE GAP COMPUTATION, + // THE SUM OF MORTAR WEIGHTS IS ACTUALLY OVER SHAPE FUNCTIONS + // DEFINED AT NODE "b", SO WE NEED TO USE (n_ab)^T. + RealT nab_1 = elem.getNonmortarMortarWt( a, b ); // nonmortar-mortar weight + RealT nab_2 = elem.getNonmortarNonmortarWt( a, b ); // nonmortar-nonmortar weight + + g1 += dotProd( &nrml_a[0], &elem.faceCoords1[elem.dim * b], elem.dim ) * nab_1; + g2 += dotProd( &nrml_a[0], &elem.faceCoords2[elem.dim * b], elem.dim ) * nab_2; + } + + // store local gap + nonmortarMesh.getNodalFields().m_node_gap[glbId] += ( g1 - g2 ); + + } // end a-loop over nonmortar nodes + +} // end ComputeNodalGap<>() - auto& plane = planes[cpID]; - - // get pair indices - IndexT index1 = pair.m_element_id1; - IndexT index2 = pair.m_element_id2; - - // get projected face coordinates - // stores projected coordinates in row-major format - ArrayT mortarX_bar(numNodesPerFace, dim); - ArrayT nonmortarX_bar(numNodesPerFace, dim); - // stores projected coordinates in column-major format - ArrayT mortarX_barT(dim, numNodesPerFace); - ArrayT nonmortarX_barT(dim, numNodesPerFace); - ProjectFaceNodesToPlane( mortarMesh, index1, - plane.m_nX, plane.m_nY, plane.m_nZ, - plane.m_cX, plane.m_cY, plane.m_cZ, - &mortarX_barT(0, 0), - &mortarX_barT(1, 0), - &mortarX_barT(2, 0) ); - ProjectFaceNodesToPlane( nonmortarMesh, index2, - plane.m_nX, plane.m_nY, plane.m_nZ, - plane.m_cX, plane.m_cY, plane.m_cZ, - &nonmortarX_barT(0, 0), - &nonmortarX_barT(1, 0), - &nonmortarX_barT(2, 0) ); - // populate row-major projected coordinates for the purpose of sending to - // the SurfaceContactElem struct - algorithm::transpose(mortarX_barT, mortarX_bar); - algorithm::transpose(nonmortarX_barT, nonmortarX_bar); - - // construct array of polygon overlap vertex coordinates - // TODO: get rid of this copy - ArrayT overlapX(plane.m_numPolyVert, dim); - for (IndexT i{0}; i < plane.m_numPolyVert; ++i) - { - overlapX(i, 0) = plane.m_polyX[i]; - overlapX(i, 1) = plane.m_polyY[i]; - overlapX(i, 2) = plane.m_polyZ[i]; - } +//------------------------------------------------------------------------------ +void ComputeSingleMortarGaps( CouplingScheme* cs ) +{ + MeshManager& meshManager = MeshManager::getInstance(); + MeshData& nonmortarMeshData = meshManager.at( cs->getMeshId2() ); + // compute nodal normals (do this outside the element loop) + // Note, this is guarded against zero element meshes + int const dim = cs->spatialDimension(); + nonmortarMeshData.computeNodalNormals( dim ); + + auto pairs = cs->getInterfacePairs(); + const IndexT numPairs = pairs.size(); + auto planes = cs->get3DContactPlanes(); + + //////////////////////////////////////////////////////////////////////// + // + // Grab mesh views + // + //////////////////////////////////////////////////////////////////////// + auto mortarMesh = cs->getMesh1().getView(); + auto nonmortarMesh = cs->getMesh2().getView(); + + IndexT const numNodesPerFace = mortarMesh.numberOfNodesPerElement(); + + RealT const* const x1 = mortarMesh.getPosition()[0].data(); + RealT const* const y1 = mortarMesh.getPosition()[1].data(); + RealT const* const z1 = mortarMesh.getPosition()[2].data(); + IndexT const* const mortarConn = mortarMesh.getConnectivity().data(); + + RealT const* const x2 = nonmortarMesh.getPosition()[0].data(); + RealT const* const y2 = nonmortarMesh.getPosition()[1].data(); + RealT const* const z2 = nonmortarMesh.getPosition()[2].data(); + IndexT const* nonmortarConn = nonmortarMesh.getConnectivity().data(); + + // declare local variables to hold face nodal coordinates + // and overlap vertex coordinates + IndexT size = dim * numNodesPerFace; + RealT mortarX[size]; + RealT nonmortarX[size]; + + //////////////////////////////////////////////////////////////////// + // compute nonmortar gaps to determine active set of contact dofs // + //////////////////////////////////////////////////////////////////// + int cpID = 0; + for ( IndexT kp = 0; kp < numPairs; ++kp ) { + auto& pair = pairs[kp]; + + if ( !pair.m_is_contact_candidate ) { + continue; + } + + auto& plane = planes[cpID]; + + // get pair indices + IndexT index1 = pair.m_element_id1; + IndexT index2 = pair.m_element_id2; + + // populate the current configuration nodal coordinates for the + // two faces + for ( int i = 0; i < numNodesPerFace; ++i ) { + int id = dim * i; + IndexT mortar_id = mortarConn[numNodesPerFace * index1 + i]; + IndexT nonmortar_id = nonmortarConn[numNodesPerFace * index2 + i]; + + mortarX[id] = x1[mortar_id]; + mortarX[id + 1] = y1[mortar_id]; + mortarX[id + 2] = z1[mortar_id]; + nonmortarX[id] = x2[nonmortar_id]; + nonmortarX[id + 1] = y2[nonmortar_id]; + nonmortarX[id + 2] = z2[nonmortar_id]; + } + + // get projected face coordinates + // stores projected coordinates in row-major format + ArrayT mortarX_bar( numNodesPerFace, dim ); + ArrayT nonmortarX_bar( numNodesPerFace, dim ); + // stores projected coordinates in column-major format + ArrayT mortarX_barT( dim, numNodesPerFace ); + ArrayT nonmortarX_barT( dim, numNodesPerFace ); + ProjectFaceNodesToPlane( mortarMesh, index1, plane.m_nX, plane.m_nY, plane.m_nZ, plane.m_cX, plane.m_cY, plane.m_cZ, + &mortarX_barT( 0, 0 ), &mortarX_barT( 1, 0 ), &mortarX_barT( 2, 0 ) ); + ProjectFaceNodesToPlane( nonmortarMesh, index2, plane.m_nX, plane.m_nY, plane.m_nZ, plane.m_cX, plane.m_cY, + plane.m_cZ, &nonmortarX_barT( 0, 0 ), &nonmortarX_barT( 1, 0 ), &nonmortarX_barT( 2, 0 ) ); + // populate row-major projected coordinates for the purpose of sending to + // the SurfaceContactElem struct + algorithm::transpose( mortarX_barT, mortarX_bar ); + algorithm::transpose( nonmortarX_barT, nonmortarX_bar ); + + // construct array of polygon overlap vertex coordinates + ArrayT overlapX( plane.m_numPolyVert, dim ); + for ( IndexT i{ 0 }; i < plane.m_numPolyVert; ++i ) { + overlapX( i, 0 ) = plane.m_polyX[i]; + overlapX( i, 1 ) = plane.m_polyY[i]; + overlapX( i, 2 ) = plane.m_polyZ[i]; + } + + // instantiate contact surface element for purposes of computing + // mortar weights. Note, this uses projected face coords + SurfaceContactElem elem( dim, mortarX_bar.data(), nonmortarX_bar.data(), overlapX.data(), numNodesPerFace, + plane.m_numPolyVert, &mortarMesh, &nonmortarMesh, index1, index2 ); + + // compute the mortar weights to be stored on the surface + // contact element struct. This must be done prior to computing nodal gaps + elem.overlapArea = plane.m_area; + ComputeMortarWeights( elem ); + + // compute mortar gaps. Note, we have to now use current configuration + // nodal coordinates on the contact element + elem.faceCoords1 = &mortarX[0]; + elem.faceCoords2 = &nonmortarX[0]; + + ComputeNodalGap( elem ); + + // TODO: fix this to register the actual number of active nonmortar gaps. + // This is not the appropriate data structure to put this information in + // as the SurfaceContactElem goes out of scope when we exit the loop. + // HAVE TO set the number of active constraints. For now set to + // all nonmortar face nodes. + elem.numActiveGaps = numNodesPerFace; + + ++cpID; + + } // end loop over pairs to compute nodal gaps + +} // end ComputeSingleMortarGaps() - // instantiate contact surface element for purposes of computing - // mortar weights. Note, this uses projected face coords - SurfaceContactElem elem( dim, mortarX_bar.data(), nonmortarX_bar.data(), - overlapX.data(), - numNodesPerFace, - plane.m_numPolyVert, - &mortarMesh, &nonmortarMesh, index1, index2 ); - - ////////////////////////////////// - // compute equilibrium residual // - ////////////////////////////////// - - // compute mortar weight - elem.overlapArea = plane.m_area; - ComputeMortarWeights( elem ); - - // TODO fix this. This may not be required. - // HAVE TO set the number of active constraints. For now set to - // all nonmortar face nodes. - elem.numActiveGaps = numNodesPerFace; - - // loop over face nodes (BOTH MORTAR and NONMORTAR - // contributions) - for (int a=0; a( cs->getMethodData() )->storeElemBlockJ( - {elem.faceId1, elem.faceId2, elem.faceId2}, - elem.blockJ - ); - } - else if ( lm_options.sparse_mode == SparseMode::MFEM_INDEX_SET || - lm_options.sparse_mode == SparseMode::MFEM_LINKED_LIST ) - { - static_cast( cs->getMethodData() )->assembleJacobian( elem, lm_options.sparse_mode ); - } - else - { - SLIC_WARNING("Unsupported Jacobian storage method."); - return 1; - } - +//------------------------------------------------------------------------------ +template <> +int ApplyNormal( CouplingScheme* cs ) +{ + /////////////////////////////////////////////////////// + // // + // compute single mortar gaps // + // // + // Note, this routine is guarded against null meshes // + /////////////////////////////////////////////////////// + ComputeSingleMortarGaps( cs ); + + auto pairs = cs->getInterfacePairs(); + const IndexT numPairs = pairs.size(); + auto planes = cs->get3DContactPlanes(); + + int const dim = cs->spatialDimension(); + + //////////////////////////////////////////////////////////////////////// + // + // Grab mesh views + // + //////////////////////////////////////////////////////////////////////// + auto mortarMesh = cs->getMesh1().getView(); + auto nonmortarMesh = cs->getMesh2().getView(); + + IndexT const numNodesPerFace = mortarMesh.numberOfNodesPerElement(); + + RealT* const fx1 = mortarMesh.getResponse()[0].data(); + RealT* const fy1 = mortarMesh.getResponse()[1].data(); + RealT* const fz1 = mortarMesh.getResponse()[2].data(); + IndexT const* const mortarConn = mortarMesh.getConnectivity().data(); + + RealT* const fx2 = nonmortarMesh.getResponse()[0].data(); + RealT* const fy2 = nonmortarMesh.getResponse()[1].data(); + RealT* const fz2 = nonmortarMesh.getResponse()[2].data(); + IndexT const* nonmortarConn = nonmortarMesh.getConnectivity().data(); + + int numTotalNodes = cs->getNumTotalNodes(); + int numRows = dim * numTotalNodes + numTotalNodes; + const EnforcementOptions& enforcement_options = const_cast( cs->getEnforcementOptions() ); + const LagrangeMultiplierImplicitOptions& lm_options = enforcement_options.lm_implicit_options; + if ( !cs->nullMeshes() ) { + if ( lm_options.sparse_mode == SparseMode::MFEM_ELEMENT_DENSE ) { + static_cast( cs->getMethodData() ) + ->reserveBlockJ( { BlockSpace::MORTAR, BlockSpace::NONMORTAR, BlockSpace::LAGRANGE_MULTIPLIER }, numPairs ); + } else if ( lm_options.sparse_mode == SparseMode::MFEM_INDEX_SET || + lm_options.sparse_mode == SparseMode::MFEM_LINKED_LIST ) { + static_cast( cs->getMethodData() )->allocateMfemSparseMatrix( numRows ); + } else { + SLIC_WARNING( "Unsupported Jacobian storage method." ); + return 1; + } + } + + //////////////////////////////////////////////////////////////// + // // + // compute equilibrium residual and/or Jacobian contributions // + // // + //////////////////////////////////////////////////////////////// + int cpID = 0; + for ( IndexT kp = 0; kp < numPairs; ++kp ) { + auto& pair = pairs[kp]; + + if ( !pair.m_is_contact_candidate ) { + continue; + } + + auto& plane = planes[cpID]; + + // get pair indices + IndexT index1 = pair.m_element_id1; + IndexT index2 = pair.m_element_id2; + + // get projected face coordinates + // stores projected coordinates in row-major format + ArrayT mortarX_bar( numNodesPerFace, dim ); + ArrayT nonmortarX_bar( numNodesPerFace, dim ); + // stores projected coordinates in column-major format + ArrayT mortarX_barT( dim, numNodesPerFace ); + ArrayT nonmortarX_barT( dim, numNodesPerFace ); + ProjectFaceNodesToPlane( mortarMesh, index1, plane.m_nX, plane.m_nY, plane.m_nZ, plane.m_cX, plane.m_cY, plane.m_cZ, + &mortarX_barT( 0, 0 ), &mortarX_barT( 1, 0 ), &mortarX_barT( 2, 0 ) ); + ProjectFaceNodesToPlane( nonmortarMesh, index2, plane.m_nX, plane.m_nY, plane.m_nZ, plane.m_cX, plane.m_cY, + plane.m_cZ, &nonmortarX_barT( 0, 0 ), &nonmortarX_barT( 1, 0 ), &nonmortarX_barT( 2, 0 ) ); + // populate row-major projected coordinates for the purpose of sending to + // the SurfaceContactElem struct + algorithm::transpose( mortarX_barT, mortarX_bar ); + algorithm::transpose( nonmortarX_barT, nonmortarX_bar ); + + // construct array of polygon overlap vertex coordinates + // TODO: get rid of this copy + ArrayT overlapX( plane.m_numPolyVert, dim ); + for ( IndexT i{ 0 }; i < plane.m_numPolyVert; ++i ) { + overlapX( i, 0 ) = plane.m_polyX[i]; + overlapX( i, 1 ) = plane.m_polyY[i]; + overlapX( i, 2 ) = plane.m_polyZ[i]; + } + + // instantiate contact surface element for purposes of computing + // mortar weights. Note, this uses projected face coords + SurfaceContactElem elem( dim, mortarX_bar.data(), nonmortarX_bar.data(), overlapX.data(), numNodesPerFace, + plane.m_numPolyVert, &mortarMesh, &nonmortarMesh, index1, index2 ); + + ////////////////////////////////// + // compute equilibrium residual // + ////////////////////////////////// + + // compute mortar weight + elem.overlapArea = plane.m_area; + ComputeMortarWeights( elem ); + + // TODO fix this. This may not be required. + // HAVE TO set the number of active constraints. For now set to + // all nonmortar face nodes. + elem.numActiveGaps = numNodesPerFace; + + // loop over face nodes (BOTH MORTAR and NONMORTAR + // contributions) + for ( int a = 0; a < numNodesPerFace; ++a ) { + int mortarIdA = mortarConn[index1 * numNodesPerFace + a]; + int nonmortarIdA = nonmortarConn[index2 * numNodesPerFace + a]; + + // inner loop over NONMORTAR nodes + for ( int b = 0; b < numNodesPerFace; ++b ) { + int nonmortarIdB = nonmortarConn[index2 * numNodesPerFace + b]; + + // We include all nonmortar nodes even if nodal gap is in separation. + // NOTE: Per testing, we include ALL nonmortar nodes + // in the computation after the geometric filtering and judge contact + // activity based on the gap AND the pressure solution + + RealT forceX = nonmortarMesh.getNodalFields().m_node_pressure[nonmortarIdB] * + nonmortarMesh.getNodalNormals()[0][nonmortarIdB]; + RealT forceY = nonmortarMesh.getNodalFields().m_node_pressure[nonmortarIdB] * + nonmortarMesh.getNodalNormals()[1][nonmortarIdB]; + RealT forceZ = nonmortarMesh.getNodalFields().m_node_pressure[nonmortarIdB] * + nonmortarMesh.getNodalNormals()[2][nonmortarIdB]; + + // contact nodal force is the interpolated force using mortar + // weights n_ab, where "a" is mortar or nonmortar node and "b" is + // nonmortar node. + fx1[mortarIdA] += forceX * elem.getMortarNonmortarWt( a, b ); + fy1[mortarIdA] += forceY * elem.getMortarNonmortarWt( a, b ); + fz1[mortarIdA] += forceZ * elem.getMortarNonmortarWt( a, b ); + + fx2[nonmortarIdA] -= forceX * elem.getNonmortarNonmortarWt( a, b ); + fy2[nonmortarIdA] -= forceY * elem.getNonmortarNonmortarWt( a, b ); + fz2[nonmortarIdA] -= forceZ * elem.getNonmortarNonmortarWt( a, b ); + + } // end inner loop over nonmortar nodes + + } // end outer loop over nonmortar and mortar nodes + + ////////////////////////////////////////////////////////// + // compute tangent stiffness contributions if requested // + ////////////////////////////////////////////////////////// + if ( lm_options.eval_mode == ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN || + lm_options.eval_mode == ImplicitEvalMode::MORTAR_JACOBIAN ) { + ComputeSingleMortarJacobian( elem ); + if ( lm_options.sparse_mode == SparseMode::MFEM_ELEMENT_DENSE ) { + static_cast( cs->getMethodData() ) + ->storeElemBlockJ( { elem.faceId1, elem.faceId2, elem.faceId2 }, elem.blockJ ); + } else if ( lm_options.sparse_mode == SparseMode::MFEM_INDEX_SET || + lm_options.sparse_mode == SparseMode::MFEM_LINKED_LIST ) { + static_cast( cs->getMethodData() )->assembleJacobian( elem, lm_options.sparse_mode ); + } else { + SLIC_WARNING( "Unsupported Jacobian storage method." ); + return 1; } + } - ++cpID; + ++cpID; - } // end of loop over interface pairs computing residual/Jacobian contributions + } // end of loop over interface pairs computing residual/Jacobian contributions - return 0; + return 0; -} // end ApplyNormal<>() +} // end ApplyNormal<>() //------------------------------------------------------------------------------ -template< > -void ComputeResidualJacobian< SINGLE_MORTAR, PRIMAL >( SurfaceContactElem & TRIBOL_UNUSED_PARAM(elem) ) +template <> +void ComputeResidualJacobian( SurfaceContactElem& TRIBOL_UNUSED_PARAM( elem ) ) { - // There is no Jacobian contribution for this block. Be safe and zero out... - return; + // There is no Jacobian contribution for this block. Be safe and zero out... + return; } //------------------------------------------------------------------------------ -template< > -void ComputeResidualJacobian< SINGLE_MORTAR, DUAL >( SurfaceContactElem & elem ) -{ - auto& nonmortarMesh = *elem.m_mesh2; - IndexT const * const nonmortarConn = nonmortarMesh.getConnectivity().data(); - - // loop over "a" nodes accumulating sums of mortar/nonmortar - // and nonmortar/nonmortar weights - for (int a = 0; a(BlockSpace::MORTAR), - static_cast(BlockSpace::LAGRANGE_MULTIPLIER) - )[ elem_xdof ] += nrml_b[0] * n_mortar_b; - elem.blockJ( - static_cast(BlockSpace::MORTAR), - static_cast(BlockSpace::LAGRANGE_MULTIPLIER) - )[ elem_xdof + dim_offset ] += nrml_b[1] * n_mortar_b; - elem.blockJ( - static_cast(BlockSpace::MORTAR), - static_cast(BlockSpace::LAGRANGE_MULTIPLIER) - )[ elem_xdof + 2*dim_offset ] += nrml_b[2] * n_mortar_b; - - // Fill block (1, 2) - elem.blockJ( - static_cast(BlockSpace::NONMORTAR), - static_cast(BlockSpace::LAGRANGE_MULTIPLIER) - )[ elem_xdof ] -= nrml_b[0] * n_nonmortar_b; - elem.blockJ( - static_cast(BlockSpace::NONMORTAR), - static_cast(BlockSpace::LAGRANGE_MULTIPLIER) - )[ elem_xdof + dim_offset ] -= nrml_b[1] * n_nonmortar_b; - elem.blockJ( - static_cast(BlockSpace::NONMORTAR), - static_cast(BlockSpace::LAGRANGE_MULTIPLIER) - )[ elem_xdof + 2*dim_offset ] -= nrml_b[2] * n_nonmortar_b; - - } // end loop over b nodes - - } // end loop over a nodes - - return; -} // end ComputeResidualJacobian<>() - -//------------------------------------------------------------------------------ -template< > -void ComputeConstraintJacobian< SINGLE_MORTAR, PRIMAL >( SurfaceContactElem & elem ) +template <> +void ComputeResidualJacobian( SurfaceContactElem& elem ) { - auto& nonmortarMesh = *elem.m_mesh2; - IndexT const * const nonmortarConn = nonmortarMesh.getConnectivity().data(); - - // loop over nonmortar nodes for which we are accumulating Jacobian - // contributions - for (int a = 0; a(BlockSpace::LAGRANGE_MULTIPLIER), - static_cast(BlockSpace::MORTAR) - )[ elem_xdof ] += nrml_a[0] * n_mortar_a; - elem.blockJ( - static_cast(BlockSpace::LAGRANGE_MULTIPLIER), - static_cast(BlockSpace::MORTAR) - )[ elem_xdof + dim_offset ] += nrml_a[1] * n_mortar_a; - elem.blockJ( - static_cast(BlockSpace::LAGRANGE_MULTIPLIER), - static_cast(BlockSpace::MORTAR) - )[ elem_xdof + 2*dim_offset ] += nrml_a[2] * n_mortar_a; - - // Fill block (2, 1) - elem.blockJ( - static_cast(BlockSpace::LAGRANGE_MULTIPLIER), - static_cast(BlockSpace::NONMORTAR) - )[ elem_xdof ] -= nrml_a[0] * n_nonmortar_a; - elem.blockJ( - static_cast(BlockSpace::LAGRANGE_MULTIPLIER), - static_cast(BlockSpace::NONMORTAR) - )[ elem_xdof + dim_offset ] -= nrml_a[1] * n_nonmortar_a; - elem.blockJ( - static_cast(BlockSpace::LAGRANGE_MULTIPLIER), - static_cast(BlockSpace::NONMORTAR) - )[ elem_xdof + 2*dim_offset ] -= nrml_a[2] * n_nonmortar_a; - - } // end loop over b nodes - - } // end loop over a nodes - - return; -} // end ComputeConstraintJacobian + // get mortar-nonmortar and nonmortar-nonmortar mortar weights + RealT n_mortar_b = elem.getMortarNonmortarWt( a, b ); // mortar-nonmortar weight + RealT n_nonmortar_b = + elem.getNonmortarNonmortarWt( a, b ); // nonmortar-nonmortar weight, note negative in formulation + + // fill Jrp element-pair Jacobian blocks + // Fill block (0, 2) + int elem_xdof = elem.getJacobianIndex( SurfaceContactElem::JrpBlock, a, b ); + int dim_offset = elem.getJacobianDimOffset( SurfaceContactElem::JrpBlock ); + elem.blockJ( static_cast( BlockSpace::MORTAR ), + static_cast( BlockSpace::LAGRANGE_MULTIPLIER ) )[elem_xdof] += nrml_b[0] * n_mortar_b; + elem.blockJ( static_cast( BlockSpace::MORTAR ), + static_cast( BlockSpace::LAGRANGE_MULTIPLIER ) )[elem_xdof + dim_offset] += + nrml_b[1] * n_mortar_b; + elem.blockJ( static_cast( BlockSpace::MORTAR ), + static_cast( BlockSpace::LAGRANGE_MULTIPLIER ) )[elem_xdof + 2 * dim_offset] += + nrml_b[2] * n_mortar_b; + + // Fill block (1, 2) + elem.blockJ( static_cast( BlockSpace::NONMORTAR ), + static_cast( BlockSpace::LAGRANGE_MULTIPLIER ) )[elem_xdof] -= nrml_b[0] * n_nonmortar_b; + elem.blockJ( static_cast( BlockSpace::NONMORTAR ), + static_cast( BlockSpace::LAGRANGE_MULTIPLIER ) )[elem_xdof + dim_offset] -= + nrml_b[1] * n_nonmortar_b; + elem.blockJ( static_cast( BlockSpace::NONMORTAR ), + static_cast( BlockSpace::LAGRANGE_MULTIPLIER ) )[elem_xdof + 2 * dim_offset] -= + nrml_b[2] * n_nonmortar_b; + + } // end loop over b nodes + + } // end loop over a nodes + + return; +} // end ComputeResidualJacobian<>() //------------------------------------------------------------------------------ -template< > -void ComputeConstraintJacobian< SINGLE_MORTAR, DUAL >( SurfaceContactElem& TRIBOL_UNUSED_PARAM(elem) ) +template <> +void ComputeConstraintJacobian( SurfaceContactElem& elem ) { - // unless we end up solving the complementarity equation, there is - // no Jacobian contribtion for this block. Zero out to be safe... - return; -} + auto& nonmortarMesh = *elem.m_mesh2; + IndexT const* const nonmortarConn = nonmortarMesh.getConnectivity().data(); + + // loop over nonmortar nodes for which we are accumulating Jacobian + // contributions + for ( int a = 0; a < elem.numFaceVert; ++a ) { + // get global nonmortar node id to index into nodal normals on + // nonmortar mesh + RealT nrml_a[elem.dim]; + int glbId = nonmortarConn[elem.numFaceVert * elem.faceId2 + a]; + + // We assemble ALL nonmortar node contributions even if gap is in separation. + // NOTE: Per mortar method testing we compute ALL nonmortar node + // contributions for faces that have positive areas of overlap per the + // geometric filtering. Contact activity is judged based on gaps AND + // the pressure solution. + + nrml_a[0] = nonmortarMesh.getNodalNormals()[0][glbId]; + nrml_a[1] = nonmortarMesh.getNodalNormals()[1][glbId]; + if ( elem.dim == 3 ) { + nrml_a[2] = nonmortarMesh.getNodalNormals()[2][glbId]; + } + + // single loop over "b" nodes accumulating sums of + // nonmortar(a)/mortar(b) and nonmortar(a)/nonmortar(b) weights + for ( int b = 0; b < elem.numFaceVert; ++b ) { + // get nonmortar-mortar and nonmortar-nonmortar mortar weights + RealT n_mortar_a = elem.getNonmortarMortarWt( a, b ); // nonmortar-mortar weight + RealT n_nonmortar_a = + elem.getNonmortarNonmortarWt( a, b ); // nonmortar-nonmortar weight, note negative in formulation + + // fill Jgu element-pair Jacobian blocks + // Fill block (2, 0) + int dim_offset = elem.getJacobianDimOffset( SurfaceContactElem::JguBlock ); + int elem_xdof = elem.getJacobianIndex( SurfaceContactElem::JguBlock, a, b ); + elem.blockJ( static_cast( BlockSpace::LAGRANGE_MULTIPLIER ), + static_cast( BlockSpace::MORTAR ) )[elem_xdof] += nrml_a[0] * n_mortar_a; + elem.blockJ( static_cast( BlockSpace::LAGRANGE_MULTIPLIER ), + static_cast( BlockSpace::MORTAR ) )[elem_xdof + dim_offset] += nrml_a[1] * n_mortar_a; + elem.blockJ( static_cast( BlockSpace::LAGRANGE_MULTIPLIER ), + static_cast( BlockSpace::MORTAR ) )[elem_xdof + 2 * dim_offset] += nrml_a[2] * n_mortar_a; + + // Fill block (2, 1) + elem.blockJ( static_cast( BlockSpace::LAGRANGE_MULTIPLIER ), + static_cast( BlockSpace::NONMORTAR ) )[elem_xdof] -= nrml_a[0] * n_nonmortar_a; + elem.blockJ( static_cast( BlockSpace::LAGRANGE_MULTIPLIER ), + static_cast( BlockSpace::NONMORTAR ) )[elem_xdof + dim_offset] -= nrml_a[1] * n_nonmortar_a; + elem.blockJ( static_cast( BlockSpace::LAGRANGE_MULTIPLIER ), + static_cast( BlockSpace::NONMORTAR ) )[elem_xdof + 2 * dim_offset] -= + nrml_a[2] * n_nonmortar_a; + + } // end loop over b nodes + + } // end loop over a nodes + + return; +} // end ComputeConstraintJacobian //------------------------------------------------------------------------------ -void ComputeSingleMortarJacobian( SurfaceContactElem & elem ) +template <> +void ComputeConstraintJacobian( SurfaceContactElem& TRIBOL_UNUSED_PARAM( elem ) ) { - elem.allocateBlockJ( LAGRANGE_MULTIPLIER ); + // unless we end up solving the complementarity equation, there is + // no Jacobian contribtion for this block. Zero out to be safe... + return; +} - ComputeResidualJacobian < SINGLE_MORTAR, PRIMAL >( elem ); +//------------------------------------------------------------------------------ +void ComputeSingleMortarJacobian( SurfaceContactElem& elem ) +{ + elem.allocateBlockJ( LAGRANGE_MULTIPLIER ); - ComputeResidualJacobian < SINGLE_MORTAR, DUAL >( elem ); + ComputeResidualJacobian( elem ); - ComputeConstraintJacobian< SINGLE_MORTAR, PRIMAL >( elem ); + ComputeResidualJacobian( elem ); - ComputeConstraintJacobian< SINGLE_MORTAR, DUAL >( elem ); + ComputeConstraintJacobian( elem ); - // Optionally print contact element matrix. Keep commented out here. - //elem.printBlockJMatrix(); + ComputeConstraintJacobian( elem ); - return; + // Optionally print contact element matrix. Keep commented out here. + // elem.printBlockJMatrix(); + return; } //------------------------------------------------------------------------------ -template< > -int GetMethodData< MORTAR_WEIGHTS >( CouplingScheme* cs ) +template <> +int GetMethodData( CouplingScheme* cs ) { - //////////////////////////////// - // // - // compute single mortar gaps // - // // - //////////////////////////////// - ComputeSingleMortarGaps( cs ); - - auto pairs = cs->getInterfacePairs(); - IndexT const numPairs = pairs.size(); - auto planes = cs->get3DContactPlanes(); - - const int dim = cs->spatialDimension(); - - auto mortarMesh = cs->getMesh1().getView(); - auto nonmortarMesh = cs->getMesh2().getView(); - IndexT const numNodesPerFace = mortarMesh.numberOfNodesPerElement(); - - int numRows = cs->getNumTotalNodes(); - static_cast( cs->getMethodData() )->allocateMfemSparseMatrix( numRows ); - - ////////////////////////////////////////////// - // // - // aggregate data to compute mortar weights // - // // - ////////////////////////////////////////////// - - int cpID = 0; - for (IndexT kp = 0; kp < numPairs; ++kp) - { - InterfacePair pair = pairs[kp]; - - if (!pair.m_is_contact_candidate) - { - continue; - } - - auto& plane = planes[cpID]; - - // get pair indices - IndexT index1 = pair.m_element_id1; - IndexT index2 = pair.m_element_id2; - - // get projected face coordinates - // stores projected coordinates in row-major format - ArrayT mortarX_bar(numNodesPerFace, dim); - ArrayT nonmortarX_bar(numNodesPerFace, dim); - // stores projected coordinates in column-major format - ArrayT mortarX_barT(dim, numNodesPerFace); - ArrayT nonmortarX_barT(dim, numNodesPerFace); - ProjectFaceNodesToPlane( mortarMesh, index1, - plane.m_nX, plane.m_nY, plane.m_nZ, - plane.m_cX, plane.m_cY, plane.m_cZ, - &mortarX_barT(0, 0), - &mortarX_barT(1, 0), - &mortarX_barT(2, 0) ); - ProjectFaceNodesToPlane( nonmortarMesh, index2, - plane.m_nX, plane.m_nY, plane.m_nZ, - plane.m_cX, plane.m_cY, plane.m_cZ, - &nonmortarX_barT(0, 0), - &nonmortarX_barT(1, 0), - &nonmortarX_barT(2, 0) ); - // populate row-major projected coordinates for the purpose of sending to - // the SurfaceContactElem struct - algorithm::transpose(mortarX_barT, mortarX_bar); - algorithm::transpose(nonmortarX_barT, nonmortarX_bar); - - // construct array of polygon overlap vertex coordinates - ArrayT overlapX(plane.m_numPolyVert, dim); - for (IndexT i{0}; i < plane.m_numPolyVert; ++i) - { - overlapX(i, 0) = plane.m_polyX[i]; - overlapX(i, 1) = plane.m_polyY[i]; - overlapX(i, 2) = plane.m_polyZ[i]; - } - - // instantiate contact surface element for purposes of computing - // mortar weights. Note, this uses projected face coords - SurfaceContactElem elem( dim, mortarX_bar.data(), nonmortarX_bar.data(), - overlapX.data(), - numNodesPerFace, - plane.m_numPolyVert, - &mortarMesh, &nonmortarMesh, index1, index2 ); - - // compute the mortar weights to be stored on the surface - // contact element struct. This must be done prior to computing nodal gaps - elem.overlapArea = plane.m_area; - - ComputeMortarWeights( elem ); - - elem.numActiveGaps = numNodesPerFace; - - // assemble mortar weight contributions sum_alpha int_alpha phi_a phi_b da. - // Note: active nonmortar nodes (i.e. active gaps) are checked in this routine. - const EnforcementOptions& enforcement_options = const_cast(cs->getEnforcementOptions()); - const SparseMode sparse_mode = enforcement_options.lm_implicit_options.sparse_mode; - if (sparse_mode == SparseMode::MFEM_ELEMENT_DENSE) - { - SLIC_WARNING( "GetMethodData() MFEM_ELEMENT_DENSE " << - "Unassembled element dense matrix output not implemented." ); - return 1; - } - static_cast( cs->getMethodData() )->assembleMortarWts( elem, sparse_mode ); - - ++cpID; - - } // end loop over pairs to assemble mortar weights - - return 0; - -} // end GetMethodData< MORTAR_WEIGHTS >() + //////////////////////////////// + // // + // compute single mortar gaps // + // // + //////////////////////////////// + ComputeSingleMortarGaps( cs ); + + auto pairs = cs->getInterfacePairs(); + IndexT const numPairs = pairs.size(); + auto planes = cs->get3DContactPlanes(); + + const int dim = cs->spatialDimension(); + + auto mortarMesh = cs->getMesh1().getView(); + auto nonmortarMesh = cs->getMesh2().getView(); + IndexT const numNodesPerFace = mortarMesh.numberOfNodesPerElement(); + + int numRows = cs->getNumTotalNodes(); + static_cast( cs->getMethodData() )->allocateMfemSparseMatrix( numRows ); + + ////////////////////////////////////////////// + // // + // aggregate data to compute mortar weights // + // // + ////////////////////////////////////////////// + + int cpID = 0; + for ( IndexT kp = 0; kp < numPairs; ++kp ) { + InterfacePair pair = pairs[kp]; + + if ( !pair.m_is_contact_candidate ) { + continue; + } + + auto& plane = planes[cpID]; + + // get pair indices + IndexT index1 = pair.m_element_id1; + IndexT index2 = pair.m_element_id2; + + // get projected face coordinates + // stores projected coordinates in row-major format + ArrayT mortarX_bar( numNodesPerFace, dim ); + ArrayT nonmortarX_bar( numNodesPerFace, dim ); + // stores projected coordinates in column-major format + ArrayT mortarX_barT( dim, numNodesPerFace ); + ArrayT nonmortarX_barT( dim, numNodesPerFace ); + ProjectFaceNodesToPlane( mortarMesh, index1, plane.m_nX, plane.m_nY, plane.m_nZ, plane.m_cX, plane.m_cY, plane.m_cZ, + &mortarX_barT( 0, 0 ), &mortarX_barT( 1, 0 ), &mortarX_barT( 2, 0 ) ); + ProjectFaceNodesToPlane( nonmortarMesh, index2, plane.m_nX, plane.m_nY, plane.m_nZ, plane.m_cX, plane.m_cY, + plane.m_cZ, &nonmortarX_barT( 0, 0 ), &nonmortarX_barT( 1, 0 ), &nonmortarX_barT( 2, 0 ) ); + // populate row-major projected coordinates for the purpose of sending to + // the SurfaceContactElem struct + algorithm::transpose( mortarX_barT, mortarX_bar ); + algorithm::transpose( nonmortarX_barT, nonmortarX_bar ); + + // construct array of polygon overlap vertex coordinates + ArrayT overlapX( plane.m_numPolyVert, dim ); + for ( IndexT i{ 0 }; i < plane.m_numPolyVert; ++i ) { + overlapX( i, 0 ) = plane.m_polyX[i]; + overlapX( i, 1 ) = plane.m_polyY[i]; + overlapX( i, 2 ) = plane.m_polyZ[i]; + } + + // instantiate contact surface element for purposes of computing + // mortar weights. Note, this uses projected face coords + SurfaceContactElem elem( dim, mortarX_bar.data(), nonmortarX_bar.data(), overlapX.data(), numNodesPerFace, + plane.m_numPolyVert, &mortarMesh, &nonmortarMesh, index1, index2 ); + + // compute the mortar weights to be stored on the surface + // contact element struct. This must be done prior to computing nodal gaps + elem.overlapArea = plane.m_area; + + ComputeMortarWeights( elem ); + + elem.numActiveGaps = numNodesPerFace; + + // assemble mortar weight contributions sum_alpha int_alpha phi_a phi_b da. + // Note: active nonmortar nodes (i.e. active gaps) are checked in this routine. + const EnforcementOptions& enforcement_options = const_cast( cs->getEnforcementOptions() ); + const SparseMode sparse_mode = enforcement_options.lm_implicit_options.sparse_mode; + if ( sparse_mode == SparseMode::MFEM_ELEMENT_DENSE ) { + SLIC_WARNING( "GetMethodData() MFEM_ELEMENT_DENSE " + << "Unassembled element dense matrix output not implemented." ); + return 1; + } + static_cast( cs->getMethodData() )->assembleMortarWts( elem, sparse_mode ); + + ++cpID; + + } // end loop over pairs to assemble mortar weights + + return 0; + +} // end GetMethodData< MORTAR_WEIGHTS >() //------------------------------------------------------------------------------ -} // end namespace tribol +} // end namespace tribol diff --git a/src/tribol/physics/Mortar.hpp b/src/tribol/physics/Mortar.hpp index 3cef81c3..0cfae8b4 100644 --- a/src/tribol/physics/Mortar.hpp +++ b/src/tribol/physics/Mortar.hpp @@ -9,24 +9,23 @@ #include "tribol/common/Parameters.hpp" #include "Physics.hpp" -namespace tribol -{ +namespace tribol { // forward declarations struct SurfaceContactElem; enum VariableType { - PRIMAL, - DUAL, - - NUM_VARIABLES + PRIMAL, + DUAL, + + NUM_VARIABLES }; /*! * - * \brief computes the integral of (phi_a * phi_b) over a contact - * overlap for all (a,b) combinations. + * \brief computes the integral of (phi_a * phi_b) over a contact + * overlap for all (a,b) combinations. * * \note the mortar weights are stored on the SurfaceContactElem object * @@ -34,7 +33,7 @@ enum VariableType * * */ -void ComputeMortarWeights( SurfaceContactElem & elem ); +void ComputeMortarWeights( SurfaceContactElem& elem ); /*! * @@ -52,8 +51,8 @@ void ComputeSingleMortarGaps( CouplingScheme* cs ); * \param [in] elem surface contact element object for contact face-pair * */ -template< ContactMethod M > -void ComputeNodalGap( SurfaceContactElem & elem ); +template +void ComputeNodalGap( SurfaceContactElem& elem ); /*! * @@ -64,92 +63,92 @@ void ComputeNodalGap( SurfaceContactElem & elem ); * \param [in] elem surface contact element object for contact face-pair * */ -template< > -void ComputeNodalGap< SINGLE_MORTAR >( SurfaceContactElem & elem ); +template <> +void ComputeNodalGap( SurfaceContactElem& elem ); /*! * - * \brief method to compute the Jacobian contributions of the contact residual - * term with respect to either the primal or dual variable for a single + * \brief method to compute the Jacobian contributions of the contact residual + * term with respect to either the primal or dual variable for a single * contact face-pair. * * \param [in] elem surface contact element struct * */ -template< ContactMethod M, VariableType V > -void ComputeResidualJacobian( SurfaceContactElem & elem ); +template +void ComputeResidualJacobian( SurfaceContactElem& elem ); /*! * - * \brief method to compute the Jacobian contributions of the contact gap - * constraint with respect to either the primal or dual variable for a single + * \brief method to compute the Jacobian contributions of the contact gap + * constraint with respect to either the primal or dual variable for a single * contact face-pair. * * \param [in] elem surface contact element struct * */ -template< ContactMethod M, VariableType V > -void ComputeConstraintJacobian( SurfaceContactElem & elem ); +template +void ComputeConstraintJacobian( SurfaceContactElem& elem ); /*! * - * \brief routine to apply interface physics in the direction normal to the interface + * \brief routine to apply interface physics in the direction normal to the interface * * \param [in] cs pointer to the coupling scheme * * \return 0 if no error * */ -template< > -int ApplyNormal< SINGLE_MORTAR, LAGRANGE_MULTIPLIER >( CouplingScheme* cs ); +template <> +int ApplyNormal( CouplingScheme* cs ); /*! * - * \brief explicit specialization of method to compute the Jacobian contributions of - * the contact residual term with respect to the primal variable for a single + * \brief explicit specialization of method to compute the Jacobian contributions of + * the contact residual term with respect to the primal variable for a single * contact face-pair. * * \param [in] elem surface contact element struct * */ -template< > -void ComputeResidualJacobian< SINGLE_MORTAR, PRIMAL >( SurfaceContactElem & elem ); +template <> +void ComputeResidualJacobian( SurfaceContactElem& elem ); /*! * - * \brief explicit specialization of method to compute the Jacobian contributions of - * the contact residual term with respect to the dual variable for a single + * \brief explicit specialization of method to compute the Jacobian contributions of + * the contact residual term with respect to the dual variable for a single * contact face-pair. * * \param [in] elem surface contact element struct * */ -template< > -void ComputeResidualJacobian< SINGLE_MORTAR, DUAL >( SurfaceContactElem & elem ); +template <> +void ComputeResidualJacobian( SurfaceContactElem& elem ); /*! * - * \brief explicit specialization of method to compute the Jacobian contributions of - * the contact gap constraint with respect to the primal variable for a single + * \brief explicit specialization of method to compute the Jacobian contributions of + * the contact gap constraint with respect to the primal variable for a single * contact face-pair. * * \param [in] elem surface contact element struct * */ -template< > -void ComputeConstraintJacobian< SINGLE_MORTAR, PRIMAL >( SurfaceContactElem & elem ); +template <> +void ComputeConstraintJacobian( SurfaceContactElem& elem ); /*! * - * \brief explicit specialization of method to compute the Jacobian contributions of - * the contact gap constraint with respect to the dual variable for a single + * \brief explicit specialization of method to compute the Jacobian contributions of + * the contact gap constraint with respect to the dual variable for a single * contact face-pair. * * \param [in] elem surface contact element struct * */ -template< > -void ComputeConstraintJacobian< SINGLE_MORTAR, DUAL >( SurfaceContactElem & elem ); +template <> +void ComputeConstraintJacobian( SurfaceContactElem& elem ); /*! * @@ -158,7 +157,7 @@ void ComputeConstraintJacobian< SINGLE_MORTAR, DUAL >( SurfaceContactElem & elem * \param [in] elem surface contact element struct * */ -void ComputeSingleMortarJacobian( SurfaceContactElem & elem ); +void ComputeSingleMortarJacobian( SurfaceContactElem& elem ); /*! * @@ -169,9 +168,9 @@ void ComputeSingleMortarJacobian( SurfaceContactElem & elem ); * \return 0 if no error * */ -template< > -int GetMethodData< MORTAR_WEIGHTS >( CouplingScheme* cs ); +template <> +int GetMethodData( CouplingScheme* cs ); -} +} // namespace tribol #endif /* SRC_PHYSICS_MORTAR_HPP_ */ diff --git a/src/tribol/physics/Physics.cpp b/src/tribol/physics/Physics.cpp index d6850761..5032ba42 100644 --- a/src/tribol/physics/Physics.cpp +++ b/src/tribol/physics/Physics.cpp @@ -13,136 +13,116 @@ // Axom includes #include "axom/slic.hpp" -namespace tribol -{ +namespace tribol { -int ApplyInterfacePhysics( CouplingScheme* cs, - int TRIBOL_UNUSED_PARAM(cycle), - RealT TRIBOL_UNUSED_PARAM(t) ) +int ApplyInterfacePhysics( CouplingScheme* cs, int TRIBOL_UNUSED_PARAM( cycle ), RealT TRIBOL_UNUSED_PARAM( t ) ) { - // call the appropriate normal and tangential interface physics - // routines based on method, enforcement strategy, and interface - // model combinations. Note that combinations that are not yet - // implemented or are not possible are checked in a call to the - // API to register a coupling scheme; such checks are not required - // here. - - int err_nrml = false; - int err_tang = false; - int err_data = false; - - // switch over numerical method - switch ( cs->getContactMethod() ) - { - case COMMON_PLANE: + // call the appropriate normal and tangential interface physics + // routines based on method, enforcement strategy, and interface + // model combinations. Note that combinations that are not yet + // implemented or are not possible are checked in a call to the + // API to register a coupling scheme; such checks are not required + // here. + + int err_nrml = false; + int err_tang = false; + int err_data = false; + + // switch over numerical method + switch ( cs->getContactMethod() ) { + case COMMON_PLANE: // switch over enforcement method for normal (i.e. normal direction) enforcement - switch ( cs->getEnforcementMethod() ) - { - case PENALTY: - switch ( cs->getContactCase() ) - { - // apply normal physics for ALL cases - default: - { - err_nrml = ApplyNormal< COMMON_PLANE, PENALTY >( cs ); - break; - } + switch ( cs->getEnforcementMethod() ) { + case PENALTY: + switch ( cs->getContactCase() ) { + // apply normal physics for ALL cases + default: { + err_nrml = ApplyNormal( cs ); + break; + } + } + // query the model for application of tangential physics + switch ( cs->getContactModel() ) { + // no tangential physics implemented yet + default: { + break; } - // query the model for application of tangential physics - switch ( cs->getContactModel() ) - { - // no tangential physics implemented yet - default: - { - break; - } - } // end switch on contact model - break; - default: - break; - } // end switch over enforcement method - break; // end case COMMON_PLANE - - case SINGLE_MORTAR : - switch ( cs->getEnforcementMethod() ) - { - case LAGRANGE_MULTIPLIER: - switch ( cs->getContactModel() ) - { - case FRICTIONLESS : - err_nrml = ApplyNormal< SINGLE_MORTAR, LAGRANGE_MULTIPLIER >( cs ); - break; - default: - break; - } // end switch on contact model - break; - default: - break; - } // end switch on enforcement method - break; // end case SINGLE_MORTAR - - case ALIGNED_MORTAR: - switch ( cs->getEnforcementMethod() ) - { - case LAGRANGE_MULTIPLIER : - switch ( cs->getContactModel() ) - { - case FRICTIONLESS : - err_nrml = ApplyNormal< ALIGNED_MORTAR, LAGRANGE_MULTIPLIER >( cs ); - break; - default: - break; - } // end switch on contact model - break; - default: - break; - } // end switch on enforcement method - break; // end case ALIGNED_MORTAR - - case MORTAR_WEIGHTS: + } // end switch on contact model + break; + default: + break; + } // end switch over enforcement method + break; // end case COMMON_PLANE + + case SINGLE_MORTAR: + switch ( cs->getEnforcementMethod() ) { + case LAGRANGE_MULTIPLIER: + switch ( cs->getContactModel() ) { + case FRICTIONLESS: + err_nrml = ApplyNormal( cs ); + break; + default: + break; + } // end switch on contact model + break; + default: + break; + } // end switch on enforcement method + break; // end case SINGLE_MORTAR + + case ALIGNED_MORTAR: + switch ( cs->getEnforcementMethod() ) { + case LAGRANGE_MULTIPLIER: + switch ( cs->getContactModel() ) { + case FRICTIONLESS: + err_nrml = ApplyNormal( cs ); + break; + default: + break; + } // end switch on contact model + break; + default: + break; + } // end switch on enforcement method + break; // end case ALIGNED_MORTAR + + case MORTAR_WEIGHTS: // no enforcement for this method and no need to call visualization. - err_data = GetMethodData< MORTAR_WEIGHTS >( cs ); + err_data = GetMethodData( cs ); break; - default: + default: // don't do anything. Note, no need to throw an error here as unimplemented // interface methods will already have been caught break; - } // end switch (method) - - // error checking - if ( err_nrml != 0 ) - { - // note, not all ranks will get here if a rank has null-meshes - SLIC_WARNING("ApplyInterfacePhysics: error in application of " << - "'normal' physics method for " << - "coupling scheme, " << cs->getId() << "."); - - return err_nrml; - } - else if ( err_tang != 0 ) - { - // note, not all ranks will get here if a rank has null-meshes - SLIC_WARNING("ApplyInterfacePhysics: error in application of " << - "'tangential' physics method for " << - "coupling scheme, " << cs->getId() << "."); - - return err_tang; - } - else if ( err_data != 0 ) - { - // note, not all ranks will get here if a rank has null-meshes - SLIC_WARNING("ApplyInterfacePhysics: error in call to " << - "GetMethodData for coupling scheme, " << cs->getId() << "."); - return err_data; - } - else - { - // no error - return 0; - } - -} // end ApplyInterfacePhysics - -} // end namespace tribol + } // end switch (method) + + // error checking + if ( err_nrml != 0 ) { + // note, not all ranks will get here if a rank has null-meshes + SLIC_WARNING( "ApplyInterfacePhysics: error in application of " + << "'normal' physics method for " + << "coupling scheme, " << cs->getId() << "." ); + + return err_nrml; + } else if ( err_tang != 0 ) { + // note, not all ranks will get here if a rank has null-meshes + SLIC_WARNING( "ApplyInterfacePhysics: error in application of " + << "'tangential' physics method for " + << "coupling scheme, " << cs->getId() << "." ); + + return err_tang; + } else if ( err_data != 0 ) { + // note, not all ranks will get here if a rank has null-meshes + SLIC_WARNING( "ApplyInterfacePhysics: error in call to " + << "GetMethodData for coupling scheme, " << cs->getId() << "." ); + return err_data; + } else { + // no error + return 0; + } + +} // end ApplyInterfacePhysics + +} // end namespace tribol diff --git a/src/tribol/physics/Physics.hpp b/src/tribol/physics/Physics.hpp index fb12acee..1c00a859 100644 --- a/src/tribol/physics/Physics.hpp +++ b/src/tribol/physics/Physics.hpp @@ -8,8 +8,7 @@ #include "tribol/common/Parameters.hpp" -namespace tribol -{ +namespace tribol { // forward declarations struct InterfacePair; @@ -27,8 +26,7 @@ class CouplingScheme; * \return 0 if no errors * */ -int ApplyInterfacePhysics( CouplingScheme* cs, - int cycle, RealT t ); +int ApplyInterfacePhysics( CouplingScheme* cs, int cycle, RealT t ); /*! * * \brief applies interface method in the normal direction @@ -38,7 +36,7 @@ int ApplyInterfacePhysics( CouplingScheme* cs, * \return 0 if no error * */ -template< ContactMethod M, EnforcementMethod E > +template int ApplyNormal( CouplingScheme* cs ); /*! @@ -50,9 +48,7 @@ int ApplyNormal( CouplingScheme* cs ); * \return 0 if no error * */ -template< ContactMethod M, - EnforcementMethod E, - ContactModel Model > +template int ApplyTangential( CouplingScheme* cs ); /*! @@ -64,9 +60,9 @@ int ApplyTangential( CouplingScheme* cs ); * \return 0 if no error * */ -template< ContactMethod M > +template int GetMethodData( CouplingScheme* cs ); -} // end namespace TRIBOL +} // namespace tribol #endif /* SRC_PHYSICS_PHYSICS_HPP_ */ diff --git a/src/tribol/search/InterfacePairFinder.cpp b/src/tribol/search/InterfacePairFinder.cpp index b45778cd..4d080677 100644 --- a/src/tribol/search/InterfacePairFinder.cpp +++ b/src/tribol/search/InterfacePairFinder.cpp @@ -21,45 +21,37 @@ namespace primal = axom::primal; namespace spin = axom::spin; - -namespace tribol -{ +namespace tribol { /*! * Perform geometry/proximity checks 1-4 */ -TRIBOL_HOST_DEVICE bool geomFilter( IndexT element_id1, IndexT element_id2, - const MeshData::Viewer& mesh1, const MeshData::Viewer& mesh2, - ContactMode mode, bool auto_contact_check ) +TRIBOL_HOST_DEVICE bool geomFilter( IndexT element_id1, IndexT element_id2, const MeshData::Viewer& mesh1, + const MeshData::Viewer& mesh2, ContactMode mode, bool auto_contact_check ) { - /// CHECK #1: Check to make sure the two face ids are not the same + /// CHECK #1: Check to make sure the two face ids are not the same /// and the two mesh ids are not the same. - if ((mesh1.meshId() == mesh2.meshId()) && (element_id1 == element_id2)) - { + if ( ( mesh1.meshId() == mesh2.meshId() ) && ( element_id1 == element_id2 ) ) { return false; } int dim = mesh1.spatialDimension(); /// CHECK #2: Auto-contact precludes faces that share a common - /// node(s). We want to preclude two adjacent faces from interacting + /// node(s). We want to preclude two adjacent faces from interacting // due to problematic configurations, such as corners where the - // configuration and opposing normals appear to be in contact, but + // configuration and opposing normals appear to be in contact, but // are not. // // Note: non-auto-contact coupling schemes should typically be amongst // topologically disconnected surfaces unless it is known apriori that // face-pairs with shared nodes can in fact contact. - if (auto_contact_check) - { - for (IndexT i{0}; i < mesh1.numberOfNodesPerElement(); ++i) - { - int node1 = mesh1.getGlobalNodeId(element_id1, i); - for (IndexT j{0}; j < mesh2.numberOfNodesPerElement(); ++j) - { - int node2 = mesh2.getGlobalNodeId(element_id2, j); - if (node1 == node2) - { + if ( auto_contact_check ) { + for ( IndexT i{ 0 }; i < mesh1.numberOfNodesPerElement(); ++i ) { + int node1 = mesh1.getGlobalNodeId( element_id1, i ); + for ( IndexT j{ 0 }; j < mesh2.numberOfNodesPerElement(); ++j ) { + int node2 = mesh2.getGlobalNodeId( element_id2, j ); + if ( node1 == node2 ) { return false; } } @@ -68,94 +60,86 @@ TRIBOL_HOST_DEVICE bool geomFilter( IndexT element_id1, IndexT element_id2, /// CHECK #3: Check that face normals are opposing up to some tolerance. /// This uses a hard coded normal tolerance for this check. - RealT nrmlTol = -0.173648177; // taken as cos(100) between face pair - + RealT nrmlTol = -0.173648177; // taken as cos(100) between face pair + RealT nrmlCheck = 0.0; - for (int d{0}; d < dim; ++d) - { - nrmlCheck += mesh1.getElementNormals()[d][element_id1] - * mesh2.getElementNormals()[d][element_id2]; + for ( int d{ 0 }; d < dim; ++d ) { + nrmlCheck += mesh1.getElementNormals()[d][element_id1] * mesh2.getElementNormals()[d][element_id2]; } // check normal projection against tolerance - if (nrmlCheck > nrmlTol) { + if ( nrmlCheck > nrmlTol ) { return false; } - /// CHECK #4 (3D): Perform radius check, which involves seeing if + /// CHECK #4 (3D): Perform radius check, which involves seeing if /// the distance between the two face vertex averaged /// centroid is less than the sum of the two face radii. - /// The face radii are taken to be the magnitude of the - /// longest vector from that face's vertex averaged + /// The face radii are taken to be the magnitude of the + /// longest vector from that face's vertex averaged /// centroid to one its nodes. RealT offset_tol = 0.05; - if (dim == 3) - { - RealT r1 = mesh1.getFaceRadius()[ element_id1 ]; - RealT r2 = mesh2.getFaceRadius()[ element_id2 ]; + if ( dim == 3 ) { + RealT r1 = mesh1.getFaceRadius()[element_id1]; + RealT r2 = mesh2.getFaceRadius()[element_id2]; // set maximum offset of face centroids for inclusion - RealT distMax = r1 + r2; // default is sum of face radii + RealT distMax = r1 + r2; // default is sum of face radii - // check if the contact mode is conforming, in which case the + // check if the contact mode is conforming, in which case the // faces are supposed to be aligned - if (mode == SURFACE_TO_SURFACE_CONFORMING) - { - // use 5% of max face radius for conforming case as + if ( mode == SURFACE_TO_SURFACE_CONFORMING ) { + // use 5% of max face radius for conforming case as // tolerance on face offsets - distMax *= offset_tol; + distMax *= offset_tol; } // compute the distance between the two face centroids - RealT distX = mesh2.getElementCentroids()[0][ element_id2 ] - mesh1.getElementCentroids()[0][ element_id1 ]; - RealT distY = mesh2.getElementCentroids()[1][ element_id2 ] - mesh1.getElementCentroids()[1][ element_id1 ]; - RealT distZ = mesh2.getElementCentroids()[2][ element_id2 ] - mesh1.getElementCentroids()[2][ element_id1 ]; - + RealT distX = mesh2.getElementCentroids()[0][element_id2] - mesh1.getElementCentroids()[0][element_id1]; + RealT distY = mesh2.getElementCentroids()[1][element_id2] - mesh1.getElementCentroids()[1][element_id1]; + RealT distZ = mesh2.getElementCentroids()[2][element_id2] - mesh1.getElementCentroids()[2][element_id1]; + // scale the magnitude of the computed distance by 1% to include nearly coincident nodes/edges for 3D // polygons that are nearly coplanar; otherwise, we may miss this configuration - RealT distMag = 1.01*magnitude(distX, distY, distZ ); + RealT distMag = 1.01 * magnitude( distX, distY, distZ ); - if (distMag > (distMax)) { + if ( distMag > ( distMax ) ) { return false; - } - } // end of dim == 3 - else if (dim == 2) - { + } + } // end of dim == 3 + else if ( dim == 2 ) { // get 1/2 edge length off the mesh data - RealT e1 = 0.5 * mesh1.getElementAreas()[ element_id1 ]; - RealT e2 = 0.5 * mesh2.getElementAreas()[ element_id2 ]; + RealT e1 = 0.5 * mesh1.getElementAreas()[element_id1]; + RealT e2 = 0.5 * mesh2.getElementAreas()[element_id2]; // set maximum offset of edge centroids for inclusion. Scale by 1% to make // sure we include nearly proximate faces for co-planar faces - RealT distMax = 1.01*(e1 + e2); + RealT distMax = 1.01 * ( e1 + e2 ); - // check if the contact mode is conforming, in which case the + // check if the contact mode is conforming, in which case the // edges are supposed to be aligned - if (mode == SURFACE_TO_SURFACE_CONFORMING) - { - // use 5% of max face radius for conforming case as + if ( mode == SURFACE_TO_SURFACE_CONFORMING ) { + // use 5% of max face radius for conforming case as // tolerance on face offsets - distMax *= offset_tol; + distMax *= offset_tol; } // compute the distance between the two edge centroids - RealT distX = mesh2.getElementCentroids()[0][ element_id2 ] - mesh1.getElementCentroids()[0][ element_id1 ]; - RealT distY = mesh2.getElementCentroids()[1][ element_id2 ] - mesh1.getElementCentroids()[1][ element_id1 ]; + RealT distX = mesh2.getElementCentroids()[0][element_id2] - mesh1.getElementCentroids()[0][element_id1]; + RealT distY = mesh2.getElementCentroids()[1][element_id2] - mesh1.getElementCentroids()[1][element_id1]; - RealT distMag = magnitude(distX, distY); + RealT distMag = magnitude( distX, distY ); // include faces where separation equals distMax - if (distMag > (distMax)) - { + if ( distMag > ( distMax ) ) { return false; } - } // end of dim == 2 + } // end of dim == 2 // if we made it here we passed all checks return true; -} // end geomFilter() - +} // end geomFilter() /*! * \brief Base class to compute the candidate pairs for a coupling scheme @@ -163,19 +147,18 @@ TRIBOL_HOST_DEVICE bool geomFilter( IndexT element_id1, IndexT element_id2, * \a initialize() must be called prior to \a findInterfacePairs() * */ -class SearchBase -{ -public: - SearchBase() {}; - virtual ~SearchBase() {}; +class SearchBase { + public: + SearchBase(){}; + virtual ~SearchBase(){}; /*! - * Prepares the object for spatial searches - */ + * Prepares the object for spatial searches + */ virtual void initialize() = 0; /*! - * Find candidates in first mesh for each element in second mesh of coupling scheme. - */ + * Find candidates in first mesh for each element in second mesh of coupling scheme. + */ virtual void findInterfacePairs() = 0; }; @@ -192,24 +175,16 @@ class SearchBase * * \tparam D The spatial dimension of the coupling scheme mesh vertices. */ -template -class CartesianProduct : public SearchBase -{ -public: +template +class CartesianProduct : public SearchBase { + public: /*! - * Constructs a CartesianProduct instance over CouplingScheme \a couplingScheme - * \pre couplingScheme is not null - */ - CartesianProduct(CouplingScheme* couplingScheme) - : m_coupling_scheme(couplingScheme) - { - } - - - void initialize() override - { - } + * Constructs a CartesianProduct instance over CouplingScheme \a couplingScheme + * \pre couplingScheme is not null + */ + CartesianProduct( CouplingScheme* couplingScheme ) : m_coupling_scheme( couplingScheme ) {} + void initialize() override {} void findInterfacePairs() override { @@ -222,18 +197,17 @@ class CartesianProduct : public SearchBase // Reserve memory for boolean array indicating which pairs are proximate int maxNumPairs = mesh1NumElems * mesh2NumElems; bool is_symm = m_coupling_scheme->getMeshId1() == m_coupling_scheme->getMeshId2(); - if (is_symm) - { - // account for symmetry: the max number of pairs when the meshes are the - // same is the upper triangular portion of the cartesian product pair - // matrix - maxNumPairs = mesh1NumElems * (mesh1NumElems + 1) / 2; + if ( is_symm ) { + // account for symmetry: the max number of pairs when the meshes are the + // same is the upper triangular portion of the cartesian product pair + // matrix + maxNumPairs = mesh1NumElems * ( mesh1NumElems + 1 ) / 2; } - ArrayT proximityArray(maxNumPairs, maxNumPairs, m_coupling_scheme->getAllocatorId()); + ArrayT proximityArray( maxNumPairs, maxNumPairs, m_coupling_scheme->getAllocatorId() ); bool* isProximate = proximityArray.data(); // Allocate memory for a counter - ArrayT countArray(1, 1, m_coupling_scheme->getAllocatorId()); + ArrayT countArray( 1, 1, m_coupling_scheme->getAllocatorId() ); int* pCount = countArray.data(); ContactMode cmode = m_coupling_scheme->getContactMode(); @@ -241,76 +215,68 @@ class CartesianProduct : public SearchBase bool auto_contact_check = m_coupling_scheme->getParameters().auto_contact_check; // count how many pairs are proximate - forAllExec(m_coupling_scheme->getExecutionMode(), maxNumPairs, - [mesh1NumElems, mesh2NumElems, is_symm, isProximate, mesh1, mesh2, cmode, - pCount, auto_contact_check] TRIBOL_HOST_DEVICE (IndexT i) - { - IndexT fromIdx = i / mesh2NumElems; - IndexT toIdx = i % mesh2NumElems; - if (is_symm) - { - IndexT row = algorithm::symmMatrixRow(i, mesh1NumElems); - IndexT offset = row * (row + 1) / 2; - fromIdx = row; - toIdx = i - offset; - } - isProximate[i] = geomFilter( fromIdx, toIdx, - mesh1, mesh2, - cmode, auto_contact_check ); + forAllExec( m_coupling_scheme->getExecutionMode(), maxNumPairs, + [mesh1NumElems, mesh2NumElems, is_symm, isProximate, mesh1, mesh2, cmode, pCount, + auto_contact_check] TRIBOL_HOST_DEVICE( IndexT i ) { + IndexT fromIdx = i / mesh2NumElems; + IndexT toIdx = i % mesh2NumElems; + if ( is_symm ) { + IndexT row = algorithm::symmMatrixRow( i, mesh1NumElems ); + IndexT offset = row * ( row + 1 ) / 2; + fromIdx = row; + toIdx = i - offset; + } + isProximate[i] = geomFilter( fromIdx, toIdx, mesh1, mesh2, cmode, auto_contact_check ); #ifdef TRIBOL_USE_RAJA - RAJA::atomicAdd(pCount, static_cast(isProximate[i])); + RAJA::atomicAdd( pCount, static_cast( isProximate[i] ) ); #else if (isProximate[i]) { ++(*pCount); } #endif - }); - - ArrayT countArray_host(countArray); - SLIC_INFO("Found " << countArray_host[0] << " proximate pairs" ); + } ); + + ArrayT countArray_host( countArray ); + SLIC_INFO( "Found " << countArray_host[0] << " proximate pairs" ); // allocate proximate pairs array auto& contactPairs = m_coupling_scheme->getInterfacePairs(); - contactPairs.resize(countArray_host[0]); + contactPairs.resize( countArray_host[0] ); - countArray.fill(0); + countArray.fill( 0 ); auto pairs_view = m_coupling_scheme->getInterfacePairs().view(); // fill proximate pairs array - forAllExec(m_coupling_scheme->getExecutionMode(), maxNumPairs, - [isProximate, pCount, pairs_view, mesh1NumElems, mesh2NumElems, is_symm] - TRIBOL_HOST_DEVICE (IndexT i) - { - // Filtering removed this case - if (!isProximate[i]) - { - return; - } - - IndexT fromIdx = i / mesh2NumElems; - IndexT toIdx = i % mesh2NumElems; - if (is_symm) - { - IndexT row = algorithm::symmMatrixRow(i, mesh1NumElems); - IndexT offset = row * (row + 1) / 2; - fromIdx = row; - toIdx = i - offset; - } - - // get unique index for the array + forAllExec( + m_coupling_scheme->getExecutionMode(), maxNumPairs, + [isProximate, pCount, pairs_view, mesh1NumElems, mesh2NumElems, is_symm] TRIBOL_HOST_DEVICE( IndexT i ) { + // Filtering removed this case + if ( !isProximate[i] ) { + return; + } + + IndexT fromIdx = i / mesh2NumElems; + IndexT toIdx = i % mesh2NumElems; + if ( is_symm ) { + IndexT row = algorithm::symmMatrixRow( i, mesh1NumElems ); + IndexT offset = row * ( row + 1 ) / 2; + fromIdx = row; + toIdx = i - offset; + } + + // get unique index for the array #ifdef TRIBOL_USE_RAJA - auto idx = RAJA::atomicInc(pCount); + auto idx = RAJA::atomicInc( pCount ); #else - auto idx = *pCount; - ++(*pCount); + auto idx = *pCount; + ++( *pCount ); #endif - pairs_view[idx] = InterfacePair(fromIdx, toIdx, true); - } - ); + pairs_view[idx] = InterfacePair( fromIdx, toIdx, true ); + } ); - SLIC_INFO("Coupling scheme has " << contactPairs.size() - << " pairs out of a maximum possible of " << maxNumPairs - << " = " << mesh1NumElems << " * " << mesh2NumElems << "."); + SLIC_INFO( "Coupling scheme has " << contactPairs.size() << " pairs out of a maximum possible of " << maxNumPairs + << " = " << mesh1NumElems << " * " << mesh2NumElems << "." ); } -private: + + private: CouplingScheme* m_coupling_scheme; }; // End of CartesianProduct definition @@ -329,14 +295,13 @@ class CartesianProduct : public SearchBase * * \tparam D The spatial dimension of the coupling scheme mesh vertices. */ -template -class GridSearch : public SearchBase -{ -public: +template +class GridSearch : public SearchBase { + public: using BBox = primal::BoundingBox; using PointT = primal::Point; - using ImplicitGridType = spin::ImplicitGrid; + using ImplicitGridType = spin::ImplicitGrid; using SpacePoint = typename ImplicitGridType::SpacePoint; using SpaceVec = typename ImplicitGridType::SpaceVec; using SpatialBoundingBox = typename ImplicitGridType::SpatialBoundingBox; @@ -346,10 +311,11 @@ class GridSearch : public SearchBase * \pre couplingScheme is not null */ GridSearch( CouplingScheme* couplingScheme ) - : m_coupling_scheme( couplingScheme ) - , m_mesh1( m_coupling_scheme->getMesh1().getView() ) - , m_mesh2( m_coupling_scheme->getMesh2().getView() ) - {} + : m_coupling_scheme( couplingScheme ), + m_mesh1( m_coupling_scheme->getMesh1().getView() ), + m_mesh2( m_coupling_scheme->getMesh2().getView() ) + { + } /*! * Constructs spatial index over elements of coupling scheme's first mesh @@ -364,18 +330,16 @@ class GridSearch : public SearchBase // if either mesh is empty, don't initialize because... // 1) there won't be any pairs // 2) there is some division by the number of elements below - if (m_mesh1.numberOfElements() == 0 || m_mesh2.numberOfElements() == 0) - { + if ( m_mesh1.numberOfElements() == 0 || m_mesh2.numberOfElements() == 0 ) { return; } // Find the bounding boxes of the elements in the first mesh // Store them in an array for efficient reuse m_gridBBox.clear(); - m_meshBBoxes1.reserve(m_mesh1.numberOfElements()); - for(int i=0; i< m_mesh1.numberOfElements(); ++i) - { - m_meshBBoxes1.emplace_back(elementBoundingBox(m_mesh1, i)); + m_meshBBoxes1.reserve( m_mesh1.numberOfElements() ); + for ( int i = 0; i < m_mesh1.numberOfElements(); ++i ) { + m_meshBBoxes1.emplace_back( elementBoundingBox( m_mesh1, i ) ); } // Find an appropriate resolution for the spatial index grid @@ -388,10 +352,9 @@ class GridSearch : public SearchBase // * Grid resolution for each dimension is overall box width // divided by half the average element width SpaceVec ranges; - for(int i=0; i< m_mesh1.numberOfElements(); ++i) - { + for ( int i = 0; i < m_mesh1.numberOfElements(); ++i ) { auto& bbox = m_meshBBoxes1[i]; - inflateBBox(bbox); + inflateBBox( bbox ); ranges += bbox.range(); @@ -400,46 +363,40 @@ class GridSearch : public SearchBase } // inflate grid box slightly so elem bounding boxes are not on grid bdry - m_gridBBox.scale(1 + bboxTolerance); + m_gridBBox.scale( 1 + bboxTolerance ); ranges /= m_mesh1.numberOfElements(); // Compute grid resolution from average bbox size typename ImplicitGridType::GridCell resolution; SpaceVec bboxRange = m_gridBBox.range(); - const RealT scaleFac = 0.5; // TODO is this mesh dependent? - for(int i=0; i < D; ++i) - { - resolution[i] = static_cast( - std::ceil( scaleFac * bboxRange[i] / ranges[i] )); + const RealT scaleFac = 0.5; // TODO is this mesh dependent? + for ( int i = 0; i < D; ++i ) { + resolution[i] = static_cast( std::ceil( scaleFac * bboxRange[i] / ranges[i] ) ); } // Next, initialize the ImplicitGrid - m_grid.initialize( m_gridBBox, &resolution, m_mesh1.numberOfElements()); + m_grid.initialize( m_gridBBox, &resolution, m_mesh1.numberOfElements() ); // Finally, insert the elements - for(int i=0; i< m_mesh1.numberOfElements(); ++i) - { - m_grid.insert(m_meshBBoxes1[i], i); + for ( int i = 0; i < m_mesh1.numberOfElements(); ++i ) { + m_grid.insert( m_meshBBoxes1[i], i ); } // Output some info for debugging - if(true) - { - SLIC_INFO("Implicit Grid info: " - << "\n Mesh 1 bounding box (inflated): " << m_gridBBox - << "\n Avg range: " << ranges - << "\n Computed resolution: " << resolution ); + if ( true ) { + SLIC_INFO( "Implicit Grid info: " + << "\n Mesh 1 bounding box (inflated): " << m_gridBBox << "\n Avg range: " << ranges + << "\n Computed resolution: " << resolution ); SpatialBoundingBox bbox2; - for(int i=0; i< m_mesh2.numberOfElements(); ++i) - { - bbox2.addBox( elementBoundingBox(m_mesh2, i) ); + for ( int i = 0; i < m_mesh2.numberOfElements(); ++i ) { + bbox2.addBox( elementBoundingBox( m_mesh2, i ) ); } SLIC_INFO( "Mesh 2 bounding box is: " << bbox2 ); } - }; // end initialize() + }; // end initialize() /*! * Use the spatial index to find candidates in first mesh for each @@ -457,36 +414,29 @@ class GridSearch : public SearchBase // Find matches in first mesh (with index 'fromIdx') // with candidate elements in second mesh (with index 'toIdx') int k = 0; - for(int toIdx=0; toIdx< m_mesh2.numberOfElements(); ++toIdx) - { - SpatialBoundingBox bbox = elementBoundingBox(m_mesh2, toIdx); - inflateBBox(bbox); + for ( int toIdx = 0; toIdx < m_mesh2.numberOfElements(); ++toIdx ) { + SpatialBoundingBox bbox = elementBoundingBox( m_mesh2, toIdx ); + inflateBBox( bbox ); // Query the mesh auto candidateBits = m_grid.getCandidates( bbox ); // Add candidates - for(IndexT fromIdx = candidateBits.find_first() ; - fromIdx != BitsetType::npos ; - fromIdx = candidateBits.find_next( fromIdx) ) - { - // if meshId1 = meshId2, then check to make sure fromIdx < toIdx + for ( IndexT fromIdx = candidateBits.find_first(); fromIdx != BitsetType::npos; + fromIdx = candidateBits.find_next( fromIdx ) ) { + // if meshId1 = meshId2, then check to make sure fromIdx < toIdx // so we don't double count - if ( (mesh1.meshId() == mesh2.meshId()) && (fromIdx < toIdx) ) - { + if ( ( mesh1.meshId() == mesh2.meshId() ) && ( fromIdx < toIdx ) ) { continue; } // TODO: Add extra filter by bbox // Preliminary geometry/proximity checks, SRW - bool contact = geomFilter( fromIdx, toIdx, - mesh1, mesh2, - m_coupling_scheme->getContactMode(), + bool contact = geomFilter( fromIdx, toIdx, mesh1, mesh2, m_coupling_scheme->getContactMode(), m_coupling_scheme->getParameters().auto_contact_check ); - if (contact) - { + if ( contact ) { contactPairs.emplace_back( fromIdx, toIdx, true ); // SLIC_INFO("Interface pair " << k << " = " << toIdx << ", " << fromIdx); // Debug only ++k; @@ -494,23 +444,20 @@ class GridSearch : public SearchBase } } // end of loop over candidates in second mesh - } // end findInterfacePairs() - + } // end findInterfacePairs() -private: - BBox elementBoundingBox(const MeshData::Viewer& mesh, IndexT eId) + private: + BBox elementBoundingBox( const MeshData::Viewer& mesh, IndexT eId ) { BBox box; - for (int i{0}; i < mesh.numberOfNodesPerElement(); ++i) - { + for ( int i{ 0 }; i < mesh.numberOfNodesPerElement(); ++i ) { axom::primal::NumericArray vert_array; - auto vert_id = mesh.getGlobalNodeId(eId, i); - for (int d{0}; d < D; ++d) - { + auto vert_id = mesh.getGlobalNodeId( eId, i ); + for ( int d{ 0 }; d < D; ++d ) { vert_array[d] = mesh.getPosition()[d][vert_id]; } - box.addPoint( PointT(vert_array) ); + box.addPoint( PointT( vert_array ) ); } return box; @@ -518,13 +465,13 @@ class GridSearch : public SearchBase /*! * Expands bounding box by 33% of longest dimension's range */ - void inflateBBox(SpatialBoundingBox& bbox) + void inflateBBox( SpatialBoundingBox& bbox ) { - constexpr double sc = 1./3.; + constexpr double sc = 1. / 3.; int d = bbox.getLongestDimension(); - const RealT expansionFac = sc * bbox.range()[d]; - bbox.expand(expansionFac); + const RealT expansionFac = sc * bbox.range()[d]; + bbox.expand( expansionFac ); } CouplingScheme* m_coupling_scheme; @@ -535,8 +482,7 @@ class GridSearch : public SearchBase SpatialBoundingBox m_gridBBox; ArrayT m_meshBBoxes1; -}; // End of GridSearch class definition - +}; // End of GridSearch class definition /////////////////////////////////////////////////////////////////////////////// @@ -549,13 +495,12 @@ class GridSearch : public SearchBase * scheme's list of candidate pairs. * * The search is performed in \a findInterfacePairs() - * + * * \tparam D The spatial dimension of the coupling scheme mesh vertices. */ -template -class BvhSearch : public SearchBase -{ -public: +template +class BvhSearch : public SearchBase { + public: using BVHT = axom::spin::BVH; using BoxT = typename BVHT::BoxType; using PointT = primal::Point; @@ -564,62 +509,48 @@ class BvhSearch : public SearchBase using AtomicPolicy = typename axom::execution_space::atomic_policy; /*! - * Constructs a BvhSearch instance over CouplingScheme \a couplingScheme - * \pre couplingScheme is not null - */ - BvhSearch(CouplingScheme* coupling_scheme) - : m_coupling_scheme( coupling_scheme ) - , m_mesh1( m_coupling_scheme->getMesh1().getView() ) - , m_mesh2( m_coupling_scheme->getMesh2().getView() ) - , m_boxes1( axom::ArrayOptions::Uninitialized{}, - m_mesh1.numberOfElements(), - m_mesh1.numberOfElements(), - m_coupling_scheme->getAllocatorId() ) - , m_boxes2( axom::ArrayOptions::Uninitialized{}, - m_mesh2.numberOfElements(), - m_mesh2.numberOfElements(), - m_coupling_scheme->getAllocatorId() ) - , m_candidates( axom::ArrayOptions::Uninitialized{}, - 0, - 0, - m_coupling_scheme->getAllocatorId() ) - , m_offsets( axom::ArrayOptions::Uninitialized{}, - m_mesh2.numberOfElements(), - m_mesh2.numberOfElements(), - m_coupling_scheme->getAllocatorId() ) - , m_counts( axom::ArrayOptions::Uninitialized{}, - m_mesh2.numberOfElements(), - m_mesh2.numberOfElements(), - m_coupling_scheme->getAllocatorId() ) - {} + * Constructs a BvhSearch instance over CouplingScheme \a couplingScheme + * \pre couplingScheme is not null + */ + BvhSearch( CouplingScheme* coupling_scheme ) + : m_coupling_scheme( coupling_scheme ), + m_mesh1( m_coupling_scheme->getMesh1().getView() ), + m_mesh2( m_coupling_scheme->getMesh2().getView() ), + m_boxes1( axom::ArrayOptions::Uninitialized{}, m_mesh1.numberOfElements(), m_mesh1.numberOfElements(), + m_coupling_scheme->getAllocatorId() ), + m_boxes2( axom::ArrayOptions::Uninitialized{}, m_mesh2.numberOfElements(), m_mesh2.numberOfElements(), + m_coupling_scheme->getAllocatorId() ), + m_candidates( axom::ArrayOptions::Uninitialized{}, 0, 0, m_coupling_scheme->getAllocatorId() ), + m_offsets( axom::ArrayOptions::Uninitialized{}, m_mesh2.numberOfElements(), m_mesh2.numberOfElements(), + m_coupling_scheme->getAllocatorId() ), + m_counts( axom::ArrayOptions::Uninitialized{}, m_mesh2.numberOfElements(), m_mesh2.numberOfElements(), + m_coupling_scheme->getAllocatorId() ) + { + } /*! - * Allocate and fill bounding box arrays for each of the two meshes - */ + * Allocate and fill bounding box arrays for each of the two meshes + */ void initialize() override { - buildMeshBBoxes(m_boxes1, m_coupling_scheme->getMesh1().getView()); - buildMeshBBoxes(m_boxes2, m_coupling_scheme->getMesh2().getView()); - } // end initialize() - + buildMeshBBoxes( m_boxes1, m_coupling_scheme->getMesh1().getView() ); + buildMeshBBoxes( m_boxes2, m_coupling_scheme->getMesh2().getView() ); + } // end initialize() /*! - * Use the BVH to find candidates in first mesh for each - * element in second mesh of coupling scheme. - */ + * Use the BVH to find candidates in first mesh for each + * element in second mesh of coupling scheme. + */ void findInterfacePairs() override { // Build the BVH BVHT bvh; - bvh.setAllocatorID(m_coupling_scheme->getAllocatorId()); - bvh.initialize(m_boxes1.view(), m_boxes1.size()); + bvh.setAllocatorID( m_coupling_scheme->getAllocatorId() ); + bvh.initialize( m_boxes1.view(), m_boxes1.size() ); // Search for intersecting bounding boxes - bvh.findBoundingBoxes(m_offsets.view(), - m_counts.view(), - m_candidates, - m_mesh2.numberOfElements(), - m_boxes2.view()); + bvh.findBoundingBoxes( m_offsets.view(), m_counts.view(), m_candidates, m_mesh2.numberOfElements(), + m_boxes2.view() ); // Apply geom filter to check if intersecting bounding boxes are proximate // Change candidate value to -1 if geom filter checks are failed @@ -628,121 +559,105 @@ class BvhSearch : public SearchBase auto candidates_view = m_candidates.view(); // array of size 1 to track the number of candidates in a way compatible // with device kernels - ArrayT filtered_candidates_data(1, 1, m_coupling_scheme->getAllocatorId()); + ArrayT filtered_candidates_data( 1, 1, m_coupling_scheme->getAllocatorId() ); auto filtered_candidates = filtered_candidates_data.view(); const auto mesh1 = m_coupling_scheme->getMesh1().getView(); const auto mesh2 = m_coupling_scheme->getMesh2().getView(); auto cmode = m_coupling_scheme->getContactMode(); bool auto_contact_check = m_coupling_scheme->getParameters().auto_contact_check; // count the number of filtered proximate pairs - forAllExec(m_coupling_scheme->getExecutionMode(), m_candidates.size(), - [mesh1, mesh2, offsets_view, counts_view, candidates_view, - filtered_candidates, cmode, auto_contact_check] TRIBOL_HOST_DEVICE (IndexT i) - { - auto mesh1_elem = algorithm::binarySearch(offsets_view, counts_view, i); - auto mesh2_elem = candidates_view[i]; - if (geomFilter(mesh1_elem, mesh2_elem, mesh1, mesh2, cmode, auto_contact_check)) - { + forAllExec( m_coupling_scheme->getExecutionMode(), m_candidates.size(), + [mesh1, mesh2, offsets_view, counts_view, candidates_view, filtered_candidates, cmode, + auto_contact_check] TRIBOL_HOST_DEVICE( IndexT i ) { + auto mesh1_elem = algorithm::binarySearch( offsets_view, counts_view, i ); + auto mesh2_elem = candidates_view[i]; + if ( geomFilter( mesh1_elem, mesh2_elem, mesh1, mesh2, cmode, auto_contact_check ) ) { #ifdef TRIBOL_USE_RAJA - RAJA::atomicInc(filtered_candidates.data()); + RAJA::atomicInc( filtered_candidates.data() ); #else ++filtered_candidates[0]; #endif - } - else - { - candidates_view[i] = -1; - } - } - ); + } else { + candidates_view[i] = -1; + } + } ); ArrayT filtered_candidates_host( filtered_candidates_data ); - m_coupling_scheme->getInterfacePairs().resize(filtered_candidates_host[0]); - filtered_candidates_data.fill(0); + m_coupling_scheme->getInterfacePairs().resize( filtered_candidates_host[0] ); + filtered_candidates_data.fill( 0 ); auto pairs_view = m_coupling_scheme->getInterfacePairs().view(); // add filtered pairs to interface pairs array - forAllExec(m_coupling_scheme->getExecutionMode(), m_candidates.size(), - [candidates_view, offsets_view, counts_view, filtered_candidates, - pairs_view] TRIBOL_HOST_DEVICE (IndexT i) - { - // Filtering removed this case - if (candidates_view[i] == -1) - { - return; - } - - auto mesh1_elem = algorithm::binarySearch(offsets_view, counts_view, i); - auto mesh2_elem = candidates_view[i]; - - // get unique index for the array + forAllExec( + m_coupling_scheme->getExecutionMode(), m_candidates.size(), + [candidates_view, offsets_view, counts_view, filtered_candidates, pairs_view] TRIBOL_HOST_DEVICE( IndexT i ) { + // Filtering removed this case + if ( candidates_view[i] == -1 ) { + return; + } + + auto mesh1_elem = algorithm::binarySearch( offsets_view, counts_view, i ); + auto mesh2_elem = candidates_view[i]; + + // get unique index for the array #ifdef TRIBOL_USE_RAJA - auto idx = RAJA::atomicInc(filtered_candidates.data()); + auto idx = RAJA::atomicInc( filtered_candidates.data() ); #else - auto idx = filtered_candidates[0]; - ++filtered_candidates[0]; + auto idx = filtered_candidates[0]; + ++filtered_candidates[0]; #endif - pairs_view[idx] = InterfacePair(mesh1_elem, mesh2_elem, true); - } - ); - } // end findInterfacePairs() + pairs_view[idx] = InterfacePair( mesh1_elem, mesh2_elem, true ); + } ); + } // end findInterfacePairs() - void buildMeshBBoxes(ArrayT& boxes, const MeshData::Viewer& mesh) + void buildMeshBBoxes( ArrayT& boxes, const MeshData::Viewer& mesh ) { auto boxes1_view = boxes.view(); - forAllExec(m_coupling_scheme->getExecutionMode(), mesh.numberOfElements(), - [this, mesh, boxes1_view] TRIBOL_HOST_DEVICE (IndexT i) { - BoxT box; - auto num_nodes_per_elem = mesh.numberOfNodesPerElement(); - for(IndexT j{0}; j < num_nodes_per_elem; ++j) - { - IndexT node_id = mesh.getGlobalNodeId(i, j); - RealT pos[3]; - pos[0] = mesh.getPosition()[0][node_id]; - pos[1] = mesh.getPosition()[1][node_id]; - pos[2] = mesh.getPosition()[2][node_id]; // unused if D==2 - box.addPoint( PointT(pos) ); - } - // Expand the bounding box in the face normal direction - RealT vnorm[3]; - mesh.getFaceNormal(i, vnorm); - VectorT faceNormal(vnorm); - RealT faceRadius = mesh.getFaceRadius()[i]; - expandBBoxNormal(box, faceNormal, faceRadius); - boxes1_view[i] = std::move(box); - } - ); + forAllExec( m_coupling_scheme->getExecutionMode(), mesh.numberOfElements(), + [this, mesh, boxes1_view] TRIBOL_HOST_DEVICE( IndexT i ) { + BoxT box; + auto num_nodes_per_elem = mesh.numberOfNodesPerElement(); + for ( IndexT j{ 0 }; j < num_nodes_per_elem; ++j ) { + IndexT node_id = mesh.getGlobalNodeId( i, j ); + RealT pos[3]; + pos[0] = mesh.getPosition()[0][node_id]; + pos[1] = mesh.getPosition()[1][node_id]; + pos[2] = mesh.getPosition()[2][node_id]; // unused if D==2 + box.addPoint( PointT( pos ) ); + } + // Expand the bounding box in the face normal direction + RealT vnorm[3]; + mesh.getFaceNormal( i, vnorm ); + VectorT faceNormal( vnorm ); + RealT faceRadius = mesh.getFaceRadius()[i]; + expandBBoxNormal( box, faceNormal, faceRadius ); + boxes1_view[i] = std::move( box ); + } ); } -private: + private: /*! - * Expands bounding box by projecting the face normal by a distance - * equal to the effective face radius - */ - TRIBOL_HOST_DEVICE void expandBBoxNormal(BoxT& bbox, - const VectorT& faceNormal, - const RealT faceRadius) + * Expands bounding box by projecting the face normal by a distance + * equal to the effective face radius + */ + TRIBOL_HOST_DEVICE void expandBBoxNormal( BoxT& bbox, const VectorT& faceNormal, const RealT faceRadius ) { PointT p0 = bbox.getCentroid(); - RayT outwardRay(p0, faceNormal); - VectorT inwardNormal(faceNormal); + RayT outwardRay( p0, faceNormal ); + VectorT inwardNormal( faceNormal ); inwardNormal *= -1.0; // this operation is available on device - RayT inwardRay(p0, inwardNormal); - PointT pout = outwardRay.at(faceRadius); - PointT pin = inwardRay.at(faceRadius); - bbox.addPoint(pout); - bbox.addPoint(pin); + RayT inwardRay( p0, inwardNormal ); + PointT pout = outwardRay.at( faceRadius ); + PointT pin = inwardRay.at( faceRadius ); + bbox.addPoint( pout ); + bbox.addPoint( pin ); } - /*! - * Isotropically expands bounding box by the effective face radius. - */ - TRIBOL_HOST_DEVICE void inflateBBox(BoxT& bbox, - const RealT faceRadius) - { - bbox.expand(faceRadius); - } + /*! + * Isotropically expands bounding box by the effective face radius. + */ + TRIBOL_HOST_DEVICE void inflateBBox( BoxT& bbox, const RealT faceRadius ) { bbox.expand( faceRadius ); } CouplingScheme* m_coupling_scheme; const MeshData::Viewer m_mesh1; @@ -756,140 +671,129 @@ class BvhSearch : public SearchBase /////////////////////////////////////////////////////////////////////////////// -InterfacePairFinder::InterfacePairFinder(CouplingScheme* cs) - : m_coupling_scheme(cs) +InterfacePairFinder::InterfacePairFinder( CouplingScheme* cs ) : m_coupling_scheme( cs ) { - SLIC_ASSERT_MSG(cs != nullptr, "Coupling scheme was invalid (null pointer)"); - const int dim = m_coupling_scheme->spatialDimension(); - m_search = nullptr; - - if (isOnDevice(cs->getExecutionMode()) && cs->getBinningMethod() == BINNING_GRID) - { - SLIC_WARNING_ROOT("BINNING_GRID is not supported on GPU. Switching to BINNING_BVH."); - cs->setBinningMethod(BINNING_BVH); - } - - switch(cs->getBinningMethod() ) - { - case BINNING_CARTESIAN_PRODUCT: - switch( dim ) - { - case 2: - m_search = new CartesianProduct<2>(m_coupling_scheme); - break; - case 3: - m_search = new CartesianProduct<3>(m_coupling_scheme); - break; - default: - SLIC_ERROR_ROOT("Invalid dimension: " << dim ); - break; - } // end of BINNING_CARTESIAN_PRODUCT dimension switch + SLIC_ASSERT_MSG( cs != nullptr, "Coupling scheme was invalid (null pointer)" ); + const int dim = m_coupling_scheme->spatialDimension(); + m_search = nullptr; + + if ( isOnDevice( cs->getExecutionMode() ) && cs->getBinningMethod() == BINNING_GRID ) { + SLIC_WARNING_ROOT( "BINNING_GRID is not supported on GPU. Switching to BINNING_BVH." ); + cs->setBinningMethod( BINNING_BVH ); + } + + switch ( cs->getBinningMethod() ) { + case BINNING_CARTESIAN_PRODUCT: + switch ( dim ) { + case 2: + m_search = new CartesianProduct<2>( m_coupling_scheme ); + break; + case 3: + m_search = new CartesianProduct<3>( m_coupling_scheme ); + break; + default: + SLIC_ERROR_ROOT( "Invalid dimension: " << dim ); + break; + } // end of BINNING_CARTESIAN_PRODUCT dimension switch break; - case BINNING_GRID: + case BINNING_GRID: // The spatial grid is templated on the dimension - switch( dim ) - { - case 2: - m_search = new GridSearch<2>(m_coupling_scheme); - break; - case 3: - m_search = new GridSearch<3>(m_coupling_scheme); - break; - default: - SLIC_ERROR_ROOT("Invalid dimension: " << dim ); - break; - } // end of BINNING_GRID dimension switch + switch ( dim ) { + case 2: + m_search = new GridSearch<2>( m_coupling_scheme ); + break; + case 3: + m_search = new GridSearch<3>( m_coupling_scheme ); + break; + default: + SLIC_ERROR_ROOT( "Invalid dimension: " << dim ); + break; + } // end of BINNING_GRID dimension switch break; - case BINNING_BVH: + case BINNING_BVH: // The BVH is templated on the dimension and execution space - switch( dim ) - { - case 2: - switch(cs->getExecutionMode()) - { - case(ExecutionMode::Sequential): - m_search = new BvhSearch<2, axom::SEQ_EXEC>(m_coupling_scheme); - break; - #ifdef TRIBOL_USE_OPENMP - case(ExecutionMode::OpenMP): // This causes compiler to hang (EBC: Check if this is still true) - m_search = new BvhSearch<2, axom::OMP_EXEC>(m_coupling_scheme); - break; - #endif - #ifdef TRIBOL_USE_CUDA - case(ExecutionMode::Cuda): - m_search = new BvhSearch<2, axom::CUDA_EXEC>(m_coupling_scheme); - break; - #endif - #ifdef TRIBOL_USE_HIP - case(ExecutionMode::Hip): - m_search = new BvhSearch<2, axom::HIP_EXEC>(m_coupling_scheme); - break; - #endif + switch ( dim ) { + case 2: + switch ( cs->getExecutionMode() ) { + case ( ExecutionMode::Sequential ): + m_search = new BvhSearch<2, axom::SEQ_EXEC>( m_coupling_scheme ); + break; +#ifdef TRIBOL_USE_OPENMP + case ( ExecutionMode::OpenMP ): // This causes compiler to hang (EBC: Check if this is still true) + m_search = new BvhSearch<2, axom::OMP_EXEC>( m_coupling_scheme ); + break; +#endif +#ifdef TRIBOL_USE_CUDA + case ( ExecutionMode::Cuda ): + m_search = new BvhSearch<2, axom::CUDA_EXEC>( m_coupling_scheme ); + break; +#endif +#ifdef TRIBOL_USE_HIP + case ( ExecutionMode::Hip ): + m_search = new BvhSearch<2, axom::HIP_EXEC>( m_coupling_scheme ); + break; +#endif default: - SLIC_ERROR_ROOT("Invalid execution mode."); - break; - } - break; - case 3: - switch(cs->getExecutionMode()) - { - case(ExecutionMode::Sequential): - m_search = new BvhSearch<3, axom::SEQ_EXEC>(m_coupling_scheme); - break; - #ifdef TRIBOL_USE_OPENMP - case(ExecutionMode::OpenMP): // This causes compiler to hang (EBC: Check if this is still true) - m_search = new BvhSearch<3, axom::OMP_EXEC>(m_coupling_scheme); - break; - #endif - #ifdef TRIBOL_USE_CUDA - case(ExecutionMode::Cuda): - m_search = new BvhSearch<3, axom::CUDA_EXEC>(m_coupling_scheme); - break; - #endif - #ifdef TRIBOL_USE_HIP - case(ExecutionMode::Hip): - m_search = new BvhSearch<3, axom::HIP_EXEC>(m_coupling_scheme); - break; - #endif + SLIC_ERROR_ROOT( "Invalid execution mode." ); + break; + } + break; + case 3: + switch ( cs->getExecutionMode() ) { + case ( ExecutionMode::Sequential ): + m_search = new BvhSearch<3, axom::SEQ_EXEC>( m_coupling_scheme ); + break; +#ifdef TRIBOL_USE_OPENMP + case ( ExecutionMode::OpenMP ): // This causes compiler to hang (EBC: Check if this is still true) + m_search = new BvhSearch<3, axom::OMP_EXEC>( m_coupling_scheme ); + break; +#endif +#ifdef TRIBOL_USE_CUDA + case ( ExecutionMode::Cuda ): + m_search = new BvhSearch<3, axom::CUDA_EXEC>( m_coupling_scheme ); + break; +#endif +#ifdef TRIBOL_USE_HIP + case ( ExecutionMode::Hip ): + m_search = new BvhSearch<3, axom::HIP_EXEC>( m_coupling_scheme ); + break; +#endif default: - SLIC_ERROR_ROOT("Invalid execution mode."); - break; - } - break; - default: - SLIC_ERROR_ROOT("Invalid dimension: " << dim ); - break; - } // end of BINNING_BVH dimension switch + SLIC_ERROR_ROOT( "Invalid execution mode." ); + break; + } + break; + default: + SLIC_ERROR_ROOT( "Invalid dimension: " << dim ); + break; + } // end of BINNING_BVH dimension switch break; - default: - SLIC_ERROR_ROOT("Invalid binning method: " << cs->getBinningMethod() ); + default: + SLIC_ERROR_ROOT( "Invalid binning method: " << cs->getBinningMethod() ); break; - } // end of binning method switch + } // end of binning method switch } InterfacePairFinder::~InterfacePairFinder() { - if( m_search != nullptr) - { - delete m_search; - } + if ( m_search != nullptr ) { + delete m_search; + } } void InterfacePairFinder::initialize() { - SLIC_ASSERT(m_search != nullptr); - m_search->initialize(); + SLIC_ASSERT( m_search != nullptr ); + m_search->initialize(); } void InterfacePairFinder::findInterfacePairs() { - SLIC_INFO("Searching for interface pairs"); - m_search->findInterfacePairs(); - // set boolean on coupling scheme object indicating - // that binning has occurred - m_coupling_scheme->setBinned(true); + SLIC_INFO( "Searching for interface pairs" ); + m_search->findInterfacePairs(); + // set boolean on coupling scheme object indicating + // that binning has occurred + m_coupling_scheme->setBinned( true ); } - -} // end namespace tribol - +} // end namespace tribol diff --git a/src/tribol/search/InterfacePairFinder.hpp b/src/tribol/search/InterfacePairFinder.hpp index fda338f7..020d7b8f 100644 --- a/src/tribol/search/InterfacePairFinder.hpp +++ b/src/tribol/search/InterfacePairFinder.hpp @@ -9,14 +9,12 @@ #include "tribol/common/Parameters.hpp" #include "tribol/mesh/MeshData.hpp" -namespace tribol -{ +namespace tribol { // Forward Declarations class CouplingScheme; class SearchBase; - /// Free functions /*! @@ -30,9 +28,8 @@ class SearchBase; * \param [in] auto_contact_check Is auto-contact assumed? * */ -TRIBOL_HOST_DEVICE bool geomFilter( IndexT element_id1, IndexT element_id2, - const MeshData::Viewer& mesh1, const MeshData::Viewer& mesh2, - ContactMode mode, bool auto_contact_check ); +TRIBOL_HOST_DEVICE bool geomFilter( IndexT element_id1, IndexT element_id2, const MeshData::Viewer& mesh1, + const MeshData::Viewer& mesh2, ContactMode mode, bool auto_contact_check ); /*! * \class InterfacePairFinder @@ -40,32 +37,28 @@ TRIBOL_HOST_DEVICE bool geomFilter( IndexT element_id1, IndexT element_id2, * \brief This class finds pairs of interfering elements in the meshes * referred to by the CouplingScheme */ -class InterfacePairFinder -{ -public: - InterfacePairFinder(CouplingScheme* cs); - - ~InterfacePairFinder(); - - /*! - * Initializes structures for the candidate search - */ - void initialize(); - - /*! - * Computes the interacting interface pairs between the meshes - * specified in \a m_coupling_scheme - */ - void findInterfacePairs(); - -private: - - CouplingScheme* m_coupling_scheme; - SearchBase* m_search; // The search strategy +class InterfacePairFinder { + public: + InterfacePairFinder( CouplingScheme* cs ); + + ~InterfacePairFinder(); + + /*! + * Initializes structures for the candidate search + */ + void initialize(); + + /*! + * Computes the interacting interface pairs between the meshes + * specified in \a m_coupling_scheme + */ + void findInterfacePairs(); + + private: + CouplingScheme* m_coupling_scheme; + SearchBase* m_search; // The search strategy }; -} // end namespace tribol - - +} // end namespace tribol #endif /* TRIBOL_SEARCH_INTERFACE_PAIR_FINDER_HPP_ */ diff --git a/src/tribol/utils/Algorithm.hpp b/src/tribol/utils/Algorithm.hpp index 2d807f02..b0a16144 100644 --- a/src/tribol/utils/Algorithm.hpp +++ b/src/tribol/utils/Algorithm.hpp @@ -8,15 +8,13 @@ #include "tribol/common/ArrayTypes.hpp" -namespace tribol -{ +namespace tribol { -namespace algorithm -{ +namespace algorithm { /** * @brief Implements a generic binary search algorithm - * + * * @tparam LCOMP Function taking an IndexT input argument * @tparam HCOMP Function taking an IndexT input argument * @param size Number of elements to search through @@ -25,46 +23,37 @@ namespace algorithm * @return Integer index of matching element */ template -TRIBOL_HOST_DEVICE IndexT binarySearch( IndexT size, - LCOMP&& lo_comparison, - HCOMP&& hi_comparison ) +TRIBOL_HOST_DEVICE IndexT binarySearch( IndexT size, LCOMP&& lo_comparison, HCOMP&& hi_comparison ) { - if (size == 0) - { + if ( size == 0 ) { #ifdef TRIBOL_USE_HOST - SLIC_DEBUG("binarySearch: empty array given"); + SLIC_DEBUG( "binarySearch: empty array given" ); #endif return -1; } IndexT l = 0; IndexT r = size - 1; - while (l <= r) - { - IndexT m = (l + r) / 2; - if (lo_comparison(m)) - { + while ( l <= r ) { + IndexT m = ( l + r ) / 2; + if ( lo_comparison( m ) ) { l = m + 1; - } - else if (hi_comparison(m)) - { + } else if ( hi_comparison( m ) ) { r = m - 1; - } - else - { + } else { return m; } } #ifdef TRIBOL_USE_HOST - SLIC_DEBUG("binary_search: could not locate value in provided array."); + SLIC_DEBUG( "binary_search: could not locate value in provided array." ); #endif return -1; } /** * @brief Binary search for value within ranges of values - * + * * @tparam T Data type to compare; must have operator+ and comparators * @param array C-style array with each element giving the min of a range * @param range C-style array with each element giving the size of each range @@ -73,21 +62,16 @@ TRIBOL_HOST_DEVICE IndexT binarySearch( IndexT size, * @return Integer index of matching element range */ template -TRIBOL_HOST_DEVICE IndexT binarySearch( const T* array, - const T* range, - IndexT size, - T value ) +TRIBOL_HOST_DEVICE IndexT binarySearch( const T* array, const T* range, IndexT size, T value ) { return binarySearch( - size, - [=] TRIBOL_HOST_DEVICE (IndexT i) { return array[i] + range[i] <= value; }, - [=] TRIBOL_HOST_DEVICE (IndexT i) { return array[i] > value; } - ); + size, [=] TRIBOL_HOST_DEVICE( IndexT i ) { return array[i] + range[i] <= value; }, + [=] TRIBOL_HOST_DEVICE( IndexT i ) { return array[i] > value; } ); } /** * @brief Binary search for value within ranges of values - * + * * @tparam ARRAY Array type holding elements of type T; must have data() and * size() implemented * @tparam T Data type to compare @@ -97,62 +81,54 @@ TRIBOL_HOST_DEVICE IndexT binarySearch( const T* array, * @return Integer index of matching element range */ template -TRIBOL_HOST_DEVICE IndexT binarySearch( const ARRAY& array, - const ARRAY& range, - T value ) +TRIBOL_HOST_DEVICE IndexT binarySearch( const ARRAY& array, const ARRAY& range, T value ) { - return binarySearch(array.data(), range.data(), array.size(), value); + return binarySearch( array.data(), range.data(), array.size(), value ); } /** * @brief Given entries in the upper triangular portion of a symmetric matrix * stored row-major, find the row given an entry id - * + * * @param value Entry in the 1D vector * @param matrix_width Number of columns in the matrix * @return Row of the given entry */ -TRIBOL_HOST_DEVICE inline IndexT symmMatrixRow( IndexT value, - IndexT matrix_width ) +TRIBOL_HOST_DEVICE inline IndexT symmMatrixRow( IndexT value, IndexT matrix_width ) { return binarySearch( - matrix_width, - [=] TRIBOL_HOST_DEVICE (IndexT i) { return (i + 1) * (i + 2) / 2 <= value; }, - [=] TRIBOL_HOST_DEVICE (IndexT i) { return i * (i + 1) / 2 > value; }); + matrix_width, [=] TRIBOL_HOST_DEVICE( IndexT i ) { return ( i + 1 ) * ( i + 2 ) / 2 <= value; }, + [=] TRIBOL_HOST_DEVICE( IndexT i ) { return i * ( i + 1 ) / 2 > value; } ); } /** * @brief Transposes a matrix stored as a 2D array - * + * * @tparam MSPACE Memory space of the array * @tparam T Type of the array data * @param in Matrix to transpose * @param out Transposed matrix */ template -TRIBOL_HOST_DEVICE void transpose( const ArrayT& in, - ArrayT& out ) +TRIBOL_HOST_DEVICE void transpose( const ArrayT& in, ArrayT& out ) { auto h_in = in.shape()[0]; auto w_in = in.shape()[1]; #ifdef TRIBOL_USE_HOST - SLIC_ERROR_IF(h_in != out.shape()[1], "Input number of rows does not equal output number of columns."); - SLIC_ERROR_IF(w_in != out.shape()[0], "Input number of columns does not equal output number of rows."); + SLIC_ERROR_IF( h_in != out.shape()[1], "Input number of rows does not equal output number of columns." ); + SLIC_ERROR_IF( w_in != out.shape()[0], "Input number of columns does not equal output number of rows." ); #endif - for (IndexT i{0}; i < h_in; ++i) - { - for (IndexT j{0}; j < w_in; ++j) - { - out(j, i) = in(i, j); + for ( IndexT i{ 0 }; i < h_in; ++i ) { + for ( IndexT j{ 0 }; j < w_in; ++j ) { + out( j, i ) = in( i, j ); } } } -} // namespace algorithm - -} // namespace tribol +} // namespace algorithm +} // namespace tribol #endif /* SRC_UTILS_ALGORITHM_HPP_ */ \ No newline at end of file diff --git a/src/tribol/utils/ContactPlaneOutput.cpp b/src/tribol/utils/ContactPlaneOutput.cpp index c608b30a..bbf75f19 100644 --- a/src/tribol/utils/ContactPlaneOutput.cpp +++ b/src/tribol/utils/ContactPlaneOutput.cpp @@ -15,492 +15,426 @@ // C++ includes #include -namespace tribol -{ +namespace tribol { /*! * \brief free function to return vtk element type */ int GetVtkElementId( const InterfaceElementType type ) { - switch( type ) - { - case LINEAR_EDGE: - return 3; // vtk 2-node line - break; - case LINEAR_TRIANGLE: - return 5; // vtk 3-node triangle - break; - case LINEAR_QUAD: - return 9; // vtk 4-node quad - break; - case LINEAR_HEX: - return 12; // vtk 8-node hex - break; - default: - SLIC_ERROR("Unsupported element type in Tribol's VTK output"); - break; - } // end switch( type ) - return 0; -} // end GetVtkElementId() + switch ( type ) { + case LINEAR_EDGE: + return 3; // vtk 2-node line + break; + case LINEAR_TRIANGLE: + return 5; // vtk 3-node triangle + break; + case LINEAR_QUAD: + return 9; // vtk 4-node quad + break; + case LINEAR_HEX: + return 12; // vtk 8-node hex + break; + default: + SLIC_ERROR( "Unsupported element type in Tribol's VTK output" ); + break; + } // end switch( type ) + return 0; +} // end GetVtkElementId() //------------------------------------------------------------------------------ -void WriteContactPlaneMeshToVtk( const std::string& dir, const VisType v_type, - const IndexT cs_id, const IndexT mesh_id1, - const IndexT mesh_id2, const int dim, - const int cycle, const RealT time ) +void WriteContactPlaneMeshToVtk( const std::string& dir, const VisType v_type, const IndexT cs_id, + const IndexT mesh_id1, const IndexT mesh_id2, const int dim, const int cycle, + const RealT time ) { - CouplingScheme* couplingScheme = CouplingSchemeManager::getInstance().findData(cs_id); - SLIC_ERROR_ROOT_IF(!couplingScheme, "No coupling scheme registered with given cs_id."); - const auto mesh1 = couplingScheme->getMesh1().getView(); - const auto mesh2 = couplingScheme->getMesh2().getView(); - - int nranks = 1; - int rank = -1; - #ifdef TRIBOL_USE_MPI - MPI_Comm_rank(couplingScheme->getParameters().problem_comm, &rank); - MPI_Comm_size(couplingScheme->getParameters().problem_comm, &nranks); - #endif - - ///////////////////////////////////////// - // // - // Write contact faces and/or overlaps // - // // - ///////////////////////////////////////// - if (!couplingScheme->nullMeshes()) - { - int cpSize = couplingScheme->getNumActivePairs(); - bool overlaps { false }; - bool faces { false }; - bool meshes { false }; - - switch( v_type ) { - case VIS_FACES : - faces = true; - break; - case VIS_OVERLAPS : - overlaps = true; - break; - case VIS_MESH_AND_OVERLAPS : - overlaps = true; - meshes = true; - break; - case VIS_FACES_AND_OVERLAPS : - faces = true; - overlaps = true; - break; - case VIS_MESH_FACES_AND_OVERLAPS : - faces = true; - overlaps = true; - meshes = true; - break; - default : - // Can this be output on root? SRW - overlaps = true; // set default for now; refactoring - SLIC_INFO( "WriteInterfaceMeshToVtk: visualization type not supported." << - " Printing overlaps only." ); - break; - } // end switch( v_type ) - - if (faces && cpSize>0) + CouplingScheme* couplingScheme = CouplingSchemeManager::getInstance().findData( cs_id ); + SLIC_ERROR_ROOT_IF( !couplingScheme, "No coupling scheme registered with given cs_id." ); + const auto mesh1 = couplingScheme->getMesh1().getView(); + const auto mesh2 = couplingScheme->getMesh2().getView(); + + int nranks = 1; + int rank = -1; +#ifdef TRIBOL_USE_MPI + MPI_Comm_rank( couplingScheme->getParameters().problem_comm, &rank ); + MPI_Comm_size( couplingScheme->getParameters().problem_comm, &nranks ); +#endif + + ///////////////////////////////////////// + // // + // Write contact faces and/or overlaps // + // // + ///////////////////////////////////////// + if ( !couplingScheme->nullMeshes() ) { + int cpSize = couplingScheme->getNumActivePairs(); + bool overlaps{ false }; + bool faces{ false }; + bool meshes{ false }; + + switch ( v_type ) { + case VIS_FACES: + faces = true; + break; + case VIS_OVERLAPS: + overlaps = true; + break; + case VIS_MESH_AND_OVERLAPS: + overlaps = true; + meshes = true; + break; + case VIS_FACES_AND_OVERLAPS: + faces = true; + overlaps = true; + break; + case VIS_MESH_FACES_AND_OVERLAPS: + faces = true; + overlaps = true; + meshes = true; + break; + default: + // Can this be output on root? SRW + overlaps = true; // set default for now; refactoring + SLIC_INFO( "WriteInterfaceMeshToVtk: visualization type not supported." + << " Printing overlaps only." ); + break; + } // end switch( v_type ) + + if ( faces && cpSize > 0 ) { + // Compose file name and open file + std::string name = ( nranks > 1 ) ? axom::fmt::format( "y_cntct_faces_r{:04}_{:07}.vtk", rank, cycle ) + : axom::fmt::format( "y_cntct_faces_{:07}.vtk", cycle ); + std::string f_name = axom::utilities::filesystem::joinPath( dir, name ); + + std::ofstream faces; + faces.setf( std::ios::scientific ); + faces.open( f_name.c_str() ); + + // write face .vtk first + faces << "# vtk DataFile Version 3.0\n"; + faces << "vtk output\n"; + faces << "ASCII\n"; + faces << "DATASET UNSTRUCTURED_GRID\n"; + + // Add the cycle and time to FieldData + faces << "FIELD FieldData 3\n"; + faces << "TIME 1 1 double\n"; + faces << time << "\n"; + faces << "CYCLE 1 1 int\n"; + faces << cycle << "\n"; + faces << "COUPLING_SCHEME 1 1 int\n"; + faces << cs_id << "\n"; + + // count the number of face points for all contact planes + int numPoints = 0; + for ( int i = 0; i < cpSize; ++i ) { + numPoints += mesh1.numberOfNodesPerElement() + mesh2.numberOfNodesPerElement(); + } // end i-loop over contact planes + + // output the number of points + faces << "POINTS " << numPoints << " float\n"; + + // loop over all contact planes and output the face coordinates + for ( int i = 0; i < cpSize; ++i ) { + auto& cp = couplingScheme->getContactPlane( i ); + // if interpenOverlap, print interpenetrating portions of each face. + if ( cp.m_interpenOverlap ) { + for ( int j = 0; j < cp.m_numInterpenPoly1Vert; ++j ) { + axom::fmt::print( faces, "{} {} {}\n", cp.m_interpenG1X[j], cp.m_interpenG1Y[j], + dim == 3 ? cp.m_interpenG1Z[j] : 0. ); + } + + for ( int j = 0; j < cp.m_numInterpenPoly2Vert; ++j ) { + axom::fmt::print( faces, "{} {} {}\n", cp.m_interpenG2X[j], cp.m_interpenG2Y[j], + dim == 3 ? cp.m_interpenG2Z[j] : 0. ); + } + } // end if-cpMrg.m_interpenOverlap[i] + + else // print the current configuration faces + { + for ( int j = 0; j < mesh1.numberOfNodesPerElement(); ++j ) { + const int nodeId = mesh1.getGlobalNodeId( cp.getCpElementId1(), j ); + axom::fmt::print( faces, "{} {} {}\n", mesh1.getPosition()[0][nodeId], mesh1.getPosition()[1][nodeId], + dim == 3 ? mesh1.getPosition()[2][nodeId] : 0. ); + } + + for ( int j = 0; j < mesh2.numberOfNodesPerElement(); ++j ) { + const int nodeId = mesh2.getGlobalNodeId( cp.getCpElementId2(), j ); + axom::fmt::print( faces, "{} {} {}\n", mesh2.getPosition()[0][nodeId], mesh2.getPosition()[1][nodeId], + dim == 3 ? mesh2.getPosition()[2][nodeId] : 0. ); + } + } // end else + } // end i-loop over contact planes outputting face coordinates + + // output polygon connectivity. Number of points is the number of polygon + // vertices + the polygon vertices for each contact plane + axom::fmt::print( faces, "CELLS {} {}\n", 2 * cpSize, numPoints + ( 2 * cpSize ) ); + + using RSet = axom::slam::RangeSet; + int connIter = 0; // connectivity iterator + + // loop over contact plane instances and print current configuration + // face polygon connectivity + const int nNodes1 = mesh1.numberOfNodesPerElement(); + const int nNodes2 = mesh2.numberOfNodesPerElement(); + for ( int i = 0; i < cpSize; ++i ) { + axom::fmt::print( faces, "{} {}\n", nNodes1, axom::fmt::join( RSet( connIter, connIter + nNodes1 ), " " ) ); + connIter += nNodes1; + + axom::fmt::print( faces, "{} {}\n", nNodes2, axom::fmt::join( RSet( connIter, connIter + nNodes2 ), " " ) ); + connIter += nNodes2; + } + faces << std::endl; + + // print cell types as VTK integer IDs + { + axom::fmt::print( faces, "CELL_TYPES {}\n", 2 * cpSize ); + const int vtkid1 = dim == 3 ? 7 : 3; // 7 is VTK_POLYGON; 3 is VTK_LINE + const int vtkid2 = dim == 3 ? 7 : 3; + + for ( int i = 0; i < cpSize; ++i ) { + axom::fmt::print( faces, "{} {} ", vtkid1, vtkid2 ); + } + faces << std::endl; + } + + // print the contact face areas + axom::fmt::print( faces, "CELL_DATA {}\n", 2 * cpSize ); + axom::fmt::print( faces, "SCALARS {} {}\n", "face_area", "float" ); + axom::fmt::print( faces, "LOOKUP_TABLE default\n" ); + + for ( int i = 0; i < cpSize; ++i ) { + auto& cp = couplingScheme->getContactPlane( i ); + // print face areas + axom::fmt::print( faces, "{} {} ", mesh1.getElementAreas()[cp.getCpElementId1()], + mesh2.getElementAreas()[cp.getCpElementId2()] ); + } // end i-loop over contact planes + faces << std::endl; + faces.close(); + + } // end if-faces + + // open contact plane output file. For now we just output the overlaps + if ( overlaps && cpSize > 0 ) { + // Compose file name and open file + std::string name = ( nranks > 1 ) ? axom::fmt::format( "z_cntct_overlap_r{:04}_{:07}.vtk", rank, cycle ) + : axom::fmt::format( "z_cntct_overlap_{:07}.vtk", cycle ); + std::string f_name = axom::utilities::filesystem::joinPath( dir, name ); + + std::ofstream overlap; + overlap.setf( std::ios::scientific ); + overlap.open( f_name.c_str() ); + + // write contact plane data + overlap << "# vtk DataFile Version 3.0\n"; + overlap << "vtk output\n"; + overlap << "ASCII\n"; + overlap << "DATASET UNSTRUCTURED_GRID\n"; + + // Add the cycle and time to FieldData + overlap << "FIELD FieldData 3\n"; + overlap << "TIME 1 1 double\n"; + overlap << time << "\n"; + overlap << "CYCLE 1 1 int\n"; + overlap << cycle << "\n"; + overlap << "COUPLING_SCHEME 1 1 int\n"; + overlap << cs_id << "\n"; + + // count the total number of vertices for all contact plane instances. + int numPoints = 2 * cpSize; + if ( dim == 3 ) { + numPoints = 0; + for ( int k = 0; k < cpSize; ++k ) { + auto& cp = couplingScheme->getContactPlane( k ); + auto& cp3 = static_cast( cp ); + // add the number of overlap vertices + numPoints += cp3.m_numPolyVert; + } // end k-loop over contact planes + } + + axom::fmt::print( overlap, "POINTS {} float\n", numPoints ); + + // loop over contact plane instances and output polygon vertices + for ( int k = 0; k < cpSize; ++k ) { + auto& cp = couplingScheme->getContactPlane( k ); + // output the overlap polygon. Whether interpenetrating overlap or full + // overlap the vertex coordinates are stored in cp.m_polyX,Y,Z + if ( dim == 2 ) { + auto& cp2 = static_cast( cp ); + for ( int i = 0; i < 2; ++i ) { + axom::fmt::print( overlap, "{} {} {}\n", cp2.m_segX[i], cp2.m_segY[i], 0. ); + } // end i-loop over overlap vertices + } else { + auto& cp3 = static_cast( cp ); + for ( int i = 0; i < cp3.m_numPolyVert; ++i ) { + auto& cp3 = static_cast( cp ); + axom::fmt::print( overlap, "{} {} {}\n", cp3.m_polyX[i], cp3.m_polyY[i], cp3.m_polyZ[i] ); + } // end i-loop over overlap vertices + } + } // end i-loop over contact planes for overlap output + + // define the polygons + int numPolygons = cpSize; // one overlap per contact plane object + + axom::fmt::print( overlap, "CELLS {} {}\n", numPolygons, ( numPoints + numPolygons ) ); + + // output the overlap connectivity + using RSet = axom::slam::RangeSet; + int k = 0; + for ( int i = 0; i < cpSize; ++i ) { + int nVerts = 2; + if ( dim == 3 ) { + nVerts = static_cast( couplingScheme->getContactPlane( i ) ).m_numPolyVert; + } + axom::fmt::print( overlap, "{} {}\n", nVerts, axom::fmt::join( RSet( k, k + nVerts ), " " ) ); + k += nVerts; + } + + // print cell types as VTK int IDs { - // Compose file name and open file - std::string name = (nranks > 1) - ? axom::fmt::format("y_cntct_faces_r{:04}_{:07}.vtk", rank, cycle) - : axom::fmt::format("y_cntct_faces_{:07}.vtk", cycle); - std::string f_name = axom::utilities::filesystem::joinPath(dir, name); - - std::ofstream faces; - faces.setf(std::ios::scientific); - faces.open(f_name.c_str()); - - // write face .vtk first - faces << "# vtk DataFile Version 3.0\n"; - faces << "vtk output\n"; - faces << "ASCII\n"; - faces << "DATASET UNSTRUCTURED_GRID\n"; - - // Add the cycle and time to FieldData - faces << "FIELD FieldData 3\n"; - faces << "TIME 1 1 double\n"; - faces << time << "\n"; - faces << "CYCLE 1 1 int\n"; - faces << cycle << "\n"; - faces << "COUPLING_SCHEME 1 1 int\n"; - faces << cs_id << "\n"; - - // count the number of face points for all contact planes - int numPoints = 0; - for (int i=0; igetContactPlane(i); - // if interpenOverlap, print interpenetrating portions of each face. - if (cp.m_interpenOverlap) - { - for (int j=0; j; - int connIter = 0; // connectivity iterator - - // loop over contact plane instances and print current configuration - // face polygon connectivity - const int nNodes1 = mesh1.numberOfNodesPerElement(); - const int nNodes2 = mesh2.numberOfNodesPerElement(); - for (int i=0; igetContactPlane(i); - // print face areas - axom::fmt::print(faces, "{} {} ", - mesh1.getElementAreas()[cp.getCpElementId1()], - mesh2.getElementAreas()[cp.getCpElementId2()]); - } // end i-loop over contact planes - faces << std::endl; - faces.close(); - - } // end if-faces - - // open contact plane output file. For now we just output the overlaps - if (overlaps && cpSize>0) + axom::fmt::print( overlap, "CELL_TYPES {}\n", cpSize ); + const int vtkid = dim == 3 ? 7 : 3; // 7 is VTK_POLYGON; 3 is VTK_LINE + for ( int i = 0; i < cpSize; ++i ) { + axom::fmt::print( overlap, "{} ", vtkid ); + } + overlap << std::endl; + } + + /// Output scalar fields + axom::fmt::print( overlap, "CELL_DATA {}\n", numPolygons ); + + // print the contact plane area { - // Compose file name and open file - std::string name = (nranks > 1) - ? axom::fmt::format("z_cntct_overlap_r{:04}_{:07}.vtk", rank, cycle) - : axom::fmt::format("z_cntct_overlap_{:07}.vtk", cycle); - std::string f_name = axom::utilities::filesystem::joinPath(dir, name); - - std::ofstream overlap; - overlap.setf(std::ios::scientific); - overlap.open(f_name.c_str()); - - // write contact plane data - overlap << "# vtk DataFile Version 3.0\n"; - overlap << "vtk output\n"; - overlap << "ASCII\n"; - overlap << "DATASET UNSTRUCTURED_GRID\n"; - - // Add the cycle and time to FieldData - overlap << "FIELD FieldData 3\n"; - overlap << "TIME 1 1 double\n"; - overlap << time << "\n"; - overlap << "CYCLE 1 1 int\n"; - overlap << cycle << "\n"; - overlap << "COUPLING_SCHEME 1 1 int\n"; - overlap << cs_id << "\n"; - - // count the total number of vertices for all contact plane instances. - int numPoints = 2 * cpSize; - if (dim == 3) - { - numPoints = 0; - for ( int k=0 ; k< cpSize; ++k ) - { - auto& cp = couplingScheme->getContactPlane(k); - auto& cp3 = static_cast(cp); - // add the number of overlap vertices - numPoints += cp3.m_numPolyVert; - } // end k-loop over contact planes - } - - axom::fmt::print(overlap, "POINTS {} float\n", numPoints); - - // loop over contact plane instances and output polygon vertices - for( int k=0 ; k < cpSize; ++k ) - { - auto& cp = couplingScheme->getContactPlane(k); - // output the overlap polygon. Whether interpenetrating overlap or full - // overlap the vertex coordinates are stored in cp.m_polyX,Y,Z - if (dim == 2) - { - auto& cp2 = static_cast(cp); - for (int i=0; i<2; ++i) - { - axom::fmt::print(overlap, "{} {} {}\n", - cp2.m_segX[i], - cp2.m_segY[i], - 0.); - } // end i-loop over overlap vertices - } - else - { - auto& cp3 = static_cast(cp); - for (int i=0; i(cp); - axom::fmt::print(overlap, "{} {} {}\n", - cp3.m_polyX[i], - cp3.m_polyY[i], - cp3.m_polyZ[i]); - } // end i-loop over overlap vertices - } - } // end i-loop over contact planes for overlap output - - // define the polygons - int numPolygons = cpSize; // one overlap per contact plane object - - axom::fmt::print(overlap, "CELLS {} {}\n", numPolygons, (numPoints+numPolygons)); - - // output the overlap connectivity - using RSet = axom::slam::RangeSet; - int k = 0; - for (int i=0; i(couplingScheme->getContactPlane(i)).m_numPolyVert; - } - axom::fmt::print(overlap, "{} {}\n", nVerts, axom::fmt::join(RSet(k, k+nVerts), " ")); - k += nVerts; - } - - // print cell types as VTK int IDs - { - axom::fmt::print(overlap, "CELL_TYPES {}\n", cpSize); - const int vtkid = dim==3? 7 : 3; // 7 is VTK_POLYGON; 3 is VTK_LINE - for (int i=0; igetContactPlane(i); - axom::fmt::print(overlap, "{} ", - cp.m_interpenOverlap ? cp.m_interpenArea : cp.m_area ); - } - overlap << std::endl; - } - - // print the contact plane pressure scalar data - { - axom::fmt::print(overlap, "SCALARS {} {}\n", "overlap_pressure", "float"); - axom::fmt::print(overlap, "LOOKUP_TABLE default\n"); - for (int i=0; igetContactPlane(i); - axom::fmt::print(overlap, "{} ", cp.m_pressure ); - } - overlap << std::endl; - } - - // close file - overlap.close(); - - } // end if-overlaps - - ////////////////////////////////////////////////////////////// - // // - // Write registered contact meshes for this coupling scheme // - // // - ////////////////////////////////////////////////////////////// - if (meshes) + axom::fmt::print( overlap, "SCALARS {} {}\n", "overlap_area", "float" ); + axom::fmt::print( overlap, "LOOKUP_TABLE default\n" ); + for ( int i = 0; i < cpSize; ++i ) { + auto& cp = couplingScheme->getContactPlane( i ); + axom::fmt::print( overlap, "{} ", cp.m_interpenOverlap ? cp.m_interpenArea : cp.m_area ); + } + overlap << std::endl; + } + + // print the contact plane pressure scalar data { - std::string name = (nranks > 1) - ? axom::fmt::format("mesh_intrfc_cs{:02}_r{:04}_{:07}.vtk", cs_id, rank, cycle) - : axom::fmt::format("mesh_intrfc_cs{:02}_{:07}.vtk", cs_id, cycle); - std::string f_name = axom::utilities::filesystem::joinPath(dir,name); - - std::ofstream mesh; - mesh.setf(std::ios::scientific); - mesh.open(f_name.c_str()); - - mesh << "# vtk DataFile Version 3.0\n"; - mesh << "vtk output\n"; - mesh << "ASCII\n"; - mesh << "DATASET UNSTRUCTURED_GRID\n"; - - // Add the cycle and time to FieldData - mesh << "FIELD FieldData 3\n"; - mesh << "TIME 1 1 double\n"; - mesh << time << "\n"; - mesh << "CYCLE 1 1 int\n"; - mesh << cycle << "\n"; - mesh << "COUPLING_SCHEME 1 1 int\n"; - mesh << cs_id << "\n"; - - int numTotalNodes = mesh1.numberOfNodes() + - mesh2.numberOfNodes(); - mesh << "POINTS " << numTotalNodes << " float\n"; - - for (int i=0; igetContactPlane( i ); + axom::fmt::print( overlap, "{} ", cp.m_pressure ); + } + overlap << std::endl; + } + + // close file + overlap.close(); + + } // end if-overlaps + + ////////////////////////////////////////////////////////////// + // // + // Write registered contact meshes for this coupling scheme // + // // + ////////////////////////////////////////////////////////////// + if ( meshes ) { + std::string name = ( nranks > 1 ) + ? axom::fmt::format( "mesh_intrfc_cs{:02}_r{:04}_{:07}.vtk", cs_id, rank, cycle ) + : axom::fmt::format( "mesh_intrfc_cs{:02}_{:07}.vtk", cs_id, cycle ); + std::string f_name = axom::utilities::filesystem::joinPath( dir, name ); + + std::ofstream mesh; + mesh.setf( std::ios::scientific ); + mesh.open( f_name.c_str() ); + + mesh << "# vtk DataFile Version 3.0\n"; + mesh << "vtk output\n"; + mesh << "ASCII\n"; + mesh << "DATASET UNSTRUCTURED_GRID\n"; + + // Add the cycle and time to FieldData + mesh << "FIELD FieldData 3\n"; + mesh << "TIME 1 1 double\n"; + mesh << time << "\n"; + mesh << "CYCLE 1 1 int\n"; + mesh << cycle << "\n"; + mesh << "COUPLING_SCHEME 1 1 int\n"; + mesh << cs_id << "\n"; + + int numTotalNodes = mesh1.numberOfNodes() + mesh2.numberOfNodes(); + mesh << "POINTS " << numTotalNodes << " float\n"; + + for ( int i = 0; i < mesh1.numberOfNodes(); ++i ) { + axom::fmt::print( mesh, "{} {} {}\n", mesh1.getPosition()[0][i], mesh1.getPosition()[1][i], + dim == 3 ? mesh1.getPosition()[2][i] : 0. ); + } + + for ( int i = 0; i < mesh2.numberOfNodes(); ++i ) { + axom::fmt::print( mesh, "{} {} {}\n", mesh2.getPosition()[0][i], mesh2.getPosition()[1][i], + dim == 3 ? mesh2.getPosition()[2][i] : 0. ); + } + + // print mesh element connectivity + int numTotalElements = mesh1.numberOfElements() + mesh2.numberOfElements(); + int numSurfaceNodes = mesh1.numberOfElements() * mesh1.numberOfNodesPerElement() + + mesh2.numberOfElements() * mesh2.numberOfNodesPerElement(); + + axom::fmt::print( mesh, "CELLS {} {}\n", numTotalElements, numTotalElements + numSurfaceNodes ); + + for ( int i = 0; i < mesh1.numberOfElements(); ++i ) { + mesh << mesh1.numberOfNodesPerElement(); + for ( int a = 0; a < mesh1.numberOfNodesPerElement(); ++a ) { + mesh << " " << mesh1.getConnectivity()( i, a ); + } // end a-loop over nodes + mesh << std::endl; + } // end i-loop over cells + + const int m2_offset = mesh1.numberOfNodes(); + for ( int i = 0; i < mesh2.numberOfElements(); ++i ) { + mesh << mesh2.numberOfNodesPerElement(); + for ( int a = 0; a < mesh2.numberOfNodesPerElement(); ++a ) { + mesh << " " << m2_offset + mesh2.getConnectivity()( i, a ); + } // end a-loop over nodes + mesh << std::endl; + } // end i-loop over cells + + // specify integer id for each cell type. + // For 4-node quad, id = 9. + const int mesh1_element_id = GetVtkElementId( mesh1.getElementType() ); + const int mesh2_element_id = GetVtkElementId( mesh2.getElementType() ); + + if ( mesh1_element_id <= 0 || mesh2_element_id <= 0 ) { + SLIC_ERROR( "WriteInterfaceMeshToVtk(): " + << "element type not supported by vtk." ); + } + + mesh << "CELL_TYPES " << numTotalElements << std::endl; + for ( int i = 0; i < mesh1.numberOfElements(); ++i ) { + axom::fmt::print( mesh, "{} ", mesh1_element_id ); + } + for ( int i = 0; i < mesh2.numberOfElements(); ++i ) { + axom::fmt::print( mesh, "{} ", mesh2_element_id ); + } + mesh << std::endl; + + // Add a field to label each face with its source mesh + mesh << "CELL_DATA " << numTotalElements << std::endl; + mesh << "SCALARS mesh_id int 1" << std::endl; + mesh << "LOOKUP_TABLE default" << std::endl; + for ( int i = 0; i < mesh1.numberOfElements(); ++i ) { + axom::fmt::print( mesh, "{} ", mesh_id1 ); + } + + for ( int i = 0; i < mesh2.numberOfElements(); ++i ) { + axom::fmt::print( mesh, "{} ", mesh_id2 ); + } + mesh << std::endl; + + mesh.close(); + + } // end if (meshes) + + } // end write data for non-null meshes + + return; + +} // end WriteInterfaceMeshToVtk() //------------------------------------------------------------------------------ -} // end of namespace "tribol" +} // namespace tribol diff --git a/src/tribol/utils/ContactPlaneOutput.hpp b/src/tribol/utils/ContactPlaneOutput.hpp index 16dc86e2..a7cb9906 100644 --- a/src/tribol/utils/ContactPlaneOutput.hpp +++ b/src/tribol/utils/ContactPlaneOutput.hpp @@ -10,13 +10,12 @@ #include "tribol/common/Parameters.hpp" // AXOM includes -#include "axom/slic.hpp" +#include "axom/slic.hpp" // C++ includes #include -namespace tribol -{ +namespace tribol { // forward declarations class ContactPlaneManager; @@ -35,12 +34,10 @@ class ContactPlaneManager; * \param [in] t simulation time step * */ -void WriteContactPlaneMeshToVtk( const std::string& dir, const VisType v_type, - const IndexT cs_id, const IndexT mesh_id1, - const IndexT mesh_id2, const int dim, - const int cycle, const RealT t ); +void WriteContactPlaneMeshToVtk( const std::string& dir, const VisType v_type, const IndexT cs_id, + const IndexT mesh_id1, const IndexT mesh_id2, const int dim, const int cycle, + const RealT t ); - -} // end of namespace "tribol" +} // namespace tribol #endif /* SRC_UTILS_CONTACTPLANEOUTPUT_HPP_ */ diff --git a/src/tribol/utils/DataManager.hpp b/src/tribol/utils/DataManager.hpp index d864d90d..ed46966f 100644 --- a/src/tribol/utils/DataManager.hpp +++ b/src/tribol/utils/DataManager.hpp @@ -16,21 +16,19 @@ #include "axom/fmt.hpp" #include "axom/slic.hpp" -namespace tribol -{ +namespace tribol { template -class DataManager -{ -public: - DataManager(const DataManager& other) = delete; - DataManager(DataManager&& other) = delete; - void operator=(const DataManager& other) = delete; - void operator=(DataManager&& other) = delete; +class DataManager { + public: + DataManager( const DataManager& other ) = delete; + DataManager( DataManager&& other ) = delete; + void operator=( const DataManager& other ) = delete; + void operator=( DataManager&& other ) = delete; /** * @brief Return the instance of the DataManager singleton - * + * * @return Reference to the DataManager */ static DataManager& getInstance() @@ -41,116 +39,94 @@ class DataManager /** * @brief Returns the element at id - * + * * @note Throws std::out_of_range if the element doesn't exist - * + * * @param id Integer identifier * @return Reference to the element */ - T& at(IndexT id) - { - return data_map_.at(id); - } + T& at( IndexT id ) { return data_map_.at( id ); } /** * @brief Returns an iterator to the first element - * - * @return std::unordered_map::iterator + * + * @return std::unordered_map::iterator */ - typename std::unordered_map::iterator begin() noexcept - { - return data_map_.begin(); - } + typename std::unordered_map::iterator begin() noexcept { return data_map_.begin(); } /** * @brief Returns an iterator to the element following the last element - * - * @return std::unordered_map::iterator + * + * @return std::unordered_map::iterator */ - typename std::unordered_map::iterator end() noexcept - { - return data_map_.end(); - } + typename std::unordered_map::iterator end() noexcept { return data_map_.end(); } /** * @brief Removes the specified element from the container - * + * * @param id Integer identifier of the element to remove * @return Number of elements removed (0 or 1) */ - size_t erase(IndexT id) - { - return data_map_.erase(id); - } + size_t erase( IndexT id ) { return data_map_.erase( id ); } /** * @brief Erases all elements from the container */ - void clear() noexcept - { - data_map_.clear(); - } + void clear() noexcept { data_map_.clear(); } /** * @brief Returns the number of elements in the container - * + * * @return Number of elements in the container */ - size_t size() noexcept - { - return data_map_.size(); - } + size_t size() noexcept { return data_map_.size(); } /** * @brief Adds the key/value pair given by id and data - * + * * @param id Integer identifier for element * @param data Element to add * @return Reference to the element */ - T& addData(IndexT id, T&& data) + T& addData( IndexT id, T&& data ) { - data_map_.erase(id); - auto data_it = data_map_.emplace(id, std::move(data)); + data_map_.erase( id ); + auto data_it = data_map_.emplace( id, std::move( data ) ); return data_it.first->second; } /** - * @brief Returns a pointer to the element - * + * @brief Returns a pointer to the element + * * @param id Integer identifier for element * @return Pointer to element if found, nullptr otherwise */ - T* findData(IndexT id) + T* findData( IndexT id ) { - auto data_it = data_map_.find(id); - if (data_it == data_map_.end()) - { + auto data_it = data_map_.find( id ); + if ( data_it == data_map_.end() ) { return nullptr; - } - else - { + } else { return &data_it->second; } } /** * @brief Returns a reference to the element - * + * * @note Calls SLIC_ERROR_IF macro if element doesn't exist - * + * * @param id Integer identifier for element * @return Reference to element */ - T& getData(IndexT id) + T& getData( IndexT id ) { - auto data_it = data_map_.find(id); - SLIC_ERROR_IF(data_it == data_map_.end(), - axom::fmt::format("No data exists for id = {}.", id)); + auto data_it = data_map_.find( id ); + SLIC_ERROR_IF( data_it == data_map_.end(), axom::fmt::format( "No data exists for id = {}.", id ) ); return data_it->second; } -private: + private: DataManager() = default; ~DataManager() = default; @@ -160,6 +136,6 @@ class DataManager std::unordered_map data_map_; }; -} // end namespace tribol +} // end namespace tribol #endif /* SRC_UTILS_DATAMANAGER_HPP_ */ diff --git a/src/tribol/utils/Math.cpp b/src/tribol/utils/Math.cpp index f1bf83e4..435f2a38 100644 --- a/src/tribol/utils/Math.cpp +++ b/src/tribol/utils/Math.cpp @@ -6,258 +6,223 @@ #include "tribol/utils/Math.hpp" // AXOM includes -#include "axom/slic.hpp" +#include "axom/slic.hpp" // C++ includes -#include +#include -namespace tribol -{ +namespace tribol { -TRIBOL_HOST_DEVICE RealT magnitude( RealT const vx, - RealT const vy, - RealT const vz ) +TRIBOL_HOST_DEVICE RealT magnitude( RealT const vx, RealT const vy, RealT const vz ) { - return sqrt(vx * vx + vy * vy + vz * vz); + return sqrt( vx * vx + vy * vy + vz * vz ); } //------------------------------------------------------------------------------ -RealT magnitude( RealT const * const v, int const dim ) +RealT magnitude( RealT const* const v, int const dim ) { - RealT mag = 0.; - for (int i=0; i val) - { - R = m - 1; - } - else - { - return m; - } - } - - SLIC_DEBUG("binary_search: could not locate value in provided array."); - return -1; +int binary_search( const int* const array, const int n, const int val ) +{ + if ( n == 0 ) { + SLIC_DEBUG( "binary_search: n = 0 return infeasible index." ); + return -1; + } else if ( n == 1 && val == array[0] ) { + return 0; + } else if ( n == 1 && val != array[0] ) { + SLIC_DEBUG( "binary_search: val is not equal to array[0] for n = 1." ); + return -1; + } + + int L = 0; + int R = n - 1; + while ( L <= R ) { + int m = ( L + R ) / 2; + if ( array[m] < val ) { + L = m + 1; + } else if ( array[m] > val ) { + R = m - 1; + } else { + return m; + } + } + + SLIC_DEBUG( "binary_search: could not locate value in provided array." ); + return -1; } //------------------------------------------------------------------------------ -void swap_val( int *xp, int *yp ) +void swap_val( int* xp, int* yp ) { - int temp = *xp; - *xp = *yp; - *yp = temp; + int temp = *xp; + *xp = *yp; + *yp = temp; } //------------------------------------------------------------------------------ -void bubble_sort( int * const array, - const int n ) +void bubble_sort( int* const array, const int n ) { - int i, j; - for (i = 0; i < n-1; ++i) - { - for (j = 0; j < n-i-1; ++j) - { - if (array[j] > array[j+1]) - { - swap_val( &array[j], &array[j+1] ); - } + int i, j; + for ( i = 0; i < n - 1; ++i ) { + for ( j = 0; j < n - i - 1; ++j ) { + if ( array[j] > array[j + 1] ) { + swap_val( &array[j], &array[j + 1] ); } - } + } + } } //------------------------------------------------------------------------------ void allocRealArray( RealT** arr, int length, RealT init_val ) { - SLIC_ERROR_IF( length==0, "allocRealArray: please specify nonzero length " << - "for array allocation." ); + SLIC_ERROR_IF( length == 0, "allocRealArray: please specify nonzero length " + << "for array allocation." ); - *arr = new RealT [length]; - initRealArray( *arr, length, init_val ); + *arr = new RealT[length]; + initRealArray( *arr, length, init_val ); } //------------------------------------------------------------------------------ void allocRealArray( RealT** arr, const int length, const RealT* const data ) { - SLIC_ERROR_IF( length==0, "allocRealArray: please specify nonzero length " << - "for array allocation." ); + SLIC_ERROR_IF( length == 0, "allocRealArray: please specify nonzero length " + << "for array allocation." ); - if (data == nullptr) - { - SLIC_ERROR( "allocRealArray: input data pointer not set." ); - } + if ( data == nullptr ) { + SLIC_ERROR( "allocRealArray: input data pointer not set." ); + } - *arr = new RealT[ length ]; + *arr = new RealT[length]; - for (int i=0; i // std::abs, std::cos, std::sin +#include // std::abs, std::cos, std::sin // AXOM includes #include "axom/core.hpp" #include "axom/slic.hpp" - -namespace tribol -{ +namespace tribol { //////////////////////////////// // // @@ -26,2083 +24,1773 @@ namespace tribol // // //////////////////////////////// TestMesh::TestMesh() - : mfem_mesh (nullptr) - , mortarMeshId ( 0 ) - , nonmortarMeshId ( 0 ) - , numTotalNodes ( 0 ) - , numMortarNodes ( 0 ) - , numNonmortarNodes ( 0 ) - , numTotalElements ( 0 ) - , numMortarElements ( 0 ) - , numNonmortarElements ( 0 ) - , numTotalFaces ( 0 ) - , numMortarFaces ( 0 ) - , numNonmortarFaces ( 0 ) - , numNodesPerFace ( 0 ) - , numNodesPerElement ( 0 ) - , dim ( 3 ) // no 2D support - , dirNodesX1 ( nullptr ) - , dirNodesY1 ( nullptr ) - , dirNodesZ1 ( nullptr ) - , iDirValX1 ( nullptr ) - , iDirValY1 ( nullptr ) - , iDirValZ1 ( nullptr ) - , dirNodesX2 ( nullptr ) - , dirNodesY2 ( nullptr ) - , dirNodesZ2 ( nullptr ) - , iDirValX2 ( nullptr ) - , iDirValY2 ( nullptr ) - , iDirValZ2 ( nullptr ) - , presDofs1 ( nullptr ) - , presDofs2 ( nullptr ) - , faceConn1 ( nullptr ) - , faceConn2 ( nullptr ) - , elConn1 ( nullptr ) - , elConn2 ( nullptr ) - , fx1 ( nullptr ) - , fy1 ( nullptr ) - , fz1 ( nullptr ) - , fx2 ( nullptr ) - , fy2 ( nullptr ) - , fz2 ( nullptr ) - , vx1 ( nullptr ) - , vy1 ( nullptr ) - , vz1 ( nullptr ) - , vx2 ( nullptr ) - , vy2 ( nullptr ) - , vz2 ( nullptr ) - , x ( nullptr ) - , y ( nullptr ) - , z ( nullptr ) - , I ( nullptr ) - , J ( nullptr ) - , vals ( nullptr ) - , gaps ( nullptr ) - , pressures ( nullptr ) - , mortar_bulk_mod ( nullptr ) - , mortar_element_thickness ( nullptr ) - , nonmortar_bulk_mod ( nullptr ) - , nonmortar_element_thickness ( nullptr ) - , registered_velocities1 (false) - , registered_velocities2 (false) -{ } - -//------------------------------------------------------------------------------ -TestMesh::~TestMesh() + : mfem_mesh( nullptr ), + mortarMeshId( 0 ), + nonmortarMeshId( 0 ), + numTotalNodes( 0 ), + numMortarNodes( 0 ), + numNonmortarNodes( 0 ), + numTotalElements( 0 ), + numMortarElements( 0 ), + numNonmortarElements( 0 ), + numTotalFaces( 0 ), + numMortarFaces( 0 ), + numNonmortarFaces( 0 ), + numNodesPerFace( 0 ), + numNodesPerElement( 0 ), + dim( 3 ) // no 2D support + , + dirNodesX1( nullptr ), + dirNodesY1( nullptr ), + dirNodesZ1( nullptr ), + iDirValX1( nullptr ), + iDirValY1( nullptr ), + iDirValZ1( nullptr ), + dirNodesX2( nullptr ), + dirNodesY2( nullptr ), + dirNodesZ2( nullptr ), + iDirValX2( nullptr ), + iDirValY2( nullptr ), + iDirValZ2( nullptr ), + presDofs1( nullptr ), + presDofs2( nullptr ), + faceConn1( nullptr ), + faceConn2( nullptr ), + elConn1( nullptr ), + elConn2( nullptr ), + fx1( nullptr ), + fy1( nullptr ), + fz1( nullptr ), + fx2( nullptr ), + fy2( nullptr ), + fz2( nullptr ), + vx1( nullptr ), + vy1( nullptr ), + vz1( nullptr ), + vx2( nullptr ), + vy2( nullptr ), + vz2( nullptr ), + x( nullptr ), + y( nullptr ), + z( nullptr ), + I( nullptr ), + J( nullptr ), + vals( nullptr ), + gaps( nullptr ), + pressures( nullptr ), + mortar_bulk_mod( nullptr ), + mortar_element_thickness( nullptr ), + nonmortar_bulk_mod( nullptr ), + nonmortar_element_thickness( nullptr ), + registered_velocities1( false ), + registered_velocities2( false ) { - this->clear(); } +//------------------------------------------------------------------------------ +TestMesh::~TestMesh() { this->clear(); } + //------------------------------------------------------------------------------ void TestMesh::clear( bool keepCoords ) { - if (this->mfem_mesh != nullptr) - { - delete this->mfem_mesh; - this->mfem_mesh = nullptr; - } - - // coordinates - if (!keepCoords) - { - if (this->x != nullptr) - { - delete [] this->x; - this->x = nullptr; - } - if (this->y != nullptr) - { - delete [] this->y; - this->y = nullptr; - } - if (this->z != nullptr) - { - delete [] this->z; - this->z = nullptr; - } - } - // boundary condition nodes side 1 - if (this->dirNodesX1 != nullptr) - { - delete [] this->dirNodesX1; - this->dirNodesX1 = nullptr; - } - if (this->dirNodesY1 != nullptr) - { - delete [] this->dirNodesY1; - this->dirNodesY1 = nullptr; - } - if (this->dirNodesZ1 != nullptr) - { - delete [] this->dirNodesZ1; - this->dirNodesZ1 = nullptr; - } - // boundary condition values side 1 - if (this->iDirValX1 != nullptr) - { - delete [] this->iDirValX1; - this->iDirValX1 = nullptr; - } - if (this->iDirValY1 != nullptr) - { - delete [] this->iDirValY1; - this->iDirValY1 = nullptr; - } - if (this->iDirValZ1 != nullptr) - { - delete [] this->iDirValZ1; - this->iDirValZ1 = nullptr; - } - // boundary condition nodes side 2 - if (this->dirNodesX2 != nullptr) - { - delete [] this->dirNodesX2; - this->dirNodesX2 = nullptr; - } - if (this->dirNodesY2 != nullptr) - { - delete [] this->dirNodesY2; - this->dirNodesY2 = nullptr; - } - if (this->dirNodesZ2 != nullptr) - { - delete [] this->dirNodesZ2; - this->dirNodesZ2 = nullptr; - } - // boundary condition values side 2 - if (this->iDirValX2 != nullptr) - { - delete [] this->iDirValX2; - this->iDirValX2 = nullptr; - } - if (this->iDirValY2 != nullptr) - { - delete [] this->iDirValY2; - this->iDirValY2 = nullptr; - } - if (this->iDirValZ2 != nullptr) - { - delete [] this->iDirValZ2; - this->iDirValZ2 = nullptr; - } - // face element connectivity - if (this->faceConn1 != nullptr) - { - delete [] this->faceConn1; - this->faceConn1 = nullptr; - } - if (this->faceConn2 != nullptr) - { - delete [] this->faceConn2; - this->faceConn2 = nullptr; - } - // volume element connectivity - if (this->elConn1 != nullptr) - { - delete [] this->elConn1; - this->elConn1 = nullptr; - } - if (this->elConn2 != nullptr) - { - delete [] this->elConn2; - this->elConn2 = nullptr; - } - // pressure dofs - if (this->presDofs1 != nullptr) - { - delete [] this->presDofs1; - this->presDofs1 = nullptr; - } - if (this->presDofs2 != nullptr) - { - delete [] this->presDofs2; - this->presDofs2 = nullptr; - } - // nodal forces side 1 - if (this->fx1 != nullptr) - { - delete [] this->fx1; - this->fx1 = nullptr; - } - if (this->fy1 != nullptr) - { - delete [] this->fy1; - this->fy1 = nullptr; - } - if (this->fz1 != nullptr) - { - delete [] this->fz1; - this->fz1 = nullptr; - } - // nodal forces side 2 - if (this->fx2 != nullptr) - { - delete [] this->fx2; - this->fx2 = nullptr; - } - if (this->fy2 != nullptr) - { - delete [] this->fy2; - this->fy2 = nullptr; - } - if (this->fz2 != nullptr) - { - delete [] this->fz2; - this->fz2 = nullptr; - } - // nodal velocities side 1 - if (this->vx1 != nullptr) - { - delete [] this->vx1; - this->vx1 = nullptr; - } - if (this->vy1 != nullptr) - { - delete [] this->vy1; - this->vy1 = nullptr; - } - if (this->vz1 != nullptr) - { - delete [] this->vz1; - this->vz1 = nullptr; - } - // nodal velocities side 2 - if (this->vx2 != nullptr) - { - delete [] this->vx2; - this->vx2 = nullptr; - } - if (this->vy2 != nullptr) - { - delete [] this->vy2; - this->vy2 = nullptr; - } - if (this->vz2 != nullptr) - { - delete [] this->vz2; - this->vz2 = nullptr; - } - // nodal gaps and pressures - if (this->gaps != nullptr) - { - delete [] this->gaps; - this->gaps = nullptr; - } - if (this->pressures != nullptr) - { - delete [] this->pressures; - this->pressures = nullptr; - } - // sparse matrix data - if (this->I != nullptr) - { - delete [] this->I; - this->I = nullptr; - } - if (this->J != nullptr) - { - delete [] this->J; - this->J = nullptr; - } - if (this->vals != nullptr) - { - delete [] this->vals; - this->vals = nullptr; - } - - // penalty data - if (this->mortar_bulk_mod != nullptr) - { - delete [] this->mortar_bulk_mod; - this->mortar_bulk_mod = nullptr; - } - if (this->nonmortar_bulk_mod != nullptr) - { - delete [] this->nonmortar_bulk_mod; - this->nonmortar_bulk_mod = nullptr; - } - if (this->mortar_element_thickness != nullptr) - { - delete [] this->mortar_element_thickness; - this->mortar_element_thickness = nullptr; - } - if (this->nonmortar_element_thickness != nullptr) - { - delete [] this->nonmortar_element_thickness; - this->nonmortar_element_thickness = nullptr; - } - -} // end TestMesh::clear() + if ( this->mfem_mesh != nullptr ) { + delete this->mfem_mesh; + this->mfem_mesh = nullptr; + } + + // coordinates + if ( !keepCoords ) { + if ( this->x != nullptr ) { + delete[] this->x; + this->x = nullptr; + } + if ( this->y != nullptr ) { + delete[] this->y; + this->y = nullptr; + } + if ( this->z != nullptr ) { + delete[] this->z; + this->z = nullptr; + } + } + // boundary condition nodes side 1 + if ( this->dirNodesX1 != nullptr ) { + delete[] this->dirNodesX1; + this->dirNodesX1 = nullptr; + } + if ( this->dirNodesY1 != nullptr ) { + delete[] this->dirNodesY1; + this->dirNodesY1 = nullptr; + } + if ( this->dirNodesZ1 != nullptr ) { + delete[] this->dirNodesZ1; + this->dirNodesZ1 = nullptr; + } + // boundary condition values side 1 + if ( this->iDirValX1 != nullptr ) { + delete[] this->iDirValX1; + this->iDirValX1 = nullptr; + } + if ( this->iDirValY1 != nullptr ) { + delete[] this->iDirValY1; + this->iDirValY1 = nullptr; + } + if ( this->iDirValZ1 != nullptr ) { + delete[] this->iDirValZ1; + this->iDirValZ1 = nullptr; + } + // boundary condition nodes side 2 + if ( this->dirNodesX2 != nullptr ) { + delete[] this->dirNodesX2; + this->dirNodesX2 = nullptr; + } + if ( this->dirNodesY2 != nullptr ) { + delete[] this->dirNodesY2; + this->dirNodesY2 = nullptr; + } + if ( this->dirNodesZ2 != nullptr ) { + delete[] this->dirNodesZ2; + this->dirNodesZ2 = nullptr; + } + // boundary condition values side 2 + if ( this->iDirValX2 != nullptr ) { + delete[] this->iDirValX2; + this->iDirValX2 = nullptr; + } + if ( this->iDirValY2 != nullptr ) { + delete[] this->iDirValY2; + this->iDirValY2 = nullptr; + } + if ( this->iDirValZ2 != nullptr ) { + delete[] this->iDirValZ2; + this->iDirValZ2 = nullptr; + } + // face element connectivity + if ( this->faceConn1 != nullptr ) { + delete[] this->faceConn1; + this->faceConn1 = nullptr; + } + if ( this->faceConn2 != nullptr ) { + delete[] this->faceConn2; + this->faceConn2 = nullptr; + } + // volume element connectivity + if ( this->elConn1 != nullptr ) { + delete[] this->elConn1; + this->elConn1 = nullptr; + } + if ( this->elConn2 != nullptr ) { + delete[] this->elConn2; + this->elConn2 = nullptr; + } + // pressure dofs + if ( this->presDofs1 != nullptr ) { + delete[] this->presDofs1; + this->presDofs1 = nullptr; + } + if ( this->presDofs2 != nullptr ) { + delete[] this->presDofs2; + this->presDofs2 = nullptr; + } + // nodal forces side 1 + if ( this->fx1 != nullptr ) { + delete[] this->fx1; + this->fx1 = nullptr; + } + if ( this->fy1 != nullptr ) { + delete[] this->fy1; + this->fy1 = nullptr; + } + if ( this->fz1 != nullptr ) { + delete[] this->fz1; + this->fz1 = nullptr; + } + // nodal forces side 2 + if ( this->fx2 != nullptr ) { + delete[] this->fx2; + this->fx2 = nullptr; + } + if ( this->fy2 != nullptr ) { + delete[] this->fy2; + this->fy2 = nullptr; + } + if ( this->fz2 != nullptr ) { + delete[] this->fz2; + this->fz2 = nullptr; + } + // nodal velocities side 1 + if ( this->vx1 != nullptr ) { + delete[] this->vx1; + this->vx1 = nullptr; + } + if ( this->vy1 != nullptr ) { + delete[] this->vy1; + this->vy1 = nullptr; + } + if ( this->vz1 != nullptr ) { + delete[] this->vz1; + this->vz1 = nullptr; + } + // nodal velocities side 2 + if ( this->vx2 != nullptr ) { + delete[] this->vx2; + this->vx2 = nullptr; + } + if ( this->vy2 != nullptr ) { + delete[] this->vy2; + this->vy2 = nullptr; + } + if ( this->vz2 != nullptr ) { + delete[] this->vz2; + this->vz2 = nullptr; + } + // nodal gaps and pressures + if ( this->gaps != nullptr ) { + delete[] this->gaps; + this->gaps = nullptr; + } + if ( this->pressures != nullptr ) { + delete[] this->pressures; + this->pressures = nullptr; + } + // sparse matrix data + if ( this->I != nullptr ) { + delete[] this->I; + this->I = nullptr; + } + if ( this->J != nullptr ) { + delete[] this->J; + this->J = nullptr; + } + if ( this->vals != nullptr ) { + delete[] this->vals; + this->vals = nullptr; + } + + // penalty data + if ( this->mortar_bulk_mod != nullptr ) { + delete[] this->mortar_bulk_mod; + this->mortar_bulk_mod = nullptr; + } + if ( this->nonmortar_bulk_mod != nullptr ) { + delete[] this->nonmortar_bulk_mod; + this->nonmortar_bulk_mod = nullptr; + } + if ( this->mortar_element_thickness != nullptr ) { + delete[] this->mortar_element_thickness; + this->mortar_element_thickness = nullptr; + } + if ( this->nonmortar_element_thickness != nullptr ) { + delete[] this->nonmortar_element_thickness; + this->nonmortar_element_thickness = nullptr; + } + +} // end TestMesh::clear() //------------------------------------------------------------------------------ -void TestMesh::setupContactMeshHex( int numElemsX1, int numElemsY1, int numElemsZ1, - RealT xMin1, RealT yMin1, RealT zMin1, - RealT xMax1, RealT yMax1, RealT zMax1, - int numElemsX2, int numElemsY2, int numElemsZ2, - RealT xMin2, RealT yMin2, RealT zMin2, - RealT xMax2, RealT yMax2, RealT zMax2, - RealT thetaMortar, RealT thetaNonmortar ) +void TestMesh::setupContactMeshHex( int numElemsX1, int numElemsY1, int numElemsZ1, RealT xMin1, RealT yMin1, + RealT zMin1, RealT xMax1, RealT yMax1, RealT zMax1, int numElemsX2, int numElemsY2, + int numElemsZ2, RealT xMin2, RealT yMin2, RealT zMin2, RealT xMax2, RealT yMax2, + RealT zMax2, RealT thetaMortar, RealT thetaNonmortar ) { - // NOTE: ONLY CONTACT INTERACTIONS IN THE Z-DIRECTION ARE SUPPORTED - // AT THE MOMENT. - - // allocate mesh data arrays - this->cellType = (int)(tribol::LINEAR_QUAD); - this->numNodesPerElement = 8; // hard coded for hex8 elements - this->numNodesPerFace = 4; // hard code for quad4 faces - int numElementsBlock1, numNodesBlock1; - int numElementsBlock2, numNodesBlock2; - - this->mortarMeshId = 0; - this->nonmortarMeshId = 1; - - // ASSUMING contact is in the Z-Direction, check to make sure that the - // gap of interpenetration is not greater than either block's element - // dimension - RealT h1 = (zMax1 - zMin1) / numElemsZ1; - RealT h2 = (zMax2 - zMin2) / numElemsZ2; - RealT mesh_gap = zMin2 - zMax1; - - SLIC_ERROR_IF( mesh_gap < -h1, "TestMesh::setupContactMeshHex(): " << - "Initial mesh configuration has a gap greater than the " << - "element thickness in block 1."); - - SLIC_ERROR_IF( mesh_gap < -h2, "TestMesh::setupContactMeshHex(): " << - "Initial mesh configuration has a gap greater than the " << - "element thickness in block 2."); - - numElementsBlock1 = numElemsX1 * numElemsY1 * numElemsZ1; - numNodesBlock1 = (numElemsX1+1) * (numElemsY1+1) * (numElemsZ1+1); - this->numMortarFaces = numElemsX1 * numElemsY1; - numElementsBlock2 = numElemsX2 * numElemsY2 * numElemsZ2; - numNodesBlock2 = (numElemsX2+1) * (numElemsY2+1) * (numElemsZ2+1); - this->numNonmortarFaces = numElemsX2 * numElemsY2; - - this->numMortarNodes = numNodesBlock1; - this->numNonmortarNodes = numNodesBlock2; - this->numNonmortarSurfaceNodes = (numElemsX2+1) * (numElemsY2+1); - this->numTotalNodes = numNodesBlock1 + numNodesBlock2; - this->numMortarElements = numElementsBlock1; - this->numNonmortarElements = numElementsBlock2; - this->numTotalElements = numElementsBlock1 + numElementsBlock2; - this->numTotalFaces = this->numNonmortarFaces + this->numMortarFaces; - - this->elConn1 = new int[ this->numNodesPerElement * this->numMortarElements ]; - this->elConn2 = new int[ this->numNodesPerElement * this->numNonmortarElements ]; - this->faceConn1 = new int[ this->numNodesPerFace * this->numMortarFaces ]; - this->faceConn2 = new int[ this->numNodesPerFace * this->numNonmortarFaces ]; - this->x = new RealT[ this->numTotalNodes ]; - this->y = new RealT[ this->numTotalNodes ]; - this->z = new RealT[ this->numTotalNodes ]; - - // setup mesh nodal coordinate arrays - int ndOffset; - int numElemsX, numElemsY, numElemsZ; - RealT xMax, yMax, zMax, xMin, yMin, zMin; - RealT theta; - int * elConn, * faceConn; - for ( int iblk = 0; iblk<2; ++iblk) - { - if ( iblk == 0) - { - ndOffset = 0; - numElemsX = numElemsX1; - numElemsY = numElemsY1; - numElemsZ = numElemsZ1; - xMin = xMin1; - yMin = yMin1; - zMin = zMin1; - xMax = xMax1; - yMax = yMax1; - zMax = zMax1; - elConn = this->elConn1; - faceConn = this->faceConn1; - theta = thetaMortar; - } - else - { - ndOffset = numNodesBlock1; - numElemsX = numElemsX2; - numElemsY = numElemsY2; - numElemsZ = numElemsZ2; - xMin = xMin2; - yMin = yMin2; - zMin = zMin2; - xMax = xMax2; - yMax = yMax2; - zMax = zMax2; - elConn = this->elConn2; - faceConn = this->faceConn2; - theta = thetaNonmortar; - } - - int numNodesX = numElemsX + 1; - int numNodesY = numElemsY + 1; - int numNodesZ = numElemsZ + 1; - RealT hx = (xMax - xMin) / numElemsX; - RealT hy = (yMax - yMin) / numElemsY; - RealT hz = (zMax - zMin) / numElemsZ; - - // compute mesh coordinates - int ctr = 0; - for (int k=0; k0 && j<(numNodesY-1)) ? true : false; - bool rotationX = (i>0 && i<(numNodesX-1)) ? true : false; - - if (rotationX && rotationY) - { - RealT rot0 = std::cos(theta * M_PI/180); - RealT rot1 = -std::sin(theta * M_PI/180); - RealT rot2 = -rot1; - RealT rot3 = rot0; - - RealT x_temp = xyz[0]*rot0 + xyz[1]*rot1; - RealT y_temp = xyz[0]*rot2 + xyz[1]*rot3; - RealT z_temp = xyz[2]; - - xyz[0] = x_temp; - xyz[1] = y_temp; - xyz[2] = z_temp; - } - - this->x[ idx ] = xyz[0]; - this->y[ idx ] = xyz[1]; - this->z[ idx ] = xyz[2]; - ++ctr; - } // end loop over x nodes - } // end loop over y nodes - } // end loop over z nodes - - // populate element connectivity arrays - ctr = 0; - for (int k=0; knumNodesPerElement * ctr ] = icr; - elConn[ this->numNodesPerElement * ctr + 1] = icr + 1; - elConn[ this->numNodesPerElement * ctr + 2] = icr + numNodesX + 1; - elConn[ this->numNodesPerElement * ctr + 3] = icr + numNodesX; - - int lclZOffset = icr + numNodesX * numNodesY; - elConn[ this->numNodesPerElement * ctr + 4] = lclZOffset; - elConn[ this->numNodesPerElement * ctr + 5] = lclZOffset + 1; - elConn[ this->numNodesPerElement * ctr + 6] = lclZOffset + numNodesX + 1; - elConn[ this->numNodesPerElement * ctr + 7] = lclZOffset + numNodesX; - ++ctr; - - } // end loop over x elements - } // end loop over y elements - } // end loop over z elements - - // populate contact surface connectivity - // Note: element connectivity is not necessarily consistent with outward unit normal at - // contact faces - ctr = 0; - for (int j=0; jnumNodesPerFace * ctr ] = icr; - faceConn[ this->numNodesPerFace * ctr + 1 ] = icr + 1; - faceConn[ this->numNodesPerFace * ctr + 2 ] = icr + numNodesX + 1; - faceConn[ this->numNodesPerFace * ctr + 3 ] = icr + numNodesX; - ++ctr; - } - if (ndOffset != 0) // reorient nonmortar face connectivity per outward unit normal requirement - { - int yIdOffset = j * numNodesX; - int icr = ndOffset + yIdOffset + i; // bottom surface - faceConn[ this->numNodesPerFace * ctr ] = icr; - faceConn[ this->numNodesPerFace * ctr + 1 ] = icr + numNodesX; - faceConn[ this->numNodesPerFace * ctr + 2 ] = icr + numNodesX + 1; - faceConn[ this->numNodesPerFace * ctr + 3 ] = icr + 1; - ++ctr; - } - } + // NOTE: ONLY CONTACT INTERACTIONS IN THE Z-DIRECTION ARE SUPPORTED + // AT THE MOMENT. + + // allocate mesh data arrays + this->cellType = (int)( tribol::LINEAR_QUAD ); + this->numNodesPerElement = 8; // hard coded for hex8 elements + this->numNodesPerFace = 4; // hard code for quad4 faces + int numElementsBlock1, numNodesBlock1; + int numElementsBlock2, numNodesBlock2; + + this->mortarMeshId = 0; + this->nonmortarMeshId = 1; + + // ASSUMING contact is in the Z-Direction, check to make sure that the + // gap of interpenetration is not greater than either block's element + // dimension + RealT h1 = ( zMax1 - zMin1 ) / numElemsZ1; + RealT h2 = ( zMax2 - zMin2 ) / numElemsZ2; + RealT mesh_gap = zMin2 - zMax1; + + SLIC_ERROR_IF( mesh_gap < -h1, "TestMesh::setupContactMeshHex(): " + << "Initial mesh configuration has a gap greater than the " + << "element thickness in block 1." ); + + SLIC_ERROR_IF( mesh_gap < -h2, "TestMesh::setupContactMeshHex(): " + << "Initial mesh configuration has a gap greater than the " + << "element thickness in block 2." ); + + numElementsBlock1 = numElemsX1 * numElemsY1 * numElemsZ1; + numNodesBlock1 = ( numElemsX1 + 1 ) * ( numElemsY1 + 1 ) * ( numElemsZ1 + 1 ); + this->numMortarFaces = numElemsX1 * numElemsY1; + numElementsBlock2 = numElemsX2 * numElemsY2 * numElemsZ2; + numNodesBlock2 = ( numElemsX2 + 1 ) * ( numElemsY2 + 1 ) * ( numElemsZ2 + 1 ); + this->numNonmortarFaces = numElemsX2 * numElemsY2; + + this->numMortarNodes = numNodesBlock1; + this->numNonmortarNodes = numNodesBlock2; + this->numNonmortarSurfaceNodes = ( numElemsX2 + 1 ) * ( numElemsY2 + 1 ); + this->numTotalNodes = numNodesBlock1 + numNodesBlock2; + this->numMortarElements = numElementsBlock1; + this->numNonmortarElements = numElementsBlock2; + this->numTotalElements = numElementsBlock1 + numElementsBlock2; + this->numTotalFaces = this->numNonmortarFaces + this->numMortarFaces; + + this->elConn1 = new int[this->numNodesPerElement * this->numMortarElements]; + this->elConn2 = new int[this->numNodesPerElement * this->numNonmortarElements]; + this->faceConn1 = new int[this->numNodesPerFace * this->numMortarFaces]; + this->faceConn2 = new int[this->numNodesPerFace * this->numNonmortarFaces]; + this->x = new RealT[this->numTotalNodes]; + this->y = new RealT[this->numTotalNodes]; + this->z = new RealT[this->numTotalNodes]; + + // setup mesh nodal coordinate arrays + int ndOffset; + int numElemsX, numElemsY, numElemsZ; + RealT xMax, yMax, zMax, xMin, yMin, zMin; + RealT theta; + int *elConn, *faceConn; + for ( int iblk = 0; iblk < 2; ++iblk ) { + if ( iblk == 0 ) { + ndOffset = 0; + numElemsX = numElemsX1; + numElemsY = numElemsY1; + numElemsZ = numElemsZ1; + xMin = xMin1; + yMin = yMin1; + zMin = zMin1; + xMax = xMax1; + yMax = yMax1; + zMax = zMax1; + elConn = this->elConn1; + faceConn = this->faceConn1; + theta = thetaMortar; + } else { + ndOffset = numNodesBlock1; + numElemsX = numElemsX2; + numElemsY = numElemsY2; + numElemsZ = numElemsZ2; + xMin = xMin2; + yMin = yMin2; + zMin = zMin2; + xMax = xMax2; + yMax = yMax2; + zMax = zMax2; + elConn = this->elConn2; + faceConn = this->faceConn2; + theta = thetaNonmortar; + } + + int numNodesX = numElemsX + 1; + int numNodesY = numElemsY + 1; + int numNodesZ = numElemsZ + 1; + RealT hx = ( xMax - xMin ) / numElemsX; + RealT hy = ( yMax - yMin ) / numElemsY; + RealT hz = ( zMax - zMin ) / numElemsZ; + + // compute mesh coordinates + int ctr = 0; + for ( int k = 0; k < numNodesZ; ++k ) { + for ( int j = 0; j < numNodesY; ++j ) { + for ( int i = 0; i < numNodesX; ++i ) { + int idx = ndOffset + ctr; + RealT xyz[3]; + + // compute coordinates + xyz[0] = xMin + i * hx; + xyz[1] = yMin + j * hy; + xyz[2] = zMin + k * hz; + + // for non-boundary faces, apply coordinate rotation about z-axis + + bool rotationY = ( j > 0 && j < ( numNodesY - 1 ) ) ? true : false; + bool rotationX = ( i > 0 && i < ( numNodesX - 1 ) ) ? true : false; + + if ( rotationX && rotationY ) { + RealT rot0 = std::cos( theta * M_PI / 180 ); + RealT rot1 = -std::sin( theta * M_PI / 180 ); + RealT rot2 = -rot1; + RealT rot3 = rot0; + + RealT x_temp = xyz[0] * rot0 + xyz[1] * rot1; + RealT y_temp = xyz[0] * rot2 + xyz[1] * rot3; + RealT z_temp = xyz[2]; + + xyz[0] = x_temp; + xyz[1] = y_temp; + xyz[2] = z_temp; + } + + this->x[idx] = xyz[0]; + this->y[idx] = xyz[1]; + this->z[idx] = xyz[2]; + ++ctr; + } // end loop over x nodes + } // end loop over y nodes + } // end loop over z nodes + + // populate element connectivity arrays + ctr = 0; + for ( int k = 0; k < numElemsZ; ++k ) { + for ( int j = 0; j < numElemsY; ++j ) { + for ( int i = 0; i < numElemsX; ++i ) { + int zIdOffset = k * ( numNodesX * numNodesY ); + int yIdOffset = j * numNodesX; + int icr = ndOffset + zIdOffset + yIdOffset + i; + elConn[this->numNodesPerElement * ctr] = icr; + elConn[this->numNodesPerElement * ctr + 1] = icr + 1; + elConn[this->numNodesPerElement * ctr + 2] = icr + numNodesX + 1; + elConn[this->numNodesPerElement * ctr + 3] = icr + numNodesX; + + int lclZOffset = icr + numNodesX * numNodesY; + elConn[this->numNodesPerElement * ctr + 4] = lclZOffset; + elConn[this->numNodesPerElement * ctr + 5] = lclZOffset + 1; + elConn[this->numNodesPerElement * ctr + 6] = lclZOffset + numNodesX + 1; + elConn[this->numNodesPerElement * ctr + 7] = lclZOffset + numNodesX; + ++ctr; + + } // end loop over x elements + } // end loop over y elements + } // end loop over z elements + + // populate contact surface connectivity + // Note: element connectivity is not necessarily consistent with outward unit normal at + // contact faces + ctr = 0; + for ( int j = 0; j < numElemsY; ++j ) { + for ( int i = 0; i < numElemsX; ++i ) { + if ( ndOffset == 0 ) { + int yIdOffset = j * numNodesX; + int icr = ( numElemsX + 1 ) * ( numElemsY + 1 ) * ( numElemsZ ) + yIdOffset + i; // top surface + faceConn[this->numNodesPerFace * ctr] = icr; + faceConn[this->numNodesPerFace * ctr + 1] = icr + 1; + faceConn[this->numNodesPerFace * ctr + 2] = icr + numNodesX + 1; + faceConn[this->numNodesPerFace * ctr + 3] = icr + numNodesX; + ++ctr; + } + if ( ndOffset != 0 ) // reorient nonmortar face connectivity per outward unit normal requirement + { + int yIdOffset = j * numNodesX; + int icr = ndOffset + yIdOffset + i; // bottom surface + faceConn[this->numNodesPerFace * ctr] = icr; + faceConn[this->numNodesPerFace * ctr + 1] = icr + numNodesX; + faceConn[this->numNodesPerFace * ctr + 2] = icr + numNodesX + 1; + faceConn[this->numNodesPerFace * ctr + 3] = icr + 1; + ++ctr; + } } + } - } // end loop over blocks + } // end loop over blocks - this->mesh_constructed = true; + this->mesh_constructed = true; -} // end setupContactMeshHex() +} // end setupContactMeshHex() //------------------------------------------------------------------------------ -void TestMesh::setupContactMeshTet( int numElemsX1, int numElemsY1, int numElemsZ1, - RealT xMin1, RealT yMin1, RealT zMin1, - RealT xMax1, RealT yMax1, RealT zMax1, - int numElemsX2, int numElemsY2, int numElemsZ2, - RealT xMin2, RealT yMin2, RealT zMin2, - RealT xMax2, RealT yMax2, RealT zMax2, - RealT thetaMortar, RealT thetaNonmortar ) +void TestMesh::setupContactMeshTet( int numElemsX1, int numElemsY1, int numElemsZ1, RealT xMin1, RealT yMin1, + RealT zMin1, RealT xMax1, RealT yMax1, RealT zMax1, int numElemsX2, int numElemsY2, + int numElemsZ2, RealT xMin2, RealT yMin2, RealT zMin2, RealT xMax2, RealT yMax2, + RealT zMax2, RealT thetaMortar, RealT thetaNonmortar ) { - // NOTE: ONLY CONTACT INTERACTIONS IN THE Z-DIRECTION ARE SUPPORTED - // AT THE MOMENT. - - // Construct hex mesh first - setupContactMeshHex( numElemsX1, numElemsY1, numElemsZ1, - xMin1, yMin1, zMin1, - xMax1, yMax1, zMax1, - numElemsX2, numElemsY2, numElemsZ2, - xMin2, yMin2, zMin2, - xMax2, yMax2, zMax2, - thetaMortar, thetaNonmortar ); - - // allocate temporary storage for tet mesh data while pulling - // from hex mesh data - int numTetsPerHex = 6; - this->mesh_constructed = false; // will reset after tet mesh has been constructed - this->cellType = (int)(tribol::LINEAR_TRIANGLE); - this->numNodesPerFace = 3; // linear triangle faces - this->numNodesPerElement = 4; // linear four node tets - this->numTotalElements = numTetsPerHex * this->numTotalElements; // 6 tets per hex - this->numMortarElements = numTetsPerHex * numElemsX1 * numElemsY1 * numElemsZ1; - this->numNonmortarElements = numTetsPerHex * numElemsX2 * numElemsY2 * numElemsZ2; - this->numMortarFaces = 2 * numElemsX1 * numElemsY1; // only on contact surface - this->numNonmortarFaces = 2 * numElemsX2 * numElemsY2; // only on contact surface - this->numMortarNodes = (numElemsX1+1) * (numElemsY1+1) * (numElemsZ1+1); - this->numNonmortarNodes = (numElemsX2+1) * (numElemsY2+1) * (numElemsZ2+1); - this->numTotalNodes = this->numMortarNodes + this->numNonmortarNodes; - this->numTotalFaces = this->numMortarFaces + this->numNonmortarFaces; - - // clear prior mesh, but keep same nodal coordinate arrays - // Note, at this point nodal data arrays should not have been allocated - // on the new hex mesh, but this makes sure of that - bool keep_coords = true; - this->clear(keep_coords); - - allocIntArray( &this->faceConn1, this->numNodesPerFace*this->numMortarFaces, -1 ); - allocIntArray( &this->faceConn2, this->numNodesPerFace*this->numNonmortarFaces, -1 ); - allocIntArray( &this->elConn1, this->numNodesPerElement*this->numMortarElements, -1 ); - allocIntArray( &this->elConn2, this->numNodesPerElement*this->numNonmortarElements, -1 ); - - // tet element connectivity - int ndOffset; - int numElemsX, numElemsY, numElemsZ; - int * elConn, * faceConn; - for ( int iblk = 0; iblk<2; ++iblk) - { - if ( iblk == 0 ) - { - ndOffset = 0; - numElemsX = numElemsX1; - numElemsY = numElemsY1; - numElemsZ = numElemsZ1; - elConn = this->elConn1; - faceConn = this->faceConn1; - } - else + // NOTE: ONLY CONTACT INTERACTIONS IN THE Z-DIRECTION ARE SUPPORTED + // AT THE MOMENT. + + // Construct hex mesh first + setupContactMeshHex( numElemsX1, numElemsY1, numElemsZ1, xMin1, yMin1, zMin1, xMax1, yMax1, zMax1, numElemsX2, + numElemsY2, numElemsZ2, xMin2, yMin2, zMin2, xMax2, yMax2, zMax2, thetaMortar, thetaNonmortar ); + + // allocate temporary storage for tet mesh data while pulling + // from hex mesh data + int numTetsPerHex = 6; + this->mesh_constructed = false; // will reset after tet mesh has been constructed + this->cellType = (int)( tribol::LINEAR_TRIANGLE ); + this->numNodesPerFace = 3; // linear triangle faces + this->numNodesPerElement = 4; // linear four node tets + this->numTotalElements = numTetsPerHex * this->numTotalElements; // 6 tets per hex + this->numMortarElements = numTetsPerHex * numElemsX1 * numElemsY1 * numElemsZ1; + this->numNonmortarElements = numTetsPerHex * numElemsX2 * numElemsY2 * numElemsZ2; + this->numMortarFaces = 2 * numElemsX1 * numElemsY1; // only on contact surface + this->numNonmortarFaces = 2 * numElemsX2 * numElemsY2; // only on contact surface + this->numMortarNodes = ( numElemsX1 + 1 ) * ( numElemsY1 + 1 ) * ( numElemsZ1 + 1 ); + this->numNonmortarNodes = ( numElemsX2 + 1 ) * ( numElemsY2 + 1 ) * ( numElemsZ2 + 1 ); + this->numTotalNodes = this->numMortarNodes + this->numNonmortarNodes; + this->numTotalFaces = this->numMortarFaces + this->numNonmortarFaces; + + // clear prior mesh, but keep same nodal coordinate arrays + // Note, at this point nodal data arrays should not have been allocated + // on the new hex mesh, but this makes sure of that + bool keep_coords = true; + this->clear( keep_coords ); + + allocIntArray( &this->faceConn1, this->numNodesPerFace * this->numMortarFaces, -1 ); + allocIntArray( &this->faceConn2, this->numNodesPerFace * this->numNonmortarFaces, -1 ); + allocIntArray( &this->elConn1, this->numNodesPerElement * this->numMortarElements, -1 ); + allocIntArray( &this->elConn2, this->numNodesPerElement * this->numNonmortarElements, -1 ); + + // tet element connectivity + int ndOffset; + int numElemsX, numElemsY, numElemsZ; + int *elConn, *faceConn; + for ( int iblk = 0; iblk < 2; ++iblk ) { + if ( iblk == 0 ) { + ndOffset = 0; + numElemsX = numElemsX1; + numElemsY = numElemsY1; + numElemsZ = numElemsZ1; + elConn = this->elConn1; + faceConn = this->faceConn1; + } else { + ndOffset = this->numMortarNodes; + numElemsX = numElemsX2; + numElemsY = numElemsY2; + numElemsZ = numElemsZ2; + elConn = this->elConn2; + faceConn = this->faceConn2; + } + + int numNodesX = numElemsX + 1; + int numNodesY = numElemsY + 1; + + // populate element connectivity arrays + int ctr = 0; + for ( int k = 0; k < numElemsZ; ++k ) // loop over row in z-direction + { + for ( int j = 0; j < numElemsY; ++j ) // loop over row in y-direction { - ndOffset = this->numMortarNodes; - numElemsX = numElemsX2; - numElemsY = numElemsY2; - numElemsZ = numElemsZ2; - elConn = this->elConn2; - faceConn = this->faceConn2; - } + for ( int i = 0; i < numElemsX; ++i ) // loop over elements in x-direction + { + // same node ids and offsets as hex mesh + int zIdOffset = k * ( numNodesX * numNodesY ); // offset for z-direction node ids + int yIdOffset = j * numNodesX; // offset for y-direction node ids + int icr = ndOffset + zIdOffset + yIdOffset + i; // first node starting counter + int lclZOffset = icr + numNodesX * numNodesY; // local element z-offset for top nodes of hex + + // write out the 6 tet elements per this hex // code for each local hex node + // local node numbering for each hex // 1) icr; + // 1) 1, 5, 6, 4 // 2) icr + 1; + // 2) 1, 6, 2, 4 // 3) icr + numNodesX + 1; + // 3) 3, 2, 6, 4 // 4) icr + numNodesX; + // 4) 3, 6, 7, 4 // 5) lclZOffset; + // 5) 8, 7, 6, 4 // 6) lclZOffset + 1; + // 6) 8, 6, 5, 4 // 7) lclZOffset + numNodesX + 1; + // // 8) lclZOffset + numNodesX; + + // local tet element 1 + elConn[this->numNodesPerElement * ctr] = icr; + elConn[this->numNodesPerElement * ctr + 1] = lclZOffset; + elConn[this->numNodesPerElement * ctr + 2] = lclZOffset + 1; + elConn[this->numNodesPerElement * ctr + 3] = icr + numNodesX; + ++ctr; // incrememnt for next local tet + + // local tet element 2 + elConn[this->numNodesPerElement * ctr] = icr; + elConn[this->numNodesPerElement * ctr + 1] = lclZOffset + 1; + elConn[this->numNodesPerElement * ctr + 2] = icr + 1; + elConn[this->numNodesPerElement * ctr + 3] = icr + numNodesX; + ++ctr; // incrememnt for next local tet + + // local tet element 3 + elConn[this->numNodesPerElement * ctr] = icr + numNodesX + 1; + elConn[this->numNodesPerElement * ctr + 1] = icr + 1; + elConn[this->numNodesPerElement * ctr + 2] = lclZOffset + 1; + elConn[this->numNodesPerElement * ctr + 3] = icr + numNodesX; + ++ctr; // incrememnt for next local tet + + // local tet element 4 + elConn[this->numNodesPerElement * ctr] = icr + numNodesX + 1; + elConn[this->numNodesPerElement * ctr + 1] = lclZOffset + 1; + elConn[this->numNodesPerElement * ctr + 2] = lclZOffset + numNodesX + 1; + elConn[this->numNodesPerElement * ctr + 3] = icr + numNodesX; + ++ctr; // incrememnt for next local tet + + // local tet element 5 + elConn[this->numNodesPerElement * ctr] = lclZOffset + numNodesX; + elConn[this->numNodesPerElement * ctr + 1] = lclZOffset + numNodesX + 1; + elConn[this->numNodesPerElement * ctr + 2] = lclZOffset + 1; + elConn[this->numNodesPerElement * ctr + 3] = icr + numNodesX; + ++ctr; // incrememnt for next local tet + + // local tet element 6 + elConn[this->numNodesPerElement * ctr] = lclZOffset + numNodesX; + elConn[this->numNodesPerElement * ctr + 1] = lclZOffset + 1; + elConn[this->numNodesPerElement * ctr + 2] = lclZOffset; + elConn[this->numNodesPerElement * ctr + 3] = icr + numNodesX; + ++ctr; // incrememnt for next local tet + + } // end loop over x elements + } // end loop over y elements + } // end loop over z elements + + // populate contact surface connectivity + // Note: element connectivity is not necessarily consistent with outward unit normal at + // contact faces + ctr = 0; + for ( int j = 0; j < numElemsY; ++j ) { + for ( int i = 0; i < numElemsX; ++i ) { + if ( ndOffset == 0 ) // top surface of mortar bottom block + { + int yIdOffset = j * numNodesX; + int icr = ( numElemsX + 1 ) * ( numElemsY + 1 ) * ( numElemsZ ) + yIdOffset + i; // top surface node id + + // local tet face connectivity for top surface of bottom block + // 2 tet faces per hex face // code for each local hex node + // local el 1) 6, 7, 8 5) icr + // local el 2) 5, 6, 8 6) icr + 1 + // 7) icr + numNodesX + 1 + // 8) icr + numNodesX + + // local tet face 1 + faceConn[this->numNodesPerFace * ctr] = icr + 1; + faceConn[this->numNodesPerFace * ctr + 1] = icr + numNodesX + 1; + faceConn[this->numNodesPerFace * ctr + 2] = icr + numNodesX; + ++ctr; // increment counter to next tet face + + // local tet face 2 + faceConn[this->numNodesPerFace * ctr] = icr; + faceConn[this->numNodesPerFace * ctr + 1] = icr + 1; + faceConn[this->numNodesPerFace * ctr + 2] = icr + numNodesX; + ++ctr; // increment counter to next tet face + } + // bottom surface of nonmortar top block + // reorient nonmortar face connectivity per outward unit normal requirement + if ( ndOffset != 0 ) { + int yIdOffset = j * numNodesX; + int icr = ndOffset + yIdOffset + i; // bottom surface node id + + // local tet face connectivity for top block + // 2 tet faces per hex face // code for each local hex node + // local el 1) 1, 4, 2 1) icr + // local el 2) 2, 4, 3 2) icr + 1; + // 3) icr + numNodesX + 1 + // 4) icr + numNodesX; + + // local tet face 1 + faceConn[this->numNodesPerFace * ctr] = icr; + faceConn[this->numNodesPerFace * ctr + 1] = icr + numNodesX; + faceConn[this->numNodesPerFace * ctr + 2] = icr + 1; + ++ctr; // increment counter to next tet face + + // local tet face 2 + faceConn[this->numNodesPerFace * ctr] = icr + 1; + faceConn[this->numNodesPerFace * ctr + 1] = icr + numNodesX; + faceConn[this->numNodesPerFace * ctr + 2] = icr + numNodesX + 1; + ++ctr; // increment counter to next tet face + } + } // end loop over x-direction elements + } // end loop over y-direction elements + + } // end for loop over blocks + + this->mesh_constructed = true; + +} // end setupContactMeshTet() +//------------------------------------------------------------------------------ +void TestMesh::allocateAndSetVelocities( IndexT mesh_id, RealT valX, RealT valY, RealT valZ ) +{ + // Check that mesh ids are not the same. The TestMesh class was built around + // testing the mortar method with Lagrange multiplier enforcement, which does not + // support auto contact. + SLIC_ERROR_IF( this->mortarMeshId == this->nonmortarMeshId, + "TestMesh::allocateAndSetVelocities(): please set unique " + << "mortarMeshId and nonmortarMeshId prior to calling this routine." ); + + // check to see if pointers have been set + bool deleteVels = false; + if ( mesh_id == this->mortarMeshId ) { + if ( this->vx1 != nullptr ) { + delete[] this->vx1; + deleteVels = true; + } + if ( this->vy1 != nullptr ) { + delete[] this->vy1; + deleteVels = true; + } + if ( this->vz1 != nullptr ) { + delete[] this->vz1; + deleteVels = true; + } + + allocRealArray( &this->vx1, this->numTotalNodes, valX ); + allocRealArray( &this->vy1, this->numTotalNodes, valY ); + allocRealArray( &this->vz1, this->numTotalNodes, valZ ); + + registered_velocities1 = true; + } else if ( mesh_id == this->nonmortarMeshId ) { + if ( this->vx2 != nullptr ) { + delete[] this->vx2; + deleteVels = true; + } + if ( this->vy2 != nullptr ) { + delete[] this->vy2; + deleteVels = true; + } + if ( this->vz2 != nullptr ) { + delete[] this->vz2; + deleteVels = true; + } + + allocRealArray( &this->vx2, this->numTotalNodes, valX ); + allocRealArray( &this->vy2, this->numTotalNodes, valY ); + allocRealArray( &this->vz2, this->numTotalNodes, valZ ); + + registered_velocities2 = true; + } else { + SLIC_ERROR( "TestMesh::allocateAndSetVelocities(): " + << "not a valid mesh id." ); + } + + SLIC_DEBUG_IF( deleteVels, "TestMesh::allocateAndSetVelocities(): " + << "a velocity array has been deleted and reallocated." ); + +} // end TestMesh::allocateAndSetVelocities() - int numNodesX = numElemsX + 1; - int numNodesY = numElemsY + 1; +//------------------------------------------------------------------------------ +void TestMesh::allocateAndSetBulkModulus( IndexT mesh_id, RealT val ) +{ + // Check that mesh ids are the same. The TestMesh class was built around + // testing the mortar method with Lagrange multiplier enforcement, which does + // not support auto contact. + SLIC_ERROR_IF( this->mortarMeshId == this->nonmortarMeshId, + "TestMesh::allocateAndSetVelocities(): please set unique " + << "mortarMeshId and nonmortarMeshId prior to calling this routine." ); + + // check to see if pointers have been set + bool deleteData = false; + if ( mesh_id == this->mortarMeshId ) { + if ( this->mortar_bulk_mod != nullptr ) { + delete[] this->mortar_bulk_mod; + deleteData = true; + } + + allocRealArray( &this->mortar_bulk_mod, this->numMortarFaces, val ); + } else if ( mesh_id == this->nonmortarMeshId ) { + if ( this->nonmortar_bulk_mod != nullptr ) { + delete[] this->nonmortar_bulk_mod; + deleteData = true; + } + + allocRealArray( &this->nonmortar_bulk_mod, this->numNonmortarFaces, val ); + } else { + SLIC_ERROR( "TestMesh::allocateAndSetBulkModulus(): " + << "not a valid mesh id." ); + } + + SLIC_DEBUG_IF( deleteData, "TestMesh::allocateAndSetBulkModulus(): " + << "a bulk modulus array has been deleted and reallocated." ); + +} // end TestMesh::allocateAndSetBulkModulus() - // populate element connectivity arrays - int ctr = 0; - for (int k=0; knumNodesPerElement * ctr ] = icr; - elConn[ this->numNodesPerElement * ctr + 1] = lclZOffset; - elConn[ this->numNodesPerElement * ctr + 2] = lclZOffset + 1; - elConn[ this->numNodesPerElement * ctr + 3] = icr + numNodesX; - ++ctr; // incrememnt for next local tet - - // local tet element 2 - elConn[ this->numNodesPerElement * ctr ] = icr; - elConn[ this->numNodesPerElement * ctr + 1] = lclZOffset + 1; - elConn[ this->numNodesPerElement * ctr + 2] = icr + 1; - elConn[ this->numNodesPerElement * ctr + 3] = icr + numNodesX; - ++ctr; // incrememnt for next local tet - - // local tet element 3 - elConn[ this->numNodesPerElement * ctr ] = icr + numNodesX + 1; - elConn[ this->numNodesPerElement * ctr + 1] = icr + 1; - elConn[ this->numNodesPerElement * ctr + 2] = lclZOffset + 1; - elConn[ this->numNodesPerElement * ctr + 3] = icr + numNodesX; - ++ctr; // incrememnt for next local tet - - // local tet element 4 - elConn[ this->numNodesPerElement * ctr ] = icr + numNodesX + 1; - elConn[ this->numNodesPerElement * ctr + 1] = lclZOffset + 1; - elConn[ this->numNodesPerElement * ctr + 2] = lclZOffset + numNodesX + 1; - elConn[ this->numNodesPerElement * ctr + 3] = icr + numNodesX; - ++ctr; // incrememnt for next local tet - - // local tet element 5 - elConn[ this->numNodesPerElement * ctr ] = lclZOffset + numNodesX; - elConn[ this->numNodesPerElement * ctr + 1] = lclZOffset + numNodesX + 1; - elConn[ this->numNodesPerElement * ctr + 2] = lclZOffset + 1; - elConn[ this->numNodesPerElement * ctr + 3] = icr + numNodesX; - ++ctr; // incrememnt for next local tet - - // local tet element 6 - elConn[ this->numNodesPerElement * ctr ] = lclZOffset + numNodesX; - elConn[ this->numNodesPerElement * ctr + 1] = lclZOffset + 1; - elConn[ this->numNodesPerElement * ctr + 2] = lclZOffset; - elConn[ this->numNodesPerElement * ctr + 3] = icr + numNodesX; - ++ctr; // incrememnt for next local tet - - } // end loop over x elements - } // end loop over y elements - } // end loop over z elements - - // populate contact surface connectivity - // Note: element connectivity is not necessarily consistent with outward unit normal at - // contact faces - ctr = 0; - for (int j=0; jnumNodesPerFace * ctr ] = icr + 1; - faceConn[ this->numNodesPerFace * ctr + 1 ] = icr + numNodesX + 1; - faceConn[ this->numNodesPerFace * ctr + 2 ] = icr + numNodesX; - ++ctr; // increment counter to next tet face - - // local tet face 2 - faceConn[ this->numNodesPerFace * ctr ] = icr; - faceConn[ this->numNodesPerFace * ctr + 1 ] = icr + 1; - faceConn[ this->numNodesPerFace * ctr + 2 ] = icr + numNodesX; - ++ctr; // increment counter to next tet face - - } - // bottom surface of nonmortar top block - // reorient nonmortar face connectivity per outward unit normal requirement - if (ndOffset != 0) - { - int yIdOffset = j * numNodesX; - int icr = ndOffset + yIdOffset + i; // bottom surface node id - - // local tet face connectivity for top block - // 2 tet faces per hex face // code for each local hex node - // local el 1) 1, 4, 2 1) icr - // local el 2) 2, 4, 3 2) icr + 1; - // 3) icr + numNodesX + 1 - // 4) icr + numNodesX; - - // local tet face 1 - faceConn[ this->numNodesPerFace * ctr ] = icr; - faceConn[ this->numNodesPerFace * ctr + 1 ] = icr + numNodesX; - faceConn[ this->numNodesPerFace * ctr + 2 ] = icr + 1; - ++ctr; // increment counter to next tet face - - // local tet face 2 - faceConn[ this->numNodesPerFace * ctr ] = icr + 1; - faceConn[ this->numNodesPerFace * ctr + 1 ] = icr + numNodesX; - faceConn[ this->numNodesPerFace * ctr + 2 ] = icr + numNodesX + 1; - ++ctr; // increment counter to next tet face - - } - } // end loop over x-direction elements - } // end loop over y-direction elements - - } // end for loop over blocks - - this->mesh_constructed = true; - -} // end setupContactMeshTet() //------------------------------------------------------------------------------ -void TestMesh::allocateAndSetVelocities( IndexT mesh_id, RealT valX, RealT valY, RealT valZ ) +void TestMesh::allocateAndSetElementThickness( IndexT mesh_id, RealT t ) { - // Check that mesh ids are not the same. The TestMesh class was built around - // testing the mortar method with Lagrange multiplier enforcement, which does not - // support auto contact. - SLIC_ERROR_IF( this->mortarMeshId == this->nonmortarMeshId, - "TestMesh::allocateAndSetVelocities(): please set unique " << - "mortarMeshId and nonmortarMeshId prior to calling this routine."); - - // check to see if pointers have been set - bool deleteVels = false; - if (mesh_id == this->mortarMeshId) - { - if (this->vx1 != nullptr) - { - delete [] this->vx1; - deleteVels = true; - } - if (this->vy1 != nullptr) - { - delete [] this->vy1; - deleteVels = true; - } - if (this->vz1 != nullptr) - { - delete [] this->vz1; - deleteVels = true; - } + // check to see if pointers have been set + bool deleteData = false; + if ( mesh_id == this->mortarMeshId ) { + if ( this->mortar_element_thickness != nullptr ) { + delete[] this->mortar_element_thickness; + deleteData = true; + } + + allocRealArray( &this->mortar_element_thickness, this->numMortarFaces, t ); + } else if ( mesh_id == this->nonmortarMeshId ) { + if ( this->nonmortar_element_thickness != nullptr ) { + delete[] this->nonmortar_element_thickness; + deleteData = true; + } + + allocRealArray( &this->nonmortar_element_thickness, this->numNonmortarFaces, t ); + } else { + SLIC_ERROR( "TestMesh::allocateAndSetElementThickness(): " + << "not a valid mesh id." ); + } + + SLIC_DEBUG_IF( deleteData, "TestMesh::allocateAndSetElementThickness(): " + << "an element thickness array has been deleted and reallocated." ); + +} // end TestMesh::allocateAndSetElementThickness() - allocRealArray( &this->vx1, this->numTotalNodes, valX ); - allocRealArray( &this->vy1, this->numTotalNodes, valY ); - allocRealArray( &this->vz1, this->numTotalNodes, valZ ); +//------------------------------------------------------------------------------ +int TestMesh::simpleTribolSetupAndUpdate( ContactMethod method, EnforcementMethod TRIBOL_UNUSED_PARAM( enforcement ), + ContactModel TRIBOL_UNUSED_PARAM( model ), + ContactCase TRIBOL_UNUSED_PARAM( contact_case ), + bool TRIBOL_UNUSED_PARAM( visualization ), TestControlParameters& params ) +{ + SLIC_ERROR_IF( !this->mesh_constructed, "TestMesh::simpleTribolSetupAndUpdate(): " + << "must construct hex or tet mesh prior to calling this routine." ); + + // grab coordinate data + RealT* x = this->x; + RealT* y = this->y; + RealT* z = this->z; + + switch ( method ) { + case SINGLE_MORTAR: + case ALIGNED_MORTAR: { + // allocate gaps and pressures with length of total mesh to allow use + // of global connectivity for indexing + allocRealArray( &this->gaps, this->numTotalNodes, 0. ); + allocRealArray( &this->pressures, this->numTotalNodes, 1. ); + break; + } + case MORTAR_WEIGHTS: { + allocRealArray( &this->gaps, this->numTotalNodes, 0. ); + this->pressures = nullptr; + break; + } + default: { + // no-op + break; + } + } // end switch on method - registered_velocities1 = true; - } - else if (mesh_id == this->nonmortarMeshId) - { - if (this->vx2 != nullptr) - { - delete [] this->vx2; - deleteVels = true; - } - if (this->vy2 != nullptr) - { - delete [] this->vy2; - deleteVels = true; - } - if (this->vz2 != nullptr) - { - delete [] this->vz2; - deleteVels = true; - } + const RealT area_frac = 1.e-03; - allocRealArray( &this->vx2, this->numTotalNodes, valX ); - allocRealArray( &this->vy2, this->numTotalNodes, valY ); - allocRealArray( &this->vz2, this->numTotalNodes, valZ ); + SimpleCouplingSetup( this->dim, this->cellType, method, this->numMortarFaces, this->numTotalNodes, this->faceConn1, x, + y, z, this->numNonmortarFaces, this->numTotalNodes, this->faceConn2, x, y, z, area_frac, + this->gaps, this->pressures ); - registered_velocities2 = true; - } - else - { - SLIC_ERROR( "TestMesh::allocateAndSetVelocities(): " << - "not a valid mesh id." ); - } + int err = Update( params.dt ); - SLIC_DEBUG_IF( deleteVels, "TestMesh::allocateAndSetVelocities(): " << - "a velocity array has been deleted and reallocated." ); + return err; -} // end TestMesh::allocateAndSetVelocities() +} // end simpleTribolSetupAndUpdate() //------------------------------------------------------------------------------ -void TestMesh::allocateAndSetBulkModulus( IndexT mesh_id, RealT val ) +int TestMesh::tribolSetupAndUpdate( ContactMethod method, EnforcementMethod enforcement, ContactModel model, + ContactCase contact_case, bool visualization, TestControlParameters& params ) { - // Check that mesh ids are the same. The TestMesh class was built around - // testing the mortar method with Lagrange multiplier enforcement, which does - // not support auto contact. - SLIC_ERROR_IF( this->mortarMeshId == this->nonmortarMeshId, - "TestMesh::allocateAndSetVelocities(): please set unique " << - "mortarMeshId and nonmortarMeshId prior to calling this routine."); - - // check to see if pointers have been set - bool deleteData = false; - if (mesh_id == this->mortarMeshId) - { - if (this->mortar_bulk_mod != nullptr) - { - delete [] this->mortar_bulk_mod; - deleteData = true; + // grab coordinate data + RealT* x = this->x; + RealT* y = this->y; + RealT* z = this->z; + + // register mesh. Note: Tribol will still work with global integer + // ids for the connectivity and array lengths of numTotalNodes. + registerMesh( this->mortarMeshId, this->numMortarFaces, this->numTotalNodes, this->faceConn1, this->cellType, x, y, z, + MemorySpace::Host ); + registerMesh( this->nonmortarMeshId, this->numNonmortarFaces, this->numTotalNodes, this->faceConn2, this->cellType, x, + y, z, MemorySpace::Host ); + + // register nodal forces. Note, I was getting a seg fault when + // registering the same pointer to a single set of force arrays + // for both calls to tribol::registerNodalResponse(). As a result, + // I created two sets of nodal force arrays with their own pointers + // to the data that are registered with tribol and there is no longer + // a seg fault. + allocRealArray( &this->fx1, this->numTotalNodes, 0. ); + allocRealArray( &this->fy1, this->numTotalNodes, 0. ); + allocRealArray( &this->fz1, this->numTotalNodes, 0. ); + allocRealArray( &this->fx2, this->numTotalNodes, 0. ); + allocRealArray( &this->fy2, this->numTotalNodes, 0. ); + allocRealArray( &this->fz2, this->numTotalNodes, 0. ); + + registerNodalResponse( this->mortarMeshId, this->fx1, this->fy1, this->fz1 ); + + registerNodalResponse( this->nonmortarMeshId, this->fx2, this->fy2, this->fz2 ); + + // register nodal velocities + if ( registered_velocities1 ) { + registerNodalVelocities( this->mortarMeshId, this->vx1, this->vy1, this->vz1 ); + } + + if ( registered_velocities2 ) { + registerNodalVelocities( this->nonmortarMeshId, this->vx2, this->vy2, this->vz2 ); + } + + // register nodal pressure and nodal gap array for the nonmortar mesh + // for mortar based methods + switch ( method ) { + case SINGLE_MORTAR: + case ALIGNED_MORTAR: { + allocRealArray( &this->gaps, this->numTotalNodes, 0. ); + allocRealArray( &this->pressures, this->numTotalNodes, 1. ); + + // register nodal gaps and pressures + registerMortarGaps( this->nonmortarMeshId, this->gaps ); + registerMortarPressures( this->nonmortarMeshId, this->pressures ); + break; + } + case MORTAR_WEIGHTS: { + allocRealArray( &this->gaps, this->numTotalNodes, 0. ); + this->pressures = nullptr; + + registerMortarGaps( this->nonmortarMeshId, this->gaps ); + break; + } + default: { + // no-op + } + } // end switch on method + + // if enforcement is penalty, register penalty parameters + if ( enforcement == PENALTY ) { + if ( !params.penalty_ratio ) { + setKinematicConstantPenalty( this->mortarMeshId, params.const_penalty ); + setKinematicConstantPenalty( this->nonmortarMeshId, params.const_penalty ); + } else { + if ( this->mortar_bulk_mod == nullptr ) { + SLIC_DEBUG_ROOT( "TestMesh::tribolSetupAndUpdate(): " + << "mortar_bulk_mod not set; registering default value." ); + this->mortar_bulk_mod = new RealT[this->numMortarFaces]; + for ( int i = 0; i < this->numMortarFaces; ++i ) { + this->mortar_bulk_mod[i] = params.const_penalty; // non-physical for testing + } + } + if ( this->mortar_element_thickness == nullptr ) { + SLIC_DEBUG_ROOT( "TestMesh::tribolSetupAndUpdate(): " + << "mortar_element_thickness not set; registering default value." ); + this->mortar_element_thickness = new RealT[this->numMortarFaces]; + for ( int i = 0; i < this->numMortarFaces; ++i ) { + this->mortar_element_thickness[i] = 1.; // non-physical for testing + } } - allocRealArray( &this->mortar_bulk_mod, this->numMortarFaces, val ); - } - else if (mesh_id == this->nonmortarMeshId) - { - if (this->nonmortar_bulk_mod != nullptr) - { - delete [] this->nonmortar_bulk_mod; - deleteData = true; + if ( this->nonmortar_bulk_mod == nullptr ) { + SLIC_DEBUG_ROOT( "TestMesh::tribolSetupAndUpdate(): " + << "nonmortar_bulk_mod not set; registering default value." ); + this->nonmortar_bulk_mod = new RealT[this->numNonmortarFaces]; + for ( int i = 0; i < this->numNonmortarFaces; ++i ) { + this->nonmortar_bulk_mod[i] = params.const_penalty; // non-physical for testing + } + } + if ( this->nonmortar_element_thickness == nullptr ) { + SLIC_DEBUG_ROOT( "TestMesh::tribolSetupAndUpdate(): " + << "nonmortar_element_thickness not set; registering default value." ); + this->nonmortar_element_thickness = new RealT[this->numNonmortarFaces]; + for ( int i = 0; i < this->numNonmortarFaces; ++i ) { + this->nonmortar_element_thickness[i] = 1.; // non-physical for testing + } } - allocRealArray( &this->nonmortar_bulk_mod, this->numNonmortarFaces, val ); - } - else - { - SLIC_ERROR( "TestMesh::allocateAndSetBulkModulus(): " << - "not a valid mesh id." ); - } + // register mortar penalty data + registerRealElementField( this->mortarMeshId, BULK_MODULUS, this->mortar_bulk_mod ); + registerRealElementField( this->mortarMeshId, ELEMENT_THICKNESS, this->mortar_element_thickness ); - SLIC_DEBUG_IF( deleteData, "TestMesh::allocateAndSetBulkModulus(): " << - "a bulk modulus array has been deleted and reallocated." ); + // register nonmortar penalty data + registerRealElementField( this->nonmortarMeshId, BULK_MODULUS, this->nonmortar_bulk_mod ); + registerRealElementField( this->nonmortarMeshId, ELEMENT_THICKNESS, this->nonmortar_element_thickness ); + } // end if-penalty-ratio -} // end TestMesh::allocateAndSetBulkModulus() + // check for gap rate penalty enforcement + if ( params.constant_rate_penalty ) { + setRateConstantPenalty( this->mortarMeshId, params.constant_rate_penalty ); + setRateConstantPenalty( this->nonmortarMeshId, params.constant_rate_penalty ); + } else if ( params.percent_rate_penalty ) { + setRatePercentPenalty( this->mortarMeshId, params.rate_penalty_ratio ); + setRatePercentPenalty( this->nonmortarMeshId, params.rate_penalty_ratio ); + } -//------------------------------------------------------------------------------ -void TestMesh::allocateAndSetElementThickness( IndexT mesh_id, RealT t ) -{ - // check to see if pointers have been set - bool deleteData = false; - if (mesh_id == this->mortarMeshId) - { - if (this->mortar_element_thickness != nullptr) - { - delete [] this->mortar_element_thickness; - deleteData = true; - } + } // end if-penalty - allocRealArray( &this->mortar_element_thickness, this->numMortarFaces, t ); - } - else if (mesh_id == this->nonmortarMeshId) - { - if (this->nonmortar_element_thickness != nullptr) - { - delete [] this->nonmortar_element_thickness; - deleteData = true; - } + // register coupling scheme + const int csIndex = 0; + registerCouplingScheme( csIndex, this->mortarMeshId, this->nonmortarMeshId, SURFACE_TO_SURFACE, contact_case, method, + model, enforcement, BINNING_GRID, ExecutionMode::Sequential ); - allocRealArray( &this->nonmortar_element_thickness, this->numNonmortarFaces, t ); - } - else - { - SLIC_ERROR( "TestMesh::allocateAndSetElementThickness(): " << - "not a valid mesh id." ); - } + enableTimestepVote( csIndex, params.enable_timestep_vote ); + setTimestepPenFrac( csIndex, params.timestep_pen_frac ); + setTimestepScale( csIndex, params.timestep_scale ); - SLIC_DEBUG_IF( deleteData, "TestMesh::allocateAndSetElementThickness(): " << - "an element thickness array has been deleted and reallocated." ); + // if enforcement is penalty, register penalty parameters + if ( enforcement == PENALTY ) { + // set the penetration fraction for timestep votes computed with penalty enforcements + setAutoContactPenScale( csIndex, params.auto_contact_pen_frac ); -} // end TestMesh::allocateAndSetElementThickness() + } // end if-penalty -//------------------------------------------------------------------------------ -int TestMesh::simpleTribolSetupAndUpdate( ContactMethod method, - EnforcementMethod TRIBOL_UNUSED_PARAM(enforcement), - ContactModel TRIBOL_UNUSED_PARAM(model), - ContactCase TRIBOL_UNUSED_PARAM(contact_case), - bool TRIBOL_UNUSED_PARAM(visualization), - TestControlParameters& params ) -{ - SLIC_ERROR_IF( !this->mesh_constructed, "TestMesh::simpleTribolSetupAndUpdate(): " << - "must construct hex or tet mesh prior to calling this routine." ); - - // grab coordinate data - RealT * x = this->x; - RealT * y = this->y; - RealT * z = this->z; - - switch (method) - { - case SINGLE_MORTAR: - case ALIGNED_MORTAR: - { - // allocate gaps and pressures with length of total mesh to allow use - // of global connectivity for indexing - allocRealArray( &this->gaps, this->numTotalNodes, 0. ); - allocRealArray( &this->pressures, this->numTotalNodes, 1. ); - break; - } - case MORTAR_WEIGHTS: - { - allocRealArray( &this->gaps, this->numTotalNodes, 0. ); - this->pressures = nullptr; - break; - } - default: - { - // no-op - break; - } - } // end switch on method - - const RealT area_frac = 1.e-03; - - SimpleCouplingSetup( this->dim, - this->cellType, - method, - this->numMortarFaces, - this->numTotalNodes, - this->faceConn1, - x, y, z, - this->numNonmortarFaces, - this->numTotalNodes, - this->faceConn2, - x, y, z, - area_frac, - this->gaps, - this->pressures); - - int err = Update( params.dt ); - - return err; - -} // end simpleTribolSetupAndUpdate() + setContactAreaFrac( csIndex, 1.e-6 ); -//------------------------------------------------------------------------------ -int TestMesh::tribolSetupAndUpdate( ContactMethod method, - EnforcementMethod enforcement, - ContactModel model, - ContactCase contact_case, - bool visualization, - TestControlParameters& params ) -{ - // grab coordinate data - RealT * x = this->x; - RealT * y = this->y; - RealT * z = this->z; - - // register mesh. Note: Tribol will still work with global integer - // ids for the connectivity and array lengths of numTotalNodes. - registerMesh( this->mortarMeshId, this->numMortarFaces, - this->numTotalNodes, - this->faceConn1, this->cellType, x, y, z, MemorySpace::Host ); - registerMesh( this->nonmortarMeshId, this->numNonmortarFaces, - this->numTotalNodes, - this->faceConn2, this->cellType, x, y, z, MemorySpace::Host ); - - // register nodal forces. Note, I was getting a seg fault when - // registering the same pointer to a single set of force arrays - // for both calls to tribol::registerNodalResponse(). As a result, - // I created two sets of nodal force arrays with their own pointers - // to the data that are registered with tribol and there is no longer - // a seg fault. - allocRealArray( &this->fx1, this->numTotalNodes, 0. ); - allocRealArray( &this->fy1, this->numTotalNodes, 0. ); - allocRealArray( &this->fz1, this->numTotalNodes, 0. ); - allocRealArray( &this->fx2, this->numTotalNodes, 0. ); - allocRealArray( &this->fy2, this->numTotalNodes, 0. ); - allocRealArray( &this->fz2, this->numTotalNodes, 0. ); - - registerNodalResponse( this->mortarMeshId, - this->fx1, - this->fy1, - this->fz1 ); - - registerNodalResponse( this->nonmortarMeshId, - this->fx2, - this->fy2, - this->fz2 ); - - // register nodal velocities - if (registered_velocities1) - { - registerNodalVelocities( this->mortarMeshId, - this->vx1, - this->vy1, - this->vz1 ); - } - - if (registered_velocities2) - { - registerNodalVelocities( this->nonmortarMeshId, - this->vx2, - this->vy2, - this->vz2 ); - } - - // register nodal pressure and nodal gap array for the nonmortar mesh - // for mortar based methods - switch (method) - { - case SINGLE_MORTAR: - case ALIGNED_MORTAR: - { - allocRealArray( &this->gaps, this->numTotalNodes, 0. ); - allocRealArray( &this->pressures, this->numTotalNodes, 1. ); + if ( visualization ) { + setPlotCycleIncrement( csIndex, 1 ); + } - // register nodal gaps and pressures - registerMortarGaps( this->nonmortarMeshId, this->gaps ); - registerMortarPressures( this->nonmortarMeshId, this->pressures ); - break; - } - case MORTAR_WEIGHTS: - { - allocRealArray( &this->gaps, this->numTotalNodes, 0. ); - this->pressures = nullptr; + setLoggingLevel( csIndex, TRIBOL_WARNING ); - registerMortarGaps( this->nonmortarMeshId, this->gaps ); - break; - } - default: - { - // no-op - } - } // end switch on method + if ( method == COMMON_PLANE && enforcement == PENALTY ) { + PenaltyConstraintType constraint_type = + ( params.constant_rate_penalty || params.percent_rate_penalty ) ? KINEMATIC_AND_RATE : KINEMATIC; + KinematicPenaltyCalculation pen_calc = ( params.penalty_ratio ) ? KINEMATIC_ELEMENT : KINEMATIC_CONSTANT; - // if enforcement is penalty, register penalty parameters - if (enforcement == PENALTY) - { - if (!params.penalty_ratio) - { - setKinematicConstantPenalty( this->mortarMeshId, params.const_penalty ); - setKinematicConstantPenalty( this->nonmortarMeshId, params.const_penalty ); - } - else - { - if (this->mortar_bulk_mod == nullptr) - { - SLIC_DEBUG_ROOT( "TestMesh::tribolSetupAndUpdate(): " << - "mortar_bulk_mod not set; registering default value." ); - this->mortar_bulk_mod = new RealT[ this->numMortarFaces ]; - for (int i=0; inumMortarFaces; ++i) - { - this->mortar_bulk_mod[i] = params.const_penalty; // non-physical for testing - } - } - if (this->mortar_element_thickness == nullptr) - { - SLIC_DEBUG_ROOT( "TestMesh::tribolSetupAndUpdate(): " << - "mortar_element_thickness not set; registering default value." ); - this->mortar_element_thickness = new RealT[ this->numMortarFaces ]; - for (int i=0; inumMortarFaces; ++i) - { - this->mortar_element_thickness[i] = 1.; // non-physical for testing - } - } - - if (this->nonmortar_bulk_mod == nullptr) - { - SLIC_DEBUG_ROOT( "TestMesh::tribolSetupAndUpdate(): " << - "nonmortar_bulk_mod not set; registering default value." ); - this->nonmortar_bulk_mod = new RealT[ this->numNonmortarFaces ]; - for (int i=0; inumNonmortarFaces; ++i) - { - this->nonmortar_bulk_mod[i] = params.const_penalty; // non-physical for testing - } - } - if (this->nonmortar_element_thickness == nullptr) - { - SLIC_DEBUG_ROOT( "TestMesh::tribolSetupAndUpdate(): " << - "nonmortar_element_thickness not set; registering default value." ); - this->nonmortar_element_thickness = new RealT[ this->numNonmortarFaces ]; - for (int i=0; inumNonmortarFaces; ++i) - { - this->nonmortar_element_thickness[i] = 1.; // non-physical for testing - } - } - - // register mortar penalty data - registerRealElementField( this->mortarMeshId, BULK_MODULUS, - this->mortar_bulk_mod ); - registerRealElementField( this->mortarMeshId, ELEMENT_THICKNESS, - this->mortar_element_thickness ); - - // register nonmortar penalty data - registerRealElementField( this->nonmortarMeshId, BULK_MODULUS, - this->nonmortar_bulk_mod ); - registerRealElementField( this->nonmortarMeshId, ELEMENT_THICKNESS, - this->nonmortar_element_thickness ); - } // end if-penalty-ratio - - // check for gap rate penalty enforcement - if (params.constant_rate_penalty) - { - setRateConstantPenalty( this->mortarMeshId, params.constant_rate_penalty ); - setRateConstantPenalty( this->nonmortarMeshId, params.constant_rate_penalty ); - } - else if (params.percent_rate_penalty) - { - setRatePercentPenalty( this->mortarMeshId, params.rate_penalty_ratio ); - setRatePercentPenalty( this->nonmortarMeshId, params.rate_penalty_ratio ); - } + RatePenaltyCalculation rate_calc; + rate_calc = NO_RATE_PENALTY; + if ( params.constant_rate_penalty ) { + rate_calc = RATE_CONSTANT; + } else if ( params.percent_rate_penalty ) { + rate_calc = RATE_PERCENT; + } - } // end if-penalty - - // register coupling scheme - const int csIndex = 0; - registerCouplingScheme( csIndex, - this->mortarMeshId, - this->nonmortarMeshId, - SURFACE_TO_SURFACE, - contact_case, - method, - model, - enforcement, - BINNING_GRID, - ExecutionMode::Sequential ); - - enableTimestepVote( csIndex, params.enable_timestep_vote ); - setTimestepPenFrac( csIndex, params.timestep_pen_frac ); - setTimestepScale( csIndex, params.timestep_scale ); - - // if enforcement is penalty, register penalty parameters - if (enforcement == PENALTY) - { - // set the penetration fraction for timestep votes computed with penalty enforcements - setAutoContactPenScale( csIndex, params.auto_contact_pen_frac ); - - } // end if-penalty - - setContactAreaFrac( csIndex, 1.e-6 ); - - if (visualization) - { - setPlotCycleIncrement( csIndex, 1 ); - } - - setLoggingLevel(csIndex, TRIBOL_WARNING); - - if (method == COMMON_PLANE && enforcement == PENALTY) - { - PenaltyConstraintType constraint_type = (params.constant_rate_penalty || params.percent_rate_penalty) - ? KINEMATIC_AND_RATE : KINEMATIC; - KinematicPenaltyCalculation pen_calc = (params.penalty_ratio) ? KINEMATIC_ELEMENT : KINEMATIC_CONSTANT; - - RatePenaltyCalculation rate_calc; - rate_calc = NO_RATE_PENALTY; - if (params.constant_rate_penalty) - { - rate_calc = RATE_CONSTANT; - } - else if (params.percent_rate_penalty) - { - rate_calc = RATE_PERCENT; - } + // set penalty options after registering coupling scheme + setPenaltyOptions( csIndex, constraint_type, pen_calc, rate_calc ); + + } else if ( ( method == SINGLE_MORTAR || method == ALIGNED_MORTAR ) && enforcement == LAGRANGE_MULTIPLIER ) { + // note, eval modes and sparse modes not exposed in the interface to this class + setLagrangeMultiplierOptions( csIndex, ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN, SparseMode::MFEM_LINKED_LIST ); + } else if ( method == MORTAR_WEIGHTS ) { + // note, eval modes and sparse modes not exposed in the interface to this class + setLagrangeMultiplierOptions( csIndex, ImplicitEvalMode::MORTAR_WEIGHTS_EVAL, SparseMode::MFEM_LINKED_LIST ); + } + + // call tribol update() + int err = update( 1, 1., params.dt ); + + // print mesh to vtk + if ( visualization ) { + testMeshToVtk( "", 1, 1 ); + } + + return err; + +} // end tribolSetupAndUpdate() - // set penalty options after registering coupling scheme - setPenaltyOptions( csIndex, constraint_type, pen_calc, rate_calc ); - - } - else if ((method == SINGLE_MORTAR || method == ALIGNED_MORTAR) && enforcement == LAGRANGE_MULTIPLIER) - { - // note, eval modes and sparse modes not exposed in the interface to this class - setLagrangeMultiplierOptions( csIndex, ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN, - SparseMode::MFEM_LINKED_LIST ); - } - else if (method == MORTAR_WEIGHTS) - { - // note, eval modes and sparse modes not exposed in the interface to this class - setLagrangeMultiplierOptions( csIndex, ImplicitEvalMode::MORTAR_WEIGHTS_EVAL, - SparseMode::MFEM_LINKED_LIST ); - } - - // call tribol update() - int err = update( 1, 1., params.dt ); - - // print mesh to vtk - if (visualization) - { - testMeshToVtk( "", 1, 1 ); - } - - return err; - -} // end tribolSetupAndUpdate() - //------------------------------------------------------------------------------ -void TestMesh::setupPatchTestDirichletBCs( IndexT mesh_id, - int numElemsX, - int numElemsY, - int numElemsZ, - int nodeIdOffset, - bool inHomogeneousGap, - RealT inHomogeneousZVal ) +void TestMesh::setupPatchTestDirichletBCs( IndexT mesh_id, int numElemsX, int numElemsY, int numElemsZ, + int nodeIdOffset, bool inHomogeneousGap, RealT inHomogeneousZVal ) { - SLIC_ERROR_IF( !this->mesh_constructed, "TestMesh::setupPatchTestDirichletBCs(): " << - "mesh must be constructed prior to calling this routine." ); - - bool mortar = false; - if (mesh_id == this->mortarMeshId) - { - mortar = true; - } - - // This routine sets up x,y, and z-component Dirichlet BCs on - // three faces of each block specifically for the classical contact - // patch test problem - // - // NOTE: the number of nodes in the x, y and z-directions is the same - // for a hex mesh and tet mesh - int numNodesX = numElemsX + 1; - int numNodesY = numElemsY + 1; - int numNodesZ = numElemsZ + 1; - - int numNodes = numNodesX * numNodesY * numNodesZ; - - int* nodeIdsX, *nodeIdsY, *nodeIdsZ; - RealT* valX, *valY, *valZ; - if (mortar) // mortar BCs - { - this->dirNodesX1 = new int[ numNodes ]; - this->dirNodesY1 = new int[ numNodes ]; - this->dirNodesZ1 = new int[ numNodes ]; - nodeIdsX = this->dirNodesX1; - nodeIdsY = this->dirNodesY1; - nodeIdsZ = this->dirNodesZ1; - - this->iDirValX1 = new RealT[ numNodes ]; - this->iDirValY1 = new RealT[ numNodes ]; - this->iDirValZ1 = new RealT[ numNodes ]; - valX = this->iDirValX1; - valY = this->iDirValY1; - valZ = this->iDirValZ1; - } - else // nonmortar BCs - { - this->dirNodesX2 = new int[ numNodes ]; - this->dirNodesY2 = new int[ numNodes ]; - this->dirNodesZ2 = new int[ numNodes ]; - nodeIdsX = this->dirNodesX2; - nodeIdsY = this->dirNodesY2; - nodeIdsZ = this->dirNodesZ2; - - this->iDirValX2 = new RealT[ numNodes ]; - this->iDirValY2 = new RealT[ numNodes ]; - this->iDirValZ2 = new RealT[ numNodes ]; - valX = this->iDirValX2; - valY = this->iDirValY2; - valZ = this->iDirValZ2; - } - - // initialize arrays - initIntArray( nodeIdsX, numNodes, -1 ); - initIntArray( nodeIdsY, numNodes, -1 ); - initIntArray( nodeIdsZ, numNodes, -1 ); - initRealArray( valX, numNodes, 0. ); - initRealArray( valY, numNodes, 0. ); - initRealArray( valZ, numNodes, 0. ); - - // setup the boundary conditions for mortar (bottom) block - if (mortar) - { - // setup BCs in the x-direction - int ctr = 0; - for (int k=0; kmesh_constructed, "TestMesh::setupPatchTestDirichletBCs(): " + << "mesh must be constructed prior to calling this routine." ); + + bool mortar = false; + if ( mesh_id == this->mortarMeshId ) { + mortar = true; + } + + // This routine sets up x,y, and z-component Dirichlet BCs on + // three faces of each block specifically for the classical contact + // patch test problem + // + // NOTE: the number of nodes in the x, y and z-directions is the same + // for a hex mesh and tet mesh + int numNodesX = numElemsX + 1; + int numNodesY = numElemsY + 1; + int numNodesZ = numElemsZ + 1; + + int numNodes = numNodesX * numNodesY * numNodesZ; + + int *nodeIdsX, *nodeIdsY, *nodeIdsZ; + RealT *valX, *valY, *valZ; + if ( mortar ) // mortar BCs + { + this->dirNodesX1 = new int[numNodes]; + this->dirNodesY1 = new int[numNodes]; + this->dirNodesZ1 = new int[numNodes]; + nodeIdsX = this->dirNodesX1; + nodeIdsY = this->dirNodesY1; + nodeIdsZ = this->dirNodesZ1; + + this->iDirValX1 = new RealT[numNodes]; + this->iDirValY1 = new RealT[numNodes]; + this->iDirValZ1 = new RealT[numNodes]; + valX = this->iDirValX1; + valY = this->iDirValY1; + valZ = this->iDirValZ1; + } else // nonmortar BCs + { + this->dirNodesX2 = new int[numNodes]; + this->dirNodesY2 = new int[numNodes]; + this->dirNodesZ2 = new int[numNodes]; + nodeIdsX = this->dirNodesX2; + nodeIdsY = this->dirNodesY2; + nodeIdsZ = this->dirNodesZ2; + + this->iDirValX2 = new RealT[numNodes]; + this->iDirValY2 = new RealT[numNodes]; + this->iDirValZ2 = new RealT[numNodes]; + valX = this->iDirValX2; + valY = this->iDirValY2; + valZ = this->iDirValZ2; + } + + // initialize arrays + initIntArray( nodeIdsX, numNodes, -1 ); + initIntArray( nodeIdsY, numNodes, -1 ); + initIntArray( nodeIdsZ, numNodes, -1 ); + initRealArray( valX, numNodes, 0. ); + initRealArray( valY, numNodes, 0. ); + initRealArray( valZ, numNodes, 0. ); + + // setup the boundary conditions for mortar (bottom) block + if ( mortar ) { + // setup BCs in the x-direction + int ctr = 0; + for ( int k = 0; k < numNodesZ; ++k ) { + for ( int j = 0; j < numNodesY; ++j ) { + int yOffset = j * numNodesX; + int zOffset = numNodesX * numNodesY * k; + int id = yOffset + zOffset; + nodeIdsX[ctr] = id; + ++ctr; } - - // setupBCs in the y-dirction - ctr = 0; - for (int k=0; kmesh_constructed, "TestMesh::setupPatchTestPressureDofs(): " << - "mesh must be constructed prior to calling this routine." ); - - bool mortar = false; - if (mesh_id == this->mortarMeshId) - { - mortar = true; - } - - // this routine hard codes nonmortar pressure dofs for the bottom surface - // of the top (nonmortar) block - int numNodes = (numElemsX+1) * (numElemsY+1) * (numElemsZ+1); - int * presDofs; - - if (mortar) // mortar dofs - { - this->presDofs1 = new int[ numNodes ]; - presDofs = this->presDofs1; - } - else // nonmortar pressure dofs - { - this->presDofs2 = new int[ numNodes ]; - presDofs = this->presDofs2; - } - - for (int i=0; imesh_constructed, "TestMesh::setupPatchTestPressureDofs(): " + << "mesh must be constructed prior to calling this routine." ); + + bool mortar = false; + if ( mesh_id == this->mortarMeshId ) { + mortar = true; + } + + // this routine hard codes nonmortar pressure dofs for the bottom surface + // of the top (nonmortar) block + int numNodes = ( numElemsX + 1 ) * ( numElemsY + 1 ) * ( numElemsZ + 1 ); + int* presDofs; + + if ( mortar ) // mortar dofs + { + this->presDofs1 = new int[numNodes]; + presDofs = this->presDofs1; + } else // nonmortar pressure dofs + { + this->presDofs2 = new int[numNodes]; + presDofs = this->presDofs2; + } + + for ( int i = 0; i < numNodes; ++i ) { + presDofs[i] = -1; + } + + if ( contact ) { + int numSurfaceNodes = ( numElemsX + 1 ) * ( numElemsY + 1 ); + for ( int i = 0; i < numSurfaceNodes; ++i ) { + presDofs[i] = nodeIdOffset + i; + } + } +} // end setupPatchTestPressureDofs() //------------------------------------------------------------------------------ void TestMesh::setupMfemMesh( bool fix_orientation ) { - SLIC_ERROR_IF( !this->mesh_constructed, "TestMesh::setupMfemMesh(): " << - "test mesh must be constructed prior to calling this routine." ); - - SLIC_ERROR_IF( this->dim != 3, "TestMesh::setupMfemMesh(): Mfem meshes of dimension, " << - this->dim << ", are not supported at this time." ); - - // construct new mfem mesh - if (this->mfem_mesh != nullptr) - { - this->mfem_mesh->Clear(); - } - - this->mfem_mesh = new mfem::Mesh( this->dim, - this->numTotalNodes, - this->numTotalElements ); - - // add mortar elements and vertices. Not sure if order of adding - // elements matters, but adding vertices should probably correspond - // to the global contiguous id system - int mConn[ this->numNodesPerElement ]; - for (int iel=0; ielnumMortarElements; ++iel) - { - for (int idx=0; idxnumNodesPerElement; ++idx) - { - int index = iel * this->numNodesPerElement + idx; - mConn[ idx ] = this->elConn1[ index ]; + SLIC_ERROR_IF( !this->mesh_constructed, "TestMesh::setupMfemMesh(): " + << "test mesh must be constructed prior to calling this routine." ); + + SLIC_ERROR_IF( this->dim != 3, "TestMesh::setupMfemMesh(): Mfem meshes of dimension, " + << this->dim << ", are not supported at this time." ); + + // construct new mfem mesh + if ( this->mfem_mesh != nullptr ) { + this->mfem_mesh->Clear(); + } + + this->mfem_mesh = new mfem::Mesh( this->dim, this->numTotalNodes, this->numTotalElements ); + + // add mortar elements and vertices. Not sure if order of adding + // elements matters, but adding vertices should probably correspond + // to the global contiguous id system + int mConn[this->numNodesPerElement]; + for ( int iel = 0; iel < this->numMortarElements; ++iel ) { + for ( int idx = 0; idx < this->numNodesPerElement; ++idx ) { + int index = iel * this->numNodesPerElement + idx; + mConn[idx] = this->elConn1[index]; + } + switch ( this->cellType ) { + case LINEAR_TRIANGLE: { + this->mfem_mesh->AddTet( &mConn[0] ); + break; } - switch (this->cellType) - { - case LINEAR_TRIANGLE: - { - this->mfem_mesh->AddTet( &mConn[0] ); - break; - } - case LINEAR_QUAD: - { - this->mfem_mesh->AddHex( &mConn[0] ); - break; - } - default: - { - SLIC_ERROR("Element type not supported for creating mfem mesh from test mesh."); - } - } // end switch on surface element type - } // end loop over mortar elements - - for (int i=0; inumMortarNodes; ++i) - { - RealT vert[3] = {0., 0., 0.}; - vert[ 0 ] = this->x[ i ]; - vert[ 1 ] = this->y[ i ]; - vert[ 2 ] = this->z[ i ]; - - this->mfem_mesh->AddVertex( &vert[0] ); - } - - // add nonmortar elements and vertices. Not sure if order of adding - // elements matters, but adding vertices should probably correspond - // to the global contiguous id system - int sConn[ this->numNodesPerElement ]; - for (int iel=0; ielnumNonmortarElements; ++iel) - { - for (int idx=0; idxnumNodesPerElement; ++idx) - { - int index = iel * this->numNodesPerElement + idx; - sConn[ idx ] = this->elConn2[ index ]; + case LINEAR_QUAD: { + this->mfem_mesh->AddHex( &mConn[0] ); + break; } - switch (this->cellType) - { - case LINEAR_TRIANGLE: - { - this->mfem_mesh->AddTet( &sConn[0] ); - break; - } - case LINEAR_QUAD: - { - this->mfem_mesh->AddHex( &sConn[0] ); - break; - } - default: - { - SLIC_ERROR("Element type not supported for creating mfem mesh from test mesh."); - } - } // end switch on surface element type - } // end loop over nonmortar elements - - for (int i=0; inumNonmortarNodes; ++i) - { - RealT vert[3] = {0., 0., 0.}; - int offset = this->numMortarNodes; - vert[ 0 ] = this->x[ offset + i ]; - vert[ 1 ] = this->y[ offset + i ]; - vert[ 2 ] = this->z[ offset + i ]; - - this->mfem_mesh->AddVertex( &vert[0] ); - } - - int gen_edges = 0; // mfem default - int refine = 0; // mfem default - switch (this->cellType) - { - case LINEAR_TRIANGLE: - { - this->mfem_mesh->FinalizeTetMesh( gen_edges, refine, fix_orientation ); - break; + default: { + SLIC_ERROR( "Element type not supported for creating mfem mesh from test mesh." ); } - case LINEAR_QUAD: - { - this->mfem_mesh->FinalizeHexMesh( gen_edges, refine, fix_orientation ); - break; + } // end switch on surface element type + } // end loop over mortar elements + + for ( int i = 0; i < this->numMortarNodes; ++i ) { + RealT vert[3] = { 0., 0., 0. }; + vert[0] = this->x[i]; + vert[1] = this->y[i]; + vert[2] = this->z[i]; + + this->mfem_mesh->AddVertex( &vert[0] ); + } + + // add nonmortar elements and vertices. Not sure if order of adding + // elements matters, but adding vertices should probably correspond + // to the global contiguous id system + int sConn[this->numNodesPerElement]; + for ( int iel = 0; iel < this->numNonmortarElements; ++iel ) { + for ( int idx = 0; idx < this->numNodesPerElement; ++idx ) { + int index = iel * this->numNodesPerElement + idx; + sConn[idx] = this->elConn2[index]; + } + switch ( this->cellType ) { + case LINEAR_TRIANGLE: { + this->mfem_mesh->AddTet( &sConn[0] ); + break; } - default: - { - // no-op - break; + case LINEAR_QUAD: { + this->mfem_mesh->AddHex( &sConn[0] ); + break; } - } // end switch on surface cell type - -} // end setupMfemMesh() - + default: { + SLIC_ERROR( "Element type not supported for creating mfem mesh from test mesh." ); + } + } // end switch on surface element type + } // end loop over nonmortar elements + + for ( int i = 0; i < this->numNonmortarNodes; ++i ) { + RealT vert[3] = { 0., 0., 0. }; + int offset = this->numMortarNodes; + vert[0] = this->x[offset + i]; + vert[1] = this->y[offset + i]; + vert[2] = this->z[offset + i]; + + this->mfem_mesh->AddVertex( &vert[0] ); + } + + int gen_edges = 0; // mfem default + int refine = 0; // mfem default + switch ( this->cellType ) { + case LINEAR_TRIANGLE: { + this->mfem_mesh->FinalizeTetMesh( gen_edges, refine, fix_orientation ); + break; + } + case LINEAR_QUAD: { + this->mfem_mesh->FinalizeHexMesh( gen_edges, refine, fix_orientation ); + break; + } + default: { + // no-op + break; + } + } // end switch on surface cell type + +} // end setupMfemMesh() //------------------------------------------------------------------------------ -void TestMesh::computeEquilibriumJacobian( mfem::SparseMatrix* A, - RealT const nu, RealT const youngs ) +void TestMesh::computeEquilibriumJacobian( mfem::SparseMatrix* A, RealT const nu, RealT const youngs ) { - SLIC_ERROR_IF( this->mfem_mesh == nullptr, - "TestMesh::computeEquilibriumJacobian(): must call setupMfemMesh() " << - "prior to calling this routine." ); + SLIC_ERROR_IF( this->mfem_mesh == nullptr, "TestMesh::computeEquilibriumJacobian(): must call setupMfemMesh() " + << "prior to calling this routine." ); + + // define the FE collection and finite element space + mfem::FiniteElementSpace* fes{ nullptr }; + int order = 1; // hard coded for linear elements + mfem::H1_FECollection fe_coll( order, this->dim ); + fes = new mfem::FiniteElementSpace( this->mfem_mesh, &fe_coll, this->dim ); - // define the FE collection and finite element space - mfem::FiniteElementSpace * fes { nullptr }; - int order = 1; // hard coded for linear elements - mfem::H1_FECollection fe_coll( order, this->dim ); - fes = new mfem::FiniteElementSpace( this->mfem_mesh, &fe_coll, this->dim ); - - // compute 1st Lame parameter from Youngs modulus and Poisson's ratio - // (required for MFEM integrator) - RealT lambda_val { (youngs * nu) / ((1. + nu) * (1. - 2. * nu)) }; + // compute 1st Lame parameter from Youngs modulus and Poisson's ratio + // (required for MFEM integrator) + RealT lambda_val{ ( youngs * nu ) / ( ( 1. + nu ) * ( 1. - 2. * nu ) ) }; - // compute shear modulus from Young's Modulus and Poisson's ratio - // (required for MFEM integrator) - RealT mu_val { youngs / (2.*(1.+nu)) }; - mfem::ConstantCoefficient lambda(lambda_val); - mfem::ConstantCoefficient mu(mu_val); + // compute shear modulus from Young's Modulus and Poisson's ratio + // (required for MFEM integrator) + RealT mu_val{ youngs / ( 2. * ( 1. + nu ) ) }; + mfem::ConstantCoefficient lambda( lambda_val ); + mfem::ConstantCoefficient mu( mu_val ); - // instantiate elasticity integrator - mfem::ElasticityIntegrator elastInteg( lambda, mu ); + // instantiate elasticity integrator + mfem::ElasticityIntegrator elastInteg( lambda, mu ); - // compute element contributions on the mesh and sum them into the - // input sparse matrix - computeElementJacobianContributions( A, &elastInteg, fes ); + // compute element contributions on the mesh and sum them into the + // input sparse matrix + computeElementJacobianContributions( A, &elastInteg, fes ); - delete fes; + delete fes; -} // end TestMesh::computeEquilibriumJacobian() +} // end TestMesh::computeEquilibriumJacobian() //------------------------------------------------------------------------------ -void TestMesh::computeEquilibriumJacobian( mfem::SparseMatrix * const A, - mfem::ElasticityIntegrator * eInteg, - mfem::FiniteElementSpace * fes ) +void TestMesh::computeEquilibriumJacobian( mfem::SparseMatrix* const A, mfem::ElasticityIntegrator* eInteg, + mfem::FiniteElementSpace* fes ) { - // compute element contributions on the mesh and sum them into the - // input sparse matrix - computeElementJacobianContributions( A, eInteg, fes ); + // compute element contributions on the mesh and sum them into the + // input sparse matrix + computeElementJacobianContributions( A, eInteg, fes ); -} // end TestMesh::computeEquilibriumJacobian() +} // end TestMesh::computeEquilibriumJacobian() //------------------------------------------------------------------------------ -void TestMesh::computeElementJacobianContributions( mfem::SparseMatrix * const A, - mfem::ElasticityIntegrator * eInt, - mfem::FiniteElementSpace * fe_space, - bool matrixDebug ) +void TestMesh::computeElementJacobianContributions( mfem::SparseMatrix* const A, mfem::ElasticityIntegrator* eInt, + mfem::FiniteElementSpace* fe_space, bool matrixDebug ) { - SLIC_ERROR_IF( A == nullptr, "TestMesh::computeElementJacobianContributions(): " << - "input pointer to sparse matrix is null." ); - SLIC_ERROR_IF( eInt == nullptr, "TestMesh::computeElementJacobianContributions(): " << - "input pointer to elasticity integrator is null." ); - SLIC_ERROR_IF( fe_space == nullptr, "TestMesh::computeElementJacobianContributions(): " << - "input pointer to finite element space is null." ); - - mfem::ElementTransformation *T; - mfem::DenseMatrix elmat; - const mfem::FiniteElement *fe; - - // loop over finite elements - for (int idel=0; idelGetNE(); ++idel) - { - fe = fe_space->GetFE(idel); - T = fe_space->GetElementTransformation(idel); - - eInt->AssembleElementMatrix( *fe, *T, elmat ); - - if (matrixDebug) - { - // print out elmat for debugging - std::ofstream matrix; - matrix.setf(std::ios::scientific); - matrix.precision(2); - std::ostringstream suffix_matrix; - suffix_matrix << "el_" << idel << ".txt"; - matrix.open("matrix_" + suffix_matrix.str()); - - int numRows = elmat.NumRows(); - int numCols = elmat.NumCols(); - for (int i=0; iGetNE(); ++idel ) { + fe = fe_space->GetFE( idel ); + T = fe_space->GetElementTransformation( idel ); + + eInt->AssembleElementMatrix( *fe, *T, elmat ); + + if ( matrixDebug ) { + // print out elmat for debugging + std::ofstream matrix; + matrix.setf( std::ios::scientific ); + matrix.precision( 2 ); + std::ostringstream suffix_matrix; + suffix_matrix << "el_" << idel << ".txt"; + matrix.open( "matrix_" + suffix_matrix.str() ); + + int numRows = elmat.NumRows(); + int numCols = elmat.NumCols(); + for ( int i = 0; i < numRows; ++i ) { + for ( int j = 0; j < numCols; ++j ) { + RealT val = elmat( i, j ); + matrix << val << " "; + } + matrix << "\n"; } - // sum contributions into global jacobian input/output argument - int * el_conn; - for (int i=0; iGetDof(); ++i) - { - for (int j=0; jGetDof(); ++j) - { - // get global indices - if ( idel < this->numMortarElements ) - { - el_conn = this->elConn1 + idel * fe->GetDof(); - } - else - { - el_conn = this->elConn2 + (idel - this->numMortarElements) * fe->GetDof(); - } - - int glb_i = fe->GetDim() * el_conn[i]; - int glb_j = fe->GetDim() * el_conn[j]; - - // add the 3x3 block for the (i,j) pair - for (int m=0; mGetDim(); ++m) - { - for (int n=0; nGetDim(); ++n) - { - // get local element indices - int el_m = fe->GetDof() * m; - int el_n = fe->GetDof() * n; - - // keep in mind the local element stiffness matrix is stacked, x, then y, and - // then z contributions - A->Add(glb_i+m, glb_j+n, elmat(el_m+i,el_n+j)); - } - } - - } // end loop over column dofs - } // end loop over row dofs - } // end of loop over elements -} // end TestMesh::computeElementJacobianContributions() + matrix.close(); + } + + // sum contributions into global jacobian input/output argument + int* el_conn; + for ( int i = 0; i < fe->GetDof(); ++i ) { + for ( int j = 0; j < fe->GetDof(); ++j ) { + // get global indices + if ( idel < this->numMortarElements ) { + el_conn = this->elConn1 + idel * fe->GetDof(); + } else { + el_conn = this->elConn2 + ( idel - this->numMortarElements ) * fe->GetDof(); + } + + int glb_i = fe->GetDim() * el_conn[i]; + int glb_j = fe->GetDim() * el_conn[j]; + + // add the 3x3 block for the (i,j) pair + for ( int m = 0; m < fe->GetDim(); ++m ) { + for ( int n = 0; n < fe->GetDim(); ++n ) { + // get local element indices + int el_m = fe->GetDof() * m; + int el_n = fe->GetDof() * n; + + // keep in mind the local element stiffness matrix is stacked, x, then y, and + // then z contributions + A->Add( glb_i + m, glb_j + n, elmat( el_m + i, el_n + j ) ); + } + } + + } // end loop over column dofs + } // end loop over row dofs + } // end of loop over elements +} // end TestMesh::computeElementJacobianContributions() //------------------------------------------------------------------------------ -void TestMesh::tribolMatrixToSystemMatrix( mfem::DenseMatrix * const ATribol, - mfem::SparseMatrix * const ASystem ) +void TestMesh::tribolMatrixToSystemMatrix( mfem::DenseMatrix* const ATribol, mfem::SparseMatrix* const ASystem ) { - - SLIC_ERROR_IF( ATribol == nullptr, "TestMesh::tribolMatrixToSystemMatrix(): " - << "ATribol pointer is null." ); - - SLIC_ERROR_IF( ATribol == nullptr, "TestMesh::tribolMatrixToSystemMatrix(): " - << "ASystem pointer is null." ); - - int solveSize = this->dim * this->numTotalNodes + this->numNonmortarSurfaceNodes; - - // initialize matrix - for (int i=0; iAdd(i,j, 0.); - } - } // end of matrix initialization - - //////////////////////////// - // assemble matrix blocks // - //////////////////////////// - - // compose upper diagonal block - for (int i=0; idim * this->numTotalNodes; ++i) - { - for (int j=0; jdim * this->numTotalNodes; ++j) - { - ASystem->Add(i,j, (*ATribol)(i,j)); - } - } // end of upper diagonal block loop - - // compose off-diagonal blocks - for (int i=0; inumNonmortarSurfaceNodes; ++i) - { - int newOffset = this->dim * this->numTotalNodes; - int oldOffset = this->dim * this->numTotalNodes + - this->numMortarNodes; - int col_id = newOffset + i; - int row_id = col_id; - - for (int j=0; jdim * this->numTotalNodes; ++j) - { - // assemble upper-right diagonal block first - ASystem->Set( j, col_id, (*ATribol)( j, oldOffset + i )); - - // assemble lower left diagonal block - ASystem->Set( row_id, j, (*ATribol)( oldOffset + i, j )); - } - } // end of off-diagonal block loop - - // KEEP this as debug code for unit tests, but in general - // there should be no contributions - // TEST: compose bottom-diagonal block diagonal elements -// for (int i=0; im_mesh.numNonmortarSurfaceNodes; ++i) -// { -// int newOffset = this->m_mesh.dim * this->m_mesh.numTotalNodes; -// int oldOffset = this->m_mesh.dim * this->m_mesh.numTotalNodes + -// this->m_mesh.numMortarNodes; -// int idx = newOffset + i; -// ASystem->Add(idx,idx, (*ATribol)( oldOffset + i, oldOffset + i )); -// } - -} // end TestMesh::tribolMatrixToSystemMatrix() + SLIC_ERROR_IF( ATribol == nullptr, "TestMesh::tribolMatrixToSystemMatrix(): " + << "ATribol pointer is null." ); + + SLIC_ERROR_IF( ATribol == nullptr, "TestMesh::tribolMatrixToSystemMatrix(): " + << "ASystem pointer is null." ); + + int solveSize = this->dim * this->numTotalNodes + this->numNonmortarSurfaceNodes; + + // initialize matrix + for ( int i = 0; i < solveSize; ++i ) { + for ( int j = 0; j < solveSize; ++j ) { + ASystem->Add( i, j, 0. ); + } + } // end of matrix initialization + + //////////////////////////// + // assemble matrix blocks // + //////////////////////////// + + // compose upper diagonal block + for ( int i = 0; i < this->dim * this->numTotalNodes; ++i ) { + for ( int j = 0; j < this->dim * this->numTotalNodes; ++j ) { + ASystem->Add( i, j, ( *ATribol )( i, j ) ); + } + } // end of upper diagonal block loop + + // compose off-diagonal blocks + for ( int i = 0; i < this->numNonmortarSurfaceNodes; ++i ) { + int newOffset = this->dim * this->numTotalNodes; + int oldOffset = this->dim * this->numTotalNodes + this->numMortarNodes; + int col_id = newOffset + i; + int row_id = col_id; + + for ( int j = 0; j < this->dim * this->numTotalNodes; ++j ) { + // assemble upper-right diagonal block first + ASystem->Set( j, col_id, ( *ATribol )( j, oldOffset + i ) ); + + // assemble lower left diagonal block + ASystem->Set( row_id, j, ( *ATribol )( oldOffset + i, j ) ); + } + } // end of off-diagonal block loop + + // KEEP this as debug code for unit tests, but in general + // there should be no contributions + // TEST: compose bottom-diagonal block diagonal elements + // for (int i=0; im_mesh.numNonmortarSurfaceNodes; ++i) + // { + // int newOffset = this->m_mesh.dim * this->m_mesh.numTotalNodes; + // int oldOffset = this->m_mesh.dim * this->m_mesh.numTotalNodes + + // this->m_mesh.numMortarNodes; + // int idx = newOffset + i; + // ASystem->Add(idx,idx, (*ATribol)( oldOffset + i, oldOffset + i )); + // } + +} // end TestMesh::tribolMatrixToSystemMatrix() //------------------------------------------------------------------------------ -void TestMesh::getGapEvals( RealT * const v ) +void TestMesh::getGapEvals( RealT* const v ) { - SLIC_ERROR_IF( v == nullptr, "TestMesh::getGapEvals(): input pointer is null." ); - int presDofCtr = 0; - for (int a=0; anumNonmortarNodes; ++a) - { - // pressure dofs - int presOffset = this->dim * this->numTotalNodes; - if ( this->presDofs2[a] >= 0) // note: pressure dofs ordered sequentially - { - v[ presOffset + presDofCtr ] = - -this->gaps[ this->numMortarNodes + a ]; // we have negative rhs - ++presDofCtr; - } - } -} // end TestMesh::getGapEvals() + SLIC_ERROR_IF( v == nullptr, "TestMesh::getGapEvals(): input pointer is null." ); + int presDofCtr = 0; + for ( int a = 0; a < this->numNonmortarNodes; ++a ) { + // pressure dofs + int presOffset = this->dim * this->numTotalNodes; + if ( this->presDofs2[a] >= 0 ) // note: pressure dofs ordered sequentially + { + v[presOffset + presDofCtr] = -this->gaps[this->numMortarNodes + a]; // we have negative rhs + ++presDofCtr; + } + } +} // end TestMesh::getGapEvals() //------------------------------------------------------------------------------ -void TestMesh::enforceDirichletBCs( mfem::SparseMatrix * const A, - mfem::Vector * const b, - bool contact ) +void TestMesh::enforceDirichletBCs( mfem::SparseMatrix* const A, mfem::Vector* const b, bool contact ) { - SLIC_ERROR_IF( A == nullptr, "TestMesh::enforceDirichletBCs(): " << - "input pointer to sparse matrix is null." ); - SLIC_ERROR_IF( b == nullptr, "TestMesh::enforceDirichletBCs(): " << - "input pointer to rhs vector, b, is null." ); - - int * dirBCX, * dirBCY, * dirBCZ, * presDofs; - RealT *dirValX, *dirValY, *dirValZ; - int numBlkNodes; - - int presCtr = 0; - // loop over each mesh block and then over the number of block nodes, - // enforcing BCs - for (int iblk=0; iblk<2; ++iblk) - { - // point to mortar data if iblk == 0 - if (iblk == 0) - { - dirBCX = this->dirNodesX1; - dirBCY = this->dirNodesY1; - dirBCZ = this->dirNodesZ1; - dirValX = this->iDirValX1; - dirValY = this->iDirValY1; - dirValZ = this->iDirValZ1; - presDofs = this->presDofs1; - numBlkNodes = this->numMortarNodes; + SLIC_ERROR_IF( A == nullptr, "TestMesh::enforceDirichletBCs(): " + << "input pointer to sparse matrix is null." ); + SLIC_ERROR_IF( b == nullptr, "TestMesh::enforceDirichletBCs(): " + << "input pointer to rhs vector, b, is null." ); + + int *dirBCX, *dirBCY, *dirBCZ, *presDofs; + RealT *dirValX, *dirValY, *dirValZ; + int numBlkNodes; + + int presCtr = 0; + // loop over each mesh block and then over the number of block nodes, + // enforcing BCs + for ( int iblk = 0; iblk < 2; ++iblk ) { + // point to mortar data if iblk == 0 + if ( iblk == 0 ) { + dirBCX = this->dirNodesX1; + dirBCY = this->dirNodesY1; + dirBCZ = this->dirNodesZ1; + dirValX = this->iDirValX1; + dirValY = this->iDirValY1; + dirValZ = this->iDirValZ1; + presDofs = this->presDofs1; + numBlkNodes = this->numMortarNodes; + } + // point to nonmortar data if iblk != 0 + else { + dirBCX = this->dirNodesX2; + dirBCY = this->dirNodesY2; + dirBCZ = this->dirNodesZ2; + dirValX = this->iDirValX2; + dirValY = this->iDirValY2; + dirValZ = this->iDirValZ2; + presDofs = this->presDofs2; + numBlkNodes = this->numNonmortarNodes; + } + + // loop over all block nodes. + for ( int m = 0; m < numBlkNodes; ++m ) { + // row ids for homogeneous dirichlet BCs + if ( dirBCX[m] >= 0 ) { + int bcRowIdX = this->dim * dirBCX[m]; + RealT dirBCValX = dirValX[m]; + A->EliminateRowCol( bcRowIdX, dirBCValX, *b ); } - // point to nonmortar data if iblk != 0 - else - { - dirBCX = this->dirNodesX2; - dirBCY = this->dirNodesY2; - dirBCZ = this->dirNodesZ2; - dirValX = this->iDirValX2; - dirValY = this->iDirValY2; - dirValZ = this->iDirValZ2; - presDofs = this->presDofs2; - numBlkNodes = this->numNonmortarNodes; + + if ( dirBCY[m] >= 0 ) { + int bcRowIdY = this->dim * dirBCY[m] + 1; + RealT dirBCValY = dirValY[m]; + A->EliminateRowCol( bcRowIdY, dirBCValY, *b ); } - // loop over all block nodes. - for (int m=0; m= 0) - { - int bcRowIdX = this->dim * dirBCX[m]; - RealT dirBCValX = dirValX[m]; - A->EliminateRowCol( bcRowIdX, dirBCValX, *b ); - } - - if (dirBCY[m] >= 0) - { - int bcRowIdY = this->dim * dirBCY[m]+1; - RealT dirBCValY = dirValY[m]; - A->EliminateRowCol( bcRowIdY, dirBCValY, *b ); - } - - if (dirBCZ[m] >= 0) - { - int bcRowIdZ = this->dim * dirBCZ[m]+2; - RealT dirBCValZ = dirValZ[m]; - A->EliminateRowCol( bcRowIdZ, dirBCValZ, *b ); - } - - // KEEP THIS CODE - this is used to test pressure rows etc. - // TEST: row ids for pressure rows where we will specify - // homogeneous dirichlet BCs for the pressure Lagrange multipliers - if (!contact) - { - if (presDofs[m] >= 0) - { - // the following index assumes that all pressure dofs, which have - // contiguous integer Ids, are going to be zeroed out - int testPresRowId = this->dim * this->numTotalNodes - + presCtr; - - if (testPresRowId > A->NumRows()) - { - SLIC_ERROR("Pressure dof > number of matrix rows."); - } - A->EliminateRowCol( testPresRowId, 0., *b ); - ++presCtr; - } - } // end if (!contact) - - } // end loop over number of nodes - } // end loop over mesh blocks -} // end TestMesh::enforceDirichletBCs() + if ( dirBCZ[m] >= 0 ) { + int bcRowIdZ = this->dim * dirBCZ[m] + 2; + RealT dirBCValZ = dirValZ[m]; + A->EliminateRowCol( bcRowIdZ, dirBCValZ, *b ); + } + + // KEEP THIS CODE - this is used to test pressure rows etc. + // TEST: row ids for pressure rows where we will specify + // homogeneous dirichlet BCs for the pressure Lagrange multipliers + if ( !contact ) { + if ( presDofs[m] >= 0 ) { + // the following index assumes that all pressure dofs, which have + // contiguous integer Ids, are going to be zeroed out + int testPresRowId = this->dim * this->numTotalNodes + presCtr; + + if ( testPresRowId > A->NumRows() ) { + SLIC_ERROR( "Pressure dof > number of matrix rows." ); + } + A->EliminateRowCol( testPresRowId, 0., *b ); + ++presCtr; + } + } // end if (!contact) + + } // end loop over number of nodes + } // end loop over mesh blocks +} // end TestMesh::enforceDirichletBCs() //------------------------------------------------------------------------------ void TestMesh::testMeshToVtk( const std::string& dir, int cycle, RealT time ) { - std::ostringstream suffix_mesh; - suffix_mesh << std::setfill('0') << std::setw(7) << cycle << ".vtk"; - std::string f_name = axom::utilities::filesystem::joinPath( - dir, "meshTest3D_" + suffix_mesh.str() ); - - std::ofstream mesh; - mesh.setf(std::ios::scientific); - mesh.open(f_name.c_str()); - - mesh << "# vtk DataFile Version 3.0\n"; - mesh << "vtk output\n"; - mesh << "ASCII\n"; - mesh << "DATASET UNSTRUCTURED_GRID\n"; - - // Add the cycle and time to FieldData - mesh << "FIELD FieldData 2\n"; - mesh << "TIME 1 1 RealT\n"; - mesh << time << "\n"; - mesh << "CYCLE 1 1 int\n"; - mesh << cycle << "\n"; - - // print mesh vertices - mesh << "POINTS " << this->numTotalNodes << " float\n"; - for (int i=0; inumTotalNodes; ++i) - { - mesh << this->x[i] << " " - << this->y[i] << " " - << this->z[i] << std::endl; - } - - // print mesh element connectivity - mesh << "CELLS " << this->numTotalElements << " " - << this->numTotalElements+this->numTotalElements*this->numNodesPerElement << std::endl; - - for (int i=0; inumMortarElements; ++i) - { - mesh << this->numNodesPerElement; - for (int a=0; anumNodesPerElement; ++a) - { - int id = this->numNodesPerElement * i + a; - mesh << " " << elConn1[id]; - } - mesh << std::endl; - } - - for (int i=0; inumNonmortarElements; ++i) - { - mesh << this->numNodesPerElement; - for (int a=0; anumNodesPerElement; ++a) - { - int id = this->numNodesPerElement * i + a; - mesh << " " << elConn2[id]; - } - mesh << std::endl; - } - - // specify integer id for each cell type. - mesh << "CELL_TYPES " << this->numTotalElements << std::endl; - - int element_id; - switch (this->numNodesPerElement) - { - case 8: - element_id = 12; // vtk 8-node hexahedron - break; - case 4: - element_id = 10; // vtk 4-node tetra - break; - default : - SLIC_ERROR("TestMesh::testMeshToVtk(): element type not supported by vtk."); - } - - for (int i=0; inumTotalElements; ++i) - { - mesh << element_id << std::endl; - } - - mesh.close(); - -} // end TestMesh::testMeshToVtk() + std::ostringstream suffix_mesh; + suffix_mesh << std::setfill( '0' ) << std::setw( 7 ) << cycle << ".vtk"; + std::string f_name = axom::utilities::filesystem::joinPath( dir, "meshTest3D_" + suffix_mesh.str() ); + + std::ofstream mesh; + mesh.setf( std::ios::scientific ); + mesh.open( f_name.c_str() ); + + mesh << "# vtk DataFile Version 3.0\n"; + mesh << "vtk output\n"; + mesh << "ASCII\n"; + mesh << "DATASET UNSTRUCTURED_GRID\n"; + + // Add the cycle and time to FieldData + mesh << "FIELD FieldData 2\n"; + mesh << "TIME 1 1 RealT\n"; + mesh << time << "\n"; + mesh << "CYCLE 1 1 int\n"; + mesh << cycle << "\n"; + + // print mesh vertices + mesh << "POINTS " << this->numTotalNodes << " float\n"; + for ( int i = 0; i < this->numTotalNodes; ++i ) { + mesh << this->x[i] << " " << this->y[i] << " " << this->z[i] << std::endl; + } + + // print mesh element connectivity + mesh << "CELLS " << this->numTotalElements << " " + << this->numTotalElements + this->numTotalElements * this->numNodesPerElement << std::endl; + + for ( int i = 0; i < this->numMortarElements; ++i ) { + mesh << this->numNodesPerElement; + for ( int a = 0; a < this->numNodesPerElement; ++a ) { + int id = this->numNodesPerElement * i + a; + mesh << " " << elConn1[id]; + } + mesh << std::endl; + } + + for ( int i = 0; i < this->numNonmortarElements; ++i ) { + mesh << this->numNodesPerElement; + for ( int a = 0; a < this->numNodesPerElement; ++a ) { + int id = this->numNodesPerElement * i + a; + mesh << " " << elConn2[id]; + } + mesh << std::endl; + } + + // specify integer id for each cell type. + mesh << "CELL_TYPES " << this->numTotalElements << std::endl; + + int element_id; + switch ( this->numNodesPerElement ) { + case 8: + element_id = 12; // vtk 8-node hexahedron + break; + case 4: + element_id = 10; // vtk 4-node tetra + break; + default: + SLIC_ERROR( "TestMesh::testMeshToVtk(): element type not supported by vtk." ); + } + + for ( int i = 0; i < this->numTotalElements; ++i ) { + mesh << element_id << std::endl; + } + + mesh.close(); + +} // end TestMesh::testMeshToVtk() //------------------------------------------------------------------------------ -} // end of namespace "tribol" +} // namespace tribol -namespace mfem_ext -{ +namespace mfem_ext { -CentralDiffSolver::CentralDiffSolver(const mfem::Array& bc_vdofs_) -: bc_vdofs { bc_vdofs_ }, - first_step { true } -{} +CentralDiffSolver::CentralDiffSolver( const mfem::Array& bc_vdofs_ ) : bc_vdofs{ bc_vdofs_ }, first_step{ true } {} -void CentralDiffSolver::Step(mfem::Vector& x, mfem::Vector& dxdt, double& t, double& dt) +void CentralDiffSolver::Step( mfem::Vector& x, mfem::Vector& dxdt, double& t, double& dt ) { - // acceleration at t - f->SetTime(t); - if (first_step) - { - accel.SetSize(x.Size()); - // update acceleration given displacement, velocity using linked - // SecondOrderTimeDependentOperator (set using Init() method) - f->Mult(x, dxdt, accel); - first_step = false; - } - - // velocity at t + dt/2 - dxdt.Add(0.5*dt, accel); - - // set homogeneous velocity BC at t + dt/2 - SetHomogeneousBC(dxdt); - - // set displacement at t + dt - x.Add(dt, dxdt); - - // acceleration at t + dt - f->SetTime(t + dt); - // update acceleration given displacement, velocity using linked - // SecondOrderTimeDependentOperator (set using Init() method) - f->Mult(x, dxdt, accel); - - // velocity at t + dt - dxdt.Add(0.5*dt, accel); + // acceleration at t + f->SetTime( t ); + if ( first_step ) { + accel.SetSize( x.Size() ); + // update acceleration given displacement, velocity using linked + // SecondOrderTimeDependentOperator (set using Init() method) + f->Mult( x, dxdt, accel ); + first_step = false; + } + + // velocity at t + dt/2 + dxdt.Add( 0.5 * dt, accel ); + + // set homogeneous velocity BC at t + dt/2 + SetHomogeneousBC( dxdt ); + + // set displacement at t + dt + x.Add( dt, dxdt ); + + // acceleration at t + dt + f->SetTime( t + dt ); + // update acceleration given displacement, velocity using linked + // SecondOrderTimeDependentOperator (set using Init() method) + f->Mult( x, dxdt, accel ); + + // velocity at t + dt + dxdt.Add( 0.5 * dt, accel ); } -void CentralDiffSolver::SetHomogeneousBC(mfem::Vector& dxdt) const +void CentralDiffSolver::SetHomogeneousBC( mfem::Vector& dxdt ) const { - for (auto bc_vdof : bc_vdofs) - { - dxdt[bc_vdof] = 0.0; - } + for ( auto bc_vdof : bc_vdofs ) { + dxdt[bc_vdof] = 0.0; + } } #ifdef TRIBOL_USE_MPI -ExplicitMechanics::ExplicitMechanics( - mfem::ParFiniteElementSpace& fespace, - mfem::Coefficient& rho, - mfem::Coefficient& lambda, - mfem::Coefficient& mu -) -: f_ext { &fespace }, - elasticity { &fespace }, - inv_lumped_mass { fespace.GetVSize() } +ExplicitMechanics::ExplicitMechanics( mfem::ParFiniteElementSpace& fespace, mfem::Coefficient& rho, + mfem::Coefficient& lambda, mfem::Coefficient& mu ) + : f_ext{ &fespace }, elasticity{ &fespace }, inv_lumped_mass{ fespace.GetVSize() } { - // create inverse lumped mass matrix; store as a vector - mfem::ParBilinearForm mass { &fespace }; - mass.AddDomainIntegrator(new mfem::VectorMassIntegrator(rho)); - mass.Assemble(); - mfem::Vector ones {fespace.GetVSize()}; - ones = 1.0; - mass.SpMat().Mult(ones, inv_lumped_mass); - mfem::Vector mass_true(fespace.GetTrueVSize()); - const Operator& P = *fespace.GetProlongationMatrix(); - P.MultTranspose(inv_lumped_mass, mass_true); - for (int i {0}; i < mass_true.Size(); ++i) - { - mass_true[i] = 1.0 / mass_true[i]; - } - P.Mult(mass_true, inv_lumped_mass); - - // create elasticity stiffness matrix - elasticity.AddDomainIntegrator(new mfem::ElasticityIntegrator(lambda, mu)); - elasticity.Assemble(); + // create inverse lumped mass matrix; store as a vector + mfem::ParBilinearForm mass{ &fespace }; + mass.AddDomainIntegrator( new mfem::VectorMassIntegrator( rho ) ); + mass.Assemble(); + mfem::Vector ones{ fespace.GetVSize() }; + ones = 1.0; + mass.SpMat().Mult( ones, inv_lumped_mass ); + mfem::Vector mass_true( fespace.GetTrueVSize() ); + const Operator& P = *fespace.GetProlongationMatrix(); + P.MultTranspose( inv_lumped_mass, mass_true ); + for ( int i{ 0 }; i < mass_true.Size(); ++i ) { + mass_true[i] = 1.0 / mass_true[i]; + } + P.Mult( mass_true, inv_lumped_mass ); + + // create elasticity stiffness matrix + elasticity.AddDomainIntegrator( new mfem::ElasticityIntegrator( lambda, mu ) ); + elasticity.Assemble(); } -void ExplicitMechanics::Mult( - const mfem::Vector& u, - const mfem::Vector& AXOM_UNUSED_PARAM(dudt), - mfem::Vector& a -) const +void ExplicitMechanics::Mult( const mfem::Vector& u, const mfem::Vector& AXOM_UNUSED_PARAM( dudt ), + mfem::Vector& a ) const { - mfem::Vector f { u.Size() }; - f = 0.0; - - mfem::Vector f_int { f.Size() }; - elasticity.Mult(u, f_int); - f.Add(-1.0, f_int); - - f.Add(1.0, f_ext); - - // sum forces over ranks - auto& fespace = *elasticity.ParFESpace(); - const Operator& P = *fespace.GetProlongationMatrix(); - mfem::Vector f_true {fespace.GetTrueVSize()}; - P.MultTranspose(f, f_true); - P.Mult(f_true, f); - - for (int i {0}; i < inv_lumped_mass.Size(); ++i) - { - a[i] = inv_lumped_mass[i] * f[i]; - } + mfem::Vector f{ u.Size() }; + f = 0.0; + + mfem::Vector f_int{ f.Size() }; + elasticity.Mult( u, f_int ); + f.Add( -1.0, f_int ); + + f.Add( 1.0, f_ext ); + + // sum forces over ranks + auto& fespace = *elasticity.ParFESpace(); + const Operator& P = *fespace.GetProlongationMatrix(); + mfem::Vector f_true{ fespace.GetTrueVSize() }; + P.MultTranspose( f, f_true ); + P.Mult( f_true, f ); + + for ( int i{ 0 }; i < inv_lumped_mass.Size(); ++i ) { + a[i] = inv_lumped_mass[i] * f[i]; + } } #endif -} // end of namespace "mfem_ext" +} // namespace mfem_ext diff --git a/src/tribol/utils/TestUtils.hpp b/src/tribol/utils/TestUtils.hpp index a8759e99..6a8044fb 100644 --- a/src/tribol/utils/TestUtils.hpp +++ b/src/tribol/utils/TestUtils.hpp @@ -8,107 +8,103 @@ #include "tribol/common/Parameters.hpp" -//MFEM includes +// MFEM includes #include "mfem.hpp" #include "axom/config.hpp" - -namespace tribol -{ +namespace tribol { /// RAII struct to initialize and finalize MPI for tests and examples -struct SimpleMPIWrapper -{ - SimpleMPIWrapper(int argc, char* argv[]) - { - #ifdef TRIBOL_USE_MPI - MPI_Init(&argc, &argv); - #else - static_cast(argc); // elide warning about unused vars - static_cast(argv); - #endif - } - - ~SimpleMPIWrapper() - { - #ifdef TRIBOL_USE_MPI - MPI_Finalize(); - #endif - } +struct SimpleMPIWrapper { + SimpleMPIWrapper( int argc, char* argv[] ) + { +#ifdef TRIBOL_USE_MPI + MPI_Init( &argc, &argv ); +#else + static_cast( argc ); // elide warning about unused vars + static_cast( argv ); +#endif + } + + ~SimpleMPIWrapper() + { +#ifdef TRIBOL_USE_MPI + MPI_Finalize(); +#endif + } }; /*! * \brief Struct to hold control parameters for tests */ -struct TestControlParameters -{ - /// Constructor - TestControlParameters() : - penalty_ratio (false) - , constant_rate_penalty (false) - , percent_rate_penalty (false) - , rate_penalty (1.0) - , rate_penalty_ratio (0.0) - , const_penalty (1.0) - , enable_timestep_vote (false) - , timestep_pen_frac (0.30) - , timestep_scale (1.0) - {} - - ~TestControlParameters() - { - // no-op - } - - RealT dt {0.}; - RealT auto_contact_pen_frac {0.95}; - - // penalty control parameters - bool penalty_ratio; - bool constant_rate_penalty; - bool percent_rate_penalty; - RealT rate_penalty; - RealT rate_penalty_ratio; - RealT const_penalty; - bool enable_timestep_vote; - RealT timestep_pen_frac; - RealT timestep_scale; +struct TestControlParameters { + /// Constructor + TestControlParameters() + : penalty_ratio( false ), + constant_rate_penalty( false ), + percent_rate_penalty( false ), + rate_penalty( 1.0 ), + rate_penalty_ratio( 0.0 ), + const_penalty( 1.0 ), + enable_timestep_vote( false ), + timestep_pen_frac( 0.30 ), + timestep_scale( 1.0 ) + { + } + + ~TestControlParameters() + { + // no-op + } + + RealT dt{ 0. }; + RealT auto_contact_pen_frac{ 0.95 }; + + // penalty control parameters + bool penalty_ratio; + bool constant_rate_penalty; + bool percent_rate_penalty; + RealT rate_penalty; + RealT rate_penalty_ratio; + RealT const_penalty; + bool enable_timestep_vote; + RealT timestep_pen_frac; + RealT timestep_scale; }; /*! * \brief class to hold test mesh data */ -class TestMesh -{ -public: - /// constructor - TestMesh(); - - /// destructor - ~TestMesh(); - - /// clear function - void clear( bool keepCoords = false ///< option to keep nodal coordinate arrays for hex-to-tet mesh - ); - - /// performs tribol registration calls and calls tribol::update() - int tribolSetupAndUpdate( ContactMethod method, ///< contact method - EnforcementMethod enforcement, ///< constraint enforcement method - ContactModel model, ///< contact model - ContactCase contact_case, ///< contact case - bool visualization, ///< true if visualization - TestControlParameters & params ///< control parameters struct - ); - - /// performs tribol registration calls and calls tribol::update() using "simple" API - int simpleTribolSetupAndUpdate( ContactMethod method, ///< contact method - EnforcementMethod enforcement, ///< constraint enforcement method - ContactModel model, ///< contact model - ContactCase contact_case, ///< contact case - bool visualization, ///< true if visualization - TestControlParameters & params ///< control parameters struct - ); +class TestMesh { + public: + /// constructor + TestMesh(); + + /// destructor + ~TestMesh(); + + /// clear function + void clear( bool keepCoords = false ///< option to keep nodal coordinate arrays for hex-to-tet mesh + ); + + /// performs tribol registration calls and calls tribol::update() + int tribolSetupAndUpdate( ContactMethod method, ///< contact method + EnforcementMethod enforcement, ///< constraint enforcement method + ContactModel model, ///< contact model + ContactCase contact_case, ///< contact case + bool visualization, ///< true if visualization + TestControlParameters& params ///< control parameters struct + ); + + /// performs tribol registration calls and calls tribol::update() using "simple" API + int simpleTribolSetupAndUpdate( ContactMethod method, ///< contact method + EnforcementMethod enforcement, ///< constraint enforcement method + ContactModel model, ///< contact model + ContactCase contact_case, ///< contact case + bool visualization, ///< true if visualization + TestControlParameters& params ///< control parameters struct + ); /*! * \brief setups of a 3D contact hex mesh consisting of two blocks @@ -135,13 +131,10 @@ class TestMesh * \param [in] thetaNonmortar angle of rotation of non-z-plane vertices about z-axis * */ - void setupContactMeshHex( int numElemsX1, int numElemsY1, int numElemsZ1, - RealT xMin1, RealT yMin1, RealT zMin1, - RealT xMax1, RealT yMax1, RealT zMax1, - int numElemsX2, int numElemsY2, int numElemsZ2, - RealT xMin2, RealT yMin2, RealT zMin2, - RealT xMax2, RealT yMax2, RealT zMax2, - RealT thetaMortar, RealT thetaNonmortar ); + void setupContactMeshHex( int numElemsX1, int numElemsY1, int numElemsZ1, RealT xMin1, RealT yMin1, RealT zMin1, + RealT xMax1, RealT yMax1, RealT zMax1, int numElemsX2, int numElemsY2, int numElemsZ2, + RealT xMin2, RealT yMin2, RealT zMin2, RealT xMax2, RealT yMax2, RealT zMax2, + RealT thetaMortar, RealT thetaNonmortar ); /*! * \brief setups of a 3D contact tet mesh consisting of two blocks @@ -168,13 +161,10 @@ class TestMesh * \param [in] thetaNonmortar angle of rotation of non-z-plane vertices about z-axis * */ - void setupContactMeshTet( int numElemsX1, int numElemsY1, int numElemsZ1, - RealT xMin1, RealT yMin1, RealT zMin1, - RealT xMax1, RealT yMax1, RealT zMax1, - int numElemsX2, int numElemsY2, int numElemsZ2, - RealT xMin2, RealT yMin2, RealT zMin2, - RealT xMax2, RealT yMax2, RealT zMax2, - RealT thetaMortar, RealT thetaNonmortar ); + void setupContactMeshTet( int numElemsX1, int numElemsY1, int numElemsZ1, RealT xMin1, RealT yMin1, RealT zMin1, + RealT xMax1, RealT yMax1, RealT zMax1, int numElemsX2, int numElemsY2, int numElemsZ2, + RealT xMin2, RealT yMin2, RealT zMin2, RealT xMax2, RealT yMax2, RealT zMax2, + RealT thetaMortar, RealT thetaNonmortar ); /*! * \brief sets up an mfem mesh object representation of the original hex or tet test mesh @@ -182,15 +172,15 @@ class TestMesh * * \param [in] fix_orientation true will fix any orientation errors in original TestMesh * - * \note must call setupContactMeshHex() or setupContactMeshTet() to construct the test mesh + * \note must call setupContactMeshHex() or setupContactMeshTet() to construct the test mesh * prior to calling this routine * */ - void setupMfemMesh( bool fix_orientation = true ); + void setupMfemMesh( bool fix_orientation = true ); /*! - * \brief sets up the Dirichlet BC node ids and values for a single 3D mesh block - * for the contact PATCH TEST + * \brief sets up the Dirichlet BC node ids and values for a single 3D mesh block + * for the contact PATCH TEST * * \param [in] meshId mesh id for the given block * \param [in] numElemsX number of elements in the x-direction @@ -204,9 +194,8 @@ class TestMesh * contact enforcement. This is used in tribol/tests/tribol_mortar_pressure_sol.cpp * */ - void setupPatchTestDirichletBCs( IndexT mesh_id, int numElemsX, int numElemsY, int numElemsZ, - int nodeIdOffset, bool inHomogeneousGap, - RealT inHomogeneousZVal = 0. ); + void setupPatchTestDirichletBCs( IndexT mesh_id, int numElemsX, int numElemsY, int numElemsZ, int nodeIdOffset, + bool inHomogeneousGap, RealT inHomogeneousZVal = 0. ); /*! * \brief sets up pressure dof ids for a 3D nonmortar mesh block for PATCH TEST @@ -219,12 +208,12 @@ class TestMesh * \param [in] contact true if enforcing zero gap using contact enforcement * */ - void setupPatchTestPressureDofs( IndexT mesh_id, int numElemsX, int numElemsY, int numElemsZ, - int nodeIdOffset, bool contact ); + void setupPatchTestPressureDofs( IndexT mesh_id, int numElemsX, int numElemsY, int numElemsZ, int nodeIdOffset, + bool contact ); /*! * \brief allocates and sets velocity arrays - * + * * \param [in] valX x-velocity value to set velocity arrays * \param [in] valY y-velocity value to set velocity arrays * \param [in] valZ z-velocity value to set velocity arrays @@ -232,28 +221,28 @@ class TestMesh * \note in the future we should handle an array of velocity values * */ - void allocateAndSetVelocities( IndexT mesh_id, RealT valX, RealT valY, RealT valZ=0. ); + void allocateAndSetVelocities( IndexT mesh_id, RealT valX, RealT valY, RealT valZ = 0. ); /*! * \brief allocates and sets bulk modulus arrays on mesh - * - * \param [in] val single scalar bulk modulus + * + * \param [in] val single scalar bulk modulus * * \note in the future we should handle an array of values * */ - void allocateAndSetBulkModulus( IndexT mesh_id, RealT val ); + void allocateAndSetBulkModulus( IndexT mesh_id, RealT val ); /*! * \brief allocates and sets element thickness arrays on mesh - * + * * \param [in] val single scalar element thickness * - * \note this is suitable for a uniform mesh. In the future we + * \note this is suitable for a uniform mesh. In the future we * should handle an array of values * */ - void allocateAndSetElementThickness( IndexT mesh_id, RealT t ); + void allocateAndSetElementThickness( IndexT mesh_id, RealT t ); /*! * \brief wraps element Jacobian calculations for linear elasticity @@ -266,11 +255,10 @@ class TestMesh * \pre TestMesh::mfem_mesh != nullptr * \pre matrix A cannot have been finalized yet */ - void computeEquilibriumJacobian( mfem::SparseMatrix * const A, - RealT const nu, RealT const youngs ); + void computeEquilibriumJacobian( mfem::SparseMatrix* const A, RealT const nu, RealT const youngs ); /*! - * \brief wraps element Jacobian calculations for linear elasticity + * \brief wraps element Jacobian calculations for linear elasticity * * \param [in,out] A mfem sparse matrix data object where Jac. contributions are summed into * \param [in] eInteg mfem elasticity integrator @@ -281,9 +269,8 @@ class TestMesh * \pre TestMesh::mfem_mesh != nullptr * \pre matrix A cannot have been finalized yet */ - void computeEquilibriumJacobian( mfem::SparseMatrix * const A, - mfem::ElasticityIntegrator * eInteg, - mfem::FiniteElementSpace * fes ); + void computeEquilibriumJacobian( mfem::SparseMatrix* const A, mfem::ElasticityIntegrator* eInteg, + mfem::FiniteElementSpace* fes ); /*! * \brief computes all element Jacobian contributions for linear elasticity using MFEM @@ -297,10 +284,8 @@ class TestMesh * \pre TestMesh::mfem_mesh != nullptr * \pre matrix A cannot have been finalized yet */ - void computeElementJacobianContributions( mfem::SparseMatrix * const A, - mfem::ElasticityIntegrator * eInt, - mfem::FiniteElementSpace * fe_space, - bool matrixDebug = false ); + void computeElementJacobianContributions( mfem::SparseMatrix* const A, mfem::ElasticityIntegrator* eInt, + mfem::FiniteElementSpace* fe_space, bool matrixDebug = false ); /*! * \brief takes an oversized tribol sparse matrix and condenses it to an appropriate @@ -313,11 +298,10 @@ class TestMesh * \pre ASystem != nullptr * * \note this routine is a little odd taking a dense matrix and populating a sparse - * matrix. This reflects the way the Lagrange multiplier patch test code + * matrix. This reflects the way the Lagrange multiplier patch test code * was originally written and does not represent the optimal way of doing things */ - void tribolMatrixToSystemMatrix( mfem::DenseMatrix * const ATribol, - mfem::SparseMatrix * const ASystem ); + void tribolMatrixToSystemMatrix( mfem::DenseMatrix* const ATribol, mfem::SparseMatrix* const ASystem ); /*! * \brief populate a right hand side length vector with gap evaluations @@ -327,7 +311,7 @@ class TestMesh * \pre v of length, dim * numTotalNodes + numPressureDofs * */ - void getGapEvals( RealT * const v ); + void getGapEvals( RealT* const v ); /*! * \brief Modifies matrix A and rhs vector b to enforce Dirichlet BCs @@ -336,225 +320,211 @@ class TestMesh * \param [in,out] b mfem vector (rhs vector) * \param [in] contact applies BCs different for a contact vs. non-contact solution * - * \note The boolean, contact, is something utilized for the SINGLE_MORTAR LM + * \note The boolean, contact, is something utilized for the SINGLE_MORTAR LM * patch unit test * * \pre b of length, dim * numTotalNodes + numPressureDofs * \pre A of dimensions (length b) x (length b) * */ - void enforceDirichletBCs( mfem::SparseMatrix * const A, - mfem::Vector * const b, - bool contact = true ); - - /// print mesh to vtk file - void testMeshToVtk( const std::string& dir, ///< Name of the output directory - int cycle, ///< Cycle number - RealT time ///< Simulation time - ); - -public: - - mfem::Mesh* mfem_mesh; - - // Basic info about the mesh - IndexT mortarMeshId; ///< Mesh id for mortar portion of mesh - IndexT nonmortarMeshId; ///< Mesh id for nonmortar portion of mesh - int cellType; ///< Type of contact surface cell in mesh - int numTotalNodes; ///< Total number of nodes in the mesh - int numMortarNodes; ///< Number of mortar nodes (not just surface nodes) - int numNonmortarNodes; ///< Number of nonmortar nodes (not just surface nodes) - int numNonmortarSurfaceNodes; ///< Number of surface nodes on the nonmortar side - int numTotalElements; ///< Total number of elements - int numMortarElements; ///< Number of mortar elements - int numNonmortarElements; ///< Number of nonmortar nodes - int numTotalFaces; ///< Total number of faces - int numMortarFaces; ///< Number of mortar faces - int numNonmortarFaces; ///< Number of nonmortar faces - int numNodesPerFace; ///< Number of nodes per face - int numNodesPerElement; ///< Number of nodes per element - int dim; ///< Mesh dimension - - // Pointers to Boundary Condition Data - int *dirNodesX1; ///< Pointer to list of mortar node ids with x-component Dirichlet BCs - int *dirNodesY1; ///< Pointer to list of mortar node ids with y-component Dirichlet BCs - int *dirNodesZ1; ///< Pointer to list of mortar node ids with z-component Dirichlet BCs - RealT *iDirValX1; ///< Pointer to x-component Dirichlet BC values for specified mortar nodes - RealT *iDirValY1; ///< Pointer to y-component Dirichlet BC values for specified mortar nodes - RealT *iDirValZ1; ///< Pointer to z-component Dirichlet BC values for specified mortar nodes - int *dirNodesX2; ///< Pointer to list of nonmortar node ids with x-component Dirichlet BCs - int *dirNodesY2; ///< Pointer to list of nonmortar node ids with y-component Dirichlet BCs - int *dirNodesZ2; ///< Pointer to list of nonmortar node ids with z-component Dirichlet BCs - RealT *iDirValX2; ///< Pointer to x-component Dirichlet BC values for specified nonmortar nodes - RealT *iDirValY2; ///< Pointer to y-component Dirichlet BC values for specified nonmortar nodes - RealT *iDirValZ2; ///< Pointer to z-component Dirichlet BC values for specified nonmortar nodes - int *presDofs1; ///< Pointer to mortar node ids with a pressure BC - int *presDofs2; ///< Pointer to nonmortar node ids with a pressure BC - - // Pointers to connectivity data - int *faceConn1; ///< Pointer to mortar face connectivity - int *faceConn2; ///< Pointer to nonmortar face connectivity - int *elConn1; ///< Pointer to mortar element connectivity - int *elConn2; ///< Pointer to nonmortar element connectivity - - // TODO can we make these mfem grid functions? - RealT *fx1; ///< Mortar nodal forces, x-component - RealT *fy1; ///< Mortar nodal forces, y-component - RealT *fz1; ///< Mortar nodal forces, z-component - RealT *fx2; ///< Nonmortar nodal forces, x-component - RealT *fy2; ///< Nonmortar nodal forces, y-component - RealT *fz2; ///< Nonmortar nodal forces, z-component - RealT *vx1; ///< Mortar nodal velocities, x-component - RealT *vy1; ///< Mortar nodal velocities, y-component - RealT *vz1; ///< Mortar nodal velocities, z-component - RealT *vx2; ///< Nonmortar nodal velocities, x-component - RealT *vy2; ///< Nonmortar nodal velocities, y-component - RealT *vz2; ///< Nonmortar nodal velocities, z-component - RealT *x; ///< Nodal coordinates, x-component - RealT *y; ///< Nodal coordinates, y-component - RealT *z; ///< Nodal coordinates, z-component - - // CSR storage - int* I; ///< Offsets for CSR storage - int* J; ///< Column indices for all nonzero entries in CSR storage - RealT *vals; ///< Array of nonzero values - - RealT *gaps; ///< Array of nodal gaps - RealT *pressures; ///< Array of nodal pressures - - RealT * mortar_bulk_mod; - RealT * mortar_element_thickness; - RealT * nonmortar_bulk_mod; - RealT * nonmortar_element_thickness; - - bool registered_velocities1 {false}; - bool registered_velocities2 {false}; - bool mesh_constructed {false}; - - public: - RealT* getX() const {return x;} - RealT* getY() const {return y;} - RealT* getZ() const {return z;} - int* getMortarFaceConnectivity() const {return faceConn1;} - int* getNonmortarFaceConnectivity() const {return faceConn2;} - - int getNumTotalNodes() const { return numTotalNodes;} - - public: - /// Needed for initial shroud interface - int getMortarFaceConnectivitySize() const { return numNodesPerFace * numMortarFaces ; } - int getNonmortarFaceConnectivitySize() const { return numNodesPerFace * numNonmortarFaces ; } - -protected: - // NONE + void enforceDirichletBCs( mfem::SparseMatrix* const A, mfem::Vector* const b, bool contact = true ); + + /// print mesh to vtk file + void testMeshToVtk( const std::string& dir, ///< Name of the output directory + int cycle, ///< Cycle number + RealT time ///< Simulation time + ); + + public: + mfem::Mesh* mfem_mesh; + + // Basic info about the mesh + IndexT mortarMeshId; ///< Mesh id for mortar portion of mesh + IndexT nonmortarMeshId; ///< Mesh id for nonmortar portion of mesh + int cellType; ///< Type of contact surface cell in mesh + int numTotalNodes; ///< Total number of nodes in the mesh + int numMortarNodes; ///< Number of mortar nodes (not just surface nodes) + int numNonmortarNodes; ///< Number of nonmortar nodes (not just surface nodes) + int numNonmortarSurfaceNodes; ///< Number of surface nodes on the nonmortar side + int numTotalElements; ///< Total number of elements + int numMortarElements; ///< Number of mortar elements + int numNonmortarElements; ///< Number of nonmortar nodes + int numTotalFaces; ///< Total number of faces + int numMortarFaces; ///< Number of mortar faces + int numNonmortarFaces; ///< Number of nonmortar faces + int numNodesPerFace; ///< Number of nodes per face + int numNodesPerElement; ///< Number of nodes per element + int dim; ///< Mesh dimension + + // Pointers to Boundary Condition Data + int* dirNodesX1; ///< Pointer to list of mortar node ids with x-component Dirichlet BCs + int* dirNodesY1; ///< Pointer to list of mortar node ids with y-component Dirichlet BCs + int* dirNodesZ1; ///< Pointer to list of mortar node ids with z-component Dirichlet BCs + RealT* iDirValX1; ///< Pointer to x-component Dirichlet BC values for specified mortar nodes + RealT* iDirValY1; ///< Pointer to y-component Dirichlet BC values for specified mortar nodes + RealT* iDirValZ1; ///< Pointer to z-component Dirichlet BC values for specified mortar nodes + int* dirNodesX2; ///< Pointer to list of nonmortar node ids with x-component Dirichlet BCs + int* dirNodesY2; ///< Pointer to list of nonmortar node ids with y-component Dirichlet BCs + int* dirNodesZ2; ///< Pointer to list of nonmortar node ids with z-component Dirichlet BCs + RealT* iDirValX2; ///< Pointer to x-component Dirichlet BC values for specified nonmortar nodes + RealT* iDirValY2; ///< Pointer to y-component Dirichlet BC values for specified nonmortar nodes + RealT* iDirValZ2; ///< Pointer to z-component Dirichlet BC values for specified nonmortar nodes + int* presDofs1; ///< Pointer to mortar node ids with a pressure BC + int* presDofs2; ///< Pointer to nonmortar node ids with a pressure BC + + // Pointers to connectivity data + int* faceConn1; ///< Pointer to mortar face connectivity + int* faceConn2; ///< Pointer to nonmortar face connectivity + int* elConn1; ///< Pointer to mortar element connectivity + int* elConn2; ///< Pointer to nonmortar element connectivity + + // TODO can we make these mfem grid functions? + RealT* fx1; ///< Mortar nodal forces, x-component + RealT* fy1; ///< Mortar nodal forces, y-component + RealT* fz1; ///< Mortar nodal forces, z-component + RealT* fx2; ///< Nonmortar nodal forces, x-component + RealT* fy2; ///< Nonmortar nodal forces, y-component + RealT* fz2; ///< Nonmortar nodal forces, z-component + RealT* vx1; ///< Mortar nodal velocities, x-component + RealT* vy1; ///< Mortar nodal velocities, y-component + RealT* vz1; ///< Mortar nodal velocities, z-component + RealT* vx2; ///< Nonmortar nodal velocities, x-component + RealT* vy2; ///< Nonmortar nodal velocities, y-component + RealT* vz2; ///< Nonmortar nodal velocities, z-component + RealT* x; ///< Nodal coordinates, x-component + RealT* y; ///< Nodal coordinates, y-component + RealT* z; ///< Nodal coordinates, z-component + + // CSR storage + int* I; ///< Offsets for CSR storage + int* J; ///< Column indices for all nonzero entries in CSR storage + RealT* vals; ///< Array of nonzero values + + RealT* gaps; ///< Array of nodal gaps + RealT* pressures; ///< Array of nodal pressures + + RealT* mortar_bulk_mod; + RealT* mortar_element_thickness; + RealT* nonmortar_bulk_mod; + RealT* nonmortar_element_thickness; + + bool registered_velocities1{ false }; + bool registered_velocities2{ false }; + bool mesh_constructed{ false }; + + public: + RealT* getX() const { return x; } + RealT* getY() const { return y; } + RealT* getZ() const { return z; } + int* getMortarFaceConnectivity() const { return faceConn1; } + int* getNonmortarFaceConnectivity() const { return faceConn2; } + + int getNumTotalNodes() const { return numTotalNodes; } + + public: + /// Needed for initial shroud interface + int getMortarFaceConnectivitySize() const { return numNodesPerFace * numMortarFaces; } + int getNonmortarFaceConnectivitySize() const { return numNodesPerFace * numNonmortarFaces; } + + protected: + // NONE }; -} // end of namespace "tribol" +} // namespace tribol -namespace mfem_ext -{ +namespace mfem_ext { /// Simple central difference method with homogeneous velocity BCs. -class CentralDiffSolver : public mfem::SecondOrderODESolver -{ -public: - /** - * @brief Construct a new central difference solver object - * - * Supports homogeneous velocity BCs - * - * @param bc_vdofs_ List of vdofs to set homogeneous velocity BCs - */ - CentralDiffSolver(const mfem::Array& bc_vdofs_); - - /** - * @brief Updates x and dxdt after taking a step of size dt - * - * @param x Displacement vector - * @param dxdt Velocity vector - * @param t Current time - * @param dt Timestep size - */ - void Step(mfem::Vector& x, mfem::Vector& dxdt, double& t, double& dt) override; - -private: - /** - * @brief Acceleration vector - */ - mfem::Vector accel; - - /** - * @brief List of vdofs to apply homogeneous velocity BCs - */ - mfem::Array bc_vdofs; - - /** - * @brief Tracks whether a step has been taken yet - */ - bool first_step; - - /** - * @brief Applies homogeneous BCs to dxdt - * - * @param dxdt Velocity vector - */ - void SetHomogeneousBC(mfem::Vector& dxdt) const; +class CentralDiffSolver : public mfem::SecondOrderODESolver { + public: + /** + * @brief Construct a new central difference solver object + * + * Supports homogeneous velocity BCs + * + * @param bc_vdofs_ List of vdofs to set homogeneous velocity BCs + */ + CentralDiffSolver( const mfem::Array& bc_vdofs_ ); + + /** + * @brief Updates x and dxdt after taking a step of size dt + * + * @param x Displacement vector + * @param dxdt Velocity vector + * @param t Current time + * @param dt Timestep size + */ + void Step( mfem::Vector& x, mfem::Vector& dxdt, double& t, double& dt ) override; + + private: + /** + * @brief Acceleration vector + */ + mfem::Vector accel; + + /** + * @brief List of vdofs to apply homogeneous velocity BCs + */ + mfem::Array bc_vdofs; + + /** + * @brief Tracks whether a step has been taken yet + */ + bool first_step; + + /** + * @brief Applies homogeneous BCs to dxdt + * + * @param dxdt Velocity vector + */ + void SetHomogeneousBC( mfem::Vector& dxdt ) const; }; #ifdef TRIBOL_USE_MPI /// Explicit solid mechanics update with lumped mass -class ExplicitMechanics : public mfem::SecondOrderTimeDependentOperator -{ -public: - /** - * @brief Construct a new explicit mechanics operator (elasticity, lumped - * mass, and external force vector) - * - * @param fespace Finite element space of the primary fields - * @param rho Density - * @param lambda Lame constant - * @param mu Lame constant - */ - ExplicitMechanics( - mfem::ParFiniteElementSpace& fespace, - mfem::Coefficient& rho, - mfem::Coefficient& lambda, - mfem::Coefficient& mu - ); - - using mfem::SecondOrderTimeDependentOperator::Mult; - - /** - * @brief Compute acceleration given displacement and velocity - * - * @param u Displacement vector - * @param dudt Velocity vector - * @param a Acceleration vector - */ - void Mult( - const mfem::Vector& u, - const mfem::Vector& dudt, - mfem::Vector& a - ) const override; - - /** - * @brief External force contribution (must be manually updated) - */ - mfem::ParGridFunction f_ext; - -private: - /** - * @brief Elasticity bilinear form - */ - mfem::ParBilinearForm elasticity; - - /** - * @brief Inverse lumped mass matrix - */ - mfem::Vector inv_lumped_mass; +class ExplicitMechanics : public mfem::SecondOrderTimeDependentOperator { + public: + /** + * @brief Construct a new explicit mechanics operator (elasticity, lumped + * mass, and external force vector) + * + * @param fespace Finite element space of the primary fields + * @param rho Density + * @param lambda Lame constant + * @param mu Lame constant + */ + ExplicitMechanics( mfem::ParFiniteElementSpace& fespace, mfem::Coefficient& rho, mfem::Coefficient& lambda, + mfem::Coefficient& mu ); + + using mfem::SecondOrderTimeDependentOperator::Mult; + + /** + * @brief Compute acceleration given displacement and velocity + * + * @param u Displacement vector + * @param dudt Velocity vector + * @param a Acceleration vector + */ + void Mult( const mfem::Vector& u, const mfem::Vector& dudt, mfem::Vector& a ) const override; + + /** + * @brief External force contribution (must be manually updated) + */ + mfem::ParGridFunction f_ext; + + private: + /** + * @brief Elasticity bilinear form + */ + mfem::ParBilinearForm elasticity; + + /** + * @brief Inverse lumped mass matrix + */ + mfem::Vector inv_lumped_mass; }; #endif /* defined(TRIBOL_USE_MPI) */ -} // end of namespace "mfem_ext" +} // namespace mfem_ext #endif /* SRC_UTILS_TESTUTILS_HPP_ */