diff --git a/.github/workflows/elasticapp.yml b/.github/workflows/elasticapp.yml new file mode 100644 index 000000000..0fc979bfd --- /dev/null +++ b/.github/workflows/elasticapp.yml @@ -0,0 +1,37 @@ +name: elasticapp (Elastica++ based backend) tests + +# trigger run only on changes to the backend folder. +on: + push: + paths: + - backend/** + pull_request: + paths: + - backend/** + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + python-version: ["3.10", "3.11"] #, "3.12"] + os: [ubuntu-latest] # , macos-latest] + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4.2.0 + with: + python-version: ${{ matrix.python-version }} + + - name: Install PyElastica + run: pip install . -v + + - name: Install elasticapp backend + run: pip install ./backend -v + + - name: Run elasticapp tests + run: | + python -m pip install pytest + pytest backend/tests diff --git a/.gitignore b/.gitignore index 535b8659c..e799563d2 100644 --- a/.gitignore +++ b/.gitignore @@ -236,3 +236,6 @@ outcmaes/* # csv files *.csv + +# ./backend dependencies +deps diff --git a/backend/.clang-format b/backend/.clang-format new file mode 100644 index 000000000..48d4ec71d --- /dev/null +++ b/backend/.clang-format @@ -0,0 +1,17 @@ +--- +# We'll use defaults from the Google style. +# See http://clang.llvm.org/docs/ClangFormat.html for help. +Language: Cpp +BasedOnStyle: Google +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +PointerAlignment: Left +DerivePointerAlignment: false +FixNamespaceComments: true +IncludeCategories: + - Regex: "^<.*" + Priority: 1 + - Regex: ".*" + Priority: 2 +NamespaceIndentation: All +SortIncludes: false diff --git a/backend/.gitignore b/backend/.gitignore index 46f42f8f3..9ac281d62 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -9,3 +9,13 @@ install_manifest.txt compile_commands.json CTestTestfile.cmake _deps + +subprojects/packagecache + +subprojects/blaze +subprojects/blaze_tensor +subprojects/brigand +subprojects/cxxopts-3.1.1 +subprojects/sleef +subprojects/tbb +subprojects/yaml-cpp diff --git a/backend/CMakeLists.txt b/backend/CMakeLists.txt deleted file mode 100644 index ad6f1a798..000000000 --- a/backend/CMakeLists.txt +++ /dev/null @@ -1,113 +0,0 @@ -# Automagically build elastica along with its dependencies - -# ############################################################################## -# Distributed under the MIT License. See LICENSE for details. -# ############################################################################## -cmake_minimum_required(VERSION 3.6...3.18) - -# ############################################################################## -# Set policies -# ############################################################################## -if (POLICY CMP0074) - cmake_policy(SET CMP0074 NEW) -endif () -if (POLICY CMP0110) - # disable support for arbitrary characters (such as whitespace) in test names - # but rather use underscores - cmake_policy(SET CMP0110 OLD) -endif () - -# ############################################################################## -# Project details -# Versioning -# ############################################################################## -set(ELASTICA_VERSION "0.0.3") -set(ELASTICA_DESCRIPTION "Package to efficiently simulate soft-filaments on devices from your laptop to supercomputing clusters.") - -# C is added for the HDF5 dependency. HighFive also relies on C headers. -project(elastica - LANGUAGES CXX C - VERSION ${ELASTICA_VERSION} - DESCRIPTION ${ELASTICA_DESCRIPTION}) - - -# ############################################################################## -# Setting the directory which contains CMake modules -# ############################################################################## -# Appends the cmake path inside the MAKE_MODULE_PATH variable which stores the -# list of directories where to lookk for additional CMake modules. -list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) - -# Create a new (or overwrite existing) info file at start of configuration -file(WRITE "${CMAKE_BINARY_DIR}/BuildInformation.txt" "") - -# ############################################################################## -# Including cmake only modules -# ############################################################################## -include(Logging) -set_logs_on() -set_log_level("DEBUG") # INFO DEBUG HEAVYDEBUG - -option(ELASTICA_BUILD_PYTHON_BINDINGS "Build the python bindings for elastica" ON) - -# Define standard installation directories before since python targets utilize -include(GNUInstallDirs) - -# Disable `make install` depending on `make all` since we want to control what -# we install more closely. With this setting, and targets marked as `OPTIONAL`, -# only targets that were built will be installed. -set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY ON) - - -include(CheckCompilerVersion) -# -include(ElasticaAddInterfaceLibraryHeaders) -#include(ElasticaTargetHeaders) -#include(ElasticaTargetSources) -#include(ElasticaAddLibraries) -include(ElasticaSetupFlagsTarget) -include(EnableWarnings) -# need flags to be defined - -include(SetBuildType) -#include(SetupCraySupport) -include(SetCxxStandard) -include(SetupCxxFlags) -include(SetupElasticaInlining) -include(SetupMacOsx) -include(SetOutputDirectory) -include(SetupPic) -include(SetupSanitizers) - -# In order to use certain code analysis tools like clang-tidy and cppcheck the -# compile commands need to be accessible. CMake can write these to a -# "compile_commands.json" file. -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - -# Need to load python libs before boost.python. include(SetupPythonLibs) -# include(SetupBlas) include(SetupBoost) include(SetupBrigand) -# include(SetupCatch) include(SetupGoogleBenchmark) include(SetupGsl) -# include(SetupHdf5) include(SetupAllocator) include(SetupLIBXSMM) -# include(SetupLapack) include(SetupNumPy) include(SetupOpenMP) -# include(SetupPapi) include(SetupSciPy) include(SetupLibsharp) - -include(SetupBlaze) -include(SetupBlazeTensor) -#include(SetupBrigand) -#include(SetupProfiling) -#include(SetupHighFive) -#include(SetupSMP) -include(SetupStl) -#include(SetupYamlCpp) -include(SetupFilesystem) - -if (ELASTICA_BUILD_PYTHON_BINDINGS) - include(SetupPybind11) -endif () -#include(ElasticaSetupPythonPackage) - -#add_subdirectory(elastica) - - -include(PrintUsefulCMakeInfo) -#include(ElasticaInstall) diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 000000000..506dfeb2f --- /dev/null +++ b/backend/README.md @@ -0,0 +1,44 @@ +# Elasticapp backend for PyElastica + +This file serves as the documentation for the `elasticapp` backend. + +## Installation + +In the root of the PyElastica source tree, run the following command + +``` +pip install ./backend +``` + +> Make sure you install the package from _PyElastica source tree_. + +This command will take care of installation of all build-time dependencies, compilation of C++ source files and install the a python package called `elasticapp`. + +## Testing + +Make sure you have `pytest` installed. In the root of the PyElastica source tree, run the following command + +``` +pytest backend/tests +``` + +## Benchmarking + +Standalone scripts for benchmarking purposes are available in `backend/benchmarking` folder. + +### Benchmarking `matmul` + +For benchmarking various `matmul` implementations, run + +``` +python3 backend/benchmarking/matmul.py +``` + +## Contributed By + +- Tejaswin Parthasarathy (Teja) +- [Seung Hyun Kim](https://github.com/skim0119) +- Ankith Pai +- [Yashraj Bhosale](https://github.com/bhosale2) +- Arman Tekinalp +- Songyuan Cui diff --git a/backend/benchmarking/batchcross.py b/backend/benchmarking/batchcross.py new file mode 100644 index 000000000..eb9cc78b0 --- /dev/null +++ b/backend/benchmarking/batchcross.py @@ -0,0 +1,50 @@ +from elasticapp._linalg_numpy import batch_cross +from elasticapp._linalg import batch_cross as batch_cross_final +from elasticapp._PyArrays import Matrix +from elastica._linalg import _batch_cross +import numpy +import time + +# warm up jit for fair comparison +random_1 = numpy.random.random((3, 1)) +random_2 = numpy.random.random((3, 1)) +out1 = _batch_cross(random_1, random_2) + + +def benchmark_batchsize(funcs: list, batches: list[int], num_iterations: int = 1000): + ret: dict = {} + for batch_size in batches: + random_a = numpy.random.random((3, batch_size)) + random_b = numpy.random.random((3, batch_size)) + + ret[batch_size] = {} + for func_name, func, func_wrap in funcs: + random_a_w = func_wrap(random_a) if func_wrap else random_a + random_b_w = func_wrap(random_b) if func_wrap else random_b + + start = time.perf_counter() + for _ in range(num_iterations): + func(random_a_w, random_b_w) + + ret[batch_size][func_name] = (time.perf_counter() - start) / num_iterations + + return ret + + +results = benchmark_batchsize( + [ + ("pyelastica", _batch_cross, None), + ("elasticapp_blaze_copy", batch_cross, None), + ("elasticapp_blaze_final", batch_cross_final, Matrix), + ], + [2**i for i in range(14)], +) +for size, data in results.items(): + pyelastica = data["pyelastica"] + elasticapp_blaze_copy = data["elasticapp_blaze_copy"] + elasticapp_blaze_final = data["elasticapp_blaze_final"] + print(f"{size = }") + print(f"{pyelastica = }") + print(f"{elasticapp_blaze_copy = }, ratio: {elasticapp_blaze_copy / pyelastica}") + print(f"{elasticapp_blaze_final = }, ratio: {elasticapp_blaze_final / pyelastica}") + print() diff --git a/backend/benchmarking/inv_rotate.py b/backend/benchmarking/inv_rotate.py new file mode 100644 index 000000000..8bf7169a7 --- /dev/null +++ b/backend/benchmarking/inv_rotate.py @@ -0,0 +1,46 @@ +from elasticapp._rotations import inv_rotate, inv_rotate_scalar +from elasticapp._PyArrays import Tensor +from elastica._rotations import _inv_rotate +import numpy +import time + +# warm up jit for fair comparison +random_1 = numpy.random.random((3, 3, 1)) +out1 = _inv_rotate(random_1) + + +def benchmark_batchsize(funcs: list, batches: list[int], num_iterations: int = 1000): + ret: dict = {} + for batch_size in batches: + random_a = numpy.random.random((3, 3, batch_size)) + + ret[batch_size] = {} + for func_name, func, func_wrap in funcs: + random_a_w = func_wrap(random_a) if func_wrap else random_a + + start = time.perf_counter() + for _ in range(num_iterations): + func(random_a_w) + + ret[batch_size][func_name] = (time.perf_counter() - start) / num_iterations + + return ret + + +results = benchmark_batchsize( + [ + ("pyelastica", _inv_rotate, None), + ("elasticapp_simd", inv_rotate, Tensor), + ("elasticapp_scalar", inv_rotate_scalar, Tensor), + ], + [2**i for i in range(14)], +) +for size, data in results.items(): + pyelastica = data["pyelastica"] + elasticapp_simd = data["elasticapp_simd"] + elasticapp_scalar = data["elasticapp_scalar"] + print(f"{size = }") + print(f"{pyelastica = }") + print(f"{elasticapp_simd = }, ratio: {elasticapp_simd / pyelastica}") + print(f"{elasticapp_scalar = }, ratio: {elasticapp_scalar / pyelastica}") + print() diff --git a/backend/benchmarking/matmul.py b/backend/benchmarking/matmul.py new file mode 100644 index 000000000..87f439767 --- /dev/null +++ b/backend/benchmarking/matmul.py @@ -0,0 +1,62 @@ +from elasticapp._linalg_numpy import ( + batch_matmul_naive, + batch_matmul_blaze, + batch_matmul, +) +from elasticapp._linalg import batch_matmul as batch_matmul_final +from elasticapp._PyArrays import Tensor +from elastica._linalg import _batch_matmul +import numpy +import time + +# warm up jit for fair comparison +random_1 = numpy.random.random((3, 3, 1)) +random_2 = numpy.random.random((3, 3, 1)) +out1 = _batch_matmul(random_1, random_2) + + +def benchmark_batchsize(funcs: list, batches: list[int], num_iterations: int = 1000): + ret: dict = {} + for batch_size in batches: + random_a = numpy.random.random((3, 3, batch_size)) + random_b = numpy.random.random((3, 3, batch_size)) + + ret[batch_size] = {} + for func_name, func, func_wrap in funcs: + random_a_w = func_wrap(random_a) if func_wrap else random_a + random_b_w = func_wrap(random_b) if func_wrap else random_b + + start = time.perf_counter() + for _ in range(num_iterations): + func(random_a_w, random_b_w) + + ret[batch_size][func_name] = ( + time.perf_counter() - start + ) / num_iterations + + return ret + + +results = benchmark_batchsize( + [ + ("pyelastica", _batch_matmul, None), + ("elasticapp_naive", batch_matmul_naive, None), + ("elasticapp_blaze", batch_matmul_blaze, None), + ("elasticapp_blaze_copy", batch_matmul, None), + ("elasticapp_blaze_final", batch_matmul_final, Tensor), + ], + [2**i for i in range(14)], +) +for size, data in results.items(): + pyelastica = data["pyelastica"] + elasticapp_naive = data["elasticapp_naive"] + elasticapp_blaze = data["elasticapp_blaze"] + elasticapp_blaze_copy = data["elasticapp_blaze_copy"] + elasticapp_blaze_final = data["elasticapp_blaze_final"] + print(f"{size = }") + print(f"{pyelastica = }") + print(f"{elasticapp_naive = }, ratio: {elasticapp_naive / pyelastica}") + print(f"{elasticapp_blaze = }, ratio: {elasticapp_blaze / pyelastica}") + print(f"{elasticapp_blaze_copy = }, ratio: {elasticapp_blaze_copy / pyelastica}") + print(f"{elasticapp_blaze_final = }, ratio: {elasticapp_blaze_final / pyelastica}") + print() diff --git a/backend/benchmarking/rotate.py b/backend/benchmarking/rotate.py new file mode 100644 index 000000000..e724c136f --- /dev/null +++ b/backend/benchmarking/rotate.py @@ -0,0 +1,57 @@ +from elasticapp._rotations import rotate, rotate_scalar +from elasticapp._PyArrays import Tensor, Matrix +from elastica._rotations import _rotate +import numpy +import time + +# warm up jit for fair comparison +random_1 = numpy.random.random((3, 3, 1)) +random_2 = numpy.random.random((3, 1)) +out1 = _rotate(random_1, 1, random_2) + + +def benchmark_batchsize(funcs: list, batches: list[int], num_iterations: int = 1000): + ret: dict = {} + for batch_size in batches: + random_a = numpy.random.random((3, 3, batch_size)) + random_b = numpy.random.random((3, batch_size)) + + ret[batch_size] = {} + for func_name, func, func_arg_wrap in funcs: + tot = 0.0 + for _ in range(num_iterations): + args = func_arg_wrap(random_a, random_b) + start = time.perf_counter() + func(*args) + tot += time.perf_counter() - start + + ret[batch_size][func_name] = tot / num_iterations + + return ret + + +def _pyelastica_arg_wrap(x, y): + return x, 1.0, y + + +def _elasticapp_arg_wrap(x, y): + return Tensor(x), Matrix(y) + + +results = benchmark_batchsize( + [ + ("pyelastica", _rotate, _pyelastica_arg_wrap), + ("elasticapp_simd", rotate, _elasticapp_arg_wrap), + ("elasticapp_scalar", rotate_scalar, _elasticapp_arg_wrap), + ], + [2**i for i in range(14)], +) +for size, data in results.items(): + pyelastica = data["pyelastica"] + elasticapp_simd = data["elasticapp_simd"] + elasticapp_scalar = data["elasticapp_scalar"] + print(f"{size = }") + print(f"{pyelastica = }") + print(f"{elasticapp_simd = }, ratio: {elasticapp_simd / pyelastica}") + print(f"{elasticapp_scalar = }, ratio: {elasticapp_scalar / pyelastica}") + print() diff --git a/backend/buildscripts/build-all.sh b/backend/buildscripts/build-all.sh new file mode 100644 index 000000000..28060ca55 --- /dev/null +++ b/backend/buildscripts/build-all.sh @@ -0,0 +1,82 @@ + +set -e -x + +OLD_CWD=$(pwd) + +export ELASTICA_DEP_PREFIX="$OLD_CWD/../deps" + +mkdir -p $ELASTICA_DEP_PREFIX && cd $ELASTICA_DEP_PREFIX + +export ELASTICA_INSTALL_PREFIX="$ELASTICA_DEP_PREFIX/installed" + +# With this we +# 1) Force install prefix to $ELASTICA_INSTALL_PREFIX +# 2) use lib directory within $ELASTICA_INSTALL_PREFIX (and not lib64) +# 3) make release binaries +# 4) build shared libraries +# 5) not have @rpath in the linked dylibs (needed on macs only) +# 6) tell cmake to search in $ELASTICA_INSTALL_PREFIX for sub dependencies +export ELASTICA_BASE_CMAKE_FLAGS="-DCMAKE_INSTALL_PREFIX=$ELASTICA_INSTALL_PREFIX \ + -DCMAKE_INSTALL_LIBDIR:PATH=lib \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_SHARED_LIBS=true \ + -DCMAKE_INSTALL_NAME_DIR=$ELASTICA_INSTALL_PREFIX/lib \ + -DCMAKE_PREFIX_PATH=$ELASTICA_INSTALL_PREFIX" + +mkdir -p $ELASTICA_INSTALL_PREFIX + +# set pkg_config_path so that system can find dependencies +export PKG_CONFIG_PATH="$ELASTICA_INSTALL_PREFIX/lib/pkgconfig:$PKG_CONFIG_PATH" + +# blaze +if [ ! -d blaze ]; then + git clone --depth 1 https://bitbucket.org/blaze-lib/blaze.git && cd blaze + + mkdir build + cmake -B build $ELASTICA_BASE_CMAKE_FLAGS \ + -DBLAZE_SHARED_MEMORY_PARALLELIZATION=OFF \ + -DUSE_LAPACK=OFF \ + -S . + cmake --build build --parallel $(nproc) + cmake --install build + cd .. +fi + +# blaze tensor +if [ ! -d blaze_tensor ]; then + git clone --depth 1 https://github.com/STEllAR-GROUP/blaze_tensor.git && cd blaze_tensor + + mkdir build + cmake -B build $ELASTICA_BASE_CMAKE_FLAGS \ + -Dblaze_DIR="$ELASTICA_INSTALL_PREFIX/share/blaze/cmake/" \ + -S . + cmake --build build --parallel $(nproc) + cmake --install build + cd .. +fi + +# sleef +if [ ! -d sleef ]; then + git clone --depth 1 https://github.com/shibatch/sleef && cd sleef + + mkdir build + cmake -B build $ELASTICA_BASE_CMAKE_FLAGS \ + -S . + cmake --build build --parallel $(nproc) + cmake --install build + cd .. +fi + +# brigand +if [ ! -d brigand ]; then + git clone --depth 1 https://github.com/edouarda/brigand.git && cd brigand + + mkdir build + cmake -B build $ELASTICA_BASE_CMAKE_FLAGS \ + -S . + cmake --build build --parallel $(nproc) + cmake --install build + cd .. +fi + +cd $OLD_CWD \ No newline at end of file diff --git a/backend/cmake/.gitignore b/backend/cmake/.gitignore deleted file mode 100644 index 0d20b6487..000000000 --- a/backend/cmake/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.pyc diff --git a/backend/cmake/AddCxxFlag.cmake b/backend/cmake/AddCxxFlag.cmake deleted file mode 100644 index 2d1085f44..000000000 --- a/backend/cmake/AddCxxFlag.cmake +++ /dev/null @@ -1,142 +0,0 @@ -# Checks if CXX flag is valid for the compiler and adds it - -# Obtained with thanks from https://github.com/sxs-collaboration/spectre - -# Distributed under the MIT License. See LICENSE for details. - -# Checks if a CXX flag is supported by the compiler and creates the target -# TARGET_NAME whose INTERFACE_COMPILE_OPTIONS are set to the FLAG_TO_CHECK -# - FLAG_TO_CHECK: the CXX flag to add if the compiler supports it -# - TARGET_NAME: the name of the target whose INTERFACE_COMPILE_OPTIONS are -# set -function(create_cxx_flag_target FLAG_TO_CHECK TARGET_NAME) - # In order to check for a -Wno-* flag in gcc, you have to check the - # -W* version instead. See http://gcc.gnu.org/wiki/FAQ#wnowarning - string(REGEX REPLACE ^-Wno- -W POSITIVE_FLAG_TO_CHECK ${FLAG_TO_CHECK}) - execute_process( - COMMAND - bash -c - "LC_ALL=POSIX ${CMAKE_CXX_COMPILER} -Werror ${POSITIVE_FLAG_TO_CHECK} \ --x c++ -c - <<< \"\" -o /dev/null" - RESULT_VARIABLE RESULT - ERROR_VARIABLE ERROR_FROM_COMPILATION - OUTPUT_QUIET) - add_library(${TARGET_NAME} INTERFACE) - if (${RESULT} EQUAL 0) - set_property(TARGET ${TARGET_NAME} - APPEND PROPERTY - INTERFACE_COMPILE_OPTIONS $<$:${FLAG_TO_CHECK}>) - endif (${RESULT} EQUAL 0) -endfunction() - -# Checks which of the CXX FLAGS_TO_CHECK are supported by the compiler -# and creates the target TARGET_NAME whose INTERFACE_COMPILE_OPTIONS -# are set to the FLAGS_TO_CHECK that are supported. If adding many flags, -# this will be much faster than calling create_cxx_flags_target multiple times. -# - FLAGS_TO_CHECK: a semicolon separated string of CXX flags to try to add -# for compilation. -# - TARGET_NAME: the name of the target whose INTERFACE_COMPILE_OPTIONS are -# set -function(create_cxx_flags_target FLAGS_TO_CHECK TARGET_NAME) - # In order to check for a -Wno-* flag in gcc, you have to check the - # -W* version instead. See http://gcc.gnu.org/wiki/FAQ#wnowarning - set(POSITIVE_FLAGS_TO_CHECK) - foreach (FLAG_TO_CHECK ${FLAGS_TO_CHECK}) - string(REGEX REPLACE ^-Wno- -W POSITIVE_FLAG_TO_CHECK ${FLAG_TO_CHECK}) - list(APPEND POSITIVE_FLAGS_TO_CHECK ${POSITIVE_FLAG_TO_CHECK}) - endforeach () - string(REPLACE ";" " " - POSITIVE_FLAGS_WITH_SPACES "${POSITIVE_FLAGS_TO_CHECK}") - execute_process( - COMMAND - bash -c - "LC_ALL=POSIX ${CMAKE_CXX_COMPILER} -Werror ${POSITIVE_FLAGS_WITH_SPACES} \ --x c++ -c - <<< \"\" -o /dev/null" - RESULT_VARIABLE RESULT - ERROR_VARIABLE ERROR_FROM_COMPILATION - OUTPUT_QUIET) - - add_library(${TARGET_NAME} INTERFACE) - if (${RESULT} EQUAL 0) - set_property(TARGET ${TARGET_NAME} - APPEND PROPERTY - INTERFACE_COMPILE_OPTIONS $<$:${FLAGS_TO_CHECK}>) - else (${RESULT} EQUAL 0) - # Check each flag to see if it was marked as "invalid" in the output - unset(FLAGS_TO_ADD) - foreach (FLAG ${POSITIVE_FLAGS_TO_CHECK}) - string(FIND "${ERROR_FROM_COMPILATION}" "'${FLAG}'" FOUND_POS) - if (${FOUND_POS} EQUAL -1) - # For some reason: - # list(FIND ${POSITIVE_FLAGS_TO_CHECK} ${FLAG} INDEX_OF_FLAG) - # doesn't work with some compilers. This makes no sense but such is - # life. As a work around we basically implement a find manually. - - # Find the index of the current flag in the POSITIVE_FLAGS_TO_CHECK - # list. This is the index we use to get the original flag in the - # FLAGS_TO_CHECK list. - set(INDEX 0) - foreach (POS_FLAG ${POSITIVE_FLAGS_TO_CHECK}) - if ("${POS_FLAG}" STREQUAL "${FLAG}") - break() - endif () - MATH(EXPR INDEX "${INDEX}+1") - endforeach () - set(TARGET_INDEX ${INDEX}) - set(INDEX 0) - # Get original flag - set(NEW_FLAG "") - foreach (ORIGINAL_FLAG ${FLAGS_TO_CHECK}) - if (${INDEX} EQUAL ${TARGET_INDEX}) - set(NEW_FLAG ${ORIGINAL_FLAG}) - break() - endif () - MATH(EXPR INDEX "${INDEX}+1") - endforeach () - # Add the flag to the list of flags to add. - set(FLAGS_TO_ADD "${FLAGS_TO_ADD};${NEW_FLAG}") - endif (${FOUND_POS} EQUAL -1) - endforeach (FLAG ${FLAGS_TO_CHECK}) - set_property(TARGET ${TARGET_NAME} - APPEND PROPERTY - INTERFACE_COMPILE_OPTIONS $<$:${FLAGS_TO_ADD}>) - - endif (${RESULT} EQUAL 0) -endfunction() - -set(CMAKE_SUPPORTS_LINK_OPTIONS OFF) -if (CMAKE_VERSION VERSION_EQUAL 3.13 OR CMAKE_VERSION VERSION_GREATER 3.13) - set(CMAKE_SUPPORTS_LINK_OPTIONS ON) -endif (CMAKE_VERSION VERSION_EQUAL 3.13 OR CMAKE_VERSION VERSION_GREATER 3.13) - -if (CMAKE_SUPPORTS_LINK_OPTIONS) - # Creates a target named TARGET_NAME that, if the linker flag FLAG_TO_CHECK - # is supported, defines ${FLAG_TO_CHECK} as an INTERFACE_LINK_OPTION - function(create_cxx_link_flag_target FLAG_TO_CHECK TARGET_NAME) - include(CheckCxxLinkerFlag) - unset(CXX_LINKER_FLAG_WORKS CACHE) - set(CMAKE_REQUIRED_QUIET 1) - check_cxx_linker_flag(${FLAG_TO_CHECK} CXX_LINKER_FLAG_WORKS) - unset(CMAKE_REQUIRED_QUIET) - - add_library(${TARGET_NAME} INTERFACE) - if (CXX_LINKER_FLAG_WORKS) - set_property(TARGET ${TARGET_NAME} - APPEND PROPERTY - INTERFACE_LINK_OPTIONS $<$:${FLAG_TO_CHECK}>) - endif () - endfunction() -endif (CMAKE_SUPPORTS_LINK_OPTIONS) - -# Checks if a flag is supported by the linker and adds it if it is -function(check_and_add_cxx_link_flag FLAG_TO_CHECK) - include(CheckCxxLinkerFlag) - unset(CXX_LINKER_FLAG_WORKS CACHE) - set(CMAKE_REQUIRED_QUIET 1) - check_cxx_linker_flag(${FLAG_TO_CHECK} CXX_LINKER_FLAG_WORKS) - unset(CMAKE_REQUIRED_QUIET) - if (CXX_LINKER_FLAG_WORKS) - set(CMAKE_CXX_LINK_FLAGS - "${CMAKE_CXX_LINK_FLAGS} ${FLAG_TO_CHECK}" PARENT_SCOPE) - endif () -endfunction() diff --git a/backend/cmake/CheckCompilerVersion.cmake b/backend/cmake/CheckCompilerVersion.cmake deleted file mode 100644 index ba4056200..000000000 --- a/backend/cmake/CheckCompilerVersion.cmake +++ /dev/null @@ -1,36 +0,0 @@ -# Checks supported compiler versions for C++14 - -# Distributed under the MIT License. See LICENSE for details. -# Obtained with thanks from https://github.com/sxs-collaboration/spectre - -if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.2) - message(FATAL_ERROR "GCC version must be at least 5.2") - endif() -elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.5) - message(FATAL_ERROR "Clang version must be at least 3.5") - endif() -elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel") - message(FATAL_ERROR "Intel compiler is not supported.") -elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") - if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0) - message(FATAL_ERROR "AppleClang version must be at least 6.0") - endif() -else() - message( - WARNING - "The compiler ${CMAKE_CXX_COMPILER_ID} is unsupported compiler! " - "Compilation has only been tested with Clang, and GCC.") -endif() - -# cmake-format: off -set(CXX_COMPILER_CONFIG ${CMAKE_CXX_COMPILER_ID}-${CMAKE_CXX_COMPILER_VERSION}) -string(TOLOWER ${CXX_COMPILER_CONFIG} CXX_COMPILER_CONFIG) -log_info("CXX compiler info") -log_info("------------------") -log_info("CMAKE_CXX_COMPILER ${CMAKE_CXX_COMPILER}") -log_info("CMAKE_CXX_COMPILER_ID ${CMAKE_CXX_COMPILER_ID}") -log_info("CMAKE_CXX_COMPILER_VERSION ${CMAKE_CXX_COMPILER_VERSION}") -log_info("CXX_COMPILER_CONFIG ${CXX_COMPILER_CONFIG}") -# cmake-format: on diff --git a/backend/cmake/ElasticaAddInterfaceLibraryHeaders.cmake b/backend/cmake/ElasticaAddInterfaceLibraryHeaders.cmake deleted file mode 100644 index 10b7d3176..000000000 --- a/backend/cmake/ElasticaAddInterfaceLibraryHeaders.cmake +++ /dev/null @@ -1,98 +0,0 @@ -# Distributed under the MIT License. -# See LICENSE.txt for details. -# Obtained with thanks from https://github.com/sxs-collaboration/spectre - -# Adds the header files to the target. -# -# Usage: -# -# add_interface_lib_headers( -# TARGET TARGET_NAME -# HEADERS -# A.hpp -# B.hpp -# C.hpp -# ) -# -# This function is intended to be used with libraries added using add_library -# or added by CMake's provided find_package (e.g. Boost). The -# add_spectre_library handles adding header files for targets correctly and -# so this function does not need to be used for libraries added with -# add_spectre_library. -function(add_interface_lib_headers) - cmake_parse_arguments( - ARG "" "TARGET" "HEADERS" - ${ARGN}) - - if (NOT TARGET ${ARG_TARGET}) - message(FATAL_ERROR - "Unknown target '${ARG_TARGET}'" - ) - endif (NOT TARGET ${ARG_TARGET}) - - get_target_property( - TARGET_TYPE - ${ARG_TARGET} - TYPE - ) - if (NOT ${TARGET_TYPE} STREQUAL INTERFACE_LIBRARY) - message(FATAL_ERROR - "The target '${ARG_TARGET}' is not an INTERFACE library and so " - "add_interface_lib_headers should not be used to add header files " - "to it." - ) - endif (NOT ${TARGET_TYPE} STREQUAL INTERFACE_LIBRARY) - - get_property( - ELASTICA_INTERFACE_LIBRARY_HEADERS - GLOBAL - PROPERTY ELASTICA_INTERFACE_LIBRARY_HEADERS - ) - - # Switch to delimiting the headers by ':' because CMake uses ';' to delimit - # elements of a list. - string(REPLACE ";" ":" TARGET_HEADERS - "${ARG_TARGET}=${ARG_HEADERS}") - list(APPEND ELASTICA_INTERFACE_LIBRARY_HEADERS ${TARGET_HEADERS}) - - set_property( - GLOBAL PROPERTY ELASTICA_INTERFACE_LIBRARY_HEADERS - ${ELASTICA_INTERFACE_LIBRARY_HEADERS} - ) -endfunction(add_interface_lib_headers) - -# Returns a list of all the header files for the target `TARGET` -# by setting the variable with the name `${RESULT_NAME}` -# -# Usage: -# get_target_headers(MyTarget MY_TARGET_HEADERS) -function(get_target_headers TARGET RESULT_NAME) - get_target_property( - TARGET_TYPE - ${TARGET} - TYPE - ) - if (${TARGET_TYPE} STREQUAL INTERFACE_LIBRARY) - get_property( - _ELASTICA_INTERFACE_LIBRARY_HEADERS - GLOBAL - PROPERTY ELASTICA_INTERFACE_LIBRARY_HEADERS - ) - foreach (_LIB ${_ELASTICA_INTERFACE_LIBRARY_HEADERS}) - string(REPLACE "${TARGET}=" "" _LIB_HEADERS ${_LIB}) - if (NOT ${_LIB} STREQUAL ${_LIB_HEADERS}) - string(REPLACE ":" ";" _LIB_HEADERS ${_LIB_HEADERS}) - set(${RESULT_NAME} ${_LIB_HEADERS} PARENT_SCOPE) - break() - endif (NOT ${_LIB} STREQUAL ${_LIB_HEADERS}) - endforeach (_LIB ${_ELASTICA_INTERFACE_LIBRARY_HEADERS}) - - else (${TARGET_TYPE} STREQUAL INTERFACE_LIBRARY) - get_property( - _HEADER_FILES - TARGET ${TARGET} - PROPERTY PUBLIC_HEADER - ) - set(${RESULT_NAME} ${_HEADER_FILES} PARENT_SCOPE) - endif (${TARGET_TYPE} STREQUAL INTERFACE_LIBRARY) -endfunction(get_target_headers TARGET) diff --git a/backend/cmake/ElasticaSetupFlagsTarget.cmake b/backend/cmake/ElasticaSetupFlagsTarget.cmake deleted file mode 100644 index f8b527aab..000000000 --- a/backend/cmake/ElasticaSetupFlagsTarget.cmake +++ /dev/null @@ -1,7 +0,0 @@ -# Distributed under the MIT License. -# See LICENSE.txt for details. - -# The library that contains all the flags for building elastica targets. -# Generator expressions should be used to limit flag scopes to specific -# languages and compilers. -add_library(ElasticaFlags INTERFACE) diff --git a/backend/cmake/EnableWarnings.cmake b/backend/cmake/EnableWarnings.cmake deleted file mode 100644 index 426371924..000000000 --- a/backend/cmake/EnableWarnings.cmake +++ /dev/null @@ -1,66 +0,0 @@ -# Enable warnings, aim for no warnings - -# Obtained with thanks from https://github.com/sxs-collaboration/spectre - -# Distributed under the MIT License. See LICENSE for details. - -include(AddCxxFlag) - -# On systems where we can't use -isystem (Cray), we don't want all the warnings -# enabled because we get flooded with system warnings. -option(ENABLE_WARNINGS "Enable the default warning level" ON) -if (${ENABLE_WARNINGS}) - create_cxx_flags_target( - "-W;\ --Wall;\ --Wextra;\ --Wpedantic;\ --Wcast-align;\ --Wcast-qual;\ --Wdisabled-optimization;\ --Wformat=2;\ --Wformat-nonliteral;\ --Wformat-security;\ --Wformat-y2k;\ --Winvalid-pch;\ --Wmissing-field-initializers;\ --Wmissing-format-attribute;\ --Wmissing-include-dirs;\ --Wmissing-noreturn;\ --Wno-documentation-unknown-command;\ --Wno-mismatched-tags;\ --Wnon-virtual-dtor;\ --Wold-style-cast;\ --Woverloaded-virtual;\ --Wpacked;\ --Wpointer-arith;\ --Wredundant-decls;\ --Wshadow;\ --Wsign-conversion;\ --Wstack-protector;\ --Wswitch-default;\ --Wunreachable-code;\ --Wwrite-strings;\ --Werror=switch" ElasticaWarnings) -endif () -# Disabled these two because of errors -#-Wdocumentation;\ -#-Wnewline-eof;\ - -# GCC 7.1ish and newer warn about noexcept changing mangled names, -# but we don't care -create_cxx_flag_target("-Wno-noexcept-type" ElasticaWarnNoNoexceptType) - -#check_and_add_cxx_link_flag("-Qunused-arguments") - -target_link_libraries( - ElasticaWarnings - INTERFACE - ElasticaWarnNoNoexceptType -) - -target_link_libraries( - ElasticaFlags - INTERFACE - ElasticaWarnings -) diff --git a/backend/cmake/FindBlaze.cmake b/backend/cmake/FindBlaze.cmake deleted file mode 100644 index 265a04218..000000000 --- a/backend/cmake/FindBlaze.cmake +++ /dev/null @@ -1,59 +0,0 @@ -# Distributed under the MIT License. See LICENSE.txt for details. -# Obtained with thanks from https://github.com/sxs-collaboration/spectre - -include(GetEnvPath) -getenv_path(BLAZE_ROOT) - -find_path(BLAZE_INCLUDE_DIR - PATH_SUFFIXES include - NAMES blaze/Blaze.h - HINTS ${BLAZE_ROOT} ${ENV_BLAZE_ROOT} - DOC "Blaze include directory. Used BLAZE_ROOT to set a search dir.") - -set(BLAZE_INCLUDE_DIRS ${BLAZE_INCLUDE_DIR}) -set(BLAZE_VERSION "") - -if (EXISTS "${BLAZE_INCLUDE_DIRS}/blaze/system/Version.h") - # Extract version info from header - file(READ "${BLAZE_INCLUDE_DIRS}/blaze/system/Version.h" - BLAZE_FIND_HEADER_CONTENTS) - - string(REGEX MATCH - "#define BLAZE_MAJOR_VERSION [0-9]+" - BLAZE_MAJOR_VERSION - "${BLAZE_FIND_HEADER_CONTENTS}") - string(REPLACE "#define BLAZE_MAJOR_VERSION " - "" - BLAZE_MAJOR_VERSION - ${BLAZE_MAJOR_VERSION}) - - string(REGEX MATCH - "#define BLAZE_MINOR_VERSION [0-9]+" - BLAZE_MINOR_VERSION - "${BLAZE_FIND_HEADER_CONTENTS}") - string(REPLACE "#define BLAZE_MINOR_VERSION " - "" - BLAZE_MINOR_VERSION - ${BLAZE_MINOR_VERSION}) - - set(BLAZE_VERSION "${BLAZE_MAJOR_VERSION}.${BLAZE_MINOR_VERSION}") -else () - message(WARNING "Failed to find file " - "'${BLAZE_INCLUDE_DIRS}/blaze/system/Version.h' " - "while detecting the Blaze version.") -endif () - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Blaze - FOUND_VAR - BLAZE_FOUND - REQUIRED_VARS - BLAZE_INCLUDE_DIR - BLAZE_INCLUDE_DIRS - VERSION_VAR - BLAZE_VERSION) -mark_as_advanced(BLAZE_INCLUDE_DIR - BLAZE_INCLUDE_DIRS - BLAZE_VERSION - BLAZE_MAJOR_VERSION - BLAZE_MINOR_VERSION) diff --git a/backend/cmake/FindBlazeTensor.cmake b/backend/cmake/FindBlazeTensor.cmake deleted file mode 100644 index 22063e66c..000000000 --- a/backend/cmake/FindBlazeTensor.cmake +++ /dev/null @@ -1,61 +0,0 @@ -# Distributed under the MIT License. See LICENSE.txt for details. -# Obtained with thanks from https://github.com/sxs-collaboration/spectre - -include(GetEnvPath) - -getenv_path(BLAZE_TENSOR_ROOT) - -find_path(BLAZE_TENSOR_INCLUDE_DIR - PATH_SUFFIXES include - NAMES blaze_tensor/Blaze.h - HINTS ${BLAZE_TENSOR_ROOT} ${ENV_BLAZE_TENSOR_ROOT} - DOC "Blaze Tensor include directory. Used BLAZE_TENSOR_ROOT to set a search dir.") - -set(BLAZE_TENSOR_INCLUDE_DIRS ${BLAZE_TENSOR_INCLUDE_DIR}) -set(BLAZE_TENSOR_VERSION "") - -if (EXISTS "${BLAZE_TENSOR_INCLUDE_DIRS}/blaze_tensor/system/Version.h") - # Extract version info from header - file(READ "${BLAZE_TENSOR_INCLUDE_DIRS}/blaze_tensor/system/Version.h" - BLAZE_TENSOR_FIND_HEADER_CONTENTS) - - string(REGEX MATCH - "#define BLAZE_TENSOR_MAJOR_VERSION [0-9]+" - BLAZE_TENSOR_MAJOR_VERSION - "${BLAZE_TENSOR_FIND_HEADER_CONTENTS}") - string(REPLACE "#define BLAZE_TENSOR_MAJOR_VERSION " - "" - BLAZE_TENSOR_MAJOR_VERSION - ${BLAZE_TENSOR_MAJOR_VERSION}) - - string(REGEX MATCH - "#define BLAZE_TENSOR_MINOR_VERSION [0-9]+" - BLAZE_TENSOR_MINOR_VERSION - "${BLAZE_TENSOR_FIND_HEADER_CONTENTS}") - string(REPLACE "#define BLAZE_TENSOR_MINOR_VERSION " - "" - BLAZE_TENSOR_MINOR_VERSION - ${BLAZE_TENSOR_MINOR_VERSION}) - - set(BLAZE_TENSOR_VERSION "${BLAZE_TENSOR_MAJOR_VERSION}.${BLAZE_TENSOR_MINOR_VERSION}") - -else () - message(WARNING "Failed to find file " - "'${BLAZE_TENSOR_INCLUDE_DIRS}/blaze_tensor/system/Version.h' " - "while detecting the BlazeTensor version.") -endif () - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(BlazeTensor - FOUND_VAR - BLAZETENSOR_FOUND - REQUIRED_VARS - BLAZE_TENSOR_INCLUDE_DIR - BLAZE_TENSOR_INCLUDE_DIRS - VERSION_VAR - BLAZE_TENSOR_VERSION) -mark_as_advanced(BLAZE_TENSOR_INCLUDE_DIR - BLAZE_TENSOR_INCLUDE_DIRS - BLAZE_TENSOR_VERSION - BLAZE_TENSOR_MAJOR_VERSION - BLAZE_TENSOR_MINOR_VERSION) diff --git a/backend/cmake/FindFilesystem.cmake b/backend/cmake/FindFilesystem.cmake deleted file mode 100644 index dc8f61b78..000000000 --- a/backend/cmake/FindFilesystem.cmake +++ /dev/null @@ -1,245 +0,0 @@ -# Distributed under the OSI-approved BSD 3-Clause License. See accompanying -# file Copyright.txt or https://cmake.org/licensing for details. - -# This is copied from: -# https://github.com/vector-of-bool/CMakeCM/blob/master/modules/FindFilesystem.cmake - -#[=======================================================================[.rst: - -FindFilesystem -############## - -This module supports the C++17 standard library's filesystem utilities. Use the -:imp-target:`std::filesystem` imported target to - -Options -******* - -The ``COMPONENTS`` argument to this module supports the following values: - -.. find-component:: Experimental - :name: fs.Experimental - - Allows the module to find the "experimental" Filesystem TS version of the - Filesystem library. This is the library that should be used with the - ``std::experimental::filesystem`` namespace. - -.. find-component:: Final - :name: fs.Final - - Finds the final C++17 standard version of the filesystem library. - -If no components are provided, behaves as if the -:find-component:`fs.Final` component was specified. - -If both :find-component:`fs.Experimental` and :find-component:`fs.Final` are -provided, first looks for ``Final``, and falls back to ``Experimental`` in case -of failure. If ``Final`` is found, :imp-target:`std::filesystem` and all -:ref:`variables ` will refer to the ``Final`` version. - - -Imported Targets -**************** - -.. imp-target:: std::filesystem - - The ``std::filesystem`` imported target is defined when any requested - version of the C++ filesystem library has been found, whether it is - *Experimental* or *Final*. - - If no version of the filesystem library is available, this target will not - be defined. - - .. note:: - This target has ``cxx_std_17`` as an ``INTERFACE`` - :ref:`compile language standard feature `. Linking - to this target will automatically enable C++17 if no later standard - version is already required on the linking target. - - -.. _fs.variables: - -Variables -********* - -.. variable:: CXX_FILESYSTEM_IS_EXPERIMENTAL - - Set to ``TRUE`` when the :find-component:`fs.Experimental` version of C++ - filesystem library was found, otherwise ``FALSE``. - -.. variable:: CXX_FILESYSTEM_HAVE_FS - - Set to ``TRUE`` when a filesystem header was found. - -.. variable:: CXX_FILESYSTEM_HEADER - - Set to either ``filesystem`` or ``experimental/filesystem`` depending on - whether :find-component:`fs.Final` or :find-component:`fs.Experimental` was - found. - -.. variable:: CXX_FILESYSTEM_NAMESPACE - - Set to either ``std::filesystem`` or ``std::experimental::filesystem`` - depending on whether :find-component:`fs.Final` or - :find-component:`fs.Experimental` was found. - - -Examples -******** - -Using `find_package(Filesystem)` with no component arguments: - -.. code-block:: cmake - - find_package(Filesystem REQUIRED) - - add_executable(my-program main.cpp) - target_link_libraries(my-program PRIVATE std::filesystem) - - -#]=======================================================================] - - -if(TARGET std::filesystem) - # This module has already been processed. Don't do it again. - return() -endif() - -include(CMakePushCheckState) -include(CheckIncludeFileCXX) -include(CheckCXXSourceCompiles) - -cmake_push_check_state() - -set(CMAKE_REQUIRED_QUIET ${Filesystem_FIND_QUIETLY}) - -# All of our tests required C++17 or later -#set(CMAKE_CXX_STANDARD 14) -list(APPEND CMAKE_REQUIRED_FLAGS "-std=c++14") - -# Normalize and check the component list we were given -set(want_components ${Filesystem_FIND_COMPONENTS}) -if(Filesystem_FIND_COMPONENTS STREQUAL "") - set(want_components Final) -endif() - -# Warn on any unrecognized components -set(extra_components ${want_components}) -list(REMOVE_ITEM extra_components Final Experimental) -foreach(component IN LISTS extra_components) - message(WARNING "Extraneous find_package component for Filesystem: ${component}") -endforeach() - -# Detect which of Experimental and Final we should look for -set(find_experimental TRUE) -set(find_final TRUE) -if(NOT "Final" IN_LIST want_components) - set(find_final FALSE) -endif() -if(NOT "Experimental" IN_LIST want_components) - set(find_experimental FALSE) -endif() - -if(find_final) - check_include_file_cxx("filesystem" _CXX_FILESYSTEM_HAVE_HEADER) - mark_as_advanced(_CXX_FILESYSTEM_HAVE_HEADER) - if(_CXX_FILESYSTEM_HAVE_HEADER) - # We found the non-experimental header. Don't bother looking for the - # experimental one. - set(find_experimental FALSE) - endif() -else() - set(_CXX_FILESYSTEM_HAVE_HEADER FALSE) -endif() - -if(find_experimental) - check_include_file_cxx("experimental/filesystem" _CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER) - mark_as_advanced(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER) -else() - set(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER FALSE) -endif() - -if(_CXX_FILESYSTEM_HAVE_HEADER) - set(_have_fs TRUE) - set(_fs_header filesystem) - set(_fs_namespace std::__fs::filesystem) -elseif(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER) - set(_have_fs TRUE) - set(_fs_header experimental/filesystem) - set(_fs_namespace std::experimental::filesystem) -else() - set(_have_fs FALSE) -endif() -#message("Filesystem" ${_have_fs}) -#message("Filesystem" ${_fs_header}) - -set(CXX_FILESYSTEM_HAVE_FS ${_have_fs} CACHE BOOL "TRUE if we have the C++ filesystem headers" FORCE) -set(CXX_FILESYSTEM_HEADER ${_fs_header} CACHE STRING "The header that should be included to obtain the filesystem APIs" FORCE) -set(CXX_FILESYSTEM_NAMESPACE ${_fs_namespace} CACHE STRING "The C++ namespace that contains the filesystem APIs" FORCE) - -message("Filesystem HAVE" ${CXX_FILESYSTEM_HAVE_FS}) -message("Filesystem" ${CXX_FILESYSTEM_HEADER}) -message("Filesystem Namespace" ${CXX_FILESYSTEM_NAMESPACE}) - -set(_found FALSE) - -if(CXX_FILESYSTEM_HAVE_FS) - # We have some filesystem library available. Do link checks - string(CONFIGURE [[ - #include <@CXX_FILESYSTEM_HEADER@> - - int main() { - auto cwd = @CXX_FILESYSTEM_NAMESPACE@::current_path(); - return static_cast(cwd.string().size()); - } - ]] code @ONLY) - - set(MYSTR "this is my str; having two;") - message(${MYSTR}) - - message(${code}) - # Try to compile a simple filesystem program without any linker flags - check_cxx_source_compiles("${code}" CXX_FILESYSTEM_NO_LINK_NEEDED) - - set(can_link ${CXX_FILESYSTEM_NO_LINK_NEEDED}) - - if(NOT CXX_FILESYSTEM_NO_LINK_NEEDED) - set(prev_libraries ${CMAKE_REQUIRED_LIBRARIES}) - # Add the libstdc++ flag - set(CMAKE_REQUIRED_LIBRARIES ${prev_libraries} -lstdc++fs) - message(${code}) - check_cxx_source_compiles("${code}" CXX_FILESYSTEM_STDCPPFS_NEEDED) - set(can_link ${CXX_FILESYSTEM_STDCPPFS_NEEDED}) - message("stdc++" ${CXX_FILESYSTEM_STDCPPFS_NEEDED}) - if(NOT CXX_FILESYSTEM_STDCPPFS_NEEDED) - # Try the libc++ flag - set(CMAKE_REQUIRED_LIBRARIES ${prev_libraries} -lc++fs) - message(${code}) - check_cxx_source_compiles("${code}" CXX_FILESYSTEM_CPPFS_NEEDED) - set(can_link ${CXX_FILESYSTEM_CPPFS_NEEDED}) - message("c++gs" ${CXX_FILESYSTEM_CPPFS_NEEDED}) - endif() - endif() - - if(can_link) - add_library(std::filesystem INTERFACE IMPORTED) - target_compile_features(std::filesystem INTERFACE cxx_std_14) - set(_found TRUE) - - if(CXX_FILESYSTEM_NO_LINK_NEEDED) - # Nothing to add... - elseif(CXX_FILESYSTEM_STDCPPFS_NEEDED) - target_link_libraries(std::filesystem INTERFACE -lstdc++fs) - elseif(CXX_FILESYSTEM_CPPFS_NEEDED) - target_link_libraries(std::filesystem INTERFACE -lc++fs) - endif() - endif() -endif() - -cmake_pop_check_state() - -set(Filesystem_FOUND ${_found} CACHE BOOL "TRUE if we can compile and link a program using std::filesystem" FORCE) - -if(Filesystem_FIND_REQUIRED AND NOT Filesystem_FOUND) - message(FATAL_ERROR "Cannot Compile simple program using std::filesystem") -endif() diff --git a/backend/cmake/FindSleef.cmake b/backend/cmake/FindSleef.cmake deleted file mode 100644 index 84e32f13a..000000000 --- a/backend/cmake/FindSleef.cmake +++ /dev/null @@ -1,147 +0,0 @@ -# =============================================== -# Do the final processing for the package find. -# =============================================== -macro(findpkg_finish PREFIX) - # skip if already processed during this run - if (NOT ${PREFIX}_FOUND) - if (${PREFIX}_INCLUDE_DIR AND ${PREFIX}_LIBRARY) - set(${PREFIX}_FOUND TRUE) - set(${PREFIX}_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIR}) - set(${PREFIX}_LIBRARIES ${${PREFIX}_LIBRARY}) - else () - if (${PREFIX}_FIND_REQUIRED AND NOT ${PREFIX}_FIND_QUIETLY) - message(FATAL_ERROR "Required library ${PREFIX} not found.") - endif () - endif () - - # mark the following variables as internal variables - mark_as_advanced(${PREFIX}_INCLUDE_DIR - ${PREFIX}_LIBRARY - ${PREFIX}_LIBRARY_DEBUG - ${PREFIX}_LIBRARY_RELEASE) - endif () -endmacro() - -# =============================================== -# See if we have env vars to help us find Sleef -# =============================================== -macro(getenv_path VAR) - set(ENV_${VAR} $ENV{${VAR}}) - # replace won't work if var is blank - if (ENV_${VAR}) - string(REGEX - REPLACE "\\\\" - "/" - ENV_${VAR} - ${ENV_${VAR}}) - endif () -endmacro() - -# ============================================================================= -# Now to actually find Sleef -# - -# Get path, convert backslashes as ${ENV_${var}} -getenv_path(SLEEF_ROOT) - -# initialize search paths -set(SLEEF_PREFIX_PATH ${SLEEF_ROOT} ${ENV_SLEEF_ROOT}) -set(SLEEF_INC_SEARCH_PATH "") -set(SLEEF_LIB_SEARCH_PATH "") - -# If user built from sources -set(SLEEF_BUILD_PREFIX $ENV{SLEEF_BUILD_PREFIX}) -if (SLEEF_BUILD_PREFIX AND ENV_SLEEF_ROOT) - getenv_path(SLEEF_BUILD_DIR) - if (NOT ENV_SLEEF_BUILD_DIR) - set(ENV_SLEEF_BUILD_DIR ${ENV_SLEEF_ROOT}/build) - endif () - - # include directory under ${ENV_SLEEF_ROOT}/include - list(APPEND SLEEF_LIB_SEARCH_PATH - ${ENV_SLEEF_BUILD_DIR}/${SLEEF_BUILD_PREFIX}_release - ${ENV_SLEEF_BUILD_DIR}/${SLEEF_BUILD_PREFIX}_debug) -endif () - -# add general search paths -foreach (dir - IN - LISTS - SLEEF_PREFIX_PATH) - list(APPEND SLEEF_LIB_SEARCH_PATH - ${dir}/lib - ${dir}/Lib - ${dir}/lib/sleef - ${dir}/Libs) - list(APPEND SLEEF_INC_SEARCH_PATH - ${dir}/include - ${dir}/Include - ${dir}/include/sleef) -endforeach () - -log_heavy_debug("SLEEF PREFIX : ${SLEEF_PREFIX_PATH}") -log_heavy_debug("SLEEF_INC : ${SLEEF_INC_SEARCH_PATH}") -log_heavy_debug("SLEEF_LIB : ${SLEEF_LIB_SEARCH_PATH}") - -set(SLEEF_LIBRARY_NAMES sleef) - -find_path(SLEEF_INCLUDE_DIR NAMES sleef.h PATHS ${SLEEF_INC_SEARCH_PATH}) - -log_heavy_debug("SLEEF_INCLUDE_DIR : ${SLEEF_INCLUDE_DIR}") - -find_library(SLEEF_LIBRARY - NAMES ${SLEEF_LIBRARY_NAMES} - PATHS ${SLEEF_LIB_SEARCH_PATH}) - -log_heavy_debug("SLEEF_FIND_LIBRARY : ${SLEEF_LIBRARY}") - -findpkg_finish(SLEEF) - -# if we haven't found Sleef no point on going any further -if (NOT SLEEF_FOUND) - return() -endif () - -# ============================================================================= -# parse all the version numbers from tbb -if (NOT SLEEF_VERSION) - - set(SLEEF_HEADER "${SLEEF_INCLUDE_DIR}/sleef.h") - - # only read the start of the file - file(READ ${SLEEF_HEADER} SLEEF_VERSION_CONTENTS - LIMIT 2048) - - string(REGEX - REPLACE ".*#define SLEEF_VERSION_MAJOR ([0-9]+).*" - "\\1" - SLEEF_VERSION_MAJOR - "${SLEEF_VERSION_CONTENTS}") - - string(REGEX - REPLACE ".*#define SLEEF_VERSION_MINOR ([0-9]+).*" - "\\1" - SLEEF_VERSION_MINOR - "${SLEEF_VERSION_CONTENTS}") - - string(REGEX - REPLACE ".*#define SLEEF_VERSION_PATCHLEVEL ([0-9]+).*" - "\\1" - SLEEF_VERSION_PATCH - "${SLEEF_VERSION_CONTENTS}") - - set(SLEEF_VERSION - "${SLEEF_VERSION_MAJOR}.${SLEEF_VERSION_MINOR}.${SLEEF_VERSION_PATCH}") -endif () - -set(Sleef_VERSION ${SLEEF_VERSION}) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args( - Sleef - FOUND_VAR SLEEF_FOUND - REQUIRED_VARS SLEEF_INCLUDE_DIRS SLEEF_LIBRARIES - VERSION_VAR SLEEF_VERSION) -mark_as_advanced(SLEEF_INCLUDE_DIRS SLEEF_LIBRARIES - SLEEF_VERSION_MAJOR SLEEF_VERSION_MINOR SLEEF_VERSION_PATCH - SLEEF_VERSION Sleef_VERSION) diff --git a/backend/cmake/FindTBB.cmake b/backend/cmake/FindTBB.cmake deleted file mode 100644 index 50065acda..000000000 --- a/backend/cmake/FindTBB.cmake +++ /dev/null @@ -1,417 +0,0 @@ -# * Find ThreadingBuildingBlocks include dirs and libraries Use this module by -# invoking find_package with the form: find_package(TBB [REQUIRED] # Fail with -# error if TBB is not found ) # Once done, this will -# define -# -# TBB_FOUND - system has TBB TBB_INCLUDE_DIRS - the TBB include directories -# TBB_LIBRARIES - TBB libraries to be lined, doesn't include malloc or malloc -# proxy -# -# TBB_VERSION_MAJOR - Major Product Version Number TBB_VERSION_MINOR - Minor -# Product Version Number TBB_INTERFACE_VERSION - Engineering Focused Version -# Number TBB_COMPATIBLE_INTERFACE_VERSION - The oldest major interface version -# still supported. This uses the engineering focused interface version numbers. -# -# TBB_MALLOC_FOUND - system has TBB malloc library TBB_MALLOC_INCLUDE_DIRS - the -# TBB malloc include directories TBB_MALLOC_LIBRARIES - The TBB malloc libraries -# to be lined -# -# TBB_MALLOC_PROXY_FOUND - system has TBB malloc proxy library -# TBB_MALLOC_PROXY_INCLUDE_DIRS = the TBB malloc proxy include directories -# TBB_MALLOC_PROXY_LIBRARIES - The TBB malloc proxy libraries to be lined -# -# This module reads hints about search locations from variables: ENV -# TBB_ARCH_PLATFORM - for eg. set it to "mic" for Xeon Phi builds ENV TBB_ROOT -# or just TBB_ROOT - root directory of tbb installation ENV TBB_BUILD_PREFIX - -# specifies the build prefix for user built tbb libraries. Should be specified -# with ENV TBB_ROOT and optionally... ENV TBB_BUILD_DIR - if build directory is -# different than ${TBB_ROOT}/build -# -# Modified by Robert Maynard from the original OGRE source -# -# ------------------------------------------------------------------- -# This file is part of the CMake build system for OGRE (Object-oriented Graphics -# Rendering Engine) For the latest info, see http://www.ogre3d.org/ -# -# The contents of this file are placed in the public domain. Feel free to make -# use of it in any way you like. -# ------------------------------------------------------------------- -# -# ============================================================================= -# Copyright 2010-2012 Kitware, Inc. Copyright 2012 Rolf Eike Beer -# -# Distributed under the OSI-approved BSD License (the "License"); see -# accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the implied -# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# License for more information. -# ============================================================================= -# (To distribute this file outside of CMake, substitute the full License text -# for the above reference.) - -# ============================================================================= -# FindTBB helper functions and macros -# - -# =============================================== -# Do the final processing for the package find. -# =============================================== -macro(findpkg_finish PREFIX) - # skip if already processed during this run - if(NOT ${PREFIX}_FOUND) - if(${PREFIX}_INCLUDE_DIR AND ${PREFIX}_LIBRARY) - set(${PREFIX}_FOUND TRUE) - set(${PREFIX}_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIR}) - set(${PREFIX}_LIBRARIES ${${PREFIX}_LIBRARY}) - else() - if(${PREFIX}_FIND_REQUIRED AND NOT ${PREFIX}_FIND_QUIETLY) - message(FATAL_ERROR "Required library ${PREFIX} not found.") - endif() - endif() - - # mark the following variables as internal variables - mark_as_advanced(${PREFIX}_INCLUDE_DIR - ${PREFIX}_LIBRARY - ${PREFIX}_LIBRARY_DEBUG - ${PREFIX}_LIBRARY_RELEASE) - endif() -endmacro() - -# =============================================== -# Generate debug names from given release names -# =============================================== -macro(get_debug_names PREFIX) - foreach(i ${${PREFIX}}) - set(${PREFIX}_DEBUG - ${${PREFIX}_DEBUG} - ${i}d - ${i}D - ${i}_d - ${i}_D - ${i}_debug - ${i}) - endforeach() -endmacro() - -# =============================================== -# See if we have env vars to help us find tbb -# =============================================== -macro(getenv_path VAR) - set(ENV_${VAR} $ENV{${VAR}}) - # replace won't work if var is blank - if(ENV_${VAR}) - string(REGEX - REPLACE "\\\\" - "/" - ENV_${VAR} - ${ENV_${VAR}}) - endif() -endmacro() - -# =============================================== -# Couple a set of release AND debug libraries -# =============================================== -macro(make_library_set PREFIX) - if(${PREFIX}_RELEASE AND ${PREFIX}_DEBUG) - set(${PREFIX} - optimized - ${${PREFIX}_RELEASE} - debug - ${${PREFIX}_DEBUG}) - elseif(${PREFIX}_RELEASE) - set(${PREFIX} ${${PREFIX}_RELEASE}) - elseif(${PREFIX}_DEBUG) - set(${PREFIX} ${${PREFIX}_DEBUG}) - endif() -endmacro() - -# ============================================================================= -# Now to actually find TBB -# - -# Get path, convert backslashes as ${ENV_${var}} -getenv_path(TBB_ROOT) - -# initialize search paths -set(TBB_PREFIX_PATH ${TBB_ROOT} ${ENV_TBB_ROOT}) -set(TBB_INC_SEARCH_PATH "") -set(TBB_LIB_SEARCH_PATH "") - -# If user built from sources -set(TBB_BUILD_PREFIX $ENV{TBB_BUILD_PREFIX}) -if(TBB_BUILD_PREFIX AND ENV_TBB_ROOT) - getenv_path(TBB_BUILD_DIR) - if(NOT ENV_TBB_BUILD_DIR) - set(ENV_TBB_BUILD_DIR ${ENV_TBB_ROOT}/build) - endif() - - # include directory under ${ENV_TBB_ROOT}/include - list(APPEND TBB_LIB_SEARCH_PATH - ${ENV_TBB_BUILD_DIR}/${TBB_BUILD_PREFIX}_release - ${ENV_TBB_BUILD_DIR}/${TBB_BUILD_PREFIX}_debug) -endif() - -# For Windows, let's assume that the user might be using the precompiled TBB -# packages from the main website. These use a rather awkward directory structure -# (at least for automatically finding the right files) depending on platform and -# compiler, but we'll do our best to accommodate it. Not adding the same effort -# for the precompiled linux builds, though. Those have different versions for CC -# compiler versions and linux kernels which will never adequately match the -# user's setup, so there is no feasible way to detect the "best" version to use. -# The user will have to manually select the right files. (Chances are the -# distributions are shipping their custom version of tbb, anyway, so the problem -# is probably nonexistent.) -if(WIN32 AND MSVC) - set(COMPILER_PREFIX "vc7.1") - if(MSVC_VERSION EQUAL 1400) - set(COMPILER_PREFIX "vc8") - elseif(MSVC_VERSION EQUAL 1500) - set(COMPILER_PREFIX "vc9") - elseif(MSVC_VERSION EQUAL 1600) - set(COMPILER_PREFIX "vc10") - elseif(MSVC_VERSION EQUAL 1700) - set(COMPILER_PREFIX "vc11") - elseif(MSVC_VERSION EQUAL 1800) - set(COMPILER_PREFIX "vc12") - elseif(MSVC_VERSION EQUAL 1900) - set(COMPILER_PREFIX "vc14") - endif() - - # for each prefix path, add ia32/64\${COMPILER_PREFIX}\lib to the lib search - # path - foreach(dir - IN - LISTS - TBB_PREFIX_PATH) - if(CMAKE_CL_64) - list(APPEND TBB_LIB_SEARCH_PATH ${dir}/ia64/${COMPILER_PREFIX}/lib) - list(APPEND TBB_LIB_SEARCH_PATH ${dir}/lib/ia64/${COMPILER_PREFIX}) - list(APPEND TBB_LIB_SEARCH_PATH - ${dir}/intel64/${COMPILER_PREFIX}/lib) - list(APPEND TBB_LIB_SEARCH_PATH - ${dir}/lib/intel64/${COMPILER_PREFIX}) - else() - list(APPEND TBB_LIB_SEARCH_PATH ${dir}/ia32/${COMPILER_PREFIX}/lib) - list(APPEND TBB_LIB_SEARCH_PATH ${dir}/lib/ia32/${COMPILER_PREFIX}) - endif() - endforeach() -endif() - -# For OS X binary distribution, choose libc++ based libraries for Mavericks -# (10.9) and above and AppleClang -if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" - AND NOT CMAKE_SYSTEM_VERSION VERSION_LESS 13.0) - set(USE_LIBCXX OFF) - cmake_policy(GET CMP0025 POLICY_VAR) - - if(POLICY_VAR STREQUAL "NEW") - if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") - set(USE_LIBCXX ON) - endif() - else() - if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - set(USE_LIBCXX ON) - endif() - endif() - - if(USE_LIBCXX) - foreach(dir - IN - LISTS - TBB_PREFIX_PATH) - list(APPEND TBB_LIB_SEARCH_PATH ${dir}/lib/libc++ ${dir}/libc++/lib) - endforeach() - endif() -endif() - -# check compiler ABI -if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - set(COMPILER_PREFIX) - if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.7) - list(APPEND COMPILER_PREFIX "gcc4.7") - endif() - if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.4) - list(APPEND COMPILER_PREFIX "gcc4.4") - endif() - list(APPEND COMPILER_PREFIX "gcc4.1") -elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(COMPILER_PREFIX) - if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.6) - list(APPEND COMPILER_PREFIX "gcc4.7") - endif() - list(APPEND COMPILER_PREFIX "gcc4.4") -else() # Assume compatibility with 4.4 for other compilers - list(APPEND COMPILER_PREFIX "gcc4.4") -endif() - -# if platform architecture is explicitly specified -set(TBB_ARCH_PLATFORM $ENV{TBB_ARCH_PLATFORM}) -if(TBB_ARCH_PLATFORM) - foreach(dir - IN - LISTS - TBB_PREFIX_PATH) - list(APPEND TBB_LIB_SEARCH_PATH ${dir}/${TBB_ARCH_PLATFORM}/lib) - list(APPEND TBB_LIB_SEARCH_PATH ${dir}/lib/${TBB_ARCH_PLATFORM}) - endforeach() -endif() - -foreach(dir - IN - LISTS - TBB_PREFIX_PATH) - foreach(prefix - IN - LISTS - COMPILER_PREFIX) - if(CMAKE_SIZEOF_VOID_P EQUAL 8) - list(APPEND TBB_LIB_SEARCH_PATH ${dir}/lib/intel64) - list(APPEND TBB_LIB_SEARCH_PATH ${dir}/lib/intel64/${prefix}) - list(APPEND TBB_LIB_SEARCH_PATH ${dir}/intel64/lib) - list(APPEND TBB_LIB_SEARCH_PATH ${dir}/intel64/${prefix}/lib) - else() - list(APPEND TBB_LIB_SEARCH_PATH ${dir}/lib/ia32) - list(APPEND TBB_LIB_SEARCH_PATH ${dir}/lib/ia32/${prefix}) - list(APPEND TBB_LIB_SEARCH_PATH ${dir}/ia32/lib) - list(APPEND TBB_LIB_SEARCH_PATH ${dir}/ia32/${prefix}/lib) - endif() - endforeach() -endforeach() - -# add general search paths -foreach(dir - IN - LISTS - TBB_PREFIX_PATH) - list(APPEND TBB_LIB_SEARCH_PATH - ${dir}/lib - ${dir}/Lib - ${dir}/lib/tbb - ${dir}/Libs) - list(APPEND TBB_INC_SEARCH_PATH - ${dir}/include - ${dir}/Include - ${dir}/include/tbb) -endforeach() - -set(TBB_LIBRARY_NAMES tbb) -get_debug_names(TBB_LIBRARY_NAMES) - -find_path(TBB_INCLUDE_DIR NAMES tbb/tbb.h PATHS ${TBB_INC_SEARCH_PATH}) - -find_library(TBB_LIBRARY_RELEASE - NAMES ${TBB_LIBRARY_NAMES} - PATHS ${TBB_LIB_SEARCH_PATH}) -# Need not find debug libraries to pass on to INTERFACE_LINK_LIBRARIES -#find_library(TBB_LIBRARY_DEBUG -# NAMES ${TBB_LIBRARY_NAMES_DEBUG} -# PATHS ${TBB_LIB_SEARCH_PATH}) -make_library_set(TBB_LIBRARY) - -findpkg_finish(TBB) - -# if we haven't found TBB no point on going any further -if(NOT TBB_FOUND) - return() -endif() - -# ============================================================================= -# Look for TBB's malloc package -set(TBB_MALLOC_LIBRARY_NAMES tbbmalloc) -get_debug_names(TBB_MALLOC_LIBRARY_NAMES) - -find_path(TBB_MALLOC_INCLUDE_DIR NAMES tbb/tbb.h PATHS ${TBB_INC_SEARCH_PATH}) - -find_library(TBB_MALLOC_LIBRARY_RELEASE - NAMES ${TBB_MALLOC_LIBRARY_NAMES} - PATHS ${TBB_LIB_SEARCH_PATH}) -# Need not find debug libraries to pass on to INTERFACE_LINK_LIBRARIES -#find_library(TBB_MALLOC_LIBRARY_DEBUG -# NAMES ${TBB_MALLOC_LIBRARY_NAMES_DEBUG} -# PATHS ${TBB_LIB_SEARCH_PATH}) -make_library_set(TBB_MALLOC_LIBRARY) - -findpkg_finish(TBB_MALLOC) - -# ============================================================================= -# Look for TBB's malloc proxy package -set(TBB_MALLOC_PROXY_LIBRARY_NAMES tbbmalloc_proxy) -get_debug_names(TBB_MALLOC_PROXY_LIBRARY_NAMES) - -find_path(TBB_MALLOC_PROXY_INCLUDE_DIR - NAMES tbb/tbbmalloc_proxy.h - PATHS ${TBB_INC_SEARCH_PATH}) - -find_library(TBB_MALLOC_PROXY_LIBRARY_RELEASE - NAMES ${TBB_MALLOC_PROXY_LIBRARY_NAMES} - PATHS ${TBB_LIB_SEARCH_PATH}) -# Need not find debug libraries to pass on to INTERFACE_LINK_LIBRARIES -#find_library(TBB_MALLOC_PROXY_LIBRARY_DEBUG -# NAMES ${TBB_MALLOC_PROXY_LIBRARY_NAMES_DEBUG} -# PATHS ${TBB_LIB_SEARCH_PATH}) -make_library_set(TBB_MALLOC_PROXY_LIBRARY) - -findpkg_finish(TBB_MALLOC_PROXY) - -# ============================================================================= -# parse all the version numbers from tbb -if(NOT TBB_VERSION) - - set(TBB_STDDEF_FILE "${TBB_INCLUDE_DIR}/tbb/tbb_stddef.h") - - if (EXISTS ${TBB_STDDEF_FILE}) - # For earlier versions, the filename is tbb_stddef.h - set(TBB_VERSIONS_FILE ${TBB_STDDEF_FILE}) - else() - # For later versions, the filename is version.h - set(TBB_VERSIONS_FILE "${TBB_INCLUDE_DIR}/oneapi/tbb/version.h") - endif() - - # only read the start of the file - file(READ ${TBB_VERSIONS_FILE} TBB_VERSION_CONTENTS - LIMIT 2048) - - string(REGEX - REPLACE ".*#define TBB_VERSION_MAJOR ([0-9]+).*" - "\\1" - TBB_VERSION_MAJOR - "${TBB_VERSION_CONTENTS}") - - string(REGEX - REPLACE ".*#define TBB_VERSION_MINOR ([0-9]+).*" - "\\1" - TBB_VERSION_MINOR - "${TBB_VERSION_CONTENTS}") - - ## Don't rely on the patch version, as old tbb libraries don't provide one - # string(REGEX - # REPLACE ".*#define TBB_INTERFACE_VERSION ([0-9]+).*" - # "\\1" - # TBB_INTERFACE_VERSION - # "${TBB_VERSION_CONTENTS}") - # math(EXPR TBB_VERSION_MAJOR "${TBB_INTERFACE_VERSION}/1000") - # math(EXPR TBB_VERSION_MINOR "(${TBB_INTERFACE_VERSION}%1000)/10") - - set(TBB_VERSION - ${TBB_VERSION_MAJOR}.${TBB_VERSION_MINOR}) - -endif() - - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(TBB - FOUND_VAR - TBB_FOUND - REQUIRED_VARS - TBB_INCLUDE_DIR - TBB_INCLUDE_DIRS - VERSION_VAR - TBB_VERSION) -mark_as_advanced(TBB_INCLUDE_DIR - TBB_INCLUDE_DIRS - TBB_MALLOC_INCLUDE_DIRS - TBB_VERSION - TBB_VERSION_MAJOR - TBB_VERSION_MINOR) diff --git a/backend/cmake/FindYamlCpp.cmake b/backend/cmake/FindYamlCpp.cmake deleted file mode 100644 index d6c591705..000000000 --- a/backend/cmake/FindYamlCpp.cmake +++ /dev/null @@ -1,42 +0,0 @@ -# Distributed under the MIT License. -# See LICENSE.txt for details. - -# Find yaml-cpp: https://github.com/jbeder/yaml-cpp -# If not in one of the default paths specify -D YAMLCPP_ROOT=/path/to/yaml-cpp -# to search there as well. -# Static libraries can be use by setting -D YAMLCPP_STATIC_LIBRARY=ON - -include(CheckCXXSourceRuns) -include(GetEnvPath) - -# Get path, convert backslashes as ${ENV_${var}} -getenv_path(YamlCpp_ROOT) - -if (NOT YamlCpp_ROOT) - # Need to set to empty to avoid warnings with --warn-uninitialized - set(YamlCpp_ROOT "") -endif () - -# find the yaml-cpp include directory -find_path(YamlCpp_INCLUDE_DIRS - PATH_SUFFIXES include - NAMES yaml-cpp/yaml.h - HINTS ${YamlCpp_ROOT} ${ENV_YamlCpp_ROOT} - DOC "YamlCpp include directory. Use YamlCpp_ROOT to set a search dir.") - -if (YamlCpp_STATIC_LIBRARY) - set(YamlCpp_STATIC libyaml-cpp.a) -else (YamlCpp_STATIC_LIBRARY) - # Silence CMake uninitialized variable warning - set(YamlCpp_STATIC "") -endif () - -find_library(YamlCpp_LIBRARIES - PATH_SUFFIXES lib64 lib build - NAMES ${YamlCpp_STATIC} yaml-cpp - HINTS ${YamlCpp_ROOT} ${ENV_YamlCpp_ROOT}) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(YamlCpp - DEFAULT_MSG YamlCpp_INCLUDE_DIRS YamlCpp_LIBRARIES) -mark_as_advanced(YamlCpp_INCLUDE_DIRS YamlCpp_LIBRARIES) diff --git a/backend/cmake/GetEnvPath.cmake b/backend/cmake/GetEnvPath.cmake deleted file mode 100644 index 3855cedf1..000000000 --- a/backend/cmake/GetEnvPath.cmake +++ /dev/null @@ -1,15 +0,0 @@ -# ================================================ -# See if we have env vars to help us find packages -# ================================================ -# Gets path, convert backslashes as ${ENV_${var}} -macro(getenv_path VAR) - set(ENV_${VAR} $ENV{${VAR}}) - # replace won't work if var is blank - if(ENV_${VAR}) - string(REGEX - REPLACE "\\\\" - "/" - ENV_${VAR} - ${ENV_${VAR}}) - endif() -endmacro() diff --git a/backend/cmake/Logging.cmake b/backend/cmake/Logging.cmake deleted file mode 100644 index 0fec09f30..000000000 --- a/backend/cmake/Logging.cmake +++ /dev/null @@ -1,100 +0,0 @@ -# Utilities for writing cmake log messages. - -# Distributed under the MIT License. See LICENSE for details. - -# Global variables -set(error_log_level "1" CACHE INTERNAL "Error log level") -set(warning_log_level "2" CACHE INTERNAL "Warning log level") -set(info_log_level "3" CACHE INTERNAL "Info log level") -set(debug_log_level "4" CACHE INTERNAL "Debug log level") -set(heavy_debug_log_level "5" CACHE INTERNAL "Heavy_debug log level") - -set(current_log_level "${info_log_level}" CACHE INTERNAL "Current log level") - -function(log_message message) - if(LOG_MESSAGE_STATUS) - set(LOG_MESSAGE_PREFIX "[${PROJECT_NAME}]") - message(STATUS "${LOG_MESSAGE_PREFIX} ${message}") - endif() -endfunction(log_message) - -function(set_logs_on) - set(LOG_MESSAGE_STATUS "ON" CACHE INTERNAL "Status for log messages") -endfunction(set_logs_on) - -function(set_logs_off) - set(LOG_MESSAGE_STATUS "OFF" CACHE INTERNAL "Status for log messages") -endfunction(set_logs_off) - -function(set_log_level log_level) - if(${log_level} MATCHES "^[0-9]+$") - if((${log_level} EQUAL ${error_log_level}) - OR (${log_level} GREATER ${error_log_level})) - set(current_log_level - "${log_level}" - CACHE INTERNAL "Current log level") - endif() - else() - if(${log_level} STREQUAL "ERROR") - set(local_log_level "${error_log_level}") - elseif(${log_level} STREQUAL "WARNING") - set(local_log_level "${warning_log_level}") - elseif(${log_level} STREQUAL "INFO") - set(local_log_level "${info_log_level}") - elseif(${log_level} STREQUAL "DEBUG") - set(local_log_level "${debug_log_level}") - elseif(${log_level} STREQUAL "HEAVYDEBUG") - set(local_log_level "${heavy_debug_log_level}") - endif() - set(current_log_level - "${local_log_level}" - CACHE INTERNAL "Current log level") - endif() -endfunction(set_log_level) - -# set_log_level( "${info_log_level}" ) - -function(log_error message) - set(message_log_level "${error_log_level}") - if((${current_log_level} EQUAL ${message_log_level}) - OR (${current_log_level} GREATER ${message_log_level})) - set(LOG_MESSAGE_PREFIX "[${PROJECT_NAME}]") - message(STATUS "${LOG_MESSAGE_PREFIX} ${message}") - endif() -endfunction(log_error) - -function(log_warning message) - set(message_log_level "${warning_log_level}") - if((${current_log_level} EQUAL ${message_log_level}) - OR (${current_log_level} GREATER ${message_log_level})) - set(LOG_MESSAGE_PREFIX "[${PROJECT_NAME}]") - message(STATUS "${LOG_MESSAGE_PREFIX} ${message}") - endif() -endfunction(log_warning) - -function(log_info message) - set(message_log_level "${info_log_level}") - if((${current_log_level} EQUAL ${message_log_level}) - OR (${current_log_level} GREATER ${message_log_level})) - set(LOG_MESSAGE_PREFIX "[${PROJECT_NAME}]") - message(STATUS "${LOG_MESSAGE_PREFIX} ${message}") - endif() -endfunction(log_info) - -function(log_debug message) - set(message_log_level "${debug_log_level}") - if((${current_log_level} EQUAL ${message_log_level}) - OR (${current_log_level} GREATER ${message_log_level})) - set(LOG_MESSAGE_PREFIX "[${PROJECT_NAME}]") - message(STATUS "${LOG_MESSAGE_PREFIX} ${message}") - endif() -endfunction(log_debug) - -function(log_heavy_debug message) - set(message_log_level "${heavy_debug_log_level}") - if((${current_log_level} EQUAL ${message_log_level}) - OR (${current_log_level} GREATER ${message_log_level})) - set(LOG_MESSAGE_PREFIX "[${PROJECT_NAME}]") - message(STATUS "${LOG_MESSAGE_PREFIX} ${message}") - endif() -endfunction(log_heavy_debug) diff --git a/backend/cmake/PrintUsefulCMakeInfo.cmake b/backend/cmake/PrintUsefulCMakeInfo.cmake deleted file mode 100644 index db2163d2e..000000000 --- a/backend/cmake/PrintUsefulCMakeInfo.cmake +++ /dev/null @@ -1,63 +0,0 @@ -# Prints information for ease in backtracking - -# Obtained with thanks from https://github.com/sxs-collaboration/spectre - -# Distributed under the MIT License. See LICENSE for details. - -log_info("Project Information") -log_info("-------------------") -log_info("-> PROJECT_NAME: ${PROJECT_NAME}") -log_info("-> PROJECT_VERSION: ${PROJECT_VERSION}") -log_info("-> PROJECT_VERSION_MAJOR: ${PROJECT_VERSION_MAJOR}") -log_info("-> PROJECT_VERSION_MINOR: ${PROJECT_VERSION_MINOR}") -log_info("-> PROJECT_VERSION_PATCH: ${PROJECT_VERSION_PATCH}") -log_info("-> GIT_BRANCH: ${GIT_BRANCH}") -log_info("-> GIT_HASH: ${GIT_HASH}") -log_info("-> GIT_DESCRIPTION: ${GIT_DESCRIPTION}") - -log_info("Build Information") -log_info("-----------------") -log_info("-> BUILD_EXAMPLES ${ELASTICA_BUILD_EXAMPLES}") -log_info("-> BUILD_BENCHMARKS ${ELASTICA_BUILD_BENCHMARKS}") -log_info("-> BUILD_TESTS ${ELASTICA_BUILD_TESTS}") -log_info("-> BUILD_DOCS ${ELASTICA_BUILD_DOCUMENTATION}") -log_info("-> BUILD_PYTHON ${ELASTICA_BUILD_PYTHON_BINDINGS}") -log_info("-> Build Directory: ${CMAKE_BINARY_DIR}") -log_info("-> Source Directory: ${CMAKE_SOURCE_DIR}") -log_info("-> Bin Directory: ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") -log_info("-> CMake Modules Path: ${CMAKE_MODULE_PATH}") -log_info("-> Operating System: ${CMAKE_SYSTEM_NAME}") - -log_info("Compiler Information") -log_info("--------------------") -log_info("-> CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}") -log_info("-> CMAKE_CXX_LINK_FLAGS: ${CMAKE_CXX_LINK_FLAGS}") -log_info("-> CMAKE_CXX_FLAGS_DEBUG: ${CMAKE_CXX_FLAGS_DEBUG}") -log_info("-> CMAKE_CXX_FLAGS_RELEASE:${CMAKE_CXX_FLAGS_RELEASE}") -log_info("-> CMAKE_EXE_LINKER_FLAGS: ${CMAKE_EXE_LINKER_FLAGS}") -log_info("-> CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") -get_property(ETP GLOBAL PROPERTY ELASTICA_THIRD_PARTY_LIBS) -list(JOIN ETP " " ETP) -log_info("-> ELASTICA_LIBRARIES: ${ETP}") -unset(ETP) -log_info("-> USE_SYSTEM_INCLUDE: ${USE_SYSTEM_INCLUDE}") -# message(STATUS "USE_PCH: ${USE_PCH}") - -# if (PYTHONINTERP_FOUND) message(STATUS "Python: " ${PYTHON_EXECUTABLE}) -# message(STATUS "Python Version: ${PYTHON_VERSION_STRING}") else() -# message(STATUS "Python: Not found") endif() - -# if(CLANG_TIDY_BIN) message(STATUS "Found clang-tidy: ${CLANG_TIDY_BIN}") -# elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") message( STATUS "Could not find -# clang-tidy even though LLVM clang is installed" ) endif() - -# if (CODE_COVERAGE) message(STATUS "Code coverage enabled. All prerequisites -# found:") message(STATUS " gcov: ${GCOV}") message(STATUS " lcov: ${LCOV}") -# message(STATUS " genhtml: ${GENHTML}") message(STATUS " sed: ${SED}") -# endif() - -if (DOXYGEN_FOUND) - log_info("Doxygen: " ${DOXYGEN_EXECUTABLE}) -else () - log_info("Doxygen: Not found, documentation cannot be built.") -endif () diff --git a/backend/cmake/SetBuildType.cmake b/backend/cmake/SetBuildType.cmake deleted file mode 100644 index 223cfb26c..000000000 --- a/backend/cmake/SetBuildType.cmake +++ /dev/null @@ -1,76 +0,0 @@ -# Set type of build -# Distributed under the MIT License. See LICENSE for details. -# Obtained with thanks from https://github.com/sxs-collaboration/spectre - -# CMake lets the user define CMAKE_BUILD_TYPE on the command line and recognizes -# the values "Debug", "Release", "RelWithDebInfo", and "MinSizeRel", which add -# specific compiler or linker flags. CMake's default behavior is to add no -# additional compiler or linker flags if the user does not define -# CMAKE_BUILD_TYPE on the command line, or passes an unrecognized value. -# We add a sanity check that checks if CMAKE_BUILD_TYPE is one of the recognized -# values, and we also set the CMAKE_BUILD_TYPE to "Debug" if the user does not -# specify it on the command line. In addition, we add "None" as a valid value -# for CMAKE_BUILD_TYPE whose behavior is to add no additional compiler or linker -# flags. This is done by defining the following flags analagous to those used by -# the other build types. Additional build types can be defined in a similar -# manner by defining the appropriate flags, and adding the name of the build -# type to CMAKE_BUILD_TYPES below. -set(CMAKE_CXX_FLAGS_NONE "" CACHE STRING - "Additional flags used by the compiler for Build type None." - FORCE) - -set(CMAKE_C_FLAGS_NONE "" CACHE STRING - "Additional flags used by the compiler for Build type None." - FORCE) - -set(CMAKE_EXE_LINKER_FLAGS_NONE "" CACHE STRING - "Additional flags used by the linker for Build type None." - FORCE) - -set(CMAKE_MODULE_LINKER_FLAGS_NONE "" CACHE STRING - "Additional flags used by the linker for Build type None." - FORCE) - -set(CMAKE_SHARED_LINKER_FLAGS_NONE "" CACHE STRING - "Additional flags used by the linker for Build type None." - FORCE) - -set(CMAKE_STATIC_LINKER_FLAGS_NONE "" CACHE STRING - "Additional flags used by the linker for Build type None." - FORCE) - -mark_as_advanced( - CMAKE_CXX_FLAGS_NONE - CMAKE_C_FLAGS_NONE - CMAKE_EXE_LINKER_FLAGS_NONE - CMAKE_MODULE_LINKER_FLAGS_NONE - CMAKE_SHARED_LINKER_FLAGS_NONE - CMAKE_STATIC_LINKER_FLAGS_NONE -) - - -set(CMAKE_BUILD_TYPES - "Debug" - "Release" - "None" - "RelWithDebInfo" - "MinSizeRel") - -if (NOT CMAKE_BUILD_TYPE) - log_info("-> CMAKE_BUILD_TYPE not specified, setting to 'Debug'") - set(CMAKE_BUILD_TYPE - Debug - CACHE STRING "Choose the type of build: ${CMAKE_BUILD_TYPES}" FORCE) -else () - if (NOT ${CMAKE_BUILD_TYPE} IN_LIST CMAKE_BUILD_TYPES) - message( - FATAL_ERROR - "\n" "Invalid value for CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}\n" - "Valid values: ${CMAKE_BUILD_TYPES}\n") - endif () - # message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") -endif () - -# cmake-format: off -log_info("CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE}") -# cmake-format: on diff --git a/backend/cmake/SetCxxStandard.cmake b/backend/cmake/SetCxxStandard.cmake deleted file mode 100644 index f80e7e3fb..000000000 --- a/backend/cmake/SetCxxStandard.cmake +++ /dev/null @@ -1,7 +0,0 @@ -# Sets standard, C++14 for now - -# Distributed under the MIT License. See LICENSE for details. - -set(CMAKE_CXX_STANDARD 14) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/backend/cmake/SetOutputDirectory.cmake b/backend/cmake/SetOutputDirectory.cmake deleted file mode 100644 index d5ef81a13..000000000 --- a/backend/cmake/SetOutputDirectory.cmake +++ /dev/null @@ -1,27 +0,0 @@ -# Set the directory where the library and executables are placed The default can -# be overridden by specifying `-D CMAKE_RUNTIME_OUTPUT_DIRECTORY=/path/` - -# Distributed under the MIT License. See LICENSE for details. -# Obtained with thanks from https://github.com/sxs-collaboration/spectre - -if (NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY) - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY - "${CMAKE_BINARY_DIR}/bin/" - CACHE STRING "Choose the directory where executables are placed" FORCE) -endif (NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY) - -if (NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY) - set( - CMAKE_LIBRARY_OUTPUT_DIRECTORY - "${CMAKE_BINARY_DIR}/lib/" - CACHE STRING "Choose the directory where shared libraries are placed" FORCE - ) -endif (NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY) - -if (NOT CMAKE_ARCHIVE_OUTPUT_DIRECTORY) - set( - CMAKE_ARCHIVE_OUTPUT_DIRECTORY - "${CMAKE_BINARY_DIR}/lib/" - CACHE STRING "Choose the directory where static libraries are placed" FORCE - ) -endif (NOT CMAKE_ARCHIVE_OUTPUT_DIRECTORY) diff --git a/backend/cmake/SetupBlaze.cmake b/backend/cmake/SetupBlaze.cmake deleted file mode 100644 index 85091a856..000000000 --- a/backend/cmake/SetupBlaze.cmake +++ /dev/null @@ -1,85 +0,0 @@ -# Distributed under the MIT License. See LICENSE.txt for details. -# Obtained with thanks from https://github.com/sxs-collaboration/spectre - -option(USE_SLEEF "Use Sleef to add more vectorized instructions." OFF) - -log_info("Finding Blaze") - -find_package(Blaze 3.9 REQUIRED QUIET) - -log_debug("BLAZE_INCLUDE_DIR: ${BLAZE_INCLUDE_DIR}") -log_debug("BLAZE_VERSION: ${BLAZE_VERSION}") - -file(APPEND "${CMAKE_BINARY_DIR}/BuildInformation.txt" - "Blaze Version: ${BLAZE_VERSION}\n") - -#elastica_include_directories("${BLAZE_INCLUDE_DIR}") -add_library(Blaze INTERFACE IMPORTED) -set_property(TARGET Blaze PROPERTY - INTERFACE_INCLUDE_DIRECTORIES ${BLAZE_INCLUDE_DIR}) - -set(_BLAZE_USE_SLEEF 0) - -if (USE_SLEEF) - # Try to find Sleef to increase vectorization - include(SetupSleef) -endif () - -if (SLEEF_FOUND) - target_link_libraries( - Blaze - INTERFACE - Sleef - ) - set(_BLAZE_USE_SLEEF 1) -endif () - -# Configure Blaze. Some of the Blaze configuration options could be optimized -# for the machine we are running on. See documentation: -# https://bitbucket.org/blaze-lib/blaze/wiki/Configuration%20and%20Installation#!step-2-configuration -target_compile_definitions(Blaze - INTERFACE - # - Enable external BLAS kernels - BLAZE_BLAS_MODE=0 - # - Set default matrix storage order to column-major, since many of our - # functions are implemented for column-major layout. This default reduces - # conversions. - BLAZE_DEFAULT_STORAGE_ORDER=blaze::rowMajor - # - Disable SMP parallelization. This disables SMP parallelization for all - # possible backends (OpenMP, C++11 threads, Boost, HPX): - # https://bitbucket.org/blaze-lib/blaze/wiki/Serial%20Execution#!option-3-deactivation-of-parallel-execution - BLAZE_USE_SHARED_MEMORY_PARALLELIZATION=0 - # - Disable MPI parallelization - BLAZE_MPI_PARALLEL_MODE=0 - # - Using the default cache size, which may have been configured automatically - # by the Blaze CMake configuration for the machine we are running on. We - # could override it here explicitly to tune performance. - # BLAZE_CACHE_SIZE - BLAZE_USE_PADDING=1 - # - Always enable non-temporal stores for cache optimization of large data - # structures: https://bitbucket.org/blaze-lib/blaze/wiki/Configuration%20Files#!streaming-non-temporal-stores - BLAZE_USE_STREAMING=1 - # - Initializing default-constructed structures for fundamental types - BLAZE_USE_DEFAULT_INITIALIZATON=1 - # Use Sleef for vectorization of more math functions - BLAZE_USE_SLEEF=${_BLAZE_USE_SLEEF} - # Set inlining settings - BLAZE_USE_STRONG_INLINE=1 - BLAZE_USE_ALWAYS_INLINE=1 - # Set vectorization (leave to 1, else there is no use for blaze) - BLAZE_USE_VECTORIZATION=1 - ) - -add_interface_lib_headers( - TARGET Blaze - HEADERS - blaze/math/DynamicMatrix.h - blaze/math/DynamicVector.h - blaze/math/StaticVector.h - blaze/system/Version.h -) - -set_property( - GLOBAL APPEND PROPERTY ELASTICA_THIRD_PARTY_LIBS - Blaze -) diff --git a/backend/cmake/SetupBlazeTensor.cmake b/backend/cmake/SetupBlazeTensor.cmake deleted file mode 100644 index 673ea083b..000000000 --- a/backend/cmake/SetupBlazeTensor.cmake +++ /dev/null @@ -1,34 +0,0 @@ -# Distributed under the MIT License. See LICENSE.txt for details. -# Obtained with thanks from https://github.com/sxs-collaboration/spectre - -log_info("Finding Blaze Tensor") - -find_package(BlazeTensor 0.1 REQUIRED QUIET) - -log_debug("BLAZE_TENSOR_INCLUDE_DIR: ${BLAZE_TENSOR_INCLUDE_DIR}") -log_debug("BLAZE_TENSOR_VERSION: ${BLAZE_TENSOR_VERSION}") - -file(APPEND "${CMAKE_BINARY_DIR}/BuildInformation.txt" - "BlazeTensor Version: ${BLAZE_TENSOR_VERSION}\n") - -#elastica_include_directories("${BLAZE_INCLUDE_DIR}") -add_library(BlazeTensor INTERFACE IMPORTED) -set_property(TARGET BlazeTensor PROPERTY - INTERFACE_INCLUDE_DIRECTORIES ${BLAZE_TENSOR_INCLUDE_DIR}) - -add_interface_lib_headers( - TARGET BlazeTensor - HEADERS - blaze_tensor/Blaze.h - blaze_tensor/math/DynamicTensor.h - blaze_tensor/math/StaticTensor.h - blaze_tensor/math/SubTensor.h - blaze_tensor/math/views/ColumnSlice.h - # for the version number in CMakelists - blaze_tensor/system/Version.h -) - -set_property( - GLOBAL APPEND PROPERTY ELASTICA_THIRD_PARTY_LIBS - BlazeTensor -) diff --git a/backend/cmake/SetupCxxFlags.cmake b/backend/cmake/SetupCxxFlags.cmake deleted file mode 100644 index 67989c0d3..000000000 --- a/backend/cmake/SetupCxxFlags.cmake +++ /dev/null @@ -1,57 +0,0 @@ -# Setup all relevant compiler flags - -# Obtained with thanks from https://github.com/sxs-collaboration/spectre - -# Distributed under the MIT License. See LICENSE for details. - -option(DEBUG_SYMBOLS "Add -g to CMAKE_CXX_FLAGS if ON, -g0 if OFF." ON) - -option(OVERRIDE_ARCH "The architecture to use. Default is native." OFF) - -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DELASTICA_DEBUG") - -if (NOT ${DEBUG_SYMBOLS}) - string(REPLACE "-g " - "-g0 " - CMAKE_CXX_FLAGS_DEBUG - ${CMAKE_CXX_FLAGS_DEBUG}) -endif (NOT ${DEBUG_SYMBOLS}) - -# Always build with -g so we can view backtraces, etc. when production code -# fails. This can be overridden by passing `-D DEBUG_SYMBOLS=OFF` to CMake -if (${DEBUG_SYMBOLS}) - set_property(TARGET ElasticaFlags - APPEND PROPERTY INTERFACE_COMPILE_OPTIONS -g) -endif (${DEBUG_SYMBOLS}) - -# Always compile only for the current architecture. This can be overridden -# by passing `-D OVERRIDE_ARCH=THE_ARCHITECTURE` to CMake -if (NOT "${OVERRIDE_ARCH}" STREQUAL "OFF") - set_property(TARGET ElasticaFlags - APPEND PROPERTY - INTERFACE_COMPILE_OPTIONS - $<$:-march=${OVERRIDE_ARCH}>) -else () - # Apple silicon does not support the march native flag in all compilers. - if(NOT APPLE OR NOT "${CMAKE_HOST_SYSTEM_PROCESSOR}" STREQUAL "arm64") - set_property(TARGET ElasticaFlags - APPEND PROPERTY - INTERFACE_COMPILE_OPTIONS - $<$:-march=native>) - endif() -endif () - -# We always want a detailed backtrace of template errors to make debugging them -# easier -set_property(TARGET ElasticaFlags - APPEND PROPERTY - INTERFACE_COMPILE_OPTIONS - $<$:-ftemplate-backtrace-limit=0>) - -# By default, the LLVM optimizer assumes floating point exceptions are ignored. -create_cxx_flag_target("-ffp-exception-behavior=maytrap" ElasticaFpExceptions) -target_link_libraries( - ElasticaFlags - INTERFACE - ElasticaFpExceptions -) diff --git a/backend/cmake/SetupElasticaInlining.cmake b/backend/cmake/SetupElasticaInlining.cmake deleted file mode 100644 index e9a04364f..000000000 --- a/backend/cmake/SetupElasticaInlining.cmake +++ /dev/null @@ -1,18 +0,0 @@ -# Distributed under the MIT License. -# See LICENSE.txt for details. -# Obtained with thanks from https://github.com/sxs-collaboration/spectre - -option(ELASTICA_USE_ALWAYS_INLINE "Force elastica inlining." ON) - -set(_ELASTICA_USE_ALWAYS_INLINE 0) - -if (ELASTICA_USE_ALWAYS_INLINE) - set(_ELASTICA_USE_ALWAYS_INLINE 1) -endif () - -set_property( - TARGET ElasticaFlags - APPEND PROPERTY - INTERFACE_COMPILE_DEFINITIONS - $<$:ELASTICA_USE_ALWAYS_INLINE=${_ELASTICA_USE_ALWAYS_INLINE}> -) diff --git a/backend/cmake/SetupFilesystem.cmake b/backend/cmake/SetupFilesystem.cmake deleted file mode 100644 index bbb7d60e6..000000000 --- a/backend/cmake/SetupFilesystem.cmake +++ /dev/null @@ -1,27 +0,0 @@ -log_info("Finding Filesystem") - -# Older versions of GCC only supports experimental in some laptops -# So including it here - -#if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") -# find_package(Filesystem REQUIRED COMPONENTS Experimental) -## set(CXX_FILESYSTEM_LIBRARIES "stdc++fs") -## target_link_libraries(elastica PUBLIC ${CXX_FILESYSTEM_LIBRARIES}) -#elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") -# find_package(Filesystem REQUIRED COMPONENTS Final) -## set(CXX_FILESYSTEM_LIBRARIES "") -## set(CXX_FILESYSTEM_LIBRARIES "c++experimental") -#endif () - -# Hacky, ok for now -add_library(std::filesystem INTERFACE IMPORTED) -target_compile_features(std::filesystem INTERFACE cxx_std_14) -if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") - target_link_libraries(std::filesystem INTERFACE -lstdc++fs) -elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") - # target_link_libraries(std::filesystem INTERFACE -lc++fs) -endif () - -#find_package(Filesystem REQUIRED COMPONENTS Final Experimental) - -log_debug("FILESYSTEM_TYPE: ${CXX_FILESYSTEM_HEADER}") diff --git a/backend/cmake/SetupHDF5.cmake b/backend/cmake/SetupHDF5.cmake deleted file mode 100644 index a652b6d95..000000000 --- a/backend/cmake/SetupHDF5.cmake +++ /dev/null @@ -1,43 +0,0 @@ -# Distributed under the MIT License. -# See LICENSE.txt for details. - -log_info("Finding HDF5") - -find_package(HDF5 COMPONENTS C) - -log_debug("HDF5 library: ${HDF5_C_LIBRARIES}") -log_debug("HDF5 include: ${HDF5_C_INCLUDE_DIRS}") -log_debug("HDF5 version: ${HDF5_VERSION}") - -file(APPEND - "${CMAKE_BINARY_DIR}/BuildInformation.txt" - "HDF5 version: ${HDF5_VERSION}\n" - ) - -if (NOT TARGET hdf5::hdf5) - add_library(hdf5::hdf5 INTERFACE IMPORTED) - set_target_properties( - hdf5::hdf5 - PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${HDF5_C_INCLUDE_DIRS}" - INTERFACE_LINK_LIBRARIES "${HDF5_C_LIBRARIES}" - INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${HDF5_C_INCLUDE_DIRS}" - ) - if (DEFINED HDF5_C_DEFINITIONS) - set_target_properties( - hdf5::hdf5 - PROPERTIES - INTERFACE_COMPILE_DEFINITIONS "${HDF5_C_DEFINITIONS}" - ) - endif () -endif () - -if (HDF5_IS_PARALLEL) - message(WARNING "HDF5 is built with MPI support, but MPI was not found. " - "You may encounter build issues with HDF5, such as missing headers.") -endif () - -set_property( - GLOBAL APPEND PROPERTY ELASTICA_THIRD_PARTY_LIBS - hdf5::hdf5 -) diff --git a/backend/cmake/SetupMacOsx.cmake b/backend/cmake/SetupMacOsx.cmake deleted file mode 100644 index 7a5d93dae..000000000 --- a/backend/cmake/SetupMacOsx.cmake +++ /dev/null @@ -1,19 +0,0 @@ -# Distributed under the MIT License. -# See LICENSE.txt for details. -# Obtained with thanks from https://github.com/sxs-collaboration/spectre - -# https://stackoverflow.com/a/19819591 -if (APPLE) - # The -fvisibility=hidden flag is necessary to eliminate warnings - # when building on Apple Silicon - if("${CMAKE_HOST_SYSTEM_PROCESSOR}" STREQUAL "arm64") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden") - endif() - set(ELASTICA_MACOSX_MIN "10.9") - if (DEFINED MACOSX_MIN) - set(ELASTICA_MACOSX_MIN "${MACOSX_MIN}") - endif () - set(CMAKE_EXE_LINKER_FLAGS - "${CMAKE_EXE_LINKER_FLAGS} -mmacosx-version-min=${ELASTICA_MACOSX_MIN}") - log_info("Minimum macOS version: ${ELASTICA_MACOSX_MIN}") -endif () diff --git a/backend/cmake/SetupPic.cmake b/backend/cmake/SetupPic.cmake deleted file mode 100644 index c5d2d0b3b..000000000 --- a/backend/cmake/SetupPic.cmake +++ /dev/null @@ -1,16 +0,0 @@ -# Distributed under the MIT License. -# See LICENSE.txt for details. - -# Set up position independent code by default since this is required -# for our python libraries. -# -# Obtained with thanks from https://github.com/sxs-collaboration/spectre -# -# Using CMake's set(CMAKE_POSITION_INDEPENDENT_CODE ON) -# enables -fPIC in shared libraries and -fPIE on executables. Here instead -# of a global property, we add it to the ElasticaFlags property which -# is used by our examples, and python builds internally. -set_property(TARGET ElasticaFlags - APPEND PROPERTY - INTERFACE_COMPILE_OPTIONS - $<$:-fPIC>) diff --git a/backend/cmake/SetupPybind11.cmake b/backend/cmake/SetupPybind11.cmake deleted file mode 100644 index 393768ef9..000000000 --- a/backend/cmake/SetupPybind11.cmake +++ /dev/null @@ -1,34 +0,0 @@ -# Distributed under the MIT License. -# See LICENSE.txt for details. -log_info("Finding Pybind11") - -# Make sure to find Python first so it's consistent with pybind11 -find_package(Python COMPONENTS Interpreter Development) - -# Try to find the pybind11-config tool to find pybind11's CMake config files -find_program(PYBIND11_CONFIG_TOOL pybind11-config) -set(PYBIND11_CMAKEDIR "") -if (PYBIND11_CONFIG_TOOL) - execute_process( - COMMAND "${PYBIND11_CONFIG_TOOL}" "--cmakedir" - OUTPUT_VARIABLE PYBIND11_CMAKEDIR - OUTPUT_STRIP_TRAILING_WHITESPACE) - message(STATUS "Found pybind11-config tool (${PYBIND11_CONFIG_TOOL}) and " - "determined CMake dir: ${PYBIND11_CMAKEDIR}") -endif () - -find_package(pybind11 2.6...<3 REQUIRED - HINTS - ${PYBIND11_CMAKEDIR} - ${Python_SITEARCH} - ${Python_SITELIB} - ${Python_STDARCH} - ${Python_STDLIB} - ) - -set_property( - GLOBAL APPEND PROPERTY ELASTICA_THIRD_PARTY_LIBS - pybind11 -) - -log_debug("Pybind11 include: ${pybind11_INCLUDE_DIR}") diff --git a/backend/cmake/SetupSanitizers.cmake b/backend/cmake/SetupSanitizers.cmake deleted file mode 100644 index 97ddf81ec..000000000 --- a/backend/cmake/SetupSanitizers.cmake +++ /dev/null @@ -1,72 +0,0 @@ -# Distributed under the MIT License. -# See LICENSE.txt for details. - -option(ASAN "Add AddressSanitizer compile flags" OFF) - -# We handle the sanitizers using targets for the compile options, but modify -# CMAKE_EXE_LINKER_FLAGS for the linker flags because the sanitizers should -# only be linked into the final executable and CMake doesn't support linker -# interface flags before CMake 3.13. -add_library(Sanitizers INTERFACE) - -add_library(_Sanitizers_Address INTERFACE) -add_library(Sanitizers::Address ALIAS _Sanitizers_Address) - -add_library(_Sanitizers_UbInteger INTERFACE) -add_library(Sanitizers::UbInteger ALIAS _Sanitizers_UbInteger) - -add_library(_Sanitizers_UbUndefined INTERFACE) -add_library(Sanitizers::UbUndefined ALIAS _Sanitizers_UbUndefined) - -if (ASAN) - set_property( - TARGET _Sanitizers_Address - APPEND PROPERTY - INTERFACE_COMPILE_OPTIONS - $<$:-fno-omit-frame-pointer -fsanitize=address> - ) - set( - CMAKE_EXE_LINKER_FLAGS - "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address" - ) -endif () - -option(UBSAN_UNDEFINED "Add UBSan undefined behavior compile flags" OFF) -if (UBSAN_UNDEFINED) - set_property( - TARGET _Sanitizers_UbUndefined - APPEND PROPERTY - INTERFACE_COMPILE_OPTIONS - $<$:-fno-omit-frame-pointer -fsanitize=undefined> - ) - set( - CMAKE_EXE_LINKER_FLAGS - "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined" - ) -endif () - -option(UBSAN_INTEGER "Add UBSan unsigned integer overflow compile flags" OFF) -if (UBSAN_INTEGER) - set_property( - TARGET _Sanitizers_UbInteger - APPEND PROPERTY - INTERFACE_COMPILE_OPTIONS - $<$:-fno-omit-frame-pointer -fsanitize=integer> - ) - set( - CMAKE_CXX_FLAGS - "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer -fsanitize=integer" - ) - set( - CMAKE_EXE_LINKER_FLAGS - "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=integer" - ) -endif () - -target_link_libraries( - ElasticaFlags - INTERFACE - _Sanitizers_Address - _Sanitizers_UbInteger - _Sanitizers_UbUndefined -) diff --git a/backend/cmake/SetupSleef.cmake b/backend/cmake/SetupSleef.cmake deleted file mode 100644 index 28f548499..000000000 --- a/backend/cmake/SetupSleef.cmake +++ /dev/null @@ -1,28 +0,0 @@ -log_info("Finding Sleef") - -find_package(Sleef QUIET) - -if (SLEEF_FOUND) - file(APPEND "${CMAKE_BINARY_DIR}/BuildInformation.txt" - "Sleef Version: ${SLEEF_VERSION}\n") - - add_library(Sleef INTERFACE IMPORTED) - set_property(TARGET Sleef PROPERTY - INTERFACE_INCLUDE_DIRECTORIES ${SLEEF_INCLUDE_DIRS}) - set_property(TARGET Sleef - APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${SLEEF_LIBRARIES}) - add_interface_lib_headers( - TARGET Sleef - HEADERS - sleef.h - ) - - set_property( - GLOBAL APPEND PROPERTY ELASTICA_THIRD_PARTY_LIBS - Sleef - ) - - log_debug("SLEEF_INCLUDE_DIRS: ${SLEEF_INCLUDE_DIRS}") - log_debug("SLEEF_LIBRARIES: ${SLEEF_LIBRARIES}") - log_debug("SLEEF_VERSION: ${SLEEF_VERSION}") -endif () diff --git a/backend/cmake/SetupStl.cmake b/backend/cmake/SetupStl.cmake deleted file mode 100644 index a1c1083a2..000000000 --- a/backend/cmake/SetupStl.cmake +++ /dev/null @@ -1,123 +0,0 @@ -# Distributed under the MIT License. -# See LICENSE.txt for details. - -# Create an C++ standard library target for tracking dependencies -# through includes throughout Elastica. -if (NOT TARGET Stl) - add_library(Stl INTERFACE IMPORTED) - - add_interface_lib_headers( - TARGET Stl - HEADERS - algorithm - any - array - atomic - barrier - bit - bitset - cassert - cctype - cerrno - cfenv - cfloat - charconv - chrono - cinttypes - climits - clocale - cmath - codecvt - compare - complex - concepts - condition_variable - coroutine - csetjmp - csignal - cstdarg - cstddef - cstdint - cstdio - cstdlib - cstring - ctime - cuchar - cwchar - cwctype - deque - exception - execution - format - forward_list - fstream - functional - future - initializer_list - iomanip - ios - iosfwd - iostream - istream - iterator - latch - limits - list - locale - map - memory - memory_resource - mutex - new - numbers - numeric - optional - ostream - queue - random - ranges - ratio - regex - scoped_allocator - semaphore - set - shared_mutex - source_location - span - sstream - stack - stdexcept - stop_token - streambuf - string - string_view - strstream - syncstream - system_error - thread - tuple - type_traits - typeindex - typeinfo - unordered_map - unordered_set - utility - valarray - variant - vector - version - - # UNIX/Linux specific headers - dirent.h - libgen.h - sys/stat.h - sys/types.h - unistd.h - xmmintrin.h - ) - - set_property( - GLOBAL APPEND PROPERTY ELASTICA_THIRD_PARTY_LIBS - Stl - ) -endif (NOT TARGET Stl) diff --git a/backend/cmake/SetupTBB.cmake b/backend/cmake/SetupTBB.cmake deleted file mode 100644 index 531c69d2d..000000000 --- a/backend/cmake/SetupTBB.cmake +++ /dev/null @@ -1,34 +0,0 @@ -# Setup TBB in elastica environment - -# Distributed under the MIT License. See LICENSE for details. - -log_info("Finding TBB") - -# TBB >= 2021 required because variadic support was added in this version -# for parallel invoke, which is needed by some of RunTests. -find_package(TBB 2021.0 REQUIRED) - -file(APPEND "${CMAKE_BINARY_DIR}/BuildInformation.txt" - "TBB Version: ${TBB_VERSION}\n") - -add_library(TBB INTERFACE IMPORTED) -set_property(TARGET TBB - APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${TBB_INCLUDE_DIRS} ${TBB_MALLOC_INCLUDE_DIRS}) -set_property(TARGET TBB - APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${TBB_LIBRARIES} ${TBB_MALLOC_LIBRARIES}) - -set_property( - GLOBAL APPEND PROPERTY ELASTICA_THIRD_PARTY_LIBS - TBB -) - -#elastica_include_directories("${TBB_INCLUDE_DIRS}") -#elastica_include_directories("${TBB_MALLOC_INCLUDE_DIRS}") -#list(APPEND ELASTICA_LIBRARIES ${TBB_LIBRARIES}) -#list(APPEND ELASTICA_LIBRARIES ${TBB_MALLOC_LIBRARIES}) - -log_debug("TBB_INCLUDE_DIRS: ${TBB_INCLUDE_DIRS}") -log_debug("TBB_LIBRARIES: ${TBB_LIBRARIES}") -log_debug("TBB_MALLOC_INCLUDE_DIRS: ${TBB_MALLOC_INCLUDE_DIRS}") -log_debug("TBB_MALLOC_LIBRARIES: ${TBB_MALLOC_LIBRARIES}") -log_debug("TBB_VERSION: ${TBB_VERSION}") diff --git a/backend/meson.build b/backend/meson.build new file mode 100644 index 000000000..a49e7cb5f --- /dev/null +++ b/backend/meson.build @@ -0,0 +1,117 @@ +project( + 'elasticapp', + ['cpp'], + version: '0.0.3', + default_options: ['cpp_std=c++14'], +) + +add_global_arguments(['-Wno-unused'], + language: 'cpp') + +package = 'elasticapp' + +cc = meson.get_compiler('cpp') + +py = import('python').find_installation(pure: false) +py_dep = py.dependency() + +# set up global defines to optimize blaze usages +add_global_arguments( + # - Enable external BLAS kernels + '-DBLAZE_BLAS_MODE=0', + # - Set default matrix storage order to column-major, since many of our + # functions are implemented for column-major layout. This default reduces + # conversions. + '-DBLAZE_DEFAULT_STORAGE_ORDER=blaze::rowMajor', + # - Disable SMP parallelization. This disables SMP parallelization for all + # possible backends (OpenMP, C++11 threads, Boost, HPX): + # https://bitbucket.org/blaze-lib/blaze/wiki/Serial%20Execution#!option-3-deactivation-of-parallel-execution + '-DBLAZE_USE_SHARED_MEMORY_PARALLELIZATION=0', + # - Disable MPI parallelization + '-DBLAZE_MPI_PARALLEL_MODE=0', + # - Using the default cache size, which may have been configured automatically + # by the Blaze CMake configuration for the machine we are running on. We + # could override it here explicitly to tune performance. + # BLAZE_CACHE_SIZE + '-DBLAZE_USE_PADDING=1', + # - Always enable non-temporal stores for cache optimization of large data + # structures: https://bitbucket.org/blaze-lib/blaze/wiki/Configuration%20Files#!streaming-non-temporal-stores + '-DBLAZE_USE_STREAMING=1', + # - Initializing default-constructed structures for fundamental types + '-DBLAZE_USE_DEFAULT_INITIALIZATON=1', + # Use Sleef for vectorization of more math functions + '-DBLAZE_USE_SLEEF=1', + # Set inlining settings + '-DBLAZE_USE_STRONG_INLINE=1', + '-DBLAZE_USE_ALWAYS_INLINE=1', + # Set vectorization (leave to 1, else there is no use for blaze) + '-DBLAZE_USE_VECTORIZATION=1', + language: 'cpp' +) + +# find dependencies and create dep objects +pybind11_dep = declare_dependency( + include_directories: run_command( + py, + '-c', + 'import pybind11; print(pybind11.get_include());', + check: true, + ).stdout().strip(), +) + +# python and pybind deps are used together +py_deps = [py_dep, pybind11_dep] + +fs = import('fs') + +message('Running buildscripts/build-all.sh (might take a while)') +buildscripts = files('buildscripts/build-all.sh') +res = run_command('bash', buildscripts, check: false) +message(res.stdout().strip()) +if res.returncode() != 0 + error(res.stderr().strip()) +endif + +deps_installed = fs.parent(meson.current_source_dir()) / 'deps' / 'installed' + +deps_inc_dirs = [deps_installed / 'include'] +deps_lib_dirs = [deps_installed / 'lib'] + +# see https://github.com/mesonbuild/meson/issues/7943 +# The strategy for the below required dependencies are as follows +# - First, meson tries to resolve the dependency by searching on the system +# - if it doesn't find it, it fallbacks to the subproject wrap system or the +# ones installed in ./buildscripts + +# these three are picked up due to the installation script in ./buildscripts +blaze_dep = dependency('blaze') +blaze_tensor_dep = dependency('BlazeTensor') +sleef_dep = dependency('sleef', required: false) +if not sleef_dep.found() + sleef_dep = declare_dependency( + include_directories: deps_inc_dirs, + dependencies: cc.find_library('sleef', dirs: deps_lib_dirs), + ) +endif + +# blaze and deps commonly used together +blaze_deps = [blaze_dep, blaze_tensor_dep, sleef_dep] + +# this is picked up due to the installation script in ./buildscripts +brigand_dep = dependency('brigand', required: false) +if not brigand_dep.found() + brigand_dep = declare_dependency(include_directories: deps_inc_dirs) +endif + +# this is picked up from ./subprojects +cxxopts_dep = dependency('cxxopts', fallback : 'cxxopts') + +# meson-python: error: Could not map installation path to an equivalent wheel directory: +# tbb_dep = dependency('tbb', fallback : 'tbb') +# yaml_cpp_dep = dependency('yaml-cpp', fallback : 'yaml-cpp') + +# TODO: are these required? +# setup_library "HighFive" "https://github.com/BlueBrain/HighFive" +# setup_library "spline" "https://github.com/tp5uiuc/spline.git" + +subdir('src') diff --git a/backend/pyproject.toml b/backend/pyproject.toml index fd92373c6..35108c608 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -1,24 +1,42 @@ -[build-system] -requires = [ - "setuptools>=42", - "wheel", - "ninja", - "cmake>=3.12", +[project] +name = "elasticapp" +version = "0.0.3" +description = "A CPP accelerated backend for PyElastica kernels" +# readme = "README.rst" # for long description +requires-python = ">=3.10" +license = {text = "MIT License"} + +# this is a list that can be expanded +authors = [{name = "Seung Hyun Kim", email = "skim0119@gmail.com"}] + +# classifiers can be added here, later +classifiers = [ ] -build-backend = "setuptools.build_meta" -[tool.mypy] -files = "setup.py" -python_version = "3.7" -strict = true -show_error_codes = true -enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] -warn_unreachable = true +[project.urls] +"Homepage" = "https://github.com/GazzolaLab/PyElastica/" +"Bug Reports" = "https://github.com/GazzolaLab/PyElastica/issues" +"Source" = "https://github.com/GazzolaLab/PyElastica/" +"Documentation" = "https://docs.cosseratrods.org/" + +[build-system] +requires = ["meson-python", "pybind11"] +build-backend = "mesonpy" -[[tool.mypy.overrides]] -module = ["ninja"] -ignore_missing_imports = true +[tool.meson-python.args] +setup = ['-Doptimization=3'] +# [tool.mypy] +# files = "setup.py" +# python_version = "3.7" +# strict = true +# show_error_codes = true +# enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] +# warn_unreachable = true +# +# [[tool.mypy.overrides]] +# module = ["ninja"] +# ignore_missing_imports = true [tool.pytest.ini_options] minversion = "6.0" diff --git a/backend/setup.py b/backend/setup.py deleted file mode 100644 index 80240487e..000000000 --- a/backend/setup.py +++ /dev/null @@ -1,139 +0,0 @@ -import os -import re -import subprocess -import sys -from pathlib import Path - -from setuptools import Extension, setup -from setuptools.command.build_ext import build_ext - -# Convert distutils Windows platform specifiers to CMake -A arguments -PLAT_TO_CMAKE = { - "win32": "Win32", - "win-amd64": "x64", - "win-arm32": "ARM", - "win-arm64": "ARM64", -} - - -# A CMakeExtension needs a sourcedir instead of a file list. -# The name must be the _single_ output extension from the CMake build. -# If you need multiple extensions, see scikit-build. -class CMakeExtension(Extension): - def __init__(self, name: str, sourcedir: str = "") -> None: - super().__init__(name, sources=[]) - self.sourcedir = os.fspath(Path(sourcedir).resolve()) - - -class CMakeBuild(build_ext): - def build_extension(self, ext: CMakeExtension) -> None: - # Must be in this form due to bug in .resolve() only fixed in Python 3.10+ - ext_fullpath = Path.cwd() / self.get_ext_fullpath(ext.name) - extdir = ext_fullpath.parent.resolve() - - # Using this requires trailing slash for auto-detection & inclusion of - # auxiliary "native" libs - - debug = int(os.environ.get("DEBUG", 0)) if self.debug is None else self.debug - cfg = "Debug" if debug else "Release" - - # CMake lets you override the generator - we need to check this. - # Can be set with Conda-Build, for example. - cmake_generator = os.environ.get("CMAKE_GENERATOR", "") - - # Set Python_EXECUTABLE instead if you use PYBIND11_FINDPYTHON - # EXAMPLE_VERSION_INFO shows you how to pass a value into the C++ code - # from Python. - cmake_args = [ - f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={extdir}{os.sep}", - f"-DPYTHON_EXECUTABLE={sys.executable}", - f"-DCMAKE_BUILD_TYPE={cfg}", # not used on MSVC, but no harm - ] - build_args = [] - # Adding CMake arguments set as environment variable - # (needed e.g. to build for ARM OSx on conda-forge) - if "CMAKE_ARGS" in os.environ: - cmake_args += [item for item in os.environ["CMAKE_ARGS"].split(" ") if item] - - # In this example, we pass in the version to C++. You might not need to. - cmake_args += [f"-DEXAMPLE_VERSION_INFO={self.distribution.get_version()}"] - - if self.compiler.compiler_type != "msvc": - # Using Ninja-build since it a) is available as a wheel and b) - # multithreads automatically. MSVC would require all variables be - # exported for Ninja to pick it up, which is a little tricky to do. - # Users can override the generator with CMAKE_GENERATOR in CMake - # 3.15+. - if not cmake_generator or cmake_generator == "Ninja": - try: - import ninja - - ninja_executable_path = Path(ninja.BIN_DIR) / "ninja" - cmake_args += [ - "-GNinja", - f"-DCMAKE_MAKE_PROGRAM:FILEPATH={ninja_executable_path}", - ] - except ImportError: - pass - - else: - # Single config generators are handled "normally" - single_config = any(x in cmake_generator for x in {"NMake", "Ninja"}) - - # CMake allows an arch-in-generator style for backward compatibility - contains_arch = any(x in cmake_generator for x in {"ARM", "Win64"}) - - # Specify the arch if using MSVC generator, but only if it doesn't - # contain a backward-compatibility arch spec already in the - # generator name. - if not single_config and not contains_arch: - cmake_args += ["-A", PLAT_TO_CMAKE[self.plat_name]] - - # Multi-config generators have a different way to specify configs - if not single_config: - cmake_args += [ - f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{cfg.upper()}={extdir}" - ] - build_args += ["--config", cfg] - - if sys.platform.startswith("darwin"): - # Cross-compile support for macOS - respect ARCHFLAGS if set - archs = re.findall(r"-arch (\S+)", os.environ.get("ARCHFLAGS", "")) - if archs: - cmake_args += ["-DCMAKE_OSX_ARCHITECTURES={}".format(";".join(archs))] - - # Set CMAKE_BUILD_PARALLEL_LEVEL to control the parallel build level - # across all generators. - if "CMAKE_BUILD_PARALLEL_LEVEL" not in os.environ: - # self.parallel is a Python 3 only way to set parallel jobs by hand - # using -j in the build_ext call, not supported by pip or PyPA-build. - if hasattr(self, "parallel") and self.parallel: - # CMake 3.12+ only. - build_args += [f"-j{self.parallel}"] - - build_temp = Path(self.build_temp) / ext.name - if not build_temp.exists(): - build_temp.mkdir(parents=True) - - subprocess.run( - ["cmake", ext.sourcedir, *cmake_args], cwd=build_temp, check=True - ) - subprocess.run( - ["cmake", "--build", ".", *build_args], cwd=build_temp, check=True - ) - - -# The information here can also be placed in setup.cfg - better separation of -# logic and declaration, and simpler if you include description/version in a file. -setup( - name="elasticapp", - version="0.0.3", - author="Seung Hyun Kim", - author_email="skim0119@gmail.com", - description="A CPP accelerated backend for PyElastica kernels", - long_description="", - ext_modules=[CMakeExtension("elasticapp")], - cmdclass={"build_ext": CMakeBuild}, - zip_safe=False, - python_requires=">=3.10", -) diff --git a/backend/src/BindExamples/Bindings.cpp b/backend/src/BindExamples/Bindings.cpp new file mode 100644 index 000000000..f1a50fd70 --- /dev/null +++ b/backend/src/BindExamples/Bindings.cpp @@ -0,0 +1,100 @@ +//****************************************************************************** +// Includes +//****************************************************************************** +#include +#include +#include +#include +#include +// +// + +// + +#include "Systems/Block/Block.hpp" // slice() +#include "Systems/CosseratRods.hpp" +#include "Systems/CosseratRods/CosseratRods.hpp" +// #include "Simulator/Simulator.hpp" +// +// #include "Systems/CosseratRods/Python/Generators/BindEnergyMethods.hpp" +#include "Systems/CosseratRods/Python/Generators/BindMemberAccess.hpp" +#include "Systems/CosseratRods/Python/Generators/BindPyElasticaInterface.hpp" +#include "Systems/CosseratRods/Python/Generators/BoundChecks.hpp" +#include "Systems/common/Python/Generators/BindVariableLocator.hpp" +// +#include "Utilities/Math/Python/SliceHelpers.hpp" +#include "Utilities/Size.hpp" +#include "Utilities/TMPL.hpp" +// #include "Utilities/Get.hpp" + +namespace py = pybind11; + +namespace py_bindings { + + using StraightInit = ::elastica::cosserat_rod::StraightCosseratRodInitializer; + using Plugin = ::elastica::cosserat_rod::CosseratRod; + using T = ::elastica::cosserat_rod::detail::CosseratRodBlock; + + using UnwantedVariableTags = + tmpl::list<::elastica::tags::_DummyElementVector, + ::elastica::tags::_DummyElementVector2, + ::elastica::tags::_DummyVoronoiVector>; + + /*!\brief Helps bind a cosserat rod block and slice in \elastica + * \ingroup python_bindings + */ + void bind_tests(py::module& m) { // NOLINT + + auto py_cosserat_rod = + py::class_(m, "_CosseratRodBlock") + .def(py::init([](std::size_t n_elems) { + StraightInit straight_initializer{ + // StraightInitialization Wrapper + StraightInit::NElement{n_elems}, + StraightInit::Density{1.0}, + StraightInit::Youngs{1.00e6}, + StraightInit::ShearModulus{0.33e6}, + StraightInit::Radius{0.1}, + StraightInit::Length{1.0}, + StraightInit::Origin{::elastica::Vec3{0.0, 0.0, 0.0}}, + StraightInit::Direction{::elastica::Vec3{1.0, 0.0, 0.0}}, + StraightInit::Normal{::elastica::Vec3{0.0, 1.0, 0.0}}}; + T result{}; + auto block_slice = result.emplace_back( + straight_initializer + .initialize()); // block slice + return result; + }), + py::arg("n_elems")) + .def("__len__", [](const T& t) { return cpp17::size(t); }) + .def( + "__str__", + +[](const T& /*meta*/) { + return std::string("CosseratRodView"); + }) + .def( + "__getitem__", + +[](T& t, std::size_t index) { + variable_bounds_check(::blocks::n_units(t), index); + return ::blocks::slice(t, index); + }) + .def( + "__getitem__", +[](T& t, const py::slice slice) { + auto v = + check_slice<0UL>(::blocks::n_units(t), std::move(slice)); + return ::blocks::slice(t, v.start, v.start + v.slicelength); + }); + bind_member_access(py_cosserat_rod); + bind_variable_locator(py_cosserat_rod); + bind_pyelastica_interface(py_cosserat_rod); + } + //**************************************************************************** + +} // namespace py_bindings + +PYBIND11_MODULE(_PyExamples, m) { // NOLINT + m.doc() = R"pbdoc( + Bindings for Elastica++ Test codes + )pbdoc"; + py_bindings::bind_tests(m); +} diff --git a/backend/src/Configuration/.gitignore b/backend/src/Configuration/.gitignore new file mode 100644 index 000000000..b058cd4ce --- /dev/null +++ b/backend/src/Configuration/.gitignore @@ -0,0 +1,2 @@ +#Parallel.hpp +#Profiling.hpp diff --git a/backend/src/Configuration/Kernels.hpp b/backend/src/Configuration/Kernels.hpp new file mode 100644 index 000000000..87d7683ed --- /dev/null +++ b/backend/src/Configuration/Kernels.hpp @@ -0,0 +1,128 @@ +#pragma once + +//****************************************************************************** +/*!\brief Selection of the default backend for quadrature + * \ingroup default_config + * + * The following execution policies are available: + * - scalar + * - simd + * - blaze + * + * This can be customized if needed in the client file. + */ +#ifndef ELASTICA_COSSERATROD_LIB_QUADRATURE_BACKEND +#define ELASTICA_COSSERATROD_LIB_QUADRATURE_BACKEND simd +#endif +//****************************************************************************** + +//****************************************************************************** +/*!\brief Selection of the default backend for difference + * \ingroup default_config + * + * The following execution policies are available: + * - scalar + * - simd + * - blaze + * + * This can be customized if needed in the client file. + */ +#ifndef ELASTICA_COSSERATROD_LIB_DIFFERENCE_BACKEND +#define ELASTICA_COSSERATROD_LIB_DIFFERENCE_BACKEND simd +#endif +//****************************************************************************** + +//****************************************************************************** +/*!\brief Selection of the default backend for matvec + * \ingroup default_config + * + * The following execution policies are available: + * - scalar + * - simd + * - blaze + * + * This can be customized if needed in the client file. + */ +#ifndef ELASTICA_COSSERATROD_LIB_MATVEC_BACKEND +#define ELASTICA_COSSERATROD_LIB_MATVEC_BACKEND simd +#endif +//****************************************************************************** + +//****************************************************************************** +/*!\brief Selection of the default backend for vector-scalar division + * \ingroup default_config + * + * The following execution policies are available: + * - scalar + * - simd + * - blaze + * + * This can be customized if needed in the client file. + */ +#ifndef ELASTICA_COSSERATROD_LIB_VECSCALARDIV_BACKEND +#define ELASTICA_COSSERATROD_LIB_VECSCALARDIV_BACKEND simd +#endif +//****************************************************************************** + +//****************************************************************************** +/*!\brief Selection of the default backend for vector-scalar multiplication + * \ingroup default_config + * + * The following execution policies are available: + * - scalar + * - simd + * - blaze + * + * This can be customized if needed in the client file. + */ +#ifndef ELASTICA_COSSERATROD_LIB_VECSCALARMULT_BACKEND +#define ELASTICA_COSSERATROD_LIB_VECSCALARMULT_BACKEND simd +#endif +//****************************************************************************** + +//****************************************************************************** +/*!\brief Selection of the default backend for cross products + * \ingroup default_config + * + * The following execution policies are available: + * - scalar + * - simd + * - blaze + * + * This can be customized if needed in the client file. + */ +#ifndef ELASTICA_COSSERATROD_LIB_CROSSPRODUCT_BACKEND +#define ELASTICA_COSSERATROD_LIB_CROSSPRODUCT_BACKEND simd +#endif +//****************************************************************************** + +//****************************************************************************** +/*!\brief Selection of the default backend for inv rotate divide + * \ingroup default_config + * + * The following execution policies are available: + * - scalar + * - simd + * - blaze + * + * This can be customized if needed in the client file. + */ +#ifndef ELASTICA_COSSERATROD_LIB_INV_ROTATE_DIVIDE_BACKEND +#define ELASTICA_COSSERATROD_LIB_INV_ROTATE_DIVIDE_BACKEND simd +#endif +//****************************************************************************** + +//****************************************************************************** +/*!\brief Selection of the default backend for SO3+= + * \ingroup default_config + * + * The following execution policies are available: + * - scalar + * - simd + * + * This can be customized if needed in the client file. + */ +#ifndef ELASTICA_COSSERATROD_LIB_SO3_ADDITION_BACKEND +#define ELASTICA_COSSERATROD_LIB_SO3_ADDITION_BACKEND simd +#endif +//****************************************************************************** diff --git a/backend/src/Configuration/Logging.hpp b/backend/src/Configuration/Logging.hpp new file mode 100644 index 000000000..4ed8a55a2 --- /dev/null +++ b/backend/src/Configuration/Logging.hpp @@ -0,0 +1,25 @@ +#pragma once + +//****************************************************************************** +/*!\brief Selection of the level of logging. + * \ingroup config logging + * + * This macro selects the level of logging, to effectively filter out logs that + * are not deemed necessary. Any logs below the level chosen are not reported + * while a logging level comprises all higher logging levels. For instance, + * if the set ELASTICA_LOG_LEVEL() is `info`, then logs marked with `debug` are + * not printed, but all errors and warning are also printed to the log file(s). + * The following log levels can be chosen: + * + * - ::elastica::logging::inactive + * - ::elastica::logging::error + * - ::elastica::logging::warning + * - ::elastica::logging::info + * - ::elastica::logging::debug + * + * \see elastica::logging::LoggingLevel + */ +#ifndef ELASTICA_LOG_LEVEL +#define ELASTICA_LOG_LEVEL ::elastica::logging::info +#endif +//****************************************************************************** diff --git a/backend/src/Configuration/Parallel.hpp b/backend/src/Configuration/Parallel.hpp new file mode 100644 index 000000000..ff150a034 --- /dev/null +++ b/backend/src/Configuration/Parallel.hpp @@ -0,0 +1,57 @@ +#pragma once + +//****************************************************************************** +/*!\brief Compilation switch for the (de-)activation of the shared-memory + * parallelization + * \ingroup config parallel + * + * This compilation switch enables/disables the shared-memory parallelization. + * In case the switch is set to 1 (i.e. in case the shared-memory + * parallelization is enabled), \elastica is allowed to execute operations in + * parallel. In case the switch is set to 0 (i.e. parallelization + * is disabled), \elastica is restricted from executing operations in parallel. + * + * Possible settings for the shared-memory parallelization switch: + * - Deactivated: \b 0 + * - Activated : \b 1 (default) + * + * \note It is possible to (de-)activate the shared-memory parallelization via + * command line or by defining this symbol manually before including any + * Blaze header file: + * + * \example + * \code + * g++ ... -DELASTICA_USE_SHARED_MEMORY_PARALLELIZATION=1 ... + * \endcode + * + * \code + * #define ELASTICA_USE_SHARED_MEMORY_PARALLELIZATION 1 + * #include + * \endcode + * + */ +// #ifndef ELASTICA_USE_SHARED_MEMORY_PARALLELIZATION +// #define ELASTICA_USE_SHARED_MEMORY_PARALLELIZATION @ELASTICA_SMP@ +// #endif +//****************************************************************************** + +//****************************************************************************** +/*!\brief Compile time hint for number of threads to be used in an elastica + * application + * \ingroup parallel + */ +constexpr std::size_t elastica_threads_hint() { return 2UL; } +//****************************************************************************** + +//****************************************************************************** +/*!\brief Environment variable to set a hint for the number of threads used + * \ingroup parallel + * + * The macro defines the environment variable used to provide hint for the + * number of threads to set for Elastica++ applications. + * For user applications (i.e. when writing your own main-file), this variable + * does not factor in : rather the user can set the number of threads they want + * using the specific parallelism library they employed. + */ +#define ENV_ELASTICA_NUM_THREADS "ELASTICA_NUM_THREADS" + //**************************************************************************** diff --git a/backend/src/Configuration/README.md b/backend/src/Configuration/README.md new file mode 100644 index 000000000..6ec94d0dc --- /dev/null +++ b/backend/src/Configuration/README.md @@ -0,0 +1,14 @@ +## Configuration + +Contains all default-settings to make a default `Elastica++` simulation. The type customizations are defined as macros (as +opposed to type-aliases) to avoid including header files and complicating the editing of these files. Any numeric +customizations +(such as setting a threshold) is done using `constexpr` functions. + +These are later included as proper type-aliases inside ModuleSettings (with appropriate type and configuration checking) +. + +### !!NOTE!! +Only edit these when you feel like the default configuration that you want for `Elastica++` differs from the defaults set in +the Github repository. Else prefer making a configuration in the main.cpp file, as this promotes readability and intent +to the poor souls (made to) read(ing) your code. diff --git a/backend/src/Configuration/Systems.hpp b/backend/src/Configuration/Systems.hpp new file mode 100644 index 000000000..2b9c4a65e --- /dev/null +++ b/backend/src/Configuration/Systems.hpp @@ -0,0 +1,130 @@ +#pragma once + +//****************************************************************************** +/*!\brief Selection of the default admissible systems + * \ingroup default_config + * + * This macro selects the default admissible \systems. The purpose of this + * \a typelist is to add tags indicating the admissible \systems in the + * simulation that comprise the degrees of freedom. The following \system tags + * are available: + * + * - elastica::cosserat_rod::CosseratRod + * - elastica::cosserat_rod::CosseratRodWithoutDamping + * - elastica::rigid_body::Sphere + * + * Consult the documentation page of elastica::PhysicalSystemPlugins for more + * information. This can be customized as needed in the client file. + */ +#ifndef ELASTICA_DEFAULT_ADMISSIBLE_PLUGINS_FOR_SYSTEMS +#define ELASTICA_DEFAULT_ADMISSIBLE_PLUGINS_FOR_SYSTEMS \ + tmpl::list +#endif +//****************************************************************************** + +//****************************************************************************** +/*!\brief Selection of the default policy for adding new systems to Blocks + * within the simulator + * \ingroup default_config + * + * This macro select the default Blocking policy for adding Systems within + * \elastica. The following (simple) blocking policies are available: + * + * - elastica::configuration::RestrictSizeAcrossBlockTypesPolicy + * - elastica::configuration::LooselyRestrictSizeAcrossBlockTypesPolicy + * - elastica::configuration::AlwaysNewAcrossBlockTypesPolicy + * - elastica::configuration::AlwaysSameAcrossBlockTypesPolicy + * + * This can be customized if needed in the client file. More complicated + * policies are also possible by composition + */ +#ifndef ELASTICA_DEFAULT_BLOCKING_POLICY_FOR_SYSTEMS +#define ELASTICA_DEFAULT_BLOCKING_POLICY_FOR_SYSTEMS \ + elastica::configuration::RestrictSizeAcrossBlockTypesPolicy +#endif +//****************************************************************************** + +//****************************************************************************** +/*!\brief Selection of the size parameter for the default policy choosen in + * ELASTICA_DEFAULT_BLOCKING_CRITERIA_FOR_SYSTEMS() + * \ingroup default_config + * + * This value specifies the default size parameter for specifying the blocking + * policy. + */ +constexpr std::size_t default_blocking_policy_size() { return 1024UL; } +//****************************************************************************** + +//****************************************************************************** +/*!\brief Selection of the default iteration behavior over multiple types of + * \elastica systems + * \ingroup default_config + * + * This macro select the default behavior while iterating over multiple types + * of systems (such as a CosseratRod, Sphere, Capsule) etc. The following + * execution policies are available: + * + * - elastica::configuration::sequential_policy + * - elastica::configuration::parallel_policy + * + * This can be customized if needed in the client file. + */ +#ifndef ELASTICA_DEFAULT_ITERATION_POLICY_ACROSS_SYSTEM_TYPES +#define ELASTICA_DEFAULT_ITERATION_POLICY_ACROSS_SYSTEM_TYPES \ + ::elastica::configuration::sequential_policy +#endif +//****************************************************************************** + +//****************************************************************************** +/*!\brief Selection of the default iteration behavior over a container of + * \elastica systems + * \ingroup default_config + * + * This macro select the default behavior while iterating over multiple systems + * of the same type. The following execution policies are available: + * + * - elastica::configuration::sequential_policy + * - elastica::configuration::parallel_policy + * - elastica::configuration::hybrid_policy + * + * This can be customized if needed in the client file. + */ +#ifndef ELASTICA_DEFAULT_ITERATION_POLICY_FOR_EACH_SYSTEM_TYPE +#define ELASTICA_DEFAULT_ITERATION_POLICY_FOR_EACH_SYSTEM_TYPE \ + ::elastica::configuration::sequential_policy +#endif +//****************************************************************************** + +//****************************************************************************** +/*!\brief Environment variable to control if potential warnings are shown during + * system initialization + * \ingroup systems + * + * The macro defines the environment variable that is searched for controlling + * the behavior of (potential) warnings when initialization systems in + * Elastica++. By default, potential warnings are enabled. To disable user + * warnings the environment variable can be set to 1, i.e. + * \code + * ELASTICA_NO_SYSTEM_WARN=1 ./ [options] + * \endcode + */ +#define ENV_ELASTICA_NO_SYSTEM_WARN "ELASTICA_NO_SYSTEM_WARN" +//**************************************************************************** + +//****************************************************************************** +/*!\brief Selection of the default index checking behavior within systems module + * \elastica systems + * \ingroup default_config + * + * This macro select the default behavior while slicing/viewing indices within + * a system. The following execution policies are available: + * + * - checked (checks indices) + * - unchecked (does not check indices) + * + * This can be customized if needed in the client file. + */ +#ifndef ELASTICA_DEFAULT_SYSTEM_INDEX_CHECK +#define ELASTICA_DEFAULT_SYSTEM_INDEX_CHECK checked +#endif +//**************************************************************************** diff --git a/backend/src/ErrorHandling/Abort.hpp b/backend/src/ErrorHandling/Abort.hpp new file mode 100644 index 000000000..4e76c65af --- /dev/null +++ b/backend/src/ErrorHandling/Abort.hpp @@ -0,0 +1,21 @@ +/// \file +/// Defines function ErrorHandling::abort. + +#pragma once + +#include +#include +#include +#include + +namespace Parallel { + /// Abort the program with an error message. + [[noreturn]] inline void abort(const std::string& message) { + printf("%s", message.c_str()); + std::exit(EXIT_SUCCESS); + + // the following call is never reached, but suppresses the warning that + // a 'noreturn' functions does return + std::terminate(); // LCOV_EXCL_LINE + } +} // namespace Parallel diff --git a/backend/src/ErrorHandling/AbortWithErrorMessage.cpp b/backend/src/ErrorHandling/AbortWithErrorMessage.cpp new file mode 100644 index 000000000..a188eb37a --- /dev/null +++ b/backend/src/ErrorHandling/AbortWithErrorMessage.cpp @@ -0,0 +1,53 @@ +// Distributed under the MIT License. +// See LICENSE.txt for details. + +#include "ErrorHandling/AbortWithErrorMessage.hpp" + +#include +#include + +#include "ErrorHandling/Abort.hpp" + +void abort_with_error_message(const char* expression, const char* file, + const int line, const char* pretty_function, + const std::string& message) { + std::ostringstream os; + os << "\n" + << "############ ASSERT FAILED ############\n" + // << "Node: " << Parallel::my_node() << " Proc: " << + // Parallel::my_proc() + // << "\n" + << "Line: " << line << " of " << file << "\n" + << "'" << expression << "' violated!\n" + << "Function: " << pretty_function << "\n" + << message << "\n" + << "############ ASSERT FAILED ############\n" + << "\n"; + // We use printf instead of abort to print the error message because in the + // case of an executable not using Charm++'s main function the call to abort + // will segfault before anything is printed. + printf("%s", os.str().c_str()); + Parallel::abort(""); +} + +void abort_with_error_message(const char* file, const int line, + const char* pretty_function, + const std::string& message) { + std::ostringstream os; + os << "\n" + << "############ ERROR ############\n" + // << "Node: " << Parallel::my_node() << " Proc: " << + // Parallel::my_proc() + // << "\n" + << "Line: " << line << " of " << file << "\n" + << "Function: " << pretty_function << "\n" + << message << "\n" + << "############ ERROR ############\n" + << "\n"; + // We use printf instead of abort to print the error message because in the + // case of an executable not using Charm++'s main function the call to abort + // will segfault before anything is printed. +// printf("%s", os.c_str()); + printf("%s", os.str().c_str()); + Parallel::abort(""); +} diff --git a/backend/src/ErrorHandling/AbortWithErrorMessage.hpp b/backend/src/ErrorHandling/AbortWithErrorMessage.hpp new file mode 100644 index 000000000..4bc10b06b --- /dev/null +++ b/backend/src/ErrorHandling/AbortWithErrorMessage.hpp @@ -0,0 +1,22 @@ +// Distributed under the MIT License. +// See LICENSE.txt for details. + +/// \file +/// Declares function abort_with_error_message + +#pragma once + +#include + +/// \ingroup ErrorHandlingGroup +/// Compose an error message with an expression and abort the program. +[[noreturn]] void abort_with_error_message(const char* expression, + const char* file, int line, + const char* pretty_function, + const std::string& message); + +/// \ingroup ErrorHandlingGroup +/// Compose an error message and abort the program. +[[noreturn]] void abort_with_error_message(const char* file, int line, + const char* pretty_function, + const std::string& message); diff --git a/backend/src/ErrorHandling/Assert.hpp b/backend/src/ErrorHandling/Assert.hpp new file mode 100644 index 000000000..c635ab584 --- /dev/null +++ b/backend/src/ErrorHandling/Assert.hpp @@ -0,0 +1,63 @@ +// Distributed under the MIT License. +// See LICENSE.txt for details. + +/// \file +/// Defines macro ASSERT. + +#pragma once + +#include +#include + +#include "ErrorHandling/Abort.hpp" +#include "ErrorHandling/AbortWithErrorMessage.hpp" +#include "ErrorHandling/FloatingPointExceptions.hpp" + +/*! + * \ingroup ErrorHandlingGroup + * \brief Assert that an expression should be true. + * + * If the preprocessor macro ELASTICA_DEBUG is defined and the expression is + * false, an error message is printed to the standard error stream, and the + * program aborts. ASSERT should be used to catch coding errors as it does + * nothing in production code. + * \param a the expression that must be true + * \param m the error message as an ostream + */ +//#ifdef ELASTICA_DEBUG +#ifndef NDEBUG +// isocpp.org recommends using an `if (true)` instead of a `do +// while(false)` for macros because the latter can mess with inlining +// in some (old?) compilers: +// https://isocpp.org/wiki/faq/misc-technical-issues#macros-with-multi-stmts +// https://isocpp.org/wiki/faq/misc-technical-issues#macros-with-if +// However, Intel's reachability analyzer (as of version 16.0.3 +// 20160415) can't figure out that the else branch and everything +// after it is unreachable, causing warnings (and possibly suboptimal +// code generation). +#define ELASTICA_ASSERT(a, m) \ + do { \ + if (!(a)) { \ + disable_floating_point_exceptions(); \ + std::ostringstream avoid_name_collisions_ASSERT; \ + /* clang-tidy: macro arg in parentheses */ \ + avoid_name_collisions_ASSERT << m; /* NOLINT */ \ + abort_with_error_message(#a, __FILE__, __LINE__, \ + static_cast(__PRETTY_FUNCTION__), \ + avoid_name_collisions_ASSERT.str()); \ + } \ + } while (false) +#else +#define ELASTICA_ASSERT(a, m) \ + do { \ + if (false) { \ + static_cast(a); \ + disable_floating_point_exceptions(); \ + std::ostringstream avoid_name_collisions_ASSERT; \ + /* clang-tidy: macro arg in parentheses */ \ + avoid_name_collisions_ASSERT << m; /* NOLINT */ \ + static_cast(avoid_name_collisions_ASSERT); \ + enable_floating_point_exceptions(); \ + } \ + } while (false) +#endif diff --git a/backend/src/ErrorHandling/Breakpoint.cpp b/backend/src/ErrorHandling/Breakpoint.cpp new file mode 100644 index 000000000..fda6f8332 --- /dev/null +++ b/backend/src/ErrorHandling/Breakpoint.cpp @@ -0,0 +1,21 @@ +// Distributed under the MIT License. +// See LICENSE.txt for details. + +#include "ErrorHandling/Breakpoint.hpp" + +#include + +void breakpoint() { + // We send ourselves a SIGTRAP and ignore it. If we're not being + // traced (e.g. being run in a debugger), that doesn't do much, but if we are + // then the tracer (debugger) can see the signal delivery. We don't reset the + // signal handler afterwards in case this is called on multiple threads; we + // don't want one thread reenabling the default handler just before another + // calls raise(). + struct sigaction handler {}; + handler.sa_handler = SIG_IGN; // NOLINT + handler.sa_flags = 0; + sigemptyset(&handler.sa_mask); + sigaction(SIGTRAP, &handler, nullptr); + raise(SIGTRAP); +} diff --git a/backend/src/ErrorHandling/Breakpoint.hpp b/backend/src/ErrorHandling/Breakpoint.hpp new file mode 100644 index 000000000..e3bdc3d0c --- /dev/null +++ b/backend/src/ErrorHandling/Breakpoint.hpp @@ -0,0 +1,9 @@ +/// \file +/// Defines function ErrorHandling::breakpoint. + +#pragma once + +/*! + * \brief Raise `SIGTRAP` so that debuggers will trap. + */ +void breakpoint(); diff --git a/backend/src/ErrorHandling/Error.hpp b/backend/src/ErrorHandling/Error.hpp new file mode 100644 index 000000000..0645d2f02 --- /dev/null +++ b/backend/src/ErrorHandling/Error.hpp @@ -0,0 +1,63 @@ +// Reused from SpECTRE : https://spectre-code.org/ +// Distributed under the MIT License. +// See LICENSE.txt for details. + +#pragma once + +#include +#include + +#include "ErrorHandling/Abort.hpp" +#include "ErrorHandling/AbortWithErrorMessage.hpp" +#include "ErrorHandling/Breakpoint.hpp" +#include "ErrorHandling/FloatingPointExceptions.hpp" + +/*! + * \ingroup ErrorHandlingGroup + * \brief prints an error message to the standard error stream and aborts the + * program. + * + * ERROR should not be used for coding errors, but instead for user errors + * or failure modes of numerical algorithms. An acceptable use for error is also + * in the default case of a switch statement. + * \param m an arbitrary output stream. + */ +// isocpp.org recommends using an `if (true)` instead of a `do +// while(false)` for macros because the latter can mess with inlining +// in some (old?) compilers: +// https://isocpp.org/wiki/faq/misc-technical-issues#macros-with-multi-stmts +// https://isocpp.org/wiki/faq/misc-technical-issues#macros-with-if +// However, Intel's reachability analyzer (as of version 16.0.3 +// 20160415) can't figure out that the else branch and everything +// after it is unreachable, causing warnings (and possibly suboptimal +// code generation). +#define ERROR(m) \ + do { \ + disable_floating_point_exceptions(); \ + std::ostringstream avoid_name_collisions_ERROR; \ + /* clang-tidy: macro arg in parentheses */ \ + avoid_name_collisions_ERROR << m; /* NOLINT */ \ + abort_with_error_message(__FILE__, __LINE__, \ + static_cast(__PRETTY_FUNCTION__), \ + avoid_name_collisions_ERROR.str()); \ + } while (false) + +/*! + * \ingroup ErrorHandlingGroup + * \brief prints an error message to the standard error and aborts the + * program. + * + * CERROR is just like ERROR and so the same guidelines apply. However, because + * it does not use std::stringstream it can be used in some constexpr + * functions where ERROR cannot be. + * \param m error message as a string, may need to use string literals + */ +#define CERROR(m) \ + do { \ + breakpoint(); \ + using std::string_literals::operator""s; \ + Parallel::abort("\n################ ERROR ################\nLine: "s + \ + std::to_string(__LINE__) + " of file '"s + __FILE__ + \ + "'\n"s + m + /* NOLINT */ \ + "\n#######################################\n"s); \ + } while (false) diff --git a/backend/src/ErrorHandling/Exit.hpp b/backend/src/ErrorHandling/Exit.hpp new file mode 100644 index 000000000..ff83d7eca --- /dev/null +++ b/backend/src/ErrorHandling/Exit.hpp @@ -0,0 +1,16 @@ +/// \file +/// Defines function ErrorHandling::abort. + +#pragma once + +#include + +namespace Parallel { + /// Abort the program with an error message. + [[noreturn]] inline void exit() { + std::exit(EXIT_SUCCESS); + // the following call is never reached, but suppresses the warning that + // a 'noreturn' functions does return + std::terminate(); // LCOV_EXCL_LINE + } +} // namespace Parallel diff --git a/backend/src/ErrorHandling/ExpectsAndEnsures.hpp b/backend/src/ErrorHandling/ExpectsAndEnsures.hpp new file mode 100644 index 000000000..a42d93235 --- /dev/null +++ b/backend/src/ErrorHandling/ExpectsAndEnsures.hpp @@ -0,0 +1,72 @@ +// Distributed under the MIT License. +// See LICENSE.txt for details. + +/// \file +/// Defines macros Expects and Ensures + +#pragma once + +#include "ErrorHandling/Error.hpp" + +// part of GSL, but GSL depends on Expects and Ensures... +#ifndef UNLIKELY +#if defined(__clang__) || defined(__GNUC__) +#define UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define UNLIKELY(x) (x) +#endif +#endif + +// part of GSL, but GSL depends on Expects and Ensures... +#ifndef LIKELY +#if defined(__clang__) || defined(__GNUC__) +#define LIKELY(x) __builtin_expect(!!(x), 1) +#else +#define LIKELY(x) (x) +#endif +#endif + +/*! + * \ingroup ErrorHandlingGroup + * \brief check expectation of pre-conditions of a function + * + * The Expects macro sets the preconditions to a function's arguments, it is a + * contract (C++20) that must be satisfied. See the CppCoreGuidelines for + * details. + * \param cond the expression that is expected to be true + */ +#if defined(ELASTICA_DEBUG) || defined(EXPECTS_ENSURES) +#define Expects(cond) \ + if (UNLIKELY(!(cond))) { \ + CERROR("Expects violated: "s + #cond); \ + } else \ + static_cast(0) +#else +#define Expects(cond) \ + if (false) { \ + static_cast(cond); \ + } else \ + static_cast(0) +#endif + +/*! + * \ingroup ErrorHandlingGroup + * \brief Check that a post-condition of a function is true + * + * The Ensures macro sets the postconditions of function, it is a contract + * (C++20) that must be satisfied. See the CppCoreGuidelines for details. + * \param cond the expression that is expected to be true + */ +#if defined(ELASTICA_DEBUG) || defined(EXPECTS_ENSURES) +#define Ensures(cond) \ + if (UNLIKELY(!(cond))) { \ + CERROR("Ensures violated: "s + #cond); \ + } else \ + static_cast(0) +#else +#define Ensures(cond) \ + if (false) { \ + static_cast(cond); \ + } else \ + static_cast(0) +#endif diff --git a/backend/src/ErrorHandling/FloatingPointExceptions.cpp b/backend/src/ErrorHandling/FloatingPointExceptions.cpp new file mode 100644 index 000000000..954567bb0 --- /dev/null +++ b/backend/src/ErrorHandling/FloatingPointExceptions.cpp @@ -0,0 +1,53 @@ +// Distributed under the MIT License. +// See LICENSE.txt for details. + +#include "ErrorHandling/FloatingPointExceptions.hpp" + +#include "ErrorHandling/Abort.hpp" + +#include + +#ifdef __APPLE__ +#ifdef __arm64__ +#include +#else +#include +#endif +#else +#include +#endif + +namespace { + +#ifdef __APPLE__ +#ifndef __arm64__ + auto old_mask = _mm_getcsr(); +#endif +#endif + + [[noreturn]] void fpe_signal_handler(int /*signal*/) { + Parallel::abort("Floating point exception!"); // LCOV_EXCL_LINE + } +} // namespace + +void enable_floating_point_exceptions() { +#ifdef __APPLE__ +#ifndef __arm64__ + _mm_setcsr(_MM_MASK_MASK & + ~(_MM_MASK_OVERFLOW | _MM_MASK_INVALID | _MM_MASK_DIV_ZERO)); +#endif +#else + feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW); +#endif + std::signal(SIGFPE, fpe_signal_handler); +} + +void disable_floating_point_exceptions() { +#ifdef __APPLE__ +#ifndef __arm64__ + _mm_setcsr(old_mask); +#endif +#else + fedisableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW); +#endif +} diff --git a/backend/src/ErrorHandling/FloatingPointExceptions.hpp b/backend/src/ErrorHandling/FloatingPointExceptions.hpp new file mode 100644 index 000000000..c4841276a --- /dev/null +++ b/backend/src/ErrorHandling/FloatingPointExceptions.hpp @@ -0,0 +1,17 @@ +// Distributed under the MIT License. +// See LICENSE.txt for details. + +/// \file +/// Functions to enable/disable termination on floating point exceptions + +#pragma once + +/// \ingroup ErrorHandlingGroup +/// After a call to this function, the code will terminate with a floating +/// point exception on overflow, divide-by-zero, and invalid operations. +void enable_floating_point_exceptions(); + +/// \ingroup ErrorHandlingGroup +/// After a call to this function, the code will NOT terminate with a floating +/// point exception on overflow, divide-by-zero, and invalid operations. +void disable_floating_point_exceptions(); diff --git a/backend/src/ErrorHandling/NestedExceptions.cpp b/backend/src/ErrorHandling/NestedExceptions.cpp new file mode 100644 index 000000000..b1dbe4595 --- /dev/null +++ b/backend/src/ErrorHandling/NestedExceptions.cpp @@ -0,0 +1,22 @@ +// Distributed under the MIT License. +// See LICENSE.txt for details. + +#include "ErrorHandling/NestedExceptions.hpp" + +#include + +#include "ErrorHandling/Abort.hpp" + +void abort_with_backtrace(const std::exception& e, std::size_t level) { + std::cerr << std::string(level, ' ') << "exception: " << e.what() << '\n'; + try { + std::rethrow_if_nested(e); + } catch (const std::exception& ne) { + abort_with_backtrace(ne, level + 1); + } catch (...) { + } + + // issues with Catch here, instead of aborting, simply throw an + throw std::runtime_error("Cannot proceed with program, aborting..."); + // Parallel::abort("Cannot proceed with program, aborting..."); +} diff --git a/backend/src/ErrorHandling/NestedExceptions.hpp b/backend/src/ErrorHandling/NestedExceptions.hpp new file mode 100644 index 000000000..84f097763 --- /dev/null +++ b/backend/src/ErrorHandling/NestedExceptions.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +//****************************************************************************** +/*!\brief Print backtrace of nested exceptions + * \ingroup ErrorHandlingGroup + * + * \details + * Prints the explanatory string of an exception. If the exception is + * nested, ecurses to print the explanatory of the exception it holds. + */ +void abort_with_backtrace(const std::exception& e, std::size_t level = 0UL); +//****************************************************************************** diff --git a/backend/src/ErrorHandling/README.md b/backend/src/ErrorHandling/README.md new file mode 100644 index 000000000..59d5088c0 --- /dev/null +++ b/backend/src/ErrorHandling/README.md @@ -0,0 +1,4 @@ +## ErrorHandling + +Internal error handling utilities in `Elastica++`. Only touch this when doing parallel (MPI/SMP) operations. Else the std:: +equivalents work well. diff --git a/backend/src/ModuleSettings/Parallel.hpp b/backend/src/ModuleSettings/Parallel.hpp new file mode 100644 index 000000000..b742654ab --- /dev/null +++ b/backend/src/ModuleSettings/Parallel.hpp @@ -0,0 +1,35 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** + +namespace elastica { + + //============================================================================ + // + // PARALLEL CONFIGURATION + // + //============================================================================ + +#include "Configuration/Parallel.hpp" + +//****************************************************************************** +/*!\brief Compilation switch for TBB parallelization. + * \ingroup parallel + * + * This compilation switch enables/disables TBB parallelization. In case TBB is + * enabled during compilation, \elastica attempts to parallelize computations, + * if requested by the configuration. In case no parallelization is enabled, + * all computations are performed on a single compute core, even if the + * configuration requests it + */ +#if defined(ELASTICA_USE_SHARED_MEMORY_PARALLELIZATION) && \ + defined(ELASTICA_USE_TBB) +#define ELASTICA_TBB_PARALLEL_MODE 1 +#else +#define ELASTICA_TBB_PARALLEL_MODE 0 +#endif + //**************************************************************************** + +} // namespace elastica diff --git a/backend/src/ModuleSettings/Systems.hpp b/backend/src/ModuleSettings/Systems.hpp new file mode 100644 index 000000000..fa15b96a1 --- /dev/null +++ b/backend/src/ModuleSettings/Systems.hpp @@ -0,0 +1,30 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +#include + +namespace elastica { + + //============================================================================ + // + // SYSTEMS CONFIGURATION + // + //============================================================================ + +#include "Configuration/Systems.hpp" + + namespace detail { + + inline bool systems_warnings_enabled() /* noexcept*/ { + // if set to 0 also return true + if (const char* env_v = std::getenv(ENV_ELASTICA_NO_SYSTEM_WARN)) + return not bool(env_v[0]); + else + return true; + } + + } // namespace detail + +} // namespace elastica diff --git a/backend/src/ModuleSettings/Vectorization.hpp b/backend/src/ModuleSettings/Vectorization.hpp new file mode 100644 index 000000000..36fd75e04 --- /dev/null +++ b/backend/src/ModuleSettings/Vectorization.hpp @@ -0,0 +1,22 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** + +// Ideally the check here would include system vectorization macro checks, such +// as in blaze. +// But directly including blaze's vectorization also includes intrinsics headers +// which we want to avoid. + +//****************************************************************************** +/*!\brief Vectorization flag for some Elastica++ kernels + * \ingroup config + * + * This macro enables use of vectorized kernels when available. + */ +#if defined(__APPLE__) && defined(__arm64__) +#define ELASTICA_USE_VECTORIZATION 0 +#else +#define ELASTICA_USE_VECTORIZATION BLAZE_USE_VECTORIZATION +#endif diff --git a/backend/src/ModuleSettings/Version.hpp b/backend/src/ModuleSettings/Version.hpp new file mode 100644 index 000000000..1dc2338ed --- /dev/null +++ b/backend/src/ModuleSettings/Version.hpp @@ -0,0 +1,37 @@ +#pragma once + +//****************************************************************************** +/*!\brief Major version of \elastica. + * \ingroup config + * + * \details + * This value corresponds to the major version of \elastica. For + * instance, in \elastica version 3.9.0, the ELASTICA_MAJOR_VERSION corresponds + * to 3. + */ +#define ELASTICA_MAJOR_VERSION 0 +//****************************************************************************** + +//****************************************************************************** +/*!\brief Minor version of \elastica. + * \ingroup config + * + * \details + * This value corresponds to the minor version of \elastica. For + * instance, in \elastica version 3.9.0, the ELASTICA_MINOR_VERSION corresponds + * to 9. + */ +#define ELASTICA_MINOR_VERSION 0 +//****************************************************************************** + +//****************************************************************************** +/*!\brief Patch version of \elastica. + * \ingroup config + * + * \details + * This value corresponds to the patch version of \elastica. For + * instance, in \elastica version 3.9.0, the ELASTICA_PATCH_VERSION corresponds + * to 0. + */ +#define ELASTICA_PATCH_VERSION 2 +//****************************************************************************** diff --git a/backend/src/Simulator/Frames.hpp b/backend/src/Simulator/Frames.hpp new file mode 100644 index 000000000..2e46a78f9 --- /dev/null +++ b/backend/src/Simulator/Frames.hpp @@ -0,0 +1,95 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +#include "Simulator/Frames/EulerianFrame.hpp" +#include "Simulator/Frames/LagrangianFrame.hpp" +#include "Utilities/Math/Vec3.hpp" + +namespace elastica { + + // ? + using EulerianFrame = detail::EulerianFrame; + using detail::cast_along; + using LagrangianFrame = detail::LagrangianFrame; + // ? + + //**************************************************************************** + /*!\brief Gets a unit vector along an Eulerian direction + * \ingroup simulator + * + * \details + * Gets the unit vector along a direction `Dir` of an Eulerian Frame + * + * \param dir Direction along which to get a unit vector + */ + inline constexpr auto get_unit_vector_along( + EulerianFrame::DirectionType dir) noexcept -> Vec3 { + return { + cast_along(dir) == 0 ? 1.0 : 0.0, + cast_along(dir) == 1 ? 1.0 : 0.0, + cast_along(dir) == 2 ? 1.0 : 0.0, + }; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Gets a unit vector along an Eulerian direction + * \ingroup simulator + * + * \details + * Gets the unit vector along a direction `Dir` of an Eulerian Frame + * + * \tparam Dir Direction along which to get a unit vector + */ + template + inline constexpr auto get_unit_vector_along() noexcept -> Vec3 { + return get_unit_vector_along(Dir); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Gets a unit vector along a Lagrangian direction + * \ingroup simulator + * + * \details + * Gets the unit vector along a direction `Dir` of an Lagrangian Frame + * + * \example + * \snippet Test_LagrangianFrame.cpp vector_along_eg + * + * \param dir Direction along which to get a unit vector + */ + inline constexpr auto get_unit_vector_along( + LagrangianFrame::DirectionType dir) noexcept -> Vec3 { + return { + cast_along(dir) == 0 ? 1.0 : 0.0, + cast_along(dir) == 1 ? 1.0 : 0.0, + cast_along(dir) == 2 ? 1.0 : 0.0, + }; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Gets a unit vector along a Lagrangian direction + * \ingroup simulator + * + * \details + * Gets the unit vector along a direction `Dir` of an Lagrangian Frame + * + * \tparam Dir Direction along which to get a unit vector + */ + template + inline constexpr auto get_unit_vector_along() noexcept -> Vec3 { + return get_unit_vector_along(Dir); + } + //**************************************************************************** + + struct Frames { + static_assert(LagrangianFrame::Dimension == EulerianFrame::Dimension, + "Wrong dimension configuration!"); + static constexpr auto Dimension = LagrangianFrame::Dimension; + }; + +} // namespace elastica diff --git a/backend/src/Simulator/Frames/EulerianFrame.hpp b/backend/src/Simulator/Frames/EulerianFrame.hpp new file mode 100644 index 000000000..c48a35011 --- /dev/null +++ b/backend/src/Simulator/Frames/EulerianFrame.hpp @@ -0,0 +1,90 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** + +#include + +#include "Utilities/NonCreatable.hpp" + +namespace elastica { + + namespace detail { + + //========================================================================== + // + // CLASS DEFINITION + // + //========================================================================== + + //************************************************************************** + /*!\brief Trait defining unique IDs for directions in Eulerian frame + * \ingroup simulator + * + * EulerianFrame is a trait defining the type and number of directions in + * 3D. It serves as a strong type for all directions within the \elastica + * library. + */ + struct EulerianFrame : public NonCreatable { + public: + //**Type definitions****************************************************** + //! The type of direction ID + enum class DirectionType : std::uint8_t { x = 0, y, z, Count }; + //************************************************************************ + + //**Static members******************************************************** + //! unique ID for X direction + static constexpr DirectionType X = DirectionType::x; + //! unique ID for Y direction + static constexpr DirectionType Y = DirectionType::y; + //! unique ID for Z direction + static constexpr DirectionType Z = DirectionType::z; + //! Dimension of simulation + static constexpr auto Dimension = + static_cast(DirectionType::Count); + //************************************************************************ + }; + //************************************************************************** + + //************************************************************************** + /*!\brief Casts a Eulerian frame direction into an index value + * \ingroup simulator + * + * \details + * Converts a EulerianFrame::DirectionType into an index value in + * associative arrays. + * + * \example + * \snippet Test_EulerianFrame.cpp cast_along_eg + * + * \param dir A Direction to cast as index to + */ + inline constexpr auto cast_along(EulerianFrame::DirectionType dir) + -> std::size_t { + return static_cast(dir); + } + //************************************************************************** + + //************************************************************************** + /*!\brief Casts a Eulerian frame direction into an index value + * \ingroup simulator + * + * \details + * Converts a EulerianFrame::DirectionType into an index value in + * associative arrays. + * + * \example + * \snippet Test_EulerianFrame.cpp cast_along_template_eg + * + * \tparam Dir A Direction to cast as index to + */ + template + inline constexpr auto cast_along() -> std::size_t { + return cast_along(Dir); + } + //************************************************************************** + + } // namespace detail + +} // namespace elastica diff --git a/backend/src/Simulator/Frames/LagrangianFrame.hpp b/backend/src/Simulator/Frames/LagrangianFrame.hpp new file mode 100644 index 000000000..d6976364a --- /dev/null +++ b/backend/src/Simulator/Frames/LagrangianFrame.hpp @@ -0,0 +1,103 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** + +#include + +#include "Simulator/Frames/RotationConvention.hpp" + +#include "Utilities/NonCreatable.hpp" + +namespace elastica { + + namespace detail { + + //========================================================================== + // + // CLASS DEFINITION + // + //========================================================================== + + //************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\brief Trait defining unique IDs for different directions + * \ingroup domain + * + * LagrangianFrame is a trait defining the type and number of directions in + * 3D, in a convected frame. It serves as a strong type for all Lagrangian + * directions within the \elastica library. + */ + struct LagrangianFrame : public NonCreatable { + public: + //**Type definitions****************************************************** + //! The type of direction ID + enum class DirectionType : std::uint8_t { d1 = 0, d2, d3, Count }; + //************************************************************************ + + //**Static members******************************************************** + //! unique ID for D1 direction + static constexpr DirectionType D1 = DirectionType::d1; + //! unique ID for D2 direction + static constexpr DirectionType D2 = DirectionType::d2; + //! unique ID for D3 direction + static constexpr DirectionType D3 = DirectionType::d3; + //! unique ID for the normal direction + static constexpr DirectionType Normal = + DirectionType(RotationConvention::Normal); + //! unique ID for the binormal direction + static constexpr DirectionType Binormal = + DirectionType(RotationConvention::Binormal); + //! unique ID for the tangent direction + static constexpr DirectionType Tangent = + DirectionType(RotationConvention::Tangent); + //! Dimension of simulation + static constexpr auto Dimension = + static_cast(DirectionType::Count); + //************************************************************************ + }; + /*! \endcond */ + //************************************************************************** + + //************************************************************************** + /*!\brief Casts a Lagrangian frame direction into an index value + * \ingroup simulator + * + * \details + * Converts a LagrangianFrame::DirectionType into an index value for use in + * generic code or associative arrays. + * + * \example + * \snippet Test_LagrangianFrame.cpp cast_along_eg + * + * \param dir A Direction to cast as index to + */ + inline constexpr auto cast_along(LagrangianFrame::DirectionType dir) + -> std::size_t { + return static_cast(dir); + } + //************************************************************************** + + //************************************************************************** + /*!\brief Casts a Lagrangian frame direction into an index value + * \ingroup simulator + * + * \details + * Converts a LagrangianFrame::DirectionType into an index value for use in + * generic code or associative arrays. + * + * \example + * \snippet Test_LagrangianFrame.cpp cast_along_template_eg + * + * \tparam Dir A Direction to cast as index to + */ + template + inline constexpr auto cast_along() -> std::size_t { + return cast_along(Dir); + } + //************************************************************************** + + } // namespace detail + +} // namespace elastica diff --git a/backend/src/Simulator/Frames/RotationConvention.hpp b/backend/src/Simulator/Frames/RotationConvention.hpp new file mode 100644 index 000000000..724dfb3f9 --- /dev/null +++ b/backend/src/Simulator/Frames/RotationConvention.hpp @@ -0,0 +1,27 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +#include + +namespace elastica { + + //************************************************************************** + /*!\brief The rotation convention followed by \elastica + * \ingroup utils + * + * \details + * TODO + */ + struct RotationConvention { + //! unique ID for the normal direction + static constexpr std::uint8_t Normal = 0U; + //! unique ID for the binormal direction + static constexpr std::uint8_t Binormal = 1U; + //! unique ID for the tangent direction + static constexpr std::uint8_t Tangent = 2U; + }; + //**************************************************************************** + +} // namespace elastica diff --git a/backend/src/Simulator/Materials.hpp b/backend/src/Simulator/Materials.hpp new file mode 100644 index 000000000..bac697032 --- /dev/null +++ b/backend/src/Simulator/Materials.hpp @@ -0,0 +1,11 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** + +// Forward declarations come first +#include "Simulator/Materials/Types.hpp" +// +#include "Simulator/Materials/Materials.hpp" +// any pre-registered materials (like biological etc.) diff --git a/backend/src/Simulator/Materials/Materials.cpp b/backend/src/Simulator/Materials/Materials.cpp new file mode 100644 index 000000000..944d197df --- /dev/null +++ b/backend/src/Simulator/Materials/Materials.cpp @@ -0,0 +1,589 @@ +//============================================================================== +/*! +// \file +// \brief Source file for materials +// +// Copyright (C) 2020-2020 Tejaswin Parthasarathy - All Rights Reserved +// Copyright (C) 2020-2020 MattiaLab - All Rights Reserved +// +// Distributed under the MIT License. +// See LICENSE.txt for details. +// +// This file contains implementation from the pe library with the following +// copyrights +** +** Project home: https://www.cs10.tf.fau.de/research/software/pe/ +** +** Copyright (C) 2009 Klaus Iglberger +** +** This file is part of pe. +** +** pe is free software: you can redistribute it and/or modify it under the +** terms of the GNU General Public License as published by the Free Software +** Foundation, either version 3 of the License, or (at your option) any later +** version. +** +** pe is distributed in the hope that it will be useful, but WITHOUT ANY +** WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +** A PARTICULAR PURPOSE. See the GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License along with +** pe. If not, see . +*/ +//============================================================================== + +//****************************************************************************** +// Includes +//****************************************************************************** + +#include "Materials.hpp" + +#include +#include + +namespace elastica { + + //============================================================================ + // + // DEFINITION AND INITIALIZATION OF THE STATIC MEMBER VARIABLES + // + //============================================================================ + + Materials Material::materials_; + Material::MatrixType Material::corTable_; + Material::MatrixType Material::csfTable_; + Material::MatrixType Material::backward_csfTable_; + Material::MatrixType Material::lateral_csfTable_; + Material::MatrixType Material::cdfTable_; + Material::MatrixType Material::backward_cdfTable_; + Material::MatrixType Material::lateral_cdfTable_; + + // clang-format off + // clang-tidy (UnusedGlobalDeclarationInspection) + bool Material::materialsActivated_(activate_materials()); // NOLINT + // clang-format on + unsigned int Material::anonymousMaterials_ = 0; + + //============================================================================ + // + // CLASS MATERIALS + // + //============================================================================ + + //**************************************************************************** + /*!\brief Automatic registration of the default materials. + * + * \return \a true after the materials have been registered. + */ + bool Material::activate_materials() noexcept { + // Registering the default materials + materials_.push_back(Iron()); + materials_.push_back(Copper()); + materials_.push_back(Granite()); + materials_.push_back(Oak()); + materials_.push_back(Fir()); + + // Initializing the coefficients of restitution + // | Iron | Copper | + // Granite | Oak | + // Fir + // | + real_t const cor[5][5] = { + {static_cast(0.25), static_cast(0.25), + static_cast(0.25), static_cast(0.25), + static_cast(0.25)}, // Iron + {static_cast(0.25), static_cast(0.25), + static_cast(0.25), static_cast(0.25), + static_cast(0.25)}, // Copper + {static_cast(0.25), static_cast(0.25), + static_cast(0.25), static_cast(0.25), + static_cast(0.25)}, // Granite + {static_cast(0.25), static_cast(0.25), + static_cast(0.25), static_cast(0.25), + static_cast(0.25)}, // Oak + {static_cast(0.25), static_cast(0.25), + static_cast(0.25), static_cast(0.25), + static_cast(0.25)} // Fir + }; + corTable_ = cor; + + // Initializing the coefficients of static friction + // | Iron | Copper | + // Granite | Oak | + // Fir + // | + real_t const csf[5][5] = { + {static_cast(0.20), static_cast(0.20), + static_cast(0.20), static_cast(0.20), + static_cast(0.20)}, // Iron + {static_cast(0.20), static_cast(0.20), + static_cast(0.20), static_cast(0.20), + static_cast(0.20)}, // Copper + {static_cast(0.20), static_cast(0.20), + static_cast(0.20), static_cast(0.20), + static_cast(0.20)}, // Granite + {static_cast(0.20), static_cast(0.20), + static_cast(0.20), static_cast(0.20), + static_cast(0.20)}, // Oak + {static_cast(0.20), static_cast(0.20), + static_cast(0.20), static_cast(0.20), + static_cast(0.20)} // Fir + }; + csfTable_ = csf; + backward_csfTable_ = csf; + lateral_csfTable_ = csf; + + // Initializing the coefficients of dynamic friction + // | Iron | Copper | + // Granite | Oak | + // Fir + // | + real_t const cdf[5][5] = { + {static_cast(0.20), static_cast(0.20), + static_cast(0.20), static_cast(0.20), + static_cast(0.20)}, // Iron + {static_cast(0.20), static_cast(0.20), + static_cast(0.20), static_cast(0.20), + static_cast(0.20)}, // Copper + {static_cast(0.20), static_cast(0.20), + static_cast(0.20), static_cast(0.20), + static_cast(0.20)}, // Granite + {static_cast(0.20), static_cast(0.20), + static_cast(0.20), static_cast(0.20), + static_cast(0.20)}, // Oak + {static_cast(0.20), static_cast(0.20), + static_cast(0.20), static_cast(0.20), + static_cast(0.20)} // Fir + }; + cdfTable_ = cdf; + lateral_cdfTable_ = cdf; + backward_cdfTable_ = cdf; + + return true; + } + //**************************************************************************** + + //============================================================================ + // + // MATERIAL FUNCTIONS + // + //============================================================================ + + //**************************************************************************** + /*!\brief Creating a new custom material. + * \ingroup materials + * + * \details + * This function creates the new, custom material \a name with the given + * properties. The following example illustrates the use of this function: + * \example + * \code + * // Creates the material "myMaterial" with the following material + * // properties: + * // - material density : 2.54 + * // - coefficient of restitution : 0.8 + * // - Young's modulus : 80 + * // - Shear modulus : 120 + * // - Contact stiffness : 100 + * // - dampingN : 10 + * // - dampingT : 11 + * // - coefficient of forward static friction : 0.1 + * // - coefficient of backward static friction : 0.2 + * // - coefficient of lateral static friction : 0.15 + * // - coefficient of forward dynamic friction: 0.05 + * // - coefficient of backward dynamic friction: 0.1 + * // - coefficient of lateral dynamic friction: 0.075 + * MaterialID myMaterial = create_material("myMaterial", 2.54, 0.8, 80, + * 120, 100, 10, 11, 0.1, 0.2, 0.15, + * 0.05, 0.1, 0.075); + * \endcode + * + * In case the name of the material is already in use or if any of the + * coefficients is not in its allowed range, a std::invalid_argument exception + * is thrown. + * + * The coefficient of restitution is given for self-similar collisions that is + * collision of bodies made of similar material. The composite coefficient of + * restitution \f$e_*\f$ is estimated as proposed by Stronge: + * \f$\frac{e_*^2}{k_*} = \frac{e_1^2}{k_1} + \frac{e_2^2}{k_2}\f$. + * + * \param name The name of the material. + * \param density The density of the material \f$ (0..\infty) \f$. + * \param cor The coefficient of restitution (COR) of the material + * \f$ [0..1] \f$. + * \param young The Young's modulus of the material \f$ (0..\infty) \f$. + * \param shear The Shear modulus of the material \f$ (0..\infty) \f$. + * \param stiffness The stiffness in normal direction of the material's + * contact region. + * \param dampingN The damping coefficient in normal direction of the + * material's contact region. + * \param dampingT The damping coefficient in tangential direction of the + * material's contact region. + * \param forward_csf The coefficient of static friction (CSF) of the material + * in the forward direction \f$ [0..\infty) \f$. + * \param backward_csf The coefficient of static friction (CSF) of the + * material in the backward direction \f$ [0..\infty) \f$. + * \param lateral_csf The coefficient of static friction (CSF) of the material + * in the lateral direction \f$ [0..\infty) \f$. + * \param forward_cdf The coefficient of dynamic friction (CDF) of the + * material in the forward direction \f$ [0..\infty) \f$ + * \param backward_cdf The coefficient of dynamic friction (CDF) of the + * material in the backward direction \f$ [0..\infty) \f$ + * \param lateral_cdf The coefficient of dynamic friction (CDF) of the + * material in the lateral direction \f$ [0..\infty) \f$ + * + * \return The MaterialID for the new material. + * \exception std::invalid_argument Invalid material parameter. + */ + MaterialID create_material(std::string const& name, real_t density, + real_t cor, real_t young, real_t shear, + real_t stiffness, real_t dampingN, real_t dampingT, + real_t forward_csf, real_t backward_csf, + real_t lateral_csf, real_t forward_cdf, + real_t backward_cdf, real_t lateral_cdf) { + typedef Material M; + + // Checking the material name + auto curr(M::materials_.cbegin()); + auto end(M::materials_.cend()); + for (; curr != end; ++curr) { + if (curr->get_name() == name) + throw std::invalid_argument("Material of that name already exists!"); + } + + // Checking the density + if (density <= real_t(0)) + throw std::invalid_argument("Invalid material density!"); + + // Checking the coefficient of restitution + if (cor < real_t(0) or cor > real_t(1)) + throw std::invalid_argument("Invalid coefficient of restitution!"); + + // Checking the coefficients of static friction + if (forward_csf < real_t(0)) + throw std::invalid_argument( + "Invalid coefficient of forward static friction!"); + + // Checking the coefficient of static friction + if (backward_csf < real_t(0)) + throw std::invalid_argument( + "Invalid coefficient of backward static friction!"); + + // Checking the coefficient of static friction + if (lateral_csf < real_t(0)) + throw std::invalid_argument( + "Invalid coefficient of lateral static friction!"); + + // Checking the coefficient of dynamic friction + if (forward_cdf < real_t(0)) + throw std::invalid_argument( + "Invalid coefficient of forward dynamic friction!"); + + // Checking the coefficient of dynamic friction + if (backward_cdf < real_t(0)) + throw std::invalid_argument( + "Invalid coefficient of backward dynamic friction!"); + + // Checking the coefficient of dynamic friction + if (lateral_cdf < real_t(0)) + throw std::invalid_argument( + "Invalid coefficient of lateral dynamic friction!"); + + // // Checking the Poisson's ratio + // if (poisson < real_t(-1) or poisson > real_t(0.5)) + // throw std::invalid_argument("Invalid Poisson's ratio"); + // + // Checking the Young's modulus + if (young <= real_t(0)) + throw std::invalid_argument("Invalid Young's modulus"); + + if (shear <= real_t(0)) + throw std::invalid_argument("Invalid shear modulus"); + + // Checking the stiffness + if (stiffness <= real_t(0)) + throw std::invalid_argument("Invalid stiffness"); + + // Checking the damping coefficients + if (dampingN < real_t(0) or dampingT < real_t(0)) + throw std::invalid_argument("Invalid damping coefficients"); + + // Registering the new material + real_t const poisson(0.5); + M::materials_.push_back(Material(name, density, cor, poisson, young, shear, + stiffness, dampingN, dampingT, forward_csf, + backward_csf, lateral_csf, forward_cdf, + backward_cdf, lateral_cdf)); + MaterialID const mat(M::materials_.size() - 1); + + // Updating the restitution table, the static friction table and the dynamic + // friction table + M::corTable_.extend(1, 1, true); + M::csfTable_.extend(1, 1, true); + M::backward_csfTable_.extend(1, 1, true); + M::lateral_csfTable_.extend(1, 1, true); + M::cdfTable_.extend(1, 1, true); + M::backward_cdfTable_.extend(1, 1, true); + M::lateral_cdfTable_.extend(1, 1, true); + + ELASTICA_ASSERT(M::corTable_.rows() == M::corTable_.columns(), + "Invalid matrix size"); + + ELASTICA_ASSERT(M::csfTable_.rows() == M::csfTable_.columns(), + "Invalid matrix size"); + ELASTICA_ASSERT( + M::backward_csfTable_.rows() == M::backward_csfTable_.columns(), + "Invalid matrix size"); + ELASTICA_ASSERT( + M::lateral_csfTable_.rows() == M::lateral_csfTable_.columns(), + "Invalid matrix size"); + + ELASTICA_ASSERT(M::cdfTable_.rows() == M::cdfTable_.columns(), + "Invalid matrix size"); + ELASTICA_ASSERT( + M::backward_cdfTable_.rows() == M::backward_cdfTable_.columns(), + "Invalid matrix size"); + ELASTICA_ASSERT( + M::lateral_cdfTable_.rows() == M::lateral_cdfTable_.columns(), + "Invalid matrix size"); + + M::corTable_(mat, mat) = cor; + + M::csfTable_(mat, mat) = forward_csf + forward_csf; + M::backward_csfTable_(mat, mat) = backward_csf + backward_csf; + M::lateral_csfTable_(mat, mat) = lateral_csf + lateral_csf; + + M::cdfTable_(mat, mat) = forward_cdf + forward_cdf; + M::backward_cdfTable_(mat, mat) = backward_cdf + backward_cdf; + M::lateral_cdfTable_(mat, mat) = lateral_cdf + lateral_cdf; + + for (Materials::size_type i = 0; i < mat; ++i) { + M::corTable_(mat, i) = M::corTable_(i, mat) = + std::sqrt((sq(M::materials_[i].get_restitution()) / + M::materials_[i].get_stiffness() + + sq(cor) / stiffness) * + M::get_stiffness(mat, i)); + + M::csfTable_(mat, i) = M::csfTable_(i, mat) = + M::materials_[i].get_static_friction() + forward_csf; + M::backward_csfTable_(mat, i) = M::backward_csfTable_(i, mat) = + M::materials_[i].get_backward_static_friction() + backward_csf; + M::lateral_csfTable_(mat, i) = M::lateral_csfTable_(i, mat) = + M::materials_[i].get_lateral_static_friction() + lateral_csf; + + M::cdfTable_(mat, i) = M::cdfTable_(i, mat) = + M::materials_[i].get_dynamic_friction() + forward_cdf; + M::backward_cdfTable_(mat, i) = M::backward_cdfTable_(i, mat) = + M::materials_[i].get_backward_dynamic_friction() + backward_cdf; + M::lateral_cdfTable_(mat, i) = M::lateral_cdfTable_(i, mat) = + M::materials_[i].get_lateral_dynamic_friction() + lateral_cdf; + } + + return mat; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Creating a new anonymous custom material. + * \ingroup materials + * + * \details + * This function creates a new, custom material with the given properties. It + * will be named 'Material${X}', where X is an incrementing number. + * + * \param density The density of the material \f$ (0..\infty) \f$. + * \param cor The coefficient of restitution (COR) of the material + * \f$ [0..1] \f$. + * \param young The Young's modulus of the material \f$ (0..\infty) \f$. + * \param shear The Shear modulus of the material \f$ (0..\infty) \f$. + * \param stiffness The stiffness in normal direction of the material's + * contact region. + * \param dampingN The damping coefficient in normal direction of the + * material's contact region. + * \param dampingT The damping coefficient in tangential direction of the + * material's contact region. + * \param forward_csf The coefficient of static friction (CSF) of the material + * in the forward direction \f$ [0..\infty) \f$. + * \param backward_csf The coefficient of static friction (CSF) of the + * material in the backward direction \f$ [0..\infty) \f$. + * \param lateral_csf The coefficient of static friction (CSF) of the material + * in the lateral direction \f$ [0..\infty) \f$. + * \param forward_cdf The coefficient of dynamic friction (CDF) of the + * material in the forward direction \f$ [0..\infty) \f$ + * \param backward_cdf The coefficient of dynamic friction (CDF) of the + * material in the backward direction \f$ [0..\infty) \f$ + * \param lateral_cdf The coefficient of dynamic friction (CDF) of the + * material in the lateral direction \f$ [0..\infty) \f$ + * + * \return The MaterialID for the new material. + * \exception std::invalid_argument Invalid material parameter. + */ + MaterialID create_material(real_t density, real_t cor, real_t young, + real_t shear, real_t stiffness, real_t dampingN, + real_t dampingT, real_t forward_csf, + real_t backward_csf, real_t lateral_csf, + real_t forward_cdf, real_t backward_cdf, + real_t lateral_cdf) { + std::ostringstream sstr; + + do { + if (Material::anonymousMaterials_ + 1 == 0) + throw std::runtime_error("Index overflow for anonymous materials"); + sstr.str(""); + sstr << "Material" << ++Material::anonymousMaterials_; + } while (Material::find(sstr.str()) != invalid_material); + + return create_material(sstr.str(), density, cor, young, shear, stiffness, + dampingN, dampingT, forward_csf, backward_csf, + lateral_csf, forward_cdf, backward_cdf, lateral_cdf); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Creating a new custom material. + * \ingroup materials + * + * \details + * This function creates the new, custom material \a name with the given + * properties. The following example illustrates the use of this function: + * \example + * \code + * // Creates the material "myMaterial" with the following material + * // properties: + * // - material density : 2.54 + * // - coefficient of restitution : 0.8 + * // - Young's modulus : 80 + * // - Shear modulus : 120 + * // - Contact stiffness : 100 + * // - dampingN : 10 + * // - dampingT : 11 + * // - coefficient of static friction : 0.1 + * // - coefficient of dynamic friction: 0.05 + * MaterialID myMaterial = create_material("myMaterial", 2.54, 0.8, + * 80, 120, 100, 10, 11, 0.1, 0.05); + * \endcode + * + * In case the name of the material is already in use or if any of the + * coefficients is not in its allowed range, a std::invalid_argument exception + * is thrown. + * + * The coefficient of restitution is given for self-similar collisions that is + * collision of bodies made of similar material. The composite coefficient of + * restitution \f$e_*\f$ is estimated as proposed by Stronge: + * \f$\frac{e_*^2}{k_*} = \frac{e_1^2}{k_1} + \frac{e_2^2}{k_2}\f$. + * + * \param name The name of the material. + * \param density The density of the material \f$ (0..\infty) \f$. + * \param cor The coefficient of restitution (COR) of the material + * \f$ [0..1] \f$. + * \param young The Young's modulus of the material \f$ (0..\infty) \f$. + * \param shear The Shear modulus of the material \f$ (0..\infty) \f$. + * \param stiffness The stiffness in normal direction of the material's + * contact region. + * \param dampingN The damping coefficient in normal direction of the + * material's contact region. + * \param dampingT The damping coefficient in tangential direction of the + * material's contact region. + * \param csf The coefficient of static friction (CSF) of the material + * \f$ [0..\infty) \f$. + * \param cdf The coefficient of dynamic friction (CDF) of the material \f$ + * [0..\infty) \f$. + * + * \return The MaterialID for the new material. + * \exception std::invalid_argument Invalid material parameter. + */ + MaterialID create_material(std::string const& name, real_t density, + real_t cor, real_t young, real_t shear, + real_t stiffness, real_t dampingN, real_t dampingT, + real_t csf, real_t cdf) { + return create_material(name, density, cor, young, shear, stiffness, + dampingN, dampingT, csf, csf, csf, cdf, cdf, cdf); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Creating a new anonymous custom material. + * \ingroup materials + * + * \details + * This function creates a new, custom material with the given properties. It + * will be named 'Material${X}', where X is an incrementing number. + * + * \param density The density of the material \f$ (0..\infty) \f$. + * \param cor The coefficient of restitution (COR) of the material + * \f$ [0..1] \f$. + * \param poisson The Poisson's ratio of the material \f$ [-1..0.5] \f$. + * \param young The Young's modulus of the material \f$ (0..\infty) \f$. + * \param shear The Shear modulus of the material \f$ (0..\infty) \f$. + * \param stiffness The stiffness in normal direction of the material's + * contact region. + * \param dampingN The damping coefficient in normal direction of the + * material's contact region. + * \param dampingT The damping coefficient in tangential direction of the + * material's contact region. + * \param csf The coefficient of static friction (CSF) of the material + * \f$ [0..\infty) \f$. + * \param cdf The coefficient of dynamic friction (CDF) of the + * material \f$ [0..\infty) \f$ + * + * \return The MaterialID for the new material. + * \exception std::invalid_argument Invalid material parameter. + */ + MaterialID create_material(real_t density, real_t cor, real_t young, + real_t shear, real_t stiffness, real_t dampingN, + real_t dampingT, real_t csf, real_t cdf) { + return create_material(density, cor, young, shear, stiffness, dampingN, + dampingT, csf, csf, csf, cdf, cdf, cdf); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Searching for a registered material. + * \ingroup materials + * + * \param name The name of the material. + * \return The MaterialID of the material if the material is found, \a + * invalid_material otherwise. + * \exception std::runtime_error, in case an invalid name is specified + * + * The function searches for a registered material with the given name. If the + * material is found, the corresponding MaterialID is returned. Otherwise, \a + * invalid_material is returned. + */ + MaterialID Material::find(std::string const& name) noexcept { + for (Material::SizeType i = 0; i < Material::materials_.size(); ++i) { + if (Material::materials_[i].get_name() == name) { + return i; + } + } + // We return invalid material rather than raise an exception here for + // having anonymous materials + return invalid_material; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Searching for registered materials with a prefix. + // \ingroup materials + // + // \param prefix The prefix common to the names of the materials. + // \return A std::vector object containing the MaterialIDs of all materials + // found. + // + // The function collects all registered materials with names beginning with + // the given string. Their IDs are assembled in an std::vector object. If no + // Materials are found, the container is empty. + */ + std::vector Material::find_prefix(std::string const& prefix) { + std::vector results; + for (Material::SizeType i = 0; i < Material::materials_.size(); ++i) { + if (Material::materials_[i].get_name().compare(0, prefix.size(), + prefix) == 0) { + results.push_back(i); + } + } + return results; + } + //**************************************************************************** + +} // namespace elastica diff --git a/backend/src/Simulator/Materials/Materials.hpp b/backend/src/Simulator/Materials/Materials.hpp new file mode 100644 index 000000000..c92ffece9 --- /dev/null +++ b/backend/src/Simulator/Materials/Materials.hpp @@ -0,0 +1,1862 @@ +//============================================================================== +/*! +// \file +// \brief Header file for materials +// +// Copyright (C) 2020-2020 Tejaswin Parthasarathy - All Rights Reserved +// Copyright (C) 2020-2020 MattiaLab - All Rights Reserved +// +// Distributed under the MIT License. +// See LICENSE.txt for details. +// +// This file contains implementation from the pe library with the following +// copyrights +** +** Project home: https://www.cs10.tf.fau.de/research/software/pe/ +** +** Copyright (C) 2009 Klaus Iglberger +** +** This file is part of pe. +** +** pe is free software: you can redistribute it and/or modify it under the +** terms of the GNU General Public License as published by the Free Software +** Foundation, either version 3 of the License, or (at your option) any later +** version. +** +** pe is distributed in the hope that it will be useful, but WITHOUT ANY +** WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +** A PARTICULAR PURPOSE. See the GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License along with +** pe. If not, see . +*/ +//============================================================================== + +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** + +#include +#include +#include +// #include +#include +#include + +#include "ErrorHandling/Assert.hpp" +// +#include "Simulator/Materials/Types.hpp" +// +#include "Utilities/DefineTypes.h" +#include "Utilities/Math/Invert.hpp" +#include "Utilities/Math/Square.hpp" + +namespace elastica { + + //**************************************************************************** + /*!\defgroup materials Materials + * \ingroup simulator + * \brief Contains functionality to implement and access data of new + * materials that constitute soft and rigid-bodies. + */ + //**************************************************************************** + + //============================================================================ + // + // CLASS MATERIAL + // + //============================================================================ + + //**************************************************************************** + /*!\brief Rigid body material. + * \ingroup materials + * + * A material specifies the properties of a rigid body: the density of the + * body, the coefficient of restitution and the coefficients of static and + * dynamic friction. + * + * The elastica engine provides several predefined materials that can be + * directly used: + * + * - iron + * - copper + * - granite + * - oak + * - fir + * + * \usage + * In order to create a new custom material use the create_material() + * function: \example \code + * // Creating a new material using the following material properties: + * // - name/identifier: myMaterial + * // - density: 2.54 + * // - coefficient of restitution: 0.8 + * // - coefficient of static friction: 0.1 + * // - coefficient of dynamic friction: 0.05 + * // - Poisson's ratio: 0.2 + * // - Young's modulus: 80.0 + * // - Contact stiffness: 100 + * // - dampingN: 10 + * // - dampingT: 11 + * MaterialID myMaterial = create_material("myMaterial", 2.54, 0.8, 0.1, 0.05, + * 0.2, 80, 100, 10, 11 ); + * \endcode + * + * The following functions can be used to acquire a specific MaterialID or to + * get a specific property of a material: + * \code + * // Searching a material + * MaterialID myMaterial = Material::find( "myMaterial" ); + * + * // Getting the density, coefficient of restitution, coefficient of static + * // and dynamic friction, Poisson's ratio and Young's modulus of the + * // material + * real_t density = Material::get_density( myMaterial ); + * real_t cor = Material::get_restitution( myMaterial ); + * real_t csf = Material::get_static_friction( myMaterial ); + * real_t cdf = Material::get_dynamic_friction( myMaterial ); + * real_t poisson = Material::get_poisson_ratio( myMaterial ); + * real_t young = Material::get_youngs_modulus( myMaterial ): + * \endcode + */ + class Material { + private: + //**Type definitions******************************************************** + //! Size type of the Material class. + using SizeType = std::size_t; + //! Matrix type of the Material class. + using MatrixType = blaze::DynamicMatrix; + //************************************************************************** + + public: + //**Constructor************************************************************* + /*!\name Constructor */ + //@{ + explicit inline Material(std::string name, real_t density, real_t cor, + real_t poisson, real_t young, real_t shear_modulus, + real_t stiffness, real_t normal_damping, + real_t tangential_damping, real_t csf, real_t cdf); + explicit inline Material(std::string name, real_t density, real_t cor, + real_t poisson, real_t young, real_t shear_modulus, + real_t stiffness, real_t normal_damping, + real_t tangential_damping, real_t forward_csf, + real_t backward_csf, real_t lateral_csf, + real_t forward_cdf, real_t backward_cdf, + real_t lateral_cdf); + // No explicitly declared copy constructor. + //@} + //************************************************************************** + + //**Destructor************************************************************** + // No explicitly declared destructor. + //************************************************************************** + + //**Get functions*********************************************************** + /*!\name Get functions */ + //@{ + inline const std::string& get_name() const noexcept; + inline real_t get_density() const noexcept; + inline real_t get_restitution() const noexcept; + inline real_t get_poisson_ratio() const noexcept; + inline real_t get_youngs_modulus() const noexcept; + inline real_t get_shear_modulus() const noexcept; + inline real_t get_stiffness() const noexcept; + inline real_t get_normal_damping() const noexcept; + inline real_t get_tangential_damping() const noexcept; + inline real_t get_static_friction() const noexcept; + inline real_t get_forward_static_friction() const noexcept; + inline real_t get_backward_static_friction() const noexcept; + inline real_t get_lateral_static_friction() const noexcept; + inline real_t get_dynamic_friction() const noexcept; + inline real_t get_forward_dynamic_friction() const noexcept; + inline real_t get_backward_dynamic_friction() const noexcept; + inline real_t get_lateral_dynamic_friction() const noexcept; + + static MaterialID find(std::string const& name) noexcept; + static std::vector find_prefix(std::string const& prefix); + static inline const std::string& get_name(MaterialID material) noexcept; + static inline real_t get_density(MaterialID material) noexcept; + static inline real_t get_restitution(MaterialID material) noexcept; + static inline real_t get_restitution(MaterialID material1, + MaterialID material2) noexcept; + static inline real_t get_static_friction(MaterialID material) noexcept; + static inline real_t get_forward_static_friction( + MaterialID material) noexcept; + static inline real_t get_backward_static_friction( + MaterialID material) noexcept; + static inline real_t get_lateral_static_friction( + MaterialID material) noexcept; + static inline real_t get_static_friction(MaterialID material1, + MaterialID material2) noexcept; + static inline real_t get_forward_static_friction( + MaterialID material1, MaterialID material2) noexcept; + static inline real_t get_backward_static_friction( + MaterialID material1, MaterialID material2) noexcept; + static inline real_t get_lateral_static_friction( + MaterialID material1, MaterialID material2) noexcept; + static inline real_t get_dynamic_friction(MaterialID material) noexcept; + static inline real_t get_forward_dynamic_friction( + MaterialID material) noexcept; + static inline real_t get_backward_dynamic_friction( + MaterialID material) noexcept; + static inline real_t get_lateral_dynamic_friction( + MaterialID material) noexcept; + static inline real_t get_dynamic_friction(MaterialID material1, + MaterialID material2) noexcept; + static inline real_t get_forward_dynamic_friction( + MaterialID material1, MaterialID material2) noexcept; + static inline real_t get_backward_dynamic_friction( + MaterialID material1, MaterialID material2) noexcept; + static inline real_t get_lateral_dynamic_friction( + MaterialID material1, MaterialID material2) noexcept; + + static inline real_t get_poisson_ratio(MaterialID material) noexcept; + static inline real_t get_youngs_modulus(MaterialID material) noexcept; + static inline real_t get_youngs_modulus(MaterialID material1, + MaterialID material2) noexcept; + static inline real_t get_shear_modulus(MaterialID material) noexcept; + static inline real_t get_stiffness(MaterialID material) noexcept; + static inline real_t get_stiffness(MaterialID material1, + MaterialID material2) noexcept; + static inline real_t get_normal_damping(MaterialID material) noexcept; + static inline real_t get_normal_damping(MaterialID material1, + MaterialID material2) noexcept; + static inline real_t get_tangential_damping(MaterialID material) noexcept; + static inline real_t get_tangential_damping(MaterialID material1, + MaterialID material2) noexcept; + //@} + //************************************************************************** + + //**Set functions*********************************************************** + /*!\name Set functions */ + //@{ + static inline void set_restitution(MaterialID material1, + MaterialID material2, + real_t cor) noexcept; + static inline void set_static_friction(MaterialID material1, + MaterialID material2, + real_t csf) noexcept; + static inline void set_forward_static_friction(MaterialID material1, + MaterialID material2, + real_t forward_csf) noexcept; + static inline void set_backward_static_friction( + MaterialID material1, MaterialID material2, + real_t backward_csf) noexcept; + static inline void set_lateral_static_friction(MaterialID material1, + MaterialID material2, + real_t lateral_csf) noexcept; + static inline void set_dynamic_friction(MaterialID material1, + MaterialID material2, + real_t cdf) noexcept; + static inline void set_forward_dynamic_friction( + MaterialID material1, MaterialID material2, + real_t forward_cdf) noexcept; + static inline void set_backward_dynamic_friction( + MaterialID material1, MaterialID material2, + real_t backward_cdf) noexcept; + static inline void set_lateral_dynamic_friction( + MaterialID material1, MaterialID material2, + real_t lateral_cdf) noexcept; + //@} + //************************************************************************** + + //**Calculate functions***************************************************** + /*!\name Calculate functions */ + //@{ + static inline real_t calculate_shear_modulus(real_t young, + real_t poisson) noexcept; + //@} + //************************************************************************** + + private: + //**Setup functions********************************************************* + /*!\name Setup functions */ + //@{ + static bool activate_materials() noexcept; + //@} + //************************************************************************** + + //**Member variables******************************************************** + /*!\name Member variables */ + //@{ + //! The name of the material. + std::string name_; + //! The density of the material. + real_t density_; + //! The coefficient of restitution (COR) of a self-similar collision + //! \f$ [0..1] \f$. + /* The COR represents the energy dissipated during a collision between + * self-similar bodies, that is bodies with similar materials. A value of + * 0 corresponds to completely inelastic collision where all energy is + * dissipated, a value of 1 corresponds to a completely elastic collision + * where no energy is lost. The COR is assumed to be rate-independent. The + * COR is often determined experimentally by measuring the pre- and + * post-impact relative velocities: + * \f[ C_R = \frac{V_{2,after}-V_{1,after}}{V_{2,before}-V_{1,before}} \f] + * During a collision, the COR values of the two colliding + * rigid bodies can be used by the collision response mechanism to + * determine the restitution factor of the contact point. + */ + real_t restitution_; + //! The coefficient of static friction (CSF) \f [0..\infty) \f$ + /* The CSF is a dimensionless, non-negative quantity representing the + * amount of static friction between two touching rigid bodies. Static + * friction occurs in case the relative tangential velocity between the + * two bodies is 0. Then the force magnitudes of the normal and friction + * force are related by an inequality: + * \f[ |\vec{f_t}| \leq \mu_s |\vec{f_n}| \f] + * The direction of the friction must oppose acceleration if sliding is + * imminent and is unresticted otherwise. + */ + real_t static_; + //! The coefficient of static friction (CSF) in the backward direction + //! \f [0..\infty) \f$ + /* The CSF is a dimensionless, non-negative quantity representing the + * amount of static friction between two touching rigid bodies. Static + * friction occurs in case the relative tangential velocity between the + * two bodies is 0. Then the force magnitudes of the normal and friction + * force are related by an inequality: + * \f[ |\vec{f_t}| \leq \mu_s |\vec{f_n}| \f] + * The direction of the friction must oppose acceleration if sliding is + * imminent and is unresticted otherwise. + */ + real_t backward_static_; + //! The coefficient of static friction (CSF) in the lateral direction + //! \f [0..\infty) \f$ + /* The CSF is a dimensionless, non-negative quantity representing the + * amount of static friction between two touching rigid bodies. Static + * friction occurs in case the relative tangential velocity between the + * two bodies is 0. Then the force magnitudes of the normal and friction + * force are related by an inequality: + * \f[ |\vec{f_t}| \leq \mu_s |\vec{f_n}| \f] + * The direction of the friction must oppose acceleration if sliding is + * imminent and is unresticted otherwise. + */ + real_t lateral_static_; + //! The coefficient of dynamic friction (CDF) \f [0..\infty) \f.$ + /* The CDF is a dimensionless, non-negative quantity representing the + * amount of dynamic friction between two touching rigid bodies. Dynamic + * friction occurs in case the relative tangential velocity between the + * two bodies is greater than 0. Then the force magnitudes of the normal + * and friction force are related by an inequality: + * \f[ |\vec{f_t}| = -\mu_d |\vec{f_n}| \frac{\vec{v_t}}{|\vec{v_t}|} \f] + */ + real_t dynamic_; + //! The coefficient of dynamic friction (CDF) in the backward direction + //! \f [0..\infty) \f.$ + /* The CDF is a dimensionless, non-negative quantity representing the + * amount of dynamic friction between two touching rigid bodies. Dynamic + * friction occurs in case the relative tangential velocity between the + * two bodies is greater than 0. Then the force magnitudes of the normal + * and friction force are related by an inequality: + * \f[ |\vec{f_t}| = -\mu_d |\vec{f_n}| \frac{\vec{v_t}}{|\vec{v_t}|} \f] + */ + real_t backward_dynamic_; + //! The coefficient of dynamic friction (CDF) in the lateral direction + //! \f [0..\infty) \f.$ + /* The CDF is a dimensionless, non-negative quantity representing the + * amount of dynamic friction between two touching rigid bodies. Dynamic + * friction occurs in case the relative tangential velocity between the + * two bodies is greater than 0. Then the force magnitudes of the normal + * and friction force are related by an inequality: + * \f[ |\vec{f_t}| = -\mu_d |\vec{f_n}| \frac{\vec{v_t}}{|\vec{v_t}|} \f] + */ + real_t lateral_dynamic_; + //! The Poisson's ratio for the material \f$ [-1..0.5] \f$. + /* When a material is compressed in one direction, it usually tends to + * expand in the other two directions perpendicular to the direction of + * compression. This effect is called Poisson effect. In this context, the + * Poisson's ratio is the ratio of the contraction or transverse strain + * (perpendicular to the applied load) to the extension or axial strain + * (in the direction of the applied load). For stable, isotropic, linear + * elastic materials this ratio cannot be less than -1.0 nor greater than + * 0.5 due to the requirement that Young's modulus has positive values. + */ + real_t poisson_; + //! The Young's modulus for the material \f$ (0..\infty) \f$. + /* The Young's modulus is a measure for the stiffness of an isotropic + * elastic material. It is defined as the ratio of the uniaxial stress + * over the uniaxial strain in the range of stress in which Hooke's law + * holds. The SI unit for Young's modulus is \f$ Pa \f$ or \f$ N/m^2 \f$. + */ + real_t young_; + //! The shear modulus for the material \f$ (0..\infty) \f$. + /* The shear modulus is a measure for the stiffness of an an-isotropic + * elastic material. It is defined as the ratio of the shear stress + * over the shear strain in the range of stress in which Hooke's law + * holds. The SI unit for Young's modulus is \f$ Pa \f$ or \f$ N/m^2 \f$. + */ + real_t shear_; + //! The stiffness of the contact region \f$ (0..\infty) \f$. + /* Rigid body theory assumes that the deformation during contact is + * localized to the contact region. This local compliance can be modelled + * simplified as a spring-damper. The spring constant corresponds to this + * parameter. + */ + real_t stiffness_; + //! The damping at the contact zone in normal direction \f$ [0..\infty)\f$ + /* Rigid body theory assumes that the deformation during contact is + * localized to the contact region. This local compliance in normal + * direction can be modelled simplified as a spring-damper. The viscous + * damping coefficient corresponds to this parameter. + */ + real_t dampingN_; + //! The damping at the contact zone in tangent direction \f$ [0..\infty)\f$ + /* Friction counteracts the tangential relative velocity and thus can be + * modelled as a viscous damper with a limited damping force. The viscous + * damping coefficient corresponds to this parameter. + */ + real_t dampingT_; + //! Vector for the registered materials. + static Materials materials_; + //! Lookup Table for the coefficients of restitution. + static MatrixType corTable_; + //! Lookup Table for the coefficients of static friction. + static MatrixType csfTable_; + //! Lookup Table for the coefficients of backward static friction. + static MatrixType backward_csfTable_; + //! Lookup Table for the coefficients of lateral static friction. + static MatrixType lateral_csfTable_; + //!< Lookup Table for the coefficients of dynamic friction. + static MatrixType cdfTable_; + //!< Lookup Table for the coefficients of backward dynamic friction. + static MatrixType backward_cdfTable_; + //!< Lookup Table for the coefficients of lateral dynamic friction. + static MatrixType lateral_cdfTable_; + //!< Helper variable for the automatic registration process. + static bool materialsActivated_; + //!< Counter for the amount of anonymous materials. + static unsigned int anonymousMaterials_; + //@} + //************************************************************************** + + //**Friend declarations***************************************************** + /*! \cond ELASTICA_INTERNAL */ + friend MaterialID create_material(std::string const& name, real_t density, + real_t cor, real_t young, real_t shear, + real_t stiffness, real_t dampingN, + real_t dampingT, real_t csf, real_t cdf); + friend MaterialID create_material(std::string const& name, real_t density, + real_t cor, real_t young, real_t shear, + real_t stiffness, real_t dampingN, + real_t dampingT, real_t forward_csf, + real_t backward_csf, real_t lateral_csf, + real_t forward_cdf, real_t backward_cdf, + real_t lateral_cdf); + friend MaterialID create_material(real_t density, real_t cor, real_t young, + real_t shear, real_t stiffness, + real_t dampingN, real_t dampingT, + real_t csf, real_t cdf); + friend MaterialID create_material(real_t density, real_t cor, real_t young, + real_t shear, real_t stiffness, + real_t dampingN, real_t dampingT, + real_t forward_csf, real_t backward_csf, + real_t lateral_csf, real_t forward_cdf, + real_t backward_cdf, real_t lateral_cdf); + /*! \endcond */ + //************************************************************************** + }; + //**************************************************************************** + + //**************************************************************************** + /*!\brief The constructor of the Material class. + * + * \param name The name of the material. + * \param density The density of the material \f$ (0..\infty) \f$. + * \param cor The coefficient of restitution (COR) of the material + * \f$ [0..1] \f$. + * \param poisson The Poisson's ratio of the material \f$ [-1..0.5] \f$. + * \param young The Young's modulus of the material \f$ (0..\infty) \f$. + * \param shear The Shear modulus of the material \f$ (0..\infty) \f$. + * \param stiffness The stiffness in normal direction of the material's + * contact region. + * \param normal_damping The damping coefficient in normal direction of the + * material's contact region. + * \param tangential_damping The damping coefficient in tangential direction + * of the material's contact region. \param forward_csf The coefficient of + * static friction (CSF) of the material \f$ [0..\infty) \f$. \param + * backward_csf The coefficient of backward static friction (CSF) of the + * material \f$ [0..\infty) \f$. \param lateral_csf The coefficient of lateral + * static friction (CSF) of the material \f$ [0..\infty) \f$. \param + * forward_cdf The coefficient of dynamic friction (CDF) of the material \f$ + * [0..\infty) \f$. \param backward_cdf The coefficient of backward dynamic + * friction (CSF) of the material \f$ [0..\infty) \f$. \param lateral_cdf The + * coefficient of lateral dynamic friction (CSF) of the material \f$ + * [0..\infty) \f$. + */ + inline Material::Material(std::string name, real_t density, real_t cor, + real_t poisson, real_t young, real_t shear, + real_t stiffness, real_t normal_damping, + real_t tangential_damping, real_t forward_csf, + real_t backward_csf, real_t lateral_csf, + real_t forward_cdf, real_t backward_cdf, + real_t lateral_cdf) + : name_(std::move(name)), // The name of the material + density_(density), // The density of the material + restitution_(cor), // The coefficient of restitution of the material + static_(forward_csf), // The coefficient of forward static friction + backward_static_(backward_csf), + lateral_static_(lateral_csf), + dynamic_(forward_cdf), // The coefficient of dynamic friction of the + // material + backward_dynamic_(backward_cdf), + lateral_dynamic_(lateral_cdf), + poisson_(poisson), // The Poisson's ratio for the material + young_(young), // The Young's modulus for the material + shear_(shear), // The shear modulus for the material + stiffness_(stiffness), // The stiffness in normal direction of the + // material's contact region. + dampingN_( + normal_damping), // The damping coefficient in normal direction of + // the material's contact region. + dampingT_( + tangential_damping) // The damping coefficient in tangential + // direction of the material's contact region. + {} + //**************************************************************************** + + //**************************************************************************** + /*!\brief The constructor of the Material class. + * + * \param name The name of the material. + * \param density The density of the material \f$ (0..\infty) \f$. + * \param cor The coefficient of restitution (COR) of the material + * \f$ [0..1] \f$. + * \param poisson The Poisson's ratio of the material \f$ [-1..0.5] \f$. + * \param young The Young's modulus of the material \f$ (0..\infty) \f$. + * \param stiffness The stiffness in normal direction of the material's + * contact region. + * \param normal_damping The damping coefficient in normal direction of the + * material's contact region. + * \param tangential_damping The damping coefficient in tangential direction + * of the material's contact region. \param csf The coefficient of static + * friction (CSF) of the material \f$ [0..\infty) \f$. \param cdf The + * coefficient of dynamic friction (CDF) of the material \f$ [0..\infty) \f$. + */ + inline Material::Material(std::string name, real_t density, real_t cor, + real_t poisson, real_t young, real_t shear, + real_t stiffness, real_t normal_damping, + real_t tangential_damping, real_t csf, real_t cdf) + : Material(std::move(name), density, cor, poisson, young, shear, + stiffness, normal_damping, tangential_damping, csf, csf, csf, + cdf, cdf, cdf) {} + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the name of the material. + * + * \return The name of the material. + */ + inline const std::string& Material::get_name() const noexcept { + return name_; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the density of the material. + * + * \return The density of the material. + */ + inline real_t Material::get_density() const noexcept { return density_; } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the coefficient of restitution of the material. + * + * \return The coefficient of restitution of the material. + */ + inline real_t Material::get_restitution() const noexcept { + return restitution_; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the coefficient of static friction of the material. + * + * \return The coefficient of static friction of the material. + */ + inline real_t Material::get_static_friction() const noexcept { + return static_; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the coefficient of forward static friction of the material. + * + * \return The coefficient of static friction of the material. + */ + inline real_t Material::get_forward_static_friction() const noexcept { + return get_static_friction(); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the coefficient of backward static friction of the material. + * + * \return The coefficient of backward static friction of the material. + */ + inline real_t Material::get_backward_static_friction() const noexcept { + return backward_static_; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the coefficient of lateral static friction of the material. + * + * \return The coefficient of lateral static friction of the material. + */ + inline real_t Material::get_lateral_static_friction() const noexcept { + return lateral_static_; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the coefficient of dynamic friction of the material. + * + * \return The coefficient of dynamic friction of the material. + */ + inline real_t Material::get_dynamic_friction() const noexcept { + return dynamic_; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the coefficient of forward dynamic friction of the material. + * + * \return The coefficient of dynamic friction of the material. + */ + inline real_t Material::get_forward_dynamic_friction() const noexcept { + return get_dynamic_friction(); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the coefficient of backward dynamic friction of the + * material. + * + * \return The coefficient of backward dynamic friction of the material. + */ + inline real_t Material::get_backward_dynamic_friction() const noexcept { + return backward_dynamic_; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the coefficient of lateral dynamic friction of the material. + * + * \return The coefficient of lateral dynamic friction of the material. + */ + inline real_t Material::get_lateral_dynamic_friction() const noexcept { + return lateral_dynamic_; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the Poisson's ratio of the material. + * + * \return The Poisson's ratio of the material. + */ + inline real_t Material::get_poisson_ratio() const noexcept { + return poisson_; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the Young's modulus of the material. + * + * \return The Young's modulus of the material. + */ + inline real_t Material::get_youngs_modulus() const noexcept { return young_; } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the shear modulus of the material. + * + * \return The Shear modulus of the material. + */ + inline real_t Material::get_shear_modulus() const noexcept { return shear_; } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the stiffness in normal direction of the material's contact + * region. + * + * \return The stiffness in normal direction of the material's contact region. + */ + inline real_t Material::get_stiffness() const noexcept { return stiffness_; } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the damping coefficient in normal direction of the + * material's contact region. + * + * \return The damping coefficient in normal direction of the material's + * contact region. + */ + inline real_t Material::get_normal_damping() const noexcept { + return dampingN_; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the damping coefficient in tangential direction of the + * material's contact region. + * + * \return The damping coefficient in tangential direction of the material's + * contact region. + */ + inline real_t Material::get_tangential_damping() const noexcept { + return dampingT_; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the name of the given material. + * \ingroup materials + * + * \param material The material to be queried. + * \return The name of the given material. + */ + inline const std::string& Material::get_name(MaterialID material) noexcept { + ELASTICA_ASSERT(material < materials_.size(), "Invalid material ID"); + return materials_[material].get_name(); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the density of the given material. + * \ingroup materials + * + * \param material The material to be queried. + * \return The density of the given material. + */ + inline real_t Material::get_density(MaterialID material) noexcept { + ELASTICA_ASSERT(material < materials_.size(), "Invalid material ID"); + return materials_[material].get_density(); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the coefficient of restitution of the given material. + * \ingroup materials + * + * \param material The material to be queried. + * \return The coefficient of restitution of the given material. + */ + inline real_t Material::get_restitution(MaterialID material) noexcept { + ELASTICA_ASSERT(material < materials_.size(), "Invalid material ID"); + return materials_[material].get_restitution(); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the composite coefficient of restitution for a collision + * between two rigid bodies. \ingroup materials + * + * \param material1 The material of the first colliding rigid body. + * \param material2 The material of the second colliding rigid body. + * \return The resulting composite coefficient of restitution of the + * collision. + */ + inline real_t Material::get_restitution(MaterialID material1, + MaterialID material2) noexcept { + ELASTICA_ASSERT(material1 < materials_.size(), "Invalid material ID"); + ELASTICA_ASSERT(material2 < materials_.size(), "Invalid material ID"); + return corTable_(material1, material2); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the coefficient of static friction of the given material. + * \ingroup materials + * + * \param material The material to be queried. + * \return The coefficient of static friction of the given material. + */ + inline real_t Material::get_static_friction(MaterialID material) noexcept { + ELASTICA_ASSERT(material < materials_.size(), "Invalid material ID"); + return materials_[material].get_static_friction(); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the coefficient of static friction for a collision between + * two rigid bodies. + * \ingroup materials + * + * \param material1 The material of the first colliding rigid body. + * \param material2 The material of the second colliding rigid body. + * \return The resulting coefficient of static friction of the collision. + */ + inline real_t Material::get_static_friction(MaterialID material1, + MaterialID material2) noexcept { + ELASTICA_ASSERT(material1 < materials_.size(), "Invalid material ID"); + ELASTICA_ASSERT(material2 < materials_.size(), "Invalid material ID"); + return csfTable_(material1, material2); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the coefficient of forward static friction of the given + * material. + * \ingroup materials + * + * \param material The material to be queried. + * \return The coefficient of forward static friction of the given material. + */ + inline real_t Material::get_forward_static_friction( + MaterialID material) noexcept { + return Material::get_static_friction(material); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the coefficient of forward static friction for a collision + * between two rigid bodies. + * \ingroup materials + * + * \param material1 The material of the first colliding rigid body. + * \param material2 The material of the second colliding rigid body. + * \return The resulting coefficient of forward static friction of the + * collision. + */ + inline real_t Material::get_forward_static_friction( + MaterialID material1, MaterialID material2) noexcept { + return Material::get_static_friction(material1, material2); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the coefficient of backward static friction of the given + * material. + * \ingroup materials + * + * \param material The material to be queried. + * \return The coefficient of backward static friction of the given material. + */ + inline real_t Material::get_backward_static_friction( + MaterialID material) noexcept { + ELASTICA_ASSERT(material < materials_.size(), "Invalid material ID"); + return materials_[material].get_backward_static_friction(); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the coefficient of backward static friction for a collision + * between two rigid bodies. + * \ingroup materials + * + * \param material1 The material of the first colliding rigid body. + * \param material2 The material of the second colliding rigid body. + * \return The resulting coefficient of backward static friction of the + * collision. + */ + inline real_t Material::get_backward_static_friction( + MaterialID material1, MaterialID material2) noexcept { + ELASTICA_ASSERT(material1 < materials_.size(), "Invalid material ID"); + ELASTICA_ASSERT(material2 < materials_.size(), "Invalid material ID"); + return backward_csfTable_(material1, material2); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the coefficient of lateral static friction of the given + * material. + * \ingroup materials + * + * \param material The material to be queried. + * \return The coefficient of lateral static friction of the given material. + */ + inline real_t Material::get_lateral_static_friction( + MaterialID material) noexcept { + ELASTICA_ASSERT(material < materials_.size(), "Invalid material ID"); + return materials_[material].get_lateral_static_friction(); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the coefficient of lateral static friction for a collision + * between two rigid bodies. + * \ingroup materials + * + * \param material1 The material of the first colliding rigid body. + * \param material2 The material of the second colliding rigid body. + * \return The resulting coefficient of lateral static friction of the + * collision. + */ + inline real_t Material::get_lateral_static_friction( + MaterialID material1, MaterialID material2) noexcept { + ELASTICA_ASSERT(material1 < materials_.size(), "Invalid material ID"); + ELASTICA_ASSERT(material2 < materials_.size(), "Invalid material ID"); + return lateral_csfTable_(material1, material2); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the coefficient of dynamic friction of the given material. + * \ingroup materials + * + * \param material The material to be queried. + * \return The coefficient of dynamic friction of the given material. + */ + inline real_t Material::get_dynamic_friction(MaterialID material) noexcept { + ELASTICA_ASSERT(material < materials_.size(), "Invalid material ID"); + return materials_[material].get_dynamic_friction(); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the coefficient of dynamic friction for a collision between + * two rigid bodies. + * \ingroup materials + * + * \param material1 The material of the first colliding rigid body. + * \param material2 The material of the second colliding rigid body. + * \return The resulting coefficient of dynamic friction of the collision. + */ + inline real_t Material::get_dynamic_friction(MaterialID material1, + MaterialID material2) noexcept { + ELASTICA_ASSERT(material1 < materials_.size(), "Invalid material ID"); + ELASTICA_ASSERT(material2 < materials_.size(), "Invalid material ID"); + return cdfTable_(material1, material2); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the coefficient of forward dynamic friction of the given + * material. + * \ingroup materials + * + * \param material The material to be queried. + * \return The coefficient of forward dynamic friction of the given material. + */ + inline real_t Material::get_forward_dynamic_friction( + MaterialID material) noexcept { + return Material::get_dynamic_friction(material); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the coefficient of forward dynamic friction for a collision + * between two rigid bodies. + * \ingroup materials + * + * \param material1 The material of the first colliding rigid body. + * \param material2 The material of the second colliding rigid body. + * \return The resulting coefficient of forward dynamic friction of the + * collision. + */ + inline real_t Material::get_forward_dynamic_friction( + MaterialID material1, MaterialID material2) noexcept { + return Material::get_dynamic_friction(material1, material2); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the coefficient of backward dynamic friction of the given + * material. + * \ingroup materials + * + * \param material The material to be queried. + * \return The coefficient of backward dynamic friction of the given material. + */ + inline real_t Material::get_backward_dynamic_friction( + MaterialID material) noexcept { + ELASTICA_ASSERT(material < materials_.size(), "Invalid material ID"); + return materials_[material].get_backward_dynamic_friction(); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the coefficient of backward dynamic friction for a collision + * between two rigid bodies. + * \ingroup materials + * + * \param material1 The material of the first colliding rigid body. + * \param material2 The material of the second colliding rigid body. + * \return The resulting coefficient of backward dynamic friction of the + * collision. + */ + inline real_t Material::get_backward_dynamic_friction( + MaterialID material1, MaterialID material2) noexcept { + ELASTICA_ASSERT(material1 < materials_.size(), "Invalid material ID"); + ELASTICA_ASSERT(material2 < materials_.size(), "Invalid material ID"); + return backward_cdfTable_(material1, material2); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the coefficient of lateral dynamic friction of the given + * material. + * \ingroup materials + * + * \param material The material to be queried. + * \return The coefficient of lateral dynamic friction of the given material. + */ + inline real_t Material::get_lateral_dynamic_friction( + MaterialID material) noexcept { + ELASTICA_ASSERT(material < materials_.size(), "Invalid material ID"); + return materials_[material].get_lateral_dynamic_friction(); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the coefficient of lateral dynamic friction for a collision + * between two rigid bodies. + * \ingroup materials + * + * \param material1 The material of the first colliding rigid body. + * \param material2 The material of the second colliding rigid body. + * \return The resulting coefficient of lateral dynamic friction of the + * collision. + */ + inline real_t Material::get_lateral_dynamic_friction( + MaterialID material1, MaterialID material2) noexcept { + ELASTICA_ASSERT(material1 < materials_.size(), "Invalid material ID"); + ELASTICA_ASSERT(material2 < materials_.size(), "Invalid material ID"); + return lateral_cdfTable_(material1, material2); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the Poisson's ratio of the given material. + * \ingroup materials + * + * \param material The material to be queried. + * \return The Poisson's ratio of the given material. + */ + inline real_t Material::get_poisson_ratio(MaterialID material) noexcept { + ELASTICA_ASSERT(material < materials_.size(), "Invalid material ID"); + return materials_[material].get_poisson_ratio(); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the Young's modulus of the given material. + * \ingroup materials + * + * \param material The material to be queried. + * \return The Young's modulus of the given material. + */ + inline real_t Material::get_youngs_modulus(MaterialID material) noexcept { + ELASTICA_ASSERT(material < materials_.size(), "Invalid material ID"); + return materials_[material].get_youngs_modulus(); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the (effective) Young's modulus for a collision between two + * rigid bodies. + * \ingroup materials + * + * \param material1 The material of the first colliding rigid body. + * \param material2 The material of the second colliding rigid body. + * \return The resulting (effective) Young's modulus of the collision. + * + * This function returns the effective Young's modulus for a collision between + * two rigid bodies. The effective Young's modulus is calculated as + * + * \f[\frac{1}{E_{eff}} = \frac{1 - \nu_1^2}{E_1}+\frac{1 - \nu_2^2}{E_2} \f] + * + * where \f$ E_1 \f$ and \f$ E_2 \f$ are the Young's modulus for the first and + * second material, respectively, and \f$ \nu_1 \f$ and \f$ \nu_2 \f$ are the + * Poisson's ratio for the materials. + */ + inline real_t Material::get_youngs_modulus(MaterialID material1, + MaterialID material2) noexcept { + ELASTICA_ASSERT(material1 < materials_.size(), "Invalid material ID"); + ELASTICA_ASSERT(material2 < materials_.size(), "Invalid material ID"); + + real_t const nu1(get_poisson_ratio(material1)); + real_t const nu2(get_poisson_ratio(material2)); + real_t const y1(get_youngs_modulus(material1)); + real_t const y2(get_youngs_modulus(material2)); + + real_t const tmp1(y2 * (real_t(1) - nu1 * nu1)); + real_t const tmp2(y1 * (real_t(1) - nu2 * nu2)); + + return ((y1 * y2) / (tmp1 + tmp2)); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the Shear modulus of the given material. + * \ingroup materials + * + * \param material The material to be queried. + * \return The Shear modulus of the given material. + */ + inline real_t Material::get_shear_modulus(MaterialID material) noexcept { + ELASTICA_ASSERT(material < materials_.size(), "Invalid material ID"); + return materials_[material].get_shear_modulus(); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the stiffness in normal direction of the material's contact + * region. + * \ingroup materials + * + * \param material The material to be queried. + * \return The stiffness in normal direction of the contact region of the + * given material. + */ + inline real_t Material::get_stiffness(MaterialID material) noexcept { + ELASTICA_ASSERT(material < materials_.size(), "Invalid material ID"); + return materials_[material].get_stiffness(); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the stiffness in normal direction of the contact between two + * materials. + * \ingroup materials + * + * \param material1 The material of the first colliding rigid body. + * \param material2 The material of the second colliding rigid body. + * \return The stiffness in normal direction of the contact between two + * materials. + * + * Rigid body theory assumes that deformation during contact is localized to + * the contact region. Therefore the contact region is often modelled + * simplified as a spring-damper. When two bodies are in contact the + * spring-dampers are serially connected and thus the contact stiffness can be + * expressed as the series connection of two springs: \f$ k_*^{-1} = k_1^{-1} + * + k_2^{-1}\f$. + */ + inline real_t Material::get_stiffness(MaterialID material1, + MaterialID material2) noexcept { + ELASTICA_ASSERT(material1 < materials_.size(), "Invalid material ID"); + ELASTICA_ASSERT(material2 < materials_.size(), "Invalid material ID"); + + return inv(inv(get_stiffness(material1)) + inv(get_stiffness(material2))); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the damping coefficient in normal direction of the + * material's contact region. + * \ingroup materials + * + * \param material The material to be queried. + * \return The damping in normal direction of the contact region of the given + * material. + */ + inline real_t Material::get_normal_damping(MaterialID material) noexcept { + ELASTICA_ASSERT(material < materials_.size(), "Invalid material ID"); + return materials_[material].get_normal_damping(); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the damping in normal direction of the contact between two + * materials. + * \ingroup materials + * + * \param material1 The material of the first colliding rigid body. + * \param material2 The material of the second colliding rigid body. + * \return The damping in normal direction of the contact between two + * materials. + * + * Rigid body theory assumes that deformation during contact is localized to + * the contact region. Therefore the contact region is often modelled + * simplified as a spring-damper. When two bodies are in contact the + * spring-dampers are serially connected and thus the contact damping can be + * expressed as the series connection of two viscous dampers: \f$ c_*^{-1} = + * c_1^{-1} + c_2^{-1}\f$. + */ + inline real_t Material::get_normal_damping(MaterialID material1, + MaterialID material2) noexcept { + ELASTICA_ASSERT(material1 < materials_.size(), "Invalid material ID"); + ELASTICA_ASSERT(material2 < materials_.size(), "Invalid material ID"); + + return inv(inv(get_normal_damping(material1)) + + inv(get_normal_damping(material2))); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the damping coefficient in tangential direction of the + * material's contact region. + * \ingroup materials + * + * \param material The material to be queried. + * \return The damping in tangential direction of the contact region of the + * given material. + */ + inline real_t Material::get_tangential_damping(MaterialID material) noexcept { + ELASTICA_ASSERT(material < materials_.size(), "Invalid material ID"); + return materials_[material].get_tangential_damping(); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns the damping in tangential direction of the contact between + * two materials. + * \ingroup materials + * + * \param material1 The material of the first colliding rigid body. + * \param material2 The material of the second colliding rigid body. + * \return The damping in tangential direction of the contact between two + * materials. + * + * Rigid body theory assumes that deformation during contact is localized to + * the contact region. Therefore the contact region is often modelled + * simplified as a spring-damper. When two bodies are in contact the + * spring-dampers are serially connected and thus the contact damping can be + * expressed as the series connection of two viscous dampers: \f$ c_*^{-1} = + * c_1^{-1} + c_2^{-1}\f$. + */ + inline real_t Material::get_tangential_damping( + MaterialID material1, MaterialID material2) noexcept { + ELASTICA_ASSERT(material1 < materials_.size(), "Invalid material ID"); + ELASTICA_ASSERT(material2 < materials_.size(), "Invalid material ID"); + + return inv(inv(get_tangential_damping(material1)) + + inv(get_tangential_damping(material2))); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Setting the coefficient of restitution between material \a material1 + * and \a material2. + * \ingroup materials + * + * \param material1 The material of the first colliding rigid body. + * \param material2 The material of the second colliding rigid body. + * \param cor The coefficient of restitution between \a material1 and \a + * material2. + * \return void + */ + inline void Material::set_restitution(MaterialID material1, + MaterialID material2, + real_t cor) noexcept { + ELASTICA_ASSERT(material1 < materials_.size(), "Invalid material ID"); + ELASTICA_ASSERT(material2 < materials_.size(), "Invalid material ID"); + corTable_(material1, material2) = cor; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Setting the coefficient of static friction between material \a + * material1 and \a material2. + * \ingroup materials + * + * \param material1 The material of the first colliding rigid body. + * \param material2 The material of the second colliding rigid body. + * \param csf The coefficient of static friction between \a material1 and \a + * material2. + * \return void + */ + inline void Material::set_static_friction(MaterialID material1, + MaterialID material2, + real_t csf) noexcept { + ELASTICA_ASSERT(material1 < materials_.size(), "Invalid material ID"); + ELASTICA_ASSERT(material2 < materials_.size(), "Invalid material ID"); + csfTable_(material1, material2) = csf; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Setting the coefficient of forward static friction between material + * \a material1 and \a material2. + * \ingroup materials + * + * \param material1 The material of the first colliding rigid body. + * \param material2 The material of the second colliding rigid body. + * \param forward_csf The coefficient of forward static friction between \a + * material1 and \a material2. \return void + */ + inline void Material::set_forward_static_friction( + MaterialID material1, MaterialID material2, real_t forward_csf) noexcept { + Material::set_static_friction(material1, material2, forward_csf); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Setting the coefficient of backward static friction between material + * \a material1 and \a material2. + * \ingroup materials + * + * \param material1 The material of the first colliding rigid body. + * \param material2 The material of the second colliding rigid body. + * \param backward_csf The coefficient of backward static friction between \a + * material1 and \a material2. \return void + */ + inline void Material::set_backward_static_friction( + MaterialID material1, MaterialID material2, + real_t backward_csf) noexcept { + ELASTICA_ASSERT(material1 < materials_.size(), "Invalid material ID"); + ELASTICA_ASSERT(material2 < materials_.size(), "Invalid material ID"); + backward_csfTable_(material1, material2) = backward_csf; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Setting the coefficient of lateral static friction between material + * \a material1 and \a material2. + * \ingroup materials + * + * \param material1 The material of the first colliding rigid body. + * \param material2 The material of the second colliding rigid body. + * \param lateral_csf The coefficient of lateral_static friction between \a + * material1 and \a material2. \return void + */ + inline void Material::set_lateral_static_friction( + MaterialID material1, MaterialID material2, real_t lateral_csf) noexcept { + ELASTICA_ASSERT(material1 < materials_.size(), "Invalid material ID"); + ELASTICA_ASSERT(material2 < materials_.size(), "Invalid material ID"); + lateral_csfTable_(material1, material2) = lateral_csf; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Setting the coefficient of dynamic friction between material \a + * material1 and \a material2. + * \ingroup materials + * + * \param material1 The material of the first colliding rigid body. + * \param material2 The material of the second colliding rigid body. + * \param cdf The coefficient of dynamic friction between \a material1 and \a + * material2. + * \return void + */ + inline void Material::set_dynamic_friction(MaterialID material1, + MaterialID material2, + real_t cdf) noexcept { + ELASTICA_ASSERT(material1 < materials_.size(), "Invalid material ID"); + ELASTICA_ASSERT(material2 < materials_.size(), "Invalid material ID"); + cdfTable_(material1, material2) = cdf; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Setting the coefficient of forward dynamic friction between material + * \a material1 and \a material2. + * \ingroup materials + * + * \param material1 The material of the first colliding rigid body. + * \param material2 The material of the second colliding rigid body. + * \param csf The coefficient of forward dynamic friction between \a material1 + * and \a material2. + * \return void + */ + inline void Material::set_forward_dynamic_friction( + MaterialID material1, MaterialID material2, real_t forward_cdf) noexcept { + Material::set_dynamic_friction(material1, material2, forward_cdf); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Setting the coefficient of backward dynamic friction between + * material \a material1 and \a material2. \ingroup materials + * + * \param material1 The material of the first colliding rigid body. + * \param material2 The material of the second colliding rigid body. + * \param backward_cdf The coefficient of backward dynamic friction between \a + * material1 and \a material2. \return void + */ + inline void Material::set_backward_dynamic_friction( + MaterialID material1, MaterialID material2, + real_t backward_cdf) noexcept { + ELASTICA_ASSERT(material1 < materials_.size(), "Invalid material ID"); + ELASTICA_ASSERT(material2 < materials_.size(), "Invalid material ID"); + backward_cdfTable_(material1, material2) = backward_cdf; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Setting the coefficient of lateral dynamic friction between material + * \a material1 and \a material2. + * \ingroup materials + * + * \param material1 The material of the first colliding rigid body. + * \param material2 The material of the second colliding rigid body. + * \param lateral_cdf The coefficient of lateral_dynamic friction between \a + * material1 and \a material2. \return void + */ + inline void Material::set_lateral_dynamic_friction( + MaterialID material1, MaterialID material2, real_t lateral_cdf) noexcept { + ELASTICA_ASSERT(material1 < materials_.size(), "Invalid material ID"); + ELASTICA_ASSERT(material2 < materials_.size(), "Invalid material ID"); + lateral_cdfTable_(material1, material2) = lateral_cdf; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Computes the shear modulus given the youngs modulus and the poisson + * ratio + * \ingroup materials + * + */ + inline real_t Material::calculate_shear_modulus(real_t young, + real_t poisson) noexcept { + return static_cast(0.5) * young / + (static_cast(1.0) + poisson); + } + //**************************************************************************** + + //============================================================================ + // + // MATERIAL IRON + // + //============================================================================ + + //**************************************************************************** + /*!\brief Specification of the material iron. + * \ingroup materials + * + * The Iron class represents the material iron. It is implemented as a veneer + class for the + * Material base class to set the properties of iron: + * + * - Name: "iron" + * - Density: \f$ 7.874 \frac{kg}{dm^3} \f$ + * - Coefficient of restitution: 0.5 + * - Coefficient of static friction: 0.1 + * - Coefficient of dynamic friction: 0.1 + * - Poisson's ratio: 0.24 + * - Young's modulus: 200 GPa + * - Stiffness: \f$ ~200 \frac{N}{m} \f$ + * - Normal Damping: \f$ 0 \frac{Ns}{m} \f$ + * - Tangential Damping: \f$ 0 \frac{Ns}{m} \f$ + * + * Since several parameters are not unitless they might not match the scaling + of the simulation. + * In that case custom materials must be created. Also even though the + stiffness is proportional + * to Young's modulus the proportionality constant depends on other parameters + such as the shape of + * the contact region or the radii of the objects. Thus if the simulation does + rely on the value of + * the stiffness the user must supply an appropriate stiffness coefficient. + Since no published + * values were available for the damping coefficients they are deactivated. + * + * The iron material is automatically registered and can be directly used by + the predefined + * constant specifier \a iron: + + \code + // Creating an iron sphere + SphereID sphere = createSphere( 1, 0.0, 0.0, 0.0, iron ); + \endcode + */ + class Iron : public Material { + public: + //**Constructor************************************************************* + /*!\name Constructor */ + //@{ + explicit inline Iron(); + //@} + //************************************************************************** + }; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Default constructor for the Iron class. + */ + inline Iron::Iron() + : Material("iron", static_cast(7.874), static_cast(0.5), + static_cast(0.24), static_cast(200), + Material::calculate_shear_modulus(200, 0.24), + static_cast(200), static_cast(1e-10), + static_cast(1e-10), static_cast(0.1), + static_cast(0.1)) { + // Added small dampingN and damping T to prevent division by 0 in inv. + static_assert(sizeof(Material) == sizeof(Iron), "Not same size"); + } + //**************************************************************************** + + //================================================================================================= + // + // MATERIAL COPPER + // + //================================================================================================= + + //************************************************************************************************* + /*!\brief Specification of the material copper. + * \ingroup materials + * + * The Copper class represents the material copper. It is implemented as a + veneer class for + * the Material base class to set the properties of iron: + * + * - Name: "copper" + * - Density: \f$ 8.92 \frac{kg}{dm^3} \f$ + * - Coefficient of restitution: 0.5 + * - Coefficient of static friction: 0.1 + * - Coefficient of dynamic friction: 0.1 + * - Poisson's ratio: 0.33 + * - Young's modulus: 117 GPa + * - Stiffness: \f$ ~117 \frac{N}{m} \f$ + * - Normal Damping: \f$ 0 \frac{Ns}{m} \f$ + * - Tangential Damping: \f$ 0 \frac{Ns}{m} \f$ + * + * Since several parameters are not unitless they might not match the scaling + of the simulation. + * In that case custom materials must be created. Also even though the + stiffness is proportional + * to Young's modulus the proportionality constant depends on other parameters + such as the shape of + * the contact region or the radii of the objects. Thus if the simulation does + rely on the value of + * the stiffness the user must supply an appropriate stiffness coefficient. + Since no published + * values were available for the damping coefficients they are deactivated. + * + * The copper material is automatically registered and can be directly used by + the predefined + * constant specifier \a copper: + + \code + // Creating a copper sphere + SphereID sphere = createSphere( 1, 0.0, 0.0, 0.0, copper ); + \endcode + */ + class Copper : public Material { + public: + //**Constructor********************************************************************************* + /*!\name Constructor */ + //@{ + explicit inline Copper(); + //@} + //********************************************************************************************** + }; + //************************************************************************************************* + + //************************************************************************************************* + /*!\brief Default constructor for the Copper class. + */ + inline Copper::Copper() + : Material("copper", static_cast(8.92), static_cast(0.5), + static_cast(0.33), static_cast(117), + Material::calculate_shear_modulus(117, 0.33), + static_cast(117), static_cast(1e-10), + static_cast(1e-10), static_cast(0.1), + static_cast(0.1)) { + static_assert(sizeof(Material) == sizeof(Copper), "Not same size"); + } + //************************************************************************************************* + + //================================================================================================= + // + // MATERIAL GRANITE + // + //================================================================================================= + + //************************************************************************************************* + /*!\brief Specification of the material granite. + * \ingroup materials + * + * The Granite class represents the material granite. It is implemented as a + veneer class for + * the Material base class to set the properties of granite: + * + * - Name: "granite" + * - Density: \f$ 2.80 \frac{kg}{dm^3} \f$ + * - Coefficient of restitution: 0.5 + * - Coefficient of static friction: 0.1 + * - Coefficient of dynamic friction: 0.1 + * - Poisson's ratio: 0.25 + * - Young's modulus: 55 GPa + * - Stiffness: \f$ ~55 \frac{N}{m} \f$ + * - Normal Damping: \f$ 0 \frac{Ns}{m} \f$ + * - Tangential Damping: \f$ 0 \frac{Ns}{m} \f$ + * + * Since several parameters are not unitless they might not match the scaling + of the simulation. + * In that case custom materials must be created. Also even though the + stiffness is proportional + * to Young's modulus the proportionality constant depends on other parameters + such as the shape of + * the contact region or the radii of the objects. Thus if the simulation does + rely on the value of + * the stiffness the user must supply an appropriate stiffness coefficient. + Since no published + * values were available for the damping coefficients they are deactivated. + * + * The granite material is automatically registered and can be directly used + by the predefined + * constant specifier \a granite: + + \code + // Creating a granite sphere + SphereID sphere = createSphere( 1, 0.0, 0.0, 0.0, granite ); + \endcode + */ + class Granite : public Material { + public: + //**Constructor********************************************************************************* + /*!\name Constructor */ + //@{ + explicit inline Granite(); + //@} + //********************************************************************************************** + }; + //************************************************************************************************* + + //************************************************************************************************* + /*!\brief Default constructor for the Granite class. + */ + inline Granite::Granite() + : Material("granite", static_cast(2.8), static_cast(0.5), + static_cast(0.25), static_cast(55), + Material::calculate_shear_modulus(55, 0.25), + static_cast(55), static_cast(1e-10), + static_cast(1e-10), static_cast(0.1), + static_cast(0.1)) { + static_assert(sizeof(Material) == sizeof(Granite), "Not same size"); + } + //************************************************************************************************* + + //================================================================================================= + // + // MATERIAL OAK + // + //================================================================================================= + + //************************************************************************************************* + /*!\brief Specification of the material oak. + * \ingroup materials + * + * The Oak class represents the material oak wood. It is implemented as a + veneer class for the + * Material base class to set the properties of oak wood: + * + * - Name: "oak" + * - Density: \f$ 0.8 \frac{kg}{dm^3} \f$ + * - Coefficient of restitution: 0.5 + * - Coefficient of static friction: 0.1 + * - Coefficient of dynamic friction: 0.1 + * - Poisson's ratio: 0.35 + * - Young's modulus: 11 GPa + * - Stiffness: \f$ ~11 \frac{N}{m} \f$ + * - Normal Damping: \f$ 0 \frac{Ns}{m} \f$ + * - Tangential Damping: \f$ 0 \frac{Ns}{m} \f$ + * + * Since several parameters are not unitless they might not match the scaling + of the simulation. + * In that case custom materials must be created. Also even though the + stiffness is proportional + * to Young's modulus the proportionality constant depends on other parameters + such as the shape of + * the contact region or the radii of the objects. Thus if the simulation does + rely on the value of + * the stiffness the user must supply an appropriate stiffness coefficient. + Since no published + * values were available for the damping coefficients they are deactivated. + * + * The oak wood material is automatically registered and can be directly used + by the predefined + * constant specifier \a oak: + + \code + // Creating an oak wood sphere + SphereID sphere = createSphere( 1, 0.0, 0.0, 0.0, oak ); + \endcode + */ + class Oak : public Material { + public: + //**Constructor********************************************************************************* + /*!\name Constructor */ + //@{ + explicit inline Oak(); + //@} + //********************************************************************************************** + }; + //************************************************************************************************* + + //************************************************************************************************* + /*!\brief Default constructor for the Oak class. + */ + inline Oak::Oak() + : Material("oak", static_cast(0.8), static_cast(0.5), + static_cast(0.35), static_cast(11), + Material::calculate_shear_modulus(11, 0.35), + static_cast(11), static_cast(1e-10), + static_cast(1e-10), static_cast(0.1), + static_cast(0.1)) { + static_assert(sizeof(Material) == sizeof(Oak), "Not same size"); + } + //**************************************************************************** + + //============================================================================ + // + // MATERIAL FIR + // + //============================================================================ + + //**************************************************************************** + /*!\brief Specification of the material fir. + * \ingroup materials + * + * The Fir class represents the material fir wood. It is implemented as a + veneer class for the + * Material base class to set the properties of fir wood: + * + * - Name: "fir" + * - Density: \f$ 0.5 \frac{kg}{dm^3} \f$ + * - Coefficient of restitution: 0.5 + * - Coefficient of static friction: 0.1 + * - Coefficient of dynamic friction: 0.1 + * - Poisson's ratio: 0.34 + * - Young's modulus: 13 GPa + * - Stiffness: \f$ ~13 \frac{N}{m} \f$ + * - Normal Damping: \f$ 0 \frac{Ns}{m} \f$ + * - Tangential Damping: \f$ 0 \frac{Ns}{m} \f$ + * + * Since several parameters are not unitless they might not match the scaling + of the simulation. + * In that case custom materials must be created. Also even though the + stiffness is proportional + * to Young's modulus the proportionality constant depends on other parameters + such as the shape of + * the contact region or the radii of the objects. Thus if the simulation does + rely on the value of + * the stiffness the user must supply an appropriate stiffness coefficient. + Since no published + * values were available for the damping coefficients they are deactivated. + * + * The fir wood material is automatically registered and can be directly used + by the predefined + * constant specifier \a fir: + + \code + // Creating a fir wood sphere + SphereID sphere = createSphere( 1, 0.0, 0.0, 0.0, fir ); + \endcode + */ + class Fir : public Material { + public: + //**Constructor************************************************************* + /*!\name Constructor */ + //@{ + explicit inline Fir(); + //@} + //************************************************************************** + }; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Default constructor for the Fir class. + */ + inline Fir::Fir() + : Material("fir", static_cast(0.5), static_cast(0.5), + static_cast(0.34), static_cast(13), + Material::calculate_shear_modulus(13, 0.34), + static_cast(13), static_cast(1e-10), + static_cast(1e-10), static_cast(0.1), + static_cast(0.1)) { + static_assert(sizeof(Material) == sizeof(Fir), "Not same size"); + } + //**************************************************************************** + + //============================================================================ + // + // MATERIAL CONSTANTS + // + //============================================================================ + + //**************************************************************************** + /*!\brief ID for the material iron. + * \ingroup materials + * + * This material can be used to create iron rigid bodies. Iron has the + * following material properties: + * + * - Name: "iron" + * - Density: \f$ 7.874 \frac{kg}{dm^3} \f$ + * - Restitution factor: 0.5 + */ + MaterialID const iron = 0; + //**************************************************************************** + + //**************************************************************************** + /*!\brief ID for the material copper. + * \ingroup materials + * + * This material can be used to create copper rigid bodies. Copper has the + * following material properties: + * + * - Name: "copper" + * - Density: \f$ 8.92 \frac{kg}{dm^3} \f$ + * - Coefficient of restitution: 0.5 + * - Coefficient of static friction: 0.1 + * - Coefficient of dynamic friction: 0.1 + */ + MaterialID const copper = 1; + //**************************************************************************** + + //**************************************************************************** + /*!\brief ID for the material granite. + * \ingroup materials + * + * This material can be used to create granite rigid bodies. + */ + MaterialID const granite = 2; + //**************************************************************************** + + //**************************************************************************** + /*!\brief ID for the material oak wood. + * \ingroup materials + * + * This material can be used to create rigid bodies made from oak wood. + */ + MaterialID const oak = 3; + //**************************************************************************** + + //**************************************************************************** + /*!\brief ID for the material fir wood. + * \ingroup materials + * + * This material can be used to create rigid bodies made from fir wood. + */ + MaterialID const fir = 4; + //**************************************************************************** + + //**************************************************************************** + /*!\brief ID for an invalid material. + * \ingroup materials + * + * This MaterialID is returned by the getMaterial() function in case no + * material with the specified name is returned. This value should not be used + * to create rigid bodies or in any other function! + */ + MaterialID const invalid_material = static_cast(-1); + //**************************************************************************** + + //============================================================================ + // + // MATERIAL FUNCTIONS + // + //============================================================================ + + //**************************************************************************** + /*!\name Material functions */ + //@{ + MaterialID create_material(std::string const& name, real_t density, + real_t cor, real_t young, real_t shear, + real_t stiffness, real_t dampingN, real_t dampingT, + real_t forward_csf, real_t backward_csf, + real_t lateral_csf, real_t forward_cdf, + real_t backward_cdf, real_t lateral_cdf); + MaterialID create_material(real_t density, real_t cor, real_t young, + real_t shear, real_t stiffness, real_t dampingN, + real_t dampingT, real_t forward_csf, + real_t backward_csf, real_t lateral_csf, + real_t forward_cdf, real_t backward_cdf, + real_t lateral_cdf); + MaterialID create_material(std::string const& name, real_t density, + real_t cor, real_t young, real_t shear, + real_t stiffness, real_t dampingN, real_t dampingT, + real_t csf, real_t cdf); + MaterialID create_material(real_t density, real_t cor, real_t young, + real_t shear, real_t stiffness, real_t dampingN, + real_t dampingT, real_t csf, real_t cdf); + //@} + //**************************************************************************** + +} // namespace elastica diff --git a/backend/src/Simulator/Materials/Types.hpp b/backend/src/Simulator/Materials/Types.hpp new file mode 100644 index 000000000..e86be408c --- /dev/null +++ b/backend/src/Simulator/Materials/Types.hpp @@ -0,0 +1,31 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** + +#include +#include // This seems like an anti-pattern + +namespace elastica { + + ////////////////////////////////////////////////////////////////////////////// + // + // Forward declarations of material types + // + ////////////////////////////////////////////////////////////////////////////// + + //**************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + class Material; + /*! \endcond */ + //**************************************************************************** + + //**Type definitions********************************************************** + //! ID type for materials + using MaterialID = std::uint32_t; + //! Collection of materials + using Materials = std::vector; + //**************************************************************************** + +} diff --git a/backend/src/Systems/Block.hpp b/backend/src/Systems/Block.hpp new file mode 100644 index 000000000..7929d995b --- /dev/null +++ b/backend/src/Systems/Block.hpp @@ -0,0 +1,8 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +#include "Systems/Block/Block.hpp" +// do not include in block as it overrides an elastica namespace function +// #include "Systems/Block/Serialize.hpp" diff --git a/backend/src/Systems/Block/Block.hpp b/backend/src/Systems/Block/Block.hpp new file mode 100644 index 000000000..8b62c3066 --- /dev/null +++ b/backend/src/Systems/Block/Block.hpp @@ -0,0 +1,318 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +// +#include "Systems/Block/Types.hpp" +// +#include "Systems/Block/Protocols.hpp" +#include "Systems/Block/TypeTraits.hpp" +// include implementations now +#include "Systems/Block/Block/Block.hpp" +#include "Systems/Block/Block/BlockFacade.hpp" +#include "Systems/Block/Block/BlockIterator.hpp" +#include "Systems/Block/Block/BlockSlice.hpp" +#include "Systems/Block/Block/BlockView.hpp" +#include "Systems/Block/Block/Metadata.hpp" +#include "Systems/Block/Block/VariableCache.hpp" +// Variable implementations +#include "BlockVariables/BlockInitializer.hpp" +#include "BlockVariables/BlockVariables.hpp" +// +#include "Utilities/End.hpp" // from_end + +//============================================================================== +// +// DOXYGEN DOCUMENTATION +// +//============================================================================== + +//****************************************************************************** +/*!\defgroup blocks Blocks + * \ingroup systems + * \brief Efficient blockwise-programming of \elastica entities + * + * The blocks module contains the interface for efficiently programming + * different \elastica entities (such as rods, rigid bodies etc.) and more + * generally, Lagrangian data-structures. + */ +//****************************************************************************** + +//****************************************************************************** +/*!\brief Block data-structures and routines +// \ingroup blocks +*/ +namespace blocks {} +//****************************************************************************** + +namespace blocks { + + //**************************************************************************** + /*!\brief Get number of units from a Block + * \ingroup blocks + * + * \details + * Get number of units from a Block. By 'units' we mean the number of + * individual BlockSlice(s) composing the Block. + * + * \usage + * \code + * Block<...> b; + * std::size_t n_units = blocks::n_units(b); + * \endcode + * + * \param block_like The block whose number of units is to be extracted. + * + * \note + * These need to be customized for your block type. + */ + template + inline auto n_units(Block const& block) noexcept -> std::size_t; + //**************************************************************************** + + //**Iterator support********************************************************** + /*!\name Block iterators */ + //@{ + + //**************************************************************************** + /*!\brief Get a random access iterator to the start of the block + * \ingroup blocks + */ + template + inline auto begin(Block& block) noexcept -> BlockIterator { + return {&block, 0UL}; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Get a random access iterator to the end of the block + * \ingroup blocks + */ + template + inline auto end(Block& block) noexcept -> BlockIterator { + return {&block, n_units(block)}; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Get a random access const iterator to the start of the block + * \ingroup blocks + */ + template + inline auto cbegin(Block const& block) noexcept + -> ConstBlockIterator { + return {&block, 0UL}; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Get a random access const iterator to the end of the block + * \ingroup blocks + */ + template + inline auto cend(Block const& block) noexcept + -> ConstBlockIterator { + return {&block, n_units(block)}; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Get a random access const iterator to the start of the block + * \ingroup blocks + */ + template + inline auto begin(Block const& block) noexcept { + return cbegin(block); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Get a random access const iterator to the end of the block + * \ingroup blocks + */ + template + inline auto end(Block const& block) noexcept { + return cend(block); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Get a random access iterator to the start of the block + * \ingroup blocks + */ + template + inline auto begin(BlockView& block_view) noexcept + -> BlockIterator { + return {&block_view.parent(), block_view.region().start}; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Get a random access iterator to the end of the block + * \ingroup blocks + */ + template + inline auto end(BlockView& block_view) noexcept + -> BlockIterator { + return {&block_view.parent(), + block_view.region().start + block_view.region().size}; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Get a random access const iterator to the start of the block + * \ingroup blocks + */ + template + inline auto cbegin(BlockView const& block_view) noexcept + -> ConstBlockIterator { + return {&cpp17::as_const(block_view.parent()), block_view.region().start}; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Get a random access const iterator to the end of the block + * \ingroup blocks + */ + template + inline auto cend(BlockView const& block_view) noexcept + -> ConstBlockIterator { + return {&cpp17::as_const(block_view.parent()), + block_view.region().start + block_view.region().size}; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Get a random access const iterator to the start of the block + * \ingroup blocks + */ + template + inline auto begin(BlockView const& block) noexcept { + return cbegin(block); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Get a random access const iterator to the end of the block + * \ingroup blocks + */ + template + inline auto end(BlockView const& block) noexcept { + return cend(block); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Get a random access iterator to the start of the block + * \ingroup blocks + */ + template + inline auto cbegin(ConstBlockView const& block_view) noexcept + -> ConstBlockIterator { + return {&block_view.parent(), block_view.region().start}; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Get a random access iterator to the end of the block + * \ingroup blocks + */ + template + inline auto cend(ConstBlockView const& block_view) noexcept + -> ConstBlockIterator { + return {&block_view.parent(), + block_view.region().start + block_view.region().size}; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Get a random access iterator to the start of the block + * \ingroup blocks + */ + template + inline auto begin(ConstBlockView const& block_view) noexcept { + return cbegin(block_view); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Get a random access iterator to the end of the block + * \ingroup blocks + */ + template + inline auto end(ConstBlockView const& block_view) noexcept { + return cend(block_view); + } + //**************************************************************************** + + // @} + //**************************************************************************** + + //**Slice support************************************************************* + /*!\name Whole block slice */ + //@{ + + //**************************************************************************** + /*!\brief Slice operator for an entire block + * \ingroup blocks + * + * \details + * Gets a view into the entire block + * + * \param block_like Data-structure modeling the block concept + * + * \example + * Given a block `b`, we can slice it using + * \code + * auto b_slice = block::slice(b); // Generates a view into the block + * \endcode + * + * \note + * not marked noexcept because we can have out of range indices + */ + template + inline constexpr decltype(auto) slice(Block& block_like) { + return slice(block_like, 0UL, ::elastica::end); + } + //**************************************************************************** + + //**************************************************************************** + template + inline constexpr decltype(auto) slice(Block const& block_like) { + return slice(block_like, 0UL, ::elastica::end); + } + //**************************************************************************** + + // @} + //**************************************************************************** + + //**************************************************************************** + /*!\brief Adapt a function to be applied over slices instead of blocks + * \ingroup blocks + * + * \details + * Given a callable `func`, adapt it so that it can be applied on a + * slice-wise basis rather than a blockwise basis. This proves useful for + * iteration in the ::elastica::modules::Systems module of Simulator. + * + * \usage + * \code + * auto func = [](auto & system){ // User code to iterate on systems; } + * // use to apply over slices + * auto func_to_be_applied_over_slices = + * blocks::for_each_slice_adapter(func); + * \endcode + * + * \param func Callable to be applied over slices + */ + template + constexpr auto for_each_slice_adapter(F func) noexcept { + return [func = std::move(func)](auto& block) /*mutable*/ -> void { + for (auto slice : block) + func(slice); + }; + } + //**************************************************************************** + +} // namespace blocks diff --git a/backend/src/Systems/Block/Block/Aliases.hpp b/backend/src/Systems/Block/Block/Aliases.hpp new file mode 100644 index 000000000..a7f39df05 --- /dev/null +++ b/backend/src/Systems/Block/Block/Aliases.hpp @@ -0,0 +1,80 @@ +#pragma once + +namespace blocks { + + //============================================================================ + // + // ALIAS DECLARATIONS + // + //============================================================================ + + //**************************************************************************** + /*!\brief Gets the nested `Variables` from `T` + * \ingroup block_tt + * + * The variables_t alias declaration provides a convenient + * shortcut to access the nested `Variables` type definition of + * the given type \a T. The following code example shows both ways to access + * the nested type definition: + * + * \example + * \code + * using Type1 = typename T::Variables; + * using Type2 = variables_t; + * \endcode + * + * \see Block, BlockSlice + */ + // [variables_t] + template + using variables_t = typename T::Variables; + // [variables_t] + //**************************************************************************** + + //**************************************************************************** + /*!\brief Gets the nested `InitializedVariables` from `T` + * \ingroup block_tt + * + * The initialized_variables_t alias declaration provides a convenient + * shortcut to access the nested `InitializedVariables` type definition of + * the given type \a T. The following code example shows both ways to access + * the nested type definition: + * + * \example + * \code + * using Type1 = typename T::InitializedVariables; + * using Type2 = initialized_variables_t; + * \endcode + * + * \see Block, BlockSlice + */ + // [initialized_variables_t] + template + using initialized_variables_t = typename T::InitializedVariables; + // [initialized_variables_t] + //**************************************************************************** + + //**************************************************************************** + /*!\brief Gets the nested `ComputedVariables` from `T` + * \ingroup block_tt + * + * The computed_variables_t alias declaration provides a convenient + * shortcut to access the nested `ComputedVariables` type definition of + * the given type \a T. The following code example shows both ways to access + * the nested type definition: + * + * \example + * \code + * using Type1 = typename T::ComputedVariables; + * using Type2 = computed_variables_t; + * \endcode + * + * \see Block, BlockSlice + */ + // [computed_variables_t] + template + using computed_variables_t = typename T::ComputedVariables; + // [computed_variables_t] + //**************************************************************************** + +} // namespace blocks diff --git a/backend/src/Systems/Block/Block/AsVariables.hpp b/backend/src/Systems/Block/Block/AsVariables.hpp new file mode 100644 index 000000000..af12673ab --- /dev/null +++ b/backend/src/Systems/Block/Block/AsVariables.hpp @@ -0,0 +1,39 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** + +#include "Utilities/AsTaggedTuple.hpp" + +namespace blocks { + + //**************************************************************************** + /*!\brief Metafunction to define storage a typelist of block variables + * \ingroup blocks + * + * \details + * The as_block_variables template type helps obtain the heterogeneous + * variable storage container from a typelist of declared blocks::Variable, + * used as shown below. + * + * \example + * \code + * // ... Declare variables Var1, Var2, Var3 + * + * // TypeList of all variables + * using VariablesList = tmpl::list; + * + * // Convert into a storage type which has value semantics + * using BlockVariables = as_block_variables; + * \endcode + * + * \tparam L typelist of block::Variables + * + * \see blocks::Variables, Block + */ + template + using as_block_variables = tmpl::as_tagged_tuple; + //**************************************************************************** + +} // namespace blocks diff --git a/backend/src/Systems/Block/Block/Block.hpp b/backend/src/Systems/Block/Block/Block.hpp new file mode 100644 index 000000000..692057a49 --- /dev/null +++ b/backend/src/Systems/Block/Block/Block.hpp @@ -0,0 +1,49 @@ +#pragma once + +namespace blocks { + + //============================================================================ + // + // CLASS DEFINITION + // + //============================================================================ + + //**************************************************************************** + /*!\brief Implementation of \a ComputationalBlock concept in \elastica + * \ingroup blocks + * + * The Block template class is \elastica's implementation of the + * \a ComputationalBlock concept and is used throughout the code base. It IS + * the gateway to perform efficient inter-element operations for (m)any + * Lagrangian-based simulation codes, and is used primarily to implement + * efficient intra-rod operations within \elastica. The fundamental idea is + * to separate out \a data and \a operations on data into different orthogonal + * modules (usually implemented as \a policy classes), but facilitate their + * interaction, within the contained hierarchy, using Block. This then turns + * conventional top-down class hierarchy into a \a star hierarchy, the Block + * being the central \a hub in this topology. The hub serves as a central spot + * for storing all \a data, to be used by \a operations from the nodes. + * + * Block heavily relies on some patterns used in modern C++---Curiously + * Recurring Template Pattern (CRTP, across multiple hierarchies), + * policy-based class hierarchy design, trait and type based customization. + * + * The Block is intended to be customized (specialized) for any Lagrangian + * entity with complex data inter-communication patterns, for a concrete + * example, please see @ref cosserat_rod. + * + * From a first look, the usage of block is very similar to an implementation + * class of a conventional pattern CRTP, and hence is used as a template + * parameter in most cases. Its intended usage is however more closer to a + * CRTP class used as a policy class! While confusing at a first glance, this + * pattern lets us imbibe the SOLID design principles in class design. + * + * \tparam Plugin The computational plugin modeling a Lagrangian entity + * + * \see BlockSlice + */ + template + class Block; + //**************************************************************************** + +} // namespace blocks diff --git a/backend/src/Systems/Block/Block/BlockFacade.hpp b/backend/src/Systems/Block/Block/BlockFacade.hpp new file mode 100644 index 000000000..c88636124 --- /dev/null +++ b/backend/src/Systems/Block/Block/BlockFacade.hpp @@ -0,0 +1,466 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +#include "Systems/Block/Block/Types.hpp" +// +#include "Systems/Block/Block/Concepts.hpp" +// +#include "Systems/Block/Block/Aliases.hpp" +#include "Systems/Block/Block/AsVariables.hpp" +#include "Systems/Block/BlockVariables/TypeTraits.hpp" +// +#include "Utilities/CRTP.hpp" +#include "Utilities/Requires.hpp" +#include "Utilities/TypeTraits/Cpp17.hpp" +#include "Utilities/TypeTraits/IsDetected.hpp" +// +#include + +namespace blocks { + + namespace detail { + + //************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\brief Fallback for when `Parent` does not require InitializedVariables + * in a BlockFacade + * \ingroup blocks + */ + struct EmptyInitializedVariables { + using InitializedVariables = tmpl::list<>; + }; + /*! \endcond */ + //************************************************************************** + + //************************************************************************ + /*! \cond ELASTICA_INTERNAL */ + /*!\brief Signature of expected a derived Block + * \ingroup blocks + * + * Very similar to protocols, but we don't need to derive from the + * protocol class. Rather, signatures rely on an implicit interface + * guarantee. + * + * \tparam BlockImplementation The block that derives from BlockFacade + */ + template + struct Signatures { + /// [slice_signature] + template + using slice_signature = + decltype(std::declval().template slice( + std::declval())); + // This is equivalent to + // template + // // some_return_type + // slice(std::size_t index) & + // { + // // return the slice at the location index + // } + /// [slice_signature] + + /// [const_slice_signature] + template + using const_slice_signature = + decltype(std::declval() + .template slice(std::declval())); + // This is equivalent to + // template + // // some_return_type + // slice(std::size_t index) const & + // { + // // return the slice at the location index + // } + /// [const_slice_signature] + }; + /*! \endcond */ + //************************************************************************ + + } // namespace detail + + //============================================================================ + // + // CLASS DEFINITION + // + //============================================================================ + + //**************************************************************************** + /*!\brief A helper facade for ease in implementing new Block types + * \ingroup blocks + * + * \details + * The BlockFacade is a helper template to assist in conforming to the + * protocols expected by a Block class, when specializing it for + * particular Plugin types. It provides the interface of a block in terms of a + * few core functions and associated types, which are to be supplied by a + * Block that derives from BlockFacade. Internally, BlockFacade uses the CRTP + * pattern to access the core functions supplied by the Block. + * + * BlockFacade is modeled after boost::iterator_facade. Notably, it differs + * from boost::iterator_facade in one key aspect : the BlockFacade is + * internally templated on blocks::Block---hence its use for anything + * other then implementing a specialization of blocks::Block template is + * ill-formed by definition. + * + * \usage + * While customizing a blocks::block for some type `CustomPlugin` (which needs + * to meet the expected interface for a valid `Plugin`, see Blocks), we can + * use BlockFacade to minimize the code we need to write, as shown below: + * \snippet Test_BlockFacade.cpp block_facade_usage + * For correct use with a BlockFacade, the following members in the deriving + * Block need to be defined. + * \snippet this slice_signature + * \snippet this const_slice_signature + * + * where + * - `Tag` is the tag to be retrieved + * + * \example + * For a simple custom Plugin as shown below, + * \snippet Test_BlocksFramework.cpp int_plugin + * Without a BlockFacade, one needs to write out + * \snippet Test_BlocksFramework.cpp int_block + * With a BlockFacade however, this process becomes more simpler + * \snippet Test_BlocksFramework.cpp fac_int_block + * + * Additionally for more practical, operate-intensive Plugins in \elastica, + * such as Cosserat rods, BlockFacade takes care of setting up the necessary + * type definitions for slicing/initialization among others, including setting + * up subscript operators etc given the Block has the necessary functions + * + * \tparam Plugin The computational plugin modeling a Lagrangian entity + * + * \see Block + */ + template + class BlockFacade : public elastica::CRTPHelper, BlockFacade>, + public Gettable>, + public Spannable>, + public Plugin { + protected: + //**Type definitions******************************************************** + /*! \cond ELASTICA_INTERNAL */ + //! Tag marking need for the block to be initialized + using to_be_initialized_tag = std::true_type; + //! Tag marking no need for block to be initialized + using need_not_be_initialized_tag = std::false_type; + /*! \endcond */ + //************************************************************************** + + //************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\brief Compile-time check for initialized variables in Plugin + * + * \details + * If the `Plugin` class has no initialized_variables_t alias, + * meaning that there are no variables to initialized, we can simply skip + * initialization, and the `Plugin` doesn't need to initialize these members + * and initialization can be skipped. This check for skipping can happen in + * compile-time and is facilitated by this function. + * + * \metareturns cpp17::bool_constant + */ + static inline constexpr auto parent_to_be_initialized() noexcept { + // This one-liner doesn't work if the initialized_variables_t is protected + // which is the intended use case + // return ::tt::is_detected{}; + return ::tt::is_detected{}; + } + /*! \endcond */ + //************************************************************************** + + //************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\brief Compile-time check for initialized variables in Plugin + * + * \metareturns cpp17::bool_constant + * + * \see parent_to_be_initialized() + */ + static inline constexpr auto parent_need_not_be_initialized() noexcept { + return cpp17::negation{}; + } + /*! \endcond */ + //************************************************************************** + + private: + //**Type definitions******************************************************** + //! Parent type + using Parent = Plugin; + //! This type + using This = BlockFacade; + //************************************************************************** + + public: + //**Type definitions******************************************************** + //! Plugin type + using PluginType = Parent; + //! List of variables + using typename Parent::Variables; + //! List of initialized variables + using InitializedVariables = blocks::initialized_variables_t< + std::conditional_t>; + //! Container for all variables + using BlockVariables = as_block_variables; + //! Conformant mapping between tags and variables + using VariableMap = VariableMapping; + //************************************************************************** + + private: + //**Type definitions******************************************************** + //! CRTP Type + using CRTP = elastica::CRTPHelper, BlockFacade>; + //! CRTP methods + using CRTP::self; + //! Type of gettable + using GetAffordance = Gettable>; + //! Type of spannable + using SpanAffordance = Spannable>; + //************************************************************************** + + protected: + //**Constructors************************************************************ + /*!\name Constructors */ + //@{ + + //************************************************************************** + /*!\brief The default constructor. + * + */ + BlockFacade() noexcept( + std::is_nothrow_default_constructible::value) + : CRTP(), + GetAffordance(), + SpanAffordance(), + Parent(), + variables_(){}; + //************************************************************************** + + //************************************************************************** + /*!\brief The move constructor. + * + * \param other block to move from + */ + BlockFacade(BlockFacade&& other) noexcept + : CRTP(), + GetAffordance(), + SpanAffordance(), + Parent(), + variables_(std::move(other.variables_)){}; + //************************************************************************** + + public: + //************************************************************************** + /*!\brief Deleted copy constructor. + * + */ + BlockFacade(BlockFacade const&) = delete; + //************************************************************************** + + //@} + //************************************************************************** + + public: + //**Destructor************************************************************** + /*!\name Destructor */ + //@{ + ~BlockFacade() = default; + //@} + //************************************************************************** + + public: + //**Access operators******************************************************** + /*!\name Access operators */ + //@{ + //! Operator for slicing and viewing + using SpanAffordance::operator[]; + //@} + //************************************************************************** + + public: + //**Data access************************************************************* + /*!\name Data access */ + //@{ + + //************************************************************************** + /*!\brief Access to the underlying data + * + * \return Mutable lvalue reference to the underlying data + */ + inline constexpr BlockVariables& data() & noexcept { return variables_; } + //************************************************************************** + + //************************************************************************** + /*!\brief Access to the underlying data + * + * \return Constant lvalue reference to the underlying data + */ + inline constexpr BlockVariables const& data() const& noexcept { + return variables_; + } + //************************************************************************** + + //************************************************************************** + /*!\brief Access to the underlying data + * + * \return Mutable rvalue reference to the underlying data + */ + inline constexpr BlockVariables&& data() && noexcept { + return static_cast(variables_); + } + + //************************************************************************** + + //************************************************************************** + /*!\brief Access to the underlying data + * + * \return Const rvalue reference to the underlying data + */ + inline constexpr BlockVariables const&& data() const&& noexcept { + return static_cast(variables_); + } + //************************************************************************** + + //@} + //************************************************************************** + + //**Utility methods********************************************************* + /*!\name Utility methods*/ + //@{ + + //************************************************************************** + /*!\brief Returns the current block + */ + inline constexpr auto parent() & noexcept -> Block& { + return self(); + } + //************************************************************************** + + //************************************************************************** + /*!\brief Returns the current block + */ + inline constexpr auto parent() const& noexcept -> Block const& { + return self(); + } + //************************************************************************** + + //************************************************************************** + /*!\brief Returns the current block + */ + inline constexpr auto parent() && noexcept -> Block&& { + return static_cast&&>(*this); + } + //************************************************************************** + + //************************************************************************** + /*!\brief Returns the current block + */ + inline constexpr auto parent() const&& noexcept -> Block const&& { + return static_cast const&&>(*this); + } + //************************************************************************** + + //@} + //************************************************************************** + + private: + //************************************************************************** + /*!\brief Implementation to initialize the underlying data + */ + template + static void initialize_impl(DownstreamBlock&& downstream_block, + Initializer&& initializer, + to_be_initialized_tag /* meta */) { + Parent::initialize(std::forward(downstream_block), + std::forward(initializer)); + } + //************************************************************************** + + //************************************************************************** + /*!\brief Implementation to initialize the underlying data + */ + template + static void initialize_impl(DownstreamBlock&&, Initializer&&, + need_not_be_initialized_tag /* meta */) {} + //************************************************************************** + + protected: + //************************************************************************** + /*!\brief Initialize the underlying data + */ + template + static void initialize(DownstreamBlock&& downstream_block, + Initializer&& initializer) { + This::initialize_impl(std::forward(downstream_block), + std::forward(initializer), + This::parent_to_be_initialized()); + } + //************************************************************************** + + private: + //**Member variables******************************************************** + /*!\name Member variables */ + //@{ + //! All block variables + BlockVariables variables_; + //@} + //************************************************************************** + }; + //**************************************************************************** + + //**************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\name Get functions for Block */ + //@{ + + //**************************************************************************** + /*!\brief Extract element from a Block + * \ingroup blocks + * + * \details + * Extracts the element of the Block `block_like` whose tag type is `Tag`. + * Fails to compile unless the block has the `Tag` being extracted. + * + * \usage + * The usage is similar to std::get(), shown below + * \code + Block<...> b; + auto my_tag_data = blocks::get(b); + * \endcode + * + * \tparam Tag Tag to extract + * + * \param block_like The block to extract the tag from + */ + template + inline constexpr decltype(auto) get_backend(Block& block) noexcept { + return tuples::get(block.data()); + } + + template + inline constexpr decltype(auto) get_backend( + Block const& block) noexcept { + return tuples::get(block.data()); + } + + template + inline constexpr decltype(auto) get_backend(Block&& block) noexcept { + return tuples::get( + static_cast&&>(block).data()); + } + + template + inline constexpr decltype(auto) get_backend( + Block const&& block) noexcept { + return tuples::get( + static_cast const&&>(block).data()); + } + + //@} + /*! \endcond */ + //**************************************************************************** + +} // namespace blocks diff --git a/backend/src/Systems/Block/Block/BlockIterator.hpp b/backend/src/Systems/Block/Block/BlockIterator.hpp new file mode 100644 index 000000000..0b3b8aba6 --- /dev/null +++ b/backend/src/Systems/Block/Block/BlockIterator.hpp @@ -0,0 +1,617 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +#include +#include +#include + +#include "Systems/Block/Block/TypeTraits.hpp" +#include "Systems/Block/Block/Types.hpp" +// + +namespace blocks { + + //============================================================================ + // + // CLASS DEFINITION + // + //============================================================================ + + //**************************************************************************** + /*!\brief Implementation of a iterator for blocks. + * \ingroup blocks + * + * The BlockIterator represents a generic random-access iterator that can be + * used for all block types in \elastica + */ + template + class BlockIterator { + private: + //**Type definitions******************************************************** + using ParentBlock = Block; + //************************************************************************** + + public: + //**Type definitions******************************************************** + //! The iterator category. + using IteratorCategory = std::random_access_iterator_tag; + //! Type of the underlying elements. + using ValueType = + typename PluginFrom::template to::type; + //! Pointer return type. + using PointerType = ValueType; + //! Reference return type. + using ReferenceType = ValueType; + //! Difference between two iterators. + using DifferenceType = std::size_t; + + // STL iterator requirements + //! The iterator category. + using iterator_category = IteratorCategory; + //! Type of the underlying elements. + using value_type = ValueType; + //! Pointer return type. + using pointer = PointerType; + //! Reference return type. + using reference = ReferenceType; + //! Difference between two iterators. + using difference_type = DifferenceType; + //************************************************************************** + + public: + //**Constructors************************************************************ + /*!\name Constructors */ + //@{ + + //************************************************************************** + /*!\brief Default constructor for the BlockIterator class. + */ + explicit constexpr BlockIterator() noexcept + : block_(nullptr), curr_idx_(std::numeric_limits::max()) {} + //************************************************************************** + + //************************************************************************** + /*!\brief Constructor for the BlockIterator class. + * + * \param ptr Pointer to the block + * \param idx Index of slicing + */ + constexpr BlockIterator(ParentBlock* ptr, std::size_t idx) noexcept + : block_(ptr), curr_idx_(idx) {} + BlockIterator(const BlockIterator&) = default; + //@} + //************************************************************************** + + //**Destructor************************************************************** + /*!\name Destructor */ + //@{ + ~BlockIterator() = default; + //@} + //************************************************************************** + + //**Assignment operators**************************************************** + /*!\name Assignment operators */ + //@{ + + //************************************************************************** + /*!\brief Addition assignment operator. + * + * \param inc The increment of the iterator. + * \return Reference to the incremented iterator. + */ + constexpr BlockIterator& operator+=(difference_type inc) noexcept { + curr_idx_ += inc; + return *this; + }; + //************************************************************************** + + //************************************************************************** + /*!\brief Subtraction assignment operator. + * + * \param dec The decrement of the iterator. + * \return Reference to the decremented iterator. + */ + constexpr BlockIterator& operator-=(difference_type dec) noexcept { + // does this by any chance go beyond bounds? + curr_idx_ -= dec; + return *this; + }; + //************************************************************************** + + BlockIterator& operator=(const BlockIterator&) = default; + //@} + //************************************************************************** + + //**Increment/decrement operators******************************************* + /*!\name Increment/decrement operators */ + //@{ + + //************************************************************************** + /*!\brief Pre-increment operator. + * + * \return Reference to the incremented iterator. + */ + constexpr BlockIterator& operator++() noexcept { + ++curr_idx_; + return *this; + }; + //************************************************************************** + + //************************************************************************** + /*!\brief Post-increment operator. + * + * \return The previous position of the iterator. + */ + constexpr const BlockIterator operator++(int) noexcept { + return BlockIterator(block_, curr_idx_++); + }; + //************************************************************************** + + //************************************************************************** + /*!\brief Pre-decrement operator. + * + * \return Reference to the decremented iterator. + */ + constexpr BlockIterator& operator--() noexcept { + --curr_idx_; + return *this; + }; + //************************************************************************** + + //************************************************************************** + /*!\brief Post-decrement operator. + * + * \return The previous position of the iterator. + */ + constexpr const BlockIterator operator--(int) noexcept { + return BlockIterator(block_, curr_idx_--); + }; + //************************************************************************** + + //@} + //************************************************************************** + + //**Access operators******************************************************** + /*!\name Access operators */ + //@{ + + //************************************************************************** + /*!\brief Direct access to the underlying slice. + * + * \param index Access index. + * \return Accessed slice. + */ + constexpr ReferenceType operator[](size_t index) const noexcept { + // A static cast is necessary to properly return tagged systems. Tagged + // systems may be implemented as deriving from a Block, in which case the + // slice method() returns a non-tagged reference, which is then converted + // to a tagged reference. + return static_cast(slice(*block_, curr_idx_ + index)); + }; + //************************************************************************** + + //************************************************************************** + /*!\brief Direct access to the slice at the current iterator position. + * + * \return Current slice. + */ + constexpr ReferenceType operator*() const noexcept { + return static_cast(slice_backend(*block_, curr_idx_)); + }; + //@} + //************************************************************************** + + //**Utility functions******************************************************* + /*!\name Utility functions */ + //@{ + + //************************************************************************** + /*!\brief Low-level access to the underlying data of the iterator. + * + * \return Pointer to the parent block. + */ + constexpr ParentBlock* data() const noexcept { return block_; } + //************************************************************************** + + //************************************************************************ + /*!\brief Gets the parent block + */ + inline constexpr auto parent() & noexcept -> ParentBlock& { + return *block_; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Gets the parent block + */ + inline constexpr auto parent() const& noexcept -> ParentBlock const& { + return *block_; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Gets the parent block + * + * \note + * This method is useful when xvalue block-slices are generated + * on-the-fly via BlockRefs. If this overload is not present, then the + * const& is picked up, and it is not possible to assign values to the + * parent block anymore. + */ + inline constexpr auto parent() && noexcept -> ParentBlock& { + return *block_; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Gets the parent block + * + * \note + * This method is provided for symmetry with the overload above. + */ + inline constexpr auto parent() const&& noexcept -> ParentBlock const& { + return *block_; + } + //************************************************************************ + + //************************************************************************** + /*!\brief Low-level access to the index of the iterator. + * + * \return Index into the current slice location + */ + constexpr auto index() const noexcept -> std::size_t { return curr_idx_; } + //************************************************************************** + + //@} + //************************************************************************** + + private: + //**Member variables******************************************************** + /*!\name Member variables */ + //@{ + //! Pointer to the held block. + ParentBlock* block_; + //! Current slicing index + std::size_t curr_idx_; + //@} + //************************************************************************** + }; + //**************************************************************************** + + //============================================================================ + // + // GLOBAL OPERATORS + // + //============================================================================ + + //**************************************************************************** + /*!\name BlockIterator operators */ + //@{ + + //**************************************************************************** + /*!\brief Equality comparison between two BlockIterator objects. + * + * \param lhs The left-hand side iterator. + * \param rhs The right-hand side iterator. + * \return \a true if the iterators refer to the same element, \a false if + * not. + */ + template + constexpr inline bool operator==(const BlockIterator& lhs, + const BlockIterator& rhs) noexcept { + return lhs.data() == rhs.data() and lhs.index() == rhs.index(); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief InEquality comparison between two BlockIterator objects. + * + * \param lhs The left-hand side iterator. + * \param rhs The right-hand side iterator. + * \return \a true if the iterators don't refer to the same element, \a false + * if they do. + */ + template + constexpr inline bool operator!=(const BlockIterator& lhs, + const BlockIterator& rhs) noexcept { + return lhs.data() != rhs.data() or lhs.index() != rhs.index(); + } + //**************************************************************************** + + //@} + //**************************************************************************** + +} // namespace blocks + +namespace blocks { + + //============================================================================ + // + // CLASS DEFINITION + // + //============================================================================ + + //**************************************************************************** + /*!\brief Implementation of a iterator for blocks. + * \ingroup blocks + * + * The ConstBlockIterator represents a generic random-access iterator that can + * be used for all block types in \elastica + */ + template + class ConstBlockIterator { + private: + //**Type definitions******************************************************** + using ParentBlock = Block; + //************************************************************************** + + public: + //**Type definitions******************************************************** + //! The iterator category. + using IteratorCategory = std::random_access_iterator_tag; + //! Type of the underlying elements. + using ValueType = + typename PluginFrom::template to::type; + //! Pointer return type. + using PointerType = ValueType; + //! Reference return type. + using ReferenceType = ValueType; + //! Difference between two iterators. + using DifferenceType = std::size_t; + + // STL iterator requirements + //! The iterator category. + using iterator_category = IteratorCategory; + //! Type of the underlying elements. + using value_type = ValueType; + //! Pointer return type. + using pointer = PointerType; + //! Reference return type. + using reference = ReferenceType; + //! Difference between two iterators. + using difference_type = DifferenceType; + //************************************************************************** + + public: + //**Constructors************************************************************ + /*!\name Constructors */ + //@{ + + //************************************************************************** + /*!\brief Default constructor for the ConstBlockIterator class. + */ + explicit constexpr ConstBlockIterator() noexcept + : block_(nullptr), curr_idx_(std::numeric_limits::max()) {} + //************************************************************************** + + //************************************************************************** + /*!\brief Constructor for the ConstBlockIterator class. + * + * \param ptr Pointer to the block + * \param idx Index of slicing + */ + constexpr ConstBlockIterator(ParentBlock const* ptr, + std::size_t idx) noexcept + : block_(ptr), curr_idx_(idx) {} + //************************************************************************** + + //************************************************************************** + /*!\brief Conversion constructor from a BlockIterator instance. + * + * \param it The BlockIterator instance to be copied. + */ + constexpr ConstBlockIterator(BlockIterator const& it) noexcept + : block_(it.data()), curr_idx_(it.index()){}; + //************************************************************************** + + ConstBlockIterator(const ConstBlockIterator&) = default; + //@} + //************************************************************************** + + //**Destructor************************************************************** + /*!\name Destructor */ + //@{ + ~ConstBlockIterator() = default; + //@} + //************************************************************************** + + //**Assignment operators**************************************************** + /*!\name Assignment operators */ + //@{ + + //************************************************************************** + /*!\brief Addition assignment operator. + * + * \param inc The increment of the iterator. + * \return Reference to the incremented iterator. + */ + constexpr ConstBlockIterator& operator+=(difference_type inc) noexcept { + curr_idx_ += inc; + return *this; + }; + //************************************************************************** + + //************************************************************************** + /*!\brief Subtraction assignment operator. + * + * \param dec The decrement of the iterator. + * \return Reference to the decremented iterator. + */ + constexpr ConstBlockIterator& operator-=(difference_type dec) noexcept { + // does this by any chance go beyond bounds? + curr_idx_ -= dec; + return *this; + }; + //************************************************************************** + + ConstBlockIterator& operator=(const ConstBlockIterator&) = default; + //@} + //************************************************************************** + + //**Increment/decrement operators******************************************* + /*!\name Increment/decrement operators */ + //@{ + + //************************************************************************** + /*!\brief Pre-increment operator. + * + * \return Reference to the incremented iterator. + */ + constexpr ConstBlockIterator& operator++() noexcept { + ++curr_idx_; + return *this; + }; + //************************************************************************** + + //************************************************************************** + /*!\brief Post-increment operator. + * + * \return The previous position of the iterator. + */ + constexpr const ConstBlockIterator operator++(int) noexcept { + return ConstBlockIterator(block_, curr_idx_++); + }; + //************************************************************************** + + //************************************************************************** + /*!\brief Pre-decrement operator. + * + * \return Reference to the decremented iterator. + */ + constexpr ConstBlockIterator& operator--() noexcept { + --curr_idx_; + return *this; + }; + //************************************************************************** + + //************************************************************************** + /*!\brief Post-decrement operator. + * + * \return The previous position of the iterator. + */ + constexpr const ConstBlockIterator operator--(int) noexcept { + return ConstBlockIterator(block_, curr_idx_--); + }; + //************************************************************************** + + //@} + //************************************************************************** + + //**Access operators******************************************************** + /*!\name Access operators */ + //@{ + + //************************************************************************** + /*!\brief Direct access to the underlying slice. + * + * \param index Access index. + * \return Accessed slice. + */ + constexpr ReferenceType operator[](size_t index) const noexcept { + return static_cast(slice(*block_, curr_idx_ + index)); + }; + //************************************************************************** + + //************************************************************************** + /*!\brief Direct access to the slice at the current iterator position. + * + * \return Current slice. + */ + constexpr ReferenceType operator*() const noexcept { + return static_cast(slice(*block_, curr_idx_)); + }; + //@} + //************************************************************************** + + //**Utility functions******************************************************* + /*!\name Utility functions */ + //@{ + + //************************************************************************** + /*!\brief Low-level access to the underlying data of the iterator. + * + * \return Pointer to the parent block. + */ + constexpr ParentBlock const* data() const noexcept { return block_; } + //************************************************************************** + + //************************************************************************** + /*!\brief Gets the parent block + */ + inline constexpr auto parent() const noexcept -> ParentBlock const& { + return *block_; + } + //************************************************************************** + + //************************************************************************** + /*!\brief Low-level access to the index of the iterator. + * + * \return Index into the current slice location + */ + constexpr auto index() const noexcept -> std::size_t { return curr_idx_; } + //************************************************************************** + + //@} + //************************************************************************** + + private: + //**Member variables******************************************************** + /*!\name Member variables */ + //@{ + //! Pointer to the held (const) block. + ParentBlock const* block_; + //! Current slicing index + std::size_t curr_idx_; + //@} + //************************************************************************** + }; + //**************************************************************************** + + //============================================================================ + // + // GLOBAL OPERATORS + // + //============================================================================ + + //**************************************************************************** + /*!\name ConstBlockIterator operators */ + //@{ + + //**************************************************************************** + /*!\brief Equality comparison between two ConstBlockIterator objects. + * + * \param lhs The left-hand side iterator. + * \param rhs The right-hand side iterator. + * \return \a true if the iterators refer to the same element, \a false if + * not. + */ + template + constexpr inline bool operator==( + const ConstBlockIterator& lhs, + const ConstBlockIterator& rhs) noexcept { + return lhs.data() == rhs.data() and lhs.index() == rhs.index(); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief InEquality comparison between two ConstBlockIterator objects. + * + * \param lhs The left-hand side iterator. + * \param rhs The right-hand side iterator. + * \return \a true if the iterators don't refer to the same element, \a false + * if they do. + */ + template + constexpr inline bool operator!=( + const ConstBlockIterator& lhs, + const ConstBlockIterator& rhs) noexcept { + return lhs.data() != rhs.data() or lhs.index() != rhs.index(); + } + //**************************************************************************** + + //@} + //**************************************************************************** + +} // namespace blocks diff --git a/backend/src/Systems/Block/Block/BlockSlice.hpp b/backend/src/Systems/Block/Block/BlockSlice.hpp new file mode 100644 index 000000000..685bef5f8 --- /dev/null +++ b/backend/src/Systems/Block/Block/BlockSlice.hpp @@ -0,0 +1,430 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** + +#include "Systems/Block/Block/Types.hpp" +// +#include "Systems/Block/Block/AsVariables.hpp" +#include "Systems/Block/Block/BlockSliceFacade.hpp" +#include "Systems/Block/Block/TypeTraits.hpp" +#include "Systems/Block/BlockVariables/TypeTraits.hpp" + +namespace blocks { + + //============================================================================ + // + // CLASS DEFINITION + // + //============================================================================ + + //**************************************************************************** + /*!\brief Implementation of a slice/view on an \elastica Block + * \ingroup blocks + * + * \details + * The BlockSlice template class provides a slice (or) view into a Block of + * `data` and `operations`. It also models the `ComputationalBlock` concept + * and hence can be used across places where a \elastica Block is used as + * a template parameter. + * + * Similar to Block, BlockSlice can also be customized (specialized) for any + * Lagrangian entity, but experience suggests that extensive customization is + * not needed. For a concrete example, please see specializations of + * BlockSlice in @ref cosserat_rod. + * + * \usage + * The intended usage of a BlockSlice is when a view is required into the data + * held by a block---this is frequently the case when a user either + * 1. adds an entity into the Simulator, or + * 2. requires access (for e.g. reading/writing to disk) to only a portion of + * the block. + * The pattern that is most commonly seen in the use case (1) is for a + * BlockSlice templated on some `Plugin` type to be only used with a Block + * of the same `Plugin` type, when adding new entities to the Block. + * For use case (2) we suggest the user to the blocks::slice() function, which + * has an intuitive, explicit slice syntax, or even the subscript operator[]. + * We note that we might explicitly disable the subscript operator [] for + * slicing in the future, as the semantics are potentially unclear. + * + * Finally, with no additional coding effort, the BlockSlice has exactly the + * same operations as the mother Block (aka Block of the same `Plugin` type), + * but now it operates only on that slice of the data. This means that a + * BlockSlice can be used as a small Block in itself which greatly + * sbackendifies interfacing different components of \elastica---the user need + * not care or even know about whether the data that she has is a Block or a + * BlockSlice! For example, \code + * // ... make simulator etc ... + * auto my_rod = simulator.emplace_back( * ...args... *); + * // the args go and form a Block + * // which in turn returns a BlockSlice + * // which the user gets as my_rod + * + * // use my_rod like a regular cosserat rod + * simulator->constrain(my_rod)->using( *...args... *); + * \endcode + * This abstraction helped us constrain \c my_rod, embedded in a Block of + * data, using \c SomeConstraint just like any non-Blocked item of the + * \elastica library. + * + * \tparam Plugin The computational plugin modeling a Lagrangian entity + * + * \see Block, blocks::slice + */ + template + class BlockSlice : public detail::BlockSliceFacade, + public Gettable>, + public Plugin { + // NOTE : Plugin is inherited after Facade so that indices and references to + // the parent block is filled first. This is because some elements of Plugin + // may require that the slice (and index) be valid, for example to generate + // internal refs/pointers for time-stepping. + protected: + //**Type definitions******************************************************** + //! Type of the parent plugin + using Parent = detail::BlockSliceFacade; + //! Type of the slice + using This = BlockSlice; + //! Type of gettable + using GetAffordance = Gettable; + //************************************************************************** + + public: + //**Type definitions******************************************************** + //! Type of the parent plugin + using typename Parent::PluginType; + //! Type of the parent block + using typename Parent::ParentBlock; + //! Type of Variables + using typename Parent::Variables; + //************************************************************************** + + //**Friendships************************************************************* + //! Friend the main block + friend ParentBlock; + //************************************************************************** + + public: + //**Constructors************************************************************ + /*!\name Constructors */ + //@{ + + //************************************************************************** + /*!\brief The default constructor. + * + * \param parent_block Parent block for the slice + * \param index Index of the slice + */ + explicit BlockSlice(ParentBlock& parent_block, std::size_t index) noexcept + : Parent(parent_block, index), GetAffordance(), PluginType() {} + //************************************************************************** + + //************************************************************************** + /*!\brief The copy constructor. + * + * \param other slice to copy + */ + BlockSlice(BlockSlice const& other) + : Parent(static_cast(other)), + GetAffordance(), + PluginType(static_cast(other)){}; + //************************************************************************** + + //************************************************************************** + /*!\brief The move constructor. + * + * \param other slice to move from + */ + BlockSlice(BlockSlice&& other) noexcept + : Parent(static_cast(other)), + GetAffordance(), + PluginType(static_cast(other)){}; + //************************************************************************** + + //@} + //************************************************************************** + + public: + //**Destructor************************************************************** + /*!\name Destructor */ + //@{ + ~BlockSlice() = default; + //@} + //************************************************************************** + }; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Implementation of a slice/view on a const \elastica Block + * \ingroup blocks + * + * \details + * The ConstBlockSlice template class provides a slice (or) view into a + * constant Block of \a data and \a operations. It also models the + * `ComputationalBlock` concept and hence can be used across places where a + * \elastica Block is used as a template parameter. + * + * Notably, it differs from BlockSlice in one key aspect : the underlying + * data/view is always constant and cannot be modified (it is only read only). + * Hence ConstBlock is useful for propagating const-correctness throughout the + * code. It can be used in places where one needs to pass (const)-data to the + * user which she can then copy and use it for her own purposes. + * + * \tparam Plugin The computational plugin modeling a Lagrangian entity + * + * \see BlockSlice + */ + template + class ConstBlockSlice : public detail::ConstBlockSliceFacade, + public Gettable>, + public Plugin { + // NOTE : Plugin is inherited after Facade so that indices and references to + // the parent block is filled first. This is because some elements of Plugin + // may require that the slice (and index) be valid, for example to generate + // internal refs/pointers for time-stepping. + protected: + //**Type definitions******************************************************** + //! Type of the parent plugin + using Parent = detail::ConstBlockSliceFacade; + //! Type of the slice + using This = ConstBlockSlice; + //! Type of gettable + using GetAffordance = Gettable; + //************************************************************************** + + public: + //**Type definitions******************************************************** + //! Type of the parent plugin + using typename Parent::PluginType; + //! Type of the parent block + using typename Parent::ParentBlock; + //! Type of Variables + using typename Parent::Variables; + //************************************************************************** + + //**Friendships************************************************************* + //! Friend the main block + friend ParentBlock; + //************************************************************************** + + public: + //**Constructors************************************************************ + /*!\name Constructors */ + //@{ + + //************************************************************************** + /*!\brief The default constructor. + * + * \param parent_block Parent block for the slice + * \param index Index of the slice + */ + ConstBlockSlice(ParentBlock const& parent_block, std::size_t index) noexcept + : Parent(parent_block, index), GetAffordance(), PluginType() {} + //************************************************************************** + + //************************************************************************** + /*!\brief The copy constructor. + * + * \param other slice to copy + */ + ConstBlockSlice(ConstBlockSlice const& other) + : Parent(static_cast(other)), + GetAffordance(), + PluginType(static_cast(other)){}; + //************************************************************************** + + //************************************************************************** + /*!\brief The move constructor. + * + * \param other slice to move from + */ + ConstBlockSlice(ConstBlockSlice&& other) noexcept + : Parent(static_cast(other)), + GetAffordance(), + PluginType(static_cast(other)){}; + //************************************************************************** + + //@} + //************************************************************************** + + public: + //**Destructor************************************************************** + /*!\name Destructor */ + //@{ + ~ConstBlockSlice() = default; + //@} + //************************************************************************** + }; + //**************************************************************************** + + //============================================================================ + // + // GLOBAL OPERATORS + // + //============================================================================ + + //**Equality operator********************************************************* + /*!\brief Equality comparison between two BlockSlice objects. + * + * \param lhs The left-hand side slice. + * \param rhs The right-hand side slice. + * \return \a true if the slices are same, else \a false + */ + template + inline constexpr auto operator==(BlockSlice const& lhs, + BlockSlice const& rhs) noexcept + -> bool { + return static_cast const&>(lhs) == + static_cast const&>(rhs); + } + //**************************************************************************** + + //**Equality operator********************************************************* + /*!\brief Equality comparison between two ConstBlockSlice objects. + * + * \param lhs The left-hand side const slice. + * \param rhs The right-hand side const slice. + * \return \a true if the const slices are same, else \a false + */ + template + inline constexpr auto operator==(ConstBlockSlice const& lhs, + ConstBlockSlice const& rhs) noexcept + -> bool { + return static_cast const&>(lhs) == + static_cast const&>(rhs); + } + //**************************************************************************** + + //============================================================================ + // + // GET FUNCTIONS + // + //============================================================================ + + namespace detail { + + template + inline constexpr decltype(auto) get_slice(SliceLike&& slice_like) noexcept { + return std::forward(slice_like) + .parent() + .template slice( + std::forward(slice_like).index()); + } + + } // namespace detail + + //**************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\name Get functions for Slice types */ + //@{ + + //**************************************************************************** + /*!\brief Extract element from a BlockSlice + * \ingroup blocks + * + * \details + * Extracts the element of the Block `block_slice` whose tag type is `Tag`. + * Fails to compile unless the block has the `Tag` being extracted. + * + * \usage + * The usage is similar to std::get(), shown below + * \code + Block<...> b; + auto my_tag_data = blocks::get(b); + * \endcode + * + * \tparam Tag Tag to extract + * + * \param block_like The block to extract the tag from + */ + template + inline constexpr decltype(auto) get_backend( + BlockSlice& block_slice) noexcept { + return detail::get_slice(block_slice); + } + + template + inline constexpr decltype(auto) get_backend( + BlockSlice const& block_slice) noexcept { + return detail::get_slice(block_slice); + } + + template + inline constexpr decltype(auto) get_backend( + BlockSlice&& block_slice) noexcept { + return detail::get_slice( + static_cast&&>(block_slice)); + } + + template + inline constexpr decltype(auto) get_backend( + BlockSlice const&& block_slice) noexcept { + return detail::get_slice( + static_cast const&&>(block_slice)); + } + + template + inline constexpr decltype(auto) get_backend( + ConstBlockSlice const& block_slice) noexcept { + return detail::get_slice(block_slice); + } + //@} + /*! \endcond */ + //**************************************************************************** + + //============================================================================ + // + // SLICE FUNCTIONS + // + //============================================================================ + + //**************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\name Block slice functions */ + //@{ + + //**************************************************************************** + /*!\brief Slice backend for Block + * \ingroup blocks + * + * \details + * Implements slices on entities modeling block concept. + * + * \param block_like Data-structure modeling the block concept + * \param index The index of the slice (int or from_end) + * + * \example + * Given a block `b`, we can slice it using + * \code + * auto b_slice = block::slice(b, 5UL); // at index 5 from start + * auto b_slice_from_end = block::slice(b, elastica::end - 2UL); // 2 from end + * \endcode + * + * \note + * not marked noexcept because we can have out of range indices + */ + template + inline decltype(auto) slice_backend(Block& block_like, + std::size_t index) noexcept { + using ReturnType = + typename PluginFrom>::template to::type; + return ReturnType{block_like, index}; + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice_backend(Block const& block_like, + std::size_t index) noexcept { + using ReturnType = + typename PluginFrom>::template to::type; + return ReturnType{block_like, index}; + } + //**************************************************************************** + + //@} + /*! \endcond */ + //**************************************************************************** + +} // namespace blocks diff --git a/backend/src/Systems/Block/Block/BlockSliceFacade.hpp b/backend/src/Systems/Block/Block/BlockSliceFacade.hpp new file mode 100644 index 000000000..4bc36eaeb --- /dev/null +++ b/backend/src/Systems/Block/Block/BlockSliceFacade.hpp @@ -0,0 +1,476 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +#include "Systems/Block/Block/Types.hpp" +// +#include "Systems/Block/Block/AsVariables.hpp" +#include "Systems/Block/Block/TypeTraits.hpp" +#include "Systems/Block/BlockVariables/TypeTraits.hpp" + +namespace blocks { + + namespace detail { + + //========================================================================== + // + // CLASS DEFINITION + // + //========================================================================== + + //************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\brief Implementation of a slice/view on an \elastica Block + * \ingroup blocks + * + * \details + * The BlockSliceFacade template class provides a slice (or) view into a + * Block of `data` and `operations`. It also models the `ComputationalBlock` + * concept and hence can be used across places where a \elastica Block is + * used as a template parameter. + * + * Similar to Block, BlockSliceFacade can also be customized (specialized) + * for any Lagrangian entity, but experience suggests that extensive + * customization is not needed. For a concrete example, please see + * specializations of BlockSliceFacade in @ref cosserat_rod + * + * \usage + * The intended usage of a BlockSliceFacade is when a view is required into + the data + * held by a block---this is frequently the case when a user either + * 1. adds an entity into the Simulator, or + * 2. requires access (for e.g. reading/writing to disk) to only a portion + of + * the block. + * The pattern that is most commonly seen in the use case (1) is for a + * BlockSliceFacade templated on some `Plugin` type to be only used with a + Block + * of the same `Plugin` type, when adding new entities to the Block. + * For use case (2) we suggest the user to the blocks::slice() function, + which + * has an intuitive, explicit slice syntax, or even the subscript + operator[]. + * We note that we might explicitly disable the subscript operator [] for + * slicing in the future, as the semantics are potentially unclear. + * + * Finally, with no additional coding effort, the BlockSliceFacade has + exactly the + * same operations as the mother Block (aka Block of the same `Plugin` + type), + * but now it operates only on that slice of the data. This means that a + * BlockSliceFacade can be used as a small Block in itself which greatly + simplifies + * interfacing different components of \elastica---the user need not care or + * even know about whether the data that she has is a Block or a + BlockSliceFacade! + * For example, + \code + // ... make simulator etc ... + auto my_rod = simulator.emplace_back( * ...args... *); + // the args go and form a Block + // which in turn returns a BlockSliceFacade + // which the user gets as my_rod + + // use my_rod like a regular cosserat rod + simulator->constrain(my_rod)->using( *...args... *); + \endcode + * This abstraction helped us constrain \c my_rod, embedded in a Block of + * data, using \c SomeConstraint just like any non-Blocked item of the + * \elastica library. + * + * \tparam Plugin The computational plugin modeling a Lagrangian entity + * + * \see Block, blocks::slice + */ + template + class BlockSliceFacade { + protected: + //**Type definitions****************************************************** + //! Type of the slice + using This = BlockSliceFacade; + //************************************************************************ + + public: + //**Type definitions****************************************************** + //! Type of the plugin + using PluginType = Plugin; + //! List of variables + using Variables = typename PluginType::Variables; + //! Typelist of all variable slices + using VariableSlicesList = typename as_slices::slices; + //! Values of all variable slices + using BlockVariableSlices = as_block_variables; + //! Typelist of all variable slices + using VariableConstSlicesList = + typename as_slices::const_slices; + //! Values of all variable slices + using BlockVariableConstSlices = + as_block_variables; + //! Conformant mapping between tags and variables + using VariableMap = VariableMapping; + //! Type of the parent block + using ParentBlock = + typename PluginFrom>::template to::type; + //************************************************************************ + + protected: + //**Constructors********************************************************** + /*!\name Constructors */ + //@{ + + //************************************************************************ + /*!\brief The default constructor. + * + * \param parent_block Parent block for the slice + * \param index Index of the slice + */ + BlockSliceFacade(ParentBlock& parent_block, std::size_t index) noexcept + : index_(index), parent_block_(parent_block) {} + //************************************************************************ + + //************************************************************************ + /*!\brief The copy constructor. + * + * \param other slice to copy + */ + BlockSliceFacade(BlockSliceFacade const& other) noexcept + : index_(other.index_), parent_block_(other.parent_block_){}; + //************************************************************************ + + //************************************************************************ + /*!\brief The move constructor. + * + * \param other slice to move from + */ + BlockSliceFacade(BlockSliceFacade&& other) noexcept + : index_(other.index_), parent_block_(other.parent_block_){}; + //************************************************************************ + + //@} + //************************************************************************ + + public: + //**Destructor************************************************************ + /*!\name Destructor */ + //@{ + ~BlockSliceFacade() = default; + //@} + //************************************************************************ + + //**Utility methods******************************************************* + /*!\name Utility methods*/ + //@{ + + //************************************************************************ + /*!\brief Gets the parent block + */ + inline constexpr auto parent() & noexcept -> ParentBlock& { + return parent_block_; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Gets the parent block + */ + inline constexpr auto parent() const& noexcept -> ParentBlock const& { + return parent_block_; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Gets the parent block + * + * \note + * This method is useful when xvalue block-slices are generated + * on-the-fly via BlockRefs. If this overload is not present, then the + * const& is picked up, and it is not possible to assign values to the + * parent block anymore. + * + * \note + * Returning a reference is valid as the parent block outlives the block + * slice. + */ + inline constexpr auto parent() && noexcept -> ParentBlock& { + return parent_block_; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Gets the parent block + * + * \note + * This method is provided for symmetry with the overload above. + * + * \note + * Returning a reference is valid as the parent block outlives the block + * slice. + */ + inline constexpr auto parent() const&& noexcept -> ParentBlock const& { + return parent_block_; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Returns the slice index + */ + inline constexpr auto index() const noexcept -> std::size_t { + return index_; + } + //************************************************************************ + + //@} + //************************************************************************ + + //**Data access*********************************************************** + /*!\name Data access */ + //@{ + protected: + template + inline constexpr decltype(auto) generate_data(tmpl::list /*meta*/ + ) & { + using RT = BlockVariableSlices; + return RT{parent().template slice(index())...}; + } + + template + inline constexpr decltype(auto) generate_data(tmpl::list /*meta*/ + ) const& { + using RT = BlockVariableConstSlices const; + return RT{parent().template slice(index())...}; + } + + public: + //************************************************************************ + /*!\brief Access to the underlying data + // + // \return Underlying data + */ + inline constexpr decltype(auto) data() & noexcept { + return generate_data(Variables{}); + } + //************************************************************************ + + //************************************************************************ + /*!\brief Access to the underlying data + // + // \return Constant lvalue reference to the underlying data + */ + inline constexpr decltype(auto) data() const& noexcept { + return generate_data(Variables{}); + } + //************************************************************************ + + //@} + //************************************************************************ + + private: + //**Member variables****************************************************** + /*!\name Member variables */ + //@{ + //! Slice index + std::size_t index_; + //! Reference to the parent block + ParentBlock& parent_block_; + //@} + //************************************************************************ + }; + /*! \endcond */ + //************************************************************************** + + //************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\brief Implementation of a slice/view on a const \elastica Block + * \ingroup blocks + * + * \details + * The ConstBlockSliceFacade template class provides a slice (or) view into + * a constant Block of \a data and \a operations. It also models the + * `ComputationalBlock` concept and hence can be used across places where a + * \elastica Block is used as a template parameter. + * + * Notably, it differs from BlockSliceFacade in one key aspect : the + * underlying data/view is always constant and cannot be modified (it is + * only read only). Hence ConstBlock is useful for propagating + * const-correctness throughout the code. It can be used in places where one + * needs to pass (const)-data to the user which she can then copy and use it + * for her own purposes. + * + * \tparam Plugin The computational plugin modeling a Lagrangian entity + * + * \see BlockSliceFacade + */ + template // The computational plugin type + class ConstBlockSliceFacade { + protected: + //**Type definitions****************************************************** + //! Type of the slice + using This = ConstBlockSliceFacade; + //************************************************************************ + + public: + //**Type definitions****************************************************** + //! Type of the plugin + using PluginType = Plugin; + //! List of variables + using Variables = typename PluginType::Variables; + //! Typelist of all variable slices + using VariableSlicesList = typename as_slices::const_slices; + //! Values of all variable slices + using BlockVariableSlices = as_block_variables; + //! Conformant mapping between tags and variables + using VariableMap = VariableMapping; + //! Type of the parent block + using ParentBlock = typename PluginFrom< + ConstBlockSlice>::template to::type; + //************************************************************************ + + // ideally we make the constructors protected, but this interferes with + // nothrow specifications + // https://stackoverflow.com/questions/40441994/usage-of-noexcept-in-derived-classes + protected: + //**Constructors********************************************************** + /*!\name Constructors */ + //@{ + + //************************************************************************ + /*!\brief The default constructor. + * + * \param parent_block Parent block for the slice + * \param index Index of the slice + */ + ConstBlockSliceFacade(ParentBlock const& parent_block, + std::size_t index) noexcept + : index_(index), parent_block_(parent_block) {} + //************************************************************************ + + //************************************************************************ + /*!\brief The copy constructor. + * + * \param other slice to copy + */ + ConstBlockSliceFacade(ConstBlockSliceFacade const& other) noexcept + : index_(other.index_), parent_block_(other.parent_block_){}; + //************************************************************************ + + //************************************************************************ + /*!\brief The move constructor. + * + * \param other slice to move from + */ + ConstBlockSliceFacade(ConstBlockSliceFacade&& other) noexcept + : index_(other.index_), parent_block_(other.parent_block_){}; + //************************************************************************ + + //@} + //************************************************************************ + + public: + //**Destructor************************************************************ + /*!\name Destructor */ + //@{ + ~ConstBlockSliceFacade() = default; + //@} + //************************************************************************ + + //**Utility methods******************************************************* + /*!\name Utility methods*/ + //@{ + + //************************************************************************ + /*!\brief Gets the parent block + */ + inline constexpr auto parent() const noexcept -> ParentBlock const& { + return parent_block_; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Returns the slice index + */ + inline constexpr auto index() const noexcept -> std::size_t { + return index_; + } + //************************************************************************ + + //@} + //************************************************************************ + + //**Data access*********************************************************** + /*!\name Data access */ + //@{ + protected: + template + inline constexpr decltype(auto) generate_data(tmpl::list /*meta*/ + ) const& { + using RT = BlockVariableSlices; + return RT{parent().template slice(index())...}; + } + + public: + //************************************************************************ + /*!\brief Access to the underlying data + // + // \return Constant lvalue reference to the underlying data + */ + inline constexpr decltype(auto) data() const& noexcept { + return generate_data(Variables{}); + } + //************************************************************************ + + //@} + //************************************************************************ + + private: + //**Member variables****************************************************** + /*!\name Member variables */ + //@{ + //! Slice index + std::size_t index_; + //! Reference to the parent block + ParentBlock const& parent_block_; + //@} + //************************************************************************ + }; + /*! \endcond */ + //************************************************************************** + + //========================================================================== + // + // GLOBAL OPERATORS + // + //========================================================================== + + //**Equality operator******************************************************* + /*!\brief Equality comparison between two BlockSliceFacade objects. + * + * \param lhs The left-hand side slice. + * \param rhs The right-hand side slice. + * \return \a true if the slices are same, else \a false + */ + template + inline constexpr auto operator==( + BlockSliceFacade const& lhs, + BlockSliceFacade const& rhs) noexcept -> bool { + return (&lhs.parent() == &rhs.parent()) and lhs.index() == rhs.index(); + } + //************************************************************************** + + //**Equality operator******************************************************* + /*!\brief Equality comparison between two ConstBlockSliceFacade objects. + * + * \param lhs The left-hand side const slice. + * \param rhs The right-hand side const slice. + * \return \a true if the const slices are same, else \a false + */ + template + inline constexpr auto operator==( + ConstBlockSliceFacade const& lhs, + ConstBlockSliceFacade const& rhs) noexcept -> bool { + return (&lhs.parent() == &rhs.parent()) and lhs.index() == rhs.index(); + } + //************************************************************************** + + } // namespace detail + +} // namespace blocks diff --git a/backend/src/Systems/Block/Block/BlockView.hpp b/backend/src/Systems/Block/Block/BlockView.hpp new file mode 100644 index 000000000..ea1842eb5 --- /dev/null +++ b/backend/src/Systems/Block/Block/BlockView.hpp @@ -0,0 +1,708 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** + +#include "Systems/Block/Block/Types.hpp" +// +#include "Systems/Block/Block/Concepts.hpp" +// +#include "Systems/Block/Block/AsVariables.hpp" +#include "Systems/Block/Block/BlockViewFacade.hpp" +#include "Systems/Block/Block/TypeTraits.hpp" +#include "Systems/Block/BlockVariables/TypeTraits.hpp" +// +#include "Utilities/AsConst.hpp" +#include "Utilities/End.hpp" +#include "Utilities/TMPL.hpp" +// +#include +#include + +namespace blocks { + + //**************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + template + inline constexpr auto operator==(BlockView const& lhs, + BlockView const& rhs) noexcept + -> bool; + template + inline constexpr auto operator==(ConstBlockView const& lhs, + ConstBlockView const& rhs) noexcept + -> bool; + // template + // inline auto size(BlockView const& view) -> bool; + // template + // inline auto size(ConstBlockView const& view) -> bool; + /*! \endcond */ + //**************************************************************************** + + //============================================================================ + // + // CLASS DEFINITION + // + //============================================================================ + + //**************************************************************************** + /*!\brief Implementation of a view/view on an \elastica Block + * \ingroup blocks + * + * \details + * The BlockView template class provides a view (or) view into a Block of + * `data` and `operations`. It also models the `ComputationalBlock` concept + * and hence can be used across places where a \elastica Block is used as + * a template parameter. + * + * Similar to Block, BlockView can also be customized (specialized) for any + * Lagrangian entity, but experience suggests that extensive customization is + * not needed. For a concrete example, please see specializations of + * BlockView in @ref cosserat_rod. + * + * BlockView is different from a BlockSlice : a BlockSlice represents a single + * physical entity, meanwhile BlockView may represent a group of entities. + * + * \usage + * The intended usage of a BlockView is when a view is required into the data + * held by a block---this is frequently the case when a user either + * 1. adds an entity into the Simulator, or + * 2. requires access (for e.g. reading/writing to disk) to only a portion of + * the block. + * The pattern that is most commonly seen in the use case (1) is for a + * BlockView templated on some `Plugin` type to be only used with a Block + * of the same `Plugin` type, when adding new entities to the Block. + * For use case (2) we suggest the user to the blocks::slice() function, which + * has an intuitive, explicit view syntax, or even the subscript operator[]. + * We note that we might explicitly disable the subscript operator [] for + * slicing in the future, as the semantics are potentially unclear. + * + * Finally, with no additional coding effort, the BlockView has exactly the + * same operations as the mother Block (aka Block of the same `Plugin` type), + * but now it operates only on that view of the data. This means that a + * BlockView can be used as a small Block in itself which greatly simplifies + * interfacing different components of \elastica---the user need not care or + * even know about whether the data that she has is a Block or a BlockView! + * + * \tparam Plugin The computational plugin modeling a Lagrangian entity + * + * \see Block, blocks::slice + */ + template + class BlockView : public detail::BlockViewFacade, + public Gettable>, + public Spannable>, + public Plugin { + // NOTE : Plugin is inherited after Facade so that indices and references to + // the parent block is filled first. This is because some elements of Plugin + // may require that the slice (and index) be valid, for example to generate + // internal refs/pointers for time-stepping. + protected: + //**Type definitions******************************************************** + //! Type of the parent plugin + using Parent = detail::BlockViewFacade; + //! Type of the view + using This = BlockView; + //! Type of gettable + using GetAffordance = Gettable; + //! Type of sliceable + using SpanAffordance = Spannable; + //************************************************************************** + + public: + //**Type definitions******************************************************** + //! Type of the parent plugin + using typename Parent::PluginType; + //! Type of the parent block + using typename Parent::ParentBlock; + //! Type of Variables + using typename Parent::Variables; + //************************************************************************** + + //**Friendships************************************************************* + //! Friend the main block + friend ParentBlock; + //************************************************************************** + + public: + //**Constructors************************************************************ + /*!\name Constructors */ + //@{ + + //************************************************************************** + /*!\brief The default constructor. + * + * \param parent_block The parent block that + * \param start_index The start index + * \param region_size The stop index + */ + explicit BlockView(ParentBlock& parent_block, std::size_t start_index, + std::size_t region_size) noexcept + : Parent(parent_block, start_index, region_size), + GetAffordance(), + SpanAffordance(), + PluginType() {} + //************************************************************************** + + //************************************************************************** + /*!\brief The copy constructor. + * + * \param other view to copy + */ + BlockView(BlockView const& other) + : Parent(static_cast(other)), + GetAffordance(), + SpanAffordance(), + PluginType(static_cast(other)){}; + //************************************************************************** + + //************************************************************************** + /*!\brief The move constructor. + * + * \param other view to move from + */ + BlockView(BlockView&& other) noexcept + : Parent(static_cast(other)), + GetAffordance(), + SpanAffordance(), + PluginType(static_cast(other)){}; + //************************************************************************** + + //@} + //************************************************************************** + + public: + //**Destructor************************************************************** + /*!\name Destructor */ + //@{ + ~BlockView() = default; + //@} + //************************************************************************** + + public: + //**Access operators******************************************************** + /*!\name Access operators */ + //@{ + //! Operator for slicing and viewing + using SpanAffordance::operator[]; + //@} + //************************************************************************** + }; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Implementation of a view/view on a const \elastica Block + * \ingroup blocks + * + * \details + * The ConstBlockView template class provides a view (or) view into a + * constant Block of \a data and \a operations. It also models the + * `ComputationalBlock` concept and hence can be used across places where a + * \elastica Block is used as a template parameter. + * + * Notably, it differs from BlockView in one key aspect : the underlying + * data/view is always constant and cannot be modified (it is only read only). + * Hence ConstBlock is useful for propagating const-correctness throughout the + * code. It can be used in places where one needs to pass (const)-data to the + * user which she can then copy and use it for her own purposes. + * + * \tparam Plugin The computational plugin modeling a Lagrangian entity + * + * \see BlockView + */ + template + class ConstBlockView : public detail::ConstBlockViewFacade, + public Gettable>, + public Spannable>, + public Plugin { + // NOTE : Plugin is inherited after Facade so that indices and references to + // the parent block is filled first. This is because some elements of Plugin + // may require that the slice (and index) be valid, for example to generate + // internal refs/pointers for time-stepping. + protected: + //**Type definitions******************************************************** + //! Type of the parent plugin + using Parent = detail::ConstBlockViewFacade; + //! Type of the view + using This = ConstBlockView; + //! Type of gettable + using GetAffordance = Gettable; + //! Type of sliceable + using SpanAffordance = Spannable; + //************************************************************************** + + public: + //**Type definitions******************************************************** + //! Type of the parent plugin + using typename Parent::PluginType; + //! Type of the parent block + using typename Parent::ParentBlock; + //! Type of Variables + using typename Parent::Variables; + //************************************************************************** + + //**Friendships************************************************************* + //! Friend the main block + friend ParentBlock; + //************************************************************************** + + public: + //**Constructors************************************************************ + /*!\name Constructors */ + //@{ + + //************************************************************************** + /*!\brief The default constructor. + * + * \param parent_block The parent block that + * \param start_index The start index + * \param region_size The stop index + */ + explicit ConstBlockView(ParentBlock const& parent_block, + std::size_t start_index, + std::size_t region_size) noexcept + : Parent(parent_block, start_index, region_size), + GetAffordance(), + SpanAffordance(), + PluginType() {} + //************************************************************************** + + //************************************************************************** + /*!\brief The copy constructor. + * + * \param other view to copy + */ + ConstBlockView(ConstBlockView const& other) + : Parent(static_cast(other)), + GetAffordance(), + SpanAffordance(), + PluginType(static_cast(other)){}; + //************************************************************************** + + //************************************************************************** + /*!\brief The move constructor. + * + * \param other view to move from + */ + ConstBlockView(ConstBlockView&& other) noexcept + : Parent(static_cast(other)), + GetAffordance(), + SpanAffordance(), + PluginType(static_cast(other)){}; + //************************************************************************** + + //@} + //************************************************************************** + + public: + //**Destructor************************************************************** + /*!\name Destructor */ + //@{ + ~ConstBlockView() = default; + //@} + //************************************************************************** + + public: + //**Access operators******************************************************** + /*!\name Access operators */ + //@{ + //! Operator for slicing and viewing + using SpanAffordance::operator[]; + //@} + //************************************************************************** + }; + //**************************************************************************** + + //============================================================================ + // + // GLOBAL OPERATORS + // + //============================================================================ + + //**Equality operator********************************************************* + /*!\brief Equality comparison between two BlockView objects. + * + * \param lhs The left-hand side view. + * \param rhs The right-hand side view. + * \return \a true if the views are same, else \a false + */ + template + inline constexpr auto operator==(BlockView const& lhs, + BlockView const& rhs) noexcept + -> bool { + return static_cast const&>(lhs) == + static_cast const&>(rhs); + } + //**************************************************************************** + + //**Equality operator********************************************************* + /*!\brief Equality comparison between two ConstBlockView objects. + * + * \param lhs The left-hand side const view. + * \param rhs The right-hand side const view. + * \return \a true if the const views are same, else \a false + */ + template + inline constexpr auto operator==(ConstBlockView const& lhs, + ConstBlockView const& rhs) noexcept + -> bool { + return static_cast const&>(lhs) == + static_cast const&>(rhs); + } + //**************************************************************************** + + //============================================================================ + // + // FREE FUNCTIONS + // + //============================================================================ + + //**************************************************************************** + /*!\brief Get number of units from a BlockView + * \ingroup blocks + * + * \details + * Get number of units from a BlockView. By 'units' we mean the number of + * individual BlockSlice(s) composing the View. + * + * \usage + * \code + * BlockView<...> b; + * std::size_t n_units = blocks::n_units(b); + * \endcode + * + * \param block_like The block whose number of units is to be extracted. + * + * \note + * These can be customized for your block type. + */ + template + inline auto n_units(BlockView const& block_view) noexcept + -> std::size_t { + return block_view.region().size; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Get number of units from a ConstBlockView + * \ingroup blocks + * + * \details + * Get number of units from a ConstBlockView. By 'units' we mean the number of + * individual BlockSlice(s) composing the View. + * + * \usage + * \code + * ConstBlockView<...> b; + * std::size_t n_units = blocks::n_units(b); + * \endcode + * + * \param block_like The block whose number of units is to be extracted. + * + * \note + * These can be customized for your block type. + */ + template + inline auto n_units(ConstBlockView const& block_view) noexcept + -> std::size_t { + return block_view.region().size; + } + //**************************************************************************** + + //============================================================================ + // + // GET FUNCTIONS + // + //============================================================================ + + namespace detail { + + template + inline constexpr decltype(auto) get_view(ViewLike&& view_like) noexcept { + return std::forward(view_like) + .parent() + .template slice( + std::forward(view_like).region().start, + std::forward(view_like).region().size); + } + + } // namespace detail + + //**************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\name Get functions for Slice types */ + //@{ + + //**************************************************************************** + /*!\brief Extract element from a BlockView + * \ingroup blocks + * + * \details + * Extracts the element of the BlockView `block_view` whose tag type is `Tag`. + * Fails to compile unless the block has the `Tag` being extracted. + * + * \usage + * The usage is similar to std::get(), shown below + * \code + Block<...> b; + auto my_tag_data = blocks::get(b); + * \endcode + * + * \tparam Tag Tag to extract + * + * \param block_like The block to extract the tag from + */ + template + inline constexpr decltype(auto) get_backend( + BlockView& block_view) noexcept { + return detail::get_view(block_view); + } + + template + inline constexpr decltype(auto) get_backend( + BlockView const& block_view) noexcept { + return detail::get_view(block_view); + } + + template + inline constexpr decltype(auto) get_backend( + BlockView&& block_view) noexcept { + return detail::get_view( + static_cast&&>(block_view)); + } + + template + inline constexpr decltype(auto) get_backend( + BlockView const&& block_view) noexcept { + return detail::get_view( + static_cast const&&>(block_view)); + } + + template + inline constexpr decltype(auto) get_backend( + ConstBlockView const& block_view) noexcept { + return detail::get_view(block_view); + } + //@} + /*! \endcond */ + //**************************************************************************** + + //============================================================================ + // + // VIEW FUNCTIONS + // + //============================================================================ + + //**************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\name Block view functions */ + //@{ + + //**************************************************************************** + /*!\brief View into blocks + * \ingroup blocks + * + * \details + * Implements contiguous views on Blocks + * + * \param block_like Data-structure modeling the block concept + * \param start_index The start index of the slice (int or from_end) + * \param stop_index The stop index of the slice (int or from_end) + * + * \example + * Given a block or a view `b`, we can obtain a view into it by using + * \code + * auto b_view = block::slice(b, 5UL, 10UL); // gets b[5, 6, 7, 8, 9] + * // Gets b[end - 5, end - 4, end - 3] + * auto b_view_from_end = block::slice(b, elastica::end - 5UL, elastica::end - + * 2UL); + * \endcode + */ + template + inline decltype(auto) slice_backend(Block& block_like, + std::size_t start_index, + std::size_t region_size) noexcept { + using ReturnType = + typename PluginFrom>::template to::type; + return ReturnType{block_like, start_index, region_size}; + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice_backend(Block const& block_like, + std::size_t start_index, + std::size_t region_size) noexcept { + using ReturnType = const typename PluginFrom>::template to< + ConstBlockView>::type; + return ReturnType{block_like, start_index, region_size}; + } + //**************************************************************************** + + //@} + /*! \endcond */ + //**************************************************************************** + + //**************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\name BlockView view functions */ + //@{ + + //**************************************************************************** + /*!\brief View into views + * \ingroup blocks + * + * \details + * Implements contiguous views on Views + * + * \param block_like Data-structure modeling the block concept + * \param start_index The start index of the slice + * \param region_size The size of the slice + * + * \example + * Given a block or a view `b`, we can obtain a view into it by using + * \code + * auto b_view = block::slice(b, 5UL, 10UL); // gets b[5, 6, 7, 8, 9] + * // Gets b[end - 5, end - 4, end - 3] + * auto b_view_from_end = block::slice(b, elastica::end - 5UL, elastica::end - + * 2UL); + * \endcode + * + */ + + //**************************************************************************** + template + inline decltype(auto) slice_backend(BlockView& block_like, + std::size_t start_index, + std::size_t region_size) noexcept { + // index is already confirmed within the acceptable limits, so use + // slice_backend instead of slice(parent()) + return slice_backend(block_like.parent(), + block_like.region().start + start_index, region_size); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice_backend(BlockView const& block_like, + std::size_t start_index, + std::size_t region_size) noexcept { + // index is already confirmed within the acceptable limits, so use + // slice_backend instead of slice(parent()) + return slice_backend(block_like.parent(), + block_like.region().start + start_index, region_size); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice_backend(BlockView&& block_like, + std::size_t start_index, + std::size_t region_size) noexcept { + // index is already confirmed within the acceptable limits, so use + // slice_backend instead of slice(parent()) + return slice_backend( + static_cast&&>(block_like).parent(), + static_cast&&>(block_like).region().start + + start_index, + region_size); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice_backend(ConstBlockView const& block_like, + std::size_t start_index, + std::size_t region_size) noexcept { + // index is already confirmed within the acceptable limits, so use + // slice_backend instead of slice(parent()) + return slice_backend(block_like.parent(), + block_like.region().start + start_index, region_size); + } + //**************************************************************************** + + //@} + /*! \endcond */ + //**************************************************************************** + + //============================================================================ + // + // SLICE FUNCTIONS + // + //============================================================================ + + //**************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\name BlockView slice functions */ + //@{ + + //**************************************************************************** + /*!\brief Slice into views + * \ingroup block_customization + * + * \details + * Implements slices on entities modeling block concept. + * + * \param block_like Data-structure modeling the block concept + * \param index The index of the slice + * + * \example + * Given a view `b`, we can obtain a slice + * \code + * auto b_slice = block::slice(b, 5UL); // at index 5 from start + * // 2 from end + * auto b_slice_from_end = block::slice(b, elastica::end - 2UL); + * \endcode + * + * \note + * not marked noexcept because we can have out of range indices + */ + template + inline decltype(auto) slice_backend(BlockView& block_like, + std::size_t index) noexcept { + // index is already confirmed within the acceptable limits, so use + // slice_backend instead of slice(parent()) + return slice_backend(block_like.parent(), + block_like.region().start + index); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice_backend(BlockView const& block_like, + std::size_t index) noexcept { + // index is already confirmed within the acceptable limits, so use + // slice_backend instead of slice(parent()) + return slice_backend(block_like.parent(), + block_like.region().start + index); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice_backend(BlockView&& block_like, + std::size_t index) noexcept { + // index is already confirmed within the acceptable limits, so use + // slice_backend instead of slice(parent()) + return slice_backend( + static_cast&&>(block_like).parent(), + static_cast&&>(block_like).region().start + index); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice_backend(ConstBlockView const& block_like, + std::size_t index) noexcept { + // index is already confirmed within the acceptable limits, so use + // slice_backend instead of slice(parent()) + return slice_backend(block_like.parent(), + block_like.region().start + index); + } + //**************************************************************************** + + //@} + /*! \endcond */ + //**************************************************************************** + +} // namespace blocks diff --git a/backend/src/Systems/Block/Block/BlockViewFacade.hpp b/backend/src/Systems/Block/Block/BlockViewFacade.hpp new file mode 100644 index 000000000..e8b3785cb --- /dev/null +++ b/backend/src/Systems/Block/Block/BlockViewFacade.hpp @@ -0,0 +1,503 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +#include "Systems/Block/Block/Types.hpp" +// +#include "Systems/Block/Block/AsVariables.hpp" +#include "Systems/Block/Block/TypeTraits.hpp" +#include "Systems/Block/BlockVariables/TypeTraits.hpp" + +namespace blocks { + + namespace detail { + + //************************************************************************** + //! Region over a block + struct Region { + //! Start region + std::size_t start; + //! Region size + std::size_t size; + }; + //************************************************************************** + + //**Equality operator******************************************************* + /*!\brief Equality comparison between two Region objects. + * + * \param lhs The left-hand side region. + * \param rhs The right-hand side region. + * \return \a true if the regions are over the same space, else \a false + */ + inline constexpr auto operator==(Region const& lhs, + Region const& rhs) noexcept -> bool { + // Brings in the tuple header + // return std::tie(lhs.start, lhs.size) == std::tie(rhs.start, rhs.size); + return (lhs.start == rhs.start) && (lhs.size == rhs.size); + } + /*! \endcond */ + //**************************************************************************** + + //========================================================================== + // + // CLASS DEFINITION + // + //========================================================================== + + //************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\brief Implementation of a slice/view on an \elastica Block + * \ingroup blocks + * + * \details + * The BlockViewFacade template class provides a slice (or) view into a + * Block of `data` and `operations`. It also models the `ComputationalBlock` + * concept and hence can be used across places where a \elastica Block is + * used as a template parameter. + * + * Similar to Block, BlockViewFacade can also be customized (specialized) + * for any Lagrangian entity, but experience suggests that extensive + * customization is not needed. For a concrete example, please see + * specializations of BlockViewFacade in @ref cosserat_rod + * + * \usage + * The intended usage of a BlockViewFacade is when a view is required into + the data + * held by a block---this is frequently the case when a user either + * 1. adds an entity into the Simulator, or + * 2. requires access (for e.g. reading/writing to disk) to only a portion + of + * the block. + * The pattern that is most commonly seen in the use case (1) is for a + * BlockViewFacade templated on some `Plugin` type to be only used with a + Block + * of the same `Plugin` type, when adding new entities to the Block. + * For use case (2) we suggest the user to the blocks::slice() function, + which + * has an intuitive, explicit slice syntax, or even the subscript + operator[]. + * We note that we might explicitly disable the subscript operator [] for + * slicing in the future, as the semantics are potentially unclear. + * + * Finally, with no additional coding effort, the BlockViewFacade has + exactly the + * same operations as the mother Block (aka Block of the same `Plugin` + type), + * but now it operates only on that slice of the data. This means that a + * BlockViewFacade can be used as a small Block in itself which greatly + simplifies + * interfacing different components of \elastica---the user need not care or + * even know about whether the data that she has is a Block or a + BlockViewFacade! + * For example, + \code + // ... make simulator etc ... + auto my_rod = simulator.emplace_back( * ...args... *); + // the args go and form a Block + // which in turn returns a BlockViewFacade + // which the user gets as my_rod + + // use my_rod like a regular cosserat rod + simulator->constrain(my_rod)->using( *...args... *); + \endcode + * This abstraction helped us constrain \c my_rod, embedded in a Block of + * data, using \c SomeConstraint just like any non-Blocked item of the + * \elastica library. + * + * \tparam Plugin The computational plugin modeling a Lagrangian entity + * + * \see Block, blocks::slice + */ + template + class BlockViewFacade { + protected: + //**Type definitions****************************************************** + //! Type of the slice + using This = BlockViewFacade; + //************************************************************************ + + public: + //**Type definitions****************************************************** + //! Type of the plugin + using PluginType = Plugin; + //! List of variables + using Variables = typename PluginType::Variables; + //! Typelist of all variable slices + using VariableSlicesList = typename as_slices::slices; + //! Values of all variable slices + using BlockVariableSlices = as_block_variables; + //! Typelist of all variable slices + using VariableConstSlicesList = + typename as_slices::const_slices; + //! Values of all variable slices + using BlockVariableConstViews = + as_block_variables; + //! Conformant mapping between tags and variables + using VariableMap = VariableMapping; + //! Type of the parent block + using ParentBlock = + typename PluginFrom>::template to::type; + //************************************************************************ + + protected: + //**Constructors********************************************************** + /*!\name Constructors */ + //@{ + + //************************************************************************ + /*!\brief The default constructor. + * + * \param parent_block Parent block for the slice + * \param region Index of the slice + */ + BlockViewFacade(ParentBlock& parent_block, std::size_t start_index, + std::size_t region_size) noexcept + : region_{start_index, region_size}, parent_block_(parent_block) {} + //************************************************************************ + + //************************************************************************ + /*!\brief The copy constructor. + * + * \param other slice to copy + */ + BlockViewFacade(BlockViewFacade const& other) noexcept + : region_(other.region_), parent_block_(other.parent_block_){}; + //************************************************************************ + + //************************************************************************ + /*!\brief The move constructor. + * + * \param other slice to move from + */ + BlockViewFacade(BlockViewFacade&& other) noexcept + : region_(std::move(other.region_)), + parent_block_(other.parent_block_){}; + //************************************************************************ + + //@} + //************************************************************************ + + public: + //**Destructor************************************************************ + /*!\name Destructor */ + //@{ + ~BlockViewFacade() = default; + //@} + //************************************************************************ + + //**Utility methods******************************************************* + /*!\name Utility methods*/ + //@{ + + //************************************************************************ + /*!\brief Gets the parent block + */ + inline constexpr auto parent() & noexcept -> ParentBlock& { + return parent_block_; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Gets the parent block + */ + inline constexpr auto parent() const& noexcept -> ParentBlock const& { + return parent_block_; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Gets the parent block + * + * \note + * This method is useful when xvalue block-slices are generated + * on-the-fly via BlockRefs. If this overload is not present, then the + * const& is picked up, and it is not possible to assign values to the + * parent block anymore. + * + * \note + * Returning a reference is valid as the parent block outlives the block + * slice. + */ + inline constexpr auto parent() && noexcept -> ParentBlock& { + return parent_block_; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Gets the parent block + * + * \note + * This method is provided for symmetry with the overload above. + * + * \note + * Returning a reference is valid as the parent block outlives the block + * slice. + */ + inline constexpr auto parent() const&& noexcept -> ParentBlock const& { + return parent_block_; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Returns the slice region + */ + inline constexpr auto region() const noexcept -> Region const& { + return region_; + } + //************************************************************************ + + //**Data access*********************************************************** + /*!\name Data access */ + //@{ + protected: + template + inline constexpr decltype(auto) generate_data(tmpl::list /*meta*/ + ) & { + using RT = BlockVariableSlices; + return RT{ + parent().template slice(region().start, region().size)...}; + } + + template + inline constexpr decltype(auto) generate_data(tmpl::list /*meta*/ + ) const& { + using RT = BlockVariableConstViews const; + return RT{ + parent().template slice(region().start, region().size)...}; + } + + public: + //************************************************************************ + /*!\brief Access to the underlying data + // + // \return Underlying data + */ + inline constexpr decltype(auto) data() & noexcept { + return generate_data(Variables{}); + } + //************************************************************************ + + //************************************************************************ + /*!\brief Access to the underlying data + // + // \return Constant lvalue reference to the underlying data + */ + inline constexpr decltype(auto) data() const& noexcept { + return generate_data(Variables{}); + } + //************************************************************************ + + //@} + //************************************************************************ + + private: + //**Member variables****************************************************** + /*!\name Member variables */ + //@{ + //! View region + detail::Region region_; + //! Reference to the parent block + ParentBlock& parent_block_; + //@} + //************************************************************************ + }; + /*! \endcond */ + //************************************************************************** + + //************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\brief Implementation of a slice/view on a const \elastica Block + * \ingroup blocks + * + * \details + * The ConstBlockViewFacade template class provides a slice (or) view into + * a constant Block of \a data and \a operations. It also models the + * `ComputationalBlock` concept and hence can be used across places where a + * \elastica Block is used as a template parameter. + * + * Notably, it differs from BlockViewFacade in one key aspect : the + * underlying data/view is always constant and cannot be modified (it is + * only read only). Hence ConstBlock is useful for propagating + * const-correctness throughout the code. It can be used in places where one + * needs to pass (const)-data to the user which she can then copy and use it + * for her own purposes. + * + * \tparam Plugin The computational plugin modeling a Lagrangian entity + * + * \see BlockViewFacade + */ + template // The computational plugin type + class ConstBlockViewFacade { + protected: + //**Type definitions****************************************************** + //! Type of the slice + using This = ConstBlockViewFacade; + //************************************************************************ + + public: + //**Type definitions****************************************************** + //! Type of the plugin + using PluginType = Plugin; + //! List of variables + using Variables = typename PluginType::Variables; + //! Typelist of all variable slices + using VariableSlicesList = typename as_slices::const_slices; + //! Values of all variable slices + using BlockVariableSlices = as_block_variables; + //! Conformant mapping between tags and variables + using VariableMap = VariableMapping; + //! Type of the parent block + using ParentBlock = + typename PluginFrom>::template to::type; + //************************************************************************ + + protected: + //**Constructors********************************************************** + /*!\name Constructors */ + //@{ + + //************************************************************************ + /*!\brief The default constructor. + * + * \param parent_block Parent block for the slice + * \param region Index of the slice + */ + ConstBlockViewFacade(ParentBlock const& parent_block, + std::size_t start_index, + std::size_t region_size) noexcept + : region_{start_index, region_size}, parent_block_(parent_block) {} + //************************************************************************ + + //************************************************************************ + /*!\brief The copy constructor. + * + * \param other slice to copy + */ + ConstBlockViewFacade(ConstBlockViewFacade const& other) noexcept + : region_(other.region_), parent_block_(other.parent_block_){}; + //************************************************************************ + + //************************************************************************ + /*!\brief The move constructor. + * + * \param other slice to move from + */ + ConstBlockViewFacade(ConstBlockViewFacade&& other) noexcept + : region_(std::move(other.region_)), + parent_block_(other.parent_block_){}; + //************************************************************************ + + //@} + //************************************************************************ + + public: + //**Destructor************************************************************ + /*!\name Destructor */ + //@{ + ~ConstBlockViewFacade() = default; + //@} + //************************************************************************ + + //**Utility methods******************************************************* + /*!\name Utility methods*/ + //@{ + + //************************************************************************ + /*!\brief Gets the parent block + */ + inline constexpr auto parent() const noexcept -> ParentBlock const& { + return parent_block_; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Returns the slice region + */ + inline constexpr auto region() const noexcept -> Region const& { + return region_; + } + //************************************************************************ + + //@} + //************************************************************************ + + //**Data access*********************************************************** + /*!\name Data access */ + //@{ + protected: + template + inline constexpr decltype(auto) generate_data(tmpl::list /*meta*/ + ) const& { + using RT = BlockVariableSlices; + return RT{ + parent().template slice(region().start, region().size)...}; + } + + public: + //************************************************************************ + /*!\brief Access to the underlying data + // + // \return Constant lvalue reference to the underlying data + */ + inline constexpr decltype(auto) data() const& noexcept { + return generate_data(Variables{}); + } + //************************************************************************ + + //@} + //************************************************************************ + + private: + //**Member variables****************************************************** + /*!\name Member variables */ + //@{ + //! View region + detail::Region region_; + //! Reference to the parent block + ParentBlock const& parent_block_; + //@} + //************************************************************************ + }; + /*! \endcond */ + //************************************************************************** + + //========================================================================== + // + // GLOBAL OPERATORS + // + //========================================================================== + + //**Equality operator******************************************************* + /*!\brief Equality comparison between two BlockViewFacade objects. + * + * \param lhs The left-hand side slice. + * \param rhs The right-hand side slice. + * \return \a true if the slices are same, else \a false + */ + template + inline constexpr auto operator==( + BlockViewFacade const& lhs, + BlockViewFacade const& rhs) noexcept -> bool { + return (&lhs.parent() == &rhs.parent()) and lhs.region() == rhs.region(); + } + //************************************************************************** + + //**Equality operator******************************************************* + /*!\brief Equality comparison between two ConstBlockViewFacade objects. + * + * \param lhs The left-hand side const slice. + * \param rhs The right-hand side const slice. + * \return \a true if the const slices are same, else \a false + */ + template + inline constexpr auto operator==( + ConstBlockViewFacade const& lhs, + ConstBlockViewFacade const& rhs) noexcept -> bool { + return (&lhs.parent() == &rhs.parent()) and lhs.region() == rhs.region(); + } + //************************************************************************** + + } // namespace detail + +} // namespace blocks diff --git a/backend/src/Systems/Block/Block/Concepts.hpp b/backend/src/Systems/Block/Block/Concepts.hpp new file mode 100644 index 000000000..fd2286c87 --- /dev/null +++ b/backend/src/Systems/Block/Block/Concepts.hpp @@ -0,0 +1,28 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** + +#include "Systems/Block/Block/Concepts/Types.hpp" +// +#include "Systems/Block/Block/Concepts/Gettable.hpp" +#include "Systems/Block/Block/Concepts/Sliceable.hpp" +#include "Systems/Block/Block/Concepts/Spannable.hpp" +#include "Systems/Block/Block/Concepts/Subdividable.hpp" +#include "Systems/Block/Block/Concepts/Viewable.hpp" + +//============================================================================== +// +// DOXYGEN DOCUMENTATION +// +//============================================================================== + +//****************************************************************************** +/*!\defgroup block_concepts Block concepts + * \ingroup blocks + * \brief Core concepts for modeling blocks + * + * Contains concepts used in modeling blocks as affordance classes. + */ +//****************************************************************************** diff --git a/backend/src/Systems/Block/Block/Concepts/Gettable.hpp b/backend/src/Systems/Block/Block/Concepts/Gettable.hpp new file mode 100644 index 000000000..0e146c07d --- /dev/null +++ b/backend/src/Systems/Block/Block/Concepts/Gettable.hpp @@ -0,0 +1,212 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +// +#include "Systems/Block/Block/Concepts/Types.hpp" +// +#include "Systems/Block/BlockVariables/TypeTraits.hpp" // IsVariable +// +#include "Utilities/CRTP.hpp" +#include "Utilities/TypeTraits/Cpp20.hpp" +// +#include // size_t + +namespace blocks { + + //**Customization************************************************************* + /*!\name Block get customization + * \brief Customization of get operation + * \ingroup block_concepts + * + * \details + * The get_backend() is a customization point for implementing + * get backend of a block-like type. + * + * Customization is achieved by overloading this function for the target + * block type. If not overload is provided, then a compiler/linker error is + * raised. + * + * \example + * The following shows an example of customizing the slice backend. + * \snippet Transfer/Test_Transfer.cpp customization_eg + * + * \see blocks::get() + */ + //@{ + template + decltype(auto) get_backend(Gettable& block_like); + template + decltype(auto) get_backend(Gettable const& block_like); + template + decltype(auto) get_backend(Gettable&& block_like); + template + decltype(auto) get_backend(Gettable const&& block_like); + //@} + //**************************************************************************** + + //============================================================================ + // + // CLASS DEFINITION + // + //============================================================================ + + //**************************************************************************** + /*!\brief Models the Gettable concept + * \ingroup block_concepts + * + * \details + * The Gettable template class represents the concept of extracting data from + * a block-like data-structure using the blocks::get() method. + * + * \see blocks::slice(), Viewable, BlockSlice + */ + template + class Gettable : public elastica::CRTPHelper { + private: + //**Type definitions******************************************************** + //! CRTP Type + using CRTP = elastica::CRTPHelper; + //************************************************************************** + + public: + //**Self methods************************************************************ + //! CRTP methods + using CRTP::self; + //************************************************************************** + }; + //**************************************************************************** + + namespace detail { + + struct tag_metadata { + //**Type definitions****************************************************** + //! Tag for marking a block variable + using is_a_block_variable = std::true_type; + //! Tag for marking a not block variable + using is_not_a_block_variable = std::false_type; + //! Variable template for determining if VarTag is a block variable + template + using is_block_variable = IsVariable; + //************************************************************************ + }; + + } // namespace detail + + //**************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + template + inline decltype(auto) get_backend( + BlockLike&& block_like, + detail::tag_metadata::is_a_block_variable /* meta*/ + ) noexcept { + return get_backend(std::forward(block_like)); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) get_backend( + BlockLike&& block_like, + detail::tag_metadata::is_not_a_block_variable /* meta*/ + ) noexcept { + // parent is always & or const& + using ParentBlock = cpp20::remove_cvref_t< + decltype(std::forward(block_like).parent())>; + using VariableTag = + typename ParentBlock::VariableMap::template variable_from; + return get_backend(std::forward(block_like)); + } + /*! \endcond */ + //**************************************************************************** + + //============================================================================ + // + // BLOCK GET FUNCTION + // + //============================================================================ + + //**Get functions************************************************************* + /*!\name Get functions*/ + //@{ + + //**************************************************************************** + /*!\brief Extract element from a Gettable datastructure + * \ingroup block_concepts + * + * \details + * Extracts the element on data-structures modeling the Gettable concept. + * Extraction is performed for the Gettable type `gettable` whose element to + * be extracted has a tag type `Tag`. Fails to compile unless the block has + * the `Tag` being extracted. + * + * \usage + * The usage is similar to std::get(), shown below + * \code + Block<...> b; + auto my_tag_data = blocks::get(b); + * \endcode + * + * \tparam Tag Tag to extract + * + * \param block_like The block to extract the tag from + */ + + //**************************************************************************** + /*!\brief Slice operator + * \ingroup block_concepts + * + * \details + * Implements slices on entities modeling Sliceable concept. + * + * \param sliceable Data-structure modeling the Sliceable concept + * \param index The index of the slice (int or from_end) + * + * \example + * Given a block `b`, we can slice it using + * \code + * auto b_slice = block::slice(b, 5UL); // Gets b[5], at index 5 from start + * // Gets b[-2] + * auto b_slice_from_end = block::slice(b, elastica::end - 2UL); // 2 from end + * \endcode + * + * \note + * not marked noexcept because we can have out of range indices + * + * \see blocks::Sliceable , blocks::BlockSlice + */ + + //**************************************************************************** + template + inline decltype(auto) get(Gettable& gettable) noexcept { + return get_backend(gettable.self(), + detail::tag_metadata::is_block_variable{}); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) get(Gettable const& gettable) noexcept { + return get_backend(gettable.self(), + detail::tag_metadata::is_block_variable{}); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) get(Gettable&& gettable) noexcept { + return get_backend(static_cast(gettable), + detail::tag_metadata::is_block_variable{}); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) get(Gettable const&& gettable) noexcept { + return get_backend(static_cast(gettable), + detail::tag_metadata::is_block_variable{}); + } + //**************************************************************************** + +} // namespace blocks diff --git a/backend/src/Systems/Block/Block/Concepts/Sliceable.hpp b/backend/src/Systems/Block/Block/Concepts/Sliceable.hpp new file mode 100644 index 000000000..489e7f846 --- /dev/null +++ b/backend/src/Systems/Block/Block/Concepts/Sliceable.hpp @@ -0,0 +1,285 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +// +#include "Systems/Block/Block/Concepts/Types.hpp" +// +#include "Utilities/CRTP.hpp" +#include "Utilities/End.hpp" +// +#include // size_t + +namespace blocks { + + //**Customization************************************************************* + /*!\name Block slice customization + * \brief Customization of slicing operation + * \ingroup block_concepts + * + * \details + * The slice_backend() is a customization point for implementing + * slicing backend of a block-like type. + * + * Customization is achieved by overloading this function for the target + * block type. If not overload is provided, then a compiler/linker error is + * raised. + * + * \example + * The following shows an example of customizing the slice backend. + * \snippet Transfer/Test_Transfer.cpp customization_eg + * + * \see blocks::slice() + */ + //@{ + template + decltype(auto) slice_backend(Sliceable& block_like, + std::size_t index); + template + decltype(auto) slice_backend(Sliceable const& block_like, + std::size_t index); + template + decltype(auto) slice_backend(Sliceable&& block_like, + std::size_t index); + template + decltype(auto) slice_backend(Sliceable const&& block_like, + std::size_t index); + //@} + //**************************************************************************** + + //**************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + template + inline decltype(auto) slice(Sliceable& sliceable, + std::size_t index); + template + inline decltype(auto) slice(Sliceable& sliceable, + ::elastica::from_end index); + template + inline decltype(auto) slice(Sliceable&& sliceable, + std::size_t index); + template + inline decltype(auto) slice(Sliceable&& sliceable, + ::elastica::from_end index); + template + inline decltype(auto) slice(Sliceable const& sliceable, + std::size_t index); + template + inline decltype(auto) slice(Sliceable const& sliceable, + ::elastica::from_end index); + template + inline decltype(auto) slice(Sliceable const&& sliceable, + std::size_t index); + template + inline decltype(auto) slice(Sliceable const&& sliceable, + ::elastica::from_end index); + /*! \endcond */ + //**************************************************************************** + + //============================================================================ + // + // CLASS DEFINITION + // + //============================================================================ + + //**************************************************************************** + /*!\brief Models the Sliceable concept + * \ingroup block_concepts + * + * \details + * The Sliceable template class represents the concept of slicing a + * block-like data-structure using the blocks::slice() or #operator[] methods. + * It provides a slice into the data held by the parent---and hence acts like + * a ponter to the parents data. This slice cannot be further sliced or + * viewed. + * + * \see blocks::slice(), Viewable, BlockSlice + */ + template + class Sliceable : public elastica::CRTPHelper { + private: + //**Type definitions******************************************************** + //! CRTP Type + using CRTP = elastica::CRTPHelper; + //************************************************************************** + + public: + //**Self methods************************************************************ + //! CRTP methods + using CRTP::self; + //************************************************************************** + + // NOTE : Since this is an interface class, we eschew templates for the + // index types in favor of actual types below, for it to appear nicer in + // Doxygen. + + //**Slice operators********************************************************* + /*!\name slice operators */ + //@{ + + //************************************************************************** + /*!\brief Subscript operator for slicing the block + * + * \param index Index of slice + * \return Slice of the current block + */ + decltype(auto) operator[](std::size_t index) & { + using ::blocks::slice; + return slice(self(), index); + } + //************************************************************************** + + //************************************************************************** + /*!\brief Subscript operator for slicing the block + * + * \param index Index of slice + * \return Slice of the current block + */ + decltype(auto) operator[](::elastica::from_end index) & { + using ::blocks::slice; + return slice(self(), index); + } + //************************************************************************** + + //************************************************************************** + /*!\brief Subscript operator for slicing the block + * + * \param index Index of slice + * \return Slice of the current block + */ + decltype(auto) operator[](std::size_t index) const& { + using ::blocks::slice; + return slice(self(), index); + } + //************************************************************************** + + //************************************************************************** + /*!\brief Subscript operator for slicing the block + * + * \param index Index of slice + * \return Slice of the current block + */ + decltype(auto) operator[](::elastica::from_end index) const& { + using ::blocks::slice; + return slice(self(), index); + } + //************************************************************************** + + //@} + //************************************************************************** + }; + //**************************************************************************** + + //============================================================================ + // + // BLOCK SLICE FUNCTION + // + //============================================================================ + + //**Block slice*************************************************************** + /*!\name Block slice functions*/ + //@{ + + //**************************************************************************** + /*!\brief Slice operator + * \ingroup block_concepts + * + * \details + * Implements slices on entities modeling Sliceable concept. + * + * \param sliceable Data-structure modeling the Sliceable concept + * \param index The index of the slice (int or from_end) + * + * \example + * Given a block `b`, we can slice it using + * \code + * auto b_slice = block::slice(b, 5UL); // Gets b[5], at index 5 from start + * // Gets b[-2] + * auto b_slice_from_end = block::slice(b, elastica::end - 2UL); // 2 from end + * \endcode + * + * \note + * not marked noexcept because we can have out of range indices + * + * \see blocks::Sliceable , blocks::BlockSlice + */ + template + inline decltype(auto) slice(Sliceable& sliceable, + std::size_t index) /*noexcept*/ { + return slice_backend(sliceable.self(), + units_check(sliceable.self(), index)); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice(Sliceable& sliceable, + elastica::from_end index) /*noexcept*/ { + return slice_backend(sliceable.self(), + units_check(sliceable.self(), index)); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice(Sliceable&& sliceable, + std::size_t index) /*noexcept*/ { + return slice_backend( + static_cast(sliceable), + units_check(static_cast(sliceable), index)); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice(Sliceable&& sliceable, + elastica::from_end index) /*noexcept*/ { + return slice_backend( + static_cast(sliceable), + units_check(static_cast(sliceable), index)); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice(Sliceable const& sliceable, + std::size_t index) /*noexcept*/ { + return slice_backend(sliceable.self(), + units_check(sliceable.self(), index)); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice(Sliceable const& sliceable, + elastica::from_end index) /*noexcept*/ { + return slice_backend(sliceable.self(), + units_check(sliceable.self(), index)); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice(Sliceable const&& sliceable, + std::size_t index) /*noexcept*/ { + return slice_backend( + static_cast(sliceable), + units_check(static_cast(sliceable), index)); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice(Sliceable const&& sliceable, + elastica::from_end index) /*noexcept*/ { + return slice_backend( + static_cast(sliceable), + units_check(static_cast(sliceable), index)); + } + //**************************************************************************** + + //@} + //**************************************************************************** + +} // namespace blocks diff --git a/backend/src/Systems/Block/Block/Concepts/Spannable.hpp b/backend/src/Systems/Block/Block/Concepts/Spannable.hpp new file mode 100644 index 000000000..5cb386c32 --- /dev/null +++ b/backend/src/Systems/Block/Block/Concepts/Spannable.hpp @@ -0,0 +1,69 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +// +#include "Systems/Block/Block/Concepts/Types.hpp" +// +#include "Systems/Block/Block/Concepts/Spannable.hpp" +#include "Systems/Block/Block/Concepts/Subdividable.hpp" +#include "Systems/Block/Block/Concepts/Viewable.hpp" +// +#include "Utilities/CRTP.hpp" +#include "Utilities/End.hpp" +// +#include // size_t + +namespace blocks { + + //============================================================================ + // + // CLASS DEFINITION + // + //============================================================================ + + //**************************************************************************** + /*!\brief Models the Spannable concept + * \ingroup block_concepts + * + * \details + * The Spannable template class represents the (composite) concept of a + * spannable block-like data-structure. A Spannable must satisfy the + * requirements of blocks::Subdividable, blocks::Sliceable, blocks::Viewable + */ + template + class Spannable : public elastica::CRTPHelper, + public Subdividable, + public Sliceable, + public Viewable { + private: + //**Type definitions******************************************************** + //! CRTP Type + using CRTP = elastica::CRTPHelper; + //************************************************************************** + + protected: + //**Type definitions******************************************************** + //! Type of subdividable + using SubdividableAffordance = Subdividable; + //! Type of sliceable + using SliceAffordance = Sliceable; + //! Type of viewable + using ViewAffordance = Viewable; + //************************************************************************** + + public: + //**Access operators******************************************************** + /*!\name Access operators */ + //@{ + //! Operator for slicing + using SliceAffordance::operator[]; + //! Operator for viewing + using ViewAffordance::operator[]; + //@} + //************************************************************************** + }; + //**************************************************************************** + +} // namespace blocks diff --git a/backend/src/Systems/Block/Block/Concepts/Subdividable.hpp b/backend/src/Systems/Block/Block/Concepts/Subdividable.hpp new file mode 100644 index 000000000..243054978 --- /dev/null +++ b/backend/src/Systems/Block/Block/Concepts/Subdividable.hpp @@ -0,0 +1,115 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +// +#include "Systems/Block/Block/Concepts/Types.hpp" +// +#include "Utilities/CRTP.hpp" +#include "Utilities/End.hpp" +#include "Utilities/PrettyType.hpp" +// +#include // size_t +#include + +namespace blocks { + + //**Customization************************************************************* + /*!\name Block units customization + * \brief Customization of units + * \ingroup block_concepts + * + * \details + * The n_units() is a customization point for implementing + * units backend of a block-like type. + * + * Customization is achieved by overloading this function for the target + * block type. If not overload is provided, then a compiler/linker error is + * raised. + * + * \example + * The following shows an example of customizing the slice backend. + * \snippet Transfer/Test_Transfer.cpp customization_eg + * + * \see blocks::n_units() + */ + //@{ + template + auto n_units(Subdividable const& block_like) noexcept + -> std::size_t; + //@} + //**************************************************************************** + + //============================================================================ + // + // CLASS DEFINITION + // + //============================================================================ + + //**************************************************************************** + /*!\brief Models the Subdividable concept + * \ingroup block_concepts + * + * \details + * The Subdividable template class represents the concept of a data-structure + * with multiple units/entities. It is useful as a template in supporting + * generation of slices/views from the data. + * + * \see blocks::n_units() + */ + template + class Subdividable : public elastica::CRTPHelper { + private: + //**Type definitions******************************************************** + //! CRTP Type + using CRTP = elastica::CRTPHelper; + //************************************************************************** + + public: + //**Self methods************************************************************ + //! CRTP methods + using CRTP::self; + //************************************************************************** + }; + //**************************************************************************** + + //**Index check support******************************************************* + /*!\name Block index check */ + /*!\brief Checks whether block unit indices are within range of accessible + * values + */ + //@{ + template + auto units_check(Subdividable const& subdividable, + std::size_t index_to_be_sliced) -> std::size_t { + using ::blocks::n_units; + if (index_to_be_sliced < n_units(subdividable.self())) { + return index_to_be_sliced; + } else { + throw std::out_of_range( + "Index to be sliced exceeds the number of units inside a " + + pretty_type::name()); + } + } + + template + inline auto units_check(Subdividable const& subdividable, + elastica::from_end index_to_be_sliced) + -> std::size_t { /* This index cannot be 0 */ + using ::blocks::n_units; + + auto const fe = index_to_be_sliced.i; + auto const un = n_units(subdividable.self()); + if (fe <= un) { + return un - fe; + } else { + throw std::out_of_range( + "Index to be sliced exceeds the number of units inside a " + + pretty_type::name()); + } + } + // @} + //**************************************************************************** + +} // namespace blocks diff --git a/backend/src/Systems/Block/Block/Concepts/Types.hpp b/backend/src/Systems/Block/Block/Concepts/Types.hpp new file mode 100644 index 000000000..508c5e226 --- /dev/null +++ b/backend/src/Systems/Block/Block/Concepts/Types.hpp @@ -0,0 +1,30 @@ +#pragma once + +namespace blocks { + + ////////////////////////////////////////////////////////////////////////////// + // + // Forward declarations of block concepts + // + ////////////////////////////////////////////////////////////////////////////// + + //**************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + template + class Gettable; + + template + class Sliceable; + + template + class Spannable; + + template + class Subdividable; + + template + class Viewable; + /*! \endcond */ + //**************************************************************************** + +} // namespace blocks diff --git a/backend/src/Systems/Block/Block/Concepts/Viewable.hpp b/backend/src/Systems/Block/Block/Concepts/Viewable.hpp new file mode 100644 index 000000000..8f3e5e4eb --- /dev/null +++ b/backend/src/Systems/Block/Block/Concepts/Viewable.hpp @@ -0,0 +1,543 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +// +#include "Systems/Block/Block/Concepts/Types.hpp" +// +#include "Utilities/CRTP.hpp" +#include "Utilities/End.hpp" +// +#include // size_t +#include +#include // logic error + +namespace blocks { + + //**Customization************************************************************* + /*!\name Block slice customization + * \brief Customization of viewing operation + * \ingroup block_concepts + * + * \details + * The slice_backend() is a customization point for implementing + * viewing backend of a block-like type. + * + * Customization is achieved by overloading this function for the target + * block type. If not overload is provided, then a compiler/linker error is + * raised. + * + * \example + * The following shows an example of customizing the slice backend. + * \snippet Transfer/Test_Transfer.cpp customization_eg + * + * \see blocks::slice() + */ + //@{ + template + decltype(auto) slice_backend(Viewable& block_like, + std::size_t start_index, + std::size_t region_size); + template + decltype(auto) slice_backend(Viewable const& block_like, + std::size_t start_index, + std::size_t region_size); + template + decltype(auto) slice_backend(Viewable&& block_like, + std::size_t start_index, + std::size_t region_size); + template + decltype(auto) slice_backend(Viewable const&& block_like, + std::size_t start_index, + std::size_t region_size); + //@} + //**************************************************************************** + + //**************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + // There are 16 combinations here, we only list the most important ones. + template + inline decltype(auto) slice(Viewable& viewable, + std::size_t start_index, std::size_t stop_index); + template + inline decltype(auto) slice(Viewable& viewable, + std::size_t start_index, + ::elastica::from_end stop_index); + template + inline decltype(auto) slice(Viewable& viewable, + ::elastica::from_end start_index, + std::size_t stop_index); + template + inline decltype(auto) slice(Viewable& viewable, + ::elastica::from_end start_index, + ::elastica::from_end stop_index); + /*! \endcond */ + //**************************************************************************** + + //============================================================================ + // + // CLASS DEFINITION + // + //============================================================================ + + //**************************************************************************** + /*!\brief Viewable concept + * \ingroup block_concepts + * + * \details + * The Viewable template class represents the concept of getting a view (i.e. + * a chunk of more than one system) into a + * block-like data-structure using the blocks::slice() or #operator[] methods. + * The view can be further viewed or sliced. + * + * \see blocks::slice(), Sliceable, BlockView + */ + template + class Viewable : public elastica::CRTPHelper { + private: + //**Type definitions******************************************************** + //! CRTP Type + using CRTP = elastica::CRTPHelper; + //************************************************************************** + + public: + //**Self methods************************************************************ + //! CRTP methods + using CRTP::self; + //************************************************************************** + + // NOTE : Since this is an interface class, we eschew templates for the + // index types below in favor of actual types, for it to appear nicer in + // Doxygen. + + //**Slice operators********************************************************* + /*!\name slice operators */ + //@{ + + //************************************************************************** + /*!\brief Subscript operator for viewing the block + * + * \param index The range of indices to view + * \return Slice of the current block + */ + decltype(auto) operator[](std::pair index) & { + using ::blocks::slice; + return slice(self(), index.first, index.second); + } + //************************************************************************** + + //************************************************************************** + /*!\brief Subscript operator for viewing the block + * + * \param index The range of indices to view + * \return Slice of the current block + */ + decltype(auto) operator[]( + std::pair index) & { + using ::blocks::slice; + return slice(self(), index.first, index.second); + } + //************************************************************************** + + //************************************************************************** + /*!\brief Subscript operator for viewing the block + * + * \param index The range of indices to view + * \return Slice of the current block + */ + decltype(auto) operator[]( + std::pair<::elastica::from_end, std::size_t> index) & { + using ::blocks::slice; + return slice(self(), index.first, index.second); + } + //************************************************************************** + + //************************************************************************** + /*!\brief Subscript operator for viewing the block + * + * \param index The range of indices to view + * \return Slice of the current block + */ + decltype(auto) operator[]( + std::pair<::elastica::from_end, ::elastica::from_end> index) & { + using ::blocks::slice; + return slice(self(), index.first, index.second); + } + //************************************************************************** + + //************************************************************************** + /*!\brief Subscript operator for viewing the block + * + * \param index The range of indices to view + * \return Slice of the current block + */ + decltype(auto) operator[]( + std::pair index) const& { + using ::blocks::slice; + return slice(self(), index.first, index.second); + } + //************************************************************************** + + //************************************************************************** + /*!\brief Subscript operator for viewing the block + * + * \param index The range of indices to view + * \return Slice of the current block + */ + decltype(auto) operator[]( + std::pair index) const& { + using ::blocks::slice; + return slice(self(), index.first, index.second); + } + //************************************************************************** + + //************************************************************************** + /*!\brief Subscript operator for viewing the block + * + * \param index The range of indices to view + * \return Slice of the current block + */ + decltype(auto) operator[]( + std::pair<::elastica::from_end, std::size_t> index) const& { + using ::blocks::slice; + return slice(self(), index.first, index.second); + } + //************************************************************************** + + //************************************************************************** + /*!\brief Subscript operator for viewing the block + * + * \param index The range of indices to view + * \return Slice of the current block + */ + decltype(auto) operator[]( + std::pair<::elastica::from_end, ::elastica::from_end> index) const& { + using ::blocks::slice; + return slice(self(), index.first, index.second); + } + //************************************************************************** + + //************************************************************************** + /*!\brief Subscript operator for viewing the block + * + * \param index The range of indices to view + * \return Slice of the current block + */ + decltype(auto) operator[]( + std::pair index) & { + using ::blocks::slice; + return slice(self(), index.first, index.second); + } + //************************************************************************** + + //************************************************************************** + /*!\brief Subscript operator for viewing the block + * + * \param index The range of indices to view + * \return Slice of the current block + */ + decltype(auto) operator[]( + std::pair<::elastica::from_end, decltype(::elastica::end)> index) & { + using ::blocks::slice; + return slice(self(), index.first, index.second); + } + //************************************************************************** + + //@} + //************************************************************************** + }; + //**************************************************************************** + + namespace detail { + + template + decltype(auto) slice_logic(V&& viewable, FI start_index, SI stop_index) { + std::size_t const start_idx = + units_check(std::forward(viewable), start_index); + // workaround to support slices with last index : stop_index - 1 does not + // throw in check(), and once we have the numeric index, we add one to + // recover original index. + std::size_t const stop_idx = + units_check(std::forward(viewable), stop_index - 1UL) + 1UL; + if (not(stop_idx > start_idx)) { + throw std::logic_error( + "Stop index must be greater than the start index"); + } + return slice_backend(std::forward(viewable), start_idx, + stop_idx - start_idx); + } + + } // namespace detail + + //============================================================================ + // + // BLOCK VIEW FUNCTION + // + //============================================================================ + + //**Block view**************************************************************** + /*!\name Block view functions*/ + //@{ + + //**************************************************************************** + /*!\brief View operator + * \ingroup block_concepts + * + * \details + * Implements contiguous views on entities modeling block concept. + * + * \param block_like Data-structure modeling the Viewable concept + * \param start_index The start index of the slice (int or from_end) + * \param stop_index The stop index of the slice (int or from_end) + * + * \example + * Given a block or a view `b`, we can obtain a view into it by using + * \code + * auto b_view = block::slice(b, 5UL, 10UL); // gets b[5, 6, 7, 8, 9] + * // Gets b[end - 5, end - 4, end - 3] + * auto b_view_from_end = block::slice(b, elastica::end - 5UL, elastica::end - + * 2UL); + * \endcode + * + * \note + * not marked noexcept because we can have out of range indices + * + * \note + * Views must have size >= 1 and hence the same indices to start and end will + * raise an error. + * + * \see blocks::Viewable, blocks::BlockView + */ + + template + inline decltype(auto) slice(Viewable& viewable, + std::size_t start_index, + std::size_t stop_index) /*noexcept*/ { + return detail::slice_logic(viewable.self(), start_index, stop_index); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice(Viewable& viewable, + ::elastica::from_end start_index, + std::size_t stop_index) /*noexcept*/ { + return detail::slice_logic(viewable.self(), start_index, stop_index); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice(Viewable& viewable, + std::size_t start_index, + ::elastica::from_end stop_index) /*noexcept*/ { + return detail::slice_logic(viewable.self(), start_index, stop_index); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice(Viewable& viewable, + ::elastica::from_end start_index, + ::elastica::from_end stop_index) /*noexcept*/ { + return detail::slice_logic(viewable.self(), start_index, stop_index); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice( + Viewable& viewable, std::size_t start_index, + decltype(::elastica::end) stop_index) /*noexcept*/ { + return detail::slice_logic(viewable.self(), start_index, stop_index); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice( + Viewable& viewable, ::elastica::from_end start_index, + decltype(::elastica::end) stop_index) /*noexcept*/ { + return detail::slice_logic(viewable.self(), start_index, stop_index); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice(Viewable const& viewable, + std::size_t start_index, + std::size_t stop_index) /*noexcept*/ { + return detail::slice_logic(viewable.self(), start_index, stop_index); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice(Viewable const& viewable, + ::elastica::from_end start_index, + std::size_t stop_index) /*noexcept*/ { + return detail::slice_logic(viewable.self(), start_index, stop_index); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice(Viewable const& viewable, + std::size_t start_index, + ::elastica::from_end stop_index) /*noexcept*/ { + return detail::slice_logic(viewable.self(), start_index, stop_index); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice(Viewable const& viewable, + ::elastica::from_end start_index, + ::elastica::from_end stop_index) /*noexcept*/ { + return detail::slice_logic(viewable.self(), start_index, stop_index); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice( + Viewable const& viewable, std::size_t start_index, + decltype(::elastica::end) stop_index) /*noexcept*/ { + return detail::slice_logic(viewable.self(), start_index, stop_index); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice( + Viewable const& viewable, ::elastica::from_end start_index, + decltype(::elastica::end) stop_index) /*noexcept*/ { + return detail::slice_logic(viewable.self(), start_index, stop_index); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice(Viewable&& viewable, + std::size_t start_index, + std::size_t stop_index) /*noexcept*/ { + return detail::slice_logic(static_cast(viewable), start_index, + stop_index); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice(Viewable&& viewable, + ::elastica::from_end start_index, + std::size_t stop_index) /*noexcept*/ { + return detail::slice_logic(static_cast(viewable), start_index, + stop_index); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice(Viewable&& viewable, + std::size_t start_index, + ::elastica::from_end stop_index) /*noexcept*/ { + return detail::slice_logic(static_cast(viewable), start_index, + stop_index); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice( + Viewable&& viewable, std::size_t start_index, + decltype(::elastica::end) stop_index) /*noexcept*/ { + return detail::slice_logic(static_cast(viewable), start_index, + stop_index); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice( + Viewable&& viewable, ::elastica::from_end start_index, + decltype(::elastica::end) stop_index) /*noexcept*/ { + return detail::slice_logic(static_cast(viewable), start_index, + stop_index); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice(Viewable&& viewable, + ::elastica::from_end start_index, + ::elastica::from_end stop_index) /*noexcept*/ { + return detail::slice_logic(static_cast(viewable), start_index, + stop_index); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice(Viewable const&& viewable, + std::size_t start_index, + std::size_t stop_index) /*noexcept*/ { + return detail::slice_logic(static_cast(viewable), + start_index, stop_index); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice(Viewable const&& viewable, + ::elastica::from_end start_index, + std::size_t stop_index) /*noexcept*/ { + return detail::slice_logic(static_cast(viewable), + start_index, stop_index); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice(Viewable const&& viewable, + std::size_t start_index, + ::elastica::from_end stop_index) /*noexcept*/ { + return detail::slice_logic(static_cast(viewable), + start_index, stop_index); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice(Viewable const&& viewable, + ::elastica::from_end start_index, + ::elastica::from_end stop_index) /*noexcept*/ { + return detail::slice_logic(static_cast(viewable), + start_index, stop_index); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice( + Viewable const&& viewable, std::size_t start_index, + decltype(::elastica::end) stop_index) /*noexcept*/ { + return detail::slice_logic(static_cast(viewable), + start_index, stop_index); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice( + Viewable const&& viewable, ::elastica::from_end start_index, + decltype(::elastica::end) stop_index) /*noexcept*/ { + return detail::slice_logic(static_cast(viewable), + start_index, stop_index); + } + //**************************************************************************** + + //@} + //**************************************************************************** + +} // namespace blocks diff --git a/backend/src/Systems/Block/Block/Metadata.hpp b/backend/src/Systems/Block/Block/Metadata.hpp new file mode 100644 index 000000000..72ae9ea22 --- /dev/null +++ b/backend/src/Systems/Block/Block/Metadata.hpp @@ -0,0 +1,308 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +#include "Systems/Block/Block/TypeTraits.hpp" +#include "Systems/Block/BlockVariables/Types.hpp" +#include "Utilities/PrettyType.hpp" +#include "Utilities/Requires.hpp" +#include "Utilities/StdVectorHelpers.hpp" +#include "Utilities/TMPL.hpp" +#include "Utilities/WidthStream.hpp" // TODO : move into CPP file +// +#include +#include +#include // transform +#include // move iterator +#include // move, pair +#include + +namespace blocks { + + //**************************************************************************** + /*!\brief Variable metadata + * \ingroup blocks + * + * Carries meta-data information about variables in a block plugin + */ + struct VariableMetadata { + using VariableAttribute = std::pair; + using VariableAttributes = std::vector; + VariableAttributes data; + }; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Plugin metadata + * \ingroup blocks + * + * Carries meta-data information about plugins + */ + struct PluginMetadata { + using Metadata = std::vector; + //! Name of plugin + std::string name; + //! Metadata associated with the plugin + Metadata data; + }; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Customization point for generating metadata of plugins + * \ingroup blocks + * + * \details + * Metadata provides the customization point for specifying the + * behavior of blocks::metadata() for any `Plugin` type. By default, \elastica + * generates basic metadata about plugin variables : Metadata can be + * customized to add more attributes to this metadata. + * If no customized implementation is provided, \elastica falls back to this + * default Metadata class, which does a no-operation. + * + * \usage + * The no-operation above uses the static apply() function templated on the + * variable + * \code + * auto attributes = Metadata::template apply(os); + * \endcode + * + * \section customization Metadata customization + * As seen from the example above, the Metadata class needs to + * define a static apply() function with the following signature + * \snippet this apply_signature + * + * Hence to customize Metadata for your own plugin, we rely on template + * specialization, typically done in the translation unit where the Plugin + * type is defined. As an example of implementing Metadata for a + * custom plugin, consider the following example + * + * \example + * With the setup for Plugin shown below + * \snippet Test_Metadata.cpp customized_plugin + * the following code demonstrates the customization of Metadata + * \snippet Test_Metadata.cpp customization_for_plugin + * + * \tparam Plugin Plugin for which blocks::metadata() need to be + * customized + * + * \see Block, blocks::protocols::Plugin, blocks::Variable, blocks::metadata() + */ + template + struct Metadata { + /// [apply_signature] + // VariableAttributes is a std::vector> + template + static inline auto apply() noexcept -> + typename VariableMetadata::VariableAttributes { + return {}; + } + /// [apply_signature] + }; + //**************************************************************************** + + namespace detail { + + using VariableAttributeCollection = + std::vector; + + //************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + template + auto collect_attributes(tmpl::type_) { + typename VariableMetadata::VariableAttributes attributes{}; + + attributes.emplace_back("Name", pretty_type::name()); + attributes.emplace_back("Parameter", + pretty_type::get_name>()); + attributes.emplace_back( + "Initialized", + tt::is_detected_v ? "true" : "false"); + attributes.emplace_back("Rank", pretty_type::get_name>()); + attributes.emplace_back("Type", + pretty_type::get_name()); + return attributes; + } + + template > = nullptr> + auto collect_attributes() -> VariableAttributeCollection { + using Variables = variables_t; + using CustomizationPoint = Metadata; + + VariableAttributeCollection metadata; + + tmpl::for_each([&metadata](auto var) { + auto attributes = collect_attributes(var); + append(attributes, CustomizationPoint::template apply< + tmpl::type_from>()); + metadata.emplace_back(std::move(attributes)); + }); + + return metadata; + } + /*! \endcond */ + //************************************************************************** + + } // namespace detail + + //============================================================================ + // + // FREE FUNCTIONS + // + //============================================================================ + + // TODO : Move to CPP file + //**************************************************************************** + /*!\brief Stream operator for VariableMetaData + */ + inline std::ostream& operator<<(std::ostream& os, + VariableMetadata const& metadata) { + // This is a very simple YAML formatting. + // For a better one, see YAML Archive + for (auto const& data : metadata.data) { + os << data.first << ": " + << "\"" << data.second << "\"" + << "\n"; + } + return os; + } + //**************************************************************************** + + // TODO : Move to CPP file + //**************************************************************************** + /*!\brief Stream operator for PluginMetaData + */ + inline std::ostream& operator<<(std::ostream& os, + PluginMetadata const& metadata) { + // This is a very simple YAML formatting. + // For a better one, see YAML Archive + constexpr std::size_t line_length = 120UL; + ::elastica::widthstream stream{line_length, os}; + + stream << "Plugin: " << metadata.name << "\n"; + const std::string information_key("Variables:"); + + stream << information_key + << (metadata.data.empty() ? " No variables!" : "\n"); + + int constexpr tab_width = 2; + stream.indent(tab_width); + + for (auto const& data : metadata.data) { + stream << "- " + << "\n"; + stream.indent(tab_width); + stream << data << "\n"; + stream.indent(-tab_width); + } + + stream.indent(-tab_width); + return os; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Generates variable information associated with a Plugin + * \ingroup blocks + * + * \example + * \code + * using Plugin = MyCustomPlugin; + * auto meta = blocks::metadata(); + * \endcode + * + * \tparam Plugin A valid plugin template conforming to protocols::Plugin + */ + template + auto metadata() -> PluginMetadata { + auto raw_metadata = detail::collect_attributes(); + PluginMetadata metadata{pretty_type::name(), {}}; + metadata.data.reserve(raw_metadata.size()); + + // Convert vec> -> vec + std::transform(std::make_move_iterator(std::begin(raw_metadata)), + std::make_move_iterator(std::end(raw_metadata)), + std::back_inserter(metadata.data), + [](typename VariableMetadata::VariableAttributes&& meta) { + return VariableMetadata{std::move(meta)}; + }); + return metadata; + } + //**************************************************************************** + + //**Metadata functions******************************************************** + /*!\name Metadata functions */ + //@{ + + //**************************************************************************** + /*!\brief Generates variable information associated with a type modeling the + * block concept + * \ingroup blocks + * + * \example + * \code + * auto block_like = ...; // Models the block concept + * auto meta = blocks::metadata(block_like); + * \endcode + * + * \tparam Plugin A valid plugin template conforming to protocols::Plugin + */ + template + auto metadata(Block const&) -> PluginMetadata { + return metadata(); + } + //**************************************************************************** + + //**************************************************************************** + template + auto metadata(BlockSlice const&) -> PluginMetadata { + return metadata(); + } + //**************************************************************************** + + //**************************************************************************** + template + auto metadata(ConstBlockSlice const&) -> PluginMetadata { + return metadata(); + } + //**************************************************************************** + + //**************************************************************************** + template + auto metadata(BlockView const&) -> PluginMetadata { + return metadata(); + } + //**************************************************************************** + + //**************************************************************************** + template + auto metadata(ConstBlockView const&) -> PluginMetadata { + return metadata(); + } + //**************************************************************************** + + //@} + //**************************************************************************** + + namespace detail { + + // TODO : Move to CPP file + inline auto form_map(PluginMetadata metadata) { + using AttributeKey = std::string; + using Name = std::string; + using InnerMapType = std::unordered_map; + using ReturnType = std::unordered_map; + ReturnType um; + + for (auto& vm : metadata.data) { + auto& dst = um[(*vm.data.begin()).second]; + dst.insert(std::make_move_iterator(vm.data.begin() + 1), + std::make_move_iterator(vm.data.end())); + } + + return um; + } + + } // namespace detail + +} // namespace blocks diff --git a/backend/src/Systems/Block/Block/Protocols.hpp b/backend/src/Systems/Block/Block/Protocols.hpp new file mode 100644 index 000000000..1db4e8524 --- /dev/null +++ b/backend/src/Systems/Block/Block/Protocols.hpp @@ -0,0 +1,93 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** + +#include + +#include "Aliases.hpp" +#include "Systems/Block/BlockVariables/Protocols.hpp" +#include "Utilities/IgnoreUnused.hpp" +#include "Utilities/ProtocolHelpers.hpp" +#include "Utilities/TMPL.hpp" +#include "Utilities/TypeTraits/IsA.hpp" +#include "Utilities/TypeTraits/IsDetected.hpp" + +namespace blocks { + + namespace protocols { + + //========================================================================== + // + // CLASS DEFINITION + // + //========================================================================== + + //************************************************************************** + /*!\brief Block plugin protocol. + * \ingroup block_protocols + * + * Class to enforce adherence to interface expected of a Plugin in blocks. + * Any valid blocks Plugin within the \elastica library should (publicly) + * conform to this class using + * \code + * tt::ConformsTo + * \endcode + * to indicate it qualifies as a block Plugin. Only in case a class + * expresses such conformance, the ::tt::conforms_to and + * ::tt::assert_conforms_to type traits recognizes the class as valid + * Plugin. + * + * Requires that conforming type has the following types: + * \snippet Block/Aliases.hpp variables_t + * which should be a type list and each type within that list in turn + * conforms to protocols::Variable + * + * The following shows an example of minimal conformance to + * protocols::Plugin. + * + * \example + * \snippet Block/Test_Protocols.cpp plugin_protocol_eg + */ + struct Plugin { + //************************************************************************ + /*! \cond ELASTICA_INTERNAL */ + /*!\brief Auxiliary helper struct for enforcing protocols. + // \ingroup protocols + */ + template + struct test { + public: + // Check for nested types of the ConfirmingType + // static_assert(std::is_same::value, "Failure!"); + static_assert(::tt::is_detected_v, + "Not a conforming Block Plugin, doesn't have a " + "nested type called `Variables`"); + using Variables = variables_t; + // the variables_t should be a tmpl::list + static_assert(::tt::is_a_v, + "Not a conforming Block Plugin, nested `Variables` type " + "is not a `tmpl::list`"); + + // and all of its contents should conform to a block variable protocol + template + struct ReportVariableProtocolConformation : std::true_type { + static_assert( + tt::assert_conforms_to, + "Variable called `Var` above does not conform to the protocols " + "expected by a Variable!"); + }; + + using variable_checks IGNORE_UNUSED = decltype( + tmpl::transform>{}); + }; + /*! \endcond */ + //************************************************************************ + }; + //************************************************************************** + + } // namespace protocols + +} // namespace blocks diff --git a/backend/src/Systems/Block/Block/TypeTraits.hpp b/backend/src/Systems/Block/Block/TypeTraits.hpp new file mode 100644 index 000000000..a38e84252 --- /dev/null +++ b/backend/src/Systems/Block/Block/TypeTraits.hpp @@ -0,0 +1,862 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** + +/// +#include "Systems/Block/Block/Protocols.hpp" +#include "Systems/Block/Block/Types.hpp" +/// +#include "Utilities/ProtocolHelpers.hpp" +#include "Utilities/TypeTraits/Cpp17.hpp" +#include "Utilities/TypeTraits/IsA.hpp" + +namespace blocks { + + //============================================================================ + // + // CLASS DEFINITION + // + //============================================================================ + + //**************************************************************************** + /*!\brief Check whether a given type `B` is a Block + * \ingroup block_tt + * + * \details + * Inherits from std::true_type if `B` is a template specialization of a + * blocks::Block, otherwise inherits from std::false_type. + * + * \usage + * For any type `B`, + * \code + * using result = IsBlock; + * \endcode + * + * \metareturns + * cpp17::bool_constant + * + * \semantics + * If the type `B` is an instantiation of elastica::Block, then + * \code + * typename result::type = std::true_type; + * \endcode + * otherwise + * \code + * typename result::type = std::false_type; + * \endcode + * + * \example + * \snippet Block/Test_TypeTraits.cpp is_block_example + * + * \tparam B : the type to check + * + * \see Block + */ + template + struct IsBlock : ::tt::is_a {}; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Auxiliary variable template for the IsBlock type trait. + * \ingroup block_tt + * + * The is_block_v variable template provides a convenient + * shortcut to access the nested \a value of the IsBlock + * class template. For instance, given the type \a T the following two + * statements are identical: + * \example + * \code + * constexpr bool value1 = IsBlock::value; + * constexpr bool value2 = is_block_v; + * \endcode + * + * \tparam B : the type to check + * + * \see IsBlock + */ + template + constexpr bool is_block_v = IsBlock::value; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Check whether a given type `B` is a BlockSlice + * \ingroup block_tt + * + * \details + * Inherits from std::true_type if `B` is a template specialization of a + * blocks::BlockSlice, otherwise inherits from std::false_type. + * + * \usage + * For any type `B`, + * \code + * using result = IsBlockSlice; + * \endcode + * + * \metareturns + * cpp17::bool_constant + * + * \semantics + * If the type `B` is an instantiation of elastica::BlockSlice, then + * \code + * typename result::type = std::true_type; + * \endcode + * otherwise + * \code + * typename result::type = std::false_type; + * \endcode + * + * \example + * \snippet Block/Test_TypeTraits.cpp is_blockslice_example + * + * \tparam B : the type to check + * + * \see BlockSlice + */ + template + struct IsBlockSlice : ::tt::is_a {}; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Auxiliary variable template for the IsBlockSlice type trait. + * \ingroup block_tt + * + * The is_block_slice_v variable template provides a convenient + * shortcut to access the nested \a value of the IsBlockSlice + * class template. For instance, given the type \a T the following two + * statements are identical: + * \example + * \code + * constexpr bool value1 = IsBlockSlice::value; + * constexpr bool value2 = is_block_slice_v; + * \endcode + * + * \tparam B : the type to check + * + * \see IsBlockSlice + */ + template + constexpr bool is_block_slice_v = IsBlockSlice::value; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Check whether a given type `B` is a ConstBlockSlice + * \ingroup block_tt + * + * \details + * Inherits from std::true_type if `B` is a template specialization of a + * blocks::ConstBlockSlice, otherwise inherits from std::false_type. + * + * \usage + * For any type `B`, + * \code + * using result = IsConstBlockSlice; + * \endcode + * + * \metareturns + * cpp17::bool_constant + * + * \semantics + * If the type `B` is an instantiation of elastica::ConstBlockSlice, then + * \code + * typename result::type = std::true_type; + * \endcode + * otherwise + * \code + * typename result::type = std::false_type; + * \endcode + * + * \example + * \snippet Block/Test_TypeTraits.cpp is_const_blockslice_example + * + * \tparam B : the type to check + * + * \see ConstBlockSlice + */ + template + struct IsConstBlockSlice : ::tt::is_a {}; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Auxiliary variable template for the IsConstBlockSlice type trait. + * \ingroup block_tt + * + * The is_const_block_slice_v variable template provides a convenient + * shortcut to access the nested \a value of the IsConstBlockSlice + * class template. For instance, given the type \a T the following two + * statements are identical: + * \example + * \code + * constexpr bool value1 = IsConstBlockSlice::value; + * constexpr bool value2 = is_const_block_slice_v; + * \endcode + * + * \tparam B : the type to check + * + * \see IsBlockSlice + */ + template + constexpr bool is_const_block_slice_v = IsConstBlockSlice::value; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Check whether a given type `B` is a BlockView + * \ingroup block_tt + * + * \details + * Inherits from std::true_type if `B` is a template specialization of a + * blocks::BlockView, otherwise inherits from std::false_type. + * + * \usage + * For any type `B`, + * \code + * using result = IsBlockView; + * \endcode + * + * \metareturns + * cpp17::bool_constant + * + * \semantics + * If the type `B` is an instantiation of elastica::BlockView, then + * \code + * typename result::type = std::true_type; + * \endcode + * otherwise + * \code + * typename result::type = std::false_type; + * \endcode + * + * \example + * \snippet Block/Test_TypeTraits.cpp is_blockview_example + * + * \tparam B : the type to check + * + * \see BlockView + */ + template + struct IsBlockView : ::tt::is_a {}; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Auxiliary variable template for the IsBlockView type trait. + * \ingroup block_tt + * + * The is_block_view_v variable template provides a convenient + * shortcut to access the nested \a value of the IsBlockView + * class template. For instance, given the type \a T the following two + * statements are identical: + * \example + * \code + * constexpr bool value1 = IsBlockView::value; + * constexpr bool value2 = is_block_view_v; + * \endcode + * + * \tparam B : the type to check + * + * \see IsBlockView + */ + template + constexpr bool is_block_view_v = IsBlockView::value; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Check whether a given type `B` is a ConstBlockView + * \ingroup block_tt + * + * \details + * Inherits from std::true_type if `B` is a template specialization of a + * blocks::ConstBlockView, otherwise inherits from std::false_type. + * + * \usage + * For any type `B`, + * \code + * using result = IsConstBlockView; + * \endcode + * + * \metareturns + * cpp17::bool_constant + * + * \semantics + * If the type `B` is an instantiation of elastica::ConstBlockView, then + * \code + * typename result::type = std::true_type; + * \endcode + * otherwise + * \code + * typename result::type = std::false_type; + * \endcode + * + * \example + * \snippet Block/Test_TypeTraits.cpp is_const_blockview_example + * + * \tparam B : the type to check + * + * \see ConstBlockView + */ + template + struct IsConstBlockView : ::tt::is_a {}; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Auxiliary variable template for the IsConstBlockView type trait. + * \ingroup block_tt + * + * The is_const_block_view_v variable template provides a convenient + * shortcut to access the nested \a value of the IsConstBlockView + * class template. For instance, given the type \a T the following two + * statements are identical: + * \example + * \code + * constexpr bool value1 = IsConstBlockView::value; + * constexpr bool value2 = is_const_block_view_v; + * \endcode + * + * \tparam B : the type to check + * + * \see IsBlockView + */ + template + constexpr bool is_const_block_view_v = IsConstBlockView::value; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Check whether a given type `P` is a Plugin type + * \ingroup block_tt + * + * \details + * Inherits from std::true_type if `B` is a Plugin type otherwise inherits + * from std::false_type. + * + * \usage + * For any type `P`, + * \code + * using result = blocks::IsPlugin

; + * \endcode + * + * \metareturns + * cpp17::bool_constant + * + * \semantics + * If the type `P` is marked as a Plugin by inheriting from + * blocks::protocols::Plugin, then + * \code + * typename result::type = std::true_type; + * \endcode + * otherwise + * \code + * typename result::type = std::false_type; + * \endcode + * + * \example + * \snippet Block/Test_TypeTraits.cpp is_plugin_example + * + * \tparam P : the type to check + * + * \see blocks::protocols::Plugin + */ + template + struct IsPlugin : public tt::conforms_to {}; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Auxiliary variable template for the IsPlugin type trait. + * \ingroup block_tt + * + * The is_plugin_v variable template provides a convenient shortcut to access + * the nested `value` of the IsPlugin class template. For instance, given the + * type `T` the following two statements are identical: + * + * \example + * \code + * constexpr bool value1 = IsPlugin::value; + * constexpr bool value2 = is_plugin_v; + * \endcode + * + * \tparam P : the type to check + * + * \see IsPlugin + */ + template + constexpr bool is_plugin_v = IsPlugin

::value; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Check whether a given type `B` models the \a Block concept + * \ingroup block_tt + * + * \details + * Inherits from std::true_type if `B` models the \a Block concept, that is + * `B` is one of blocks::Block, blocks::BlockSlice, otherwise inherits from + * std::false_type. + * + * \usage + * For any type `B`, + * \code + * using result = ModelsBlockConcept; + * \endcode + * + * \metareturns + * cpp17::bool_constant + * + * \semantics + * If the type `B` is an instantiation of either a blocks::Block or a blocks + * ::BlockSlice, then + * \code + * typename result::type = std::true_type; + * \endcode + * otherwise + * \code + * typename result::type = std::false_type; + * \endcode + * + * \example + * \snippet Block/Test_TypeTraits.cpp models_block_concept_example + * + * \tparam B : the type to check + * + * \see Block, BlockSlice, ConstBlockSlice, BlockView + */ + template + struct ModelsBlockConcept + : ::cpp17::disjunction, IsBlockSlice, IsConstBlockSlice, + IsBlockView, IsConstBlockView> {}; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Get the plugin type `P` of a block type `B` + * \ingroup block_tt + * + * \details + * The PluginTrait type trait has a nested member `type` representing the + * plugin `P` when the meta-argument `B` models block concept + * + * \usage + * For any type `B` such that ModelsBlockConcept::type = std::true_type, + * \code + * using result = PluginTrait; + * \endcode + * \metareturns + * the type `T = P` such that `B = X

` where `X` is the type modeling block + * concept + * + * \example + * \snippet Block/Test_TypeTraits.cpp plugin_trait_example + * + * \tparam B Type (modeling block concept) whose `PluginTrait` is to be + * retrieved + * + * \see ModelsBlockConcept + */ + template + struct PluginTrait; + //**************************************************************************** + + //**************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\brief Specialization of PluginTrait for a plain plugin + * \ingroup block_tt + */ + template + struct PluginTrait> { + template