diff --git a/.gitattributes b/.gitattributes index e994a8c4f..f7a70128c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1 @@ -python/akantu/_version.py export-subst +cmake/git_info export-subst diff --git a/.gitignore b/.gitignore index 9d56fc628..6b1ba8e19 100644 --- a/.gitignore +++ b/.gitignore @@ -1,31 +1,34 @@ build* .dir-locals.el TAGS third-party/*/ !third-party/cmake/* !third-party/akantu_iterators !third-party/iohelper *~ release .*.swp *.tar.gz *.tgz *.tbz *.tar.bz2 .idea __pycache__ .mailmap paraview/* *.vtu *.pvd *.pvtu *.vtk compile_commands.json .clangd .iwyu.imp .cache setup.cfg .vscode .auctex* .clangd .ccls-cache +.ccls +VERSION + diff --git a/.gitlab-ci.d/templates.yaml b/.gitlab-ci.d/templates.yaml index b65156cc2..47807cbaa 100644 --- a/.gitlab-ci.d/templates.yaml +++ b/.gitlab-ci.d/templates.yaml @@ -1,135 +1,140 @@ # yaml-language-server: $format.enable=false, $schemaStore.enable=true, $schemas=gitlab-ci # Configuration template .configure: stage: configure variables: BLA_VENDOR: "Generic" CMAKE_GENERATOR: "Unix Makefiles" # CMAKE_GENERATOR: 'Ninja' script: # Create the build folder - cmake -E make_directory build - cd build - echo BUILD_TYPE=${BUILD_TYPE} # Configure the project - cmake -DAKANTU_COHESIVE_ELEMENT:BOOL=TRUE -DAKANTU_IMPLICIT:BOOL=TRUE -DAKANTU_PARALLEL:BOOL=TRUE -DAKANTU_STRUCTURAL_MECHANICS:BOOL=TRUE -DAKANTU_HEAT_TRANSFER:BOOL=TRUE -DAKANTU_DAMAGE_NON_LOCAL:BOOL=TRUE -DAKANTU_PHASE_FIELD:BOOL=TRUE -DAKANTU_PYTHON_INTERFACE:BOOL=TRUE -DAKANTU_CONTACT_MECHANICS:BOOL=TRUE -DAKANTU_EXAMPLES:BOOL=TRUE -DAKANTU_BUILD_ALL_EXAMPLES:BOOL=TRUE -DAKANTU_TESTS:BOOL=TRUE -DAKANTU_RUN_IN_DOCKER:BOOL=TRUE -DAKANTU_TEST_EXAMPLES:BOOL=${TEST_EXAMPLES} -DCMAKE_BUILD_TYPE:STRING=${BUILD_TYPE} -G "${CMAKE_GENERATOR}" .. - # Copie the compile commands for the code quality + # Copy the compile commands for the code quality - if [ -e compile_commands.json ]; then - - cp compile_commands.json .. + - cp compile_commands.json .. - fi artifacts: when: on_success paths: - build - compile_commands.json expire_in: 10h # Build the libraries .build_libs: stage: build_libs script: - echo BUILD_TYPE=${BUILD_TYPE} - cmake --build build --target akantu -j2 > >(tee -a build-${output}-out.log) 2> >(tee -a build-${output}-err.log >&2) - cmake --build build --target py11_akantu -j2 > >(tee -a build-${output}-out.log) 2> >(tee -a build-${output}-err.log >&2) artifacts: when: on_success paths: - build/ - build-${output}-err.log - compile_commands.json expire_in: 10h # build the tests .build_tests: stage: build_tests script: - cmake --build build -j2 > >(tee -a build-${output}-out.log) 2> >(tee -a build-${output}-err.log >&2) artifacts: when: on_success paths: - build/ - build-${output}-err.log - compile_commands.json exclude: - build/**/*.o expire_in: 10h # Build all .build_all: stage: build_libs script: - cmake --build build/src > >(tee -a build-${output}-out.log) 2> >(tee -a build-${output}-err.log >&2) - cmake --build build/python > >(tee -a build-${output}-out.log) 2> >(tee -a build-${output}-err.log >&2) - cmake --build build/test/ > >(tee -a build-${output}-out.log) 2> >(tee -a build-${output}-err.log >&2) - cmake --build build/examples > >(tee -a build-${output}-out.log) 2> >(tee -a build-${output}-err.log >&2) artifacts: when: on_success paths: - build/ - build-${output}-err.log - compile_commands.json exclude: - build/**/*.o expire_in: 10h # Run the tests .tests: stage: test script: - cd build - ctest -T test --output-on-failure --no-compress-output --timeout 1800 after_script: - cd build - tag=$(head -n 1 < Testing/TAG) - if [ -e Testing/${tag}/Test.xml ]; then - xsltproc -o ./juint.xml ${CI_PROJECT_DIR}/test/ci/ctest2junit.xsl Testing/${tag}/Test.xml; - fi - if [ ${BUILD_TYPE} = "Coverage" ]; then - gcovr --xml --gcov-executable "${GCOV_EXECUTABLE}" + --xml-pretty + --exclude-unreachable-branches + --print-summary --output coverage.xml --object-directory ${CI_PROJECT_DIR}/build --root ${CI_PROJECT_DIR} -s || true - fi artifacts: when: always + expire_in: 2 days paths: - build/juint.xml - build/coverage.xml reports: junit: - build/juint.xml - cobertura: - - build/coverage.xml + coverage_report: + coverage_format: cobertura + path: build/coverage.xml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3e3829484..95ed3cec7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,350 +1,375 @@ # yaml-language-server: $schema=gitlab-ci # yaml-language-server: $format.enable=false stages: + - version - configure - build_libs - build_tests - test - code_quality - deploy include: local: '.gitlab-ci.d/*.yaml' #------------------------------------------------------------------------------- # Rebuilding the docker images if needed #------------------------------------------------------------------------------- docker build:debian-bullseye: variables: IMAGE_NAME: debian:bullseye extends: .docker_build docker build:ubuntu-lts: variables: IMAGE_NAME: ubuntu:lts extends: .docker_build docker build:manylinux: variables: IMAGE_NAME: manylinux:2010_x86_64 extends: .docker_build +version_determination: + stage: version + image: python:latest + variables: + GIT_STRATEGY: fetch + GIT_DEPTH: 0 + script: + - python3 cmake/semver.py > VERSION + - cat VERSION + artifacts: + when: on_success + paths: + - VERSION + expire_in: 1day # ------------------------------------------------------------------------------ # Debian bullseye compiled with GCC # ------------------------------------------------------------------------------ configure:debian_bullseye_gcc: extends: - .debian_bullseye_gcc - .build_release - .configure - + needs: + - version_determination + build:debian_bullseye_gcc: extends: - .debian_bullseye_gcc - .build_release - .build_all needs: - job: configure:debian_bullseye_gcc test:debian_bullseye_gcc: extends: - .debian_bullseye_gcc - .build_release - .tests coverage: '/^lines: (\d+\.\d+\%)/' needs: - job: build:debian_bullseye_gcc # ------------------------------------------------------------------------------ # Debian bullseye compiled with Clang # ------------------------------------------------------------------------------ configure:debian_bullseye_clang: extends: - .debian_bullseye_clang - .build_release - .configure + needs: + - version_determination build:debian_bullseye_clang: extends: - .debian_bullseye_clang - .build_release - .build_all needs: - job: configure:debian_bullseye_clang test:debian_bullseye_clang: extends: - .debian_bullseye_clang - .build_release - .tests coverage: '/^lines: (\d+\.\d+\%)/' needs: - job: build:debian_bullseye_clang # ------------------------------------------------------------------------------ # Ubuntu LTS compiled with GCC # ------------------------------------------------------------------------------ configure:ubuntu_lts_gcc: extends: - .ubuntu_lts_gcc - .build_release - .configure + needs: + - version_determination build:ubuntu_lts_gcc: extends: - .ubuntu_lts_gcc - .build_release - .build_all needs: - job: configure:ubuntu_lts_gcc test:ubuntu_lts_gcc: extends: - .ubuntu_lts_gcc - .build_release - .tests needs: - job: build:ubuntu_lts_gcc # ------------------------------------------------------------------------------ # Debian bullseye compiled with GCC tested with valgrind # ------------------------------------------------------------------------------ configure:ubuntu_lts_gcc_valgrind: extends: - .ubuntu_lts_gcc - .build_valgrind - .configure + needs: + - version_determination build:ubuntu_lts_gcc_valgrind: extends: - .ubuntu_lts_gcc - .build_valgrind - .build_all needs: - job: configure:ubuntu_lts_gcc_valgrind test:ubuntu_lts_gcc_valgrind: extends: - .ubuntu_lts_gcc - .build_valgrind - .tests needs: - job: build:ubuntu_lts_gcc_valgrind - + # ------------------------------------------------------------------------------ # Manylinux to build python packages # ------------------------------------------------------------------------------ configure:python_package: stage: configure extends: - .manylinux_2010_x64_gcc - .build_release script: # create the build folder - cmake -E make_directory build - cd build # Variables for cmake - export CMAKE_PREFIX_PATH=/softs/view - export BOOST_ROOT=/softs/view # Configure in sequential and without tests or examples - cmake -DAKANTU_COHESIVE_ELEMENT:BOOL=TRUE -DAKANTU_IMPLICIT:BOOL=TRUE -DAKANTU_PARALLEL:BOOL=FALSE -DAKANTU_STRUCTURAL_MECHANICS:BOOL=TRUE -DAKANTU_HEAT_TRANSFER:BOOL=TRUE -DAKANTU_DAMAGE_NON_LOCAL:BOOL=TRUE -DAKANTU_PHASE_FIELD:BOOL=TRUE -DAKANTU_PYTHON_INTERFACE:BOOL=FALSE -DAKANTU_CONTACT_MECHANICS:BOOL=TRUE -DAKANTU_EXAMPLES:BOOL=FALSE -DAKANTU_TESTS:BOOL=FALSE -DMUMPS_DETECT_DEBUG:BOOL=TRUE -DCMAKE_INSTALL_PREFIX:PATH=${CI_PROJECT_DIR}/install -DCMAKE_BUILD_TYPE:STRING=${BUILD_TYPE} .. + needs: + - version_determination artifacts: when: on_success paths: - build/ + - VERSION expire_in: 10h build_akantu:python_package: extends: - .build_libs - .build_release - .manylinux_2010_x64_gcc - script: - stage: build_libs script: - cmake --build build --target akantu -j1 - cmake --install build artifacts: when: on_success paths: - install/ + - VERSION expire_in: 10h needs: - job: configure:python_package build_pip:python_package: stage: build_tests extends: - .build_release - .manylinux_2010_x64_gcc script: - export CI_AKANTU_INSTALL_PREFIX=${CI_PROJECT_DIR}/install - export CMAKE_PREFIX_PATH=/softs/view:${CI_AKANTU_INSTALL_PREFIX} - test/ci/make-wheels.sh needs: - job: build_akantu:python_package artifacts: when: on_success paths: - wheelhouse expire_in: 10h test:python_package: stage: test image: python:3.8 needs: - job: build_pip:python_package script: - pip install numpy scipy - pip install akantu --no-index --find-links=${PWD}/wheelhouse - python -c "import akantu" - cd examples/python/dynamics/ - apt update && apt install -y gmsh - gmsh -2 bar.geo - python ./dynamics.py package:python_gitlab: stage: deploy image: python:latest script: - pip install twine - TWINE_PASSWORD=${CI_JOB_TOKEN} TWINE_USERNAME=gitlab-ci-token python3 -m twine upload --repository-url https://gitlab.com/api/v4/projects/${CI_PROJECT_ID}/packages/pypi wheelhouse/* needs: - job: build_pip:python_package - job: test:python_package only: - master package:python_pypi: stage: deploy image: python:latest script: - pip install twine - TWINE_PASSWORD=${PYPI_TOKEN} TWINE_USERNAME=__token__ python3 -m twine upload --verbose wheelhouse/* needs: - job: build_pip:python_package - job: test:python_package only: - tags # ------------------------------------------------------------------------------ # Code Quality # ------------------------------------------------------------------------------ cq:code_quality: extends: - .code_quality_gitlab_template needs: - job: build:debian_bullseye_clang artifacts: paths: - gl-code-quality-report.json cq:clang_tidy: extends: - .clang_tools script: - test/ci/scripts/cq -x third-party -x extra-packages -x pybind11 -x test + -x build ${FILE_LIST_ARG} clang-tidy -p ${CI_PROJECT_DIR}/build > gl-clang-tidy-report.json needs: - job: build:debian_bullseye_clang artifacts: paths: - gl-clang-tidy-report.json cq:clang_format: extends: - .clang_tools script: - test/ci/scripts/cq -x third-party -x extra-packages + -x build clang-format -p ${CI_PROJECT_DIR}/build > gl-clang-format-report.json needs: - job: build:debian_bullseye_clang artifacts: paths: - gl-clang-format-report.json cq:compilation_warnings: stage: code_quality image: python:latest script: - pip install warning-parser termcolor Click - ls build-*-err.log - test/ci/scripts/cq -x third-party -x extra-packages + -x build warnings build-*-err.log > gl-warnings-report.json needs: - job: build:debian_bullseye_clang - job: build:debian_bullseye_gcc - job: build:ubuntu_lts_gcc artifacts: paths: - gl-warnings-report.json cq:merge_code_quality: stage: deploy extends: - .debian_bullseye_clang script: - jq -Ms '[.[][]]' gl-*-report.json | tee gl-codequality.json | jq -C needs: - job: cq:code_quality - job: cq:clang_tidy - job: cq:clang_format - job: cq:compilation_warnings - artifacts: - paths: - - gl-codequality.json artifacts: reports: codequality: [gl-codequality.json] # ------------------------------------------------------------------------------ # Deploy pages # ------------------------------------------------------------------------------ pages: stage: deploy extends: - .debian_bullseye_gcc script: - cd build - cmake -DAKANTU_DOCUMENTATION=ON .. - cmake --build . -t sphinx-doc - mv doc/dev-doc/html ../public needs: - job: build:debian_bullseye_gcc artifacts: paths: - public only: - master diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ca64da1a..afbecb4c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,219 +1,214 @@ #=============================================================================== # @file CMakeLists.txt # # @author Guillaume Anciaux # @author Nicolas Richart # # @date creation: Mon Jun 14 2010 # @date last modification: Sat Mar 13 2021 # # @brief main configuration file # # # @section LICENSE # # Copyright (©) 2010-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) # Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) # # Akantu is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # # Akantu 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License along # with Akantu. If not, see . # # @section DESCRIPTION #------------------------------------------------------------------------------- # _ _ # | | | | # __ _| | ____ _ _ __ | |_ _ _ # / _` | |/ / _` | '_ \| __| | | | # | (_| | < (_| | | | | |_| |_| | # \__,_|_|\_\__,_|_| |_|\__|\__,_| # #=============================================================================== #=============================================================================== # CMake Project #=============================================================================== cmake_minimum_required(VERSION 3.5.1) # add this options before PROJECT keyword set(CMAKE_DISABLE_SOURCE_CHANGES ON) set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) if(CMAKE_VERSION VERSION_GREATER 3.12) cmake_policy(SET CMP0074 NEW) endif() set(AKANTU_COPYRIGHT "2010-2021, EPFL (Ecole Polytechnique Fédérale de Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)") set(AKANTU_MAINTAINER "Nicolas Richart") set(AKANTU_HOMEPAGE_URL "https://akantu.ch") if(CMAKE_VERSION VERSION_GREATER 3.12) project(Akantu HOMEPAGE_URL "https://akantu.ch") else() project(Akantu) endif() enable_language(CXX) #=============================================================================== # Misc. config for cmake #=============================================================================== set(AKANTU_CMAKE_DIR "${PROJECT_SOURCE_DIR}/cmake") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/Modules") set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE BOOL "Enable/Disable output of compile commands during generation" FORCE) mark_as_advanced(BUILD_SHARED_LIBS) if(NOT AKANTU_TARGETS_EXPORT) set(AKANTU_TARGETS_EXPORT AkantuTargets) endif() include(CMakeVersionGenerator) include(CMakePackagesSystem) include(CMakeFlagsHandling) include(AkantuPackagesSystem) include(AkantuMacros) include(AkantuCleaning) #cmake_activate_debug_message() include(GNUInstallDirs) set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") # add the automatically determined parts of the RPATH # which point to directories outside the build tree to the install RPATH set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) #=============================================================================== # Version Number #=============================================================================== # AKANTU version number. define_project_version() -if(NOT AKANTU_BYPASS_AKANTU_TARGET) - configure_file("cmake/akantu_version.py.in" - "${PROJECT_BINARY_DIR}/akantu_version.py" - @ONLY) -endif() #=============================================================================== # Options #=============================================================================== option(AKANTU_EXAMPLES "Activate examples" OFF) option(AKANTU_TESTS "Activate tests" OFF) option(AKANTU_SHARED "Build Akantu as a shared library" ON) option(AKANTU_POSITION_INDEPENDENT "Build with -fPIC when static" ON) option(AKANTU_RUN_IN_DOCKER "Set the approriate flage tu run in docker" OFF) set(AKANTU_PREFERRED_PYTHON_VERSION 3 CACHE STRING "Preferred version for python related things") mark_as_advanced( AKANTU_PREFERRED_PYTHON_VERSION AKANTU_RUN_IN_DOCKER AKANTU_POSITION_INDEPENDENT AKANTU_SHARED ) if (AKANTU_SHARED) set(BUILD_SHARED_LIBS ON CACHE BOOL "Build shared libraries.") else() set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared libraries.") set(CMAKE_POSITION_INDEPENDENT_CODE ON) endif() include(AkantuExtraCompilationProfiles) #=============================================================================== # Dependencies #=============================================================================== declare_akantu_types() package_list_packages(${PROJECT_SOURCE_DIR}/packages EXTRA_PACKAGES_FOLDER ${PROJECT_SOURCE_DIR}/extra_packages NO_AUTO_COMPILE_FLAGS) #=============================================================================== # Akantu library #=============================================================================== if (NOT AKANTU_BYPASS_AKANTU_TARGET) add_subdirectory(src) else() find_package(Akantu REQUIRED) if (Akantu_FOUND) get_target_property(_lib akantu INTERFACE_LINK_LIBRARIES) message(STATUS "Found Akantu: ${_lib} (found version ${AKANTU_VERSION})") endif() endif() #=============================================================================== # Documentation #=============================================================================== if(AKANTU_DOCUMENTATION OR AKANTU_DOCUMENTATION_MANUAL) add_subdirectory(doc) else() set(AKANTU_DOC_EXCLUDE_FILES "${PROJECT_SOURCE_DIR}/doc/manual" CACHE INTERNAL "") endif() #=============================================================================== # Python interface #=============================================================================== package_is_activated(python_interface _python_act) if(_python_act) include(AkantuNeedPybind11) if(IS_ABSOLUTE "${CMAKE_INSTALL_PREFIX}") set(AKANTU_PYTHON_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}) else() set(AKANTU_PYTHON_INSTALL_PREFIX "${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_PREFIX}") endif() add_subdirectory(python) endif() #=============================================================================== # Examples and tests #=============================================================================== include(AkantuTestsMacros) include(AkantuExampleMacros) if(AKANTU_TESTS) include(AkantuNeedPybind11) option(AKANTU_BUILD_ALL_TESTS "Build all tests" ON) find_package(GMSH REQUIRED) endif() # tests add_test_tree(test) if(AKANTU_EXAMPLES) if(AKANTU_TESTS) option(AKANTU_TEST_EXAMPLES "Run the examples" ON) endif() find_package(GMSH REQUIRED) add_subdirectory(examples) endif() #=============================================================================== # Install and Packaging #=============================================================================== if (NOT AKANTU_BYPASS_AKANTU_TARGET) include(AkantuInstall) option(AKANTU_DISABLE_CPACK "This option commands the generation of extra info for the \"make package\" target" ON) mark_as_advanced(AKANTU_DISABLE_CPACK) if(NOT AKANTU_DISABLE_CPACK) include(AkantuCPack) endif() endif() diff --git a/README.md b/README.md index 8c78c41fc..eb882ca16 100644 --- a/README.md +++ b/README.md @@ -1,93 +1,94 @@ # `Akantu`: Swiss-Made Open-Source Finite-Element Library `Akantu` means a little element in Kinyarwanda, a Bantu language. From now on it is also an open- source object-oriented library which has the ambi- tion to be generic and efficient. # Building `Akantu` ## Dependencies In order to compile `Akantu` any compiler supporting fully C++14 should work. In addition some libraries are required: - CMake (>= 3.5.1) - Boost (preprocessor and Spirit) - zlib - blas/lapack For the python interface: - Python (>=3 is recommended) - pybind11 (if not present the build system will try to download it) To run parallel simulations: - MPI - Scotch To use the static or implicit dynamic solvers at least one of the following libraries is needed: - MUMPS (since this is usually compiled in static you also need MUMPS dependencies) - PETSc To compile the tests and examples: - Gmsh - google-test (if not present the build system will try to download it) ### On `.deb` based systems ``` sh -> sudo apt install cmake libboost-dev libzlib-dev liblapack3 gmsh +> sudo apt install cmake libboost-dev zlib1g-dev liblapack-dev libblas-dev gmsh # For parallel > sudo apt install mpi-default-dev libmumps-dev # For sequential > sudo apt install libmumps-seq-dev ``` ## Configuring and compilation `Akantu` is a [CMake](https://cmake.org/) project, so to configure it, you can follow the usual way: ``` sh > cd akantu > mkdir build > cd build > ccmake .. [ Set the options that you need ] > make > make install + ``` ## Using the python interface -You can install ``Akantu`` using pip: +You can install ``Akantu`` using pip, this will install a pre-compiled version: ``` sh > pip install akantu ``` You can then import the package in a python script as: ``` python import akantu ``` The python API is similar to the C++ one. If you encounter any problem with the python interface, you are welcome to do a merge request or post an issue on [GitLab](https://gitlab.com/akantu/akantu/-/issues). # Tutorials with the python interface To help getting started, multiple tutorials using the python interface are available as notebooks with pre-installed version of `Akantu` on Binder. The following tutorials are currently available: [Plate whith a hole loaded](https://mybinder.org/v2/git/https%3A%2F%2Fgitlab.com%2Fakantu%2Ftutorials.git/HEAD?filepath=plate-hole/plate-hole.ipynb) [Loaded cohesive crack](https://mybinder.org/v2/git/https%3A%2F%2Fgitlab.com%2Fakantu%2Ftutorials.git/HEAD?filepath=cohesive-fracture/cohesive-fracture.ipynb) [Making your constitutive law in python](https://mybinder.org/v2/git/https%3A%2F%2Fgitlab.com%2Fakantu%2Ftutorials.git/HEAD?filepath=constitutive-laws/python_constitutive_law.ipynb) diff --git a/VERSION b/VERSION deleted file mode 100644 index fcdb2e109..000000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -4.0.0 diff --git a/cmake/AkantuConfigVersion.cmake.in b/cmake/AkantuConfigVersion.cmake.in deleted file mode 100644 index db1b45bef..000000000 --- a/cmake/AkantuConfigVersion.cmake.in +++ /dev/null @@ -1,42 +0,0 @@ -#=============================================================================== -# @file AkantuConfigVersion.cmake.in -# -# @author Nicolas Richart -# -# @date creation: Thu Dec 01 2011 -# @date last modification: Sun Oct 19 2014 -# -# @brief configures akantu version for cmake -# -# @section LICENSE -# -# Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de -# Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des -# Solides) -# -# Akantu is free software: you can redistribute it and/or modify it under the -# terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 3 of the License, or (at your option) any -# later version. -# -# Akantu 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 Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with Akantu. If not, see . -# -#=============================================================================== - -set(PACKAGE_VERSION "@AKANTU_VERSION@") - -# Check whether the requested PACKAGE_FIND_VERSION is compatible -if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") - set(PACKAGE_VERSION_COMPATIBLE FALSE) -else() - set(PACKAGE_VERSION_COMPATIBLE TRUE) - if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}") - set(PACKAGE_VERSION_EXACT TRUE) - endif() -endif() diff --git a/cmake/AkantuInstall.cmake b/cmake/AkantuInstall.cmake index 796331f6f..f05328181 100644 --- a/cmake/AkantuInstall.cmake +++ b/cmake/AkantuInstall.cmake @@ -1,164 +1,163 @@ #=============================================================================== # @file AkantuInstall.cmake # # @author Nicolas Richart # # @date creation: Wed Oct 17 2012 # @date last modification: Fri Jan 15 2021 # # @brief Create the files that allows users to link with Akantu in an other # cmake project # # # @section LICENSE # # Copyright (©) 2010-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) # Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) # # Akantu is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # # Akantu 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License along # with Akantu. If not, see . # #=============================================================================== #=============================================================================== # Config gen for external packages #=============================================================================== configure_file(cmake/AkantuBuildTreeSettings.cmake.in "${PROJECT_BINARY_DIR}/AkantuBuildTreeSettings.cmake" @ONLY) file(WRITE "${PROJECT_BINARY_DIR}/AkantuConfigInclude.cmake" " #=============================================================================== # @file AkantuConfigInclude.cmake # @author Nicolas Richart # @date Fri Jun 11 09:46:59 2010 # # @section LICENSE # # Copyright (©) 2010-2011 EPFL (Ecole Polytechnique Fédérale de Lausanne) # Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) # # Akantu is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # # Akantu 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with Akantu. If not, see . # # @section DESCRIPTION # #=============================================================================== ") package_get_all_packages(_package_list) foreach(_pkg_name ${_package_list}) # package_pkg_name(${_option} _pkg_name) _package_is_activated(${_pkg_name} _acctivated) _package_get_real_name(${_pkg_name} _real_name) string(TOUPPER ${_real_name} _real_pkg_name) file(APPEND "${PROJECT_BINARY_DIR}/AkantuConfigInclude.cmake" " set(AKANTU_HAS_${_real_pkg_name} ${_acctivated})") _package_get_libraries(${_pkg_name} _libs) if(_libs) file(APPEND "${PROJECT_BINARY_DIR}/AkantuConfigInclude.cmake" " set(AKANTU_${_real_pkg_name}_LIBRARIES ${_libs})") endif() _package_get_include_dir(${_pkg_name} _incs) if(_incs) file(APPEND "${PROJECT_BINARY_DIR}/AkantuConfigInclude.cmake" " set(AKANTU_${_real_pkg_name}_INCLUDE_DIR ${_incs}) ") endif() _package_get_compile_flags(${_pkg_name} CXX _compile_flags) if(_compile_flags) file(APPEND "${PROJECT_BINARY_DIR}/AkantuConfigInclude.cmake" " set(AKANTU_${_real_pkg_name}_COMPILE_CXX_FLAGS ${_compile_flags}) ") endif() endforeach() file(APPEND "${PROJECT_BINARY_DIR}/AkantuConfigInclude.cmake" " set(AKANTU_EXTRA_CXX_FLAGS \"${AKANTU_EXTRA_CXX_FLAGS}\") ") # Create the AkantuConfig.cmake and AkantuConfigVersion files get_filename_component(CONF_REL_INCLUDE_DIR "${CMAKE_INSTALL_PREFIX}" ABSOLUTE) configure_file(cmake/AkantuConfig.cmake.in "${PROJECT_BINARY_DIR}/AkantuConfig.cmake" @ONLY) -#configure_file(cmake/AkantuConfigVersion.cmake.in "${PROJECT_BINARY_DIR}/AkantuConfigVersion.cmake" @ONLY) configure_file(cmake/AkantuUse.cmake "${PROJECT_BINARY_DIR}/AkantuUse.cmake" COPYONLY) package_is_activated(pybind11 _is_pybind11_activated) package_is_activated(swig _is_swig_activated) configure_file(cmake/akantu_environement.sh.in ${PROJECT_BINARY_DIR}/akantu_environement.sh @ONLY) configure_file(cmake/akantu_environement.csh.in ${PROJECT_BINARY_DIR}/akantu_environement.csh @ONLY) include(GNUInstallDirs) package_is_activated(python_interface _is_activated) if(_is_activated) find_package(PythonInterp ${AKANTU_PREFERRED_PYTHON_VERSION}) configure_file(cmake/akantu_install_environement.sh.in ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/akantu_environement.sh @ONLY) configure_file(cmake/akantu_install_environement.csh.in ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/akantu_environement.csh @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/akantu_environement.sh ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/akantu_environement.csh DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/akantu${AKANTU_VERSION}) endif() include(CMakePackageConfigHelpers) configure_package_config_file(cmake/AkantuConfig.cmake.in "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" INSTALL_DESTINATION lib/cmake/${PROJECT_NAME} ) write_basic_package_version_file(${PROJECT_BINARY_DIR}/AkantuConfigVersion.cmake VERSION ${AKANTU_VERSION} COMPATIBILITY SameMajorVersion) # Install the export set for use with the install-tree install(FILES ${PROJECT_SOURCE_DIR}/cmake/Modules/FindScaLAPACK.cmake ${PROJECT_SOURCE_DIR}/cmake/Modules/FindMETIS.cmake ${PROJECT_SOURCE_DIR}/cmake/Modules/FindParMETIS.cmake ${PROJECT_SOURCE_DIR}/cmake/Modules/FindPETSc.cmake ${PROJECT_SOURCE_DIR}/cmake/Modules/FindMumps.cmake ${PROJECT_SOURCE_DIR}/cmake/Modules/FindScotch.cmake ${PROJECT_SOURCE_DIR}/cmake/Modules/FindGMSH.cmake ${PROJECT_BINARY_DIR}/AkantuConfig.cmake ${PROJECT_BINARY_DIR}/AkantuConfigInclude.cmake ${PROJECT_BINARY_DIR}/AkantuConfigVersion.cmake ${PROJECT_SOURCE_DIR}/cmake/AkantuUse.cmake ${PROJECT_SOURCE_DIR}/cmake/AkantuSimulationMacros.cmake ${PROJECT_SOURCE_DIR}/cmake/Modules/FindGMSH.cmake DESTINATION lib/cmake/${PROJECT_NAME} COMPONENT dev) diff --git a/cmake/AkantuPackagesSystem.cmake b/cmake/AkantuPackagesSystem.cmake index 146937dfc..e26ebcda4 100644 --- a/cmake/AkantuPackagesSystem.cmake +++ b/cmake/AkantuPackagesSystem.cmake @@ -1,355 +1,354 @@ #=============================================================================== # @file AkantuPackagesSystem.cmake # # @author Nicolas Richart # # @date creation: Sat Jul 18 2015 # @date last modification: Fri Mar 16 2018 # # @brief Addition to the PackageSystem specific for Akantu # # # @section LICENSE # # Copyright (©) 2015-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) # Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) # # Akantu is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # # Akantu 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License along # with Akantu. If not, see . # #=============================================================================== #=============================================================================== # Element Types #=============================================================================== #------------------------------------------------------------------------------- function(package_declare_elements pkg) set(_options KIND ELEMENT_TYPES GEOMETRICAL_TYPES INTERPOLATION_TYPES GEOMETRICAL_SHAPES GAUSS_INTEGRATION_TYPES INTERPOLATION_KIND FE_ENGINE_LISTS ) cmake_parse_arguments(_opt_pkg "" "" "${_options}" ${ARGN}) foreach(_opt ${_options}) if(_opt_pkg_${_opt}) package_set_variable(ET_${_opt} ${pkg} ${_opt_pkg_${_opt}}) endif() endforeach() endfunction() #------------------------------------------------------------------------------- function(_transfer_list_to_enum types enum) if(${enum}) set(_enum_tmp ${${enum}}) else() unset(_enum_tmp) endif() foreach(_type ${${types}}) # defining the types for the enum if(DEFINED _enum_tmp) set(_enum_tmp "${_enum_tmp} ${_type},") else() set(_enum_tmp "${_type},") endif() endforeach() set(${enum} ${_enum_tmp} PARENT_SCOPE) endfunction() #------------------------------------------------------------------------------- function(_transfer_list_to_boost_seq types boost_seq) if(${boost_seq}) set(_boost_seq_tmp ${${boost_seq}}) endif() foreach(_type ${${types}}) if(DEFINED _boost_seq_tmp) set(_boost_seq_tmp "${_boost_seq_tmp}${_tabs}\\ (${_type})") else() set(_boost_seq_tmp " (${_type})") endif() string(LENGTH "${_type}" _length) if(_length LESS 13) set(_tabs "\t\t\t\t") elseif(_length LESS 28) set(_tabs "\t\t\t") else() set(_tabs "\t\t") endif() endforeach() set(${boost_seq} ${_boost_seq_tmp} PARENT_SCOPE) endfunction() #------------------------------------------------------------------------------- function(package_get_element_lists) package_get_all_activated_packages(_activated_list) set(_lists KIND ELEMENT_TYPES GEOMETRICAL_TYPES INTERPOLATION_TYPES GEOMETRICAL_SHAPES GAUSS_INTEGRATION_TYPES INTERPOLATION_TYPES INTERPOLATION_KIND FE_ENGINE_LISTS ) set(_element_kind "#define AKANTU_ELEMENT_KIND") set(_all_element_types "#define AKANTU_ALL_ELEMENT_TYPE\t") set(_inter_types_boost_seq "#define AKANTU_INTERPOLATION_TYPES\t\t") foreach(_pkg_name ${_activated_list}) foreach(_list ${_lists}) string(TOLOWER "${_list}" _l_list) _package_get_variable(ET_${_list} ${_pkg_name} _${_l_list}) _transfer_list_to_enum(_${_l_list} _${_l_list}_enum) endforeach() if(_interpolation_types) _transfer_list_to_boost_seq(_interpolation_types _inter_types_boost_seq) endif() if(_kind) string(TOUPPER "${_kind}" _u_kind) if(_element_types) set(_boosted_element_types "${_boosted_element_types} #define AKANTU_ek_${_kind}_ELEMENT_TYPE\t") _transfer_list_to_boost_seq(_element_types _boosted_element_types) set(_boosted_element_types "${_boosted_element_types}\n") # defininf the kinds variables set(_element_kinds "${_element_kinds} #define AKANTU_${_u_kind}_KIND\t(_ek_${_kind})") # defining the full list of element set(_all_element_types "${_all_element_types}\t\\ AKANTU_ek_${_kind}_ELEMENT_TYPE") endif() # defining the full list of kinds set(_element_kind "${_element_kind}${_kind_tabs}\t\t\\ AKANTU_${_u_kind}_KIND") set(_kind_tabs "\t") # defining the macros set(_boost_macros "${_boost_macros} #define AKANTU_BOOST_${_u_kind}_ELEMENT_SWITCH(macro) \\ AKANTU_BOOST_ELEMENT_SWITCH(macro, \\ AKANTU_ek_${_kind}_ELEMENT_TYPE) #define AKANTU_BOOST_${_u_kind}_ELEMENT_LIST(macro) \\ AKANTU_BOOST_APPLY_ON_LIST(macro, \\ AKANTU_ek_${_kind}_ELEMENT_TYPE) ") list(APPEND _aka_fe_lists ${_fe_engine_lists}) foreach(_fe_engine_list ${_fe_engine_lists}) if(NOT DEFINED _fe_engine_list_${_fe_engine_list}) string(TOUPPER "${_fe_engine_list}" _u_list) string(LENGTH "#define AKANTU_FE_ENGINE_LIST_${_u_list}" _length) math(EXPR _length "72 - ${_length}") set(_space "") while(_length GREATER 0) if(CMAKE_VERSION VERSION_GREATER 3.0) string(CONCAT _space "${_space}" " ") else() set(_space "${_space} ") endif() math(EXPR _length "${_length} - 1") endwhile() set(_fe_engine_list_${_fe_engine_list} "#define AKANTU_FE_ENGINE_LIST_${_u_list}${_space}\\ AKANTU_GENERATE_KIND_LIST((_ek_${_kind})") else() set(_fe_engine_list_${_fe_engine_list} "${_fe_engine_list_${_fe_engine_list}}\t\t\t\t\\ (_ek_${_kind})") endif() endforeach() endif() endforeach() if(_aka_fe_lists) list(REMOVE_DUPLICATES _aka_fe_lists) foreach(_fe_list ${_aka_fe_lists}) set(_aka_fe_defines "${_fe_engine_list_${_fe_list}})\n${_aka_fe_defines}") endforeach() endif() foreach(_list ${_lists}) string(TOLOWER "${_list}" _l_list) set(AKANTU_${_list}_ENUM ${_${_l_list}_enum} PARENT_SCOPE) endforeach() set(AKANTU_INTERPOLATION_TYPES_BOOST_SEQ ${_inter_types_boost_seq} PARENT_SCOPE) set(AKANTU_ELEMENT_TYPES_BOOST_SEQ ${_boosted_element_types} PARENT_SCOPE) set(AKANTU_ELEMENT_KINDS_BOOST_SEQ ${_element_kinds} PARENT_SCOPE) set(AKANTU_ELEMENT_KIND_BOOST_SEQ ${_element_kind} PARENT_SCOPE) set(AKANTU_ALL_ELEMENT_BOOST_SEQ ${_all_element_types} PARENT_SCOPE) set(AKANTU_ELEMENT_KINDS_BOOST_MACROS ${_boost_macros} PARENT_SCOPE) set(AKANTU_FE_ENGINE_LISTS ${_aka_fe_defines} PARENT_SCOPE) endfunction() #------------------------------------------------------------------------------- function(package_get_element_types pkg list) package_get_name(${pkg} _pkg_name) _package_get_variable(ET_ELEMENT_TYPES ${_pkg_name} _tmp_list) set(${list} ${_tmp_list} PARENT_SCOPE) endfunction() #=============================================================================== # Material specific #=============================================================================== #------------------------------------------------------------------------------- function(package_declare_material_infos pkg) cmake_parse_arguments(_opt_pkg "" "" "LIST;INCLUDE" ${ARGN}) package_set_variable(MATERIAL_LIST ${pkg} ${_opt_pkg_LIST}) package_set_variable(MATERIAL_INCLUDE ${pkg} ${_opt_pkg_INCLUDE}) endfunction() #------------------------------------------------------------------------------- function(package_get_all_material_includes includes) _package_get_variable_for_activated(MATERIAL_INCLUDE _includes) foreach(_mat_inc ${_includes}) if(DEFINED _mat_includes) set(_mat_includes "${_mat_includes}\n#include \"${_mat_inc}\"") else() set(_mat_includes "#include \"${_mat_inc}\"") endif() endforeach() set(${includes} ${_mat_includes} PARENT_SCOPE) endfunction() #------------------------------------------------------------------------------- function(package_get_all_material_lists lists) _package_get_variable_for_activated(MATERIAL_LIST _lists) foreach(_mat_list ${_lists}) if(DEFINED _mat_lists) set(_mat_lists "${_mat_lists}\n ${_mat_list}\t\t\t\\") else() set(_mat_lists " ${_mat_list}\t\t\t\\") endif() endforeach() set(${lists} ${_mat_lists} PARENT_SCOPE) endfunction() # ------------------------------------------------------------------------------ # Extra files to consider in source package generated by CPack # ------------------------------------------------------------------------------ function(package_declare_extra_files_to_package pkg) set(_types SOURCES MANUAL TESTS PROJECT) cmake_parse_arguments(_extra_files "" "" "${_types}" ${ARGN}) set(_files ${_extra_files_UNPARSED_ARGUMENTS}) package_get_sources_folder(${pkg} _folder_SOURCES) package_get_manual_folder(${pkg} _folder_MANUAL) package_get_tests_folder(${pkg} _folder_TESTS) set(_folder_PROJECT ${PROJECT_SOURCE_DIR}) foreach(_type ${_types}) if(_extra_files_${_type}) foreach(_file ${_extra_files_${_type}}) list(APPEND _files ${_folder_${_type}}/${_file}) if(NOT EXISTS ${_folder_${_type}}/${_file}) message(SEND_ERROR "The package ${pkg} tries to register the file ${_file} (as a ${_type} file). This file cannot be found.") endif() endforeach() endif() endforeach() package_set_variable(EXTRA_FILES ${pkg} ${_files}) endfunction() # ------------------------------------------------------------------------------ function(package_add_files_to_package) set(_files) foreach(_file ${ARGN}) list(APPEND _files ${PROJECT_SOURCE_DIR}/${_file}) endforeach() package_add_to_project_variable(EXTRA_FILES ${_files}) endfunction() function(package_get_files_for_package files) package_get_project_variable(EXTRA_FILES _tmp) set(${files} ${_tmp} PARENT_SCOPE) endfunction() package_add_files_to_package( .clang-format AUTHORS README VERSION COPYING COPYING.lesser CTestConfig.cmake cmake/akantu_environement.sh.in cmake/akantu_environement.csh.in cmake/akantu_install_environement.sh.in cmake/akantu_install_environement.csh.in cmake/Modules/CMakeFlagsHandling.cmake cmake/Modules/CMakePackagesSystem.cmake cmake/Modules/CMakePackagesSystemGlobalFunctions.cmake cmake/Modules/CMakePackagesSystemPrivateFunctions.cmake cmake/Modules/CMakeVersionGenerator.cmake cmake/Modules/PCHgcc.cmake cmake/AkantuBuildTreeSettings.cmake.in cmake/AkantuConfig.cmake.in - cmake/AkantuConfigVersion.cmake.in cmake/AkantuCPack.cmake cmake/AkantuCPackMacros.cmake cmake/AkantuInstall.cmake cmake/AkantuMacros.cmake cmake/AkantuPackagesSystem.cmake cmake/AkantuUse.cmake cmake/AkantuSimulationMacros.cmake cmake/material_lister.cc cmake/Modules/FindGMSH.cmake ) diff --git a/cmake/Modules/CMakeVersionGenerator.cmake b/cmake/Modules/CMakeVersionGenerator.cmake index 1f37c8ad6..08926dd4e 100644 --- a/cmake/Modules/CMakeVersionGenerator.cmake +++ b/cmake/Modules/CMakeVersionGenerator.cmake @@ -1,250 +1,278 @@ #=============================================================================== # @file CMakeVersionGenerator.cmake # # @author Guillaume Anciaux # @author Nicolas Richart # # @date creation: Sun Oct 19 2014 # @date last modification: Mon Jan 18 2016 # # @brief Set of macros used by akantu to handle the package system # # # @section LICENSE # # Copyright (©) 2015-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) # Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) # # Akantu is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # # Akantu 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License along # with Akantu. If not, see . # #=============================================================================== - if(__DEFINE_PROJECT_VERSION__) return() endif() set(__DEFINE_PROJECT_VERSION__ TRUE) function(_match_semver _input_semver prefix) set(_semver_regexp "^([0-9]+(\\.[0-9]+)?(\\.[0-9]+)?)(-([a-zA-Z0-9\-]*))?(\\+(.*))?") if(_input_semver MATCHES "^([0-9]+(\\.[0-9]+)?(\\.[0-9]+)?)(-([a-zA-Z0-9-]*))?(\\+(.*))?") set(${prefix}_version ${CMAKE_MATCH_1} PARENT_SCOPE) if(CMAKE_MATCH_4) set(${prefix}_version_prerelease "${CMAKE_MATCH_5}" PARENT_SCOPE) endif() if(CMAKE_MATCH_6) set(${prefix}_version_metadata "${CMAKE_MATCH_7}" PARENT_SCOPE) endif() endif() endfunction() function(_get_version_from_git) if(NOT CMAKE_VERSION_GENERATOR_TAG_PREFIX) set(CMAKE_VERSION_GENERATOR_TAG_PREFIX "v") endif() find_package(Git) - if(NOT Git_FOUND) - return() + set(is_git FALSE) + if(Git_FOUND) + execute_process( + COMMAND ${GIT_EXECUTABLE} rev-parse --git-dir + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + RESULT_VARIABLE _res + OUTPUT_QUIET + ERROR_QUIET) + if(_res EQUAL 0) + set(is_git TRUE) + endif() endif() - execute_process( - COMMAND ${GIT_EXECUTABLE} describe - --tags - --abbrev=0 - --match ${CMAKE_VERSION_GENERATOR_TAG_PREFIX}* - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - RESULT_VARIABLE _res - OUTPUT_VARIABLE _out_tag - OUTPUT_STRIP_TRAILING_WHITESPACE - ERROR_VARIABLE _err_tag) - - if(NOT _res EQUAL 0) - return() - endif() + if(is_git) + execute_process( + COMMAND ${GIT_EXECUTABLE} describe + --tags + --abbrev=0 + --match ${CMAKE_VERSION_GENERATOR_TAG_PREFIX}* + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + RESULT_VARIABLE _res + OUTPUT_VARIABLE _out_tag + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_VARIABLE _err_tag) - string(REGEX REPLACE "^${CMAKE_VERSION_GENERATOR_TAG_PREFIX}(.*)" "\\1" _tag "${_out_tag}") + if(NOT _res EQUAL 0) + return() + endif() - _match_semver("${_tag}" _tag) + string(REGEX REPLACE "^${CMAKE_VERSION_GENERATOR_TAG_PREFIX}(.*)" "\\1" _tag "${_out_tag}") - execute_process( - COMMAND ${GIT_EXECUTABLE} describe - --tags - --dirty - --always - --long - --match ${CMAKE_VERSION_GENERATOR_TAG_PREFIX}* - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - RESULT_VARIABLE _res - OUTPUT_VARIABLE _out - OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process( + COMMAND ${GIT_EXECUTABLE} describe + --tags + --dirty + --always + --long + --match ${CMAKE_VERSION_GENERATOR_TAG_PREFIX}* + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + RESULT_VARIABLE _res + OUTPUT_VARIABLE _out + OUTPUT_STRIP_TRAILING_WHITESPACE) + else() + file(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/cmake/git_info lines) + + foreach(line ${lines}) + if(line MATCHES + "describe: (${CMAKE_VERSION_GENERATOR_TAG_PREFIX}((0|[1-9][0-9]*)(.(0|[1-9][0-9]*))?(.(0|[1-9][0-9]*))?)-(.*))?") + set(_tag ${CMAKE_MATCH_2}) + set(_out ${CMAKE_MATCH_1}) + break() + endif() + endforeach() + endif() - set(_git_version ${_tag_version} PARENT_SCOPE) + _match_semver("${_tag}" _tag) + set(_git_version ${_tag_version} PARENT_SCOPE) if(_tag_version_prerelease) set(_git_version_prerelease ${_tag_version_prerelease} PARENT_SCOPE) endif() # git describe to PEP404 version set(_version_regex "^${CMAKE_VERSION_GENERATOR_TAG_PREFIX}${_tag}(-([0-9]+)-(g[0-9a-f]+)(-dirty)?)?$") if(_out MATCHES ${_version_regex}) if(CMAKE_MATCH_1) if(_tag_version_metadata) set(_metadata "${_tag_version_metadata}.") endif() set(_metadata "${_metadata}${CMAKE_MATCH_2}.${CMAKE_MATCH_3}") endif() if(CMAKE_MATCH_4) set(_metadata "${_metadata}.dirty") endif() - else() + elseif(is_git) execute_process( COMMAND ${GIT_EXECUTABLE} rev-list HEAD --count WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} RESULT_VARIABLE _res OUTPUT_VARIABLE _out_count OUTPUT_STRIP_TRAILING_WHITESPACE) if(_out MATCHES "^([0-9a-f]+)(-dirty)?$") set(_metadata "${CMAKE_MATCH_1}") if(_res EQUAL 0) set(_metadata "${_out_count}.${_metadata}") endif() if(CMAKE_MATCH_2) set(_metadata "${_metadata}.dirty") endif() endif() endif() set(_git_version_metadata ${_metadata} PARENT_SCOPE) - endfunction() function(_get_version_from_file) if(EXISTS ${PROJECT_SOURCE_DIR}/VERSION) file(STRINGS ${PROJECT_SOURCE_DIR}/VERSION _file_version) + _match_semver("${_file_version}" "_file") + set(_file_version ${_file_version} PARENT_SCOPE) if(_file_version_metadata) set(_file_version_metadata ${_file_version_metadata} PARENT_SCOPE) endif() if(_file_version_prerelease) set(_file_version_prerelease ${_file_version_prerelease} PARENT_SCOPE) endif() endif() endfunction() function(_get_metadata_from_ci) if(NOT DEFINED ENV{CI}) return() endif() if(DEFINED ENV{CI_MERGE_REQUEST_ID}) - set(_ci_version_metadata "ci.mr$ENV{CI_MERGE_REQUEST_ID}" PARENT_SCOPE) + set(_ci_version_metadata ".mr$ENV{CI_MERGE_REQUEST_ID}" PARENT_SCOPE) endif() endfunction() function(define_project_version) string(TOUPPER ${PROJECT_NAME} _project) + if(${_project}_VERSION) + return() + endif() + _get_version_from_git() if(_git_version) set(_version "${_git_version}") if(_git_version_metadata) set(_version_metadata "${_git_version_metadata}") endif() if (_git_version_prerelease) set(_version_prerelease "${_git_version_prerelease}") endif() else() # we can get metadata if and no version if not tag is properly defined if(_git_version_metadata) set(git_version_metadata ".${_git_version_metadata}") endif() _get_version_from_file() if(_file_version_metadata) - set(_version_metadata "${_version_metadata}${_git_version_metadata}") + set(_version_metadata "${_file_version_metadata}${_git_version_metadata}") endif() if (_file_version) set(_version "${_file_version}") endif() if (_file_version_prerelease) set(_version_prerelease "${_file_version_prerelease}") endif() endif() _get_metadata_from_ci() - + if(_ci_version_metadata) + set(_version_metadata "${_version_metadata}${_ci_version_metadata}") + endif() + if(_version) if(_version_prerelease) set(_version_prerelease "-${_version_prerelease}") endif() if(_version_metadata) set(_version_metadata "+${_version_metadata}") if(_ci_version_metadata) set(_version_metadata "${_version_metadata}.${_ci_version_metadata}") endif() endif() set(${_project}_VERSION ${_version} PARENT_SCOPE) set(_semver "${_version}${_version_prerelease}${_version_metadata}") set(${_project}_SEMVER "${_semver}" PARENT_SCOPE) message(STATUS "${PROJECT_NAME} version: ${_semver}") if(_version MATCHES "^([0-9]+)(\\.([0-9]+))?(\\.([0-9]+))?") set(_major_version ${CMAKE_MATCH_1}) set(${_project}_MAJOR_VERSION ${_major_version} PARENT_SCOPE) if(CMAKE_MATCH_2) set(_minor_version ${CMAKE_MATCH_3}) set(${_project}_MINOR_VERSION ${_minor_version} PARENT_SCOPE) endif() if(CMAKE_MATCH_4) set(_patch_version ${CMAKE_MATCH_5}) set(${_project}_PATCH_VERSION ${_patch_version} PARENT_SCOPE) endif() if(_version_prerelease) set(${_project}_PRERELEASE_VERSION ${_version_prerelease} PARENT_SCOPE) endif() if(_version_metadata) set(${_project}_LOCAL_VERSION ${_version_metadata} PARENT_SCOPE) endif() if(_version_metadata MATCHES "(\\+([0-9]+))(\\..*)*") set(${_project}_TWEAK ${CMAKE_MATCH_1} PARENT_SCOPE) endif() endif() else() message(FATAL_ERROR "Could not determine the VERSION for ${PROJECT_NAME}") endif() if(NOT ${_project}_NO_LIBRARY_VERSION) set(${_project}_LIBRARY_PROPERTIES ${${_project}_LIBRARY_PROPERTIES} VERSION "${_version}" SOVERSION "${_major_version}.${_minor_version}" PARENT_SCOPE) endif() endfunction() diff --git a/cmake/Modules/FindMumps.cmake b/cmake/Modules/FindMumps.cmake index 102eaf8f8..a7f2218bf 100644 --- a/cmake/Modules/FindMumps.cmake +++ b/cmake/Modules/FindMumps.cmake @@ -1,343 +1,393 @@ #=============================================================================== # @file FindMumps.cmake # # @author Mathias Lebihain # @author Philip Mueller # @author Nicolas Richart # # @date creation: Sun Oct 19 2014 # @date last modification: Fri Jan 22 2021 # # @brief The find_package file for the Mumps solver # # # @section LICENSE # # Copyright (©) 2015-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) # Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) # # Akantu is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # # Akantu 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License along # with Akantu. If not, see . # #=============================================================================== set(_MUMPS_COMPONENTS "sequential" "parallel" "double" "float" "complex_double" "complex_float") if(NOT Mumps_FIND_COMPONENTS) set(Mumps_FIND_COMPONENTS "parallel" "double" "float" "complex_double" "complex_float") endif() + #=============================================================================== enable_language(Fortran) option(MUMPS_DETECT_DEBUG "Helps to debug mumps detection problems" OFF) mark_as_advanced(MUMPS_DETECT_DEBUG) set(MUMPS_PRECISIONS) set(MUMPS_PLAT) + foreach(_comp ${Mumps_FIND_COMPONENTS}) if("${_comp}" STREQUAL "sequential") set(MUMPS_PLAT _seq) #default plat on debian based distribution endif() if("${_comp}" STREQUAL "float") list(APPEND MUMPS_PRECISIONS s) endif() if("${_comp}" STREQUAL "double") list(APPEND MUMPS_PRECISIONS d) endif() if("${_comp}" STREQUAL "complex_float") list(APPEND MUMPS_PRECISIONS c) endif() if("${_comp}" STREQUAL "complex_double") list(APPEND MUMPS_PRECISIONS z) endif() endforeach() if(NOT MUMPS_PRECISIONS) set(MUMPS_PRECISIONS s d c z) endif() list(GET MUMPS_PRECISIONS 0 _first_precision) string(TOUPPER "${_first_precision}" _u_first_precision) find_path(MUMPS_INCLUDE_DIR ${_first_precision}mumps_c.h - PATHS "${MUMPS_DIR}" - ENV MUMPS_DIR + PATHS "${MUMPS_DIR}" ENV MUMPS_DIR PATH_SUFFIXES include ) mark_as_advanced(MUMPS_INCLUDE_DIR) set(_mumps_required_vars) foreach(_precision ${MUMPS_PRECISIONS}) string(TOUPPER "${_precision}" _u_precision) - find_library(MUMPS_LIBRARY_${_u_precision}MUMPS NAMES ${_precision}mumps${MUMPS_PREFIX} - PATHS "${MUMPS_DIR}" - ENV MUMPS_DIR + + if(DEFINED MUMPS_LIBRARY_${_u_precision}MUMPS AND + (NOT "${Mumps_FIND_COMPONENTS}" STREQUAL "${Mumps_FIND_COMPONENTS_SAVE}")) + set(MUMPS_LIBRARY_${_u_precision}MUMPS NOTFOUND CACHE PATH "" FORCE) + endif() + + + find_library(MUMPS_LIBRARY_${_u_precision}MUMPS + NAMES ${_precision}mumps${MUMPS_PLAT} ${_precision}mumps + PATHS "${MUMPS_DIR}" ENV MUMPS_DIR PATH_SUFFIXES lib ) mark_as_advanced(MUMPS_LIBRARY_${_u_precision}MUMPS) list(APPEND _mumps_required_vars MUMPS_LIBRARY_${_u_precision}MUMPS) list(APPEND MUMPS_LIBRARIES_ALL ${MUMPS_LIBRARY_${_u_precision}MUMPS}) endforeach() - if(MUMPS_LIBRARY_${_u_first_precision}MUMPS MATCHES ".*${_first_precision}mumps.*${CMAKE_STATIC_LIBRARY_SUFFIX}") # Assuming mumps was compiled as a static library set(MUMPS_LIBRARY_TYPE STATIC CACHE INTERNAL "" FORCE) - - if (CMAKE_Fortran_COMPILER MATCHES ".*gfortran") - set(_compiler_specific gfortran) - elseif (CMAKE_Fortran_COMPILER MATCHES ".*ifort") - set(_compiler_specific ifcore) - else() - message("Compiler ${CMAKE_Fortran_COMPILER} is not known, you will probably " - "have to add semething instead of this message to be able to test mumps " - "install") - endif() else() set(MUMPS_LIBRARY_TYPE SHARED CACHE INTERNAL "" FORCE) endif() macro(find_mpiseq) - find_library(MUMPS_LIBRARY_MPISEQ mpiseq${MUMPS_PREFIX} - PATHS "${MUMPS_DIR}" - ENV MUMPS_DIR + find_library(MUMPS_LIBRARY_MPISEQ mpiseq${MUMPS_PLAT} mpiseq + PATHS "${MUMPS_DIR}" ENV MUMPS_DIR PATH_SUFFIXES lib ) if (NOT MUMPS_LIBRARY_MPISEQ) message("Could not find libmpiseq for sequential version of MUMPS, was " "MUMPS compiled in sequential ?") endif() set(${_libs} ${MUMPS_LIBRARY_MPISEQ} PARENT_SCOPE) mark_as_advanced(MUMPS_LIBRARY_MPISEQ) endmacro() -function(mumps_add_dependency _pdep _libs) +macro(debug_message) + if(MUMPS_DETECT_DEBUG) + message(${ARGN}) + endif() +endmacro() + +function(mumps_add_dependency _pdep _libs _incs) string(TOUPPER ${_pdep} _u_pdep) if(_pdep STREQUAL "mumps_common") - find_library(MUMPS_LIBRARY_COMMON mumps_common${MUMPS_PREFIX} - PATHS "${MUMPS_DIR}" - ENV MUMPS_DIR + find_library(MUMPS_LIBRARY_COMMON + NAMES mumps_common${MUMPS_PLAT} mumps_common + PATHS "${MUMPS_DIR}" ENV MUMPS_DIR PATH_SUFFIXES lib ) set(${_libs} ${MUMPS_LIBRARY_COMMON} PARENT_SCOPE) mark_as_advanced(MUMPS_LIBRARY_COMMON) elseif(_pdep STREQUAL "pord") - find_library(MUMPS_LIBRARY_PORD pord${MUMPS_PREFIX} - PATHS "${MUMPS_DIR}" - ENV MUMPS_DIR + find_library(MUMPS_LIBRARY_PORD + NAMES pord${MUMPS_PLAT} pord + PATHS "${MUMPS_DIR}" ENV MUMPS_DIR PATH_SUFFIXES lib ) set(${_libs} ${MUMPS_LIBRARY_PORD} PARENT_SCOPE) mark_as_advanced(MUMPS_LIBRARY_PORD) elseif(_pdep MATCHES "Scotch") find_package(Scotch REQUIRED ${ARGN} QUIET) if(ARGN) list(GET ARGN 1 _comp) string(TOUPPER ${_comp} _u_comp) set(${_libs} ${SCOTCH_LIBRARY_${_u_comp}} PARENT_SCOPE) else() set(${_libs} ${${_u_pdep}_LIBRARIES} PARENT_SCOPE) endif() elseif(_pdep MATCHES "MPI") if(MUMPS_PLAT STREQUAL "_seq") find_mpiseq() else() - find_package(MPI REQUIRED C Fortran QUIET) + if(NOT CMAKE_Fortran_COMPILER_LOADED) + enable_language(Fortran) + endif() + find_package(MPI REQUIRED QUIET + COMPONENTS C Fortran) set(${_libs} ${MPI_C_LIBRARIES} ${MPI_Fortran_LIBRARIES} PARENT_SCOPE) + set(${_incs} + ${MPI_C_INCLUDE_PATH} # deprecated + ${MPI_C_INCLUDE_DIRS} + ${MPI_Fortran_INCLUDE_PATH} # deprecated + ${MPI_Fortran_INCLUDE_DIRS} + PARENT_SCOPE) endif() elseif(_pdep MATCHES "Threads") - find_package(Threads REQUIRED) + find_package(Threads REQUIRED QUIET) set(${_libs} Threads::Threads PARENT_SCOPE) elseif(_pdep MATCHES "OpenMP") - find_package(OpenMP REQUIRED) + find_package(OpenMP REQUIRED QUIET) set(${_libs} OpenMP::OpenMP_C PARENT_SCOPE) elseif(_pdep MATCHES "Math") set(${_libs} m PARENT_SCOPE) elseif(_pdep MATCHES "ScaLAPACK") if(MUMPS_PLAT STREQUAL "_seq") # ScaLAPACK symbols are in mpiseq form 5.20+ find_mpiseq() else() find_package(ScaLAPACK REQUIRED QUIET) set(${_libs} ${SCALAPACK_LIBRARIES} PARENT_SCOPE) endif() + elseif(_pdep MATCHES "gfortran") + if(NOT CMAKE_Fortran_COMPILER_LOADED) + enable_language(Fortran) + endif() + set(${_libs} gfortran PARENT_SCOPE) else() find_package(${_pdep} REQUIRED QUIET) set(${_libs} ${${_u_pdep}_LIBRARIES} ${${_u_pdep}_LIBRARY} PARENT_SCOPE) endif() endfunction() function(mumps_find_dependencies) set(_libraries_all m ${MUMPS_LIBRARIES_ALL}) set(_include_dirs ${MUMPS_INCLUDE_DIR}) set(_mumps_test_dir "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}") file(READ ${CMAKE_CURRENT_LIST_DIR}/CheckFindMumps.c _output) file(WRITE "${_mumps_test_dir}/mumps_test_code.c" "#include <${_first_precision}mumps_c.h> ${_u_first_precision}MUMPS_STRUC_C id; #define mumps_c ${_first_precision}mumps_c #define Real ${_u_first_precision}MUMPS_REAL ") if(MUMPS_PLAT STREQUAL _seq) file(APPEND "${_mumps_test_dir}/mumps_test_code.c" "#define MUMPS_SEQ ") else() file(APPEND "${_mumps_test_dir}/mumps_test_code.c" "// #undef MUMPS_SEQ ") - find_package(MPI REQUIRED) - list(APPEND _compiler_specific ${MPI_C_LIBRARIES}) - list(APPEND _include_dirs ${MPI_C_INCLUDE_PATH} ${MPI_INCLUDE_DIR}) endif() file(APPEND "${_mumps_test_dir}/mumps_test_code.c" "${_output}") #=============================================================================== - set(_mumps_dep_symbol_MPI mpi_send) - set(_mumps_dep_symbol_BLAS ${_first_precision}gemm) - set(_mumps_dep_symbol_ScaLAPACK numroc) - set(_mumps_dep_symbol_LAPACK ilaenv) - set(_mumps_dep_symbol_Scotch SCOTCH_graphInit) - set(_mumps_dep_symbol_Scotch_ptscotch scotchfdgraphexit) - set(_mumps_dep_symbol_Scotch_esmumps esmumps) - set(_mumps_dep_symbol_mumps_common mumps_abort) - set(_mumps_dep_symbol_pord SPACE_ordering) - set(_mumps_dep_symbol_METIS metis_nodend) - set(_mumps_dep_symbol_Threads pthread_create) - set(_mumps_dep_symbol_OpenMP GOMP_loop_end_nowait) + # ADD here the symbols needed to compile + set(_mumps_dep_compile_MPI mpi.h) + # ADD here the symbols needed to link + set(_mumps_dep_link_MPI mpi_send mpi_type_free mpi_allreduce) + set(_mumps_dep_link_BLAS ${_first_precision}gemm) + set(_mumps_dep_link_ScaLAPACK numroc) + set(_mumps_dep_link_LAPACK ilaenv) + set(_mumps_dep_link_Scotch SCOTCH_graphInit scotchfstratexit) + set(_mumps_dep_link_Scotch_ptscotch scotchfdgraphexit) + set(_mumps_dep_link_Scotch_esmumps esmumps) + set(_mumps_dep_link_mumps_common mumps_abort mumps_get_perlu) + set(_mumps_dep_link_pord SPACE_ordering) + set(_mumps_dep_link_METIS metis_nodend) + set(_mumps_dep_link_Threads pthread_create) + set(_mumps_dep_link_OpenMP GOMP_loop_end_nowait) + set(_mumps_dep_link_gfortran gfortran) # TODO find missing symbols for IOMP - set(_mumps_dep_symbol_Math lround) - set(_mumps_dep_symbol_ParMETIS ParMETIS_V3_NodeND) + set(_mumps_dep_link_Math lround) + set(_mumps_dep_link_ParMETIS ParMETIS_V3_NodeND) - # added for fucking macosx that cannot fail at link - set(_mumps_run_dep_symbol_mumps_common mumps_fac_descband) - set(_mumps_run_dep_symbol_MPI mpi_bcast) - set(_mumps_run_dep_symbol_ScaLAPACK idamax) + # ADD here the symbols needed to run + set(_mumps_dep_run_mumps_common mumps_fac_descband) + set(_mumps_dep_run_MPI mpi_bcast) + set(_mumps_dep_run_ScaLAPACK idamax numroc) + set(_mumps_dep_run_Scotch_ptscotch scotchfdgraphbuild) set(_mumps_dep_comp_Scotch_ptscotch COMPONENTS ptscotch) set(_mumps_dep_comp_Scotch_esmumps COMPONENTS esmumps) set(_mumps_potential_dependencies - MPI mumps_common pord + MPI Threads OpenMP BLAS LAPACK ScaLAPACK Scotch Scotch_ptscotch Scotch_esmumps METIS ParMETIS - Threads OpenMP - Math) + gfortran Math) #=============================================================================== set(_retry_try_run TRUE) set(_retry_count 0) - # trying only as long as we add dependencies to avoid inifinte loop in case of an unkown dependency + # trying only as long as we add dependencies to avoid infinite loop in case of + # an unknown dependency while (_retry_try_run AND _retry_count LESS 100) try_run(_mumps_run _mumps_compiles "${_mumps_test_dir}" "${_mumps_test_dir}/mumps_test_code.c" CMAKE_FLAGS "-DINCLUDE_DIRECTORIES:STRING=${_include_dirs}" - LINK_LIBRARIES ${_libraries_all} ${_libraries_all} ${_compiler_specific} + LINK_LIBRARIES ${_libraries_all} ${_libraries_all} RUN_OUTPUT_VARIABLE _run COMPILE_OUTPUT_VARIABLE _out) set(_retry_compile FALSE) - if(MUMPS_DETECT_DEBUG) - message("COMPILATION outputs: \n${_out} \n RUN OUTPUT \n${_run}") - endif() + + debug_message("COMPILATION outputs: \n${_out} \n RUN OUTPUT \n${_run}") + if(_mumps_compiles AND NOT (_mumps_run STREQUAL "FAILED_TO_RUN")) break() endif() + if(_retry_count EQUAL 0 AND + (NOT _mumps_compiles OR _mumps_run STREQUAL "FAILED_TO_RUN")) + message(STATUS "Searching for MUMPS link dependencies") + endif() + foreach(_pdep ${_mumps_potential_dependencies}) set(_libs) - if(MUMPS_DETECT_DEBUG) - message("Trying to add: ${_pdep} as a dependency") - endif() + set(_incs) set(_add_pdep FALSE) - if (NOT _mumps_compiles AND - _out MATCHES "undefined reference.*${_mumps_dep_symbol_${_pdep}}") - set(_add_pdep TRUE) - #message("NEED COMPILE ${_pdep}") + + debug_message("Trying to add: ${_pdep} as a dependency") + + if (NOT _mumps_compiles) + if(DEFINED _mumps_dep_link_${_pdep}) + foreach (_link_dep ${_mumps_dep_link_${_pdep}}) + debug_message(" - test ${_link_dep}") + if(_out MATCHES "undefined reference to.*${_link_dep}" OR + _out MATCHES "${_link_dep}.*referenced from") + set(_add_pdep TRUE) + debug_message(" - ${_pdep} is a link dependency") + endif() + endforeach() + endif() + if (DEFINED _mumps_dep_compile_${_pdep}) + foreach (_compile_dep ${_mumps_dep_compile_${_pdep}}) + if(_out MATCHES "${_compile_dep}.*(No such file|file not found)") + set(_add_pdep TRUE) + debug_message(" - ${_pdep} is a compile dependency") + endif() + endforeach() + endif() elseif(_mumps_run STREQUAL "FAILED_TO_RUN" AND - DEFINED _mumps_run_dep_symbol_${_pdep} AND - _run MATCHES "${_mumps_run_dep_symbol_${_pdep}}") - set(_add_pdep TRUE) - #message("NEED RUN ${_pdep}") + DEFINED _mumps_dep_run_${_pdep}) + foreach(_run_dep ${_mumps_dep_run_${_pdep}}) + if(_run MATCHES "${_run_dep}") + set(_add_pdep TRUE) + debug_message(" - ${_pdep} is a run dependency") + endif() + endforeach() endif() if(_add_pdep) - mumps_add_dependency(${_pdep} _libs ${_mumps_dep_comp_${_pdep}}) - if(MUMPS_DETECT_DEBUG) - message("Found: ${_pdep} (${_libs})") - endif() + mumps_add_dependency(${_pdep} _libs _incs ${_mumps_dep_comp_${_pdep}}) + debug_message(" - Found: ${_pdep} (${_libs})") if(NOT _libs) message(FATAL_ERROR "MUMPS depends on ${_pdep} but no libraries where found") + else() + message(STATUS " Found MUMPS dependency ${_pdep} (${_libs})") endif() list(APPEND _libraries_all ${_libs}) + if(_incs) + list(APPEND _include_dirs ${_incs}) + endif() set(_retry_try_run TRUE) endif() endforeach() math(EXPR _retry_count "${_retry_count} + 1") endwhile() if(_retry_count GREATER 10) - message(FATAL_ERROR "Do not know what to do to link with mumps on your system, I give up!") - message("Last compilation outputs: \n${_out} \n And last run output \n${_run}") + message(FATAL_ERROR "Do not know what to do to link with mumps on your system, I give up!" + "Last compilation outputs: \n${_out} \n And last run output \n${_run}") endif() if(APPLE) # in doubt add some stuff because mumps was perhaps badly compiled - mumps_add_dependency(pord _libs) + mumps_add_dependency(pord _libs _incs) list(APPEND _libraries_all ${_libs}) endif() set(MUMPS_LIBRARIES_ALL ${_libraries_all} PARENT_SCOPE) endfunction() mumps_find_dependencies() set(MUMPS_LIBRARIES ${MUMPS_LIBRARIES_ALL} CACHE INTERNAL "" FORCE) #=============================================================================== include(FindPackageHandleStandardArgs) if(CMAKE_VERSION VERSION_GREATER 2.8.12) if(MUMPS_INCLUDE_DIR) file(STRINGS ${MUMPS_INCLUDE_DIR}/dmumps_c.h _versions REGEX "^#define MUMPS_VERSION .*") foreach(_ver ${_versions}) string(REGEX MATCH "MUMPS_VERSION *\"([0-9.]+)\"" _tmp "${_ver}") set(_mumps_VERSION ${CMAKE_MATCH_1}) endforeach() set(MUMPS_VERSION "${_mumps_VERSION}" CACHE INTERNAL "") endif() find_package_handle_standard_args(Mumps REQUIRED_VARS ${_mumps_required_vars} MUMPS_INCLUDE_DIR VERSION_VAR MUMPS_VERSION ) else() find_package_handle_standard_args(Mumps DEFAULT_MSG ${_mumps_required_vars} MUMPS_INCLUDE_DIR) endif() + +if(Mumps_FOUND) + set(Mumps_FIND_COMPONENTS_SAVE "${Mumps_FIND_COMPONENTS}" CACHE INTERNAL "") +endif() diff --git a/cmake/Modules/FindScotch.cmake b/cmake/Modules/FindScotch.cmake index c93176451..1cbab5ffa 100644 --- a/cmake/Modules/FindScotch.cmake +++ b/cmake/Modules/FindScotch.cmake @@ -1,245 +1,257 @@ #=============================================================================== # @file FindScotch.cmake # # @author Nicolas Richart # # @date creation: Fri Oct 24 2014 # @date last modification: Wed Jan 13 2016 # # @brief The find_package file for Scotch # # @section LICENSE # # Copyright (©) 2015 EPFL (Ecole Polytechnique Fédérale de Lausanne) Laboratory # (LSMS - Laboratoire de Simulation en Mécanique des Solides) # # Akantu is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # # Akantu 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with Akantu. If not, see . # #=============================================================================== set(_SCOTCH_COMPONENTS "metis" "parmetis" "esmumps" "ptscotch") if(NOT Scotch_FIND_COMPONENTS) set(Scotch_FIND_COMPONENTS) endif() -find_path(SCOTCH_INCLUDE_DIR scotch.h PATHS "${SCOTCH_DIR}" ENV SCOTCH_DIR +find_path(SCOTCH_INCLUDE_DIR scotch.h + PATHS "${SCOTCH_DIR}" + ENV SCOTCH_DIR PATH_SUFFIXES include include/scotch ) -find_library(SCOTCH_LIBRARY scotch PATHS "${SCOTCH_DIR}" ENV SCOTCH_DIR PATH_SUFFIXES lib) +find_library(SCOTCH_LIBRARY scotch + PATHS "${SCOTCH_DIR}" + ENV SCOTCH_DIR + PATH_SUFFIXES lib) set(_scotch_test_dir "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}") file(WRITE "${_scotch_test_dir}/scotch_test_code.c" "#include #include #include int main() { SCOTCH_Graph graph; SCOTCH_graphInit(&graph); return 0; } ") #=============================================================================== include(FindPackageHandleStandardArgs) if(CMAKE_VERSION VERSION_GREATER 2.8.12) if(SCOTCH_INCLUDE_DIR) file(STRINGS ${SCOTCH_INCLUDE_DIR}/scotch.h _versions REGEX "^#define\ +SCOTCH_(VERSION|RELEASE|PATCHLEVEL) .*") foreach(_ver ${_versions}) string(REGEX MATCH "SCOTCH_(VERSION|RELEASE|PATCHLEVEL) *([0-9.]+)" _tmp "${_ver}") set(_scotch_${CMAKE_MATCH_1} ${CMAKE_MATCH_2}) endforeach() - set(SCOTCH_VERSION "${_scotch_VERSION}.${_scotch_RELEASE}.${_scotch_PATCHLEVEL}" CACHE INTERNAL "") + set(SCOTCH_VERSION + "${_scotch_VERSION}.${_scotch_RELEASE}.${_scotch_PATCHLEVEL}" + CACHE INTERNAL "") endif() find_package_handle_standard_args(Scotch REQUIRED_VARS SCOTCH_LIBRARY SCOTCH_INCLUDE_DIR VERSION_VAR SCOTCH_VERSION) else() find_package_handle_standard_args(Scotch DEFAULT_MSG SCOTCH_LIBRARY SCOTCH_INCLUDE_DIR) endif() set(SCOTCH_LIBRARIES_ALL ${SCOTCH_LIBRARY}) -try_compile(_scotch_compiles "${_scotch_test_dir}" SOURCES "${_scotch_test_dir}/scotch_test_code.c" +try_compile(_scotch_compiles "${_scotch_test_dir}" + SOURCES "${_scotch_test_dir}/scotch_test_code.c" CMAKE_FLAGS "-DINCLUDE_DIRECTORIES:STRING=${SCOTCH_INCLUDE_DIR}" LINK_LIBRARIES ${SCOTCH_LIBRARY} OUTPUT_VARIABLE _out) get_filename_component(_scotch_hint "${SCOTCH_LIBRARY}" DIRECTORY) if(SCOTCH_LIBRARY MATCHES ".*scotch.*${CMAKE_STATIC_LIBRARY_SUFFIX}") # Assuming scotch was compiled as a static library set(SCOTCH_LIBRARY_TYPE STATIC CACHE INTERNAL "" FORCE) else() set(SCOTCH_LIBRARY_TYPE SHARED CACHE INTERNAL "" FORCE) endif() if(NOT _scotch_compiles) if(_out MATCHES "SCOTCH_errorPrint") find_library(SCOTCH_LIBRARY_ERR scotcherr HINTS ${_scotch_hint}) find_library(SCOTCH_LIBRARY_ERREXIT scotcherrexit HINTS ${_scotch_hint}) if(NOT TARGET Scotch::err) add_library(Scotch::err ${SCOTCH_LIBRARY_TYPE} IMPORTED GLOBAL) endif() if(NOT TARGET Scotch::errexit) add_library(Scotch::errexit ${SCOTCH_LIBRARY_TYPE} IMPORTED GLOBAL) endif() set_target_properties(Scotch::errexit PROPERTIES IMPORTED_LOCATION "${SCOTCH_LIBRARY_ERREXIT}" INTERFACE_INCLUDE_DIRECTORIES "${SCOTCH_INCLUDE_DIR}" IMPORTED_LINK_INTERFACE_LANGUAGES "C") set_target_properties(Scotch::err PROPERTIES IMPORTED_LOCATION "${SCOTCH_LIBRARY_ERR}" INTERFACE_INCLUDE_DIRECTORIES "${SCOTCH_INCLUDE_DIR}" IMPORTED_LINK_INTERFACE_LANGUAGES "C" INTERFACE_LINK_LIBRARIES "Scotch::errexit") mark_as_advanced(SCOTCH_LIBRARY_ERR SCOTCH_LIBRARY_ERREXIT) list(APPEND SCOTCH_LIBRARIES_ALL ${SCOTCH_LIBRARY_ERR} ${SCOTCH_LIBRARY_ERREXIT}) set(_scotch_link_lib INTERFACE_LINK_LIBRARIES "Scotch::err") endif() endif() if(NOT TARGET Scotch::scotch) add_library(Scotch::scotch ${SCOTCH_LIBRARY_TYPE} IMPORTED GLOBAL) endif() set_target_properties(Scotch::scotch PROPERTIES IMPORTED_LOCATION "${SCOTCH_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${SCOTCH_INCLUDE_DIR}" IMPORTED_LINK_INTERFACE_LANGUAGES "C" ${_scotch_link_lib}) set(SCOTCH_LIBRARIES ${SCOTCH_LIBRARIES_ALL} CACHE INTERNAL "Libraries for Scotch" FORCE) mark_as_advanced(SCOTCH_LIBRARY SCOTCH_INCLUDE_DIR SCOTCH_LIBRARIES) if("${Scotch_FIND_COMPONENTS}" MATCHES "esmumps") find_library(SCOTCH_LIBRARY_ESMUMPS esmumps HINTS ${_scotch_hint}) if(NOT TARGET Scotch::esmumps) add_library(Scotch::esmumps ${SCOTCH_LIBRARY_TYPE} IMPORTED GLOBAL) endif() set_target_properties(Scotch::esmumps PROPERTIES IMPORTED_LOCATION "${SCOTCH_LIBRARY_ESMUMPS}" INTERFACE_INCLUDE_DIRECTORIES "${SCOTCH_INCLUDE_DIR}" IMPORTED_LINK_INTERFACE_LANGUAGES "C") mark_as_advanced(SCOTCH_LIBRARY_ESMUMPS) endif() if("${Scotch_FIND_COMPONENTS}" MATCHES "metis") find_library(SCOTCH_LIBRARY_METIS scotchmetis HINTS ${_scotch_hint}) if(NOT TARGET Scotch::metis) add_library(Scotch::metis ${SCOTCH_LIBRARY_TYPE} IMPORTED GLOBAL) endif() set_target_properties(Scotch::metis PROPERTIES IMPORTED_LOCATION "${SCOTCH_LIBRARY_METIS}" INTERFACE_INCLUDE_DIRECTORIES "${SCOTCH_INCLUDE_DIR}" IMPORTED_LINK_INTERFACE_LANGUAGES "C") mark_as_advanced(SCOTCH_LIBRARY_METIS) endif() if("${Scotch_FIND_COMPONENTS}" MATCHES "parmetis") find_library(SCOTCH_LIBRARY_PARMETIS scotchparmetis HINTS ${_scotch_hint}) if(NOT TARGET Scotch::parmetis) add_library(Scotch::parmetis ${SCOTCH_LIBRARY_TYPE} IMPORTED GLOBAL) endif() set_target_properties(Scotch::parmetis PROPERTIES IMPORTED_LOCATION "${SCOTCH_LIBRARY_PARMETIS}" INTERFACE_INCLUDE_DIRECTORIES "${SCOTCH_INCLUDE_DIR}" IMPORTED_LINK_INTERFACE_LANGUAGES "C" INTERFACE_INCLUDE_DIRECTORIES "Scotch::metis") mark_as_advanced(SCOTCH_LIBRARY_PARMETIS) endif() # ##=============================================================================== if("${Scotch_FIND_COMPONENTS}" MATCHES "ptscotch") file(WRITE "${_scotch_test_dir}/ptscotch_test_code.c" "#include #include #include #include int main() { SCOTCH_Dgraph graph; SCOTCH_dgraphInit(&graph, MPI_COMM_WORLD); return 0; } ") - find_package(MPI REQUIRED) + find_package(MPI REQUIRED COMPONENTS C QUIET) find_library(SCOTCH_LIBRARY_PTSCOTCH ptscotch HINTS ${_scotch_hint}) - try_compile(_scotch_compiles "${_scotch_test_dir}" SOURCES "${_scotch_test_dir}/ptscotch_test_code.c" + try_compile(_scotch_compiles "${_scotch_test_dir}" + SOURCES "${_scotch_test_dir}/ptscotch_test_code.c" CMAKE_FLAGS "-DINCLUDE_DIRECTORIES:STRING=${SCOTCH_INCLUDE_DIR};${MPI_C_INCLUDE_PATH}" LINK_LIBRARIES ${SCOTCH_LIBRARY_PTSCOTCH} ${MPI_C_LIBRARIES} OUTPUT_VARIABLE _out) if(NOT _scotch_compiles) if(_out MATCHES "SCOTCH_archExit") set(_scotch_link_lib INTERFACE_LINK_LIBRARIES "Scotch::scotch") endif() endif() if(NOT TARGET Scotch::ptscotch) add_library(Scotch::ptscotch ${SCOTCH_LIBRARY_TYPE} IMPORTED GLOBAL) endif() set_target_properties(Scotch::ptscotch PROPERTIES IMPORTED_LOCATION "${SCOTCH_LIBRARY_PTSCOTCH}" INTERFACE_INCLUDE_DIRECTORIES "${SCOTCH_INCLUDE_DIR}" IMPORTED_LINK_INTERFACE_LANGUAGES "C" ${_scotch_link_lib}) - set(PTSCOTCH_LIBRARIES ${SCOTCH_LIBRARY_PTSCOTCH} ${SCOTCH_LIBRARIES} CACHE INTERNAL "Libraries for PT-Scotch" FORCE) + set(PTSCOTCH_LIBRARIES + ${SCOTCH_LIBRARY_PTSCOTCH} + ${SCOTCH_LIBRARIES} + CACHE INTERNAL "Libraries for PT-Scotch" FORCE) mark_as_advanced(SCOTCH_LIBRARY_PTSCOTCH PTSCOTCH_LIBRARIES) if("${Scotch_FIND_COMPONENTS}" MATCHES "esmumps") find_library(SCOTCH_LIBRARY_PTESMUMPS ptesmumps HINTS ${_scotch_hint} PATH_SUFFIXES lib .) if(NOT TARGET Scotch::ptesmumps) add_library(Scotch::ptesmumps ${SCOTCH_LIBRARY_TYPE} IMPORTED GLOBAL) endif() set_target_properties(Scotch::ptesmumps PROPERTIES IMPORTED_LOCATION "${SCOTCH_LIBRARY_ESMUMPS}" INTERFACE_INCLUDE_DIRECTORIES "${SCOTCH_INCLUDE_DIR}" IMPORTED_LINK_INTERFACE_LANGUAGES "C") mark_as_advanced(SCOTCH_LIBRARY_PTESMUMPS) endif() endif() diff --git a/cmake/akantu_version.py.in b/cmake/akantu_version.py.in deleted file mode 100644 index 2cbc4dd6f..000000000 --- a/cmake/akantu_version.py.in +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python3 - -def get_version(): - AKANTU_VERSION_MAJOR = @AKANTU_MAJOR_VERSION@ - AKANTU_VERSION_MINOR = @AKANTU_MINOR_VERSION@ - AKANTU_VERSION_PATCH = @AKANTU_PATCH_VERSION@ - AKANTU_VERSION_PRERELEASE = '@AKANTU_PRERELASE_VERSION@' - AKANTU_VERSION_LOCAL = '@AKANTU_LOCAL_VERSION@' - - return '{}.{}.{}{}{}'.format( - AKANTU_VERSION_MAJOR, - AKANTU_VERSION_MINOR, - AKANTU_VERSION_PATCH, - AKANTU_VERSION_PRERELEASE, - AKANTU_VERSION_LOCAL) diff --git a/cmake/git_info b/cmake/git_info new file mode 100644 index 000000000..6c1986b9d --- /dev/null +++ b/cmake/git_info @@ -0,0 +1 @@ +describe: $Format:%(describe:tags=true,match=v*)$ diff --git a/cmake/semver.py b/cmake/semver.py new file mode 100644 index 000000000..fa0b2d808 --- /dev/null +++ b/cmake/semver.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python3 +import os +import re +import subprocess + + +def run_git_command(args): + git_dir = os.path.realpath(os.path.join(os.path.dirname(__file__), os.pardir)) + + cmd = ["git"] + args + p = subprocess.Popen( + cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=git_dir + ) + stdout = p.communicate()[0].strip().decode() + if p.returncode != 0: + return None, p.returncode + return stdout, p.returncode + + +def _split_git_describe(describe): + describe_mo = re.search( + r"^(?P.+)" + r"-(?P\d+)" + r"-g(?P[0-9a-f]+)" + r"(-(?Pdirty))?$", + describe, + ) + + if describe_mo: + pieces = {} + pieces["tag"] = describe_mo.group("tag") + # distance: number of commits since tag + pieces["distance"] = int(describe_mo.group("distance")) + + # commit: short hex revision ID + pieces["short"] = describe_mo.group("short") + if describe_mo.group("dirty"): + pieces["dirty"] = describe_mo.group("dirty") + + return pieces + + return None + + +semver_re = re.compile( + r"^(?P0|[1-9]\d*)" + r"(\.(?P0|[1-9]\d*))?" + r"(\.(?P0|[1-9]\d*))?" + r"(?:-(?P(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?" + r"(?:\+(?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$" +) + + +def _parse_semver(version): + pieces = {} + semver_mo = semver_re.search(version) + if semver_mo: + for p in ["major", "minor", "patch", "prerelease", "build"]: + if semver_mo.group(p): + pieces[p] = semver_mo.group(p) + return pieces + + +def get_git_version(): + out, rc = run_git_command(["rev-parse", "--git-dir"]) + if rc != 0: + return None + + git_describe, rc = run_git_command( + ["describe", "--tags", "--dirty", "--always", "--match", "v*"] + ) + + if '-g' in git_describe: + # TAG-DISTANCE-gHEX + pieces = _split_git_describe(git_describe) + else: + # tag only or no tag and hash + pieces = {"tag": git_describe} + + # major.minor.patch-prerelease+build + if not pieces or ("tag" not in pieces): + return None + + semver_mo = semver_re.search(pieces["tag"][1:]) + if semver_mo: + for p in ["major", "minor", "patch", "prerelease", "build"]: + if semver_mo.group(p): + pieces[p] = semver_mo.group(p) + + return pieces + + +def get_git_attributes_version(): + file_dir = os.path.dirname(os.path.realpath(os.path.abspath(__file__))) + attributes = None + pieces = None + + with open(os.path.join(file_dir, "git_info")) as fh: + describe_re = re.compile(r"describe: ([^$].*[^$])") + for line in fh: + mo = describe_re.search(line) + if mo: + attributes = mo.group(1) + break + + if attributes: + pieces = _split_git_describe(attributes) + + return pieces + + +def get_ci_version(): + pieces = None + if "CI_AKANTU_INSTALL_PREFIX" not in os.environ: + return None + + ci_akantu_install_prefix = os.environ["CI_AKANTU_INSTALL_PREFIX"] + akantu_dir = os.path.join(ci_akantu_install_prefix, "lib", "cmake", "Akantu") + cmake_config = os.path.join(akantu_dir, "AkantuConfig.cmake") + + if not os.path.exists(cmake_config): + return None + + version = None + with open(cmake_config, "r") as fh: + version_re = re.compile(r"^set\(AKANTU_VERSION (.*)\)$") + for line in fh: + version_mo = version_re.search(line) + if version_mo: + version = version_mo.group(1) + break + + if not version: + return None + + pieces = _parse_semver(version) + return pieces + + +def get_version_file(): + version_path = os.path.join( + os.path.realpath( + os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)) + ), + "VERSION", + ) + + if not os.path.exists(version_path): + return None + + version = None + with open(version_path, "r") as fh: + version = fh.readline() + + if not version: + return None + + pieces = _parse_semver(version) + return pieces + + +def get_version(): + pieces = None + + if not pieces: + pieces = get_ci_version() + + if not pieces: + pieces = get_git_version() + + if not pieces: + pieces = get_git_attributes_version() + + if not pieces: + pieces = get_version_file() + + if not pieces: + raise Exception("No version could be determined") + + semver_build = [] + if "build" in pieces: + semver_build = [pieces["build"]] + + if "distance" in pieces: + semver_build.extend([str(pieces["distance"]), "g" + pieces["short"]]) + if "dirty" in pieces and pieces["dirty"]: + semver_build.append(pieces["dirty"]) + + if semver_build: + pieces["build_part"] = "+" + ".".join(semver_build) + else: + pieces["build_part"] = "" + + if "prerelease" in pieces: + pieces["prerelease"] = "-" + pieces["prerelease"] + else: + pieces["prerelease"] = "" + + semver = "{major}.{minor}.{patch}{prerelease}{build_part}".format(**pieces) + + if "CI_MERGE_REQUEST_ID" in os.environ: + semver = "{}.mr{}".format(semver, os.environ["CI_MERGE_REQUEST_ID"]) + + return semver + + +if __name__ == "__main__": + print(get_version()) diff --git a/doc/dev-doc/akantu.dox.j2 b/doc/dev-doc/akantu.dox.j2 index 7a944900d..1af6a6939 100644 --- a/doc/dev-doc/akantu.dox.j2 +++ b/doc/dev-doc/akantu.dox.j2 @@ -1,52 +1,52 @@ PROJECT_NAME = Akantu -PROJECT_NUMBER = {{ akantu_version }} +#PROJECT_NUMBER = {{ akantu_version }} STRIP_FROM_PATH = {{ akantu_source_path }} STRIP_FROM_INC_PATH = {{ akantu_source_path }} TAB_SIZE = 4 ALIASES = "rst=\verbatim embed:rst" \ "endrst=\endverbatim" QUIET = NO WARN_IF_UNDOCUMENTED = NO WARN_IF_DOC_ERROR = NO WARN_AS_ERROR = NO INPUT = {{ akantu_source_path }}/src \ {{ akantu_source_path }}/test/ci/includes_for_ci FILE_PATTERNS = *.c *.cc *.hh *.py EXCLUDE = {{ akantu_source_path }}/src/common/aka_fwd.hh \ {{ akantu_source_path }}/src/io/dumper/dumpable_dummy.hh {{ akantu_source_path }}/src/common/aka_config.hh.in RECURSIVE = YES EXAMPLE_PATH = {{ akantu_source_path }}/examples EXAMPLE_RECURSIVE = YES SOURCE_BROWSER = NO GENERATE_HTML = NO GENERATE_HTMLHELP = NO USE_MATHJAX = YES GENERATE_LATEX = NO GENERATE_XML = YES XML_OUTPUT = xml DOXYGEN_INCLUDE_PATH = {{ akantu_source_path }}/src/common \ {{ akantu_source_path }}/src/fe_engine \ {{ akantu_source_path }}/src/mesh \ {{ akantu_source_path }}/src/model \ {{ akantu_source_path }}/test/ci/includes_for_ci ENABLE_PREPROCESSING = YES MACRO_EXPANSION = YES PREDEFINED = DOXYGEN \ __cplusplus=201402L \ "AKANTU_GET_MACRO(name, value, type) = type get##name() const;" \ "AKANTU_GET_MACRO_NOT_CONST(name, value, type) = type get##name();" EXPAND_AS_DEFINED = __BEGIN_AKANTU__ \ __END_AKANTU__ \ __BEGIN_AKANTU_DUMPER__ \ __END_AKANTU_DUMPER__ \ AKANTU_SET_MACRO \ AKANTU_GET_MACRO_DEREF_PTR \ AKANTU_GET_MACRO_BY_ELEMENT_TYPE \ AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST COLLABORATION_GRAPH = NO UML_LOOK = YES TEMPLATE_RELATIONS = YES CALL_GRAPH = YES CALLER_GRAPH = YES LOOKUP_CACHE_SIZE = 0 diff --git a/doc/dev-doc/conf.py b/doc/dev-doc/conf.py index 12886ef12..f6fe0c67d 100644 --- a/doc/dev-doc/conf.py +++ b/doc/dev-doc/conf.py @@ -1,335 +1,331 @@ # -*- coding: utf-8 -*- # # Configuration file for the Sphinx documentation builder. # # This file does only contain a selection of the most common options. For a # full list see the documentation: # http://www.sphinx-doc.org/en/master/config # -- Path setup -------------------------------------------------------------- # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # import os import shutil import jinja2 -import git -import re + +# import git +# import re import subprocess # -- General configuration --------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Number figures numfig = True # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.intersphinx', - 'sphinx.ext.coverage', - 'sphinx.ext.mathjax', - 'sphinx.ext.ifconfig', - 'sphinx.ext.viewcode', - 'sphinxcontrib.bibtex', - 'breathe', + "sphinx.ext.autodoc", + "sphinx.ext.intersphinx", + "sphinx.ext.coverage", + "sphinx.ext.mathjax", + "sphinx.ext.ifconfig", + "sphinx.ext.viewcode", + "sphinxcontrib.bibtex", + "breathe", ] -read_the_docs_build = os.environ.get('READTHEDOCS', None) == 'True' +read_the_docs_build = os.environ.get("READTHEDOCS", None) == "True" if read_the_docs_build: akantu_path = "." akantu_source_path = "../../" else: akantu_path = "@CMAKE_CURRENT_BINARY_DIR@" akantu_source_path = "@CMAKE_SOURCE_DIR@" # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] -source_suffix = '.rst' +source_suffix = ".rst" # The master toctree document. -master_doc = 'index' +master_doc = "index" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ['CMakeLists.txt', - 'manual/appendix/elements.rst', - 'manual/appendix/material-parameters.rst', - 'manual/constitutive-laws.rst', - 'manual/new-constitutive-laws.rst'] +exclude_patterns = [ + "CMakeLists.txt", + "manual/appendix/elements.rst", + "manual/appendix/material-parameters.rst", + "manual/constitutive-laws.rst", + "manual/new-constitutive-laws.rst", +] # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" -primary_domain = 'cpp' -highlight_language = 'cpp' +primary_domain = "cpp" +highlight_language = "cpp" -bibtex_bibfiles = ['manual/manual-bibliography.bib'] +bibtex_bibfiles = ["manual/manual-bibliography.bib"] # -- Project information ----------------------------------------------------- -project = 'Akantu' -copyright = '2021 EPFL (Ecole Polytechnique Fédérale de Lausanne)' + \ - ' Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)' -author = 'Nicolas Richart' - -with open(os.path.join(akantu_source_path, 'VERSION'), 'r') as fh: - version_file = fh.readlines() - file_release = version_file[0].strip() - -try: - tag_prefix = 'v' - git_repo = git.Repo(akantu_source_path) - - git_describe = git_repo.git.describe('--tags', '--dirty', '--always', - '--long', - '--match', '{}*'.format(tag_prefix)) - - print("GIT Describe: {}".format(git_describe)) - - # git describe to PEP404 version - describe_matches = re.search( - (r'^{}(?P.+?)' + - r'(?:-(?P\d+)-g(?P[0-9a-f]+)' + - r'(?:-(?Pdirty))?)?$').format(tag_prefix), - git_describe) - - if describe_matches: - describe_matches = describe_matches.groupdict() - - release = describe_matches['version'] - if describe_matches['distance']: - release += '.' if '+' in release else '+' - release += '{distance}.{sha}'.format(**describe_matches) - if describe_matches['dirty']: - release += '.dirty' - else: - count = git_repo.git.rev_list('HEAD', '--count') - describe_matches = re.search( - (r'^(?P[0-9a-f]+)' + - r'(?:-(?Pdirty))?$').format(tag_prefix), - git_describe).groupdict() - release = '{}.{}+{}'.format(file_release, count, - describe_matches['sha']) - -except git.InvalidGitRepositoryError: - release = file_release - -version = re.sub(r'^([0-9]+)\.([0-9+]).*', - r'\1.\2', - release) - -print('Release: {} - Version: {}'.format(release, version)) +project = "Akantu" +copyright = ( + "2021 EPFL (Ecole Polytechnique Fédérale de Lausanne)" + + " Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)" +) +author = "Nicolas Richart" + +# try: +# tag_prefix = 'v' +# git_repo = git.Repo(akantu_source_path) + +# git_describe = git_repo.git.describe('--tags', '--dirty', '--always', +# '--long', +# '--match', '{}*'.format(tag_prefix)) + +# print("GIT Describe: {}".format(git_describe)) + +# # git describe to PEP404 version +# describe_matches = re.search( +# (r'^{}(?P.+?)' + +# r'(?:-(?P\d+)-g(?P[0-9a-f]+)' + +# r'(?:-(?Pdirty))?)?$').format(tag_prefix), +# git_describe) + +# if describe_matches: +# describe_matches = describe_matches.groupdict() + +# release = describe_matches['version'] +# if describe_matches['distance']: +# release += '.' if '+' in release else '+' +# release += '{distance}.{sha}'.format(**describe_matches) +# if describe_matches['dirty']: +# release += '.dirty' +# else: +# count = git_repo.git.rev_list('HEAD', '--count') +# describe_matches = re.search( +# (r'^(?P[0-9a-f]+)' + +# r'(?:-(?Pdirty))?$').format(tag_prefix), +# git_describe).groupdict() +# release = '{}.{}+{}'.format(file_release, count, +# describe_matches['sha']) + +# except git.InvalidGitRepositoryError: +# with open(os.path.join(akantu_source_path, 'VERSION'), 'r') as fh: +# version_file = fh.readlines() +# file_release = version_file[0].strip() +# release = file_release + + +# print("Release: {} - Version: {}".format(release, version)) # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # if read_the_docs_build: - html_theme = 'default' + html_theme = "default" else: - html_theme = 'sphinx_rtd_theme' + html_theme = "sphinx_rtd_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] -html_logo = '_static/logo_only_akantu.svg' +html_static_path = ["_static"] +html_logo = "_static/logo_only_akantu.svg" # Custom sidebar templates, must be a dictionary that maps document names # to template names. # # The default sidebars (for documents that don't match any pattern) are # defined by theme itself. Builtin themes are using these templates by # default: ``['localtoc.html', 'relations.html', 'sourcelink.html', # 'searchbox.html']``. # # html_sidebars = {} html_sidebars = { - '**': [ - 'relations.html', # needs 'show_related': True theme option to display - 'searchbox.html', - ]} + "**": [ + "relations.html", # needs 'show_related': True theme option to display + "searchbox.html", + ] +} math_eqref_format = "Eq. {number}" # MathJax configuration if not read_the_docs_build: mathjax_config = { - 'extensions': [ - "tex2jax.js", - "siunitx.js" - ], - 'TeX': { - 'Macros': { - 'st': [r'\mathrm{#1}', 1], - 'mat': [r'\mathbf{#1}', 1], - 'half': [r'\frac{1}{2}', 0], + "extensions": ["tex2jax.js", "siunitx.js"], + "TeX": { + "Macros": { + "st": [r"\mathrm{#1}", 1], + "mat": [r"\mathbf{#1}", 1], + "half": [r"\frac{1}{2}", 0], }, - 'extensions': ["AMSmath.js", "AMSsymbols.js", "sinuitx.js"], + "extensions": ["AMSmath.js", "AMSsymbols.js", "sinuitx.js"], }, } else: mathjax3_config = { - 'tex': { - 'macros': { - 'st': [r'\mathrm{#1}', 1], - 'mat': [r'\mathbf{#1}', 1], - 'half': [r'\frac{1}{2}', 0], + "tex": { + "macros": { + "st": [r"\mathrm{#1}", 1], + "mat": [r"\mathbf{#1}", 1], + "half": [r"\frac{1}{2}", 0], }, - 'packages': ['base', 'ams'], - }, - 'loader': { - 'load': ['[tex]/ams'] + "packages": ["base", "ams"], }, + "loader": {"load": ["[tex]/ams"]}, } # -- Options for HTMLHelp output --------------------------------------------- # Output file base name for HTML help builder. -htmlhelp_basename = 'Akantudoc' +htmlhelp_basename = "Akantudoc" # -- Options for LaTeX output ------------------------------------------------ latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. # - 'preamble': r'''\usepackage{amsmath}''', - + "preamble": r"""\usepackage{amsmath}""", # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'Akantu.tex', 'Akantu Documentation', - 'Nicolas Richart', 'manual'), + (master_doc, "Akantu.tex", "Akantu Documentation", "Nicolas Richart", "manual"), ] # -- Options for manual page output ------------------------------------------ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'akantu', 'Akantu Documentation', - [author], 1) -] +man_pages = [(master_doc, "akantu", "Akantu Documentation", [author], 1)] # -- Options for Texinfo output ---------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'Akantu', 'Akantu Documentation', - author, 'Akantu', 'One line description of project.', - 'Miscellaneous'), + ( + master_doc, + "Akantu", + "Akantu Documentation", + author, + "Akantu", + "One line description of project.", + "Miscellaneous", + ), ] # -- Options for Epub output ------------------------------------------------- # Bibliographic Dublin Core info. epub_title = project epub_author = author epub_publisher = author epub_copyright = copyright # The unique identifier of the text. This can be a ISBN number # or the project homepage. # -epub_identifier = '' +epub_identifier = "" # A unique identification for the text. # # epub_uid = '' # A list of files that should not be packed into the epub file. -epub_exclude_files = ['search.html'] +epub_exclude_files = ["search.html"] # -- Extension configuration ------------------------------------------------- j2_args = {} if read_the_docs_build: - j2_template_path = '.' + j2_template_path = "." else: - j2_template_path = '@CMAKE_CURRENT_SOURCE_DIR@' - os.makedirs(os.path.join(akantu_path, '_static'), exist_ok=True) + j2_template_path = "@CMAKE_CURRENT_SOURCE_DIR@" + os.makedirs(os.path.join(akantu_path, "_static"), exist_ok=True) shutil.copyfile( - os.path.join('@CMAKE_CURRENT_SOURCE_DIR@', html_logo), - os.path.join(akantu_path, html_logo)) + os.path.join("@CMAKE_CURRENT_SOURCE_DIR@", html_logo), + os.path.join(akantu_path, html_logo), + ) j2_args = { - 'akantu_source_path': akantu_source_path, - 'akantu_version': version.replace('v', ''), + "akantu_source_path": akantu_source_path, + #'akantu_version': version.replace('v', ''), } print(akantu_path) j2_env = jinja2.Environment( - loader=jinja2.FileSystemLoader(j2_template_path), - undefined=jinja2.DebugUndefined) + loader=jinja2.FileSystemLoader(j2_template_path), undefined=jinja2.DebugUndefined +) -j2_template = j2_env.get_template('akantu.dox.j2') +j2_template = j2_env.get_template("akantu.dox.j2") -with open(os.path.join(akantu_path, 'akantu.dox'), 'w') as fh: +with open(os.path.join(akantu_path, "akantu.dox"), "w") as fh: fh.write(j2_template.render(j2_args)) -subprocess.run(['doxygen', 'akantu.dox'], - cwd=akantu_path) +subprocess.run(["doxygen", "akantu.dox"], cwd=akantu_path) # print("akantu_path = '{}'".format(akantu_path)) breathe_projects = {"Akantu": os.path.join(akantu_path, "xml")} breathe_default_project = "Akantu" -breathe_default_members = ('members', 'undoc-members') -breathe_implementation_filename_extensions = ['.c', '.cc', '.cpp'] +breathe_default_members = ("members", "undoc-members") +breathe_implementation_filename_extensions = [".c", ".cc", ".cpp"] breathe_show_enumvalue_initializer = True breathe_debug_trace_directives = True # -- Options for intersphinx extension --------------------------------------- intersphinx_mapping = { - 'numpy': ('https://docs.scipy.org/doc/numpy/', None), - 'scipy': ('https://docs.scipy.org/doc/scipy/reference', None), + "numpy": ("https://docs.scipy.org/doc/numpy/", None), + "scipy": ("https://docs.scipy.org/doc/scipy/reference", None), } diff --git a/packages/mpi.cmake b/packages/mpi.cmake index 75976643e..73ea479fd 100644 --- a/packages/mpi.cmake +++ b/packages/mpi.cmake @@ -1,160 +1,160 @@ #=============================================================================== # @file mpi.cmake # # @author Guillaume Anciaux # @author Nicolas Richart # # @date creation: Mon Nov 21 2011 # @date last modification: Fri Dec 13 2019 # # @brief package description for mpi # # # @section LICENSE # # Copyright (©) 2010-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) # Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) # # Akantu is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # # Akantu 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License along # with Akantu. If not, see . # #=============================================================================== set(MPI_CXX_SKIP_MPICXX TRUE CACHE BOOL "NO CXX support for mpi" FORCE) package_declare(MPI EXTERNAL DESCRIPTION "Add MPI support in akantu" - EXTRA_PACKAGE_OPTIONS PREFIX MPI_C MPI + EXTRA_PACKAGE_OPTIONS PREFIX MPI_C MPI ARGS "COMPONENTS;C" ) package_declare_sources(MPI synchronizer/mpi_communicator_data.hh synchronizer/communicator_mpi_inline_impl.hh ) function(_add_to_mpi_preflags flag) if(NOT MPIEXEC_PREFLAGS MATCHES "${flag}") - string(STRIP "${flag} ${MPIEXEC_PREFLAGS}" _preflags) - set(MPIEXEC_PREFLAGS "${_preflags}" CACHE STRING "" FORCE) - endif() + string(STRIP "${flag} ${MPIEXEC_PREFLAGS}" _preflags) + set(MPIEXEC_PREFLAGS "${_preflags}" CACHE STRING "" FORCE) + endif() endfunction() function(add_extra_mpi_options) unset(MPI_ID CACHE) package_get_include_dir(MPI _include_dir) foreach(_inc_dir ${_include_dir}) if(EXISTS "${_inc_dir}/mpi.h") if(NOT MPI_ID) file(STRINGS "${_inc_dir}/mpi.h" _mpi_version REGEX "#define MPI_(SUB)?VERSION .*") foreach(_ver ${_mpi_version}) string(REGEX MATCH "MPI_(VERSION|SUBVERSION) *([0-9]+)" _tmp "${_ver}") set(_mpi_${CMAKE_MATCH_1} ${CMAKE_MATCH_2}) endforeach() set(MPI_STD_VERSION "${_mpi_VERSION}.${_mpi_SUBVERSION}" CACHE INTERNAL "") endif() if(NOT MPI_ID) # check if openmpi file(STRINGS "${_inc_dir}/mpi.h" _ompi_version REGEX "#define OMPI_.*_VERSION .*") if(_ompi_version) set(MPI_ID "OpenMPI" CACHE INTERNAL "") foreach(_version ${_ompi_version}) string(REGEX MATCH "OMPI_(.*)_VERSION (.*)" _tmp "${_version}") if(_tmp) set(MPI_VERSION_${CMAKE_MATCH_1} ${CMAKE_MATCH_2}) endif() endforeach() set(MPI_ID_VERSION "${MPI_VERSION_MAJOR}.${MPI_VERSION_MINOR}.${MPI_VERSION_RELEASE}" CACHE INTERNAL "") _add_to_mpi_preflags("--oversubscribe") if(AKANTU_RUN_IN_DOCKER) _add_to_mpi_preflags("--allow-run-as-root") endif() endif() endif() if(NOT MPI_ID) # check if intelmpi file(STRINGS "${_inc_dir}/mpi.h" _impi_version REGEX "#define I_MPI_VERSION .*") if(_impi_version) set(MPI_ID "IntelMPI" CACHE INTERNAL "") string(REGEX MATCH "I_MPI_VERSION \"(.*)\"" _tmp "${_impi_version}") if(_tmp) set(MPI_ID_VERSION "${CMAKE_MATCH_1}" CACHE INTERNAL "") endif() endif() endif() if(NOT MPI_ID) # check if mvapich2 file(STRINGS "${_inc_dir}/mpi.h" _mvapich2_version REGEX "#define MVAPICH2_VERSION .*") if(_mvapich2_version) set(MPI_ID "MPVAPICH2" CACHE INTERNAL "") string(REGEX MATCH "MVAPICH2_VERSION \"(.*)\"" _tmp "${_mvapich2_version}") if(_tmp) set(MPI_ID_VERSION "${CMAKE_MATCH_1}" CACHE INTERNAL "") endif() endif() endif() if(NOT MPI_ID) # check if mpich (mpich as to be checked after all the mpi that derives from it) file(STRINGS "${_inc_dir}/mpi.h" _mpich_version REGEX "#define MPICH_VERSION .*") if(_mpich_version) set(MPI_ID "MPICH" CACHE INTERNAL "") string(REGEX MATCH "I_MPI_VERSION \"(.*)\"" _tmp "${_mpich_version}") if(_tmp) set(MPI_ID_VERSION "${CMAKE_MATCH_1}" CACHE INTERNAL "") endif() endif() endif() endif() endforeach() if(MPI_ID STREQUAL "IntelMPI" OR MPI_ID STREQUAL "MPICH" OR MPI_ID STREQUAL "MVAPICH2") set(_flags "-DMPICH_IGNORE_CXX_SEEK") elseif(MPI_ID STREQUAL "OpenMPI") set(_flags "-DOMPI_SKIP_MPICXX") if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set( _flags "${_flags} -Wno-literal-suffix") endif() endif() include(FindPackageMessage) if(MPI_FOUND) find_package_message(MPI "MPI ID: ${MPI_ID} ${MPI_ID_VERSION} (MPI standard ${MPI_STD_VERSION})" "${MPI_STD_VERSION}") endif() set(MPI_EXTRA_COMPILE_FLAGS "${_flags}" CACHE STRING "Extra flags for MPI" FORCE) mark_as_advanced(MPI_EXTRA_COMPILE_FLAGS) #package_get_source_files(MPI _srcs _pub _priv) #list(APPEND _srcs "common/aka_error.cc") #set_property(SOURCE ${_srcs} PROPERTY COMPILE_FLAGS "${_flags}") package_set_compile_flags(MPI CXX ${_flags}) endfunction() package_on_enabled_script(MPI " add_extra_mpi_options() mask_package_options(MPI) " ) package_set_package_system_dependency(MPI deb mpi-default-bin) package_set_package_system_dependency(MPI deb-src mpi-default-dev) diff --git a/packages/mumps.cmake b/packages/z_mumps.cmake similarity index 94% rename from packages/mumps.cmake rename to packages/z_mumps.cmake index 0b5fa473e..7201e5a57 100644 --- a/packages/mumps.cmake +++ b/packages/z_mumps.cmake @@ -1,72 +1,73 @@ #=============================================================================== # @file mumps.cmake # # @author Nicolas Richart # # @date creation: Mon Nov 21 2011 # @date last modification: Wed Dec 18 2019 # # @brief package description for mumps support # # # @section LICENSE # # Copyright (©) 2010-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) # Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) # # Akantu is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # # Akantu 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License along # with Akantu. If not, see . # #=============================================================================== - - package_declare(Mumps EXTERNAL DESCRIPTION "Add Mumps support in akantu" ) package_declare_sources(Mumps model/common/non_linear_solver/non_linear_solver_linear.cc model/common/non_linear_solver/non_linear_solver_linear.hh model/common/non_linear_solver/non_linear_solver_newton_raphson.cc model/common/non_linear_solver/non_linear_solver_newton_raphson.hh solver/sparse_solver_mumps.cc solver/sparse_solver_mumps.hh ) set(_mumps_float_type ${AKANTU_FLOAT_TYPE}) if(AKANTU_FLOAT_TYPE STREQUAL "float" OR AKANTU_FLOAT_TYPE STREQUAL "double") set(_mumps_components ${AKANTU_FLOAT_TYPE}) else() if(DEFINED AKANTU_FLOAT_TYPE) message(FATAL_ERROR "MUMPS does not support floating point type \"${AKANTU_FLOAT_TYPE}\"") endif() endif() package_get_option_name(parallel _par_option) if(${_par_option}) list(APPEND _mumps_components "parallel") + package_set_package_system_dependency(Mumps deb libmumps) package_set_package_system_dependency(Mumps deb-src libmumps-dev) + package_add_dependencies(Mumps PRIVATE parallel) else() list(APPEND _mumps_components "sequential") package_set_package_system_dependency(Mumps deb libmumps-seq) package_set_package_system_dependency(Mumps deb-src libmumps-seq-dev) + package_remove_dependencies(Mumps PRIVATE parallel) endif() package_set_find_package_extra_options(Mumps ARGS COMPONENTS "${_mumps_components}") -package_declare_extra_files_to_package(MUMPS +package_declare_extra_files_to_package(Mumps PROJECT cmake/Modules/FindMumps.cmake ) diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index f03678e28..97f9e0114 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -1,149 +1,150 @@ #=============================================================================== # @file CMakeLists.txt # # @author Guillaume Anciaux # @author Nicolas Richart # # @date creation: Fri Dec 12 2014 # @date last modification: Fri May 07 2021 # # @brief CMake file for the python wrapping of akantu # # # @section LICENSE # # Copyright (©) 2015-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) # Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) # # Akantu is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # # Akantu 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License along # with Akantu. If not, see . # #=============================================================================== set(PYBIND11_PYTHON_VERSION ${AKANTU_PREFERRED_PYTHON_VERSION} CACHE INTERNAL "") if(NOT SKBUILD) package_get_all_include_directories( AKANTU_LIBRARY_INCLUDE_DIRS ) package_get_all_external_informations( PRIVATE_INCLUDE AKANTU_PRIVATE_EXTERNAL_INCLUDE_DIR INTERFACE_INCLUDE AKANTU_INTERFACE_EXTERNAL_INCLUDE_DIR LIBRARIES AKANTU_EXTERNAL_LIBRARIES ) endif() set(PYAKANTU_SRCS py_aka_common.cc py_aka_error.cc py_akantu.cc py_boundary_conditions.cc py_dof_manager.cc py_fe_engine.cc py_group_manager.cc + py_integration_scheme.cc py_mesh.cc py_model.cc py_parser.cc py_solver.cc py_dumpable.cc ) package_is_activated(solid_mechanics _is_activated) if (_is_activated) list(APPEND PYAKANTU_SRCS py_solid_mechanics_model.cc py_material.cc py_material_selector.cc ) endif() package_is_activated(cohesive_element _is_activated) if (_is_activated) list(APPEND PYAKANTU_SRCS py_solid_mechanics_model_cohesive.cc py_fragment_manager.cc ) endif() package_is_activated(heat_transfer _is_activated) if (_is_activated) list(APPEND PYAKANTU_SRCS py_heat_transfer_model.cc ) endif() package_is_activated(contact_mechanics _is_activated) if(_is_activated) list(APPEND PYAKANTU_SRCS py_contact_mechanics_model.cc py_model_couplers.cc ) endif() package_is_activated(phase_field _is_activated) if (_is_activated) list(APPEND PYAKANTU_SRCS py_phase_field_model.cc ) endif() package_is_activated(structural_mechanics _is_activated) if (_is_activated) list(APPEND PYAKANTU_SRCS py_structural_mechanics_model.cc ) endif() pybind11_add_module(py11_akantu ${PYAKANTU_SRCS}) # to avoid compilation warnings from pybind11 target_include_directories(py11_akantu SYSTEM BEFORE PRIVATE ${PYBIND11_INCLUDE_DIR} PRIVATE ${pybind11_INCLUDE_DIR} PRIVATE ${Python_INCLUDE_DIRS}) target_link_libraries(py11_akantu PUBLIC akantu) set_target_properties(py11_akantu PROPERTIES DEBUG_POSTFIX "" LIBRARY_OUTPUT_DIRECTORY akantu) if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") target_compile_options(py11_akantu PUBLIC -fsized-deallocation) endif() file(COPY akantu DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) if(NOT Python_MAJOR) set(Python_VERSION_MAJOR ${PYTHON_VERSION_MAJOR}) set(Python_VERSION_MINOR ${PYTHON_VERSION_MINOR}) endif() if(NOT SKBUILD) set(_python_install_dir ${CMAKE_INSTALL_LIBDIR}/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}/site-packages/akantu) else() set(_python_install_dir python/akantu) endif() install(TARGETS py11_akantu LIBRARY DESTINATION ${_python_install_dir}) if(NOT SKBUILD) install(DIRECTORY akantu DESTINATION ${_python_install_dir} FILES_MATCHING PATTERN "*.py") endif() diff --git a/python/akantu/__init__.py b/python/akantu/__init__.py index 5f2555313..1beea099d 100644 --- a/python/akantu/__init__.py +++ b/python/akantu/__init__.py @@ -1,74 +1,72 @@ """ __init__.py: akantu python module""" __author__ = "Guillaume Anciaux and Nicolas Richart" __credits__ = [ "Guillaume Anciaux ", "Nicolas Richart ", ] __copyright__ = "Copyright (©) 2018-2021 EPFL (Ecole Polytechnique Fédérale" \ " de Lausanne) Laboratory (LSMS - Laboratoire de Simulation" \ " en Mécanique des Solides)" __license__ = "LGPLv3" import warnings as _aka_warn import scipy.sparse as _aka_sparse import numpy as _aka_np from . import py11_akantu as _py11_akantu private_keys = set(dir(_py11_akantu)) - set(dir()) for k in private_keys: globals()[k] = getattr(_py11_akantu, k) if _py11_akantu.has_mpi(): try: from mpi4py import MPI # noqa: F401 except Exception: pass def initialize(*args, **kwargs): raise RuntimeError("No need to call initialize," " use parseInput to read an input file") def finalize(*args, **kwargs): _aka_warn.warn("No need to call finalize", DeprecationWarning) class AkantuSparseMatrix (_aka_sparse.coo_matrix): def __init__(self, aka_sparse): self.aka_sparse = aka_sparse matrix_type = self.aka_sparse.getMatrixType() sz = self.aka_sparse.size() row = self.aka_sparse.getIRN()[:, 0] - 1 col = self.aka_sparse.getJCN()[:, 0] - 1 data = self.aka_sparse.getA()[:, 0] row = row.copy() col = col.copy() data = data.copy() if matrix_type == _py11_akantu._symmetric: non_diags = (row != col) row_sup = col[non_diags] col_sup = row[non_diags] data_sup = data[non_diags] col = _aka_np.concatenate((col, col_sup)) row = _aka_np.concatenate((row, row_sup)) data = _aka_np.concatenate((data, data_sup)) _aka_sparse.coo_matrix.__init__( - self, (data, (row, col)), shape=(sz, sz)) + self, (data, (row, col)), shape=(sz, sz), dtype=data.dtype) FromStress = _py11_akantu.FromHigherDim FromTraction = _py11_akantu.FromSameDim _py11_akantu.__initialize() -from ._version import get_versions # NOQA(402@) -__version__ = get_versions()['version'] -del get_versions +__version__ = _py11_akantu.getVersion() diff --git a/python/akantu/_version.py b/python/akantu/_version.py deleted file mode 100644 index 04d3c3f97..000000000 --- a/python/akantu/_version.py +++ /dev/null @@ -1,533 +0,0 @@ -""" _version.py: _version file generated by versioneer""" - -__author__ = "Nicolas Richart" -__credits__ = [ - "Nicolas Richart ", -] -__copyright__ = "Copyright (©) 2018-2021 EPFL (Ecole Polytechnique Fédérale" \ - " de Lausanne) Laboratory (LSMS - Laboratoire de Simulation" \ - " en Mécanique des Solides)" -__license__ = "LGPLv3" - -# This file helps to compute a version number in source trees obtained from -# git-archive tarball (such as those provided by githubs download-from-tag -# feature). Distribution tarballs (built by setup.py sdist) and build -# directories (produced by setup.py build) will contain a much shorter file -# that just contains the computed version number. - -# This file is released into the public domain. Generated by -# versioneer-0.19 (https://github.com/python-versioneer/python-versioneer) - -import errno -import os -import re -import subprocess -import sys - - -def get_keywords(): - """Get the keywords needed to look up the version information.""" - # these strings will be replaced by git during git-archive. - # setup.py/versioneer.py will grep for the variable names, so they must - # each be defined on a line of their own. _version.py will just call - # get_keywords(). - git_refnames = "$Format:%d$" - git_full = "$Format:%H$" - git_date = "$Format:%ci$" - keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} - return keywords - - -class VersioneerConfig: - """Container for Versioneer configuration parameters.""" - - -def get_config(): - """Create, populate and return the VersioneerConfig() object.""" - # these strings are filled in when 'setup.py versioneer' creates - # _version.py - cfg = VersioneerConfig() - cfg.VCS = "git" - cfg.style = "pep440" - cfg.tag_prefix = "v" - cfg.parentdir_prefix = "None" - cfg.versionfile_source = "akantu/python/akantu/_version.py" - cfg.verbose = False - return cfg - - -class NotThisMethod(Exception): - """Exception raised if a method is not valid for the current scenario.""" - - -LONG_VERSION_PY = {} -HANDLERS = {} - - -def register_vcs_handler(vcs, method): # decorator - """Create decorator to mark a method as the handler of a VCS.""" - def decorate(f): - """Store f in HANDLERS[vcs][method].""" - if vcs not in HANDLERS: - HANDLERS[vcs] = {} - HANDLERS[vcs][method] = f - return f - return decorate - - -def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, - env=None): - """Call the given command(s).""" - assert isinstance(commands, list) - p = None - for c in commands: - try: - dispcmd = str([c] + args) - # remember shell=False, so use git.cmd on windows, not just git - p = subprocess.Popen([c] + args, cwd=cwd, env=env, - stdout=subprocess.PIPE, - stderr=(subprocess.PIPE if hide_stderr - else None)) - break - except EnvironmentError: - e = sys.exc_info()[1] - if e.errno == errno.ENOENT: - continue - if verbose: - print("unable to run %s" % dispcmd) - print(e) - return None, None - else: - if verbose: - print("unable to find command, tried %s" % (commands,)) - return None, None - stdout = p.communicate()[0].strip().decode() - if p.returncode != 0: - if verbose: - print("unable to run %s (error)" % dispcmd) - print("stdout was %s" % stdout) - return None, p.returncode - return stdout, p.returncode - - -def versions_from_parentdir(parentdir_prefix, root, verbose): - """Try to determine the version from the parent directory name. - - Source tarballs conventionally unpack into a directory that includes both - the project name and a version string. We will also support searching up - two directory levels for an appropriately named parent directory - """ - rootdirs = [] - - for i in range(3): - dirname = os.path.basename(root) - if dirname.startswith(parentdir_prefix): - return {"version": dirname[len(parentdir_prefix):], - "full-revisionid": None, - "dirty": False, "error": None, "date": None} - else: - rootdirs.append(root) - root = os.path.dirname(root) # up a level - - if verbose: - print("Tried directories %s but none started with prefix %s" % - (str(rootdirs), parentdir_prefix)) - raise NotThisMethod("rootdir doesn't start with parentdir_prefix") - - -@register_vcs_handler("git", "get_keywords") -def git_get_keywords(versionfile_abs): - """Extract version information from the given file.""" - # the code embedded in _version.py can just fetch the value of these - # keywords. When used from setup.py, we don't want to import _version.py, - # so we do it with a regexp instead. This function is not used from - # _version.py. - keywords = {} - try: - f = open(versionfile_abs, "r") - for line in f.readlines(): - if line.strip().startswith("git_refnames ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["refnames"] = mo.group(1) - if line.strip().startswith("git_full ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["full"] = mo.group(1) - if line.strip().startswith("git_date ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["date"] = mo.group(1) - f.close() - except EnvironmentError: - pass - return keywords - - -@register_vcs_handler("git", "keywords") -def git_versions_from_keywords(keywords, tag_prefix, verbose): - """Get version information from git keywords.""" - if not keywords: - raise NotThisMethod("no keywords at all, weird") - date = keywords.get("date") - if date is not None: - # Use only the last line. Previous lines may contain GPG signature - # information. - date = date.splitlines()[-1] - - # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant - # datestamp. However we prefer "%ci" (which expands to an "ISO-8601 - # -like" string, which we must then edit to make compliant), because - # it's been around since git-1.5.3, and it's too difficult to - # discover which version we're using, or to work around using an - # older one. - date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) - refnames = keywords["refnames"].strip() - if refnames.startswith("$Format"): - if verbose: - print("keywords are unexpanded, not using") - raise NotThisMethod("unexpanded keywords, not a git-archive tarball") - refs = set([r.strip() for r in refnames.strip("()").split(",")]) - # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of - # just "foo-1.0". If we see a "tag: " prefix, prefer those. - TAG = "tag: " - tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)]) - if not tags: - # Either we're using git < 1.8.3, or there really are no tags. We use - # a heuristic: assume all version tags have a digit. The old git %d - # expansion behaves like git log --decorate=short and strips out the - # refs/heads/ and refs/tags/ prefixes that would let us distinguish - # between branches and tags. By ignoring refnames without digits, we - # filter out many common branch names like "release" and - # "stabilization", as well as "HEAD" and "master". - tags = set([r for r in refs if re.search(r'\d', r)]) - if verbose: - print("discarding '%s', no digits" % ",".join(refs - tags)) - if verbose: - print("likely tags: %s" % ",".join(sorted(tags))) - for ref in sorted(tags): - # sorting will prefer e.g. "2.0" over "2.0rc1" - if ref.startswith(tag_prefix): - r = ref[len(tag_prefix):] - if verbose: - print("picking %s" % r) - return {"version": r, - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": None, - "date": date} - # no suitable tags, so version is "0+unknown", but full hex is still there - if verbose: - print("no suitable tags, using unknown + full revision id") - return {"version": "0+unknown", - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": "no suitable tags", "date": None} - - -@register_vcs_handler("git", "pieces_from_vcs") -def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): - """Get version from 'git describe' in the root of the source tree. - - This only gets called if the git-archive 'subst' keywords were *not* - expanded, and _version.py hasn't already been rewritten with a short - version string, meaning we're inside a checked out source tree. - """ - GITS = ["git"] - if sys.platform == "win32": - GITS = ["git.cmd", "git.exe"] - - out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, - hide_stderr=True) - if rc != 0: - if verbose: - print("Directory %s not under git control" % root) - raise NotThisMethod("'git rev-parse --git-dir' returned error") - - # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] - # if there isn't one, this yields HEX[-dirty] (no NUM) - describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty", - "--always", "--long", - "--match", "%s*" % tag_prefix], - cwd=root) - # --long was added in git-1.5.5 - if describe_out is None: - raise NotThisMethod("'git describe' failed") - describe_out = describe_out.strip() - full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) - if full_out is None: - raise NotThisMethod("'git rev-parse' failed") - full_out = full_out.strip() - - pieces = {} - pieces["long"] = full_out - pieces["short"] = full_out[:7] # maybe improved later - pieces["error"] = None - - # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] - # TAG might have hyphens. - git_describe = describe_out - - # look for -dirty suffix - dirty = git_describe.endswith("-dirty") - pieces["dirty"] = dirty - if dirty: - git_describe = git_describe[:git_describe.rindex("-dirty")] - - # now we have TAG-NUM-gHEX or HEX - - if "-" in git_describe: - # TAG-NUM-gHEX - mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) - if not mo: - # unparseable. Maybe git-describe is misbehaving? - pieces["error"] = ("unable to parse git-describe output: '%s'" - % describe_out) - return pieces - - # tag - full_tag = mo.group(1) - if not full_tag.startswith(tag_prefix): - if verbose: - fmt = "tag '%s' doesn't start with prefix '%s'" - print(fmt % (full_tag, tag_prefix)) - pieces["error"] = ("tag '%s' doesn't start with prefix '%s'" - % (full_tag, tag_prefix)) - return pieces - pieces["closest-tag"] = full_tag[len(tag_prefix):] - - # distance: number of commits since tag - pieces["distance"] = int(mo.group(2)) - - # commit: short hex revision ID - pieces["short"] = mo.group(3) - - else: - # HEX: no tags - pieces["closest-tag"] = None - count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], - cwd=root) - pieces["distance"] = int(count_out) # total number of commits - - # commit date: see ISO-8601 comment in git_versions_from_keywords() - date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], - cwd=root)[0].strip() - # Use only the last line. Previous lines may contain GPG signature - # information. - date = date.splitlines()[-1] - pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) - - return pieces - - -def plus_or_dot(pieces): - """Return a + if we don't already have one, else return a .""" - if "+" in pieces.get("closest-tag", ""): - return "." - return "+" - - -def render_pep440(pieces): - """Build up version string, with post-release "local version identifier". - - Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you - get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty - - Exceptions: - 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += plus_or_dot(pieces) - rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - else: - # exception #1 - rendered = "0+untagged.%d.g%s" % (pieces["distance"], - pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - return rendered - - -def render_pep440_pre(pieces): - """TAG[.post0.devDISTANCE] -- No -dirty. - - Exceptions: - 1: no tags. 0.post0.devDISTANCE - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"]: - rendered += ".post0.dev%d" % pieces["distance"] - else: - # exception #1 - rendered = "0.post0.dev%d" % pieces["distance"] - return rendered - - -def render_pep440_post(pieces): - """TAG[.postDISTANCE[.dev0]+gHEX] . - - The ".dev0" means dirty. Note that .dev0 sorts backwards - (a dirty tree will appear "older" than the corresponding clean one), - but you shouldn't be releasing software with -dirty anyways. - - Exceptions: - 1: no tags. 0.postDISTANCE[.dev0] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - rendered += plus_or_dot(pieces) - rendered += "g%s" % pieces["short"] - else: - # exception #1 - rendered = "0.post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - rendered += "+g%s" % pieces["short"] - return rendered - - -def render_pep440_old(pieces): - """TAG[.postDISTANCE[.dev0]] . - - The ".dev0" means dirty. - - Exceptions: - 1: no tags. 0.postDISTANCE[.dev0] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - else: - # exception #1 - rendered = "0.post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - return rendered - - -def render_git_describe(pieces): - """TAG[-DISTANCE-gHEX][-dirty]. - - Like 'git describe --tags --dirty --always'. - - Exceptions: - 1: no tags. HEX[-dirty] (note: no 'g' prefix) - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"]: - rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) - else: - # exception #1 - rendered = pieces["short"] - if pieces["dirty"]: - rendered += "-dirty" - return rendered - - -def render_git_describe_long(pieces): - """TAG-DISTANCE-gHEX[-dirty]. - - Like 'git describe --tags --dirty --always -long'. - The distance/hash is unconditional. - - Exceptions: - 1: no tags. HEX[-dirty] (note: no 'g' prefix) - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) - else: - # exception #1 - rendered = pieces["short"] - if pieces["dirty"]: - rendered += "-dirty" - return rendered - - -def render(pieces, style): - """Render the given version pieces into the requested style.""" - if pieces["error"]: - return {"version": "unknown", - "full-revisionid": pieces.get("long"), - "dirty": None, - "error": pieces["error"], - "date": None} - - if not style or style == "default": - style = "pep440" # the default - - if style == "pep440": - rendered = render_pep440(pieces) - elif style == "pep440-pre": - rendered = render_pep440_pre(pieces) - elif style == "pep440-post": - rendered = render_pep440_post(pieces) - elif style == "pep440-old": - rendered = render_pep440_old(pieces) - elif style == "git-describe": - rendered = render_git_describe(pieces) - elif style == "git-describe-long": - rendered = render_git_describe_long(pieces) - else: - raise ValueError("unknown style '%s'" % style) - - return {"version": rendered, "full-revisionid": pieces["long"], - "dirty": pieces["dirty"], "error": None, - "date": pieces.get("date")} - - -def get_versions(): - """Get version information or return default if unable to do so.""" - # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have - # __file__, we can work backwards from there to the root. Some - # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which - # case we can only use expanded keywords. - - cfg = get_config() - verbose = cfg.verbose - - try: - return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, - verbose) - except NotThisMethod: - pass - - try: - root = os.path.realpath(__file__) - # versionfile_source is the relative path from the top of the source - # tree (where the .git directory might live) to this file. Invert - # this to find the root from __file__. - for i in cfg.versionfile_source.split('/'): - root = os.path.dirname(root) - except NameError: - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, - "error": "unable to find root of source tree", - "date": None} - - try: - pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) - return render(pieces, cfg.style) - except NotThisMethod: - pass - - try: - if cfg.parentdir_prefix: - return versions_from_parentdir(cfg.parentdir_prefix, root, verbose) - except NotThisMethod: - pass - - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, - "error": "unable to compute version", "date": None} diff --git a/python/py_aka_error.cc b/python/py_aka_error.cc index 3a45390a9..ab2520407 100644 --- a/python/py_aka_error.cc +++ b/python/py_aka_error.cc @@ -1,70 +1,95 @@ /** * @file py_aka_error.cc * * @author Guillaume Anciaux * @author Nicolas Richart * * @date creation: Tue May 07 2019 * @date last modification: Tue Sep 29 2020 * * @brief pybind11 interface to aka_error * * * @section LICENSE * * Copyright (©) 2018-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "py_aka_error.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ #include #include #include /* -------------------------------------------------------------------------- */ namespace py = pybind11; /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ void register_error(py::module & mod) { - mod.def("setDebugLevel", &debug::setDebugLevel); - mod.def("getDebugLevel", &debug::getDebugLevel); - mod.def("printBacktrace", - [](bool flag) { debug::debugger.printBacktrace(flag); }); + py::module mod_debug = mod.def_submodule("debug"); + + mod.def("setDebugLevel", [](DebugLevel lvl) { + debug::setDebugLevel(lvl); + PyErr_WarnEx(PyExc_DeprecationWarning, + "setDebugLevel() is deprecated, it has moved in the " + "submodule debug", + 1); + }); + mod.def("getDebugLevel", []() { + PyErr_WarnEx(PyExc_DeprecationWarning, + "getDebugLevel() is deprecated, it has moved in the " + "submodule debug", + 1); + return debug::getDebugLevel(); + }); + + mod.def("printBacktrace", [](bool flag) { + debug::debugger.printBacktrace(flag); + PyErr_WarnEx(PyExc_DeprecationWarning, + "printBacktrace() is deprecated, it has moved in the " + "submodule debug", + 1); + }); + + mod_debug.def("setDebugLevel", &debug::setDebugLevel); + mod_debug.def("getDebugLevel", &debug::getDebugLevel); + mod_debug.def("printBacktrace", + [](bool flag) { debug::debugger.printBacktrace(flag); }); py::enum_(mod, "DebugLevel") .value("dblError", dblError) .value("dblException", dblException) .value("dblCritical", dblCritical) .value("dblMajor", dblMajor) .value("dblWarning", dblWarning) .value("dblInfo", dblInfo) .value("dblTrace", dblTrace) .value("dblAccessory", dblAccessory) .value("dblDebug", dblDebug) .value("dblDump", dblDump) .value("dblTest", dblTest) .export_values(); } } // namespace akantu diff --git a/python/py_akantu.cc b/python/py_akantu.cc index 85cb2fde6..c084f7cae 100644 --- a/python/py_akantu.cc +++ b/python/py_akantu.cc @@ -1,169 +1,173 @@ /** * @file py_akantu.cc * * @author Guillaume Anciaux * @author Philip Mueller * @author Mohit Pundir * @author Nicolas Richart * * @date creation: Wed Oct 31 2018 * @date last modification: Mon Mar 29 2021 * * @brief pybind11 interface to akantu main's file * * * @section LICENSE * * Copyright (©) 2018-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_config.hh" /* -------------------------------------------------------------------------- */ #include "py_aka_common.hh" #include "py_aka_error.hh" #include "py_boundary_conditions.hh" #include "py_dof_manager.hh" #include "py_dumpable.hh" #include "py_fe_engine.hh" #include "py_group_manager.hh" +#include "py_integration_scheme.hh" #include "py_mesh.hh" #include "py_model.hh" #include "py_parser.hh" #include "py_solver.hh" #if defined(AKANTU_SOLID_MECHANICS) #include "py_material.hh" #include "py_material_selector.hh" #include "py_solid_mechanics_model.hh" #endif #if defined(AKANTU_HEAT_TRANSFER) #include "py_heat_transfer_model.hh" #endif #if defined(AKANTU_COHESIVE_ELEMENT) #include "py_fragment_manager.hh" #include "py_solid_mechanics_model_cohesive.hh" #endif #if defined(AKANTU_CONTACT_MECHANICS) #include "py_contact_mechanics_model.hh" #include "py_model_couplers.hh" #endif #if defined(AKANTU_PHASE_FIELD) #include "py_phase_field_model.hh" #endif #if defined(AKANTU_STRUCTURAL_MECHANICS) #include "py_structural_mechanics_model.hh" #endif /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ namespace py = pybind11; namespace akantu { void register_all(pybind11::module & mod) { register_initialize(mod); register_enums(mod); register_error(mod); register_functions(mod); register_parser(mod); register_solvers(mod); register_group_manager(mod); register_dumpable(mod); register_mesh(mod); register_fe_engine(mod); + register_integration_schemes(mod); register_dof_manager(mod); register_boundary_conditions(mod); register_model(mod); #if defined(AKANTU_HEAT_TRANSFER) register_heat_transfer_model(mod); #endif #if defined(AKANTU_SOLID_MECHANICS) register_solid_mechanics_model(mod); register_material(mod); register_material_selector(mod); #endif #if defined(AKANTU_COHESIVE_ELEMENT) register_solid_mechanics_model_cohesive(mod); register_fragment_manager(mod); #endif #if defined(AKANTU_STRUCTURAL_MECHANICS) register_structural_mechanics_model(mod); #endif #if defined(AKANTU_CONTACT_MECHANICS) register_contact_mechanics_model(mod); register_model_couplers(mod); #endif #if defined(AKANTU_PHASE_FIELD) register_phase_field_model(mod); register_phase_field_coupler(mod); #endif } } // namespace akantu /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ PYBIND11_MODULE(py11_akantu, mod) { mod.doc() = "Akantu python interface"; static py::exception akantu_exception(mod, "Exception"); py::register_exception_translator([](std::exception_ptr ptr) { try { if (ptr) { std::rethrow_exception(ptr); } } catch (akantu::debug::Exception & e) { if (akantu::debug::debugger.printBacktrace()) { akantu::debug::printBacktrace(); } akantu_exception(e.info().c_str()); } }); akantu::register_all(mod); - mod.def("has_mpi", []() { + mod.def("has_mpi", + []() { #if defined(AKANTU_USE_MPI) - return true; + return true; #else return false; #endif - }); + }) + .def("getVersion", &akantu::getVersion); } // Module akantu diff --git a/python/py_dof_manager.cc b/python/py_dof_manager.cc index e0f3e59cb..1a073da25 100644 --- a/python/py_dof_manager.cc +++ b/python/py_dof_manager.cc @@ -1,211 +1,216 @@ /* * Copyright (©) 2018-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "py_dof_manager.hh" #include "py_aka_array.hh" #include "py_akantu_pybind11_compatibility.hh" /* -------------------------------------------------------------------------- */ #include #include #include +#include /* -------------------------------------------------------------------------- */ #include #include #include /* -------------------------------------------------------------------------- */ namespace py = pybind11; /* -------------------------------------------------------------------------- */ namespace akantu { namespace { class PySolverCallback : public SolverCallback { public: using SolverCallback::SolverCallback; /// get the type of matrix needed MatrixType getMatrixType(const ID & matrix_id) const override { // NOLINTNEXTLINE PYBIND11_OVERRIDE_PURE(MatrixType, SolverCallback, getMatrixType, matrix_id); } /// callback to assemble a Matrix void assembleMatrix(const ID & matrix_id) override { // NOLINTNEXTLINE PYBIND11_OVERRIDE_PURE(void, SolverCallback, assembleMatrix, matrix_id); } /// callback to assemble a lumped Matrix void assembleLumpedMatrix(const ID & matrix_id) override { // NOLINTNEXTLINE PYBIND11_OVERRIDE_PURE(void, SolverCallback, assembleLumpedMatrix, matrix_id); } /// callback to assemble the residual (rhs) void assembleResidual() override { // NOLINTNEXTLINE PYBIND11_OVERRIDE_PURE(void, SolverCallback, assembleResidual); } /// callback for the predictor (in case of dynamic simulation) void predictor() override { // NOLINTNEXTLINE PYBIND11_OVERRIDE(void, SolverCallback, predictor); } /// callback for the corrector (in case of dynamic simulation) void corrector() override { // NOLINTNEXTLINE PYBIND11_OVERRIDE(void, SolverCallback, corrector); } void beforeSolveStep() override { // NOLINTNEXTLINE PYBIND11_OVERRIDE(void, SolverCallback, beforeSolveStep); } void afterSolveStep(bool converged) override { // NOLINTNEXTLINE PYBIND11_OVERRIDE(void, SolverCallback, afterSolveStep, converged); } }; class PyInterceptSolverCallback : public InterceptSolverCallback { public: using InterceptSolverCallback::InterceptSolverCallback; MatrixType getMatrixType(const ID & matrix_id) const override { // NOLINTNEXTLINE PYBIND11_OVERRIDE(MatrixType, InterceptSolverCallback, getMatrixType, matrix_id); } void assembleMatrix(const ID & matrix_id) override { // NOLINTNEXTLINE PYBIND11_OVERRIDE(void, InterceptSolverCallback, assembleMatrix, matrix_id); } /// callback to assemble a lumped Matrix void assembleLumpedMatrix(const ID & matrix_id) override { // NOLINTNEXTLINE PYBIND11_OVERRIDE(void, InterceptSolverCallback, assembleLumpedMatrix, matrix_id); } void assembleResidual() override { // NOLINTNEXTLINE PYBIND11_OVERRIDE(void, InterceptSolverCallback, assembleResidual); } void predictor() override { // NOLINTNEXTLINE PYBIND11_OVERRIDE(void, InterceptSolverCallback, predictor); } void corrector() override { // NOLINTNEXTLINE PYBIND11_OVERRIDE(void, InterceptSolverCallback, corrector); } void beforeSolveStep() override { // NOLINTNEXTLINE PYBIND11_OVERRIDE(void, InterceptSolverCallback, beforeSolveStep); } void afterSolveStep(bool converged) override { // NOLINTNEXTLINE PYBIND11_OVERRIDE(void, InterceptSolverCallback, afterSolveStep, converged); } }; } // namespace /* -------------------------------------------------------------------------- */ void register_dof_manager(py::module & mod) { py::class_>(mod, "DOFManager") .def("getMatrix", &DOFManager::getMatrix, py::return_value_policy::reference) .def( "getNewMatrix", [](DOFManager & self, const std::string & name, const std::string & matrix_to_copy_id) -> decltype(auto) { return self.getNewMatrix(name, matrix_to_copy_id); }, py::return_value_policy::reference) .def( "getResidual", [](DOFManager & self) -> decltype(auto) { return self.getResidual(); }, py::return_value_policy::reference) .def("getArrayPerDOFs", &DOFManager::getArrayPerDOFs) .def( "hasMatrix", [](DOFManager & self, const ID & name) -> bool { return self.hasMatrix(name); }, py::arg("name")) .def("assembleToResidual", &DOFManager::assembleToResidual, py::arg("dof_id"), py::arg("array_to_assemble"), py::arg("scale_factor") = 1.) .def("assembleToLumpedMatrix", &DOFManager::assembleToLumpedMatrix, py::arg("dof_id"), py::arg("array_to_assemble"), py::arg("lumped_mtx"), py::arg("scale_factor") = 1.) .def("assemblePreassembledMatrix", &DOFManager::assemblePreassembledMatrix, py::arg("matrix_id"), - py::arg("terms")); + py::arg("terms")) + .def("zeroResidual", &DOFManager::zeroResidual); py::class_(mod, "NonLinearSolver") .def( "set", [](NonLinearSolver & self, const std::string & id, const Real & val) { if (id == "max_iterations") { self.set(id, int(val)); } else { self.set(id, val); } }) .def("set", [](NonLinearSolver & self, const std::string & id, const SolveConvergenceCriteria & val) { self.set(id, val); }); + py::class_(mod, "TimeStepSolver") + .def("getIntegrationScheme", &TimeStepSolver::getIntegrationScheme); + py::class_(mod, "SolverCallback") .def(py::init_alias()) .def("getMatrixType", &SolverCallback::getMatrixType) .def("assembleMatrix", &SolverCallback::assembleMatrix) .def("assembleLumpedMatrix", &SolverCallback::assembleLumpedMatrix) .def("assembleResidual", [](SolverCallback & self) { self.assembleResidual(); }) .def("predictor", &SolverCallback::predictor) .def("corrector", &SolverCallback::corrector) .def("beforeSolveStep", &SolverCallback::beforeSolveStep) .def("afterSolveStep", &SolverCallback::afterSolveStep) .def_property_readonly("dof_manager", &SolverCallback::getSCDOFManager, py::return_value_policy::reference); py::class_(mod, "InterceptSolverCallback") .def(py::init_alias()); } } // namespace akantu diff --git a/python/py_group_manager.cc b/python/py_group_manager.cc index ada654e20..a7c14a63b 100644 --- a/python/py_group_manager.cc +++ b/python/py_group_manager.cc @@ -1,176 +1,179 @@ /** * @file py_group_manager.cc * * @author Guillaume Anciaux * @author Nicolas Richart * * @date creation: Sun Jun 16 2019 * @date last modification: Mon Dec 02 2019 * * @brief pybind11 interface to GroupManager, ElementGroup and NodeGroup * * * @section LICENSE * * Copyright (©) 2018-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "py_aka_array.hh" /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ namespace py = pybind11; /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ void register_group_manager(py::module & mod) { /* ------------------------------------------------------------------------ */ py::class_(mod, "NodeGroup") .def( "getNodes", [](NodeGroup & self) -> decltype(auto) { return self.getNodes(); }, py::return_value_policy::reference) .def("__len__", &NodeGroup::size) .def( "__iter__", [](const NodeGroup & self) { return py::make_iterator(self.begin(), self.end()); }, py::keep_alive<0, 1>()) .def("__contains__", [](const NodeGroup & self, UInt node) { return self.find(node) != -1; }) .def("getName", &NodeGroup::getName) .def("clear", &NodeGroup::clear) .def("empty", &NodeGroup::empty) .def("append", &NodeGroup::append) .def("add", &NodeGroup::add, py::arg("node"), py::arg("check_for_duplicate") = true) .def("remove", &NodeGroup::add); /* ------------------------------------------------------------------------ */ py::class_(mod, "ElementGroup") .def( "getNodeGroup", [](ElementGroup & self) -> decltype(auto) { return self.getNodeGroup(); }, py::return_value_policy::reference) .def("getName", &ElementGroup::getName) .def( "getElements", [](ElementGroup & self) -> decltype(auto) { return self.getElements(); }, py::return_value_policy::reference) .def( "getNodeGroup", [](ElementGroup & self) -> decltype(auto) { return self.getNodeGroup(); }, py::return_value_policy::reference) .def("__len__", [](const ElementGroup & self) { return self.size(); }) .def("clear", [](ElementGroup & self) { self.clear(); }) .def("empty", &ElementGroup::empty) .def("append", &ElementGroup::append) + .def("optimize", &ElementGroup::optimize) .def( "add", [](ElementGroup & self, const Element & element, bool add_nodes, bool check_for_duplicate) { self.add(element, add_nodes, check_for_duplicate); }, py::arg("element"), py::arg("add_nodes") = false, py::arg("check_for_duplicate") = true) .def("fillFromNodeGroup", &ElementGroup::fillFromNodeGroup) .def("addDimension", &ElementGroup::addDimension); /* ------------------------------------------------------------------------ */ py::class_(mod, "GroupManager") .def( "getElementGroup", [](GroupManager & self, const std::string & name) -> decltype(auto) { return self.getElementGroup(name); }, py::return_value_policy::reference) .def("iterateElementGroups", [](GroupManager & self) -> decltype(auto) { std::vector> groups; for (auto & group : self.iterateElementGroups()) { groups.emplace_back(group); } return groups; }) .def("iterateNodeGroups", [](GroupManager & self) -> decltype(auto) { std::vector> groups; for (auto & group : self.iterateNodeGroups()) { groups.emplace_back(group); } return groups; }) .def("createNodeGroup", &GroupManager::createNodeGroup, py::return_value_policy::reference) .def( "createElementGroup", [](GroupManager & self, const std::string & id, Int spatial_dimension, - bool b) -> decltype(auto) { - return self.createElementGroup(id, spatial_dimension, b); + bool replace_group) -> decltype(auto) { + return self.createElementGroup(id, spatial_dimension, + replace_group); }, - py::return_value_policy::reference) + py::arg("id"), py::arg("spatial_dimension"), + py::arg("replace_group") = false, py::return_value_policy::reference) .def("createGroupsFromMeshDataUInt", &GroupManager::createGroupsFromMeshData) .def("createElementGroupFromNodeGroup", &GroupManager::createElementGroupFromNodeGroup, py::arg("name"), py::arg("node_group"), py::arg("dimension") = _all_dimensions) .def( "getNodeGroup", [](GroupManager & self, const std::string & name) -> decltype(auto) { return self.getNodeGroup(name); }, py::return_value_policy::reference) .def( "nodeGroups", [](GroupManager & self) { std::vector groups; for (auto & g : self.iterateNodeGroups()) { groups.push_back(&g); } return groups; }, py::return_value_policy::reference) .def( "elementGroups", [](GroupManager & self) { std::vector groups; for (auto & g : self.iterateElementGroups()) { groups.push_back(&g); } return groups; }, py::return_value_policy::reference) .def("createBoundaryGroupFromGeometry", &GroupManager::createBoundaryGroupFromGeometry); } } // namespace akantu diff --git a/python/py_integration_scheme.cc b/python/py_integration_scheme.cc new file mode 100644 index 000000000..c06bdf321 --- /dev/null +++ b/python/py_integration_scheme.cc @@ -0,0 +1,65 @@ +/** + * Copyright (©) 2022 EPFL (Ecole Polytechnique Fédérale de Lausanne) + * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) + * + * Akantu is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * Akantu 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 Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Akantu. If not, see . + */ + +/* -------------------------------------------------------------------------- */ +#include "py_integration_scheme.hh" +/* -------------------------------------------------------------------------- */ +#include +#include +#include +#include +/* -------------------------------------------------------------------------- */ +#include +/* -------------------------------------------------------------------------- */ +namespace py = pybind11; + +namespace akantu { + +/* -------------------------------------------------------------------------- */ +void register_integration_schemes(py::module & mod) { + auto integration_scheme_class = + py::class_(mod, "IntegrationScheme"); + + py::enum_(integration_scheme_class, + "SolutionType") + .value("_not_defined", IntegrationScheme::SolutionType::_not_defined) + .value("_displacement", IntegrationScheme::SolutionType::_displacement) + .value("_temperature", IntegrationScheme::SolutionType::_temperature) + .value("_damage", IntegrationScheme::SolutionType::_damage) + .value("_velocity", IntegrationScheme::SolutionType::_velocity) + .value("_temperature_rate", + IntegrationScheme::SolutionType::_temperature_rate) + .value("_acceleration", IntegrationScheme::SolutionType::_acceleration) + .export_values(); + + py::class_( + mod, "IntegrationScheme2ndOrder"); + + py::class_(mod, "NewmarkBeta") + .def(py::init(), + py::arg("dof_manager"), py::arg("id"), py::arg("alpha"), + py::arg("beta")) + .def_property("alpha", &NewmarkBeta::getAlpha, &NewmarkBeta::setAlpha) + .def_property("beta", &NewmarkBeta::getBeta, &NewmarkBeta::setBeta); + + py::class_(mod, "CentralDifference"); + py::class_(mod, "TrapezoidalRule2"); + py::class_(mod, "FoxGoodwin"); + py::class_(mod, "LinearAceleration"); +} +} // namespace akantu diff --git a/python/py_integration_scheme.hh b/python/py_integration_scheme.hh new file mode 100644 index 000000000..5b0deea4d --- /dev/null +++ b/python/py_integration_scheme.hh @@ -0,0 +1,32 @@ +/** + * Copyright (©) 2022 EPFL (Ecole Polytechnique Fédérale de Lausanne) + * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) + * + * Akantu is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * Akantu 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 Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Akantu. If not, see . + * + */ + +/* -------------------------------------------------------------------------- */ +#include + +#ifndef AKANTU_PY_INTEGRATION_SCHEME_HH +#define AKANTU_PY_INTEGRATION_SCHEME_HH + +namespace akantu { + +void register_integration_schemes(pybind11::module & mod); + +} + +#endif // AKANTU_PY_INTEGRATION_SCHEME_HH diff --git a/python/py_material.cc b/python/py_material.cc index a57346bb3..f06ba10d2 100644 --- a/python/py_material.cc +++ b/python/py_material.cc @@ -1,213 +1,261 @@ /** * @file py_material.cc * * @author Guillaume Anciaux * @author Mohit Pundir * @author Nicolas Richart * * @date creation: Thu Jun 20 2019 * @date last modification: Fri Apr 09 2021 * * @brief pybind11 interface to Material * * * @section LICENSE * * Copyright (©) 2018-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "py_aka_array.hh" #include "py_akantu_pybind11_compatibility.hh" /* -------------------------------------------------------------------------- */ #include #include #if defined(AKANTU_COHESIVE_ELEMENT) #include #endif #include /* -------------------------------------------------------------------------- */ #include #include #include /* -------------------------------------------------------------------------- */ namespace py = pybind11; /* -------------------------------------------------------------------------- */ namespace akantu { namespace { template class PyMaterial : public _Material { public: /* Inherit the constructors */ using _Material::_Material; ~PyMaterial() override = default; void initMaterial() override { // NOLINTNEXTLINE PYBIND11_OVERRIDE(void, _Material, initMaterial, ); }; void computeStress(ElementType el_type, GhostType ghost_type = _not_ghost) override { // NOLINTNEXTLINE PYBIND11_OVERRIDE_PURE(void, _Material, computeStress, el_type, ghost_type); } void computeTangentModuli(ElementType el_type, Array & tangent_matrix, GhostType ghost_type = _not_ghost) override { // NOLINTNEXTLINE PYBIND11_OVERRIDE(void, _Material, computeTangentModuli, el_type, tangent_matrix, ghost_type); } void computePotentialEnergy(ElementType el_type) override { // NOLINTNEXTLINE PYBIND11_OVERRIDE(void, _Material, computePotentialEnergy, el_type); } Real getPushWaveSpeed(const Element & element) const override { // NOLINTNEXTLINE PYBIND11_OVERRIDE(Real, _Material, getPushWaveSpeed, element); } Real getShearWaveSpeed(const Element & element) const override { // NOLINTNEXTLINE PYBIND11_OVERRIDE(Real, _Material, getShearWaveSpeed, element); } template void registerInternal(const std::string & name, UInt nb_component) { auto && internal = std::make_shared>(name, *this); AKANTU_DEBUG_INFO("alloc internal " << name << " " << &this->internals[name]); internal->initialize(nb_component); this->internals[name] = internal; } protected: std::map> internals; }; /* ------------------------------------------------------------------------ */ template void register_internal_field(py::module & mod, const std::string & name) { py::class_, ElementTypeMapArray, std::shared_ptr>>( mod, ("InternalField" + name).c_str()); } /* ------------------------------------------------------------------------ */ template void register_material_classes(py::module & mod, const std::string & name) { py::class_<_Material, Material, Parsable, PyMaterial<_Material>>( mod, name.c_str(), py::multiple_inheritance()) .def(py::init()); } } // namespace /* -------------------------------------------------------------------------- */ void register_material(py::module & mod) { py::class_(mod, "MaterialFactory") .def_static( "getInstance", []() -> MaterialFactory & { return Material::getFactory(); }, py::return_value_policy::reference) .def("registerAllocator", [](MaterialFactory & self, const std::string id, py::function func) { self.registerAllocator( id, [func, id](Int dim, const ID & /*unused*/, SolidMechanicsModel & model, const ID & option) -> std::unique_ptr { py::object obj = func(dim, id, model, option); auto & ptr = py::cast(obj); obj.release(); return std::unique_ptr(&ptr); }); }) .def("getPossibleAllocators", &MaterialFactory::getPossibleAllocators); register_internal_field(mod, "Real"); register_internal_field(mod, "UInt"); py::class_>( mod, "Material", py::multiple_inheritance()) .def(py::init()) .def( "getGradU", [](Material & self, ElementType el_type, GhostType ghost_type = _not_ghost) -> decltype(auto) { return self.getGradU(el_type, ghost_type); }, py::arg("el_type"), py::arg("ghost_type") = _not_ghost, py::return_value_policy::reference) .def( "getStress", [](Material & self, ElementType el_type, GhostType ghost_type = _not_ghost) -> decltype(auto) { return self.getStress(el_type, ghost_type); }, py::arg("el_type"), py::arg("ghost_type") = _not_ghost, py::return_value_policy::reference) .def( "getPotentialEnergy", [](Material & self, ElementType el_type) -> decltype(auto) { return self.getPotentialEnergy(el_type); }, - py::return_value_policy::reference) + py::arg("el_type"), py::return_value_policy::reference) + .def( + "getPotentialEnergy", + [](Material & self, ElementType el_type, UInt index) -> Real { + return self.getPotentialEnergy(el_type, index); + }, + py::arg("el_type"), py::arg("index")) + .def("getPotentialEnergy", + [](Material & self) -> Real { return self.getPotentialEnergy(); }) .def("initMaterial", &Material::initMaterial) .def("getModel", &Material::getModel) .def("registerInternalReal", [](Material & self, const std::string & name, UInt nb_component) { return dynamic_cast &>(self) .registerInternal(name, nb_component); }) .def("registerInternalUInt", [](Material & self, const std::string & name, UInt nb_component) { return dynamic_cast &>(self) .registerInternal(name, nb_component); }) .def( "getInternalReal", [](Material & self, const ID & id) -> decltype(auto) { return self.getInternal(id); }, py::arg("id"), py::return_value_policy::reference) .def( "getInternalUInt", [](Material & self, const ID & id) -> decltype(auto) { return self.getInternal(id); }, py::arg("id"), py::return_value_policy::reference) .def( "getElementFilter", [](Material & self) -> decltype(auto) { return self.getElementFilter(); }, py::return_value_policy::reference) + + /* + * These functions override the `Parsable` interface. + * This ensure that the `updateInternalParameters()` function is called. + */ + .def( + "setReal", + [](Material & self, const ID & name, const Real value) -> void { + self.setParam(name, value); + return; + }, + py::arg("name"), py::arg("value")) + .def( + "setBool", + [](Material & self, const ID & name, const bool value) -> void { + self.setParam(name, value); + return; + }, + py::arg("name"), py::arg("value")) + .def( + "setString", + [](Material & self, const ID & name, + const std::string & value) -> void { + self.setParam(name, value); + return; + }, + py::arg("name"), py::arg("value")) + .def( + "setInt", + [](Material & self, const ID & name, const int value) -> void { + self.setParam(name, value); + return; + }, + py::arg("name"), py::arg("value")) + .def("getPushWaveSpeed", &Material::getPushWaveSpeed) - .def("getShearWaveSpeed", &Material::getShearWaveSpeed); + .def("getShearWaveSpeed", &Material::getShearWaveSpeed) + .def("__repr__", [](Material & self) { + std::stringstream sstr; + sstr << self; + return sstr.str(); + }); register_material_classes>(mod, "MaterialElastic2D"); register_material_classes>(mod, "MaterialElastic3D"); } } // namespace akantu diff --git a/python/py_material_selector.cc b/python/py_material_selector.cc index 9dc3e0408..207dad93d 100644 --- a/python/py_material_selector.cc +++ b/python/py_material_selector.cc @@ -1,117 +1,117 @@ /** * @file py_material_selector.cc * * @author Nicolas Richart * * @date creation: Wed May 26 2021 * @date last modification: Wed May 26 2021 * * @brief Material selector python binding * * * @section LICENSE * * Copyright (©) 2018-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "py_material_selector.hh" #include "py_akantu_pybind11_compatibility.hh" /* -------------------------------------------------------------------------- */ #include #include #if defined(AKANTU_COHESIVE_ELEMENT) #include #include #endif /* -------------------------------------------------------------------------- */ #include #include #include /* -------------------------------------------------------------------------- */ namespace py = pybind11; /* -------------------------------------------------------------------------- */ namespace akantu { namespace { template class PyMaterialSelector : public Base { public: /* Inherit the constructors */ using Base::Base; ~PyMaterialSelector() override = default; Idx operator()(const Element & element) override { // NOLINTNEXTLINE PYBIND11_OVERRIDE_NAME(Idx, MaterialSelector, "__call__", operator(), element); } }; template decltype(auto) register_material_selectors(py::module & mod, const std::string & class_name) { return py::class_, std::shared_ptr>( mod, class_name.c_str()); } } // namespace void register_material_selector(py::module & mod) { py::class_, std::shared_ptr>(mod, "MaterialSelector") .def(py::init()) .def("setFallback", [](MaterialSelector & self, UInt f) { self.setFallback(f); }) .def("setFallback", [](MaterialSelector & self, const std::shared_ptr & fallback_selector) { self.setFallback(fallback_selector); }) .def("__call__", &MaterialSelector::operator()); register_material_selectors( mod, "DefaultMaterialSelector") - .def(py::init>()); + .def(py::init &>()); register_material_selectors>( mod, "MeshDataMaterialSelectorString") .def(py::init(), py::arg("name"), py::arg("model"), py::arg("first_index") = 1); #if defined(AKANTU_COHESIVE_ELEMENT) register_material_selectors( mod, "DefaultMaterialCohesiveSelector") .def(py::init()); register_material_selectors( mod, "MeshDataMaterialCohesiveSelector") .def(py::init()); register_material_selectors( mod, "MaterialCohesiveRulesSelector") .def(py::init(), py::arg("model"), py::arg("rules"), py::arg("mesh_data_id") = "physical_names"); #endif } } // namespace akantu diff --git a/python/py_mesh.cc b/python/py_mesh.cc index 314a821e2..bbe55b43a 100644 --- a/python/py_mesh.cc +++ b/python/py_mesh.cc @@ -1,207 +1,240 @@ /** * @file py_mesh.cc * * @author Guillaume Anciaux * @author Philip Mueller * @author Mohit Pundir * @author Nicolas Richart * * @date creation: Sun Jun 16 2019 * @date last modification: Mon Mar 15 2021 * * @brief pybind11 interface to Mesh * * * @section LICENSE * * Copyright (©) 2018-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_config.hh" /* -------------------------------------------------------------------------- */ #include "py_aka_array.hh" /* -------------------------------------------------------------------------- */ #include #include #include /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ namespace py = pybind11; /* -------------------------------------------------------------------------- */ namespace akantu { namespace { /* ------------------------------------------------------------------------ */ template - void register_element_type_map_array(py::module & mod, - const std::string & name) { - py::class_, std::shared_ptr>>( - mod, ("ElementTypeMapArray" + name).c_str()) + decltype(auto) register_element_type_map_array(py::module & mod, + const std::string & name) { + return py::class_, + std::shared_ptr>>( + mod, ("ElementTypeMapArray" + name).c_str()) + .def(py::init(), + py::arg("id") = "by_element_type_array", + py::arg("parent_id") = "no_parent") .def( "__call__", [](ElementTypeMapArray & self, ElementType type, GhostType ghost_type) -> decltype(auto) { return self(type, ghost_type); }, py::arg("type"), py::arg("ghost_type") = _not_ghost, - py::return_value_policy::reference) + py::return_value_policy::reference, py::keep_alive<0, 1>()) .def( "elementTypes", [](ElementTypeMapArray & self, UInt _dim, GhostType _ghost_type, ElementKind _kind) -> std::vector { auto types = self.elementTypes(_dim, _ghost_type, _kind); std::vector _types; for (auto && t : types) { _types.push_back(t); } return _types; }, py::arg("dim") = _all_dimensions, - py::arg("ghost_type") = _not_ghost, py::arg("kind") = _ek_regular); + py::arg("ghost_type") = _not_ghost, py::arg("kind") = _ek_regular) + .def( + "initialize", + [](ElementTypeMapArray & self, const Mesh & mesh, + GhostType ghost_type = _casper, UInt nb_component = 1, + UInt spatial_dimension = UInt(-2), + ElementKind element_kind = _ek_not_defined, + bool with_nb_element = false, + bool with_nb_nodes_per_element = false, T default_value = T(), + bool do_not_default = false) { + self.initialize( + mesh, _ghost_type = ghost_type, _nb_component = nb_component, + _spatial_dimension = (spatial_dimension == UInt(-2) + ? mesh.getSpatialDimension() + : spatial_dimension), + _element_kind = element_kind, + _with_nb_element = with_nb_element, + _with_nb_nodes_per_element = with_nb_nodes_per_element, + _default_value = default_value, + _do_not_default = do_not_default); + }, + py::arg("mesh"), py::arg("ghost_type") = _casper, + py::arg("nb_component") = 1, + py::arg("spatial_dimension") = UInt(-2), + py::arg("element_kind") = _ek_not_defined, + py::arg("with_nb_element") = false, + py::arg("with_nb_nodes_per_element") = false, + py::arg("default_value") = T(), py::arg("do_not_default") = false); } } // namespace /* -------------------------------------------------------------------------- */ void register_mesh(py::module & mod) { - - register_element_type_map_array(mod, "Real"); - register_element_type_map_array(mod, "UInt"); - // register_element_type_map_array(mod, "String"); - py::class_(mod, "PeriodicSlaves") .def( "__iter__", [](Mesh::PeriodicSlaves & _this) { py::make_iterator(_this.begin(), _this.end()); }, py::keep_alive<0, 1>()); py::class_(mod, "MeshData") .def( "getElementalDataUInt", [](MeshData & _this, const ID & name) -> decltype(auto) { return _this.getElementalData(name); }, py::return_value_policy::reference) .def( "getElementalDataReal", [](MeshData & _this, const ID & name) -> decltype(auto) { return _this.getElementalData(name); }, py::return_value_policy::reference); py::class_(mod, "Mesh", py::multiple_inheritance()) .def(py::init(), py::arg("spatial_dimension"), py::arg("id") = "mesh") .def("read", &Mesh::read, py::arg("filename"), py::arg("mesh_io_type") = _miot_auto, "read the mesh from a file") .def( "getNodes", [](Mesh & self) -> decltype(auto) { return self.getNodes(); }, py::return_value_policy::reference) .def("getNbNodes", &Mesh::getNbNodes) .def( "getConnectivity", [](Mesh & self, ElementType type) -> decltype(auto) { return self.getConnectivity(type); }, py::return_value_policy::reference) .def( "addConnectivityType", [](Mesh & self, ElementType type, GhostType ghost_type) -> void { self.addConnectivityType(type, ghost_type); }, py::arg("type"), py::arg("ghost_type") = _not_ghost) .def("distribute", [](Mesh & self) { self.distribute(); }) + .def("isDistributed", [](const Mesh& self) { return self.isDistributed(); }) .def("fillNodesToElements", &Mesh::fillNodesToElements, py::arg("dimension") = _all_dimensions) .def("getAssociatedElements", [](Mesh & self, const UInt & node, py::list list) { Array elements; self.getAssociatedElements(node, elements); for (auto && element : elements) { list.append(element); } }) .def("makePeriodic", [](Mesh & self, const SpatialDirection & direction) { self.makePeriodic(direction); }) .def( "getNbElement", [](Mesh & self, const Int spatial_dimension, GhostType ghost_type, ElementKind kind) { return self.getNbElement(spatial_dimension, ghost_type, kind); }, py::arg("spatial_dimension") = _all_dimensions, py::arg("ghost_type") = _not_ghost, py::arg("kind") = _ek_not_defined) .def( "getNbElement", [](Mesh & self, ElementType type, GhostType ghost_type) { return self.getNbElement(type, ghost_type); }, py::arg("type"), py::arg("ghost_type") = _not_ghost) .def_static( "getSpatialDimension", [](ElementType & type) { return Mesh::getSpatialDimension(type); }) .def( "getDataReal", [](Mesh & _this, const ID & name, ElementType type, GhostType ghost_type) -> decltype(auto) { return _this.getData(name, type, ghost_type); }, py::arg("name"), py::arg("type"), py::arg("ghost_type") = _not_ghost, py::return_value_policy::reference) .def( "hasDataReal", [](Mesh & _this, const ID & name, ElementType type, GhostType ghost_type) -> bool { return _this.hasData(name, type, ghost_type); }, py::arg("name"), py::arg("type"), py::arg("ghost_type") = _not_ghost) .def("isPeriodic", [](const Mesh & _this) { return _this.isPeriodic(); }) .def("getPeriodicMaster", &Mesh::getPeriodicMaster) .def("getPeriodicSlaves", &Mesh::getPeriodicSlaves) .def("isPeriodicSlave", &Mesh::isPeriodicSlave) - .def("isPeriodicMaster", &Mesh::isPeriodicMaster); + .def("isPeriodicMaster", &Mesh::isPeriodicMaster) + .def("initMeshFacets", &Mesh::initMeshFacets, + py::arg("id") = "mesh_facets", py::return_value_policy::reference); /* ------------------------------------------------------------------------ */ py::class_(mod, "MeshUtils") .def_static("buildFacets", &MeshUtils::buildFacets); py::class_(mod, "MeshAccessor") .def(py::init(), py::arg("mesh")) .def( "resizeConnectivity", [](MeshAccessor & self, UInt new_size, ElementType type, GhostType gt) -> void { self.resizeConnectivity(new_size, type, gt); }, py::arg("new_size"), py::arg("type"), py::arg("ghost_type") = _not_ghost) .def( "resizeNodes", [](MeshAccessor & self, UInt new_size) -> void { self.resizeNodes(new_size); }, py::arg("new_size")) .def("makeReady", &MeshAccessor::makeReady); + + register_element_type_map_array(mod, "Real"); + register_element_type_map_array(mod, "UInt"); + // register_element_type_map_array(mod, "String"); } } // namespace akantu diff --git a/python/py_model.cc b/python/py_model.cc index fe0718911..4f0a9312c 100644 --- a/python/py_model.cc +++ b/python/py_model.cc @@ -1,128 +1,149 @@ /** * @file py_model.cc * * @author Guillaume Anciaux * @author Emil Gallyamov * @author Philip Mueller * @author Mohit Pundir * @author Nicolas Richart * * @date creation: Sun Jun 16 2019 * @date last modification: Sat Mar 13 2021 * * @brief pybind11 interface to Model and parent classes * * * @section LICENSE * * Copyright (©) 2018-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "py_aka_array.hh" /* -------------------------------------------------------------------------- */ #include #include #include #include +#include /* -------------------------------------------------------------------------- */ #include #include #include /* -------------------------------------------------------------------------- */ namespace py = pybind11; /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ void register_model(py::module & mod) { py::class_(mod, "ModelSolver", py::multiple_inheritance()) - .def("getNonLinearSolver", - (NonLinearSolver & (ModelSolver::*)(const ID &)) & - ModelSolver::getNonLinearSolver, - py::arg("solver_id") = "", py::return_value_policy::reference) + .def( + "getNonLinearSolver", + [](ModelSolver & self, const ID & solver_id) -> NonLinearSolver & { + return self.getNonLinearSolver(solver_id); + }, + py::arg("solver_id") = "", py::return_value_policy::reference) + .def( + "getTimeStepSolver", + [](ModelSolver & self, const ID & solver_id) -> TimeStepSolver & { + return self.getTimeStepSolver(solver_id); + }, + py::arg("solver_id") = "", py::return_value_policy::reference) .def( "solveStep", [](ModelSolver & self, const ID & solver_id) { self.solveStep(solver_id); }, py::arg("solver_id") = "") .def( "solveStep", [](ModelSolver & self, SolverCallback & callback, const ID & solver_id) { self.solveStep(callback, solver_id); }, py::arg("callback"), py::arg("solver_id") = ""); py::class_(mod, "Model", py::multiple_inheritance()) .def("setBaseName", &Model::setBaseName) .def("setDirectory", &Model::setDirectory) .def("getFEEngine", &Model::getFEEngine, py::arg("name") = "", py::return_value_policy::reference) .def("getFEEngineBoundary", &Model::getFEEngine, py::arg("name") = "", py::return_value_policy::reference) .def("addDumpFieldVector", &Model::addDumpFieldVector) .def("addDumpField", &Model::addDumpField) .def("setBaseNameToDumper", &Model::setBaseNameToDumper) .def("addDumpFieldVectorToDumper", &Model::addDumpFieldVectorToDumper) .def("addDumpFieldToDumper", &Model::addDumpFieldToDumper) .def("dump", [](Model & self) { self.dump(); }) .def( "dump", [](Model & self, UInt step) { self.dump(step); }, py::arg("step")) .def( "dump", [](Model & self, Real time, UInt step) { self.dump(time, step); }, py::arg("time"), py::arg("step")) .def( "dump", [](Model & self, const std::string & dumper) { self.dump(dumper); }, py::arg("dumper_name")) .def( "dump", [](Model & self, const std::string & dumper, UInt step) { self.dump(dumper, step); }, py::arg("dumper_name"), py::arg("step")) .def( "dump", [](Model & self, const std::string & dumper, Real time, UInt step) { self.dump(dumper, time, step); }, py::arg("dumper_name"), py::arg("time"), py::arg("step")) .def("initNewSolver", &Model::initNewSolver) .def( "getNewSolver", [](Model & self, const std::string id, const TimeStepSolverType & time, const NonLinearSolverType & type) { self.getNewSolver(id, time, type); }, py::return_value_policy::reference) - .def("setIntegrationScheme", - [](Model & self, const std::string id, const std::string primal, - const IntegrationSchemeType & scheme) { - self.setIntegrationScheme(id, primal, scheme); - }) + .def( + "setIntegrationScheme", + [](Model & self, const std::string id, const std::string primal, + const IntegrationSchemeType & scheme_type, + IntegrationScheme::SolutionType solution_type) { + self.setIntegrationScheme(id, primal, scheme_type, solution_type); + }, + py::arg("id"), py::arg("primal"), py::arg("scheme_type"), + py::arg("solution_type") = + IntegrationScheme::SolutionType::_not_defined) + // .def("setIntegrationScheme", + // [](Model & self, const std::string id, const std::string primal, + // std::unique_ptr & scheme, + // IntegrationScheme::SolutionType solution_type) { + // self.setIntegrationScheme(id, primal, scheme, solution_type); + // }) + .def("getDOFManager", &Model::getDOFManager, py::return_value_policy::reference) .def("assembleMatrix", &Model::assembleMatrix); } } // namespace akantu diff --git a/python/py_model_couplers.cc b/python/py_model_couplers.cc index 7cc6f6424..9ab064d05 100644 --- a/python/py_model_couplers.cc +++ b/python/py_model_couplers.cc @@ -1,122 +1,121 @@ /** * @file py_model_couplers.cc * * @author Mohit Pundir * @author Nicolas Richart * * @date creation: Thu Jun 20 2019 * @date last modification: Thu Jun 24 2021 * * @brief Model Coupler python binding * * * @section LICENSE * * Copyright (©) 2018-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "py_aka_array.hh" /* -------------------------------------------------------------------------- */ +#include #include #include #include #include /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ namespace py = pybind11; /* -------------------------------------------------------------------------- */ namespace akantu { namespace { template auto register_coupler_solid_contact(py::module & mod, const std::string & name) -> py::class_ { return py::class_(mod, name.c_str(), py::multiple_inheritance()) - .def(py::init, - const ModelType>(), + .def(py::init>(), py::arg("mesh"), py::arg("spatial_dimension") = _all_dimensions, py::arg("id") = "coupler_solid_contact", - py::arg("dof_manager") = nullptr, - py::arg("model_type") = ModelType::_coupler_solid_contact) + py::arg("dof_manager") = nullptr) .def("applyBC", [](CouplerSolidContact_ & self, BC::Dirichlet::DirichletFunctor & func, const std::string & element_group) { self.applyBC(func, element_group); }) .def("applyBC", [](CouplerSolidContact_ & self, BC::Neumann::NeumannFunctor & func, const std::string & element_group) { self.applyBC(func, element_group); }) .def("setTimeStep", &CouplerSolidContact_::setTimeStep, py::arg("time_step"), py::arg("solver_id") = "") .def("getContactMechanicsModel", &CouplerSolidContact_::getContactMechanicsModel, py::return_value_policy::reference); } } // namespace /* -------------------------------------------------------------------------- */ void register_model_couplers(py::module & mod) { register_coupler_solid_contact(mod, "CouplerSolidContact") .def( "getSolidMechanicsModel", [](CouplerSolidContact & self) -> decltype(auto) { return self.getSolidMechanicsModel(); }, py::return_value_policy::reference) .def( "initFull", [](CouplerSolidContact & self, const AnalysisMethod & analysis_method) { self.initFull(_analysis_method = analysis_method); }, py::arg("_analysis_method") = _explicit_lumped_mass); register_coupler_solid_contact( mod, "CouplerSolidCohesiveContact") .def( "initFull", [](CouplerSolidCohesiveContact & self, const AnalysisMethod & analysis_method, bool is_extrinsic) { self.initFull(_analysis_method = analysis_method, _is_extrinsic = is_extrinsic); }, py::arg("_analysis_method") = _explicit_lumped_mass, py::arg("_is_extrinsic") = false) .def("checkCohesiveStress", [](CouplerSolidCohesiveContact & self) { return self.checkCohesiveStress(); }) .def( "getSolidMechanicsModelCohesive", [](CouplerSolidCohesiveContact & self) -> decltype(auto) { return self.getSolidMechanicsModelCohesive(); }, py::return_value_policy::reference); } } // namespace akantu diff --git a/python/py_parser.cc b/python/py_parser.cc index dc66fade8..c3673d9d0 100644 --- a/python/py_parser.cc +++ b/python/py_parser.cc @@ -1,103 +1,131 @@ /** * @file py_parser.cc * * @author Mohit Pundir * @author Nicolas Richart * * @date creation: Tue Sep 29 2020 * @date last modification: Mon Mar 01 2021 * * @brief pybind11 interface to Mesh * * * @section LICENSE * * Copyright (©) 2018-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "py_aka_array.hh" /* -------------------------------------------------------------------------- */ #include #include #include #include /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ namespace py = pybind11; /* -------------------------------------------------------------------------- */ namespace akantu { std::map> map_params; void register_parser(py::module & mod) { py::enum_(mod, "ParameterAccessType", py::arithmetic()) .value("_pat_internal", _pat_internal) .value("_pat_writable", _pat_writable) .value("_pat_readable", _pat_readable) .value("_pat_modifiable", _pat_modifiable) .value("_pat_parsable", _pat_parsable) .value("_pat_parsmod", _pat_parsmod) .export_values(); py::class_(mod, "ParameterRegistry", py::multiple_inheritance()) .def("registerParamReal", [](ParameterRegistry & self, const std::string & name, UInt type, const std::string & description) { Real * p = new Real; map_params[&self][name] = p; self.registerParam(name, *p, ParameterAccessType(type), description); }) .def("registerParamReal", [](ParameterRegistry & self, const Real & _default, const std::string & name, UInt type, const std::string & description) { Real * p = new Real; map_params[&self][name] = p; self.registerParam(name, *p, _default, ParameterAccessType(type), description); }) + + .def("setReal", [](ParameterRegistry & self, const std::string & name, + const Real value) { self.set(name, value); }) .def("getReal", - [](ParameterRegistry & self, const std::string & name) { - return Real(self.get(name)); + [](ParameterRegistry & self, const std::string & name) -> Real { + return self.get(name); + }) + + .def("setBool", [](ParameterRegistry & self, const std::string & name, + const bool value) { self.set(name, value); }) + .def("getBool", + [](ParameterRegistry & self, const std::string & name) -> bool { + return self.get(name); + }) + + .def("setString", + [](ParameterRegistry & self, const std::string & name, + const std::string & value) { self.set(name, value); }) + .def("getString", + [](ParameterRegistry & self, + const std::string & name) -> std::string { + std::string tmp = self.get(name); + return tmp; + }) + + .def("setInt", [](ParameterRegistry & self, const std::string & name, + const Int value) { self.set(name, value); }) + .def("getInt", + [](ParameterRegistry & self, const std::string & name) -> Int { + return self.get(name); }) + .def( "getMatrix", [](ParameterRegistry & self, const std::string & name) { const Matrix & res = static_cast &>(self.get(name)); return res; }, py::return_value_policy::copy); py::class_(mod, "Parsable", py::multiple_inheritance()) .def(py::init()); mod.def( "parseInput", [](const std::string & input_file) { getStaticParser().parse(input_file); }, "Parse an Akantu input file"); } } // namespace akantu diff --git a/python/py_solid_mechanics_model.cc b/python/py_solid_mechanics_model.cc index e5d823f0c..bd8d801fc 100644 --- a/python/py_solid_mechanics_model.cc +++ b/python/py_solid_mechanics_model.cc @@ -1,165 +1,186 @@ /** * @file py_solid_mechanics_model.cc * * @author Guillaume Anciaux * @author Mohit Pundir * @author Nicolas Richart * * @date creation: Sun Jun 16 2019 * @date last modification: Sat Mar 13 2021 * * @brief pybind11 interface to SolidMechanicsModel * * * @section LICENSE * * Copyright (©) 2018-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "py_aka_array.hh" /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ namespace py = pybind11; /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ #define def_deprecated(func_name, mesg) \ def(func_name, [](py::args, py::kwargs) { AKANTU_ERROR(mesg); }) #define def_function_nocopy(func_name) \ def( \ #func_name, \ [](SolidMechanicsModel & self) -> decltype(auto) { \ return self.func_name(); \ }, \ py::return_value_policy::reference) #define def_function(func_name) \ def(#func_name, [](SolidMechanicsModel & self) -> decltype(auto) { \ return self.func_name(); \ }) /* -------------------------------------------------------------------------- */ void register_solid_mechanics_model(py::module & mod) { py::class_(mod, "SolidMechanicsModelOptions") .def(py::init(), py::arg("_analysis_method") = _explicit_lumped_mass); py::class_(mod, "SolidMechanicsModel", py::multiple_inheritance()) .def(py::init, const ModelType>(), py::arg("mesh"), py::arg("spatial_dimension") = _all_dimensions, py::arg("id") = "solid_mechanics_model", py::arg("dof_manager") = nullptr, py::arg("model_type") = ModelType::_solid_mechanics_model) .def( "initFull", [](SolidMechanicsModel & self, const SolidMechanicsModelOptions & options) { self.initFull(options); }, py::arg("option") = SolidMechanicsModelOptions()) .def( "initFull", [](SolidMechanicsModel & self, const AnalysisMethod & analysis_method) { self.initFull(_analysis_method = analysis_method); }, py::arg("_analysis_method")) .def_deprecated("applyDirichletBC", "Deprecated: use applyBC") .def("applyBC", [](SolidMechanicsModel & self, BC::Dirichlet::DirichletFunctor & func, const std::string & element_group) { self.applyBC(func, element_group); }) .def("applyBC", [](SolidMechanicsModel & self, BC::Neumann::NeumannFunctor & func, const std::string & element_group) { self.applyBC(func, element_group); }) .def("setTimeStep", &SolidMechanicsModel::setTimeStep, py::arg("time_step"), py::arg("solver_id") = "") .def( "getEnergy", [](SolidMechanicsModel & self, const std::string & energy_id) { return self.getEnergy(energy_id); }, py::arg("energy_id")) .def( "getEnergy", [](SolidMechanicsModel & self, const std::string & energy_id, const std::string & group_id) { return self.getEnergy(energy_id, group_id); }, py::arg("energy_id"), py::arg("group_id")) .def_function(assembleStiffnessMatrix) .def_function(assembleInternalForces) .def_function(assembleMass) .def_function(assembleMassLumped) .def_function(getStableTimeStep) .def_function_nocopy(getExternalForce) .def_function_nocopy(getDisplacement) .def_function_nocopy(getPreviousDisplacement) .def_function_nocopy(getCurrentPosition) .def_function_nocopy(getIncrement) .def_function_nocopy(getInternalForce) .def_function_nocopy(getMass) .def_function_nocopy(getVelocity) .def_function_nocopy(getAcceleration) .def_function_nocopy(getInternalForce) .def_function_nocopy(getBlockedDOFs) .def_function_nocopy(getMesh) .def( "getMaterial", [](SolidMechanicsModel & self, UInt material_id) -> decltype(auto) { return self.getMaterial(material_id); }, py::arg("material_id"), py::return_value_policy::reference) .def( "getMaterial", [](SolidMechanicsModel & self, const ID & material_name) -> decltype(auto) { return self.getMaterial(material_name); }, py::arg("material_name"), py::return_value_policy::reference) + .def( + "getMaterial", + [](SolidMechanicsModel & self, const Element & element) + -> decltype(auto) { return self.getMaterial(element); }, + py::arg("element"), py::return_value_policy::reference) + + .def("getNbMaterials", &SolidMechanicsModel::getNbMaterials) .def("getMaterialIndex", &SolidMechanicsModel::getMaterialIndex) - // .def( - // "setMaterialSelector", - // [](SolidMechanicsModel & self, MaterialSelector & - // material_selector) { - // self.setMaterialSelector(material_selector.shared_from_this()); - // }) .def("setMaterialSelector", [](SolidMechanicsModel & self, std::shared_ptr material_selector) { std::cout << (*material_selector)(ElementNull) << std::endl; self.setMaterialSelector(material_selector); }) - .def("getMaterialSelector", &SolidMechanicsModel::getMaterialSelector); + .def("getMaterialSelector", &SolidMechanicsModel::getMaterialSelector) + .def( + "getMaterialByElement", + [](const SolidMechanicsModel & self) -> decltype(auto) { + return self.getMaterialByElement(); + }, + py::return_value_policy::reference, py::keep_alive<0, 1>()) + .def("reassignMaterial", &SolidMechanicsModel::reassignMaterial) + .def( + "registerNewMaterial", + [](SolidMechanicsModel & self, const ID & mat_name, + const ID & mat_type, const ID & opt_param) -> decltype(auto) { + return self.registerNewMaterial(mat_name, mat_type, opt_param); + }, + py::arg("material_name"), py::arg("material_type"), + py::arg("option") = "", py::return_value_policy::reference) + .def("initMaterials", &SolidMechanicsModel::initMaterials) + .def("flattenInternal", &SolidMechanicsModel::flattenInternal, + py::return_value_policy::reference) + .def("inflateInternal", &SolidMechanicsModel::inflateInternal, + py::return_value_policy::reference); } } // namespace akantu diff --git a/python/py_solid_mechanics_model_cohesive.cc b/python/py_solid_mechanics_model_cohesive.cc index 6d4ce5f3e..ba64b18c5 100644 --- a/python/py_solid_mechanics_model_cohesive.cc +++ b/python/py_solid_mechanics_model_cohesive.cc @@ -1,95 +1,96 @@ /** * @file py_solid_mechanics_model_cohesive.cc * * @author Nicolas Richart * * @date creation: Tue Jul 21 2020 * @date last modification: Tue Sep 29 2020 * * @brief pybind11 interface to SolidMechanicsModelCohesive * * * @section LICENSE * * Copyright (©) 2018-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "py_aka_array.hh" /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ namespace py = pybind11; /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ #define def_deprecated(func_name, mesg) \ def(func_name, [](py::args, py::kwargs) { AKANTU_ERROR(mesg); }) #define def_function_nocopy(func_name) \ def( \ #func_name, \ [](SolidMechanicsModel & self) -> decltype(auto) { \ return self.func_name(); \ }, \ py::return_value_policy::reference) #define def_function(func_name) \ def(#func_name, [](SolidMechanicsModel & self) -> decltype(auto) { \ return self.func_name(); \ }) void register_solid_mechanics_model_cohesive(py::module & mod) { py::class_(mod, "CohesiveElementInserter") .def("setLimit", &CohesiveElementInserter::setLimit); py::class_( mod, "SolidMechanicsModelCohesiveOptions") .def(py::init(), py::arg("analysis_method") = _explicit_lumped_mass, py::arg("is_extrinsic") = false); py::class_( mod, "SolidMechanicsModelCohesive") .def(py::init(), py::arg("mesh"), py::arg("spatial_dimension") = _all_dimensions, py::arg("id") = "solid_mechanics_model") .def( "initFull", [](SolidMechanicsModel & self, const AnalysisMethod & analysis_method, bool is_extrinsic) { self.initFull(_analysis_method = analysis_method, _is_extrinsic = is_extrinsic); }, - py::arg("_analysis_method"), py::arg("_is_extrinsic") = false) + py::arg("_analysis_method") = _explicit_lumped_mass, + py::arg("_is_extrinsic") = false) .def("checkCohesiveStress", &SolidMechanicsModelCohesive::checkCohesiveStress) .def("getElementInserter", &SolidMechanicsModelCohesive::getElementInserter, py::return_value_policy::reference) .def("updateAutomaticInsertion", &SolidMechanicsModelCohesive::updateAutomaticInsertion); } } // namespace akantu diff --git a/python/py_solver.cc b/python/py_solver.cc index f2c83cfe6..d5e20670d 100644 --- a/python/py_solver.cc +++ b/python/py_solver.cc @@ -1,105 +1,119 @@ /** * @file py_solver.cc * * @author Nicolas Richart * * @date creation: Tue Sep 29 2020 * @date last modification: Sat Mar 06 2021 * * @brief pybind11 interface to Solver and SparseMatrix * * * @section LICENSE * * Copyright (©) 2018-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "py_solver.hh" #include "py_aka_array.hh" /* -------------------------------------------------------------------------- */ #include #include #include #include /* -------------------------------------------------------------------------- */ #include #include #include /* -------------------------------------------------------------------------- */ namespace py = pybind11; /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ void register_solvers(py::module & mod) { py::class_(mod, "SparseMatrix") .def("getMatrixType", &SparseMatrix::getMatrixType) .def("size", &SparseMatrix::size) .def("zero", &SparseMatrix::zero) .def("saveProfile", &SparseMatrix::saveProfile) .def("saveMatrix", &SparseMatrix::saveMatrix) .def( "add", [](SparseMatrix & self, UInt i, UInt j) { self.add(i, j); }, "Add entry in the profile") .def( "add", [](SparseMatrix & self, UInt i, UInt j, Real value) { self.add(i, j, value); }, "Add the value to the matrix") .def( "add", [](SparseMatrix & self, SparseMatrix & A, Real alpha) { self.add(A, alpha); }, "Add a matrix to the matrix", py::arg("A"), py::arg("alpha") = 1.) + + .def("isFinite", &SparseMatrix::isFinite) + + .def("getRelease", + [](const SparseMatrix & self) -> UInt { return self.getRelease(); }) .def("__call__", [](const SparseMatrix & self, UInt i, UInt j) { return self(i, j); }) .def("getRelease", &SparseMatrix::getRelease); py::class_(mod, "SparseMatrixAIJ") .def("getIRN", &SparseMatrixAIJ::getIRN) .def("getJCN", &SparseMatrixAIJ::getJCN) .def("getA", &SparseMatrixAIJ::getA); - py::class_(mod, "SolverVector"); + py::class_(mod, "SolverVector") + .def( + "getValues", + [](SolverVector & self) -> decltype(auto) { + return static_cast &>(self); + }, + py::return_value_policy::reference_internal, + "Transform this into a vector, Is not copied.") + .def("isDistributed", + [](const SolverVector & self) { return self.isDistributed(); }); py::class_(mod, "TermToAssemble") .def(py::init()) .def(py::self += Real()) .def_property_readonly("i", &TermsToAssemble::TermToAssemble::i) .def_property_readonly("j", &TermsToAssemble::TermToAssemble::j); py::class_(mod, "TermsToAssemble") .def(py::init()) .def("getDOFIdM", &TermsToAssemble::getDOFIdM) .def("getDOFIdN", &TermsToAssemble::getDOFIdN) .def( "__call__", [](TermsToAssemble & self, UInt i, UInt j, Real val) { auto & term = self(i, j); term = val; return term; }, py::arg("i"), py::arg("j"), py::arg("val") = 0., py::return_value_policy::reference); } } // namespace akantu diff --git a/python/py_structural_mechanics_model.cc b/python/py_structural_mechanics_model.cc index 1c197916d..a7968f81f 100644 --- a/python/py_structural_mechanics_model.cc +++ b/python/py_structural_mechanics_model.cc @@ -1,162 +1,179 @@ /** * @file py_structural_mechanics_model.cc * * @author Philip Mueller * @author Mohit Pundir * @author Nicolas Richart * * @date creation: Wed Feb 03 2021 * @date last modification: Thu Apr 01 2021 * * @brief pybind11 interface to StructuralMechanicsModel * * * @section LICENSE * * Copyright (©) 2018-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "py_aka_array.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ namespace py = pybind11; /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ #define def_deprecated(func_name, mesg) \ def(func_name, [](py::args, py::kwargs) { AKANTU_ERROR(mesg); }) #define def_function_nocopy(func_name) \ def( \ #func_name, \ [](StructuralMechanicsModel & self) -> decltype(auto) { \ return self.func_name(); \ }, \ py::return_value_policy::reference) #define def_function_(func_name) \ def(#func_name, [](StructuralMechanicsModel & self) -> decltype(auto) { \ return self.func_name(); \ }) #define def_plainmember(M) def_readwrite(#M, &StructuralMaterial::M) /* -------------------------------------------------------------------------- */ void register_structural_mechanics_model(pybind11::module & mod) { /* First we have to register the material class * The wrapper aims to mimic the behaviour of the real material. */ py::class_(mod, "StructuralMaterial") .def(py::init<>()) .def(py::init()) .def_plainmember(E) .def_plainmember(A) .def_plainmember(I) .def_plainmember(Iz) .def_plainmember(Iy) .def_plainmember(GJ) .def_plainmember(rho) .def_plainmember(t) .def_plainmember(nu); /* Now we create the structural model wrapper * Note that this is basically a port from the solid mechanic part. */ py::class_(mod, "StructuralMechanicsModel") .def(py::init(), py::arg("mesh"), py::arg("spatial_dimension") = _all_dimensions, py::arg("id") = "structural_mechanics_model") .def( "initFull", [](StructuralMechanicsModel & self, const AnalysisMethod & analysis_method) -> void { self.initFull(_analysis_method = analysis_method); }, py::arg("_analysis_method")) .def("initFull", [](StructuralMechanicsModel & self) -> void { self.initFull(); }) .def_function_nocopy(getExternalForce) .def_function_nocopy(getDisplacement) .def_function_nocopy(getInternalForce) .def_function_nocopy(getVelocity) .def_function_nocopy(getAcceleration) .def_function_nocopy(getInternalForce) .def_function_nocopy(getBlockedDOFs) .def_function_nocopy(getMesh) .def("setTimeStep", &StructuralMechanicsModel::setTimeStep, py::arg("time_step"), py::arg("solver_id") = "") .def( "getElementMaterial", [](StructuralMechanicsModel & self, ElementType type, GhostType ghost_type) -> decltype(auto) { return self.getElementMaterial(type, ghost_type); }, "This function returns the map that maps elements to materials.", py::arg("type"), py::arg("ghost_type") = _not_ghost, py::return_value_policy::reference) .def( "getMaterialByElement", [](StructuralMechanicsModel & self, Element element) -> decltype(auto) { return self.getMaterialByElement(element); }, "This function returns the `StructuralMaterial` instance that is " "associated with element `element`.", py::arg("element"), py::return_value_policy::reference) .def( "addMaterial", [](StructuralMechanicsModel & self, StructuralMaterial & mat, const ID & name) -> UInt { return self.addMaterial(mat, name); }, "This function adds the `StructuralMaterial` `mat` to `self`." " The function returns the ID of the new material.", py::arg("mat"), py::arg("name") = "") .def( "getMaterial", - [](StructuralMechanicsModel & self, UInt material_index) - -> decltype(auto) { return self.getMaterial(material_index); }, + [](const StructuralMechanicsModel & self, + UInt material_index) -> StructuralMaterial { + return self.getMaterial(material_index); + }, "This function returns the `i`th material of `self`." - " Note a reference is returned which allows to modify the material " - "inside `self`.", - py::arg("i"), py::return_value_policy::reference) + " The function returns a copy of the material.", + py::arg("material_index"), py::return_value_policy::copy) .def( "getMaterial", - [](StructuralMechanicsModel & self, const ID & name) - -> decltype(auto) { return self.getMaterial(name); }, - "This function returns the material with name `i` of `self`." - " Note a reference is returned which allows to modify the material " - "inside `self`.", - py::arg("i"), py::return_value_policy::reference) + [](const StructuralMechanicsModel & self, const ID & name) + -> StructuralMaterial { return self.getMaterial(name); }, + "This function returns the `i`th material of `self`." + " The function returns a copy of the material.", + py::arg("material_index"), py::return_value_policy::copy) .def( "getNbMaterials", [](StructuralMechanicsModel & self) { return self.getNbMaterials(); }, "Returns the number of different materials inside `self`.") + .def("getKineticEnergy", &StructuralMechanicsModel::getKineticEnergy, "Compute kinetic energy") .def("getPotentialEnergy", &StructuralMechanicsModel::getPotentialEnergy, "Compute potential energy") .def("getEnergy", &StructuralMechanicsModel::getEnergy, - "Compute the specified energy"); + "Compute the specified energy") -} // End: register structural mechanical model + .def( + "getLumpedMass", + [](const StructuralMechanicsModel & self) -> decltype(auto) { + return self.getLumpedMass(); + }, + py::return_value_policy::reference_internal) + .def( + "getMass", + [](const StructuralMechanicsModel & self) -> decltype(auto) { + return self.getMass(); + }, + py::return_value_policy::reference_internal) + .def("assembleLumpedMassMatrix", + &StructuralMechanicsModel::assembleLumpedMassMatrix, + "Assembles the lumped mass matrix") + .def("hasLumpedMass", &StructuralMechanicsModel::hasLumpedMass); +} } // namespace akantu diff --git a/setup.cfg b/setup.cfg index c8a4ee85a..2215693b2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,29 +1,15 @@ - -# See the docstring in versioneer.py for instructions. Note that you must -# re-run 'versioneer.py setup' after changing this section, and commit the -# resulting files. - -[versioneer] -VCS = git -style = pep440 -versionfile_source = python/akantu/_version.py -versionfile_build = akantu/_version.py -tag_prefix = v -verbose = true -#parentdir_prefix = - [cmake_config] AKANTU_COHESIVE_ELEMENT = TRUE AKANTU_IMPLICIT = TRUE AKANTU_PARALLEL = FALSE AKANTU_STRUCTURAL_MECHANICS = TRUE AKANTU_HEAT_TRANSFER = TRUE AKANTU_DAMAGE_NON_LOCAL = TRUE AKANTU_PHASE_FIELD = TRUE AKANTU_PYTHON_INTERFACE = TRUE AKANTU_CONTACT_MECHANICS = TRUE AKANTU_EXAMPLES = FALSE AKANTU_TESTS = FALSE CMAKE_JOB_POOL_COMPILE = compile CMAKE_JOB_POOL_LINK = link CMAKE_JOB_POOLS = 'compile=10;link=2' diff --git a/setup.py b/setup.py index 740d2f777..d244a44cb 100755 --- a/setup.py +++ b/setup.py @@ -1,136 +1,118 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- import sys +import re import os.path import pybind11 as py11 import configparser from setuptools import find_packages from packaging.version import LegacyVersion from skbuild.exceptions import SKBuildError from skbuild.cmaker import get_cmake_version try: from skbuild import setup except ImportError: sys.stderr.write( "Please update pip, you need pip 10 or greater,\n" " or you need to install the PEP 518 requirements in" " pyproject.toml yourself" ) raise -# This is needed for versioneer to be importable when building with PEP 517. -# See and links -# therein for more information. +# This is needed for semver.py to be importable source_folder = os.path.dirname(__file__) -sys.path.append(source_folder) +sys.path.insert(0, os.path.join(source_folder, "cmake")) parser = configparser.ConfigParser() parser.read("setup.cfg") cmake_args = ["-Dpybind11_DIR:PATH={}".format(py11.get_cmake_dir())] -_version = None -if ("cmake_config" in parser) and ("akantu_dir" in parser["cmake_config"]): - sys.path.append(parser["cmake_config"]["akantu_dir"]) - try: - import akantu_version - - _version = akantu_version.get_version() - except ImportError: - pass - -try: - import versioneer - - if not _version: - _version = versioneer.get_version() - setup_kw = { - "version": _version, - "cmdclass": versioneer.get_cmdclass(), - } - cmake_args.append("-DAKANTU_VERSION={}".format(_version)) -except ImportError: - # see https://github.com/warner/python-versioneer/issues/192 - print("WARNING: failed to import versioneer," " falling back to no version for now") - setup_kw = {} - - if "cmake_config" in parser: for k, v in parser["cmake_config"].items(): k = k.upper() cmake_args.append("-D{}:BOOL={}".format(k, v)) akantu_libs = [] - if "CI_AKANTU_INSTALL_PREFIX" in os.environ: + ci_akantu_install_prefix = os.environ["CI_AKANTU_INSTALL_PREFIX"] + akantu_dir = os.path.join(ci_akantu_install_prefix, "lib", "cmake", "Akantu") akantu_libs.extend( [ # paths comming from the manylinux install via gitlab-ci "/softs/view/lib/*", "/softs/view/lib64/*", - os.path.join(os.environ["CI_AKANTU_INSTALL_PREFIX"], "lib64/*"), - os.path.join(os.environ["CI_AKANTU_INSTALL_PREFIX"], "lib/*"), + os.path.join(ci_akantu_install_prefix, "lib64/*"), + os.path.join(ci_akantu_install_prefix, "lib/*"), ] ) cmake_args.extend( [ "-DAKANTU_BYPASS_AKANTU_TARGET:BOOL=ON", - "-DAkantu_DIR:PATH={}".format( - os.path.join( - os.environ["CI_AKANTU_INSTALL_PREFIX"], "lib", "cmake", "Akantu" - ) - ), + "-DAkantu_DIR:PATH={}".format(akantu_dir), ] ) +setup_kw = {} +try: + import semver + + _version = semver.get_version() + setup_kw = { + "version": _version, + } + cmake_args.append("-DAKANTU_VERSION={}".format(_version)) +except ImportError: + pass + + # Add CMake as a build requirement if cmake is not installed or is too low a # version setup_requires = [] try: if LegacyVersion(get_cmake_version()) < LegacyVersion("3.4"): setup_requires.append("cmake") except SKBuildError: setup_requires.append("cmake") - with open(os.path.join(source_folder, "README.md"), "r") as fh: long_description = fh.read() setup( name="akantu", url="https://akantu.ch", author="Nicolas Richart", author_email="nicolas.richart@epfl.ch", description="Akantu: Swiss-Made Open-Source Finite-Element Library", long_description=long_description, long_description_content_type="text/markdown", platforms="", license="L-GPLv3", license_files=["COPYING", "COPYING.lesser"], project_urls={ "Bug Tracker": "https://github.com/akantu/akantu/issues", }, setup_requires=setup_requires, install_requires=["numpy", "scipy"], package_data={"AkantuLibs": akantu_libs}, packages=find_packages(where="python"), package_dir={"": "python"}, include_package_data=False, cmake_args=cmake_args, cmake_languages=["CXX"], classifiers=[ "Development Status :: 4 - Beta", "Environment :: Console", "Intended Audience :: Developers", "Intended Audience :: Education", "Intended Audience :: Science/Research", "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)", "Natural Language :: English", "Operating System :: POSIX :: Linux", "Programming Language :: C++", "Programming Language :: Python", "Topic :: Education", "Topic :: Scientific/Engineering", ], - **setup_kw + **setup_kw, ) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d67cbac1c..11cde1aef 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,270 +1,276 @@ #=============================================================================== # @file CMakeLists.txt # # @author Guillaume Anciaux # @author Nicolas Richart # # @date creation: Mon Jun 14 2010 # @date last modification: Tue Feb 13 2018 # # @brief CMake file for the library # # @section LICENSE # # Copyright (©) 2010-2018 EPFL (Ecole Polytechnique Fédérale de Lausanne) # Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) # # Akantu is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # # Akantu 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with Akantu. If not, see . # #=============================================================================== #=============================================================================== # Package Management #=============================================================================== package_get_all_source_files( AKANTU_LIBRARY_SRCS AKANTU_LIBRARY_PUBLIC_HDRS AKANTU_LIBRARY_PRIVATE_HDRS ) package_get_all_include_directories( AKANTU_LIBRARY_INCLUDE_DIRS ) package_get_all_external_informations( PRIVATE_INCLUDE AKANTU_PRIVATE_EXTERNAL_INCLUDE_DIR INTERFACE_INCLUDE AKANTU_INTERFACE_EXTERNAL_INCLUDE_DIR LIBRARIES AKANTU_EXTERNAL_LIBRARIES ) package_get_all_compilation_flags(CXX _cxx_flags) set(AKANTU_EXTRA_CXX_FLAGS "${_cxx_flags}" CACHE STRING "Extra flags defined by loaded packages" FORCE) mark_as_advanced(AKANTU_EXTRA_CXX_FLAGS) foreach(src_ ${AKANTU_SPIRIT_SOURCES}) set_property(SOURCE ${src_} PROPERTY COMPILE_FLAGS "-g0 -Werror") endforeach() #=========================================================================== # header for blas/lapack (any other fortran libraries) #=========================================================================== package_is_activated(BLAS _blas_activated) package_is_activated(LAPACK _lapack_activated) if(_blas_activated OR _lapack_activated) if(CMAKE_Fortran_COMPILER) # ugly hack set(CMAKE_Fortran_COMPILER_LOADED TRUE) endif() include(FortranCInterface) FortranCInterface_HEADER( "${CMAKE_CURRENT_BINARY_DIR}/aka_fortran_mangling.hh" MACRO_NAMESPACE "AKA_FC_") mark_as_advanced(CDEFS) list(APPEND AKANTU_LIBRARY_PUBLIC_HDRS "${CMAKE_CURRENT_BINARY_DIR}/aka_fortran_mangling.hh" ) endif() list(APPEND AKANTU_LIBRARY_INCLUDE_DIRS "${CMAKE_CURRENT_BINARY_DIR}") set(AKANTU_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR} ${AKANTU_LIBRARY_INCLUDE_DIRS} CACHE INTERNAL "Internal include directories to link with Akantu as a subproject") #=========================================================================== # configurations #=========================================================================== package_get_all_material_includes(AKANTU_MATERIAL_INCLUDES) package_get_all_material_lists(AKANTU_MATERIAL_LISTS) configure_file(model/solid_mechanics/material_list.hh.in "${CMAKE_CURRENT_BINARY_DIR}/material_list.hh" @ONLY) package_get_element_lists() configure_file(common/aka_element_classes_info.hh.in "${CMAKE_CURRENT_BINARY_DIR}/aka_element_classes_info.hh" @ONLY) configure_file(common/aka_config.hh.in "${CMAKE_CURRENT_BINARY_DIR}/aka_config.hh" @ONLY) list(APPEND AKANTU_LIBRARY_PUBLIC_HDRS "${CMAKE_CURRENT_BINARY_DIR}/material_list.hh" "${CMAKE_CURRENT_BINARY_DIR}/aka_element_classes_info.hh" "${CMAKE_CURRENT_BINARY_DIR}/aka_config.hh") +configure_file(common/aka_config.cc.in + "${CMAKE_CURRENT_BINARY_DIR}/aka_config.cc" @ONLY) + +list(APPEND AKANTU_LIBRARY_SRCS + "${CMAKE_CURRENT_BINARY_DIR}/aka_config.cc") + #=============================================================================== # Debug infos #=============================================================================== set(AKANTU_GDB_DIR ${PROJECT_SOURCE_DIR}/cmake) if(UNIX AND NOT APPLE) string(TOUPPER "${CMAKE_BUILD_TYPE}" _u_build_type) if(_u_build_type STREQUAL "DEBUG" OR _u_build_type STREQUAL "RELWITHDEBINFO") configure_file(${PROJECT_SOURCE_DIR}/cmake/libakantu-gdb.py.in "${PROJECT_BINARY_DIR}/libakantu-gdb.py" @ONLY) configure_file(${PROJECT_SOURCE_DIR}/cmake/akantu-debug.cc.in "${PROJECT_BINARY_DIR}/akantu-debug.cc" @ONLY) list(APPEND AKANTU_LIBRARY_SRCS ${PROJECT_BINARY_DIR}/akantu-debug.cc) endif() else() find_program(GDB_EXECUTABLE gdb) if(GDB_EXECUTABLE) execute_process(COMMAND ${GDB_EXECUTABLE} --batch -x "${PROJECT_SOURCE_DIR}/cmake/gdb_python_path" OUTPUT_VARIABLE AKANTU_PYTHON_GDB_DIR ERROR_QUIET RESULT_VARIABLE _res) if(_res EQUAL 0 AND UNIX) set(GDB_USER_CONFIG $ENV{HOME}/.gdb/auto-load) file(MAKE_DIRECTORY ${GDB_USER_CONFIG}) configure_file(${PROJECT_SOURCE_DIR}/cmake/libakantu-gdb.py.in "${GDB_USER_CONFIG}/${CMAKE_SHARED_LIBRARY_PREFIX}akantu${CMAKE_SHARED_LIBRARY_SUFFIX}.${AKANTU_VERSION}-gdb.py" @ONLY) endif() endif() endif() #=============================================================================== # GIT info #=============================================================================== #include(AkantuBuildOptions) #git_version_info(akantu_git_info ALL # OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/akantu_git_info.hh # ) #list(APPEND AKANTU_LIBRARY_SRCS ${CMAKE_CURRENT_BINARY_DIR}/akantu_git_info.hh) #=============================================================================== # Library generation #=============================================================================== add_library(akantu ${AKANTU_LIBRARY_SRCS}) target_include_directories(akantu PRIVATE $ INTERFACE $ ) # small trick for build includes in public set_property(TARGET akantu APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES $) target_include_directories(akantu SYSTEM PUBLIC ${AKANTU_INTERFACE_EXTERNAL_INCLUDE_DIR} ) target_include_directories(akantu SYSTEM PRIVATE ${AKANTU_PRIVATE_EXTERNAL_INCLUDE_DIR} ) target_link_libraries(akantu PUBLIC ${AKANTU_EXTERNAL_LIBRARIES}) separate_arguments(_separated_cxx_flags UNIX_COMMAND ${_cxx_flags}) target_compile_options(akantu PUBLIC ${_separated_cxx_flags}) set_target_properties(akantu PROPERTIES ${AKANTU_LIBRARY_PROPERTIES} # this contains the version ) if(NOT AKANTU_SHARED) set_property(TARGET akantu PROPERTY POSITION_INDEPENDENT_CODE ${AKANTU_POSITION_INDEPENDENT}) endif() if(AKANTU_LIBRARY_PUBLIC_HDRS) set_property(TARGET akantu PROPERTY PUBLIC_HEADER ${AKANTU_LIBRARY_PUBLIC_HDRS}) endif() if(AKANTU_LIBRARY_PRIVATE_HDRS) set_property(TARGET akantu PROPERTY PRIVATE_HEADER ${AKANTU_LIBRARY_PRIVATE_HDRS}) endif() if(NOT CMAKE_VERSION VERSION_LESS 3.1) package_get_all_features_public(_PUBLIC_features) package_get_all_features_private(_PRIVATE_features) foreach(_type PRIVATE PUBLIC) if(_${_type}_features) target_compile_features(akantu ${_type} ${_${_type}_features}) endif() endforeach() else() set_target_properties(akantu PROPERTIES CXX_STANDARD 14 ) endif() package_get_all_extra_dependencies(_extra_target_dependencies) if(_extra_target_dependencies) # This only adding todo: find a solution for when a dependency was add the is removed... add_dependencies(akantu ${_extra_target_dependencies}) endif() package_get_all_export_list(AKANTU_EXPORT_LIST) list(APPEND AKANTU_EXPORT_LIST akantu) # TODO separate public from private headers install(TARGETS akantu EXPORT ${AKANTU_TARGETS_EXPORT} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Akantu_runtime # NAMELINK_ONLY # LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} # COMPONENT Akantu_development # NAMELINK_SKIP Akantu_development ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Akantu_runtime RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Akantu_runtime PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/akantu/ COMPONENT Akantu_development ) if("${AKANTU_TARGETS_EXPORT}" STREQUAL "AkantuTargets") install(EXPORT AkantuTargets DESTINATION lib/cmake/${PROJECT_NAME} COMPONENT dev) #Export for build tree export(EXPORT AkantuTargets FILE "${CMAKE_BINARY_DIR}/AkantuTargets.cmake") export(PACKAGE Akantu) endif() #=============================================================================== # Adding module names for debug package_get_all_packages(_pkg_list) foreach(_pkg ${_pkg_list}) _package_get_real_name(${_pkg} _pkg_name) _package_get_source_files(${_pkg} _srcs _public_hdrs _private_hdrs) string(TOLOWER "${_pkg_name}" _l_package_name) set_property(SOURCE ${_srcs} ${_public_hdrs} ${_private_hdrs} APPEND PROPERTY COMPILE_DEFINITIONS AKANTU_MODULE=${_l_package_name}) endforeach() # print out the list of materials generate_material_list() register_target_to_tidy(akantu) register_tidy_all(${AKANTU_LIBRARY_SRCS}) register_code_to_format( ${AKANTU_LIBRARY_SRCS} ${AKANTU_LIBRARY_PUBLIC_HDRS} ${AKANTU_LIBRARY_PRIVATE_HDRS} ) diff --git a/src/common/aka_array.hh b/src/common/aka_array.hh index e4ab9e03b..e195a046c 100644 --- a/src/common/aka_array.hh +++ b/src/common/aka_array.hh @@ -1,428 +1,433 @@ /** * @file aka_array.hh * * @author Till Junge * @author Nicolas Richart * * @date creation: Fri Jun 18 2010 * @date last modification: Sun Nov 22 2020 * * @brief Array container for Akantu This container differs from the * std::vector from the fact it as 2 dimensions a main dimension and the size * stored per entries * * * @section LICENSE * * Copyright (©) 2010-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_common.hh" #include "aka_types.hh" #include "aka_view_iterators.hh" /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ #ifndef AKANTU_ARRAY_HH_ #define AKANTU_ARRAY_HH_ namespace akantu { /// class that afford to store vectors in static memory // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions) class ArrayBase { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: using size_type = Int; explicit ArrayBase(const ID & id = "") : id(id) {} ArrayBase(const ArrayBase & other, const ID & id = "") { this->id = (id.empty()) ? other.id : id; } ArrayBase(ArrayBase && other) = default; ArrayBase & operator=(const ArrayBase & other) = default; ArrayBase & operator=(ArrayBase && other) noexcept = default; virtual ~ArrayBase() = default; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ public: /// get the amount of space allocated in bytes virtual Int getMemorySize() const = 0; // changed empty to match std::vector empty inline bool empty() const __attribute__((warn_unused_result)) { return size_ == 0; } /// function to print the containt of the class virtual void printself(std::ostream & stream, int indent = 0) const = 0; /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: /// Get the Size of the Array decltype(auto) size() const { return size_; } /// Get the number of components decltype(auto) getNbComponent() const { return nb_component; } /// Get the name of th arrya AKANTU_GET_MACRO_AUTO(ID, id); /// Set the name of th array AKANTU_SET_MACRO(ID, id, const ID &); /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ protected: /// id of the vector ID id; /// the size used Int size_{0}; /// number of components Int nb_component{1}; }; /* -------------------------------------------------------------------------- */ /* Memory handling layer */ /* -------------------------------------------------------------------------- */ enum class ArrayAllocationType { _default, _pod, }; template struct ArrayAllocationTrait : public std::conditional_t< aka::is_scalar::value, std::integral_constant, std::integral_constant> {}; /* -------------------------------------------------------------------------- */ template ::value> class ArrayDataLayer : public ArrayBase { public: using value_type = T; using size_type = typename ArrayBase::size_type; using reference = value_type &; using pointer_type = value_type *; using const_reference = const value_type &; public: ~ArrayDataLayer() override = default; /// Allocation of a new vector explicit ArrayDataLayer(Int size = 0, Int nb_component = 1, const ID & id = ""); /// Allocation of a new vector with a default value ArrayDataLayer(Int size, Int nb_component, const_reference value, const ID & id = ""); /// Copy constructor (deep copy) ArrayDataLayer(const ArrayDataLayer & vect, const ID & id = ""); /// Copy constructor (deep copy) explicit ArrayDataLayer(const std::vector & vect); // copy operator ArrayDataLayer & operator=(const ArrayDataLayer & other); // move constructor ArrayDataLayer(ArrayDataLayer && other) noexcept = default; // move assign ArrayDataLayer & operator=(ArrayDataLayer && other) noexcept = default; protected: // deallocate the memory virtual void deallocate() {} // allocate the memory virtual void allocate(Int size, Int nb_component); // allocate and initialize the memory virtual void allocate(Int size, Int nb_component, const T & value); public: /// append a tuple of size nb_component containing value inline void push_back(const_reference value); /// append a vector // inline void push_back(const value_type new_elem[]); /// append a Vector or a Matrix template inline void push_back(const Eigen::MatrixBase & new_elem); /// changes the allocated size but not the size, if new_size = 0, the size is /// set to min(current_size and reserve size) virtual void reserve(Int size, Int new_size = Int(-1)); /// change the size of the Array virtual void resize(Int size); /// change the size of the Array and initialize the values virtual void resize(Int size, const T & val); /// get the amount of space allocated in bytes inline Int getMemorySize() const override; /// Get the real size allocated in memory inline Int getAllocatedSize() const; /// give the address of the memory allocated for this vector [[deprecated("use data instead to be stl compatible")]] T * storage() const { return values; }; T * data() const { return values; }; protected: /// allocation type agnostic data access T * values{nullptr}; /// data storage std::vector data_storage; }; /* -------------------------------------------------------------------------- */ /* Actual Array */ /* -------------------------------------------------------------------------- */ template class Array : public ArrayDataLayer { private: using parent = ArrayDataLayer; /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: using value_type = typename parent::value_type; using size_type = typename parent::size_type; using reference = typename parent::reference; using pointer_type = typename parent::pointer_type; using const_reference = typename parent::const_reference; using array_type = Array; ~Array() override; Array() : Array(0){}; /// Allocation of a new vector explicit Array(Int size, Int nb_component = 1, const ID & id = ""); /// Allocation of a new vector with a default value explicit Array(Int size, Int nb_component, const_reference value, const ID & id = ""); /// Copy constructor Array(const Array & vect, const ID & id = ""); /// Copy constructor (deep copy) explicit Array(const std::vector & vect); // copy operator Array & operator=(const Array & other); // move constructor Array(Array && other) noexcept = default; // move assign Array & operator=(Array && other) noexcept = default; /* ------------------------------------------------------------------------ */ /* Iterator */ /* ------------------------------------------------------------------------ */ /// iterator for Array of nb_component = 1 using scalar_iterator = view_iterator; /// const_iterator for Array of nb_component = 1 using const_scalar_iterator = const_view_iterator; /// iterator returning Vectors of size n on entries of Array with /// nb_component = n using vector_iterator = view_iterator>; /// const_iterator returning Vectors of n size on entries of Array with /// nb_component = n using const_vector_iterator = const_view_iterator>; /// iterator returning Matrices of size (m, n) on entries of Array with /// nb_component = m*n using matrix_iterator = view_iterator>; /// const iterator returning Matrices of size (m, n) on entries of Array with /// nb_component = m*n using const_matrix_iterator = const_view_iterator>; /// iterator returning Tensor3 of size (m, n, k) on entries of Array with /// nb_component = m*n*k using tensor3_iterator = view_iterator>; /// const iterator returning Tensor3 of size (m, n, k) on entries of Array /// with nb_component = m*n*k using const_tensor3_iterator = const_view_iterator>; /* ------------------------------------------------------------------------ */ template inline auto begin(Ns &&... n); template inline auto end(Ns &&... n); template inline auto begin(Ns &&... n) const; template inline auto end(Ns &&... n) const; template inline auto cbegin(Ns &&... n) const; template inline auto cend(Ns &&... n) const; template [[deprecated("use make_view instead")]] inline auto begin_reinterpret(Ns &&... n); template [[deprecated("use make_view instead")]] inline auto end_reinterpret(Ns &&... n); template [[deprecated("use make_view instead")]] inline auto begin_reinterpret(Ns &&... n) const; template [[deprecated("use make_view instead")]] inline auto end_reinterpret(Ns &&... n) const; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ public: /// search elem in the vector, return the position of the first occurrence or /// -1 if not found Idx find(const_reference elem) const; /// @see Array::find(const_reference elem) const // Int find(T elem[]) const; /// append a value to the end of the Array inline void push_back(const_reference value) { parent::push_back(value); } /// append a Vector or a Matrix template inline void push_back(const Eigen::MatrixBase & new_elem) { parent::push_back(new_elem); } template inline void push_back(const const_view_iterator & it) { push_back(*it); } template inline void push_back(const view_iterator & it) { push_back(*it); } /// erase the value at position i inline void erase(Idx i); /// erase the entry corresponding to the iterator template inline auto erase(const view_iterator & it); /// @see Array::find(const_reference elem) const template ::value> * = nullptr> inline Idx find(const C & elem); /// set all entries of the array to the value t /// @param t value to fill the array with inline void set(T t) { std::fill_n(this->values, this->size_ * this->nb_component, t); } /// set all tuples of the array to a given vector or matrix /// @param vm Matrix or Vector to fill the array with template ::value> * = nullptr> inline void set(const C & vm); /// set the array to T{} inline void zero() { this->set({}); } /// resize the array to 0 inline void clear() { this->resize(0); } /// Append the content of the other array to the current one void append(const Array & other); /// copy another Array in the current Array, the no_sanity_check allows you to /// force the copy in cases where you know what you do with two non matching /// Arrays in terms of n void copy(const Array & other, bool no_sanity_check = false); /// function to print the containt of the class void printself(std::ostream & stream, int indent = 0) const override; + /// Tests if all elements are finite. + template ::value> * = nullptr> + bool isFinite() const noexcept; + /* ------------------------------------------------------------------------ */ /* Operators */ /* ------------------------------------------------------------------------ */ public: /// substraction entry-wise Array & operator-=(const Array & vect); /// addition entry-wise Array & operator+=(const Array & vect); /// multiply evry entry by alpha Array & operator*=(const T & alpha); /// check if the array are identical entry-wise bool operator==(const Array & other) const; /// @see Array::operator==(const Array & other) const bool operator!=(const Array & other) const; /// return a reference to the j-th entry of the i-th tuple inline reference operator()(Idx i, Idx j = 0); /// return a const reference to the j-th entry of the i-th tuple inline const_reference operator()(Idx i, Idx j = 0) const; /// return a reference to the ith component of the 1D array inline reference operator[](Idx i); /// return a const reference to the ith component of the 1D array inline const_reference operator[](Idx i) const; }; /* -------------------------------------------------------------------------- */ /* Inline Functions Array */ /* -------------------------------------------------------------------------- */ template inline std::ostream & operator<<(std::ostream & stream, const Array & _this) { _this.printself(stream); return stream; } /* -------------------------------------------------------------------------- */ /* Inline Functions ArrayBase */ /* -------------------------------------------------------------------------- */ inline std::ostream & operator<<(std::ostream & stream, const ArrayBase & _this) { _this.printself(stream); return stream; } } // namespace akantu #include "aka_array_tmpl.hh" #endif /* AKANTU_ARRAY_HH_ */ diff --git a/src/common/aka_array_tmpl.hh b/src/common/aka_array_tmpl.hh index 25c60bc1f..f1ed5963b 100644 --- a/src/common/aka_array_tmpl.hh +++ b/src/common/aka_array_tmpl.hh @@ -1,1187 +1,1196 @@ /** * @file aka_array_tmpl.hh * * @author Guillaume Anciaux * @author Nicolas Richart * * @date creation: Thu Jul 15 2010 * @date last modification: Fri Feb 26 2021 * * @brief Inline functions of the classes Array and ArrayBase * * * @section LICENSE * * Copyright (©) 2010-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ /* Inline Functions Array */ /* -------------------------------------------------------------------------- */ #include "aka_array.hh" // NOLINT /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ //#ifndef __AKANTU_AKA_ARRAY_TMPL_HH__ //#define __AKANTU_AKA_ARRAY_TMPL_HH__ /* -------------------------------------------------------------------------- */ namespace akantu { namespace debug { struct ArrayException : public Exception {}; } // namespace debug /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ template ArrayDataLayer::ArrayDataLayer(Int size, Int nb_component, const ID & id) : ArrayBase(id) { allocate(size, nb_component); } /* -------------------------------------------------------------------------- */ template ArrayDataLayer::ArrayDataLayer(Int size, Int nb_component, const_reference value, const ID & id) : ArrayBase(id) { allocate(size, nb_component, value); } /* -------------------------------------------------------------------------- */ template ArrayDataLayer::ArrayDataLayer(const ArrayDataLayer & vect, const ID & id) : ArrayBase(vect, id) { this->data_storage = vect.data_storage; this->size_ = vect.size_; this->nb_component = vect.nb_component; this->values = this->data_storage.data(); } /* -------------------------------------------------------------------------- */ template ArrayDataLayer::ArrayDataLayer( const std::vector & vect) { this->data_storage = vect; this->size_ = vect.size(); this->nb_component = 1; this->values = this->data_storage.data(); } /* -------------------------------------------------------------------------- */ template ArrayDataLayer & ArrayDataLayer::operator=(const ArrayDataLayer & other) { if (this != &other) { this->data_storage = other.data_storage; this->nb_component = other.nb_component; this->size_ = other.size_; this->values = this->data_storage.data(); } return *this; } /* -------------------------------------------------------------------------- */ template void ArrayDataLayer::allocate(Int new_size, Int nb_component) { this->nb_component = nb_component; this->resize(new_size); } /* -------------------------------------------------------------------------- */ template void ArrayDataLayer::allocate(Int new_size, Int nb_component, const T & val) { this->nb_component = nb_component; this->resize(new_size, val); } /* -------------------------------------------------------------------------- */ template void ArrayDataLayer::resize(Int new_size) { this->data_storage.resize(new_size * this->nb_component); this->values = this->data_storage.data(); this->size_ = new_size; } /* -------------------------------------------------------------------------- */ template void ArrayDataLayer::resize(Int new_size, const T & value) { this->data_storage.resize(new_size * this->nb_component, value); this->values = this->data_storage.data(); this->size_ = new_size; } /* -------------------------------------------------------------------------- */ template void ArrayDataLayer::reserve(Int size, Int new_size) { if (new_size != -1) { this->data_storage.resize(new_size * this->nb_component); } this->data_storage.reserve(size * this->nb_component); this->values = this->data_storage.data(); } /* -------------------------------------------------------------------------- */ /** * append a tuple to the array with the value value for all components * @param value the new last tuple or the array will contain nb_component copies * of value */ template inline void ArrayDataLayer::push_back(const T & value) { this->data_storage.push_back(value); this->values = this->data_storage.data(); this->size_ += 1; } /* -------------------------------------------------------------------------- */ /** * append a matrix or a vector to the array * @param new_elem a reference to a Matrix or Vector */ template template inline void ArrayDataLayer::push_back( const Eigen::MatrixBase & new_elem) { AKANTU_DEBUG_ASSERT( nb_component == new_elem.size(), "The vector(" << new_elem.size() << ") as not a size compatible with the Array (nb_component=" << nb_component << ")."); for (Idx i = 0; i < new_elem.size(); ++i) { this->data_storage.push_back(new_elem.array()[i]); } this->values = this->data_storage.data(); this->size_ += 1; } /* -------------------------------------------------------------------------- */ template inline Int ArrayDataLayer::getAllocatedSize() const { return this->data_storage.capacity() / this->nb_component; } /* -------------------------------------------------------------------------- */ template inline Int ArrayDataLayer::getMemorySize() const { return this->data_storage.capacity() * sizeof(T); } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ template class ArrayDataLayer : public ArrayBase { public: using value_type = T; using reference = value_type &; using pointer_type = value_type *; using size_type = typename ArrayBase::size_type; using const_reference = const value_type &; public: ~ArrayDataLayer() override { deallocate(); } /// Allocation of a new vector ArrayDataLayer(Int size = 0, Int nb_component = 1, const ID & id = "") : ArrayBase(id) { allocate(size, nb_component); } /// Allocation of a new vector with a default value ArrayDataLayer(Int size, Int nb_component, const_reference value, const ID & id = "") : ArrayBase(id) { allocate(size, nb_component, value); } /// Copy constructor (deep copy) ArrayDataLayer(const ArrayDataLayer & vect, const ID & id = "") : ArrayBase(vect, id) { allocate(vect.size(), vect.getNbComponent()); std::copy_n(vect.data(), this->size_ * this->nb_component, values); } /// Copy constructor (deep copy) explicit ArrayDataLayer(const std::vector & vect) { allocate(vect.size(), 1); std::copy_n(vect.data(), this->size_ * this->nb_component, values); } // copy operator inline ArrayDataLayer & operator=(const ArrayDataLayer & other) { if (this != &other) { allocate(other.size(), other.getNbComponent()); std::copy_n(other.data(), this->size_ * this->nb_component, values); } return *this; } // move constructor inline ArrayDataLayer(ArrayDataLayer && other) noexcept = default; // move assign inline ArrayDataLayer & operator=(ArrayDataLayer && other) noexcept = default; protected: // deallocate the memory virtual void deallocate() { // NOLINTNEXTLINE(cppcoreguidelines-owning-memory, // cppcoreguidelines-no-malloc) free(this->values); } // allocate the memory virtual inline void allocate(Int size, Int nb_component) { if (size != 0) { // malloc can return a non NULL pointer in case size is 0 this->values = static_cast( // NOLINT std::malloc(nb_component * size * sizeof(T))); // NOLINT } if (this->values == nullptr and size != 0) { throw std::bad_alloc(); } this->nb_component = nb_component; this->allocated_size = this->size_ = size; } // allocate and initialize the memory virtual inline void allocate(Int size, Int nb_component, const T & value) { allocate(size, nb_component); std::fill_n(values, size * nb_component, value); } public: /// append a tuple of size nb_component containing value inline void push_back(const_reference value) { resize(this->size_ + 1, value); } /// append a Vector or a Matrix template inline void push_back(const Eigen::MatrixBase & new_elem) { AKANTU_DEBUG_ASSERT( nb_component == new_elem.size(), "The vector(" << new_elem.size() << ") as not a size compatible with the Array (nb_component=" << nb_component << ")."); this->resize(this->size_ + 1); make_view(*this, new_elem.rows(), new_elem.cols()) .begin()[this->size_ - 1] = new_elem; } /// changes the allocated size but not the size virtual void reserve(Int size, Int new_size = Int(-1)) { auto tmp_size = this->size_; if (new_size != Int(-1)) { tmp_size = new_size; } this->resize(size); this->size_ = std::min(this->size_, tmp_size); } /// change the size of the Array virtual void resize(Int size) { if (size * this->nb_component == 0) { free(values); // NOLINT: cppcoreguidelines-no-malloc values = nullptr; this->allocated_size = 0; } else { if (this->values == nullptr) { this->allocate(size, this->nb_component); return; } Int diff = size - allocated_size; Int size_to_allocate = (std::abs(diff) > AKANTU_MIN_ALLOCATION) ? size : (diff > 0) ? allocated_size + AKANTU_MIN_ALLOCATION : allocated_size; if (size_to_allocate == allocated_size) { // otherwhy the reserve + push_back might fail... this->size_ = size; return; } auto * tmp_ptr = reinterpret_cast( // NOLINT realloc(this->values, size_to_allocate * this->nb_component * sizeof(T))); if (tmp_ptr == nullptr) { throw std::bad_alloc(); } this->values = tmp_ptr; this->allocated_size = size_to_allocate; } this->size_ = size; } /// change the size of the Array and initialize the values virtual void resize(Int size, const T & val) { Int tmp_size = this->size_; this->resize(size); if (size > tmp_size) { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) std::fill_n(values + this->nb_component * tmp_size, (size - tmp_size) * this->nb_component, val); } } /// get the amount of space allocated in bytes inline size_type getMemorySize() const final { return this->allocated_size * this->nb_component * sizeof(T); } /// Get the real size allocated in memory inline Int getAllocatedSize() const { return this->allocated_size; } /// give the address of the memory allocated for this vector [[deprecated("use data instead to be stl compatible")]] T * storage() const { return values; }; T * data() const { return values; }; protected: /// allocation type agnostic data access T * values{nullptr}; Int allocated_size{0}; }; /* -------------------------------------------------------------------------- */ template inline auto Array::operator()(Int i, Int j) -> reference { AKANTU_DEBUG_ASSERT(this->size_ > 0, "The array \"" << this->id << "\" is empty"); AKANTU_DEBUG_ASSERT((i < this->size_) && (j < this->nb_component), "The value at position [" << i << "," << j << "] is out of range in array \"" << this->id << "\""); return this->values[i * this->nb_component + j]; } /* -------------------------------------------------------------------------- */ template inline auto Array::operator()(Int i, Int j) const -> const_reference { AKANTU_DEBUG_ASSERT(this->size_ > 0, "The array \"" << this->id << "\" is empty"); AKANTU_DEBUG_ASSERT((i < this->size_) && (j < this->nb_component), "The value at position [" << i << "," << j << "] is out of range in array \"" << this->id << "\""); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) return this->values[i * this->nb_component + j]; } template inline auto Array::operator[](Int i) -> reference { AKANTU_DEBUG_ASSERT(this->size_ > 0, "The array \"" << this->id << "\" is empty"); AKANTU_DEBUG_ASSERT((i < this->size_ * this->nb_component), "The value at position [" << i << "] is out of range in array \"" << this->id << "\""); return this->values[i]; } /* -------------------------------------------------------------------------- */ template inline auto Array::operator[](Int i) const -> const_reference { AKANTU_DEBUG_ASSERT(this->size_ > 0, "The array \"" << this->id << "\" is empty"); AKANTU_DEBUG_ASSERT((i < this->size_ * this->nb_component), "The value at position [" << i << "] is out of range in array \"" << this->id << "\""); return this->values[i]; } /* -------------------------------------------------------------------------- */ /** * erase an element. If the erased element is not the last of the array, the * last element is moved into the hole in order to maintain contiguity. This * may invalidate existing iterators (For instance an iterator obtained by * Array::end() is no longer correct) and will change the order of the * elements. * @param i index of element to erase */ template inline void Array::erase(Idx i) { AKANTU_DEBUG_IN(); AKANTU_DEBUG_ASSERT((this->size_ > 0), "The array is empty"); AKANTU_DEBUG_ASSERT((i < this->size_), "The element at position [" << i << "] is out of range (" << i << ">=" << this->size_ << ")"); if (i != (this->size_ - 1)) { for (Idx j = 0; j < this->nb_component; ++j) { this->values[i * this->nb_component + j] = this->values[(this->size_ - 1) * this->nb_component + j]; } } this->resize(this->size_ - 1); AKANTU_DEBUG_OUT(); } /* ------------------------------------------------------------------------ */ template template inline auto Array::erase(const view_iterator & it) { auto && curr = it.data(); Idx pos = (curr - this->values) / this->nb_component; erase(pos); view_iterator rit = it; return --rit; } /* -------------------------------------------------------------------------- */ /** * Subtract another array entry by entry from this array in place. Both arrays * must * have the same size and nb_component. If the arrays have different shapes, * code compiled in debug mode will throw an expeption and optimised code * will behave in an unpredicted manner * @param other array to subtract from this * @return reference to modified this */ template Array & Array::operator-=(const Array & vect) { AKANTU_DEBUG_ASSERT((this->size_ == vect.size_) && (this->nb_component == vect.nb_component), "The too array don't have the same sizes"); T * a = this->values; T * b = vect.data(); for (Idx i = 0; i < this->size_ * this->nb_component; ++i) { *a -= *b; ++a; ++b; } return *this; } /* -------------------------------------------------------------------------- */ /** * Add another array entry by entry to this array in * place. Both arrays must have the same size and * nb_component. If the arrays have different shapes, code * compiled in debug mode will throw an expeption and * optimised code will behave in an unpredicted manner * @param other array to add to this * @return reference to modified this */ template Array & Array::operator+=(const Array & vect) { AKANTU_DEBUG_ASSERT((this->size_ == vect.size()) && (this->nb_component == vect.nb_component), "The too array don't have the same sizes"); T * a = this->values; T * b = vect.data(); for (Idx i = 0; i < this->size_ * this->nb_component; ++i) { *a++ += *b++; } return *this; } /* -------------------------------------------------------------------------- */ /** * Multiply all entries of this array by a scalar in place * @param alpha scalar multiplicant * @return reference to modified this */ template auto Array::operator*=(const T & alpha) -> Array & { T * a = this->values; for (Idx i = 0; i < this->size_ * this->nb_component; ++i) { *a++ *= alpha; } return *this; } /* ------------------------------------------------------------------------- */ /** * Compare this array element by element to another. * @param other array to compare to * @return true it all element are equal and arrays have * the same shape, else false */ template bool Array::operator==(const Array & other) const { bool equal = this->nb_component == other.nb_component && this->size_ == other.size_ && this->id == other.id; if (not equal) { return false; } if (this->values == other.data()) { return true; } return std::equal(this->values, this->values + this->size_ * this->nb_component, other.data()); } /* ------------------------------------------------------------------------ */ template bool Array::operator!=(const Array & other) const { return !operator==(other); } /* ------------------------------------------------------------------------ */ /** * set all tuples of the array to a given vector or matrix * @param vm Matrix or Vector to fill the array with */ template template ::value> *> inline void Array::set(const C & elem) { AKANTU_DEBUG_ASSERT( this->nb_component == elem.array().size(), "The size of the object does not match the number of components"); for (auto && v : make_view(*this, this->nb_component)) { v = elem.reshaped(this->nb_component, 1); } } /* ------------------------------------------------------------------------ */ template void Array::append(const Array & other) { AKANTU_DEBUG_ASSERT( this->nb_component == other.nb_component, "Cannot append an array with a different number of component"); Idx old_size = this->size_; this->resize(this->size_ + other.size()); T * tmp = this->values + this->nb_component * old_size; std::copy_n(other.data(), other.size() * this->nb_component, tmp); } /* ------------------------------------------------------------------------ */ /* Functions Array */ /* ------------------------------------------------------------------------ */ template Array::Array(Int size, Int nb_component, const ID & id) : parent(size, nb_component, id) {} template <> inline Array::Array(Int size, Int nb_component, const ID & id) : parent(size, nb_component, "", id) {} /* ------------------------------------------------------------------------ */ template Array::Array(Int size, Int nb_component, const_reference value, const ID & id) : parent(size, nb_component, value, id) {} /* ------------------------------------------------------------------------ */ template Array::Array(const Array & vect, const ID & id) : parent(vect, id) {} /* ------------------------------------------------------------------------ */ template auto Array::operator=(const Array & other) -> Array & { AKANTU_DEBUG_WARNING("You are copying the array " << this->id << " are you sure it is on purpose"); if (&other == this) { return *this; } parent::operator=(other); return *this; } /* ------------------------------------------------------------------------ */ template Array::Array(const std::vector & vect) : parent(vect) {} /* ------------------------------------------------------------------------ */ template Array::~Array() = default; /* ------------------------------------------------------------------------ */ /** * search elem in the array, return the position of the * first occurrence or -1 if not found * @param elem the element to look for * @return index of the first occurrence of elem or -1 if * elem is not present */ template Idx Array::find(const_reference elem) const { AKANTU_DEBUG_IN(); auto begin = this->begin(); auto end = this->end(); auto it = std::find(begin, end, elem); AKANTU_DEBUG_OUT(); return (it != end) ? it - begin : Idx(-1); } /* ------------------------------------------------------------------------ */ template template ::value> *> inline Idx Array::find(const V & elem) { AKANTU_DEBUG_ASSERT(elem.size() == this->nb_component, "Cannot find an element with a wrong size (" << elem.size() << ") != " << this->nb_component); auto && view = make_view(*this, elem.size()); auto begin = view.begin(); auto end = view.end(); auto it = std::find(begin, end, elem); return (it != end) ? it - begin : Idx(-1); } /* ------------------------------------------------------------------------ */ /** * copy the content of another array. This overwrites the * current content. * @param other Array to copy into this array. It has to * have the same nb_component as this. If compiled in * debug mode, an incorrect other will result in an * exception being thrown. Optimised code may result in * unpredicted behaviour. * @param no_sanity_check turns off all checkes */ template void Array::copy(const Array & other, bool no_sanity_check) { AKANTU_DEBUG_IN(); if (not no_sanity_check and (other.nb_component != this->nb_component)) { AKANTU_ERROR("The two arrays do not have the same " "number of components"); } this->resize((other.size_ * other.nb_component) / this->nb_component); std::copy_n(other.data(), this->size_ * this->nb_component, this->values); AKANTU_DEBUG_OUT(); } /* ------------------------------------------------------------------------ */ template class ArrayPrintHelper { public: template static void print_content(const Array & vect, std::ostream & stream, int indent) { std::string space(indent, AKANTU_INDENT); stream << space << " + values : {"; for (Idx i = 0; i < vect.size(); ++i) { stream << "{"; for (Idx j = 0; j < vect.getNbComponent(); ++j) { stream << vect(i, j); if (j != vect.getNbComponent() - 1) { stream << ", "; } } stream << "}"; if (i != vect.size() - 1) { stream << ", "; } } stream << "}" << std::endl; } }; template <> class ArrayPrintHelper { public: template static void print_content(__attribute__((unused)) const Array & vect, __attribute__((unused)) std::ostream & stream, __attribute__((unused)) int indent) {} }; /* ------------------------------------------------------------------------ */ template void Array::printself(std::ostream & stream, int indent) const { std::string space(indent, AKANTU_INDENT); std::streamsize prec = stream.precision(); std::ios_base::fmtflags ff = stream.flags(); stream.setf(std::ios_base::showbase); stream.precision(2); stream << space << "Array<" << debug::demangle(typeid(T).name()) << "> [" << std::endl; stream << space << " + id : " << this->id << std::endl; stream << space << " + size : " << this->size_ << std::endl; stream << space << " + nb_component : " << this->nb_component << std::endl; stream << space << " + allocated size : " << this->getAllocatedSize() << std::endl; stream << space << " + memory size : " << printMemorySize(this->getMemorySize()) << std::endl; if (not AKANTU_DEBUG_LEVEL_IS_TEST()) { stream << space << " + address : " << std::hex << this->values << std::dec << std::endl; } stream.precision(prec); stream.flags(ff); if (AKANTU_DEBUG_TEST(dblDump) || AKANTU_DEBUG_LEVEL_IS_TEST()) { ArrayPrintHelper::value>::print_content( *this, stream, indent); } stream << space << "]" << std::endl; } +/* -------------------------------------------------------------------------- */ +template +template ::value> *> +bool Array::isFinite() const noexcept { + return std::all_of(this->values, + this->values + this->size_ * this->nb_component, + [](auto && a) { return std::isfinite(a); }); +} + /* ------------------------------------------------------------------------ */ /* ArrayFilter */ /* ------------------------------------------------------------------------ */ template class ArrayFilter { public: /// const iterator template class const_iterator { public: Idx getCurrentIndex() { return (*this->filter_it * this->nb_item_per_elem + this->sub_element_counter); } inline const_iterator() = default; inline const_iterator(original_iterator origin_it, filter_iterator filter_it, Int nb_item_per_elem) : origin_it(std::move(origin_it)), filter_it(std::move(filter_it)), nb_item_per_elem(nb_item_per_elem), sub_element_counter(0){}; inline bool operator!=(const_iterator & other) const { return !((*this) == other); } inline bool operator==(const_iterator & other) const { return (this->origin_it == other.origin_it && this->filter_it == other.filter_it && this->sub_element_counter == other.sub_element_counter); } inline bool operator!=(const const_iterator & other) const { return !((*this) == other); } inline bool operator==(const const_iterator & other) const { return (this->origin_it == other.origin_it && this->filter_it == other.filter_it && this->sub_element_counter == other.sub_element_counter); } inline const_iterator & operator++() { ++sub_element_counter; if (sub_element_counter == nb_item_per_elem) { sub_element_counter = 0; ++filter_it; } return *this; }; inline decltype(auto) operator*() { return origin_it[nb_item_per_elem * (*filter_it) + sub_element_counter]; }; private: original_iterator origin_it; filter_iterator filter_it; /// the number of item per element Int nb_item_per_elem; /// counter for every sub element group Int sub_element_counter; }; using vector_iterator = void; // iterator>; using array_type = Array; using const_vector_iterator = const_iterator::const_scalar_iterator>; using value_type = typename array_type::value_type; private: /* ---------------------------------------------------------------------- */ /* Constructors/Destructors */ /* ---------------------------------------------------------------------- */ public: ArrayFilter(const Array & array, const Array & filter, Int nb_item_per_elem) : array(array), filter(filter), nb_item_per_elem(nb_item_per_elem){}; decltype(auto) begin_reinterpret(Int n, Int new_size) const { Int new_nb_item_per_elem = this->nb_item_per_elem; if (new_size != 0 && n != 0) new_nb_item_per_elem = this->array.getNbComponent() * this->filter.size() * this->nb_item_per_elem / (n * new_size); return const_vector_iterator(make_view(this->array, n).begin(), this->filter.begin(), new_nb_item_per_elem); }; decltype(auto) end_reinterpret(Int n, Int new_size) const { Int new_nb_item_per_elem = this->nb_item_per_elem; if (new_size != 0 && n != 0) new_nb_item_per_elem = this->array.getNbComponent() * this->filter.size() * this->nb_item_per_elem / (n * new_size); return const_vector_iterator(make_view(this->array, n).begin(), this->filter.end(), new_nb_item_per_elem); }; // vector_iterator begin_reinterpret(Int, Int) { throw; }; // vector_iterator end_reinterpret(Int, Int) { throw; }; /// return the size of the filtered array which is the filter size Int size() const { return this->filter.size() * this->nb_item_per_elem; }; /// the number of components of the filtered array Int getNbComponent() const { return this->array.getNbComponent(); }; /// tells if the container is empty [[nodiscard]] bool empty() const { return (size() == 0); } /* ---------------------------------------------------------------------- */ /* Class Members */ /* ---------------------------------------------------------------------- */ private: /// reference to array of data const Array & array; /// reference to the filter used to select elements const Array & filter; /// the number of item per element Int nb_item_per_elem; }; /* ------------------------------------------------------------------------ */ /* Begin/End functions implementation */ /* ------------------------------------------------------------------------ */ namespace detail { template constexpr auto take_front_impl(Tuple && t, std::index_sequence /*idxs*/) { return std::make_tuple(std::get(std::forward(t))...); } template constexpr auto take_front(Tuple && t) { return take_front_impl(std::forward(t), std::make_index_sequence{}); } template std::string to_string_all(T &&... t) { if (sizeof...(T) == 0) { return ""; } std::stringstream ss; bool noComma = true; ss << "("; (void)std::initializer_list{ (ss << (noComma ? "" : ", ") << t, noComma = false)...}; ss << ")"; return ss.str(); } template struct InstantiationHelper { template static auto instantiate(T && data, Ns... ns) { return std::make_unique(data, ns...); } }; template <> struct InstantiationHelper<0> { template static auto instantiate(T && data) { return data; } }; template decltype(auto) get_iterator(Arr && array, T * data, Ns &&... ns) { const bool is_const_arr = std::is_const>::value; using type = ViewIteratorHelper_t; using iterator = std::conditional_t, view_iterator>; static_assert(sizeof...(Ns), "You should provide a least one size"); if (array.getNbComponent() * array.size() != Int(product_all(std::forward(ns)...))) { AKANTU_CUSTOM_EXCEPTION_INFO( debug::ArrayException(), "The iterator on " << debug::demangle(typeid(Arr).name()) << to_string_all(array.size(), array.getNbComponent()) << "is not compatible with the type " << debug::demangle(typeid(type).name()) << to_string_all(ns...)); } return aka::apply([&](auto... n) { return iterator(data, n...); }, take_front(std::make_tuple(ns...))); } } // namespace detail /* ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ */ template template inline auto Array::begin(Ns &&... ns) { return detail::get_iterator(*this, this->values, std::forward(ns)..., this->size_); } template template inline auto Array::end(Ns &&... ns) { return detail::get_iterator(*this, this->values + this->nb_component * this->size_, std::forward(ns)..., this->size_); } template template inline auto Array::begin(Ns &&... ns) const { return detail::get_iterator(*this, this->values, std::forward(ns)..., this->size_); } template template inline auto Array::end(Ns &&... ns) const { return detail::get_iterator(*this, this->values + this->nb_component * this->size_, std::forward(ns)..., this->size_); } template template inline auto Array::cbegin(Ns &&... ns) const { return detail::get_iterator(*this, this->values, std::forward(ns)..., this->size_); } template template inline auto Array::cend(Ns &&... ns) const { return detail::get_iterator(*this, this->values + this->nb_component * this->size_, std::forward(ns)..., this->size_); } template template inline auto Array::begin_reinterpret(Ns &&... ns) { return detail::get_iterator(*this, this->values, std::forward(ns)...); } template template inline auto Array::end_reinterpret(Ns &&... ns) { return detail::get_iterator( *this, this->values + detail::product_all(std::forward(ns)...), std::forward(ns)...); } template template inline auto Array::begin_reinterpret(Ns &&... ns) const { return detail::get_iterator(*this, this->values, std::forward(ns)...); } template template inline auto Array::end_reinterpret(Ns &&... ns) const { return detail::get_iterator( *this, this->values + detail::product_all(std::forward(ns)...), std::forward(ns)...); } /* ------------------------------------------------------------------------ */ /* Views */ /* ------------------------------------------------------------------------ */ namespace detail { template class ArrayView { using tuple = std::tuple; public: using size_type = Idx; ~ArrayView() = default; constexpr ArrayView(Array && array, Ns... ns) noexcept : array(array), sizes(std::move(ns)...) {} constexpr ArrayView(const ArrayView & array_view) = default; constexpr ArrayView(ArrayView && array_view) noexcept = default; constexpr ArrayView & operator=(const ArrayView & array_view) = default; constexpr ArrayView & operator=(ArrayView && array_view) noexcept = default; auto begin() { return aka::apply( [&](auto &&... ns) { return detail::get_iterator(array.get(), array.get().data(), std::forward(ns)...); }, sizes); } auto begin() const { return aka::apply( [&](auto &&... ns) { return detail::get_iterator(array.get(), array.get().data(), std::forward(ns)...); }, sizes); } auto end() { return aka::apply( [&](auto &&... ns) { return detail::get_iterator( array.get(), array.get().data() + detail::product_all(std::forward(ns)...), std::forward(ns)...); }, sizes); } auto end() const { return aka::apply( [&](auto &&... ns) { return detail::get_iterator( array.get(), array.get().data() + detail::product_all(std::forward(ns)...), std::forward(ns)...); }, sizes); } auto cbegin() const { return this->begin(); } auto cend() const { return this->end(); } constexpr auto size() const { return std::get::value - 1>(sizes); } constexpr auto dims() const { return std::tuple_size::value - 1; } private: std::reference_wrapper> array; tuple sizes; }; /* ---------------------------------------------------------------------- */ template class ArrayView &, Ns...> { using tuple = std::tuple; public: constexpr ArrayView(const ArrayFilter & array, Ns... ns) : array(array), sizes(std::move(ns)...) {} constexpr ArrayView(const ArrayView & array_view) = default; constexpr ArrayView(ArrayView && array_view) = default; constexpr ArrayView & operator=(const ArrayView & array_view) = default; constexpr ArrayView & operator=(ArrayView && array_view) = default; auto begin() const { return aka::apply( [&](auto &&... ns) { return array.get().begin_reinterpret( std::forward(ns)...); }, sizes); } auto end() const { return aka::apply( [&](auto &&... ns) { return array.get().end_reinterpret( std::forward(ns)...); }, sizes); } auto cbegin() const { return this->begin(); } auto cend() const { return this->end(); } constexpr auto size() const { return std::get::value - 1>(sizes); } constexpr auto dims() const { return std::tuple_size::value - 1; } private: std::reference_wrapper> array; tuple sizes; }; } // namespace detail /* ------------------------------------------------------------------------ */ template >...>::value> * = nullptr> decltype(auto) make_view(Array && array, const Ns... ns) { AKANTU_DEBUG_ASSERT((detail::product_all(ns...) != 0), "You must specify non zero dimensions"); auto size = std::forward(array).size() * std::forward(array).getNbComponent() / detail::product_all(ns...); return detail::ArrayView..., std::common_type_t>( std::forward(array), std::move(ns)..., size); } template decltype(auto) make_const_view(const Array & array, const Ns... ns) { return make_view(array, std::move(ns)...); } } // namespace akantu //#endif /* __AKANTU_AKA_ARRAY_TMPL_HH__ */ diff --git a/src/common/aka_config.cc.in b/src/common/aka_config.cc.in new file mode 100644 index 000000000..0ee47db13 --- /dev/null +++ b/src/common/aka_config.cc.in @@ -0,0 +1,11 @@ +/* -------------------------------------------------------------------------- */ +#include "aka_config.hh" +/* -------------------------------------------------------------------------- */ + +namespace akantu { +std::string getVersion() { + return std::string( + "@AKANTU_MAJOR_VERSION@.@AKANTU_MINOR_VERSION@.@AKANTU_PATCH_VERSION@" + "@AKANTU_PRERELEASE_VERSION@@AKANTU_LOCAL_VERSION@"); +} +} // namespace akantu diff --git a/src/common/aka_config.hh.in b/src/common/aka_config.hh.in index 7d36fff17..29f34b50a 100644 --- a/src/common/aka_config.hh.in +++ b/src/common/aka_config.hh.in @@ -1,97 +1,102 @@ /** * @file aka_config.hh.in * * @author Nicolas Richart * * @date creation: Sun Sep 26 2010 * @date last modification: Thu Jan 25 2018 * * @brief Compilation time configuration of Akantu * * * Copyright (©) 2010-2018 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ +/* -------------------------------------------------------------------------- */ +#include /* -------------------------------------------------------------------------- */ #ifndef AKANTU_AKA_CONFIG_HH_ #define AKANTU_AKA_CONFIG_HH_ // clang-format off #define AKANTU_VERSION_MAJOR @AKANTU_MAJOR_VERSION@ #define AKANTU_VERSION_MINOR @AKANTU_MINOR_VERSION@ #define AKANTU_VERSION_PATCH @AKANTU_PATCH_VERSION@ -#define AKANTU_VERSION_LOCAL "@AKANTU_LOCAL_VERSION@" +#define AKANTU_VERSION_PRERELEASE "@AKANTU_PRERELEASE_VERSION@" #define AKANTU_VERSION (AKANTU_VERSION_MAJOR * 10000 \ + AKANTU_VERSION_MINOR * 100 \ + AKANTU_VERSION_PATCH) +namespace akantu { +std::string getVersion(); +} @AKANTU_TYPES_EXTRA_INCLUDES@ namespace akantu { using Real = @AKANTU_FLOAT_TYPE@; using Int = @AKANTU_SIGNED_INTEGER_TYPE@; using Idx = @AKANTU_SIGNED_INTEGER_TYPE@; using UInt = @AKANTU_UNSIGNED_INTEGER_TYPE@; } // akantu #define AKANTU_INTEGER_SIZE @AKANTU_INTEGER_SIZE@ #define AKANTU_FLOAT_SIZE @AKANTU_FLOAT_SIZE@ // clang-format on #cmakedefine AKANTU_USE_BLAS #cmakedefine AKANTU_USE_LAPACK #cmakedefine AKANTU_PARALLEL #cmakedefine AKANTU_USE_MPI #cmakedefine AKANTU_USE_SCOTCH #cmakedefine AKANTU_USE_PTSCOTCH #cmakedefine AKANTU_SCOTCH_NO_EXTERN #cmakedefine AKANTU_IMPLICIT #cmakedefine AKANTU_USE_MUMPS #cmakedefine AKANTU_USE_PETSC #cmakedefine AKANTU_USE_PYBIND11 #cmakedefine AKANTU_EXTRA_MATERIALS #cmakedefine AKANTU_STUDENTS_EXTRA_PACKAGE #cmakedefine AKANTU_DAMAGE_NON_LOCAL #cmakedefine AKANTU_SOLID_MECHANICS #cmakedefine AKANTU_STRUCTURAL_MECHANICS #cmakedefine AKANTU_HEAT_TRANSFER #cmakedefine AKANTU_PHASE_FIELD #cmakedefine AKANTU_COHESIVE_ELEMENT #cmakedefine AKANTU_CONTACT_MECHANICS #cmakedefine AKANTU_MODEL_COUPLERS #cmakedefine AKANTU_IGFEM #cmakedefine AKANTU_EMBEDDED // clang-format off // Debug tools //#cmakedefine AKANTU_NDEBUG #cmakedefine AKANTU_DEBUG_TOOLS #cmakedefine READLINK_COMMAND @READLINK_COMMAND@ #cmakedefine ADDR2LINE_COMMAND @ADDR2LINE_COMMAND@ // clang-format on #endif /* AKANTU_AKA_CONFIG_HH_ */ diff --git a/src/common/aka_error.hh b/src/common/aka_error.hh index 9adcaef2e..ddd140d07 100644 --- a/src/common/aka_error.hh +++ b/src/common/aka_error.hh @@ -1,422 +1,421 @@ /** * @file aka_error.hh * * @author Guillaume Anciaux * @author Nicolas Richart * * @date creation: Mon Jun 14 2010 * @date last modification: Tue Feb 09 2021 * * @brief error management and internal exceptions * * * @section LICENSE * * Copyright (©) 2010-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include #include #include #include #include /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ #ifndef AKANTU_ERROR_HH_ #define AKANTU_ERROR_HH_ namespace akantu { /* -------------------------------------------------------------------------- */ enum DebugLevel { dbl0 = 0, dblError = 0, dblAssert = 0, dbl1 = 1, dblException = 1, dblCritical = 1, dbl2 = 2, dblMajor = 2, dbl3 = 3, dblCall = 3, dblSecondary = 3, dblHead = 3, dbl4 = 4, dblWarning = 4, dbl5 = 5, dblInfo = 5, dbl6 = 6, dblIn = 6, dblOut = 6, dbl7 = 7, dbl8 = 8, dblTrace = 8, dbl9 = 9, dblAccessory = 9, dbl10 = 10, dblDebug = 42, dbl100 = 100, dblDump = 100, dblTest = 1337 }; /* -------------------------------------------------------------------------- */ #define AKANTU_LOCATION \ "(" << std::string(__func__) << "(): " << std::string(__FILE__) << ":" \ << std::to_string(__LINE__) \ << ")" // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay) /* -------------------------------------------------------------------------- */ namespace debug { void setDebugLevel(const DebugLevel & level); const DebugLevel & getDebugLevel(); void initSignalHandler(); std::string demangle(const char * symbol); template std::string demangle() { return demangle(typeid(T).name()); } template std::string demangle(const T & t) { return demangle(typeid(t).name()); } auto exec(const std::string & cmd) -> std::string; auto getBacktrace() -> std::vector; void printBacktrace(const std::vector & backtrace = getBacktrace()); void exit(int status) __attribute__((noreturn)); /* ------------------------------------------------------------------------ */ /// exception class that can be thrown by akantu class Exception : public std::exception { /* ---------------------------------------------------------------------- */ /* Constructors/Destructors */ /* ---------------------------------------------------------------------- */ protected: explicit Exception(const std::string & info = "") : _info(info) {} public: //! full constructor Exception(const std::string & info, const std::string & file, unsigned int line) : _info(info), _file(file), _line(line) {} /* ---------------------------------------------------------------------- */ /* Methods */ /* ---------------------------------------------------------------------- */ public: const char * what() const noexcept override { return _info.c_str(); } virtual std::string info() const noexcept { std::stringstream stream; stream << debug::demangle(typeid(*this).name()) << " : " << _info << " [" << _file << ":" << _line << "]"; return stream.str(); } public: void setInfo(const std::string & info) { _info = info; } void setFile(const std::string & file) { _file = file; } void setLine(unsigned int line) { _line = line; } void setModule(const std::string & module) { _module = module; } void setBacktrace(const std::vector & backtrace) { backtrace_ = backtrace; } decltype(auto) backtrace() const { return backtrace_; } /* ---------------------------------------------------------------------- */ /* Class Members */ /* ---------------------------------------------------------------------- */ protected: /// exception description and additionals std::string _info; private: /// file it is thrown from std::string _file; /// line it is thrown from unsigned int _line{0}; /// module in which exception was raised std::string _module{"core"}; std::vector backtrace_; }; class CriticalError : public Exception {}; class AssertException : public Exception {}; class NotImplementedException : public Exception {}; /// standard output stream operator inline std::ostream & operator<<(std::ostream & stream, const Exception & _this) { stream << _this.what(); return stream; } - /* -------------------------------------------------------------------------- - */ + /* ------------------------------------------------------------------------ */ class Debugger { public: Debugger() noexcept; virtual ~Debugger(); Debugger(const Debugger &) = default; Debugger & operator=(const Debugger &) = default; Debugger(Debugger &&) noexcept = default; Debugger & operator=(Debugger &&) noexcept = default; static void exit(int status) __attribute__((noreturn)); void throwException(const std::string & info, const std::string & file, unsigned int line, bool /*silent*/, const std::string & /*location*/, const std::string & module) const noexcept(false) __attribute__((noreturn)); /*----------------------------------------------------------------------- */ template void throwCustomException(Except ex, const std::string & info, const std::string & file, unsigned int line, const std::string & module) const noexcept(false) __attribute__((noreturn)); /*----------------------------------------------------------------------- */ template void throwCustomException(Except ex, const std::string & file, unsigned int line, const std::string & module_) const noexcept(false) __attribute__((noreturn)); void printMessage(const std::string & prefix, const DebugLevel & level, const std::string & info, const std::string & module_) const; void setOutStream(std::ostream & out) { cout = &out; } std::ostream & getOutStream() { return *cout; } public: void setParallelContext(int rank, int size); void setDebugLevel(const DebugLevel & level); const DebugLevel & getDebugLevel() const; void setLogFile(const std::string & filename); std::ostream & getOutputStream(); inline bool testLevel(const DebugLevel & level, const std::string & module = "core") const { auto level_reached = (this->level >= (level)); auto correct_module = (level <= dblCritical) or (modules_to_debug.empty()) or (modules_to_debug.find(module) != modules_to_debug.end()); return level_reached and correct_module; } void printBacktrace(bool on_off) { this->print_backtrace = on_off; } bool printBacktrace() const { return this->print_backtrace; } void addModuleToDebug(const std::string & id) { this->modules_to_debug.insert(id); } void removeModuleToDebug(const std::string & id) { auto it = this->modules_to_debug.find(id); if (it != this->modules_to_debug.end()) { this->modules_to_debug.erase(it); } } void listModules() { for (const auto & module_ : modules_to_debug) { (*cout) << module_ << std::endl; } } private: std::string parallel_context; std::ostream * cout; bool file_open; DebugLevel level; bool print_backtrace; std::set modules_to_debug; }; extern Debugger debugger; // NOLINT } // namespace debug /* -------------------------------------------------------------------------- */ #define AKANTU_STRINGIZE_(str) #str #define AKANTU_STRINGIZE(str) AKANTU_STRINGIZE_(str) /* -------------------------------------------------------------------------- */ #define AKANTU_DEBUG_MODULE AKANTU_STRINGIZE(AKANTU_MODULE) /* -------------------------------------------------------------------------- */ #define AKANTU_STRINGSTREAM_IN(_str, _sstr) \ ; \ do { \ std::stringstream _dbg_s_info; \ _dbg_s_info << _sstr; /* NOLINT */ \ (_str) = _dbg_s_info.str(); \ } while (false) /* -------------------------------------------------------------------------- */ #define AKANTU_EXCEPTION(info) AKANTU_EXCEPTION_(info, false) #define AKANTU_SILENT_EXCEPTION(info) AKANTU_EXCEPTION_(info, true) #define AKANTU_EXCEPTION_(info, silent) \ do { \ std::stringstream _dbg_str; \ _dbg_str << info; /* NOLINT */ \ std::stringstream _dbg_loc; \ _dbg_loc << AKANTU_LOCATION; \ ::akantu::debug::debugger.throwException(_dbg_str.str(), __FILE__, \ __LINE__, silent, _dbg_loc.str(), \ AKANTU_DEBUG_MODULE); \ } while (false) #define AKANTU_CUSTOM_EXCEPTION_INFO(ex, info) \ do { \ std::stringstream _dbg_str; \ _dbg_str << info; /* NOLINT */ \ ::akantu::debug::debugger.throwCustomException( \ ex, _dbg_str.str(), __FILE__, __LINE__, AKANTU_DEBUG_MODULE); \ } while (false) #define AKANTU_CUSTOM_EXCEPTION(ex) \ do { \ ::akantu::debug::debugger.throwCustomException(ex, __FILE__, __LINE__, \ AKANTU_DEBUG_MODULE); \ } while (false) /* -------------------------------------------------------------------------- */ #ifdef AKANTU_NDEBUG #define AKANTU_DEBUG_TEST(level) (false) #define AKANTU_DEBUG_LEVEL_IS_TEST() \ (::akantu::debug::debugger.testLevel(dblTest, AKANTU_DEBUG_MODULE)) #define AKANTU_DEBUG(level, info) #define AKANTU_DEBUG_(pref, level, info) #define AKANTU_DEBUG_IN() #define AKANTU_DEBUG_OUT() #define AKANTU_DEBUG_INFO(info) #define AKANTU_DEBUG_WARNING(info) #define AKANTU_DEBUG_TRACE(info) #define AKANTU_DEBUG_ASSERT(test, info) #define AKANTU_ERROR(info) \ AKANTU_CUSTOM_EXCEPTION_INFO(::akantu::debug::CriticalError(), info) /* -------------------------------------------------------------------------- */ #else #define AKANTU_DEBUG(level, info) AKANTU_DEBUG_(" ", level, info) #define AKANTU_DEBUG_(pref, level, info) \ do { \ std::string _dbg_str; \ AKANTU_STRINGSTREAM_IN(_dbg_str, \ info << " " << AKANTU_LOCATION); /* NOLINT */ \ ::akantu::debug::debugger.printMessage(pref, level, _dbg_str, \ AKANTU_DEBUG_MODULE); \ } while (false) #define AKANTU_DEBUG_TEST(level) \ (::akantu::debug::debugger.testLevel(level, AKANTU_DEBUG_MODULE)) #define AKANTU_DEBUG_LEVEL_IS_TEST() \ (::akantu::debug::debugger.testLevel(dblTest)) #define AKANTU_DEBUG_IN() \ AKANTU_DEBUG_( \ "==>", ::akantu::dblIn, \ __func__ \ << "()") // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay, // bugprone-lambda-function-name) #define AKANTU_DEBUG_OUT() \ AKANTU_DEBUG_( \ "<==", ::akantu::dblOut, \ __func__ \ << "()") // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay, // bugprone-lambda-function-name) #define AKANTU_DEBUG_INFO(info) AKANTU_DEBUG_("---", ::akantu::dblInfo, info) #define AKANTU_DEBUG_WARNING(info) \ AKANTU_DEBUG_("/!\\", ::akantu::dblWarning, info) #define AKANTU_DEBUG_TRACE(info) AKANTU_DEBUG_(">>>", ::akantu::dblTrace, info) #define AKANTU_DEBUG_ASSERT(test, info) \ do { \ if (not(test)) [[gnu::unlikely]] \ AKANTU_CUSTOM_EXCEPTION_INFO(::akantu::debug::AssertException(), \ "assert [" << #test << "] " \ << info); /* NOLINT */ \ } while (false) #define AKANTU_ERROR(info) \ do { \ AKANTU_DEBUG_("!!! ", ::akantu::dblError, info); \ AKANTU_CUSTOM_EXCEPTION_INFO(::akantu::debug::CriticalError(), \ info); /* NOLINT */ \ } while (false) #endif // AKANTU_NDEBUG #define AKANTU_TO_IMPLEMENT() \ AKANTU_CUSTOM_EXCEPTION_INFO( \ ::akantu::debug::NotImplementedException(), \ __func__ \ << " : not implemented yet !") // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay, // bugprone-lambda-function-name) /* -------------------------------------------------------------------------- */ namespace debug { /* ------------------------------------------------------------------------ */ template void Debugger::throwCustomException(Except ex, const std::string & info, const std::string & file, unsigned int line, const std::string & module_) const noexcept(false) { ex.setInfo(info); ex.setFile(file); ex.setLine(line); ex.setModule(module_); if (::akantu::debug::debugger.printBacktrace()) { ex.setBacktrace(::akantu::debug::getBacktrace()); } throw ex; } /* ------------------------------------------------------------------------ */ template void Debugger::throwCustomException(Except ex, const std::string & file, unsigned int line, const std::string & module_) const noexcept(false) { ex.setFile(file); ex.setLine(line); ex.setModule(module_); if (::akantu::debug::debugger.printBacktrace()) { ex.setBacktrace(::akantu::debug::getBacktrace()); } throw ex; } } // namespace debug } // namespace akantu #endif /* AKANTU_ERROR_HH_ */ diff --git a/src/common/aka_view_iterators.hh b/src/common/aka_view_iterators.hh index 1c99e21e4..5b55d67bd 100644 --- a/src/common/aka_view_iterators.hh +++ b/src/common/aka_view_iterators.hh @@ -1,602 +1,611 @@ /** * @file aka_view_iterators.hh * * @author Nicolas Richart * * @date creation Thu Nov 15 2018 * * @brief View iterators * * @section LICENSE * * Copyright (©) 2010-2011 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_common.hh" //#include "aka_types.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_AKA_VIEW_ITERATORS_HH__ #define __AKANTU_AKA_VIEW_ITERATORS_HH__ namespace akantu { template class TensorBase; } namespace akantu { /* -------------------------------------------------------------------------- */ /* Iterators */ /* -------------------------------------------------------------------------- */ namespace detail { template constexpr auto product_all(V &&... v) { std::common_type_t result = 1; (void)std::initializer_list{(result *= v, 0)...}; return result; } template struct IteratorHelper { static constexpr Int dim = 0; }; template struct IteratorHelper> { private: using T = typename Derived::Scalar; static constexpr Int m = Derived::RowsAtCompileTime; static constexpr Int n = Derived::ColsAtCompileTime; public: static constexpr Int dim = Eigen::MatrixBase::IsVectorAtCompileTime ? 1 : 2; using pointer = T *; using proxy = Eigen::Map>; using const_proxy = Eigen::Map>; }; template struct IteratorHelper> { private: using T = typename Derived::Scalar; static constexpr Int m = Derived::RowsAtCompileTime; static constexpr Int n = Derived::ColsAtCompileTime; public: static constexpr Int dim = Derived::IsVectorAtCompileTime and m != 1 ? 1 : 2; using pointer = T *; using proxy = Eigen::Map; using const_proxy = Eigen::Map; }; template struct IteratorHelper> { static constexpr Int dim = _dim; using pointer = T *; using proxy = TensorProxy; using const_proxy = TensorProxy; }; template struct IteratorHelper> { static constexpr Int dim = _dim; using pointer = T *; using proxy = TensorProxy; using const_proxy = TensorProxy; }; /* ------------------------------------------------------------------------ */ template >::dim> class internal_view_iterator { protected: using helper = IteratorHelper>; using internal_value_type = IR; using internal_pointer = IR *; using scalar_pointer = typename helper::pointer; using proxy_t = typename helper::proxy; static constexpr int dim_ = dim; public: using pointer = proxy_t *; using value_type = proxy_t; using reference = proxy_t &; using difference_type = Int; using iterator_category = std::random_access_iterator_tag; private: template constexpr auto get_new_proxy(scalar_pointer data, std::index_sequence) const { return ProxyType(data, dims[I]...); } constexpr auto get_new_proxy(scalar_pointer data) { return this->template get_new_proxy( data, std::make_index_sequence()); } + constexpr auto get_new_proxy(scalar_pointer data) const { + return this->template get_new_proxy( + data, std::make_index_sequence()); + } + template constexpr void reset_proxy(std::index_sequence) { new (&proxy) proxy_t(ret_ptr, dims[I]...); } constexpr auto reset_proxy() { return reset_proxy(std::make_index_sequence()); } protected: template ().data()), decltype(std::declval().data())>::value> * = nullptr> explicit internal_view_iterator( internal_view_iterator & it) : dims(it.dims), _offset(it._offset), initial(it.initial), ret_ptr(it.ret_ptr), proxy(get_new_proxy(it.ret_ptr)) {} template friend class internal_view_iterator; template using valid_args_t = std::enable_if_t< aka::conjunction, std::is_enum>...>::value and dim == sizeof...(Args), int>; public: /// Generic constructor dor any tensor dimension template = 0> internal_view_iterator(scalar_pointer data, Ns... ns) : dims({Int(ns)...}), _offset(detail::product_all(std::forward(ns)...)), initial(data), ret_ptr(data), proxy(data, ns...) {} // Specific constructor for Vector of static size 1 template , std::enable_if_t::value and RD::RowsAtCompileTime == 1 and RD::ColsAtCompileTime == 1> * = nullptr> constexpr internal_view_iterator(scalar_pointer data, Idx rows) : dims({rows, 1}), _offset(rows), initial(data), ret_ptr(data), proxy(data, rows, 1) { assert(rows == 1 && "1x1 Matrix"); } /// Specific constructor for Eigen::Map that look like /// Eigen::Map template , std::enable_if_t<(RD::RowsAtCompileTime != 1) and RD::ColsAtCompileTime == 1> * = nullptr> constexpr internal_view_iterator(scalar_pointer data, Idx rows, [[gnu::unused]] Idx cols) : dims({rows}), _offset(rows), initial(data), ret_ptr(data), proxy(data, rows, 1) { assert(cols == 1 && "nx1 Matrix"); } /// Default constructor for Eigen::Map template , std::enable_if_t<_dim == 1> * = nullptr> internal_view_iterator() : proxy(reinterpret_cast(0xdeadbeaf), RD::RowsAtCompileTime == Eigen::Dynamic ? 1 : RD::RowsAtCompileTime) { // initialized to a fake pointer to pass the static_assert in Eigen // this proxy should not be returned } /// Default constructor for Eigen::Map template , std::enable_if_t<_dim == 2> * = nullptr> internal_view_iterator() : proxy(reinterpret_cast(0xdeadbeaf), RD::RowsAtCompileTime == Eigen::Dynamic ? 1 : RD::RowsAtCompileTime, RD::ColsAtCompileTime == Eigen::Dynamic ? 1 : RD::ColsAtCompileTime) { // initialized to a fake pointer to pass the `static_assert` in `Eigen // this proxy should not be returned } internal_view_iterator(const internal_view_iterator & it) : dims(it.dims), _offset(it._offset), initial(it.initial), ret_ptr(it.ret_ptr), proxy(get_new_proxy(it.ret_ptr)) {} internal_view_iterator & operator=(internal_view_iterator && it) noexcept = default; internal_view_iterator(internal_view_iterator && it) noexcept = default; virtual ~internal_view_iterator() = default; template ().data()), decltype(std::declval().data())>::value> * = nullptr> inline internal_view_iterator & operator=(const internal_view_iterator & it) { this->dims = it.dims; this->_offset = it._offset; this->initial = it.initial; this->ret_ptr = it.ret_ptr; reset_proxy(); return *this; } inline internal_view_iterator & operator=(const internal_view_iterator & it) { if (this != &it) { this->dims = it.dims; this->_offset = it._offset; this->initial = it.initial; this->ret_ptr = it.ret_ptr; reset_proxy(); } return *this; } public: Idx getCurrentIndex() { return (this->ret_ptr - this->initial) / this->_offset; } inline reference operator*() { this->reset_proxy(); return proxy; } inline pointer operator->() { reset_proxy(); return &proxy; } inline daughter & operator++() { ret_ptr += _offset; return static_cast(*this); } inline daughter & operator--() { ret_ptr -= _offset; return static_cast(*this); } inline daughter & operator+=(Idx n) { ret_ptr += _offset * n; return static_cast(*this); } inline daughter & operator-=(Idx n) { ret_ptr -= _offset * n; return static_cast(*this); } inline auto operator[](Idx n) { return get_new_proxy(ret_ptr + n * _offset); } + inline auto operator[](Idx n) const { + return get_new_proxy(ret_ptr + n * _offset); + } + inline bool operator==(const internal_view_iterator & other) const { return this->ret_ptr == other.ret_ptr; } inline bool operator!=(const internal_view_iterator & other) const { return this->ret_ptr != other.ret_ptr; } inline bool operator<(const internal_view_iterator & other) const { return this->ret_ptr < other.ret_ptr; } inline bool operator<=(const internal_view_iterator & other) const { return this->ret_ptr <= other.ret_ptr; } inline bool operator>(const internal_view_iterator & other) const { return this->ret_ptr > other.ret_ptr; } inline bool operator>=(const internal_view_iterator & other) const { return this->ret_ptr >= other.ret_ptr; } inline auto operator+(difference_type n) const { daughter tmp(static_cast(*this)); tmp += n; return tmp; } inline auto operator-(difference_type n) const { daughter tmp(static_cast(*this)); tmp -= n; return tmp; } inline difference_type operator-(const internal_view_iterator & b) const { return (this->ret_ptr - b.ret_ptr) / _offset; } inline scalar_pointer data() const { return ret_ptr; } inline difference_type offset() const { return _offset; } inline decltype(auto) getDims() const { return dims; } protected: std::array dims; difference_type _offset{0}; scalar_pointer initial{nullptr}; scalar_pointer ret_ptr{nullptr}; proxy_t proxy; }; /* ------------------------------------------------------------------------ */ /** * Specialization for scalar types */ template class internal_view_iterator { public: using value_type = R; using pointer = R *; using reference = R &; using const_reference = const R &; using difference_type = Idx; // std::ptrdiff_t; using iterator_category = std::random_access_iterator_tag; static constexpr int dim_ = 0; protected: using internal_value_type = IR; using internal_pointer = IR *; public: internal_view_iterator(pointer data = nullptr) : ret(data), initial(data){}; internal_view_iterator(const internal_view_iterator & it) = default; internal_view_iterator(internal_view_iterator && it) = default; virtual ~internal_view_iterator() = default; inline internal_view_iterator & operator=(const internal_view_iterator & it) = default; Idx getCurrentIndex() { return (this->ret - this->initial); }; inline reference operator*() { return *ret; } inline pointer operator->() { return ret; }; inline daughter & operator++() { ++ret; return static_cast(*this); } inline daughter & operator--() { --ret; return static_cast(*this); } inline daughter & operator+=(const Idx n) { ret += n; return static_cast(*this); } inline daughter & operator-=(const Idx n) { ret -= n; return static_cast(*this); } inline reference operator[](const Idx n) { return ret[n]; } inline bool operator==(const internal_view_iterator & other) const { return ret == other.ret; } inline bool operator!=(const internal_view_iterator & other) const { return ret != other.ret; } inline bool operator<(const internal_view_iterator & other) const { return ret < other.ret; } inline bool operator<=(const internal_view_iterator & other) const { return ret <= other.ret; } inline bool operator>(const internal_view_iterator & other) const { return ret > other.ret; } inline bool operator>=(const internal_view_iterator & other) const { return ret >= other.ret; } inline daughter operator-(difference_type n) const { return daughter(ret - n); } inline daughter operator+(difference_type n) const { return daughter(ret + n); } inline difference_type operator-(const internal_view_iterator & b) const { return ret - b.ret; } inline pointer data() const { return ret; } inline decltype(auto) getDims() const { return dims; } protected: std::array dims; pointer ret{nullptr}; pointer initial{nullptr}; }; } // namespace detail /* -------------------------------------------------------------------------- */ template class view_iterator; template class const_view_iterator : public detail::internal_view_iterator, R> { public: using parent = detail::internal_view_iterator; using value_type = typename parent::value_type; using pointer = typename parent::pointer; using reference = typename parent::reference; using difference_type = typename parent::difference_type; using iterator_category = typename parent::iterator_category; protected: template static inline auto convert_helper(const Iterator & it, std::index_sequence) { return const_view_iterator(it.data(), it.getDims()[I]...); } template static inline auto convert(const Iterator & it) { return convert_helper(it, std::make_index_sequence()); } public: const_view_iterator() : parent(){}; const_view_iterator(const const_view_iterator & it) = default; const_view_iterator(const_view_iterator && it) noexcept = default; template const_view_iterator(P * data, Ns... ns) : parent(data, ns...) {} const_view_iterator & operator=(const const_view_iterator & it) = default; template ::value> * = nullptr> const_view_iterator(const const_view_iterator & it) : parent(convert(it)) {} template ::value> * = nullptr> const_view_iterator(const view_iterator & it) : parent(convert(it)) {} template ::value and std::is_convertible::value> * = nullptr> const_view_iterator & operator=(const const_view_iterator & it) { return dynamic_cast(parent::operator=(it)); } template ::value> * = nullptr> const_view_iterator operator=(const view_iterator & it) { return convert(it); } }; template ::value> struct ConstConverterIteratorHelper { protected: template static inline auto convert_helper(const view_iterator & it, std::index_sequence) { return const_view_iterator(it.data(), it.getDims()[I]...); } public: static inline auto convert(const view_iterator & it) { return convert_helper( it, std::make_index_sequence< std::tuple_size::value>()); } }; template struct ConstConverterIteratorHelper { static inline auto convert(const view_iterator & it) { return const_view_iterator(it.data()); } }; template class view_iterator : public detail::internal_view_iterator> { public: using parent = detail::internal_view_iterator; using value_type = typename parent::value_type; using pointer = typename parent::pointer; using reference = typename parent::reference; using difference_type = typename parent::difference_type; using iterator_category = typename parent::iterator_category; public: view_iterator() : parent(){}; view_iterator(const view_iterator & it) = default; view_iterator(view_iterator && it) = default; template view_iterator(P * data, Ns... ns) : parent(data, ns...) {} view_iterator & operator=(const view_iterator & it) = default; operator const_view_iterator() { return ConstConverterIteratorHelper::convert(*this); } }; namespace { template struct ViewIteratorHelper { using type = TensorProxy; }; template struct ViewIteratorHelper<0, T> { using type = T; }; template struct ViewIteratorHelper<1, T> { using type = Eigen::Map>; }; template struct ViewIteratorHelper<1, const T> { using type = Eigen::Map>; }; template struct ViewIteratorHelper<2, T> { using type = Eigen::Map>; }; template struct ViewIteratorHelper<2, const T> { using type = Eigen::Map>; }; template using ViewIteratorHelper_t = typename ViewIteratorHelper::type; } // namespace } // namespace akantu #include namespace std { template struct iterator_traits<::akantu::const_view_iterator> { protected: using iterator = ::akantu::const_view_iterator; public: using iterator_category = typename iterator::iterator_category; using value_type = typename iterator::value_type; using difference_type = typename iterator::difference_type; using pointer = typename iterator::pointer; using reference = typename iterator::reference; }; template struct iterator_traits<::akantu::view_iterator> { protected: using iterator = ::akantu::view_iterator; public: using iterator_category = typename iterator::iterator_category; using value_type = typename iterator::value_type; using difference_type = typename iterator::difference_type; using pointer = typename iterator::pointer; using reference = typename iterator::reference; }; } // namespace std #endif /* !__AKANTU_AKA_VIEW_ITERATORS_HH__ */ diff --git a/src/io/mesh_io/mesh_io_msh.cc b/src/io/mesh_io/mesh_io_msh.cc index 718f58a5f..36a8a9b1d 100644 --- a/src/io/mesh_io/mesh_io_msh.cc +++ b/src/io/mesh_io/mesh_io_msh.cc @@ -1,1117 +1,1126 @@ /** * @file mesh_io_msh.cc * * @author Dana Christen * @author Mauro Corrado * @author David Simon Kammer * @author Nicolas Richart * * @date creation: Fri Jun 18 2010 * @date last modification: Thu Oct 29 2020 * * @brief Read/Write for MSH files generated by gmsh * * * @section LICENSE * * Copyright (©) 2010-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* ----------------------------------------------------------------------------- Version (Legacy) 1.0 $NOD number-of-nodes node-number x-coord y-coord z-coord ... $ENDNOD $ELM number-of-elements elm-number elm-type reg-phys reg-elem number-of-nodes node-number-list ... $ENDELM ----------------------------------------------------------------------------- Version 2.1 $MeshFormat version-number file-type data-size $EndMeshFormat $Nodes number-of-nodes node-number x-coord y-coord z-coord ... $EndNodes $Elements number-of-elements elm-number elm-type number-of-tags < tag > ... node-number-list ... $EndElements $PhysicalNames number-of-names physical-dimension physical-number "physical-name" ... $EndPhysicalNames $NodeData number-of-string-tags < "string-tag" > ... number-of-real-tags < real-tag > ... number-of-integer-tags < integer-tag > ... node-number value ... ... $EndNodeData $ElementData number-of-string-tags < "string-tag" > ... number-of-real-tags < real-tag > ... number-of-integer-tags < integer-tag > ... elm-number value ... ... $EndElementData $ElementNodeData number-of-string-tags < "string-tag" > ... number-of-real-tags < real-tag > ... number-of-integer-tags < integer-tag > ... elm-number number-of-nodes-per-element value ... ... $ElementEndNodeData ----------------------------------------------------------------------------- elem-type 1: 2-node line. 2: 3-node triangle. 3: 4-node quadrangle. 4: 4-node tetrahedron. 5: 8-node hexahedron. 6: 6-node prism. 7: 5-node pyramid. 8: 3-node second order line 9: 6-node second order triangle 10: 9-node second order quadrangle 11: 10-node second order tetrahedron 12: 27-node second order hexahedron 13: 18-node second order prism 14: 14-node second order pyramid 15: 1-node point. 16: 8-node second order quadrangle 17: 20-node second order hexahedron 18: 15-node second order prism 19: 13-node second order pyramid 20: 9-node third order incomplete triangle 21: 10-node third order triangle 22: 12-node fourth order incomplete triangle 23: 15-node fourth order triangle 24: 15-node fifth order incomplete triangle 25: 21-node fifth order complete triangle 26: 4-node third order edge 27: 5-node fourth order edge 28: 6-node fifth order edge 29: 20-node third order tetrahedron 30: 35-node fourth order tetrahedron 31: 56-node fifth order tetrahedron -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ #include "element_group.hh" #include "mesh_io.hh" #include "mesh_utils.hh" #include "node_group.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ /* Methods Implentations */ /* -------------------------------------------------------------------------- */ MeshIOMSH::MeshIOMSH() { canReadSurface = true; canReadExtendedData = true; _msh_nodes_per_elem[_msh_not_defined] = 0; _msh_nodes_per_elem[_msh_segment_2] = 2; _msh_nodes_per_elem[_msh_triangle_3] = 3; _msh_nodes_per_elem[_msh_quadrangle_4] = 4; _msh_nodes_per_elem[_msh_tetrahedron_4] = 4; _msh_nodes_per_elem[_msh_hexahedron_8] = 8; _msh_nodes_per_elem[_msh_prism_1] = 6; _msh_nodes_per_elem[_msh_pyramid_1] = 1; _msh_nodes_per_elem[_msh_segment_3] = 3; _msh_nodes_per_elem[_msh_triangle_6] = 6; _msh_nodes_per_elem[_msh_quadrangle_9] = 9; _msh_nodes_per_elem[_msh_tetrahedron_10] = 10; _msh_nodes_per_elem[_msh_hexahedron_27] = 27; _msh_nodes_per_elem[_msh_hexahedron_20] = 20; _msh_nodes_per_elem[_msh_prism_18] = 18; _msh_nodes_per_elem[_msh_prism_15] = 15; _msh_nodes_per_elem[_msh_pyramid_14] = 14; _msh_nodes_per_elem[_msh_point] = 1; _msh_nodes_per_elem[_msh_quadrangle_8] = 8; _msh_to_akantu_element_types[_msh_not_defined] = _not_defined; _msh_to_akantu_element_types[_msh_segment_2] = _segment_2; _msh_to_akantu_element_types[_msh_triangle_3] = _triangle_3; _msh_to_akantu_element_types[_msh_quadrangle_4] = _quadrangle_4; _msh_to_akantu_element_types[_msh_tetrahedron_4] = _tetrahedron_4; _msh_to_akantu_element_types[_msh_hexahedron_8] = _hexahedron_8; _msh_to_akantu_element_types[_msh_prism_1] = _pentahedron_6; _msh_to_akantu_element_types[_msh_pyramid_1] = _not_defined; _msh_to_akantu_element_types[_msh_segment_3] = _segment_3; _msh_to_akantu_element_types[_msh_triangle_6] = _triangle_6; _msh_to_akantu_element_types[_msh_quadrangle_9] = _not_defined; _msh_to_akantu_element_types[_msh_tetrahedron_10] = _tetrahedron_10; _msh_to_akantu_element_types[_msh_hexahedron_27] = _not_defined; _msh_to_akantu_element_types[_msh_hexahedron_20] = _hexahedron_20; _msh_to_akantu_element_types[_msh_prism_18] = _not_defined; _msh_to_akantu_element_types[_msh_prism_15] = _pentahedron_15; _msh_to_akantu_element_types[_msh_pyramid_14] = _not_defined; _msh_to_akantu_element_types[_msh_point] = _point_1; _msh_to_akantu_element_types[_msh_quadrangle_8] = _quadrangle_8; _akantu_to_msh_element_types[_not_defined] = _msh_not_defined; _akantu_to_msh_element_types[_segment_2] = _msh_segment_2; _akantu_to_msh_element_types[_segment_3] = _msh_segment_3; _akantu_to_msh_element_types[_triangle_3] = _msh_triangle_3; _akantu_to_msh_element_types[_triangle_6] = _msh_triangle_6; _akantu_to_msh_element_types[_tetrahedron_4] = _msh_tetrahedron_4; _akantu_to_msh_element_types[_tetrahedron_10] = _msh_tetrahedron_10; _akantu_to_msh_element_types[_quadrangle_4] = _msh_quadrangle_4; _akantu_to_msh_element_types[_quadrangle_8] = _msh_quadrangle_8; _akantu_to_msh_element_types[_hexahedron_8] = _msh_hexahedron_8; _akantu_to_msh_element_types[_hexahedron_20] = _msh_hexahedron_20; _akantu_to_msh_element_types[_pentahedron_6] = _msh_prism_1; _akantu_to_msh_element_types[_pentahedron_15] = _msh_prism_15; _akantu_to_msh_element_types[_point_1] = _msh_point; #if defined(AKANTU_STRUCTURAL_MECHANICS) _akantu_to_msh_element_types[_bernoulli_beam_2] = _msh_segment_2; _akantu_to_msh_element_types[_bernoulli_beam_3] = _msh_segment_2; _akantu_to_msh_element_types[_discrete_kirchhoff_triangle_18] = _msh_triangle_3; #endif std::map::iterator it; for (it = _akantu_to_msh_element_types.begin(); it != _akantu_to_msh_element_types.end(); ++it) { Int nb_nodes = _msh_nodes_per_elem[it->second]; std::vector tmp(nb_nodes); for (Int i = 0; i < nb_nodes; ++i) { tmp[i] = i; } switch (it->first) { case _tetrahedron_10: tmp[8] = 9; tmp[9] = 8; break; case _pentahedron_6: tmp[0] = 2; tmp[1] = 0; tmp[2] = 1; tmp[3] = 5; tmp[4] = 3; tmp[5] = 4; break; case _pentahedron_15: tmp[0] = 2; tmp[1] = 0; tmp[2] = 1; tmp[3] = 5; tmp[4] = 3; tmp[5] = 4; tmp[6] = 8; tmp[8] = 11; tmp[9] = 6; tmp[10] = 9; tmp[11] = 10; tmp[12] = 14; tmp[14] = 12; break; case _hexahedron_20: tmp[9] = 11; tmp[10] = 12; tmp[11] = 9; tmp[12] = 13; tmp[13] = 10; tmp[17] = 19; tmp[18] = 17; tmp[19] = 18; break; default: // nothing to change break; } _read_order[it->first] = tmp; } } /* -------------------------------------------------------------------------- */ MeshIOMSH::~MeshIOMSH() = default; /* -------------------------------------------------------------------------- */ namespace { -struct File { - std::string filename; - std::ifstream infile; - std::string line; - std::size_t current_line{0}; - - std::size_t first_node_number{std::numeric_limits::max()}, - last_node_number{0}; - bool has_physical_names{false}; - - std::unordered_map node_tags; - std::unordered_map element_tags; - double version{0}; - int size_of_size_t{0}; - Mesh &mesh; - MeshAccessor mesh_accessor; - - std::multimap, int> entity_tag_to_physical_tags; - - File(const std::string &filename, Mesh &mesh) - : filename(filename), mesh(mesh), mesh_accessor(mesh) { - infile.open(filename.c_str()); - if (not infile.good()) { - AKANTU_EXCEPTION("Cannot open file " << filename); + struct File { + std::string filename; + std::ifstream infile; + std::string line; + std::size_t current_line{0}; + + std::size_t first_node_number{std::numeric_limits::max()}, + last_node_number{0}; + bool has_physical_names{false}; + + std::unordered_map node_tags; + std::unordered_map element_tags; + int version{0}; + int size_of_size_t{0}; + Mesh & mesh; + MeshAccessor mesh_accessor; + + std::multimap, int> entity_tag_to_physical_tags; + + File(const std::string & filename, Mesh & mesh) + : filename(filename), mesh(mesh), mesh_accessor(mesh) { + infile.open(filename.c_str()); + if (not infile.good()) { + AKANTU_EXCEPTION("Cannot open file " << filename); + } } - } - ~File() { infile.close(); } + ~File() { infile.close(); } - auto good() { return infile.good(); } + auto good() { return infile.good(); } - std::stringstream get_line() { - std::string tmp_str; - if (infile.eof()) { - AKANTU_EXCEPTION("Reached the end of the file " << filename); - } - std::getline(infile, tmp_str); - line = trim(tmp_str); - ++current_line; + std::stringstream get_line() { + std::string tmp_str; + if (infile.eof()) { + AKANTU_EXCEPTION("Reached the end of the file " << filename); + } + std::getline(infile, tmp_str); + line = trim(tmp_str); + ++current_line; - return std::stringstream(line); - } + return std::stringstream(line); + } - template void read_line(Ts &&...ts) { - auto &&sstr = get_line(); - (void)std::initializer_list{ - (sstr >> std::forward(ts), 0)...}; - } -}; + template void read_line(Ts &&... ts) { + auto && sstr = get_line(); + (void)std::initializer_list{ + (sstr >> std::forward(ts), 0)...}; + } + }; } // namespace /* -------------------------------------------------------------------------- */ template -void MeshIOMSH::populateReaders2(File &file, Readers &readers) { +void MeshIOMSH::populateReaders2(File & file, Readers & readers) { readers["$NOD"] = readers["$Nodes"] = [&](const std::string & /*unused*/) { UInt nb_nodes; file.read_line(nb_nodes); - Array &nodes = file.mesh_accessor.getNodes(); + Array & nodes = file.mesh_accessor.getNodes(); nodes.resize(nb_nodes); file.mesh_accessor.setNbGlobalNodes(nb_nodes); size_t index; Vector coord(3); /// for each node, read the coordinates - for (auto &&data : enumerate(make_view(nodes, nodes.getNbComponent()))) { + for (auto && data : enumerate(make_view(nodes, nodes.getNbComponent()))) { file.read_line(index, coord(0), coord(1), coord(2)); if (index > std::numeric_limits::max()) { AKANTU_EXCEPTION( "There are more nodes in this files than the index type of akantu " "can handle, consider recompiling with a bigger index type"); } file.first_node_number = std::min(file.first_node_number, index); file.last_node_number = std::max(file.last_node_number, index); - for (auto &&coord_data : zip(std::get<1>(data), coord)) { + for (auto && coord_data : zip(std::get<1>(data), coord)) { std::get<0>(coord_data) = std::get<1>(coord_data); } file.node_tags[index] = std::get<0>(data); } }; readers["$ELM"] = readers["$Elements"] = [&](const std::string & /*unused*/) { Int nb_elements; file.read_line(nb_elements); Int index; UInt msh_type; ElementType akantu_type; for (Int i = 0; i < nb_elements; ++i) { - auto &&sstr_elem = file.get_line(); + auto && sstr_elem = file.get_line(); sstr_elem >> index; sstr_elem >> msh_type; /// get the connectivity vector depending on the element type akantu_type = this->_msh_to_akantu_element_types[MSHElementType(msh_type)]; if (akantu_type == _not_defined) { AKANTU_DEBUG_WARNING("Unsuported element kind " << msh_type << " at line " << file.current_line); continue; } Element elem{akantu_type, 0, _not_ghost}; - auto &connectivity = file.mesh_accessor.getConnectivity(akantu_type); + auto & connectivity = file.mesh_accessor.getConnectivity(akantu_type); auto node_per_element = connectivity.getNbComponent(); - auto &read_order = this->_read_order[akantu_type]; + auto & read_order = this->_read_order[akantu_type]; /// read tags informations - if (file.version < 2) { + if (file.version < 2000) { Int tag0; Int tag1; Int nb_nodes; // reg-phys, reg-elem, number-of-nodes sstr_elem >> tag0 >> tag1 >> nb_nodes; - auto &data0 = + auto & data0 = file.mesh_accessor.template getData("tag_0", akantu_type); data0.push_back(tag0); - auto &data1 = + auto & data1 = file.mesh_accessor.template getData("tag_1", akantu_type); data1.push_back(tag1); - } else if (file.version < 4) { + } else if (file.version < 4000) { Int nb_tags; sstr_elem >> nb_tags; for (Int j = 0; j < nb_tags; ++j) { Int tag; sstr_elem >> tag; - auto &data = file.mesh_accessor.template getData( + auto & data = file.mesh_accessor.template getData( "tag_" + std::to_string(j), akantu_type); data.push_back(tag); } } Vector local_connect(node_per_element); for (Int j = 0; j < node_per_element; ++j) { Int node_index; sstr_elem >> node_index; AKANTU_DEBUG_ASSERT(node_index <= Int(file.last_node_number), "Node number not in range : line " << file.current_line); local_connect(read_order[j]) = file.node_tags[node_index]; } connectivity.push_back(local_connect); elem.element = connectivity.size() - 1; file.element_tags[index] = elem; } }; readers["$Periodic"] = [&](const std::string &) { Int nb_periodic_entities; file.read_line(nb_periodic_entities); file.mesh_accessor.getNodesFlags().resize(file.mesh.getNbNodes(), NodeFlag::_normal); for (Int p = 0; p < nb_periodic_entities; ++p) { // dimension slave-tag master-tag Int dimension; file.read_line(dimension); // transformation file.get_line(); // nb nodes Int nb_nodes; file.read_line(nb_nodes); for (Int n = 0; n < nb_nodes; ++n) { // slave master - auto &&sstr = file.get_line(); + auto && sstr = file.get_line(); // The info in the mesh seem inconsistent so they are ignored for now. continue; if (dimension == file.mesh.getSpatialDimension() - 1) { Idx slave, master; sstr >> slave; sstr >> master; file.mesh_accessor.addPeriodicSlave(file.node_tags[slave], file.node_tags[master]); } } } // mesh_accessor.markMeshPeriodic(); }; } /* -------------------------------------------------------------------------- */ template -void MeshIOMSH::populateReaders4(File &file, Readers &readers) { +void MeshIOMSH::populateReaders4(File & file, Readers & readers) { static std::map entity_type{ {0, "points"}, {1, "curve"}, {2, "surface"}, {3, "volume"}, }; readers["$Entities"] = [&](const std::string & /*unused*/) { size_t num_entity[4]; file.read_line(num_entity[0], num_entity[1], num_entity[2], num_entity[3]); for (auto entity_dim : arange(4)) { for (auto _ [[gnu::unused]] : arange(num_entity[entity_dim])) { - auto &&sstr = file.get_line(); + auto && sstr = file.get_line(); int tag; double min_x; double min_y; double min_z; double max_x; double max_y; double max_z; size_t num_physical_tags; sstr >> tag >> min_x >> min_y >> min_z; - if (entity_dim > 0 or file.version < 4.1) { + if (entity_dim > 0 or file.version < 4001) { sstr >> max_x >> max_y >> max_z; } sstr >> num_physical_tags; for (auto _ [[gnu::unused]] : arange(num_physical_tags)) { int phys_tag; sstr >> phys_tag; std::string physical_name; if (this->physical_names.find(phys_tag) == this->physical_names.end()) { physical_name = "msh_block_" + std::to_string(phys_tag); } else { physical_name = this->physical_names[phys_tag]; } if (not file.mesh.elementGroupExists(physical_name)) { file.mesh.createElementGroup(physical_name, entity_dim); } else { file.mesh.getElementGroup(physical_name).addDimension(entity_dim); } file.entity_tag_to_physical_tags.insert( std::make_pair(std::make_pair(tag, entity_dim), phys_tag)); } } } }; readers["$Nodes"] = [&](const std::string & /*unused*/) { size_t num_blocks; size_t num_nodes; - if (file.version >= 4.1) { + if (file.version >= 4001) { file.read_line(num_blocks, num_nodes, file.first_node_number, file.last_node_number); } else { file.read_line(num_blocks, num_nodes); } - auto &nodes = file.mesh_accessor.getNodes(); + auto & nodes = file.mesh_accessor.getNodes(); nodes.reserve(num_nodes); file.mesh_accessor.setNbGlobalNodes(num_nodes); if (num_nodes > std::numeric_limits::max()) { AKANTU_EXCEPTION( "There are more nodes in this files than the index type of akantu " "can handle, consider recompiling with a bigger index type"); } size_t node_id{0}; auto dim = nodes.getNbComponent(); for (auto block [[gnu::unused]] : arange(num_blocks)) { int entity_dim; int entity_tag; int parametric; size_t num_nodes_in_block; Vector pos(3); - if (file.version >= 4.1) { + if (file.version >= 4001) { file.read_line(entity_dim, entity_tag, parametric, num_nodes_in_block); if (parametric) { AKANTU_EXCEPTION( "Akantu does not support parametric nodes in msh files"); } for (auto _ [[gnu::unused]] : arange(num_nodes_in_block)) { size_t tag; file.read_line(tag); file.node_tags[tag] = node_id; ++node_id; } for (auto _ [[gnu::unused]] : arange(num_nodes_in_block)) { file.read_line(pos(_x), pos(_y), pos(_z)); nodes.push_back(pos.block(0, 0, dim, 1)); } } else { file.read_line(entity_tag, entity_dim, parametric, num_nodes_in_block); for (auto _ [[gnu::unused]] : arange(num_nodes_in_block)) { size_t tag; file.read_line(tag, pos(_x), pos(_y), pos(_z)); - if (file.version < 4.1) { - file.first_node_number = std::min(file.first_node_number, tag); - file.last_node_number = std::max(file.last_node_number, tag); - } + file.first_node_number = std::min(file.first_node_number, tag); + file.last_node_number = std::max(file.last_node_number, tag); nodes.push_back(pos.block(0, 0, dim, 1)); file.node_tags[tag] = node_id; ++node_id; } } } }; readers["$Elements"] = [&](const std::string & /*unused*/) { size_t num_blocks; size_t num_elements; file.read_line(num_blocks, num_elements); for (auto block [[gnu::unused]] : arange(num_blocks)) { int entity_dim; int entity_tag; int element_type; size_t num_elements_in_block; - if (file.version >= 4.1) { + if (file.version >= 4001) { file.read_line(entity_dim, entity_tag, element_type, num_elements_in_block); } else { file.read_line(entity_tag, entity_dim, element_type, num_elements_in_block); } /// get the connectivity vector depending on the element type - auto &&akantu_type = + auto && akantu_type = this->_msh_to_akantu_element_types[(MSHElementType)element_type]; if (akantu_type == _not_defined) { AKANTU_DEBUG_WARNING("Unsuported element kind " << element_type << " at line " << file.current_line); continue; } Element elem{akantu_type, 0, _not_ghost}; - auto &connectivity = file.mesh_accessor.getConnectivity(akantu_type); + auto & connectivity = file.mesh_accessor.getConnectivity(akantu_type); Vector local_connect(connectivity.getNbComponent()); - auto &&read_order = this->_read_order[akantu_type]; + auto && read_order = this->_read_order[akantu_type]; - auto &data0 = + auto & data0 = file.mesh_accessor.template getData("tag_0", akantu_type); data0.resize(data0.size() + num_elements_in_block, 0); - auto &physical_data = file.mesh_accessor.template getData( + auto range = file.entity_tag_to_physical_tags.equal_range( + std::make_pair(entity_tag, entity_dim)); + + auto & physical_data = file.mesh_accessor.template getData( "physical_names", akantu_type); physical_data.resize(physical_data.size() + num_elements_in_block, ""); for (auto _ [[gnu::unused]] : arange(num_elements_in_block)) { - auto &&sstr_elem = file.get_line(); + auto && sstr_elem = file.get_line(); std::size_t elem_tag; sstr_elem >> elem_tag; - for (auto &&c : arange(connectivity.getNbComponent())) { + for (auto && c : arange(connectivity.getNbComponent())) { std::size_t node_tag; sstr_elem >> node_tag; + AKANTU_DEBUG_ASSERT(node_tag >= file.first_node_number, + "Node number not in range : line " + << file.current_line); + AKANTU_DEBUG_ASSERT(node_tag <= file.last_node_number, "Node number not in range : line " << file.current_line); node_tag = file.node_tags[node_tag]; local_connect(read_order[c]) = node_tag; } connectivity.push_back(local_connect); elem.element = connectivity.size() - 1; file.element_tags[elem_tag] = elem; - auto range = file.entity_tag_to_physical_tags.equal_range( - std::make_pair(entity_tag, entity_dim)); bool first = true; for (auto it = range.first; it != range.second; ++it) { auto phys_it = this->physical_names.find(it->second); if (first) { data0(elem.element) = it->second; // for compatibility with version 2 if (phys_it != this->physical_names.end()) { physical_data(elem.element) = phys_it->second; } first = false; } if (phys_it != this->physical_names.end()) { file.mesh.getElementGroup(phys_it->second).add(elem, true, false); } } } } - for (auto &&element_group : file.mesh.iterateElementGroups()) { + for (auto && element_group : file.mesh.iterateElementGroups()) { element_group.getNodeGroup().optimize(); } }; } /* -------------------------------------------------------------------------- */ -void MeshIOMSH::read(const std::string &filename, Mesh &mesh) { +void MeshIOMSH::read(const std::string & filename, Mesh & mesh) { File file(filename, mesh); std::map> readers; readers["$MeshFormat"] = [&](const std::string & /*unused*/) { - auto &&sstr = file.get_line(); + auto && sstr = file.get_line(); + double version; int format; - sstr >> file.version >> format; + sstr >> version >> format; + + int major = std::trunc(version); + int minor = std::round(10 * (version - major)); + file.version = major * 1000 + minor; if (format != 0) { AKANTU_ERROR("This reader can only read ASCII files."); } - if (file.version > 2) { + if (file.version > 2000) { sstr >> file.size_of_size_t; if (file.size_of_size_t > int(sizeof(UInt))) { AKANTU_DEBUG_INFO("The size of the indexes in akantu might be to small " "to read this file (akantu " << sizeof(UInt) << " vs. msh file " << file.size_of_size_t << ")"); } } - if (file.version < 4) { + if (file.version < 4000) { this->populateReaders2(file, readers); } else { this->populateReaders4(file, readers); } }; - auto &&read_data = [&](auto &&entity_tags, auto &&get_data, - auto &&read_data) { + auto && read_data = [&](auto && entity_tags, auto && get_data, + auto && read_data) { auto read_data_tags = [&](auto x) { UInt number_of_tags{0}; file.read_line(number_of_tags); std::vector tags(number_of_tags); - for (auto &&tag : tags) { + for (auto && tag : tags) { file.read_line(tag); } return tags; }; - auto &&string_tags = read_data_tags(std::string{}); - auto &&real_tags [[gnu::unused]] = read_data_tags(double{}); - auto &&int_tags = read_data_tags(int{}); + auto && string_tags = read_data_tags(std::string{}); + auto && real_tags [[gnu::unused]] = read_data_tags(double{}); + auto && int_tags = read_data_tags(int{}); - for (auto &s : string_tags) { + for (auto & s : string_tags) { s = trim(s, '"'); } auto id = string_tags[0]; auto size = int_tags[2]; auto nb_component = int_tags[1]; - auto &data = get_data(id, size, nb_component); + auto & data = get_data(id, size, nb_component); for (auto n [[gnu::unused]] : arange(size)) { - auto &&sstr = file.get_line(); + auto && sstr = file.get_line(); size_t tag; sstr >> tag; - const auto &entity = entity_tags[tag]; + const auto & entity = entity_tags[tag]; read_data(entity, sstr, data, nb_component); } }; readers["$NodeData"] = [&](const std::string & /*unused*/) { /* $NodeData numStringTags(ASCII int) stringTag(string) ... numRealTags(ASCII int) realTag(ASCII double) ... numIntegerTags(ASCII int) integerTag(ASCII int) ... nodeTag(size_t) value(double) ... ... $EndNodeData */ read_data( file.node_tags, - [&](auto &&id, auto &&size [[gnu::unused]], - auto &&nb_component [[gnu::unused]]) -> Array & { - auto &data = + [&](auto && id, auto && size [[gnu::unused]], + auto && nb_component [[gnu::unused]]) -> Array & { + auto & data = file.mesh.template getNodalData(id, nb_component); data.resize(size); return data; }, - [&](auto &&node, auto &&sstr, auto &&data, - auto &&nb_component [[gnu::unused]]) { + [&](auto && node, auto && sstr, auto && data, + auto && nb_component [[gnu::unused]]) { for (auto c : arange(nb_component)) { sstr >> data(node, c); } }); }; readers["$ElementData"] = [&](const std::string & /*unused*/) { /* $ElementData numStringTags(ASCII int) stringTag(string) ... numRealTags(ASCII int) realTag(ASCII double) ... numIntegerTags(ASCII int) integerTag(ASCII int) ... elementTag(size_t) value(double) ... ... $EndElementData */ read_data( file.element_tags, - [&](auto &&id, auto &&size [[gnu::unused]], - auto &&nb_component + [&](auto && id, auto && size [[gnu::unused]], + auto && nb_component [[gnu::unused]]) -> ElementTypeMapArray & { file.mesh.template getElementalData(id); return file.mesh.template getElementalData(id); }, - [&](auto &&element, auto &&sstr, auto &&data, auto &&nb_component) { + [&](auto && element, auto && sstr, auto && data, auto && nb_component) { if (not data.exists(element.type)) { data.alloc(mesh.getNbElement(element.type), nb_component, element.type, element.ghost_type); } - auto &data_array = data(element.type); + auto & data_array = data(element.type); for (auto c : arange(nb_component)) { sstr >> data_array(element.element, c); } }); }; readers["$ElementNodeData"] = [&](const std::string & /*unused*/) { /* $ElementNodeData numStringTags(ASCII int) stringTag(string) ... numRealTags(ASCII int) realTag(ASCII double) ... numIntegerTags(ASCII int) integerTag(ASCII int) ... elementTag(size_t) value(double) ... ... $EndElementNodeData */ read_data( file.element_tags, - [&](auto &&id, auto &&size [[gnu::unused]], - auto &&nb_component + [&](auto && id, auto && size [[gnu::unused]], + auto && nb_component [[gnu::unused]]) -> ElementTypeMapArray & { file.mesh.template getElementalData(id); - auto &data = file.mesh.template getElementalData(id); + auto & data = file.mesh.template getElementalData(id); data.isNodal(true); return data; }, - [&](auto &&element, auto &&sstr, auto &&data, auto &&nb_component) { + [&](auto && element, auto && sstr, auto && data, auto && nb_component) { int nb_nodes_per_element; sstr >> nb_nodes_per_element; if (not data.exists(element.type)) { data.alloc(mesh.getNbElement(element.type), nb_component * nb_nodes_per_element, element.type, element.ghost_type); } - auto &data_array = data(element.type); + auto & data_array = data(element.type); for (auto c : arange(nb_component)) { sstr >> data_array(element.element, c); } }); }; readers["$PhysicalNames"] = [&](const std::string & /*unused*/) { file.has_physical_names = true; int num_of_phys_names; file.read_line(num_of_phys_names); /// the format line for (auto k [[gnu::unused]] : arange(num_of_phys_names)) { int phys_name_id; int phys_dim; std::string phys_name; file.read_line(phys_dim, phys_name_id, std::quoted(phys_name)); this->physical_names[phys_name_id] = phys_name; } }; - readers["Unsupported"] = [&](const std::string &_block) { + readers["Unsupported"] = [&](const std::string & _block) { std::string block = _block.substr(1); AKANTU_DEBUG_WARNING("Unsupported block_kind " << block << " at line " << file.current_line); - auto &&end_block = "$End" + block; + auto && end_block = "$End" + block; while (file.line != end_block) { file.get_line(); } }; while (file.good()) { std::string block; file.read_line(block); - auto &&it = readers.find(block); + auto && it = readers.find(block); if (it != readers.end()) { it->second(block); std::string end_block; file.read_line(end_block); block = block.substr(1); if (end_block != "$End" + block) { AKANTU_EXCEPTION("The reader failed to properly read the block " << block << ". Expected a $End" << block << " at line " << file.current_line); } } else if (not block.empty()) { readers["Unsupported"](block); } } // mesh.updateTypesOffsets(_not_ghost); - if (file.version < 4) { + if (file.version < 4000) { this->constructPhysicalNames("tag_0", mesh); if (file.has_physical_names) { mesh.createGroupsFromMeshData("physical_names"); } } MeshUtils::fillElementToSubElementsData(mesh); } /* -------------------------------------------------------------------------- */ -void MeshIOMSH::write(const std::string &filename, const Mesh &mesh) { +void MeshIOMSH::write(const std::string & filename, const Mesh & mesh) { std::ofstream outfile; - const Array &nodes = mesh.getNodes(); + const Array & nodes = mesh.getNodes(); outfile.open(filename.c_str()); outfile << "$MeshFormat" << "\n"; outfile << "2.2 0 8" << "\n"; outfile << "$EndMeshFormat" << "\n"; outfile << std::setprecision(std::numeric_limits::digits10); outfile << "$Nodes" << "\n"; outfile << nodes.size() << "\n"; outfile << std::uppercase; for (Int i = 0; i < nodes.size(); ++i) { auto offset = i * nodes.getNbComponent(); outfile << i + 1; for (Int j = 0; j < nodes.getNbComponent(); ++j) { outfile << " " << nodes.data()[offset + j]; } for (Int p = nodes.getNbComponent(); p < 3; ++p) { outfile << " " << 0.; } outfile << "\n"; ; } outfile << std::nouppercase; outfile << "$EndNodes" << "\n"; outfile << "$Elements" << "\n"; Int nb_elements = 0; - for (auto &&type : + for (auto && type : mesh.elementTypes(_all_dimensions, _not_ghost, _ek_not_defined)) { - const auto &connectivity = mesh.getConnectivity(type, _not_ghost); + const auto & connectivity = mesh.getConnectivity(type, _not_ghost); nb_elements += connectivity.size(); } outfile << nb_elements << "\n"; std::map element_to_msh_element; Idx element_idx = 1; auto element = ElementNull; - for (auto &&type : + for (auto && type : mesh.elementTypes(_all_dimensions, _not_ghost, _ek_not_defined)) { - const auto &connectivity = mesh.getConnectivity(type, _not_ghost); + const auto & connectivity = mesh.getConnectivity(type, _not_ghost); element.type = type; - Int *tag[2] = {nullptr, nullptr}; + Int * tag[2] = {nullptr, nullptr}; if (mesh.hasData("tag_0", type, _not_ghost)) { - const auto &data_tag_0 = mesh.getData("tag_0", type, _not_ghost); + const auto & data_tag_0 = mesh.getData("tag_0", type, _not_ghost); tag[0] = data_tag_0.data(); } if (mesh.hasData("tag_1", type, _not_ghost)) { - const auto &data_tag_1 = mesh.getData("tag_1", type, _not_ghost); + const auto & data_tag_1 = mesh.getData("tag_1", type, _not_ghost); tag[1] = data_tag_1.data(); } - for (auto &&data : + for (auto && data : enumerate(make_view(connectivity, connectivity.getNbComponent()))) { element.element = std::get<0>(data); - const auto &conn = std::get<1>(data); + const auto & conn = std::get<1>(data); element_to_msh_element.insert(std::make_pair(element, element_idx)); outfile << element_idx << " " << _akantu_to_msh_element_types[type] << " 2"; /// \todo write the real data in the file for (Int t = 0; t < 2; ++t) { if (tag[t] != nullptr) { outfile << " " << tag[t][element.element]; } else { outfile << " 0"; } } - for (auto &&c : conn) { + for (auto && c : conn) { outfile << " " << c + 1; } outfile << "\n"; element_idx++; } } outfile << "$EndElements" << "\n"; if (mesh.hasData(MeshDataType::_nodal)) { - auto &&tags = mesh.getTagNames(); - for (auto &&tag : tags) { + auto && tags = mesh.getTagNames(); + for (auto && tag : tags) { auto type = mesh.getTypeCode(tag, MeshDataType::_nodal); if (type != MeshDataTypeCode::_real) { AKANTU_DEBUG_WARNING( "The field " << tag << " is ignored by the MSH writer, msh files do not support " << type << " data"); continue; } - auto &&data = mesh.getNodalData(tag); + auto && data = mesh.getNodalData(tag); outfile << "$NodeData" << "\n"; outfile << "1" << "\n"; outfile << "\"" << tag << "\"\n"; outfile << "1\n0.0" << "\n"; outfile << "3\n0" << "\n"; outfile << data.getNbComponent() << "\n"; outfile << data.size() << "\n"; - for (auto &&d : enumerate(make_view(data, data.getNbComponent()))) { + for (auto && d : enumerate(make_view(data, data.getNbComponent()))) { outfile << std::get<0>(d) + 1; - for (auto &&v : std::get<1>(d)) { + for (auto && v : std::get<1>(d)) { outfile << " " << v; } outfile << "\n"; } outfile << "$EndNodeData" << "\n"; } } if (mesh.hasData(MeshDataType::_elemental)) { - auto &&tags = mesh.getTagNames(); - for (auto &&tag : tags) { - auto &&data = mesh.getElementalData(tag); + auto && tags = mesh.getTagNames(); + for (auto && tag : tags) { + auto && data = mesh.getElementalData(tag); auto type = mesh.getTypeCode(tag, MeshDataType::_elemental); if (type != MeshDataTypeCode::_real) { AKANTU_DEBUG_WARNING( "The field " << tag << " is ignored by the MSH writer, msh files do not support " << type << " data"); continue; } if (data.isNodal()) { continue; } auto size = data.size(); if (size == 0) { continue; } - auto &&nb_components = data.getNbComponents(); + auto && nb_components = data.getNbComponents(); auto nb_component = nb_components(*(data.elementTypes().begin())); outfile << "$ElementData" << "\n"; outfile << "1" << "\n"; outfile << "\"" << tag << "\"\n"; outfile << "1\n0.0" << "\n"; outfile << "3\n0" << "\n"; outfile << nb_component << "\n"; outfile << size << "\n"; Element element; for (auto type : data.elementTypes()) { element.type = type; - for (auto &&_ : enumerate(make_view(data(type), nb_components(type)))) { + for (auto && _ : + enumerate(make_view(data(type), nb_components(type)))) { element.element = std::get<0>(_); outfile << element_to_msh_element[element]; - for (auto &&v : std::get<1>(_)) { + for (auto && v : std::get<1>(_)) { outfile << " " << v; } outfile << "\n"; } } outfile << "$EndElementData" << "\n"; } } outfile.close(); } /* -------------------------------------------------------------------------- */ } // namespace akantu diff --git a/src/mesh/element_group.cc b/src/mesh/element_group.cc index 20fa6d730..1fe3d44bb 100644 --- a/src/mesh/element_group.cc +++ b/src/mesh/element_group.cc @@ -1,194 +1,189 @@ /** * @file element_group.cc * * @author Dana Christen * @author Nicolas Richart * @author Marco Vocialta * * @date creation: Wed Nov 13 2013 * @date last modification: Wed Dec 09 2020 * * @brief Stores information relevent to the notion of domain boundary and * surfaces. * * * @section LICENSE * * Copyright (©) 2014-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_csr.hh" #include "dumpable.hh" #include "dumpable_inline_impl.hh" #include "group_manager.hh" #include "group_manager_inline_impl.hh" #include "mesh.hh" #include "mesh_utils.hh" #include #include #include #include "element_group.hh" /* -------------------------------------------------------------------------- */ #include "dumper_iohelper_paraview.hh" namespace akantu { /* -------------------------------------------------------------------------- */ ElementGroup::ElementGroup(const std::string & group_name, const Mesh & mesh, NodeGroup & node_group, Int dimension, const std::string & id) : mesh(mesh), name(group_name), elements("elements", id), node_group(node_group), dimension(dimension) { AKANTU_DEBUG_IN(); this->registerDumper("paraview_" + group_name, group_name, true); this->addDumpFilteredMesh(mesh, elements, node_group.getNodes(), _all_dimensions); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ ElementGroup::ElementGroup(const ElementGroup & /*other*/) = default; /* -------------------------------------------------------------------------- */ void ElementGroup::clear() { elements.free(); } /* -------------------------------------------------------------------------- */ void ElementGroup::clear(ElementType type, GhostType ghost_type) { if (elements.exists(type, ghost_type)) { elements(type, ghost_type).clear(); } } /* -------------------------------------------------------------------------- */ bool ElementGroup::empty() const { return elements.empty(); } /* -------------------------------------------------------------------------- */ void ElementGroup::append(const ElementGroup & other_group) { AKANTU_DEBUG_IN(); node_group.append(other_group.node_group); /// loop on all element types in all dimensions for (auto ghost_type : ghost_types) { for (auto type : other_group.elementTypes(_ghost_type = ghost_type, _element_kind = _ek_not_defined)) { const auto & other_elem_list = other_group.elements(type, ghost_type); auto nb_other_elem = other_elem_list.size(); auto & elem_list = elements.alloc(0, 1, type, ghost_type); auto nb_elem = elem_list.size(); /// append new elements to current list elem_list.resize(nb_elem + nb_other_elem); std::copy(other_elem_list.begin(), other_elem_list.end(), elem_list.begin() + nb_elem); - - /// remove duplicates - std::sort(elem_list.begin(), elem_list.end()); - auto end = std::unique(elem_list.begin(), elem_list.end()); - elem_list.resize(end - elem_list.begin()); } } + this->optimize(); + AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void ElementGroup::printself(std::ostream & stream, int indent) const { std::string space; for (Int i = 0; i < indent; i++, space += AKANTU_INDENT) { ; } stream << space << "ElementGroup [" << std::endl; stream << space << " + name: " << name << std::endl; stream << space << " + dimension: " << dimension << std::endl; elements.printself(stream, indent + 1); node_group.printself(stream, indent + 1); stream << space << "]" << std::endl; } /* -------------------------------------------------------------------------- */ void ElementGroup::optimize() { // increasing the locality of data when iterating on the element of a group for (auto ghost_type : ghost_types) { for (auto type : elements.elementTypes(_ghost_type = ghost_type)) { auto & els = elements(type, ghost_type); std::sort(els.begin(), els.end()); auto end = std::unique(els.begin(), els.end()); els.resize(end - els.begin()); } } node_group.optimize(); } /* -------------------------------------------------------------------------- */ void ElementGroup::fillFromNodeGroup() { CSR node_to_elem; MeshUtils::buildNode2Elements(this->mesh, node_to_elem, this->dimension); std::set seen; for (const auto & node : this->node_group) { for (const auto & elem : node_to_elem.getRow(node)) { if (this->dimension != _all_dimensions && this->dimension != Mesh::getSpatialDimension(elem.type)) { continue; } if (seen.find(elem) != seen.end()) { continue; } auto nb_nodes_per_element = Mesh::getNbNodesPerElement(elem.type); - auto conn_it = - this->mesh.getConnectivity(elem.type, elem.ghost_type) - .begin(nb_nodes_per_element); + auto conn_it = this->mesh.getConnectivity(elem.type, elem.ghost_type) + .begin(nb_nodes_per_element); const auto & conn = conn_it[elem.element]; Int count = 0; for (auto n : conn) { - count += - (this->node_group.getNodes().find(n) != Idx(-1) ? 1 : 0); + count += (this->node_group.getNodes().find(n) != Idx(-1) ? 1 : 0); } if (count == nb_nodes_per_element) { this->add(elem); } seen.insert(elem); } } this->optimize(); } /* -------------------------------------------------------------------------- */ void ElementGroup::addDimension(Int dimension) { this->dimension = std::max(dimension, this->dimension); } /* -------------------------------------------------------------------------- */ } // namespace akantu diff --git a/src/mesh/element_type_map.hh b/src/mesh/element_type_map.hh index 328daa351..3294e489f 100644 --- a/src/mesh/element_type_map.hh +++ b/src/mesh/element_type_map.hh @@ -1,507 +1,507 @@ /** * @file element_type_map.hh * * @author Lucas Frerot * @author Nicolas Richart * * @date creation: Wed Aug 31 2011 * @date last modification: Thu Mar 11 2021 * * @brief storage class by element type * * * @section LICENSE * * Copyright (©) 2010-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_array.hh" #include "aka_named_argument.hh" #include "element.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ #ifndef AKANTU_ELEMENT_TYPE_MAP_HH_ #define AKANTU_ELEMENT_TYPE_MAP_HH_ namespace akantu { class FEEngine; } // namespace akantu namespace akantu { namespace { DECLARE_NAMED_ARGUMENT(all_ghost_types); DECLARE_NAMED_ARGUMENT(default_value); DECLARE_NAMED_ARGUMENT(element_kind); DECLARE_NAMED_ARGUMENT(ghost_type); DECLARE_NAMED_ARGUMENT(nb_component); DECLARE_NAMED_ARGUMENT(nb_component_functor); DECLARE_NAMED_ARGUMENT(with_nb_element); DECLARE_NAMED_ARGUMENT(with_nb_nodes_per_element); DECLARE_NAMED_ARGUMENT(spatial_dimension); DECLARE_NAMED_ARGUMENT(do_not_default); DECLARE_NAMED_ARGUMENT(element_filter); } // namespace template class ElementTypeMap; /* -------------------------------------------------------------------------- */ /* ElementTypeMapBase */ /* -------------------------------------------------------------------------- */ /// Common non templated base class for the ElementTypeMap class class ElementTypeMapBase { public: virtual ~ElementTypeMapBase() = default; }; /* -------------------------------------------------------------------------- */ /* ElementTypeMap */ /* -------------------------------------------------------------------------- */ template class ElementTypeMap : public ElementTypeMapBase { public: using value_type = Stored; ElementTypeMap(); ~ElementTypeMap() override; inline static auto printType(SupportType type, GhostType ghost_type) -> std::string; /*! Tests whether a type is present in the object * @param type the type to check for * @param ghost_type optional: by default, the data map for non-ghost * elements is searched * @return true if the type is present. */ inline auto exists(SupportType type, GhostType ghost_type = _not_ghost) const -> bool; /*! get the stored data corresponding to a type * @param type the type to check for * @param ghost_type optional: by default, the data map for non-ghost * elements is searched * @return stored data corresponding to type. */ inline auto operator()(SupportType type, GhostType ghost_type = _not_ghost) const -> const Stored &; /*! get the stored data corresponding to a type * @param type the type to check for * @param ghost_type optional: by default, the data map for non-ghost * elements is searched * @return stored data corresponding to type. */ inline auto operator()(SupportType type, GhostType ghost_type = _not_ghost) -> Stored &; /*! insert data of a new type (not yet present) into the map. THIS METHOD IS * NOT ARRAY SAFE, when using ElementTypeMapArray, use setArray instead * @param data to insert * @param type type of data (if this type is already present in the map, * an exception is thrown). * @param ghost_type optional: by default, the data map for non-ghost * elements is searched * @return stored data corresponding to type. */ template inline auto operator()(U && insertee, SupportType type, GhostType ghost_type = _not_ghost) -> Stored &; public: /// print helper virtual void printself(std::ostream & stream, int indent = 0) const; /* ------------------------------------------------------------------------ */ /* Element type Iterator */ /* ------------------------------------------------------------------------ */ /*! iterator allows to iterate over type-data pairs of the map. The interface * expects the SupportType to be ElementType. */ using DataMap = std::map; /// helper class to use in range for constructions class type_iterator : private std::iterator { public: using value_type = const SupportType; using pointer = const SupportType *; using reference = SupportType; protected: using DataMapIterator = typename ElementTypeMap::DataMap::const_iterator; public: type_iterator(DataMapIterator & list_begin, DataMapIterator & list_end, Int dim, ElementKind ek); type_iterator(const type_iterator & it); type_iterator() = default; inline auto operator*() -> reference; inline auto operator*() const -> reference; inline auto operator++() -> type_iterator &; auto operator++(int) -> type_iterator; inline auto operator==(const type_iterator & other) const -> bool; inline auto operator!=(const type_iterator & other) const -> bool; auto operator=(const type_iterator & other) -> type_iterator &; private: DataMapIterator list_begin; DataMapIterator list_end; Int dim; ElementKind kind; }; /// helper class to use in range for constructions class ElementTypesIteratorHelper { public: using Container = ElementTypeMap; using iterator = typename Container::type_iterator; ElementTypesIteratorHelper(const Container & container, Int dim, GhostType ghost_type, ElementKind kind) : container(std::cref(container)), dim(dim), ghost_type(ghost_type), kind(kind) {} template ElementTypesIteratorHelper(const Container & container, use_named_args_t /*unused*/, pack &&... _pack) : ElementTypesIteratorHelper( container, OPTIONAL_NAMED_ARG(spatial_dimension, _all_dimensions), OPTIONAL_NAMED_ARG(ghost_type, _not_ghost), OPTIONAL_NAMED_ARG(element_kind, _ek_not_defined)) {} ElementTypesIteratorHelper(const ElementTypesIteratorHelper &) = default; auto operator=(const ElementTypesIteratorHelper &) -> ElementTypesIteratorHelper & = default; auto operator=(ElementTypesIteratorHelper &&) noexcept -> ElementTypesIteratorHelper & = default; auto begin() -> iterator; auto end() -> iterator; private: std::reference_wrapper container; Int dim; GhostType ghost_type; ElementKind kind; }; private: auto elementTypesImpl(Int dim = _all_dimensions, GhostType ghost_type = _not_ghost, ElementKind kind = _ek_not_defined) const -> ElementTypesIteratorHelper; template auto elementTypesImpl(const use_named_args_t & /*unused*/, pack &&... _pack) const -> ElementTypesIteratorHelper; public: /*! * \param _pack * \parblock * represent optional parameters: * \li \c _spatial_dimension filter for elements of given spatial * dimension * \li \c _ghost_type filter for a certain ghost_type * \li \c _element_kind filter for elements of given kind * \endparblock */ template auto elementTypes(pack &&... _pack) const -> std::enable_if_t::value, ElementTypesIteratorHelper> { return elementTypesImpl(use_named_args, std::forward(_pack)...); } template auto elementTypes(pack &&... _pack) const -> std::enable_if_t::value, ElementTypesIteratorHelper> { return elementTypesImpl(std::forward(_pack)...); } /*! Get an iterator to the beginning of a subset datamap. This method expects * the SupportType to be ElementType. * @param dim optional: iterate over data of dimension dim (e.g. when * iterating over (surface) facets of a 3D mesh, dim would be 2). * by default, all dimensions are considered. * @param ghost_type optional: by default, the data map for non-ghost * elements is iterated over. * @param kind optional: the kind of element to search for (see * aka_common.hh), by default all kinds are considered * @return an iterator to the first stored data matching the filters * or an iterator to the end of the map if none match*/ [[deprecated("Use elementTypes instead")]] inline auto firstType(Int dim = _all_dimensions, GhostType ghost_type = _not_ghost, ElementKind kind = _ek_not_defined) const -> type_iterator; /*! Get an iterator to the end of a subset datamap. This method expects * the SupportType to be ElementType. * @param dim optional: iterate over data of dimension dim (e.g. when * iterating over (surface) facets of a 3D mesh, dim would be 2). * by default, all dimensions are considered. * @param ghost_type optional: by default, the data map for non-ghost * elements is iterated over. * @param kind optional: the kind of element to search for (see * aka_common.hh), by default all kinds are considered * @return an iterator to the last stored data matching the filters * or an iterator to the end of the map if none match */ [[deprecated("Use elementTypes instead")]] inline auto lastType(Int dim = _all_dimensions, GhostType ghost_type = _not_ghost, ElementKind kind = _ek_not_defined) const -> type_iterator; /*! Direct access to the underlying data map. for internal use by daughter * classes only * @param ghost_type whether to return the data map or the ghost_data map * @return the raw map */ inline auto getData(GhostType ghost_type) -> DataMap &; /*! Direct access to the underlying data map. for internal use by daughter * classes only * @param ghost_type whether to return the data map or the ghost_data map * @return the raw map */ inline auto getData(GhostType ghost_type) const -> const DataMap &; /* ------------------------------------------------------------------------ */ protected: DataMap data; DataMap ghost_data; }; /* -------------------------------------------------------------------------- */ /* Some typedefs */ /* -------------------------------------------------------------------------- */ template class ElementTypeMapArray : public ElementTypeMap>, SupportType> { public: using value_type = T; using array_type = Array; protected: using parent = ElementTypeMap>, SupportType>; using DataMap = typename parent::DataMap; public: using type_iterator = typename parent::type_iterator; /// standard assigment (copy) operator - void operator=(const ElementTypeMapArray &) = delete; - ElementTypeMapArray(const ElementTypeMapArray & /*other*/); + auto operator=(const ElementTypeMapArray & other) -> ElementTypeMapArray &; + ElementTypeMapArray(const ElementTypeMapArray & other); /// explicit copy void copy(const ElementTypeMapArray & other); /*! Constructor * @param id optional: identifier (string) * @param parent_id optional: parent identifier. for organizational purposes * only */ ElementTypeMapArray(const ID & id = "by_element_type_array", const ID & parent_id = "no_parent") : parent(), id(parent_id + ":" + id), name(id){}; /*! allocate memory for a new array * @param size number of tuples of the new array * @param nb_component tuple size * @param type the type under which the array is indexed in the map * @param ghost_type whether to add the field to the data map or the * ghost_data map * @param default_value the default value to use to fill the array * @return a reference to the allocated array */ inline auto alloc(Int size, Int nb_component, SupportType type, GhostType ghost_type, const T & default_value = T()) -> Array &; /*! allocate memory for a new array in both the data and the ghost_data map * @param size number of tuples of the new array * @param nb_component tuple size * @param type the type under which the array is indexed in the map*/ inline void alloc(Int size, Int nb_component, SupportType type, const T & default_value = T()); /* get a reference to the array of certain type * @param type data filed under type is returned * @param ghost_type optional: by default the non-ghost map is searched * @return a reference to the array */ inline auto operator()(SupportType type, GhostType ghost_type = _not_ghost) const -> const Array &; /// access the data of an element, this combine the map and array accessor inline auto operator()(const Element & element, Int component = 0) const -> const T &; /// access the data of an element, this combine the map and array accessor inline auto operator()(const Element & element, Int component = 0) -> T &; /// access the data of an element, this combine the map and array accessor inline decltype(auto) get(const Element & element); inline decltype(auto) get(const Element & element) const; template < typename... Ns, std::enable_if_t< aka::conjunction>...>::value and sizeof...(Ns) >= 1> * = nullptr> inline decltype(auto) get(const Element & element, Ns &&... ns); template < typename... Ns, std::enable_if_t< aka::conjunction>...>::value and sizeof...(Ns) >= 1> * = nullptr> inline decltype(auto) get(const Element & element, Ns &&... ns) const; /* get a reference to the array of certain type * @param type data filed under type is returned * @param ghost_type optional: by default the non-ghost map is searched * @return a const reference to the array */ inline auto operator()(SupportType type, GhostType ghost_type = _not_ghost) -> Array &; /*! insert data of a new type (not yet present) into the map. * @param type type of data (if this type is already present in the map, * an exception is thrown). * @param ghost_type optional: by default, the data map for non-ghost * elements is searched * @param vect the vector to include into the map * @return stored data corresponding to type. */ inline void setArray(SupportType type, GhostType ghost_type, const Array & vect); /*! frees all memory related to the data*/ inline void free(); inline void clear(); inline bool empty() const __attribute__((warn_unused_result)); /*! set all values in the ElementTypeMap to zero*/ inline void zero() { this->set(T()); } /*! set all values in the ElementTypeMap to value */ template inline void set(const ST & value); /*! deletes and reorders entries in the stored arrays * @param new_numbering a ElementTypeMapArray of new indices. UInt(-1) * indicates * deleted entries. */ inline void onElementsRemoved(const ElementTypeMapArray & new_numbering); /// text output helper void printself(std::ostream & stream, int indent = 0) const override; /*! set the id * @param id the new name */ inline void setID(const ID & id) { this->id = id; } /// return the id inline auto getID() const -> ID { return this->id; } auto getNbComponents(Int dim = _all_dimensions, GhostType requested_ghost_type = _not_ghost, ElementKind kind = _ek_not_defined) const -> ElementTypeMap { ElementTypeMap nb_components; auto all_ghost_types = requested_ghost_type == _casper; for (auto ghost_type : ghost_types) { if ((not(ghost_type == requested_ghost_type)) and (not all_ghost_types)) { continue; } for (const auto & type : this->elementTypes(dim, ghost_type, kind)) { auto nb_comp = (*this)(type, ghost_type).getNbComponent(); nb_components(type, ghost_type) = nb_comp; } } return nb_components; } /* ------------------------------------------------------------------------ */ /* more evolved allocators */ /* ------------------------------------------------------------------------ */ public: /// initialize the arrays in accordance to a functor template void initialize(const Func & f, const T & default_value, bool do_not_default); /// initialize with sizes and number of components in accordance of a mesh /// content template void initialize(const Mesh & mesh, pack &&... _pack); /// initialize with sizes and number of components in accordance of a fe /// engine content (aka integration points) template void initialize(const FEEngine & fe_engine, pack &&... _pack); /* ------------------------------------------------------------------------ */ /* Accesssors */ /* ------------------------------------------------------------------------ */ public: /// get the name of the internal field AKANTU_GET_MACRO(Name, name, ID); /** * get the size of the ElementTypeMapArray * @param[in] _pack * \parblock * optional arguments can be any of: * \li \c _spatial_dimension the dimension to consider (default: * _all_dimensions) * \li \c _ghost_type (default: _not_ghost) * \li \c _element_kind (default: _ek_not_defined) * \li \c _all_ghost_types (default: false) * \endparblock **/ template auto size(pack &&... _pack) const -> Int; auto isNodal() const -> bool { return is_nodal; } void isNodal(bool is_nodal) { this->is_nodal = is_nodal; } private: auto sizeImpl(Int spatial_dimension, GhostType ghost_type, ElementKind kind) const -> Int; private: ID id; protected: /// name of the element type map: e.g. connectivity, grad_u ID name; /// Is the data stored by node of the element bool is_nodal{false}; }; /// to store data Array by element type using ElementTypeMapReal = ElementTypeMapArray; /// to store data Array by element type using ElementTypeMapInt = ElementTypeMapArray; /// to store data Array by element type using ElementTypeMapUInt = ElementTypeMapArray; /// to store data Array by element type using ElementTypeMapIdx = ElementTypeMapArray; } // namespace akantu #endif /* AKANTU_ELEMENT_TYPE_MAP_HH_ */ diff --git a/src/mesh/element_type_map_tmpl.hh b/src/mesh/element_type_map_tmpl.hh index c52555040..06c42a88e 100644 --- a/src/mesh/element_type_map_tmpl.hh +++ b/src/mesh/element_type_map_tmpl.hh @@ -1,884 +1,899 @@ /** * @file element_type_map_tmpl.hh * * @author Guillaume Anciaux * @author Lucas Frerot * @author Nicolas Richart * * @date creation: Wed Aug 31 2011 * @date last modification: Thu Mar 11 2021 * * @brief implementation of template functions of the ElementTypeMap and * ElementTypeMapArray classes * * * @section LICENSE * * Copyright (©) 2010-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_static_if.hh" //#include "element_type_map.hh" //#include "mesh.hh" /* -------------------------------------------------------------------------- */ #include "element_type_conversion.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ #ifndef AKANTU_ELEMENT_TYPE_MAP_TMPL_HH_ #define AKANTU_ELEMENT_TYPE_MAP_TMPL_HH_ namespace akantu { /* -------------------------------------------------------------------------- */ /* ElementTypeMap */ /* -------------------------------------------------------------------------- */ template inline std::string ElementTypeMap::printType(SupportType type, GhostType ghost_type) { std::stringstream sstr; sstr << "(" << ghost_type << ":" << type << ")"; return sstr.str(); } /* -------------------------------------------------------------------------- */ template inline bool ElementTypeMap::exists(SupportType type, GhostType ghost_type) const { return this->getData(ghost_type).find(type) != this->getData(ghost_type).end(); } /* -------------------------------------------------------------------------- */ template inline const Stored & ElementTypeMap::operator()(SupportType type, GhostType ghost_type) const { auto it = this->getData(ghost_type).find(type); if (it == this->getData(ghost_type).end()) { AKANTU_SILENT_EXCEPTION("No element of type " << ElementTypeMap::printType(type, ghost_type) << " in this ElementTypeMap<" << debug::demangle(typeid(Stored).name()) << "> class"); } return it->second; } /* -------------------------------------------------------------------------- */ template inline Stored & ElementTypeMap::operator()(SupportType type, GhostType ghost_type) { return this->getData(ghost_type)[type]; } /* -------------------------------------------------------------------------- */ template template inline Stored & ElementTypeMap::operator()(U && insertee, SupportType type, GhostType ghost_type) { auto it = this->getData(ghost_type).find(type); if (it != this->getData(ghost_type).end()) { AKANTU_SILENT_EXCEPTION("Element of type " << ElementTypeMap::printType(type, ghost_type) << " already in this ElementTypeMap<" << debug::demangle(typeid(Stored).name()) << "> class"); } else { auto & data = this->getData(ghost_type); const auto & res = data.insert(std::make_pair(type, std::forward(insertee))); it = res.first; } return it->second; } /* -------------------------------------------------------------------------- */ template inline typename ElementTypeMap::DataMap & ElementTypeMap::getData(GhostType ghost_type) { if (ghost_type == _not_ghost) { return data; } return ghost_data; } /* -------------------------------------------------------------------------- */ template inline const typename ElementTypeMap::DataMap & ElementTypeMap::getData(GhostType ghost_type) const { if (ghost_type == _not_ghost) { return data; } return ghost_data; } /* -------------------------------------------------------------------------- */ /// Works only if stored is a pointer to a class with a printself method template void ElementTypeMap::printself(std::ostream & stream, int indent) const { std::string space(indent, AKANTU_INDENT); stream << space << "ElementTypeMap<" << debug::demangle(typeid(Stored).name()) << "> [" << std::endl; for (auto && gt : ghost_types) { const DataMap & data = getData(gt); for (auto & pair : data) { stream << space << space << ElementTypeMap::printType(pair.first, gt) << std::endl; } } stream << space << "]" << std::endl; } /* -------------------------------------------------------------------------- */ template ElementTypeMap::ElementTypeMap() = default; /* -------------------------------------------------------------------------- */ template ElementTypeMap::~ElementTypeMap() = default; /* -------------------------------------------------------------------------- */ /* ElementTypeMapArray */ /* -------------------------------------------------------------------------- */ template void ElementTypeMapArray::copy( const ElementTypeMapArray & other) { for (auto ghost_type : ghost_types) { for (auto type : this->elementTypes(_all_dimensions, ghost_type, _ek_not_defined)) { const auto & array_to_copy = other(type, ghost_type); auto & array = this->alloc(0, array_to_copy.getNbComponent(), type, ghost_type); array.copy(array_to_copy); } } } /* -------------------------------------------------------------------------- */ template ElementTypeMapArray::ElementTypeMapArray( const ElementTypeMapArray & other) : parent(), id(other.id + "_copy"), name(other.name + "_copy") { this->copy(other); } +/* -------------------------------------------------------------------------- */ +template +auto ElementTypeMapArray::operator=( + const ElementTypeMapArray & other) -> ElementTypeMapArray & { + if (this != &other) { + AKANTU_DEBUG_WARNING("You are copying the ElementTypeMapArray " + << this->id << " are you sure it is on purpose"); + + this->id = other.id + "_copy"; + this->name = other.name + "_copy"; + this->copy(other); + } + return *this; +} + /* -------------------------------------------------------------------------- */ template inline Array & ElementTypeMapArray::alloc( Int size, Int nb_component, SupportType type, GhostType ghost_type, const T & default_value) { std::string ghost_id; if (ghost_type == _ghost) { ghost_id = ":ghost"; } auto it = this->getData(ghost_type).find(type); if (it == this->getData(ghost_type).end()) { auto id = this->id + ":" + std::to_string(type) + ghost_id; this->getData(ghost_type)[type] = std::make_unique>(size, nb_component, default_value, id); return *(this->getData(ghost_type)[type]); } AKANTU_DEBUG_INFO("The vector " << this->id << this->printType(type, ghost_type) << " already exists, it is resized instead of allocated."); auto && array = *(it->second); array.resize(size); return array; } /* -------------------------------------------------------------------------- */ template inline void ElementTypeMapArray::alloc( Int size, Int nb_component, SupportType type, const T & default_value) { this->alloc(size, nb_component, type, _not_ghost, default_value); this->alloc(size, nb_component, type, _ghost, default_value); } /* -------------------------------------------------------------------------- */ template inline void ElementTypeMapArray::free() { AKANTU_DEBUG_IN(); for (auto gt : ghost_types) { auto & data = this->getData(gt); data.clear(); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template inline void ElementTypeMapArray::clear() { for (auto gt : ghost_types) { auto & data = this->getData(gt); for (auto & vect : data) { vect.second->clear(); } } } /* -------------------------------------------------------------------------- */ template inline bool ElementTypeMapArray::empty() const { bool is_empty = true; for (auto gt : ghost_types) { auto & data = this->getData(gt); for (auto & vect : data) { is_empty &= vect.second->empty(); if (not is_empty) { return false; } } } return is_empty; } /* -------------------------------------------------------------------------- */ template template inline void ElementTypeMapArray::set(const ST & value) { for (auto gt : ghost_types) { auto & data = this->getData(gt); for (auto & vect : data) { vect.second->set(value); } } } /* -------------------------------------------------------------------------- */ template inline const Array & ElementTypeMapArray::operator()(SupportType type, GhostType ghost_type) const { auto it = this->getData(ghost_type).find(type); if (it == this->getData(ghost_type).end()) { AKANTU_SILENT_EXCEPTION("No element of type " << ElementTypeMapArray::printType(type, ghost_type) << " in this const ElementTypeMapArray<" << debug::demangle(typeid(T).name()) << "> class(\"" << this->id << "\")"); } return *(it->second); } /* -------------------------------------------------------------------------- */ template inline Array & ElementTypeMapArray::operator()(SupportType type, GhostType ghost_type) { auto it = this->getData(ghost_type).find(type); if (it == this->getData(ghost_type).end()) { AKANTU_SILENT_EXCEPTION("No element of type " << ElementTypeMapArray::printType(type, ghost_type) << " in this ElementTypeMapArray<" << debug::demangle(typeid(T).name()) << "> class (\"" << this->id << "\")"); } return *(it->second); } /* -------------------------------------------------------------------------- */ template inline void ElementTypeMapArray::setArray( SupportType type, GhostType ghost_type, const Array & vect) { auto it = this->getData(ghost_type).find(type); if (AKANTU_DEBUG_TEST(dblWarning) && it != this->getData(ghost_type).end() && it->second != &vect) { AKANTU_DEBUG_WARNING( "The Array " << this->printType(type, ghost_type) << " is already registred, this call can lead to a memory leak."); } this->getData(ghost_type)[type] = &(const_cast &>(vect)); } /* -------------------------------------------------------------------------- */ template inline void ElementTypeMapArray::onElementsRemoved( const ElementTypeMapArray & new_numbering) { for (auto gt : ghost_types) { for (auto && type : new_numbering.elementTypes(_all_dimensions, gt, _ek_not_defined)) { auto support_type = convertType(type); if (this->exists(support_type, gt)) { const auto & renumbering = new_numbering(type, gt); if (renumbering.empty()) { continue; } auto & vect = this->operator()(support_type, gt); auto nb_entry_per_element = vect.size() / renumbering.size(); auto nb_component = vect.getNbComponent(); Array tmp(renumbering.size() * nb_entry_per_element, nb_component); auto tmp_it = make_view(tmp, nb_entry_per_element * nb_component).begin(); Int new_size = 0; for (auto && data : zip(renumbering, make_view(vect, nb_entry_per_element * nb_component))) { auto new_i = std::get<0>(data); if (new_i != -1) { tmp_it[new_i] = std::get<1>(data); ++new_size; } } tmp.resize(new_size); vect.copy(tmp); } } } } /* -------------------------------------------------------------------------- */ template void ElementTypeMapArray::printself(std::ostream & stream, int indent) const { std::string space(indent, AKANTU_INDENT); stream << space << "ElementTypeMapArray<" << debug::demangle(typeid(T).name()) << "> [" << std::endl; for (UInt g = _not_ghost; g <= _ghost; ++g) { auto gt = (GhostType)g; const DataMap & data = this->getData(gt); typename DataMap::const_iterator it; for (it = data.begin(); it != data.end(); ++it) { stream << space << space << ElementTypeMapArray::printType(it->first, gt) << " [" << std::endl; it->second->printself(stream, indent + 3); stream << space << space << " ]" << std::endl; } } stream << space << "]" << std::endl; } /* -------------------------------------------------------------------------- */ /* SupportType Iterator */ /* -------------------------------------------------------------------------- */ template ElementTypeMap::type_iterator::type_iterator( DataMapIterator & list_begin, DataMapIterator & list_end, Int dim, ElementKind ek) : list_begin(list_begin), list_end(list_end), dim(dim), kind(ek) {} /* -------------------------------------------------------------------------- */ template ElementTypeMap::type_iterator::type_iterator( const type_iterator & it) : list_begin(it.list_begin), list_end(it.list_end), dim(it.dim), kind(it.kind) {} /* -------------------------------------------------------------------------- */ template typename ElementTypeMap::type_iterator & ElementTypeMap::type_iterator::operator=( const type_iterator & it) { if (this != &it) { list_begin = it.list_begin; list_end = it.list_end; dim = it.dim; kind = it.kind; } return *this; } /* -------------------------------------------------------------------------- */ template inline typename ElementTypeMap::type_iterator::reference ElementTypeMap::type_iterator::operator*() { return list_begin->first; } /* -------------------------------------------------------------------------- */ template inline typename ElementTypeMap::type_iterator::reference ElementTypeMap::type_iterator::operator*() const { return list_begin->first; } /* -------------------------------------------------------------------------- */ template inline typename ElementTypeMap::type_iterator & ElementTypeMap::type_iterator::operator++() { ++list_begin; while ((list_begin != list_end) && (((dim != _all_dimensions) && (dim != Mesh::getSpatialDimension(list_begin->first))) || ((kind != _ek_not_defined) && (kind != Mesh::getKind(list_begin->first))))) { ++list_begin; } return *this; } /* -------------------------------------------------------------------------- */ template typename ElementTypeMap::type_iterator ElementTypeMap::type_iterator::operator++(int) { type_iterator tmp(*this); operator++(); return tmp; } /* -------------------------------------------------------------------------- */ template inline bool ElementTypeMap::type_iterator::operator==( const type_iterator & other) const { return this->list_begin == other.list_begin; } /* -------------------------------------------------------------------------- */ template inline bool ElementTypeMap::type_iterator::operator!=( const type_iterator & other) const { return this->list_begin != other.list_begin; } /* -------------------------------------------------------------------------- */ template auto ElementTypeMap::ElementTypesIteratorHelper::begin() -> iterator { auto b = container.get().getData(ghost_type).begin(); auto e = container.get().getData(ghost_type).end(); // loop until the first valid type while ((b != e) && (((dim != _all_dimensions) && (dim != Mesh::getSpatialDimension(b->first))) || ((kind != _ek_not_defined) && (kind != Mesh::getKind(b->first))))) { ++b; } return iterator(b, e, dim, kind); } template auto ElementTypeMap::ElementTypesIteratorHelper::end() -> iterator { auto e = container.get().getData(ghost_type).end(); return iterator(e, e, dim, kind); } /* -------------------------------------------------------------------------- */ template auto ElementTypeMap::elementTypesImpl( Int dim, GhostType ghost_type, ElementKind kind) const -> ElementTypesIteratorHelper { return ElementTypesIteratorHelper(*this, dim, ghost_type, kind); } /* -------------------------------------------------------------------------- */ template template auto ElementTypeMap::elementTypesImpl( const use_named_args_t & unused, pack &&... _pack) const -> ElementTypesIteratorHelper { return ElementTypesIteratorHelper(*this, unused, _pack...); } /* -------------------------------------------------------------------------- */ template inline auto ElementTypeMap::firstType( Int dim, GhostType ghost_type, ElementKind kind) const -> type_iterator { return elementTypes(dim, ghost_type, kind).begin(); } /* -------------------------------------------------------------------------- */ template inline auto ElementTypeMap::lastType( Int dim, GhostType ghost_type, ElementKind kind) const -> type_iterator { typename DataMap::const_iterator e; e = getData(ghost_type).end(); return typename ElementTypeMap::type_iterator(e, e, dim, kind); } /* -------------------------------------------------------------------------- */ /// standard output stream operator template inline std::ostream & operator<<(std::ostream & stream, const ElementTypeMap & _this) { _this.printself(stream); return stream; } /* -------------------------------------------------------------------------- */ class ElementTypeMapArrayInitializer { protected: using CompFunc = std::function; public: ElementTypeMapArrayInitializer(const CompFunc & comp_func, Int spatial_dimension = _all_dimensions, GhostType ghost_type = _not_ghost, ElementKind element_kind = _ek_not_defined) : comp_func(comp_func), spatial_dimension(spatial_dimension), ghost_type(ghost_type), element_kind(element_kind) {} GhostType ghostType() const { return ghost_type; } virtual Int nbComponent(ElementType type) const { return comp_func(type, ghostType()); } virtual bool isNodal() const { return false; } protected: CompFunc comp_func; Int spatial_dimension; GhostType ghost_type; ElementKind element_kind; }; /* -------------------------------------------------------------------------- */ class MeshElementTypeMapArrayInitializer : public ElementTypeMapArrayInitializer { using CompFunc = ElementTypeMapArrayInitializer::CompFunc; public: MeshElementTypeMapArrayInitializer( const Mesh & mesh, Int nb_component = 1, Int spatial_dimension = _all_dimensions, GhostType ghost_type = _not_ghost, ElementKind element_kind = _ek_not_defined, bool with_nb_element = false, bool with_nb_nodes_per_element = false, const ElementTypeMapArray * filter = nullptr) : MeshElementTypeMapArrayInitializer( mesh, [nb_component](ElementType, GhostType) -> Int { return nb_component; }, spatial_dimension, ghost_type, element_kind, with_nb_element, with_nb_nodes_per_element, filter) {} MeshElementTypeMapArrayInitializer( const Mesh & mesh, const CompFunc & comp_func, Int spatial_dimension = _all_dimensions, GhostType ghost_type = _not_ghost, ElementKind element_kind = _ek_not_defined, bool with_nb_element = false, bool with_nb_nodes_per_element = false, const ElementTypeMapArray * filter = nullptr) : ElementTypeMapArrayInitializer(comp_func, spatial_dimension, ghost_type, element_kind), mesh(mesh), with_nb_element(with_nb_element), with_nb_nodes_per_element(with_nb_nodes_per_element), filter(filter) {} decltype(auto) elementTypes() const { if (filter != nullptr) { return filter->elementTypes(this->spatial_dimension, this->ghost_type, this->element_kind); } return mesh.elementTypes(this->spatial_dimension, this->ghost_type, this->element_kind); } virtual Idx size(ElementType type) const { if (with_nb_element) { if (filter != nullptr) { return (*filter)(type, this->ghost_type).size(); } return mesh.getNbElement(type, this->ghost_type); } return 0; } Int nbComponent(ElementType type) const override { auto res = ElementTypeMapArrayInitializer::nbComponent(type); if (with_nb_nodes_per_element) { return (res * Mesh::getNbNodesPerElement(type)); } return res; } bool isNodal() const override { return with_nb_nodes_per_element; } protected: const Mesh & mesh; bool with_nb_element{false}; bool with_nb_nodes_per_element{false}; const ElementTypeMapArray * filter{nullptr}; }; /* -------------------------------------------------------------------------- */ class FEEngineElementTypeMapArrayInitializer : public MeshElementTypeMapArrayInitializer { public: FEEngineElementTypeMapArrayInitializer( const FEEngine & fe_engine, Int nb_component = 1, Int spatial_dimension = _all_dimensions, GhostType ghost_type = _not_ghost, ElementKind element_kind = _ek_not_defined); FEEngineElementTypeMapArrayInitializer( const FEEngine & fe_engine, const ElementTypeMapArrayInitializer::CompFunc & nb_component, Int spatial_dimension = _all_dimensions, GhostType ghost_type = _not_ghost, ElementKind element_kind = _ek_not_defined); Int size(ElementType type) const override; using ElementTypesIteratorHelper = ElementTypeMapArray::ElementTypesIteratorHelper; ElementTypesIteratorHelper elementTypes() const; protected: const FEEngine & fe_engine; }; /* -------------------------------------------------------------------------- */ template template void ElementTypeMapArray::initialize(const Func & f, const T & default_value, bool do_not_default) { this->is_nodal = f.isNodal(); auto ghost_type = f.ghostType(); for (const auto & type : f.elementTypes()) { if (not this->exists(type, ghost_type)) { if (do_not_default) { auto & array = this->alloc(0, f.nbComponent(type), type, ghost_type); array.resize(f.size(type)); } else { this->alloc(f.size(type), f.nbComponent(type), type, ghost_type, default_value); } } else { auto & array = this->operator()(type, ghost_type); if (not do_not_default) { array.resize(f.size(type), default_value); } else { array.resize(f.size(type)); } } } } /* -------------------------------------------------------------------------- */ /** * All parameters are named optionals * \param _nb_component a functor giving the number of components per * (ElementType, GhostType) pair or a scalar giving a unique number of * components * regardless of type * \param _spatial_dimension a filter for the elements of a specific dimension * \param _element_kind filter with element kind (_ek_regular, _ek_structural, * ...) * \param _with_nb_element allocate the arrays with the number of elements for * each * type in the mesh * \param _with_nb_nodes_per_element multiply the number of components by the * number of nodes per element * \param _default_value default inital value * \param _do_not_default do not initialize the allocated arrays * \param _ghost_type filter a type of ghost */ template template void ElementTypeMapArray::initialize(const Mesh & mesh, pack &&... _pack) { GhostType requested_ghost_type = OPTIONAL_NAMED_ARG(ghost_type, _casper); bool all_ghost_types = OPTIONAL_NAMED_ARG(all_ghost_types, requested_ghost_type == _casper); for (GhostType ghost_type : ghost_types) { if ((not(ghost_type == requested_ghost_type)) and (not all_ghost_types)) { continue; } auto functor = MeshElementTypeMapArrayInitializer( mesh, OPTIONAL_NAMED_ARG(nb_component, 1), OPTIONAL_NAMED_ARG(spatial_dimension, mesh.getSpatialDimension()), ghost_type, OPTIONAL_NAMED_ARG(element_kind, _ek_not_defined), OPTIONAL_NAMED_ARG(with_nb_element, false), OPTIONAL_NAMED_ARG(with_nb_nodes_per_element, false), OPTIONAL_NAMED_ARG(element_filter, nullptr)); this->initialize(functor, OPTIONAL_NAMED_ARG(default_value, T()), OPTIONAL_NAMED_ARG(do_not_default, false)); } } /* -------------------------------------------------------------------------- */ /** * All parameters are named optionals * \param _nb_component a functor giving the number of components per * (ElementType, GhostType) pair or a scalar giving a unique number of * components * regardless of type * \param _spatial_dimension a filter for the elements of a specific dimension * \param _element_kind filter with element kind (_ek_regular, _ek_structural, * ...) * \param _default_value default inital value * \param _do_not_default do not initialize the allocated arrays * \param _ghost_type filter a specific ghost type * \param _all_ghost_types get all ghost types */ template template void ElementTypeMapArray::initialize(const FEEngine & fe_engine, pack &&... _pack) { GhostType requested_ghost_type = OPTIONAL_NAMED_ARG(ghost_type, _casper); bool all_ghost_types = OPTIONAL_NAMED_ARG(all_ghost_types, requested_ghost_type == _casper); for (auto ghost_type : ghost_types) { if ((not(ghost_type == requested_ghost_type)) and (not all_ghost_types)) { continue; } auto functor = FEEngineElementTypeMapArrayInitializer( fe_engine, OPTIONAL_NAMED_ARG(nb_component, 1), OPTIONAL_NAMED_ARG(spatial_dimension, Int(-2)), ghost_type, OPTIONAL_NAMED_ARG(element_kind, _ek_not_defined)); this->initialize(functor, OPTIONAL_NAMED_ARG(default_value, T()), OPTIONAL_NAMED_ARG(do_not_default, false)); } } /* -------------------------------------------------------------------------- */ template inline T & ElementTypeMapArray::operator()(const Element & element, Int component) { return this->operator()(element.type, element.ghost_type)(element.element, component); } /* -------------------------------------------------------------------------- */ template inline const T & ElementTypeMapArray::operator()(const Element & element, Int component) const { return this->operator()(element.type, element.ghost_type)(element.element, component); } /* -------------------------------------------------------------------------- */ template inline decltype(auto) ElementTypeMapArray::get(const Element & element) { auto & array = operator()(element.type, element.ghost_type); auto it = array.begin(array.getNbComponent()); return it[element.element]; } /* -------------------------------------------------------------------------- */ template inline decltype(auto) ElementTypeMapArray::get(const Element & element) const { const auto & array = operator()(element.type, element.ghost_type); auto it = array.begin(array.getNbComponent()); return it[element.element]; } /* -------------------------------------------------------------------------- */ template template >...>::value and sizeof...(Ns) >= 1> *> inline decltype(auto) ElementTypeMapArray::get(const Element & element, Ns &&... ns) { auto & array = this->operator()(element.type, element.ghost_type); auto it = make_view(array, std::forward(ns)...).begin(); return it[element.element]; } /* -------------------------------------------------------------------------- */ template template >...>::value and sizeof...(Ns) >= 1> *> inline decltype(auto) ElementTypeMapArray::get(const Element & element, Ns &&... ns) const { const auto & array = this->operator()(element.type, element.ghost_type); auto it = make_view(array, std::forward(ns)...).begin(); return it[element.element]; } /* -------------------------------------------------------------------------- */ template Int ElementTypeMapArray::sizeImpl(Int spatial_dimension, GhostType ghost_type, ElementKind kind) const { Int size = 0; for (auto && type : this->elementTypes(spatial_dimension, ghost_type, kind)) { size += this->operator()(type, ghost_type).size(); } return size; } /* -------------------------------------------------------------------------- */ template template Int ElementTypeMapArray::size(pack &&... _pack) const { Int size = 0; GhostType requested_ghost_type = OPTIONAL_NAMED_ARG(ghost_type, _casper); bool all_ghost_types = OPTIONAL_NAMED_ARG(all_ghost_types, requested_ghost_type == _casper); for (auto ghost_type : ghost_types) { if ((not(ghost_type == requested_ghost_type)) and (not all_ghost_types)) { continue; } size += sizeImpl(OPTIONAL_NAMED_ARG(spatial_dimension, _all_dimensions), ghost_type, OPTIONAL_NAMED_ARG(element_kind, _ek_not_defined)); } return size; } } // namespace akantu #endif /* AKANTU_ELEMENT_TYPE_MAP_TMPL_HH_ */ diff --git a/src/mesh/mesh.cc b/src/mesh/mesh.cc index d49543a32..afe803950 100644 --- a/src/mesh/mesh.cc +++ b/src/mesh/mesh.cc @@ -1,665 +1,667 @@ /** * @file mesh.cc * * @author Guillaume Anciaux * @author David Simon Kammer * @author Mohit Pundir * @author Nicolas Richart * @author Marco Vocialta * * @date creation: Fri Jun 18 2010 * @date last modification: Tue Feb 09 2021 * * @brief class handling meshes * * * @section LICENSE * * Copyright (©) 2010-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_config.hh" /* -------------------------------------------------------------------------- */ #include "element_class.hh" #include "group_manager_inline_impl.hh" #include "mesh.hh" #include "mesh_global_data_updater.hh" #include "mesh_io.hh" #include "mesh_iterators.hh" #include "mesh_utils.hh" /* -------------------------------------------------------------------------- */ #include "communicator.hh" #include "element_synchronizer.hh" #include "facet_synchronizer.hh" #include "mesh_utils_distribution.hh" #include "node_synchronizer.hh" #include "periodic_node_synchronizer.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ #include "dumper_field.hh" #include "dumper_internal_material_field.hh" /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ Mesh::Mesh(Int spatial_dimension, const ID & id, Communicator & communicator) : GroupManager(*this, id + ":group_manager"), MeshData("mesh_data", id), id(id), connectivities("connectivities", id), ghosts_counters("ghosts_counters", id), spatial_dimension(spatial_dimension), size(Vector::Zero(spatial_dimension)), bbox(spatial_dimension), bbox_local(spatial_dimension), communicator(&communicator) { AKANTU_DEBUG_IN(); size.fill(0.); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ Mesh::Mesh(Int spatial_dimension, Communicator & communicator, const ID & id) : Mesh(spatial_dimension, id, communicator) { AKANTU_DEBUG_IN(); this->nodes = std::make_shared>(0, spatial_dimension, id + ":coordinates"); this->nodes_flags = std::make_shared>(0, 1, NodeFlag::_normal, id + ":nodes_flags"); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ Mesh::Mesh(Int spatial_dimension, const ID & id) : Mesh(spatial_dimension, Communicator::getStaticCommunicator(), id) {} /* -------------------------------------------------------------------------- */ Mesh::Mesh(Int spatial_dimension, const std::shared_ptr> & nodes, const ID & id) : Mesh(spatial_dimension, id, Communicator::getStaticCommunicator()) { this->nodes = nodes; this->nb_global_nodes = this->nodes->size(); this->nodes_to_elements.resize(nodes->size()); for (auto & node_set : nodes_to_elements) { node_set = std::make_unique>(); } this->computeBoundingBox(); } /* -------------------------------------------------------------------------- */ void Mesh::getBarycenters(Array & barycenter, ElementType type, GhostType ghost_type) const { barycenter.resize(getNbElement(type, ghost_type)); for (auto && data : enumerate(make_view(barycenter, spatial_dimension))) { getBarycenter(Element{type, Idx(std::get<0>(data)), ghost_type}, std::get<1>(data)); } } class FacetGlobalConnectivityAccessor : public DataAccessor { public: FacetGlobalConnectivityAccessor(Mesh & mesh) : global_connectivity("global_connectivity", "facet_connectivity_synchronizer") { global_connectivity.initialize( mesh, _spatial_dimension = _all_dimensions, _with_nb_element = true, _with_nb_nodes_per_element = true, _element_kind = _ek_regular); mesh.getGlobalConnectivity(global_connectivity); } Int getNbData(const Array & elements, const SynchronizationTag & tag) const override { Int size = 0; if (tag == SynchronizationTag::_smmc_facets_conn) { Int nb_nodes = Mesh::getNbNodesPerElementList(elements); size += nb_nodes * sizeof(Idx); } return size; } void packData(CommunicationBuffer & buffer, const Array & elements, const SynchronizationTag & tag) const override { if (tag == SynchronizationTag::_smmc_facets_conn) { for (const auto & element : elements) { const auto & conns = global_connectivity(element.type, element.ghost_type); for (auto n : arange(conns.getNbComponent())) { buffer << conns(element.element, n); } } } } void unpackData(CommunicationBuffer & buffer, const Array & elements, const SynchronizationTag & tag) override { if (tag == SynchronizationTag::_smmc_facets_conn) { for (const auto & element : elements) { auto & conns = global_connectivity(element.type, element.ghost_type); for (auto n : arange(conns.getNbComponent())) { buffer >> conns(element.element, n); } } } } AKANTU_GET_MACRO(GlobalConnectivity, (global_connectivity), decltype(auto)); protected: ElementTypeMapArray global_connectivity; }; /* -------------------------------------------------------------------------- */ // const Array & Mesh::getNormals(ElementType element_type, // GhostType ghost_type) { // if (this->hasData("normals", element_type, ghost_type)) { // return this->getData("normals", element_type, ghost_type); // } // auto & normals = getDataPointer("normals", element_type, ghost_type, // spatial_dimension, true); // for (auto && data [[gnu::unused]] : // enumerate(make_view(normals, spatial_dimension))) { // AKANTU_TO_IMPLEMENT(); // } // AKANTU_TO_IMPLEMENT(); // } /* -------------------------------------------------------------------------- */ Mesh & Mesh::initMeshFacets(const ID & id) { AKANTU_DEBUG_IN(); if (mesh_facets) { AKANTU_DEBUG_OUT(); return *mesh_facets; } mesh_facets = std::make_unique(spatial_dimension, this->nodes, getID() + ":" + id); mesh_facets->mesh_parent = this; mesh_facets->is_mesh_facets = true; mesh_facets->nodes_flags = this->nodes_flags; mesh_facets->nodes_global_ids = this->nodes_global_ids; MeshUtils::buildAllFacets(*this, *mesh_facets, 0); if (mesh.isDistributed()) { mesh_facets->is_distributed = true; mesh_facets->element_synchronizer = std::make_unique( *mesh_facets, mesh.getElementSynchronizer()); FacetGlobalConnectivityAccessor data_accessor(*mesh_facets); /// communicate mesh_facets->element_synchronizer->synchronizeOnce( data_accessor, SynchronizationTag::_smmc_facets_conn); /// flip facets MeshUtils::flipFacets(*mesh_facets, data_accessor.getGlobalConnectivity(), _ghost); } /// transfers the the mesh physical names to the mesh facets if (not this->hasData("physical_names")) { AKANTU_DEBUG_OUT(); return *mesh_facets; } auto & mesh_phys_data = this->getData("physical_names"); auto & phys_data = mesh_facets->getData("physical_names"); phys_data.initialize(*mesh_facets, _spatial_dimension = spatial_dimension - 1, _with_nb_element = true); ElementTypeMapArray barycenters(getID(), "temporary_barycenters"); barycenters.initialize(*mesh_facets, _nb_component = spatial_dimension, _spatial_dimension = spatial_dimension - 1, _with_nb_element = true); for (auto && ghost_type : ghost_types) { for (auto && type : barycenters.elementTypes(spatial_dimension - 1, ghost_type)) { mesh_facets->getBarycenters(barycenters(type, ghost_type), type, ghost_type); } } for_each_element( mesh, [&](auto && element) { Vector barycenter(spatial_dimension); mesh.getBarycenter(element, barycenter); auto norm_barycenter = barycenter.norm(); auto tolerance = Math::getTolerance(); if (norm_barycenter > tolerance) { tolerance *= norm_barycenter; } Vector barycenter_facet(spatial_dimension); auto range = enumerate(make_view( barycenters(element.type, element.ghost_type), spatial_dimension)); #ifndef AKANTU_NDEBUG auto min_dist = std::numeric_limits::max(); #endif // this is a spacial search coded the most inefficient way. auto facet = std::find_if(range.begin(), range.end(), [&](auto && data) { auto norm_distance = barycenter.distance(std::get<1>(data)); #ifndef AKANTU_NDEBUG min_dist = std::min(min_dist, norm_distance); #endif return (norm_distance < tolerance); }); if (facet == range.end()) { AKANTU_DEBUG_INFO("The element " << element << " did not find its associated facet in the " "mesh_facets! Try to decrease math tolerance. " "The closest element was at a distance of " << min_dist); return; } // set physical name auto && facet_element = Element{element.type, std::get<0>(*facet), element.ghost_type}; phys_data(facet_element) = mesh_phys_data(element); }, _spatial_dimension = spatial_dimension - 1); mesh_facets->createGroupsFromMeshData("physical_names"); AKANTU_DEBUG_OUT(); return *mesh_facets; } /* -------------------------------------------------------------------------- */ void Mesh::defineMeshParent(const Mesh & mesh) { AKANTU_DEBUG_IN(); this->mesh_parent = &mesh; this->is_mesh_facets = true; AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ Mesh::~Mesh() = default; /* -------------------------------------------------------------------------- */ void Mesh::read(const std::string & filename, const MeshIOType & mesh_io_type) { AKANTU_DEBUG_ASSERT(not is_distributed, "You cannot read a mesh that is already distributed"); MeshIO::read(filename, *this, mesh_io_type); auto types = this->elementTypes(spatial_dimension, _not_ghost, _ek_not_defined); auto it = types.begin(); auto last = types.end(); if (it == last) { AKANTU_DEBUG_WARNING( "The mesh contained in the file " << filename << " does not seem to be of the good dimension." << " No element of dimension " << spatial_dimension << " were read."); } this->makeReady(); } /* -------------------------------------------------------------------------- */ void Mesh::write(const std::string & filename, const MeshIOType & mesh_io_type) { MeshIO::write(filename, *this, mesh_io_type); } /* -------------------------------------------------------------------------- */ void Mesh::makeReady() { this->nb_global_nodes = this->nodes->size(); this->computeBoundingBox(); this->nodes_flags->resize(nodes->size(), NodeFlag::_normal); this->nodes_to_elements.resize(nodes->size()); for (auto & node_set : nodes_to_elements) { node_set = std::make_unique>(); } } /* -------------------------------------------------------------------------- */ void Mesh::printself(std::ostream & stream, int indent) const { std::string space(indent, AKANTU_INDENT); stream << space << "Mesh [" << std::endl; stream << space << " + id : " << getID() << std::endl; stream << space << " + spatial dimension : " << this->spatial_dimension << std::endl; stream << space << " + nodes [" << std::endl; nodes->printself(stream, indent + 2); stream << space << " + connectivities [" << std::endl; connectivities.printself(stream, indent + 2); stream << space << " ]" << std::endl; GroupManager::printself(stream, indent + 1); stream << space << "]" << std::endl; } /* -------------------------------------------------------------------------- */ void Mesh::computeBoundingBox() { AKANTU_DEBUG_IN(); bbox_local.reset(); for (auto & pos : make_view(*nodes, spatial_dimension)) { // if(!isPureGhostNode(i)) bbox_local += pos; } if (this->is_distributed) { bbox = bbox_local.allSum(*communicator); } else { bbox = bbox_local; } size = bbox.size(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void Mesh::getGlobalConnectivity( ElementTypeMapArray & global_connectivity) { AKANTU_DEBUG_IN(); for (auto && ghost_type : ghost_types) { for (auto type : global_connectivity.elementTypes(_spatial_dimension = _all_dimensions, _element_kind = _ek_not_defined, _ghost_type = ghost_type)) { if (not connectivities.exists(type, ghost_type)) { continue; } auto local_conn_view = make_view(connectivities(type, ghost_type)); auto global_conn_view = make_view(global_connectivity(type, ghost_type)); std::transform(local_conn_view.begin(), local_conn_view.end(), global_conn_view.begin(), [&](Idx l) -> Idx { return this->getNodeGlobalId(l); }); } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ DumperIOHelper & Mesh::getGroupDumper(const std::string & dumper_name, const std::string & group_name) { if (group_name == "all") { return this->getDumper(dumper_name); } return element_groups[group_name]->getDumper(dumper_name); } /* -------------------------------------------------------------------------- */ template ElementTypeMap Mesh::getNbDataPerElem(ElementTypeMapArray & arrays) { ElementTypeMap nb_data_per_elem; for (auto type : arrays.elementTypes(_element_kind = _ek_not_defined)) { auto nb_elements = this->getNbElement(type); auto & array = arrays(type); nb_data_per_elem(type) = array.getNbComponent() * array.size(); nb_data_per_elem(type) /= nb_elements; } return nb_data_per_elem; } /* -------------------------------------------------------------------------- */ template ElementTypeMap Mesh::getNbDataPerElem(ElementTypeMapArray & array); template ElementTypeMap Mesh::getNbDataPerElem(ElementTypeMapArray & array); /* -------------------------------------------------------------------------- */ template std::shared_ptr Mesh::createFieldFromAttachedData(const std::string & field_id, const std::string & group_name, ElementKind element_kind) { std::shared_ptr field; ElementTypeMapArray * internal = nullptr; try { internal = &(this->getData(field_id)); } catch (...) { return nullptr; } auto && nb_data_per_elem = this->getNbDataPerElem(*internal); field = this->createElementalField( *internal, group_name, this->spatial_dimension, element_kind, nb_data_per_elem); return field; } template std::shared_ptr Mesh::createFieldFromAttachedData(const std::string & field_id, const std::string & group_name, ElementKind element_kind); template std::shared_ptr Mesh::createFieldFromAttachedData(const std::string & field_id, const std::string & group_name, ElementKind element_kind); /* -------------------------------------------------------------------------- */ void Mesh::distributeImpl( Communicator & communicator, const std::function & edge_weight_function [[gnu::unused]], const std::function & vertex_weight_function [[gnu::unused]]) { AKANTU_DEBUG_ASSERT(is_distributed == false, "This mesh is already distribute"); this->communicator = &communicator; this->element_synchronizer = std::make_unique( *this, this->getID() + ":element_synchronizer", true); this->node_synchronizer = std::make_unique( *this, this->getID() + ":node_synchronizer", true); auto psize = this->communicator->getNbProc(); if (psize > 1) { #ifdef AKANTU_USE_SCOTCH auto prank = this->communicator->whoAmI(); if (prank == 0) { MeshPartitionScotch partition(*this, spatial_dimension); partition.partitionate(psize, edge_weight_function, vertex_weight_function); MeshUtilsDistribution::distributeMeshCentralized(*this, 0, partition); } else { MeshUtilsDistribution::distributeMeshCentralized(*this, 0); } + #else - if (psize > 1) { - AKANTU_ERROR("Cannot distribute a mesh without a partitioning tool"); - } + AKANTU_ERROR("Cannot distribute a mesh without a partitioning tool"); #endif } // if (psize > 1) this->is_distributed = true; this->computeBoundingBox(); + + MeshIsDistributedEvent event(AKANTU_CURRENT_FUNCTION); + this->sendEvent(event); } /* -------------------------------------------------------------------------- */ void Mesh::getAssociatedElements(const Array & node_list, Array & elements) { for (const auto & node : node_list) { for (const auto & element : *nodes_to_elements[node]) { elements.push_back(element); } } } /* -------------------------------------------------------------------------- */ void Mesh::getAssociatedElements(const Idx & node, Array & elements) const { for (const auto & element : *nodes_to_elements[node]) { elements.push_back(element); } } /* -------------------------------------------------------------------------- */ void Mesh::fillNodesToElements(Int dimension) { Element e; auto nb_nodes = nodes->size(); this->nodes_to_elements.resize(nb_nodes); for (Int n = 0; n < nb_nodes; ++n) { if (this->nodes_to_elements[n]) { this->nodes_to_elements[n]->clear(); } else { this->nodes_to_elements[n] = std::make_unique>(); } } for (auto ghost_type : ghost_types) { e.ghost_type = ghost_type; for (const auto & type : elementTypes(dimension, ghost_type, _ek_not_defined)) { e.type = type; auto nb_element = this->getNbElement(type, ghost_type); auto connectivity = connectivities(type, ghost_type); auto conn_it = connectivity.begin(connectivity.getNbComponent()); for (Int el = 0; el < nb_element; ++el, ++conn_it) { e.element = el; const auto & conn = *conn_it; for (auto node : conn) { nodes_to_elements[node]->insert(e); } } } } } /* -------------------------------------------------------------------------- */ std::tuple Mesh::updateGlobalData(NewNodesEvent & nodes_event, NewElementsEvent & elements_event) { if (global_data_updater) { return this->global_data_updater->updateData(nodes_event, elements_event); } return std::make_tuple(nodes_event.getList().size(), elements_event.getList().size()); } /* -------------------------------------------------------------------------- */ void Mesh::registerGlobalDataUpdater( std::unique_ptr && global_data_updater) { this->global_data_updater = std::move(global_data_updater); } /* -------------------------------------------------------------------------- */ void Mesh::eraseElements(const Array & elements) { ElementTypeMap last_element; RemovedElementsEvent event(*this, "new_numbering", AKANTU_CURRENT_FUNCTION); auto & remove_list = event.getList(); auto & new_numbering = event.getNewNumbering(); for (auto && el : elements) { if (el.ghost_type != _not_ghost) { auto & count = ghosts_counters(el); --count; AKANTU_DEBUG_ASSERT(count >= 0, "Something went wrong in the ghost element counter"); if (count > 0) { continue; } } remove_list.push_back(el); if (not new_numbering.exists(el.type, el.ghost_type)) { auto nb_element = mesh.getNbElement(el.type, el.ghost_type); auto & numbering = new_numbering.alloc(nb_element, 1, el.type, el.ghost_type); for (auto && pair : enumerate(numbering)) { std::get<1>(pair) = std::get<0>(pair); } } new_numbering(el) = -1; } auto find_last_not_deleted = [](auto && array, Int start) -> Int { do { --start; } while (start >= 0 and array[start] == -1); return start; }; auto find_first_deleted = [](auto && array, Int start) -> Int { auto begin = array.begin(); auto it = std::find_if(begin + start, array.end(), [](auto & el) { return el == -1; }); return Int(it - begin); }; for (auto ghost_type : ghost_types) { for (auto type : new_numbering.elementTypes(_ghost_type = ghost_type)) { auto & numbering = new_numbering(type, ghost_type); auto last_not_delete = find_last_not_deleted(numbering, numbering.size()); if (last_not_delete < 0) { continue; } auto pos = find_first_deleted(numbering, 0); while (pos < last_not_delete) { std::swap(numbering[pos], numbering[last_not_delete]); last_not_delete = find_last_not_deleted(numbering, last_not_delete); pos = find_first_deleted(numbering, pos + 1); } } } this->ghosts_counters.onElementsRemoved(new_numbering); this->sendEvent(event); } } // namespace akantu diff --git a/src/mesh/mesh_events.hh b/src/mesh/mesh_events.hh index 03fdf9f3f..cf4dd1708 100644 --- a/src/mesh/mesh_events.hh +++ b/src/mesh/mesh_events.hh @@ -1,206 +1,220 @@ /** * @file mesh_events.hh * * @author Nicolas Richart * * @date creation: Fri Feb 20 2015 * @date last modification: Thu Feb 20 2020 * * @brief Classes corresponding to mesh events type * * * @section LICENSE * * Copyright (©) 2015-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include #include "aka_array.hh" #include "element.hh" #include "element_type_map.hh" /* -------------------------------------------------------------------------- */ #ifndef AKANTU_MESH_EVENTS_HH_ #define AKANTU_MESH_EVENTS_HH_ namespace akantu { /// akantu::MeshEvent is the base event for meshes template class MeshEvent { public: MeshEvent(const std::string & origin = "") : origin_(origin) {} virtual ~MeshEvent() = default; /// Get the list of entity modified by the event nodes or elements const Array & getList() const { return list; } /// Get the list of entity modified by the event nodes or elements Array & getList() { return list; } std::string origin() const { return origin_; } protected: Array list; private: std::string origin_; }; class Mesh; /// akantu::MeshEvent related to new nodes in the mesh class NewNodesEvent : public MeshEvent { public: NewNodesEvent(const std::string & origin = "") : MeshEvent(origin) {} ~NewNodesEvent() override = default; }; /// akantu::MeshEvent related to nodes removed from the mesh class RemovedNodesEvent : public MeshEvent { public: inline RemovedNodesEvent(const Mesh & mesh, const std::string & origin = ""); ~RemovedNodesEvent() override = default; /// Get the new numbering following suppression of nodes from nodes arrays AKANTU_GET_MACRO_NOT_CONST(NewNumbering, new_numbering, auto &); /// Get the new numbering following suppression of nodes from nodes arrays AKANTU_GET_MACRO(NewNumbering, new_numbering, const auto &); private: Array new_numbering; }; /// akantu::MeshEvent related to new elements in the mesh class NewElementsEvent : public MeshEvent { public: NewElementsEvent(const std::string & origin = "") : MeshEvent(origin) {} ~NewElementsEvent() override = default; }; +/// akantu::MeshEvent related to the case the mesh is made distributed. +/// Note that the `list` has no meaning for this event. +class MeshIsDistributedEvent : public MeshEvent { +public: + MeshIsDistributedEvent(const std::string & origin = "") + : MeshEvent(origin) {} + ~MeshIsDistributedEvent() override = default; +}; + /// akantu::MeshEvent related to elements removed from the mesh class RemovedElementsEvent : public MeshEvent { public: inline RemovedElementsEvent(const Mesh & mesh, const ID & new_numbering_id = "new_numbering", const std::string & origin = ""); ~RemovedElementsEvent() override = default; /// Get the new numbering following suppression of elements from elements /// arrays - AKANTU_GET_MACRO(NewNumbering, new_numbering, - const auto &); + AKANTU_GET_MACRO(NewNumbering, new_numbering, const auto &); /// Get the new numbering following suppression of elements from elements /// arrays - AKANTU_GET_MACRO_NOT_CONST(NewNumbering, new_numbering, - auto &); + AKANTU_GET_MACRO_NOT_CONST(NewNumbering, new_numbering, auto &); /// Get the new numbering following suppression of elements from elements /// arrays AKANTU_GET_MACRO_BY_ELEMENT_TYPE(NewNumbering, new_numbering, Idx); /// Get the new numbering following suppression of elements from elements /// arrays AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(NewNumbering, new_numbering, Idx); protected: ElementTypeMapArray new_numbering; }; /// akantu::MeshEvent for element that changed in some sort, can be seen as a /// combination of removed and added elements class ChangedElementsEvent : public RemovedElementsEvent { public: inline ChangedElementsEvent( const Mesh & mesh, const ID & new_numbering_id = "changed_event:new_numbering", const std::string & origin = "") : RemovedElementsEvent(mesh, new_numbering_id, origin) {} ~ChangedElementsEvent() override = default; AKANTU_GET_MACRO(ListOld, list, const Array &); AKANTU_GET_MACRO_NOT_CONST(ListOld, list, Array &); AKANTU_GET_MACRO(ListNew, new_list, const Array &); AKANTU_GET_MACRO_NOT_CONST(ListNew, new_list, Array &); protected: Array new_list; }; /* -------------------------------------------------------------------------- */ class MeshEventHandler { public: virtual ~MeshEventHandler() = default; /* ------------------------------------------------------------------------ */ /* Internal code */ /* ------------------------------------------------------------------------ */ private: /// send a akantu::NewNodesEvent inline void sendEvent(const NewNodesEvent & event) { onNodesAdded(event.getList(), event); } /// send a akantu::RemovedNodesEvent inline void sendEvent(const RemovedNodesEvent & event) { onNodesRemoved(event.getList(), event.getNewNumbering(), event); } /// send a akantu::NewElementsEvent inline void sendEvent(const NewElementsEvent & event) { onElementsAdded(event.getList(), event); } /// send a akantu::RemovedElementsEvent inline void sendEvent(const RemovedElementsEvent & event) { onElementsRemoved(event.getList(), event.getNewNumbering(), event); } /// send a akantu::ChangedElementsEvent inline void sendEvent(const ChangedElementsEvent & event) { onElementsChanged(event.getListOld(), event.getListNew(), event.getNewNumbering(), event); } + /// send a akantu::MeshIsDistributedEvent + inline void sendEvent(const MeshIsDistributedEvent & event) { + onMeshIsDistributed(event); + } template friend class EventHandlerManager; /* ------------------------------------------------------------------------ */ /* Interface */ /* ------------------------------------------------------------------------ */ public: /// function to implement to react on akantu::NewNodesEvent virtual void onNodesAdded(const Array & /*nodes_list*/, const NewNodesEvent & /*event*/) {} /// function to implement to react on akantu::RemovedNodesEvent virtual void onNodesRemoved(const Array & /*nodes_list*/, const Array & /*new_numbering*/, const RemovedNodesEvent & /*event*/) {} /// function to implement to react on akantu::NewElementsEvent virtual void onElementsAdded(const Array & /*elements_list*/, const NewElementsEvent & /*event*/) {} /// function to implement to react on akantu::RemovedElementsEvent virtual void onElementsRemoved(const Array & /*elements_list*/, const ElementTypeMapArray & /*new_numbering*/, const RemovedElementsEvent & /*event*/) {} /// function to implement to react on akantu::ChangedElementsEvent virtual void onElementsChanged(const Array & /*old_elements_list*/, const Array & /*new_elements_list*/, const ElementTypeMapArray & /*new_numbering*/, const ChangedElementsEvent & /*event*/) {} + + /// function to implement to react on akantu::MeshIsDistributedEvent + virtual void onMeshIsDistributed(const MeshIsDistributedEvent & /*event*/) {} }; } // namespace akantu #endif /* AKANTU_MESH_EVENTS_HH_ */ diff --git a/src/model/common/dof_manager/dof_manager.cc b/src/model/common/dof_manager/dof_manager.cc index cf54f2583..87092acc8 100644 --- a/src/model/common/dof_manager/dof_manager.cc +++ b/src/model/common/dof_manager/dof_manager.cc @@ -1,1010 +1,1032 @@ /** * @file dof_manager.cc * * @author Nicolas Richart * * @date creation: Tue Aug 18 2015 * @date last modification: Sat Mar 06 2021 * * @brief Implementation of the common parts of the DOFManagers * * * @section LICENSE * * Copyright (©) 2015-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "dof_manager.hh" #include "communicator.hh" #include "mesh.hh" #include "mesh_utils.hh" #include "node_group.hh" #include "node_synchronizer.hh" #include "non_linear_solver.hh" #include "periodic_node_synchronizer.hh" #include "time_step_solver.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ DOFManager::DOFManager(const ID & id) : id(id), dofs_flag(0, 1, std::string(id + ":dofs_type")), global_equation_number(0, 1, "global_equation_number"), communicator(Communicator::getStaticCommunicator()) {} /* -------------------------------------------------------------------------- */ DOFManager::DOFManager(Mesh & mesh, const ID & id) : id(id), mesh(&mesh), dofs_flag(0, 1, std::string(id + ":dofs_type")), global_equation_number(0, 1, "global_equation_number"), communicator(mesh.getCommunicator()) { this->mesh->registerEventHandler(*this, _ehp_dof_manager); } /* -------------------------------------------------------------------------- */ DOFManager::~DOFManager() = default; /* -------------------------------------------------------------------------- */ std::vector DOFManager::getDOFIDs() const { std::vector keys; for (const auto & dof_data : this->dofs) { keys.push_back(dof_data.first); } return keys; } /* -------------------------------------------------------------------------- */ void DOFManager::assembleElementalArrayLocalArray( const Array & elementary_vect, Array & array_assembeled, ElementType type, GhostType ghost_type, Real scale_factor, const Array & filter_elements) { AKANTU_DEBUG_IN(); Int nb_element; auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type); auto nb_degree_of_freedom = elementary_vect.getNbComponent() / nb_nodes_per_element; Idx * filter_it = nullptr; if (filter_elements != empty_filter) { nb_element = filter_elements.size(); filter_it = filter_elements.data(); } else { nb_element = this->mesh->getNbElement(type, ghost_type); } AKANTU_DEBUG_ASSERT(elementary_vect.size() == nb_element, "The vector elementary_vect(" << elementary_vect.getID() << ") has not the good size."); const auto & connectivity = this->mesh->getConnectivity(type, ghost_type); auto elem_it = make_view(elementary_vect, nb_degree_of_freedom, nb_nodes_per_element) .begin(); auto assemble_it = make_view(array_assembeled, nb_degree_of_freedom).begin(); for (Int el = 0; el < nb_element; ++el, ++elem_it) { auto element = el; if (filter_it != nullptr) { element = *filter_it; } // const Vector & conn = *conn_it; const auto & elemental_val = *elem_it; for (Int n = 0; n < nb_nodes_per_element; ++n) { auto node = connectivity(element, n); auto && assemble = assemble_it[node]; assemble += scale_factor * elemental_val(n); } if (filter_it != nullptr) { ++filter_it; } // else // ++conn_it; } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void DOFManager::assembleElementalArrayToResidual( const ID & dof_id, const Array & elementary_vect, ElementType type, GhostType ghost_type, Real scale_factor, const Array & filter_elements) { AKANTU_DEBUG_IN(); auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type); auto nb_degree_of_freedom = elementary_vect.getNbComponent() / nb_nodes_per_element; Array array_localy_assembeled(this->mesh->getNbNodes(), nb_degree_of_freedom); array_localy_assembeled.zero(); this->assembleElementalArrayLocalArray( elementary_vect, array_localy_assembeled, type, ghost_type, scale_factor, filter_elements); this->assembleToResidual(dof_id, array_localy_assembeled, 1); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void DOFManager::assembleElementalArrayToLumpedMatrix( const ID & dof_id, const Array & elementary_vect, const ID & lumped_mtx, ElementType type, GhostType ghost_type, Real scale_factor, const Array & filter_elements) { AKANTU_DEBUG_IN(); auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type); auto nb_degree_of_freedom = elementary_vect.getNbComponent() / nb_nodes_per_element; Array array_localy_assembeled(this->mesh->getNbNodes(), nb_degree_of_freedom); array_localy_assembeled.zero(); this->assembleElementalArrayLocalArray( elementary_vect, array_localy_assembeled, type, ghost_type, scale_factor, filter_elements); this->assembleToLumpedMatrix(dof_id, array_localy_assembeled, lumped_mtx, 1); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void DOFManager::assembleMatMulDOFsToResidual(const ID & A_id, Real scale_factor) { for (auto & pair : this->dofs) { const auto & dof_id = pair.first; auto & dof_data = *pair.second; this->assembleMatMulVectToResidual(dof_id, A_id, *dof_data.dof, scale_factor); } } /* -------------------------------------------------------------------------- */ void DOFManager::splitSolutionPerDOFs() { for (auto && data : this->dofs) { auto & dof_data = *data.second; dof_data.solution.resize(dof_data.dof->size() * dof_data.dof->getNbComponent()); this->getSolutionPerDOFs(data.first, dof_data.solution); } } /* -------------------------------------------------------------------------- */ void DOFManager::getSolutionPerDOFs(const ID & dof_id, Array & solution_array) { AKANTU_DEBUG_IN(); this->getArrayPerDOFs(dof_id, this->getSolution(), solution_array); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void DOFManager::getLumpedMatrixPerDOFs(const ID & dof_id, const ID & lumped_mtx, Array & lumped) { AKANTU_DEBUG_IN(); this->getArrayPerDOFs(dof_id, this->getLumpedMatrix(lumped_mtx), lumped); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void DOFManager::assembleToResidual(const ID & dof_id, Array & array_to_assemble, Real scale_factor) { AKANTU_DEBUG_IN(); // this->makeConsistentForPeriodicity(dof_id, array_to_assemble); this->assembleToGlobalArray(dof_id, array_to_assemble, this->getResidual(), scale_factor); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void DOFManager::assembleToLumpedMatrix(const ID & dof_id, Array & array_to_assemble, const ID & lumped_mtx, Real scale_factor) { AKANTU_DEBUG_IN(); // this->makeConsistentForPeriodicity(dof_id, array_to_assemble); auto & lumped = this->getLumpedMatrix(lumped_mtx); this->assembleToGlobalArray(dof_id, array_to_assemble, lumped, scale_factor); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ DOFManager::DOFData::DOFData(const ID & dof_id) : support_type(_dst_generic), group_support("__mesh__"), solution(0, 1, dof_id + ":solution"), local_equation_number(0, 1, dof_id + ":local_equation_number"), associated_nodes(0, 1, dof_id + "associated_nodes") {} /* -------------------------------------------------------------------------- */ DOFManager::DOFData::~DOFData() = default; /* -------------------------------------------------------------------------- */ template auto DOFManager::countDOFsForNodes(const DOFData & dof_data, Int nb_nodes, Func && getNode) { auto nb_local_dofs = nb_nodes; decltype(nb_local_dofs) nb_pure_local = 0; for (auto n : arange(nb_nodes)) { UInt node = getNode(n); // http://www.open-std.org/jtc1/sc22/open/n2356/conv.html // bool are by convention casted to 0 and 1 when promoted to int nb_pure_local += this->mesh->isLocalOrMasterNode(node); nb_local_dofs -= this->mesh->isPeriodicSlave(node); } const auto & dofs_array = *dof_data.dof; nb_pure_local *= dofs_array.getNbComponent(); nb_local_dofs *= dofs_array.getNbComponent(); return std::make_pair(nb_local_dofs, nb_pure_local); } /* -------------------------------------------------------------------------- */ auto DOFManager::getNewDOFDataInternal(const ID & dof_id) -> DOFData & { auto it = this->dofs.find(dof_id); if (it != this->dofs.end()) { AKANTU_EXCEPTION("This dof array has already been registered"); } std::unique_ptr dof_data_ptr = this->getNewDOFData(dof_id); DOFData & dof_data = *dof_data_ptr; this->dofs[dof_id] = std::move(dof_data_ptr); return dof_data; } /* -------------------------------------------------------------------------- */ void DOFManager::registerDOFs(const ID & dof_id, Array & dofs_array, DOFSupportType support_type) { auto & dofs_storage = this->getNewDOFDataInternal(dof_id); dofs_storage.support_type = support_type; this->registerDOFsInternal(dof_id, dofs_array); resizeGlobalArrays(); } /* -------------------------------------------------------------------------- */ void DOFManager::registerDOFs(const ID & dof_id, Array & dofs_array, const ID & support_group) { auto & dofs_storage = this->getNewDOFDataInternal(dof_id); dofs_storage.support_type = _dst_nodal; dofs_storage.group_support = support_group; this->registerDOFsInternal(dof_id, dofs_array); resizeGlobalArrays(); } /* -------------------------------------------------------------------------- */ std::tuple DOFManager::registerDOFsInternal(const ID & dof_id, Array & dofs_array) { DOFData & dof_data = this->getDOFData(dof_id); dof_data.dof = &dofs_array; Int nb_local_dofs = 0; Int nb_pure_local = 0; const auto & support_type = dof_data.support_type; switch (support_type) { case _dst_nodal: { const auto & group = dof_data.group_support; std::function getNode; if (group == "__mesh__") { AKANTU_DEBUG_ASSERT( dofs_array.size() == this->mesh->getNbNodes(), "The array of dof is too short to be associated to nodes."); std::tie(nb_local_dofs, nb_pure_local) = countDOFsForNodes( dof_data, this->mesh->getNbNodes(), [](auto && n) { return n; }); } else { const auto & node_group = this->mesh->getElementGroup(group).getNodeGroup().getNodes(); AKANTU_DEBUG_ASSERT( dofs_array.size() == node_group.size(), "The array of dof is too shot to be associated to nodes."); std::tie(nb_local_dofs, nb_pure_local) = countDOFsForNodes(dof_data, node_group.size(), [&node_group](auto && n) { return node_group(n); }); } break; } case _dst_generic: { nb_local_dofs = nb_pure_local = dofs_array.size() * dofs_array.getNbComponent(); break; } default: { AKANTU_EXCEPTION("This type of dofs is not handled yet."); } } dof_data.local_nb_dofs = nb_local_dofs; dof_data.pure_local_nb_dofs = nb_pure_local; dof_data.ghosts_nb_dofs = nb_local_dofs - nb_pure_local; this->pure_local_system_size += nb_pure_local; this->local_system_size += nb_local_dofs; auto nb_total_pure_local = nb_pure_local; communicator.allReduce(nb_total_pure_local, SynchronizerOperation::_sum); this->system_size += nb_total_pure_local; // updating the dofs data after counting is finished switch (support_type) { case _dst_nodal: { const auto & group = dof_data.group_support; if (group != "__mesh__") { auto & support_nodes = this->mesh->getElementGroup(group).getNodeGroup().getNodes(); this->updateDOFsData( dof_data, nb_local_dofs, nb_pure_local, support_nodes.size(), [&support_nodes](Idx node) -> Idx { return support_nodes[node]; }); } else { this->updateDOFsData(dof_data, nb_local_dofs, nb_pure_local, mesh->getNbNodes(), [](Idx node) -> Idx { return node; }); } break; } case _dst_generic: { this->updateDOFsData(dof_data, nb_local_dofs, nb_pure_local); break; } } return std::make_tuple(nb_local_dofs, nb_pure_local, nb_total_pure_local); } /* -------------------------------------------------------------------------- */ void DOFManager::registerDOFsPrevious(const ID & dof_id, Array & array) { DOFData & dof = this->getDOFData(dof_id); if (dof.previous != nullptr) { AKANTU_EXCEPTION("The previous dofs array for " << dof_id << " has already been registered"); } dof.previous = &array; } /* -------------------------------------------------------------------------- */ void DOFManager::registerDOFsIncrement(const ID & dof_id, Array & array) { DOFData & dof = this->getDOFData(dof_id); if (dof.increment != nullptr) { AKANTU_EXCEPTION("The dofs increment array for " << dof_id << " has already been registered"); } dof.increment = &array; } /* -------------------------------------------------------------------------- */ void DOFManager::registerDOFsDerivative(const ID & dof_id, Int order, Array & dofs_derivative) { auto & dof = this->getDOFData(dof_id); auto & derivatives = dof.dof_derivatives; if (Int(derivatives.size()) < order) { derivatives.resize(order, nullptr); } else { if (derivatives[order - 1] != nullptr) { AKANTU_EXCEPTION("The dof derivatives of order " << order << " already been registered for this dof (" << dof_id << ")"); } } derivatives[order - 1] = &dofs_derivative; } /* -------------------------------------------------------------------------- */ void DOFManager::registerBlockedDOFs(const ID & dof_id, Array & blocked_dofs) { auto & dof = this->getDOFData(dof_id); if (dof.blocked_dofs != nullptr) { AKANTU_EXCEPTION("The blocked dofs array for " << dof_id << " has already been registered"); } dof.blocked_dofs = &blocked_dofs; } /* -------------------------------------------------------------------------- */ SparseMatrix & DOFManager::registerSparseMatrix(const ID & matrix_id, std::unique_ptr & matrix) { auto it = this->matrices.find(matrix_id); if (it != this->matrices.end()) { AKANTU_EXCEPTION("The matrix " << matrix_id << " already exists in " << this->id); } auto & ret = *matrix; this->matrices[matrix_id] = std::move(matrix); return ret; } /* -------------------------------------------------------------------------- */ /// Get an instance of a new SparseMatrix SolverVector & DOFManager::registerLumpedMatrix(const ID & matrix_id, std::unique_ptr & matrix) { auto it = this->lumped_matrices.find(matrix_id); if (it != this->lumped_matrices.end()) { AKANTU_EXCEPTION("The lumped matrix " << matrix_id << " already exists in " << this->id); } auto & ret = *matrix; this->lumped_matrices[matrix_id] = std::move(matrix); ret.resize(); return ret; } /* -------------------------------------------------------------------------- */ NonLinearSolver & DOFManager::registerNonLinearSolver( const ID & non_linear_solver_id, std::unique_ptr & non_linear_solver) { NonLinearSolversMap::const_iterator it = this->non_linear_solvers.find(non_linear_solver_id); if (it != this->non_linear_solvers.end()) { AKANTU_EXCEPTION("The non linear solver " << non_linear_solver_id << " already exists in " << this->id); } NonLinearSolver & ret = *non_linear_solver; this->non_linear_solvers[non_linear_solver_id] = std::move(non_linear_solver); return ret; } /* -------------------------------------------------------------------------- */ TimeStepSolver & DOFManager::registerTimeStepSolver( const ID & time_step_solver_id, std::unique_ptr & time_step_solver) { TimeStepSolversMap::const_iterator it = this->time_step_solvers.find(time_step_solver_id); if (it != this->time_step_solvers.end()) { AKANTU_EXCEPTION("The non linear solver " << time_step_solver_id << " already exists in " << this->id); } TimeStepSolver & ret = *time_step_solver; this->time_step_solvers[time_step_solver_id] = std::move(time_step_solver); return ret; } /* -------------------------------------------------------------------------- */ SparseMatrix & DOFManager::getMatrix(const ID & id) { ID matrix_id = this->id + ":mtx:" + id; SparseMatricesMap::const_iterator it = this->matrices.find(matrix_id); if (it == this->matrices.end()) { AKANTU_SILENT_EXCEPTION("The matrix " << matrix_id << " does not exists in " << this->id); } return *(it->second); } /* -------------------------------------------------------------------------- */ bool DOFManager::hasMatrix(const ID & id) const { ID mtx_id = this->id + ":mtx:" + id; auto it = this->matrices.find(mtx_id); return it != this->matrices.end(); } /* -------------------------------------------------------------------------- */ SolverVector & DOFManager::getLumpedMatrix(const ID & id) { ID matrix_id = this->id + ":lumped_mtx:" + id; LumpedMatricesMap::const_iterator it = this->lumped_matrices.find(matrix_id); if (it == this->lumped_matrices.end()) { AKANTU_SILENT_EXCEPTION("The lumped matrix " << matrix_id << " does not exists in " << this->id); } return *(it->second); } /* -------------------------------------------------------------------------- */ const SolverVector & DOFManager::getLumpedMatrix(const ID & id) const { ID matrix_id = this->id + ":lumped_mtx:" + id; auto it = this->lumped_matrices.find(matrix_id); if (it == this->lumped_matrices.end()) { AKANTU_SILENT_EXCEPTION("The lumped matrix " << matrix_id << " does not exists in " << this->id); } return *(it->second); } /* -------------------------------------------------------------------------- */ bool DOFManager::hasLumpedMatrix(const ID & id) const { ID mtx_id = this->id + ":lumped_mtx:" + id; auto it = this->lumped_matrices.find(mtx_id); return it != this->lumped_matrices.end(); } /* -------------------------------------------------------------------------- */ NonLinearSolver & DOFManager::getNonLinearSolver(const ID & id) { ID non_linear_solver_id = this->id + ":nls:" + id; NonLinearSolversMap::const_iterator it = this->non_linear_solvers.find(non_linear_solver_id); if (it == this->non_linear_solvers.end()) { AKANTU_EXCEPTION("The non linear solver " << non_linear_solver_id << " does not exists in " << this->id); } return *(it->second); } /* -------------------------------------------------------------------------- */ bool DOFManager::hasNonLinearSolver(const ID & id) const { ID solver_id = this->id + ":nls:" + id; auto it = this->non_linear_solvers.find(solver_id); return it != this->non_linear_solvers.end(); } /* -------------------------------------------------------------------------- */ TimeStepSolver & DOFManager::getTimeStepSolver(const ID & id) { ID time_step_solver_id = this->id + ":tss:" + id; TimeStepSolversMap::const_iterator it = this->time_step_solvers.find(time_step_solver_id); if (it == this->time_step_solvers.end()) { AKANTU_EXCEPTION("The non linear solver " << time_step_solver_id << " does not exists in " << this->id); } return *(it->second); } /* -------------------------------------------------------------------------- */ bool DOFManager::hasTimeStepSolver(const ID & solver_id) const { ID time_step_solver_id = this->id + ":tss:" + solver_id; auto it = this->time_step_solvers.find(time_step_solver_id); return it != this->time_step_solvers.end(); } /* -------------------------------------------------------------------------- */ void DOFManager::savePreviousDOFs(const ID & dofs_id) { this->getPreviousDOFs(dofs_id).copy(this->getDOFs(dofs_id)); } /* -------------------------------------------------------------------------- */ void DOFManager::zeroResidual() { this->residual->zero(); } /* -------------------------------------------------------------------------- */ void DOFManager::zeroMatrix(const ID & mtx) { this->getMatrix(mtx).zero(); } /* -------------------------------------------------------------------------- */ void DOFManager::zeroLumpedMatrix(const ID & mtx) { this->getLumpedMatrix(mtx).zero(); } /* -------------------------------------------------------------------------- */ /* Mesh Events */ /* -------------------------------------------------------------------------- */ std::pair DOFManager::updateNodalDOFs(const ID & dof_id, const Array & nodes_list) { auto & dof_data = this->getDOFData(dof_id); Int nb_new_local_dofs, nb_new_pure_local; std::tie(nb_new_local_dofs, nb_new_pure_local) = countDOFsForNodes(dof_data, nodes_list.size(), [&nodes_list](auto && n) { return nodes_list(n); }); this->pure_local_system_size += nb_new_pure_local; this->local_system_size += nb_new_local_dofs; auto nb_new_global = nb_new_pure_local; communicator.allReduce(nb_new_global, SynchronizerOperation::_sum); this->system_size += nb_new_global; dof_data.solution.resize(local_system_size); updateDOFsData(dof_data, nb_new_local_dofs, nb_new_pure_local, nodes_list.size(), [&nodes_list](auto pos) -> UInt { return nodes_list[pos]; }); return std::make_pair(nb_new_local_dofs, nb_new_pure_local); } /* -------------------------------------------------------------------------- */ void DOFManager::resizeGlobalArrays() { // resize all relevant arrays this->residual->resize(); this->solution->resize(); this->data_cache->resize(); for (auto & lumped_matrix : lumped_matrices) { lumped_matrix.second->resize(); } for (auto & matrix : matrices) { matrix.second->clearProfile(); } } /* -------------------------------------------------------------------------- */ void DOFManager::onNodesAdded(const Array & nodes_list, const NewNodesEvent &) { for (auto & pair : this->dofs) { const auto & dof_id = pair.first; auto & dof_data = this->getDOFData(dof_id); if (dof_data.support_type != _dst_nodal) { continue; } const auto & group = dof_data.group_support; if (group == "__mesh__") { this->updateNodalDOFs(dof_id, nodes_list); } else { const auto & node_group = this->mesh->getElementGroup(group).getNodeGroup(); Array new_nodes_list; for (const auto & node : nodes_list) { if (node_group.find(node) != Int(-1)) { new_nodes_list.push_back(node); } } this->updateNodalDOFs(dof_id, new_nodes_list); } } this->resizeGlobalArrays(); } +/* -------------------------------------------------------------------------- */ +void DOFManager::onMeshIsDistributed(const MeshIsDistributedEvent & /*event*/) { + AKANTU_DEBUG_ASSERT(this->mesh != nullptr, "The `Mesh` pointer is not set."); + + // check if the distributed state of the residual and the mesh are the same. + if (this->mesh->isDistributed() != this->residual->isDistributed()) { + // TODO: Allow to reallocate the internals, in that case one could actually + // react on that event. + auto is_or_is_not = [](bool q) { + return ((q) ? std::string("is") : std::string("is not")); + }; + AKANTU_EXCEPTION("There is an inconsistency about the distribution state " + "of the `DOFManager`." + " It seams that the `Mesh` " + << is_or_is_not(this->mesh->isDistributed()) + << " distributed, but the `DOFManager`'s residual " + << is_or_is_not(this->residual->isDistributed()) + << ", which is of type " + << debug::demangle(typeid(this->residual).name()) << "."); + } +} + /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ class GlobalDOFInfoDataAccessor : public DataAccessor { public: GlobalDOFInfoDataAccessor(DOFManager::DOFData & dof_data, DOFManager & dof_manager) : dof_data(dof_data), dof_manager(dof_manager) { for (auto && pair : zip(dof_data.local_equation_number, dof_data.associated_nodes)) { Idx node; Idx dof; std::tie(dof, node) = pair; dofs_per_node[node].push_back(dof); } } Int getNbData(const Array & nodes, const SynchronizationTag & tag) const override { if (tag == SynchronizationTag::_ask_nodes or tag == SynchronizationTag::_giu_global_conn) { return nodes.size() * dof_data.dof->getNbComponent() * sizeof(Idx); } return 0; } void packData(CommunicationBuffer & buffer, const Array & nodes, const SynchronizationTag & tag) const override { if (tag == SynchronizationTag::_ask_nodes or tag == SynchronizationTag::_giu_global_conn) { for (const auto & node : nodes) { const auto & dofs = dofs_per_node.at(node); for (const auto & dof : dofs) { buffer << dof_manager.global_equation_number(dof); } } } } void unpackData(CommunicationBuffer & buffer, const Array & nodes, const SynchronizationTag & tag) override { if (tag == SynchronizationTag::_ask_nodes or tag == SynchronizationTag::_giu_global_conn) { for (const auto & node : nodes) { const auto & dofs = dofs_per_node[node]; for (const auto & dof : dofs) { Idx global_dof; buffer >> global_dof; AKANTU_DEBUG_ASSERT( (dof_manager.global_equation_number(dof) == -1 or dof_manager.global_equation_number(dof) == global_dof), "This dof already had a global_dof_id which is different from " "the received one. " << dof_manager.global_equation_number(dof) << " != " << global_dof); dof_manager.global_equation_number(dof) = global_dof; dof_manager.global_to_local_mapping[global_dof] = dof; } } } } protected: std::unordered_map> dofs_per_node; DOFManager::DOFData & dof_data; DOFManager & dof_manager; }; /* -------------------------------------------------------------------------- */ auto DOFManager::computeFirstDOFIDs(Int nb_new_local_dofs, Int nb_new_pure_local) { // determine the first local/global dof id to use Int offset = 0; this->communicator.exclusiveScan(nb_new_pure_local, offset); auto first_global_dof_id = this->first_global_dof_id + offset; auto first_local_dof_id = this->local_system_size - nb_new_local_dofs; offset = nb_new_pure_local; this->communicator.allReduce(offset); this->first_global_dof_id += offset; return std::make_pair(first_local_dof_id, first_global_dof_id); } /* -------------------------------------------------------------------------- */ void DOFManager::updateDOFsData(DOFData & dof_data, Int nb_new_local_dofs, Int nb_new_pure_local, Int nb_node, const std::function & getNode) { auto nb_local_dofs_added = nb_node * dof_data.dof->getNbComponent(); auto first_dof_pos = dof_data.local_equation_number.size(); dof_data.local_equation_number.reserve(dof_data.local_equation_number.size() + nb_local_dofs_added); dof_data.associated_nodes.reserve(dof_data.associated_nodes.size() + nb_local_dofs_added); this->dofs_flag.resize(this->local_system_size, NodeFlag::_normal); this->global_equation_number.resize(this->local_system_size, -1); std::unordered_map, Idx> masters_dofs; // update per dof info Int local_eq_num, first_global_dof_id; std::tie(local_eq_num, first_global_dof_id) = computeFirstDOFIDs(nb_new_local_dofs, nb_new_pure_local); for (auto d : arange(nb_local_dofs_added)) { auto node = getNode(d / dof_data.dof->getNbComponent()); auto dof_flag = this->mesh->getNodeFlag(node); dof_data.associated_nodes.push_back(node); auto is_local_dof = this->mesh->isLocalOrMasterNode(node); auto is_periodic_slave = this->mesh->isPeriodicSlave(node); auto is_periodic_master = this->mesh->isPeriodicMaster(node); if (is_periodic_slave) { dof_data.local_equation_number.push_back(-1); continue; } // update equation numbers this->dofs_flag(local_eq_num) = dof_flag; dof_data.local_equation_number.push_back(local_eq_num); if (is_local_dof) { this->global_equation_number(local_eq_num) = first_global_dof_id; this->global_to_local_mapping[first_global_dof_id] = local_eq_num; ++first_global_dof_id; } else { this->global_equation_number(local_eq_num) = -1; } if (is_periodic_master) { auto node = getNode(d / dof_data.dof->getNbComponent()); auto dof = d % dof_data.dof->getNbComponent(); masters_dofs.insert( std::make_pair(std::make_pair(node, dof), local_eq_num)); } ++local_eq_num; } // correct periodic slave equation numbers if (this->mesh->isPeriodic()) { auto assoc_begin = dof_data.associated_nodes.begin(); for (auto d : arange(nb_local_dofs_added)) { auto node = dof_data.associated_nodes(first_dof_pos + d); if (not this->mesh->isPeriodicSlave(node)) { continue; } auto master_node = this->mesh->getPeriodicMaster(node); auto dof = d % dof_data.dof->getNbComponent(); dof_data.local_equation_number(first_dof_pos + d) = masters_dofs[std::make_pair(master_node, dof)]; } } // synchronize the global numbering for slaves nodes if (this->mesh->isDistributed()) { GlobalDOFInfoDataAccessor data_accessor(dof_data, *this); if (this->mesh->isPeriodic()) { mesh->getPeriodicNodeSynchronizer().synchronizeOnce( data_accessor, SynchronizationTag::_giu_global_conn); } auto & node_synchronizer = this->mesh->getNodeSynchronizer(); node_synchronizer.synchronizeOnce(data_accessor, SynchronizationTag::_ask_nodes); } } /* -------------------------------------------------------------------------- */ void DOFManager::updateDOFsData(DOFData & dof_data, Int nb_new_local_dofs, Int nb_new_pure_local) { dof_data.local_equation_number.reserve(dof_data.local_equation_number.size() + nb_new_local_dofs); Int first_local_dof_id, first_global_dof_id; std::tie(first_local_dof_id, first_global_dof_id) = computeFirstDOFIDs(nb_new_local_dofs, nb_new_pure_local); this->dofs_flag.resize(this->local_system_size, NodeFlag::_normal); this->global_equation_number.resize(this->local_system_size, -1); // update per dof info for (auto _ [[gnu::unused]] : arange(nb_new_local_dofs)) { // update equation numbers this->dofs_flag(first_local_dof_id) = NodeFlag::_normal; dof_data.local_equation_number.push_back(first_local_dof_id); this->global_equation_number(first_local_dof_id) = first_global_dof_id; this->global_to_local_mapping[first_global_dof_id] = first_local_dof_id; ++first_global_dof_id; ++first_local_dof_id; } } /* -------------------------------------------------------------------------- */ void DOFManager::onNodesRemoved(const Array &, const Array &, const RemovedNodesEvent &) {} /* -------------------------------------------------------------------------- */ void DOFManager::onElementsAdded(const Array & /*unused*/, const NewElementsEvent & /*unused*/) {} /* -------------------------------------------------------------------------- */ void DOFManager::onElementsRemoved(const Array &, const ElementTypeMapArray &, const RemovedElementsEvent &) {} /* -------------------------------------------------------------------------- */ void DOFManager::onElementsChanged(const Array &, const Array &, const ElementTypeMapArray &, const ChangedElementsEvent &) {} /* -------------------------------------------------------------------------- */ void DOFManager::updateGlobalBlockedDofs() { this->previous_global_blocked_dofs.copy(this->global_blocked_dofs); this->global_blocked_dofs.reserve(this->local_system_size, 0); this->previous_global_blocked_dofs_release = this->global_blocked_dofs_release; for (auto & pair : dofs) { if (not this->hasBlockedDOFs(pair.first)) { continue; } DOFData & dof_data = *pair.second; for (auto && data : zip(dof_data.getLocalEquationsNumbers(), make_view(*dof_data.blocked_dofs))) { const auto & dof = std::get<0>(data); const auto & is_blocked = std::get<1>(data); if (is_blocked) { this->global_blocked_dofs.push_back(dof); } } } std::sort(this->global_blocked_dofs.begin(), this->global_blocked_dofs.end()); auto last = std::unique(this->global_blocked_dofs.begin(), this->global_blocked_dofs.end()); this->global_blocked_dofs.resize(last - this->global_blocked_dofs.begin()); auto are_equal = global_blocked_dofs.size() == previous_global_blocked_dofs.size() and std::equal(global_blocked_dofs.begin(), global_blocked_dofs.end(), previous_global_blocked_dofs.begin()); if (not are_equal) { ++this->global_blocked_dofs_release; } } /* -------------------------------------------------------------------------- */ void DOFManager::applyBoundary(const ID & matrix_id) { auto & J = this->getMatrix(matrix_id); if (this->jacobian_release == J.getRelease()) { if (this->hasBlockedDOFsChanged()) { J.applyBoundary(); } previous_global_blocked_dofs.copy(global_blocked_dofs); } else { J.applyBoundary(); } this->jacobian_release = J.getRelease(); this->previous_global_blocked_dofs_release = this->global_blocked_dofs_release; } /* -------------------------------------------------------------------------- */ void DOFManager::assembleMatMulVectToGlobalArray(const ID & dof_id, const ID & A_id, const Array & x, SolverVector & array, Real scale_factor) { auto & A = this->getMatrix(A_id); data_cache->resize(); data_cache->zero(); this->assembleToGlobalArray(dof_id, x, *data_cache, 1.); A.matVecMul(*data_cache, array, scale_factor, 1.); } /* -------------------------------------------------------------------------- */ void DOFManager::assembleMatMulVectToResidual(const ID & dof_id, const ID & A_id, const Array & x, Real scale_factor) { assembleMatMulVectToGlobalArray(dof_id, A_id, x, *residual, scale_factor); } } // namespace akantu diff --git a/src/model/common/dof_manager/dof_manager.hh b/src/model/common/dof_manager/dof_manager.hh index e7197c8b2..3f38c1099 100644 --- a/src/model/common/dof_manager/dof_manager.hh +++ b/src/model/common/dof_manager/dof_manager.hh @@ -1,706 +1,718 @@ /** * @file dof_manager.hh * * @author Nicolas Richart * * @date creation: Tue Aug 18 2015 * @date last modification: Fri Jul 24 2020 * * @brief Class handling the different types of dofs * * * @section LICENSE * * Copyright (©) 2015-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_factory.hh" #include "mesh.hh" /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ #ifndef AKANTU_DOF_MANAGER_HH_ #define AKANTU_DOF_MANAGER_HH_ namespace akantu { class TermsToAssemble; class NonLinearSolver; class TimeStepSolver; class SparseMatrix; class SolverVector; class SolverCallback; } // namespace akantu namespace akantu { class DOFManager : protected MeshEventHandler { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ protected: struct DOFData; public: - DOFManager(const ID &id = "dof_manager"); - DOFManager(Mesh &mesh, const ID &id = "dof_manager"); + DOFManager(const ID & id = "dof_manager"); + DOFManager(Mesh & mesh, const ID & id = "dof_manager"); ~DOFManager() override; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ public: /// register an array of degree of freedom - virtual void registerDOFs(const ID &dof_id, Array &dofs_array, + virtual void registerDOFs(const ID & dof_id, Array & dofs_array, DOFSupportType support_type); /// the dof as an implied type of _dst_nodal and is defined only on a subset /// of nodes - virtual void registerDOFs(const ID &dof_id, Array &dofs_array, - const ID &support_group); + virtual void registerDOFs(const ID & dof_id, Array & dofs_array, + const ID & support_group); /// register an array of previous values of the degree of freedom - virtual void registerDOFsPrevious(const ID &dof_id, Array &dofs_array); + virtual void registerDOFsPrevious(const ID & dof_id, + Array & dofs_array); /// register an array of increment of degree of freedom - virtual void registerDOFsIncrement(const ID &dof_id, Array &dofs_array); + virtual void registerDOFsIncrement(const ID & dof_id, + Array & dofs_array); /// register an array of derivatives for a particular dof array - virtual void registerDOFsDerivative(const ID &dof_id, Int order, - Array &dofs_derivative); + virtual void registerDOFsDerivative(const ID & dof_id, Int order, + Array & dofs_derivative); /// register array representing the blocked degree of freedoms - virtual void registerBlockedDOFs(const ID &dof_id, Array &blocked_dofs); + virtual void registerBlockedDOFs(const ID & dof_id, + Array & blocked_dofs); /// Assemble an array to the global residual array - virtual void assembleToResidual(const ID &dof_id, - Array &array_to_assemble, + virtual void assembleToResidual(const ID & dof_id, + Array & array_to_assemble, Real scale_factor = 1.); /// Assemble an array to the global lumped matrix array - virtual void assembleToLumpedMatrix(const ID &dof_id, - Array &array_to_assemble, - const ID &lumped_mtx, + virtual void assembleToLumpedMatrix(const ID & dof_id, + Array & array_to_assemble, + const ID & lumped_mtx, Real scale_factor = 1.); /** * Assemble elementary values to a local array of the size nb_nodes * * nb_dof_per_node. The dof number is implicitly considered as * conn(el, n) * nb_nodes_per_element + d. * With 0 < n < nb_nodes_per_element and 0 < d < nb_dof_per_node **/ virtual void assembleElementalArrayLocalArray( - const Array &elementary_vect, Array &array_assembeled, + const Array & elementary_vect, Array & array_assembeled, ElementType type, GhostType ghost_type, Real scale_factor = 1., - const Array &filter_elements = empty_filter); + const Array & filter_elements = empty_filter); /** * Assemble elementary values to the global residual array. The dof number is * implicitly considered as conn(el, n) * nb_nodes_per_element + d. * With 0 < n < nb_nodes_per_element and 0 < d < nb_dof_per_node **/ virtual void assembleElementalArrayToResidual( - const ID &dof_id, const Array &elementary_vect, ElementType type, + const ID & dof_id, const Array & elementary_vect, ElementType type, GhostType ghost_type, Real scale_factor = 1., - const Array &filter_elements = empty_filter); + const Array & filter_elements = empty_filter); /** * Assemble elementary values to a global array corresponding to a lumped * matrix */ virtual void assembleElementalArrayToLumpedMatrix( - const ID &dof_id, const Array &elementary_vect, - const ID &lumped_mtx, ElementType type, GhostType ghost_type, - Real scale_factor = 1., const Array &filter_elements = empty_filter); + const ID & dof_id, const Array & elementary_vect, + const ID & lumped_mtx, ElementType type, GhostType ghost_type, + Real scale_factor = 1., + const Array & filter_elements = empty_filter); /** * Assemble elementary values to the global residual array. The dof number is * implicitly considered as conn(el, n) * nb_nodes_per_element + d. With 0 < * n < nb_nodes_per_element and 0 < d < nb_dof_per_node **/ virtual void assembleElementalMatricesToMatrix( - const ID &matrix_id, const ID &dof_id, const Array &elementary_mat, - ElementType type, GhostType ghost_type = _not_ghost, - const MatrixType &elemental_matrix_type = _symmetric, - const Array &filter_elements = empty_filter) = 0; + const ID & matrix_id, const ID & dof_id, + const Array & elementary_mat, ElementType type, + GhostType ghost_type = _not_ghost, + const MatrixType & elemental_matrix_type = _symmetric, + const Array & filter_elements = empty_filter) = 0; /// multiply a vector by a matrix and assemble the result to the residual - virtual void assembleMatMulVectToArray(const ID &dof_id, const ID &A_id, - const Array &x, - Array &array, + virtual void assembleMatMulVectToArray(const ID & dof_id, const ID & A_id, + const Array & x, + Array & array, Real scale_factor = 1) = 0; /// multiply a vector by a lumped matrix and assemble the result to the /// residual - virtual void assembleLumpedMatMulVectToResidual(const ID &dof_id, - const ID &A_id, - const Array &x, + virtual void assembleLumpedMatMulVectToResidual(const ID & dof_id, + const ID & A_id, + const Array & x, Real scale_factor = 1) = 0; /// assemble coupling terms between to dofs - virtual void assemblePreassembledMatrix(const ID &matrix_id, - const TermsToAssemble &terms) = 0; + virtual void assemblePreassembledMatrix(const ID & matrix_id, + const TermsToAssemble & terms) = 0; /// multiply a vector by a matrix and assemble the result to the residual - virtual void assembleMatMulVectToResidual(const ID &dof_id, const ID &A_id, - const Array &x, + virtual void assembleMatMulVectToResidual(const ID & dof_id, const ID & A_id, + const Array & x, Real scale_factor = 1); /// multiply the dofs by a matrix and assemble the result to the residual - virtual void assembleMatMulDOFsToResidual(const ID &A_id, + virtual void assembleMatMulDOFsToResidual(const ID & A_id, Real scale_factor = 1); /// updates the global blocked_dofs array virtual void updateGlobalBlockedDofs(); /// sets the residual to 0 virtual void zeroResidual(); /// sets the matrix to 0 - virtual void zeroMatrix(const ID &mtx); + virtual void zeroMatrix(const ID & mtx); /// sets the lumped matrix to 0 - virtual void zeroLumpedMatrix(const ID &mtx); + virtual void zeroLumpedMatrix(const ID & mtx); - virtual void applyBoundary(const ID &matrix_id = "J"); + virtual void applyBoundary(const ID & matrix_id = "J"); // virtual void applyBoundaryLumped(const ID & matrix_id = "J"); /// extract a lumped matrix part corresponding to a given dof - virtual void getLumpedMatrixPerDOFs(const ID &dof_id, const ID &lumped_mtx, - Array &lumped); + virtual void getLumpedMatrixPerDOFs(const ID & dof_id, const ID & lumped_mtx, + Array & lumped); /// splits the solution storage from a global view to the per dof storages void splitSolutionPerDOFs(); private: /// dispatch the creation of the dof data and register it - DOFData &getNewDOFDataInternal(const ID &dof_id); + DOFData & getNewDOFDataInternal(const ID & dof_id); protected: /// common function to help registering dofs the return values are the add new /// numbers of local dofs, pure local dofs, and system size virtual std::tuple - registerDOFsInternal(const ID &dof_id, Array &dofs_array); + registerDOFsInternal(const ID & dof_id, Array & dofs_array); /// minimum functionality to implement per derived version of the DOFManager /// to allow the splitSolutionPerDOFs function to work - virtual void getSolutionPerDOFs(const ID &dof_id, - Array &solution_array); + virtual void getSolutionPerDOFs(const ID & dof_id, + Array & solution_array); /// fill a Vector with the equation numbers corresponding to the given /// connectivity static inline void extractElementEquationNumber( - const Array &equation_numbers, const Vector &connectivity, - Int nb_degree_of_freedom, Vector &local_equation_number); + const Array & equation_numbers, const Vector & connectivity, + Int nb_degree_of_freedom, Vector & local_equation_number); /// Assemble a array to a global one - void assembleMatMulVectToGlobalArray(const ID &dof_id, const ID &A_id, - const Array &x, - SolverVector &array, + void assembleMatMulVectToGlobalArray(const ID & dof_id, const ID & A_id, + const Array & x, + SolverVector & array, Real scale_factor = 1.); /// common function that can be called by derived class with proper matrice /// types template - void assemblePreassembledMatrix_(Mat &A, const TermsToAssemble &terms); + void assemblePreassembledMatrix_(Mat & A, const TermsToAssemble & terms); template void - assembleElementalMatricesToMatrix_(Mat &A, const ID &dof_id, - const Array &elementary_mat, + assembleElementalMatricesToMatrix_(Mat & A, const ID & dof_id, + const Array & elementary_mat, ElementType type, GhostType ghost_type, - const MatrixType &elemental_matrix_type, - const Array &filter_elements); + const MatrixType & elemental_matrix_type, + const Array & filter_elements); template - void assembleMatMulVectToArray_(const ID &dof_id, const ID &A_id, - const Array &x, Array &array, + void assembleMatMulVectToArray_(const ID & dof_id, const ID & A_id, + const Array & x, Array & array, Real scale_factor); /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: /// Get the location type of a given dof inline bool isLocalOrMasterDOF(Idx local_dof_num); /// Answer to the question is a dof a slave dof ? inline bool isSlaveDOF(Idx local_dof_num); /// Answer to the question is a dof a slave dof ? inline bool isPureGhostDOF(Idx local_dof_num); /// tells if the dof manager knows about a global dof bool hasGlobalEquationNumber(Idx global) const; /// return the local index of the global equation number inline Idx globalToLocalEquationNumber(Idx global) const; /// converts local equation numbers to global equation numbers; inline Idx localToGlobalEquationNumber(Idx local) const; /// get the array of dof types (use only if you know what you do...) inline NodeFlag getDOFFlag(Idx local_id) const; /// defines if the boundary changed bool hasBlockedDOFsChanged() const { return this->global_blocked_dofs_release != this->previous_global_blocked_dofs_release; } /// Global number of dofs AKANTU_GET_MACRO_AUTO(SystemSize, this->system_size); /// Local number of dofs AKANTU_GET_MACRO_AUTO(LocalSystemSize, this->local_system_size); /// Pure local number of dofs AKANTU_GET_MACRO_AUTO(PureLocalSystemSize, this->pure_local_system_size); /// Retrieve all the registered DOFs std::vector getDOFIDs() const; /* ------------------------------------------------------------------------ */ /* DOFs and derivatives accessors */ /* ------------------------------------------------------------------------ */ /// Get a reference to the registered dof array for a given id - inline Array &getDOFs(const ID &dofs_id); + inline Array & getDOFs(const ID & dofs_id); /// Get the support type of a given dof - inline DOFSupportType getSupportType(const ID &dofs_id) const; + inline DOFSupportType getSupportType(const ID & dofs_id) const; /// are the dofs registered - inline bool hasDOFs(const ID &dof_id) const; + inline bool hasDOFs(const ID & dof_id) const; /// Get a reference to the registered dof derivatives array for a given id - inline Array &getDOFsDerivatives(const ID &dofs_id, Int order); + inline Array & getDOFsDerivatives(const ID & dofs_id, Int order); /// Does the dof has derivatives - inline bool hasDOFsDerivatives(const ID &dofs_id, Int order) const; + inline bool hasDOFsDerivatives(const ID & dofs_id, Int order) const; /// Get a reference to the blocked dofs array registered for the given id - inline const Array &getBlockedDOFs(const ID &dofs_id) const; + inline const Array & getBlockedDOFs(const ID & dofs_id) const; /// Does the dof has a blocked array - inline bool hasBlockedDOFs(const ID &dofs_id) const; + inline bool hasBlockedDOFs(const ID & dofs_id) const; /// Get a reference to the registered dof increment array for a given id - inline Array &getDOFsIncrement(const ID &dofs_id); + inline Array & getDOFsIncrement(const ID & dofs_id); /// Does the dof has a increment array - inline bool hasDOFsIncrement(const ID &dofs_id) const; + inline bool hasDOFsIncrement(const ID & dofs_id) const; /// Does the dof has a previous array - inline Array &getPreviousDOFs(const ID &dofs_id); + inline Array & getPreviousDOFs(const ID & dofs_id); /// Get a reference to the registered dof array for previous step values a /// given id - inline bool hasPreviousDOFs(const ID &dofs_id) const; + inline bool hasPreviousDOFs(const ID & dofs_id) const; /// saves the values from dofs to previous dofs - virtual void savePreviousDOFs(const ID &dofs_id); + virtual void savePreviousDOFs(const ID & dofs_id); /// Get a reference to the solution array registered for the given id - inline const Array &getSolution(const ID &dofs_id) const; + inline const Array & getSolution(const ID & dofs_id) const; /// Get a reference to the solution array registered for the given id - inline Array &getSolution(const ID &dofs_id); + inline Array & getSolution(const ID & dofs_id); /// Get the blocked dofs array AKANTU_GET_MACRO_AUTO(GlobalBlockedDOFs, global_blocked_dofs); /// Get the blocked dofs array AKANTU_GET_MACRO_AUTO(PreviousGlobalBlockedDOFs, previous_global_blocked_dofs); /* ------------------------------------------------------------------------ */ /* Matrices accessors */ /* ------------------------------------------------------------------------ */ /// Get an instance of a new SparseMatrix - virtual SparseMatrix &getNewMatrix(const ID &matrix_id, - const MatrixType &matrix_type) = 0; + virtual SparseMatrix & getNewMatrix(const ID & matrix_id, + const MatrixType & matrix_type) = 0; /// Get an instance of a new SparseMatrix as a copy of the SparseMatrix /// matrix_to_copy_id - virtual SparseMatrix &getNewMatrix(const ID &matrix_id, - const ID &matrix_to_copy_id) = 0; + virtual SparseMatrix & getNewMatrix(const ID & matrix_id, + const ID & matrix_to_copy_id) = 0; /// Get the equation numbers corresponding to a dof_id. This might be used to /// access the matrix. - inline decltype(auto) getLocalEquationsNumbers(const ID &dof_id) const; + inline decltype(auto) getLocalEquationsNumbers(const ID & dof_id) const; protected: /// get the array of dof types (use only if you know what you do...) - inline decltype(auto) getDOFsAssociatedNodes(const ID &dof_id) const; + inline decltype(auto) getDOFsAssociatedNodes(const ID & dof_id) const; protected: /* ------------------------------------------------------------------------ */ /// register a matrix - SparseMatrix ®isterSparseMatrix(const ID &matrix_id, - std::unique_ptr &matrix); + SparseMatrix & registerSparseMatrix(const ID & matrix_id, + std::unique_ptr & matrix); /// register a lumped matrix (aka a Vector) - SolverVector ®isterLumpedMatrix(const ID &matrix_id, - std::unique_ptr &matrix); + SolverVector & registerLumpedMatrix(const ID & matrix_id, + std::unique_ptr & matrix); /// register a non linear solver instantiated by a derived class NonLinearSolver & - registerNonLinearSolver(const ID &non_linear_solver_id, - std::unique_ptr &non_linear_solver); + registerNonLinearSolver(const ID & non_linear_solver_id, + std::unique_ptr & non_linear_solver); /// register a time step solver instantiated by a derived class TimeStepSolver & - registerTimeStepSolver(const ID &time_step_solver_id, - std::unique_ptr &time_step_solver); + registerTimeStepSolver(const ID & time_step_solver_id, + std::unique_ptr & time_step_solver); template - NonLinearSolver ®isterNonLinearSolver(DMType &dm, const ID &id, - const NonLinearSolverType &type) { + NonLinearSolver & registerNonLinearSolver(DMType & dm, const ID & id, + const NonLinearSolverType & type) { ID non_linear_solver_id = this->id + ":nls:" + id; std::unique_ptr nls = std::make_unique(dm, type, non_linear_solver_id); return this->registerNonLinearSolver(non_linear_solver_id, nls); } template - TimeStepSolver ®isterTimeStepSolver(DMType &dm, const ID &id, - const TimeStepSolverType &type, - NonLinearSolver &non_linear_solver, - SolverCallback &solver_callback) { + TimeStepSolver & registerTimeStepSolver(DMType & dm, const ID & id, + const TimeStepSolverType & type, + NonLinearSolver & non_linear_solver, + SolverCallback & solver_callback) { ID time_step_solver_id = this->id + ":tss:" + id; std::unique_ptr tss = std::make_unique( dm, type, non_linear_solver, solver_callback, time_step_solver_id); return this->registerTimeStepSolver(time_step_solver_id, tss); } template - SparseMatrix ®isterSparseMatrix(DMType &dm, const ID &id, - const MatrixType &matrix_type) { + SparseMatrix & registerSparseMatrix(DMType & dm, const ID & id, + const MatrixType & matrix_type) { ID matrix_id = this->id + ":mtx:" + id; std::unique_ptr sm = std::make_unique(dm, matrix_type, matrix_id); return this->registerSparseMatrix(matrix_id, sm); } template - SparseMatrix ®isterSparseMatrix(const ID &id, - const ID &matrix_to_copy_id) { + SparseMatrix & registerSparseMatrix(const ID & id, + const ID & matrix_to_copy_id) { ID matrix_id = this->id + ":mtx:" + id; - auto &sm_to_copy = + auto & sm_to_copy = aka::as_type(this->getMatrix(matrix_to_copy_id)); std::unique_ptr sm = std::make_unique(sm_to_copy, matrix_id); return this->registerSparseMatrix(matrix_id, sm); } template - SolverVector ®isterLumpedMatrix(DMType &dm, const ID &id) { + SolverVector & registerLumpedMatrix(DMType & dm, const ID & id) { ID matrix_id = this->id + ":lumped_mtx:" + id; std::unique_ptr sm = std::make_unique(dm, matrix_id); return this->registerLumpedMatrix(matrix_id, sm); } protected: - virtual void makeConsistentForPeriodicity(const ID &dof_id, - SolverVector &array) = 0; + virtual void makeConsistentForPeriodicity(const ID & dof_id, + SolverVector & array) = 0; - virtual void assembleToGlobalArray(const ID &dof_id, - const Array &array_to_assemble, - SolverVector &global_array, + virtual void assembleToGlobalArray(const ID & dof_id, + const Array & array_to_assemble, + SolverVector & global_array, Real scale_factor) = 0; public: /// extract degrees of freedom (identified by ID) from a global solver array - virtual void getArrayPerDOFs(const ID &dof_id, const SolverVector &global, - Array &local) = 0; + virtual void getArrayPerDOFs(const ID & dof_id, const SolverVector & global, + Array & local) = 0; /// Get the reference of an existing matrix - SparseMatrix &getMatrix(const ID &matrix_id); + SparseMatrix & getMatrix(const ID & matrix_id); /// check if the given matrix exists - bool hasMatrix(const ID &matrix_id) const; + bool hasMatrix(const ID & matrix_id) const; /// Get an instance of a new lumped matrix - virtual SolverVector &getNewLumpedMatrix(const ID &matrix_id) = 0; + virtual SolverVector & getNewLumpedMatrix(const ID & matrix_id) = 0; /// Get the lumped version of a given matrix - const SolverVector &getLumpedMatrix(const ID &matrix_id) const; + const SolverVector & getLumpedMatrix(const ID & matrix_id) const; /// Get the lumped version of a given matrix - SolverVector &getLumpedMatrix(const ID &matrix_id); + SolverVector & getLumpedMatrix(const ID & matrix_id); /// check if the given matrix exists - bool hasLumpedMatrix(const ID &matrix_id) const; + bool hasLumpedMatrix(const ID & matrix_id) const; /* ------------------------------------------------------------------------ */ /* Non linear system solver */ /* ------------------------------------------------------------------------ */ /// Get instance of a non linear solver - virtual NonLinearSolver & - getNewNonLinearSolver(const ID &nls_solver_id, - const NonLinearSolverType &_non_linear_solver_type) = 0; + virtual NonLinearSolver & getNewNonLinearSolver( + const ID & nls_solver_id, + const NonLinearSolverType & _non_linear_solver_type) = 0; /// get instance of a non linear solver - virtual NonLinearSolver &getNonLinearSolver(const ID &nls_solver_id); + virtual NonLinearSolver & getNonLinearSolver(const ID & nls_solver_id); /// check if the given solver exists - bool hasNonLinearSolver(const ID &solver_id) const; + bool hasNonLinearSolver(const ID & solver_id) const; /* ------------------------------------------------------------------------ */ /* Time-Step Solver */ /* ------------------------------------------------------------------------ */ /// Get instance of a time step solver - virtual TimeStepSolver &getNewTimeStepSolver( - const ID &time_step_solver_id, const TimeStepSolverType &type, - NonLinearSolver &non_linear_solver, SolverCallback &solver_callback) = 0; + virtual TimeStepSolver & + getNewTimeStepSolver(const ID & time_step_solver_id, + const TimeStepSolverType & type, + NonLinearSolver & non_linear_solver, + SolverCallback & solver_callback) = 0; /// get instance of a time step solver - virtual TimeStepSolver &getTimeStepSolver(const ID &time_step_solver_id); + virtual TimeStepSolver & getTimeStepSolver(const ID & time_step_solver_id); /// check if the given solver exists - bool hasTimeStepSolver(const ID &solver_id) const; + bool hasTimeStepSolver(const ID & solver_id) const; /* ------------------------------------------------------------------------ */ - const Mesh &getMesh() { + const Mesh & getMesh() { if (mesh != nullptr) { return *mesh; } AKANTU_EXCEPTION("No mesh registered in this dof manager"); } /* ------------------------------------------------------------------------ */ AKANTU_GET_MACRO_AUTO(Communicator, communicator); AKANTU_GET_MACRO_AUTO_NOT_CONST(Communicator, communicator); /* ------------------------------------------------------------------------ */ AKANTU_GET_MACRO_DEREF_PTR(Solution, solution); AKANTU_GET_MACRO_DEREF_PTR_NOT_CONST(Solution, solution); AKANTU_GET_MACRO_DEREF_PTR(Residual, residual); AKANTU_GET_MACRO_DEREF_PTR_NOT_CONST(Residual, residual); /* ------------------------------------------------------------------------ */ /* MeshEventHandler interface */ /* ------------------------------------------------------------------------ */ protected: friend class GlobalDOFInfoDataAccessor; /// helper function for the DOFManager::onNodesAdded method - virtual std::pair updateNodalDOFs(const ID &dof_id, - const Array &nodes_list); + virtual std::pair updateNodalDOFs(const ID & dof_id, + const Array & nodes_list); template - auto countDOFsForNodes(const DOFData &dof_data, Int nb_nodes, Func &&getNode); + auto countDOFsForNodes(const DOFData & dof_data, Int nb_nodes, + Func && getNode); - void updateDOFsData(DOFData &dof_data, Int nb_new_local_dofs, + void updateDOFsData(DOFData & dof_data, Int nb_new_local_dofs, Int nb_new_pure_local, Int nb_nodes, - const std::function &getNode); + const std::function & getNode); - void updateDOFsData(DOFData &dof_data, Int nb_new_local_dofs, + void updateDOFsData(DOFData & dof_data, Int nb_new_local_dofs, Int nb_new_pure_local); auto computeFirstDOFIDs(Int nb_new_local_dofs, Int nb_new_pure_local); /// resize all the global information and takes the needed measure like /// cleaning matrices profiles virtual void resizeGlobalArrays(); public: /// function to implement to react on akantu::NewNodesEvent - void onNodesAdded(const Array &nodes_list, - const NewNodesEvent &event) override; + void onNodesAdded(const Array & nodes_list, + const NewNodesEvent & event) override; /// function to implement to react on akantu::RemovedNodesEvent - void onNodesRemoved(const Array &nodes_list, - const Array &new_numbering, - const RemovedNodesEvent &event) override; + void onNodesRemoved(const Array & nodes_list, + const Array & new_numbering, + const RemovedNodesEvent & event) override; /// function to implement to react on akantu::NewElementsEvent - void onElementsAdded(const Array &elements_list, - const NewElementsEvent &event) override; + void onElementsAdded(const Array & elements_list, + const NewElementsEvent & event) override; /// function to implement to react on akantu::RemovedElementsEvent - void onElementsRemoved(const Array &elements_list, - const ElementTypeMapArray &new_numbering, - const RemovedElementsEvent &event) override; + void onElementsRemoved(const Array & elements_list, + const ElementTypeMapArray & new_numbering, + const RemovedElementsEvent & event) override; /// function to implement to react on akantu::ChangedElementsEvent - void onElementsChanged(const Array &old_elements_list, - const Array &new_elements_list, - const ElementTypeMapArray &new_numbering, - const ChangedElementsEvent &event) override; + void onElementsChanged(const Array & old_elements_list, + const Array & new_elements_list, + const ElementTypeMapArray & new_numbering, + const ChangedElementsEvent & event) override; + + /// function to implement to react on akantu::MeshIsDistributedEvent + void onMeshIsDistributed(const MeshIsDistributedEvent & event) override; protected: - inline DOFData &getDOFData(const ID &dof_id); - inline const DOFData &getDOFData(const ID &dof_id) const; - template inline DOFData_ &getDOFDataTyped(const ID &dof_id); + inline DOFData & getDOFData(const ID & dof_id); + inline const DOFData & getDOFData(const ID & dof_id) const; + template + inline DOFData_ & getDOFDataTyped(const ID & dof_id); template - inline const DOFData_ &getDOFDataTyped(const ID &dof_id) const; + inline const DOFData_ & getDOFDataTyped(const ID & dof_id) const; - virtual std::unique_ptr getNewDOFData(const ID &dof_id) = 0; + virtual std::unique_ptr getNewDOFData(const ID & dof_id) = 0; /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ protected: /// dof representations in the dof manager struct DOFData { DOFData() = delete; - explicit DOFData(const ID &dof_id); + explicit DOFData(const ID & dof_id); virtual ~DOFData(); /// DOF support type (nodal, general) this is needed to determine how the /// dof are shared among processors DOFSupportType support_type; ID group_support; /// Degree of freedom array - Array *dof{nullptr}; + Array * dof{nullptr}; /// Blocked degree of freedoms array - Array *blocked_dofs{nullptr}; + Array * blocked_dofs{nullptr}; /// Degree of freedoms increment - Array *increment{nullptr}; + Array * increment{nullptr}; /// Degree of freedoms at previous step - Array *previous{nullptr}; + Array * previous{nullptr}; /// Solution associated to the dof Array solution; /* ---------------------------------------------------------------------- */ /* data for dynamic simulations */ /* ---------------------------------------------------------------------- */ /// Degree of freedom derivatives arrays std::vector *> dof_derivatives; /* ---------------------------------------------------------------------- */ /// number of dofs to consider locally for this dof id Int local_nb_dofs{0}; /// Number of purely local dofs Int pure_local_nb_dofs{0}; /// number of ghost dofs Int ghosts_nb_dofs{0}; /// local numbering equation numbers Array local_equation_number; /// associated node for _dst_nodal dofs only Array associated_nodes; - virtual Array &getLocalEquationsNumbers() { + virtual Array & getLocalEquationsNumbers() { return local_equation_number; } }; /// type to store dofs information using DOFStorage = std::map>; /// type to store all the matrices using SparseMatricesMap = std::map>; /// type to store all the lumped matrices using LumpedMatricesMap = std::map>; /// type to store all the non linear solver using NonLinearSolversMap = std::map>; /// type to store all the time step solver using TimeStepSolversMap = std::map>; ID id; /// store a reference to the dof arrays DOFStorage dofs; /// list of sparse matrices that where created SparseMatricesMap matrices; /// list of lumped matrices LumpedMatricesMap lumped_matrices; /// non linear solvers storage NonLinearSolversMap non_linear_solvers; /// time step solvers storage TimeStepSolversMap time_step_solvers; /// reference to the underlying mesh - Mesh *mesh{nullptr}; + Mesh * mesh{nullptr}; /// Total number of degrees of freedom (size with the ghosts) Int local_system_size{0}; /// Number of purely local dofs (size without the ghosts) Int pure_local_system_size{0}; /// Total number of degrees of freedom Int system_size{0}; /// rhs to the system of equation corresponding to the residual linked to the /// different dofs std::unique_ptr residual; /// solution of the system of equation corresponding to the different dofs std::unique_ptr solution; /// a vector that helps internally to perform some tasks std::unique_ptr data_cache; /// define the dofs type, local, shared, ghost Array dofs_flag; /// equation number in global numbering Array global_equation_number; using equation_numbers_map = std::unordered_map; /// dual information of global_equation_number equation_numbers_map global_to_local_mapping; /// Communicator used for this manager, should be the same as in the mesh if a /// mesh is registered - Communicator &communicator; + Communicator & communicator; /// accumulator to know what would be the next global id to use Int first_global_dof_id{0}; /// Release at last apply boundary on jacobian Int jacobian_release{0}; /// blocked degree of freedom in the system equation corresponding to the /// different dofs Array global_blocked_dofs; Int global_blocked_dofs_release{0}; /// blocked degree of freedom in the system equation corresponding to the /// different dofs Array previous_global_blocked_dofs; Int previous_global_blocked_dofs_release{0}; private: /// This is for unit testing friend class DOFManagerTester; }; using DefaultDOFManagerFactory = Factory; using DOFManagerFactory = Factory; } // namespace akantu #include "dof_manager_inline_impl.hh" #endif /* AKANTU_DOF_MANAGER_HH_ */ diff --git a/src/model/common/integration_scheme/newmark-beta.cc b/src/model/common/integration_scheme/newmark-beta.cc index aeb924cd5..316723061 100644 --- a/src/model/common/integration_scheme/newmark-beta.cc +++ b/src/model/common/integration_scheme/newmark-beta.cc @@ -1,263 +1,269 @@ /** * @file newmark-beta.cc * * @author David Simon Kammer * @author Nicolas Richart * * @date creation: Fri Oct 23 2015 * @date last modification: Wed Mar 27 2019 * * @brief implementation of the newmark-@f$\beta@f$ integration scheme. This * implementation is taken from Méthodes numériques en mécanique des solides by * Alain Curnier \note{ISBN: 2-88074-247-1} * * * @section LICENSE * * Copyright (©) 2015-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "newmark-beta.hh" #include "dof_manager.hh" #include "sparse_matrix.hh" /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ -NewmarkBeta::NewmarkBeta(DOFManager &dof_manager, const ID &dof_id, Real alpha, - Real beta) +NewmarkBeta::NewmarkBeta(DOFManager & dof_manager, const ID & dof_id, + Real alpha, Real beta) : IntegrationScheme2ndOrder(dof_manager, dof_id), beta(beta), alpha(alpha) { this->registerParam("alpha", this->alpha, alpha, _pat_parsmod, "The alpha parameter"); this->registerParam("beta", this->beta, beta, _pat_parsmod, "The beta parameter"); } /* -------------------------------------------------------------------------- */ /* * @f$ \tilde{u_{n+1}} = u_{n} + \Delta t \dot{u}_n + \frac{\Delta t^2}{2} * \ddot{u}_n @f$ * @f$ \tilde{\dot{u}_{n+1}} = \dot{u}_{n} + \Delta t \ddot{u}_{n} @f$ * @f$ \tilde{\ddot{u}_{n}} = \ddot{u}_{n} @f$ */ -void NewmarkBeta::predictor(Real delta_t, Array &u, Array &u_dot, - Array &u_dot_dot, - const Array &blocked_dofs) const { +void NewmarkBeta::predictor(Real delta_t, Array & u, Array & u_dot, + Array & u_dot_dot, + const Array & blocked_dofs) const { AKANTU_DEBUG_IN(); auto nb_nodes = u.size(); auto nb_degree_of_freedom = u.getNbComponent() * nb_nodes; - auto *u_val = u.data(); - auto *u_dot_val = u_dot.data(); - auto *u_dot_dot_val = u_dot_dot.data(); - auto *blocked_dofs_val = blocked_dofs.data(); + auto * u_val = u.data(); + auto * u_dot_val = u_dot.data(); + auto * u_dot_dot_val = u_dot_dot.data(); + auto * blocked_dofs_val = blocked_dofs.data(); for (Int d = 0; d < nb_degree_of_freedom; d++) { if (!(*blocked_dofs_val)) { Real dt_a_n = delta_t * *u_dot_dot_val; *u_val += (1 - k * alpha) * delta_t * *u_dot_val + (.5 - h * alpha * beta) * delta_t * dt_a_n; *u_dot_val = (1 - k) * *u_dot_val + (1 - h * beta) * dt_a_n; *u_dot_dot_val = (1 - h) * *u_dot_dot_val; } u_val++; u_dot_val++; u_dot_dot_val++; blocked_dofs_val++; } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ -void NewmarkBeta::corrector(const SolutionType &type, Real delta_t, - Array &u, Array &u_dot, - Array &u_dot_dot, - const Array &blocked_dofs, - const Array &delta) const { +void NewmarkBeta::corrector(const SolutionType & type, Real delta_t, + Array & u, Array & u_dot, + Array & u_dot_dot, + const Array & blocked_dofs, + const Array & delta) const { AKANTU_DEBUG_IN(); switch (type) { case _acceleration: { this->allCorrector<_acceleration>(delta_t, u, u_dot, u_dot_dot, blocked_dofs, delta); break; } case _velocity: { this->allCorrector<_velocity>(delta_t, u, u_dot, u_dot_dot, blocked_dofs, delta); break; } case _displacement: { this->allCorrector<_displacement>(delta_t, u, u_dot, u_dot_dot, blocked_dofs, delta); break; } default: AKANTU_EXCEPTION("The corrector type : " << type << " is not supported by this type of integration scheme"); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ -Real NewmarkBeta::getAccelerationCoefficient(const SolutionType &type, +Real NewmarkBeta::getAccelerationCoefficient(const SolutionType & type, Real delta_t) const { switch (type) { case _acceleration: return 1.; case _velocity: return 1. / (beta * delta_t); case _displacement: return 1. / (alpha * beta * delta_t * delta_t); default: AKANTU_EXCEPTION("The corrector type : " << type << " is not supported by this type of integration scheme"); } } /* -------------------------------------------------------------------------- */ -Real NewmarkBeta::getVelocityCoefficient(const SolutionType &type, +Real NewmarkBeta::getVelocityCoefficient(const SolutionType & type, Real delta_t) const { switch (type) { case _acceleration: return beta * delta_t; case _velocity: return 1.; case _displacement: return 1. / (alpha * delta_t); default: AKANTU_EXCEPTION("The corrector type : " << type << " is not supported by this type of integration scheme"); } } /* -------------------------------------------------------------------------- */ -Real NewmarkBeta::getDisplacementCoefficient(const SolutionType &type, +Real NewmarkBeta::getDisplacementCoefficient(const SolutionType & type, Real delta_t) const { switch (type) { case _acceleration: return alpha * beta * delta_t * delta_t; case _velocity: return alpha * delta_t; case _displacement: return 1.; default: AKANTU_EXCEPTION("The corrector type : " << type << " is not supported by this type of integration scheme"); } } /* -------------------------------------------------------------------------- */ template -void NewmarkBeta::allCorrector(Real delta_t, Array &u, Array &u_dot, - Array &u_dot_dot, - const Array &blocked_dofs, - const Array &delta) const { +void NewmarkBeta::allCorrector(Real delta_t, Array & u, + Array & u_dot, Array & u_dot_dot, + const Array & blocked_dofs, + const Array & delta) const { AKANTU_DEBUG_IN(); auto nb_nodes = u.size(); auto nb_degree_of_freedom = u.getNbComponent() * nb_nodes; auto c = getAccelerationCoefficient(type, delta_t); auto d = getVelocityCoefficient(type, delta_t); auto e = getDisplacementCoefficient(type, delta_t); - auto *u_val = u.data(); - auto *u_dot_val = u_dot.data(); - auto *u_dot_dot_val = u_dot_dot.data(); - auto *delta_val = delta.data(); - auto *blocked_dofs_val = blocked_dofs.data(); + auto * u_val = u.data(); + auto * u_dot_val = u_dot.data(); + auto * u_dot_dot_val = u_dot_dot.data(); + auto * delta_val = delta.data(); + auto * blocked_dofs_val = blocked_dofs.data(); for (Int dof = 0; dof < nb_degree_of_freedom; dof++) { if (!(*blocked_dofs_val)) { *u_val += e * *delta_val; *u_dot_val += d * *delta_val; *u_dot_dot_val += c * *delta_val; } u_val++; u_dot_val++; u_dot_dot_val++; delta_val++; blocked_dofs_val++; } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ -void NewmarkBeta::assembleJacobian(const SolutionType &type, Real delta_t) { +void NewmarkBeta::assembleJacobian(const SolutionType & type, Real delta_t) { AKANTU_DEBUG_IN(); - auto &J = this->dof_manager.getMatrix("J"); + auto & J = this->dof_manager.getMatrix("J"); - const auto &M = this->dof_manager.getMatrix("M"); - const auto &K = this->dof_manager.getMatrix("K"); + const auto & M = this->dof_manager.getMatrix("M"); + + auto c = this->getAccelerationCoefficient(type, delta_t); + auto e = this->getDisplacementCoefficient(type, delta_t); bool does_j_need_update = false; does_j_need_update |= M.getRelease() != m_release; - does_j_need_update |= K.getRelease() != k_release; + + // in explicit this coefficient is exactly 0. + if (not(e == 0.)) { + const auto & K = this->dof_manager.getMatrix("K"); + does_j_need_update |= K.getRelease() != k_release; + } + if (this->dof_manager.hasMatrix("C")) { - const auto &C = this->dof_manager.getMatrix("C"); + const auto & C = this->dof_manager.getMatrix("C"); does_j_need_update |= C.getRelease() != c_release; } does_j_need_update |= this->dof_manager.hasBlockedDOFsChanged(); - if (!does_j_need_update) { + if (not does_j_need_update) { AKANTU_DEBUG_OUT(); return; } - J.copyProfile(K); + J.copyProfile(M); // J.zero(); - auto c = this->getAccelerationCoefficient(type, delta_t); - auto e = this->getDisplacementCoefficient(type, delta_t); - - if (!(e == 0.)) { // in explicit this coefficient is exactly 0. + if (not(e == 0.)) { + const auto & K = this->dof_manager.getMatrix("K"); J.add(K, e); + k_release = K.getRelease(); } J.add(M, c); - m_release = M.getRelease(); - k_release = K.getRelease(); if (this->dof_manager.hasMatrix("C")) { auto d = this->getVelocityCoefficient(type, delta_t); - const auto &C = this->dof_manager.getMatrix("C"); + const auto & C = this->dof_manager.getMatrix("C"); J.add(C, d); c_release = C.getRelease(); } AKANTU_DEBUG_OUT(); } + /* -------------------------------------------------------------------------- */ } // namespace akantu diff --git a/src/model/common/integration_scheme/newmark-beta.hh b/src/model/common/integration_scheme/newmark-beta.hh index 641912ce4..845159075 100644 --- a/src/model/common/integration_scheme/newmark-beta.hh +++ b/src/model/common/integration_scheme/newmark-beta.hh @@ -1,194 +1,197 @@ /** * @file newmark-beta.hh * * @author David Simon Kammer * @author Nicolas Richart * * @date creation: Tue Oct 05 2010 * @date last modification: Sat Sep 12 2020 * * @brief implementation of the newmark-@f$\beta@f$ integration scheme. This * implementation is taken from Méthodes numériques en mécanique des solides by * Alain Curnier \note{ISBN: 2-88074-247-1} * * * @section LICENSE * * Copyright (©) 2010-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "integration_scheme_2nd_order.hh" /* -------------------------------------------------------------------------- */ #ifndef AKANTU_NEWMARK_BETA_HH_ #define AKANTU_NEWMARK_BETA_HH_ /* -------------------------------------------------------------------------- */ namespace akantu { /** * The three differentiate equations (dynamic and cinematic) are : * \f{eqnarray*}{ * M \ddot{u}_{n+1} + C \dot{u}_{n+1} + K u_{n+1} &=& q_{n+1} \\ * u_{n+1} &=& u_{n} + (1 - \alpha) \Delta t \dot{u}_{n} + \alpha \Delta t *\dot{u}_{n+1} + (1/2 - \alpha) \Delta t^2 \ddot{u}_n \\ * \dot{u}_{n+1} &=& \dot{u}_{n} + (1 - \beta) \Delta t \ddot{u}_{n} + \beta *\Delta t \ddot{u}_{n+1} * \f} * * Predictor: * \f{eqnarray*}{ * u^{0}_{n+1} &=& u_{n} + \Delta t \dot{u}_n + \frac{\Delta t^2}{2} *\ddot{u}_n \\ * \dot{u}^{0}_{n+1} &=& \dot{u}_{n} + \Delta t \ddot{u}_{n} \\ * \ddot{u}^{0}_{n+1} &=& \ddot{u}_{n} * \f} * * Solve : * \f[ (c M + d C + e K^i_{n+1}) w = = q_{n+1} - f^i_{n+1} - C \dot{u}^i_{n+1} *- M \ddot{u}^i_{n+1} \f] * * Corrector : * \f{eqnarray*}{ * \ddot{u}^{i+1}_{n+1} &=& \ddot{u}^{i}_{n+1} + c w \\ * \dot{u}^{i+1}_{n+1} &=& \dot{u}^{i}_{n+1} + d w \\ * u^{i+1}_{n+1} &=& u^{i}_{n+1} + e w * \f} * * c, d and e are parameters depending on the method used to solve the equations *\n * For acceleration : \f$ w = \delta \ddot{u}, e = \alpha \beta \Delta t^2, d = *\beta \Delta t, c = 1 \f$ \n * For velocity : \f$ w = \delta \dot{u}, e = 1/\beta \Delta t, d = *1, c = \alpha \Delta t \f$ \n * For displacement : \f$ w = \delta u, e = 1, d = *1/\alpha \Delta t, c = 1/\alpha \beta \Delta t^2 \f$ */ class NewmarkBeta : public IntegrationScheme2ndOrder { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: NewmarkBeta(DOFManager &dof_manager, const ID &dof_id, Real alpha = 0., Real beta = 0.); /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ public: void predictor(Real delta_t, Array &u, Array &u_dot, Array &u_dot_dot, const Array &blocked_dofs) const override; void corrector(const SolutionType &type, Real delta_t, Array &u, Array &u_dot, Array &u_dot_dot, const Array &blocked_dofs, const Array &delta) const override; void assembleJacobian(const SolutionType &type, Real delta_t) override; public: Real getAccelerationCoefficient(const SolutionType &type, Real delta_t) const override; Real getVelocityCoefficient(const SolutionType &type, Real delta_t) const override; Real getDisplacementCoefficient(const SolutionType &type, Real delta_t) const override; private: template void allCorrector(Real delta_t, Array &u, Array &u_dot, Array &u_dot_dot, const Array &blocked_dofs, const Array &delta) const; /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: AKANTU_GET_MACRO(Beta, beta, Real); + AKANTU_SET_MACRO(Beta, beta, Real); + AKANTU_GET_MACRO(Alpha, alpha, Real); + AKANTU_SET_MACRO(Alpha, alpha, Real); /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ protected: /// the \f$\beta\f$ parameter Real beta; /// the \f$\alpha\f$ parameter Real alpha; Real k{0}; Real h{0}; /// last release of K matrix Int k_release{0}; /// last release of C matrix Int c_release{0}; }; /** * central difference method (explicit) * undamped stability condition : * \f$ \Delta t = \alpha \Delta t_{crit} = \frac{2}{\omega_{max}} \leq \min_{e} *\frac{l_e}{c_e}\f$ * */ class CentralDifference : public NewmarkBeta { public: CentralDifference(DOFManager &dof_manager, const ID &dof_id) : NewmarkBeta(dof_manager, dof_id, 0., 1. / 2.){}; std::vector getNeededMatrixList() override { return {"M", "C"}; } }; //#include "integration_scheme/central_difference.hh" /// undamped trapezoidal rule (implicit) class TrapezoidalRule2 : public NewmarkBeta { public: TrapezoidalRule2(DOFManager &dof_manager, const ID &dof_id) : NewmarkBeta(dof_manager, dof_id, 1. / 2., 1. / 2.){}; }; /// Fox-Goodwin rule (implicit) class FoxGoodwin : public NewmarkBeta { public: FoxGoodwin(DOFManager &dof_manager, const ID &dof_id) : NewmarkBeta(dof_manager, dof_id, 1. / 6., 1. / 2.){}; }; /// Linear acceleration (implicit) class LinearAceleration : public NewmarkBeta { public: LinearAceleration(DOFManager &dof_manager, const ID &dof_id) : NewmarkBeta(dof_manager, dof_id, 1. / 3., 1. / 2.){}; }; /* -------------------------------------------------------------------------- */ } // namespace akantu #endif /* AKANTU_NEWMARK_BETA_HH_ */ diff --git a/src/model/common/time_step_solvers/time_step_solver.hh b/src/model/common/time_step_solvers/time_step_solver.hh index f4456e5cb..050e9c275 100644 --- a/src/model/common/time_step_solvers/time_step_solver.hh +++ b/src/model/common/time_step_solvers/time_step_solver.hh @@ -1,167 +1,171 @@ /** * @file time_step_solver.hh * * @author Mohit Pundir * @author Nicolas Richart * * @date creation: Fri Jun 18 2010 * @date last modification: Tue Sep 08 2020 * * @brief This corresponding to the time step evolution solver * * * @section LICENSE * * Copyright (©) 2010-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_array.hh" #include "integration_scheme.hh" #include "parameter_registry.hh" #include "solver_callback.hh" /* -------------------------------------------------------------------------- */ #ifndef AKANTU_TIME_STEP_SOLVER_HH_ #define AKANTU_TIME_STEP_SOLVER_HH_ namespace akantu { class DOFManager; class NonLinearSolver; } // namespace akantu namespace akantu { class TimeStepSolver : public ParameterRegistry, public SolverCallback { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: TimeStepSolver(DOFManager & dof_manager, const TimeStepSolverType & type, NonLinearSolver & non_linear_solver, SolverCallback & solver_callback, const ID & id); ~TimeStepSolver() override; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ public: /// solves on step virtual void solveStep(SolverCallback & solver_callback) = 0; /// register an integration scheme for a given dof void setIntegrationScheme(const ID & dof_id, const IntegrationSchemeType & type, IntegrationScheme::SolutionType solution_type = IntegrationScheme::_not_defined); /// register an integration scheme for a given dof void setIntegrationScheme(const ID & dof_id, std::unique_ptr & scheme, IntegrationScheme::SolutionType solution_type = IntegrationScheme::_not_defined); + virtual IntegrationScheme & getIntegrationScheme(const ID & /*dof_id*/) { + AKANTU_TO_IMPLEMENT(); + } + protected: /// register an integration scheme for a given dof virtual std::unique_ptr getIntegrationSchemeInternal(const ID & dof_id, const IntegrationSchemeType & type, IntegrationScheme::SolutionType solution_type = IntegrationScheme::_not_defined) = 0; virtual void setIntegrationSchemeInternal(const ID & dof_id, std::unique_ptr & scheme, IntegrationScheme::SolutionType solution_type = IntegrationScheme::_not_defined) = 0; public: /// replies if a integration scheme has been set virtual bool hasIntegrationScheme(const ID & dof_id) const = 0; /* ------------------------------------------------------------------------ */ /* Solver Callback interface */ /* ------------------------------------------------------------------------ */ public: /// implementation of the SolverCallback::getMatrixType() MatrixType getMatrixType(const ID & /*unused*/) const final { return _mt_not_defined; } /// implementation of the SolverCallback::predictor() void predictor() override; /// implementation of the SolverCallback::corrector() void corrector() override; /// implementation of the SolverCallback::assembleJacobian() void assembleMatrix(const ID & matrix_id) override; /// implementation of the SolverCallback::assembleJacobian() void assembleLumpedMatrix(const ID & matrix_id) override; /// implementation of the SolverCallback::assembleResidual() void assembleResidual() override; /// implementation of the SolverCallback::assembleResidual() void assembleResidual(const ID & residual_part) override; void beforeSolveStep() override; void afterSolveStep(bool converged = true) override; bool canSplitResidual() const override { return solver_callback->canSplitResidual(); } /* ------------------------------------------------------------------------ */ /* Accessor */ /* ------------------------------------------------------------------------ */ public: AKANTU_GET_MACRO(TimeStep, time_step, Real); AKANTU_SET_MACRO(TimeStep, time_step, Real); AKANTU_GET_MACRO(NonLinearSolver, non_linear_solver, const NonLinearSolver &); AKANTU_GET_MACRO_NOT_CONST(NonLinearSolver, non_linear_solver, NonLinearSolver &); protected: MatrixType getCommonMatrixType(); /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ protected: ID id; /// Underlying dof manager containing the dof to treat DOFManager & _dof_manager; /// Type of solver TimeStepSolverType type; /// The time step for this solver Real time_step; /// Temporary storage for solver callback SolverCallback * solver_callback; /// NonLinearSolver used by this tome step solver NonLinearSolver & non_linear_solver; /// List of required matrices std::map needed_matrices; /// specifies if the solvers gives to full solution or just the increment of /// solution bool is_solution_increment{true}; }; } // namespace akantu #endif /* AKANTU_TIME_STEP_SOLVER_HH_ */ diff --git a/src/model/common/time_step_solvers/time_step_solver_default.hh b/src/model/common/time_step_solvers/time_step_solver_default.hh index a87cf2695..a02e96d2a 100644 --- a/src/model/common/time_step_solvers/time_step_solver_default.hh +++ b/src/model/common/time_step_solvers/time_step_solver_default.hh @@ -1,134 +1,141 @@ /** * @file time_step_solver_default.hh * * @author Mohit Pundir * @author Nicolas Richart * * @date creation: Fri Jun 18 2010 * @date last modification: Tue Sep 08 2020 * * @brief Default implementation for the time stepper * * * @section LICENSE * * Copyright (©) 2010-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "integration_scheme.hh" #include "time_step_solver.hh" /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ #ifndef AKANTU_TIME_STEP_SOLVER_DEFAULT_HH_ #define AKANTU_TIME_STEP_SOLVER_DEFAULT_HH_ namespace akantu { class DOFManager; } namespace akantu { class TimeStepSolverDefault : public TimeStepSolver { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: TimeStepSolverDefault(DOFManager & dof_manager, const TimeStepSolverType & type, NonLinearSolver & non_linear_solver, SolverCallback & solver_callback, const ID & id); ~TimeStepSolverDefault() override; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ protected: /// registers an integration scheme for a given dof std::unique_ptr getIntegrationSchemeInternal(const ID & dof_id, const IntegrationSchemeType & type, IntegrationScheme::SolutionType solution_type = IntegrationScheme::_not_defined) override; void setIntegrationSchemeInternal( const ID & dof_id, std::unique_ptr & integration_scheme, IntegrationScheme::SolutionType solution_type) override; public: bool hasIntegrationScheme(const ID & dof_id) const override; /// implementation of the TimeStepSolver::predictor() void predictor() override; /// implementation of the TimeStepSolver::corrector() void corrector() override; /// implementation of the TimeStepSolver::assembleMatrix() void assembleMatrix(const ID & matrix_id) override; // void assembleLumpedMatrix(const ID & matrix_id) override; /// implementation of the TimeStepSolver::assembleResidual() void assembleResidual() override; void assembleResidual(const ID & residual_part) override; void beforeSolveStep() override; void afterSolveStep(bool converged = true) override; /// implementation of the generic TimeStepSolver::solveStep() void solveStep(SolverCallback & solver_callback) override; + IntegrationScheme & getIntegrationScheme(const ID & dof_id) override { + auto it = integration_schemes.find(dof_id); + AKANTU_DEBUG_ASSERT(it != integration_schemes.end(), + "No integration scheme"); + return *(it->second); + } + private: template void for_each_integrator(Func && function) { for (auto & pair : this->integration_schemes) { const auto & dof_id = pair.first; auto & integration_scheme = pair.second; function(dof_id, *integration_scheme); } } /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ private: using DOFsIntegrationSchemes = std::map>; using DOFsIntegrationSchemesSolutionTypes = std::map; using DOFsIntegrationSchemesOwner = std::set; /// Underlying integration scheme per dof, \todo check what happens in dynamic /// in case of coupled equations DOFsIntegrationSchemes integration_schemes; /// defines if the solver is owner of the memory or not DOFsIntegrationSchemesOwner integration_schemes_owner; /// Type of corrector to use DOFsIntegrationSchemesSolutionTypes solution_types; /// define if the mass matrix is lumped or not bool is_mass_lumped{false}; }; } // namespace akantu #endif /* AKANTU_TIME_STEP_SOLVER_DEFAULT_HH_ */ diff --git a/src/model/model_couplers/coupler_solid_cohesive_contact.cc b/src/model/model_couplers/coupler_solid_cohesive_contact.cc index 7b81d791b..d6032f1df 100644 --- a/src/model/model_couplers/coupler_solid_cohesive_contact.cc +++ b/src/model/model_couplers/coupler_solid_cohesive_contact.cc @@ -1,76 +1,76 @@ /** * @file coupler_solid_cohesive_contact.cc * * @author Mohit Pundir * @author Nicolas Richart * * @date creation: Mon Jan 21 2019 * @date last modification: Wed Jun 23 2021 * * @brief class for coupling of solid mechanics cohesive and conatct mechanics * model * * * @section LICENSE * * Copyright (©) 2018-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "coupler_solid_cohesive_contact.hh" /* -------------------------------------------------------------------------- */ namespace akantu { template <> CouplerSolidContactTemplate:: CouplerSolidContactTemplate(Mesh & mesh, Int dim, const ID & id, - std::shared_ptr dof_manager, - ModelType model_type) - : Model(mesh, model_type, dof_manager, dim, id) { + std::shared_ptr dof_manager) + : Model(mesh, ModelType::_coupler_solid_cohesive_contact, dof_manager, dim, + id) { this->mesh.registerDumper("coupler_solid_cohesive_contact", id, true); this->mesh.addDumpMeshToDumper("coupler_solid_cohesive_contact", mesh, Model::spatial_dimension, _not_ghost, _ek_cohesive); this->registerDataAccessor(*this); solid = std::make_unique( mesh, Model::spatial_dimension, "solid_mechanics_model_cohesive", this->dof_manager); contact = std::make_unique(mesh.getMeshFacets(), Model::spatial_dimension, "contact_mechanics_model"); } /* -------------------------------------------------------------------------- */ template <> void CouplerSolidContactTemplate::initFullImpl( const ModelOptions & options) { Model::initFullImpl(options); const auto & cscc_options = aka::as_type(options); solid->initFull(_analysis_method = cscc_options.analysis_method, _is_extrinsic = cscc_options.is_extrinsic); contact->initFull(_analysis_method = cscc_options.analysis_method); } } // namespace akantu diff --git a/src/model/model_couplers/coupler_solid_contact.cc b/src/model/model_couplers/coupler_solid_contact.cc index 70c504412..ffed6b62b 100644 --- a/src/model/model_couplers/coupler_solid_contact.cc +++ b/src/model/model_couplers/coupler_solid_contact.cc @@ -1,69 +1,69 @@ /** * @file coupler_solid_contact.cc * * @author Mohit Pundir * @author Nicolas Richart * * @date creation: Mon Jan 21 2019 * @date last modification: Wed Jun 23 2021 * * @brief class for coupling of solid mechanics and conatct mechanics * model * * * @section LICENSE * * Copyright (©) 2018-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "coupler_solid_contact.hh" /* -------------------------------------------------------------------------- */ namespace akantu { template <> CouplerSolidContactTemplate::CouplerSolidContactTemplate( Mesh & mesh, Int dim, const ID & id, - std::shared_ptr dof_manager, ModelType model_type) - : Model(mesh, model_type, dof_manager, dim, id) { + std::shared_ptr dof_manager) + : Model(mesh, ModelType::_coupler_solid_contact, dof_manager, dim, id) { this->mesh.registerDumper("coupler_solid_contact", id, true); this->mesh.addDumpMeshToDumper("coupler_solid_contact", mesh, Model::spatial_dimension, _not_ghost, _ek_regular); this->registerDataAccessor(*this); solid = std::make_unique(mesh, Model::spatial_dimension, "solid_mechanics_model", this->dof_manager); contact = std::make_unique( mesh, Model::spatial_dimension, "contact_mechanics_model", this->dof_manager); } /* -------------------------------------------------------------------------- */ template <> void CouplerSolidContactTemplate::initFullImpl( const ModelOptions & options) { Model::initFullImpl(options); solid->initFull(_analysis_method = this->method); contact->initFull(_analysis_method = this->method); } } // namespace akantu diff --git a/src/model/model_couplers/coupler_solid_contact.hh b/src/model/model_couplers/coupler_solid_contact.hh index e6059dc7b..7e92c480c 100644 --- a/src/model/model_couplers/coupler_solid_contact.hh +++ b/src/model/model_couplers/coupler_solid_contact.hh @@ -1,280 +1,276 @@ /** * @file coupler_solid_contact.hh * * @author Mohit Pundir * @author Nicolas Richart * * @date creation: Fri Jun 18 2010 * @date last modification: Sat Jun 26 2021 * * @brief class for coupling of solid mechanics and conatct mechanics * model in explicit * * * @section LICENSE * * Copyright (©) 2010-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "contact_mechanics_model.hh" #include "solid_mechanics_model.hh" #if defined(AKANTU_COHESIVE_ELEMENT) #include "solid_mechanics_model_cohesive.hh" #endif /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_COUPLER_SOLID_CONTACT_HH__ #define __AKANTU_COUPLER_SOLID_CONTACT_HH__ /* ------------------------------------------------------------------------ */ /* Coupling : Solid Mechanics / Contact Mechanics */ /* ------------------------------------------------------------------------ */ namespace akantu { /* -------------------------------------------------------------------------- */ template class CouplerSolidContactTemplate : public Model, public DataAccessor, public DataAccessor { static_assert( std::is_base_of::value, "SolidMechanicsModelType should be derived from SolidMechanicsModel"); /* ------------------------------------------------------------------------ */ /* Constructor/Destructor */ /* ------------------------------------------------------------------------ */ public: CouplerSolidContactTemplate( Mesh & mesh, Int dim = _all_dimensions, const ID & id = "coupler_solid_contact", - std::shared_ptr dof_manager = nullptr, - ModelType model_type = std::is_same::value - ? ModelType::_coupler_solid_cohesive_contact - : ModelType::_coupler_solid_contact); + std::shared_ptr dof_manager = nullptr); ~CouplerSolidContactTemplate() override; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ protected: /// initialize completely the model void initFullImpl(const ModelOptions & options) override; /// get some default values for derived classes std::tuple getDefaultSolverID(const AnalysisMethod & method) override; /* ------------------------------------------------------------------------ */ /* Solver Interface */ /* ------------------------------------------------------------------------ */ public: /// assembles the contact stiffness matrix virtual void assembleStiffnessMatrix(); /// assembles the contant internal forces virtual void assembleInternalForces(); #if defined(AKANTU_COHESIVE_ELEMENT) template ::value> * = nullptr> UInt checkCohesiveStress() { return solid->checkCohesiveStress(); } #endif template inline void applyBC(const FunctorType & func) { solid->applyBC(func); } template inline void applyBC(const FunctorType & func, const std::string & group_name) { solid->applyBC(func, group_name); } template inline void applyBC(const FunctorType & func, const ElementGroup & element_group) { solid->applyBC(func, element_group); } protected: /// callback for the solver, this adds f_{ext} - f_{int} to the residual void assembleResidual() override; /// callback for the solver, this adds f_{ext} or f_{int} to the residual void assembleResidual(const ID & residual_part) override; bool canSplitResidual() const override { return true; } /// get the type of matrix needed MatrixType getMatrixType(const ID & matrix_id) const override; /// callback for the solver, this assembles different matrices void assembleMatrix(const ID & matrix_id) override; /// callback for the solver, this assembles the stiffness matrix void assembleLumpedMatrix(const ID & matrix_id) override; /// callback for the solver, this is called at beginning of solve void predictor() override; /// callback for the solver, this is called at end of solve void corrector() override; /// callback for the solver, this is called at beginning of solve void beforeSolveStep() override; /// callback for the solver, this is called at end of solve void afterSolveStep(bool converged = true) override; /// callback for the model to instantiate the matricess when needed void initSolver(TimeStepSolverType time_step_solver_type, NonLinearSolverType non_linear_solver_type) override; /* ------------------------------------------------------------------------ */ /* Mass matrix for solid mechanics model */ /* ------------------------------------------------------------------------ */ public: /// assemble the lumped mass matrix void assembleMassLumped(); /// assemble the mass matrix for consistent mass resolutions void assembleMass(); protected: /// assemble the lumped mass matrix for local and ghost elements void assembleMassLumped(GhostType ghost_type); /// assemble the mass matrix for either _ghost or _not_ghost elements void assembleMass(GhostType ghost_type); protected: /* ------------------------------------------------------------------------ */ TimeStepSolverType getDefaultSolverType() const override; /* ------------------------------------------------------------------------ */ ModelSolverOptions getDefaultSolverOptions(const TimeStepSolverType & type) const override; public: bool isDefaultSolverExplicit() { return method == _explicit_lumped_mass; } /* ------------------------------------------------------------------------ */ public: // DataAccessor Int getNbData(const Array & /*elements*/, const SynchronizationTag & /*tag*/) const override { return 0; } void packData(CommunicationBuffer & /*buffer*/, const Array & /*elements*/, const SynchronizationTag & /*tag*/) const override {} void unpackData(CommunicationBuffer & /*buffer*/, const Array & /*elements*/, const SynchronizationTag & /*tag*/) override {} // DataAccessor nodes Int getNbData(const Array & /*nodes*/, const SynchronizationTag & /*tag*/) const override { return 0; } void packData(CommunicationBuffer & /*buffer*/, const Array & /*nodes*/, const SynchronizationTag & /*tag*/) const override {} void unpackData(CommunicationBuffer & /*buffer*/, const Array & /*nodes*/, const SynchronizationTag & /*tag*/) override {} /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: /// get the solid mechanics model #if defined(AKANTU_COHESIVE_ELEMENT) template ::value> * = nullptr> SolidMechanicsModelCohesive & getSolidMechanicsModelCohesive() { return *solid; } #endif template ::value> * = nullptr> SolidMechanicsModelType & getSolidMechanicsModel() { return *solid; } /// get the contact mechanics model AKANTU_GET_MACRO(ContactMechanicsModel, *contact, ContactMechanicsModel &) /* ------------------------------------------------------------------------ */ /* Dumpable interface */ /* ------------------------------------------------------------------------ */ public: std::shared_ptr createNodalFieldReal(const std::string & field_name, const std::string & group_name, bool padding_flag) override; std::shared_ptr createNodalFieldInt(const std::string & field_name, const std::string & group_name, bool padding_flag) override; std::shared_ptr createNodalFieldBool(const std::string & field_name, const std::string & group_name, bool padding_flag) override; std::shared_ptr createElementalField(const std::string & field_name, const std::string & group_name, bool padding_flag, Int spatial_dimension, ElementKind kind) override; void dump(const std::string & dumper_name) override; void dump(const std::string & dumper_name, Int step) override; void dump(const std::string & dumper_name, Real time, Int step) override; void dump() override; void dump(Int step) override; void dump(Real time, Int step) override; /* ------------------------------------------------------------------------ */ /* Members */ /* ------------------------------------------------------------------------ */ private: /// solid mechanics model std::unique_ptr solid; /// contact mechanics model std::unique_ptr contact; UInt step; }; using CouplerSolidContact = CouplerSolidContactTemplate; } // namespace akantu #include "coupler_solid_contact_tmpl.hh" #endif /* __COUPLER_SOLID_CONTACT_HH__ */ diff --git a/src/model/model_options.hh b/src/model/model_options.hh index 77128acc7..dee905a52 100644 --- a/src/model/model_options.hh +++ b/src/model/model_options.hh @@ -1,205 +1,207 @@ /** * @file model_options.hh * * @author Lucas Frerot * @author Mohit Pundir * @author Nicolas Richart * * @date creation: Mon Dec 04 2017 * @date last modification: Fri Jun 12 2020 * * @brief Description of the model options * * * @section LICENSE * * Copyright (©) 2016-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_common.hh" #include "aka_named_argument.hh" /* -------------------------------------------------------------------------- */ #ifndef AKANTU_MODEL_OPTIONS_HH_ #define AKANTU_MODEL_OPTIONS_HH_ namespace akantu { namespace { DECLARE_NAMED_ARGUMENT(analysis_method); } struct ModelOptions { explicit ModelOptions(AnalysisMethod analysis_method = _static) : analysis_method(analysis_method) {} template ModelOptions(use_named_args_t /*unused*/, pack &&... _pack) : ModelOptions(OPTIONAL_NAMED_ARG(analysis_method, _static)) {} virtual ~ModelOptions() = default; AnalysisMethod analysis_method; }; #ifdef AKANTU_SOLID_MECHANICS /* -------------------------------------------------------------------------- */ struct SolidMechanicsModelOptions : public ModelOptions { explicit SolidMechanicsModelOptions( AnalysisMethod analysis_method = _explicit_lumped_mass) : ModelOptions(analysis_method) {} template SolidMechanicsModelOptions(use_named_args_t /*unused*/, pack &&... _pack) : SolidMechanicsModelOptions( OPTIONAL_NAMED_ARG(analysis_method, _explicit_lumped_mass)) {} }; #endif /* -------------------------------------------------------------------------- */ #ifdef AKANTU_COHESIVE_ELEMENT namespace { DECLARE_NAMED_ARGUMENT(is_extrinsic); } /* -------------------------------------------------------------------------- */ struct SolidMechanicsModelCohesiveOptions : public SolidMechanicsModelOptions { SolidMechanicsModelCohesiveOptions( AnalysisMethod analysis_method = _explicit_lumped_mass, bool extrinsic = false) : SolidMechanicsModelOptions(analysis_method), is_extrinsic(extrinsic) {} template SolidMechanicsModelCohesiveOptions(use_named_args_t /*unused*/, pack &&... _pack) : SolidMechanicsModelCohesiveOptions( OPTIONAL_NAMED_ARG(analysis_method, _explicit_lumped_mass), OPTIONAL_NAMED_ARG(is_extrinsic, false)) {} bool is_extrinsic{false}; }; #endif #ifdef AKANTU_HEAT_TRANSFER /* -------------------------------------------------------------------------- */ struct HeatTransferModelOptions : public ModelOptions { explicit HeatTransferModelOptions( AnalysisMethod analysis_method = _explicit_lumped_mass) : ModelOptions(analysis_method) {} template HeatTransferModelOptions(use_named_args_t /*unused*/, pack &&... _pack) : HeatTransferModelOptions( OPTIONAL_NAMED_ARG(analysis_method, _explicit_lumped_mass)) {} }; #endif #ifdef AKANTU_PHASE_FIELD /* -------------------------------------------------------------------------- */ struct PhaseFieldModelOptions : public ModelOptions { explicit PhaseFieldModelOptions( AnalysisMethod analysis_method = _explicit_lumped_mass) : ModelOptions(analysis_method) {} template PhaseFieldModelOptions(use_named_args_t /*unused*/, pack &&... _pack) : PhaseFieldModelOptions( OPTIONAL_NAMED_ARG(analysis_method, _explicit_lumped_mass)) {} }; #endif /* -------------------------------------------------------------------------- */ #ifdef AKANTU_EMBEDDED namespace { DECLARE_NAMED_ARGUMENT(init_intersections); } /* -------------------------------------------------------------------------- */ struct EmbeddedInterfaceModelOptions : SolidMechanicsModelOptions { /** * @brief Constructor for EmbeddedInterfaceModelOptions * @param analysis_method see SolidMechanicsModelOptions * @param init_intersections compute intersections */ EmbeddedInterfaceModelOptions( AnalysisMethod analysis_method = _explicit_lumped_mass, bool init_intersections = true) : SolidMechanicsModelOptions(analysis_method), has_intersections(init_intersections) {} template EmbeddedInterfaceModelOptions(use_named_args_t /*unused*/, pack &&... _pack) : EmbeddedInterfaceModelOptions( OPTIONAL_NAMED_ARG(analysis_method, _explicit_lumped_mass), OPTIONAL_NAMED_ARG(init_intersections, true)) {} /// Should consider reinforcements bool has_intersections; }; #endif #ifdef AKANTU_CONTACT_MECHANICS /* -------------------------------------------------------------------------- */ struct ContactMechanicsModelOptions : public ModelOptions { explicit ContactMechanicsModelOptions( AnalysisMethod analysis_method = _explicit_lumped_mass) : ModelOptions(analysis_method) {} template ContactMechanicsModelOptions(use_named_args_t /*unused*/, pack &&... _pack) : ContactMechanicsModelOptions( OPTIONAL_NAMED_ARG(analysis_method, _explicit_lumped_mass)) {} }; #endif #ifdef AKANTU_MODEL_COUPLERS /* -------------------------------------------------------------------------- */ struct CouplerSolidContactOptions : public ModelOptions { explicit CouplerSolidContactOptions( AnalysisMethod analysis_method = _explicit_lumped_mass) : ModelOptions(analysis_method) {} template CouplerSolidContactOptions(use_named_args_t /*unused*/, pack &&... _pack) : CouplerSolidContactOptions( OPTIONAL_NAMED_ARG(analysis_method, _explicit_lumped_mass)) {} }; +#ifdef AKANTU_COHESIVE_ELEMENT /* -------------------------------------------------------------------------- */ struct CouplerSolidCohesiveContactOptions : public ModelOptions { CouplerSolidCohesiveContactOptions( AnalysisMethod analysis_method = _explicit_lumped_mass, bool extrinsic = false) : ModelOptions(analysis_method), is_extrinsic(extrinsic) {} template CouplerSolidCohesiveContactOptions(use_named_args_t /*unused*/, pack &&... _pack) : CouplerSolidCohesiveContactOptions( OPTIONAL_NAMED_ARG(analysis_method, _explicit_lumped_mass), OPTIONAL_NAMED_ARG(is_extrinsic, false)) {} bool is_extrinsic{false}; }; +#endif #endif } // namespace akantu #endif /* AKANTU_MODEL_OPTIONS_HH_ */ diff --git a/src/model/solid_mechanics/material.hh b/src/model/solid_mechanics/material.hh index b4ecd11de..fe9f8a0d6 100644 --- a/src/model/solid_mechanics/material.hh +++ b/src/model/solid_mechanics/material.hh @@ -1,812 +1,819 @@ /** * @file material.hh * * @author Fabian Barras * @author Aurelia Isabel Cuba Ramos * @author Lucas Frerot * @author Enrico Milanese * @author Daniel Pino Muñoz * @author Nicolas Richart * @author Marco Vocialta * * @date creation: Fri Jun 18 2010 * @date last modification: Fri Apr 09 2021 * * @brief Mother class for all materials * * * @section LICENSE * * Copyright (©) 2010-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_factory.hh" #include "aka_voigthelper.hh" #include "data_accessor.hh" #include "integration_point.hh" #include "parsable.hh" #include "parser.hh" /* -------------------------------------------------------------------------- */ #include "internal_field.hh" #include "random_internal_field.hh" /* -------------------------------------------------------------------------- */ #include "mesh_events.hh" #include "solid_mechanics_model_event_handler.hh" /* -------------------------------------------------------------------------- */ #ifndef AKANTU_MATERIAL_HH_ #define AKANTU_MATERIAL_HH_ /* -------------------------------------------------------------------------- */ namespace akantu { class Model; class SolidMechanicsModel; class Material; } // namespace akantu namespace akantu { using MaterialFactory = Factory; /** * Interface of all materials * Prerequisites for a new material * - inherit from this class * - implement the following methods: * \code * virtual Real getStableTimeStep(Real h, const Element & element = * ElementNull); * * virtual void computeStress(ElementType el_type, * GhostType ghost_type = _not_ghost); * * virtual void computeTangentStiffness(ElementType el_type, * Array & tangent_matrix, * GhostType ghost_type = _not_ghost); * \endcode * */ class Material : public DataAccessor, public Parsable, public MeshEventHandler, protected SolidMechanicsModelEventHandler { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: Material(const Material & mat) = delete; Material & operator=(const Material & mat) = delete; /// Initialize material with defaults Material(SolidMechanicsModel & model, const ID & id = ""); /// Initialize material with custom mesh & fe_engine Material(SolidMechanicsModel & model, Int dim, const Mesh & mesh, FEEngine & fe_engine, const ID & id = ""); /// Destructor ~Material() override; protected: void initialize(); /* ------------------------------------------------------------------------ */ /* Function that materials can/should reimplement */ /* ------------------------------------------------------------------------ */ protected: /// constitutive law virtual void computeStress(ElementType /* el_type */, GhostType /* ghost_type */ = _not_ghost) { AKANTU_TO_IMPLEMENT(); } /// compute the tangent stiffness matrix virtual void computeTangentModuli(ElementType /*el_type*/, Array & /*tangent_matrix*/, GhostType /*ghost_type*/ = _not_ghost) { AKANTU_TO_IMPLEMENT(); } /// compute the potential energy virtual void computePotentialEnergy(ElementType el_type); /// compute the potential energy for an element [[gnu::deprecated("Use the interface with an Element")]] virtual void computePotentialEnergyByElement(ElementType /*type*/, Int /*index*/, Vector & /*epot_on_quad_points*/) { AKANTU_TO_IMPLEMENT(); } virtual void computePotentialEnergyByElement(const Element & /*element*/, Vector & /*epot_on_quad_points*/) { AKANTU_TO_IMPLEMENT(); } virtual void updateEnergies(ElementType /*el_type*/) {} virtual void updateEnergiesAfterDamage(ElementType /*el_type*/) {} /// set the material to steady state (to be implemented for materials that /// need it) virtual void setToSteadyState(ElementType /*el_type*/, GhostType /*ghost_type*/ = _not_ghost) {} /// function called to update the internal parameters when the modifiable /// parameters are modified virtual void updateInternalParameters() {} public: /// extrapolate internal values virtual void extrapolateInternal(const ID & id, const Element & element, const Matrix & points, Matrix & extrapolated); /// compute the p-wave speed in the material virtual Real getPushWaveSpeed(const Element & /*element*/) const { AKANTU_TO_IMPLEMENT(); } /// compute the s-wave speed in the material virtual Real getShearWaveSpeed(const Element & /*element*/) const { AKANTU_TO_IMPLEMENT(); } /// get a material celerity to compute the stable time step (default: is the /// push wave speed) virtual Real getCelerity(const Element & element) const { return getPushWaveSpeed(element); } /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ public: template void registerInternal(InternalField & /*vect*/) { AKANTU_TO_IMPLEMENT(); } template void unregisterInternal(InternalField & /*vect*/) { AKANTU_TO_IMPLEMENT(); } /// initialize the material computed parameter virtual void initMaterial(); /// compute the residual for this material // virtual void updateResidual(GhostType ghost_type = _not_ghost); /// assemble the residual for this material virtual void assembleInternalForces(GhostType ghost_type); /// save the internals in the previous_stress if needed virtual void savePreviousState(); /// restore the internals from previous_stress if needed virtual void restorePreviousState(); /// compute the stresses for this material virtual void computeAllStresses(GhostType ghost_type = _not_ghost); // virtual void // computeAllStressesFromTangentModuli(GhostType ghost_type = _not_ghost); virtual void computeAllCauchyStresses(GhostType ghost_type = _not_ghost); /// set material to steady state void setToSteadyState(GhostType ghost_type = _not_ghost); /// compute the stiffness matrix virtual void assembleStiffnessMatrix(GhostType ghost_type); /// add an element to the local mesh filter inline auto addElement(ElementType type, Int element, GhostType ghost_type); inline auto addElement(const Element & element); /// add many elements at once void addElements(const Array & elements_to_add); /// remove many element at once void removeElements(const Array & elements_to_remove); /// function to print the contain of the class void printself(std::ostream & stream, int indent = 0) const override; /** * interpolate stress on given positions for each element by means * of a geometrical interpolation on quadrature points */ void interpolateStress(ElementTypeMapArray & result, GhostType ghost_type = _not_ghost); /** * interpolate stress on given positions for each element by means * of a geometrical interpolation on quadrature points and store the * results per facet */ void interpolateStressOnFacets(ElementTypeMapArray & result, ElementTypeMapArray & by_elem_result, GhostType ghost_type = _not_ghost); /** * function to initialize the elemental field interpolation * function by inverting the quadrature points' coordinates */ void initElementalFieldInterpolation( const ElementTypeMapArray & interpolation_points_coordinates); /* ------------------------------------------------------------------------ */ /* Common part */ /* ------------------------------------------------------------------------ */ protected: /* ------------------------------------------------------------------------ */ constexpr static inline Int getTangentStiffnessVoigtSize(Int dim) { return (dim * (dim - 1) / 2 + dim); } template constexpr static inline Int getTangentStiffnessVoigtSize() { return getTangentStiffnessVoigtSize(dim); } /// compute the potential energy by element void computePotentialEnergyByElements(); /// resize the intenals arrays virtual void resizeInternals(); template decltype(auto) getArguments(ElementType el_type, GhostType ghost_type) { auto && args = zip(tuple::get<"grad_u"_h>() = make_view(this->gradu(el_type, ghost_type)), tuple::get<"previous_sigma"_h>() = make_view(this->stress.previous(el_type, ghost_type)), tuple::get<"previous_grad_u"_h>() = make_view(this->gradu.previous(el_type, ghost_type))); if (not finite_deformation) { return zip_append(std::forward(args), tuple::get<"sigma"_h>() = make_view( this->stress(el_type, ghost_type))); } return zip_append(std::forward(args), tuple::get<"sigma"_h>() = make_view( this->piola_kirchhoff_2(el_type, ghost_type))); } template decltype(auto) getArgumentsTangent(Array & tangent_matrix, ElementType el_type, GhostType ghost_type) { constexpr auto tangent_size = Material::getTangentStiffnessVoigtSize(dim); return zip(tuple::get<"tangent_moduli"_h>() = make_view(tangent_matrix), tuple::get<"grad_u"_h>() = make_view(this->gradu(el_type, ghost_type))); } /* ------------------------------------------------------------------------ */ /* Finite deformation functions */ /* This functions area implementing what is described in the paper of Bathe */ /* et al, in IJNME, Finite Element Formulations For Large Deformation */ /* Dynamic Analysis, Vol 9, 353-386, 1975 */ /* ------------------------------------------------------------------------ */ protected: /// assemble the residual template void assembleInternalForces(GhostType ghost_type); template void computeAllStressesFromTangentModuli(ElementType type, GhostType ghost_type); template void assembleStiffnessMatrix(ElementType type, GhostType ghost_type); /// assembling in finite deformation template void assembleStiffnessMatrixNL(ElementType type, GhostType ghost_type); template void assembleStiffnessMatrixL2(ElementType type, GhostType ghost_type); /* ------------------------------------------------------------------------ */ /* Conversion functions */ /* ------------------------------------------------------------------------ */ public: /// Size of the Stress matrix for the case of finite deformation see: Bathe et /// al, IJNME, Vol 9, 353-386, 1975 static constexpr inline Int getCauchyStressMatrixSize(Int dim); /// Sets the stress matrix according to Bathe et al, IJNME, Vol 9, 353-386, /// 1975 template static constexpr inline void setCauchyStressMatrix(const Eigen::MatrixBase & S_t, Eigen::MatrixBase & sigma); /// write the stress tensor in the Voigt notation. template static constexpr inline decltype(auto) stressToVoigt(const Eigen::MatrixBase & stress) { return VoigtHelper::matrixToVoigt(stress); } /// write the strain tensor in the Voigt notation. template static constexpr inline decltype(auto) strainToVoigt(const Eigen::MatrixBase & strain) { return VoigtHelper::matrixToVoigtWithFactors(strain); } /// write a voigt vector to stress template static constexpr inline void voigtToStress(const Eigen::MatrixBase & voigt, Eigen::MatrixBase & stress) { VoigtHelper::voigtToMatrix(voigt, stress); } /// Computation of Cauchy stress tensor in the case of finite deformation from /// the 2nd Piola-Kirchhoff for a given element type template void StoCauchy(ElementType el_type, GhostType ghost_type = _not_ghost); /// Computation the Cauchy stress the 2nd Piola-Kirchhoff and the deformation /// gradient template static constexpr inline void StoCauchy(const Eigen::MatrixBase & F, const Eigen::MatrixBase & S, Eigen::MatrixBase & sigma, const Real & C33 = 1.0); template static constexpr inline decltype(auto) StoCauchy(const Eigen::MatrixBase & F, const Eigen::MatrixBase & S, const Real & C33 = 1.0); template static constexpr inline void gradUToF(const Eigen::MatrixBase & grad_u, Eigen::MatrixBase & F); template static constexpr inline decltype(auto) gradUToF(const Eigen::MatrixBase & grad_u); template static constexpr inline void rightCauchy(const Eigen::MatrixBase & F, Eigen::MatrixBase & C); template static constexpr inline decltype(auto) rightCauchy(const Eigen::MatrixBase & F); template static constexpr inline void leftCauchy(const Eigen::MatrixBase & F, Eigen::MatrixBase & B); template static constexpr inline decltype(auto) leftCauchy(const Eigen::MatrixBase & F); template static constexpr inline void gradUToEpsilon(const Eigen::MatrixBase & grad_u, Eigen::MatrixBase & epsilon); template static constexpr inline decltype(auto) gradUToEpsilon(const Eigen::MatrixBase & grad_u); template static constexpr inline void gradUToE(const Eigen::MatrixBase & grad_u, Eigen::MatrixBase & E); template static constexpr inline decltype(auto) gradUToE(const Eigen::MatrixBase & grad_u); template static constexpr inline void computeDeviatoric(const Eigen::MatrixBase & sigma, Eigen::MatrixBase & sigma_dev) { sigma_dev = sigma - Matrix::Identity() * sigma.trace() / dim; } template static constexpr inline decltype(auto) computeDeviatoric(const Eigen::MatrixBase & sigma) { Matrix sigma_dev; Material::computeDeviatoric(sigma, sigma_dev); return sigma_dev; } template static inline Real stressToVonMises(const Eigen::MatrixBase & stress); protected: /// converts global element to local element inline Element convertToLocalElement(const Element & global_element) const; /// converts local element to global element inline Element convertToGlobalElement(const Element & local_element) const; /// converts global quadrature point to local quadrature point inline IntegrationPoint convertToLocalPoint(const IntegrationPoint & global_point) const; /// converts local quadrature point to global quadrature point inline IntegrationPoint convertToGlobalPoint(const IntegrationPoint & local_point) const; /* ------------------------------------------------------------------------ */ /* DataAccessor inherited members */ /* ------------------------------------------------------------------------ */ public: inline Int getNbData(const Array & elements, const SynchronizationTag & tag) const override; inline void packData(CommunicationBuffer & buffer, const Array & elements, const SynchronizationTag & tag) const override; inline void unpackData(CommunicationBuffer & buffer, const Array & elements, const SynchronizationTag & tag) override; template inline void packElementDataHelper(const ElementTypeMapArray & data_to_pack, CommunicationBuffer & buffer, const Array & elements, const ID & fem_id = ID()) const; template inline void unpackElementDataHelper(ElementTypeMapArray & data_to_unpack, CommunicationBuffer & buffer, const Array & elements, const ID & fem_id = ID()); /* ------------------------------------------------------------------------ */ /* MeshEventHandler inherited members */ /* ------------------------------------------------------------------------ */ public: /* ------------------------------------------------------------------------ */ void onNodesAdded(const Array &, const NewNodesEvent &) override{}; void onNodesRemoved(const Array &, const Array &, const RemovedNodesEvent &) override{}; void onElementsAdded(const Array & element_list, const NewElementsEvent & event) override; void onElementsRemoved(const Array & element_list, const ElementTypeMapArray & new_numbering, const RemovedElementsEvent & event) override; void onElementsChanged(const Array &, const Array &, const ElementTypeMapArray &, const ChangedElementsEvent &) override{}; /* ------------------------------------------------------------------------ */ /* SolidMechanicsModelEventHandler inherited members */ /* ------------------------------------------------------------------------ */ public: virtual void beforeSolveStep(); virtual void afterSolveStep(bool converged = true); void onDamageIteration() override; void onDamageUpdate() override; void onDump() override; /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: AKANTU_GET_MACRO(Name, name, const std::string &); + AKANTU_SET_MACRO(Name, name, const std::string &); AKANTU_GET_MACRO(Model, model, const SolidMechanicsModel &) AKANTU_GET_MACRO(ID, id, const ID &); AKANTU_GET_MACRO(Rho, rho, Real); AKANTU_SET_MACRO(Rho, rho, Real); AKANTU_GET_MACRO(SpatialDimension, spatial_dimension, Int); /// return the potential energy for the subset of elements contained by the /// material Real getPotentialEnergy(); /// return the potential energy for the provided element Real getPotentialEnergy(const Element & element); [[gnu::deprecated("Use the interface with an Element")]] Real getPotentialEnergy(ElementType type, Int index); /// return the energy (identified by id) for the subset of elements contained /// by the material virtual Real getEnergy(const std::string & type); /// return the energy (identified by id) for the provided element virtual Real getEnergy(const std::string & energy_id, const Element & element); [[gnu::deprecated("Use the interface with an Element")]] virtual Real getEnergy(const std::string & energy_id, ElementType type, Idx index) final { return getEnergy(energy_id, {type, index, _not_ghost}); } AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(ElementFilter, element_filter, Idx); AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(GradU, gradu, Real); AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(Stress, stress, Real); AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(PotentialEnergy, potential_energy, Real); AKANTU_GET_MACRO(GradU, gradu, const ElementTypeMapArray &); AKANTU_GET_MACRO(Stress, stress, const ElementTypeMapArray &); AKANTU_GET_MACRO(ElementFilter, element_filter, const ElementTypeMapArray &); AKANTU_GET_MACRO(FEEngine, fem, FEEngine &); bool isNonLocal() const { return is_non_local; } template const Array & getArray(const ID & id, ElementType type, GhostType ghost_type = _not_ghost) const; template Array & getArray(const ID & id, ElementType type, GhostType ghost_type = _not_ghost); template const InternalField & getInternal(const ID & id) const; template InternalField & getInternal(const ID & id); template inline bool isInternal(const ID & id, ElementKind element_kind) const; template ElementTypeMap getInternalDataPerElem(const ID & id, ElementKind element_kind) const; bool isFiniteDeformation() const { return finite_deformation; } bool isInelasticDeformation() const { return inelastic_deformation; } template inline void setParam(const ID & param, T value); inline const Parameter & getParam(const ID & param) const; template void flattenInternal(const std::string & field_id, ElementTypeMapArray & internal_flat, GhostType ghost_type = _not_ghost, ElementKind element_kind = _ek_not_defined) const; + template + void inflateInternal(const std::string & field_id, + const ElementTypeMapArray & field, + GhostType ghost_type = _not_ghost, + ElementKind element_kind = _ek_not_defined); + /// apply a constant eigengrad_u everywhere in the material virtual void applyEigenGradU(const Matrix & prescribed_eigen_grad_u, GhostType /*ghost_type*/ = _not_ghost); bool hasMatrixChanged(const ID & id) { if (id == "K") { return hasStiffnessMatrixChanged() or finite_deformation; } return true; } MatrixType getMatrixType(const ID & id) { if (id == "K") { return getTangentType(); } if (id == "M") { return _symmetric; } return _mt_not_defined; } /// specify if the matrix need to be recomputed for this material virtual bool hasStiffnessMatrixChanged() { return true; } /// specify the type of matrix, if not overloaded the material is not valid /// for static or implicit computations virtual MatrixType getTangentType() { return _mt_not_defined; } /// static method to reteive the material factory static MaterialFactory & getFactory(); protected: bool isInit() const { return is_init; } /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ protected: /// boolean to know if the material has been initialized bool is_init{false}; std::map *> internal_vectors_real; std::map *> internal_vectors_int; std::map *> internal_vectors_bool; protected: ID id; /// Link to the fem object in the model FEEngine & fem; /// Finite deformation bool finite_deformation{false}; /// Finite deformation bool inelastic_deformation{false}; /// material name std::string name; /// The model to witch the material belong SolidMechanicsModel & model; /// density : rho Real rho{0.}; /// spatial dimension Int spatial_dimension; /// list of element handled by the material ElementTypeMapArray element_filter; /// stresses arrays ordered by element types InternalField stress; /// eigengrad_u arrays ordered by element types InternalField eigengradu; /// grad_u arrays ordered by element types InternalField gradu; /// Green Lagrange strain (Finite deformation) InternalField green_strain; /// Second Piola-Kirchhoff stress tensor arrays ordered by element types /// (Finite deformation) InternalField piola_kirchhoff_2; /// potential energy by element InternalField potential_energy; /// tell if using in non local mode or not bool is_non_local{false}; /// tell if the material need the previous stress state bool use_previous_stress{false}; /// tell if the material need the previous strain state bool use_previous_gradu{false}; /// elemental field interpolation coordinates InternalField interpolation_inverse_coordinates; /// elemental field interpolation points InternalField interpolation_points_matrices; /// vector that contains the names of all the internals that need to /// be transferred when material interfaces move std::vector internals_to_transfer; private: /// eigen_grad_u for the parser Matrix eigen_grad_u; }; /// standard output stream operator inline std::ostream & operator<<(std::ostream & stream, const Material & _this) { _this.printself(stream); return stream; } } // namespace akantu #include "material_inline_impl.hh" #include "internal_field_tmpl.hh" #include "random_internal_field_tmpl.hh" /* -------------------------------------------------------------------------- */ /* Auto loop */ /* -------------------------------------------------------------------------- */ /// This can be used to automatically write the loop on quadrature points in /// functions such as computeStress. This macro in addition to write the loop /// provides two tensors (matrices) sigma and grad_u #define MATERIAL_STRESS_QUADRATURE_POINT_LOOP_BEGIN(el_type, ghost_type) \ auto && grad_u_view = \ make_view(this->gradu(el_type, ghost_type), this->spatial_dimension, \ this->spatial_dimension); \ \ auto stress_view = \ make_view(this->stress(el_type, ghost_type), this->spatial_dimension, \ this->spatial_dimension); \ \ if (this->isFiniteDeformation()) { \ stress_view = make_view(this->piola_kirchhoff_2(el_type, ghost_type), \ this->spatial_dimension, this->spatial_dimension); \ } \ \ for (auto && data : zip(grad_u_view, stress_view)) { \ [[gnu::unused]] auto && grad_u = std::get<0>(data); \ [[gnu::unused]] auto && sigma = std::get<1>(data) #define MATERIAL_STRESS_QUADRATURE_POINT_LOOP_END } /// This can be used to automatically write the loop on quadrature points in /// functions such as computeTangentModuli. This macro in addition to write the /// loop provides two tensors (matrices) sigma_tensor, grad_u, and a matrix /// where the elemental tangent moduli should be stored in Voigt Notation #define MATERIAL_TANGENT_QUADRATURE_POINT_LOOP_BEGIN(tangent_mat) \ auto && grad_u_view = \ make_view(this->gradu(el_type, ghost_type), this->spatial_dimension, \ this->spatial_dimension); \ \ auto && stress_view = \ make_view(this->stress(el_type, ghost_type), this->spatial_dimension, \ this->spatial_dimension); \ \ auto tangent_size = \ Material::getTangentStiffnessVoigtSize(this->spatial_dimension); \ \ auto && tangent_view = make_view(tangent_mat, tangent_size, tangent_size); \ \ for (auto && data : zip(grad_u_view, stress_view, tangent_view)) { \ [[gnu::unused]] auto && grad_u = std::get<0>(data); \ [[gnu::unused]] auto && sigma = std::get<1>(data); \ auto && tangent = std::get<2>(data); #define MATERIAL_TANGENT_QUADRATURE_POINT_LOOP_END } /* -------------------------------------------------------------------------- */ #define INSTANTIATE_MATERIAL_ONLY(mat_name) \ template class mat_name<1>; /* NOLINT */ \ template class mat_name<2>; /* NOLINT */ \ template class mat_name<3> /* NOLINT */ #define MATERIAL_DEFAULT_PER_DIM_ALLOCATOR(id, mat_name) \ [](Int dim, const ID &, SolidMechanicsModel & model, \ const ID & id) /* NOLINT */ \ -> std::unique_ptr< \ Material> { /* NOLINT */ \ switch (dim) { \ case 1: \ return std::make_unique>(/* NOLINT */ \ model, id); \ case 2: \ return std::make_unique>(/* NOLINT */ \ model, id); \ case 3: \ return std::make_unique>(/* NOLINT */ \ model, id); \ default: \ AKANTU_EXCEPTION( \ "The dimension " \ << dim \ << "is not a valid dimension for the material " \ << #id); \ } \ } #define INSTANTIATE_MATERIAL(id, mat_name) \ INSTANTIATE_MATERIAL_ONLY(mat_name); \ static bool material_is_alocated_##id = \ MaterialFactory::getInstance().registerAllocator( \ #id, MATERIAL_DEFAULT_PER_DIM_ALLOCATOR(id, mat_name)) #define INSTANTIATE_MATERIAL_NO_INSTATIATION(id, mat_name) \ static bool material_is_alocated_##id [[gnu::unused]] = \ MaterialFactory::getInstance().registerAllocator( \ #id, MATERIAL_DEFAULT_PER_DIM_ALLOCATOR(id, mat_name)) #endif /* AKANTU_MATERIAL_HH_ */ diff --git a/src/model/solid_mechanics/material_inline_impl.hh b/src/model/solid_mechanics/material_inline_impl.hh index 7a666ba4d..0ee2fd8a2 100644 --- a/src/model/solid_mechanics/material_inline_impl.hh +++ b/src/model/solid_mechanics/material_inline_impl.hh @@ -1,557 +1,599 @@ /** * @file material_inline_impl.hh * * @author Fabian Barras * @author Aurelia Isabel Cuba Ramos * @author Lucas Frerot * @author Enrico Milanese * @author Daniel Pino Muñoz * @author Nicolas Richart * @author Marco Vocialta * * @date creation: Tue Jul 27 2010 * @date last modification: Fri Apr 09 2021 * * @brief Implementation of the inline functions of the class material * * * @section LICENSE * * Copyright (©) 2010-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "integration_point.hh" #include "material.hh" #include "solid_mechanics_model.hh" /* -------------------------------------------------------------------------- */ // #ifndef __AKANTU_MATERIAL_INLINE_IMPL_CC__ // #define __AKANTU_MATERIAL_INLINE_IMPL_CC__ namespace akantu { /* -------------------------------------------------------------------------- */ inline auto Material::addElement(ElementType type, Int element, GhostType ghost_type) { - auto &el_filter = this->element_filter(type, ghost_type); + auto & el_filter = this->element_filter(type, ghost_type); el_filter.push_back(element); return el_filter.size() - 1; } /* -------------------------------------------------------------------------- */ -inline auto Material::addElement(const Element &element) { +inline auto Material::addElement(const Element & element) { return this->addElement(element.type, element.element, element.ghost_type); } /* -------------------------------------------------------------------------- */ constexpr inline Int Material::getCauchyStressMatrixSize(Int dim) { return (dim * dim); } /* -------------------------------------------------------------------------- */ template -constexpr inline void Material::gradUToF(const Eigen::MatrixBase &grad_u, - Eigen::MatrixBase &F) { +constexpr inline void Material::gradUToF(const Eigen::MatrixBase & grad_u, + Eigen::MatrixBase & F) { assert(F.size() >= grad_u.size() && grad_u.size() == dim * dim && "The dimension of the tensor F should be greater or " "equal to the dimension of the tensor grad_u."); F.Identity(); F.template block(0, 0) += grad_u; } /* -------------------------------------------------------------------------- */ template constexpr inline decltype(auto) -Material::gradUToF(const Eigen::MatrixBase &grad_u) { +Material::gradUToF(const Eigen::MatrixBase & grad_u) { Matrix F; gradUToF(grad_u, F); return F; } /* -------------------------------------------------------------------------- */ template -constexpr inline void Material::StoCauchy(const Eigen::MatrixBase &F, - const Eigen::MatrixBase &S, - Eigen::MatrixBase &sigma, - const Real &C33) { +constexpr inline void Material::StoCauchy(const Eigen::MatrixBase & F, + const Eigen::MatrixBase & S, + Eigen::MatrixBase & sigma, + const Real & C33) { Real J = F.determinant() * sqrt(C33); Matrix F_S; F_S = F * S; Real constant = J ? 1. / J : 0; sigma = constant * F_S * F.transpose(); } /* -------------------------------------------------------------------------- */ template constexpr inline decltype(auto) -Material::StoCauchy(const Eigen::MatrixBase &F, - const Eigen::MatrixBase &S, const Real &C33) { +Material::StoCauchy(const Eigen::MatrixBase & F, + const Eigen::MatrixBase & S, const Real & C33) { Matrix sigma; Material::StoCauchy(F, S, sigma, C33); return sigma; } /* -------------------------------------------------------------------------- */ template -constexpr inline void Material::rightCauchy(const Eigen::MatrixBase &F, - Eigen::MatrixBase &C) { +constexpr inline void Material::rightCauchy(const Eigen::MatrixBase & F, + Eigen::MatrixBase & C) { C = F.transpose() * F; } /* -------------------------------------------------------------------------- */ template constexpr inline decltype(auto) -Material::rightCauchy(const Eigen::MatrixBase &F) { +Material::rightCauchy(const Eigen::MatrixBase & F) { Matrix C; rightCauchy(F, C); return C; } /* -------------------------------------------------------------------------- */ template -constexpr inline void Material::leftCauchy(const Eigen::MatrixBase &F, - Eigen::MatrixBase &B) { +constexpr inline void Material::leftCauchy(const Eigen::MatrixBase & F, + Eigen::MatrixBase & B) { B = F * F.transpose(); } /* -------------------------------------------------------------------------- */ template constexpr inline decltype(auto) -Material::leftCauchy(const Eigen::MatrixBase &F) { +Material::leftCauchy(const Eigen::MatrixBase & F) { Matrix B; rightCauchy(F, B); return B; } /* -------------------------------------------------------------------------- */ template constexpr inline void -Material::gradUToEpsilon(const Eigen::MatrixBase &grad_u, - Eigen::MatrixBase &epsilon) { +Material::gradUToEpsilon(const Eigen::MatrixBase & grad_u, + Eigen::MatrixBase & epsilon) { epsilon = .5 * (grad_u.transpose() + grad_u); } /* -------------------------------------------------------------------------- */ template inline decltype(auto) constexpr Material::gradUToEpsilon( - const Eigen::MatrixBase &grad_u) { + const Eigen::MatrixBase & grad_u) { Matrix epsilon; Material::gradUToEpsilon(grad_u, epsilon); return epsilon; } /* -------------------------------------------------------------------------- */ template -constexpr inline void Material::gradUToE(const Eigen::MatrixBase &grad_u, - Eigen::MatrixBase &E) { +constexpr inline void Material::gradUToE(const Eigen::MatrixBase & grad_u, + Eigen::MatrixBase & E) { E = (grad_u.transpose() * grad_u + grad_u.transpose() + grad_u) / 2.; } /* -------------------------------------------------------------------------- */ template constexpr inline decltype(auto) -Material::gradUToE(const Eigen::MatrixBase &grad_u) { +Material::gradUToE(const Eigen::MatrixBase & grad_u) { Matrix E; gradUToE(grad_u, E); return E; } /* -------------------------------------------------------------------------- */ template -inline Real Material::stressToVonMises(const Eigen::MatrixBase &stress) { +inline Real Material::stressToVonMises(const Eigen::MatrixBase & stress) { // compute deviatoric stress auto dim = stress.cols(); - auto &&deviatoric_stress = + auto && deviatoric_stress = stress - Matrix::Identity(dim, dim) * stress.trace() / 3.; // return Von Mises stress return std::sqrt(3. * deviatoric_stress.doubleDot(deviatoric_stress) / 2.); } /* -------------------------------------------------------------------------- */ template constexpr inline void -Material::setCauchyStressMatrix(const Eigen::MatrixBase &S_t, - Eigen::MatrixBase &sigma) { +Material::setCauchyStressMatrix(const Eigen::MatrixBase & S_t, + Eigen::MatrixBase & sigma) { sigma.zero(); /// see Finite ekement formulations for large deformation dynamic analysis, /// Bathe et al. IJNME vol 9, 1975, page 364 ^t \f$\tau\f$ for (Int i = 0; i < dim; ++i) { for (Int m = 0; m < dim; ++m) { for (Int n = 0; n < dim; ++n) { sigma(i * dim + m, i * dim + n) = S_t(m, n); } } } } /* -------------------------------------------------------------------------- */ inline Element -Material::convertToLocalElement(const Element &global_element) const { +Material::convertToLocalElement(const Element & global_element) const { auto ge = global_element.element; #ifndef AKANTU_NDEBUG auto model_mat_index = this->model.getMaterialByElement( global_element.type, global_element.ghost_type)(ge); auto mat_index = this->model.getMaterialIndex(this->name); AKANTU_DEBUG_ASSERT(model_mat_index == mat_index, "Conversion of a global element in a local element for " "the wrong material " << this->name << std::endl); #endif auto le = this->model.getMaterialLocalNumbering( global_element.type, global_element.ghost_type)(ge); Element tmp_quad{global_element.type, le, global_element.ghost_type}; return tmp_quad; } /* -------------------------------------------------------------------------- */ inline Element -Material::convertToGlobalElement(const Element &local_element) const { +Material::convertToGlobalElement(const Element & local_element) const { auto le = local_element.element; auto ge = this->element_filter(local_element.type, local_element.ghost_type)(le); Element tmp_quad{local_element.type, ge, local_element.ghost_type}; return tmp_quad; } /* -------------------------------------------------------------------------- */ inline IntegrationPoint -Material::convertToLocalPoint(const IntegrationPoint &global_point) const { - const FEEngine &fem = this->model.getFEEngine(); - auto &&nb_quad = fem.getNbIntegrationPoints(global_point.type); - auto &&el = +Material::convertToLocalPoint(const IntegrationPoint & global_point) const { + const FEEngine & fem = this->model.getFEEngine(); + auto && nb_quad = fem.getNbIntegrationPoints(global_point.type); + auto && el = this->convertToLocalElement(static_cast(global_point)); return IntegrationPoint(el, global_point.num_point, nb_quad); } /* -------------------------------------------------------------------------- */ inline IntegrationPoint -Material::convertToGlobalPoint(const IntegrationPoint &local_point) const { - const FEEngine &fem = this->model.getFEEngine(); +Material::convertToGlobalPoint(const IntegrationPoint & local_point) const { + const FEEngine & fem = this->model.getFEEngine(); auto nb_quad = fem.getNbIntegrationPoints(local_point.type); Element el = this->convertToGlobalElement(static_cast(local_point)); IntegrationPoint tmp_quad(el, local_point.num_point, nb_quad); return tmp_quad; } /* -------------------------------------------------------------------------- */ -inline Int Material::getNbData(const Array &elements, - const SynchronizationTag &tag) const { +inline Int Material::getNbData(const Array & elements, + const SynchronizationTag & tag) const { if (tag == SynchronizationTag::_smm_stress) { return (this->isFiniteDeformation() ? 3 : 1) * spatial_dimension * spatial_dimension * sizeof(Real) * this->getModel().getNbIntegrationPoints(elements); } return 0; } /* -------------------------------------------------------------------------- */ -inline void Material::packData(CommunicationBuffer &buffer, - const Array &elements, - const SynchronizationTag &tag) const { +inline void Material::packData(CommunicationBuffer & buffer, + const Array & elements, + const SynchronizationTag & tag) const { if (tag == SynchronizationTag::_smm_stress) { if (this->isFiniteDeformation()) { packElementDataHelper(piola_kirchhoff_2, buffer, elements); packElementDataHelper(gradu, buffer, elements); } packElementDataHelper(stress, buffer, elements); } } /* -------------------------------------------------------------------------- */ -inline void Material::unpackData(CommunicationBuffer &buffer, - const Array &elements, - const SynchronizationTag &tag) { +inline void Material::unpackData(CommunicationBuffer & buffer, + const Array & elements, + const SynchronizationTag & tag) { if (tag == SynchronizationTag::_smm_stress) { if (this->isFiniteDeformation()) { unpackElementDataHelper(piola_kirchhoff_2, buffer, elements); unpackElementDataHelper(gradu, buffer, elements); } unpackElementDataHelper(stress, buffer, elements); } } /* -------------------------------------------------------------------------- */ -inline const Parameter &Material::getParam(const ID ¶m) const { +inline const Parameter & Material::getParam(const ID & param) const { try { return get(param); } catch (...) { AKANTU_EXCEPTION("No parameter " << param << " in the material " << getID()); } } /* -------------------------------------------------------------------------- */ -template inline void Material::setParam(const ID ¶m, T value) { +template +inline void Material::setParam(const ID & param, T value) { try { set(param, value); } catch (...) { AKANTU_EXCEPTION("No parameter " << param << " in the material " << getID()); } updateInternalParameters(); } /* -------------------------------------------------------------------------- */ template inline void Material::packElementDataHelper( - const ElementTypeMapArray &data_to_pack, CommunicationBuffer &buffer, - const Array &elements, const ID &fem_id) const { + const ElementTypeMapArray & data_to_pack, CommunicationBuffer & buffer, + const Array & elements, const ID & fem_id) const { DataAccessor::packElementalDataHelper(data_to_pack, buffer, elements, true, model.getFEEngine(fem_id)); } /* -------------------------------------------------------------------------- */ template inline void Material::unpackElementDataHelper( - ElementTypeMapArray &data_to_unpack, CommunicationBuffer &buffer, - const Array &elements, const ID &fem_id) { + ElementTypeMapArray & data_to_unpack, CommunicationBuffer & buffer, + const Array & elements, const ID & fem_id) { DataAccessor::unpackElementalDataHelper(data_to_unpack, buffer, elements, true, model.getFEEngine(fem_id)); } /* -------------------------------------------------------------------------- */ template <> -inline void Material::registerInternal(InternalField &vect) { +inline void Material::registerInternal(InternalField & vect) { internal_vectors_real[vect.getID()] = &vect; } template <> -inline void Material::registerInternal(InternalField &vect) { +inline void Material::registerInternal(InternalField & vect) { internal_vectors_int[vect.getID()] = &vect; } template <> -inline void Material::registerInternal(InternalField &vect) { +inline void Material::registerInternal(InternalField & vect) { internal_vectors_bool[vect.getID()] = &vect; } /* -------------------------------------------------------------------------- */ template <> -inline void Material::unregisterInternal(InternalField &vect) { +inline void Material::unregisterInternal(InternalField & vect) { internal_vectors_real.erase(vect.getID()); } template <> -inline void Material::unregisterInternal(InternalField &vect) { +inline void Material::unregisterInternal(InternalField & vect) { internal_vectors_int.erase(vect.getID()); } template <> -inline void Material::unregisterInternal(InternalField &vect) { +inline void Material::unregisterInternal(InternalField & vect) { internal_vectors_bool.erase(vect.getID()); } /* -------------------------------------------------------------------------- */ template inline bool Material::isInternal(const ID & /*id*/, ElementKind /*element_kind*/) const { AKANTU_TO_IMPLEMENT(); } template <> -inline bool Material::isInternal(const ID &id, +inline bool Material::isInternal(const ID & id, ElementKind element_kind) const { auto internal_array = internal_vectors_real.find(this->getID() + ":" + id); return not(internal_array == internal_vectors_real.end() || internal_array->second->getElementKind() != element_kind); } /* -------------------------------------------------------------------------- */ template inline ElementTypeMap -Material::getInternalDataPerElem(const ID &field_id, +Material::getInternalDataPerElem(const ID & field_id, ElementKind element_kind) const { if (!this->template isInternal(field_id, element_kind)) { AKANTU_EXCEPTION("Cannot find internal field " << id << " in material " << this->name); } - const InternalField &internal_field = + const InternalField & internal_field = this->template getInternal(field_id); - const auto &fe_engine = internal_field.getFEEngine(); + const auto & fe_engine = internal_field.getFEEngine(); auto nb_data_per_quad = internal_field.getNbComponent(); ElementTypeMap res; for (auto ghost_type : ghost_types) { - for (auto &&type : internal_field.elementTypes(ghost_type)) { + for (auto && type : internal_field.elementTypes(ghost_type)) { auto nb_quadrature_points = fe_engine.getNbIntegrationPoints(type, ghost_type); res(type, ghost_type) = nb_data_per_quad * nb_quadrature_points; } } return res; } /* -------------------------------------------------------------------------- */ template -void Material::flattenInternal(const std::string &field_id, - ElementTypeMapArray &internal_flat, +void Material::flattenInternal(const std::string & field_id, + ElementTypeMapArray & internal_flat, const GhostType ghost_type, ElementKind element_kind) const { if (!this->template isInternal(field_id, element_kind)) { AKANTU_EXCEPTION("Cannot find internal field " << id << " in material " << this->name); } - const auto &internal_field = this->template getInternal(field_id); + const auto & internal_field = this->template getInternal(field_id); - const auto &fe_engine = internal_field.getFEEngine(); - const auto &mesh = fe_engine.getMesh(); + const auto & fe_engine = internal_field.getFEEngine(); + const auto & mesh = fe_engine.getMesh(); - for (auto &&type : internal_field.filterTypes(ghost_type)) { - const auto &src_vect = internal_field(type, ghost_type); - const auto &filter = internal_field.getFilter(type, ghost_type); + for (auto && type : internal_field.filterTypes(ghost_type)) { + const auto & src_vect = internal_field(type, ghost_type); + const auto & filter = internal_field.getFilter(type, ghost_type); // total number of elements in the corresponding mesh auto nb_element_dst = mesh.getNbElement(type, ghost_type); // number of element in the internal field - auto nb_element_src = filter.size(); + // auto nb_element_src = filter.size(); // number of quadrature points per elem auto nb_quad_per_elem = fe_engine.getNbIntegrationPoints(type); // number of data per quadrature point auto nb_data_per_quad = internal_field.getNbComponent(); if (not internal_flat.exists(type, ghost_type)) { internal_flat.alloc(nb_element_dst * nb_quad_per_elem, nb_data_per_quad, type, ghost_type); } - if (nb_element_src == 0) { - continue; - } - // number of data per element auto nb_data = nb_quad_per_elem * nb_data_per_quad; - auto &dst_vect = internal_flat(type, ghost_type); + auto & dst_vect = internal_flat(type, ghost_type); dst_vect.resize(nb_element_dst * nb_quad_per_elem); auto it_dst = make_view(dst_vect, nb_data).begin(); - for (auto &&data : zip(filter, make_view(src_vect, nb_data))) { + for (auto && data : zip(filter, make_view(src_vect, nb_data))) { it_dst[std::get<0>(data)] = std::get<1>(data); } } } +/* -------------------------------------------------------------------------- */ +template +void Material::inflateInternal(const std::string & field_id, + const ElementTypeMapArray & field, + GhostType ghost_type, ElementKind element_kind) { + if (!this->template isInternal(field_id, element_kind)) { + AKANTU_EXCEPTION("Cannot find internal field " << id << " in material " + << this->name); + } + + InternalField & internal_field = this->template getInternal(field_id); + const FEEngine & fe_engine = internal_field.getFEEngine(); + + for (auto && type : field.elementTypes(ghost_type)) { + if (not internal_field.exists(type, ghost_type)) { + continue; + } + const auto & filter = internal_field.getFilter(type, ghost_type); + + const auto & src_array = field(type, ghost_type); + auto & dest_array = internal_field(type, ghost_type); + + auto nb_quad_per_elem = fe_engine.getNbIntegrationPoints(type); + auto nb_component = src_array.getNbComponent(); + + AKANTU_DEBUG_ASSERT( + field.size() == fe_engine.getMesh().getNbElement(type, ghost_type) * + nb_quad_per_elem, + "The ElementTypeMapArray to inflate is not of the proper size"); + AKANTU_DEBUG_ASSERT( + dest_array.getNbComponent() == nb_component, + "The ElementTypeMapArray has not the proper number of components"); + + auto src = + make_view(field(type, ghost_type), nb_component, nb_quad_per_elem) + .begin(); + for (auto && data : + zip(filter, make_view(dest_array, nb_component, nb_quad_per_elem))) { + std::get<1>(data) = src[std::get<0>(data)]; + } + } +} + /* -------------------------------------------------------------------------- */ template inline const InternalField & Material::getInternal(const ID & /*int_id*/) const { AKANTU_TO_IMPLEMENT(); } /* -------------------------------------------------------------------------- */ template -inline InternalField &Material::getInternal(const ID & /*int_id*/) { +inline InternalField & Material::getInternal(const ID & /*int_id*/) { AKANTU_TO_IMPLEMENT(); } /* -------------------------------------------------------------------------- */ template <> inline const InternalField & -Material::getInternal(const ID &int_id) const { +Material::getInternal(const ID & int_id) const { auto it = internal_vectors_real.find(getID() + ":" + int_id); if (it == internal_vectors_real.end()) { AKANTU_SILENT_EXCEPTION("The material " << name << "(" << getID() << ") does not contain an internal " << int_id << " (" << (getID() + ":" + int_id) << ")"); } return *it->second; } /* -------------------------------------------------------------------------- */ template <> -inline InternalField &Material::getInternal(const ID &int_id) { +inline InternalField & Material::getInternal(const ID & int_id) { auto it = internal_vectors_real.find(getID() + ":" + int_id); if (it == internal_vectors_real.end()) { AKANTU_SILENT_EXCEPTION("The material " << name << "(" << getID() << ") does not contain an internal " << int_id << " (" << (getID() + ":" + int_id) << ")"); } return *it->second; } /* -------------------------------------------------------------------------- */ template <> -inline const InternalField &Material::getInternal(const ID &int_id) const { +inline const InternalField & +Material::getInternal(const ID & int_id) const { auto it = internal_vectors_int.find(getID() + ":" + int_id); if (it == internal_vectors_int.end()) { AKANTU_SILENT_EXCEPTION("The material " << name << "(" << getID() << ") does not contain an internal " << int_id << " (" << (getID() + ":" + int_id) << ")"); } return *it->second; } /* -------------------------------------------------------------------------- */ -template <> inline InternalField &Material::getInternal(const ID &int_id) { +template <> +inline InternalField & Material::getInternal(const ID & int_id) { auto it = internal_vectors_int.find(getID() + ":" + int_id); if (it == internal_vectors_int.end()) { AKANTU_SILENT_EXCEPTION("The material " << name << "(" << getID() << ") does not contain an internal " << int_id << " (" << (getID() + ":" + int_id) << ")"); } return *it->second; } /* -------------------------------------------------------------------------- */ template -inline const Array &Material::getArray(const ID &vect_id, ElementType type, - GhostType ghost_type) const { +inline const Array & Material::getArray(const ID & vect_id, ElementType type, + GhostType ghost_type) const { try { return this->template getInternal(vect_id)(type, ghost_type); - } catch (debug::Exception &e) { + } catch (debug::Exception & e) { AKANTU_SILENT_EXCEPTION("The material " << name << "(" << getID() << ") does not contain a vector " << vect_id << " [" << e << "]"); } } /* -------------------------------------------------------------------------- */ template -inline Array &Material::getArray(const ID &vect_id, ElementType type, - GhostType ghost_type) { +inline Array & Material::getArray(const ID & vect_id, ElementType type, + GhostType ghost_type) { try { return this->template getInternal(vect_id)(type, ghost_type); - } catch (debug::Exception &e) { + } catch (debug::Exception & e) { AKANTU_SILENT_EXCEPTION("The material " << name << "(" << getID() << ") does not contain a vector " << vect_id << " [" << e << "]"); } } } // namespace akantu //#endif /* __AKANTU_MATERIAL_INLINE_IMPL_CC__ */ diff --git a/src/model/solid_mechanics/material_selector.hh b/src/model/solid_mechanics/material_selector.hh index 6954b578b..919e9187a 100644 --- a/src/model/solid_mechanics/material_selector.hh +++ b/src/model/solid_mechanics/material_selector.hh @@ -1,159 +1,159 @@ /** * @file material_selector.hh * * @author Lucas Frerot * @author Nicolas Richart * * @date creation: Wed Nov 13 2013 * @date last modification: Fri Apr 09 2021 * * @brief class describing how to choose a material for a given element * * * @section LICENSE * * Copyright (©) 2014-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "element.hh" #include "mesh.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ #ifndef AKANTU_MATERIAL_SELECTOR_HH_ #define AKANTU_MATERIAL_SELECTOR_HH_ /* -------------------------------------------------------------------------- */ namespace akantu { class SolidMechanicsModel; /** * main class to assign same or different materials for different * elements */ class MaterialSelector { public: MaterialSelector() = default; virtual ~MaterialSelector() = default; virtual inline Int operator()(const Element & element) { if (fallback_selector) { return (*fallback_selector)(element); } return fallback_value; } inline void setFallback(Int f) { fallback_value = f; } inline void setFallback(const std::shared_ptr & fallback_selector) { this->fallback_selector = fallback_selector; } inline std::shared_ptr & getFallbackSelector() { return this->fallback_selector; } inline Int getFallbackValue() const { return this->fallback_value; } protected: Int fallback_value{0}; std::shared_ptr fallback_selector; }; /* -------------------------------------------------------------------------- */ /** * class that assigns the first material to regular elements by default */ class DefaultMaterialSelector : public MaterialSelector { public: explicit DefaultMaterialSelector( const ElementTypeMapArray & material_index) : material_index(material_index) {} Int operator()(const Element & element) override { if (not material_index.exists(element.type, element.ghost_type)) { return MaterialSelector::operator()(element); } const auto & mat_indexes = material_index(element.type, element.ghost_type); if (element.element < mat_indexes.size()) { auto && tmp_mat = mat_indexes(element.element); if (tmp_mat != Int(-1)) { return tmp_mat; } } return MaterialSelector::operator()(element); } private: const ElementTypeMapArray & material_index; }; /* -------------------------------------------------------------------------- */ /** * Use elemental data to assign materials */ template class ElementDataMaterialSelector : public MaterialSelector { public: ElementDataMaterialSelector(const ElementTypeMapArray & element_data, const SolidMechanicsModel & model, Int first_index = 1) : element_data(element_data), model(model), first_index(first_index) {} inline T elementData(const Element & element) { DebugLevel dbl = debug::getDebugLevel(); debug::setDebugLevel(dblError); - T data = element_data(element.type, element.ghost_type)(element.element); + T data = element_data(element); debug::setDebugLevel(dbl); return data; } inline Int operator()(const Element & element) override; protected: /// list of element with the specified data (i.e. tag value) const ElementTypeMapArray & element_data; /// the model that the materials belong const SolidMechanicsModel & model; /// first material index: equal to 1 if none specified Int first_index; }; /* -------------------------------------------------------------------------- */ /** * class to use mesh data information to assign different materials * where name is the tag value: tag_0, tag_1 */ template class MeshDataMaterialSelector : public ElementDataMaterialSelector { public: MeshDataMaterialSelector(const std::string & name, const SolidMechanicsModel & model, Int first_index = 1); }; } // namespace akantu #endif /* AKANTU_MATERIAL_SELECTOR_HH_ */ diff --git a/src/model/solid_mechanics/solid_mechanics_model.cc b/src/model/solid_mechanics/solid_mechanics_model.cc index 7b7cd1020..558a77db6 100644 --- a/src/model/solid_mechanics/solid_mechanics_model.cc +++ b/src/model/solid_mechanics/solid_mechanics_model.cc @@ -1,1242 +1,1250 @@ /** * @file solid_mechanics_model.cc * * @author Ramin Aghababaei * @author Guillaume Anciaux * @author Mauro Corrado * @author Aurelia Isabel Cuba Ramos * @author David Simon Kammer * @author Daniel Pino Muñoz * @author Nicolas Richart * @author Clement Roux * @author Marco Vocialta * * @date creation: Tue Jul 27 2010 * @date last modification: Fri Apr 09 2021 * * @brief Implementation of the SolidMechanicsModel class * * * @section LICENSE * * Copyright (©) 2010-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "solid_mechanics_model.hh" #include "integrator_gauss.hh" #include "shape_lagrange.hh" #include "solid_mechanics_model_tmpl.hh" #include "communicator.hh" #include "element_synchronizer.hh" #include "sparse_matrix.hh" #include "synchronizer_registry.hh" #include "dumpable_inline_impl.hh" /* -------------------------------------------------------------------------- */ #include "dumper_iohelper_paraview.hh" /* -------------------------------------------------------------------------- */ #include "material_non_local.hh" /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ /** * A solid mechanics model need a mesh and a dimension to be created. the model * by it self can not do a lot, the good init functions should be called in * order to configure the model depending on what we want to do. * * @param mesh mesh representing the model we want to simulate * @param dim spatial dimension of the problem, if dim = 0 (default value) the * dimension of the problem is assumed to be the on of the mesh * @param id an id to identify the model * @param model_type this is an internal parameter for inheritance purposes */ SolidMechanicsModel::SolidMechanicsModel( Mesh & mesh, Int dim, const ID & id, std::shared_ptr dof_manager, const ModelType model_type) : Model(mesh, model_type, std::move(dof_manager), dim, id), material_index("material index", id), material_local_numbering("material local numbering", id) { AKANTU_DEBUG_IN(); this->registerFEEngineObject("SolidMechanicsFEEngine", mesh, Model::spatial_dimension); this->mesh.registerDumper("solid_mechanics_model", id, true); this->mesh.addDumpMesh(mesh, Model::spatial_dimension, _not_ghost, _ek_regular); material_selector = std::make_shared(material_index); this->registerDataAccessor(*this); if (this->mesh.isDistributed()) { auto & synchronizer = this->mesh.getElementSynchronizer(); this->registerSynchronizer(synchronizer, SynchronizationTag::_material_id); this->registerSynchronizer(synchronizer, SynchronizationTag::_smm_mass); this->registerSynchronizer(synchronizer, SynchronizationTag::_smm_stress); this->registerSynchronizer(synchronizer, SynchronizationTag::_for_dump); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ SolidMechanicsModel::~SolidMechanicsModel() = default; /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::setTimeStep(Real time_step, const ID & solver_id) { Model::setTimeStep(time_step, solver_id); this->mesh.getDumper().setTimeStep(time_step); } /* -------------------------------------------------------------------------- */ /* Initialization */ /* -------------------------------------------------------------------------- */ /** * This function groups many of the initialization in on function. For most of * basics case the function should be enough. The functions initialize the * model, the internal vectors, set them to 0, and depending on the parameters * it also initialize the explicit or implicit solver. * * @param options * \parblock * contains the different options to initialize the model * \li \c analysis_method specify the type of solver to use * \endparblock */ void SolidMechanicsModel::initFullImpl(const ModelOptions & options) { material_index.initialize(mesh, _element_kind = _ek_not_defined, _default_value = -1, _with_nb_element = true); material_local_numbering.initialize(mesh, _element_kind = _ek_not_defined, _with_nb_element = true); Model::initFullImpl(options); // initialize the materials if (not this->parser.getLastParsedFile().empty()) { this->instantiateMaterials(); this->initMaterials(); } this->initBC(*this, *displacement, *displacement_increment, *external_force); } /* -------------------------------------------------------------------------- */ TimeStepSolverType SolidMechanicsModel::getDefaultSolverType() const { return TimeStepSolverType::_dynamic_lumped; } /* -------------------------------------------------------------------------- */ ModelSolverOptions SolidMechanicsModel::getDefaultSolverOptions( const TimeStepSolverType & type) const { ModelSolverOptions options; switch (type) { case TimeStepSolverType::_dynamic_lumped: { options.non_linear_solver_type = NonLinearSolverType::_lumped; options.integration_scheme_type["displacement"] = IntegrationSchemeType::_central_difference; options.solution_type["displacement"] = IntegrationScheme::_acceleration; break; } case TimeStepSolverType::_static: { options.non_linear_solver_type = NonLinearSolverType::_newton_raphson; options.integration_scheme_type["displacement"] = IntegrationSchemeType::_pseudo_time; options.solution_type["displacement"] = IntegrationScheme::_not_defined; break; } case TimeStepSolverType::_dynamic: { if (this->method == _explicit_consistent_mass) { options.non_linear_solver_type = NonLinearSolverType::_newton_raphson; options.integration_scheme_type["displacement"] = IntegrationSchemeType::_central_difference; options.solution_type["displacement"] = IntegrationScheme::_acceleration; } else { options.non_linear_solver_type = NonLinearSolverType::_newton_raphson; options.integration_scheme_type["displacement"] = IntegrationSchemeType::_trapezoidal_rule_2; options.solution_type["displacement"] = IntegrationScheme::_displacement; } break; } default: AKANTU_EXCEPTION(type << " is not a valid time step solver type"); } return options; } /* -------------------------------------------------------------------------- */ std::tuple SolidMechanicsModel::getDefaultSolverID(const AnalysisMethod & method) { switch (method) { case _explicit_lumped_mass: { return std::make_tuple("explicit_lumped", TimeStepSolverType::_dynamic_lumped); } case _explicit_consistent_mass: { return std::make_tuple("explicit", TimeStepSolverType::_dynamic); } case _static: { return std::make_tuple("static", TimeStepSolverType::_static); } case _implicit_dynamic: { return std::make_tuple("implicit", TimeStepSolverType::_dynamic); } default: return std::make_tuple("unknown", TimeStepSolverType::_not_defined); } } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::initSolver(TimeStepSolverType time_step_solver_type, NonLinearSolverType /*unused*/) { auto & dof_manager = this->getDOFManager(); /* ------------------------------------------------------------------------ */ // for alloc type of solvers this->allocNodalField(this->displacement, spatial_dimension, "displacement"); this->allocNodalField(this->previous_displacement, spatial_dimension, "previous_displacement"); this->allocNodalField(this->displacement_increment, spatial_dimension, "displacement_increment"); this->allocNodalField(this->internal_force, spatial_dimension, "internal_force"); this->allocNodalField(this->external_force, spatial_dimension, "external_force"); this->allocNodalField(this->blocked_dofs, spatial_dimension, "blocked_dofs"); this->allocNodalField(this->current_position, spatial_dimension, "current_position"); // initialize the current positions this->current_position->copy(this->mesh.getNodes()); /* ------------------------------------------------------------------------ */ if (!dof_manager.hasDOFs("displacement")) { dof_manager.registerDOFs("displacement", *this->displacement, _dst_nodal); dof_manager.registerBlockedDOFs("displacement", *this->blocked_dofs); dof_manager.registerDOFsIncrement("displacement", *this->displacement_increment); dof_manager.registerDOFsPrevious("displacement", *this->previous_displacement); } /* ------------------------------------------------------------------------ */ // for dynamic if (time_step_solver_type == TimeStepSolverType::_dynamic || time_step_solver_type == TimeStepSolverType::_dynamic_lumped) { this->allocNodalField(this->velocity, spatial_dimension, "velocity"); this->allocNodalField(this->acceleration, spatial_dimension, "acceleration"); if (!dof_manager.hasDOFsDerivatives("displacement", 1)) { dof_manager.registerDOFsDerivative("displacement", 1, *this->velocity); dof_manager.registerDOFsDerivative("displacement", 2, *this->acceleration); } } } /* -------------------------------------------------------------------------- */ /** * Initialize the model,basically it pre-compute the shapes, shapes derivatives * and jacobian */ void SolidMechanicsModel::initModel() { /// \todo add the current position as a parameter to initShapeFunctions for /// large deformation getFEEngine().initShapeFunctions(_not_ghost); getFEEngine().initShapeFunctions(_ghost); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::assembleResidual() { AKANTU_DEBUG_IN(); /* ------------------------------------------------------------------------ */ // computes the internal forces this->assembleInternalForces(); /* ------------------------------------------------------------------------ */ this->getDOFManager().assembleToResidual("displacement", *this->external_force, 1); this->getDOFManager().assembleToResidual("displacement", *this->internal_force, 1); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::assembleResidual(const ID & residual_part) { AKANTU_DEBUG_IN(); if ("external" == residual_part) { this->getDOFManager().assembleToResidual("displacement", *this->external_force, 1); AKANTU_DEBUG_OUT(); return; } if ("internal" == residual_part) { this->assembleInternalForces(); this->getDOFManager().assembleToResidual("displacement", *this->internal_force, 1); AKANTU_DEBUG_OUT(); return; } AKANTU_CUSTOM_EXCEPTION( debug::SolverCallbackResidualPartUnknown(residual_part)); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ MatrixType SolidMechanicsModel::getMatrixType(const ID & matrix_id) const { // \TODO check the materials to know what is the correct answer if (matrix_id == "C") { return _mt_not_defined; } if (matrix_id == "K") { auto matrix_type = _unsymmetric; for (auto & material : materials) { matrix_type = std::max(matrix_type, material->getMatrixType(matrix_id)); } } return _symmetric; } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::assembleMatrix(const ID & matrix_id) { if (matrix_id == "K") { this->assembleStiffnessMatrix(); } else if (matrix_id == "M") { this->assembleMass(); } } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::assembleLumpedMatrix(const ID & matrix_id) { if (matrix_id == "M") { this->assembleMassLumped(); } } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::beforeSolveStep() { for (auto & material : materials) { material->beforeSolveStep(); } } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::afterSolveStep(bool converged) { for (auto & material : materials) { material->afterSolveStep(converged); } } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::predictor() { ++displacement_release; } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::corrector() { ++displacement_release; } /* -------------------------------------------------------------------------- */ /** * This function computes the internal forces as \f$F_{int} = \int_{\Omega} N * \sigma d\Omega@\f$ */ void SolidMechanicsModel::assembleInternalForces() { AKANTU_DEBUG_IN(); AKANTU_DEBUG_INFO("Assemble the internal forces"); this->internal_force->zero(); // compute the stresses of local elements AKANTU_DEBUG_INFO("Compute local stresses"); for (auto & material : materials) { material->computeAllStresses(_not_ghost); } /* ------------------------------------------------------------------------ */ /* Computation of the non local part */ if (this->non_local_manager) { this->non_local_manager->computeAllNonLocalStresses(); } // communicate the stresses AKANTU_DEBUG_INFO("Send data for residual assembly"); this->asynchronousSynchronize(SynchronizationTag::_smm_stress); // assemble the forces due to local stresses AKANTU_DEBUG_INFO("Assemble residual for local elements"); for (auto & material : materials) { material->assembleInternalForces(_not_ghost); } // finalize communications AKANTU_DEBUG_INFO("Wait distant stresses"); this->waitEndSynchronize(SynchronizationTag::_smm_stress); // assemble the stresses due to ghost elements AKANTU_DEBUG_INFO("Assemble residual for ghost elements"); for (auto & material : materials) { material->assembleInternalForces(_ghost); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::assembleStiffnessMatrix(bool need_to_reassemble) { AKANTU_DEBUG_IN(); AKANTU_DEBUG_INFO("Assemble the new stiffness matrix."); + if (not this->getDOFManager().hasMatrix("K")) { + this->getDOFManager().getNewMatrix("K", this->getMatrixType("K")); + } + // Check if materials need to recompute the matrix for (auto & material : materials) { need_to_reassemble |= material->hasMatrixChanged("K"); } if (need_to_reassemble) { this->getDOFManager().getMatrix("K").zero(); // call compute stiffness matrix on each local elements for (auto & material : materials) { material->assembleStiffnessMatrix(_not_ghost); } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::updateCurrentPosition() { if (this->current_position_release == this->displacement_release) { return; } this->current_position->copy(this->mesh.getNodes()); for (auto && data : zip(make_view(*this->current_position, spatial_dimension), make_view(*this->displacement, spatial_dimension))) { std::get<0>(data) += std::get<1>(data); } this->current_position_release = this->displacement_release; } /* -------------------------------------------------------------------------- */ const Array & SolidMechanicsModel::getCurrentPosition() { this->updateCurrentPosition(); return *this->current_position; } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::updateDataForNonLocalCriterion( ElementTypeMapReal & criterion) { const ID field_name = criterion.getName(); for (auto & material : materials) { if (!material->isInternal(field_name, _ek_regular)) { continue; } for (auto ghost_type : ghost_types) { material->flattenInternal(field_name, criterion, ghost_type, _ek_regular); } } } /* -------------------------------------------------------------------------- */ /* Information */ /* -------------------------------------------------------------------------- */ Real SolidMechanicsModel::getStableTimeStep() { AKANTU_DEBUG_IN(); Real min_dt = getStableTimeStep(_not_ghost); /// reduction min over all processors mesh.getCommunicator().allReduce(min_dt, SynchronizerOperation::_min); AKANTU_DEBUG_OUT(); return min_dt; } /* -------------------------------------------------------------------------- */ Real SolidMechanicsModel::getStableTimeStep(GhostType ghost_type) { AKANTU_DEBUG_IN(); Real min_dt = std::numeric_limits::max(); this->updateCurrentPosition(); Element elem; elem.ghost_type = ghost_type; for (auto type : mesh.elementTypes(Model::spatial_dimension, ghost_type, _ek_regular)) { elem.type = type; auto nb_nodes_per_element = mesh.getNbNodesPerElement(type); Array X(0, nb_nodes_per_element * Model::spatial_dimension); FEEngine::extractNodalToElementField(mesh, *current_position, X, type, _not_ghost); for (auto && data : zip(make_view(X, spatial_dimension, nb_nodes_per_element), make_view(material_index(type, ghost_type)), make_view(material_local_numbering(type, ghost_type)))) { auto && X_el = std::get<0>(data); auto && mat_idx = std::get<1>(data); elem.element = std::get<2>(data); auto el_h = getFEEngine().getElementInradius(X_el, type); auto el_c = this->materials[mat_idx]->getCelerity(elem); auto el_dt = el_h / el_c; min_dt = std::min(min_dt, el_dt); } } AKANTU_DEBUG_OUT(); return min_dt; } /* -------------------------------------------------------------------------- */ Real SolidMechanicsModel::getKineticEnergy() { AKANTU_DEBUG_IN(); Real ekin = 0.; auto nb_nodes = mesh.getNbNodes(); if (this->getDOFManager().hasLumpedMatrix("M")) { + this->assembleLumpedMatrix("M"); + auto m_it = this->mass->begin(Model::spatial_dimension); auto m_end = this->mass->end(Model::spatial_dimension); auto v_it = this->velocity->begin(Model::spatial_dimension); for (Int n = 0; m_it != m_end; ++n, ++m_it, ++v_it) { const auto & v = *v_it; const auto & m = *m_it; Real mv2 = 0.; auto is_local_node = mesh.isLocalOrMasterNode(n); // bool is_not_pbc_slave_node = !isPBCSlaveNode(n); auto count_node = is_local_node; // && is_not_pbc_slave_node; if (count_node) { for (Int i = 0; i < spatial_dimension; ++i) { if (m(i) > std::numeric_limits::epsilon()) { mv2 += v(i) * v(i) * m(i); } } } ekin += mv2; } } else if (this->getDOFManager().hasMatrix("M")) { + this->assembleMatrix("M"); + Array Mv(nb_nodes, Model::spatial_dimension); this->getDOFManager().assembleMatMulVectToArray("displacement", "M", *this->velocity, Mv); for (auto && data : zip(arange(nb_nodes), make_view(Mv, spatial_dimension), make_view(*this->velocity, spatial_dimension))) { ekin += std::get<2>(data).dot(std::get<1>(data)) * static_cast(mesh.isLocalOrMasterNode(std::get<0>(data))); } } else { AKANTU_ERROR("No function called to assemble the mass matrix."); } mesh.getCommunicator().allReduce(ekin, SynchronizerOperation::_sum); AKANTU_DEBUG_OUT(); return ekin * .5; } /* -------------------------------------------------------------------------- */ Real SolidMechanicsModel::getKineticEnergy(const Element & element) { AKANTU_DEBUG_IN(); auto nb_quadrature_points = getFEEngine().getNbIntegrationPoints(element.type); Array vel_on_quad(nb_quadrature_points, Model::spatial_dimension); Array filter_element(1, 1, element.element); getFEEngine().interpolateOnIntegrationPoints( *velocity, vel_on_quad, Model::spatial_dimension, element.type, _not_ghost, filter_element); Vector rho_v2(nb_quadrature_points); Real rho = materials[material_index(element)]->getRho(); for (auto && data : enumerate(make_view(vel_on_quad, spatial_dimension))) { auto && vel = std::get<1>(data); rho_v2(std::get<0>(data)) = rho * vel.dot(vel); } AKANTU_DEBUG_OUT(); return .5 * getFEEngine().integrate(rho_v2, element); } /* -------------------------------------------------------------------------- */ Real SolidMechanicsModel::getExternalWork() { AKANTU_DEBUG_IN(); Array * incr_or_velo; if (this->method == _static) { incr_or_velo = this->displacement_increment.get(); } else { incr_or_velo = this->velocity.get(); } Real work = 0.; auto nb_nodes = this->mesh.getNbNodes(); for (auto && data : zip(make_view(*external_force, spatial_dimension), make_view(*internal_force, spatial_dimension), make_view(*blocked_dofs, spatial_dimension), make_view(*incr_or_velo, spatial_dimension), arange(nb_nodes))) { auto && int_force = std::get<0>(data); auto && ext_force = std::get<1>(data); auto && boun = std::get<2>(data); auto && incr_or_velo = std::get<3>(data); auto && n = std::get<4>(data); auto is_local_node = this->mesh.isLocalOrMasterNode(n); // bool is_not_pbc_slave_node = !this->isPBCSlaveNode(n); auto count_node = is_local_node; // && is_not_pbc_slave_node; if (count_node) { for (Int i = 0; i < spatial_dimension; ++i) { if (boun(i)) { work -= int_force(i) * incr_or_velo(i); } else { work += ext_force(i) * incr_or_velo(i); } } } } mesh.getCommunicator().allReduce(work, SynchronizerOperation::_sum); if (this->method != _static) { work *= this->getTimeStep(); } AKANTU_DEBUG_OUT(); return work; } /* -------------------------------------------------------------------------- */ Real SolidMechanicsModel::getEnergy(const std::string & energy_id) { AKANTU_DEBUG_IN(); if (energy_id == "kinetic") { return getKineticEnergy(); } if (energy_id == "external work") { return getExternalWork(); } Real energy = 0.; for (auto & material : materials) { energy += material->getEnergy(energy_id); } /// reduction sum over all processors mesh.getCommunicator().allReduce(energy, SynchronizerOperation::_sum); AKANTU_DEBUG_OUT(); return energy; } /* -------------------------------------------------------------------------- */ Real SolidMechanicsModel::getEnergy(const std::string & energy_id, const Element & element) { AKANTU_DEBUG_IN(); if (energy_id == "kinetic") { return getKineticEnergy(element); } auto mat_index = this->material_index(element); auto mat_loc_num = this->material_local_numbering(element); auto energy = this->materials[mat_index]->getEnergy( energy_id, {element.type, mat_loc_num, element.ghost_type}); AKANTU_DEBUG_OUT(); return energy; } /* -------------------------------------------------------------------------- */ Real SolidMechanicsModel::getEnergy(const ID & energy_id, const ID & group_id) { auto && group = mesh.getElementGroup(group_id); auto energy = 0.; for (auto && type : group.elementTypes()) { for (auto el : group.getElementsIterable(type)) { energy += getEnergy(energy_id, el); } } /// reduction sum over all processors mesh.getCommunicator().allReduce(energy, SynchronizerOperation::_sum); return energy; } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::onElementsAdded(const Array & element_list, const NewElementsEvent & event) { AKANTU_DEBUG_IN(); this->material_index.initialize(mesh, _element_kind = _ek_not_defined, _with_nb_element = true, _default_value = -1); this->material_local_numbering.initialize( mesh, _element_kind = _ek_not_defined, _with_nb_element = true, _default_value = -1); ElementTypeMapArray filter("new_element_filter", this->getID()); for (const auto & elem : element_list) { if (mesh.getSpatialDimension(elem.type) != spatial_dimension) { continue; } if (!filter.exists(elem.type, elem.ghost_type)) { filter.alloc(0, 1, elem.type, elem.ghost_type); } filter(elem.type, elem.ghost_type).push_back(elem.element); } // this fails in parallel if the event is sent on facet between constructor // and initFull \todo: to debug... this->assignMaterialToElements(&filter); for (auto & material : materials) { material->onElementsAdded(element_list, event); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::onElementsRemoved( const Array & element_list, const ElementTypeMapArray & new_numbering, const RemovedElementsEvent & event) { for (auto & material : materials) { material->onElementsRemoved(element_list, new_numbering, event); } } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::onNodesAdded(const Array & nodes_list, const NewNodesEvent & event) { AKANTU_DEBUG_IN(); auto nb_nodes = mesh.getNbNodes(); if (displacement) { displacement->resize(nb_nodes, 0.); ++displacement_release; } if (mass) { mass->resize(nb_nodes, 0.); } if (velocity) { velocity->resize(nb_nodes, 0.); } if (acceleration) { acceleration->resize(nb_nodes, 0.); } if (external_force) { external_force->resize(nb_nodes, 0.); } if (internal_force) { internal_force->resize(nb_nodes, 0.); } if (blocked_dofs) { blocked_dofs->resize(nb_nodes, false); } if (current_position) { current_position->resize(nb_nodes, 0.); } if (previous_displacement) { previous_displacement->resize(nb_nodes, 0.); } if (displacement_increment) { displacement_increment->resize(nb_nodes, 0.); } for (auto & material : materials) { material->onNodesAdded(nodes_list, event); } need_to_reassemble_lumped_mass = true; need_to_reassemble_mass = true; AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::onNodesRemoved(const Array & /*element_list*/, const Array & new_numbering, const RemovedNodesEvent & /*event*/) { if (displacement) { mesh.removeNodesFromArray(*displacement, new_numbering); ++displacement_release; } if (mass) { mesh.removeNodesFromArray(*mass, new_numbering); } if (velocity) { mesh.removeNodesFromArray(*velocity, new_numbering); } if (acceleration) { mesh.removeNodesFromArray(*acceleration, new_numbering); } if (internal_force) { mesh.removeNodesFromArray(*internal_force, new_numbering); } if (external_force) { mesh.removeNodesFromArray(*external_force, new_numbering); } if (blocked_dofs) { mesh.removeNodesFromArray(*blocked_dofs, new_numbering); } // if (increment_acceleration) // mesh.removeNodesFromArray(*increment_acceleration, new_numbering); if (displacement_increment) { mesh.removeNodesFromArray(*displacement_increment, new_numbering); } if (previous_displacement) { mesh.removeNodesFromArray(*previous_displacement, new_numbering); } } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::printself(std::ostream & stream, int indent) const { std::string space(indent, AKANTU_INDENT); stream << space << "Solid Mechanics Model [" << std::endl; stream << space << " + id : " << id << std::endl; stream << space << " + spatial dimension : " << Model::spatial_dimension << std::endl; stream << space << " + fem [" << std::endl; getFEEngine().printself(stream, indent + 2); stream << space << " ]" << std::endl; stream << space << " + nodals information [" << std::endl; displacement->printself(stream, indent + 2); if (velocity) { velocity->printself(stream, indent + 2); } if (acceleration) { acceleration->printself(stream, indent + 2); } if (mass) { mass->printself(stream, indent + 2); } external_force->printself(stream, indent + 2); internal_force->printself(stream, indent + 2); blocked_dofs->printself(stream, indent + 2); stream << space << " ]" << std::endl; stream << space << " + material information [" << std::endl; material_index.printself(stream, indent + 2); stream << space << " ]" << std::endl; stream << space << " + materials [" << std::endl; for (const auto & material : materials) { material->printself(stream, indent + 2); } stream << space << " ]" << std::endl; stream << space << "]" << std::endl; } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::initializeNonLocal() { this->non_local_manager->synchronize(*this, SynchronizationTag::_material_id); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::insertIntegrationPointsInNeighborhoods( GhostType ghost_type) { for (auto & mat : materials) { if (not aka::is_of_type(mat)) { continue; } auto && mat_non_local = dynamic_cast(*mat); ElementTypeMapArray quadrature_points_coordinates( "quadrature_points_coordinates_tmp_nl", this->id); quadrature_points_coordinates.initialize(this->getFEEngine(), _nb_component = spatial_dimension, _ghost_type = ghost_type); for (const auto & type : quadrature_points_coordinates.elementTypes( Model::spatial_dimension, ghost_type)) { this->getFEEngine().computeIntegrationPointsCoordinates( quadrature_points_coordinates(type, ghost_type), type, ghost_type); } mat_non_local.initMaterialNonLocal(); mat_non_local.insertIntegrationPointsInNeighborhoods( ghost_type, quadrature_points_coordinates); } } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::computeNonLocalStresses(GhostType ghost_type) { for (auto & mat : materials) { if (not aka::is_of_type(*mat)) { continue; } auto & mat_non_local = dynamic_cast(*mat); mat_non_local.computeNonLocalStresses(ghost_type); } } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::updateLocalInternal( ElementTypeMapReal & internal_flat, GhostType ghost_type, ElementKind kind) { const ID field_name = internal_flat.getName(); for (auto & material : materials) { if (material->isInternal(field_name, kind)) { material->flattenInternal(field_name, internal_flat, ghost_type, kind); } } } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::updateNonLocalInternal( ElementTypeMapReal & internal_flat, GhostType ghost_type, ElementKind kind) { const ID field_name = internal_flat.getName(); for (auto & mat : materials) { if (not aka::is_of_type(*mat)) { continue; } auto & mat_non_local = dynamic_cast(*mat); mat_non_local.updateNonLocalInternals(internal_flat, field_name, ghost_type, kind); } } /* -------------------------------------------------------------------------- */ FEEngine & SolidMechanicsModel::getFEEngineBoundary(const ID & name) { return getFEEngineClassBoundary(name); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::splitElementByMaterial( const Array & elements, std::vector> & elements_per_mat) const { for (const auto & el : elements) { Element mat_el = el; mat_el.element = this->material_local_numbering(el); AKANTU_DEBUG_ASSERT(mat_el.element != -1, "The element" << el << " has no defined material"); elements_per_mat[this->material_index(el)].push_back(mat_el); } } /* -------------------------------------------------------------------------- */ Int SolidMechanicsModel::getNbData(const Array & elements, const SynchronizationTag & tag) const { AKANTU_DEBUG_IN(); Int size = 0; Int nb_nodes_per_element = 0; for (const Element & el : elements) { nb_nodes_per_element += Mesh::getNbNodesPerElement(el.type); } switch (tag) { case SynchronizationTag::_material_id: { size += elements.size() * sizeof(decltype(material_index)::value_type); break; } case SynchronizationTag::_smm_mass: { size += nb_nodes_per_element * sizeof(Real) * Model::spatial_dimension; // mass vector break; } case SynchronizationTag::_smm_for_gradu: { size += nb_nodes_per_element * Model::spatial_dimension * sizeof(Real); // displacement break; } case SynchronizationTag::_smm_boundary: { // force, displacement, boundary size += nb_nodes_per_element * Model::spatial_dimension * (2 * sizeof(Real) + sizeof(bool)); break; } case SynchronizationTag::_for_dump: { // displacement, velocity, acceleration, residual, force size += nb_nodes_per_element * Model::spatial_dimension * sizeof(Real) * 5; break; } default: { } } if (tag != SynchronizationTag::_material_id) { splitByMaterial(elements, [&](auto && mat, auto && elements) { size += mat.getNbData(elements, tag); }); } AKANTU_DEBUG_OUT(); return size; } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::packData(CommunicationBuffer & buffer, const Array & elements, const SynchronizationTag & tag) const { AKANTU_DEBUG_IN(); switch (tag) { case SynchronizationTag::_material_id: { packElementalDataHelper(material_index, buffer, elements, false, getFEEngine()); break; } case SynchronizationTag::_smm_mass: { packNodalDataHelper(*mass, buffer, elements, mesh); break; } case SynchronizationTag::_smm_for_gradu: { packNodalDataHelper(*displacement, buffer, elements, mesh); break; } case SynchronizationTag::_for_dump: { packNodalDataHelper(*displacement, buffer, elements, mesh); packNodalDataHelper(*velocity, buffer, elements, mesh); packNodalDataHelper(*acceleration, buffer, elements, mesh); packNodalDataHelper(*internal_force, buffer, elements, mesh); packNodalDataHelper(*external_force, buffer, elements, mesh); break; } case SynchronizationTag::_smm_boundary: { packNodalDataHelper(*external_force, buffer, elements, mesh); packNodalDataHelper(*velocity, buffer, elements, mesh); packNodalDataHelper(*blocked_dofs, buffer, elements, mesh); break; } default: { } } if (tag != SynchronizationTag::_material_id) { splitByMaterial(elements, [&](auto && mat, auto && elements) { mat.packData(buffer, elements, tag); }); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::unpackData(CommunicationBuffer & buffer, const Array & elements, const SynchronizationTag & tag) { AKANTU_DEBUG_IN(); switch (tag) { case SynchronizationTag::_material_id: { for (auto && element : elements) { decltype(material_index)::value_type recv_mat_index; buffer >> recv_mat_index; auto & mat_index = material_index(element); if (mat_index != -1) { continue; } // add ghosts element to the correct material mat_index = recv_mat_index; auto index = materials[mat_index]->addElement(element); material_local_numbering(element) = index; } break; } case SynchronizationTag::_smm_mass: { unpackNodalDataHelper(*mass, buffer, elements, mesh); break; } case SynchronizationTag::_smm_for_gradu: { unpackNodalDataHelper(*displacement, buffer, elements, mesh); break; } case SynchronizationTag::_for_dump: { unpackNodalDataHelper(*displacement, buffer, elements, mesh); unpackNodalDataHelper(*velocity, buffer, elements, mesh); unpackNodalDataHelper(*acceleration, buffer, elements, mesh); unpackNodalDataHelper(*internal_force, buffer, elements, mesh); unpackNodalDataHelper(*external_force, buffer, elements, mesh); break; } case SynchronizationTag::_smm_boundary: { unpackNodalDataHelper(*external_force, buffer, elements, mesh); unpackNodalDataHelper(*velocity, buffer, elements, mesh); unpackNodalDataHelper(*blocked_dofs, buffer, elements, mesh); break; } default: { } } if (tag != SynchronizationTag::_material_id) { splitByMaterial(elements, [&](auto && mat, auto && elements) { mat.unpackData(buffer, elements, tag); }); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ Int SolidMechanicsModel::getNbData(const Array & dofs, const SynchronizationTag & tag) const { AKANTU_DEBUG_IN(); Int size = 0; switch (tag) { case SynchronizationTag::_smm_uv: { size += sizeof(Real) * Model::spatial_dimension * 2; break; } case SynchronizationTag::_smm_res: /* FALLTHRU */ case SynchronizationTag::_smm_mass: { size += sizeof(Real) * Model::spatial_dimension; break; } case SynchronizationTag::_for_dump: { size += sizeof(Real) * Model::spatial_dimension * 5; break; } default: { AKANTU_ERROR("Unknown ghost synchronization tag : " << tag); } } AKANTU_DEBUG_OUT(); return size * dofs.size(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::packData(CommunicationBuffer & buffer, const Array & dofs, const SynchronizationTag & tag) const { AKANTU_DEBUG_IN(); switch (tag) { case SynchronizationTag::_smm_uv: { packDOFDataHelper(*displacement, buffer, dofs); packDOFDataHelper(*velocity, buffer, dofs); break; } case SynchronizationTag::_smm_res: { packDOFDataHelper(*internal_force, buffer, dofs); break; } case SynchronizationTag::_smm_mass: { packDOFDataHelper(*mass, buffer, dofs); break; } case SynchronizationTag::_for_dump: { packDOFDataHelper(*displacement, buffer, dofs); packDOFDataHelper(*velocity, buffer, dofs); packDOFDataHelper(*acceleration, buffer, dofs); packDOFDataHelper(*internal_force, buffer, dofs); packDOFDataHelper(*external_force, buffer, dofs); break; } default: { AKANTU_ERROR("Unknown ghost synchronization tag : " << tag); } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::unpackData(CommunicationBuffer & buffer, const Array & dofs, const SynchronizationTag & tag) { AKANTU_DEBUG_IN(); switch (tag) { case SynchronizationTag::_smm_uv: { unpackDOFDataHelper(*displacement, buffer, dofs); unpackDOFDataHelper(*velocity, buffer, dofs); break; } case SynchronizationTag::_smm_res: { unpackDOFDataHelper(*internal_force, buffer, dofs); break; } case SynchronizationTag::_smm_mass: { unpackDOFDataHelper(*mass, buffer, dofs); break; } case SynchronizationTag::_for_dump: { unpackDOFDataHelper(*displacement, buffer, dofs); unpackDOFDataHelper(*velocity, buffer, dofs); unpackDOFDataHelper(*acceleration, buffer, dofs); unpackDOFDataHelper(*internal_force, buffer, dofs); unpackDOFDataHelper(*external_force, buffer, dofs); break; } default: { AKANTU_ERROR("Unknown ghost synchronization tag : " << tag); } } AKANTU_DEBUG_OUT(); } } // namespace akantu diff --git a/src/model/solid_mechanics/solid_mechanics_model.hh b/src/model/solid_mechanics/solid_mechanics_model.hh index dabdc39ef..493f8738d 100644 --- a/src/model/solid_mechanics/solid_mechanics_model.hh +++ b/src/model/solid_mechanics/solid_mechanics_model.hh @@ -1,596 +1,604 @@ /** * @file solid_mechanics_model.hh * * @author Guillaume Anciaux * @author Daniel Pino Muñoz * @author Nicolas Richart * * @date creation: Tue Jul 27 2010 * @date last modification: Fri Apr 09 2021 * * @brief Model of Solid Mechanics * * * @section LICENSE * * Copyright (©) 2010-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "boundary_condition.hh" #include "data_accessor.hh" #include "fe_engine.hh" #include "model.hh" #include "non_local_manager_callback.hh" #include "solid_mechanics_model_event_handler.hh" /* -------------------------------------------------------------------------- */ #ifndef AKANTU_SOLID_MECHANICS_MODEL_HH_ #define AKANTU_SOLID_MECHANICS_MODEL_HH_ namespace akantu { class Material; class MaterialSelector; class DumperIOHelper; class NonLocalManager; template class IntegratorGauss; template class ShapeLagrange; } // namespace akantu /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ class SolidMechanicsModel : public Model, public DataAccessor, public DataAccessor, public BoundaryCondition, public NonLocalManagerCallback, public EventHandlerManager { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: class NewMaterialElementsEvent : public NewElementsEvent { public: AKANTU_GET_MACRO_NOT_CONST(MaterialList, material, Array &); AKANTU_GET_MACRO(MaterialList, material, const Array &); protected: Array material; }; using MyFEEngineType = FEEngineTemplate; protected: using EventManager = EventHandlerManager; public: SolidMechanicsModel(Mesh &mesh, Int dim = _all_dimensions, const ID &id = "solid_mechanics_model", std::shared_ptr dof_manager = nullptr, ModelType model_type = ModelType::_solid_mechanics_model); ~SolidMechanicsModel() override; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ protected: /// initialize completely the model void initFullImpl( const ModelOptions &options = SolidMechanicsModelOptions()) override; public: /// initialize all internal arrays for materials virtual void initMaterials(); protected: /// initialize the model void initModel() override; /// function to print the containt of the class void printself(std::ostream &stream, int indent = 0) const override; /// get some default values for derived classes std::tuple getDefaultSolverID(const AnalysisMethod &method) override; /* ------------------------------------------------------------------------ */ /* Solver interface */ /* ------------------------------------------------------------------------ */ public: /// assembles the stiffness matrix, virtual void assembleStiffnessMatrix(bool need_to_reassemble = false); /// assembles the internal forces in the array internal_forces virtual void assembleInternalForces(); protected: /// callback for the solver, this adds f_{ext} - f_{int} to the residual void assembleResidual() override; /// callback for the solver, this adds f_{ext} or f_{int} to the residual void assembleResidual(const ID &residual_part) override; bool canSplitResidual() const override { return true; } /// get the type of matrix needed MatrixType getMatrixType(const ID &matrix_id) const override; /// callback for the solver, this assembles different matrices void assembleMatrix(const ID &matrix_id) override; /// callback for the solver, this assembles the stiffness matrix void assembleLumpedMatrix(const ID &matrix_id) override; /// callback for the solver, this is called at beginning of solve void predictor() override; /// callback for the solver, this is called at end of solve void corrector() override; /// callback for the solver, this is called at beginning of solve void beforeSolveStep() override; /// callback for the solver, this is called at end of solve void afterSolveStep(bool converged = true) override; /// Callback for the model to instantiate the matricees when needed void initSolver(TimeStepSolverType time_step_solver_type, NonLinearSolverType non_linear_solver_type) override; protected: /* ------------------------------------------------------------------------ */ TimeStepSolverType getDefaultSolverType() const override; /* ------------------------------------------------------------------------ */ ModelSolverOptions getDefaultSolverOptions(const TimeStepSolverType &type) const override; public: bool isDefaultSolverExplicit() { return method == _explicit_lumped_mass || method == _explicit_consistent_mass; } protected: /// update the current position vector void updateCurrentPosition(); /* ------------------------------------------------------------------------ */ /* Materials (solid_mechanics_model_material.cc) */ /* ------------------------------------------------------------------------ */ public: /// register an empty material of a given type Material ®isterNewMaterial(const ID &mat_name, const ID &mat_type, const ID &opt_param); /// reassigns materials depending on the material selector virtual void reassignMaterial(); /// apply a constant eigen_grad_u on all quadrature points of a given material virtual void applyEigenGradU(const Matrix &prescribed_eigen_grad_u, const ID &material_name, GhostType ghost_type = _not_ghost); protected: /// register a material in the dynamic database Material ®isterNewMaterial(const ParserSection &mat_section); /// read the material files to instantiate all the materials void instantiateMaterials(); /// set the element_id_by_material and add the elements to the good materials virtual void assignMaterialToElements(const ElementTypeMapArray *filter = nullptr); /* ------------------------------------------------------------------------ */ /* Mass (solid_mechanics_model_mass.cc) */ /* ------------------------------------------------------------------------ */ public: /// assemble the lumped mass matrix void assembleMassLumped(); /// assemble the mass matrix for consistent mass resolutions void assembleMass(); public: /// assemble the lumped mass matrix for local and ghost elements void assembleMassLumped(GhostType ghost_type); /// assemble the mass matrix for either _ghost or _not_ghost elements void assembleMass(GhostType ghost_type); protected: /// fill a vector of rho void computeRho(Array &rho, ElementType type, GhostType ghost_type); /// compute the kinetic energy Real getKineticEnergy(); [[gnu::deprecated("Use the interface with an Element")]] Real getKineticEnergy(ElementType type, Idx index) { return getKineticEnergy({type, index, _not_ghost}); } Real getKineticEnergy(const Element &element); /// compute the external work (for impose displacement, the velocity should be /// given too) Real getExternalWork(); /* ------------------------------------------------------------------------ */ /* NonLocalManager inherited members */ /* ------------------------------------------------------------------------ */ protected: void initializeNonLocal() override; void updateDataForNonLocalCriterion(ElementTypeMapReal &criterion) override; void computeNonLocalStresses(GhostType ghost_type) override; void insertIntegrationPointsInNeighborhoods(GhostType ghost_type) override; /// update the values of the non local internal void updateLocalInternal(ElementTypeMapReal &internal_flat, GhostType ghost_type, ElementKind kind) override; /// copy the results of the averaging in the materials void updateNonLocalInternal(ElementTypeMapReal &internal_flat, GhostType ghost_type, ElementKind kind) override; /* ------------------------------------------------------------------------ */ /* Data Accessor inherited members */ /* ------------------------------------------------------------------------ */ public: Int getNbData(const Array &elements, const SynchronizationTag &tag) const override; void packData(CommunicationBuffer &buffer, const Array &elements, const SynchronizationTag &tag) const override; void unpackData(CommunicationBuffer &buffer, const Array &elements, const SynchronizationTag &tag) override; Int getNbData(const Array &dofs, const SynchronizationTag &tag) const override; void packData(CommunicationBuffer &buffer, const Array &dofs, const SynchronizationTag &tag) const override; void unpackData(CommunicationBuffer &buffer, const Array &dofs, const SynchronizationTag &tag) override; protected: void splitElementByMaterial(const Array &elements, std::vector> &elements_per_mat) const; template void splitByMaterial(const Array &elements, Operation &&op) const; /* ------------------------------------------------------------------------ */ /* Mesh Event Handler inherited members */ /* ------------------------------------------------------------------------ */ protected: void onNodesAdded(const Array &nodes_list, const NewNodesEvent &event) override; void onNodesRemoved(const Array &element_list, const Array &new_numbering, const RemovedNodesEvent &event) override; void onElementsAdded(const Array &element_list, const NewElementsEvent &event) override; void onElementsRemoved(const Array &element_list, const ElementTypeMapArray &new_numbering, const RemovedElementsEvent &event) override; void onElementsChanged(const Array &, const Array &, const ElementTypeMapArray &, const ChangedElementsEvent &) override{}; /* ------------------------------------------------------------------------ */ /* Dumpable interface (kept for convenience) and dumper relative functions */ /* ------------------------------------------------------------------------ */ public: virtual void onDump(); //! decide wether a field is a material internal or not bool isInternal(const std::string &field_name, ElementKind element_kind); //! give the amount of data per element virtual ElementTypeMap getInternalDataPerElem(const std::string &field_name, ElementKind kind); //! flatten a given material internal field ElementTypeMapArray &flattenInternal(const std::string &field_name, ElementKind kind, GhostType ghost_type = _not_ghost); //! flatten all the registered material internals void flattenAllRegisteredInternals(ElementKind kind); + //! inverse operation of the flatten + void inflateInternal(const std::string & field_name, + const ElementTypeMapArray & field, + ElementKind kind, GhostType ghost_type = _not_ghost); + std::shared_ptr createNodalFieldReal(const std::string &field_name, const std::string &group_name, bool padding_flag) override; std::shared_ptr createNodalFieldBool(const std::string &field_name, const std::string &group_name, bool padding_flag) override; std::shared_ptr createElementalField(const std::string &field_name, const std::string &group_name, bool padding_flag, Int spatial_dimension, ElementKind kind) override; void dump(const std::string &dumper_name) override; void dump(const std::string &dumper_name, Int step) override; void dump(const std::string &dumper_name, Real time, Int step) override; void dump() override; void dump(Int step) override; void dump(Real time, Int step) override; /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: /// set the value of the time step void setTimeStep(Real time_step, const ID &solver_id = "") override; /// get the value of the conversion from forces/ mass to acceleration AKANTU_GET_MACRO(F_M2A, f_m2a, Real); /// set the value of the conversion from forces/ mass to acceleration AKANTU_SET_MACRO(F_M2A, f_m2a, Real); /// get the SolidMechanicsModel::displacement array AKANTU_GET_MACRO_DEREF_PTR_NOT_CONST(Displacement, displacement); /// get the SolidMechanicsModel::displacement array AKANTU_GET_MACRO_DEREF_PTR(Displacement, displacement); /// get the SolidMechanicsModel::previous_displacement array AKANTU_GET_MACRO_DEREF_PTR(PreviousDisplacement, previous_displacement); /// get the SolidMechanicsModel::current_position array const Array &getCurrentPosition(); /// get the SolidMechanicsModel::displacement_increment array AKANTU_GET_MACRO_DEREF_PTR(Increment, displacement_increment); /// get the SolidMechanicsModel::displacement_increment array AKANTU_GET_MACRO_DEREF_PTR_NOT_CONST(Increment, displacement_increment); /// get the lumped SolidMechanicsModel::mass array AKANTU_GET_MACRO_DEREF_PTR(Mass, mass); /// get the SolidMechanicsModel::velocity array AKANTU_GET_MACRO_DEREF_PTR_NOT_CONST(Velocity, velocity); /// get the SolidMechanicsModel::velocity array AKANTU_GET_MACRO_DEREF_PTR(Velocity, velocity); /// get the SolidMechanicsModel::acceleration array AKANTU_GET_MACRO_DEREF_PTR_NOT_CONST(Acceleration, acceleration); /// get the SolidMechanicsModel::acceleration array AKANTU_GET_MACRO_DEREF_PTR(Acceleration, acceleration); /// get the SolidMechanicsModel::external_force array AKANTU_GET_MACRO_DEREF_PTR_NOT_CONST(ExternalForce, external_force); /// get the SolidMechanicsModel::external_force array AKANTU_GET_MACRO_DEREF_PTR(ExternalForce, external_force); /// get the SolidMechanicsModel::force array (external forces) [[deprecated("Use getExternalForce instead of this function")]] Array & getForce() { return getExternalForce(); } /// get the SolidMechanicsModel::internal_force array (internal forces) AKANTU_GET_MACRO_DEREF_PTR_NOT_CONST(InternalForce, internal_force); /// get the SolidMechanicsModel::internal_force array (internal forces) AKANTU_GET_MACRO_DEREF_PTR(InternalForce, internal_force); /// get the SolidMechanicsModel::blocked_dofs array AKANTU_GET_MACRO_DEREF_PTR_NOT_CONST(BlockedDOFs, blocked_dofs); /// get the SolidMechanicsModel::blocked_dofs array AKANTU_GET_MACRO_DEREF_PTR(BlockedDOFs, blocked_dofs); /// get an iterable on the materials inline decltype(auto) getMaterials(); /// get an iterable on the materials inline decltype(auto) getMaterials() const; /// get a particular material (by numerical material index) inline Material &getMaterial(UInt mat_index); /// get a particular material (by numerical material index) inline const Material &getMaterial(UInt mat_index) const; /// get a particular material (by material name) inline Material &getMaterial(const std::string &name); /// get a particular material (by material name) inline const Material &getMaterial(const std::string &name) const; + /// get a particular material (by material name) + inline const Material & getMaterial(const Element & element) const; + /// get a particular material id from is name inline Int getMaterialIndex(const std::string &name) const; /// give the number of materials inline Int getNbMaterials() const { return materials.size(); } /// give the material internal index from its id Int getInternalIndexFromID(const ID &id) const; /// compute the stable time step Real getStableTimeStep(); /** * @brief Returns the total energy for a given energy type * * Energy types of SolidMechanicsModel expected as argument are: * - `kinetic` * - `external work` * * Other energy types are passed on to the materials. All materials should * define a `potential` energy type. For additional energy types, see material * documentation. */ Real getEnergy(const std::string &energy_id); /// compute the energy for one element Real getEnergy(const std::string &energy_id, const Element &element); [[gnu::deprecated("Use the interface with an Element")]] Real getEnergy(const std::string &energy_id, ElementType type, Int index) { return getEnergy(energy_id, Element{type, index, _not_ghost}); } /// Compute energy for an element group Real getEnergy(const ID &energy_id, const ID &group_id); AKANTU_GET_MACRO_AUTO(MaterialByElement, material_index); AKANTU_GET_MACRO_AUTO(MaterialLocalNumbering, material_local_numbering); /// vectors containing local material element index for each global element /// index AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(MaterialByElement, material_index, Int); // AKANTU_GET_MACRO_BY_ELEMENT_TYPE(MaterialByElement, material_index, Int); AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(MaterialLocalNumbering, material_local_numbering, Int); // AKANTU_GET_MACRO_BY_ELEMENT_TYPE(MaterialLocalNumbering, // material_local_numbering, UInt); AKANTU_GET_MACRO_NOT_CONST(MaterialSelector, material_selector, std::shared_ptr); void setMaterialSelector(std::shared_ptr material_selector) { this->material_selector = std::move(material_selector); } /// Access the non_local_manager interface AKANTU_GET_MACRO(NonLocalManager, *non_local_manager, NonLocalManager &); /// get the FEEngine object to integrate or interpolate on the boundary FEEngine &getFEEngineBoundary(const ID &name = "") override; protected: /// compute the stable time step Real getStableTimeStep(GhostType ghost_type); /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ private: /// release version of the displacement array Int displacement_release{0}; /// release version of the current_position array Int current_position_release{0}; /// Check if materials need to recompute the mass array bool need_to_reassemble_lumped_mass{true}; /// Check if materials need to recompute the mass matrix bool need_to_reassemble_mass{true}; /// mapping between material name and material internal id std::map materials_names_to_id; protected: /// conversion coefficient form force/mass to acceleration Real f_m2a{1.0}; /// displacements array std::unique_ptr> displacement; /// displacements array at the previous time step (used in finite deformation) std::unique_ptr> previous_displacement; /// increment of displacement std::unique_ptr> displacement_increment; /// lumped mass array std::unique_ptr> mass; /// velocities array std::unique_ptr> velocity; /// accelerations array std::unique_ptr> acceleration; /// external forces array std::unique_ptr> external_force; /// internal forces array std::unique_ptr> internal_force; /// array specifing if a degree of freedom is blocked or not std::unique_ptr> blocked_dofs; /// array of current position used during update residual std::unique_ptr> current_position; /// Arrays containing the material index for each element ElementTypeMapArray material_index; /// Arrays containing the position in the element filter of the material /// (material's local numbering) ElementTypeMapArray material_local_numbering; /// list of used materials std::vector> materials; /// class defining of to choose a material std::shared_ptr material_selector; using flatten_internal_map = std::map, std::unique_ptr>>; /// tells if the material are instantiated flatten_internal_map registered_internals; /// non local manager std::unique_ptr non_local_manager; /// tells if the material are instantiated bool are_materials_instantiated{false}; friend class Material; template friend class CouplerSolidContactTemplate; }; /* -------------------------------------------------------------------------- */ namespace BC { namespace Neumann { using FromStress = FromHigherDim; using FromTraction = FromSameDim; } // namespace Neumann } // namespace BC } // namespace akantu /* -------------------------------------------------------------------------- */ /* inline functions */ /* -------------------------------------------------------------------------- */ #include "material.hh" #include "parser.hh" #include "solid_mechanics_model_inline_impl.hh" #include "solid_mechanics_model_tmpl.hh" /* -------------------------------------------------------------------------- */ #endif /* AKANTU_SOLID_MECHANICS_MODEL_HH_ */ diff --git a/src/model/solid_mechanics/solid_mechanics_model_cohesive/material_selector_cohesive.cc b/src/model/solid_mechanics/solid_mechanics_model_cohesive/material_selector_cohesive.cc index 72a5212f8..13faf653e 100644 --- a/src/model/solid_mechanics/solid_mechanics_model_cohesive/material_selector_cohesive.cc +++ b/src/model/solid_mechanics/solid_mechanics_model_cohesive/material_selector_cohesive.cc @@ -1,169 +1,166 @@ /** * @file material_selector_cohesive.cc * * @author Mauro Corrado * @author Nicolas Richart * @author Marco Vocialta * * @date creation: Fri Dec 11 2015 * @date last modification: Fri Apr 09 2021 * * @brief Material selector for cohesive elements * * * @section LICENSE * * Copyright (©) 2015-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "material_selector_cohesive.hh" #include "solid_mechanics_model_cohesive.hh" /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ DefaultMaterialCohesiveSelector::DefaultMaterialCohesiveSelector( const SolidMechanicsModelCohesive & model) : facet_material(model.getFacetMaterial()), mesh(model.getMesh()) { // backward compatibility v3: to get the former behavior back when the user // creates its own selector this->fallback_selector = std::make_shared(model.getMaterialByElement()); } /* -------------------------------------------------------------------------- */ Int DefaultMaterialCohesiveSelector::operator()(const Element & element) { if (Mesh::getKind(element.type) == _ek_cohesive) { try { const Array & cohesive_el_to_facet = mesh.getMeshFacets().getSubelementToElement(element.type, element.ghost_type); bool third_dimension = (mesh.getSpatialDimension() == 3); const Element & facet = cohesive_el_to_facet(element.element, UInt(third_dimension)); if (facet_material.exists(facet.type, facet.ghost_type)) { return facet_material(facet.type, facet.ghost_type)(facet.element); } return fallback_value; } catch (...) { return fallback_value; } } else if (Mesh::getSpatialDimension(element.type) == mesh.getSpatialDimension() - 1) { return facet_material(element.type, element.ghost_type)(element.element); } else { return MaterialSelector::operator()(element); } } /* -------------------------------------------------------------------------- */ MeshDataMaterialCohesiveSelector::MeshDataMaterialCohesiveSelector( const SolidMechanicsModelCohesive & model) : model(model), mesh_facets(model.getMeshFacets()), material_index(mesh_facets.getData("physical_names")) { third_dimension = (model.getSpatialDimension() == 3); // backward compatibility v3: to get the former behavior back when the user // creates its own selector this->fallback_selector = std::make_shared>("physical_names", model); } /* -------------------------------------------------------------------------- */ Int MeshDataMaterialCohesiveSelector::operator()(const Element & element) { if (Mesh::getKind(element.type) == _ek_cohesive or Mesh::getSpatialDimension(element.type) == mesh_facets.getSpatialDimension() - 1) { Element facet; if (Mesh::getKind(element.type) == _ek_cohesive) { facet = mesh_facets.getSubelementToElement(element.type, element.ghost_type)( element.element, UInt(third_dimension)); } else { facet = element; } try { std::string material_name = this->material_index(facet); return this->model.getMaterialIndex(material_name); } catch (...) { return fallback_value; } } return MaterialSelector::operator()(element); } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ MaterialCohesiveRulesSelector::MaterialCohesiveRulesSelector( const SolidMechanicsModelCohesive & model, const MaterialCohesiveRules & rules, ID mesh_data_id) // what we have here is the name of model and also // the name of different materials : model(model), mesh_data_id(std::move(mesh_data_id)), mesh(model.getMesh()), mesh_facets(model.getMeshFacets()), spatial_dimension(model.getSpatialDimension()), rules(rules) { // cohesive fallback this->fallback_selector = std::make_shared(model); // non cohesive fallback this->fallback_selector->setFallback( - std::make_shared>(mesh_data_id, - model)); + std::make_shared>( + this->mesh_data_id, model)); } /* -------------------------------------------------------------------------- */ Int MaterialCohesiveRulesSelector::operator()(const Element & element) { if (mesh_facets.getSpatialDimension(element.type) == (spatial_dimension - 1)) { - const auto & element_to_subelement = - mesh_facets.getElementToSubelement(element.type, - element.ghost_type)(element.element); - // Array & facets_check = model.getFacetsCheck(); - + const auto & element_to_subelement = mesh_facets.getElementToSubelement( + element.type, element.ghost_type)(element.element); const auto & el1 = element_to_subelement[0]; const auto & el2 = element_to_subelement[1]; auto id1 = mesh.getData(mesh_data_id, el1.type, - el1.ghost_type)(el1.element); + el1.ghost_type)(el1.element); auto id2 = id1; if (el2 != ElementNull) { id2 = mesh.getData(mesh_data_id, el2.type, el2.ghost_type)(el2.element); } auto rit = rules.find(std::make_pair(id1, id2)); if (rit == rules.end()) { rit = rules.find(std::make_pair(id2, id1)); } if (rit != rules.end()) { return model.getMaterialIndex(rit->second); } } return MaterialSelector::operator()(element); } } // namespace akantu diff --git a/src/model/solid_mechanics/solid_mechanics_model_inline_impl.hh b/src/model/solid_mechanics/solid_mechanics_model_inline_impl.hh index 93fa2d716..81704dd27 100644 --- a/src/model/solid_mechanics/solid_mechanics_model_inline_impl.hh +++ b/src/model/solid_mechanics/solid_mechanics_model_inline_impl.hh @@ -1,109 +1,117 @@ /** * @file solid_mechanics_model_inline_impl.hh * * @author Guillaume Anciaux * @author Daniel Pino Muñoz * @author Nicolas Richart * * @date creation: Wed Aug 04 2010 * @date last modification: Fri Mar 26 2021 * * @brief Implementation of the inline functions of the SolidMechanicsModel * class * * * @section LICENSE * * Copyright (©) 2015-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_named_argument.hh" #include "material_selector.hh" #include "material_selector_tmpl.hh" //#include "solid_mechanics_model.hh" /* -------------------------------------------------------------------------- */ #ifndef AKANTU_SOLID_MECHANICS_MODEL_INLINE_IMPL_HH_ #define AKANTU_SOLID_MECHANICS_MODEL_INLINE_IMPL_HH_ namespace akantu { /* -------------------------------------------------------------------------- */ inline decltype(auto) SolidMechanicsModel::getMaterials() { return make_dereference_adaptor(materials); } /* -------------------------------------------------------------------------- */ inline decltype(auto) SolidMechanicsModel::getMaterials() const { return make_dereference_adaptor(materials); } /* -------------------------------------------------------------------------- */ inline Material & SolidMechanicsModel::getMaterial(UInt mat_index) { AKANTU_DEBUG_ASSERT(mat_index < materials.size(), "The model " << id << " has no material no " << mat_index); return *materials.at(mat_index); } /* -------------------------------------------------------------------------- */ inline const Material & SolidMechanicsModel::getMaterial(UInt mat_index) const { AKANTU_DEBUG_ASSERT(mat_index < materials.size(), "The model " << id << " has no material no " << mat_index); return *materials.at(mat_index); } /* -------------------------------------------------------------------------- */ inline Material & SolidMechanicsModel::getMaterial(const std::string & name) { auto it = materials_names_to_id.find(name); - if(it == materials_names_to_id.end()) { - AKANTU_SILENT_EXCEPTION("The model " << id << " has no material named " << name); + if (it == materials_names_to_id.end()) { + AKANTU_SILENT_EXCEPTION("The model " << id << " has no material named " + << name); } return *materials[it->second]; } +/* -------------------------------------------------------------------------- */ +inline const Material & +SolidMechanicsModel::getMaterial(const Element & element) const { + auto mat_id = material_index(element); + return *materials[mat_id]; +} + /* -------------------------------------------------------------------------- */ inline Int SolidMechanicsModel::getMaterialIndex(const std::string & name) const { auto it = materials_names_to_id.find(name); if (it == materials_names_to_id.end()) { AKANTU_SILENT_EXCEPTION("The model " << id << " has no material named " << name); } return it->second; } /* -------------------------------------------------------------------------- */ inline const Material & SolidMechanicsModel::getMaterial(const std::string & name) const { auto it = materials_names_to_id.find(name); if (it == materials_names_to_id.end()) { AKANTU_SILENT_EXCEPTION("The model " << id << " has no material named " << name); } return *materials[it->second]; } /* -------------------------------------------------------------------------- */ } // namespace akantu #endif /* AKANTU_SOLID_MECHANICS_MODEL_INLINE_IMPL_HH_ */ diff --git a/src/model/solid_mechanics/solid_mechanics_model_io.cc b/src/model/solid_mechanics/solid_mechanics_model_io.cc index 1e0bc8ff3..3dba25afc 100644 --- a/src/model/solid_mechanics/solid_mechanics_model_io.cc +++ b/src/model/solid_mechanics/solid_mechanics_model_io.cc @@ -1,312 +1,324 @@ /** * @file solid_mechanics_model_io.cc * * @author Guillaume Anciaux * @author David Simon Kammer * @author Nicolas Richart * * @date creation: Sun Jul 09 2017 * @date last modification: Fri Apr 09 2021 * * @brief Dumpable part of the SolidMechnicsModel * * * @section LICENSE * * Copyright (©) 2016-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ #include "solid_mechanics_model.hh" #include "group_manager_inline_impl.hh" #include "dumpable_inline_impl.hh" /* -------------------------------------------------------------------------- */ #include "dumper_element_partition.hh" #include "dumper_elemental_field.hh" #include "dumper_field.hh" #include "dumper_homogenizing_field.hh" #include "dumper_internal_material_field.hh" #include "dumper_iohelper.hh" #include "dumper_material_padders.hh" #include "dumper_paraview.hh" /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ bool SolidMechanicsModel::isInternal(const std::string & field_name, ElementKind element_kind) { /// check if at least one material contains field_id as an internal for (auto & material : materials) { bool is_internal = material->isInternal(field_name, element_kind); if (is_internal) { return true; } } return false; } /* -------------------------------------------------------------------------- */ ElementTypeMap SolidMechanicsModel::getInternalDataPerElem(const std::string & field_name, ElementKind element_kind) { if (!(this->isInternal(field_name, element_kind))) { AKANTU_EXCEPTION("unknown internal " << field_name); } for (auto & material : materials) { if (material->isInternal(field_name, element_kind)) { return material->getInternalDataPerElem(field_name, element_kind); } } return ElementTypeMap(); } /* -------------------------------------------------------------------------- */ ElementTypeMapArray & SolidMechanicsModel::flattenInternal(const std::string & field_name, ElementKind kind, const GhostType ghost_type) { auto key = std::make_pair(field_name, kind); ElementTypeMapArray * internal_flat; auto it = this->registered_internals.find(key); if (it == this->registered_internals.end()) { auto internal = std::make_unique>(field_name, this->id); internal_flat = internal.get(); this->registered_internals[key] = std::move(internal); } else { internal_flat = it->second.get(); } for (auto type : mesh.elementTypes(Model::spatial_dimension, ghost_type, kind)) { if (internal_flat->exists(type, ghost_type)) { auto & internal = (*internal_flat)(type, ghost_type); internal.resize(0); } } for (auto & material : materials) { if (material->isInternal(field_name, kind)) { material->flattenInternal(field_name, *internal_flat, ghost_type, kind); } } return *internal_flat; } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::flattenAllRegisteredInternals(ElementKind kind) { ElementKind _kind; ID _id; for (auto & internal : this->registered_internals) { std::tie(_id, _kind) = internal.first; if (kind == _kind) { this->flattenInternal(_id, kind); } } } +/* -------------------------------------------------------------------------- */ +void SolidMechanicsModel::inflateInternal( + const std::string & field_name, const ElementTypeMapArray & field, + ElementKind kind, GhostType ghost_type) { + + for (auto & material : materials) { + if (material->isInternal(field_name, kind)) { + material->inflateInternal(field_name, field, ghost_type, kind); + } + } +} + /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::onDump() { this->flattenAllRegisteredInternals(_ek_regular); } /* -------------------------------------------------------------------------- */ std::shared_ptr SolidMechanicsModel::createElementalField( const std::string & field_name, const std::string & group_name, bool padding_flag, Int spatial_dimension, ElementKind kind) { std::shared_ptr field; if (field_name == "partitions") { field = mesh.createElementalField( mesh.getConnectivities(), group_name, spatial_dimension, kind); } else if (field_name == "material_index") { field = mesh.createElementalField, dumpers::ElementalField>( material_index, group_name, spatial_dimension, kind); } else { // this copy of field_name is used to compute derivated data such as // strain and von mises stress that are based on grad_u and stress std::string field_name_copy(field_name); if (field_name == "strain" || field_name == "Green strain" || field_name == "principal strain" || field_name == "principal Green strain") { field_name_copy = "grad_u"; } else if (field_name == "Von Mises stress") { field_name_copy = "stress"; } bool is_internal = this->isInternal(field_name_copy, kind); if (is_internal) { auto nb_data_per_elem = this->getInternalDataPerElem(field_name_copy, kind); auto & internal_flat = this->flattenInternal(field_name_copy, kind); field = mesh.createElementalField( internal_flat, group_name, spatial_dimension, kind, nb_data_per_elem); std::unique_ptr func; if (field_name == "strain") { func = std::make_unique>(*this); } else if (field_name == "Von Mises stress") { func = std::make_unique(*this); } else if (field_name == "Green strain") { func = std::make_unique>(*this); } else if (field_name == "principal strain") { func = std::make_unique>(*this); } else if (field_name == "principal Green strain") { func = std::make_unique>(*this); } if (func) { field = dumpers::FieldComputeProxy::createFieldCompute(field, std::move(func)); } // treat the paddings if (padding_flag) { if (field_name == "stress") { if (spatial_dimension == 2) { auto foo = std::make_unique>(*this); field = dumpers::FieldComputeProxy::createFieldCompute( field, std::move(foo)); } } else if (field_name == "strain" || field_name == "Green strain") { if (spatial_dimension == 2) { auto foo = std::make_unique>(*this); field = dumpers::FieldComputeProxy::createFieldCompute( field, std::move(foo)); } } } // homogenize the field auto foo = dumpers::HomogenizerProxy::createHomogenizer(*field); field = dumpers::FieldComputeProxy::createFieldCompute(field, std::move(foo)); } } return field; } /* -------------------------------------------------------------------------- */ std::shared_ptr SolidMechanicsModel::createNodalFieldReal(const std::string & field_name, const std::string & group_name, bool padding_flag) { std::map *> real_nodal_fields; real_nodal_fields["displacement"] = this->displacement.get(); real_nodal_fields["mass"] = this->mass.get(); real_nodal_fields["velocity"] = this->velocity.get(); real_nodal_fields["acceleration"] = this->acceleration.get(); real_nodal_fields["external_force"] = this->external_force.get(); real_nodal_fields["internal_force"] = this->internal_force.get(); real_nodal_fields["increment"] = this->displacement_increment.get(); if (field_name == "force") { AKANTU_EXCEPTION("The 'force' field has been renamed in 'external_force'"); } else if (field_name == "residual") { AKANTU_EXCEPTION( "The 'residual' field has been replaced by 'internal_force'"); } std::shared_ptr field; if (padding_flag) { field = this->mesh.createNodalField(real_nodal_fields[field_name], group_name, 3); } else { field = this->mesh.createNodalField(real_nodal_fields[field_name], group_name); } return field; } /* -------------------------------------------------------------------------- */ std::shared_ptr SolidMechanicsModel::createNodalFieldBool( const std::string & field_name, const std::string & group_name, __attribute__((unused)) bool padding_flag) { std::map *> uint_nodal_fields; uint_nodal_fields["blocked_dofs"] = blocked_dofs.get(); std::shared_ptr field; field = mesh.createNodalField(uint_nodal_fields[field_name], group_name); return field; } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::dump(const std::string & dumper_name) { this->onDump(); EventManager::sendEvent(SolidMechanicsModelEvent::BeforeDumpEvent()); mesh.dump(dumper_name); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::dump(const std::string & dumper_name, Int step) { this->onDump(); EventManager::sendEvent(SolidMechanicsModelEvent::BeforeDumpEvent()); mesh.dump(dumper_name, step); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::dump(const std::string & dumper_name, Real time, Int step) { this->onDump(); EventManager::sendEvent(SolidMechanicsModelEvent::BeforeDumpEvent()); mesh.dump(dumper_name, time, step); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::dump() { this->onDump(); EventManager::sendEvent(SolidMechanicsModelEvent::BeforeDumpEvent()); mesh.dump(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::dump(Int step) { this->onDump(); EventManager::sendEvent(SolidMechanicsModelEvent::BeforeDumpEvent()); mesh.dump(step); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::dump(Real time, Int step) { this->onDump(); EventManager::sendEvent(SolidMechanicsModelEvent::BeforeDumpEvent()); mesh.dump(time, step); } } // namespace akantu diff --git a/src/model/solid_mechanics/solid_mechanics_model_mass.cc b/src/model/solid_mechanics/solid_mechanics_model_mass.cc index c1613755e..547c6d41b 100644 --- a/src/model/solid_mechanics/solid_mechanics_model_mass.cc +++ b/src/model/solid_mechanics/solid_mechanics_model_mass.cc @@ -1,154 +1,158 @@ /** * @file solid_mechanics_model_mass.cc * * @author Nicolas Richart * * @date creation: Tue Oct 05 2010 * @date last modification: Fri Jul 24 2020 * * @brief function handling mass computation * * * @section LICENSE * * Copyright (©) 2010-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "integrator_gauss.hh" #include "material.hh" #include "model_solver.hh" #include "shape_lagrange.hh" #include "solid_mechanics_model.hh" /* -------------------------------------------------------------------------- */ namespace akantu { class ComputeRhoFunctor { public: explicit ComputeRhoFunctor(const SolidMechanicsModel & model) : model(model){}; void operator()(Matrix & rho, const Element & element) { const auto & mat_indexes = model.getMaterialByElement(element.type, element.ghost_type); Real mat_rho = model.getMaterial(mat_indexes(element.element)).getParam("rho"); rho.set(mat_rho); } private: const SolidMechanicsModel & model; }; /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::assembleMassLumped() { AKANTU_DEBUG_IN(); if (not need_to_reassemble_lumped_mass) { return; } this->allocNodalField(this->mass, spatial_dimension, "mass"); mass->zero(); if (!this->getDOFManager().hasLumpedMatrix("M")) { this->getDOFManager().getNewLumpedMatrix("M"); } this->getDOFManager().zeroLumpedMatrix("M"); assembleMassLumped(_not_ghost); assembleMassLumped(_ghost); this->getDOFManager().getLumpedMatrixPerDOFs("displacement", "M", *(this->mass)); /// for not connected nodes put mass to one in order to avoid #if !defined(AKANTU_NDEBUG) std::set unconnected_nodes; for (auto && data : enumerate(make_view(*mass))) { auto & m = std::get<1>(data); if (std::abs(m) < std::numeric_limits::epsilon() || Math::isnan(m)) { m = 0.; unconnected_nodes.insert(std::get<0>(data)); } } if (unconnected_nodes.begin() != unconnected_nodes.end()) { AKANTU_DEBUG_WARNING("There are nodes that seem to not be connected to any " "elements, beware that they have lumped mass of 0."); } #endif this->synchronize(SynchronizationTag::_smm_mass); need_to_reassemble_lumped_mass = false; AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::assembleMass() { AKANTU_DEBUG_IN(); + if (not this->getDOFManager().hasMatrix("M")) { + this->getDOFManager().getNewMatrix("M", this->getMatrixType("M")); + } + if (not need_to_reassemble_mass) { return; } this->getDOFManager().zeroMatrix("M"); assembleMass(_not_ghost); need_to_reassemble_mass = false; AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::assembleMassLumped(GhostType ghost_type) { AKANTU_DEBUG_IN(); auto & fem = getFEEngineClass(); ComputeRhoFunctor compute_rho(*this); for (auto type : mesh.elementTypes(Model::spatial_dimension, ghost_type, _ek_regular)) { fem.assembleFieldLumped(compute_rho, "M", "displacement", this->getDOFManager(), type, ghost_type); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::assembleMass(GhostType ghost_type) { AKANTU_DEBUG_IN(); auto & fem = getFEEngineClass(); ComputeRhoFunctor compute_rho(*this); for (auto type : mesh.elementTypes(Model::spatial_dimension, ghost_type, _ek_regular)) { fem.assembleFieldMatrix(compute_rho, "M", "displacement", this->getDOFManager(), type, ghost_type); } AKANTU_DEBUG_OUT(); } } // namespace akantu diff --git a/src/model/solid_mechanics/solid_mechanics_model_material.cc b/src/model/solid_mechanics/solid_mechanics_model_material.cc index e0b26be70..9993783b8 100644 --- a/src/model/solid_mechanics/solid_mechanics_model_material.cc +++ b/src/model/solid_mechanics/solid_mechanics_model_material.cc @@ -1,246 +1,247 @@ /** * @file solid_mechanics_model_material.cc * * @author Guillaume Anciaux * @author Aurelia Isabel Cuba Ramos * @author Nicolas Richart * * @date creation: Fri Nov 26 2010 * @date last modification: Fri Mar 26 2021 * * @brief instatiation of materials * * * @section LICENSE * * Copyright (©) 2010-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_factory.hh" #include "aka_math.hh" #include "material_non_local.hh" #include "mesh_iterators.hh" #include "non_local_manager.hh" #include "solid_mechanics_model.hh" /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ Material & SolidMechanicsModel::registerNewMaterial(const ParserSection & section) { std::string mat_name; std::string mat_type = section.getName(); std::string opt_param = section.getOption(); try { std::string tmp = section.getParameter("name"); mat_name = tmp; /** this can seam weird, but there is an ambiguous operator * overload that i couldn't solve. @todo remove the * weirdness of this code */ } catch (debug::Exception &) { AKANTU_ERROR("A material of type \'" << mat_type << "\' in the input file has been defined without a name!"); } Material & mat = this->registerNewMaterial(mat_name, mat_type, opt_param); mat.parseSection(section); return mat; } /* -------------------------------------------------------------------------- */ Material & SolidMechanicsModel::registerNewMaterial(const ID & mat_name, const ID & mat_type, const ID & opt_param) { AKANTU_DEBUG_ASSERT(materials_names_to_id.find(mat_name) == materials_names_to_id.end(), "A material with this name '" << mat_name << "' has already been registered. " << "Please use unique names for materials"); auto mat_count = materials.size(); materials_names_to_id[mat_name] = mat_count; - std::stringstream sstr_mat; - sstr_mat << this->id << ":" << mat_count << ":" << mat_type; - ID mat_id = sstr_mat.str(); + ID mat_id = this->id + ":" + std::to_string(mat_count) + ":" + mat_type; std::unique_ptr material = MaterialFactory::getInstance().allocate( mat_type, spatial_dimension, opt_param, *this, mat_id); - + material->setName(mat_name); materials.push_back(std::move(material)); return *(materials.back()); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::instantiateMaterials() { ParserSection model_section; bool is_empty; std::tie(model_section, is_empty) = this->getParserSection(); if (not is_empty) { auto model_materials = model_section.getSubSections(ParserType::_material); for (const auto & section : model_materials) { this->registerNewMaterial(section); } } auto sub_sections = this->parser.getSubSections(ParserType::_material); for (const auto & section : sub_sections) { this->registerNewMaterial(section); } #ifdef AKANTU_DAMAGE_NON_LOCAL for (auto & material : materials) { if (dynamic_cast(material.get()) == nullptr) { continue; } this->non_local_manager = std::make_unique( *this, *this, id + ":non_local_manager"); break; } #endif if (materials.empty()) { AKANTU_EXCEPTION("No materials where instantiated for the model" << getID()); } are_materials_instantiated = true; } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::assignMaterialToElements( const ElementTypeMapArray * filter) { for_each_element( mesh, [&](auto && element) { auto mat_index = (*material_selector)(element); AKANTU_DEBUG_ASSERT( mat_index < Int(materials.size()), "The material selector returned an index that does not exists"); material_index(element) = mat_index; }, _element_filter = filter, _ghost_type = _not_ghost); if (non_local_manager) { non_local_manager->synchronize(*this, SynchronizationTag::_material_id); } for_each_element( mesh, [&](auto && element) { auto mat_index = material_index(element); auto index = materials[mat_index]->addElement(element); material_local_numbering(element) = index; }, _element_filter = filter, _ghost_type = _not_ghost); // synchronize the element material arrays this->synchronize(SynchronizationTag::_material_id); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::initMaterials() { AKANTU_DEBUG_ASSERT(not materials.empty(), "No material to initialize !"); // if (!are_materials_instantiated) // instantiateMaterials(); this->assignMaterialToElements(); for (auto & material : materials) { /// init internals properties material->initMaterial(); } this->synchronize(SynchronizationTag::_smm_init_mat); if (this->non_local_manager) { this->non_local_manager->initialize(); } } /* -------------------------------------------------------------------------- */ Int SolidMechanicsModel::getInternalIndexFromID(const ID & id) const { AKANTU_DEBUG_IN(); auto it = materials.begin(); auto end = materials.end(); for (; it != end; ++it) { if ((*it)->getID() == id) { AKANTU_DEBUG_OUT(); return (it - materials.begin()); } } AKANTU_DEBUG_OUT(); return -1; } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::reassignMaterial() { AKANTU_DEBUG_IN(); std::vector> element_to_add(materials.size()); std::vector> element_to_remove(materials.size()); - for_each_element(mesh, [&](auto && element) { - auto old_material = material_index(element); - auto new_material = (*material_selector)(element); - if (old_material != new_material) { - element_to_add[new_material].push_back(element); - element_to_remove[old_material].push_back(element); - } - }); + for_each_element( + mesh, + [&](auto && element) { + auto old_material = material_index(element); + auto new_material = (*material_selector)(element); + if (old_material != new_material) { + element_to_add[new_material].push_back(element); + element_to_remove[old_material].push_back(element); + } + }, + _spatial_dimension = spatial_dimension); for (auto && data : enumerate(materials)) { auto mat_index = std::get<0>(data); auto & mat = *std::get<1>(data); mat.removeElements(element_to_remove[mat_index]); mat.addElements(element_to_add[mat_index]); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::applyEigenGradU( const Matrix & prescribed_eigen_grad_u, const ID & material_name, const GhostType ghost_type) { AKANTU_DEBUG_ASSERT(prescribed_eigen_grad_u.size() == spatial_dimension * spatial_dimension, "The prescribed grad_u is not of the good size"); for (auto & material : materials) { if (material->getName() == material_name) { material->applyEigenGradU(prescribed_eigen_grad_u, ghost_type); } } } /* -------------------------------------------------------------------------- */ } // namespace akantu diff --git a/src/model/structural_mechanics/structural_mechanics_model.cc b/src/model/structural_mechanics/structural_mechanics_model.cc index 273142544..3198fb7ab 100644 --- a/src/model/structural_mechanics/structural_mechanics_model.cc +++ b/src/model/structural_mechanics/structural_mechanics_model.cc @@ -1,621 +1,776 @@ /** * @file structural_mechanics_model.cc * * @author Fabian Barras * @author Lucas Frerot * @author Sébastien Hartmann * @author Nicolas Richart * @author Damien Spielmann * * @date creation: Fri Jul 15 2011 * @date last modification: Mon Mar 15 2021 * * @brief Model implementation for Structural Mechanics elements * * * @section LICENSE * * Copyright (©) 2010-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "structural_mechanics_model.hh" #include "dof_manager.hh" #include "integrator_gauss.hh" #include "mesh.hh" #include "shape_structural.hh" #include "sparse_matrix.hh" #include "time_step_solver.hh" /* -------------------------------------------------------------------------- */ #include "dumpable_inline_impl.hh" #include "dumper_elemental_field.hh" #include "dumper_internal_material_field.hh" #include "dumper_iohelper_paraview.hh" #include "group_manager_inline_impl.hh" /* -------------------------------------------------------------------------- */ #include "structural_element_bernoulli_beam_2.hh" #include "structural_element_bernoulli_beam_3.hh" #include "structural_element_kirchhoff_shell.hh" /* -------------------------------------------------------------------------- */ //#include "structural_mechanics_model_inline_impl.hh" /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ inline UInt StructuralMechanicsModel::getNbDegreeOfFreedom(ElementType type) { return tuple_dispatch>( - [&](auto &&enum_type) { + [&](auto && enum_type) { constexpr ElementType type = std::decay_t::value; return ElementClass::getNbDegreeOfFreedom(); }, type); } /* -------------------------------------------------------------------------- */ -StructuralMechanicsModel::StructuralMechanicsModel(Mesh &mesh, Int dim, - const ID &id) +StructuralMechanicsModel::StructuralMechanicsModel(Mesh & mesh, Int dim, + const ID & id) : Model(mesh, ModelType::_structural_mechanics_model, dim, id), f_m2a(1.0), stress("stress", id), element_material("element_material", id), set_ID("beam sets", id) { AKANTU_DEBUG_IN(); registerFEEngineObject("StructuralMechanicsFEEngine", mesh, spatial_dimension); if (spatial_dimension == 2) { nb_degree_of_freedom = 3; } else if (spatial_dimension == 3) { nb_degree_of_freedom = 6; } else { AKANTU_TO_IMPLEMENT(); } this->mesh.registerDumper("structural_mechanics_model", id, true); this->mesh.addDumpMesh(mesh, spatial_dimension, _not_ghost, _ek_structural); this->initDOFManager(); this->dumper_default_element_kind = _ek_structural; mesh.getElementalData("extra_normal") .initialize(mesh, _element_kind = _ek_structural, _nb_component = spatial_dimension, _with_nb_element = true, _default_value = 0.); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ StructuralMechanicsModel::~StructuralMechanicsModel() = default; /* -------------------------------------------------------------------------- */ -void StructuralMechanicsModel::initFullImpl(const ModelOptions &options) { +void StructuralMechanicsModel::initFullImpl(const ModelOptions & options) { Model::initFullImpl(options); // Initializing stresses ElementTypeMap stress_components; /// TODO this is ugly af, maybe add a function to FEEngine - for (auto &&type : mesh.elementTypes(_spatial_dimension = _all_dimensions, - _element_kind = _ek_structural)) { + for (auto && type : mesh.elementTypes(_spatial_dimension = _all_dimensions, + _element_kind = _ek_structural)) { UInt nb_components = 0; // Getting number of components for each element type #define GET_(type) nb_components = ElementClass::getNbStressComponents() AKANTU_BOOST_STRUCTURAL_ELEMENT_SWITCH(GET_); #undef GET_ stress_components(nb_components, type); } stress.initialize( getFEEngine(), _spatial_dimension = _all_dimensions, _element_kind = _ek_structural, _nb_component = [&stress_components](ElementType type, GhostType /*unused*/) -> UInt { return stress_components(type); }); } /* -------------------------------------------------------------------------- */ void StructuralMechanicsModel::initFEEngineBoundary() { /// TODO: this function should not be reimplemented /// we're just avoiding a call to Model::initFEEngineBoundary() } /* -------------------------------------------------------------------------- */ void StructuralMechanicsModel::setTimeStep(Real time_step, - const ID &solver_id) { + const ID & solver_id) { Model::setTimeStep(time_step, solver_id); this->mesh.getDumper().setTimeStep(time_step); } /* -------------------------------------------------------------------------- */ /* Initialisation */ /* -------------------------------------------------------------------------- */ void StructuralMechanicsModel::initSolver( TimeStepSolverType time_step_solver_type, NonLinearSolverType /*unused*/) { AKANTU_DEBUG_IN(); this->allocNodalField(displacement_rotation, nb_degree_of_freedom, "displacement"); this->allocNodalField(external_force, nb_degree_of_freedom, "external_force"); this->allocNodalField(internal_force, nb_degree_of_freedom, "internal_force"); this->allocNodalField(blocked_dofs, nb_degree_of_freedom, "blocked_dofs"); - auto &dof_manager = this->getDOFManager(); + auto & dof_manager = this->getDOFManager(); if (not dof_manager.hasDOFs("displacement")) { dof_manager.registerDOFs("displacement", *displacement_rotation, _dst_nodal); dof_manager.registerBlockedDOFs("displacement", *this->blocked_dofs); } if (time_step_solver_type == TimeStepSolverType::_dynamic || time_step_solver_type == TimeStepSolverType::_dynamic_lumped) { this->allocNodalField(velocity, nb_degree_of_freedom, "velocity"); this->allocNodalField(acceleration, nb_degree_of_freedom, "acceleration"); if (!dof_manager.hasDOFsDerivatives("displacement", 1)) { dof_manager.registerDOFsDerivative("displacement", 1, *this->velocity); dof_manager.registerDOFsDerivative("displacement", 2, *this->acceleration); } + + /* Only allocate the mass if the "lumped" mode is ennabled. + * Also it is not a 1D array, but has an element for every + * DOF, which are most of the time equal, but makes handling + * some operations a bit simpler. */ + if (time_step_solver_type == TimeStepSolverType::_dynamic_lumped) { + this->allocateLumpedMassArray(); + } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void StructuralMechanicsModel::initModel() { element_material.initialize(mesh, _element_kind = _ek_structural, _default_value = 0, _with_nb_element = true); getFEEngine().initShapeFunctions(_not_ghost); getFEEngine().initShapeFunctions(_ghost); } /* -------------------------------------------------------------------------- */ void StructuralMechanicsModel::assembleStiffnessMatrix() { AKANTU_DEBUG_IN(); if (not need_to_reassemble_stiffness) { return; } if (not getDOFManager().hasMatrix("K")) { getDOFManager().getNewMatrix("K", getMatrixType("K")); } this->getDOFManager().zeroMatrix("K"); - for (const auto &type : + for (const auto & type : mesh.elementTypes(spatial_dimension, _not_ghost, _ek_structural)) { #define ASSEMBLE_STIFFNESS_MATRIX(type) assembleStiffnessMatrix(); AKANTU_BOOST_STRUCTURAL_ELEMENT_SWITCH(ASSEMBLE_STIFFNESS_MATRIX); #undef ASSEMBLE_STIFFNESS_MATRIX } need_to_reassemble_stiffness = false; AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void StructuralMechanicsModel::computeStresses() { AKANTU_DEBUG_IN(); - for (const auto &type : + for (const auto & type : mesh.elementTypes(spatial_dimension, _not_ghost, _ek_structural)) { #define COMPUTE_STRESS_ON_QUAD(type) computeStressOnQuad(); AKANTU_BOOST_STRUCTURAL_ELEMENT_SWITCH(COMPUTE_STRESS_ON_QUAD); #undef COMPUTE_STRESS_ON_QUAD } AKANTU_DEBUG_OUT(); } +/* -------------------------------------------------------------------------- */ +bool StructuralMechanicsModel::allocateLumpedMassArray() { + if (this->mass != nullptr) // Already allocated, so nothing to do. + { + return true; + }; + + // now allocate it + this->allocNodalField(this->mass, this->nb_degree_of_freedom, "lumped_mass"); + + return true; +}; + /* -------------------------------------------------------------------------- */ std::shared_ptr StructuralMechanicsModel::createNodalFieldBool( - const std::string &field_name, const std::string &group_name, + const std::string & field_name, const std::string & group_name, __attribute__((unused)) bool padding_flag) { std::map *> uint_nodal_fields; uint_nodal_fields["blocked_dofs"] = blocked_dofs.get(); return mesh.createNodalField(uint_nodal_fields[field_name], group_name); } /* -------------------------------------------------------------------------- */ std::shared_ptr -StructuralMechanicsModel::createNodalFieldReal(const std::string &field_name, - const std::string &group_name, +StructuralMechanicsModel::createNodalFieldReal(const std::string & field_name, + const std::string & group_name, bool padding_flag) { UInt n; if (spatial_dimension == 2) { n = 2; } else { n = 3; } UInt padding_size = 0; if (padding_flag) { padding_size = 3; } if (field_name == "displacement") { return mesh.createStridedNodalField(displacement_rotation.get(), group_name, n, 0, padding_size); } if (field_name == "velocity") { return mesh.createStridedNodalField(velocity.get(), group_name, n, 0, padding_size); } if (field_name == "acceleration") { return mesh.createStridedNodalField(acceleration.get(), group_name, n, 0, padding_size); } if (field_name == "rotation") { return mesh.createStridedNodalField(displacement_rotation.get(), group_name, nb_degree_of_freedom - n, n, padding_size); } if (field_name == "force") { return mesh.createStridedNodalField(external_force.get(), group_name, n, 0, padding_size); } if (field_name == "external_force") { return mesh.createStridedNodalField(external_force.get(), group_name, n, 0, padding_size); } if (field_name == "momentum") { return mesh.createStridedNodalField(external_force.get(), group_name, nb_degree_of_freedom - n, n, padding_size); } if (field_name == "internal_force") { return mesh.createStridedNodalField(internal_force.get(), group_name, n, 0, padding_size); } if (field_name == "internal_momentum") { return mesh.createStridedNodalField(internal_force.get(), group_name, nb_degree_of_freedom - n, n, padding_size); } + if (field_name == "mass") { + AKANTU_DEBUG_ASSERT(this->mass.get() != nullptr, + "The lumped mass matrix was not allocated."); + return mesh.createStridedNodalField(this->mass.get(), group_name, n, 0, + padding_size); + } + return nullptr; } /* -------------------------------------------------------------------------- */ std::shared_ptr StructuralMechanicsModel::createElementalField( - const std::string &field_name, const std::string &group_name, + const std::string & field_name, const std::string & group_name, bool /*unused*/, Int spatial_dimension, ElementKind kind) { std::shared_ptr field; if (field_name == "element_index_by_material") { field = mesh.createElementalField, dumpers::ElementalField>( field_name, group_name, spatial_dimension, kind); } if (field_name == "stress") { ElementTypeMap nb_data_per_elem = this->mesh.getNbDataPerElem(stress); field = mesh.createElementalField( stress, group_name, this->spatial_dimension, kind, nb_data_per_elem); } return field; } /* -------------------------------------------------------------------------- */ /* Virtual methods from SolverCallback */ /* -------------------------------------------------------------------------- */ /// get the type of matrix needed MatrixType StructuralMechanicsModel::getMatrixType(const ID & /*id*/) const { return _symmetric; } /// callback to assemble a Matrix -void StructuralMechanicsModel::assembleMatrix(const ID &id) { +void StructuralMechanicsModel::assembleMatrix(const ID & id) { if (id == "K") { assembleStiffnessMatrix(); } else if (id == "M") { assembleMassMatrix(); } } /// callback to assemble a lumped Matrix -void StructuralMechanicsModel::assembleLumpedMatrix(const ID & /*id*/) {} +void StructuralMechanicsModel::assembleLumpedMatrix(const ID & id) { + if ("M" == id) { + this->assembleLumpedMassMatrix(); + } + return; +} /// callback to assemble the residual StructuralMechanicsModel::(rhs) void StructuralMechanicsModel::assembleResidual() { AKANTU_DEBUG_IN(); - auto &dof_manager = getDOFManager(); + auto & dof_manager = getDOFManager(); assembleInternalForce(); + // Ensures that the matrix are assembled. + if (dof_manager.hasMatrix("K")) { + this->assembleMatrix("K"); + } + if (dof_manager.hasMatrix("M")) { + this->assembleMatrix("M"); + } + if (dof_manager.hasLumpedMatrix("M")) { + this->assembleLumpedMassMatrix(); + } + + /* This is essentially a summing up of forces + * first the external forces are counted for and then stored inside the + * residual. + */ dof_manager.assembleToResidual("displacement", *external_force, 1); dof_manager.assembleToResidual("displacement", *internal_force, 1); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ -void StructuralMechanicsModel::assembleResidual(const ID &residual_part) { +void StructuralMechanicsModel::assembleResidual(const ID & residual_part) { AKANTU_DEBUG_IN(); + auto & dof_manager = this->getDOFManager(); + if ("external" == residual_part) { - this->getDOFManager().assembleToResidual("displacement", - *this->external_force, 1); + dof_manager.assembleToResidual("displacement", *this->external_force, 1); AKANTU_DEBUG_OUT(); return; } if ("internal" == residual_part) { this->assembleInternalForce(); - this->getDOFManager().assembleToResidual("displacement", - *this->internal_force, 1); + dof_manager.assembleToResidual("displacement", *this->internal_force, 1); AKANTU_DEBUG_OUT(); return; } AKANTU_CUSTOM_EXCEPTION( debug::SolverCallbackResidualPartUnknown(residual_part)); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ /* Virtual methods from Model */ /* -------------------------------------------------------------------------- */ /// get some default values for derived classes std::tuple -StructuralMechanicsModel::getDefaultSolverID(const AnalysisMethod &method) { +StructuralMechanicsModel::getDefaultSolverID(const AnalysisMethod & method) { switch (method) { case _static: { return std::make_tuple("static", TimeStepSolverType::_static); } case _implicit_dynamic: { return std::make_tuple("implicit", TimeStepSolverType::_dynamic); } + case _explicit_lumped_mass: { // Taken from the solid mechanics part + return std::make_tuple("explicit_lumped", + TimeStepSolverType::_dynamic_lumped); + } + case _explicit_consistent_mass: { // Taken from the solid mechanics part + return std::make_tuple("explicit", TimeStepSolverType::_dynamic); + } default: + std::cout << "UNKOWN." << std::endl; return std::make_tuple("unknown", TimeStepSolverType::_not_defined); } } /* ------------------------------------------------------------------------ */ ModelSolverOptions StructuralMechanicsModel::getDefaultSolverOptions( - const TimeStepSolverType &type) const { + const TimeStepSolverType & type) const { ModelSolverOptions options; switch (type) { + case TimeStepSolverType::_dynamic_lumped: { // Taken from the solid mechanic + // part + options.non_linear_solver_type = NonLinearSolverType::_lumped; + options.integration_scheme_type["displacement"] = + IntegrationSchemeType::_central_difference; + options.solution_type["displacement"] = IntegrationScheme::_acceleration; + break; + } case TimeStepSolverType::_static: { - options.non_linear_solver_type = NonLinearSolverType::_newton_raphson; + options.non_linear_solver_type = + NonLinearSolverType::_newton_raphson; // _linear; options.integration_scheme_type["displacement"] = IntegrationSchemeType::_pseudo_time; options.solution_type["displacement"] = IntegrationScheme::_not_defined; break; } - case TimeStepSolverType::_dynamic: { +#if 1 + case TimeStepSolverType::_dynamic: { // Copied from solid + if (this->method == _explicit_consistent_mass) { + options.non_linear_solver_type = NonLinearSolverType::_newton_raphson; + options.integration_scheme_type["displacement"] = + IntegrationSchemeType::_central_difference; + options.solution_type["displacement"] = IntegrationScheme::_acceleration; + } else { + options.non_linear_solver_type = NonLinearSolverType::_newton_raphson; + options.integration_scheme_type["displacement"] = + IntegrationSchemeType::_trapezoidal_rule_2; + options.solution_type["displacement"] = IntegrationScheme::_displacement; + } + break; + } +#else + case TimeStepSolverType::_dynamic: { // Original options.non_linear_solver_type = NonLinearSolverType::_newton_raphson; options.integration_scheme_type["displacement"] = IntegrationSchemeType::_trapezoidal_rule_2; options.solution_type["displacement"] = IntegrationScheme::_displacement; break; } +#endif default: AKANTU_EXCEPTION(type << " is not a valid time step solver type"); } return options; } /* -------------------------------------------------------------------------- */ void StructuralMechanicsModel::assembleInternalForce() { + internal_force->zero(); computeStresses(); for (auto type : mesh.elementTypes(_spatial_dimension = _all_dimensions, _element_kind = _ek_structural)) { assembleInternalForce(type, _not_ghost); // assembleInternalForce(type, _ghost); } } /* -------------------------------------------------------------------------- */ void StructuralMechanicsModel::assembleInternalForce(ElementType type, GhostType gt) { - auto &fem = getFEEngine(); - auto &sigma = stress(type, gt); + auto & fem = getFEEngine(); + auto & sigma = stress(type, gt); auto ndof = getNbDegreeOfFreedom(type); auto nb_nodes = mesh.getNbNodesPerElement(type); auto ndof_per_elem = ndof * nb_nodes; Array BtSigma(fem.getNbIntegrationPoints(type) * mesh.getNbElement(type), ndof_per_elem, "BtSigma"); fem.computeBtD(sigma, BtSigma, type, gt); Array intBtSigma(0, ndof_per_elem, "intBtSigma"); fem.integrate(BtSigma, intBtSigma, ndof_per_elem, type, gt); getDOFManager().assembleElementalArrayLocalArray(intBtSigma, *internal_force, type, gt, -1.); } /* -------------------------------------------------------------------------- */ Real StructuralMechanicsModel::getKineticEnergy() { - if (not this->getDOFManager().hasMatrix("M")) { - return 0.; - } - Real ekin = 0.; - UInt nb_nodes = mesh.getNbNodes(); + const UInt nb_nodes = mesh.getNbNodes(); + const UInt nb_degree_of_freedom = this->nb_degree_of_freedom; + Real ekin = 0.; // used to sum up energy (is divided by two at the very end) - Array Mv(nb_nodes, nb_degree_of_freedom); - this->getDOFManager().assembleMatMulVectToArray("displacement", "M", - *this->velocity, Mv); + // if mass matrix was not assembled, assemble it now + this->assembleMassMatrix(); - for (auto &&data : zip(arange(nb_nodes), make_view(Mv, nb_degree_of_freedom), - make_view(*this->velocity, nb_degree_of_freedom))) { - ekin += std::get<2>(data).dot(std::get<1>(data)) * - static_cast(mesh.isLocalOrMasterNode(std::get<0>(data))); + if (this->getDOFManager().hasLumpedMatrix("M")) { + /* This code computes the kinetic energy for the case when the mass is + * lumped. It is based on the solid mechanic equivalent. + */ + AKANTU_DEBUG_ASSERT(this->mass != nullptr, + "The lumped mass is not allocated."); + + if (this->need_to_reassemble_lumpedMass) { + this->assembleLumpedMatrix("M"); + } + + /* Iterating over all nodes. + * Important the velocity and mass also contains the rotational parts. + * However, they can be handled in an uniform way. */ + for (auto && data : + zip(arange(nb_nodes), make_view(*this->velocity, nb_degree_of_freedom), + make_view(*this->mass, nb_degree_of_freedom))) { + const UInt n = std::get<0>(data); // This is the ID of the current node + + if (not mesh.isLocalOrMasterNode( + n)) // Only handle the node if it belongs to us. + { + continue; + } + + const auto & v = + std::get<1>(data); // Get the velocity and mass of that node. + const auto & m = std::get<2>(data); + Real mv2 = 0.; // Contribution of this node. + + for (UInt i = 0; i < nb_degree_of_freedom; ++i) { + /* In the solid mechanics part, only masses that are above a certain + * value are considered. + * However, the structural part, does not do this. */ + const Real v_ = v(i); + const Real m_ = m(i); + mv2 += v_ * v_ * m_; + } // end for(i): going through the components + + ekin += mv2; // add continution + } // end for(n): iterating through all nodes + } else if (this->getDOFManager().hasMatrix("M")) { + /* Handle the case where no lumped mass is there. + * This is basically the original code. + */ + if (this->need_to_reassemble_mass) { + this->assembleMassMatrix(); + } + + Array Mv(nb_nodes, nb_degree_of_freedom); + this->getDOFManager().assembleMatMulVectToArray("displacement", "M", + *this->velocity, Mv); + + for (auto && data : + zip(arange(nb_nodes), make_view(Mv, nb_degree_of_freedom), + make_view(*this->velocity, nb_degree_of_freedom))) { + if (mesh.isLocalOrMasterNode(std::get<0>( + data))) // only consider the node if we are blonging to it + { + ekin += std::get<2>(data).dot(std::get<1>(data)); + } + } + } else { + /* This is the case where no mass is present, for whatever reason, such as + * the static case. We handle it specially be returning directly zero. + * However, by doing that there will not be a syncronizing event as in the + * other cases. Which is faster, but could be a problem in case the user + * expects this. + * + * Another not is, that the solid mechanics part, would generate an error in + * this clause. But, since the original implementation of the structural + * part, did not do that, I, Philip, decided to refrain from that. However, + * it is an option that should be considered. + */ + return 0.; } + // Sum up across the comunicator mesh.getCommunicator().allReduce(ekin, SynchronizerOperation::_sum); - return ekin / 2.; + return ekin / 2.; // finally divide the energy by two } /* -------------------------------------------------------------------------- */ Real StructuralMechanicsModel::getPotentialEnergy() { Real epot = 0.; UInt nb_nodes = mesh.getNbNodes(); + // if stiffness matrix is not assembled, do it + this->assembleStiffnessMatrix(); + Array Ku(nb_nodes, nb_degree_of_freedom); this->getDOFManager().assembleMatMulVectToArray( "displacement", "K", *this->displacement_rotation, Ku); - for (auto &&data : + for (auto && data : zip(arange(nb_nodes), make_view(Ku, nb_degree_of_freedom), make_view(*this->displacement_rotation, nb_degree_of_freedom))) { epot += std::get<2>(data).dot(std::get<1>(data)) * static_cast(mesh.isLocalOrMasterNode(std::get<0>(data))); } mesh.getCommunicator().allReduce(epot, SynchronizerOperation::_sum); return epot / 2.; } /* -------------------------------------------------------------------------- */ -Real StructuralMechanicsModel::getEnergy(const ID &energy) { +Real StructuralMechanicsModel::getEnergy(const ID & energy) { if (energy == "kinetic") { return getKineticEnergy(); } if (energy == "potential") { return getPotentialEnergy(); } return 0; } /* -------------------------------------------------------------------------- */ void StructuralMechanicsModel::computeForcesByLocalTractionArray( - const Array &tractions, ElementType type) { + const Array & tractions, ElementType type) { AKANTU_DEBUG_IN(); auto nb_element = getFEEngine().getMesh().getNbElement(type); auto nb_nodes_per_element = getFEEngine().getMesh().getNbNodesPerElement(type); auto nb_quad = getFEEngine().getNbIntegrationPoints(type); // check dimension match AKANTU_DEBUG_ASSERT( Mesh::getSpatialDimension(type) == getFEEngine().getElementDimension(), "element type dimension does not match the dimension of boundaries : " << getFEEngine().getElementDimension() << " != " << Mesh::getSpatialDimension(type)); // check size of the vector AKANTU_DEBUG_ASSERT( tractions.size() == nb_quad * nb_element, "the size of the vector should be the total number of quadrature points"); // check number of components AKANTU_DEBUG_ASSERT(tractions.getNbComponent() == nb_degree_of_freedom, "the number of components should be the spatial " "dimension of the problem"); Array Ntbs(nb_element * nb_quad, nb_degree_of_freedom * nb_nodes_per_element); - auto &fem = getFEEngine(); + auto & fem = getFEEngine(); fem.computeNtb(tractions, Ntbs, type); // allocate the vector that will contain the integrated values auto name = id + std::to_string(type) + ":integral_boundary"; Array int_funct(nb_element, nb_degree_of_freedom * nb_nodes_per_element, name); // do the integration getFEEngine().integrate(Ntbs, int_funct, nb_degree_of_freedom * nb_nodes_per_element, type); // assemble the result into force vector getDOFManager().assembleElementalArrayLocalArray(int_funct, *external_force, type, _not_ghost, 1); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void StructuralMechanicsModel::computeForcesByGlobalTractionArray( - const Array &traction_global, ElementType type) { + const Array & traction_global, ElementType type) { AKANTU_DEBUG_IN(); auto nb_element = mesh.getNbElement(type); auto nb_quad = getFEEngine().getNbIntegrationPoints(type); Array traction_local(nb_element * nb_quad, nb_degree_of_freedom, id + ":structuralmechanics:imposed_linear_load"); auto R_it = getFEEngineClass() .getShapeFunctions() .getRotations(type) .begin(nb_degree_of_freedom, nb_degree_of_freedom); auto Te_it = traction_global.begin(nb_degree_of_freedom); auto te_it = traction_local.begin(nb_degree_of_freedom); for (Int e = 0; e < nb_element; ++e, ++R_it) { for (Int q = 0; q < nb_quad; ++q, ++Te_it, ++te_it) { // turn the traction in the local referential *te_it = *R_it * *Te_it; } } computeForcesByLocalTractionArray(traction_local, type); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void StructuralMechanicsModel::afterSolveStep(bool converged) { if (converged) { assembleInternalForce(); } } } // namespace akantu diff --git a/src/model/structural_mechanics/structural_mechanics_model.hh b/src/model/structural_mechanics/structural_mechanics_model.hh index 13bfdc10a..b78ac80e9 100644 --- a/src/model/structural_mechanics/structural_mechanics_model.hh +++ b/src/model/structural_mechanics/structural_mechanics_model.hh @@ -1,334 +1,397 @@ /** * @file structural_mechanics_model.hh * * @author Fabian Barras * @author Lucas Frerot * @author Sébastien Hartmann * @author Philip Mueller * @author Nicolas Richart * @author Damien Spielmann * * @date creation: Fri Jul 15 2011 * @date last modification: Thu Apr 01 2021 * * @brief Particular implementation of the structural elements in the * StructuralMechanicsModel * * * @section LICENSE * * Copyright (©) 2010-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_named_argument.hh" #include "model.hh" /* -------------------------------------------------------------------------- */ #ifndef AKANTU_STRUCTURAL_MECHANICS_MODEL_HH_ #define AKANTU_STRUCTURAL_MECHANICS_MODEL_HH_ /* -------------------------------------------------------------------------- */ namespace akantu { class Material; class MaterialSelector; class DumperIOHelper; class NonLocalManager; template class IntegratorGauss; template class ShapeStructural; } // namespace akantu namespace akantu { struct StructuralMaterial { Real E{0}; Real A{1}; Real I{0}; Real Iz{0}; Real Iy{0}; Real GJ{0}; Real rho{0}; Real t{0}; Real nu{0}; }; class StructuralMechanicsModel : public Model { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: using MyFEEngineType = FEEngineTemplate; StructuralMechanicsModel(Mesh & mesh, Int dim = _all_dimensions, const ID & id = "structural_mechanics_model"); ~StructuralMechanicsModel() override; /// Init full model void initFullImpl(const ModelOptions & options) override; /// Init boundary FEEngine void initFEEngineBoundary() override; /* ------------------------------------------------------------------------ */ /* Virtual methods from SolverCallback */ /* ------------------------------------------------------------------------ */ /// get the type of matrix needed MatrixType getMatrixType(const ID & matrix_id) const override; /// callback to assemble a Matrix void assembleMatrix(const ID & matrix_id) override; /// callback to assemble a lumped Matrix void assembleLumpedMatrix(const ID & matrix_id) override; /// callback to assemble the residual (rhs) void assembleResidual() override; void assembleResidual(const ID & residual_part) override; bool canSplitResidual() const override { return true; } void afterSolveStep(bool converged) override; /// compute kinetic energy Real getKineticEnergy(); /// compute potential energy Real getPotentialEnergy(); /// compute the specified energy Real getEnergy(const ID & energy); + /** + * \brief This function computes the an approximation of the lumped mass. + * + * The mass is computed by looping over all beams and computing their mass. + * The mass of a single beam is computed by the (initial) length of the beam, + * its cross sectional area and its density. + * The beam mass is then equaly distributed among the two nodes. + * + * For computing the rotational inertia, the function assumes that the mass of + * a node is uniformaly distributed inside a disc (2D) or a sphere (3D). The + * size of that disc, depends on the volume of the beam. + * + * Note that the computation of the mass is not unambigius. + * The reason for this is, that the units of `StructralMaterial::rho` are not + * clear. By default the function assumes that its unit are 'Mass per Volume'. + * However, this makes the computed mass different than the consistent mass, + * which seams to assume that its units are 'mass per unit length'. + * The main difference between thge two are not the values, but that the + * first version depends on `StructuralMaterial::A` while the later does not. + * By defining the macro `AKANTU_STRUCTURAL_MECHANICS_CONSISTENT_LUMPED_MASS` + * the function will compute the mass in a way that is consistent with the + * consistent mass matrix. + * + * \note The lumped mass is not stored inside the DOFManager. + * + * \param ghost_type Should ghost types be computed. + */ + void assembleLumpedMassMatrix(); + /* ------------------------------------------------------------------------ */ /* Virtual methods from Model */ /* ------------------------------------------------------------------------ */ protected: /// get some default values for derived classes std::tuple getDefaultSolverID(const AnalysisMethod & method) override; ModelSolverOptions getDefaultSolverOptions(const TimeStepSolverType & type) const override; static UInt getNbDegreeOfFreedom(ElementType type); /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ void initSolver(TimeStepSolverType time_step_solver_type, NonLinearSolverType non_linear_solver_type) override; /// initialize the model void initModel() override; /// compute the stresses per elements void computeStresses(); /// compute the nodal forces void assembleInternalForce(); /// compute the nodal forces for an element type void assembleInternalForce(ElementType type, GhostType gt); /// assemble the stiffness matrix void assembleStiffnessMatrix(); /// assemble the mass matrix for consistent mass resolutions void assembleMassMatrix(); protected: /// assemble the mass matrix for either _ghost or _not_ghost elements void assembleMassMatrix(GhostType ghost_type); /// computes rho void computeRho(Array & rho, ElementType type, GhostType ghost_type); /// finish the computation of residual to solve in increment void updateResidualInternal(); /* ------------------------------------------------------------------------ */ private: template void assembleStiffnessMatrix(); template void computeStressOnQuad(); template void computeTangentModuli(Array & tangent_moduli); /* ------------------------------------------------------------------------ */ /* Dumpable interface */ /* ------------------------------------------------------------------------ */ public: std::shared_ptr createNodalFieldReal(const std::string & field_name, const std::string & group_name, bool padding_flag) override; std::shared_ptr createNodalFieldBool(const std::string & field_name, const std::string & group_name, bool padding_flag) override; std::shared_ptr createElementalField(const std::string & field_name, const std::string & group_name, bool padding_flag, Int spatial_dimension, ElementKind kind) override; /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: /// set the value of the time step void setTimeStep(Real time_step, const ID & solver_id = "") override; /// get the StructuralMechanicsModel::displacement vector AKANTU_GET_MACRO(Displacement, *displacement_rotation, Array &); /// get the StructuralMechanicsModel::velocity vector AKANTU_GET_MACRO(Velocity, *velocity, Array &); /// get the StructuralMechanicsModel::acceleration vector, updated /// by /// StructuralMechanicsModel::updateAcceleration AKANTU_GET_MACRO(Acceleration, *acceleration, Array &); /// get the StructuralMechanicsModel::external_force vector AKANTU_GET_MACRO(ExternalForce, *external_force, Array &); /// get the StructuralMechanicsModel::internal_force vector (boundary forces) AKANTU_GET_MACRO(InternalForce, *internal_force, Array &); /// get the StructuralMechanicsModel::boundary vector AKANTU_GET_MACRO(BlockedDOFs, *blocked_dofs, Array &); + /** + * Returns a const reference to the array that stores the lumped mass. + * + * The returned array has dimension `N x d` where `N` is the number of nodes + * and `d`, is the number of degrees of freedom per node. + */ + inline const Array & getLumpedMass() const { + if (this->mass == nullptr) { + AKANTU_EXCEPTION("The pointer to the mass was not allocated."); + }; + return *(this->mass); + }; + + // These function is an alias, for compability with the solid mechanics + inline const Array & getMass() const { return this->getLumpedMass(); } + + // Creates the array for storing the mass + bool allocateLumpedMassArray(); + + /** + * Tests if *this has a lumped mass pointer. + */ + inline bool hasLumpedMass() const { return (this->mass != nullptr); }; + AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(RotationMatrix, rotation_matrix, Real); AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(Stress, stress, Real); AKANTU_GET_MACRO_BY_ELEMENT_TYPE(ElementMaterial, element_material, UInt); AKANTU_GET_MACRO_BY_ELEMENT_TYPE(Set_ID, set_ID, UInt); /** * \brief This function adds the `StructuralMaterial` material to the list of * materials managed by *this. * * It is important that this function might invalidate all references to * structural materials, that were previously optained by `getMaterial()`. * * \param material The new material. * * \return The ID of the material that was added. * * \note The return type is is new. */ UInt addMaterial(StructuralMaterial & material, const ID & name = ""); const StructuralMaterial & getMaterialByElement(const Element & element) const; /** * \brief Returns the ith material of *this. * \param i The ith material */ const StructuralMaterial & getMaterial(UInt material_index) const; const StructuralMaterial & getMaterial(const ID & name) const; /** * \brief Returns the number of the different materials inside *this. */ UInt getNbMaterials() const { return materials.size(); } /* ------------------------------------------------------------------------ */ /* Boundaries (structural_mechanics_model_boundary.cc) */ /* ------------------------------------------------------------------------ */ public: /// Compute Linear load function set in global axis void computeForcesByGlobalTractionArray(const Array & traction_global, ElementType type); /// Compute Linear load function set in local axis void computeForcesByLocalTractionArray(const Array & tractions, ElementType type); /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ private: /// time step Real time_step; /// conversion coefficient form force/mass to acceleration Real f_m2a; /// displacements array std::unique_ptr> displacement_rotation; /// velocities array std::unique_ptr> velocity; /// accelerations array std::unique_ptr> acceleration; /// forces array std::unique_ptr> internal_force; /// forces array std::unique_ptr> external_force; - /// lumped mass array + /** + * \brief This is the "lumped" mass array. + * + * It is a bit special, since it is not a one dimensional array, bit it is + * actually a matrix. The number of rows equals the number of nodes. The + * number of colums equals the number of degrees of freedoms per node. This + * layout makes the thing a bit more simple. + * + * Note that it is only allocated in case, the "Lumped" mode is enabled. + */ std::unique_ptr> mass; /// boundaries array std::unique_ptr> blocked_dofs; /// stress array ElementTypeMapArray stress; ElementTypeMapArray element_material; // Define sets of beams ElementTypeMapArray set_ID; /// number of degre of freedom Int nb_degree_of_freedom; // Rotation matrix ElementTypeMapArray rotation_matrix; // /// analysis method check the list in akantu::AnalysisMethod // AnalysisMethod method; /// flag defining if the increment must be computed or not bool increment_flag; bool need_to_reassemble_mass{true}; bool need_to_reassemble_stiffness{true}; + bool need_to_reassemble_lumpedMass{true}; /* ------------------------------------------------------------------------ */ std::vector materials; std::map materials_names_to_id; }; } // namespace akantu #include "structural_mechanics_model_inline_impl.hh" #endif /* AKANTU_STRUCTURAL_MECHANICS_MODEL_HH_ */ diff --git a/src/model/structural_mechanics/structural_mechanics_model_mass.cc b/src/model/structural_mechanics/structural_mechanics_model_mass.cc index d444e27e5..e263f09cc 100644 --- a/src/model/structural_mechanics/structural_mechanics_model_mass.cc +++ b/src/model/structural_mechanics/structural_mechanics_model_mass.cc @@ -1,91 +1,219 @@ /** * @file structural_mechanics_model_mass.cc * * @author Lucas Frerot * @author Sébastien Hartmann * @author Nicolas Richart * * @date creation: Mon Jul 07 2014 * @date last modification: Thu Mar 04 2021 * * @brief function handling mass computation * * * @section LICENSE * * Copyright (©) 2014-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ +#include "aka_math.hh" #include "integrator_gauss.hh" #include "material.hh" #include "shape_structural.hh" #include "structural_mechanics_model.hh" - /* -------------------------------------------------------------------------- */ + namespace akantu { class ComputeRhoFunctorStruct { public: explicit ComputeRhoFunctorStruct(const StructuralMechanicsModel & model) : model(model){}; void operator()(Matrix & rho, const Element & element) const { - Real mat_rho = model.getMaterialByElement(element).rho; - rho.set(mat_rho); + const auto & mat = model.getMaterialByElement(element); + const Real mat_rho = mat.rho; // This is the density + const Real mat_A = mat.A; // This is the cross section + const Real mat_mass_per_length = + mat_rho * mat_A; // Mass of the beam per unit length + + rho.set( + mat_mass_per_length); // The integrator _recquiers_ mass per unit length } private: const StructuralMechanicsModel & model; }; /* -------------------------------------------------------------------------- */ void StructuralMechanicsModel::assembleMassMatrix() { AKANTU_DEBUG_IN(); if (not need_to_reassemble_mass) { return; } if (not getDOFManager().hasMatrix("M")) { getDOFManager().getNewMatrix("M", getMatrixType("M")); } this->getDOFManager().zeroMatrix("M"); assembleMassMatrix(_not_ghost); need_to_reassemble_mass = false; AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void StructuralMechanicsModel::assembleMassMatrix(GhostType ghost_type) { AKANTU_DEBUG_IN(); auto & fem = getFEEngineClass(); ComputeRhoFunctorStruct compute_rho(*this); for (auto type : mesh.elementTypes(spatial_dimension, ghost_type, _ek_structural)) { fem.assembleFieldMatrix(compute_rho, "M", "displacement", this->getDOFManager(), type, ghost_type); } AKANTU_DEBUG_OUT(); } +/* -------------------------------------------------------------------------- */ +void StructuralMechanicsModel::assembleLumpedMassMatrix() { + + if (not this->need_to_reassemble_lumpedMass) + return; + + allocNodalField(this->mass, nb_degree_of_freedom, "lumped_mass"); + + if (!this->getDOFManager().hasLumpedMatrix("M")) { + this->getDOFManager().getNewLumpedMatrix("M"); + } + + this->getDOFManager().zeroLumpedMatrix("M"); + + // Preparing + const UInt nb_nodes = mesh.getNbNodes(); + const auto & nodes = mesh.getNodes(); + const auto & node_it = make_view(nodes, spatial_dimension).begin(); + auto & lumped_mass = *(this->mass); + + lumped_mass.zero(); // Set the lumped mass to zero + + Array volumes(nb_nodes, 1, 0., "volumes"); + + /// We now compute the mass and the volume, but not the inertia + for (auto type : + mesh.elementTypes(spatial_dimension, _not_ghost, _ek_structural)) { + const auto & element_material_id = this->element_material(type); + const auto & connectivity = mesh.getConnectivity(type); + // const auto nb_element = connectivity.size(); + + if (type != _bernoulli_beam_3 or type != _bernoulli_beam_2) { + AKANTU_EXCEPTION( + "The lumped mass was not implemented for non Bernoulli Beams"); + } + + // Now iterate through all the connectivity + for (auto && data : zip(make_view(connectivity, 2), element_material_id)) { + const auto & conn = std::get<0>(data); + auto node1 = conn(0); + auto node2 = conn(0); + + const auto & material = this->materials.at(std::get<1>(data)); + const auto rho = material.rho; + const auto cross_section = material.A; + + auto && coord_node_1 = node_it[node1]; + auto && coord_node_2 = node_it[node2]; + + const auto length = coord_node_1.distance(coord_node_2); + const auto volume = length * cross_section; + const auto mass = volume * rho; + + // Now distribute the mass at the right places + for (auto d : arange(spatial_dimension)) { + lumped_mass(node1, d) += mass / 2.; + lumped_mass(node2, d) += mass / 2.; + }; // end for(d): + + volumes(node1) += volume / 2.; // this entry never point to mass + volumes(node2) += volume / 2.; + } + } + + const auto pi = std::atan(1.) * 4; + + // We now compute the inertia. + if (spatial_dimension == 2) { + /* This is the 2D case, so we are assuming that the mass is inside a disc. + * Which is given as + * \begin{align} + * I := m \cdot r^2 + * \end{align} + * + * The radius is obtained by assuming that the volume, that is associated to + * a beam, forms a uniformly disc. From this volume we can compute the + * radius. + */ + for (auto && data : + zip(make_view(lumped_mass, nb_degree_of_freedom), volumes)) { + const Real volume = std::get<1>(data); + auto & masses = std::get<0>(data); + + const Real r2 = volume / pi; // The square of the volume + + const Real inertia = masses(0) * r2; + masses(spatial_dimension) = inertia; + } + } else if (spatial_dimension == 3) { + /* This is essentially the same as for 2D only that we assume here, + * that the mass is uniformly distributed inside a sphere. + * And thus we have + * \begin{align} + * I := \frac{2}{5} m \cdot r^2 + * \end{align} + * + * We also have to set three values, for the three axis. + * But since we assume a sphere, they are all the same. + */ + for (auto && data : + zip(make_view(lumped_mass, nb_degree_of_freedom), volumes)) { + const Real volume = std::get<1>(data); + auto & masses = std::get<0>(data); + + const Real r2 = std::pow((volume * 3.) / (4 * pi), 2. / 3.); + const Real inertia = (2. / 5.) * masses(0) * r2; + + for (auto && m : masses) { + m = inertia; + } + } + } else { + AKANTU_EXCEPTION("The dimension " << spatial_dimension << " is not known."); + } + + this->getDOFManager().assembleToLumpedMatrix("displacement", lumped_mass, + "M"); + this->need_to_reassemble_lumpedMass = false; + return; +} + } // namespace akantu diff --git a/src/solver/solver_petsc.cc b/src/solver/solver_petsc.cc index 696c75c24..baa032bde 100644 --- a/src/solver/solver_petsc.cc +++ b/src/solver/solver_petsc.cc @@ -1,93 +1,100 @@ /** * @file solver_petsc.cc * * @author Alejandro M. Aragón * @author Aurelia Isabel Cuba Ramos * @author Nicolas Richart * * @date creation: Tue May 13 2014 * @date last modification: Tue May 26 2020 * * @brief Solver class implementation for the petsc solver * * * @section LICENSE * * Copyright (©) 2014-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "solver_petsc.hh" #include "dof_manager_petsc.hh" #include "mpi_communicator_data.hh" #include "solver_vector_petsc.hh" #include "sparse_matrix_petsc.hh" /* -------------------------------------------------------------------------- */ #include //#include /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ SolverPETSc::SolverPETSc(DOFManagerPETSc & dof_manager, const ID & matrix_id, const ID & id) : SparseSolver(dof_manager, matrix_id, id), dof_manager(dof_manager), matrix(dof_manager.getMatrix(matrix_id)) { auto && mpi_comm = dof_manager.getMPIComm(); /// create a solver context PETSc_call(KSPCreate, mpi_comm, &this->ksp); } /* -------------------------------------------------------------------------- */ SolverPETSc::~SolverPETSc() { if (ksp != nullptr) { PETSc_call(KSPDestroy, &ksp); } } /* -------------------------------------------------------------------------- */ void SolverPETSc::setOperators() { // set the matrix that defines the linear system and the matrix for // preconditioning (here they are the same) #if PETSC_VERSION_MAJOR >= 3 && PETSC_VERSION_MINOR >= 5 PETSc_call(KSPSetOperators, ksp, this->matrix.getMat(), this->matrix.getMat()); #else PETSc_call(KSPSetOperators, ksp, this->matrix.getMat(), this->matrix.getMat(), SAME_NONZERO_PATTERN); #endif // If this is not called the solution vector is zeroed in the call to // KSPSolve(). PETSc_call(KSPSetInitialGuessNonzero, ksp, PETSC_TRUE); PETSc_call(KSPSetFromOptions, ksp); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolverPETSc::solve() { Vec & rhs(this->dof_manager.getResidual()); Vec & solution(this->dof_manager.getSolution()); PETSc_call(KSPSolve, ksp, rhs, solution); } +/* -------------------------------------------------------------------------- */ +bool SolverPETSc::isFinite() const { + PetscReal norm; + PETSc_call(VecNorm, NORM_INFINITY, &norm); + return std::isfinite(norm); +} + } // namespace akantu diff --git a/src/solver/solver_petsc.hh b/src/solver/solver_petsc.hh index e0b573f35..d14feca41 100644 --- a/src/solver/solver_petsc.hh +++ b/src/solver/solver_petsc.hh @@ -1,82 +1,84 @@ /** * @file solver_petsc.hh * * @author Alejandro M. Aragón * @author Aurelia Isabel Cuba Ramos * @author Nicolas Richart * * @date creation: Tue May 13 2014 * @date last modification: Tue May 26 2020 * * @brief Solver class interface for the petsc solver * * * @section LICENSE * * Copyright (©) 2014-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "sparse_solver.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ #ifndef AKANTU_SOLVER_PETSC_HH_ #define AKANTU_SOLVER_PETSC_HH_ namespace akantu { class SparseMatrixPETSc; class DOFManagerPETSc; } // namespace akantu namespace akantu { class SolverPETSc : public SparseSolver { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: SolverPETSc(DOFManagerPETSc & dof_manager, const ID & matrix_id, const ID & id = "solver_petsc"); ~SolverPETSc() override; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ public: /// create the solver context and set the matrices virtual void setOperators(); void solve() override; + bool isFinite() const override; + private: /// DOFManager correctly typed DOFManagerPETSc & dof_manager; /// PETSc linear solver KSP ksp; /// Matrix defining the system of equations SparseMatrixPETSc & matrix; }; } // namespace akantu #endif /* AKANTU_SOLVER_PETSC_HH_ */ diff --git a/src/solver/solver_vector.hh b/src/solver/solver_vector.hh index b16c17e78..d41946875 100644 --- a/src/solver/solver_vector.hh +++ b/src/solver/solver_vector.hh @@ -1,91 +1,96 @@ /** * @file solver_vector.hh * * @author Nicolas Richart * * @date creation: Thu Feb 21 2013 * @date last modification: Tue May 26 2020 * * @brief Solver vector interface base class * * * @section LICENSE * * Copyright (©) 2018-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_array.hh" /* -------------------------------------------------------------------------- */ #ifndef AKANTU_SOLVER_VECTOR_HH_ #define AKANTU_SOLVER_VECTOR_HH_ namespace akantu { class DOFManager; } namespace akantu { class SolverVector { public: SolverVector(DOFManager & dof_manager, const ID & id = "solver_vector") : id(id), _dof_manager(dof_manager) {} SolverVector(const SolverVector & vector, const ID & id = "solver_vector") : id(id), _dof_manager(vector._dof_manager) {} virtual ~SolverVector() = default; // resize the vector to the size of the problem virtual void resize() = 0; // clear the vector virtual void set(Real val) = 0; void zero() { this->set({}); } virtual operator const Array &() const = 0; virtual Int size() const = 0; virtual Int localSize() const = 0; virtual SolverVector & operator+(const SolverVector & y) = 0; virtual SolverVector & operator=(const SolverVector & y) = 0; Int & release() { return release_; } Int release() const { return release_; } virtual void printself(std::ostream & stream, int indent = 0) const = 0; + virtual bool isFinite() const = 0; + + /// Returns `true` if `*this` is distributed or not. + virtual bool isDistributed() const { return false; } + protected: ID id; /// Underlying dof manager DOFManager & _dof_manager; Int release_{0}; }; inline std::ostream & operator<<(std::ostream & stream, SolverVector & _this) { _this.printself(stream); return stream; } } // namespace akantu #endif /* AKANTU_SOLVER_VECTOR_HH_ */ diff --git a/src/solver/solver_vector_default.hh b/src/solver/solver_vector_default.hh index 060c95668..49e11dd24 100644 --- a/src/solver/solver_vector_default.hh +++ b/src/solver/solver_vector_default.hh @@ -1,144 +1,156 @@ /** * @file solver_vector_default.hh * * @author Nicolas Richart * * @date creation: Tue Jan 01 2019 * @date last modification: Sat May 23 2020 * * @brief Solver vector interface to Array * * * @section LICENSE * * Copyright (©) 2018-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "solver_vector.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ #ifndef AKANTU_SOLVER_VECTOR_DEFAULT_HH_ #define AKANTU_SOLVER_VECTOR_DEFAULT_HH_ namespace akantu { class DOFManagerDefault; } // namespace akantu namespace akantu { class SolverVectorArray : public SolverVector { public: SolverVectorArray(DOFManagerDefault & dof_manager, const ID & id); SolverVectorArray(const SolverVectorArray & vector, const ID & id); ~SolverVectorArray() override = default; virtual Array & getVector() = 0; virtual const Array & getVector() const = 0; void printself(std::ostream & stream, int indent = 0) const override { std::string space(indent, AKANTU_INDENT); stream << space << "SolverVectorArray [" << std::endl; stream << space << " + id: " << id << std::endl; this->getVector().printself(stream, indent + 1); stream << space << "]" << std::endl; } + + using SolverVector::isDistributed; }; /* -------------------------------------------------------------------------- */ template class SolverVectorArrayTmpl : public SolverVectorArray { public: SolverVectorArrayTmpl(DOFManagerDefault & dof_manager, Array_ & vector, const ID & id = "solver_vector_default") : SolverVectorArray(dof_manager, id), dof_manager(dof_manager), vector(vector) {} template ::value> * = nullptr> SolverVectorArrayTmpl(DOFManagerDefault & dof_manager, const ID & id = "solver_vector_default") : SolverVectorArray(dof_manager, id), dof_manager(dof_manager), vector(0, 1, id + ":vector") {} SolverVectorArrayTmpl(const SolverVectorArrayTmpl & vector, const ID & id = "solver_vector_default") : SolverVectorArray(vector, id), dof_manager(vector.dof_manager), vector(vector.vector) {} operator const Array &() const override { return getVector(); }; virtual operator Array &() { return getVector(); }; SolverVector & operator+(const SolverVector & y) override; SolverVector & operator=(const SolverVector & y) override; void resize() override { static_assert(not std::is_const>::value, "Cannot resize a const Array"); this->vector.resize(this->localSize(), 0.); ++this->release_; } void set(Real val) override { static_assert(not std::is_const>::value, "Cannot clear a const Array"); this->vector.set(val); ++this->release_; } + virtual + bool + isDistributed() + const + override + { return false; } + + public: Array & getVector() override { return vector; } const Array & getVector() const override { return vector; } Int size() const override; Int localSize() const override; virtual Array & getGlobalVector() { return this->vector; } virtual void setGlobalVector(const Array & solution) { this->vector.copy(solution); } + bool isFinite() const override { return vector.isFinite(); } + protected: DOFManagerDefault & dof_manager; Array_ vector; template friend class SolverVectorArrayTmpl; }; /* -------------------------------------------------------------------------- */ using SolverVectorDefault = SolverVectorArrayTmpl>; /* -------------------------------------------------------------------------- */ template using SolverVectorDefaultWrap = SolverVectorArrayTmpl; template decltype(auto) make_solver_vector_default_wrap(DOFManagerDefault & dof_manager, Array & vector) { return SolverVectorDefaultWrap(dof_manager, vector); } } // namespace akantu /* -------------------------------------------------------------------------- */ #include "solver_vector_default_tmpl.hh" /* -------------------------------------------------------------------------- */ #endif /* AKANTU_SOLVER_VECTOR_DEFAULT_HH_ */ diff --git a/src/solver/solver_vector_distributed.cc b/src/solver/solver_vector_distributed.cc index 4eacf65a1..7d2865eef 100644 --- a/src/solver/solver_vector_distributed.cc +++ b/src/solver/solver_vector_distributed.cc @@ -1,81 +1,90 @@ /** * @file solver_vector_distributed.cc * * @author Nicolas Richart * * @date creation: Tue Jan 01 2019 * @date last modification: Sat May 23 2020 * * @brief Solver vector interface for distributed arrays * * * @section LICENSE * * Copyright (©) 2018-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "solver_vector_distributed.hh" #include "dof_manager_default.hh" #include "dof_synchronizer.hh" /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ SolverVectorDistributed::SolverVectorDistributed( DOFManagerDefault & dof_manager, const ID & id) : SolverVectorDefault(dof_manager, id) {} /* -------------------------------------------------------------------------- */ SolverVectorDistributed::SolverVectorDistributed( const SolverVectorDefault & vector, const ID & id) : SolverVectorDefault(vector, id) {} /* -------------------------------------------------------------------------- */ Array & SolverVectorDistributed::getGlobalVector() { auto & synchronizer = dof_manager.getSynchronizer(); if (not this->global_vector) { this->global_vector = std::make_unique>(0, 1, "global_residual"); } if (synchronizer.getCommunicator().whoAmI() == 0) { this->global_vector->resize(dof_manager.getSystemSize()); synchronizer.gather(this->vector, *this->global_vector); } else { synchronizer.gather(this->vector); } return *this->global_vector; } /* -------------------------------------------------------------------------- */ void SolverVectorDistributed::setGlobalVector(const Array & solution) { auto & synchronizer = dof_manager.getSynchronizer(); if (synchronizer.getCommunicator().whoAmI() == 0) { synchronizer.scatter(this->vector, solution); } else { synchronizer.scatter(this->vector); } } +/* -------------------------------------------------------------------------- */ +bool SolverVectorDistributed::isFinite() const { + auto & synchronizer = dof_manager.getSynchronizer(); + bool is_finite = this->vector.isFinite(); + synchronizer.getCommunicator().allReduce(is_finite, + SynchronizerOperation::_land); + return is_finite; +} + /* -------------------------------------------------------------------------- */ } // namespace akantu diff --git a/src/solver/solver_vector_distributed.hh b/src/solver/solver_vector_distributed.hh index 26df926f4..a928e0ac8 100644 --- a/src/solver/solver_vector_distributed.hh +++ b/src/solver/solver_vector_distributed.hh @@ -1,59 +1,63 @@ /** * @file solver_vector_distributed.hh * * @author Nicolas Richart * * @date creation: Thu Feb 21 2013 * @date last modification: Tue Jan 01 2019 * * @brief Solver vector interface for distributed arrays * * * @section LICENSE * * Copyright (©) 2018-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "solver_vector_default.hh" /* -------------------------------------------------------------------------- */ #ifndef AKANTU_SOLVER_VECTOR_DISTRIBUTED_HH_ #define AKANTU_SOLVER_VECTOR_DISTRIBUTED_HH_ namespace akantu { class SolverVectorDistributed : public SolverVectorDefault { public: SolverVectorDistributed(DOFManagerDefault & dof_manager, const ID & id = "solver_vector_mumps"); SolverVectorDistributed(const SolverVectorDefault & vector, const ID & id = "solver_vector_mumps"); Array & getGlobalVector() override; void setGlobalVector(const Array & solution) override; + bool isFinite() const override; + + virtual bool isDistributed() const override { return true; } + protected: // full vector in case it needs to be centralized on master std::unique_ptr> global_vector; }; } // namespace akantu #endif /* AKANTU_SOLVER_VECTOR_DISTRIBUTED_HH_ */ diff --git a/src/solver/solver_vector_petsc.hh b/src/solver/solver_vector_petsc.hh index bb52c214b..e5e7fb827 100644 --- a/src/solver/solver_vector_petsc.hh +++ b/src/solver/solver_vector_petsc.hh @@ -1,208 +1,215 @@ /** * @file solver_vector_petsc.hh * * @author Nicolas Richart * * @date creation: Tue Jan 01 2019 * @date last modification: Fri Jul 24 2020 * * @brief Solver vector interface for PETSc * * * @section LICENSE * * Copyright (©) 2018-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "dof_manager_petsc.hh" #include "solver_vector.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ #ifndef AKANTU_SOLVER_VECTOR_PETSC_HH_ #define AKANTU_SOLVER_VECTOR_PETSC_HH_ namespace akantu { class DOFManagerPETSc; } // namespace akantu namespace akantu { /* -------------------------------------------------------------------------- */ namespace internal { /* ------------------------------------------------------------------------ */ class PETScVector { public: virtual ~PETScVector() = default; operator Vec &() { return x; } operator const Vec &() const { return x; } Int size() const { PetscInt n; PETSc_call(VecGetSize, x, &n); return n; } Int local_size() const { PetscInt n; PETSc_call(VecGetLocalSize, x, &n); return n; } AKANTU_GET_MACRO_NOT_CONST(Vec, x, auto &); AKANTU_GET_MACRO(Vec, x, const auto &); protected: Vec x{nullptr}; }; } // namespace internal /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ class SolverVectorPETSc : public SolverVector, public internal::PETScVector { public: SolverVectorPETSc(DOFManagerPETSc & dof_manager, const ID & id = "solver_vector_petsc"); SolverVectorPETSc(const SolverVectorPETSc & vector, const ID & id = "solver_vector_petsc"); SolverVectorPETSc(Vec x, DOFManagerPETSc & dof_manager, const ID & id = "solver_vector_petsc"); ~SolverVectorPETSc() override; // resize the vector to the size of the problem void resize() override; void set(Real val) override; operator const Array &() const override; SolverVector & operator+(const SolverVector & y) override; SolverVector & operator=(const SolverVector & y) override; SolverVectorPETSc & operator=(const SolverVectorPETSc & y); /// get values using processors global indexes void getValues(const Array & idx, Array & values) const; /// get values using processors local indexes void getValuesLocal(const Array & idx, Array & values) const; /// adding values to the vector using the global indices void addValues(const Array & gidx, const Array & values, Real scale_factor = 1.); /// adding values to the vector using the local indices void addValuesLocal(const Array & lidx, const Array & values, Real scale_factor = 1.); Int size() const override { return internal::PETScVector::size(); } Int localSize() const override { return internal::PETScVector::local_size(); } void printself(std::ostream & stream, int indent = 0) const override; + virtual + bool + isDistributed() + const + override + { return true; } + protected: void applyModifications(); void updateGhost(); protected: DOFManagerPETSc & dof_manager; // used for the conversion operator Array cache; }; /* -------------------------------------------------------------------------- */ namespace internal { /* ------------------------------------------------------------------------ */ template class PETScWrapedVector : public PETScVector { public: PETScWrapedVector(Array && array) : array(array) { PETSc_call(VecCreateSeqWithArray, PETSC_COMM_SELF, 1, array.size(), array.data(), &x); } ~PETScWrapedVector() override { PETSc_call(VecDestroy, &x); } private: Array array; }; /* ------------------------------------------------------------------------ */ template class PETScLocalVector : public PETScVector { public: PETScLocalVector(const Vec & g) : g(g) { PETSc_call(VecGetLocalVectorRead, g, x); } PETScLocalVector(const SolverVectorPETSc & g) : PETScLocalVector(g.getVec()) {} ~PETScLocalVector() override { PETSc_call(VecRestoreLocalVectorRead, g, x); PETSc_call(VecDestroy, &x); } private: const Vec & g; }; template <> class PETScLocalVector : public PETScVector { public: PETScLocalVector(Vec & g) : g(g) { PETSc_call(VecGetLocalVectorRead, g, x); } PETScLocalVector(SolverVectorPETSc & g) : PETScLocalVector(g.getVec()) {} ~PETScLocalVector() override { PETSc_call(VecRestoreLocalVectorRead, g, x); PETSc_call(VecDestroy, &x); } private: Vec & g; }; /* ------------------------------------------------------------------------ */ template decltype(auto) make_petsc_wraped_vector(Array && array) { return PETScWrapedVector(std::forward(array)); } template < typename V, std::enable_if_t>::value> * = nullptr> decltype(auto) make_petsc_local_vector(V && vec) { constexpr auto read_only = std::is_const>::value; return PETScLocalVector(vec); } template >::value> * = nullptr> decltype(auto) make_petsc_local_vector(V && vec) { constexpr auto read_only = std::is_const>::value; return PETScLocalVector( dynamic_cast &>(vec)); } } // namespace internal } // namespace akantu #endif /* AKANTU_SOLVER_VECTOR_PETSC_HH_ */ diff --git a/src/solver/sparse_matrix.hh b/src/solver/sparse_matrix.hh index ee99a4898..f9b304336 100644 --- a/src/solver/sparse_matrix.hh +++ b/src/solver/sparse_matrix.hh @@ -1,167 +1,170 @@ /** * @file sparse_matrix.hh * * @author Nicolas Richart * * @date creation: Mon Dec 13 2010 * @date last modification: Wed Sep 16 2020 * * @brief sparse matrix storage class (distributed assembled matrix) * This is a COO format (Coordinate List) * * * @section LICENSE * * Copyright (©) 2010-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_common.hh" /* -------------------------------------------------------------------------- */ #ifndef AKANTU_SPARSE_MATRIX_HH_ #define AKANTU_SPARSE_MATRIX_HH_ /* -------------------------------------------------------------------------- */ namespace akantu { class DOFManager; class TermsToAssemble; class SolverVector; } // namespace akantu namespace akantu { class SparseMatrix { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: SparseMatrix(DOFManager & dof_manager, const MatrixType & matrix_type, const ID & id = "sparse_matrix"); SparseMatrix(const SparseMatrix & matrix, const ID & id = "sparse_matrix"); virtual ~SparseMatrix(); /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ public: /// remove the existing profile virtual void clearProfile(); /// set the matrix to 0 virtual void set(Real val) = 0; virtual void zero() { this->set(0); } /// add a non-zero element to the profile virtual Idx add(Idx i, Idx j) = 0; /// assemble a local matrix in the sparse one virtual void add(Idx i, Idx j, Real value) = 0; /// save the profil in a file using the MatrixMarket file format virtual void saveProfile(const std::string & /* filename */) const { AKANTU_TO_IMPLEMENT(); } /// save the matrix in a file using the MatrixMarket file format virtual void saveMatrix(const std::string & /* filename */) const { AKANTU_TO_IMPLEMENT(); }; /// multiply the matrix by a coefficient virtual void mul(Real alpha) = 0; /// add matrices virtual void add(const SparseMatrix & B, Real alpha = 1.); /// Equivalent of *gemv in blas virtual void matVecMul(const SolverVector & x, SolverVector & y, Real alpha = 1., Real beta = 0.) const = 0; /// modify the matrix to "remove" the blocked dof virtual void applyBoundary(Real block_val = 1.) = 0; /// copy the profile of another matrix virtual void copyProfile(const SparseMatrix & other) = 0; /// operator *= SparseMatrix & operator*=(Real alpha) { this->mul(alpha); return *this; } + /// Check if all entries are finite. The default implementation throws. + virtual bool isFinite() const { AKANTU_TO_IMPLEMENT(); } + protected: /// This is the revert of add \f[B += \alpha * *this\f]; virtual void addMeTo(SparseMatrix & B, Real alpha) const = 0; /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: /// return the values at potition i, j virtual inline Real operator()(Idx /*i*/, Idx /*j*/) const { AKANTU_TO_IMPLEMENT(); } /// return the values at potition i, j virtual inline Real & operator()(Idx /*i*/, Idx /*j*/) { AKANTU_TO_IMPLEMENT(); } AKANTU_GET_MACRO_AUTO(NbNonZero, nb_non_zero); Int size() const { return size_; } AKANTU_GET_MACRO_AUTO(MatrixType, matrix_type); virtual Int getRelease() const = 0; virtual Real min() = 0; /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ protected: ID id; /// Underlying dof manager DOFManager & _dof_manager; /// sparce matrix type MatrixType matrix_type; /// Size of the matrix Int size_; /// number of processors Int nb_proc; /// number of non zero element Int nb_non_zero; }; // Array & operator*=(Array & vect, const SparseMatrix & mat); } // namespace akantu /* -------------------------------------------------------------------------- */ /* inline functions */ /* -------------------------------------------------------------------------- */ #include "sparse_matrix_inline_impl.hh" #endif /* AKANTU_SPARSE_MATRIX_HH_ */ diff --git a/src/solver/sparse_matrix_aij.cc b/src/solver/sparse_matrix_aij.cc index 0028f945c..67baf5fa3 100644 --- a/src/solver/sparse_matrix_aij.cc +++ b/src/solver/sparse_matrix_aij.cc @@ -1,298 +1,302 @@ /** * @file sparse_matrix_aij.cc * * @author Nicolas Richart * * @date creation: Fri Aug 21 2015 * @date last modification: Fri Jul 24 2020 * * @brief Implementation of the AIJ sparse matrix * * * @section LICENSE * * Copyright (©) 2015-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "sparse_matrix_aij.hh" #include "aka_iterators.hh" +#include "aka_math.hh" #include "dof_manager_default.hh" #include "dof_synchronizer.hh" #include "solver_vector_default.hh" #include "terms_to_assemble.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ SparseMatrixAIJ::SparseMatrixAIJ(DOFManagerDefault & dof_manager, const MatrixType & matrix_type, const ID & id) : SparseMatrix(dof_manager, matrix_type, id), dof_manager(dof_manager), irn(0, 1, id + ":irn"), jcn(0, 1, id + ":jcn"), a(0, 1, id + ":a") {} /* -------------------------------------------------------------------------- */ SparseMatrixAIJ::SparseMatrixAIJ(const SparseMatrixAIJ & matrix, const ID & id) : SparseMatrix(matrix, id), dof_manager(matrix.dof_manager), irn(matrix.irn, id + ":irn"), jcn(matrix.jcn, id + ":jcn"), a(matrix.a, id + ":a") {} /* -------------------------------------------------------------------------- */ SparseMatrixAIJ::~SparseMatrixAIJ() = default; /* -------------------------------------------------------------------------- */ void SparseMatrixAIJ::applyBoundary(Real block_val) { AKANTU_DEBUG_IN(); const auto & blocked_dofs = this->dof_manager.getGlobalBlockedDOFs(); auto begin = blocked_dofs.begin(); auto end = blocked_dofs.end(); auto is_blocked = [&](auto && i) -> bool { auto il = this->dof_manager.globalToLocalEquationNumber(i); return std::binary_search(begin, end, il); }; for (auto && ij_a : zip(irn, jcn, a)) { auto ni = std::get<0>(ij_a) - 1; auto nj = std::get<1>(ij_a) - 1; if (is_blocked(ni) or is_blocked(nj)) { std::get<2>(ij_a) = std::get<0>(ij_a) != std::get<1>(ij_a) ? 0. : this->dof_manager.isLocalOrMasterDOF( this->dof_manager.globalToLocalEquationNumber(ni)) ? block_val : 0.; } } this->value_release++; AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SparseMatrixAIJ::saveProfile(const std::string & filename) const { AKANTU_DEBUG_IN(); std::ofstream outfile; outfile.open(filename.c_str()); auto m = this->size_; auto & comm = dof_manager.getCommunicator(); // write header if (comm.whoAmI() == 0) { outfile << "%%MatrixMarket matrix coordinate pattern"; if (this->matrix_type == _symmetric) { outfile << " symmetric"; } else { outfile << " general"; } outfile << std::endl; outfile << m << " " << m << " " << this->nb_non_zero << std::endl; } for (auto p : arange(comm.getNbProc())) { // write content if (comm.whoAmI() == p) { for (auto && data : zip(this->irn, this->jcn)) { outfile << std::get<0>(data) << " " << std::get<1>(data) << " 1" << std::endl; } } comm.barrier(); } outfile.close(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SparseMatrixAIJ::saveMatrix(const std::string & filename) const { AKANTU_DEBUG_IN(); auto & comm = dof_manager.getCommunicator(); // open and set the properties of the stream std::ofstream outfile; if (0 == comm.whoAmI()) { outfile.open(filename.c_str()); } else { outfile.open(filename.c_str(), std::ios_base::app); } outfile.precision(std::numeric_limits::digits10); // write header decltype(nb_non_zero) nnz = this->nb_non_zero; comm.allReduce(nnz); if (comm.whoAmI() == 0) { outfile << "%%MatrixMarket matrix coordinate real"; if (this->matrix_type == _symmetric) { outfile << " symmetric"; } else { outfile << " general"; } outfile << std::endl; outfile << this->size_ << " " << this->size_ << " " << nnz << std::endl; } for (auto p : arange(comm.getNbProc())) { // write content if (comm.whoAmI() == p) { for (auto && data : zip(this->irn, this->jcn, this->a)) { outfile << std::get<0>(data) << " " << std::get<1>(data) << " " << std::get<2>(data) << std::endl; } } comm.barrier(); } // time to end outfile.close(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SparseMatrixAIJ::matVecMul(const Array & x, Array & y, Real alpha, Real beta) const { AKANTU_DEBUG_IN(); y *= beta; auto x_it = make_view(x).begin(); auto y_it = make_view(y).begin(); for (auto && data : zip(this->irn, this->jcn, this->a)) { auto i = this->dof_manager.globalToLocalEquationNumber(std::get<0>(data) - 1); auto j = this->dof_manager.globalToLocalEquationNumber(std::get<1>(data) - 1); const auto & A = std::get<2>(data); y_it[i] += alpha * A * x_it[j]; if ((this->matrix_type == _symmetric) && (i != j)) { y_it[j] += alpha * A * x_it[i]; } } if (this->dof_manager.hasSynchronizer()) { this->dof_manager.getSynchronizer().reduceSynchronizeArray(y); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SparseMatrixAIJ::matVecMul(const SolverVector & _x, SolverVector & _y, Real alpha, Real beta) const { AKANTU_DEBUG_IN(); auto && x = aka::as_type(_x).getVector(); auto && y = aka::as_type(_y).getVector(); this->matVecMul(x, y, alpha, beta); } /* -------------------------------------------------------------------------- */ void SparseMatrixAIJ::copyContent(const SparseMatrix & matrix) { AKANTU_DEBUG_IN(); const auto & mat = aka::as_type(matrix); AKANTU_DEBUG_ASSERT(nb_non_zero == mat.getNbNonZero(), "The to matrix don't have the same profiles"); memcpy(a.data(), mat.getA().data(), nb_non_zero * sizeof(Real)); this->value_release++; AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SparseMatrixAIJ::copyProfile(const SparseMatrix & other) { const auto & A = aka::as_type(other); SparseMatrix::clearProfile(); this->irn.copy(A.irn); this->jcn.copy(A.jcn); this->irn_jcn_k.clear(); Idx i; Idx j; Idx k; for (auto && data : enumerate(irn, jcn)) { std::tie(k, i, j) = data; this->irn_jcn_k[this->key(i - 1, j - 1)] = k; } this->nb_non_zero = this->irn.size(); this->a.resize(this->nb_non_zero); this->a.set(0.); this->size_ = A.size_; this->profile_release = A.profile_release; this->value_release++; } /* -------------------------------------------------------------------------- */ template void SparseMatrixAIJ::addMeToTemplated(MatrixType & B, Real alpha) const { Idx i; Idx j; Real A_ij; for (auto && tuple : zip(irn, jcn, a)) { std::tie(i, j, A_ij) = tuple; B.add(i - 1, j - 1, alpha * A_ij); } } /* -------------------------------------------------------------------------- */ void SparseMatrixAIJ::addMeTo(SparseMatrix & B, Real alpha) const { if (aka::is_of_type(B)) { this->addMeToTemplated(aka::as_type(B), alpha); } else { // this->addMeToTemplated(*this, alpha); } } /* -------------------------------------------------------------------------- */ void SparseMatrixAIJ::mul(Real alpha) { this->a *= alpha; this->value_release++; } /* -------------------------------------------------------------------------- */ void SparseMatrixAIJ::set(Real val) { a.set(val); this->value_release++; } +/* -------------------------------------------------------------------------- */ +bool SparseMatrixAIJ::isFinite() const { return this->a.isFinite(); }; + } // namespace akantu diff --git a/src/solver/sparse_matrix_aij.hh b/src/solver/sparse_matrix_aij.hh index 06085533b..b0aa4e939 100644 --- a/src/solver/sparse_matrix_aij.hh +++ b/src/solver/sparse_matrix_aij.hh @@ -1,201 +1,204 @@ /** * @file sparse_matrix_aij.hh * * @author Nicolas Richart * * @date creation: Mon Dec 13 2010 * @date last modification: Wed Sep 16 2020 * * @brief AIJ implementation of the SparseMatrix (this the format used by * Mumps) * * * @section LICENSE * * Copyright (©) 2010-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_array.hh" #include "aka_common.hh" #include "sparse_matrix.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ #ifndef AKANTU_SPARSE_MATRIX_AIJ_HH_ #define AKANTU_SPARSE_MATRIX_AIJ_HH_ namespace akantu { class DOFManagerDefault; class TermsToAssemble; } // namespace akantu namespace akantu { class SparseMatrixAIJ : public SparseMatrix { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: SparseMatrixAIJ(DOFManagerDefault & dof_manager, const MatrixType & matrix_type, const ID & id = "sparse_matrix_aij"); SparseMatrixAIJ(const SparseMatrixAIJ & matrix, const ID & id = "sparse_matrix_aij"); ~SparseMatrixAIJ() override; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ public: /// remove the existing profile inline void clearProfile() override; /// add a non-zero element inline Idx add(Idx i, Idx j) override; /// set the matrix to 0 void set(Real val) override; /// assemble a local matrix in the sparse one inline void add(Idx i, Idx j, Real value) override; /// add a block of values inline void addValues(const Vector & is, const Vector & js, const Matrix & values, MatrixType values_type); /// set the size of the matrix void resize(Int size) { this->size_ = size; } /// modify the matrix to "remove" the blocked dof void applyBoundary(Real block_val = 1.) override; /// save the profil in a file using the MatrixMarket file format void saveProfile(const std::string & filename) const override; /// save the matrix in a file using the MatrixMarket file format void saveMatrix(const std::string & filename) const override; /// copy assuming the profile are the same virtual void copyContent(const SparseMatrix & matrix); /// multiply the matrix by a scalar void mul(Real alpha) override; /// Equivalent of *gemv in blas void matVecMul(const SolverVector & x, SolverVector & y, Real alpha = 1., Real beta = 0.) const override; void matVecMul(const Array & x, Array & y, Real alpha = 1., Real beta = 0.) const; /// copy the profile of another matrix void copyProfile(const SparseMatrix & other) override; + /// Check if all entries are finite + virtual bool isFinite() const override; + /* ------------------------------------------------------------------------ */ /// accessor to A_{ij} - if (i, j) not present it returns 0 inline Real operator()(Idx i, Idx j) const override; /// accessor to A_{ij} - if (i, j) not present it fails, (i, j) should be /// first added to the profile inline Real & operator()(Idx i, Idx j) override; /// accessor to get the minimum value of A_{ij} inline Real min() override; protected: void addMeTo(SparseMatrix & B, Real alpha) const override; inline void addSymmetricValuesToSymmetric(const Vector & is, const Vector & js, const Matrix & values); inline void addUnsymmetricValuesToSymmetric(const Vector & is, const Vector & js, const Matrix & values); inline void addValuesToUnsymmetric(const Vector & is, const Vector & js, const Matrix & values); private: /// This is just to inline the addToMatrix function template void addMeToTemplated(MatrixType & B, Real alpha) const; /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: AKANTU_GET_MACRO_AUTO(IRN, irn); AKANTU_GET_MACRO_AUTO(JCN, jcn); AKANTU_GET_MACRO_AUTO(A, a); /// The release changes at each call of a function that changes the profile, /// it in increasing but could overflow so it should be checked as /// (my_release != release) and not as (my_release < release) AKANTU_GET_MACRO_AUTO(ProfileRelease, profile_release); AKANTU_GET_MACRO_AUTO(ValueRelease, value_release); Int getRelease() const override { return value_release; } protected: using KeyCOO = std::pair; using coordinate_list_map = std::unordered_map; /// get the pair corresponding to (i, j) inline KeyCOO key(Idx i, Idx j) const { if (this->matrix_type == _symmetric && (i > j)) { return std::make_pair(j, i); } return std::make_pair(i, j); } /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ private: DOFManagerDefault & dof_manager; /// row indexes Array irn; /// column indexes Array jcn; /// values : A[k] = Matrix[irn[k]][jcn[k]] Array a; /// Profile release Int profile_release{1}; /// Value release Int value_release{1}; /// map for (i, j) -> k correspondence coordinate_list_map irn_jcn_k; }; } // namespace akantu /* -------------------------------------------------------------------------- */ /* inline functions */ /* -------------------------------------------------------------------------- */ #include "sparse_matrix_aij_inline_impl.hh" #endif /* AKANTU_SPARSE_MATRIX_AIJ_HH_ */ diff --git a/test/ci/make-wheels.sh b/test/ci/make-wheels.sh index e252d6bd5..018cd8bca 100755 --- a/test/ci/make-wheels.sh +++ b/test/ci/make-wheels.sh @@ -1,33 +1,37 @@ #!/usr/bin/env bash set -eo pipefail set +x export PLAT=manylinux2010_x86_64 source /etc/profile function repair_wheel { wheel="$1" if ! auditwheel show "$wheel"; then echo "Skipping non-platform wheel $wheel" else auditwheel repair "$wheel" --plat "$PLAT" fi } # Compile wheels for PYBIN in /opt/python/cp3*/bin; do + if "${PYBIN}/python" --version | grep -q 3.11; then + echo "Skip python version 3.11" + continue + fi ccache --zero-stats echo "${PYBIN}/pip" wheel . --no-deps -w dist/ "${PYBIN}/pip" wheel . --no-deps -w dist/ ccache --show-stats done export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${CI_AKANTU_INSTALL_PREFIX}/lib64 # Bundle external shared libraries into the wheels for whl in dist/*.whl; do echo repair_wheel "$whl" repair_wheel "$whl" done diff --git a/test/ci/manjaro:stable/Dockerfile b/test/ci/manjaro:stable/Dockerfile new file mode 100644 index 000000000..791defed5 --- /dev/null +++ b/test/ci/manjaro:stable/Dockerfile @@ -0,0 +1,87 @@ +FROM manjarolinux/base:latest as base + +# squashing the whole base image into one layer +FROM scratch AS release +COPY --from=base / / + +ARG TARGETPLATFORM + +ARG CACHEBUST=1 +ENV LANG=en_US.UTF-8 + +ENV PATH="/usr/bin:${PATH}" + +RUN uname -m && \ + pacman-key --init && \ + pacman-mirrors -f 5 + +RUN [[ "${TARGETPLATFORM}" == "linux/amd64" ]] || exit 0 && \ + pacman -Syy --noconfirm --needed archlinux-keyring manjaro-keyring && \ + pacman-key --populate archlinux manjaro + +RUN [[ "${TARGETPLATFORM}" == "linux/arm64" ]] || exit 0 && \ + pacman -Syy --noconfirm --needed archlinuxarm-keyring manjaro-arm-keyring && \ + pacman-key --populate archlinuxarm manjaro-arm + +RUN pacman -S --noconfirm --needed \ + shadow \ + git \ + git-lfs \ + cmake \ + libseccomp \ + autoconf \ + automake \ + binutils \ + bison \ + fakeroot \ + file \ + findutils \ + flex \ + gawk \ + gcc \ + gettext \ + grep \ + groff \ + gzip \ + libtool \ + m4 \ + make \ + pacman \ + patch \ + pkgconf \ + sed \ + sudo \ + texinfo \ + lsb-release \ + manjaro-release \ + boost \ + which && \ + # docker context give real space limits + sed -i -e 's~CheckSpace.*~#CheckSpace~g' '/etc/pacman.conf' && \ + pacman -Syyu --noconfirm --needed + +RUN ls /etc/*-release && cat /etc/*-release + +# user 'builder' can be used as the running user for applications prohibiting root usage (pacman) +RUN id -u builder &>/dev/null || (useradd -d /builder -m builder && \ + echo "builder ALL=(ALL) NOPASSWD: ALL" >>/etc/sudoers) + +RUN pacman -Syu --noconfirm rust + +USER builder + +RUN cd && git clone https://aur.archlinux.org/paru.git && \ + cd paru && makepkg -s && \ + sudo pacman -U --noconfirm paru*.zst && \ + cd /builder && rm -rf paru + +RUN paru -S --noconfirm scotch scalapack parmetis metis + +RUN cd && curl -O http://mumps.enseeiht.fr/MUMPS_5.4.1.tar.gz && \ + tar xvzf MUMPS_5.4.1.tar.gz + +COPY Makefile.inc /builder/MUMPS_5.4.1 + +RUN cd /builder/MUMPS_5.4.1 && make + +CMD ["/usr/bin/bash"] diff --git a/test/ci/manjaro:stable/Makefile.inc b/test/ci/manjaro:stable/Makefile.inc new file mode 100644 index 000000000..bd6598d34 --- /dev/null +++ b/test/ci/manjaro:stable/Makefile.inc @@ -0,0 +1,167 @@ +# +# This file is part of MUMPS 5.4.1, released +# on Tue Aug 3 09:49:43 UTC 2021 +# +################################################################################ +# +# Makefile.inc.generic +# +# This defines some parameters dependent on your platform; you should +# look for the approriate file in the directory ./Make.inc/ and copy it +# into a file called Makefile.inc. For example, from the MUMPS root +# directory, use +# "cp Make.inc/Makefile.inc.generic ./Makefile.inc" +# (see the main README file for details) +# +# If you do not find any suitable Makefile in Makefile.inc, use this file: +# "cp Make.inc/Makefile.inc.generic ./Makefile.inc" and modify it according +# to the comments given below. If you manage to build MUMPS on a new platform, +# and think that this could be useful to others, you may want to send us +# the corresponding Makefile.inc file. +# +################################################################################ + + +######################################################################## +#Begin orderings +# +# NOTE that PORD is distributed within MUMPS by default. It is recommended to +# install other orderings. For that, you need to obtain the corresponding package +# and modify the variables below accordingly. +# For example, to have Metis available within MUMPS: +# 1/ download Metis and compile it +# 2/ uncomment (suppress # in first column) lines +# starting with LMETISDIR, LMETIS +# 3/ add -Dmetis in line ORDERINGSF +# ORDERINGSF = -Dpord -Dmetis +# 4/ Compile and install MUMPS +# make clean; make (to clean up previous installation) +# +# Metis/ParMetis and SCOTCH/PT-SCOTCH (ver 6.0 and later) orderings are recommended. +# + +SCOTCHDIR = /usr +ISCOTCH = -I$(SCOTCHDIR)/include/scotch +# +# You have to choose one among the following two lines depending on +# the type of analysis you want to perform. If you want to perform only +# sequential analysis choose the first (remember to add -Dscotch in the ORDERINGSF +# variable below); for both parallel and sequential analysis choose the second +# line (remember to add -Dptscotch in the ORDERINGSF variable below) + +#LSCOTCH = -L$(SCOTCHDIR)/lib -lesmumps -lscotch -lscotcherr +#LSCOTCH = -L$(SCOTCHDIR)/lib -lptesmumps -lptscotch -lptscotcherr +LSCOTCH = -L$(SCOTCHDIR)/lib -lptesmumps -lscotch -lptscotch -lptscotcherr -lptscotcherrexit + +LPORDDIR = $(topdir)/PORD/lib/ +IPORD = -I$(topdir)/PORD/include/ +LPORD = -L$(LPORDDIR) -lpord + +LMETISDIR = /usr +IMETIS = -I$(LMETISDIR)/include + +# You have to choose one among the following two lines depending on +# the type of analysis you want to perform. If you want to perform only +# sequential analysis choose the first (remember to add -Dmetis in the ORDERINGSF +# variable below); for both parallel and sequential analysis choose the second +# line (remember to add -Dparmetis in the ORDERINGSF variable below) + +#LMETIS = -L$(LMETISDIR) -lmetis +LMETIS = -L$(LMETISDIR) -lparmetis -lmetis + +# The following variables will be used in the compilation process. +# Please note that -Dptscotch and -Dparmetis imply -Dscotch and -Dmetis respectively. +# If you want to use Metis 4.X or an older version, you should use -Dmetis4 instead of -Dmetis +# or in addition with -Dparmetis (if you are using parmetis 3.X or older). +ORDERINGSF = -Dscotch -Dmetis -Dpord -Dptscotch -Dparmetis +#ORDERINGSF = -Dpord +ORDERINGSC = $(ORDERINGSF) + +LORDERINGS = $(LMETIS) $(LPORD) $(LSCOTCH) +IORDERINGSF = $(ISCOTCH) +IORDERINGSC = $(IMETIS) $(IPORD) $(ISCOTCH) + +#End orderings +######################################################################## + +######################################################################## +# DEFINE HERE SOME COMMON COMMANDS, THE COMPILER NAMES, ETC... + +# PLAT : use it to add a default suffix to the generated libraries +PLAT = +# Library extension, + C and Fortran "-o" option +# may be different under Windows +LIBEXT = .a +OUTC = -o +OUTF = -o +# RM : remove files +RM = /bin/rm -f +# CC : C compiler +CC = mpicc +# FC : Fortran 90 compiler +FC = mpif90 +# FL : Fortran linker +FL = mpif90 +# AR : Archive object in a library +# keep a space at the end if options have to be separated from lib name +AR = ar vr +# RANLIB : generate index of an archive file +# (optionnal use "RANLIB = echo" in case of problem) +RANLIB = ranlib +#RANLIB = echo + +# DEFINE HERE YOUR LAPACK LIBRARY + +LAPACK = -llapack + +# SCALAP should define the SCALAPACK and BLACS libraries. +SCALAP = -lscalapack + +# INCLUDE DIRECTORY FOR MPI +INCPAR = -I/usr/include + +# LIBRARIES USED BY THE PARALLEL VERSION OF MUMPS: $(SCALAP) and MPI +LIBPAR = $(SCALAP) $(LAPACK) -L/usr/lib -lmpi + +# The parallel version is not concerned by the next two lines. +# They are related to the sequential library provided by MUMPS, +# to use instead of ScaLAPACK and MPI. +INCSEQ = -I$(topdir)/libseq +LIBSEQ = $(LAPACK) -L$(topdir)/libseq -lmpiseq + +# DEFINE HERE YOUR BLAS LIBRARY + +LIBBLAS = -lblas + +# DEFINE HERE YOUR PTHREAD LIBRARY +LIBOTHERS = -lpthread + +# FORTRAN/C COMPATIBILITY: +# Use: +# -DAdd_ if your Fortran compiler adds an underscore at the end +# of symbols, +# -DAdd__ if your Fortran compiler adds 2 underscores, +# +# -DUPPER if your Fortran compiler uses uppercase symbols +# +# leave empty if your Fortran compiler does not change the symbols. +# + +CDEFS = -DAdd_ + +#COMPILER OPTIONS +OPTF = -O2 -fopenmp -fPIC -w -fallow-argument-mismatch +OPTC = -O2 -fopenmp -fPIC +OPTL = -O2 -fopenmp -fPIC + +# CHOOSE BETWEEN USING THE SEQUENTIAL OR THE PARALLEL VERSION. + +#Sequential: +#INCS = $(INCSEQ) +#LIBS = $(LIBSEQ) +#LIBSEQNEEDED = libseqneeded + +#Parallel: +INCS = $(INCPAR) +LIBS = $(LIBPAR) +LIBSEQNEEDED = diff --git a/test/test_model/patch_tests/patch_test_linear_fixture.py b/test/test_model/patch_tests/patch_test_linear_fixture.py index 2cd441a6f..33fa4366e 100644 --- a/test/test_model/patch_tests/patch_test_linear_fixture.py +++ b/test/test_model/patch_tests/patch_test_linear_fixture.py @@ -1,137 +1,137 @@ #!/usr/bin/env python3 """ patch_test_linear_fixture.py: linear patch test in python""" __author__ = "Guillaume Anciaux and Nicolas Richart" __credits__ = [ "Guillaume Anciaux ", "Nicolas Richart ", ] __copyright__ = "Copyright (©) 2016-2021 EPFL (Ecole Polytechnique Fédérale" \ " de Lausanne) Laboratory (LSMS - Laboratoire de Simulation" \ " en Mécanique des Solides)" __license__ = "LGPLv3" import unittest import numpy as np import akantu class TestPatchTestLinear(unittest.TestCase): alpha = np.array([[0.01, 0.02, 0.03, 0.04], [0.05, 0.06, 0.07, 0.08], [0.09, 0.10, 0.11, 0.12]]) gradient_tolerance = 1e-13 result_tolerance = 1e-13 dofs_tolerance = 1e-15 def __init__(self, test_name, elem_type_str, functor=None): self.test_name = test_name self.functor = functor self.elem_type = akantu.getElementTypes()[elem_type_str] self.elem_type_str = elem_type_str self.dim = akantu.Mesh.getSpatialDimension(self.elem_type) super().__init__(test_name) def _internal_call(self): self.functor(self) def __getattr__(self, key): if key == self.test_name: return self._internal_call def setUp(self): self.mesh = akantu.Mesh(self.dim, self.elem_type_str) self.mesh.read(str(self.elem_type_str) + ".msh") akantu.MeshUtils.buildFacets(self.mesh) self.mesh.createBoundaryGroupFromGeometry() self.model = self.model_type(self.mesh) def tearDown(self): del self.model del self.mesh def initModel(self, method, material_file): akantu.parseInput(material_file) - akantu.setDebugLevel(akantu.dblError) + akantu.debug.setDebugLevel(akantu.dblError) self.model.initFull(method) self.applyBC() if method != akantu._static: self.model.setTimeStep(0.8 * self.model.getStableTimeStep()) def applyBC(self): boundary = self.model.getBlockedDOFs() for eg in self.mesh.iterateElementGroups(): nodes = eg.getNodeGroup().getNodes() boundary[nodes, :] = True def applyBConDOFs(self, dofs): coordinates = self.mesh.getNodes() for eg in self.mesh.iterateElementGroups(): nodes = eg.getNodeGroup().getNodes().flatten() dofs[nodes] = self.setLinearDOF(dofs[nodes], coordinates[nodes]) def prescribed_gradient(self, dof): gradient = self.alpha[:dof.shape[1], 1:self.dim + 1] return gradient def checkGradient(self, gradient, dofs): pgrad = self.prescribed_gradient(dofs).T gradient = gradient.reshape(gradient.shape[0], *pgrad.shape) diff = gradient[:] - pgrad norm = np.abs(pgrad).max() gradient_error = np.abs(diff).max() / norm self.assertAlmostEqual(0, gradient_error, delta=self.gradient_tolerance) def checkResults(self, presult_func, results, dofs): presult = presult_func(self.prescribed_gradient(dofs)).flatten() remaining_size = np.prod(np.array(results.shape[1:])) results = results.reshape((results.shape[0], remaining_size)) for result in results: diff = result - presult norm = np.abs(result).max() if norm == 0: result_error = np.abs(diff).max() else: result_error = np.abs(diff).max() / norm self.assertAlmostEqual(0., result_error, delta=self.result_tolerance) def setLinearDOF(self, dof, coord): nb_dofs = dof.shape[1] dof[:] = np.einsum('ik,ak->ai', self.alpha[:nb_dofs, 1:self.dim + 1], coord) for i in range(0, nb_dofs): dof[:, i] += self.alpha[i, 0] return dof def checkDOFs(self, dofs): coordinates = self.mesh.getNodes() ref_dofs = np.zeros_like(dofs) self.setLinearDOF(ref_dofs, coordinates) diff = dofs - ref_dofs dofs_error = np.abs(diff).max() self.assertAlmostEqual(0., dofs_error, delta=self.dofs_tolerance) @classmethod def TYPED_TEST(cls, functor, label): for type_name, _type in akantu.getElementTypes().items(): if type_name == "_point_1": continue method_name = type_name + '_' + label test_case = cls(method_name, type_name, functor) test_case.setUp() functor(test_case) test_case.tearDown() diff --git a/test/test_model/test_common/CMakeLists.txt b/test/test_model/test_common/CMakeLists.txt index 391ed3b73..93e2ffacc 100644 --- a/test/test_model/test_common/CMakeLists.txt +++ b/test/test_model/test_common/CMakeLists.txt @@ -1,40 +1,45 @@ #=============================================================================== # @file CMakeLists.txt # # @author Nicolas Richart # # @date creation: Wed Jan 30 2019 # @date last modification: Tue Feb 26 2019 # # @brief CMakeLists for common part of Models # # # @section LICENSE # # Copyright (©) 2018-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) # Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) # # Akantu is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # # Akantu 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License along # with Akantu. If not, see . # #=============================================================================== add_akantu_test(test_model_solver "Test for the solvers") add_akantu_test(test_non_local_toolbox "Test of the functionalities in the non-local toolbox") add_mesh(dof_manager_mesh mesh.geo 3 1) register_gtest_sources( SOURCES test_dof_manager.cc PACKAGE core) register_gtest_test(test_dof_manager DEPENDS dof_manager_mesh) + +register_gtest_sources( + SOURCES test_dof_mesh_distribute.cc + PACKAGE parallel) +register_gtest_test(test_dof_mesh_distribute DEPENDS dof_manager_mesh) diff --git a/test/test_model/test_common/test_dof_mesh_distribute.cc b/test/test_model/test_common/test_dof_mesh_distribute.cc new file mode 100644 index 000000000..32f566e36 --- /dev/null +++ b/test/test_model/test_common/test_dof_mesh_distribute.cc @@ -0,0 +1,77 @@ +/** + * @file test_dof_mesh_distribute.cc + * + * @author Philip Müller + * + * @brief Test if the DOFManager can handle the distribution event of teh mesh. + * + * This test is based on the file `test_dof_synchronizer.cc`. + * + * @section LICENSE + * + * Copyright (©) 2010-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) + * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) + * + * Akantu is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * Akantu 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 Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Akantu. If not, see . + * + */ + +/* -------------------------------------------------------------------------- */ +#include "communicator.hh" +#include "dof_synchronizer.hh" +#include "element_synchronizer.hh" +#include "mesh_io.hh" +#include "mesh_partition_scotch.hh" + +#include +#include + +/* -------------------------------------------------------------------------- */ +#include "io_helper.hh" + +/* -------------------------------------------------------------------------- */ +using namespace akantu; + +TEST(TestMesh, TestMeshDistribute) { + + const UInt spatial_dimension = 3; + + int argc = 0; + char ** argv = nullptr; + initialize(argc, argv); + + const auto & comm = akantu::Communicator::getStaticCommunicator(); + Int prank = comm.whoAmI(); + + Mesh mesh1(spatial_dimension); + Mesh mesh2(spatial_dimension); + + if (prank == 0) { + mesh1.read("mesh.msh"); + mesh2.read("mesh.msh"); + } + + /* This should fail since the mesh is distributed after the + * DOFManager is created. */ + DOFManagerDefault dof_manager(mesh1, "test_dof_manager_1__failing"); + EXPECT_THROW(mesh1.distribute(), debug::Exception); + + /* This will succeed since the mesh is distributed before the manager is + * created. */ + mesh2.distribute(); + EXPECT_NO_THROW( + DOFManagerDefault dof_manager2(mesh2, "test_dof_manager_2__succeeding")); + + finalize(); +} diff --git a/third-party/cmake/pybind11.cmake b/third-party/cmake/pybind11.cmake index aa9c5167f..cd2f0c53c 100644 --- a/third-party/cmake/pybind11.cmake +++ b/third-party/cmake/pybind11.cmake @@ -1,35 +1,35 @@ set(_working_dir ${PROJECT_BINARY_DIR}/third-party/src/pybind11-download) configure_file(${PROJECT_SOURCE_DIR}/third-party/pybind11.cmake.in ${_working_dir}/CMakeLists.txt) -if(NOT EXISTS ${PROJECT_SOURCE_DIR}/third-party/pybind11) +if(NOT EXISTS ${PROJECT_SOURCE_DIR}/third-party/pybind11/CMakeLists.txt) message(STATUS "Downloading pybind11") execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . RESULT_VARIABLE result WORKING_DIRECTORY ${_working_dir} OUTPUT_FILE ${_working_dir}/configure-out.log ERROR_FILE ${_working_dir}/configure-error.log) execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY ${_working_dir} OUTPUT_FILE ${_working_dir}/build-out.log ERROR_FILE ${_working_dir}/build-error.log) endif() set(PYBIND11_PYTHON_VERSION ${AKANTU_PREFERRED_PYTHON_VERSION}) add_subdirectory(${PROJECT_SOURCE_DIR}/third-party/pybind11) set_property(TARGET pybind11 APPEND PROPERTY INTERFACE_SYSTEM_INCLUDE_DIRECTORIES $ $ $ ) if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") target_compile_options(pybind11 INTERFACE -fsized-deallocation) endif() set(pybind11_FOUND TRUE CACHE INTERNAL "" FORCE) set(PYBIND11_INCLUDE_DIR "${PYBIND11_INCLUDE_DIR};${PYTHON_INCLUDE_DIRS}" CACHE INTERNAL "") set(PYBIND11_LIBRARIES "${PYTHON_LIBRARIES}" CACHE INTERNAL "") mask_package_options(PYBIND11) mark_as_advanced(USE_PYTHON_INCLUDE_DIR) diff --git a/versioneer.py b/versioneer.py deleted file mode 100644 index 480c4ab6b..000000000 --- a/versioneer.py +++ /dev/null @@ -1,1861 +0,0 @@ - -# Version: 0.19 - -"""The Versioneer - like a rocketeer, but for versions. - -The Versioneer -============== - -* like a rocketeer, but for versions! -* https://github.com/python-versioneer/python-versioneer -* Brian Warner -* License: Public Domain -* Compatible with: Python 3.6, 3.7, 3.8, 3.9 and pypy3 -* [![Latest Version][pypi-image]][pypi-url] -* [![Build Status][travis-image]][travis-url] - -This is a tool for managing a recorded version number in distutils-based -python projects. The goal is to remove the tedious and error-prone "update -the embedded version string" step from your release process. Making a new -release should be as easy as recording a new tag in your version-control -system, and maybe making new tarballs. - - -## Quick Install - -* `pip install versioneer` to somewhere in your $PATH -* add a `[versioneer]` section to your setup.cfg (see [Install](INSTALL.md)) -* run `versioneer install` in your source tree, commit the results -* Verify version information with `python setup.py version` - -## Version Identifiers - -Source trees come from a variety of places: - -* a version-control system checkout (mostly used by developers) -* a nightly tarball, produced by build automation -* a snapshot tarball, produced by a web-based VCS browser, like github's - "tarball from tag" feature -* a release tarball, produced by "setup.py sdist", distributed through PyPI - -Within each source tree, the version identifier (either a string or a number, -this tool is format-agnostic) can come from a variety of places: - -* ask the VCS tool itself, e.g. "git describe" (for checkouts), which knows - about recent "tags" and an absolute revision-id -* the name of the directory into which the tarball was unpacked -* an expanded VCS keyword ($Id$, etc) -* a `_version.py` created by some earlier build step - -For released software, the version identifier is closely related to a VCS -tag. Some projects use tag names that include more than just the version -string (e.g. "myproject-1.2" instead of just "1.2"), in which case the tool -needs to strip the tag prefix to extract the version identifier. For -unreleased software (between tags), the version identifier should provide -enough information to help developers recreate the same tree, while also -giving them an idea of roughly how old the tree is (after version 1.2, before -version 1.3). Many VCS systems can report a description that captures this, -for example `git describe --tags --dirty --always` reports things like -"0.7-1-g574ab98-dirty" to indicate that the checkout is one revision past the -0.7 tag, has a unique revision id of "574ab98", and is "dirty" (it has -uncommitted changes). - -The version identifier is used for multiple purposes: - -* to allow the module to self-identify its version: `myproject.__version__` -* to choose a name and prefix for a 'setup.py sdist' tarball - -## Theory of Operation - -Versioneer works by adding a special `_version.py` file into your source -tree, where your `__init__.py` can import it. This `_version.py` knows how to -dynamically ask the VCS tool for version information at import time. - -`_version.py` also contains `$Revision$` markers, and the installation -process marks `_version.py` to have this marker rewritten with a tag name -during the `git archive` command. As a result, generated tarballs will -contain enough information to get the proper version. - -To allow `setup.py` to compute a version too, a `versioneer.py` is added to -the top level of your source tree, next to `setup.py` and the `setup.cfg` -that configures it. This overrides several distutils/setuptools commands to -compute the version when invoked, and changes `setup.py build` and `setup.py -sdist` to replace `_version.py` with a small static file that contains just -the generated version data. - -## Installation - -See [INSTALL.md](./INSTALL.md) for detailed installation instructions. - -## Version-String Flavors - -Code which uses Versioneer can learn about its version string at runtime by -importing `_version` from your main `__init__.py` file and running the -`get_versions()` function. From the "outside" (e.g. in `setup.py`), you can -import the top-level `versioneer.py` and run `get_versions()`. - -Both functions return a dictionary with different flavors of version -information: - -* `['version']`: A condensed version string, rendered using the selected - style. This is the most commonly used value for the project's version - string. The default "pep440" style yields strings like `0.11`, - `0.11+2.g1076c97`, or `0.11+2.g1076c97.dirty`. See the "Styles" section - below for alternative styles. - -* `['full-revisionid']`: detailed revision identifier. For Git, this is the - full SHA1 commit id, e.g. "1076c978a8d3cfc70f408fe5974aa6c092c949ac". - -* `['date']`: Date and time of the latest `HEAD` commit. For Git, it is the - commit date in ISO 8601 format. This will be None if the date is not - available. - -* `['dirty']`: a boolean, True if the tree has uncommitted changes. Note that - this is only accurate if run in a VCS checkout, otherwise it is likely to - be False or None - -* `['error']`: if the version string could not be computed, this will be set - to a string describing the problem, otherwise it will be None. It may be - useful to throw an exception in setup.py if this is set, to avoid e.g. - creating tarballs with a version string of "unknown". - -Some variants are more useful than others. Including `full-revisionid` in a -bug report should allow developers to reconstruct the exact code being tested -(or indicate the presence of local changes that should be shared with the -developers). `version` is suitable for display in an "about" box or a CLI -`--version` output: it can be easily compared against release notes and lists -of bugs fixed in various releases. - -The installer adds the following text to your `__init__.py` to place a basic -version in `YOURPROJECT.__version__`: - - from ._version import get_versions - __version__ = get_versions()['version'] - del get_versions - -## Styles - -The setup.cfg `style=` configuration controls how the VCS information is -rendered into a version string. - -The default style, "pep440", produces a PEP440-compliant string, equal to the -un-prefixed tag name for actual releases, and containing an additional "local -version" section with more detail for in-between builds. For Git, this is -TAG[+DISTANCE.gHEX[.dirty]] , using information from `git describe --tags ---dirty --always`. For example "0.11+2.g1076c97.dirty" indicates that the -tree is like the "1076c97" commit but has uncommitted changes (".dirty"), and -that this commit is two revisions ("+2") beyond the "0.11" tag. For released -software (exactly equal to a known tag), the identifier will only contain the -stripped tag, e.g. "0.11". - -Other styles are available. See [details.md](details.md) in the Versioneer -source tree for descriptions. - -## Debugging - -Versioneer tries to avoid fatal errors: if something goes wrong, it will tend -to return a version of "0+unknown". To investigate the problem, run `setup.py -version`, which will run the version-lookup code in a verbose mode, and will -display the full contents of `get_versions()` (including the `error` string, -which may help identify what went wrong). - -## Known Limitations - -Some situations are known to cause problems for Versioneer. This details the -most significant ones. More can be found on Github -[issues page](https://github.com/python-versioneer/python-versioneer/issues). - -### Subprojects - -Versioneer has limited support for source trees in which `setup.py` is not in -the root directory (e.g. `setup.py` and `.git/` are *not* siblings). The are -two common reasons why `setup.py` might not be in the root: - -* Source trees which contain multiple subprojects, such as - [Buildbot](https://github.com/buildbot/buildbot), which contains both - "master" and "slave" subprojects, each with their own `setup.py`, - `setup.cfg`, and `tox.ini`. Projects like these produce multiple PyPI - distributions (and upload multiple independently-installable tarballs). -* Source trees whose main purpose is to contain a C library, but which also - provide bindings to Python (and perhaps other languages) in subdirectories. - -Versioneer will look for `.git` in parent directories, and most operations -should get the right version string. However `pip` and `setuptools` have bugs -and implementation details which frequently cause `pip install .` from a -subproject directory to fail to find a correct version string (so it usually -defaults to `0+unknown`). - -`pip install --editable .` should work correctly. `setup.py install` might -work too. - -Pip-8.1.1 is known to have this problem, but hopefully it will get fixed in -some later version. - -[Bug #38](https://github.com/python-versioneer/python-versioneer/issues/38) is tracking -this issue. The discussion in -[PR #61](https://github.com/python-versioneer/python-versioneer/pull/61) describes the -issue from the Versioneer side in more detail. -[pip PR#3176](https://github.com/pypa/pip/pull/3176) and -[pip PR#3615](https://github.com/pypa/pip/pull/3615) contain work to improve -pip to let Versioneer work correctly. - -Versioneer-0.16 and earlier only looked for a `.git` directory next to the -`setup.cfg`, so subprojects were completely unsupported with those releases. - -### Editable installs with setuptools <= 18.5 - -`setup.py develop` and `pip install --editable .` allow you to install a -project into a virtualenv once, then continue editing the source code (and -test) without re-installing after every change. - -"Entry-point scripts" (`setup(entry_points={"console_scripts": ..})`) are a -convenient way to specify executable scripts that should be installed along -with the python package. - -These both work as expected when using modern setuptools. When using -setuptools-18.5 or earlier, however, certain operations will cause -`pkg_resources.DistributionNotFound` errors when running the entrypoint -script, which must be resolved by re-installing the package. This happens -when the install happens with one version, then the egg_info data is -regenerated while a different version is checked out. Many setup.py commands -cause egg_info to be rebuilt (including `sdist`, `wheel`, and installing into -a different virtualenv), so this can be surprising. - -[Bug #83](https://github.com/python-versioneer/python-versioneer/issues/83) describes -this one, but upgrading to a newer version of setuptools should probably -resolve it. - - -## Updating Versioneer - -To upgrade your project to a new release of Versioneer, do the following: - -* install the new Versioneer (`pip install -U versioneer` or equivalent) -* edit `setup.cfg`, if necessary, to include any new configuration settings - indicated by the release notes. See [UPGRADING](./UPGRADING.md) for details. -* re-run `versioneer install` in your source tree, to replace - `SRC/_version.py` -* commit any changed files - -## Future Directions - -This tool is designed to make it easily extended to other version-control -systems: all VCS-specific components are in separate directories like -src/git/ . The top-level `versioneer.py` script is assembled from these -components by running make-versioneer.py . In the future, make-versioneer.py -will take a VCS name as an argument, and will construct a version of -`versioneer.py` that is specific to the given VCS. It might also take the -configuration arguments that are currently provided manually during -installation by editing setup.py . Alternatively, it might go the other -direction and include code from all supported VCS systems, reducing the -number of intermediate scripts. - -## Similar projects - -* [setuptools_scm](https://github.com/pypa/setuptools_scm/) - a non-vendored build-time - dependency -* [minver](https://github.com/jbweston/miniver) - a lightweight reimplementation of - versioneer - -## License - -To make Versioneer easier to embed, all its code is dedicated to the public -domain. The `_version.py` that it creates is also in the public domain. -Specifically, both are released under the Creative Commons "Public Domain -Dedication" license (CC0-1.0), as described in -https://creativecommons.org/publicdomain/zero/1.0/ . - -[pypi-image]: https://img.shields.io/pypi/v/versioneer.svg -[pypi-url]: https://pypi.python.org/pypi/versioneer/ -[travis-image]: -https://img.shields.io/travis/com/python-versioneer/python-versioneer.svg -[travis-url]: https://travis-ci.com/github/python-versioneer/python-versioneer - -""" - -import configparser -import errno -import json -import os -import re -import subprocess -import sys - - -class VersioneerConfig: - """Container for Versioneer configuration parameters.""" - - -def get_root(): - """Get the project root directory. - - We require that all commands are run from the project root, i.e. the - directory that contains setup.py, setup.cfg, and versioneer.py . - """ - root = os.path.realpath(os.path.abspath(os.getcwd())) - setup_py = os.path.join(root, "setup.py") - versioneer_py = os.path.join(root, "versioneer.py") - if not (os.path.exists(setup_py) or os.path.exists(versioneer_py)): - # allow 'python path/to/setup.py COMMAND' - root = os.path.dirname(os.path.realpath(os.path.abspath(sys.argv[0]))) - setup_py = os.path.join(root, "setup.py") - versioneer_py = os.path.join(root, "versioneer.py") - if not (os.path.exists(setup_py) or os.path.exists(versioneer_py)): - err = ("Versioneer was unable to run the project root directory. " - "Versioneer requires setup.py to be executed from " - "its immediate directory (like 'python setup.py COMMAND'), " - "or in a way that lets it use sys.argv[0] to find the root " - "(like 'python path/to/setup.py COMMAND').") - raise VersioneerBadRootError(err) - try: - # Certain runtime workflows (setup.py install/develop in a setuptools - # tree) execute all dependencies in a single python process, so - # "versioneer" may be imported multiple times, and python's shared - # module-import table will cache the first one. So we can't use - # os.path.dirname(__file__), as that will find whichever - # versioneer.py was first imported, even in later projects. - me = os.path.realpath(os.path.abspath(__file__)) - me_dir = os.path.normcase(os.path.splitext(me)[0]) - vsr_dir = os.path.normcase(os.path.splitext(versioneer_py)[0]) - if me_dir != vsr_dir: - print("Warning: build in %s is using versioneer.py from %s" - % (os.path.dirname(me), versioneer_py)) - except NameError: - pass - return root - - -def get_config_from_root(root): - """Read the project setup.cfg file to determine Versioneer config.""" - # This might raise EnvironmentError (if setup.cfg is missing), or - # configparser.NoSectionError (if it lacks a [versioneer] section), or - # configparser.NoOptionError (if it lacks "VCS="). See the docstring at - # the top of versioneer.py for instructions on writing your setup.cfg . - setup_cfg = os.path.join(root, "setup.cfg") - parser = configparser.ConfigParser() - with open(setup_cfg, "r") as f: - parser.read_file(f) - VCS = parser.get("versioneer", "VCS") # mandatory - - def get(parser, name): - if parser.has_option("versioneer", name): - return parser.get("versioneer", name) - return None - cfg = VersioneerConfig() - cfg.VCS = VCS - cfg.style = get(parser, "style") or "" - cfg.versionfile_source = get(parser, "versionfile_source") - cfg.versionfile_build = get(parser, "versionfile_build") - cfg.tag_prefix = get(parser, "tag_prefix") - if cfg.tag_prefix in ("''", '""'): - cfg.tag_prefix = "" - cfg.parentdir_prefix = get(parser, "parentdir_prefix") - cfg.verbose = get(parser, "verbose") - cfg.vcs_root = get(parser, "vcs_root") - return cfg - - -class NotThisMethod(Exception): - """Exception raised if a method is not valid for the current scenario.""" - - -# these dictionaries contain VCS-specific tools -LONG_VERSION_PY = {} -HANDLERS = {} - - -def register_vcs_handler(vcs, method): # decorator - """Create decorator to mark a method as the handler of a VCS.""" - def decorate(f): - """Store f in HANDLERS[vcs][method].""" - if vcs not in HANDLERS: - HANDLERS[vcs] = {} - HANDLERS[vcs][method] = f - return f - return decorate - - -def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, - env=None): - """Call the given command(s).""" - assert isinstance(commands, list) - p = None - for c in commands: - try: - dispcmd = str([c] + args) - # remember shell=False, so use git.cmd on windows, not just git - p = subprocess.Popen([c] + args, cwd=cwd, env=env, - stdout=subprocess.PIPE, - stderr=(subprocess.PIPE if hide_stderr - else None)) - break - except EnvironmentError: - e = sys.exc_info()[1] - if e.errno == errno.ENOENT: - continue - if verbose: - print("unable to run %s" % dispcmd) - print(e) - return None, None - else: - if verbose: - print("unable to find command, tried %s" % (commands,)) - return None, None - stdout = p.communicate()[0].strip().decode() - if p.returncode != 0: - if verbose: - print("unable to run %s (error)" % dispcmd) - print("stdout was %s" % stdout) - return None, p.returncode - return stdout, p.returncode - - -LONG_VERSION_PY['git'] = r''' -# This file helps to compute a version number in source trees obtained from -# git-archive tarball (such as those provided by githubs download-from-tag -# feature). Distribution tarballs (built by setup.py sdist) and build -# directories (produced by setup.py build) will contain a much shorter file -# that just contains the computed version number. - -# This file is released into the public domain. Generated by -# versioneer-0.19 (https://github.com/python-versioneer/python-versioneer) - -"""Git implementation of _version.py.""" - -import errno -import os -import re -import subprocess -import sys - - -def get_keywords(): - """Get the keywords needed to look up the version information.""" - # these strings will be replaced by git during git-archive. - # setup.py/versioneer.py will grep for the variable names, so they must - # each be defined on a line of their own. _version.py will just call - # get_keywords(). - git_refnames = "%(DOLLAR)sFormat:%%d%(DOLLAR)s" - git_full = "%(DOLLAR)sFormat:%%H%(DOLLAR)s" - git_date = "%(DOLLAR)sFormat:%%ci%(DOLLAR)s" - keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} - return keywords - - -class VersioneerConfig: - """Container for Versioneer configuration parameters.""" - - -def get_config(): - """Create, populate and return the VersioneerConfig() object.""" - # these strings are filled in when 'setup.py versioneer' creates - # _version.py - cfg = VersioneerConfig() - cfg.VCS = "git" - cfg.style = "%(STYLE)s" - cfg.tag_prefix = "%(TAG_PREFIX)s" - cfg.parentdir_prefix = "%(PARENTDIR_PREFIX)s" - cfg.versionfile_source = "%(VERSIONFILE_SOURCE)s" - cfg.verbose = False - return cfg - - -class NotThisMethod(Exception): - """Exception raised if a method is not valid for the current scenario.""" - - -LONG_VERSION_PY = {} -HANDLERS = {} - - -def register_vcs_handler(vcs, method): # decorator - """Create decorator to mark a method as the handler of a VCS.""" - def decorate(f): - """Store f in HANDLERS[vcs][method].""" - if vcs not in HANDLERS: - HANDLERS[vcs] = {} - HANDLERS[vcs][method] = f - return f - return decorate - - -def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, - env=None): - """Call the given command(s).""" - assert isinstance(commands, list) - p = None - for c in commands: - try: - dispcmd = str([c] + args) - # remember shell=False, so use git.cmd on windows, not just git - p = subprocess.Popen([c] + args, cwd=cwd, env=env, - stdout=subprocess.PIPE, - stderr=(subprocess.PIPE if hide_stderr - else None)) - break - except EnvironmentError: - e = sys.exc_info()[1] - if e.errno == errno.ENOENT: - continue - if verbose: - print("unable to run %%s" %% dispcmd) - print(e) - return None, None - else: - if verbose: - print("unable to find command, tried %%s" %% (commands,)) - return None, None - stdout = p.communicate()[0].strip().decode() - if p.returncode != 0: - if verbose: - print("unable to run %%s (error)" %% dispcmd) - print("stdout was %%s" %% stdout) - return None, p.returncode - return stdout, p.returncode - - -def versions_from_parentdir(parentdir_prefix, root, verbose): - """Try to determine the version from the parent directory name. - - Source tarballs conventionally unpack into a directory that includes both - the project name and a version string. We will also support searching up - two directory levels for an appropriately named parent directory - """ - rootdirs = [] - - for i in range(3): - dirname = os.path.basename(root) - if dirname.startswith(parentdir_prefix): - return {"version": dirname[len(parentdir_prefix):], - "full-revisionid": None, - "dirty": False, "error": None, "date": None} - else: - rootdirs.append(root) - root = os.path.dirname(root) # up a level - - if verbose: - print("Tried directories %%s but none started with prefix %%s" %% - (str(rootdirs), parentdir_prefix)) - raise NotThisMethod("rootdir doesn't start with parentdir_prefix") - - -@register_vcs_handler("git", "get_keywords") -def git_get_keywords(versionfile_abs): - """Extract version information from the given file.""" - # the code embedded in _version.py can just fetch the value of these - # keywords. When used from setup.py, we don't want to import _version.py, - # so we do it with a regexp instead. This function is not used from - # _version.py. - keywords = {} - try: - f = open(versionfile_abs, "r") - for line in f.readlines(): - if line.strip().startswith("git_refnames ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["refnames"] = mo.group(1) - if line.strip().startswith("git_full ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["full"] = mo.group(1) - if line.strip().startswith("git_date ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["date"] = mo.group(1) - f.close() - except EnvironmentError: - pass - return keywords - - -@register_vcs_handler("git", "keywords") -def git_versions_from_keywords(keywords, tag_prefix, verbose): - """Get version information from git keywords.""" - if not keywords: - raise NotThisMethod("no keywords at all, weird") - date = keywords.get("date") - if date is not None: - # Use only the last line. Previous lines may contain GPG signature - # information. - date = date.splitlines()[-1] - - # git-2.2.0 added "%%cI", which expands to an ISO-8601 -compliant - # datestamp. However we prefer "%%ci" (which expands to an "ISO-8601 - # -like" string, which we must then edit to make compliant), because - # it's been around since git-1.5.3, and it's too difficult to - # discover which version we're using, or to work around using an - # older one. - date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) - refnames = keywords["refnames"].strip() - if refnames.startswith("$Format"): - if verbose: - print("keywords are unexpanded, not using") - raise NotThisMethod("unexpanded keywords, not a git-archive tarball") - refs = set([r.strip() for r in refnames.strip("()").split(",")]) - # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of - # just "foo-1.0". If we see a "tag: " prefix, prefer those. - TAG = "tag: " - tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)]) - if not tags: - # Either we're using git < 1.8.3, or there really are no tags. We use - # a heuristic: assume all version tags have a digit. The old git %%d - # expansion behaves like git log --decorate=short and strips out the - # refs/heads/ and refs/tags/ prefixes that would let us distinguish - # between branches and tags. By ignoring refnames without digits, we - # filter out many common branch names like "release" and - # "stabilization", as well as "HEAD" and "master". - tags = set([r for r in refs if re.search(r'\d', r)]) - if verbose: - print("discarding '%%s', no digits" %% ",".join(refs - tags)) - if verbose: - print("likely tags: %%s" %% ",".join(sorted(tags))) - for ref in sorted(tags): - # sorting will prefer e.g. "2.0" over "2.0rc1" - if ref.startswith(tag_prefix): - r = ref[len(tag_prefix):] - if verbose: - print("picking %%s" %% r) - return {"version": r, - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": None, - "date": date} - # no suitable tags, so version is "0+unknown", but full hex is still there - if verbose: - print("no suitable tags, using unknown + full revision id") - return {"version": "0+unknown", - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": "no suitable tags", "date": None} - - -@register_vcs_handler("git", "pieces_from_vcs") -def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): - """Get version from 'git describe' in the root of the source tree. - - This only gets called if the git-archive 'subst' keywords were *not* - expanded, and _version.py hasn't already been rewritten with a short - version string, meaning we're inside a checked out source tree. - """ - GITS = ["git"] - if sys.platform == "win32": - GITS = ["git.cmd", "git.exe"] - - out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, - hide_stderr=True) - if rc != 0: - if verbose: - print("Directory %%s not under git control" %% root) - raise NotThisMethod("'git rev-parse --git-dir' returned error") - - # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] - # if there isn't one, this yields HEX[-dirty] (no NUM) - describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty", - "--always", "--long", - "--match", "%%s*" %% tag_prefix], - cwd=root) - # --long was added in git-1.5.5 - if describe_out is None: - raise NotThisMethod("'git describe' failed") - describe_out = describe_out.strip() - full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) - if full_out is None: - raise NotThisMethod("'git rev-parse' failed") - full_out = full_out.strip() - - pieces = {} - pieces["long"] = full_out - pieces["short"] = full_out[:7] # maybe improved later - pieces["error"] = None - - # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] - # TAG might have hyphens. - git_describe = describe_out - - # look for -dirty suffix - dirty = git_describe.endswith("-dirty") - pieces["dirty"] = dirty - if dirty: - git_describe = git_describe[:git_describe.rindex("-dirty")] - - # now we have TAG-NUM-gHEX or HEX - - if "-" in git_describe: - # TAG-NUM-gHEX - mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) - if not mo: - # unparseable. Maybe git-describe is misbehaving? - pieces["error"] = ("unable to parse git-describe output: '%%s'" - %% describe_out) - return pieces - - # tag - full_tag = mo.group(1) - if not full_tag.startswith(tag_prefix): - if verbose: - fmt = "tag '%%s' doesn't start with prefix '%%s'" - print(fmt %% (full_tag, tag_prefix)) - pieces["error"] = ("tag '%%s' doesn't start with prefix '%%s'" - %% (full_tag, tag_prefix)) - return pieces - pieces["closest-tag"] = full_tag[len(tag_prefix):] - - # distance: number of commits since tag - pieces["distance"] = int(mo.group(2)) - - # commit: short hex revision ID - pieces["short"] = mo.group(3) - - else: - # HEX: no tags - pieces["closest-tag"] = None - count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], - cwd=root) - pieces["distance"] = int(count_out) # total number of commits - - # commit date: see ISO-8601 comment in git_versions_from_keywords() - date = run_command(GITS, ["show", "-s", "--format=%%ci", "HEAD"], - cwd=root)[0].strip() - # Use only the last line. Previous lines may contain GPG signature - # information. - date = date.splitlines()[-1] - pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) - - return pieces - - -def plus_or_dot(pieces): - """Return a + if we don't already have one, else return a .""" - if "+" in pieces.get("closest-tag", ""): - return "." - return "+" - - -def render_pep440(pieces): - """Build up version string, with post-release "local version identifier". - - Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you - get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty - - Exceptions: - 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += plus_or_dot(pieces) - rendered += "%%d.g%%s" %% (pieces["distance"], pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - else: - # exception #1 - rendered = "0+untagged.%%d.g%%s" %% (pieces["distance"], - pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - return rendered - - -def render_pep440_pre(pieces): - """TAG[.post0.devDISTANCE] -- No -dirty. - - Exceptions: - 1: no tags. 0.post0.devDISTANCE - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"]: - rendered += ".post0.dev%%d" %% pieces["distance"] - else: - # exception #1 - rendered = "0.post0.dev%%d" %% pieces["distance"] - return rendered - - -def render_pep440_post(pieces): - """TAG[.postDISTANCE[.dev0]+gHEX] . - - The ".dev0" means dirty. Note that .dev0 sorts backwards - (a dirty tree will appear "older" than the corresponding clean one), - but you shouldn't be releasing software with -dirty anyways. - - Exceptions: - 1: no tags. 0.postDISTANCE[.dev0] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%%d" %% pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - rendered += plus_or_dot(pieces) - rendered += "g%%s" %% pieces["short"] - else: - # exception #1 - rendered = "0.post%%d" %% pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - rendered += "+g%%s" %% pieces["short"] - return rendered - - -def render_pep440_old(pieces): - """TAG[.postDISTANCE[.dev0]] . - - The ".dev0" means dirty. - - Exceptions: - 1: no tags. 0.postDISTANCE[.dev0] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%%d" %% pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - else: - # exception #1 - rendered = "0.post%%d" %% pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - return rendered - - -def render_git_describe(pieces): - """TAG[-DISTANCE-gHEX][-dirty]. - - Like 'git describe --tags --dirty --always'. - - Exceptions: - 1: no tags. HEX[-dirty] (note: no 'g' prefix) - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"]: - rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"]) - else: - # exception #1 - rendered = pieces["short"] - if pieces["dirty"]: - rendered += "-dirty" - return rendered - - -def render_git_describe_long(pieces): - """TAG-DISTANCE-gHEX[-dirty]. - - Like 'git describe --tags --dirty --always -long'. - The distance/hash is unconditional. - - Exceptions: - 1: no tags. HEX[-dirty] (note: no 'g' prefix) - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"]) - else: - # exception #1 - rendered = pieces["short"] - if pieces["dirty"]: - rendered += "-dirty" - return rendered - - -def render(pieces, style): - """Render the given version pieces into the requested style.""" - if pieces["error"]: - return {"version": "unknown", - "full-revisionid": pieces.get("long"), - "dirty": None, - "error": pieces["error"], - "date": None} - - if not style or style == "default": - style = "pep440" # the default - - if style == "pep440": - rendered = render_pep440(pieces) - elif style == "pep440-pre": - rendered = render_pep440_pre(pieces) - elif style == "pep440-post": - rendered = render_pep440_post(pieces) - elif style == "pep440-old": - rendered = render_pep440_old(pieces) - elif style == "git-describe": - rendered = render_git_describe(pieces) - elif style == "git-describe-long": - rendered = render_git_describe_long(pieces) - else: - raise ValueError("unknown style '%%s'" %% style) - - return {"version": rendered, "full-revisionid": pieces["long"], - "dirty": pieces["dirty"], "error": None, - "date": pieces.get("date")} - - -def get_versions(): - """Get version information or return default if unable to do so.""" - # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have - # __file__, we can work backwards from there to the root. Some - # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which - # case we can only use expanded keywords. - - cfg = get_config() - verbose = cfg.verbose - - try: - return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, - verbose) - except NotThisMethod: - pass - - try: - root = os.path.realpath(__file__) - # versionfile_source is the relative path from the top of the source - # tree (where the .git directory might live) to this file. Invert - # this to find the root from __file__. - for i in cfg.versionfile_source.split('/'): - root = os.path.dirname(root) - except NameError: - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, - "error": "unable to find root of source tree", - "date": None} - - try: - pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) - return render(pieces, cfg.style) - except NotThisMethod: - pass - - try: - if cfg.parentdir_prefix: - return versions_from_parentdir(cfg.parentdir_prefix, root, verbose) - except NotThisMethod: - pass - - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, - "error": "unable to compute version", "date": None} -''' - - -@register_vcs_handler("git", "get_keywords") -def git_get_keywords(versionfile_abs): - """Extract version information from the given file.""" - # the code embedded in _version.py can just fetch the value of these - # keywords. When used from setup.py, we don't want to import _version.py, - # so we do it with a regexp instead. This function is not used from - # _version.py. - keywords = {} - try: - f = open(versionfile_abs, "r") - for line in f.readlines(): - if line.strip().startswith("git_refnames ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["refnames"] = mo.group(1) - if line.strip().startswith("git_full ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["full"] = mo.group(1) - if line.strip().startswith("git_date ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["date"] = mo.group(1) - f.close() - except EnvironmentError: - pass - return keywords - - -@register_vcs_handler("git", "keywords") -def git_versions_from_keywords(keywords, tag_prefix, verbose): - """Get version information from git keywords.""" - if not keywords: - raise NotThisMethod("no keywords at all, weird") - date = keywords.get("date") - if date is not None: - # Use only the last line. Previous lines may contain GPG signature - # information. - date = date.splitlines()[-1] - - # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant - # datestamp. However we prefer "%ci" (which expands to an "ISO-8601 - # -like" string, which we must then edit to make compliant), because - # it's been around since git-1.5.3, and it's too difficult to - # discover which version we're using, or to work around using an - # older one. - date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) - refnames = keywords["refnames"].strip() - if refnames.startswith("$Format"): - if verbose: - print("keywords are unexpanded, not using") - raise NotThisMethod("unexpanded keywords, not a git-archive tarball") - refs = set([r.strip() for r in refnames.strip("()").split(",")]) - # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of - # just "foo-1.0". If we see a "tag: " prefix, prefer those. - TAG = "tag: " - tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)]) - if not tags: - # Either we're using git < 1.8.3, or there really are no tags. We use - # a heuristic: assume all version tags have a digit. The old git %d - # expansion behaves like git log --decorate=short and strips out the - # refs/heads/ and refs/tags/ prefixes that would let us distinguish - # between branches and tags. By ignoring refnames without digits, we - # filter out many common branch names like "release" and - # "stabilization", as well as "HEAD" and "master". - tags = set([r for r in refs if re.search(r'\d', r)]) - if verbose: - print("discarding '%s', no digits" % ",".join(refs - tags)) - if verbose: - print("likely tags: %s" % ",".join(sorted(tags))) - for ref in sorted(tags): - # sorting will prefer e.g. "2.0" over "2.0rc1" - if ref.startswith(tag_prefix): - r = ref[len(tag_prefix):] - if verbose: - print("picking %s" % r) - return {"version": r, - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": None, - "date": date} - # no suitable tags, so version is "0+unknown", but full hex is still there - if verbose: - print("no suitable tags, using unknown + full revision id") - return {"version": "0+unknown", - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": "no suitable tags", "date": None} - - -@register_vcs_handler("git", "pieces_from_vcs") -def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): - """Get version from 'git describe' in the root of the source tree. - - This only gets called if the git-archive 'subst' keywords were *not* - expanded, and _version.py hasn't already been rewritten with a short - version string, meaning we're inside a checked out source tree. - """ - GITS = ["git"] - if sys.platform == "win32": - GITS = ["git.cmd", "git.exe"] - - out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, - hide_stderr=True) - if rc != 0: - if verbose: - print("Directory %s not under git control" % root) - raise NotThisMethod("'git rev-parse --git-dir' returned error") - - # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] - # if there isn't one, this yields HEX[-dirty] (no NUM) - describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty", - "--always", "--long", - "--match", "%s*" % tag_prefix], - cwd=root) - # --long was added in git-1.5.5 - if describe_out is None: - raise NotThisMethod("'git describe' failed") - describe_out = describe_out.strip() - full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) - if full_out is None: - raise NotThisMethod("'git rev-parse' failed") - full_out = full_out.strip() - - pieces = {} - pieces["long"] = full_out - pieces["short"] = full_out[:7] # maybe improved later - pieces["error"] = None - - # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] - # TAG might have hyphens. - git_describe = describe_out - - # look for -dirty suffix - dirty = git_describe.endswith("-dirty") - pieces["dirty"] = dirty - if dirty: - git_describe = git_describe[:git_describe.rindex("-dirty")] - - # now we have TAG-NUM-gHEX or HEX - - if "-" in git_describe: - # TAG-NUM-gHEX - mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) - if not mo: - # unparseable. Maybe git-describe is misbehaving? - pieces["error"] = ("unable to parse git-describe output: '%s'" - % describe_out) - return pieces - - # tag - full_tag = mo.group(1) - if not full_tag.startswith(tag_prefix): - if verbose: - fmt = "tag '%s' doesn't start with prefix '%s'" - print(fmt % (full_tag, tag_prefix)) - pieces["error"] = ("tag '%s' doesn't start with prefix '%s'" - % (full_tag, tag_prefix)) - return pieces - pieces["closest-tag"] = full_tag[len(tag_prefix):] - - # distance: number of commits since tag - pieces["distance"] = int(mo.group(2)) - - # commit: short hex revision ID - pieces["short"] = mo.group(3) - - else: - # HEX: no tags - pieces["closest-tag"] = None - count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], - cwd=root) - pieces["distance"] = int(count_out) # total number of commits - - # commit date: see ISO-8601 comment in git_versions_from_keywords() - date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], - cwd=root)[0].strip() - # Use only the last line. Previous lines may contain GPG signature - # information. - date = date.splitlines()[-1] - pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) - - return pieces - - -def do_vcs_install(manifest_in, versionfile_source, ipy): - """Git-specific installation logic for Versioneer. - - For Git, this means creating/changing .gitattributes to mark _version.py - for export-subst keyword substitution. - """ - GITS = ["git"] - if sys.platform == "win32": - GITS = ["git.cmd", "git.exe"] - files = [manifest_in, versionfile_source] - if ipy: - files.append(ipy) - try: - me = __file__ - if me.endswith(".pyc") or me.endswith(".pyo"): - me = os.path.splitext(me)[0] + ".py" - versioneer_file = os.path.relpath(me) - except NameError: - versioneer_file = "versioneer.py" - files.append(versioneer_file) - present = False - try: - f = open(".gitattributes", "r") - for line in f.readlines(): - if line.strip().startswith(versionfile_source): - if "export-subst" in line.strip().split()[1:]: - present = True - f.close() - except EnvironmentError: - pass - if not present: - f = open(".gitattributes", "a+") - f.write("%s export-subst\n" % versionfile_source) - f.close() - files.append(".gitattributes") - run_command(GITS, ["add", "--"] + files) - - -def versions_from_parentdir(parentdir_prefix, root, verbose): - """Try to determine the version from the parent directory name. - - Source tarballs conventionally unpack into a directory that includes both - the project name and a version string. We will also support searching up - two directory levels for an appropriately named parent directory - """ - rootdirs = [] - - for i in range(3): - dirname = os.path.basename(root) - if dirname.startswith(parentdir_prefix): - return {"version": dirname[len(parentdir_prefix):], - "full-revisionid": None, - "dirty": False, "error": None, "date": None} - else: - rootdirs.append(root) - root = os.path.dirname(root) # up a level - - if verbose: - print("Tried directories %s but none started with prefix %s" % - (str(rootdirs), parentdir_prefix)) - raise NotThisMethod("rootdir doesn't start with parentdir_prefix") - - -SHORT_VERSION_PY = """ -# This file was generated by 'versioneer.py' (0.19) from -# revision-control system data, or from the parent directory name of an -# unpacked source archive. Distribution tarballs contain a pre-generated copy -# of this file. - -import json - -version_json = ''' -%s -''' # END VERSION_JSON - - -def get_versions(): - return json.loads(version_json) -""" - - -def versions_from_file(filename): - """Try to determine the version from _version.py if present.""" - try: - with open(filename) as f: - contents = f.read() - except EnvironmentError: - raise NotThisMethod("unable to read _version.py") - mo = re.search(r"version_json = '''\n(.*)''' # END VERSION_JSON", - contents, re.M | re.S) - if not mo: - mo = re.search(r"version_json = '''\r\n(.*)''' # END VERSION_JSON", - contents, re.M | re.S) - if not mo: - raise NotThisMethod("no version_json in _version.py") - return json.loads(mo.group(1)) - - -def write_to_version_file(filename, versions): - """Write the given version number to the given _version.py file.""" - os.unlink(filename) - contents = json.dumps(versions, sort_keys=True, - indent=1, separators=(",", ": ")) - with open(filename, "w") as f: - f.write(SHORT_VERSION_PY % contents) - - print("set %s to '%s'" % (filename, versions["version"])) - - -def plus_or_dot(pieces): - """Return a + if we don't already have one, else return a .""" - if "+" in pieces.get("closest-tag", ""): - return "." - return "+" - - -def render_pep440(pieces): - """Build up version string, with post-release "local version identifier". - - Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you - get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty - - Exceptions: - 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += plus_or_dot(pieces) - rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - else: - # exception #1 - rendered = "0+untagged.%d.g%s" % (pieces["distance"], - pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - return rendered - - -def render_pep440_pre(pieces): - """TAG[.post0.devDISTANCE] -- No -dirty. - - Exceptions: - 1: no tags. 0.post0.devDISTANCE - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"]: - rendered += ".post0.dev%d" % pieces["distance"] - else: - # exception #1 - rendered = "0.post0.dev%d" % pieces["distance"] - return rendered - - -def render_pep440_post(pieces): - """TAG[.postDISTANCE[.dev0]+gHEX] . - - The ".dev0" means dirty. Note that .dev0 sorts backwards - (a dirty tree will appear "older" than the corresponding clean one), - but you shouldn't be releasing software with -dirty anyways. - - Exceptions: - 1: no tags. 0.postDISTANCE[.dev0] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - rendered += plus_or_dot(pieces) - rendered += "g%s" % pieces["short"] - else: - # exception #1 - rendered = "0.post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - rendered += "+g%s" % pieces["short"] - return rendered - - -def render_pep440_old(pieces): - """TAG[.postDISTANCE[.dev0]] . - - The ".dev0" means dirty. - - Exceptions: - 1: no tags. 0.postDISTANCE[.dev0] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - else: - # exception #1 - rendered = "0.post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - return rendered - - -def render_git_describe(pieces): - """TAG[-DISTANCE-gHEX][-dirty]. - - Like 'git describe --tags --dirty --always'. - - Exceptions: - 1: no tags. HEX[-dirty] (note: no 'g' prefix) - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"]: - rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) - else: - # exception #1 - rendered = pieces["short"] - if pieces["dirty"]: - rendered += "-dirty" - return rendered - - -def render_git_describe_long(pieces): - """TAG-DISTANCE-gHEX[-dirty]. - - Like 'git describe --tags --dirty --always -long'. - The distance/hash is unconditional. - - Exceptions: - 1: no tags. HEX[-dirty] (note: no 'g' prefix) - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) - else: - # exception #1 - rendered = pieces["short"] - if pieces["dirty"]: - rendered += "-dirty" - return rendered - - -def render(pieces, style): - """Render the given version pieces into the requested style.""" - if pieces["error"]: - return {"version": "unknown", - "full-revisionid": pieces.get("long"), - "dirty": None, - "error": pieces["error"], - "date": None} - - if not style or style == "default": - style = "pep440" # the default - - if style == "pep440": - rendered = render_pep440(pieces) - elif style == "pep440-pre": - rendered = render_pep440_pre(pieces) - elif style == "pep440-post": - rendered = render_pep440_post(pieces) - elif style == "pep440-old": - rendered = render_pep440_old(pieces) - elif style == "git-describe": - rendered = render_git_describe(pieces) - elif style == "git-describe-long": - rendered = render_git_describe_long(pieces) - else: - raise ValueError("unknown style '%s'" % style) - - return {"version": rendered, "full-revisionid": pieces["long"], - "dirty": pieces["dirty"], "error": None, - "date": pieces.get("date")} - - -class VersioneerBadRootError(Exception): - """The project root directory is unknown or missing key files.""" - - -def get_versions(verbose=False): - """Get the project version from whatever source is available. - - Returns dict with two keys: 'version' and 'full'. - """ - if "versioneer" in sys.modules: - # see the discussion in cmdclass.py:get_cmdclass() - del sys.modules["versioneer"] - - root = get_root() - cfg = get_config_from_root(root) - - assert cfg.VCS is not None, "please set [versioneer]VCS= in setup.cfg" - handlers = HANDLERS.get(cfg.VCS) - assert handlers, "unrecognized VCS '%s'" % cfg.VCS - verbose = verbose or cfg.verbose - assert cfg.versionfile_source is not None, \ - "please set versioneer.versionfile_source" - assert cfg.tag_prefix is not None, "please set versioneer.tag_prefix" - - versionfile_abs = os.path.join(root, cfg.versionfile_source) - - # extract version from first of: _version.py, VCS command (e.g. 'git - # describe'), parentdir. This is meant to work for developers using a - # source checkout, for users of a tarball created by 'setup.py sdist', - # and for users of a tarball/zipball created by 'git archive' or github's - # download-from-tag feature or the equivalent in other VCSes. - - get_keywords_f = handlers.get("get_keywords") - from_keywords_f = handlers.get("keywords") - if get_keywords_f and from_keywords_f: - try: - keywords = get_keywords_f(versionfile_abs) - ver = from_keywords_f(keywords, cfg.tag_prefix, verbose) - if verbose: - print("got version from expanded keyword %s" % ver) - return ver - except NotThisMethod: - pass - - try: - ver = versions_from_file(versionfile_abs) - if verbose: - print("got version from file %s %s" % (versionfile_abs, ver)) - return ver - except NotThisMethod: - pass - - from_vcs_f = handlers.get("pieces_from_vcs") - if from_vcs_f: - try: - if cfg.vcs_root: - root = cfg.vcs_root - if not os.path.isabs(root) and 'CI_PROJECT_DIR' in os.environ: - root = os.path.join(os.environ['CI_PROJECT_DIR'], root) - - pieces = from_vcs_f(cfg.tag_prefix, root, verbose) - ver = render(pieces, cfg.style) - if verbose: - print("got version from VCS %s" % ver) - return ver - except NotThisMethod: - pass - - try: - if cfg.parentdir_prefix: - ver = versions_from_parentdir(cfg.parentdir_prefix, root, verbose) - if verbose: - print("got version from parentdir %s" % ver) - return ver - except NotThisMethod: - pass - - if verbose: - print("unable to compute version") - - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, "error": "unable to compute version", - "date": None} - - -def get_version(): - """Get the short version string for this project.""" - return get_versions()["version"] - - -def get_cmdclass(cmdclass=None): - """Get the custom setuptools/distutils subclasses used by Versioneer. - - If the package uses a different cmdclass (e.g. one from numpy), it - should be provide as an argument. - """ - if "versioneer" in sys.modules: - del sys.modules["versioneer"] - # this fixes the "python setup.py develop" case (also 'install' and - # 'easy_install .'), in which subdependencies of the main project are - # built (using setup.py bdist_egg) in the same python process. Assume - # a main project A and a dependency B, which use different versions - # of Versioneer. A's setup.py imports A's Versioneer, leaving it in - # sys.modules by the time B's setup.py is executed, causing B to run - # with the wrong versioneer. Setuptools wraps the sub-dep builds in a - # sandbox that restores sys.modules to it's pre-build state, so the - # parent is protected against the child's "import versioneer". By - # removing ourselves from sys.modules here, before the child build - # happens, we protect the child from the parent's versioneer too. - # Also see https://github.com/python-versioneer/python-versioneer/issues/52 - - cmds = {} if cmdclass is None else cmdclass.copy() - - # we add "version" to both distutils and setuptools - from distutils.core import Command - - class cmd_version(Command): - description = "report generated version string" - user_options = [] - boolean_options = [] - - def initialize_options(self): - pass - - def finalize_options(self): - pass - - def run(self): - vers = get_versions(verbose=True) - print("Version: %s" % vers["version"]) - print(" full-revisionid: %s" % vers.get("full-revisionid")) - print(" dirty: %s" % vers.get("dirty")) - print(" date: %s" % vers.get("date")) - if vers["error"]: - print(" error: %s" % vers["error"]) - cmds["version"] = cmd_version - - # we override "build_py" in both distutils and setuptools - # - # most invocation pathways end up running build_py: - # distutils/build -> build_py - # distutils/install -> distutils/build ->.. - # setuptools/bdist_wheel -> distutils/install ->.. - # setuptools/bdist_egg -> distutils/install_lib -> build_py - # setuptools/install -> bdist_egg ->.. - # setuptools/develop -> ? - # pip install: - # copies source tree to a tempdir before running egg_info/etc - # if .git isn't copied too, 'git describe' will fail - # then does setup.py bdist_wheel, or sometimes setup.py install - # setup.py egg_info -> ? - - # we override different "build_py" commands for both environments - if 'build_py' in cmds: - _build_py = cmds['build_py'] - elif "setuptools" in sys.modules: - from setuptools.command.build_py import build_py as _build_py - else: - from distutils.command.build_py import build_py as _build_py - - class cmd_build_py(_build_py): - def run(self): - root = get_root() - cfg = get_config_from_root(root) - versions = get_versions() - _build_py.run(self) - # now locate _version.py in the new build/ directory and replace - # it with an updated value - if cfg.versionfile_build: - target_versionfile = os.path.join(self.build_lib, - cfg.versionfile_build) - print("UPDATING %s" % target_versionfile) - write_to_version_file(target_versionfile, versions) - cmds["build_py"] = cmd_build_py - - if "setuptools" in sys.modules: - from setuptools.command.build_ext import build_ext as _build_ext - else: - from distutils.command.build_ext import build_ext as _build_ext - - class cmd_build_ext(_build_ext): - def run(self): - root = get_root() - cfg = get_config_from_root(root) - versions = get_versions() - _build_ext.run(self) - if self.inplace: - # build_ext --inplace will only build extensions in - # build/lib<..> dir with no _version.py to write to. - # As in place builds will already have a _version.py - # in the module dir, we do not need to write one. - return - # now locate _version.py in the new build/ directory and replace - # it with an updated value - target_versionfile = os.path.join(self.build_lib, - cfg.versionfile_build) - print("UPDATING %s" % target_versionfile) - write_to_version_file(target_versionfile, versions) - cmds["build_ext"] = cmd_build_ext - - if "cx_Freeze" in sys.modules: # cx_freeze enabled? - from cx_Freeze.dist import build_exe as _build_exe - # nczeczulin reports that py2exe won't like the pep440-style string - # as FILEVERSION, but it can be used for PRODUCTVERSION, e.g. - # setup(console=[{ - # "version": versioneer.get_version().split("+", 1)[0], # FILEVERSION - # "product_version": versioneer.get_version(), - # ... - - class cmd_build_exe(_build_exe): - def run(self): - root = get_root() - cfg = get_config_from_root(root) - versions = get_versions() - target_versionfile = cfg.versionfile_source - print("UPDATING %s" % target_versionfile) - write_to_version_file(target_versionfile, versions) - - _build_exe.run(self) - os.unlink(target_versionfile) - with open(cfg.versionfile_source, "w") as f: - LONG = LONG_VERSION_PY[cfg.VCS] - f.write(LONG % - {"DOLLAR": "$", - "STYLE": cfg.style, - "TAG_PREFIX": cfg.tag_prefix, - "PARENTDIR_PREFIX": cfg.parentdir_prefix, - "VERSIONFILE_SOURCE": cfg.versionfile_source, - }) - cmds["build_exe"] = cmd_build_exe - del cmds["build_py"] - - if 'py2exe' in sys.modules: # py2exe enabled? - from py2exe.distutils_buildexe import py2exe as _py2exe - - class cmd_py2exe(_py2exe): - def run(self): - root = get_root() - cfg = get_config_from_root(root) - versions = get_versions() - target_versionfile = cfg.versionfile_source - print("UPDATING %s" % target_versionfile) - write_to_version_file(target_versionfile, versions) - - _py2exe.run(self) - os.unlink(target_versionfile) - with open(cfg.versionfile_source, "w") as f: - LONG = LONG_VERSION_PY[cfg.VCS] - f.write(LONG % - {"DOLLAR": "$", - "STYLE": cfg.style, - "TAG_PREFIX": cfg.tag_prefix, - "PARENTDIR_PREFIX": cfg.parentdir_prefix, - "VERSIONFILE_SOURCE": cfg.versionfile_source, - }) - cmds["py2exe"] = cmd_py2exe - - # we override different "sdist" commands for both environments - if 'sdist' in cmds: - _sdist = cmds['sdist'] - elif "setuptools" in sys.modules: - from setuptools.command.sdist import sdist as _sdist - else: - from distutils.command.sdist import sdist as _sdist - - class cmd_sdist(_sdist): - def run(self): - versions = get_versions() - self._versioneer_generated_versions = versions - # unless we update this, the command will keep using the old - # version - self.distribution.metadata.version = versions["version"] - return _sdist.run(self) - - def make_release_tree(self, base_dir, files): - root = get_root() - cfg = get_config_from_root(root) - _sdist.make_release_tree(self, base_dir, files) - # now locate _version.py in the new base_dir directory - # (remembering that it may be a hardlink) and replace it with an - # updated value - target_versionfile = os.path.join(base_dir, cfg.versionfile_source) - print("UPDATING %s" % target_versionfile) - write_to_version_file(target_versionfile, - self._versioneer_generated_versions) - cmds["sdist"] = cmd_sdist - - return cmds - - -CONFIG_ERROR = """ -setup.cfg is missing the necessary Versioneer configuration. You need -a section like: - - [versioneer] - VCS = git - style = pep440 - versionfile_source = src/myproject/_version.py - versionfile_build = myproject/_version.py - tag_prefix = - parentdir_prefix = myproject- - -You will also need to edit your setup.py to use the results: - - import versioneer - setup(version=versioneer.get_version(), - cmdclass=versioneer.get_cmdclass(), ...) - -Please read the docstring in ./versioneer.py for configuration instructions, -edit setup.cfg, and re-run the installer or 'python versioneer.py setup'. -""" - -SAMPLE_CONFIG = """ -# See the docstring in versioneer.py for instructions. Note that you must -# re-run 'versioneer.py setup' after changing this section, and commit the -# resulting files. - -[versioneer] -#VCS = git -#style = pep440 -#versionfile_source = -#versionfile_build = -#tag_prefix = -#parentdir_prefix = - -""" - -INIT_PY_SNIPPET = """ -from ._version import get_versions -__version__ = get_versions()['version'] -del get_versions -""" - - -def do_setup(): - """Do main VCS-independent setup function for installing Versioneer.""" - root = get_root() - try: - cfg = get_config_from_root(root) - except (EnvironmentError, configparser.NoSectionError, - configparser.NoOptionError) as e: - if isinstance(e, (EnvironmentError, configparser.NoSectionError)): - print("Adding sample versioneer config to setup.cfg", - file=sys.stderr) - with open(os.path.join(root, "setup.cfg"), "a") as f: - f.write(SAMPLE_CONFIG) - print(CONFIG_ERROR, file=sys.stderr) - return 1 - - print(" creating %s" % cfg.versionfile_source) - with open(cfg.versionfile_source, "w") as f: - LONG = LONG_VERSION_PY[cfg.VCS] - f.write(LONG % {"DOLLAR": "$", - "STYLE": cfg.style, - "TAG_PREFIX": cfg.tag_prefix, - "PARENTDIR_PREFIX": cfg.parentdir_prefix, - "VERSIONFILE_SOURCE": cfg.versionfile_source, - }) - - ipy = os.path.join(os.path.dirname(cfg.versionfile_source), - "__init__.py") - if os.path.exists(ipy): - try: - with open(ipy, "r") as f: - old = f.read() - except EnvironmentError: - old = "" - if INIT_PY_SNIPPET not in old: - print(" appending to %s" % ipy) - with open(ipy, "a") as f: - f.write(INIT_PY_SNIPPET) - else: - print(" %s unmodified" % ipy) - else: - print(" %s doesn't exist, ok" % ipy) - ipy = None - - # Make sure both the top-level "versioneer.py" and versionfile_source - # (PKG/_version.py, used by runtime code) are in MANIFEST.in, so - # they'll be copied into source distributions. Pip won't be able to - # install the package without this. - manifest_in = os.path.join(root, "MANIFEST.in") - simple_includes = set() - try: - with open(manifest_in, "r") as f: - for line in f: - if line.startswith("include "): - for include in line.split()[1:]: - simple_includes.add(include) - except EnvironmentError: - pass - # That doesn't cover everything MANIFEST.in can do - # (http://docs.python.org/2/distutils/sourcedist.html#commands), so - # it might give some false negatives. Appending redundant 'include' - # lines is safe, though. - if "versioneer.py" not in simple_includes: - print(" appending 'versioneer.py' to MANIFEST.in") - with open(manifest_in, "a") as f: - f.write("include versioneer.py\n") - else: - print(" 'versioneer.py' already in MANIFEST.in") - if cfg.versionfile_source not in simple_includes: - print(" appending versionfile_source ('%s') to MANIFEST.in" % - cfg.versionfile_source) - with open(manifest_in, "a") as f: - f.write("include %s\n" % cfg.versionfile_source) - else: - print(" versionfile_source already in MANIFEST.in") - - # Make VCS-specific changes. For git, this means creating/changing - # .gitattributes to mark _version.py for export-subst keyword - # substitution. - do_vcs_install(manifest_in, cfg.versionfile_source, ipy) - return 0 - - -def scan_setup_py(): - """Validate the contents of setup.py against Versioneer's expectations.""" - found = set() - setters = False - errors = 0 - with open("setup.py", "r") as f: - for line in f.readlines(): - if "import versioneer" in line: - found.add("import") - if "versioneer.get_cmdclass()" in line: - found.add("cmdclass") - if "versioneer.get_version()" in line: - found.add("get_version") - if "versioneer.VCS" in line: - setters = True - if "versioneer.versionfile_source" in line: - setters = True - if len(found) != 3: - print("") - print("Your setup.py appears to be missing some important items") - print("(but I might be wrong). Please make sure it has something") - print("roughly like the following:") - print("") - print(" import versioneer") - print(" setup( version=versioneer.get_version(),") - print(" cmdclass=versioneer.get_cmdclass(), ...)") - print("") - errors += 1 - if setters: - print("You should remove lines like 'versioneer.VCS = ' and") - print("'versioneer.versionfile_source = ' . This configuration") - print("now lives in setup.cfg, and should be removed from setup.py") - print("") - errors += 1 - return errors - - -if __name__ == "__main__": - cmd = sys.argv[1] - if cmd == "setup": - errors = do_setup() - errors += scan_setup_py() - if errors: - sys.exit(1)