Page MenuHomec4science

No OneTemporary

File Metadata

Created
Thu, Apr 18, 21:22
This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/.clang-tidy b/.clang-tidy
index 59ad5a5e7..b044a7204 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -1,12 +1,15 @@
Checks: "
- modernize-use-*, -modernize-use-trailing-return-type*,
- performance-*,
- mpi-*,
- openmp-*,
- bugprone-*,
- readability-*, -readability-magic-numbers, -readability-redundant-access-specifiers,
- -clang-analyzer-*
+ -*,
+
+ modernize-use-*,
+ -modernize-use-trailing-return-type*,
+
+ readability-*,
+ -readability-magic-numbers,
+ -readability-redundant-access-specifiers,
+ -readability-convert-member-functions-to-static,
+ -readability-isolate-declaration,
"
AnalyzeTemporaryDtors: false
HeaderFilterRegex: 'src/.*'
FormatStyle: file
diff --git a/.codeclimate.yml b/.codeclimate.yml
index bd3c67280..ed1c1d15c 100644
--- a/.codeclimate.yml
+++ b/.codeclimate.yml
@@ -1,11 +1,55 @@
+checks:
+ duplicate:
+ enabled: true
+ exclude_patterns:
+ - "test/"
+ - "examples/"
+ structure:
+ enabled: true
+ exclude_patterns:
+ - "test/"
+
plugins:
editorconfig:
enabled: false
- pylint:
+ config:
+ editorconfig: .editorconfig
+ exclude_patterns:
+ - ".clangd/"
+ - ".cache/"
+ pep8:
enabled: true
+ exclude_patterns:
+ - "test/test_fe_engine/py_engine/py_engine.py"
cppcheck:
- enabled: true
+ enabled: false
project: compile_commands.json
language: c++
check: warning, style, performance
stds: [c++14]
+ fixme:
+ enabled: true
+ exclude_patterns:
+ - "doc/"
+ clang-tidy:
+ enabled: false
+ checks:
+ extra-arg:
+ - -std=c++14
+ - -Ithird-party/akantu_iterators/include
+ - -Ithird-party/iohelper/src
+ - -Itest/ci/includes_for_ci
+ - -Isrc/mesh
+ checks:
+ - '-*'
+ - 'modernize-*'
+ exclude_patterns:
+ - test/
+ - cmake/
+ - examples/
+ - extra_packages/
+ - .clangd/
+
+exclude_patterns:
+- "third-party/"
+- "build*/"
diff --git a/.editorconfig b/.editorconfig
index 70f49cc88..ea74e14a8 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,23 +1,23 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
end_of_line = lf
insert_final_newline = true
max_line_length = 80
[*.{py,cc,hh,cmake,tcc}]
charset = utf-8
indent_style = space
indent_size = 4
[CMakeLists.txt]
charset = utf-8
indent_style = space
-indent_size = 4
+indent_size = 2
[*.tcc]
file_type_emacs = c++
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index db0da6ad4..0195450da 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,299 +1,349 @@
stages:
- configure
- build
- check-warnings
- test
- deploy
.docker_build:
image: 'docker:19.03.11'
stage: .pre
services:
- docker:19.03.11-dind
variables:
# Use TLS https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#tls-enabled
DOCKER_HOST: tcp://docker:2376
DOCKER_TLS_CERTDIR: "/certs"
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- cd test/ci/${IMAGE_NAME}/
- docker build -t registry.gitlab.com/akantu/akantu/${IMAGE_NAME} .
- docker push registry.gitlab.com/akantu/akantu/${IMAGE_NAME}
docker build:debian-testing:
variables:
IMAGE_NAME: debian:testing
extends: .docker_build
rules:
- changes:
- test/ci/debian:testing/Dockerfile
docker build:ubuntu-lts:
variables:
IMAGE_NAME: ubuntu:lts
extends: .docker_build
rules:
- changes:
- test/ci/ubuntu:lts/Dockerfile
.configure:
stage: configure
except:
- tags
variables:
BLA_VENDOR: 'Generic'
script:
- cmake -E make_directory build
- cd build
- 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_PYTHON_INTERFACE:BOOL=TRUE
-DAKANTU_CONTACT_MECHANICS:BOOL=TRUE
-DAKANTU_EXAMPLES:BOOL=TRUE
-DAKANTU_BUILD_ALL_EXAMPLES:BOOL=TRUE
- -DAKANTU_TEST_EXAMPLES:BOOL=FALSE
+ -DAKANTU_TEST_EXAMPLES:BOOL=TRUE
-DAKANTU_TESTS:BOOL=TRUE
-DAKANTU_RUN_IN_DOCKER:BOOL=TRUE
-DCMAKE_BUILD_TYPE:STRING=Coverage ..
+ - cp compile_commmands.json ..
artifacts:
when: on_success
paths:
- build
+ - compile_commands.json
expire_in: 10h
.build:
stage: build
script:
- cmake --build build/src > >(tee -a ${output}-out.log) 2> >(tee -a ${output}-err.log >&2)
- cmake --build build/python > >(tee -a ${output}-out.log) 2> >(tee -a ${output}-err.log >&2)
- cmake --build build/test/ > >(tee -a ${output}-out.log) 2> >(tee -a ${output}-err.log >&2)
- cmake --build build/examples > >(tee -a ${output}-out.log) 2> >(tee -a ${output}-err.log >&2)
artifacts:
when: on_success
paths:
- build/
#- ${output}-out.log
- ${output}-err.log
+ - compile_commands.json
expire_in: 10h
.tests:
stage: test
script:
- cd build
- ctest -T test --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
- gcovr --xml
+ --gcov-executable "${GCOV_EXECUTABLE}"
--output coverage.xml
--object-directory ${CI_PROJECT_DIR}/build
--root ${CI_PROJECT_DIR} -s || true
artifacts:
when: always
paths:
- build/juint.xml
- build/coverage.xml
reports:
junit:
- build/juint.xml
cobertura:
- build/coverage.xml
.analyse_build:
stage: check-warnings
script:
- if [[ $(cat ${output}-err.log | grep warning -i) ]]; then
- cat ${output}-err.log;
- exit 1;
- fi
allow_failure: true
artifacts:
when: on_failure
paths:
- "$output-err.log"
# ------------------------------------------------------------------------------
.cache_build:
variables:
- CCACHE_BASEDIRE: ${CI_PROJECT_DIR}/build
+ CCACHE_BASEDIR: ${CI_PROJECT_DIR}/build
CCACHE_DIR: ${CI_PROJECT_DIR}/.ccache
CCACHE_NOHASHDIR: 1
CCACHE_COMPILERCHECK: content
cache:
key: ${output}
policy: pull-push
paths:
- .ccache/
- third-party/google-test
- third-party/pybind11
before_script:
- ccache --zero-stats || true
after_script:
- ccache --show-stats || true
# ------------------------------------------------------------------------------
.image_debian_testing:
image: registry.gitlab.com/akantu/akantu/debian:testing
.image_ubuntu_lts:
image: registry.gitlab.com/akantu/akantu/ubuntu:lts
# ------------------------------------------------------------------------------
.compiler_gcc:
variables:
CC: /usr/lib/ccache/gcc
CXX: /usr/lib/ccache/g++
FC: gfortran
+ GCOV_EXECUTABLE: gcov
.compiler_clang:
variables:
CC: /usr/lib/ccache/clang
CXX: /usr/lib/ccache/clang++
FC: gfortran
+ GCOV_EXECUTABLE: llvm-cov gcov
# ------------------------------------------------------------------------------
.debian_testing_gcc:
variables:
output: debian_testing_gcc
extends:
- .compiler_gcc
- .image_debian_testing
- .cache_build
.debian_testing_clang:
variables:
output: debian_testing_clang
extends:
- .compiler_clang
- .image_debian_testing
- .cache_build
.ubuntu_lts_gcc:
variables:
output: ubuntu_lts_gcc
extends:
- .compiler_gcc
- .image_ubuntu_lts
- .cache_build
# ------------------------------------------------------------------------------
# ------------------------------------------------------------------------------
configure:debian_testing_gcc:
extends:
- .debian_testing_gcc
- .configure
cache:
policy: pull-push
build:debian_testing_gcc:
extends:
- .debian_testing_gcc
- .build
dependencies:
- configure:debian_testing_gcc
test:debian_testing_gcc:
extends:
- .debian_testing_gcc
- .tests
dependencies:
- build:debian_testing_gcc
analyse_build:debian_testing_gcc:
extends:
- .debian_testing_gcc
- .analyse_build
dependencies:
- build:debian_testing_gcc
# ------------------------------------------------------------------------------
configure:debian_testing_clang:
extends:
- .debian_testing_clang
- .configure
cache:
policy: pull-push
build:debian_testing_clang:
extends:
- .debian_testing_clang
- .build
dependencies:
- configure:debian_testing_clang
test:debian_testing_clang:
extends:
- .debian_testing_clang
- .tests
dependencies:
- build:debian_testing_clang
analyse_build:debian_testing_clang:
extends:
- .debian_testing_clang
- .analyse_build
dependencies:
- build:debian_testing_clang
# ------------------------------------------------------------------------------
configure:ubuntu_lts_gcc:
extends:
- .ubuntu_lts_gcc
- .configure
cache:
policy: pull-push
build:ubuntu_lts_gcc:
extends:
- .ubuntu_lts_gcc
- .build
dependencies:
- configure:ubuntu_lts_gcc
analyse_build:ubuntu_lts_gcc:
extends:
- .ubuntu_lts_gcc
- .analyse_build
dependencies:
- build:ubuntu_lts_gcc
test:ubuntu_lts_gcc:
extends:
- .ubuntu_lts_gcc
- .tests
dependencies:
- build:ubuntu_lts_gcc
# ------------------------------------------------------------------------------
-include:
- - template: Code-Quality.gitlab-ci.yml
-
code_quality:
+ stage: test
+ image: docker:19.03.12
+ allow_failure: true
+ services:
+ - docker:19.03.12-dind
+ variables:
+ DOCKER_DRIVER: overlay2
+ DOCKER_HOST: tcp://docker:2376
+ DOCKER_TLS_CERTDIR: "/certs"
+ CODECLIMATE_DEV: 1
+ CODE_QUALITY_IMAGE: "registry.gitlab.com/gitlab-org/ci-cd/codequality:0.85.22"
+ needs: []
+ script:
+ - export SOURCE_CODE=$PWD
+ - | # this is required to avoid undesirable reset of Docker image ENV variables being set on build stage
+ function propagate_env_vars() {
+ CURRENT_ENV=$(printenv)
+ for VAR_NAME; do
+ echo $CURRENT_ENV | grep "${VAR_NAME}=" > /dev/null && echo "--env $VAR_NAME "
+ done
+ }
+ - docker pull --quiet "$CODE_QUALITY_IMAGE"
+ - |
+ - docker build -t codeclimate/codeclimate-clang-tidy test/ci/codeclimate/codeclimate-clang-tidy
+ - |
+ docker run \
+ $(propagate_env_vars \
+ SOURCE_CODE \
+ TIMEOUT_SECONDS \
+ CODECLIMATE_DEBUG \
+ CODECLIMATE_DEV \
+ REPORT_STDOUT \
+ REPORT_FORMAT \
+ ENGINE_MEMORY_LIMIT_BYTES \
+ ) \
+ --volume "$PWD":/code \
+ --volume /var/run/docker.sock:/var/run/docker.sock \
+ "$CODE_QUALITY_IMAGE" /code
artifacts:
- paths: [gl-code-quality-report.json]
+ paths:
+ - gl-code-quality-report.json
+ reports:
+ codequality: gl-code-quality-report.json
+ expire_in: 1 week
+ dependencies: []
+ rules:
+ - if: '$CODE_QUALITY_DISABLED'
+ when: never
+ - if: '$CI_COMMIT_TAG || $CI_COMMIT_BRANCH'
# ------------------------------------------------------------------------------
pages:
stage: deploy
extends:
- .debian_testing_gcc
script:
- cd build
- - cmake -DAKANTU_DOCUMENTATION_DEVELOPER_MANUAL=ON ..
+ - cmake -DAKANTU_DOCUMENTATION=ON ..
- cmake --build . -t sphinx-doc
- mv doc/dev-doc/html ../public
dependencies:
- build:debian_testing_gcc
artifacts:
paths:
- public
only:
- - features/doc
+ - master
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index dbdaf96ca..000000000
--- a/.gitmodules
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "extra_packages/students-extra-package"]
- path = extra_packages/students-extra-package
- url = git@lsmssrv1.epfl.ch:students-extra-package.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f5918d099..2d1124c59 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,227 +1,227 @@
#===============================================================================
# @file CMakeLists.txt
#
# @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
# @author Nicolas Richart <nicolas.richart@epfl.ch>
#
# @date creation: Mon Jun 14 2010
# @date last modification: Fri Jan 22 2016
#
# @brief main configuration file
#
# @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 <http://www.gnu.org/licenses/>.
#
# @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_MAJOR_VERSION 4)
set(AKANTU_MINOR_VERSION 0)
set(AKANTU_PATCH_VERSION 0)
-set(AKANTU_COPYRIGHT "2010-2020, EPFL (Ecole Polytechnique Fédérale de Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)")
+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 "htpps://akantu.ch")
+set(AKANTU_HOMEPAGE_URL "https://akantu.ch")
if(CMAKE_VERSION VERSION_GREATER 3.12)
project(Akantu
- HOMEPAGE_URL "htpps://akantu.ch")
+ 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(BUILD_SHARED_LIBS ON CACHE BOOL "Build shared libraries.")
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. An even minor number corresponds to releases.
define_project_version()
#===============================================================================
# Options
#===============================================================================
# Debug
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG -DAKANTU_NDEBUG"
CACHE STRING "Flags used by the compiler during release builds" FORCE)
#add_flags(cxx "-Wall -Wextra -pedantic -Werror")
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
add_flags(cxx "-Wall -Wextra -pedantic") # -Weffc++
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG_INIT} -ggdb3"
CACHE STRING "Flags used by the compiler during debug builds" FORCE)
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT} -ggdb3"
CACHE STRING "Flags used by the compiler during debug builds" FORCE)
option (FORCE_COLORED_OUTPUT "Always produce ANSI-colored output (GNU/Clang only)." FALSE)
mark_as_advanced(FORCE_COLORED_OUTPUT)
if(FORCE_COLORED_OUTPUT)
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
add_flags(cxx "-fcolor-diagnostics")
else()
add_flags(cxx "-fdiagnostics-color=always")
endif()
endif()
else()
add_flags(cxx "-Wall")
endif()
option(AKANTU_EXAMPLES "Activate examples" OFF)
option(AKANTU_TESTS "Activate tests" OFF)
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)
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)
## meta option \todo better way to do it when multiple package give enable the
## same feature
if(AKANTU_SCOTCH)
set(AKANTU_PARTITIONER ON)
else()
set(AKANTU_PARTITIONER OFF)
endif()
if(AKANTU_MUMPS)
set(AKANTU_SOLVER ON)
else()
set(AKANTU_SOLVER OFF)
endif()
#===============================================================================
# Akantu library
#===============================================================================
add_subdirectory(src)
#===============================================================================
# Documentation
#===============================================================================
-if(AKANTU_DOCUMENTATION_DEVELOPER_MANUAL OR AKANTU_DOCUMENTATION_USER_MANUAL)
+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)
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)
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
#===============================================================================
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()
diff --git a/cmake/AkantuCleaning.cmake b/cmake/AkantuCleaning.cmake
index 8fd50e667..d852abf84 100644
--- a/cmake/AkantuCleaning.cmake
+++ b/cmake/AkantuCleaning.cmake
@@ -1,82 +1,103 @@
#===============================================================================
# @file AkantuCleaning.cmake
#
# @author Nicolas Richart <nicolas.richart@epfl.ch>
#
# @date creation: Thu Jun 1, 2017
#
# @brief set of tools to clean the code
#
# @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 <http://www.gnu.org/licenses/>.
#===============================================================================
# Adding clang-format target if executable is found
-find_program(CLANG_FORMAT "clang-format")
-mark_as_advanced(CLANG_FORMAT)
-macro(register_code_to_format)
- if(CLANG_FORMAT)
- add_custom_target(
- clang-format-all
- COMMAND ${CLANG_FORMAT}
- -i
- -style=file
- ${ARGN}
- )
- endif()
-endmacro()
-
+find_program(CLANG_FORMAT_EXECUTABLE "clang-format")
# Adding clang-tidy target if executable is found
-find_program(CLANG_TIDY "clang-tidy")
-mark_as_advanced(CLANG_TIDY)
-macro(register_target_to_tidy target)
- if(CLANG_TIDY)
- option(AKANTU_CLANG_TIDY_AUTOFIX OFF)
- mark_as_advanced(AKANTU_CLANG_TIDY_AUTOFIX)
+find_program(CLANG_TIDY_EXECUTABLE "clang-tidy")
+find_program(RUN_CLANG_TIDY_EXECUTABLE "run-clang-tidy")
- set(_autofix_option)
- if(AKANTU_CLANG_TIDY_AUTOFIX)
- set(_autofix_option -fix)
- endif()
- get_target_property(_sources ${target} SOURCES)
+mark_as_advanced(
+ CLANG_FORMAT_EXECUTABLE
+ CLANG_TIDY_EXECUTABLE
+ RUN_CLANG_TIDY_EXECUTABLE
+ )
- file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/clang-tidy)
+function(register_code_to_format)
+ if(NOT CLANG_FORMAT_EXECUTABLE)
+ endif()
+
+ add_custom_target(
+ clang-format-all
+ COMMAND ${CLANG_FORMAT_EXECUTABLE}
+ -style=file
+ --output-replacements-xml
+ ${ARGN} || /bin/true
+ )
+endfunction()
+
+function(register_tidy_all directory)
+ if(NOT RUN_CLANG_TIDY_EXECUTABLE)
+ return()
+ endif()
- set(_depends)
+ add_custom_target(
+ clang-tidy-all
+ COMMAND ${RUN_CLANG_TIDY_EXECUTABLE}
+ ${ARGN}
+ )
+endfunction()
- foreach(_src ${_sources})
- get_filename_component(_src_dir ${_src} DIRECTORY)
- file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/clang-tidy/${_src_dir})
+function(register_target_to_tidy target)
+ if(NOT CLANG_TIDY_EXECUTABLE)
+ return()
+ endif()
- add_custom_command(
- OUTPUT ${PROJECT_BINARY_DIR}/clang-tidy/${_src}.yaml
- COMMAND ${CLANG_TIDY}
- -p=${PROJECT_BINARY_DIR}
- -export-fixes=${PROJECT_BINARY_DIR}/clang-tidy/${_src}.yaml
- ${_autofix_option}
- ${_src}
- COMMENT "Tidying ${_src}"
- WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
- )
+ option(AKANTU_CLANG_TIDY_AUTOFIX OFF)
+ mark_as_advanced(AKANTU_CLANG_TIDY_AUTOFIX)
- list(APPEND _depends ${PROJECT_BINARY_DIR}/clang-tidy/${_src}.yaml)
- endforeach()
- add_custom_target(clang-tidy DEPENDS ${_depends})
+ set(_autofix_option)
+ if(AKANTU_CLANG_TIDY_AUTOFIX)
+ set(_autofix_option -fix)
endif()
-endmacro()
+ get_target_property(_sources ${target} SOURCES)
+
+ file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/clang-tidy)
+
+ set(_depends)
+
+ foreach(_src ${_sources})
+ get_filename_component(_src_dir ${_src} DIRECTORY)
+ file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/clang-tidy/${_src_dir})
+
+ add_custom_command(
+ OUTPUT ${PROJECT_BINARY_DIR}/clang-tidy/${_src}.yaml
+ COMMAND ${CLANG_TIDY_EXECUTABLE}
+ -p=${PROJECT_BINARY_DIR}
+ -export-fixes=${PROJECT_BINARY_DIR}/clang-tidy/${_src}.yaml
+ ${_autofix_option}
+ ${_src}
+ COMMENT "Tidying ${_src}"
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ )
+
+ list(APPEND _depends ${PROJECT_BINARY_DIR}/clang-tidy/${_src}.yaml)
+ endforeach()
+ add_custom_target(clang-tidy DEPENDS ${_depends})
+endfunction()
diff --git a/cmake/AkantuConfig.cmake.in b/cmake/AkantuConfig.cmake.in
index 78f26c5e3..866b1567f 100644
--- a/cmake/AkantuConfig.cmake.in
+++ b/cmake/AkantuConfig.cmake.in
@@ -1,65 +1,68 @@
#===============================================================================
# @file AkantuConfig.cmake.in
#
# @author Nicolas Richart <nicolas.richart@epfl.ch>
#
# @date creation: Thu Dec 01 2011
# @date last modification: Mon Jan 18 2016
#
# @brief CMake file for the library
#
# @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 <http://www.gnu.org/licenses/>.
#
#===============================================================================
@PACKAGE_INIT@
# Compute paths
get_filename_component(AKANTU_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
set(AKANTU_USE_FILE "${AKANTU_CMAKE_DIR}/AkantuUse.cmake")
include(${AKANTU_USE_FILE})
if(EXISTS "${AKANTU_CMAKE_DIR}/CMakeCache.txt")
# In build tree
include("${AKANTU_CMAKE_DIR}/AkantuBuildTreeSettings.cmake")
include(AkantuSimulationMacros)
else()
# In install tree
- set(AKANTU_INCLUDE_DIRS "${AKANTU_CMAKE_DIR}/../../include/akantu")
+ set(AKANTU_INCLUDE_DIRS "@CMAKE_INSTALL_PREFIX@/include/akantu")
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${AKANTU_CMAKE_DIR}")
include(AkantuSimulationMacros)
endif()
include("${AKANTU_CMAKE_DIR}/AkantuTargets.cmake")
# Dependencies
include("${AKANTU_CMAKE_DIR}/AkantuConfigInclude.cmake")
set(AKANTU_BUILD_TYPE @CMAKE_BUILD_TYPE@)
# find_akantu_dependencies()
set(AKANTU_LIBRARY akantu)
-list(APPEND AKANTU_LIBRARIES ${AKANTU_LIBRARY} ${AKANTU_EXTRA_LIBRARIES})
+set(_akantu_libraries ${AKANTU_LIBRARIES})
+list(APPEND _akantu_libraries ${AKANTU_LIBRARY} ${AKANTU_EXTRA_LIBRARIES})
list(APPEND AKANTU_INCLUDE_DIRS ${AKANTU_EXTRA_INCLUDE_DIR})
+set(AKANTU_LIBRARIES ${_akantu_libraries} CACHE INTERNAL "List of akantu necessary libraries" FORCE)
+
# set(AKANTU_VERSION @AKANTU_VERSION@)
# set_and_check(AKANTU_INCLUDE_DIR "@PACKAGE_INCLUDE_INSTALL_DIR@")
check_required_components(Akantu)
diff --git a/cmake/AkantuExtraCompilationProfiles.cmake b/cmake/AkantuExtraCompilationProfiles.cmake
index fd0fb5ae4..95b8f4b03 100644
--- a/cmake/AkantuExtraCompilationProfiles.cmake
+++ b/cmake/AkantuExtraCompilationProfiles.cmake
@@ -1,87 +1,89 @@
#Profiling
set(_profiling "-g -ggdb3 -pg -DNDEBUG -DAKANTU_NDEBUG -O2")
set(CMAKE_CXX_FLAGS_PROFILING ${_profiling}
CACHE STRING "Flags used by the compiler during profiling builds")
set(CMAKE_C_FLAGS_PROFILING ${_profiling}
CACHE STRING "Flags used by the compiler during profiling builds")
set(CMAKE_Fortran_FLAGS_PROFILING ${_profiling}
CACHE STRING "Flags used by the compiler during profiling builds")
set(CMAKE_EXE_LINKER_FLAGS_PROFILING "-pg"
CACHE STRING "Flags used by the linker during profiling builds")
set(CMAKE_SHARED_LINKER_FLAGS_PROFILING "-pg"
CACHE STRING "Flags used by the linker during profiling builds")
mark_as_advanced(
CMAKE_CXX_FLAGS_PROFILING
CMAKE_C_FLAGS_PROFILING
CMAKE_Fortran_FLAGS_PROFILING
CMAKE_EXE_LINKER_FLAGS_PROFILING
CMAKE_SHARED_LINKER_FLAGS_PROFILING
)
+set(_coverage "-g -ggdb3 -DNDEBUG -DAKANTU_NDEBUG -O2 --coverage")
+set(CMAKE_CXX_FLAGS_COVERAGE ${_coverage}
+ CACHE STRING "Flags used by the compiler during profiling builds" FORCE)
+set(CMAKE_C_FLAGS_COVERAGE ${_coverage}
+ CACHE STRING "Flags used by the compiler during profiling builds" FORCE)
+set(CMAKE_Fortran_FLAGS_COVERAGE ${_coverage}
+ CACHE STRING "Flags used by the compiler during profiling builds" FORCE)
+set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE ${_coverage}
+ CACHE STRING "Flags used by the compiler during profiling builds" FORCE)
+set(CMAKE_EXE_LINKER_FLAGS_COVERAGE ${_coverage}
+ CACHE STRING "Flags used by the linker during sanitizing builds" FORCE)
+
+mark_as_advanced(
+ CMAKE_CXX_FLAGS_COVERAGE
+ CMAKE_C_FLAGS_COVERAGE
+ CMAKE_Fortran_FLAGS_COVERAGE
+ CMAKE_SHARED_LINKER_FLAGS_SANITIZEMEMORY
+ CMAKE_EXE_LINKER_FLAGS_SANITIZEMEMORY
+ )
+
# Sanitize the code
if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "5.2") OR
CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(_sanitize "-g -ggdb3 -O2 -fsanitize=address -fsanitize=leak -fsanitize=undefined -fno-omit-frame-pointer -fsanitize-blacklist=${PROJECT_SOURCE_DIR}/cmake/sanitize-blacklist.txt")
set(CMAKE_CXX_FLAGS_SANITIZE ${_sanitize}
CACHE STRING "Flags used by the compiler during sanitizing builds")
set(CMAKE_C_FLAGS_SANITIZE ${_sanitize}
CACHE STRING "Flags used by the compiler during sanitizing builds")
set(CMAKE_Fortran_FLAGS_SANITIZE ${_sanitize}
CACHE STRING "Flags used by the compiler during sanitizing builds")
set(CMAKE_EXE_LINKER_FLAGS_SANITIZE ${_sanitize}
CACHE STRING "Flags used by the linker during sanitizing builds")
set(CMAKE_SHARED_LINKER_FLAGS_SANITIZE ${_sanitize}
CACHE STRING "Flags used by the linker during sanitizing builds")
mark_as_advanced(
CMAKE_CXX_FLAGS_SANITIZE
CMAKE_C_FLAGS_SANITIZE
CMAKE_Fortran_FLAGS_SANITIZE
CMAKE_SHARED_LINKER_FLAGS_SANITIZE
CMAKE_EXE_LINKER_FLAGS_SANITIZE
)
endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
-
set(_sanitize "-g -ggdb3 -O2 -fPIE -fsanitize=memory -fsanitize-memory-track-origins -fsanitize-recover=all -fno-omit-frame-pointer -fsanitize-blacklist=${PROJECT_SOURCE_DIR}/cmake/sanitize-blacklist.txt")
set(CMAKE_CXX_FLAGS_SANITIZEMEMORY ${_sanitize}
CACHE STRING "Flags used by the compiler during sanitizing builds")
set(CMAKE_C_FLAGS_SANITIZEMEMORY ${_sanitize}
CACHE STRING "Flags used by the compiler during sanitizing builds")
set(CMAKE_Fortran_FLAGS_SANITIZEMEMORY ${_sanitize}
CACHE STRING "Flags used by the compiler during sanitizing builds")
set(CMAKE_EXE_LINKER_FLAGS_SANITIZEMEMORY ${_sanitize}
CACHE STRING "Flags used by the linker during sanitizing builds")
set(CMAKE_SHARED_LINKER_FLAGS_SANITIZEMEMORY ${_sanitize}
CACHE STRING "Flags used by the linker during sanitizing builds")
mark_as_advanced(
CMAKE_CXX_FLAGS_SANITIZEMEMORY
CMAKE_C_FLAGS_SANITIZEMEMORY
CMAKE_Fortran_FLAGS_SANITIZEMEMORY
CMAKE_SHARED_LINKER_FLAGS_SANITIZEMEMORY
CMAKE_EXE_LINKER_FLAGS_SANITIZEMEMORY
)
endif()
-
-find_program(GCOV_EXECUTABLE gcov)
-if (GCOV_EXECUTABLE)
- set(_coverage "-g -ggdb3 -DNDEBUG -DAKANTU_NDEBUG -O2 -fprofile-arcs -ftest-coverage")
- set(CMAKE_CXX_FLAGS_COVERAGE ${_coverage}
- CACHE STRING "Flags used by the compiler during profiling builds")
- set(CMAKE_C_FLAGS_COVERAGE ${_coverage}
- CACHE STRING "Flags used by the compiler during profiling builds")
- set(CMAKE_Fortran_FLAGS_COVERAGE ${_coverage}
- CACHE STRING "Flags used by the compiler during profiling builds")
-
- mark_as_advanced(
- CMAKE_CXX_FLAGS_COVERAGE
- CMAKE_C_FLAGS_COVERAGE
- CMAKE_Fortran_FLAGS_COVERAGE
- )
-endif()
diff --git a/cmake/AkantuInstall.cmake b/cmake/AkantuInstall.cmake
index ed7530186..93b932af6 100644
--- a/cmake/AkantuInstall.cmake
+++ b/cmake/AkantuInstall.cmake
@@ -1,165 +1,163 @@
#===============================================================================
# @file AkantuInstall.cmake
#
# @author Nicolas Richart <nicolas.richart@epfl.ch>
#
# @date creation: Wed Oct 17 2012
# @date last modification: Fri Jan 22 2016
#
# @brief Create the files that allows users to link with Akantu in an other
# cmake project
#
# @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 <http://www.gnu.org/licenses/>.
#
#===============================================================================
#===============================================================================
# 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 <nicolas.richart@epfl.ch>
# @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 <http://www.gnu.org/licenses/>.
#
# @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_acticated)
if(_is_acticated)
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)
-endif()
-include(GNUInstallDirs)
-
-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})
+ 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 ${CMAKE_INSTALL_DATAROOTDIR}/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 ${CMAKE_INSTALL_DATAROOTDIR}/cmake/${PROJECT_NAME}
COMPONENT dev)
diff --git a/cmake/AkantuUse.cmake b/cmake/AkantuUse.cmake
index 863d17d2d..8973e1a4b 100644
--- a/cmake/AkantuUse.cmake
+++ b/cmake/AkantuUse.cmake
@@ -1,69 +1,73 @@
#===============================================================================
# @file AkantuUse.cmake
#
# @author Nicolas Richart <nicolas.richart@epfl.ch>
#
# @date creation: Tue Dec 07 2010
# @date last modification: Mon Aug 17 2015
#
# @brief CMake file for the library
#
# @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 <http://www.gnu.org/licenses/>.
#
#===============================================================================
+if (DEFINED CMAKE_PACKAGES_SYSTEM_LOADED)
+ return()
+endif()
+set(CMAKE_PACKAGES_SYSTEM_LOADED TRUE)
function(package_is_activated pkg activated)
string(TOUPPER ${pkg} _u_pkg)
set(${activated} ${AKANTU_HAS_${_u_pkg}} PARENT_SCOPE)
endfunction()
function(package_get_include_dir pkg include_dir)
string(TOUPPER ${pkg} _u_pkg)
set(${include_dir} ${AKANTU_${_u_pkg}_INCLUDE_DIR} PARENT_SCOPE)
endfunction()
function(package_get_libraries pkg libs)
string(TOUPPER ${pkg} _u_pkg)
set(${libs} ${AKANTU_${_u_pkg}_LIBRARIES} PARENT_SCOPE)
endfunction()
function(package_get_compile_flags pkg lang flags)
string(TOUPPER ${pkg} _u_pkg)
set(${flags} ${AKANTU_${_u_pkg}_COMPILE_${lang}_FLAGS} PARENT_SCOPE)
endfunction()
function(get_target_list_of_associated_files tgt files)
get_target_property(_type ${tgt} TYPE)
if(_type STREQUAL "SHARED_LIBRARY"
OR _type STREQUAL "STATIC_LIBRARY"
OR _type STREQUAL "MODULE_LIBRARY"
OR _type STREQUAL "EXECUTABLE")
get_target_property(_srcs ${tgt} SOURCES)
set(_dep_ressources)
foreach(_file ${_srcs})
list(APPEND _dep_ressources ${CMAKE_CURRENT_SOURCE_DIR}/${_file})
endforeach()
else()
get_target_property(_dep_ressources ${tgt} RESSOURCES)
endif()
set(${files} ${_dep_ressources} PARENT_SCOPE)
endfunction()
diff --git a/cmake/Modules/CMakePackagesSystem.cmake b/cmake/Modules/CMakePackagesSystem.cmake
index 5acdcee4c..a8d2eea0e 100644
--- a/cmake/Modules/CMakePackagesSystem.cmake
+++ b/cmake/Modules/CMakePackagesSystem.cmake
@@ -1,1094 +1,1106 @@
#===============================================================================
# @file CMakePackagesSystem.cmake
#
# @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
# @author Nicolas Richart <nicolas.richart@epfl.ch>
#
# @date creation: Wed Nov 05 2014
# @date last modification: Wed Jan 20 2016
#
# @brief Set of macros used by akantu to handle the package system
#
# @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 <http://www.gnu.org/licenses/>.
#
#===============================================================================
#[=======================================================================[.rst:
#CMakePackagesSystem
#-------------------
#
#This package defines multiple function to handle packages. This packages can
#be of two kinds regular ones and extra_packages (ex: in akantu the LGPL part
#is regular packages and extra packages are on Propetary license)
#
#Package are loaded with the help of the command:
#
#.. command:: package_list_packages
#
# package_list_packages(<regular_package_folder>
# [ EXTRA_PACKAGE_FOLDER <extra_package_folder> ]
# [ SOURCE_FOLDER <source_folder>]
# [ TEST_FOLDER <test_folder> ]
# [ MANUAL_FOLDER <manual_folder> ]
# )
#
# This command will look for packages name like ``<regular_package_folder>/<package>.cmake``
# OR ``<extra_package_folder>/<package>/package.cmake``
#
#A package is a cmake script that should contain at list the declaration of a
#package
#
#.. command:: package_declare
#
# package_declare(<package real name>
# [EXTERNAL] [META] [ADVANCED] [NOT_OPTIONAL]
# [DESCRIPTION <description>] [DEFAULT <default_value>]
# [DEPENDS <pkg> ...]
# [BOOST_COMPONENTS <pkg> ...]
# [EXTRA_PACKAGE_OPTIONS <opt> ...]
# [COMPILE_FLAGS <lang> <flags>]
# [SYSTEM <ON|OFF|AUTO> [ <script_to_compile> ]]
# [FEATURES_PUBLIC <feature> ...]
# [FEATURES_PRIVATE <feature> ...]
# [EXCLUDE_FROM_ALL]
# )
#
#.. command:: package_declare_sources
#
# It can also declare multiple informations:
# source files:
#
# package_declare_sources(<package real name>
# <src1> <src2> ... <srcn>)
#
#.. command:: package_declare_documentation
#
# a LaTeX documentation
# package_declare_documentation(<package real name>
# <line1> <line2> ...<linen>)
#
#.. command:: package_declare_documentation_files
#
# LaTeX documentation files
# package_declare_documentation_files(<package real name>
# <file1> <file2> ... <filen>)
#
#Different function can also be retrieved from the package system by using the
#different accessors
#
#.. command:: package_get_name
# package_get_name(<pkg> <retval>)
#
#.. command:: package_get_real_name
# package_get_real_name(<pkg> <retval>)
#
#.. command:: package_get_option_name
# package_get_option_name(<pkg> <retval>)
#
#.. command:: package_use_system
# package_use_system(<pkg> <retval>)
#
#.. command:: package_get_nature
# package_get_nature(<pkg> <retval>)
#
#.. command:: package_get_description
# package_get_description(<pkg> <retval>)
#
#.. command:: package_get_filename
# package_get_filename(<pkg> <retval>)
#
#.. command:: package_get_sources_folder
# package_get_sources_folder(<pkg> <retval>)
#.. command:: package_get_tests_folder
# package_get_tests_folder(<pkg> <retval>)
#.. command:: package_get_manual_folder
# package_get_manual_folder(<pkg> <retval>)
#
#.. command:: package_get_find_package_extra_options
# package_get_find_package_extra_options(<pkg> <retval>)
#
#.. command:: package_get_compile_flags
# package_get_compile_flags(<pkg> <lang> <retval>)
#.. command:: package_set_compile_flags
# package_set_compile_flags(<pkg> <lang> <flag1> <flag2> ... <flagn>)
#
#.. command:: package_get_include_dir
# package_get_include_dir(<pkg> <retval>)
#.. command:: package_set_include_dir
# package_set_include_dir(<pkg> <inc1> <inc2> ... <incn>)
#.. command:: package_add_include_dir
# package_add_include_dir(<pkg> <inc1> <inc2> ... <incn>)
#
#.. command:: package_get_libraries
# package_get_libraries(<pkg> <retval>)
#.. command:: package_set_libraries
# package_set_libraries(<pkg> <lib1> <lib2> ... <libn>)
#
#.. command:: package_add_extra_dependency
# package_add_extra_dependency(pkg <dep1> <dep2> ... <depn>)
#.. command:: package_rm_extra_dependency
# package_rm_extra_dependency(<pkg> <dep>)
#.. command:: package_get_extra_dependencies
# package_get_extra_dependencies(<pkg> <retval>)
#
#.. command:: package_is_activated
# package_is_activated(<pkg> <retval>)
#.. command:: package_is_deactivated
# package_is_deactivated(<pkg> <retval>)
#
#.. command:: package_get_dependencies
# package_get_dependencies(<pkg> <PRIVATE|INTERFACE> <retval>)
#.. command:: package_add_dependencies
# package_add_dependencies(<pkg> <PRIVATE|INTERFACE> <dep1> <dep2> ... <depn>)
# package_remove_dependencies(<pkg> <dep1> <dep2> ... <depn>)
# package_remove_dependency(<pkg> <dep>)
#
#.. command:: package_on_enabled_script
# package_on_enabled_script(<pkg> <script>)
#
#.. command:: package_get_all_source_files
# package_get_all_source_files(<srcs> <public_headers> <private_headers>)
#.. command:: package_get_all_include_directories
# package_get_all_include_directories(<inc_dirs>)
#.. command:: package_get_all_external_informations
# package_get_all_external_informations(<include_dir> <libraries>)
#.. command:: package_get_all_definitions
# package_get_all_definitions(<definitions>)
#.. command:: package_get_all_extra_dependencies
# package_get_all_extra_dependencies(<dependencies>)
#.. command:: package_get_all_test_folders
# package_get_all_test_folders(<test_dirs>)
#.. command:: package_get_all_documentation_files
# package_get_all_documentation_files(<doc_files>)
#.. command:: package_get_all_activated_packages
# package_get_all_activated_packages(<activated_list>)
#.. command:: package_get_all_deactivated_packages
# package_get_all_deactivated_packages(<deactivated_list>)
#.. command:: package_get_all_packages
# package_get_all_packages(<packages_list>)
#.. command:: package_get_all_features_public
# package_get_all_features_public(<features>)
#.. command:: package_get_all_features_private
# package_get_all_features_private(<features>)
#
#
# .. command:: package_set_package_system_dependency
#
# package_set_package_system_dependency(<pkg> <system> <dep1>
# <dep2> ... <depn>)
#
# .. command:: package_get_package_system_dependency
#
# package_get_package_system_dependency(<pkg> <var>)
#
#
#]=======================================================================]
+if (DEFINED CMAKE_PACKAGES_SYSTEM_LOADED)
+ return()
+endif()
+set(CMAKE_PACKAGES_SYSTEM_LOADED TRUE)
include(CMakeParseArguments)
#===============================================================================
# Package Management
#===============================================================================
if(__CMAKE_PACKAGES_SYSTEM)
return()
endif()
set(__CMAKE_PACKAGES_SYSTEM TRUE)
if(CMAKE_VERSION VERSION_GREATER 3.1.2)
cmake_policy(SET CMP0054 NEW)
endif()
#===============================================================================
option(AUTO_MOVE_UNKNOWN_FILES
"Give to cmake the permission to move the unregistered files to the ${PROJECT_SOURCE_DIR}/tmp directory" FALSE)
mark_as_advanced(AUTO_MOVE_UNKNOWN_FILES)
include(CMakePackagesSystemGlobalFunctions)
include(CMakePackagesSystemPrivateFunctions)
# ==============================================================================
# "Public" Accessors
# ==============================================================================
# ------------------------------------------------------------------------------
# Package name
# ------------------------------------------------------------------------------
function(package_get_name pkg pkg_name)
string(TOUPPER ${PROJECT_NAME} _project)
string(REPLACE "-" "_" _str_pkg "${pkg}")
string(TOUPPER ${_str_pkg} _u_package)
set(${pkg_name} ${_project}_PKG_${_u_package} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Real name
# ------------------------------------------------------------------------------
function(package_get_real_name pkg ret)
package_get_name(${pkg} _pkg_name)
_package_get_real_name(${_pkg_name} _tmp)
set(${ret} ${_tmp} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Option name
# ------------------------------------------------------------------------------
function(package_get_option_name pkg ret)
package_get_name(${pkg} _pkg_name)
_package_get_option_name(${_pkg_name} _tmp)
set(${ret} ${_tmp} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Set if system package or compile external lib
# ------------------------------------------------------------------------------
function(package_use_system pkg ret)
package_get_name(${pkg} _pkg_name)
_package_use_system(${_pkg_name} _tmp)
set(${ret} ${_tmp} PARENT_SCOPE)
endfunction()
function(package_add_third_party_script_variable pkg var)
package_get_name(${pkg} _pkg_name)
_package_add_third_party_script_variable(${_pkg_name} ${var} ${ARGN})
set(${var} ${ARGN} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
-# Set if system package or compile external lib
+# Add package's targets to the export list
# ------------------------------------------------------------------------------
function(package_add_to_export_list pkg)
package_get_name(${pkg} _pkg_name)
_package_add_to_export_list(${_pkg_name} ${ARGN})
endfunction()
+# ------------------------------------------------------------------------------
+# Removes packages's targets from export list
+# ------------------------------------------------------------------------------
+function(package_remove_from_export_list pkg)
+ package_get_name(${pkg} _pkg_name)
+ _package_remove_from_export_list(${_pkg_name} ${ARGN})
+endfunction()
+
# ------------------------------------------------------------------------------
# Nature
# ------------------------------------------------------------------------------
function(package_get_nature pkg ret)
package_get_name(${pkg} _pkg_name)
_package_get_nature(${_pkg_name} _tmp)
set(${ret} ${_tmp} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Description
# ------------------------------------------------------------------------------
function(package_get_description pkg ret)
package_get_name(${pkg} _pkg_name)
_package_get_description(${_pkg_name} _tmp)
set(${ret} ${_tmp} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Package file name
# ------------------------------------------------------------------------------
function(package_get_filename pkg ret)
package_get_name(${pkg} _pkg_name)
_package_get_filename(${_pkg_name} _tmp)
set(${ret} ${_tmp} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Source files
# ------------------------------------------------------------------------------
function(package_get_source_files pkg ret_srcs ret_pub ret_priv)
package_get_name(${pkg} _pkg_name)
_package_get_source_files(${_pkg_name} _tmp_srcs _tmp_pub _tmp_priv)
set(${ret_srcs} ${_tmp_srcs} PARENT_SCOPE)
set(${ret_pub} ${_tmp_pub} PARENT_SCOPE)
set(${ret_priv} ${_tmp_pric} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Source folder
# ------------------------------------------------------------------------------
function(package_get_sources_folder pkg ret)
package_get_name(${pkg} _pkg_name)
_package_get_sources_folder(${_pkg_name} _tmp)
set(${ret} ${_tmp} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Test folder
# ------------------------------------------------------------------------------
function(package_get_tests_folder pkg ret)
package_get_name(${pkg} _pkg_name)
_package_get_tests_folder(${_pkg_name} _tmp)
set(${ret} ${_tmp} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Manual folder
# ------------------------------------------------------------------------------
function(package_get_manual_folder pkg ret)
package_get_name(${pkg} _pkg_name)
_package_get_manual_folder(${_pkg_name} _tmp)
set(${ret} ${_tmp} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Extra option for the find_package
# ------------------------------------------------------------------------------
function(package_get_find_package_extra_options pkg ret)
package_get_name(${pkg} _pkg_name)
_package_get_find_package_extra_options(${_pkg_name} _tmp)
set(${ret} ${_tmp} PARENT_SCOPE)
endfunction()
function(package_set_find_package_extra_options pkg)
package_get_name(${pkg} _pkg_name)
_package_set_find_package_extra_options(${_pkg_name} ${ARGN})
endfunction()
# ------------------------------------------------------------------------------
# Compilation flags
# ------------------------------------------------------------------------------
function(package_get_compile_flags pkg lang ret)
package_get_name(${pkg} _pkg_name)
_package_get_compile_flags(${_pkg_name} ${lang} _tmp)
set(${ret} "${_tmp}" PARENT_SCOPE)
endfunction()
function(package_set_compile_flags pkg lang)
package_get_name(${pkg} _pkg_name)
_package_set_compile_flags(${_pkg_name} ${lang} ${ARGN})
endfunction()
function(package_unset_compile_flags pkg lang)
package_get_name(${pkg} _pkg_name)
_package_unset_compile_flags(${_pkg_name} ${lang})
endfunction()
# ------------------------------------------------------------------------------
# Include dir
# ------------------------------------------------------------------------------
function(package_get_include_dir pkg ret)
package_get_name(${pkg} _pkg_name)
_package_get_include_dir(${_pkg_name} _tmp)
set(${ret} ${_tmp} PARENT_SCOPE)
endfunction()
function(package_set_include_dir pkg)
package_get_name(${pkg} _pkg_name)
_package_set_include_dir(${_pkg_name} ${ARGN})
endfunction()
function(package_add_include_dir pkg)
package_get_name(${pkg} _pkg_name)
_package_add_include_dir(${_pkg_name} ${ARGN})
endfunction()
# ------------------------------------------------------------------------------
# Libraries
# ------------------------------------------------------------------------------
function(package_get_libraries pkg ret)
package_get_name(${pkg} _pkg_name)
_package_get_libraries(${_pkg_name} _tmp)
set(${ret} ${_tmp} PARENT_SCOPE)
endfunction()
function(package_set_libraries pkg)
package_get_name(${pkg} _pkg_name)
_package_set_libraries(${_pkg_name} ${ARGN})
endfunction()
# ------------------------------------------------------------------------------
# Extra dependencies like custom commands of ExternalProject
# ------------------------------------------------------------------------------
function(package_add_extra_dependency pkg)
package_get_name(${pkg} _pkg_name)
_package_add_extra_dependency(${_pkg_name} ${ARGN})
endfunction()
function(package_rm_extra_dependency pkg dep)
package_get_name(${pkg} _pkg_name)
_package_rm_extra_dependency(${_pkg_name} ${dep})
endfunction()
function(package_get_extra_dependencies pkg ret)
package_get_name(${pkg} _pkg_name)
_package_get_extra_dependencies(${_pkg_name} _tmp)
set(${ret} ${_tmp} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Activate/deactivate
# ------------------------------------------------------------------------------
function(package_is_activated pkg ret)
package_get_name(${pkg} _pkg_name)
_package_is_activated(${_pkg_name} _tmp)
set(${ret} ${_tmp} PARENT_SCOPE)
endfunction()
function(package_is_deactivated pkg ret)
package_get_name(${pkg} _pkg_name)
_package_is_deactivated(${_pkg_name} _tmp)
set(${ret} ${_tmp} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Direct dependencies
# ------------------------------------------------------------------------------
function(package_get_dependencies pkg type ret)
package_get_name(${pkg} _pkg_name)
_package_get_dependencies(${_pkg_name} ${type} _tmp_name)
_package_get_real_name(${_tmp_name} _tmp)
set(${ret} ${_tmp} PARENT_SCOPE)
endfunction()
function(package_add_dependencies pkg type)
package_get_name(${pkg} _pkg_name)
foreach(_dep ${ARGN})
package_get_name(${_dep} _dep_pkg_name)
list(APPEND _tmp_deps ${_dep_pkg_name})
endforeach()
_package_add_dependencies(${_pkg_name} ${type} ${_tmp_deps})
endfunction()
function(package_remove_dependencies pkg type)
foreach(_dep ${ARGN})
package_remove_dependency(${pkg} _dep)
endforeach()
endfunction()
function(package_remove_dependency pkg dep)
package_get_name(${pkg} _pkg_name)
package_get_name(${dep} _dep_pkg_name)
_package_remove_dependency(${_pkg_name} PRIVATE ${_dep_pkg_name})
_package_remove_dependency(${_pkg_name} INTERFACE ${_dep_pkg_name})
endfunction()
# ------------------------------------------------------------------------------
# Documentation related functions
# ------------------------------------------------------------------------------
function(package_declare_documentation pkg)
package_get_name(${pkg} _pkg_name)
_package_set_documentation(${_pkg_name} ${ARGN})
endfunction()
function(package_declare_documentation_files pkg)
package_get_name(${pkg} _pkg_name)
_package_set_documentation_files(${_pkg_name} ${ARGN})
endfunction()
# ------------------------------------------------------------------------------
# Set any user variables needed
# ------------------------------------------------------------------------------
function(package_set_variable variable pkg)
package_get_name(${pkg} _pkg_name)
_package_set_variable(${variable} ${_pkg_name} ${ARGN})
endfunction()
function(package_add_to_variable variable pkg)
package_get_name(${pkg} _pkg_name)
_package_add_to_variable(${variable} ${_pkg_name} ${ARGN})
endfunction()
function(package_get_variable variable pkg value)
package_get_name(${pkg} _pkg_name)
_package_get_variable(${variable} ${_pkg_name} _value_tmp)
if(_value_tmp)
set(${value} ${_value_tmp} PARENT_SCOPE)
else()
unset(${value} PARENT_SCOPE)
endif()
endfunction()
# ------------------------------------------------------------------------------
# Exteral package system as apt rpm dependencies
# ------------------------------------------------------------------------------
function(package_set_package_system_dependency pkg system)
package_get_name(${pkg} _pkg_name)
_package_set_package_system_dependency(${_pkg_name} ${system} ${ARGN})
endfunction()
function(package_get_package_system_dependency pkg system var)
package_get_name(${pkg} _pkg_name)
_package_set_package_system_dependency(${_pkg_name} ${sytem} _tmp)
set(${var} ${_tmp} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# ==============================================================================
# Global accessors
# ==============================================================================
# ------------------------------------------------------------------------------
# get the list of source files
# ------------------------------------------------------------------------------
function(package_get_all_source_files SRCS PUBLIC_HEADERS PRIVATE_HEADERS)
string(TOUPPER ${PROJECT_NAME} _project)
unset(_tmp_srcs)
unset(_tmp_public_headers)
unset(_tmp_private_headers)
package_get_all_activated_packages(_activated_list)
foreach(_pkg_name ${_activated_list})
_package_get_source_files(${_pkg_name}
_pkg_srcs
_pkg_public_headers
_pkg_private_headers
)
list(APPEND _tmp_srcs ${_pkg_srcs})
list(APPEND _tmp_public_headers ${_pkg_public_headers})
list(APPEND _tmp_private_headers ${_pkg_private_headers})
endforeach()
set(${SRCS} ${_tmp_srcs} PARENT_SCOPE)
set(${PUBLIC_HEADERS} ${_tmp_public_headers} PARENT_SCOPE)
set(${PRIVATE_HEADERS} ${_tmp_private_headers} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Get include directories
# ------------------------------------------------------------------------------
function(package_get_all_include_directories inc_dirs)
set(_tmp)
package_get_all_activated_packages(_activated_list)
foreach(_pkg_name ${_activated_list})
foreach(_type SRCS PUBLIC_HEADERS PRIVATE_HEADERS)
foreach(_file ${${_pkg_name}_${_type}})
get_filename_component(_path "${_file}" PATH)
list(APPEND _tmp "${_path}")
endforeach()
endforeach()
endforeach()
if(_tmp)
list(REMOVE_DUPLICATES _tmp)
endif()
set(${inc_dirs} ${_tmp} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Get external libraries informations
# ------------------------------------------------------------------------------
function(package_get_all_external_informations)
cmake_parse_arguments(_opt "" "PRIVATE_INCLUDE;INTERFACE_INCLUDE;LIBRARIES" "" ${ARGN})
foreach(_type PRIVATE INTERFACE)
if(_opt_${_type}_INCLUDE)
_package_get_variable_for_external_dependencies(INCLUDE_DIR ${_type} tmp_INCLUDE_DIR)
foreach(_dir ${tmp_INCLUDE_DIR})
string(FIND "${_dir}" "${CMAKE_CURRENT_SOURCE_DIR}" _pos)
if(NOT _pos EQUAL -1)
list(REMOVE_ITEM tmp_INCLUDE_DIR ${_dir})
endif()
endforeach()
set(${_opt_${_type}_INCLUDE} ${tmp_INCLUDE_DIR} PARENT_SCOPE)
endif()
endforeach()
if(_opt_LIBRARIES)
_package_get_variable_for_external_dependencies(LIBRARIES PRIVATE tmp_LIBRARIES)
_package_get_variable_for_external_dependencies(LIBRARIES INTERFACE tmp_LIBRARIES_INTERFACE)
set(${_opt_LIBRARIES} ${tmp_LIBRARIES} ${tmp_LIBRARIES_INTERFACE} PARENT_SCOPE)
endif()
endfunction()
# ------------------------------------------------------------------------------
# Get export list for all activated packages
# ------------------------------------------------------------------------------
function(package_get_all_export_list export_list)
_package_get_variable_for_activated(EXPORT_LIST _tmp)
set(${export_list} ${_tmp} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Get definitions like external projects
# ------------------------------------------------------------------------------
function(package_get_all_definitions definitions)
_package_get_variable_for_activated(OPTION_NAME _tmp)
set(${definitions} ${_tmp} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Get extra dependencies like external projects
# ------------------------------------------------------------------------------
function(package_get_all_extra_dependencies deps)
_package_get_variable_for_activated(EXTRA_DEPENDENCY _tmp)
set(${deps} ${_tmp} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Get extra infos
# ------------------------------------------------------------------------------
function(package_get_all_test_folders TEST_DIRS)
_package_get_variable_for_activated(TEST_FOLDER _tmp)
set(${TEST_DIRS} ${_tmp} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Get compilation flags
# ------------------------------------------------------------------------------
function(package_get_all_compilation_flags LANG FLAGS)
_package_get_variable_for_activated(COMPILE_${LANG}_FLAGS _tmp_flags)
string(REPLACE ";" " " _flags "${_tmp_flags}")
set(${FLAGS} ${_flags} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Documentation informations
# ------------------------------------------------------------------------------
function(package_get_all_documentation_files doc_files)
set(_tmp_DOC_FILES)
package_get_all_activated_packages(_activated_list)
foreach(_pkg_name ${_activated_list})
_package_get_manual_folder(${_pkg_name} _doc_dir)
_package_get_documentation_files(${_pkg_name} _doc_files)
foreach(_doc_file ${_doc_files})
list(APPEND _tmp_DOC_FILES ${_doc_dir}/${_doc_file})
endforeach()
endforeach()
if(_tmp_DOC_FILES)
list(REMOVE_DUPLICATES _tmp_DOC_FILES)
endif()
set(${doc_files} ${_tmp_DOC_FILES} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Get package systems dependencies
# ------------------------------------------------------------------------------
function(package_get_all_package_system_dependency system deps)
string(TOUPPER ${system} _u_system)
_package_get_variable_for_activated(PACKAGE_SYSTEM_${_u_system} _tmp)
set(${deps} ${_tmp} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# List packages
# ------------------------------------------------------------------------------
function(package_get_all_activated_packages activated_list)
package_get_project_variable(ACTIVATED_PACKAGE_LIST _activated_list)
set(${activated_list} ${_activated_list} PARENT_SCOPE)
endfunction()
function(package_get_all_deactivated_packages deactivated_list)
package_get_project_variable(DEACTIVATED_PACKAGE_LIST _deactivated_list)
set(${deactivated_list} ${_deactivated_list} PARENT_SCOPE)
endfunction()
function(package_get_all_packages packages_list)
package_get_project_variable(ALL_PACKAGES_LIST _packages_list)
set(${packages_list} ${_packages_list} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# List all the needed features
# ------------------------------------------------------------------------------
function(package_get_all_features_public features)
_package_get_variable_for_activated(FEATURES_PUBLIC _tmp)
set(${features} ${_tmp} PARENT_SCOPE)
endfunction()
function(package_get_all_features_private features)
_package_get_variable_for_activated(FEATURES_PRIVATE _tmp)
set(${features} ${_tmp} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Callbacks
# ------------------------------------------------------------------------------
function(package_on_enabled_script pkg script)
package_get_name(${pkg} _pkg_name)
_package_on_enable_script(${_pkg_name} "${script}")
endfunction()
# ------------------------------------------------------------------------------
# list all the packages in the PACKAGE_FOLDER
# extra packages can be given with an EXTRA_PACKAGE_FOLDER
# <package_folder>/<package>.cmake
#
# Extra packages folder structure
# <extra_package_folder>/<package>/package.cmake
# /src
# /test
# /manual
#
# ------------------------------------------------------------------------------
function(package_list_packages PACKAGE_FOLDER)
cmake_parse_arguments(_opt_pkg
"NO_AUTO_COMPILE_FLAGS"
"SOURCE_FOLDER;EXTRA_PACKAGES_FOLDER;TEST_FOLDER;MANUAL_FOLDER"
""
${ARGN})
string(TOUPPER ${PROJECT_NAME} _project)
# Cleaning some states to start correctly
package_get_all_packages(_already_loaded_pkg)
foreach(_pkg_name ${_already_loaded_pkg})
_package_unset_extra_dependencies(${_pkg_name})
_package_unset_dependencies(${_pkg_name} PRIVATE)
_package_unset_dependencies(${_pkg_name} INTERFACE)
_package_unset_activated(${_pkg_name})
endforeach()
if(_opt_pkg_SOURCE_FOLDER)
set(_src_folder "${_opt_pkg_SOURCE_FOLDER}")
else()
set(_src_folder "src/")
endif()
get_filename_component(_abs_src_folder ${_src_folder} ABSOLUTE)
if(_opt_pkg_TEST_FOLDER)
set(_test_folder "${_opt_pkg_TEST_FOLDER}")
else()
set(_test_folder "test/")
endif()
if(_opt_pkg_MANUAL_FOLDER)
set(_manual_folder "${_opt_pkg_MANUAL_FOLDER}")
else()
set(_manual_folder "doc/manual")
endif()
if(_opt_pkg_NO_AUTO_COMPILE_FLAGS)
package_set_project_variable(NO_AUTO_COMPILE_FLAGS TRUE)
else()
package_set_project_variable(NO_AUTO_COMPILE_FLAGS FALSE)
endif()
get_filename_component(_abs_test_folder ${_test_folder} ABSOLUTE)
get_filename_component(_abs_manual_folder ${_manual_folder} ABSOLUTE)
# check all the packages in the <package_folder>
file(GLOB _package_list "${PACKAGE_FOLDER}/*.cmake")
set(_package_files)
foreach(_pkg ${_package_list})
get_filename_component(_basename ${_pkg} NAME)
if(NOT _basename MATCHES "^\\.#.*")
list(APPEND _package_files ${_basename})
endif()
endforeach()
if(_package_files)
list(SORT _package_files)
endif()
# check all packages
set(_packages_list_all)
foreach(_pkg_file ${_package_files})
string(REGEX REPLACE "[0-9]+_" "" _pkg_file_stripped ${_pkg_file})
string(REGEX REPLACE "\\.cmake" "" _pkg ${_pkg_file_stripped})
set(_current_src_folder "${_abs_src_folder}" CACHE INTERNAL "" FORCE)
set(_current_test_folder "${_abs_test_folder}" CACHE INTERNAL "" FORCE)
set(_current_manual_folder "${_abs_manual_folder}" CACHE INTERNAL "" FORCE)
include("${PACKAGE_FOLDER}/${_pkg_file}")
unset(_current_src_folder CACHE)
unset(_current_test_folder CACHE)
unset(_current_manual_folder CACHE)
endforeach()
# check the extra_packages if they exists
if(_opt_pkg_EXTRA_PACKAGES_FOLDER)
file(GLOB _extra_package_list RELATIVE
"${_opt_pkg_EXTRA_PACKAGES_FOLDER}" "${_opt_pkg_EXTRA_PACKAGES_FOLDER}/*")
foreach(_pkg ${_extra_package_list})
if(EXISTS "${_opt_pkg_EXTRA_PACKAGES_FOLDER}/${_pkg}/package.cmake")
package_get_name(${_pkg} _pkg_name)
_package_set_filename(${_pkg_name}
"${_opt_pkg_EXTRA_PACKAGES_FOLDER}/${_pkg}/package.cmake")
set(_current_src_folder "${_opt_pkg_EXTRA_PACKAGES_FOLDER}/${_pkg}/src" CACHE INTERNAL "" FORCE)
if(EXISTS "${_opt_pkg_EXTRA_PACKAGES_FOLDER}/${_pkg}/test")
set(_current_test_folder "${_opt_pkg_EXTRA_PACKAGES_FOLDER}/${_pkg}/test" CACHE INTERNAL "" FORCE)
endif()
if(EXISTS "${_opt_pkg_EXTRA_PACKAGES_FOLDER}/${_pkg}/manual")
set(_current_manual_folder "${_opt_pkg_EXTRA_PACKAGES_FOLDER}/${_pkg}/manual" CACHE INTERNAL "" FORCE)
endif()
list(APPEND _extra_pkg_src_folders "${_opt_pkg_EXTRA_PACKAGES_FOLDER}/${_pkg}/src")
include("${_opt_pkg_EXTRA_PACKAGES_FOLDER}/${_pkg}/package.cmake")
unset(_current_src_folder CACHE)
unset(_current_test_folder CACHE)
unset(_current_manual_folder CACHE)
endif()
endforeach()
endif()
_package_build_rdependencies()
_package_load_packages()
_package_check_files_exists()
_package_check_files_registered(${_abs_src_folder} ${_extra_pkg_src_folders})
# Load boost components if boost was loaded
package_is_activated(Boost _ret)
if(_ret)
_package_load_boost_components()
endif()
endfunction()
# ------------------------------------------------------------------------------
# macro to include internal/external packages packages
# package_declare(<package real name>
# [EXTERNAL] [META] [ADVANCED] [NOT_OPTIONAL]
# [DESCRIPTION <description>] [DEFAULT <default_value>]
# [DEPENDS <pkg> ...]
# [BOOST_COMPONENTS <pkg> ...]
# [EXTRA_PACKAGE_OPTIONS <opt> ...]
# [COMPILE_FLAGS <lang> <flags>]
# [SYSTEM <bool> [ <script_to_compile> ]]
# [FEATURES_PUBLIC <feature> ...]
# [FEATURES_PRIVATE <feature> ...])
# ------------------------------------------------------------------------------
function(package_declare pkg)
package_get_name(${pkg} _pkg_name)
_package_set_real_name(${_pkg_name} ${pkg})
_package_set_filename(${_pkg_name} "${CMAKE_CURRENT_LIST_FILE}")
_package_set_sources_folder(${_pkg_name} "${_current_src_folder}")
_package_variable_unset(SRCS ${_pkg_name})
_package_variable_unset(PUBLIC_HEADERS ${_pkg_name})
_package_variable_unset(PRIVATE_HEADERS ${_pkg_name})
if(_current_test_folder)
_package_set_tests_folder(${_pkg_name} "${_current_test_folder}")
endif()
if(_current_manual_folder)
_package_set_manual_folder(${_pkg_name} "${_current_manual_folder}")
endif()
package_get_project_variable(ALL_PACKAGES_LIST _tmp_pkg_list)
list(APPEND _tmp_pkg_list ${_pkg_name})
list(REMOVE_DUPLICATES _tmp_pkg_list)
package_set_project_variable(ALL_PACKAGES_LIST ${_tmp_pkg_list})
set(_options
EXTERNAL
NOT_OPTIONAL
META
ADVANCED
EXCLUDE_FROM_ALL)
set(_one_valued_options
DEFAULT
DESCRIPTION)
set(_multi_valued_options
DEPENDS
EXTRA_PACKAGE_OPTIONS
COMPILE_FLAGS
BOOST_COMPONENTS
SYSTEM
FEATURES_PUBLIC
FEATURES_PRIVATE)
cmake_parse_arguments(_opt_pkg
"${_options}"
"${_one_valued_options}"
"${_multi_valued_options}"
${ARGN})
if(_opt_pkg_UNPARSED_ARGUMENTS)
message("You gave to many arguments while registering the package ${pkg} \"${_opt_pkg_UNPARSED_ARGUMENTS}\"")
endif()
# set the nature
if(_opt_pkg_EXTERNAL)
_package_set_nature(${_pkg_name} "external")
elseif(_opt_pkg_META)
_package_set_nature(${_pkg_name} "meta")
else()
_package_set_nature(${_pkg_name} "internal")
endif()
_package_declare_option(${_pkg_name})
# set description
if(_opt_pkg_DESCRIPTION)
_package_set_description(${_pkg_name} ${_opt_pkg_DESCRIPTION})
else()
_package_set_description(${_pkg_name} "")
endif()
_package_get_option_name(${_pkg_name} _option_name)
_package_get_description(${_pkg_name} _description)
# get the default value
if(DEFINED _opt_pkg_DEFAULT)
set(_default ${_opt_pkg_DEFAULT})
else()
if(_opt_pkg_NOT_OPTIONAL)
set(_default ON)
else()
set(_default OFF)
endif()
endif()
# set the option if needed
if(_opt_pkg_NOT_OPTIONAL)
_package_get_nature(${_pkg_name} _nature)
_package_set_nature(${_pkg_name} "${_nature}_not_optional")
set(${_option_name} ${_default} CACHE INTERNAL "${_description}" FORCE)
mark_as_advanced(${_option_name})
else()
option(${_option_name} "${_description}" ${_default})
if(_opt_pkg_ADVANCED OR _opt_pkg_EXTERNAL)
mark_as_advanced(${_option_name})
endif()
endif()
# Set the option for third-partie that can be compiled as an ExternalProject
if(DEFINED _opt_pkg_SYSTEM)
list(LENGTH _opt_pkg_SYSTEM _length)
list(GET _opt_pkg_SYSTEM 0 _bool)
_package_set_system_option(${_pkg_name} ${_bool})
if(_length GREATER 1)
list(GET _opt_pkg_SYSTEM 1 _script)
_package_set_system_script(${_pkg_name} ${_script})
endif()
endif()
# set the dependecies
if(_opt_pkg_DEPENDS)
set(_deps_types PRIVATE PUBLIC INTERFACE)
cmake_parse_arguments(_pkg_deps
""
""
"${_deps_types}"
${_opt_pkg_DEPENDS})
list(APPEND _pkg_deps_PRIVATE ${_pkg_deps_UNPARSED_ARGUMENTS})
foreach(_type ${_deps_types})
set(_depends)
foreach(_dep ${_pkg_deps_${_type}})
package_get_name(${_dep} _dep_pkg_name)
list(APPEND _depends ${_dep_pkg_name})
endforeach()
_package_add_dependencies(${_pkg_name} ${_type} ${_depends})
endforeach()
endif()
# keep the extra option for the future find package
if(_opt_pkg_EXTRA_PACKAGE_OPTIONS)
_package_set_find_package_extra_options(${_pkg_name} "${_opt_pkg_EXTRA_PACKAGE_OPTIONS}")
endif()
# register the compilation flags
if(_opt_pkg_COMPILE_FLAGS)
set(_languages C CXX Fortran)
cmake_parse_arguments(_compile_flags
"" "" "${_languages}"
${_opt_pkg_COMPILE_FLAGS}
)
# this is done to maintain backward compatibility
if(_compile_flags_UNPARSED_ARGUMENTS)
set(_compile_flags_CXX ${_compile_flags_UNPARSED_ARGUMENTS})
endif()
foreach(_lang ${_languages})
if(_compile_flags_${_lang})
_package_set_compile_flags(${_pkg_name} ${_lang} ${_compile_flags_${_lang}})
else()
_package_unset_compile_flags(${_pkg_name} ${_lang})
endif()
endforeach()
endif()
# set the boost dependencies
if(_opt_pkg_BOOST_COMPONENTS)
_package_set_boost_component_needed(${_pkg_name} "${_opt_pkg_BOOST_COMPONENTS}")
endif()
set(_variables FEATURES_PUBLIC FEATURES_PRIVATE EXCLUDE_FROM_ALL)
foreach(_variable ${_variables})
if(_opt_pkg_${_variable})
_package_set_variable(${_variable} ${_pkg_name} "${_opt_pkg_${_variable}}")
endif()
endforeach()
endfunction()
# ------------------------------------------------------------------------------
# declare the source files of a given package
#
# package_declare_sources(<package> <list of sources>
# SOURCES <source file> ...
# PUBLIC_HEADER <header file> ...
# PRIVATE_HEADER <header file> ...)
# ------------------------------------------------------------------------------
function(package_declare_sources pkg)
package_get_name(${pkg} _pkg_name)
# get 3 lists, if none of the options given try to distinguish the different lists
cmake_parse_arguments(_opt_pkg
""
""
"SOURCES;PUBLIC_HEADERS;PRIVATE_HEADERS"
${ARGN})
set(_tmp_srcs ${_opt_pkg_SOURCES})
set(_tmp_pub_hdrs ${_opt_pkg_PUBLIC_HEADER})
set(_tmp_pri_hdrs ${_opt_pkg_PRIVATE_HEADERS})
foreach(_file ${_opt_pkg_UNPARSED_ARGUMENTS})
if(${_file} MATCHES ".*inline.*\\.cc")
list(APPEND _tmp_pub_hdrs ${_file})
elseif(${_file} MATCHES ".*\\.h+")
list(APPEND _tmp_pub_hdrs ${_file})
else()
list(APPEND _tmp_srcs ${_file})
endif()
endforeach()
_package_get_sources_folder(${_pkg_name} _src_folder)
foreach(_type _srcs _pub_hdrs _pri_hdrs)
set(${_type})
foreach(_file ${_tmp${_type}})
# get the full name
set(_full_path "${_src_folder}/${_file}")
list(APPEND ${_type} "${_full_path}")
endforeach()
endforeach()
set(${_pkg_name}_SRCS "${_srcs}"
CACHE INTERNAL "List of sources files" FORCE)
set(${_pkg_name}_PUBLIC_HEADERS "${_pub_hdrs}"
CACHE INTERNAL "List of public header files" FORCE)
set(${_pkg_name}_PRIVATE_HEADERS "${_pri_hdrs}"
CACHE INTERNAL "List of private header files" FORCE)
endfunction()
# ------------------------------------------------------------------------------
diff --git a/cmake/Modules/CMakePackagesSystemPrivateFunctions.cmake b/cmake/Modules/CMakePackagesSystemPrivateFunctions.cmake
index 08498bd78..d1a58736a 100644
--- a/cmake/Modules/CMakePackagesSystemPrivateFunctions.cmake
+++ b/cmake/Modules/CMakePackagesSystemPrivateFunctions.cmake
@@ -1,968 +1,981 @@
#===============================================================================
# @file CMakePackagesSystemPrivateFunctions.cmake
#
# @author Nicolas Richart <nicolas.richart@epfl.ch>
#
# @date creation: Sat Jul 18 2015
# @date last modification: Wed Jan 20 2016
#
# @brief Set of macros used by the package system, internal functions
#
# @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 <http://www.gnu.org/licenses/>.
#
#===============================================================================
+if (DEFINED CMAKE_PACKAGES_SYSTEM_PRIVATE_FUNCTIONS_LOADED)
+ return()
+endif()
+set(CMAKE_PACKAGES_SYSTEM_PRIVATE_FUNCTIONS_LOADED TRUE)
+
# ==============================================================================
# "Private" Accessors
# ==============================================================================
# ------------------------------------------------------------------------------
# Real name
# ------------------------------------------------------------------------------
function(_package_get_real_name pkg_name real_name)
set(${real_name} ${${pkg_name}} PARENT_SCOPE)
endfunction()
function(_package_set_real_name pkg_name real_name)
set(${pkg_name} ${real_name} CACHE INTERNAL "" FORCE)
endfunction()
# ------------------------------------------------------------------------------
# Option name
# ------------------------------------------------------------------------------
function(_package_declare_option pkg_name)
string(TOUPPER "${PROJECT_NAME}" _project)
_package_get_real_name(${pkg_name} _real_name)
string(TOUPPER "${_real_name}" _u_package)
_package_get_nature(${pkg_name} _nature)
if(${_nature} MATCHES "internal" OR ${_nature} MATCHES "meta")
set(_opt_name ${_project}_${_u_package})
elseif(${_nature} MATCHES "external")
set(_opt_name ${_project}_USE_${_u_package})
else()
set(_opt_name UNKNOWN_NATURE_${_project}_${_u_package})
endif()
_package_set_variable(OPTION_NAME ${pkg_name} ${_opt_name})
endfunction()
function(_package_get_option_name pkg_name opt_name)
_package_get_variable(OPTION_NAME ${pkg_name} _opt_name)
set(${opt_name} ${_opt_name} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Set if system package or compile external lib
# ------------------------------------------------------------------------------
function(_package_set_system_option pkg_name default)
string(TOUPPER "${PROJECT_NAME}" _project)
_package_get_real_name(${pkg_name} _real_name)
string(TOUPPER "${_real_name}" _u_package)
set(${_project}_USE_SYSTEM_${_u_package} ${default} CACHE STRING
"Should akantu compile the third-party: \"${_real_name}\"")
mark_as_advanced(${_project}_USE_SYSTEM_${_u_package})
set_property(CACHE ${_project}_USE_SYSTEM_${_u_package} PROPERTY STRINGS ON OFF AUTO)
endfunction()
function(_package_use_system pkg_name use)
string(TOUPPER "${PROJECT_NAME}" _project)
_package_get_real_name(${pkg_name} _real_name)
string(TOUPPER "${_real_name}" _u_package)
if(DEFINED ${_project}_USE_SYSTEM_${_u_package})
if(${${_project}_USE_SYSTEM_${_u_package}} MATCHES "(ON|AUTO)")
set(${use} TRUE PARENT_SCOPE)
else()
set(${use} FALSE PARENT_SCOPE)
endif()
else()
set(${use} TRUE PARENT_SCOPE)
endif()
endfunction()
function(_package_has_system_fallback pkg_name fallback)
string(TOUPPER "${PROJECT_NAME}" _project)
_package_get_real_name(${pkg_name} _real_name)
string(TOUPPER "${_real_name}" _u_package)
if(DEFINED ${_project}_USE_SYSTEM_${_u_package})
if(${${_project}_USE_SYSTEM_${_u_package}} MATCHES "AUTO")
set(${fallback} TRUE PARENT_SCOPE)
else()
set(${fallback} FALSE PARENT_SCOPE)
endif()
else()
set(${fallback} FALSE PARENT_SCOPE)
endif()
endfunction()
function(_package_set_system_script pkg_name script)
_package_set_variable(COMPILE_SCRIPT ${pkg_name} "${script}")
endfunction()
function(_package_add_third_party_script_variable pkg_name var)
_package_set_variable(VARIABLE_${var} ${pkg_name} "${ARGN}")
set(${var} ${ARGN} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
function(_package_load_third_party_script pkg_name)
if(${pkg_name}_COMPILE_SCRIPT)
# set the stored variable
get_cmake_property(_all_vars VARIABLES)
foreach(_var ${_all_vars})
if(_var MATCHES "^${pkg_name}_VARIABLE_.*")
string(REPLACE "${pkg_name}_VARIABLE_" "" _orig_var "${_var}")
set(${_orig_var} ${${_var}})
endif()
endforeach()
_package_get_real_name(${pkg_name} _name)
string(TOUPPER "${_name}" _u_name)
_package_get_option_name(${pkg_name} _opt_name)
if(${_opt_name}_VERSION)
set(_version "${${_opt_name}_VERSION}")
set(${_u_name}_VERSION "${_version}" CACHE INTERNAL "" FORCE)
elseif(${_u_name}_VERSION)
set(_version "${${_u_name}_VERSION}")
endif()
# load the script
include(ExternalProject)
include(${${pkg_name}_COMPILE_SCRIPT})
if(${_u_name}_LIBRARIES)
_package_set_libraries(${pkg_name} ${${_u_name}_LIBRARIES})
list(APPEND _required_vars ${_u_name}_LIBRARIES)
endif()
if(${_u_name}_INCLUDE_DIR)
_package_set_include_dir(${pkg_name} ${${_u_name}_INCLUDE_DIR})
list(APPEND _required_vars ${_u_name}_INCLUDE_DIR)
endif()
include(FindPackageHandleStandardArgs)
+ if (NOT _required_vars)
+ message(FATAL_ERROR "The package ${_name} does not define any of the variables ${_u_name}_INCLUDE_DIR nor ${_u_name}_LIBRARIES")
+ endif()
+
if(CMAKE_VERSION VERSION_GREATER 2.8.12)
find_package_handle_standard_args(${_name}
REQUIRED_VARS ${_required_vars}
VERSION_VAR _version
FAIL_MESSAGE "Something was not configured by a the third-party script for ${_name}"
)
else()
find_package_handle_standard_args(${_name}
"Something was not configured by a the third-party script for ${_name}"
${_required_vars}
)
endif()
endif()
set(${pkg_name}_USE_SYSTEM_PREVIOUS FALSE CACHE INTERNAL "" FORCE)
endfunction()
# ------------------------------------------------------------------------------
# Nature
# ------------------------------------------------------------------------------
function(_package_set_nature pkg_name nature)
_package_set_variable(NATURE ${pkg_name} ${nature})
endfunction()
function(_package_get_nature pkg_name nature)
_package_get_variable(NATURE ${pkg_name} _nature "unknown")
set(${nature} ${_nature} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Description
# ------------------------------------------------------------------------------
function(_package_set_description pkg_name desc)
_package_set_variable(DESC ${pkg_name} ${desc})
endfunction()
function(_package_get_description pkg_name desc)
_package_get_variable(DESC ${pkg_name} _desc "No description set for the package ${${pkg_name}} (${pkg_name})")
set(${desc} ${_desc} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Package file name
# ------------------------------------------------------------------------------
function(_package_set_filename pkg_name file)
_package_set_variable(FILE ${pkg_name} ${file})
endfunction()
function(_package_get_filename pkg_name file)
_package_get_variable(FILE ${pkg_name} _file "No filename set for the package ${${pkg_name}}")
set(${file} ${_file} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Source folder
# ------------------------------------------------------------------------------
function(_package_set_sources_folder pkg_name src_folder)
_package_set_variable(SRC_FOLDER ${pkg_name} ${src_folder})
endfunction()
function(_package_get_sources_folder pkg_name src_folder)
_package_get_variable(SRC_FOLDER ${pkg_name} _src_folder)
set(${src_folder} ${_src_folder} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Test folder
# ------------------------------------------------------------------------------
function(_package_set_tests_folder pkg_name test_folder)
_package_set_variable(TEST_FOLDER ${pkg_name} ${test_folder})
endfunction()
function(_package_get_tests_folder pkg_name test_folder)
_package_get_variable(TEST_FOLDER ${pkg_name} _test_folder)
set(${test_folder} ${_test_folder} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Manual folder
# ------------------------------------------------------------------------------
function(_package_set_manual_folder pkg_name manual_folder)
_package_set_variable(MANUAL_FOLDER ${pkg_name} ${manual_folder})
endfunction()
function(_package_get_manual_folder pkg_name manual_folder)
_package_get_variable(MANUAL_FOLDER ${pkg_name} _manual_folder)
set(${manual_folder} ${_manual_folder} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Extra option for the find_package
# ------------------------------------------------------------------------------
function(_package_set_find_package_extra_options pkg_name)
_package_set_variable(FIND_PKG_OPTIONS ${pkg_name} ${ARGN})
endfunction()
function(_package_get_find_package_extra_options pkg_name options)
_package_get_variable(FIND_PKG_OPTIONS ${pkg_name} _options)
set(${options} ${_options} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Compilation flags
# ------------------------------------------------------------------------------
function(_package_set_compile_flags pkg_name lang)
_package_set_variable(COMPILE_${lang}_FLAGS ${pkg_name} ${ARGN})
endfunction()
function(_package_unset_compile_flags pkg_name lang)
_package_variable_unset(COMPILE_${lang}_FLAGS ${pkg_name})
endfunction()
function(_package_get_compile_flags pkg_name lang flags)
_package_get_variable(COMPILE_${lang}_FLAGS ${pkg_name} _tmp_flags)
string(REPLACE ";" " " _flags "${_tmp_flags}")
set(${flags} "${_flags}" PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Include dir
# ------------------------------------------------------------------------------
function(_package_set_include_dir pkg_name)
_package_set_variable(INCLUDE_DIR ${pkg_name} ${ARGN})
endfunction()
function(_package_get_include_dir pkg_name include_dir)
_package_get_variable(INCLUDE_DIR ${pkg_name} _include_dir "")
set(${include_dir} ${_include_dir} PARENT_SCOPE)
endfunction()
function(_package_add_include_dir pkg_name)
_package_add_to_variable(INCLUDE_DIR ${pkg_name} ${ARGN})
endfunction()
# ------------------------------------------------------------------------------
# Libraries
# ------------------------------------------------------------------------------
function(_package_set_libraries pkg_name)
_package_set_variable(LIBRARIES ${pkg_name} ${ARGN})
endfunction()
function(_package_get_libraries pkg_name libraries)
_package_get_variable(LIBRARIES ${pkg_name} _libraries "")
set(${libraries} ${_libraries} PARENT_SCOPE)
endfunction()
function(_package_add_libraries pkg_name)
_package_add_to_variable(LIBRARIES ${pkg_name} ${ARGN})
endfunction()
# ------------------------------------------------------------------------------
# Extra dependencies like custom commands of ExternalProject
# ------------------------------------------------------------------------------
function(_package_add_extra_dependency pkg_name)
_package_add_to_variable(EXTRA_DEPENDENCY ${pkg_name} ${ARGN})
endfunction()
function(_package_rm_extra_dependency pkg_name dep)
_package_remove_from_variable(EXTRA_DEPENDENCY ${pkg_name} ${dep})
endfunction()
function(_package_set_extra_dependencies pkg)
_package_set_variable(EXTRA_DEPENDENCY ${pkg_name} ${ARGN})
endfunction()
function(_package_get_extra_dependencies pkg deps)
_package_get_variable(EXTRA_DEPENDENCY ${pkg_name} _deps "")
set(${deps} ${_deps} PARENT_SCOPE)
endfunction()
function(_package_unset_extra_dependencies pkg_name)
_package_variable_unset(EXTRA_DEPENDENCY ${pkg_name})
endfunction()
# ------------------------------------------------------------------------------
# Activate/deactivate
# ------------------------------------------------------------------------------
function(_package_activate pkg_name)
_package_set_variable(STATE ${pkg_name} ON)
endfunction()
function(_package_deactivate pkg_name)
_package_set_variable(STATE ${pkg_name} OFF)
endfunction()
function(_package_is_activated pkg_name act)
_package_get_variable(STATE ${pkg_name} _state OFF)
if(_state)
set(${act} TRUE PARENT_SCOPE)
else()
set(${act} FALSE PARENT_SCOPE)
endif()
endfunction()
function(_package_is_deactivated pkg_name act)
_package_get_variable(STATE ${pkg_name} _state OFF)
if(NOT _state)
set(${act} TRUE PARENT_SCOPE)
else()
set(${act} FALSE PARENT_SCOPE)
endif()
endfunction()
function(_package_unset_activated pkg_name)
_package_variable_unset(STATE ${pkg_name})
endfunction()
# ------------------------------------------------------------------------------
# Callbacks
# ------------------------------------------------------------------------------
function(_package_on_enable_script pkg_name script)
string(TOLOWER "${pkg_name}" _l_pkg_name)
set(_output_file "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${_l_pkg_name}.cmake")
file(WRITE "${_output_file}"
"${script}")
_package_set_variable(CALLBACK_SCRIPT ${pkg_name} "${_output_file}")
endfunction()
function(_package_get_callback_script pkg_name filename)
_package_get_variable(CALLBACK_SCRIPT ${pkg_name} _filename NOTFOUND)
set(${filename} ${_filename} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Export list
# ------------------------------------------------------------------------------
function(_package_add_to_export_list pkg_name)
_package_add_to_variable(EXPORT_LIST ${pkg_name} ${ARGN})
endfunction()
+function(_package_remove_from_export_list pkg_name)
+ _package_remove_from_variable(EXPORT_LIST ${pkg_name} ${ARGN})
+endfunction()
+
function(_package_get_export_list pkg_name export_list)
_package_get_variable(EXPORT_LIST ${pkg_name} _export_list)
set(${export_list} ${_export_list} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Direct dependencies
# ------------------------------------------------------------------------------
function(_package_add_dependencies pkg_name type)
_package_add_to_variable(DEPENDENCIES_${type} ${pkg_name} ${ARGN})
endfunction()
function(_package_get_dependencies pkg_name type dependencies)
_package_get_variable(DEPENDENCIES_${type} ${pkg_name} _dependencies)
set(${dependencies} ${_dependencies} PARENT_SCOPE)
endfunction()
function(_package_unset_dependencies pkg_name type)
_package_variable_unset(DEPENDENCIES_${type} ${pkg_name})
endfunction()
function(_package_remove_dependency pkg_name type dep)
set(_deps ${${pkg_name}_DEPENDENCIES_${type}})
_package_get_fdependencies(${dep} _fdeps)
# check if this is the last reverse dependency
list(LENGTH _fdeps len)
list(FIND _fdeps ${pkg_name} pos)
if((len EQUAL 1) AND (NOT pos EQUAL -1))
_package_get_description(${dep} _dep_desc)
_package_get_option_name(${dep} _dep_option_name)
set(${_dep_option_name} ${${dep}_OLD} CACHE BOOL "${_dep_desc}" FORCE)
unset(${dep}_OLD CACHE)
endif()
# remove the pkg_name form the reverse dependency
_package_remove_fdependency(${dep} ${pkg_name})
list(FIND _deps ${dep} pos)
if(NOT pos EQUAL -1)
list(REMOVE_AT _deps ${pos})
_package_set_variable(DEPENDENCIES_${type} ${pkg_name} ${_deps})
endif()
endfunction()
# ------------------------------------------------------------------------------
# Functions to handle reverse dependencies
# ------------------------------------------------------------------------------
function(_package_set_rdependencies pkg_name)
_package_set_variable(RDEPENDENCIES ${pkg_name} ${ARGN})
endfunction()
function(_package_get_rdependencies pkg_name rdependencies)
_package_get_variable(RDEPENDENCIES ${pkg_name} _rdependencies)
set(${rdependencies} ${_rdependencies} PARENT_SCOPE)
endfunction()
function(_package_add_rdependency pkg_name rdep)
# store the reverse dependency
_package_add_to_variable(RDEPENDENCIES ${pkg_name} ${rdep})
endfunction()
function(_package_remove_rdependency pkg_name rdep)
_package_remove_from_variable(RDEPENDENCIES ${pkg_name} ${rdep})
endfunction()
# ------------------------------------------------------------------------------
# Function to handle forcing dependencies (Package turn ON that enforce their
# dependencies ON)
# ------------------------------------------------------------------------------
function(_package_set_fdependencies pkg_name)
_package_set_variable(FDEPENDENCIES ${pkg_name} ${ARGN})
endfunction()
function(_package_get_fdependencies pkg_name fdependencies)
_package_get_variable(FDEPENDENCIES ${pkg_name} _fdependencies)
set(${fdependencies} ${_fdependencies} PARENT_SCOPE)
endfunction()
function(_package_add_fdependency pkg_name fdep)
# store the reverse dependency
_package_add_to_variable(FDEPENDENCIES ${pkg_name} ${fdep})
endfunction()
function(_package_remove_fdependency pkg_name fdep)
_package_remove_from_variable(FDEPENDENCIES ${pkg_name} ${fdep})
endfunction()
# ------------------------------------------------------------------------------
# Exteral package system as apt rpm dependencies
# ------------------------------------------------------------------------------
function(_package_set_package_system_dependency pkg system)
string(TOUPPER "${_system}" _u_system)
_package_set_variable(PACKAGE_SYSTEM_${_u_system} ${_pkg_name} ${ARGN})
endfunction()
function(_package_get_package_system_dependency pkg system var)
string(TOUPPER "${_system}" _u_system)
_package_get_variable(PACKAGE_SYSTEM_${_u_system} ${_pkg_name} ${_deps})
set(${var} ${_deps} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Documentation related functions
# ------------------------------------------------------------------------------
function(_package_set_documentation_files pkg_name)
_package_set_variable(DOCUMENTATION_FILES ${pkg_name} ${ARGN})
endfunction()
function(_package_get_documentation_files pkg_name doc_files)
_package_get_variable(DOCUMENTATION_FILES ${pkg_name} _doc_files "")
set(${doc_files} ${_doc_files} PARENT_SCOPE)
endfunction()
function(_package_set_documentation pkg_name)
# \n replaced by && and \\ by ££ to avoid cache problems
set(_doc_str "")
foreach(_str ${ARGN})
set(_doc_str "${_doc_str}&&${_str}")
endforeach()
string(REPLACE "\\" "££" _doc_escaped "${_doc_str}")
_package_set_variable(DOCUMENTATION ${pkg_name} "${_doc_str}")
endfunction()
function(_package_get_documentation pkg_name _doc)
# \n replaced by && and \\ by ££ to avoid cache problems
_package_get_variable(DOCUMENTATION ${pkg_name} _doc_tmp "")
string(REPLACE "££" "\\" _doc_escaped "${_doc_tmp}")
string(REPLACE "&&" "\n" _doc_newlines "${_doc_escaped}")
set(${_doc} "${_doc_newlines}" PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Special boost thingy
# ------------------------------------------------------------------------------
function(_package_set_boost_component_needed pkg_name)
_package_add_to_variable(BOOST_COMPONENTS_NEEDED ${pkg_name} ${ARGN})
package_get_name(Boost _boost_pkg_name)
_package_add_dependencies(${pkg_name} PRIVATE ${_boost_pkg_name})
endfunction()
function(_package_get_boost_component_needed pkg_name needed)
_package_get_variable(BOOST_COMPONENTS_NEEDED ${pkg_name} _needed)
set(${needed} ${_needed} PARENT_SCOPE)
endfunction()
function(_package_load_boost_components)
string(TOUPPER ${PROJECT_NAME} _project)
_package_get_variable_for_activated(BOOST_COMPONENTS_NEEDED _boost_needed_components)
package_get_name(Boost _pkg_name)
if(_boost_needed_components)
message(STATUS "Looking for Boost liraries: ${_boost_needed_components}")
foreach(_comp ${_boost_needed_components})
find_package(Boost COMPONENTS ${_comp} QUIET)
string(TOUPPER ${_comp} _u_comp)
if(Boost_${_u_comp}_FOUND)
message(STATUS " ${_comp}: FOUND")
package_set_project_variable(BOOST_${_u_comp} TRUE)
# Generate the libraries for the package
_package_add_libraries(${_pkg_name} ${Boost_${_u_comp}_LIBRARY})
else()
message(STATUS " ${_comp}: NOT FOUND")
endif()
endforeach()
endif()
endfunction()
# ------------------------------------------------------------------------------
# get the list of source files for a given package
# ------------------------------------------------------------------------------
function(_package_get_source_files pkg_name SRCS PUBLIC_HEADERS PRIVATE_HEADERS)
string(TOUPPER ${PROJECT_NAME} _project)
set(tmp_SRCS)
set(tmp_PUBLIC_HEADERS)
set(tmp_PRIVATE_HEADERS)
foreach(_type SRCS PUBLIC_HEADERS PRIVATE_HEADERS)
foreach(_file ${${pkg_name}_${_type}})
string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" _rel_file "${_file}")
list(APPEND tmp_${_type} "${_rel_file}")
endforeach()
endforeach()
set(${SRCS} ${tmp_SRCS} PARENT_SCOPE)
set(${PUBLIC_HEADERS} ${tmp_PUBLIC_HEADERS} PARENT_SCOPE)
set(${PRIVATE_HEADERS} ${tmp_PRIVATE_HEADERS} PARENT_SCOPE)
endfunction()
# ==============================================================================
# Internal functions
# ==============================================================================
# ------------------------------------------------------------------------------
# Build the reverse dependencies from the dependencies
# ------------------------------------------------------------------------------
function(_package_build_rdependencies)
package_get_all_packages(_pkg_list)
# set empty lists
foreach(_pkg_name ${_pkg_list})
set(${_pkg_name}_rdeps)
endforeach()
# fill the dependencies list
foreach(_pkg_name ${_pkg_list})
_package_get_dependencies(${_pkg_name} PRIVATE _deps_priv)
_package_get_dependencies(${_pkg_name} INTERFACE _deps_interface)
foreach(_dep_name ${_deps_priv} ${_deps_interface})
list(APPEND ${_dep_name}_rdeps ${_pkg_name})
endforeach()
endforeach()
# clean and set the reverse dependencies
foreach(_pkg_name ${_pkg_list})
if(${_pkg_name}_rdeps)
list(REMOVE_DUPLICATES ${_pkg_name}_rdeps)
_package_set_rdependencies(${_pkg_name} ${${_pkg_name}_rdeps})
endif()
endforeach()
endfunction()
# ------------------------------------------------------------------------------
# This function resolve the dependance order run the needed find_packages
# ------------------------------------------------------------------------------
function(_package_load_packages)
package_get_all_packages(_pkg_list)
# Activate the dependencies of activated package and generate an ordered list
# of dependencies
set(ordered_loading_list)
foreach(_pkg_name ${_pkg_list})
_package_load_dependencies_package(${_pkg_name} ordered_loading_list)
endforeach()
# Load the packages in the propoer order
foreach(_pkg_name ${ordered_loading_list})
_package_get_option_name(${_pkg_name} _option_name)
if(${_option_name})
_package_load_package(${_pkg_name})
else()
# deactivate the packages than can already be deactivated
_package_deactivate(${_pkg_name})
endif()
endforeach()
# generates the activated and unactivated lists of packages
set(_packages_activated)
set(_packages_deactivated)
foreach(_pkg_name ${_pkg_list})
_package_is_activated(${_pkg_name} _active)
set(_exclude FALSE)
_package_get_variable(EXCLUDE_FROM_ALL ${_pkg_name} _exclude)
if(_active AND NOT _exclude)
list(APPEND _packages_activated ${_pkg_name})
else()
list(APPEND _packages_deactivated ${_pkg_name})
endif()
endforeach()
# generate the list usable by the calling code
package_set_project_variable(ACTIVATED_PACKAGE_LIST "${_packages_activated}")
package_set_project_variable(DEACTIVATED_PACKAGE_LIST "${_packages_deactivated}")
endfunction()
# ------------------------------------------------------------------------------
# This load an external package and recursively all its dependencies
# ------------------------------------------------------------------------------
function(_package_load_dependencies_package pkg_name loading_list)
# Get packages informations
_package_get_option_name(${pkg_name} _pkg_option_name)
_package_get_dependencies(${pkg_name} PRIVATE _deps_private)
_package_get_dependencies(${pkg_name} INTERFACE _deps_interface)
set(_dependencies ${_deps_private} ${_deps_interface})
# handle the dependencies
foreach(_dep_name ${_dependencies})
_package_get_description(${_dep_name} _dep_desc)
_package_get_option_name(${_dep_name} _dep_option_name)
_package_get_fdependencies(${_dep_name} _fdeps)
if(${_pkg_option_name})
if("${_fdeps}" STREQUAL "")
set(${_dep_name}_OLD ${${_dep_option_name}} CACHE INTERNAL "" FORCE)
endif()
# set the option to on
set(_type BOOL)
_package_get_nature(${_dep_name} _nature)
if(_nature MATCHES ".*not_optional$")
set(_type INTERNAL)
endif()
set(${_dep_option_name} ON CACHE BOOL "${_dep_desc}" FORCE)
# store the reverse dependency
_package_add_fdependency(${_dep_name} ${pkg_name})
else()
# check if this is the last reverse dependency
list(LENGTH _fdeps len)
list(FIND _fdeps ${pkg_name} pos)
if((len EQUAL 1) AND (NOT pos EQUAL -1))
set(${_dep_option_name} ${${_dep_name}_OLD} CACHE BOOL "${_dep_desc}" FORCE)
unset(${_dep_name}_OLD CACHE)
endif()
# remove the pkg_name form the reverse dependency
_package_remove_fdependency(${_dep_name} ${pkg_name})
endif()
# recusively load the dependencies
_package_load_dependencies_package(${_dep_name} ${loading_list})
endforeach()
# get the compile flags
_package_get_compile_flags(${pkg_name} CXX _pkg_compile_flags)
package_get_project_variable(NO_AUTO_COMPILE_FLAGS _no_auto_compile_flags)
# if package option is on add it in the list
if(${_pkg_option_name})
list(FIND ${loading_list} ${pkg_name} _pos)
if(_pos EQUAL -1)
set(_tmp_loading_list ${${loading_list}})
list(APPEND _tmp_loading_list ${pkg_name})
set(${loading_list} "${_tmp_loading_list}" PARENT_SCOPE)
endif()
#add the comilation flags if needed
if(_pkg_compile_flags AND NOT _no_auto_compile_flags)
add_flags(cxx ${_pkg_compile_flags})
endif()
else()
#remove the comilation flags if needed
if(_pkg_comile_flags AND NOT _no_auto_compile_flags)
remove_flags(cxx ${_pkg_compile_flags})
endif()
endif()
endfunction()
# ------------------------------------------------------------------------------
# Load the package if it is an external one
# ------------------------------------------------------------------------------
function(_package_load_package pkg_name)
# load the package if it is an external
_package_get_nature(${pkg_name} _nature)
if(${_nature} MATCHES "external")
_package_use_system(${pkg_name} _use_system)
set(_activated TRUE)
if(_use_system)
_package_load_external_package(${pkg_name} _activated)
endif()
_package_has_system_fallback(${pkg_name} _fallback)
if((NOT _use_system) OR (_fallback AND (NOT _activated)))
_package_load_third_party_script(${pkg_name})
set(_activated TRUE)
endif()
if(_activated)
_package_activate(${pkg_name})
elseif()
_package_deactivate(${pkg_name})
endif()
else()
_package_activate(${pkg_name})
endif()
endfunction()
# ------------------------------------------------------------------------------
# Load external packages
# ------------------------------------------------------------------------------
function(_package_load_external_package pkg_name activate)
_package_get_real_name(${pkg_name} _real_name)
string(TOUPPER ${_real_name} _u_package)
if(NOT ${pkg_name}_USE_SYSTEM_PREVIOUS)
#if system was off before clear the cache of preset variables
get_cmake_property(_all_vars VARIABLES)
foreach(_var ${_all_vars})
if(_var MATCHES "^${_u_package}_.*")
unset(${_var} CACHE)
endif()
endforeach()
set(${pkg_name}_USE_SYSTEM_PREVIOUS TRUE CACHE INTERNAL "" FORCE)
endif()
_package_get_find_package_extra_options(${pkg_name} _options)
if(_options)
cmake_parse_arguments(_opt_pkg "" "LANGUAGE" "LINK_LIBRARIES;PREFIX;FOUND;ARGS" ${_options})
if(_opt_pkg_UNPARSED_ARGUMENTS)
message("You passed too many options for the find_package related to ${${pkg_name}} \"${_opt_pkg_UNPARSED_ARGUMENTS}\"")
endif()
endif()
if(_opt_pkg_LANGUAGE)
foreach(_language ${_opt_pkg_LANGUAGE})
enable_language(${_language})
endforeach()
endif()
# find the package
set(_required REQUIRED)
_package_has_system_fallback(${pkg_name} _fallback)
if(_fallback)
set(_required QUIET)
endif()
find_package(${_real_name} ${_required} ${_opt_pkg_ARGS})
# check if the package is found
if(_opt_pkg_PREFIX)
set(_package_prefix ${_opt_pkg_PREFIX})
else()
set(_package_prefix ${_u_package})
endif()
set(_act FALSE)
set(_prefix_to_consider)
if(DEFINED _opt_pkg_FOUND)
if(${_opt_pkg_FOUND})
set(_act TRUE)
set(_prefix_to_consider ${_package_prefix})
endif()
else()
foreach(_prefix ${_package_prefix})
if(${_prefix}_FOUND)
set(_act TRUE)
list(APPEND _prefix_to_consider ${_prefix})
endif()
endforeach()
endif()
if(_act)
foreach(_prefix ${_prefix_to_consider})
# Generate the include dir for the package
if(DEFINED ${_prefix}_INCLUDE_DIRS)
_package_set_include_dir(${pkg_name} ${${_prefix}_INCLUDE_DIRS})
elseif(DEFINED ${_prefix}_INCLUDE_DIR)
_package_set_include_dir(${pkg_name} ${${_prefix}_INCLUDE_DIR})
elseif(DEFINED ${_prefix}_INCLUDE_PATH)
_package_set_include_dir(${pkg_name} ${${_prefix}_INCLUDE_PATH})
endif()
# Generate the libraries for the package
if(_opt_pkg_LINK_LIBRARIES)
_package_set_libraries(${pkg_name} ${_opt_pkg_LINK_LIBRARIES})
elseif(DEFINED ${_prefix}_LIBRARIES)
_package_set_libraries(${pkg_name} ${${_prefix}_LIBRARIES})
elseif(DEFINED ${_prefix}_LIBRARY)
_package_set_libraries(${pkg_name} ${${_prefix}_LIBRARY})
endif()
endforeach()
_package_get_callback_script(${pkg_name} _script_file)
if(_script_file)
include("${_script_file}")
endif()
endif()
set(${activate} ${_act} PARENT_SCOPE)
endfunction()
# ------------------------------------------------------------------------------
# Sanity check functions
# ------------------------------------------------------------------------------
function(_package_check_files_exists)
set(_message FALSE)
package_get_all_packages(_pkg_list)
foreach(_pkg_name ${_pkg_list})
set(_pkg_files
${${_pkg_name}_SRCS}
${${_pkg_name}_PUBLIC_HEADERS}
${${_pkg_name}_PRIVATE_HEADERS}
)
_package_get_real_name(${_pkg_name} _real_name)
foreach(_file ${_pkg_files})
if(NOT EXISTS "${_file}")
if(NOT _message)
set(_message TRUE)
message("This file(s) is(are) present in a package but are not present on disk.")
endif()
message(" PACKAGE ${_real_name} FILE ${_file}")
endif()
endforeach()
endforeach()
if(_message)
message(SEND_ERROR "Please check the content of your packages to correct this warnings")
endif()
endfunction()
# ------------------------------------------------------------------------------
function(_package_check_files_registered)
set(_pkg_files)
package_get_all_packages(_pkg_list)
# generates a file list of registered files
foreach(_pkg_name ${_pkg_list})
list(APPEND _pkg_files
${${_pkg_name}_SRCS}
${${_pkg_name}_PUBLIC_HEADERS}
${${_pkg_name}_PRIVATE_HEADERS}
)
endforeach()
# generates the list of files in the source folders
set(_all_src_files)
foreach(_src_folder ${ARGN})
foreach(_ext "cc" "hh" "c" "h" "hpp")
file(GLOB_RECURSE _src_files "${_src_folder}/*.${_ext}")
list(APPEND _all_src_files ${_src_files})
endforeach()
endforeach()
if(_all_src_files)
list(REMOVE_DUPLICATES _all_src_files)
endif()
set(_not_registerd_files)
# check only sources files in the source folders
foreach(_src_folder ${ARGN})
foreach(_file ${_all_src_files})
if("${_file}" MATCHES "${_src_folder}")
list(FIND _pkg_files "${_file}" _index)
if (_index EQUAL -1)
list(APPEND _not_registerd_files ${_file})
endif()
endif()
endforeach()
endforeach()
if(AUTO_MOVE_UNKNOWN_FILES)
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/unknown_files)
endif()
# warn the user and move the files if needed
if(_not_registerd_files)
if(EXISTS ${PROJECT_BINARY_DIR}/missing_files_in_packages)
file(REMOVE ${PROJECT_BINARY_DIR}/missing_files_in_packages)
endif()
message("This files are present in the source folders but are not registered in any package")
foreach(_file ${_not_registerd_files})
message(" ${_file}")
if(AUTO_MOVE_UNKNOWN_FILES)
get_filename_component(_file_name ${_file} NAME)
file(RENAME ${_file} ${PROJECT_BINARY_DIR}/unknown_files/${_file_name})
endif()
file(APPEND ${PROJECT_BINARY_DIR}/missing_files_in_packages "${_file}
")
endforeach()
if(AUTO_MOVE_UNKNOWN_FILES)
message(SEND_ERROR "The files where moved in the followinf folder ${PROJECT_BINARY_DIR}/unknown_files\n
Please register them in the good package or clean the sources")
else()
message(SEND_ERROR "Please register them in the good package or clean the sources")
endif()
endif()
endfunction()
# ------------------------------------------------------------------------------
diff --git a/cmake/Modules/FindMumps.cmake b/cmake/Modules/FindMumps.cmake
index 01b22a4d9..0db04fb96 100644
--- a/cmake/Modules/FindMumps.cmake
+++ b/cmake/Modules/FindMumps.cmake
@@ -1,289 +1,315 @@
#===============================================================================
# @file FindMumps.cmake
#
# @author Nicolas Richart <nicolas.richart@epfl.ch>
#
# @date creation: Fri Oct 24 2014
# @date last modification: Wed Jan 13 2016
#
# @brief The find_package file for the Mumps solver
#
# @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 <http://www.gnu.org/licenses/>.
#
#===============================================================================
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)
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
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
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()
function(mumps_add_dependency _pdep _libs)
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
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
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_library(MUMPS_LIBRARY_MPISEQ mpiseq${MUMPS_PREFIX}
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)
else()
find_package(MPI REQUIRED C Fortran QUIET)
set(${_libs} ${MPI_C_LIBRARIES} ${MPI_Fortran_LIBRARIES} PARENT_SCOPE)
endif()
+ elseif(_pdep MATCHES "Threads")
+ find_package(Threads REQUIRED)
+ set(${_libs} Threads::Threads PARENT_SCOPE)
+ elseif(_pdep MATCHES "OpenMP")
+ find_package(OpenMP REQUIRED)
+ set(${_libs} OpenMP::OpenMP_C PARENT_SCOPE)
+ elseif(_pdep MATCHES "Math")
+ set(${_libs} m 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 ${MUMPS_LIBRARIES_ALL})
+ 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_BLAS ${_first_precision}gemm)
set(_mumps_dep_symbol_ScaLAPACK numroc)
+ set(_mumps_dep_symbol_LAPACK ilaenv)
set(_mumps_dep_symbol_MPI mpi_send)
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)
+ # TODO find missing symbols for IOMP
+ set(_mumps_dep_symbol_Math lround)
set(_mumps_dep_symbol_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)
set(_mumps_dep_comp_Scotch_ptscotch COMPONENTS ptscotch)
set(_mumps_dep_comp_Scotch_esmumps COMPONENTS esmumps)
- set(_mumps_potential_dependencies mumps_common pord BLAS ScaLAPACK MPI
- Scotch Scotch_ptscotch Scotch_esmumps METIS ParMETIS)
+ set(_mumps_potential_dependencies
+ mumps_common pord
+ BLAS LAPACK ScaLAPACK
+ MPI
+ Scotch Scotch_ptscotch Scotch_esmumps
+ METIS ParMETIS
+ Threads OpenMP
+ 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
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"
+ 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}
RUN_OUTPUT_VARIABLE _run
COMPILE_OUTPUT_VARIABLE _out)
set(_retry_compile FALSE)
#message("COMPILATION outputs: \n${_out} \n RUN OUTPUT \n${_run}")
if(_mumps_compiles AND NOT (_mumps_run STREQUAL "FAILED_TO_RUN"))
break()
endif()
foreach(_pdep ${_mumps_potential_dependencies})
#message("CHECKING ${_pdep}")
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}")
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}")
+ #message("NEED RUN ${_pdep}")
endif()
if(_add_pdep)
mumps_add_dependency(${_pdep} _libs ${_mumps_dep_comp_${_pdep}})
#message("ADDING ${_libs}")
if(NOT _libs)
message(FATAL_ERROR "MUMPS depends on ${_pdep} but no libraries where found")
endif()
list(APPEND _libraries_all ${_libs})
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}")
endif()
if(APPLE)
# in doubt add some stuff because mumps was perhaps badly compiled
mumps_add_dependency(pord _libs)
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()
diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt
index 044a41d8c..59700fa1e 100644
--- a/doc/CMakeLists.txt
+++ b/doc/CMakeLists.txt
@@ -1,92 +1,93 @@
#===============================================================================
# @file CMakeLists.txt
#
# @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
# @author Nicolas Richart <nicolas.richart@epfl.ch>
#
# @date creation: Fri Sep 03 2010
# @date last modification: Fri Jan 30 2015
#
# @brief Build the documentation
#
# @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 <http://www.gnu.org/licenses/>.
#
#===============================================================================
-if(AKANTU_DOCUMENTATION_DEVELOPER_MANUAL)
+if(AKANTU_DOCUMENTATION)
add_subdirectory(dev-doc)
endif()
file(GLOB_RECURSE __all_files "*.*")
set(_all_files)
foreach(_file ${__all_files})
if("${_file}" MATCHES "${PROJECT_SOURCE_DIR}/doc/manual")
file(RELATIVE_PATH __file ${PROJECT_SOURCE_DIR} ${_file})
list(APPEND _all_files ${__file})
endif()
endforeach()
set(AKANTU_MANUAL_FILES)
set(_akantu_manual_not_found_files)
foreach(_pkg ${${_project}_PACKAGE_SYSTEM_PACKAGES_ON})
string(TOUPPER "${_pkg}" _pkg)
foreach(_f ${AKANTU_${_pkg}_MANUAL_FILES})
#check if some file are registered but not present
list(FIND _all_files doc/manual/${_f} _ret)
if(_ret EQUAL -1)
list(APPEND _akantu_manual_not_found_files "${_pkg}: doc/manual/${_f} ")
else()
list(APPEND AKANTU_MANUAL_FILES doc/manual/${_f})
endif()
endforeach()
endforeach()
if(_akantu_manual_not_found_files)
message("")
message("******************************************************")
message("There are files registered in packages but not present")
message("******************************************************")
foreach(_file ${_akantu_manual_not_found_files})
message(${_file})
endforeach()
message("******************************************************")
message(FATAL_ERROR "abort")
endif()
################################################################
#construct the list of files to exclude because not registered
################################################################
set(_akantu_doc_exclude_files)
foreach(_file ${_all_files})
list(FIND AKANTU_MANUAL_FILES ${_file} _ret)
if(_ret EQUAL -1)
list(APPEND _akantu_doc_exclude_files /${_file})
endif()
endforeach()
list(REMOVE_ITEM _akantu_doc_exclude_files /doc/manual/CMakeLists.txt)
-set(AKANTU_DOC_EXCLUDE_FILES ${_akantu_doc_exclude_files} CACHE INTERNAL "Documentation files to excluse from Akantu Package" FORCE)
+set(AKANTU_DOC_EXCLUDE_FILES ${_akantu_doc_exclude_files}
+ CACHE INTERNAL "Documentation files to excluse from Akantu Package" FORCE)
if (AKANTU_DOCUMENTATION_MANUAL)
add_subdirectory(manual)
endif()
diff --git a/doc/dev-doc/CMakeLists.txt b/doc/dev-doc/CMakeLists.txt
index 35f27a4ea..18b4160ba 100644
--- a/doc/dev-doc/CMakeLists.txt
+++ b/doc/dev-doc/CMakeLists.txt
@@ -1,196 +1,199 @@
set(DOXYGEN_INPUT_DOX ${CMAKE_CURRENT_BINARY_DIR}/akantu.dox)
set(DOXYGEN_XML_DIR ${CMAKE_CURRENT_BINARY_DIR}/xml)
set(DOXYGEN_OUTPUT ${DOXYGEN_XML_DIR}/index.xml)
# configured documentation tools and intermediate build results
set(BINARY_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/_build")
# Sphinx cache with pickled ReST documents
set(SPHINX_CACHE_DIR "${CMAKE_CURRENT_BINARY_DIR}/_doctrees")
# HTML output directory
set(SPHINX_HTML_DIR "${CMAKE_CURRENT_BINARY_DIR}/html")
set(SPHINX_OUTPUT "${SPHINX_HTML_DIR}/index.html")
+set(SPHINX_INPUT "${CMAKE_CURRENT_BINARY_DIR}/conf.py")
# ---------------------------------------------------------------------------- #
# Doxygen #
# ---------------------------------------------------------------------------- #
find_package(Doxygen REQUIRED)
set(DOXYGEN_WARNINGS NO)
set(DOXYGEN_QUIET YES)
if(CMAKE_VERBOSE_MAKEFILE)
set(DOXYGEN_WARNINGS YES)
set(DOXYGEN_QUIET NO)
endif(CMAKE_VERBOSE_MAKEFILE)
package_get_all_source_files(
AKANTU_LIBRARY_SRCS
AKANTU_LIBRARY_PUBLIC_HDRS
AKANTU_LIBRARY_PRIVATE_HDRS
)
package_get_all_include_directories(
_akantu_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
)
list(APPEND _akantu_include_dirs
${AKANTU_PRIVATE_EXTERNAL_INCLUDE_DIR}
${AKANTU_INTERFACE_EXTERNAL_INCLUDE_DIR}
${PROJECT_BINARY_DIR}/src)
file(STRINGS ${PROJECT_SOURCE_DIR}/.clang-format AKANTU_TAB_SIZE
REGEX "^TabWidth: *([0-9]*)"
)
string(REGEX REPLACE ".*([0-9]+)" "\\1" AKANTU_TAB_SIZE "${AKANTU_TAB_SIZE}")
if (CMAKE_VERSION VERSION_GREATER 3.9.5)
#set(DOXYGEN_WARNINGS YES)
#set(DOXYGEN_QUIET NO)
set(DOXYGEN_STRIP_FROM_PATH ${PROJECT_SOURCE_DIR})
set(DOXYGEN_STRIP_FROM_INC_PATH ${PROJECT_SOURCE_DIR})
set(DOXYGEN_TAB_SIZE ${AKANTU_TAB_SIZE})
set(DOXYGEN_ALIASES
"rst=\\verbatim embed:rst"
"endrst=\\endverbatim"
)
set(DOXYGEN_WARN_IF_UNDOCUMENTED NO)
set(DOXYGEN_WARN_IF_DOC_ERROR NO)
set(DOXYGEN_WARN_AS_ERROR NO)
set(DOXYGEN_EXCLUDE "${PROJECT_SOURCE_DIR}/src/common/aka_fwd.hh")
set(DOXYGEN_RECURSIVE YES)
set(DOXYGEN_EXCLUDE
"aka_named_argument.hh"
)
set(DOXYGEN_EXAMPLE_PATH "${PROJECT_SOURCE_DIR}/examples")
set(DOXYGEN_EXAMPLE_RECURSIVE YES)
set(DOXYGEN_SOURCE_BROWSER NO)
set(DOXYGEN_CLANG_ASSISTED_PARSING NO)
#set(DOXYGEN_CLANG_OPTIONS )
set(DOXYGEN_CLANG_DATABASE_PATH ${CMAKE_BINARY_DIR})
set(DOXYGEN_USE_MATHJAX YES)
set(DOXYGEN_GENERATE_HTML NO)
set(DOXYGEN_GENERATE_HTMLHELP NO)
set(DOXYGEN_GENERATE_LATEX NO)
set(DOXYGEN_GENERATE_XML YES)
set(DOXYGEN_XML_OUTPUT xml)
set(DOXYGEN_ENABLE_PREPROCESSING YES)
set(DOXYGEN_MACRO_EXPANSION YES)
set(DOXYGEN_INCLUDE_PATH ${_akantu_include_dirs})
-# set(DOXYGEN_PREDEFINED
-# ${AKANTU_DEFINITIONS}
-# "DOXYGEN"
+ set(DOXYGEN_PREDEFINED
+ ${AKANTU_DEFINITIONS}
+ "DOXYGEN"
# "AKANTU_TO_IMPLEMENT()="
# "DECLARE_NAMED_ARGUMENT()="
# "OPTIONAL_NAMED_ARGUMENT(n, v)=v"
# "REQUIRED_NAMED_ARGUMENT(n)="
-# )
+ )
set(DOXYGEN_COLLABORATION_GRAPH NO)
set(DOXYGEN_UML_LOOK YES)
set(DOXYGEN_TEMPLATE_RELATIONS YES)
set(DOXYGEN_CALL_GRAPH YES)
set(DOXYGEN_CALLER_GRAPH YES)
set(DOXYGEN_DOT_GRAPH_MAX_NODES 500)
set(DOXYGEN_SHOW_FILES NO)
set(DOXYGEN_LOOKUP_CACHE_SIZE 9)
set(_SRCS
+ ${PROJECT_BINARY_DIR}/src/aka_config.hh
+ ${PROJECT_BINARY_DIR}/src/aka_element_classes_info.hh
${AKANTU_LIBRARY_SRCS}
${AKANTU_LIBRARY_PUBLIC_HDRS}
${AKANTU_LIBRARY_PRIVATE_HDRS}
)
list(REMOVE_ITEM _SRCS
"${PROJECT_SOURCE_DIR}/src/common/aka_named_argument.hh")
doxygen_add_docs(doxygen-doc
${_SRCS}
USE_STAMP_FILE
COMMENT "Building XML documentation with Doxygen in ${DOXYGEN_XML_DIR}"
)
else()
string(REGEX REPLACE ";" " " AKANTU_DOXYGEN_DEFINTIONS "${AKANTU_DEFINITIONS};DOXYGEN")
string(REGEX REPLACE ";" " " AKANTU_DOXYGEN_INCLUDE_DIRS "${_akantu_include_dirs}")
make_directory(${DOXYGEN_XML_DIR})
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/akantu.dox.in
${DOXYGEN_INPUT_DOX}
)
add_custom_command(
OUTPUT ${DOXYGEN_OUTPUT}
COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_INPUT_DOX}
DEPENDS ${DOXYGEN_INPUT_DOX}
COMMENT "Building XML documentation with Doxygen in ${DOXYGEN_XML_DIR}"
)
add_custom_target(doxygen-doc ALL
DEPENDS ${DOXYGEN_OUTPUT}
)
add_custom_target(doxygen-doc-forced
COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_INPUT}
COMMENT "Building XML documentation with Doxygen (forced) in ${DOXYGEN_XML_DIR}"
)
endif()
# ---------------------------------------------------------------------------- #
# Sphinx #
# ---------------------------------------------------------------------------- #
find_package(Sphinx REQUIRED)
set(SPHINX_VERBOSE_FLAG "-q")
if(CMAKE_VERBOSE_MAKEFILE)
set(SPHINX_VERBOSE_FLAG)
endif(CMAKE_VERBOSE_MAKEFILE)
# set(AKANTU_IN_READTHEDOC TRUE)
# configure_file(
# "${CMAKE_CURRENT_SOURCE_DIR}/conf.py.in"
# "${CMAKE_CURRENT_SOURCE_DIR}/conf_rtd.py"
# @ONLY)
set(AKANTU_IN_READTHEDOC FALSE)
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/conf.py.in"
- "${CMAKE_CURRENT_BINARY_DIR}/conf.py"
+ "${SPHINX_INPUT}"
@ONLY)
set(SPHINX_PARALLEL_FLAG)
if (SPHINX_VERSION VERSION_GREATER_EQUAL 1.7.0)
set(SPHINX_PARALLEL_FLAG -j auto)
endif()
set(_sphinx_command ${SPHINX_BUILD_EXECUTABLE}
${SPHINX_PARALLEL_FLAG}
${SPHINX_VERBOSE_FLAG} -b html
-c "${CMAKE_CURRENT_BINARY_DIR}"
-d "${SPHINX_CACHE_DIR}"
"${CMAKE_CURRENT_SOURCE_DIR}"
"${SPHINX_HTML_DIR}"
)
file(GLOB_RECURSE _SPHINX_SRCS
"*.rst")
add_custom_command(
OUTPUT ${SPHINX_OUTPUT}
COMMAND ${_sphinx_command}
DEPENDS doxygen-doc ${SPHINX_INPUT} ${_SPHINX_SRCS}
COMMENT "Building HTML documentation with Sphinx in ${SPHINX_HTML_DIR}"
)
add_custom_target(sphinx-doc ALL
DEPENDS ${SPHINX_OUTPUT})
add_custom_target(sphinx-doc-forced
COMMAND ${_sphinx_command}
DEPENDS doxygen-doc
COMMENT "Building HTML documentation with Sphinx (forced) in ${SPHINX_HTML_DIR}"
)
diff --git a/doc/dev-doc/_static/logo_akantu.svg b/doc/dev-doc/_static/logo_akantu.svg
new file mode 100644
index 000000000..3d5d0cc98
--- /dev/null
+++ b/doc/dev-doc/_static/logo_akantu.svg
@@ -0,0 +1,1742 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="471.63986pt"
+ height="125.81169pt"
+ viewBox="0 0 471.63986 125.81169"
+ version="1.2"
+ id="svg1041"
+ sodipodi:docname="logo_akantu.svg"
+ inkscape:version="1.0.2 (e86c870879, 2021-01-15)">
+ <metadata
+ id="metadata1045">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1920"
+ inkscape:window-height="1053"
+ id="namedview1043"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:zoom="0.46161158"
+ inkscape:cx="299.46663"
+ inkscape:cy="772.35636"
+ inkscape:window-x="0"
+ inkscape:window-y="-9"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="surface741" />
+ <defs
+ id="defs562">
+ <g
+ id="g110">
+ <symbol
+ overflow="visible"
+ id="glyph0-0">
+ <path
+ style="stroke:none"
+ d=""
+ id="path2" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-1">
+ <path
+ style="stroke:none"
+ d="m 51.046875,-2.265625 c 0,0 0,-0.921875 -1.015625,-2.25 L 32.15625,-27.9375 47.96875,-42.625 c 1.234375,-1.140625 1.546875,-1.4375 1.546875,-2.265625 0,-2.15625 -2.0625,-2.15625 -3.5,-2.15625 H 40.0625 c -1.84375,0 -3.796875,0 -6.171875,2.15625 L 19.109375,-31.125 v -35.234375 c 0,-3.28125 -0.71875,-4.921875 -4.9375,-4.921875 h -2.15625 c -3.703125,0 -4.921875,1.125 -4.921875,4.921875 V -4.9375 c 0,3.703125 1.125,4.9375 4.921875,4.9375 h 1.84375 c 3.90625,0 4.9375,-1.328125 4.9375,-4.9375 v -10.671875 c 1.640625,-1.4375 3.1875,-2.984375 4.828125,-4.421875 L 36.984375,-2.5625 C 38.71875,-0.3125 39.4375,0 42.421875,0 h 4.9375 c 1.4375,0 3.6875,0 3.6875,-2.265625 z m 0,0"
+ id="path5" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-2">
+ <path
+ style="stroke:none"
+ d="m 48.6875,-4.9375 v -27.515625 c 0,-16.234375 -15.609375,-16.34375 -20.75,-16.34375 -4.828125,0 -9.96875,0.421875 -16.84375,3.296875 -2.15625,0.921875 -2.671875,1.234375 -2.671875,2.359375 0,0.71875 0.625,6.671875 0.71875,7.5 0.109375,0.609375 0.625,1.125 1.34375,1.125 0.5,0 0.8125,-0.3125 1.125,-0.609375 4.421875,-4.625 9.546875,-6.890625 15.921875,-6.890625 5.546875,0 7.1875,3.390625 7.1875,9.453125 v 3.59375 c -3.59375,0 -30.5,0.203125 -30.5,15.3125 0,7.1875 5.75,14.78125 14.78125,14.78125 3.5,0 11.203125,-1.015625 16.03125,-8.109375 V -4.9375 C 35.03125,-1.640625 35.75,0 39.953125,0 h 3.8125 C 47.65625,0 48.6875,-1.328125 48.6875,-4.9375 Z m -13.96875,-10.875 c 0,9.546875 -9.765625,9.546875 -10.0625,9.546875 -4.3125,0 -7.09375,-3.703125 -7.09375,-7.59375 0,-10.078125 14.28125,-10.796875 17.15625,-10.890625 z m 0,0"
+ id="path8" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-3">
+ <path
+ style="stroke:none"
+ d="m 51.359375,-4.9375 v -28.453125 c 0,-10.578125 -4.515625,-14.78125 -15,-14.78125 -10.78125,0 -15.40625,7.796875 -16.734375,10.578125 h -0.109375 v -5.140625 c 0,-3.28125 -0.71875,-4.921875 -4.921875,-4.921875 h -3.5 c -3.703125,0 -4.9375,1.125 -4.9375,4.921875 V -4.9375 C 6.15625,-1.234375 7.296875,0 11.09375,0 h 4.109375 c 3.90625,0 4.9375,-1.328125 4.9375,-4.9375 V -27.625 c 0,-8.9375 5.953125,-13.15625 11.296875,-13.15625 4.921875,0 5.953125,2.359375 5.953125,7.609375 V -4.9375 c 0,3.296875 0.71875,4.9375 4.921875,4.9375 h 4.109375 c 3.90625,0 4.9375,-1.328125 4.9375,-4.9375 z m 0,0"
+ id="path11" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-4">
+ <path
+ style="stroke:none"
+ d="m 38.3125,-4.828125 c 0,-0.3125 -0.203125,-1.03125 -0.8125,-3.1875 -0.515625,-2.046875 -0.734375,-2.671875 -1.65625,-2.671875 -0.515625,0 -0.609375,0.109375 -1.234375,0.71875 -1.125,0.828125 -3.890625,3.09375 -8.203125,3.09375 -2.46875,0 -4.015625,-1.75 -4.015625,-8.53125 v -24.25 h 9.140625 c 1.234375,0 4.9375,0 4.9375,-3.6875 0,-3.703125 -3.703125,-3.703125 -4.9375,-3.703125 h -9.140625 v -8.53125 c 0,-3.28125 -0.71875,-4.921875 -4.921875,-4.921875 h -3.1875 c -3.703125,0 -4.9375,1.125 -4.9375,4.921875 v 8.53125 H 6.984375 c -1.125,0 -4.9375,0 -4.9375,3.703125 0,3.6875 3.703125,3.6875 4.9375,3.6875 h 2.0625 v 26 c 0,10.375 3.6875,14.78125 10.78125,14.78125 0.921875,0 5.4375,0 10.78125,-1.640625 1.75,-0.515625 7.703125,-2.359375 7.703125,-4.3125 z m 0,0"
+ id="path14" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-5">
+ <path
+ style="stroke:none"
+ d="m 51.359375,-4.9375 v -37.171875 c 0,-3.296875 -0.71875,-4.9375 -4.9375,-4.9375 H 42.3125 c -3.890625,0 -4.921875,1.34375 -4.921875,4.9375 v 24.34375 c 0,6.78125 -3.59375,12.625 -10.890625,12.625 -5.75,0 -6.359375,-1.4375 -6.359375,-6.875 v -30.09375 c 0,-3.296875 -0.734375,-4.9375 -4.9375,-4.9375 H 11.09375 c -3.703125,0 -4.9375,1.125 -4.9375,4.9375 V -12.9375 c 0,10.984375 5.96875,14.0625 16.640625,14.0625 2.78125,0 10.078125,0 14.90625,-8.71875 V -4.9375 C 37.703125,-1.640625 38.421875,0 42.625,0 h 3.796875 c 3.90625,0 4.9375,-1.328125 4.9375,-4.9375 z m 0,0"
+ id="path17" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-6">
+ <path
+ style="stroke:none"
+ d="m 70.875,-2.765625 c 0,-0.515625 0,-0.734375 -0.515625,-2.0625 L 48.6875,-66.875 c -1.546875,-4.40625 -4.515625,-4.40625 -6.578125,-4.40625 h -9.03125 c -2.0625,0 -5.03125,0 -6.578125,4.40625 L 4.828125,-4.828125 C 4.3125,-3.5 4.3125,-3.28125 4.3125,-2.765625 4.3125,0 6.875,0 8.3125,0 h 3.703125 c 1.84375,0 5.03125,0 6.46875,-4.109375 L 22.5,-15.515625 H 50.953125 L 54.4375,-5.65625 C 55.671875,-2.046875 56.390625,0 61.328125,0 H 66.875 c 1.4375,0 4,0 4,-2.765625 z M 47.859375,-24.34375 H 25.578125 L 32.96875,-46.125 c 1.546875,-4.515625 2.875,-8.421875 3.703125,-12.109375 h 0.09375 c 0.515625,2.453125 0.515625,2.65625 1.34375,5.03125 z m 0,0"
+ id="path20" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-7">
+ <path
+ style="stroke:none"
+ d="m 54.859375,-4.9375 v -0.921875 c 0,-2.359375 -0.109375,-4.921875 -4.828125,-4.921875 L 24.453125,-10.375 v -55.984375 c 0,-3.28125 -0.71875,-4.921875 -4.9375,-4.921875 H 14.28125 c -3.703125,0 -4.9375,1.125 -4.9375,4.921875 V -4.9375 c 0,3.703125 1.140625,4.9375 4.9375,4.9375 h 35.640625 c 3.90625,0 4.9375,-1.328125 4.9375,-4.9375 z m 0,0"
+ id="path23" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-8">
+ <path
+ style="stroke:none"
+ d="m 19.921875,-4.9375 v -37.171875 c 0,-3.296875 -0.71875,-4.9375 -4.921875,-4.9375 h -3.796875 c -3.703125,0 -4.9375,1.125 -4.9375,4.9375 V -4.9375 c 0,3.703125 1.125,4.9375 4.9375,4.9375 H 15 c 3.90625,0 4.921875,-1.328125 4.921875,-4.9375 z m 0.71875,-57.3125 v -1.953125 c 0,-3.28125 -0.71875,-4.921875 -4.921875,-4.921875 h -5.234375 c -3.90625,0 -4.9375,1.328125 -4.9375,4.921875 V -62.25 c 0,2.984375 0.515625,4.9375 4.9375,4.9375 h 5.234375 c 4.3125,0 4.921875,-1.859375 4.921875,-4.9375 z m 0,0"
+ id="path26" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-9">
+ <path
+ style="stroke:none"
+ d="m 53.71875,-23.734375 c 0,-4.921875 0,-24.4375 -19.3125,-24.4375 -8.421875,0 -13.546875,4.71875 -14.484375,5.75 v -23.9375 c 0,-3.28125 -0.71875,-4.921875 -4.921875,-4.921875 h -3.796875 c -3.703125,0 -4.9375,1.125 -4.9375,4.921875 V -4.9375 c 0,3.703125 1.125,4.9375 4.9375,4.9375 H 15.3125 c 1.734375,0 4.828125,0 4.921875,-4 4.71875,5.125 9.96875,5.125 11.609375,5.125 21.875,0 21.875,-19.71875 21.875,-24.859375 z M 39.75,-23.625 c 0,7.59375 0,17.359375 -11.5,17.359375 -4.3125,0 -6.890625,-2.5625 -8.015625,-4 V -37.1875 c 1.34375,-1.234375 4.421875,-3.59375 9.140625,-3.59375 10.375,0 10.375,9.453125 10.375,17.15625 z m 0,0"
+ id="path29" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-10">
+ <path
+ style="stroke:none"
+ d="m 91.015625,-4.9375 v -61.421875 c 0,-3.28125 -0.71875,-4.921875 -4.9375,-4.921875 h -8.625 c -2.578125,0 -5.140625,0.203125 -6.890625,4.40625 l -13.546875,33.28125 c -2.15625,5.25 -5.96875,14.6875 -6.78125,18.28125 H 50.125 C 49.203125,-19 44.984375,-29.171875 42.828125,-34.609375 L 29.6875,-67.078125 c -1.75,-4.203125 -5.03125,-4.203125 -6.890625,-4.203125 H 14.28125 c -3.703125,0 -4.9375,1.125 -4.9375,4.921875 V -4.9375 c 0,3.703125 1.140625,4.9375 4.9375,4.9375 h 2.765625 c 3.90625,0 4.9375,-1.328125 4.9375,-4.9375 v -53.09375 h 0.09375 c 1.140625,4.625 5.765625,16.015625 8.21875,22.078125 l 11.921875,29.28125 c 1.84375,4.40625 4.3125,4.40625 7.8125,4.40625 3.484375,0 5.953125,0 7.796875,-4.40625 L 70.984375,-38.9375 C 72,-41.390625 77.546875,-55.359375 78.265625,-58.03125 H 78.375 V -4.9375 C 78.375,-1.640625 79.09375,0 83.3125,0 h 2.765625 c 3.90625,0 4.9375,-1.328125 4.9375,-4.9375 z m 0,0"
+ id="path32" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-11">
+ <path
+ style="stroke:none"
+ d="m 19.921875,-4.9375 v -61.421875 c 0,-3.28125 -0.71875,-4.921875 -4.921875,-4.921875 h -3.796875 c -3.703125,0 -4.9375,1.125 -4.9375,4.921875 V -4.9375 c 0,3.703125 1.125,4.9375 4.9375,4.9375 H 15 c 3.90625,0 4.921875,-1.328125 4.921875,-4.9375 z m 0,0"
+ id="path35" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-12">
+ <path
+ style="stroke:none"
+ d="m 56.390625,-20.546875 c 0,-11.5 -8.53125,-20.125 -18.796875,-22.5 l -9.859375,-2.25 c -2.265625,-0.515625 -8.625,-2.875 -8.625,-8.734375 0,-8.734375 8.734375,-8.9375 12.21875,-8.9375 5.75,0 10.890625,1.34375 15.921875,6.265625 1.640625,1.546875 1.75,1.640625 2.15625,1.640625 0.625,0 1.125,-0.296875 1.4375,-1.84375 l 1.546875,-8.9375 c 0.203125,-0.828125 0.203125,-1.03125 0.203125,-1.328125 0,-0.828125 -0.109375,-0.9375 -2.671875,-2.265625 -7.296875,-3.59375 -13.359375,-4.109375 -18.59375,-4.109375 -9.140625,0 -25.0625,2.15625 -25.0625,21.359375 0,7.296875 3.796875,12.03125 5.75,14.078125 5.234375,5.34375 10.0625,6.375 18.078125,8.21875 5.75,1.328125 8.015625,1.84375 10.28125,3.90625 1.015625,1.03125 3.171875,3.1875 3.171875,7.078125 0,9.65625 -8.828125,9.96875 -12.21875,9.96875 -8.625,0 -16.125,-3.484375 -21.265625,-8.109375 -1.234375,-1.234375 -1.4375,-1.234375 -1.84375,-1.234375 -0.625,0 -1.125,0.3125 -1.4375,1.84375 L 5.234375,-7.5 C 5.03125,-6.671875 5.03125,-6.46875 5.03125,-6.15625 c 0,2.15625 10.6875,5.84375 11.203125,6.046875 6.875,2.15625 12.421875,2.375 15.09375,2.375 15.40625,0 25.0625,-6.890625 25.0625,-22.8125 z m 0,0"
+ id="path38" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-13">
+ <path
+ style="stroke:none"
+ d="m 46.9375,-5.140625 c 0,-1.125 -0.40625,-5.84375 -0.609375,-7.1875 0,-0.3125 -0.203125,-1.328125 -1.234375,-1.328125 -0.40625,0 -0.609375,0 -1.640625,1.015625 -1.75,1.546875 -6.578125,5.765625 -15,5.765625 -10.6875,0 -10.6875,-10.375 -10.6875,-16.953125 0,-8.328125 0.515625,-16.953125 11,-16.953125 6.671875,0 9.453125,1.640625 13.140625,4.734375 1.234375,1.125 1.4375,1.125 1.859375,1.125 1.015625,0 1.21875,-0.921875 1.328125,-1.34375 0.203125,-0.8125 1.234375,-6.5625 1.234375,-7.078125 0,-0.921875 -0.71875,-1.34375 -2.46875,-2.15625 -4.921875,-2.265625 -8.015625,-3.296875 -15.203125,-3.296875 -15.296875,0 -24.859375,7.203125 -24.859375,25.171875 0,17.046875 8.84375,24.75 24.34375,24.75 2.78125,0 8.9375,-0.09375 15.828125,-3.59375 C 46.84375,-4 46.9375,-4.109375 46.9375,-5.140625 Z m 0,0"
+ id="path41" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-14">
+ <path
+ style="stroke:none"
+ d="m 49.203125,-26.703125 c 0,-13.25 -6.15625,-22.09375 -21.46875,-22.09375 -16.4375,0 -24.546875,9.25 -24.546875,24.859375 0,16.84375 9.75,25.0625 25.875,25.0625 3.90625,0 9.765625,-0.515625 16.546875,-3.890625 2.25,-1.140625 2.984375,-1.453125 2.984375,-2.671875 0,-0.71875 -0.3125,-3.703125 -0.421875,-4.53125 -0.40625,-3.171875 -0.515625,-3.28125 -1.4375,-3.28125 -0.40625,0 -0.609375,0 -1.75,1.03125 -6.046875,5.234375 -12.21875,5.953125 -15.5,5.953125 -11.609375,0 -12.84375,-9.140625 -13.15625,-15.71875 h 27.9375 c 2.375,0 4.9375,-0.09375 4.9375,-4.71875 z m -11.40625,-0.71875 H 16.4375 c 0.3125,-6.265625 2.359375,-13.96875 11.296875,-13.96875 8.21875,0 9.765625,5.953125 10.0625,13.96875 z m 0,0"
+ id="path44" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-0">
+ <path
+ style="stroke:none"
+ d=""
+ id="path47" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-1">
+ <path
+ style="stroke:none"
+ d="m 70.875,-2.765625 c 0,-0.515625 0,-0.734375 -0.515625,-2.0625 L 48.6875,-66.875 c -1.546875,-4.40625 -4.515625,-4.40625 -6.578125,-4.40625 h -9.03125 c -2.0625,0 -5.03125,0 -6.578125,4.40625 L 4.828125,-4.828125 C 4.3125,-3.5 4.3125,-3.28125 4.3125,-2.765625 4.3125,0 6.875,0 8.3125,0 h 3.703125 c 1.84375,0 5.03125,0 6.46875,-4.109375 L 22.5,-15.515625 H 50.953125 L 54.4375,-5.65625 C 55.671875,-2.046875 56.390625,0 61.328125,0 H 66.875 c 1.4375,0 4,0 4,-2.765625 z M 47.859375,-24.34375 H 25.578125 L 32.96875,-46.125 c 1.546875,-4.515625 2.875,-8.421875 3.703125,-12.109375 h 0.09375 c 0.515625,2.453125 0.515625,2.65625 1.34375,5.03125 z m 0,0"
+ id="path50" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-2">
+ <path
+ style="stroke:none"
+ d="m 54.859375,-4.9375 v -0.921875 c 0,-2.359375 -0.109375,-4.921875 -4.828125,-4.921875 L 24.453125,-10.375 v -55.984375 c 0,-3.28125 -0.71875,-4.921875 -4.9375,-4.921875 H 14.28125 c -3.703125,0 -4.9375,1.125 -4.9375,4.921875 V -4.9375 c 0,3.703125 1.140625,4.9375 4.9375,4.9375 h 35.640625 c 3.90625,0 4.9375,-1.328125 4.9375,-4.9375 z m 0,0"
+ id="path53" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-0">
+ <path
+ style="stroke:none"
+ d=""
+ id="path56" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-1">
+ <path
+ style="stroke:none"
+ d="m 5.515625,-0.21875 c 0,-0.03125 0,-0.046875 -0.03125,-0.15625 l -1.6875,-4.828125 C 3.671875,-5.546875 3.4375,-5.546875 3.28125,-5.546875 H 2.578125 c -0.15625,0 -0.390625,0 -0.515625,0.34375 L 0.375,-0.375 C 0.34375,-0.265625 0.34375,-0.25 0.34375,-0.21875 0.34375,0 0.53125,0 0.640625,0 H 0.9375 c 0.140625,0 0.390625,0 0.5,-0.3125 L 1.75,-1.203125 H 3.96875 L 4.234375,-0.4375 C 4.34375,-0.15625 4.390625,0 4.78125,0 h 0.421875 c 0.109375,0 0.3125,0 0.3125,-0.21875 z m -1.78125,-1.671875 h -1.75 L 2.5625,-3.59375 C 2.6875,-3.9375 2.796875,-4.25 2.859375,-4.53125 2.90625,-4.34375 2.90625,-4.328125 2.96875,-4.140625 Z m 0,0"
+ id="path59" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-2">
+ <path
+ style="stroke:none"
+ d="m 5.609375,-0.234375 -2.234375,-3 2.09375,-1.984375 C 5.5625,-5.296875 5.5625,-5.359375 5.5625,-5.390625 c 0,-0.15625 -0.15625,-0.15625 -0.265625,-0.15625 H 4.8125 c -0.140625,0 -0.296875,0 -0.46875,0.15625 l -2.578125,2.4375 v -2.21875 c 0,-0.25 -0.046875,-0.375 -0.375,-0.375 h -0.28125 c -0.28125,0 -0.375,0.078125 -0.375,0.375 v 4.78125 C 0.734375,-0.09375 0.8125,0 1.109375,0 h 0.28125 c 0.296875,0 0.375,-0.109375 0.375,-0.390625 v -1.34375 L 2.65625,-2.5625 4.453125,-0.171875 C 4.578125,0 4.703125,0 4.84375,0 H 5.328125 C 5.5,0 5.609375,0 5.609375,-0.234375 Z m 0,0"
+ id="path62" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-3">
+ <path
+ style="stroke:none"
+ d="m 5.609375,-0.390625 v -4.78125 c 0,-0.25 -0.046875,-0.375 -0.375,-0.375 h -0.21875 c -0.296875,0 -0.390625,0.09375 -0.390625,0.375 V -0.96875 L 2.421875,-5.234375 c -0.171875,-0.3125 -0.40625,-0.3125 -0.5625,-0.3125 h -0.75 c -0.28125,0 -0.375,0.078125 -0.375,0.375 v 4.78125 C 0.734375,-0.09375 0.8125,0 1.109375,0 h 0.21875 C 1.625,0 1.71875,-0.109375 1.71875,-0.390625 v -4.1875 L 3.921875,-0.3125 C 4.09375,0 4.328125,0 4.484375,0 h 0.75 c 0.296875,0 0.375,-0.109375 0.375,-0.390625 z m 0,0"
+ id="path65" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-4">
+ <path
+ style="stroke:none"
+ d="M 5.53125,-5.0625 V -5.125 c 0,-0.25 -0.046875,-0.375 -0.375,-0.375 H 0.703125 C 0.421875,-5.5 0.3125,-5.421875 0.3125,-5.125 v 0.0625 c 0,0.390625 0.203125,0.390625 0.421875,0.390625 l 1.609375,-0.03125 v 4.3125 C 2.34375,-0.09375 2.4375,0 2.734375,0 h 0.40625 c 0.296875,0 0.375,-0.109375 0.375,-0.390625 v -4.3125 L 5.125,-4.671875 c 0.21875,0 0.40625,0 0.40625,-0.390625 z m 0,0"
+ id="path68" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-5">
+ <path
+ style="stroke:none"
+ d="m 5.375,-1.828125 v -3.34375 c 0,-0.25 -0.0625,-0.375 -0.390625,-0.375 h -0.3125 c -0.296875,0 -0.390625,0.09375 -0.390625,0.375 V -1.8125 c 0,0.78125 -0.21875,1.296875 -1.203125,1.296875 -0.921875,0 -1.171875,-0.46875 -1.171875,-1.296875 v -3.359375 c 0,-0.25 -0.0625,-0.375 -0.390625,-0.375 h -0.40625 c -0.28125,0 -0.375,0.078125 -0.375,0.375 v 3.328125 c 0,1.6875 1.328125,2.015625 2.34375,2.015625 0.90625,0 2.296875,-0.265625 2.296875,-2 z m 0,0"
+ id="path71" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-6">
+ <path
+ style="stroke:none"
+ d="m 4.265625,-0.390625 v -0.0625 c 0,-0.1875 0,-0.390625 -0.375,-0.390625 L 1.90625,-0.8125 v -4.359375 c 0,-0.25 -0.0625,-0.375 -0.390625,-0.375 h -0.40625 c -0.28125,0 -0.375,0.078125 -0.375,0.375 v 4.78125 C 0.734375,-0.09375 0.8125,0 1.109375,0 h 2.78125 c 0.296875,0 0.375,-0.109375 0.375,-0.390625 z m 0,0"
+ id="path74" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-7">
+ <path
+ style="stroke:none"
+ d="m 1.90625,-0.390625 v -4.78125 c 0,-0.25 -0.046875,-0.375 -0.375,-0.375 H 1.125 c -0.296875,0 -0.390625,0.078125 -0.390625,0.375 v 4.78125 C 0.734375,-0.09375 0.828125,0 1.125,0 h 0.40625 c 0.296875,0 0.375,-0.109375 0.375,-0.390625 z m 0,0"
+ id="path77" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-8">
+ <path
+ style="stroke:none"
+ d="m 5.375,-1.546875 c 0,-1.140625 -1.125,-1.3125 -1.421875,-1.359375 0.75,-0.171875 1.171875,-0.578125 1.171875,-1.203125 0,-1.4375 -1.703125,-1.4375 -2.046875,-1.4375 h -1.96875 c -0.28125,0 -0.375,0.078125 -0.375,0.375 v 4.78125 C 0.734375,-0.09375 0.8125,0 1.109375,0 h 2.1875 C 3.890625,0 5.375,-0.109375 5.375,-1.546875 Z M 4.03125,-4.09375 c 0,0.921875 -1.046875,0.921875 -1.25,0.921875 H 1.859375 v -1.6875 h 0.9375 c 0.3125,0 1.234375,0.015625 1.234375,0.765625 z M 4.25,-1.5625 c 0,0.8125 -0.84375,0.875 -1.234375,0.875 h -1.15625 v -1.90625 h 1 c 0.28125,0 1.390625,0 1.390625,1.03125 z m 0,0"
+ id="path80" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-9">
+ <path
+ style="stroke:none"
+ d="m 7.09375,-0.390625 v -4.78125 c 0,-0.25 -0.0625,-0.375 -0.390625,-0.375 H 6.03125 c -0.203125,0 -0.40625,0.015625 -0.53125,0.34375 l -1.0625,2.59375 C 4.265625,-2.203125 3.96875,-1.46875 3.90625,-1.1875 3.828125,-1.484375 3.5,-2.265625 3.34375,-2.703125 L 2.3125,-5.21875 C 2.171875,-5.546875 1.921875,-5.546875 1.78125,-5.546875 H 1.109375 c -0.28125,0 -0.375,0.078125 -0.375,0.375 v 4.78125 C 0.734375,-0.09375 0.8125,0 1.109375,0 h 0.21875 C 1.625,0 1.71875,-0.109375 1.71875,-0.390625 v -4.125 c 0.09375,0.359375 0.453125,1.25 0.640625,1.71875 l 0.921875,2.28125 c 0.15625,0.34375 0.34375,0.34375 0.609375,0.34375 0.28125,0 0.46875,0 0.609375,-0.34375 L 5.53125,-3.03125 c 0.078125,-0.1875 0.515625,-1.28125 0.5625,-1.484375 h 0.015625 v 4.125 C 6.109375,-0.125 6.15625,0 6.484375,0 h 0.21875 c 0.3125,0 0.390625,-0.109375 0.390625,-0.390625 z m 0,0"
+ id="path83" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-10">
+ <path
+ style="stroke:none"
+ d="m 4.390625,-1.59375 c 0,-0.90625 -0.65625,-1.578125 -1.46875,-1.765625 L 2.15625,-3.53125 C 1.984375,-3.5625 1.484375,-3.75 1.484375,-4.203125 c 0,-0.6875 0.6875,-0.703125 0.953125,-0.703125 0.453125,0 0.84375,0.109375 1.25,0.484375 0.125,0.125 0.125,0.140625 0.15625,0.140625 0.046875,0 0.09375,-0.03125 0.109375,-0.15625 l 0.125,-0.6875 C 4.09375,-5.1875 4.09375,-5.203125 4.09375,-5.234375 c 0,-0.0625 0,-0.0625 -0.203125,-0.171875 C 3.3125,-5.6875 2.84375,-5.734375 2.4375,-5.734375 c -0.703125,0 -1.953125,0.171875 -1.953125,1.671875 0,0.5625 0.296875,0.9375 0.453125,1.09375 0.40625,0.421875 0.78125,0.5 1.40625,0.640625 0.453125,0.109375 0.625,0.140625 0.796875,0.296875 0.078125,0.09375 0.25,0.25 0.25,0.5625 0,0.75 -0.6875,0.765625 -0.953125,0.765625 -0.671875,0 -1.25,-0.265625 -1.65625,-0.625 -0.09375,-0.09375 -0.109375,-0.09375 -0.140625,-0.09375 -0.046875,0 -0.09375,0.015625 -0.109375,0.140625 l -0.125,0.703125 c -0.015625,0.0625 -0.015625,0.078125 -0.015625,0.09375 0,0.171875 0.828125,0.453125 0.875,0.46875 0.53125,0.171875 0.96875,0.1875 1.171875,0.1875 1.203125,0 1.953125,-0.53125 1.953125,-1.765625 z m 0,0"
+ id="path86" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-11">
+ <path
+ style="stroke:none"
+ d="m 5.171875,-0.46875 -0.0625,-0.6875 c 0,-0.046875 -0.046875,-0.078125 -0.09375,-0.078125 -0.03125,0 -0.046875,0 -0.078125,0.015625 C 4.734375,-1.0625 4.5,-0.890625 4.234375,-0.8125 3.96875,-0.71875 3.6875,-0.71875 3.421875,-0.71875 c -0.5,0 -1,-0.125 -1.3125,-0.5 -0.359375,-0.421875 -0.453125,-1 -0.453125,-1.5625 0,-0.5625 0.09375,-1.125 0.453125,-1.5625 0.3125,-0.375 0.8125,-0.5 1.3125,-0.5 0.234375,0 0.484375,0.03125 0.71875,0.109375 C 4.375,-4.640625 4.5625,-4.5 4.75,-4.34375 c 0.015625,0.03125 0.046875,0.03125 0.0625,0.03125 0.0625,0 0.09375,-0.046875 0.109375,-0.078125 L 5.078125,-5.25 c 0,-0.0625 -0.03125,-0.09375 -0.0625,-0.109375 C 4.75,-5.453125 4.484375,-5.53125 4.21875,-5.578125 3.953125,-5.625 3.6875,-5.640625 3.421875,-5.640625 c -0.8125,0 -1.625,0.15625 -2.203125,0.71875 C 0.65625,-4.375 0.484375,-3.5625 0.484375,-2.78125 c 0,0.796875 0.171875,1.59375 0.734375,2.15625 0.578125,0.546875 1.390625,0.71875 2.203125,0.71875 0.296875,0 0.59375,-0.015625 0.875,-0.09375 0.296875,-0.078125 0.5625,-0.21875 0.828125,-0.375 0.015625,-0.015625 0.046875,-0.046875 0.046875,-0.09375 z m 0,0"
+ id="path89" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-12">
+ <path
+ style="stroke:none"
+ d="M 4.765625,-0.390625 V -0.5 c 0,-0.375 -0.1875,-0.375 -0.453125,-0.375 L 3.078125,-0.859375 1.90625,-0.84375 v -1.640625 h 2.109375 c 0.234375,0 0.375,-0.0625 0.375,-0.375 0,-0.296875 -0.109375,-0.375 -0.375,-0.375 H 1.90625 V -4.71875 h 0.390625 l 1.875,0.015625 c 0.28125,0 0.46875,0 0.46875,-0.375 v -0.0625 C 4.640625,-5.40625 4.578125,-5.53125 4.25,-5.53125 H 1.109375 c -0.28125,0 -0.375,0.09375 -0.375,0.390625 v 4.75 C 0.734375,-0.09375 0.8125,0 1.109375,0 H 4.375 c 0.3125,0 0.390625,-0.109375 0.390625,-0.390625 z m 0,0"
+ id="path92" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-0">
+ <path
+ style="stroke:none"
+ d="M 5.734375,20.359375 V -81.234375 H 63.34375 v 101.59375 z m 6.46875,-6.40625 h 44.71875 v -88.71875 h -44.71875 z m 0,0"
+ id="path95" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-1">
+ <path
+ style="stroke:none"
+ d="m 14.28125,-9.5625 h 18.5625 v -64.078125 l -20.1875,4.0625 V -79.9375 l 20.078125,-4.046875 H 44.09375 V -9.5625 H 62.671875 V 0 H 14.28125 Z m 0,0"
+ id="path98" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-2">
+ <path
+ style="stroke:none"
+ d="m 22.109375,-9.5625 h 39.65625 V 0 H 8.4375 v -9.5625 c 4.3125,-4.457031 10.1875,-10.445312 17.625,-17.96875 7.445312,-7.519531 12.128906,-12.367188 14.046875,-14.546875 3.632813,-4.082031 6.171875,-7.539063 7.609375,-10.375 1.445312,-2.832031 2.171875,-5.617187 2.171875,-8.359375 0,-4.457031 -1.570313,-8.09375 -4.703125,-10.90625 -3.125,-2.8125 -7.199219,-4.21875 -12.21875,-4.21875 -3.5625,0 -7.324219,0.621094 -11.28125,1.859375 -3.960938,1.230469 -8.1875,3.105469 -12.6875,5.625 V -79.9375 c 4.570312,-1.832031 8.84375,-3.21875 12.8125,-4.15625 3.976562,-0.9375 7.617188,-1.40625 10.921875,-1.40625 8.695313,0 15.632813,2.179688 20.8125,6.53125 5.175781,4.34375 7.765625,10.152344 7.765625,17.421875 0,3.460937 -0.648438,6.734375 -1.9375,9.828125 -1.292969,3.09375 -3.648438,6.742188 -7.0625,10.9375 -0.9375,1.085938 -3.921875,4.226562 -8.953125,9.421875 -5.023437,5.199219 -12.105469,12.464844 -21.25,21.796875 z m 0,0"
+ id="path101" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-3">
+ <path
+ style="stroke:none"
+ d="m 46.75,-45.28125 c 5.4375,1.167969 9.679688,3.589844 12.734375,7.265625 3.0625,3.667969 4.59375,8.203125 4.59375,13.609375 0,8.28125 -2.855469,14.695312 -8.5625,19.234375 C 49.816406,-0.640625 41.71875,1.625 31.21875,1.625 27.695312,1.625 24.066406,1.273438 20.328125,0.578125 16.597656,-0.109375 12.75,-1.144531 8.78125,-2.53125 V -13.5 c 3.144531,1.835938 6.59375,3.21875 10.34375,4.15625 3.75,0.9375 7.664062,1.40625 11.75,1.40625 7.125,0 12.550781,-1.40625 16.28125,-4.21875 3.738281,-2.8125 5.609375,-6.894531 5.609375,-12.25 0,-4.957031 -1.734375,-8.832031 -5.203125,-11.625 -3.46875,-2.789062 -8.296875,-4.1875 -14.484375,-4.1875 H 23.28125 v -9.34375 h 10.25 c 5.582031,0 9.851562,-1.113281 12.8125,-3.34375 2.96875,-2.226562 4.453125,-5.441406 4.453125,-9.640625 0,-4.3125 -1.53125,-7.617187 -4.59375,-9.921875 -3.054687,-2.3125 -7.429687,-3.46875 -13.125,-3.46875 -3.117187,0 -6.453125,0.339844 -10.015625,1.015625 -3.5625,0.667969 -7.480469,1.714844 -11.75,3.140625 v -10.125 C 15.625,-83.101562 19.660156,-84 23.421875,-84.59375 27.191406,-85.195312 30.75,-85.5 34.09375,-85.5 c 8.625,0 15.445312,1.960938 20.46875,5.875 5.03125,3.917969 7.546875,9.214844 7.546875,15.890625 0,4.65625 -1.335937,8.589844 -4,11.796875 -2.667969,3.199219 -6.453125,5.417969 -11.359375,6.65625 z m 0,0"
+ id="path104" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-4">
+ <path
+ style="stroke:none"
+ d="M 43.53125,-74.078125 14.84375,-29.25 h 28.6875 z m -2.96875,-9.90625 H 54.84375 V -29.25 h 11.984375 v 9.453125 H 54.84375 V 0 H 43.53125 V -19.796875 H 5.625 v -10.96875 z m 0,0"
+ id="path107" />
+ </symbol>
+ </g>
+ <clipPath
+ id="clip1">
+ <path
+ d="M 435.19922,768 H 561 V 893.8125 H 435.19922 Z m 0,0"
+ id="path112" />
+ </clipPath>
+ <image
+ id="image747"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask0">
+ <use
+ xlink:href="#image747"
+ id="use116"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image746"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip2">
+ <path
+ d="M 0,0 H 1315.1992 V 1392.4063 H 0 Z m 0,0"
+ id="path120" />
+ </clipPath>
+ <filter
+ id="alpha"
+ filterUnits="objectBoundingBox"
+ x="0"
+ y="0"
+ width="1"
+ height="1">
+ <feColorMatrix
+ type="matrix"
+ in="SourceGraphic"
+ values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
+ id="feColorMatrix123" />
+ </filter>
+ <image
+ id="image757"
+ width="1316"
+ height="1393"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABSQAAAVxCAYAAACeLII5AAAABmJLR0QA/wD/AP+gvaeTAAAgAElEQVR4nOzBAQEAAACAkP6v7ggKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA27uDlNhiKIqiN4/4HarjdhSCCP6OU3BHyFpQ/bQ3574CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPij1s8PAAAAAOC3fe+Z+Xf6FQAAAADAFT4FSQAAAACg8rVn5vX0KwAAAACAK3xYSAIAAAAAlSVIAgAAAAAVQRIAAAAAyDy+IQkAAAAAVCwkAQAAAICMIAkAAAAAZARJAAAAACDjG5IAAAAAQMZCEgAAAADICJIAAAAAQGY52QYAAAAAKo+FJAAAAABQcbINAAAAAGScbAMAAAAAGSfbAAAAAEDGyTYAAAAAkHGyDQAAAABkLCQBAAAAgIxvSAIAAAAAGSfbAAAAAEDGyTYAAAAAkBEkAQAAAIDM42QbAAAAAKhYSAIAAAAAGUESAAAAAMj4l20AAAAAIPNYSAIAAAAAFSfbAAAAAEDGyTYAAAAAkHn2zLycfgUAAAAAcIUlSAIAAAAAlbVnZp9+BQAAAABwBwtJAAAAAKBiIQkAAAAAZHxDEgAAAADIWEgCAAAAABkLSQAAAAAgYyEJAAAAAGQsJAEAAACAjIUkAAAAAJBZe2ae068AAAAAAO4gRgIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAEWAGZwAAADrSURBVABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAILNn5u30IwAAAACAK7z/B6OjCui6lNXsAAAAAElFTkSuQmCC" />
+ <mask
+ id="mask1">
+ <g
+ filter="url(#alpha)"
+ id="g129">
+ <use
+ xlink:href="#image757"
+ id="use127"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </g>
+ </mask>
+ <clipPath
+ id="clip4">
+ <path
+ d="M 438,770 H 553 V 885 H 438 Z m 0,0"
+ id="path132" />
+ </clipPath>
+ <clipPath
+ id="clip5">
+ <path
+ d="m 452.13281,770.49609 h 86.65625 c 7.69141,0 13.87891,6.1875 13.87891,13.87891 v 86.65625 c 0,7.6875 -6.1875,13.87891 -13.87891,13.87891 h -86.65625 c -7.6875,0 -13.8789,-6.19141 -13.8789,-13.87891 V 784.375 c 0,-7.69141 6.1914,-13.87891 13.8789,-13.87891 z m 0,0"
+ id="path135" />
+ </clipPath>
+ <clipPath
+ id="clip3">
+ <rect
+ x="0"
+ y="0"
+ width="1316"
+ height="1393"
+ id="rect138" />
+ </clipPath>
+ <g
+ id="surface756"
+ clip-path="url(#clip3)">
+ <g
+ clip-path="url(#clip4)"
+ clip-rule="nonzero"
+ id="g145">
+ <g
+ clip-path="url(#clip5)"
+ clip-rule="nonzero"
+ id="g143">
+ <path
+ style="fill:#303030;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="M 438.25391,770.49609 V 884.91016 H 552.66797 V 770.49609 Z m 0,0"
+ id="path141" />
+ </g>
+ </g>
+ </g>
+ <clipPath
+ id="clip6">
+ <path
+ d="M 564,789.47656 H 827 V 873 H 564 Z m 0,0"
+ id="path148" />
+ </clipPath>
+ <image
+ id="image762"
+ width="329"
+ height="104"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask2">
+ <use
+ xlink:href="#image762"
+ id="use152"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image761"
+ width="329"
+ height="104"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip7">
+ <path
+ d="M 291.19922,767.07422 H 417 V 893 H 291.19922 Z m 0,0"
+ id="path156" />
+ </clipPath>
+ <image
+ id="image768"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask3">
+ <use
+ xlink:href="#image768"
+ id="use160"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image767"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip8">
+ <path
+ d="M 0,0 H 1315.1992 V 1392.4063 H 0 Z m 0,0"
+ id="path164" />
+ </clipPath>
+ <image
+ id="image779"
+ width="1316"
+ height="1393"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABSQAAAVxCAYAAACeLII5AAAABmJLR0QA/wD/AP+gvaeTAAAgAElEQVR4nOzBAQEAAACAkP6v7ggKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA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AgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAIO+bLAAAAEASURBVEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQGbPzOv0EAAAAADAFT5/AcjGCwWyQYs3AAAAAElFTkSuQmCC" />
+ <mask
+ id="mask4">
+ <g
+ filter="url(#alpha)"
+ id="g170">
+ <use
+ xlink:href="#image779"
+ id="use168"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </g>
+ </mask>
+ <clipPath
+ id="clip10">
+ <path
+ d="M 294,769 H 409 V 885 H 294 Z m 0,0"
+ id="path173" />
+ </clipPath>
+ <clipPath
+ id="clip11">
+ <path
+ d="m 308.13281,769.90234 h 86.65625 c 7.69141,0 13.87891,6.19141 13.87891,13.88282 v 86.65625 c 0,7.6875 -6.1875,13.8789 -13.87891,13.8789 h -86.65625 c -7.6875,0 -13.8789,-6.1914 -13.8789,-13.8789 v -86.65625 c 0,-7.69141 6.1914,-13.88282 13.8789,-13.88282 z m 0,0"
+ id="path176" />
+ </clipPath>
+ <clipPath
+ id="clip9">
+ <rect
+ x="0"
+ y="0"
+ width="1316"
+ height="1393"
+ id="rect179" />
+ </clipPath>
+ <g
+ id="surface778"
+ clip-path="url(#clip9)">
+ <g
+ clip-path="url(#clip10)"
+ clip-rule="nonzero"
+ id="g186">
+ <g
+ clip-path="url(#clip11)"
+ clip-rule="nonzero"
+ id="g184">
+ <path
+ style="fill:#303030;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="M 294.25391,769.90234 V 884.32031 H 408.66797 V 769.90234 Z m 0,0"
+ id="path182" />
+ </g>
+ </g>
+ </g>
+ <clipPath
+ id="clip12">
+ <path
+ d="M 441,1125.4766 H 566.73828 V 1251 H 441 Z m 0,0"
+ id="path189" />
+ </clipPath>
+ <image
+ id="image784"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask5">
+ <use
+ xlink:href="#image784"
+ id="use193"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image783"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip13">
+ <path
+ d="M 0,0 H 1315.1992 V 1392.4063 H 0 Z m 0,0"
+ id="path197" />
+ </clipPath>
+ <image
+ id="image795"
+ width="1316"
+ height="1393"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABSQAAAVxCAYAAACeLII5AAAABmJLR0QA/wD/AP+gvaeTAAAd1klEQVR4nOzBAQEAAACAkP6v7ggKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA27tjnAhiIIqC7ZGBo3JuTkGCBAlX4BnJVdLmjp9+zwIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMB/t35/AAAAAAB/7fs5/QIAAAAA4B57LCQBAAAAgIaFJAAAAADQsZAEAAAAACrLQhIAAAAAyFhIAgAAAAAZC0kAAAAAIGMhCQAAAABkLCQBAAAAgMweURIAAAAAiOyZeT39CAAAAADgCl+CJAAAAABQ+dwz83b6FQAAAADAFZaFJAAAAABQESQBAAAAgMzjZBsAAAAAqFhIAgAAAAAZQRIAAAAAyCwn2wAAAABA5bGQBAAAAAAqTrYBAAAAgIyTbQAAAAAg42QbAAAAAMg42QYAAAAAMk62AQAAAICMhSQAAAAAkPENSQAAAAAg42QbAAAAAMg42QYAAAAAMoIkAAAAAJB5nGwDAAAAABULSQAAAAAgI0gCAAAAABn/sg0AAAAAZB4LSQAAAACg4mQbAAAAAMg42QYAAAAAMk62AQAAAICMk20AAAAAIONkGwAAAADIWEgCAAAAABnfkAQAAAAAMmvPzMvpVwAAAAAAV1h7ZvbpVwAAAAAAVxAkAQAAAICOIAkAAAAAVCwkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAHUESAAAAAKhYSAIAAAAAGUESAAAAAMisPTPr9CsAAAAAgDs8px8AAAAAANxDkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQGbPzPvpRwAAAAAAV/j4AaPBCD7AeLRZAAAAAElFTkSuQmCC" />
+ <mask
+ id="mask6">
+ <g
+ filter="url(#alpha)"
+ id="g203">
+ <use
+ xlink:href="#image795"
+ id="use201"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </g>
+ </mask>
+ <clipPath
+ id="clip15">
+ <path
+ d="m 443,1128 h 116 v 115 H 443 Z m 0,0"
+ id="path206" />
+ </clipPath>
+ <clipPath
+ id="clip16">
+ <path
+ d="m 457.78906,1128.4688 h 86.65625 c 7.69141,0 13.88281,6.1914 13.88281,13.8789 v 86.6562 c 0,7.6914 -6.1914,13.8789 -13.88281,13.8789 h -86.65625 c -7.6875,0 -13.8789,-6.1875 -13.8789,-13.8789 v -86.6562 c 0,-7.6875 6.1914,-13.8789 13.8789,-13.8789 z m 0,0"
+ id="path209" />
+ </clipPath>
+ <clipPath
+ id="clip14">
+ <rect
+ x="0"
+ y="0"
+ width="1316"
+ height="1393"
+ id="rect212" />
+ </clipPath>
+ <g
+ id="surface794"
+ clip-path="url(#clip14)">
+ <g
+ clip-path="url(#clip15)"
+ clip-rule="nonzero"
+ id="g219">
+ <g
+ clip-path="url(#clip16)"
+ clip-rule="nonzero"
+ id="g217">
+ <path
+ style="fill:#303030;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 443.91016,1128.4688 v 114.414 h 114.41796 v -114.414 z m 0,0"
+ id="path215" />
+ </g>
+ </g>
+ </g>
+ <clipPath
+ id="clip17">
+ <path
+ d="m 570,1148 h 263 v 83.8125 H 570 Z m 0,0"
+ id="path222" />
+ </clipPath>
+ <image
+ id="image800"
+ width="329"
+ height="104"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask7">
+ <use
+ xlink:href="#image800"
+ id="use226"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image799"
+ width="329"
+ height="104"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip18">
+ <path
+ d="m 563.19922,407.07422 h 341.4414 V 491 h -341.4414 z m 0,0"
+ id="path230" />
+ </clipPath>
+ <image
+ id="image806"
+ width="426"
+ height="104"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask8">
+ <use
+ xlink:href="#image806"
+ id="use234"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image805"
+ width="426"
+ height="104"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip19">
+ <path
+ d="M 433,383.07422 H 558.73828 V 509 H 433 Z m 0,0"
+ id="path238" />
+ </clipPath>
+ <image
+ id="image812"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask9">
+ <use
+ xlink:href="#image812"
+ id="use242"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image811"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip20">
+ <path
+ d="M 0,0 H 1315.1992 V 1392.4063 H 0 Z m 0,0"
+ id="path246" />
+ </clipPath>
+ <image
+ id="image823"
+ width="1316"
+ height="1393"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABSQAAAVxCAYAAACeLII5AAAABmJLR0QA/wD/AP+gvaeTAAAgAElEQVR4nOzdyXEAIRAEwQEhyX+DdXhRPMi0gHdFz+4MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA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gIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQEyf927JgAABAGYNgOvKIbJ7goB4mC3gUAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAB8VT7QAAARnSURBVAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMmtm9usIAAAAAOAL5wLd0Q0b4GzECAAAAABJRU5ErkJggg==" />
+ <mask
+ id="mask10">
+ <g
+ filter="url(#alpha)"
+ id="g252">
+ <use
+ xlink:href="#image823"
+ id="use250"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </g>
+ </mask>
+ <clipPath
+ id="clip22">
+ <path
+ d="M 435,386 H 551 V 501 H 435 Z m 0,0"
+ id="path255" />
+ </clipPath>
+ <clipPath
+ id="clip23">
+ <path
+ d="m 449.87109,386.16016 h 86.65625 c 7.6875,0 13.87891,6.1914 13.87891,13.8789 v 86.65625 c 0,7.69141 -6.19141,13.87891 -13.87891,13.87891 h -86.65625 c -7.6914,0 -13.8789,-6.1875 -13.8789,-13.87891 v -86.65625 c 0,-7.6875 6.1875,-13.8789 13.8789,-13.8789 z m 0,0"
+ id="path258" />
+ </clipPath>
+ <clipPath
+ id="clip21">
+ <rect
+ x="0"
+ y="0"
+ width="1316"
+ height="1393"
+ id="rect261" />
+ </clipPath>
+ <g
+ id="surface822"
+ clip-path="url(#clip21)">
+ <g
+ clip-path="url(#clip22)"
+ clip-rule="nonzero"
+ id="g268">
+ <g
+ clip-path="url(#clip23)"
+ clip-rule="nonzero"
+ id="g266">
+ <path
+ style="fill:#303030;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="M 435.99219,386.16016 V 500.57422 H 550.40625 V 386.16016 Z m 0,0"
+ id="path264" />
+ </g>
+ </g>
+ </g>
+ <clipPath
+ id="clip24">
+ <path
+ d="M 0,0 H 1315.1992 V 1392.4063 H 0 Z m 0,0"
+ id="path271" />
+ </clipPath>
+ <image
+ id="image833"
+ width="1316"
+ height="1393"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABSQAAAVxCAYAAACeLII5AAAABmJLR0QA/wD/AP+gvaeTAAAf60lEQVR4nOzcwW0DMRAEwZXBkJx/XDIEHB2Fmg9WRbDvxpCvmdkDAAAAAPB9vz+nLwAAAAAA7iFIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQGbNzD59BAAAAABwBwtJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQWTOzTx8BAAAAANxhzczn9BEAAAAAwBX2mpm/01cAAAAAAFd4BEkAAAAAoGIhCQAAAABkLCQBAAAAgIyFJAAAAACQESQBAAAAgIwn2wAAAABAZq+ZeZ++AgAAAAC4goUkAAAAAJDxhyQAAAAAkLGQBAAAAAAyFpIAAAAAQEaQBAAAAAAynmwDAAAAABkLSQAAAAAgYyEJAAAAAGQsJAEAAACAjCAJAAAAAGQ82QYAAAAAMhaSAAAAAEDmWTPzPn0FAAAAAHAFC0kAAAAAIOMPSQAAAAAgYyEJAAAAAGQESQAAAAAg48k2AAAAAJCxkAQAAAAAMhaSAAAAAEDGQhIAAAAAyAiSAAAAAEDGk20AAAAAIGMhCQAAAABknjUzz+krAAAAAIA7CJIAAAAAQEaQBAAAAAAqW5AEAAAAADKCJAAAAABQsZAEAAAAADqCJAAAAACQESQBAAAAgIon2wAAAABAR5AEAAAAACr79AEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/7cEBCQAAAICg/6/7ESoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHMBVG5VrWVi1d0AAAAASUVORK5CYII=" />
+ <mask
+ id="mask11">
+ <g
+ filter="url(#alpha)"
+ id="g277">
+ <use
+ xlink:href="#image833"
+ id="use275"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </g>
+ </mask>
+ <clipPath
+ id="clip26">
+ <path
+ d="m 443,391 h 100 v 24 H 443 Z m 0,0"
+ id="path280" />
+ </clipPath>
+ <clipPath
+ id="clip27">
+ <path
+ d="m 455.21484,391.55078 h 76.10938 c 6.38672,0 11.53125,2.96094 11.53125,6.63672 v 9.61328 c 0,3.67969 -5.14453,6.64063 -11.53125,6.64063 h -76.10938 c -6.39062,0 -11.53515,-2.96094 -11.53515,-6.64063 v -9.61328 c 0,-3.67578 5.14453,-6.63672 11.53515,-6.63672 z m 0,0"
+ id="path283" />
+ </clipPath>
+ <clipPath
+ id="clip25">
+ <rect
+ x="0"
+ y="0"
+ width="1316"
+ height="1393"
+ id="rect286" />
+ </clipPath>
+ <g
+ id="surface832"
+ clip-path="url(#clip25)">
+ <g
+ clip-path="url(#clip26)"
+ clip-rule="nonzero"
+ id="g293">
+ <g
+ clip-path="url(#clip27)"
+ clip-rule="nonzero"
+ id="g291">
+ <path
+ style="fill:#d7f3ee;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 443.67969,391.55078 v 22.89063 h 99.17578 v -22.89063 z m 0,0"
+ id="path289" />
+ </g>
+ </g>
+ </g>
+ <image
+ id="image838"
+ width="800"
+ height="109"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask12">
+ <use
+ xlink:href="#image838"
+ id="use297"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image837"
+ width="800"
+ height="109"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip28">
+ <path
+ d="M 435.19922,909 H 561 v 125.6133 H 435.19922 Z m 0,0"
+ id="path301" />
+ </clipPath>
+ <image
+ id="image844"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask13">
+ <use
+ xlink:href="#image844"
+ id="use305"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image843"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip29">
+ <path
+ d="M 0,0 H 1315.1992 V 1392.4063 H 0 Z m 0,0"
+ id="path309" />
+ </clipPath>
+ <image
+ id="image855"
+ width="1316"
+ height="1393"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABSQAAAVxCAYAAACeLII5AAAABmJLR0QA/wD/AP+gvaeTAAAfoklEQVR4nOzBAQEAAACAkP6v7ggKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA27uDmwhiIIqC7ZEXQt24yYIDSETB88FVEfj89HsGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA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AAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADI7Jl5n34EAAAAAHCFrz9PlwjQ7AoW/wAAAABJRU5ErkJggg==" />
+ <mask
+ id="mask14">
+ <g
+ filter="url(#alpha)"
+ id="g315">
+ <use
+ xlink:href="#image855"
+ id="use313"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </g>
+ </mask>
+ <clipPath
+ id="clip31">
+ <path
+ d="m 438,911 h 115 v 116 H 438 Z m 0,0"
+ id="path318" />
+ </clipPath>
+ <clipPath
+ id="clip32">
+ <path
+ d="m 452.13281,911.91406 h 86.65625 c 7.69141,0 13.87891,6.19141 13.87891,13.88281 v 86.65623 c 0,7.6875 -6.1875,13.8789 -13.87891,13.8789 h -86.65625 c -7.6875,0 -13.8789,-6.1914 -13.8789,-13.8789 v -86.65623 c 0,-7.6914 6.1914,-13.88281 13.8789,-13.88281 z m 0,0"
+ id="path321" />
+ </clipPath>
+ <clipPath
+ id="clip30">
+ <rect
+ x="0"
+ y="0"
+ width="1316"
+ height="1393"
+ id="rect324" />
+ </clipPath>
+ <g
+ id="surface854"
+ clip-path="url(#clip30)">
+ <g
+ clip-path="url(#clip31)"
+ clip-rule="nonzero"
+ id="g331">
+ <g
+ clip-path="url(#clip32)"
+ clip-rule="nonzero"
+ id="g329">
+ <path
+ style="fill:#303030;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="M 438.25391,911.91406 V 1026.332 H 552.66797 V 911.91406 Z m 0,0"
+ id="path327" />
+ </g>
+ </g>
+ </g>
+ <clipPath
+ id="clip33">
+ <path
+ d="m 562.39844,929 h 584.41016 v 87 H 562.39844 Z m 0,0"
+ id="path334" />
+ </clipPath>
+ <image
+ id="image860"
+ width="730"
+ height="109"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask15">
+ <use
+ xlink:href="#image860"
+ id="use338"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image859"
+ width="730"
+ height="109"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip34">
+ <path
+ d="M 291.19922,909 H 417 v 125.6133 H 291.19922 Z m 0,0"
+ id="path342" />
+ </clipPath>
+ <image
+ id="image866"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask16">
+ <use
+ xlink:href="#image866"
+ id="use346"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image865"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip35">
+ <path
+ d="M 0,0 H 1315.1992 V 1392.4063 H 0 Z m 0,0"
+ id="path350" />
+ </clipPath>
+ <image
+ id="image877"
+ width="1316"
+ height="1393"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABSQAAAVxCAYAAACeLII5AAAABmJLR0QA/wD/AP+gvaeTAAAfw0lEQVR4nOzBAQEAAACAkP6v7ggKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA27tjnAhiKAqCfwYDR+XcXIIAIUGyV9i2kKukyR23/DwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA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CQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAya2Y+dh8CAAAAADjC5x/qzArhTm/VnwAAAABJRU5ErkJggg==" />
+ <mask
+ id="mask17">
+ <g
+ filter="url(#alpha)"
+ id="g356">
+ <use
+ xlink:href="#image877"
+ id="use354"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </g>
+ </mask>
+ <clipPath
+ id="clip37">
+ <path
+ d="m 294,911 h 115 v 115 H 294 Z m 0,0"
+ id="path359" />
+ </clipPath>
+ <clipPath
+ id="clip38">
+ <path
+ d="m 308.13281,911.32422 h 86.65625 c 7.69141,0 13.87891,6.1914 13.87891,13.8789 v 86.66018 c 0,7.6875 -6.1875,13.8789 -13.87891,13.8789 h -86.65625 c -7.6875,0 -13.8789,-6.1914 -13.8789,-13.8789 v -86.66018 c 0,-7.6875 6.1914,-13.8789 13.8789,-13.8789 z m 0,0"
+ id="path362" />
+ </clipPath>
+ <clipPath
+ id="clip36">
+ <rect
+ x="0"
+ y="0"
+ width="1316"
+ height="1393"
+ id="rect365" />
+ </clipPath>
+ <g
+ id="surface876"
+ clip-path="url(#clip36)">
+ <g
+ clip-path="url(#clip37)"
+ clip-rule="nonzero"
+ id="g372">
+ <g
+ clip-path="url(#clip38)"
+ clip-rule="nonzero"
+ id="g370">
+ <path
+ style="fill:#303030;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="M 294.25391,911.32422 V 1025.7422 H 408.66797 V 911.32422 Z m 0,0"
+ id="path368" />
+ </g>
+ </g>
+ </g>
+ <clipPath
+ id="clip39">
+ <path
+ d="M 435.19922,531.07422 H 561 V 657 H 435.19922 Z m 0,0"
+ id="path375" />
+ </clipPath>
+ <image
+ id="image882"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask18">
+ <use
+ xlink:href="#image882"
+ id="use379"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image881"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip40">
+ <path
+ d="M 0,0 H 1315.1992 V 1392.4063 H 0 Z m 0,0"
+ id="path383" />
+ </clipPath>
+ <image
+ id="image893"
+ width="1316"
+ height="1393"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABSQAAAVxCAYAAACeLII5AAAABmJLR0QA/wD/AP+gvaeTAAAgAElEQVR4nOzdQU4AIAwAwUJQ//9dTYyfMMuBmRf0vGlhBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOD/rZnZt4cAAAAAAJ7we2bm4/YUAAAAAMATvgVJAAAAAKDyI0gCAAAAAJV1ZubcngIAAAAAeMKyIQkAAAAAVARJAAAAACAjSAIAAAAAGW9IAgAAAAAZG5IAAAAAQEaQBAAAAAAy68zM5+0pAAAAAIAnCJIAAAAAQEaQBAAAAAAy+8zM1+0pAAAAAIAn2JAEAAAAADKCJAAAAACQWU62AQAAAIDKtiEJAAAAAFScbAMAAAAAGSfbAAAAAEDGyTYAAAAAkHGyDQAAAABknGwDAAAAABkbkgAAAABAxhuSAAAAAEDGyTYAAAAAkHGyDQAAAABkBEkAAAAAILOdbAMAAAAAFRuSAAAAAEBGkAQAAAAAMn7ZBgAAAAAy24YkAAAAAFBxsg0AAAAAZARJAAAAACAjSAIAAAAAGW9IAgAAAAAZG5IAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMT20AAAAAgIwNSQAAAAAgs/btCQAAAACAdwiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAD8tWPHBACAMADDduAV3TjBRTlIFPQuABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCSFT1ioAAAMGSURBVAAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJBZM7NfRwAAAAAAXzgXuNILze+fCBwAAAAASUVORK5CYII=" />
+ <mask
+ id="mask19">
+ <g
+ filter="url(#alpha)"
+ id="g389">
+ <use
+ xlink:href="#image893"
+ id="use387"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </g>
+ </mask>
+ <clipPath
+ id="clip42">
+ <path
+ d="M 438,533 H 553 V 649 H 438 Z m 0,0"
+ id="path392" />
+ </clipPath>
+ <clipPath
+ id="clip43">
+ <path
+ d="m 452.16016,533.73047 h 86.65625 c 7.6914,0 13.8789,6.1875 13.8789,13.8789 v 86.65625 c 0,7.6875 -6.1875,13.87891 -13.8789,13.87891 h -86.65625 c -7.6875,0 -13.87891,-6.19141 -13.87891,-13.87891 v -86.65625 c 0,-7.6914 6.19141,-13.8789 13.87891,-13.8789 z m 0,0"
+ id="path395" />
+ </clipPath>
+ <clipPath
+ id="clip41">
+ <rect
+ x="0"
+ y="0"
+ width="1316"
+ height="1393"
+ id="rect398" />
+ </clipPath>
+ <g
+ id="surface892"
+ clip-path="url(#clip41)">
+ <g
+ clip-path="url(#clip42)"
+ clip-rule="nonzero"
+ id="g405">
+ <g
+ clip-path="url(#clip43)"
+ clip-rule="nonzero"
+ id="g403">
+ <path
+ style="fill:#303030;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="M 438.28125,533.73047 V 648.14453 H 552.69531 V 533.73047 Z m 0,0"
+ id="path401" />
+ </g>
+ </g>
+ </g>
+ <clipPath
+ id="clip44">
+ <path
+ d="M 0,0 H 1315.1992 V 1392.4063 H 0 Z m 0,0"
+ id="path408" />
+ </clipPath>
+ <image
+ id="image903"
+ width="1316"
+ height="1393"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABSQAAAVxCAYAAACeLII5AAAABmJLR0QA/wD/AP+gvaeTAAAgAElEQVR4nOzdy21FIRAFwXkWITn/uPy9OAo3C6oimHXrIF4zswcAAAAA4P+9v52+AAAAAAC4hyAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgMyamX36CAAAAADgDmtmfk8fAQAAAABcYa+Z+Tl9BQAAAABwhW0hCQAAAABkBEkAAAAAoGIhCQAAAAB0BEkAAAAAoOJTGwAAAAAg48k2AAAAANARJAEAAACAioUkAAAAAJDZa2a+T18BAAAAAFxhr5n5PH0FAAAAAHAFQRIAAAAAyOw1Mx+nrwAAAAAArvBYSAIAAAAAFU+2AQAAAICMhSQAAAAAkLGQBAAAAAAyFpIAAAAAQMZCEgAAAADICJIAAAAAQMaTbQAAAAAgYyEJAAAAAGQsJAEAAACAjIUkAAAAAJARJAEAAACAzLNm5uP0FQAAAADAFSwkAQAAAICMT20AAAAAgIyFJAAAAACQsZAEAAAAADIWkgAAAABAZq+Z+Tp9BQAAAABwhUeQBAAAAAAqFpIAAAAAQEaQBAAAAAAygiQAAAAAkPHLNgAAAACQ8akNAAAAAJDxZBsAAAAAyAiSAAAAAEBGkAQAAAAAMs9rZl6nrwAAAAAArrBPHwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADw1x4cCAAAAAAI8rdeYIQKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI/CkxMAAAE5SURBVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgBELhV8rTwD7iAAAAAElFTkSuQmCC" />
+ <mask
+ id="mask20">
+ <g
+ filter="url(#alpha)"
+ id="g414">
+ <use
+ xlink:href="#image903"
+ id="use412"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </g>
+ </mask>
+ <clipPath
+ id="clip46">
+ <path
+ d="m 445,539 h 101 v 24 H 445 Z m 0,0"
+ id="path417" />
+ </clipPath>
+ <clipPath
+ id="clip47">
+ <path
+ d="m 457.50391,539.12109 h 76.10937 c 6.39063,0 11.53125,2.96094 11.53125,6.63672 v 9.61328 c 0,3.67969 -5.14062,6.64063 -11.53125,6.64063 h -76.10937 c -6.39063,0 -11.53125,-2.96094 -11.53125,-6.64063 v -9.61328 c 0,-3.67578 5.14062,-6.63672 11.53125,-6.63672 z m 0,0"
+ id="path420" />
+ </clipPath>
+ <clipPath
+ id="clip45">
+ <rect
+ x="0"
+ y="0"
+ width="1316"
+ height="1393"
+ id="rect423" />
+ </clipPath>
+ <g
+ id="surface902"
+ clip-path="url(#clip45)">
+ <g
+ clip-path="url(#clip46)"
+ clip-rule="nonzero"
+ id="g430">
+ <g
+ clip-path="url(#clip47)"
+ clip-rule="nonzero"
+ id="g428">
+ <path
+ style="fill:#d7f3ee;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 445.97266,539.12109 v 22.89063 h 99.17187 v -22.89063 z m 0,0"
+ id="path426" />
+ </g>
+ </g>
+ </g>
+ <clipPath
+ id="clip48">
+ <path
+ d="M 438.39844,1266.2773 H 564 V 1392 H 438.39844 Z m 0,0"
+ id="path433" />
+ </clipPath>
+ <image
+ id="image908"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask21">
+ <use
+ xlink:href="#image908"
+ id="use437"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image907"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip49">
+ <path
+ d="M 0,0 H 1315.1992 V 1392.4063 H 0 Z m 0,0"
+ id="path441" />
+ </clipPath>
+ <image
+ id="image919"
+ width="1316"
+ height="1393"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABSQAAAVxCAYAAACeLII5AAAABmJLR0QA/wD/AP+gvaeTAAAcg0lEQVR4nOzBAQEAAACAkP6v7ggKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA27ljm4piKIiC15aBxn+vlEBAAAktcF7gGcn5jVe7BgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADorL8HAAAAAPDffs4IJAEAAACAhkASAAAAAMgsgSQAAAAAkNlPHwAAAAAA3ENDEgAAAADICCQBAAAAgIxAEgAAAADI+EMSAAAAAMhoSAIAAAAAlXVm5u3pKwAAAACAK3yfmfl4+goAAAAA4ApfZ2ben74CAAAAALjCEkgCAAAAABWBJAAAAACQ2f6QBAAAAAAqGpIAAAAAQEYgCQAAAABklsk2AAAAAFDZGpIAAAAAQEVDEgAAAADI+EMSAAAAAMiYbAMAAAAAGZNtAAAAACBjsg0AAAAAZASSAAAAAEBmm2wDAAAAABUNSQAAAAAgI5AEAAAAADICSQAAAAAg4w9JAAAAACCjIQkAAAAAZASSAAAAAEBmmWwDAAAAAJWtIQkAAAAAVDQkAQAAAICMPyQBAAAAgIzJNgAAAACQMdkGAAAAADIm2wAAAABARiAJAAAAAGS2yTYAAAAAUFlnZl5PXwEAAAAAXOHzF7NXBvvky+6DAAAAAElFTkSuQmCC" />
+ <mask
+ id="mask22">
+ <g
+ filter="url(#alpha)"
+ id="g447">
+ <use
+ xlink:href="#image919"
+ id="use445"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </g>
+ </mask>
+ <clipPath
+ id="clip51">
+ <path
+ d="m 441,1269 h 116 v 115 H 441 Z m 0,0"
+ id="path450" />
+ </clipPath>
+ <clipPath
+ id="clip52">
+ <path
+ d="m 455.52734,1269.4297 h 86.65625 c 7.69141,0 13.87891,6.1875 13.87891,13.8789 v 86.6562 c 0,7.6875 -6.1875,13.879 -13.87891,13.879 h -86.65625 c -7.6875,0 -13.8789,-6.1915 -13.8789,-13.879 v -86.6562 c 0,-7.6914 6.1914,-13.8789 13.8789,-13.8789 z m 0,0"
+ id="path453" />
+ </clipPath>
+ <clipPath
+ id="clip50">
+ <rect
+ x="0"
+ y="0"
+ width="1316"
+ height="1393"
+ id="rect456" />
+ </clipPath>
+ <g
+ id="surface918"
+ clip-path="url(#clip50)">
+ <g
+ clip-path="url(#clip51)"
+ clip-rule="nonzero"
+ id="g463">
+ <g
+ clip-path="url(#clip52)"
+ clip-rule="nonzero"
+ id="g461">
+ <path
+ style="fill:#303030;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 441.64844,1269.4297 v 114.4141 H 556.0625 v -114.4141 z m 0,0"
+ id="path459" />
+ </g>
+ </g>
+ </g>
+ <clipPath
+ id="clip53">
+ <path
+ d="m 566,1286.2773 h 584 v 87.3243 H 566 Z m 0,0"
+ id="path466" />
+ </clipPath>
+ <image
+ id="image924"
+ width="730"
+ height="109"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask23">
+ <use
+ xlink:href="#image924"
+ id="use470"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image923"
+ width="730"
+ height="109"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip54">
+ <path
+ d="m 563.19922,24 h 341.4414 v 83.8125 h -341.4414 z m 0,0"
+ id="path474" />
+ </clipPath>
+ <image
+ id="image930"
+ width="426"
+ height="104"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask24">
+ <use
+ xlink:href="#image930"
+ id="use478"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image929"
+ width="426"
+ height="104"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip55">
+ <path
+ d="M 433,0 H 558.73828 V 125.8125 H 433 Z m 0,0"
+ id="path482" />
+ </clipPath>
+ <image
+ id="image936"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJ0AAACdCAAAAACLTSUKAAAAAmJLR0QA/4ePzL8AAAYYSURBVHic7ZxJb+M4EEarihQlWd6SON3z//9dd5aObVkbyao5ULLd6QEGINBSDnyIEYTJ4aHI4pLDh/AJDB/8PP53kbvPJ5nffkLA8DWbGQAICIiAyCc//bscIiIhIc7pJyAiIizCn+p3Z4eASESkSBHRfHoCIsLs2bNn+a1+NwNEREU6oIgQ5yqeCLP3zjnrvPcsctXD63ckUlobkxtjMq0UzWUnIuydtUM/DIO13gtPevomp3Rm8rIsy6IwmVZECH+/dyWUztmh79u2bbveWg9T9fRVTmtTlNV6va5WRWG0UjNNrYB4drbvmqau60vTonU8rr2pdqSzvKw2u912u6nKwoy1+/t+AmFi+66pT6fj0WjVofOCMtkhksryar3fPz7s95uqKDKtZupaAQgz2zXn00dV5poARIBRgh0iqiyvtvunw+Fpv9usCpOp0LNz1G4qXlufNlWRKQQQcHKrndKmXO8P35+fD/vtqjRZWHUzrTsQZu9st15XZZ4pBGFhYRDQY+mKavf07Z9vz4+7dZFrTTTnWRb2lNWqLExGKN579iICoAEBlTLl5uHw7fu3w35d5lqPW/FctQMBFu+yLNMKxVtrrWMREA2ARFle7R4Pz4fDw7bMs7ka4l5RidJKK0Jxtu/73jqPYWZJm3K9fXh8etxvqzxTs85qAEHC7QO87dumaXvrGUEjBrvd/vFhty7zTM+zz/0pKIgI7Ib2Up/rplNewszqvFxvd9vtJLeAGyAAKODCbprzcXMs2gEBNCApbcpqs92sinHNLQMCkDbFar3ZrFfBRAMiaVOsqmpV5lrRUm4AgEBKm7ysqir0JmhEUtoUZVkWRi81rVc9JG2KclUWuVaESOEGYPIiN4utuTu/cI/L80wrRCDAMGLCwb+w3E1GEyESBj2ttVYLuwU/UlrrTBMhAAEgkVJKqfASW1gulG+0AQoDSilabi/5XY+IVDg3kMIAjiwtFx78owsC3QYWbtcJxNEmrLswAF+hcAFEGG1oHPgihRuZKkXLavwPyS6eZBdPsosn2cWT7OJJdvEku3iSXTzJLp5kF0+yiyfZxZPs4kl28SS7eJJdPMkunmQXT7KLJ9nFk+ziSXbxJLt4kl08yS6eZBdPsosn2cWT7OJJdvEku3iSXTzJLp5kF0+yiyfZxZPs4kl28SS7eJJdPMkunmQXT7KLJ9nFk+ziSXbxJLt4kl08yS6eYCciIvI/fzofVxsCAJEQ/PVF9MYMNxEAmvKiZMzuWxqRu5kMtRNm5q/gBhAy3ZhFRIRCVBSz/xp6EnQ8s0CYWWF2zjnvl+8MAWHvvXOemQVoTO4brHXMsnBnTHFzg3OeRYQAhL0dhn6w1i+vx97ZoR9667wAEIh4Z/uu63rrPC8qJyLs7dC1bT84zyIkwuyGvrlcmm5wiy49AeEQ9BWSvmSs3dA2dX1p+kX1BISd7dtLXV9GOw0g7Pq2Pp9O6zLThGqRFDIAAQ5y59OpvnSDZxEFUwiTyaccTQSY2zCcV872l+P768+fLx91N3gGBQiEREpnWQhhggUO3Jvc+dfrj58vb8dLZ0OGG7C33eVYFiZTCFwYPWfK7CTH7G3fnN/fXl/fPup2cCwAGgTE274xJssUiHe2MLPFaU5yIuK97dvz8e3Hj9f3Y90NnsdETRFvW61D5uGwWRUzh35JSPzs28vx18vPHy+/Tk3vWEJmpQCDRSJE8bZvm3U5hZHOZRcq113OH++vLy9vH3Vnw7mgw6+9RQTwtm8v501VhkzN2fKNmb0buqY+fby/vb8fz2HVTTOLwt5iWH/1cVOtcjMVb5aMXvF+GNqmPh0/Po6nOmzFt9oBeBAJXbNZV2Vu5k4QHoa2udTnU1033TDJjRm9AuAFmG3f1sfVKs9nbNuQRGqHvm2bS9N2/eCvl5FbNjQR6czkeZHneZbNmw3tvbND33chhtRfs6uvBiG6WmU6M5nWmuZLmg2Z385Za611nvl2EblL/b4G441hePP1bIj99t778Lr5j0zyyQ+JJrfZEoRlehaywP31/HMW/pg+h7Omusn9K/b+CvJZAce0uRlvUTIKwp//j/gXPCmyvzDydeIAAAAASUVORK5CYII=" />
+ <mask
+ id="mask25">
+ <use
+ xlink:href="#image936"
+ id="use486"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image935"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip56">
+ <path
+ d="M 0,0 H 1315.1992 V 1392.4063 H 0 Z m 0,0"
+ id="path490" />
+ </clipPath>
+ <image
+ id="image947"
+ width="1316"
+ height="1393"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABSQAAAVxCAYAAACeLII5AAAABmJLR0QA/wD/AP+gvaeTAAAcf0lEQVR4nOzdsRHDMBADQchibe6/DXcgJ5KrMBj8bgWIf47DI8knAAAAAAD/915Jrt0rAAAAAIAR7pXku3sFAAAAADDC4yAJAAAAALQoJAEAAACAGoUkAAAAAFCjkAQAAAAAahSSAAAAAECNgyQAAAAAUOPJNgAAAABQo5AEAAAAAGoUkgAAAABAjUISAAAAAKh5VpJr9woAAAAAYARPtgEAAACAGk+2AQAAAIAahSQAAAAAUKOQBAAAAABqFJIAAAAAQI1CEgAAAACocZAEAAAAAGo82QYAAAAAahSSAAAAAECNQhIAAAAAqFFIAgAAAAA1DpIAAAAAQM29kly7VwAAAAAAIygkAQAAAIAan9oAAAAAADUKSQAAAACgRiEJAAAAANQoJAEAAACAGgdJAAAAAKDmXkme3SsAAAAAgBkcJAEAAACAmtfuAQAAAADAHApJAAAAAKBGIQkAAAAA1JxJjt0jAAAAAIARHoUkAAAAAFBz7h4AAAAAAMyhkAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+LUHhwQAAAAAgv6/doQVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAI4KtEtzvY2/0AAAAASUVORK5CYII=" />
+ <mask
+ id="mask26">
+ <g
+ filter="url(#alpha)"
+ id="g496">
+ <use
+ xlink:href="#image947"
+ id="use494"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </g>
+ </mask>
+ <clipPath
+ id="clip58">
+ <path
+ d="M 443,8 H 543 V 32 H 443 Z m 0,0"
+ id="path499" />
+ </clipPath>
+ <clipPath
+ id="clip59">
+ <path
+ d="m 455.21484,8.351562 h 76.10938 c 6.38672,0 11.53125,2.960938 11.53125,6.636719 v 9.613281 c 0,3.679688 -5.14453,6.640626 -11.53125,6.640626 h -76.10938 c -6.39062,0 -11.53515,-2.960938 -11.53515,-6.640626 v -9.613281 c 0,-3.675781 5.14453,-6.636719 11.53515,-6.636719 z m 0,0"
+ id="path502" />
+ </clipPath>
+ <clipPath
+ id="clip57">
+ <rect
+ x="0"
+ y="0"
+ width="1316"
+ height="1393"
+ id="rect505" />
+ </clipPath>
+ <g
+ id="surface946"
+ clip-path="url(#clip57)">
+ <g
+ clip-path="url(#clip58)"
+ clip-rule="nonzero"
+ id="g512">
+ <g
+ clip-path="url(#clip59)"
+ clip-rule="nonzero"
+ id="g510">
+ <path
+ style="fill:#d7f3ee;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 443.67969,8.351562 v 22.890626 h 99.17578 V 8.351562 Z m 0,0"
+ id="path508" />
+ </g>
+ </g>
+ </g>
+ <image
+ id="image952"
+ width="800"
+ height="109"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask27">
+ <use
+ xlink:href="#image952"
+ id="use516"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image951"
+ width="800"
+ height="109"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip60">
+ <path
+ d="M 435.19922,148 H 561 V 273.8125 H 435.19922 Z m 0,0"
+ id="path520" />
+ </clipPath>
+ <image
+ id="image958"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask28">
+ <use
+ xlink:href="#image958"
+ id="use524"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image957"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip61">
+ <path
+ d="M 0,0 H 1315.1992 V 1392.4063 H 0 Z m 0,0"
+ id="path528" />
+ </clipPath>
+ <image
+ id="image969"
+ width="1316"
+ height="1393"
+ xlink:href="data:image/png;base64,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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPDXHhwIAAAAAAjytx5hgQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgBf+71XNGFSbhwAAAABJRU5ErkJggg==" />
+ <mask
+ id="mask29">
+ <g
+ filter="url(#alpha)"
+ id="g534">
+ <use
+ xlink:href="#image969"
+ id="use532"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </g>
+ </mask>
+ <clipPath
+ id="clip63">
+ <path
+ d="m 445,155 h 101 v 24 H 445 Z m 0,0"
+ id="path537" />
+ </clipPath>
+ <clipPath
+ id="clip64">
+ <path
+ d="m 457.50391,155.92187 h 76.10937 c 6.39063,0 11.53125,2.95704 11.53125,6.63672 v 9.61328 c 0,3.67579 -5.14062,6.63672 -11.53125,6.63672 h -76.10937 c -6.39063,0 -11.53125,-2.96093 -11.53125,-6.63672 v -9.61328 c 0,-3.67968 5.14062,-6.63672 11.53125,-6.63672 z m 0,0"
+ id="path540" />
+ </clipPath>
+ <clipPath
+ id="clip62">
+ <rect
+ x="0"
+ y="0"
+ width="1316"
+ height="1393"
+ id="rect543" />
+ </clipPath>
+ <g
+ id="surface968"
+ clip-path="url(#clip62)">
+ <g
+ clip-path="url(#clip63)"
+ clip-rule="nonzero"
+ id="g550">
+ <g
+ clip-path="url(#clip64)"
+ clip-rule="nonzero"
+ id="g548">
+ <path
+ style="fill:#d7f3ee;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 445.97266,155.92187 v 22.88672 h 99.17187 v -22.88672 z m 0,0"
+ id="path546" />
+ </g>
+ </g>
+ </g>
+ <clipPath
+ id="clip65">
+ <path
+ d="m 0,1088 h 1315.1992 v 5 H 0 Z m 0,0"
+ id="path553" />
+ </clipPath>
+ <clipPath
+ id="clip66">
+ <path
+ d="m 0,705 h 1315.1992 v 5 H 0 Z m 0,0"
+ id="path556" />
+ </clipPath>
+ <clipPath
+ id="clip67">
+ <path
+ d="m 0,323 h 1315.1992 v 5 H 0 Z m 0,0"
+ id="path559" />
+ </clipPath>
+ </defs>
+ <g
+ id="surface741"
+ transform="translate(-433)">
+ <g
+ clip-path="url(#clip54)"
+ clip-rule="nonzero"
+ id="g910">
+ <use
+ xlink:href="#image929"
+ mask="url(#mask24)"
+ transform="matrix(0.801502,0,0,0.807077,563.2,23.876028)"
+ id="use908"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </g>
+ <g
+ style="fill:#ff7d28;fill-opacity:1"
+ id="g924">
+ <use
+ xlink:href="#glyph0-6"
+ x="562.63934"
+ y="98.922928"
+ id="use912"
+ width="100%"
+ height="100%" />
+ <use
+ xlink:href="#glyph0-1"
+ x="637.93146"
+ y="98.922928"
+ id="use914"
+ width="100%"
+ height="100%" />
+ <use
+ xlink:href="#glyph0-2"
+ x="692.47455"
+ y="98.922928"
+ id="use916"
+ width="100%"
+ height="100%" />
+ <use
+ xlink:href="#glyph0-3"
+ x="746.40137"
+ y="98.922928"
+ id="use918"
+ width="100%"
+ height="100%" />
+ <use
+ xlink:href="#glyph0-4"
+ x="804.026"
+ y="98.922928"
+ id="use920"
+ width="100%"
+ height="100%" />
+ <use
+ xlink:href="#glyph0-5"
+ x="845.52399"
+ y="98.922928"
+ id="use922"
+ width="100%"
+ height="100%" />
+ </g>
+ <g
+ clip-path="url(#clip55)"
+ clip-rule="nonzero"
+ id="g928">
+ <use
+ xlink:href="#image935"
+ mask="url(#mask25)"
+ transform="matrix(0.802138,0,0,0.802138,432.8,-0.123972)"
+ id="use926"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </g>
+ <path
+ style="fill:#1e7866;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 449.87109,2.960938 h 86.65625 c 7.6875,0 13.87891,6.1875 13.87891,13.878906 v 86.656246 c 0,7.6875 -6.19141,13.87891 -13.87891,13.87891 h -86.65625 c -7.6914,0 -13.8789,-6.19141 -13.8789,-13.87891 V 16.839844 c 0,-7.691406 6.1875,-13.878906 13.8789,-13.878906 z m 0,0"
+ id="path930" />
+ <path
+ style="fill:#ff7d28;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 449.87109,2.960938 h 86.65625 c 7.6875,0 13.87891,6.1875 13.87891,13.878906 v 86.656246 c 0,7.6875 -6.19141,13.87891 -13.87891,13.87891 h -86.65625 c -7.6914,0 -13.8789,-6.19141 -13.8789,-13.87891 V 16.839844 c 0,-7.691406 6.1875,-13.878906 13.8789,-13.878906 z m 0,0"
+ id="path932" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.566698;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -469.08203,23.070312 h 4.52344 c 1.80078,0 3.25,1.449219 3.25,3.253907 v 4.523437 c 0,1.800782 -1.44922,3.25 -3.25,3.25 h -4.52344 c -1.80078,0 -3.25391,-1.449218 -3.25391,-3.25 v -4.523437 c 0,-1.804688 1.45313,-3.253907 3.25391,-3.253907 z m 0,0"
+ transform="scale(-1,1)"
+ id="path934" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.566698;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -469.08203,37.265625 h 4.52344 c 1.80078,0 3.25,1.449219 3.25,3.25 V 95.21875 c 0,1.800781 -1.44922,3.253906 -3.25,3.253906 h -4.52344 c -1.80078,0 -3.25391,-1.453125 -3.25391,-3.253906 V 40.515625 c 0,-1.800781 1.45313,-3.25 3.25391,-3.25 z m 0,0"
+ transform="scale(-1,1)"
+ id="path936" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.566698;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -502.07031,23.070312 h 22.76562 c 1.80469,0 3.25391,1.449219 3.25391,3.253907 v 4.523437 c 0,1.800782 -1.44922,3.25 -3.25391,3.25 h -22.76562 c -1.80078,0 -3.25,-1.449218 -3.25,-3.25 v -4.523437 c 0,-1.804688 1.44922,-3.253907 3.25,-3.253907 z m 0,0"
+ transform="scale(-1,1)"
+ id="path938" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.566698;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -46.828125,-525.47266 c 13.140625,0 23.789063,10.65235 23.789063,23.79297 h -23.789063 z m 0,0"
+ transform="matrix(0,-1,-1,0,0,0)"
+ id="path940" />
+ <path
+ style="fill:#ff7d28;fill-opacity:1;fill-rule:nonzero;stroke:#ff7d28;stroke-width:0.806224;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -47.03125,-513.82422 c 6.761719,0 12.246094,5.48438 12.246094,12.2461 H -47.03125 Z m 0,0"
+ transform="matrix(0,-1,-1,0,0,0)"
+ id="path942" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.566698;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -522.23437,44.191406 h 4.52343 c 1.80078,0 3.25,1.449219 3.25,3.253906 v 33.972657 c 0,1.800781 -1.44922,3.253906 -3.25,3.253906 h -4.52343 c -1.80079,0 -3.25391,-1.453125 -3.25391,-3.253906 V 47.445312 c 0,-1.804687 1.45312,-3.253906 3.25391,-3.253906 z m 0,0"
+ transform="scale(-1,1)"
+ id="path944" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.566698;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -522.23437,87.390625 h 4.52343 c 1.80078,0 3.25,1.449219 3.25,3.253906 v 4.523438 c 0,1.800781 -1.44922,3.25 -3.25,3.25 h -4.52343 c -1.80079,0 -3.25391,-1.449219 -3.25391,-3.25 v -4.523438 c 0,-1.804687 1.45312,-3.253906 3.25391,-3.253906 z m 0,0"
+ transform="scale(-1,1)"
+ id="path946" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.566929;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1"
+ d="m 58.601562,-522.98437 h 4.527344 c 1.800782,0 3.25,1.45312 3.25,3.2539 v 54.72656 c 0,1.80079 -1.449218,3.25 -3.25,3.25 h -4.527344 c -1.800781,0 -3.25,-1.44921 -3.25,-3.25 v -54.72656 c 0,-1.80078 1.449219,-3.2539 3.25,-3.2539 z m 0,0"
+ transform="rotate(90)"
+ id="path948" />
+ <g
+ clip-path="url(#clip56)"
+ clip-rule="nonzero"
+ id="g952">
+ <use
+ xlink:href="#surface946"
+ mask="url(#mask26)"
+ id="use950"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </g>
+ </g>
+</svg>
diff --git a/doc/dev-doc/_static/logo_only_akantu.svg b/doc/dev-doc/_static/logo_only_akantu.svg
new file mode 100644
index 000000000..e60abdc60
--- /dev/null
+++ b/doc/dev-doc/_static/logo_only_akantu.svg
@@ -0,0 +1,1682 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="125.73566pt"
+ height="125.81169pt"
+ viewBox="0 0 125.73566 125.81169"
+ version="1.2"
+ id="svg1041"
+ sodipodi:docname="logo_only_akantu.svg"
+ inkscape:version="1.0.2 (e86c870879, 2021-01-15)">
+ <metadata
+ id="metadata1045">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1920"
+ inkscape:window-height="1053"
+ id="namedview1043"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:zoom="0.46161158"
+ inkscape:cx="299.46663"
+ inkscape:cy="512.39754"
+ inkscape:window-x="0"
+ inkscape:window-y="-9"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="surface741" />
+ <defs
+ id="defs562">
+ <g
+ id="g110">
+ <symbol
+ overflow="visible"
+ id="glyph0-0">
+ <path
+ style="stroke:none"
+ d=""
+ id="path2" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-1">
+ <path
+ style="stroke:none"
+ d="m 51.046875,-2.265625 c 0,0 0,-0.921875 -1.015625,-2.25 L 32.15625,-27.9375 47.96875,-42.625 c 1.234375,-1.140625 1.546875,-1.4375 1.546875,-2.265625 0,-2.15625 -2.0625,-2.15625 -3.5,-2.15625 H 40.0625 c -1.84375,0 -3.796875,0 -6.171875,2.15625 L 19.109375,-31.125 v -35.234375 c 0,-3.28125 -0.71875,-4.921875 -4.9375,-4.921875 h -2.15625 c -3.703125,0 -4.921875,1.125 -4.921875,4.921875 V -4.9375 c 0,3.703125 1.125,4.9375 4.921875,4.9375 h 1.84375 c 3.90625,0 4.9375,-1.328125 4.9375,-4.9375 v -10.671875 c 1.640625,-1.4375 3.1875,-2.984375 4.828125,-4.421875 L 36.984375,-2.5625 C 38.71875,-0.3125 39.4375,0 42.421875,0 h 4.9375 c 1.4375,0 3.6875,0 3.6875,-2.265625 z m 0,0"
+ id="path5" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-2">
+ <path
+ style="stroke:none"
+ d="m 48.6875,-4.9375 v -27.515625 c 0,-16.234375 -15.609375,-16.34375 -20.75,-16.34375 -4.828125,0 -9.96875,0.421875 -16.84375,3.296875 -2.15625,0.921875 -2.671875,1.234375 -2.671875,2.359375 0,0.71875 0.625,6.671875 0.71875,7.5 0.109375,0.609375 0.625,1.125 1.34375,1.125 0.5,0 0.8125,-0.3125 1.125,-0.609375 4.421875,-4.625 9.546875,-6.890625 15.921875,-6.890625 5.546875,0 7.1875,3.390625 7.1875,9.453125 v 3.59375 c -3.59375,0 -30.5,0.203125 -30.5,15.3125 0,7.1875 5.75,14.78125 14.78125,14.78125 3.5,0 11.203125,-1.015625 16.03125,-8.109375 V -4.9375 C 35.03125,-1.640625 35.75,0 39.953125,0 h 3.8125 C 47.65625,0 48.6875,-1.328125 48.6875,-4.9375 Z m -13.96875,-10.875 c 0,9.546875 -9.765625,9.546875 -10.0625,9.546875 -4.3125,0 -7.09375,-3.703125 -7.09375,-7.59375 0,-10.078125 14.28125,-10.796875 17.15625,-10.890625 z m 0,0"
+ id="path8" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-3">
+ <path
+ style="stroke:none"
+ d="m 51.359375,-4.9375 v -28.453125 c 0,-10.578125 -4.515625,-14.78125 -15,-14.78125 -10.78125,0 -15.40625,7.796875 -16.734375,10.578125 h -0.109375 v -5.140625 c 0,-3.28125 -0.71875,-4.921875 -4.921875,-4.921875 h -3.5 c -3.703125,0 -4.9375,1.125 -4.9375,4.921875 V -4.9375 C 6.15625,-1.234375 7.296875,0 11.09375,0 h 4.109375 c 3.90625,0 4.9375,-1.328125 4.9375,-4.9375 V -27.625 c 0,-8.9375 5.953125,-13.15625 11.296875,-13.15625 4.921875,0 5.953125,2.359375 5.953125,7.609375 V -4.9375 c 0,3.296875 0.71875,4.9375 4.921875,4.9375 h 4.109375 c 3.90625,0 4.9375,-1.328125 4.9375,-4.9375 z m 0,0"
+ id="path11" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-4">
+ <path
+ style="stroke:none"
+ d="m 38.3125,-4.828125 c 0,-0.3125 -0.203125,-1.03125 -0.8125,-3.1875 -0.515625,-2.046875 -0.734375,-2.671875 -1.65625,-2.671875 -0.515625,0 -0.609375,0.109375 -1.234375,0.71875 -1.125,0.828125 -3.890625,3.09375 -8.203125,3.09375 -2.46875,0 -4.015625,-1.75 -4.015625,-8.53125 v -24.25 h 9.140625 c 1.234375,0 4.9375,0 4.9375,-3.6875 0,-3.703125 -3.703125,-3.703125 -4.9375,-3.703125 h -9.140625 v -8.53125 c 0,-3.28125 -0.71875,-4.921875 -4.921875,-4.921875 h -3.1875 c -3.703125,0 -4.9375,1.125 -4.9375,4.921875 v 8.53125 H 6.984375 c -1.125,0 -4.9375,0 -4.9375,3.703125 0,3.6875 3.703125,3.6875 4.9375,3.6875 h 2.0625 v 26 c 0,10.375 3.6875,14.78125 10.78125,14.78125 0.921875,0 5.4375,0 10.78125,-1.640625 1.75,-0.515625 7.703125,-2.359375 7.703125,-4.3125 z m 0,0"
+ id="path14" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-5">
+ <path
+ style="stroke:none"
+ d="m 51.359375,-4.9375 v -37.171875 c 0,-3.296875 -0.71875,-4.9375 -4.9375,-4.9375 H 42.3125 c -3.890625,0 -4.921875,1.34375 -4.921875,4.9375 v 24.34375 c 0,6.78125 -3.59375,12.625 -10.890625,12.625 -5.75,0 -6.359375,-1.4375 -6.359375,-6.875 v -30.09375 c 0,-3.296875 -0.734375,-4.9375 -4.9375,-4.9375 H 11.09375 c -3.703125,0 -4.9375,1.125 -4.9375,4.9375 V -12.9375 c 0,10.984375 5.96875,14.0625 16.640625,14.0625 2.78125,0 10.078125,0 14.90625,-8.71875 V -4.9375 C 37.703125,-1.640625 38.421875,0 42.625,0 h 3.796875 c 3.90625,0 4.9375,-1.328125 4.9375,-4.9375 z m 0,0"
+ id="path17" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-6">
+ <path
+ style="stroke:none"
+ d="m 70.875,-2.765625 c 0,-0.515625 0,-0.734375 -0.515625,-2.0625 L 48.6875,-66.875 c -1.546875,-4.40625 -4.515625,-4.40625 -6.578125,-4.40625 h -9.03125 c -2.0625,0 -5.03125,0 -6.578125,4.40625 L 4.828125,-4.828125 C 4.3125,-3.5 4.3125,-3.28125 4.3125,-2.765625 4.3125,0 6.875,0 8.3125,0 h 3.703125 c 1.84375,0 5.03125,0 6.46875,-4.109375 L 22.5,-15.515625 H 50.953125 L 54.4375,-5.65625 C 55.671875,-2.046875 56.390625,0 61.328125,0 H 66.875 c 1.4375,0 4,0 4,-2.765625 z M 47.859375,-24.34375 H 25.578125 L 32.96875,-46.125 c 1.546875,-4.515625 2.875,-8.421875 3.703125,-12.109375 h 0.09375 c 0.515625,2.453125 0.515625,2.65625 1.34375,5.03125 z m 0,0"
+ id="path20" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-7">
+ <path
+ style="stroke:none"
+ d="m 54.859375,-4.9375 v -0.921875 c 0,-2.359375 -0.109375,-4.921875 -4.828125,-4.921875 L 24.453125,-10.375 v -55.984375 c 0,-3.28125 -0.71875,-4.921875 -4.9375,-4.921875 H 14.28125 c -3.703125,0 -4.9375,1.125 -4.9375,4.921875 V -4.9375 c 0,3.703125 1.140625,4.9375 4.9375,4.9375 h 35.640625 c 3.90625,0 4.9375,-1.328125 4.9375,-4.9375 z m 0,0"
+ id="path23" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-8">
+ <path
+ style="stroke:none"
+ d="m 19.921875,-4.9375 v -37.171875 c 0,-3.296875 -0.71875,-4.9375 -4.921875,-4.9375 h -3.796875 c -3.703125,0 -4.9375,1.125 -4.9375,4.9375 V -4.9375 c 0,3.703125 1.125,4.9375 4.9375,4.9375 H 15 c 3.90625,0 4.921875,-1.328125 4.921875,-4.9375 z m 0.71875,-57.3125 v -1.953125 c 0,-3.28125 -0.71875,-4.921875 -4.921875,-4.921875 h -5.234375 c -3.90625,0 -4.9375,1.328125 -4.9375,4.921875 V -62.25 c 0,2.984375 0.515625,4.9375 4.9375,4.9375 h 5.234375 c 4.3125,0 4.921875,-1.859375 4.921875,-4.9375 z m 0,0"
+ id="path26" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-9">
+ <path
+ style="stroke:none"
+ d="m 53.71875,-23.734375 c 0,-4.921875 0,-24.4375 -19.3125,-24.4375 -8.421875,0 -13.546875,4.71875 -14.484375,5.75 v -23.9375 c 0,-3.28125 -0.71875,-4.921875 -4.921875,-4.921875 h -3.796875 c -3.703125,0 -4.9375,1.125 -4.9375,4.921875 V -4.9375 c 0,3.703125 1.125,4.9375 4.9375,4.9375 H 15.3125 c 1.734375,0 4.828125,0 4.921875,-4 4.71875,5.125 9.96875,5.125 11.609375,5.125 21.875,0 21.875,-19.71875 21.875,-24.859375 z M 39.75,-23.625 c 0,7.59375 0,17.359375 -11.5,17.359375 -4.3125,0 -6.890625,-2.5625 -8.015625,-4 V -37.1875 c 1.34375,-1.234375 4.421875,-3.59375 9.140625,-3.59375 10.375,0 10.375,9.453125 10.375,17.15625 z m 0,0"
+ id="path29" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-10">
+ <path
+ style="stroke:none"
+ d="m 91.015625,-4.9375 v -61.421875 c 0,-3.28125 -0.71875,-4.921875 -4.9375,-4.921875 h -8.625 c -2.578125,0 -5.140625,0.203125 -6.890625,4.40625 l -13.546875,33.28125 c -2.15625,5.25 -5.96875,14.6875 -6.78125,18.28125 H 50.125 C 49.203125,-19 44.984375,-29.171875 42.828125,-34.609375 L 29.6875,-67.078125 c -1.75,-4.203125 -5.03125,-4.203125 -6.890625,-4.203125 H 14.28125 c -3.703125,0 -4.9375,1.125 -4.9375,4.921875 V -4.9375 c 0,3.703125 1.140625,4.9375 4.9375,4.9375 h 2.765625 c 3.90625,0 4.9375,-1.328125 4.9375,-4.9375 v -53.09375 h 0.09375 c 1.140625,4.625 5.765625,16.015625 8.21875,22.078125 l 11.921875,29.28125 c 1.84375,4.40625 4.3125,4.40625 7.8125,4.40625 3.484375,0 5.953125,0 7.796875,-4.40625 L 70.984375,-38.9375 C 72,-41.390625 77.546875,-55.359375 78.265625,-58.03125 H 78.375 V -4.9375 C 78.375,-1.640625 79.09375,0 83.3125,0 h 2.765625 c 3.90625,0 4.9375,-1.328125 4.9375,-4.9375 z m 0,0"
+ id="path32" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-11">
+ <path
+ style="stroke:none"
+ d="m 19.921875,-4.9375 v -61.421875 c 0,-3.28125 -0.71875,-4.921875 -4.921875,-4.921875 h -3.796875 c -3.703125,0 -4.9375,1.125 -4.9375,4.921875 V -4.9375 c 0,3.703125 1.125,4.9375 4.9375,4.9375 H 15 c 3.90625,0 4.921875,-1.328125 4.921875,-4.9375 z m 0,0"
+ id="path35" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-12">
+ <path
+ style="stroke:none"
+ d="m 56.390625,-20.546875 c 0,-11.5 -8.53125,-20.125 -18.796875,-22.5 l -9.859375,-2.25 c -2.265625,-0.515625 -8.625,-2.875 -8.625,-8.734375 0,-8.734375 8.734375,-8.9375 12.21875,-8.9375 5.75,0 10.890625,1.34375 15.921875,6.265625 1.640625,1.546875 1.75,1.640625 2.15625,1.640625 0.625,0 1.125,-0.296875 1.4375,-1.84375 l 1.546875,-8.9375 c 0.203125,-0.828125 0.203125,-1.03125 0.203125,-1.328125 0,-0.828125 -0.109375,-0.9375 -2.671875,-2.265625 -7.296875,-3.59375 -13.359375,-4.109375 -18.59375,-4.109375 -9.140625,0 -25.0625,2.15625 -25.0625,21.359375 0,7.296875 3.796875,12.03125 5.75,14.078125 5.234375,5.34375 10.0625,6.375 18.078125,8.21875 5.75,1.328125 8.015625,1.84375 10.28125,3.90625 1.015625,1.03125 3.171875,3.1875 3.171875,7.078125 0,9.65625 -8.828125,9.96875 -12.21875,9.96875 -8.625,0 -16.125,-3.484375 -21.265625,-8.109375 -1.234375,-1.234375 -1.4375,-1.234375 -1.84375,-1.234375 -0.625,0 -1.125,0.3125 -1.4375,1.84375 L 5.234375,-7.5 C 5.03125,-6.671875 5.03125,-6.46875 5.03125,-6.15625 c 0,2.15625 10.6875,5.84375 11.203125,6.046875 6.875,2.15625 12.421875,2.375 15.09375,2.375 15.40625,0 25.0625,-6.890625 25.0625,-22.8125 z m 0,0"
+ id="path38" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-13">
+ <path
+ style="stroke:none"
+ d="m 46.9375,-5.140625 c 0,-1.125 -0.40625,-5.84375 -0.609375,-7.1875 0,-0.3125 -0.203125,-1.328125 -1.234375,-1.328125 -0.40625,0 -0.609375,0 -1.640625,1.015625 -1.75,1.546875 -6.578125,5.765625 -15,5.765625 -10.6875,0 -10.6875,-10.375 -10.6875,-16.953125 0,-8.328125 0.515625,-16.953125 11,-16.953125 6.671875,0 9.453125,1.640625 13.140625,4.734375 1.234375,1.125 1.4375,1.125 1.859375,1.125 1.015625,0 1.21875,-0.921875 1.328125,-1.34375 0.203125,-0.8125 1.234375,-6.5625 1.234375,-7.078125 0,-0.921875 -0.71875,-1.34375 -2.46875,-2.15625 -4.921875,-2.265625 -8.015625,-3.296875 -15.203125,-3.296875 -15.296875,0 -24.859375,7.203125 -24.859375,25.171875 0,17.046875 8.84375,24.75 24.34375,24.75 2.78125,0 8.9375,-0.09375 15.828125,-3.59375 C 46.84375,-4 46.9375,-4.109375 46.9375,-5.140625 Z m 0,0"
+ id="path41" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-14">
+ <path
+ style="stroke:none"
+ d="m 49.203125,-26.703125 c 0,-13.25 -6.15625,-22.09375 -21.46875,-22.09375 -16.4375,0 -24.546875,9.25 -24.546875,24.859375 0,16.84375 9.75,25.0625 25.875,25.0625 3.90625,0 9.765625,-0.515625 16.546875,-3.890625 2.25,-1.140625 2.984375,-1.453125 2.984375,-2.671875 0,-0.71875 -0.3125,-3.703125 -0.421875,-4.53125 -0.40625,-3.171875 -0.515625,-3.28125 -1.4375,-3.28125 -0.40625,0 -0.609375,0 -1.75,1.03125 -6.046875,5.234375 -12.21875,5.953125 -15.5,5.953125 -11.609375,0 -12.84375,-9.140625 -13.15625,-15.71875 h 27.9375 c 2.375,0 4.9375,-0.09375 4.9375,-4.71875 z m -11.40625,-0.71875 H 16.4375 c 0.3125,-6.265625 2.359375,-13.96875 11.296875,-13.96875 8.21875,0 9.765625,5.953125 10.0625,13.96875 z m 0,0"
+ id="path44" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-0">
+ <path
+ style="stroke:none"
+ d=""
+ id="path47" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-1">
+ <path
+ style="stroke:none"
+ d="m 70.875,-2.765625 c 0,-0.515625 0,-0.734375 -0.515625,-2.0625 L 48.6875,-66.875 c -1.546875,-4.40625 -4.515625,-4.40625 -6.578125,-4.40625 h -9.03125 c -2.0625,0 -5.03125,0 -6.578125,4.40625 L 4.828125,-4.828125 C 4.3125,-3.5 4.3125,-3.28125 4.3125,-2.765625 4.3125,0 6.875,0 8.3125,0 h 3.703125 c 1.84375,0 5.03125,0 6.46875,-4.109375 L 22.5,-15.515625 H 50.953125 L 54.4375,-5.65625 C 55.671875,-2.046875 56.390625,0 61.328125,0 H 66.875 c 1.4375,0 4,0 4,-2.765625 z M 47.859375,-24.34375 H 25.578125 L 32.96875,-46.125 c 1.546875,-4.515625 2.875,-8.421875 3.703125,-12.109375 h 0.09375 c 0.515625,2.453125 0.515625,2.65625 1.34375,5.03125 z m 0,0"
+ id="path50" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-2">
+ <path
+ style="stroke:none"
+ d="m 54.859375,-4.9375 v -0.921875 c 0,-2.359375 -0.109375,-4.921875 -4.828125,-4.921875 L 24.453125,-10.375 v -55.984375 c 0,-3.28125 -0.71875,-4.921875 -4.9375,-4.921875 H 14.28125 c -3.703125,0 -4.9375,1.125 -4.9375,4.921875 V -4.9375 c 0,3.703125 1.140625,4.9375 4.9375,4.9375 h 35.640625 c 3.90625,0 4.9375,-1.328125 4.9375,-4.9375 z m 0,0"
+ id="path53" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-0">
+ <path
+ style="stroke:none"
+ d=""
+ id="path56" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-1">
+ <path
+ style="stroke:none"
+ d="m 5.515625,-0.21875 c 0,-0.03125 0,-0.046875 -0.03125,-0.15625 l -1.6875,-4.828125 C 3.671875,-5.546875 3.4375,-5.546875 3.28125,-5.546875 H 2.578125 c -0.15625,0 -0.390625,0 -0.515625,0.34375 L 0.375,-0.375 C 0.34375,-0.265625 0.34375,-0.25 0.34375,-0.21875 0.34375,0 0.53125,0 0.640625,0 H 0.9375 c 0.140625,0 0.390625,0 0.5,-0.3125 L 1.75,-1.203125 H 3.96875 L 4.234375,-0.4375 C 4.34375,-0.15625 4.390625,0 4.78125,0 h 0.421875 c 0.109375,0 0.3125,0 0.3125,-0.21875 z m -1.78125,-1.671875 h -1.75 L 2.5625,-3.59375 C 2.6875,-3.9375 2.796875,-4.25 2.859375,-4.53125 2.90625,-4.34375 2.90625,-4.328125 2.96875,-4.140625 Z m 0,0"
+ id="path59" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-2">
+ <path
+ style="stroke:none"
+ d="m 5.609375,-0.234375 -2.234375,-3 2.09375,-1.984375 C 5.5625,-5.296875 5.5625,-5.359375 5.5625,-5.390625 c 0,-0.15625 -0.15625,-0.15625 -0.265625,-0.15625 H 4.8125 c -0.140625,0 -0.296875,0 -0.46875,0.15625 l -2.578125,2.4375 v -2.21875 c 0,-0.25 -0.046875,-0.375 -0.375,-0.375 h -0.28125 c -0.28125,0 -0.375,0.078125 -0.375,0.375 v 4.78125 C 0.734375,-0.09375 0.8125,0 1.109375,0 h 0.28125 c 0.296875,0 0.375,-0.109375 0.375,-0.390625 v -1.34375 L 2.65625,-2.5625 4.453125,-0.171875 C 4.578125,0 4.703125,0 4.84375,0 H 5.328125 C 5.5,0 5.609375,0 5.609375,-0.234375 Z m 0,0"
+ id="path62" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-3">
+ <path
+ style="stroke:none"
+ d="m 5.609375,-0.390625 v -4.78125 c 0,-0.25 -0.046875,-0.375 -0.375,-0.375 h -0.21875 c -0.296875,0 -0.390625,0.09375 -0.390625,0.375 V -0.96875 L 2.421875,-5.234375 c -0.171875,-0.3125 -0.40625,-0.3125 -0.5625,-0.3125 h -0.75 c -0.28125,0 -0.375,0.078125 -0.375,0.375 v 4.78125 C 0.734375,-0.09375 0.8125,0 1.109375,0 h 0.21875 C 1.625,0 1.71875,-0.109375 1.71875,-0.390625 v -4.1875 L 3.921875,-0.3125 C 4.09375,0 4.328125,0 4.484375,0 h 0.75 c 0.296875,0 0.375,-0.109375 0.375,-0.390625 z m 0,0"
+ id="path65" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-4">
+ <path
+ style="stroke:none"
+ d="M 5.53125,-5.0625 V -5.125 c 0,-0.25 -0.046875,-0.375 -0.375,-0.375 H 0.703125 C 0.421875,-5.5 0.3125,-5.421875 0.3125,-5.125 v 0.0625 c 0,0.390625 0.203125,0.390625 0.421875,0.390625 l 1.609375,-0.03125 v 4.3125 C 2.34375,-0.09375 2.4375,0 2.734375,0 h 0.40625 c 0.296875,0 0.375,-0.109375 0.375,-0.390625 v -4.3125 L 5.125,-4.671875 c 0.21875,0 0.40625,0 0.40625,-0.390625 z m 0,0"
+ id="path68" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-5">
+ <path
+ style="stroke:none"
+ d="m 5.375,-1.828125 v -3.34375 c 0,-0.25 -0.0625,-0.375 -0.390625,-0.375 h -0.3125 c -0.296875,0 -0.390625,0.09375 -0.390625,0.375 V -1.8125 c 0,0.78125 -0.21875,1.296875 -1.203125,1.296875 -0.921875,0 -1.171875,-0.46875 -1.171875,-1.296875 v -3.359375 c 0,-0.25 -0.0625,-0.375 -0.390625,-0.375 h -0.40625 c -0.28125,0 -0.375,0.078125 -0.375,0.375 v 3.328125 c 0,1.6875 1.328125,2.015625 2.34375,2.015625 0.90625,0 2.296875,-0.265625 2.296875,-2 z m 0,0"
+ id="path71" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-6">
+ <path
+ style="stroke:none"
+ d="m 4.265625,-0.390625 v -0.0625 c 0,-0.1875 0,-0.390625 -0.375,-0.390625 L 1.90625,-0.8125 v -4.359375 c 0,-0.25 -0.0625,-0.375 -0.390625,-0.375 h -0.40625 c -0.28125,0 -0.375,0.078125 -0.375,0.375 v 4.78125 C 0.734375,-0.09375 0.8125,0 1.109375,0 h 2.78125 c 0.296875,0 0.375,-0.109375 0.375,-0.390625 z m 0,0"
+ id="path74" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-7">
+ <path
+ style="stroke:none"
+ d="m 1.90625,-0.390625 v -4.78125 c 0,-0.25 -0.046875,-0.375 -0.375,-0.375 H 1.125 c -0.296875,0 -0.390625,0.078125 -0.390625,0.375 v 4.78125 C 0.734375,-0.09375 0.828125,0 1.125,0 h 0.40625 c 0.296875,0 0.375,-0.109375 0.375,-0.390625 z m 0,0"
+ id="path77" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-8">
+ <path
+ style="stroke:none"
+ d="m 5.375,-1.546875 c 0,-1.140625 -1.125,-1.3125 -1.421875,-1.359375 0.75,-0.171875 1.171875,-0.578125 1.171875,-1.203125 0,-1.4375 -1.703125,-1.4375 -2.046875,-1.4375 h -1.96875 c -0.28125,0 -0.375,0.078125 -0.375,0.375 v 4.78125 C 0.734375,-0.09375 0.8125,0 1.109375,0 h 2.1875 C 3.890625,0 5.375,-0.109375 5.375,-1.546875 Z M 4.03125,-4.09375 c 0,0.921875 -1.046875,0.921875 -1.25,0.921875 H 1.859375 v -1.6875 h 0.9375 c 0.3125,0 1.234375,0.015625 1.234375,0.765625 z M 4.25,-1.5625 c 0,0.8125 -0.84375,0.875 -1.234375,0.875 h -1.15625 v -1.90625 h 1 c 0.28125,0 1.390625,0 1.390625,1.03125 z m 0,0"
+ id="path80" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-9">
+ <path
+ style="stroke:none"
+ d="m 7.09375,-0.390625 v -4.78125 c 0,-0.25 -0.0625,-0.375 -0.390625,-0.375 H 6.03125 c -0.203125,0 -0.40625,0.015625 -0.53125,0.34375 l -1.0625,2.59375 C 4.265625,-2.203125 3.96875,-1.46875 3.90625,-1.1875 3.828125,-1.484375 3.5,-2.265625 3.34375,-2.703125 L 2.3125,-5.21875 C 2.171875,-5.546875 1.921875,-5.546875 1.78125,-5.546875 H 1.109375 c -0.28125,0 -0.375,0.078125 -0.375,0.375 v 4.78125 C 0.734375,-0.09375 0.8125,0 1.109375,0 h 0.21875 C 1.625,0 1.71875,-0.109375 1.71875,-0.390625 v -4.125 c 0.09375,0.359375 0.453125,1.25 0.640625,1.71875 l 0.921875,2.28125 c 0.15625,0.34375 0.34375,0.34375 0.609375,0.34375 0.28125,0 0.46875,0 0.609375,-0.34375 L 5.53125,-3.03125 c 0.078125,-0.1875 0.515625,-1.28125 0.5625,-1.484375 h 0.015625 v 4.125 C 6.109375,-0.125 6.15625,0 6.484375,0 h 0.21875 c 0.3125,0 0.390625,-0.109375 0.390625,-0.390625 z m 0,0"
+ id="path83" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-10">
+ <path
+ style="stroke:none"
+ d="m 4.390625,-1.59375 c 0,-0.90625 -0.65625,-1.578125 -1.46875,-1.765625 L 2.15625,-3.53125 C 1.984375,-3.5625 1.484375,-3.75 1.484375,-4.203125 c 0,-0.6875 0.6875,-0.703125 0.953125,-0.703125 0.453125,0 0.84375,0.109375 1.25,0.484375 0.125,0.125 0.125,0.140625 0.15625,0.140625 0.046875,0 0.09375,-0.03125 0.109375,-0.15625 l 0.125,-0.6875 C 4.09375,-5.1875 4.09375,-5.203125 4.09375,-5.234375 c 0,-0.0625 0,-0.0625 -0.203125,-0.171875 C 3.3125,-5.6875 2.84375,-5.734375 2.4375,-5.734375 c -0.703125,0 -1.953125,0.171875 -1.953125,1.671875 0,0.5625 0.296875,0.9375 0.453125,1.09375 0.40625,0.421875 0.78125,0.5 1.40625,0.640625 0.453125,0.109375 0.625,0.140625 0.796875,0.296875 0.078125,0.09375 0.25,0.25 0.25,0.5625 0,0.75 -0.6875,0.765625 -0.953125,0.765625 -0.671875,0 -1.25,-0.265625 -1.65625,-0.625 -0.09375,-0.09375 -0.109375,-0.09375 -0.140625,-0.09375 -0.046875,0 -0.09375,0.015625 -0.109375,0.140625 l -0.125,0.703125 c -0.015625,0.0625 -0.015625,0.078125 -0.015625,0.09375 0,0.171875 0.828125,0.453125 0.875,0.46875 0.53125,0.171875 0.96875,0.1875 1.171875,0.1875 1.203125,0 1.953125,-0.53125 1.953125,-1.765625 z m 0,0"
+ id="path86" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-11">
+ <path
+ style="stroke:none"
+ d="m 5.171875,-0.46875 -0.0625,-0.6875 c 0,-0.046875 -0.046875,-0.078125 -0.09375,-0.078125 -0.03125,0 -0.046875,0 -0.078125,0.015625 C 4.734375,-1.0625 4.5,-0.890625 4.234375,-0.8125 3.96875,-0.71875 3.6875,-0.71875 3.421875,-0.71875 c -0.5,0 -1,-0.125 -1.3125,-0.5 -0.359375,-0.421875 -0.453125,-1 -0.453125,-1.5625 0,-0.5625 0.09375,-1.125 0.453125,-1.5625 0.3125,-0.375 0.8125,-0.5 1.3125,-0.5 0.234375,0 0.484375,0.03125 0.71875,0.109375 C 4.375,-4.640625 4.5625,-4.5 4.75,-4.34375 c 0.015625,0.03125 0.046875,0.03125 0.0625,0.03125 0.0625,0 0.09375,-0.046875 0.109375,-0.078125 L 5.078125,-5.25 c 0,-0.0625 -0.03125,-0.09375 -0.0625,-0.109375 C 4.75,-5.453125 4.484375,-5.53125 4.21875,-5.578125 3.953125,-5.625 3.6875,-5.640625 3.421875,-5.640625 c -0.8125,0 -1.625,0.15625 -2.203125,0.71875 C 0.65625,-4.375 0.484375,-3.5625 0.484375,-2.78125 c 0,0.796875 0.171875,1.59375 0.734375,2.15625 0.578125,0.546875 1.390625,0.71875 2.203125,0.71875 0.296875,0 0.59375,-0.015625 0.875,-0.09375 0.296875,-0.078125 0.5625,-0.21875 0.828125,-0.375 0.015625,-0.015625 0.046875,-0.046875 0.046875,-0.09375 z m 0,0"
+ id="path89" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-12">
+ <path
+ style="stroke:none"
+ d="M 4.765625,-0.390625 V -0.5 c 0,-0.375 -0.1875,-0.375 -0.453125,-0.375 L 3.078125,-0.859375 1.90625,-0.84375 v -1.640625 h 2.109375 c 0.234375,0 0.375,-0.0625 0.375,-0.375 0,-0.296875 -0.109375,-0.375 -0.375,-0.375 H 1.90625 V -4.71875 h 0.390625 l 1.875,0.015625 c 0.28125,0 0.46875,0 0.46875,-0.375 v -0.0625 C 4.640625,-5.40625 4.578125,-5.53125 4.25,-5.53125 H 1.109375 c -0.28125,0 -0.375,0.09375 -0.375,0.390625 v 4.75 C 0.734375,-0.09375 0.8125,0 1.109375,0 H 4.375 c 0.3125,0 0.390625,-0.109375 0.390625,-0.390625 z m 0,0"
+ id="path92" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-0">
+ <path
+ style="stroke:none"
+ d="M 5.734375,20.359375 V -81.234375 H 63.34375 v 101.59375 z m 6.46875,-6.40625 h 44.71875 v -88.71875 h -44.71875 z m 0,0"
+ id="path95" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-1">
+ <path
+ style="stroke:none"
+ d="m 14.28125,-9.5625 h 18.5625 v -64.078125 l -20.1875,4.0625 V -79.9375 l 20.078125,-4.046875 H 44.09375 V -9.5625 H 62.671875 V 0 H 14.28125 Z m 0,0"
+ id="path98" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-2">
+ <path
+ style="stroke:none"
+ d="m 22.109375,-9.5625 h 39.65625 V 0 H 8.4375 v -9.5625 c 4.3125,-4.457031 10.1875,-10.445312 17.625,-17.96875 7.445312,-7.519531 12.128906,-12.367188 14.046875,-14.546875 3.632813,-4.082031 6.171875,-7.539063 7.609375,-10.375 1.445312,-2.832031 2.171875,-5.617187 2.171875,-8.359375 0,-4.457031 -1.570313,-8.09375 -4.703125,-10.90625 -3.125,-2.8125 -7.199219,-4.21875 -12.21875,-4.21875 -3.5625,0 -7.324219,0.621094 -11.28125,1.859375 -3.960938,1.230469 -8.1875,3.105469 -12.6875,5.625 V -79.9375 c 4.570312,-1.832031 8.84375,-3.21875 12.8125,-4.15625 3.976562,-0.9375 7.617188,-1.40625 10.921875,-1.40625 8.695313,0 15.632813,2.179688 20.8125,6.53125 5.175781,4.34375 7.765625,10.152344 7.765625,17.421875 0,3.460937 -0.648438,6.734375 -1.9375,9.828125 -1.292969,3.09375 -3.648438,6.742188 -7.0625,10.9375 -0.9375,1.085938 -3.921875,4.226562 -8.953125,9.421875 -5.023437,5.199219 -12.105469,12.464844 -21.25,21.796875 z m 0,0"
+ id="path101" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-3">
+ <path
+ style="stroke:none"
+ d="m 46.75,-45.28125 c 5.4375,1.167969 9.679688,3.589844 12.734375,7.265625 3.0625,3.667969 4.59375,8.203125 4.59375,13.609375 0,8.28125 -2.855469,14.695312 -8.5625,19.234375 C 49.816406,-0.640625 41.71875,1.625 31.21875,1.625 27.695312,1.625 24.066406,1.273438 20.328125,0.578125 16.597656,-0.109375 12.75,-1.144531 8.78125,-2.53125 V -13.5 c 3.144531,1.835938 6.59375,3.21875 10.34375,4.15625 3.75,0.9375 7.664062,1.40625 11.75,1.40625 7.125,0 12.550781,-1.40625 16.28125,-4.21875 3.738281,-2.8125 5.609375,-6.894531 5.609375,-12.25 0,-4.957031 -1.734375,-8.832031 -5.203125,-11.625 -3.46875,-2.789062 -8.296875,-4.1875 -14.484375,-4.1875 H 23.28125 v -9.34375 h 10.25 c 5.582031,0 9.851562,-1.113281 12.8125,-3.34375 2.96875,-2.226562 4.453125,-5.441406 4.453125,-9.640625 0,-4.3125 -1.53125,-7.617187 -4.59375,-9.921875 -3.054687,-2.3125 -7.429687,-3.46875 -13.125,-3.46875 -3.117187,0 -6.453125,0.339844 -10.015625,1.015625 -3.5625,0.667969 -7.480469,1.714844 -11.75,3.140625 v -10.125 C 15.625,-83.101562 19.660156,-84 23.421875,-84.59375 27.191406,-85.195312 30.75,-85.5 34.09375,-85.5 c 8.625,0 15.445312,1.960938 20.46875,5.875 5.03125,3.917969 7.546875,9.214844 7.546875,15.890625 0,4.65625 -1.335937,8.589844 -4,11.796875 -2.667969,3.199219 -6.453125,5.417969 -11.359375,6.65625 z m 0,0"
+ id="path104" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-4">
+ <path
+ style="stroke:none"
+ d="M 43.53125,-74.078125 14.84375,-29.25 h 28.6875 z m -2.96875,-9.90625 H 54.84375 V -29.25 h 11.984375 v 9.453125 H 54.84375 V 0 H 43.53125 V -19.796875 H 5.625 v -10.96875 z m 0,0"
+ id="path107" />
+ </symbol>
+ </g>
+ <clipPath
+ id="clip1">
+ <path
+ d="M 435.19922,768 H 561 V 893.8125 H 435.19922 Z m 0,0"
+ id="path112" />
+ </clipPath>
+ <image
+ id="image747"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask0">
+ <use
+ xlink:href="#image747"
+ id="use116"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image746"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip2">
+ <path
+ d="M 0,0 H 1315.1992 V 1392.4063 H 0 Z m 0,0"
+ id="path120" />
+ </clipPath>
+ <filter
+ id="alpha"
+ filterUnits="objectBoundingBox"
+ x="0"
+ y="0"
+ width="1"
+ height="1">
+ <feColorMatrix
+ type="matrix"
+ in="SourceGraphic"
+ values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
+ id="feColorMatrix123" />
+ </filter>
+ <image
+ id="image757"
+ width="1316"
+ height="1393"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABSQAAAVxCAYAAACeLII5AAAABmJLR0QA/wD/AP+gvaeTAAAgAElEQVR4nOzBAQEAAACAkP6v7ggKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA27uDlNhiKIqiN4/4HarjdhSCCP6OU3BHyFpQ/bQ3574CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPij1s8PAAAAAOC3fe+Z+Xf6FQAAAADAFT4FSQAAAACg8rVn5vX0KwAAAACAK3xYSAIAAAAAlSVIAgAAAAAVQRIAAAAAyDy+IQkAAAAAVCwkAQAAAICMIAkAAAAAZARJAAAAACDjG5IAAAAAQMZCEgAAAADICJIAAAAAQGY52QYAAAAAKo+FJAAAAABQcbINAAAAAGScbAMAAAAAGSfbAAAAAEDGyTYAAAAAkHGyDQAAAABkLCQBAAAAgIxvSAIAAAAAGSfbAAAAAEDGyTYAAAAAkBEkAQAAAIDM42QbAAAAAKhYSAIAAAAAGUESAAAAAMj4l20AAAAAIPNYSAIAAAAAFSfbAAAAAEDGyTYAAAAAkHn2zLycfgUAAAAAcIUlSAIAAAAAlbVnZp9+BQAAAABwBwtJAAAAAKBiIQkAAAAAZHxDEgAAAADIWEgCAAAAABkLSQAAAAAgYyEJAAAAAGQsJAEAAACAjIUkAAAAAJBZe2ae068AAAAAAO4gRgIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAEWAGZwAAADrSURBVABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAILNn5u30IwAAAACAK7z/B6OjCui6lNXsAAAAAElFTkSuQmCC" />
+ <mask
+ id="mask1">
+ <g
+ filter="url(#alpha)"
+ id="g129">
+ <use
+ xlink:href="#image757"
+ id="use127"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </g>
+ </mask>
+ <clipPath
+ id="clip4">
+ <path
+ d="M 438,770 H 553 V 885 H 438 Z m 0,0"
+ id="path132" />
+ </clipPath>
+ <clipPath
+ id="clip5">
+ <path
+ d="m 452.13281,770.49609 h 86.65625 c 7.69141,0 13.87891,6.1875 13.87891,13.87891 v 86.65625 c 0,7.6875 -6.1875,13.87891 -13.87891,13.87891 h -86.65625 c -7.6875,0 -13.8789,-6.19141 -13.8789,-13.87891 V 784.375 c 0,-7.69141 6.1914,-13.87891 13.8789,-13.87891 z m 0,0"
+ id="path135" />
+ </clipPath>
+ <clipPath
+ id="clip3">
+ <rect
+ x="0"
+ y="0"
+ width="1316"
+ height="1393"
+ id="rect138" />
+ </clipPath>
+ <g
+ id="surface756"
+ clip-path="url(#clip3)">
+ <g
+ clip-path="url(#clip4)"
+ clip-rule="nonzero"
+ id="g145">
+ <g
+ clip-path="url(#clip5)"
+ clip-rule="nonzero"
+ id="g143">
+ <path
+ style="fill:#303030;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="M 438.25391,770.49609 V 884.91016 H 552.66797 V 770.49609 Z m 0,0"
+ id="path141" />
+ </g>
+ </g>
+ </g>
+ <clipPath
+ id="clip6">
+ <path
+ d="M 564,789.47656 H 827 V 873 H 564 Z m 0,0"
+ id="path148" />
+ </clipPath>
+ <image
+ id="image762"
+ width="329"
+ height="104"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask2">
+ <use
+ xlink:href="#image762"
+ id="use152"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image761"
+ width="329"
+ height="104"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip7">
+ <path
+ d="M 291.19922,767.07422 H 417 V 893 H 291.19922 Z m 0,0"
+ id="path156" />
+ </clipPath>
+ <image
+ id="image768"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJ0AAACdCAAAAACLTSUKAAAAAmJLR0QA/4ePzL8AAAYYSURBVHic7ZxJb+M4EEarihQlWd6SON3z//9dd5aObVkbyao5ULLd6QEGINBSDnyIEYTJ4aHI4pLDh/AJDB/8PP53kbvPJ5nffkLA8DWbGQAICIiAyCc//bscIiIhIc7pJyAiIizCn+p3Z4eASESkSBHRfHoCIsLs2bNn+a1+NwNEREU6oIgQ5yqeCLP3zjnrvPcsctXD63ckUlobkxtjMq0UzWUnIuydtUM/DIO13gtPevomp3Rm8rIsy6IwmVZECH+/dyWUztmh79u2bbveWg9T9fRVTmtTlNV6va5WRWG0UjNNrYB4drbvmqau60vTonU8rr2pdqSzvKw2u912u6nKwoy1+/t+AmFi+66pT6fj0WjVofOCMtkhksryar3fPz7s95uqKDKtZupaAQgz2zXn00dV5poARIBRgh0iqiyvtvunw+Fpv9usCpOp0LNz1G4qXlufNlWRKQQQcHKrndKmXO8P35+fD/vtqjRZWHUzrTsQZu9st15XZZ4pBGFhYRDQY+mKavf07Z9vz4+7dZFrTTTnWRb2lNWqLExGKN579iICoAEBlTLl5uHw7fu3w35d5lqPW/FctQMBFu+yLNMKxVtrrWMREA2ARFle7R4Pz4fDw7bMs7ka4l5RidJKK0Jxtu/73jqPYWZJm3K9fXh8etxvqzxTs85qAEHC7QO87dumaXvrGUEjBrvd/vFhty7zTM+zz/0pKIgI7Ib2Up/rplNewszqvFxvd9vtJLeAGyAAKODCbprzcXMs2gEBNCApbcpqs92sinHNLQMCkDbFar3ZrFfBRAMiaVOsqmpV5lrRUm4AgEBKm7ysqir0JmhEUtoUZVkWRi81rVc9JG2KclUWuVaESOEGYPIiN4utuTu/cI/L80wrRCDAMGLCwb+w3E1GEyESBj2ttVYLuwU/UlrrTBMhAAEgkVJKqfASW1gulG+0AQoDSilabi/5XY+IVDg3kMIAjiwtFx78owsC3QYWbtcJxNEmrLswAF+hcAFEGG1oHPgihRuZKkXLavwPyS6eZBdPsosn2cWT7OJJdvEku3iSXTzJLp5kF0+yiyfZxZPs4kl28SS7eJJdPMkunmQXT7KLJ9nFk+ziSXbxJLt4kl08yS6eZBdPsosn2cWT7OJJdvEku3iSXTzJLp5kF0+yiyfZxZPs4kl28SS7eJJdPMkunmQXT7KLJ9nFk+ziSXbxJLt4kl08yS6eYCciIvI/fzofVxsCAJEQ/PVF9MYMNxEAmvKiZMzuWxqRu5kMtRNm5q/gBhAy3ZhFRIRCVBSz/xp6EnQ8s0CYWWF2zjnvl+8MAWHvvXOemQVoTO4brHXMsnBnTHFzg3OeRYQAhL0dhn6w1i+vx97ZoR9667wAEIh4Z/uu63rrPC8qJyLs7dC1bT84zyIkwuyGvrlcmm5wiy49AeEQ9BWSvmSs3dA2dX1p+kX1BISd7dtLXV9GOw0g7Pq2Pp9O6zLThGqRFDIAAQ5y59OpvnSDZxEFUwiTyaccTQSY2zCcV872l+P768+fLx91N3gGBQiEREpnWQhhggUO3Jvc+dfrj58vb8dLZ0OGG7C33eVYFiZTCFwYPWfK7CTH7G3fnN/fXl/fPup2cCwAGgTE274xJssUiHe2MLPFaU5yIuK97dvz8e3Hj9f3Y90NnsdETRFvW61D5uGwWRUzh35JSPzs28vx18vPHy+/Tk3vWEJmpQCDRSJE8bZvm3U5hZHOZRcq113OH++vLy9vH3Vnw7mgw6+9RQTwtm8v501VhkzN2fKNmb0buqY+fby/vb8fz2HVTTOLwt5iWH/1cVOtcjMVb5aMXvF+GNqmPh0/Po6nOmzFt9oBeBAJXbNZV2Vu5k4QHoa2udTnU1033TDJjRm9AuAFmG3f1sfVKs9nbNuQRGqHvm2bS9N2/eCvl5FbNjQR6czkeZHneZbNmw3tvbND33chhtRfs6uvBiG6WmU6M5nWmuZLmg2Z385Za611nvl2EblL/b4G441hePP1bIj99t778Lr5j0zyyQ+JJrfZEoRlehaywP31/HMW/pg+h7Omusn9K/b+CvJZAce0uRlvUTIKwp//j/gXPCmyvzDydeIAAAAASUVORK5CYII=" />
+ <mask
+ id="mask3">
+ <use
+ xlink:href="#image768"
+ id="use160"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image767"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJ0AAACdCAIAAAAhRO2BAAAABmJLR0QA/wD/AP+gvaeTAAAMzklEQVR4nO2azW7bRhCAZ/lrSrJg0ahhGQGCouhFbA9FDzr1UEA59AHs17H8GH2F+NJDgF4CpDkUgQ7pqdSlyKUFqAIGGFfWH6ldsoexJhtKcWynsNPNfAeZsihK2o8zszukgFtRliUAnJyc4NM4jnHj8PDwdgdkTk9PcSOKIvrn8fExAAghbnq0m72hLEt02el0hsMhALTb7dFoBACDwQAAfvjhh5t+Awb5+eefAaDb7QJAu91++fLlwcFBp9M5PT2Nouj4+PhGdq+7KxpFne12+6effsJvMJlMGo3GbDar1WoAMJ/Pcf9Wq3WjX/Up8/r1awAIggAAcCRxVOM4/u6779I0TZKk1+thIF1T8Pv3IKNPnz7966+/Go3Gw4cPG40GAGRZtr29PZlMZrPZzs7OcrkEANd1P/B3foLQ0Lmui9sA4Pv++fk5+g7D0Pf90WgUx3EURf1+/+oDvsdrv9/HAw0Gg0ePHv3999+WZU2n0/39/bIsF4tFlmX1eh13VkrZtq2U+sAf+WlCQ+d5nlJKSrm1tTWfz8uy9DwPVqmx3W63Wq3hcHh14L7zBSqlABDH8eeff95qtS4uLmq1mpSSdrMsCwCKoiiKAp/iBnMjcNxoMB3HwafL5dK2bdptPp8HQTAYDLrdbhzHjx8/fpfazf8lqYPBIIoizLqO40gpMShJnm3bODemD8CniH4GMOs4joMbQgh93IQQSimSjRtSyiAIlssl1uB2u/306dN35eQNXvVIJaSU9CVgbeZdFAV+M3q87S/9FKHAwA3LsvQxzPPctm0cfAwnz/PyPG82m/V6HXPyulqn8lyXiucFhqnneZhsMS2UZal/Nn0n+qcQghPydcDcq6vFIKHRE0JgUizLEgM3z3MAGI/H4/EYZ1LrsWRVPgalhmGIUi8uLrIsy7JMSimEwFOJpOKxKk+Bpd4cNEqpWGjoVW+9rjWbzV6vd3R0pKdxALD1J/1+v9PpPHnyZHd313VdnCW5rmvbNimEVaIA7SzDt1MCQSqfxGxEjzM9VKiiVQIGADBr0uD/+uuvrVbrt99+++WXX94cirbKsjw6OoqiCOfTjuNkWea6rpTSsqz1wo4nkV5WaTLM9fVGVMIUVnW0khFxH8rbSqmyLLHuhmGYpikAUKF9E69CiL29vTiOHzx4gIfG+MMDwdvRuR6OHKMfDg1gJTD0WYs+wrQEOjs78zwvjuM4jrGS2vTOs7OzJ0+efPPNN7Ca/SqlNtqqLFIr6Zft3hRMhxWX1XmQZekv4VwHAJRSSqkgCOI41rPx5Xz45OQkSZJHjx5lWYZdD0riegZYTwh6O4J13o7KHHN9nCkt44a+GHFdF/+JV4FobuzAqrJOJpOdnR3HcbBrBavESx9Ah6PVqr620U8o5kNYN0qm19Mh7jAejz3Pe/XqVRRFb/IwVtZ//vlnd3e3Xq8vl0vMDPjOynxML/L6bsx/Cy064O3Vrb4woRjzfb9Wq+3u7gJAHMfD4fAyyNrt9sOHD6fTqZSSqrF+XmA3Sz+VKksu5r9lvfeka6ZgozQ5nU7n83kYhoeHh2VZCkrC3W4XLxJVKqteRwW3Ce+cSpMHk3Nl5kwZu9lsjsdjwH5TFEV4kVwPPvKnX6uBt9MycwfQUnNj/wf7gEVRTKdTz/OSJGm32wBgCyGSJNne3s6yjNasG5cr1Hzg3Hv3VAKUtrEgFkWB10+llFmWxXFsAcC33347mUy2t7d937csi7qG+iOsUjFLvRdoxaF39KhXb1lWnufL5bJWq2GJdQBgNBo1Go3JZOJ53nqh1qsssNd7gtY55eqCj76uRSlBEEgp0zRN09T+7LPP9vb2ZrPZ1tYWaC71tQ3V6vv7Xcwl77LgOI4QwnVdbCtZeMcv3k0IqyvklaUS1l2+9Ha/kA5cc1LXVkqJarC4NptNwPlwGIYAsFwu8d4LvbUBWmZn7h1aoVRaFngPKHoFgCRJLoVhEsaOBE6dKY/jBgfrRwKFKdoVQjiOg10HvGb3Zv2K0H2/juNQy5dXqx8h1D6kcknx6vs+7vNWglVK6Vd0OUw/ZvQGA92jSmwonHgTDRv9X/Cuq908ITIT9mom7NVM2KuZsFczYa9mwl7NhL2aCXs1E/ZqJuzVTNirmbBXM2GvZsJezYS9mgl7NRP2aibs1UzYq5mwVzNhr2bCXs2EvZoJezUT9mom7NVM2KuZsFczYa9mwl7NhL2aCXs1E/ZqJuzVTNirmbBXM2GvZsJezYS9mgl7NRP2aibs1UzYq5mwVzNhr2bCXs2EvZoJezUT9mom7NVM2KuZsFczYa9mwl7NhL2aCXs1E/ZqJuzVTNirmbBXM2GvZsJezYS9mgl7NRP2aibs1UzYq5mwVzNhr2bCXs2EvZoJezUT9mom7NVM2KuZsFczYa9mwl7NhL2aCXs1E/ZqJuzVTNirmbBXM2GvZsJezYS9mgl7NRP2aibs1UzYq5mwVzNhr2bCXs2EvZoJezUT9mom7NVMnMpzy7IsyyrL0rIulZdleeffirkWlmUVRYHKlFJvvURby+XStm0AkFICQFEU5Yo7/rrMe0GjZVkKIZbLJUqt1+tZll3ugH9qtRpuFEXhOA4ACCFIKp4R9/D1mTXKskSjmFbLsnQcB6OWpALl4TRNXddVSmHIEkIIPBZuMB8J63m0KAoppe/7zWYTMF7TNJ3NZq7r4sv0Tv0QnI0/BjDAEADAJEohh4n2xYsXAGADwN7enuu6RVHgoxBCKUWJF4+iz6SYe4FCi3RgEJJmZG9vDwCsKIra7fZkMgEApdRyuRRCuK4rhNBF6uWWuXvQHClEHbpOnDptbW2FYdjpdC7ra6PRAABM0FhlUSG+X9/Acn3XP4t5e65Dj7CaSQGA67qLxeL58+eNRsMBgNFo1Gw2syxzHCfLMn3qRO8nqffzmz5thBB6ytUTMj46joMLHgDodrsAYD979uzs7Gw2m52dnWFxhZU/ilQ8Cq2Z2O5dQtVU11mpqRh4GJlJkmxvb1tCiOFwmKZpEAT4mmVZeZ5vPIRlWXju8LLnbqhMa3DSo69QUIdSajwe+74PAAcHB0Dr1yRJDg4OyrKs1WqWZdm2radv3MDFL2Zm2r6XX/uJQHWUkij1mGifPM9d17VtWyk1m81qtVocx48fP7YB4NmzZ4vFAp27rluWJZZYvVBjpOoi6QMqOYH5QGg8Kf1iIOnzVhp5WOmXUgZBkCTJF1988f33318ubDEV41wZ+8N6+NO2frLQ2VQUBXUzmA8EJ7c0nqQT3i6C+j8B4Pz83Pf9MAx7vd7x8TFUrtM1Go35fB4EAabZyrK3ckQ9OQNf9vkvqMyMqJ2EIio5GdtHAHBxcREEAQD4vj8cDi+DmI54dHTU6/VGoxF9DDrTzxE95VIEwyr1gzZ5Zm6KPpJ6R2njNgBQTzDPcynlgwcP9vf3Dw8PUcHlvEkI0e/3R6PRYDD4+uuvd3Z28jzHYymlsHVMH185rTB1kFpguzdk46yFrq2iTl1qpZfged4ff/xxfn6epumbKql/QL/fp+08zz3PI1tSSpokU1UHbW4FWk+KuQV6ROoKKxPgSnMYACaTSaPRwGnwZq+UjafTaZ7ni8UCAJRSQgjqLFb6WBSytNLSJ1Z3MBz/d/RyhlwxblhT8YKr4zjT6dT3/cFggD0mPSydytv6/X6r1dKrLC57Kr0r2t54MYD24Xny1eirx0rXF9aGGlY1FUcVpYZhuC4VKvGK9Pv9TqczHA6bzaYetXgVjybAUkrHcda/xDVPPQbWLr1VXrVtm1p7GwtcGIZpmsKaVNh4P2K/3x8Oh51Op16vv3z5cjabAQAtqqhcY6+5AqxumuFmxXWg9YyUEgcNR0/v9ymlMLQAwHEcavEOBgOUigvW6pHf9ZFYa1ut1ldfffXnn3/ihbzxeHx5m4VlUezqk2HcqNxMw7wXfU2BkYMVFNcjnucppc7Pz3d2dmaz2f7+/ng8xpx6fHy8MX6uCqmyLE9OTnA7juMoihaLBSZnAJhOp7RNC6FKTDPXYT6f+76vT3Gx4ZBlWa1Ww1jKsgzb+mEY/v77769fv46iaD39EtX7h3UwFZycnKBUAHj16lUURdiTarVaeZ4XRYHNDiklVlxsQzLXB9Mb3niG9yjZto3d/CzLms0mNvTDMPR9v9VqpWn6448/Xl3mrlUC0S4GPgCEYSilTJIES0IQBL7vZ1lG9znimcVcE33ccCS3traopiJJkvR6PRz/K8KUuCpeCZqSYeweHh4Oh8Mvv/xyNBrhlAxPKwAYj8c3/FHMmzBoNpsvXryIoqhWqz1//rzb7eL1U8yX1CO8DreZslLdRccAgOcRkSTJLQ77KYMXw4lOp3N6eoq1DwDeNTm6gn8Bgz22xH842XgAAAAASUVORK5CYII=" />
+ <clipPath
+ id="clip8">
+ <path
+ d="M 0,0 H 1315.1992 V 1392.4063 H 0 Z m 0,0"
+ id="path164" />
+ </clipPath>
+ <image
+ id="image779"
+ width="1316"
+ height="1393"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABSQAAAVxCAYAAACeLII5AAAABmJLR0QA/wD/AP+gvaeTAAAgAElEQVR4nOzBAQEAAACAkP6v7ggKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA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AgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAIO+bLAAAAEASURBVEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQGbPzOv0EAAAAADAFT5/AcjGCwWyQYs3AAAAAElFTkSuQmCC" />
+ <mask
+ id="mask4">
+ <g
+ filter="url(#alpha)"
+ id="g170">
+ <use
+ xlink:href="#image779"
+ id="use168"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </g>
+ </mask>
+ <clipPath
+ id="clip10">
+ <path
+ d="M 294,769 H 409 V 885 H 294 Z m 0,0"
+ id="path173" />
+ </clipPath>
+ <clipPath
+ id="clip11">
+ <path
+ d="m 308.13281,769.90234 h 86.65625 c 7.69141,0 13.87891,6.19141 13.87891,13.88282 v 86.65625 c 0,7.6875 -6.1875,13.8789 -13.87891,13.8789 h -86.65625 c -7.6875,0 -13.8789,-6.1914 -13.8789,-13.8789 v -86.65625 c 0,-7.69141 6.1914,-13.88282 13.8789,-13.88282 z m 0,0"
+ id="path176" />
+ </clipPath>
+ <clipPath
+ id="clip9">
+ <rect
+ x="0"
+ y="0"
+ width="1316"
+ height="1393"
+ id="rect179" />
+ </clipPath>
+ <g
+ id="surface778"
+ clip-path="url(#clip9)">
+ <g
+ clip-path="url(#clip10)"
+ clip-rule="nonzero"
+ id="g186">
+ <g
+ clip-path="url(#clip11)"
+ clip-rule="nonzero"
+ id="g184">
+ <path
+ style="fill:#303030;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="M 294.25391,769.90234 V 884.32031 H 408.66797 V 769.90234 Z m 0,0"
+ id="path182" />
+ </g>
+ </g>
+ </g>
+ <clipPath
+ id="clip12">
+ <path
+ d="M 441,1125.4766 H 566.73828 V 1251 H 441 Z m 0,0"
+ id="path189" />
+ </clipPath>
+ <image
+ id="image784"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask5">
+ <use
+ xlink:href="#image784"
+ id="use193"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image783"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip13">
+ <path
+ d="M 0,0 H 1315.1992 V 1392.4063 H 0 Z m 0,0"
+ id="path197" />
+ </clipPath>
+ <image
+ id="image795"
+ width="1316"
+ height="1393"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABSQAAAVxCAYAAACeLII5AAAABmJLR0QA/wD/AP+gvaeTAAAd1klEQVR4nOzBAQEAAACAkP6v7ggKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA27tjnAhiIIqC7ZGBo3JuTkGCBAlX4BnJVdLmjp9+zwIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMB/t35/AAAAAAB/7fs5/QIAAAAA4B57LCQBAAAAgIaFJAAAAADQsZAEAAAAACrLQhIAAAAAyFhIAgAAAAAZC0kAAAAAIGMhCQAAAABkLCQBAAAAgMweURIAAAAAiOyZeT39CAAAAADgCl+CJAAAAABQ+dwz83b6FQAAAADAFZaFJAAAAABQESQBAAAAgMzjZBsAAAAAqFhIAgAAAAAZQRIAAAAAyCwn2wAAAABA5bGQBAAAAAAqTrYBAAAAgIyTbQAAAAAg42QbAAAAAMg42QYAAAAAMk62AQAAAICMhSQAAAAAkPENSQAAAAAg42QbAAAAAMg42QYAAAAAMoIkAAAAAJB5nGwDAAAAABULSQAAAAAgI0gCAAAAABn/sg0AAAAAZB4LSQAAAACg4mQbAAAAAMg42QYAAAAAMk62AQAAAICMk20AAAAAIONkGwAAAADIWEgCAAAAABnfkAQAAAAAMmvPzMvpVwAAAAAAV1h7ZvbpVwAAAAAAVxAkAQAAAICOIAkAAAAAVCwkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAHUESAAAAAKhYSAIAAAAAGUESAAAAAMisPTPr9CsAAAAAgDs8px8AAAAAANxDkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQGbPzPvpRwAAAAAAV/j4AaPBCD7AeLRZAAAAAElFTkSuQmCC" />
+ <mask
+ id="mask6">
+ <g
+ filter="url(#alpha)"
+ id="g203">
+ <use
+ xlink:href="#image795"
+ id="use201"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </g>
+ </mask>
+ <clipPath
+ id="clip15">
+ <path
+ d="m 443,1128 h 116 v 115 H 443 Z m 0,0"
+ id="path206" />
+ </clipPath>
+ <clipPath
+ id="clip16">
+ <path
+ d="m 457.78906,1128.4688 h 86.65625 c 7.69141,0 13.88281,6.1914 13.88281,13.8789 v 86.6562 c 0,7.6914 -6.1914,13.8789 -13.88281,13.8789 h -86.65625 c -7.6875,0 -13.8789,-6.1875 -13.8789,-13.8789 v -86.6562 c 0,-7.6875 6.1914,-13.8789 13.8789,-13.8789 z m 0,0"
+ id="path209" />
+ </clipPath>
+ <clipPath
+ id="clip14">
+ <rect
+ x="0"
+ y="0"
+ width="1316"
+ height="1393"
+ id="rect212" />
+ </clipPath>
+ <g
+ id="surface794"
+ clip-path="url(#clip14)">
+ <g
+ clip-path="url(#clip15)"
+ clip-rule="nonzero"
+ id="g219">
+ <g
+ clip-path="url(#clip16)"
+ clip-rule="nonzero"
+ id="g217">
+ <path
+ style="fill:#303030;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 443.91016,1128.4688 v 114.414 h 114.41796 v -114.414 z m 0,0"
+ id="path215" />
+ </g>
+ </g>
+ </g>
+ <clipPath
+ id="clip17">
+ <path
+ d="m 570,1148 h 263 v 83.8125 H 570 Z m 0,0"
+ id="path222" />
+ </clipPath>
+ <image
+ id="image800"
+ width="329"
+ height="104"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask7">
+ <use
+ xlink:href="#image800"
+ id="use226"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image799"
+ width="329"
+ height="104"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip18">
+ <path
+ d="m 563.19922,407.07422 h 341.4414 V 491 h -341.4414 z m 0,0"
+ id="path230" />
+ </clipPath>
+ <image
+ id="image806"
+ width="426"
+ height="104"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask8">
+ <use
+ xlink:href="#image806"
+ id="use234"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image805"
+ width="426"
+ height="104"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip19">
+ <path
+ d="M 433,383.07422 H 558.73828 V 509 H 433 Z m 0,0"
+ id="path238" />
+ </clipPath>
+ <image
+ id="image812"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask9">
+ <use
+ xlink:href="#image812"
+ id="use242"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image811"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip20">
+ <path
+ d="M 0,0 H 1315.1992 V 1392.4063 H 0 Z m 0,0"
+ id="path246" />
+ </clipPath>
+ <image
+ id="image823"
+ width="1316"
+ height="1393"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABSQAAAVxCAYAAACeLII5AAAABmJLR0QA/wD/AP+gvaeTAAAgAElEQVR4nOzdyXEAIRAEwQEhyX+DdXhRPMi0gHdFz+4MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA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gIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQEyf927JgAABAGYNgOvKIbJ7goB4mC3gUAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAB8VT7QAAARnSURBVAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMmtm9usIAAAAAOAL5wLd0Q0b4GzECAAAAABJRU5ErkJggg==" />
+ <mask
+ id="mask10">
+ <g
+ filter="url(#alpha)"
+ id="g252">
+ <use
+ xlink:href="#image823"
+ id="use250"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </g>
+ </mask>
+ <clipPath
+ id="clip22">
+ <path
+ d="M 435,386 H 551 V 501 H 435 Z m 0,0"
+ id="path255" />
+ </clipPath>
+ <clipPath
+ id="clip23">
+ <path
+ d="m 449.87109,386.16016 h 86.65625 c 7.6875,0 13.87891,6.1914 13.87891,13.8789 v 86.65625 c 0,7.69141 -6.19141,13.87891 -13.87891,13.87891 h -86.65625 c -7.6914,0 -13.8789,-6.1875 -13.8789,-13.87891 v -86.65625 c 0,-7.6875 6.1875,-13.8789 13.8789,-13.8789 z m 0,0"
+ id="path258" />
+ </clipPath>
+ <clipPath
+ id="clip21">
+ <rect
+ x="0"
+ y="0"
+ width="1316"
+ height="1393"
+ id="rect261" />
+ </clipPath>
+ <g
+ id="surface822"
+ clip-path="url(#clip21)">
+ <g
+ clip-path="url(#clip22)"
+ clip-rule="nonzero"
+ id="g268">
+ <g
+ clip-path="url(#clip23)"
+ clip-rule="nonzero"
+ id="g266">
+ <path
+ style="fill:#303030;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="M 435.99219,386.16016 V 500.57422 H 550.40625 V 386.16016 Z m 0,0"
+ id="path264" />
+ </g>
+ </g>
+ </g>
+ <clipPath
+ id="clip24">
+ <path
+ d="M 0,0 H 1315.1992 V 1392.4063 H 0 Z m 0,0"
+ id="path271" />
+ </clipPath>
+ <image
+ id="image833"
+ width="1316"
+ height="1393"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABSQAAAVxCAYAAACeLII5AAAABmJLR0QA/wD/AP+gvaeTAAAf60lEQVR4nOzcwW0DMRAEwZXBkJx/XDIEHB2Fmg9WRbDvxpCvmdkDAAAAAPB9vz+nLwAAAAAA7iFIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQGbNzD59BAAAAABwBwtJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQWTOzTx8BAAAAANxhzczn9BEAAAAAwBX2mpm/01cAAAAAAFd4BEkAAAAAoGIhCQAAAABkLCQBAAAAgIyFJAAAAACQESQBAAAAgIwn2wAAAABAZq+ZeZ++AgAAAAC4goUkAAAAAJDxhyQAAAAAkLGQBAAAAAAyFpIAAAAAQEaQBAAAAAAynmwDAAAAABkLSQAAAAAgYyEJAAAAAGQsJAEAAACAjCAJAAAAAGQ82QYAAAAAMhaSAAAAAEDmWTPzPn0FAAAAAHAFC0kAAAAAIOMPSQAAAAAgYyEJAAAAAGQESQAAAAAg48k2AAAAAJCxkAQAAAAAMhaSAAAAAEDGQhIAAAAAyAiSAAAAAEDGk20AAAAAIGMhCQAAAABknjUzz+krAAAAAIA7CJIAAAAAQEaQBAAAAAAqW5AEAAAAADKCJAAAAABQsZAEAAAAADqCJAAAAACQESQBAAAAgIon2wAAAABAR5AEAAAAACr79AEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/7cEBCQAAAICg/6/7ESoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHMBVG5VrWVi1d0AAAAASUVORK5CYII=" />
+ <mask
+ id="mask11">
+ <g
+ filter="url(#alpha)"
+ id="g277">
+ <use
+ xlink:href="#image833"
+ id="use275"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </g>
+ </mask>
+ <clipPath
+ id="clip26">
+ <path
+ d="m 443,391 h 100 v 24 H 443 Z m 0,0"
+ id="path280" />
+ </clipPath>
+ <clipPath
+ id="clip27">
+ <path
+ d="m 455.21484,391.55078 h 76.10938 c 6.38672,0 11.53125,2.96094 11.53125,6.63672 v 9.61328 c 0,3.67969 -5.14453,6.64063 -11.53125,6.64063 h -76.10938 c -6.39062,0 -11.53515,-2.96094 -11.53515,-6.64063 v -9.61328 c 0,-3.67578 5.14453,-6.63672 11.53515,-6.63672 z m 0,0"
+ id="path283" />
+ </clipPath>
+ <clipPath
+ id="clip25">
+ <rect
+ x="0"
+ y="0"
+ width="1316"
+ height="1393"
+ id="rect286" />
+ </clipPath>
+ <g
+ id="surface832"
+ clip-path="url(#clip25)">
+ <g
+ clip-path="url(#clip26)"
+ clip-rule="nonzero"
+ id="g293">
+ <g
+ clip-path="url(#clip27)"
+ clip-rule="nonzero"
+ id="g291">
+ <path
+ style="fill:#d7f3ee;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 443.67969,391.55078 v 22.89063 h 99.17578 v -22.89063 z m 0,0"
+ id="path289" />
+ </g>
+ </g>
+ </g>
+ <image
+ id="image838"
+ width="800"
+ height="109"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAABtCAAAAAB8NVqbAAAAAmJLR0QA/4ePzL8AACAASURBVHic7H3pYttGk21Vr1i5SXLm/Z/uThJbIrH2fn9UA6R2SpbzeSbTTpxYJtENoKtrO3UK4f/Gv37g498gnX9P/5kV/T4D/9ML+L/xnx0IAJh/nXdDAkiQ6D//biER6/89EZVf9lQezfOTs7wg3j9xxa+92sen/Oc3IgIgICIgjVWDJEgppZQgpf8JiuTyMX7xUsU6w5MD5OunejbPT85Cb/fRj37iik+ewPlCv25vfOXD+Nz8CIjIkNE/JCNA0hHpV0wpJkj/9NI+Mh7tgy9/ZSLPQefH+rISnRxf/FBwnQgAgM6nhJ+bBAHz4Xf+2efX/Xhl56tB+nUmxqOH/qse+ZvTA0PGGGecc844YwwZIGTxCDHEEGKIIab4zy7tI+PRPvgFr0zkOZAxhgwBaZYYU4zw8c2L59+fH4kIyJAxhnlPxBRj/OTt4HL0IZ71XqIzL374ioj5CTy+Wj5Cf8nOuHzoNFuMMcXPnheXF15/f0srIQJDzrgQQkghhBCcM8YQAFKKMYYQfB4hhBjTLzwqfmJc7AM474AvXKqgfcs454IzhoAJUowhhBBCih97XY9dvbzK9QqIyBgXnHOGCCklmiR+ZlMg4rJkts6XUl53hA89IAS6Gq1sXX2MMQQfwsfl7bo5WZ4TEYGW7gPGnxTHt1/B5ceQcS6EVDSklIJzjgwAUooheu+dc9ZaZ53znmTktxORi31ATzGG4EMMnzjbXxkCAJBxIaWSUtDLitF766zDAB+RkCeu3urn5YeKiEwIqZQUnDFIMQTnrPUe4mfkg3OZr7UopBRj8M5Z5/0HhQ4ZE4IeAF+vlvLVnI/xV0gI41wqJaVgDGky66z3PyWO77yCi88x5FwopXVRFkVRaKWkEJwzBKSDxnvnrDFmns1srHXOh/AZm+KXjsf7ANZXBvHje+qVIQCRcamKoiy1FBwxpeidMdM0mw89j2ylcGTZ04spxhiXvYqATChdlKXWgjNI0VszT5MBgI9uQETkQumyLAolOFu2dAjOzfM8G/yQ0CEyLpQuykIrwfkqbsFbM00zgv/6k5PmLMqy0EowhBi8MdPEDfifsLIQkNE4nxkxPrcSEZFxKXVRVlVdVVVZFFpJSRokC0hwzhkzz9M0TtM0zcY6F8IXmIBfOBBoHxSl1oIjpEB7iqH7slcmAJAJVTZN25SF5AxTCs5MQ98xgHT93sWss0W2oCDG6L0PHiOpIUQudd20TV0owTAGZ8a+FwyWUOJHBuOyyNdat3SMzppx6IeBmQ8IHR0QZd3UTVkowdgqbnYe+65HgPRl59F5VsZVVTdtU2nJWYrezkPfd/j5qAUAAmNMCMEFORNZEzyzEhEZE1IXVd20bdM0VVlqrYQQJFkJYooheOesmadpHIZhGIZxmo3FEH4vCVn2QVUogRi9m8ehE5jSl70ygYhcFs12v982JZ1m3k3D6UFgiiFevVREzqVSOms7iDF4a61hDshQQyZU1e73u7bWkkMMZuqPDxxiCBE/JO6IjAldbfb77abSNB0kEuz+dHzgmA2LKy/HhKrazW67aUotOfk0KQZvxv5BcYgxpo8t8Io7QCaLmh665ixFZ8bTg0LIYYtPXRQYW18BR7oHZ4217pFayhqzajab7XazaeuqLLSUgvOseShgELxzdjbTOPR93526vh9+zVnx6YGItA92u7bSkkH0ZuqOiqUvfGWCpLDZ393tt5UWHFP0duruFUbvfLj6eSDjqiirqtSKc8QUo7PzNE4TQMKEgMiFrrY3dze7ppQcYpjHUyVT8D58eE8gl0W9u7077OpCrRokODN0D6Vg6QPBJ6T73x1uD/u20pKzVR/N/akQ4J0P4WPLu2LW/NBvD9tKS4Tg5v6h4NE799Hj4uKiyJUuq6ospOSIKQVvzTSODMFDPH+KcaHLut3u9/v9tm3qUmspBM+WGZLnEmMIzlszT+PQd6fjw4PkCBB/QsN9/WBc6Hp7c3ezbQrJIPp5OJY8BedDvPpsf3sIQMZV2e5u/7jd1SQgwYwnjcFMs3Xsun2GZEC1m01TFpIjpujdPHanDikhC4BM6Hpz+Pbt0JaSY/TzUEtwZpot++DNIDKpm+3Nt7tDU8rVawjODKdaMQjBx5jCNW8SIZ9C377dHTaVlmcTy81dLZKdZ+PYl5+bTKiy2d38cberC8FScFNX8JAn+9QV6RVU7WbT1Fpxhoms2JNkkNJ6oNKxW7bb/eH2cNhvmrrUSuYA3pJNIBEJ0XtnzTSO3em+LgSmGMIn7OFfNrIevvn2bd8WkmHwc1/J5MxknP8iORZIx/H2cHu3rwvBMCVvh4L5qesGwa89O0nM9jf7bVOq7IOP3VHzFEKMgc5MXba7m9vbtlQcoje9BDt0neQIHzk0kU6Oqt3d3JGwLVva27EuOATnrA9XBuCQcVU025tv//Xt0FZKXIjbXMpohq4YGOKX2liYz5PN4fZu32jBUnRTyeM8dL3gn361jMui3R/2u7bUggFEb6f+WJAVm/c1+bVlu7u5vbu72W/bqtSStMcKV8zZ6BQTicg8tm1VCAjOOn/VsfOPDARALnW12d/cHdpCcohurkQyQ1eM2RH++SGANG6z2e53TY6oWMXc0NaF5AhXPg8y5A/f7vabSgnEFJ0ZjyVPzjofYjZlyrrd7HabUjKM3kiwfXO2aq4fdAg2m+1u3xaSMQAASCl5V2oBwc7TbH24RoVQbC3vl31bSLEkQmJ0s0hz35Tq4wt8/w7Ycge7LCCahelUFVIwvPqhP70ml2Wzv7u72dRaMEzR2/FUyeSt9as3iYyrstnd3v3x7e6wa6tCSQpewerIL3mUBCnG6H1ZVaUS4OZxnDiLn1vdrxjImFBl3W53u7YUDJIzMpmuLpVg+EWvTAAiE1KXVV3XleYMIQaJfqxLvYZ03l8pIhOq3Oxv/7jZVkowSNGbvhTRjtNsGCaKYqmiquu6LiSD5AXYuir1x28GEZmQqijruq61XNaYUvBSYDDTMIyzdXiVicW41FW7O9zc7Le1lutaUvQ82bosPiHA19wCpzhSXddKIETH4lyXWRifL/wShv7aFbOo3/5xt621JA0y1BLsNE2GkZJGZEzqarO/+68/vt3u2rqgTBKlTOCsZjIOhqUkolJKsmj6EwVxfh/5yBkK2lOCQfI8mboqtRRf9soEUp5Qaa21VhwRUsCgixyOuvZ5IOOqrLf7m9ttnU0sU4o4D8eT5IiYABmj1JRWSjJIHLzWWskPTHIxG+O0ZKXkktqDFAXHaOeh67pxdlcFGJAJWdTtdrfbtnVF2xMAIKXEktNaK3n1MfGB9ZOEKK0LrZRAiBh1obWU/NlpgbAmxvFNRBQil0W92R9u9vViYpUy2f50UuuGoZj+7vbu2x93+02tFecIQDmrFCmPT+iNBcfDI+ccwlxXhRL89yqPwBy301orwSCx5M+v7GsEWZAciozG4RQHz3/8gHwg40KVdbvZbisSkDDzOHdVSXFYRCBQgKCMLSQQKwLo4+pwuRYXi9MAAIkxTMFM29ND04/Gh3etUFyNte2mqYpLZZZS4lxQdAfhKm30kfUDYn7oQnAETIKLnIl4BClGAMzwkaVC47WbouO0bjbbTa05OZMCTF+XWjBEzFtf6mqzv737dnvYkjO/QIsWOAlBghYAIyJHTEprLQVngF9lunzNyPtACM45g5TyH54+xZ8YAhaQFF/BeowxxjIu6eppkAmpdFGWZSlJQDCZstA5LQIAGfG0JHqRJvmMsYh4vtYFvhATQtRVvdlsmqqfHXs/4IKMC1017aatc3z68u8Yoy3yC8rKENnFHQCBENgL8oGXMPT4MnJk/SjhC8qyVGQr82SrolBiFXxkQhbN9nBze9hv6oKCXTE455x1LvgYEwBjTHAppBSS0r5ICwX4VFL31w18uqfyxv1C+QCxbDdcBiwG6AX+/Zq1IuNCSCGlEBwxRYxCSnHxblag0OdnuZxu/fLl1xmXuqrbtqnKybD3MgrkohdVs2mbinLylwKyrvVXHJqP7gAun8vlh7Lg0MJiijGEV1HWiAw5F0JKKQRnmBimvMnzfSElo9rd/rDftXVB0ZTgrJnn2RhD6QNkjEspldZaaSkFx5Sxmxnh9PuIyPM99dJT/KkhaJ4nG+080fVrXS4Ej/f/xfZ/etVPzPJshfjkp1yoom7ati717N5NFiFSzqxt61IJ/nQpP7PAd8fTO3hhMkRcjFJEgBiCJ2Dta8m6y/1yxr0v7wABGJO6arf73battKRgipunsR/GcZqtCyGReyR1UZRlVZRaSw4hZtBK/Fm08VePr9xTL46l5Panr5gy2DiEwIDAuoEqCf4ZcMKSBuNSl3VLGKd3MwoUwyJ5onwK4Sp/j4GIXCilC6UER0jRW2uMsfAKFDOdAf8BEi5/iGGFryAKWVTtdrttq0IJBpCCnYfudDx1/Tgb52NcgjZFVTV1U9dlITmB373/vfKE/8gQ73/kqpEB28YYwxNHTDFYY2br3MeBJJ+ZHgASIEVpirpp26ochH/7vMOsQJqmqclqB0gAmH4XEUEuVFlVdVUoziB5Z8Zx4Pgyk0JKkOgVzIYDOenOGGOsWyQEkRO4jyxKAqbO/fH+/sf9qRsn63xMAIxzqXRR1c1ms2nbupBgrbXWkgb5d40vFBBv53HoSx7JtPWm74dhNNb/eglZGDiQQE5l3TRNqeV7eAPK7tdZgTBc8wDpd5AQJCTFZrtpay0ZRmfH/qQYFXy+8IUYCIddiegEwxSDHfp+GGfr8+cZuVxN9riA4H33f/311/1DP87OhRQpIim1Luu23e7206YumJvnDHr8vSysXz++SEBSxl4dSx5mnfMg/cP9sR/NBxCPn52dCoNyMFmoMiOgrXvLTUdExlVRNW1DHgiVhSPi7yIhTKiy3d8edk2pGAZnFuBIeAmrmlJwZuyOlYhGC06JwuP9MeeEKE0oVFFVdVmQxxWDM8Px+5//76/vp36yy8cY40KqomzaUzeM06YWYRin2Tgf/m0W1tdpkBjs1D0ULEyVEgxT8GY4/v39YZit/xUVeZdzU2UQADJECmQ1TVOXeuZvu+mMS102JEwcIaWYIhDBx2+wDxAZV1V7uPt22FSKQ3RTV0vw1lr//LYSvYL+WIpoVlj2cPr+/aGfSIVgjgNX5aowg5v74/e//vzzRzcaRy4GlV0JqXR36vpxHKdGw9SRMfCvUyFfIyAJUvRm6hQL86lcoCbj6f77Qze5X3vspFzRHRMgZxwhB6aaSkvxpo1F8NeaFAjVvcYIyPkXhtE/P1aY2O23222lOAY31Sq5aRxn+1Liks4oxaPpqqxBzNQ9/H1/mhYzlzEutS4KJQVHhBSDm4bj/ffvP+6HyfmYXXBECvVO4zhO4zRuKjQPp2Gy/v+c9M+OFL0dOUbT1YXiDCAEM/en40M/2V9uYkGKwfsITAjgiNnOrsvR8NdPvJz8r1qqS2SYoqeLpK9D8vzUoHq5ze5ws60lx2gnCWY4lhke8nSk6N3EWbJDXSrOAaJ383A6PnRjfgWEYSMsBiK9NDN0p+Px1I8Xu58YV5ywxhgzz2NXCdc9dJPx/zof/csEBGJwMyY3nUolOAOIwZlpGPrR+JB+aVwoIUXQAvCYlnLD7Hm/JZyIXBZl0zRUwpJScM5G4BFYxN8gHYaQa4HbzaaSHKKTYPu61FIgPgdzJ0jRMQQ/dVQYCTF4Mw9DP8xZiZP7rWSGfJHen0dy5O2F+YQAiMTbYM08dpWI48Ppn3Anf7vxVU46QPSQop0ysCGl4J2dZ2PsLz92UkrRW+MTDwkZB0oWNk3dKeNfKwvAc1KxIpRqcNaYgCIxzn8PJ53QVWVZloXkEAW4sij0q+jJFABTdHOvJc/sG96YeaZXkJCohoSQBFYi/LMz8zwb63wMZ2WbEBISh453dupLmUx3Goz718nH12mQFAFSdGYQhIxLKXjvnKPk0i/ebyl4O7soInCODLksKPunBH+9rJ4xocu6aetKS85I5c2eKRQi8l+73qsGwTuJkEkKDgmkVEqJ1/BrCSC6FJ0RkjNGryB454gGiTQIEq/GmWgtEj1ceFILnwAo1RhjcGbUMvlpGG2I1/kgT5f3IbF6dm+/QigfT/LGDF/mg2CKkKK3Z3o+SuTGK58pfGDRzyaP0VszRx5QcMaQC1XWTdNUxWQZe7FuCnNOoG4bQrtSHmcKLHIVf71IXzMQEDknqj3OMGaiOc5ewVIkiClF71b+uxRjiCFcbGtEzjhbmZIgkxG+xHGxFt56OykOwV5nDKzo/KWu5vKCV3x3hfZ/+NvXjnP9wEWR2KszfJ0GwRRTjJ74XXElQL6SXySvegWbP/rPe1OnFIObJ88jE1IkRKE0pTcG8VrdFOUE6jbTEAGRHU1egroqs3neoe/gIa/62CtfJqpWlpGkDJGxS5bpp1dNADFhyNXlaWFPPb8BxIVhdX3gGUWML5Q9J4CYUgreCobRe+ffoVzBBfoF+OjGF5z+m7AGgIUU8hJrmCiK//xePzeWWS5X+Pb6vkxAIGFKmDAu7+7MoP/uV8/oRjh/Fz5AvJ9ScHZyLHGlBOeU32ibt9x0gmHVTVOXOWng5nEYo2TvV1pd1DAtC/iJj70yB2JmXT+LA+YNzhhipnbA9PgQTIgpnsXn8Ss4412XSajgSEkhXmSjpGMuEBNmfBtogo+wknCW4JQul/Hak1qrGPP/PPv2G5Uw1w5cZ2HrZnsyw7Mpvk5AYLVK8v9cy02FCMgSMMLcQy7/PB9911wkRm9mg4nrIkSGTKiyogTgy2465qRy9lQYpujNPPRTLJR7R4Esj/lcxJReSmxf+bFX50DG1yqd87IZ0SiLGFg++V58RGk1oR79NK+E/gKzFi2qqixs5jx9qkTILqCYcHojS5hlj2Udt1qBCTKhOFFzv7TYRbLY+msFIyciIFq+/BIE7fqBywGz6mEgJuqUOUBTfOEVfaGA0F2uHPEpQ4be4VFCRGQcAVkuYAPI9OIxRKJqf/eppJRCcPMMSRQEy+Uql4WsIc6n06443korTqCLcegngOIdyCoCvUS2vsKYXmCLQsAs8m9+7JUpgIiMc2XH2S6nohulPAaqIY/pks1+3WiIuE766BU8YqwnNoKmbfvZRVhhpZdLzC8QF63+6jPJ9WVrJeI5DBAXLvQXSeIXyaJSJ6p9WuQr5RKYQIzUP0WfjUtxzbpE4tVPcUFAe9pwT17R1wkI5hrBvMuJQj+Gd2Am9M5TYnzhzASAGJfgig94FWNySt7ZKUZRziWpEF3WTUMBquc8HBnUmBEpGZYxj8MwI38n8Y+5IHVh90xxwfSnT3zstWeSyUHLolCSLz51pk4sysqjC/lwDSHghYQgtfugV5CoxcfZeMoVCVk1IxEJbHb9ZAMg8z6etf5ZpACygLy6OWnBnAshpKASLc5p/9H28847518iiadiPU5fXmqwOcn40mfAO+ec8+6zrQBgEQ++1JPJXOoNuSbA5ylofY/u88sEZDndpOScMUgpeu+8c+EtbnSkdHYQSKYwhfhTjN47ay2Ril+xq1KK0ZspRFHVRsvEWI70VuU4v+SmI3KpM7uv5IuL3vdGqLeTYRQplZkIPi/VOQ9P6W9f+pi/irwdcwJEF03bEtR98V6RnCvLCxdjysWyfn3GSDT1kug2Mj+9dwHytsrtAYjLcoE+N9tpthE4l8aR7KQnTvE7EZMsHkJKqZVWOrdSoGAmnXXOWmONNfl9nnc5IiJnQggppVJKKikkz4cswsqAag3h9l0InwKDUQaICyWVVlorlVcIlOxxztIU1tJ+u9gwX6hB6KUWWhF5O1VyMvsGNzpmogGWhNJFUSglBLHYE3HyPM3GEPPnu1ZW9M7YIKrGuMAzyqppmqqXLznduNTmUqUUxODmcegHq4o3Id25hqkoCiUJvuWtnWdjntLf5o9pLTlfCPPn2V5F3o6MCarH2B52bUWlXAgr955jlXExphj9o2dMp40qCq2FIDCDNbOZlwqrlNl6nV8OAeSyaKxzAblUw0QVHzGHHtNFAOAdk5Pw8UVRlGVRaC2VWDp2RDoajJmneZrm2Rh3FtglMaOUKrQuCq21klJwwThpEPq2NfM0TdM4zcZ6Hz/VH4lqXHRRliV1exBiFRAqopmmcZrm2RLf7jrDVwkIEj9IXdeZvD06Mw/DwBAgvJJUWFiUU2CqLKuKdh2DSG92HIdhGMbZOHi3Q0dKMTgze16Ns/OCrzHcqlDmGcledtHrpllivJEUyODTOwVeyLgq6rqpzoI1DcPAAPzl1/LH6gv5m8ZejE8+9sqTRELst5vt4WbfLrVchI1pdp6Vk/GU8rDTMPQMIMVEEqTKqqnrQguOKXg7j/2Q61wSUFGCtcY6H1JKSPyOPkTkUlfdMM/OLVWg5wD92+tFYMTxRcxqVVUWKm+/rEG8d9bO0ziOwzCM43wuiUSiB9NFWZVVWZZloYlFe3VGYyBAxjgMfd8P4/TB1ha0wswSVVZVXdd1VdJRnPs4hRBcpiDu+2EcZ+suT+Sv0yBMqKLZ7rZtVUgOKdi5744CU4yvkIBmyoTGaxAlLVzJXFvq7TwPQ9edTl3PZ/TvdzNI0dvZ8n6YTKEiZ1wuWcAX3HQiM6nbtq6KvIHN1Pf9kNibubC8pba77aYuFWcpeTP1pwcOMcVLT4A+tt1umlKK5WOaA8Hy33uSKItmu9tvt9vd7rCpNEELMzNcwGJjXIgpRm/n/nTM/POJBKje7rZtU0iOKdp57I4SU4wp0DPK0BKbVQgyrmJMwERRNV03zsY4l5uuhUVK3gy9LX5RVTftpm2auiqL3CMJcen55Jwx0zT0fXfquo6IuCABAnKhiqpumqap66oqCq1WGm0ioEq0fcehP52Op5McED/YT4Zsf1mUVdO2m7ap67LIBPjLAp2z8zQM3el07FQ/oYOzhHydD8K4qjaHWyJvx+TtSBTx4ZUjGTMHjWGe6bpuiGVRMMT8Hse+Oz4QGSe8+1CoohFZOYxz5UVaSm+bqhiFf+ymr2QmFzFeOw19P4wo31Ugsmx3t7f7ttKCpejn4VQJus0LHmPGZdnsbm/2m+Vj4+mHhEj9wd5/kmW7v7097Dabzaat1EJHyrgqI8pmopRd8GY8VRIyQT4uX7zZt6XmmIKd+gfNEjm3kOgUMdM0zdYH8mw4JGLSrjddN0yTMYZ6rnlPkaMU3xIRBMaELMp6s9nutptNU1XFoj/OUSzvrZ2nse9Ox3stGJ2Z2WYs6812t920TbN0YaBeokuYmE7LaexOxzNN7cckhHGpyqrdbLe7bdvWVUnUchTIgEVCxr47PdQPWgo2wVlCvkhAlpYdh2/fDptScYzBDEfNgjXGspdVSEZzpzLKoq7zujPKLnhrpqHbNGUWkHcDfCkGZyKU/TiZIrALEdCGP0GbrDjF+jKL3nfDxIvwdmoTuSqb/d0fN9u6EAjRzf2DgmBme1nfi8hV0e7vvt1uay0YRDcPDwq9NebdNCRmS+r22+2+beq6vDSxRAFc1dbHmIjNvpLEP+/ppFRlu7/747CtlcAUzHQqeXTGWEoGJbK6xnGarRYREZCBJM3UbPt+GKd5JmeVWhO6pYHnqywqjA6i3W5/2O82bV1pLcVK9guwwFWcNdPQH9tScSreoZYxXJebw83NYbdpsgFxyRK1eOrOmmk4bRpS9/ChhAjmgP5mtz/sd7u2qYqCmgAuiO0EMXpv5rE/ES4PEQCWTkFfp0E41Yd+u9kuAlLwMA/DaNxL9UdLPVCQFmRZVkWhZVavQG/SjJschAW4WPGLIxGi16ei6yfjlKCuBrk23T5pX4CLAdaUOtN1z2Pf9+Ms30RTZN+l2d7c3e1WAdHopmGY+ErDhUTU3WwPd3f7LCAmf2x8zL31wmMBZEzqstnuD/u2LrWm6lgEQJYEAJOOOqrE6OaCh7nvlGBE7ko83He3OxIQO5Y8mLEfxNr0xNtp7Pt+rArCBCMTSPSrm2Ecp3mazWxmY2azCEom+3lpRy5G8mZ/uL252W/bptRKkvO7ZB1xDQ6YaWgqyaKzxnoEAEQmVbU5fPt2u9vUpSLivowNA0SWGOeMAYRgzdRk6ktI8QP9o6j9S9VuDze3JIdFbrmUst8GiAgpBDu3TVMR+9PalPsrnXRyi7f7wyIgBQvTqS7kM7qpi29UDgsPgiJvnDNkmcMvhWCrOvdNBOoX/Y6fHoO1Qff9MFst2Wpj1YUy7tJNX1z0tm2qInND2Wnou2F08M75jgux1GFXF5xBdEahG04P+hFtbabE3u4Pq4AosMPpOir7NYq1umUkHwmQcQmMUyIjRseTrQqdGeFXIu79YRGQgse5o0AxJkjktw/dqRvqIh/WyBZ2sM08k3TMZp7neaKw02yN9/6VfkQ5Tny4vbu7vdm1Taklz2128guj5DWIlEJZloWAMA/9QAINyLiu2v3tt7tdWyrBAVJO2cWUcmtZxgRCLFxZloWSHFKkbMh1KoRiR1W7v7m7u7vZb5tKk01NQFqK/HHOUaWirEp6mNSRO5Ed93UaBLnQRd00bVtKDiko5qe6KpSgA+XlL/jIdUiMcw4hMU9suIiMAUQKWFNLn3Be8SsjpRi88arvh7EpA8dccN7U1TDx8CTEtASBteQIKSuQYQo8vGf/LG5lW2uOEL0EN9Rl8bhIgz5W1k3bNErkj4319bTjmW/xAnWRf7z8FeS99+hqZDnWbdtWiiMEy+LcLOzI+RnZaehOpxOdxqRDyAopnHXWWmusMbOZcthpHMfJGOZfaq2drZfN4e7bH99u9pumoPas4Uwyl9NznHHGhZA8uXE5JQjpQiWT+yzRwTmy7UJKyIUsirIEzjkkpZSSgkMK3pEGveY5IhD38v722x/fbg87euEpBp9TbIBMCEqLSKWUlnRgXoDQv0ZAEKnemXrFaskwBQy5b+yL1QtkIil5agAAIABJREFUEcgiIvc+Ju8ShTGlUlonyRhmSkGWn8m7jdpSCt6i7Pt+nK0SOexMkV574aYjvZeFLo4hUDi068cZ3sGr4prtKQutOUIU6Iui0JJyFeniY0JqarAszh9TgmUa6bfvJHpvzTxPgoBDCYGOzBSDp8bU2cSaptnY5dHgeXEUxcJol4wNLS4l78x4emjJnEdgC9KDSxk8Zbxz++dpHIe+77q+H6bZupfAjGvR/H/9191+uwQ8grPGWmJpBJazgFJyJiFUFRlhi59BHn7dVIWASBSoM2U7Egqpq7p1DTLOgcpYIAVrDbGrXKdBMhfxt//649vNri214BCDM/nBxcS41EVZFoViMhOJp+Cd9ws/xVfmQTiXUkophWCQMNJjeVk+8jeEUMGHaK3zPqa82qqsCpAMgbBZFLu37jU1v44UvYu87/pxsj6yRQyaulDikZvOLmgdOIMYnRn7rh8mw95RIDl3QyApwRAYhgUw9cjRouJWehj84mOvPo3L+0jB26k/lQKC9yFdRHRCcPNMBIgJYvDz8HDsxtmGmCDheXFSCA4Qk5dyJTtP5I46M3bHpi7JngeGAIAMWeKEf/PBU+Z7nseh706n4+nUDdOMHp5ISI4mbA93f/zx7YbkA2J0ZprGaTLWhZWlUReF1oJRG4ELk5u2gFJSYAx2HodhHKZpNs7HxLgqm83kEjLGGSbGSNdP4zQbd11/vcXAuvv2xx+3+7ZSEiEEMw1934+TcTHlWE4Tks6FMik4a6xxgTowfSVYkTjiOWOMQQLGeMYfvuCikwpBpMaL47iIs9JV07bOJwCRMXck8LO1b6OtE6QUXWBD3/fjXAYOyGSuGCymSzedoIwEw5IMITPe9eNkxXs9ZhfUUcbjQUrn28RHH2MLMO3px158Ghe3ASkGN/eFwmDn2fqES8gUYnTz2PfjTAQ9wZvx9OPHcTCU6blAwxGmjaB5l/z0mRqoojwnrhKCCRhBG2MM0ece0OPQn44P91WhO86sC48yWrg0c7u9vbs77BqSj2Cnsev6bhyN89mC0WVZ1VWlJYY1S79eBBAZQnDBjn3XdV0/LgIiVNnu5wCcU5Eq5nxu1w+Tva7DHsWRtze3d3e3+w21S/XzcDo9HE/daGxMTOiy2W73gTYsQgrezNM0WecjfqEGIZz9Wr8A59oceOPMTMGZ4Xg8dSTOKGRZt9u98QkQBWTYiZnGYZxm92p9OV0LUgwWyFayXrCFFq595KYvnUyahX4zRm+noev60Th8NzpCEsJeus3HHyMRwWcfe1s+ACClCHYSHLwZh9EE5DzXyKfo3dQ9PHSDcSGmRMQxDw/90ixomZU9mvRSPiAGOw9HrRVFvnRadR8mSCyjfZcm0NOw2VBZDafuB48khML0+5vb28OuqUg+3NSd7h8eTt1orI8ZSlTWTbNp6kKEBY2VEiRAqvqJwVuGduyOD8dj14+Tsd6nxLiq2skBV1IJDoggY2jm4bQ5lnp+MTL6/F1xqat2f7i9vdltai0ZBG+G44/vP34cT+PsYmJCV+1psCEhY4xJSBkbMeWA/JdqEMyY++WP9JJe/UKKwc7d/fcf96eeBISrot52k43AGDKOKCAFZ8ae9u/bONuUYnBpGvp+mIyTHBkXBNit+tmu38381k22sBLp9r4fJhv4+0i4892td31x048eBlz8fP3YFb5lCm5GCHYexjmgUEpFlgjUb6fux0p0RRD9oRtml53Ws/t+MStcHlEpoBGdUlIyBiklnQSyhY0YIUHiK0zYWdM0TVNXiytp4fwCctql2R4ON7tNXSrBIQY79Q8//v77x0LnmMFEVbvZbttap3kkf2Z5zjknPLgwdQ/39w+nbphmRxzaXJWjA6HLslSSMWAiEhN/UyppWXzfTScR3u5ubg7bttKSQ/RmPH3/66+/vj900+xjYlyXbT/7hFwILjgU3k5Dd+oGIjlKXygg8Fwc3twOKQVn+uP3P//6cexn42NCLouqG20AJgizzbgsXDv2p2M3TNa/3fAjpeDjNPRdP7RWR0TKoC7Z9Nykj2BSTdtUxdKobB66rh9nm+Q1rt/zbf6qkwVXfOz5XURyp808Gs9UWVdh2U3eTv3D33/fD8bFFFMk4Mjqgyyre30ZCVJwMxdrQjaqKFLOQ+ZF5uKuGKkfUlWubZASXEQSl1D2br/bNKXKNQPj6cef//3X94d+tC6khIxxqYuq2Wx3u7Zk9tSNxD+Xb9V7M49d4nZ4+HF//9ANk7EuswDK2YEomk1bF4GnpZdm01TUMuv9V0UZ0M1+vyf5wBjc3D98/+///vPHkQjbGJfFMLvEpNJKcMZVUbfb0/ZY9bPzX+uDfGwkijn2D9///PPHcZydTwmZ0OVkPHCpyAbInvZmkylK3rlmDMFQxs86yeHMnHjBQroQVjdVsZCZjFnrvG9h/SMjRUofOxt50WwX4BSkGL0Zu4e/v/ezC1Rj4q3LIPCrLx4sdfaCGLz3FW1+zC3aACALCUtcSEVNJaXkDKhgapWQ3JZ9u9ttGupeGIOb+ofvf/6/P38ch8yYzZALqcrq1HXdthKhf+jGBU2QiLmrP/IZ5u7+/v7hNFBQLqYEyIRPomjpZEwAxCpblGX5amz00aAYb9Vsd7tNthaSN+Pp/u8///uvH91oXIyATE7WRyaLsiqUZHR6btqmKkbO4peaWB8aBAxy83B6+PH9Rzc6FyMA43K2HrgsqlJLkQBYOnfEkYa956aHyOah7/pxLj3nyKSuMrGP9QvCWlzWoucsetcNk/X8agKWXzkSpBBjCiFGUW4m43xKFOZNMbh57E7H07QUTIW1DutK/QQQ0DAESNF756yrtBLiopXd8p8ELHG+xOIYA8hIeKSQMlVbtZvt0lwl5gPvr7/+uu/GrCao9Gsoh6HvT7VM08NxWLi0IaXozXBSQcWpe7g/dsNknQ9U2ossAtMDNQgIKSFAhg0XV6WTMtavarfbTUvxyhScGU4/vv/99/djdtwQrQsRZdlkgk2W81xVQex6/zENAkAhwf50OnXd7D3xijsfgKt1uQiME5NovcRd3sKbxBgc5fxyNp0TqL0qR5PJ03Kz0bapCiUoMDL2XTeMV4YO/4FBLZljQj5Nsz2XnCWIIXgzT8MwOiq5jUvp/vUaBGKgYuFANSptVRDZ1kVPyuzKJIrYCc4ZQszpM8iamLKS1N2RqLCJC/jH9x/3x97QAimyJoSZ53HoKgWmP/azCwkgUUuguVcwiTB1x1M/0N0uFCMJRWa1CzHCAuFRaqVOfWcsxnTbUoPelAKdyPcPp37MHEYYYkJRNJvtdnZekoFRLf3J/3MahHxO78w8jeNkrA/kIcQITBTNthvaKqSEuKz4ujR0DDBPfdddZtPrtl2aheDSEqRpm6oQnAGRmfT9MBsf+O8hILn8G91C+nYuW0oxBGetWfYfFaZ/qIAoQQSbc45mGoexqYsiV9ixi8Q9RbYw14gT/fEFyiOzXtS5rBkh0VlzOh6P3XBuSQKI6Lmzdp6HQqGb+yXolvnoBbqOhWnsSVWs2S7MJqS1zmfSx5U6lRJK79z1ystxBnF5Ow3d8UiqKhLkPgHyaei6gfJJjPZbRVKPX+ykf2zk+h16BDlOmRLjI/nMLot4bt1avI9jSimFlNF4lE3PFHK5Nh3J0yvXh5bJabuuH2bn38ay/JMjJVzryS9BaClXoYZABsz6z4dGBLApxeDsPA59t2mrqlBLIcYSwMYEgAlh4U+I3hEI5Jy1F6qs6mpJOUIM3kw9BYDsRScRxEg7fdQCo50n4xa+mhTchNFIDGZa0vW5BJ6iDRc2JORZuZC5M+w7pyWS8ZGr1hgsFmrXDVPOtdJxwZyZpnGcjQsRgHEqb80g0f+ggCxv25+RLwkjemvGcRiXODS9iWIRkHeOjRQXk4lA73juQWhcTICMCV1Q9lAKXIPIw2Tce3Ua/+gg7XBRIJ5/nFYmnE+zqSWMAAAxeDuPQ7fdtk1dFYVSOdfPiXsHsttOE8TgnJkzygMSUrCjqKqqJPxMIm6ycRjG6QkWJOXC1llyTN7ZFfGfYnCY/MwxOOrwRm5LpkyivGoiF2tJYpHN9373cESqCSrrVRkQRHykosGUtQc9CSqI9hQM4EJpimwzwP+sBoFMSHTBExBDcHbOUAXqGkUd2Jd2329HjlP0GUlAoHcyqJrc05OIbpaeUksWnXwWH/F3cNEvxlPpuPjpy3917XUxkoPv7DR0p4e2reuqLDKZgRD8TCwCAMggV9tM0zRREx1c8JjFUgW62mzTRBvtUu3ljqKOM0wx5G7sJDgeojcMIhV9Ubp5IbBCoZfKWBoIa1v0K0ISlIMpy0UXUOLIzLN1PgGyZYFkfDnrCM1EZpxS2dH5TwrIUvry6F1T6ig37aZjg7G8Ys7xRWDwxYgxIw+HqdaSIeYma1VJhYVspe3NFpYlF322IbJfeq+/0SAi5RS8m6fh1DR1U9VUEa6VUgQ0W5gTSEJyt+hhmHIIagVGagrHZ4PZGGKKf6L3clSfgmcXBHQpgo/BLWoiy0X+xZhQNQHQz28GVyDDezuBTlZdFIXKaObMp+6pBwCDC7MNyJjLi+BCqqV46z8rIADw5CDMzT5cRiemRFhTQrS9U2xEFwguh23nQjG+gN6bUksX0tIluq4oxrtKk/XXoXv+d4yUSza8NVPflVVVlVVZFmWhC01SooiAJxdVgYzB5eiHcSEmzMeWVktKgoLQiz/59EkmTBFi/twaVbjgbYRMrclX0BrjTKhqs6mzDQeQ8UxIgLb30yCMC8riLDEvMk2BCRUv6hqQCUVOfFoOZC7EAkD9jwvI05EItEoFBfSjFUDL2XuHfEoxLVZTa7VI1Em5aeuqUDZEyjteKpBpIAvrX9YaJrPuememURNCviyKYvm9yBw8ApAtOcFmszsdT/1kPe1TskTEWmwSY1iLNV6YD1ZKWrj0ToDOwAzuFFwILgTngnFOcMVtLvukgUsZzBVRXuRnEQZcyhSFLqskL7g5kHFdXVb0YEYds99CgzwbCYjd7Fyzkhe8FBK/9/VVhVgf2ALdbetCCb+4JPVSKeXmcei6nhTIr7+332jQ6R2Cs7OUSmmltS50URRFWZZlWVfE4CM5sAVW0mw2m+akZ8bIVOcrRx2pgBxxCS/XJaRn/0OryAhLvtSNKLE6QkIW1fawqS/jl8/QZa8MRMhsdiIrICTihLKZobAXsD5ELqtNW2u1APFXUn34T+ZBXh3kQC5UrLAwk+ViivfCWBRMoUBxqQRQOWnT1FUxh3AOai0W1pDRv+Fj2YT/+SMt9iy3XAgpJNEOFrosyqqum7bdNL5KUgADBJ5TCvUS7ci1J0Ks8pGZTl+Rjzzlk7GSlhIto9ZKZyeIihBV2Wwz79E5R3wV4nM5V1f5AADCLjosjbtgX0JkUjf7bXNRHY64BJJ/QwGBzHi8ampceW6veTQprhW0tRfs3NOzUB5XYVlivNPQ9Rko/O/SIHAWkUzpnAlAtS6Ksm7a7Y7wrIg5lKjKKgN0WMwFcoKfA675XIsfia8tZ59SCy1jobVS2b5hyIWu6rq4tlD5ycVphRfywYSuNo4V0+NOQMiEKje7DbVnhscxwt9QQHKo/wJAcQ5dXAMWj9lwItA7Z2zxy0fPRFFmphPCKc5D33U55/LvUiAAkEUEIhXyZAZpqZQuqrrd9ZOlckZgC0dstSQVEiDL1ebZ/4W1V8H1mX0yeqTSRVnVVVVXZVlotZahJkCudHEVMvHZpc8xYZ6bgSByoWsPqnlSOJFhl5tKMUgRFi76ry25/dqRlrqzVUJWTv/3vwohW07DVOl40ZCtdFxmaNaSRZ+GvhuG98mq/teO7DzHHGDlZO7oomr6YXYRloZUQEwrZZVL3JeayctTK1OZXK8/qOK2KKu6aZtm4TzkORocQ0qZgOdTLyc7/kv1WEJkUvvI9HxRkAKw0MGUlWIQECFYY4xd6rp+SwFZm1PRWIoVrzxGUiIi0W6YTCHTSmRd987J6sJF99aMQ9f3U45c/jtHztnl+BBDzrgQehim2UVgXAjOWEJMGYJBOzgf0MvmWy71AcIqyIEwXdbNZrPZbtq6rgqliPjc++idDwlFAKH0586vJaty4VgIHRPX7knxNiV1BEYXHaZghwwH+PqCqV81EBaleZ2N5U120yvPGTKpi7ppmt5btRJWZ75RqpR6k9H9f/8gZyQ/Yc+QczPP1kXgUq/EdYiEpFWZdSE361lfSeY7vPrAJ5OnqNrNbrfbbTdtVWklGETvKFNsrY9MVp5JrWNk6cqCsydzXAaEMXtO8LQ0FZFBcMlzhhCDGR+Op340PvyuGgSebVe8+P3970a/gN6NlmntldN4qS6y6OEMSvk3WVhrefrjkQH1CBgwIPc+ROCqqKpSS8HS0uRAqpV393k64iPASaT4YrPZHQ6H/W6bOZiiN34exzGjWphuHFNFsUAzP3W/i5O00PAa/5zNHJEx6vkTvR1Of/84DpMN8XcVkKd5oBWpddUzIuDQ0HXD2DiVe3rWbdt7q9ql7TMk8lQ6UiCfOZ/+J45Lg+jp3y0PGSNSxlkWdbtpah/4UtixUhzBaxe5fiWMq7LZHW5ub2/2W6IRT966qeu6rh/GebYeRLFNqm78693u3xwXDWwhE68OZ5z4o9UgcUymRLTra+H/7ykgT8YSIrlOQtLKJdoNs9WCQa4LGIJT7VIdkBVIN4yz9Vf0JPjfMRZ+ifQaG3gWkpQSoChOp66n8yNjPHLgav3Q4xdC0neVq4iZUOHw7du328OurUrFMbowd8eH++OxG8bZ2giqDrKhnfpxJ3GJhi71Vyn4eTgeu+FRGuTi0eQiFDsPx4f77jfWICuegMYZ+n3l99Pa72Osy7jYWJsxOrVd6eKCM+PQ9cNk/kXyQUgmzAGnN/J5EajsgNCJkROA9yIq9Cgan+s3Fj6LqySECV22+7s//uvbzb6tCsUhRj+dfnz//v3h2I2TtSGywolm+rQJnNLaWhcAIAY79w/f70+Tfd46c91sMZ+c/eR+WwFZ8TbnHFSKV/BXLyOls5tuFefIpSrrdgInty1ZWCl6ojgbZkvtln7NLfxWAyFTNQP1DHm9mVmCFIKzS8FrhlFdEhflvGB6nFC49qaRWgxtDnd/fPu239QFUY5M3Y+//vzr+33Xz8aGgNyzyTzpiXb1SJTaz9UUCCkFP4+n73/fD8a/mvZKKQRnDPVQ+T2jWJhRz5eWbozUy+WqCyTMbjpt/6U2fWOZk5vN2rVznvq+76dX+kT/7E3A9ebGPzeQcSmV4BC9c97j6xsvQxndCj588rkVWXIWkYuo6lX14rJodofb29sDgTxS8BeUPNT0UaK+gnP2xfUvZVrh3MApRe+m/vTjeze9ZGMt3yNPfrnx31BA1sz5OYAYiRb/aisru+l9N4xzqTj1Jps996IlUBoxDPYdsTR9eRY9Z26uNTf+mYHUvKEotUBKhvkQ4HUlQqfSYzLWbOgm+uuwtE1eL/8OHmj9i6Vpz/5w2G+aUnFMMdjpdP/3X3/+dd+NxvuYgIEM19sNz25gaem7sCalRAWkp4fJvub1JzqNPW03+D0z6YiZ43dBMaS0JP+vO0rSAsha3HTkUlc2Si+qtlotrKGn8oZfATJZaVh/IwkBRCGLpqk1BjOO42Qtvh8/vdDjREuaoyUp5t231sleUgG/cEkkRzgBJCS2gGaz221b6vIViPPrx/fv98fBuBAjYPopHiZa4UVfACQoq5mmwbzl1Zxrmj8NNcGfie+9d+3c8f18Fi0Nv1/rdvjCOKdCxqaQjDGhvQftRdGUmkiW57Ffsui/4hYYu7Iy9MOX/rxrQ30kd5uKh6nvlBzZKwbUGWa79DuHrDPColNo9y1Nk3HBBq6FRi9ekSC5ibgBVEm1z4USfOFEOT48HLthtiFGAEQiDoLPHDMpQQzUFyDvm4yOwRSdtf5lHn8S4gRpxcy8ISCILzpdS95+SS198cBcaUCN3iGrg+CzNXzlVdJFnrwKApDLIjIdmS6prS0pkJ6oXr5YhSAAQ/6UWP2T4wkm4qfWhVzoqj0cGhWm7lgoKWbrl5Yojye6pNhhS9QqxhB8CFmDhOCsW5hIAHIxdy4QeZZnyKcGAsQYCUGb+acyaVDIr2SYDCWmFur3zypiIs0xdiE3WKsmEFJ40UvPEwJLCDErgdcEBNcyr8uvP1nBL5ARkg8plVwai1Gx/Wu1nC+PhDGe+Uq0YIwJDVxHJrUSDNdM4udDiG/fAnIuhHjD3rhupJTgQnzxaaBofSVXgjuYUOVmf7vTaT7VZVGocTLWx5BSetr7g1EH84sWSClGslgCkbLHpeX6wq2xsrrxZ1vlbIBBCsFHYJwLVRRFoSRnDHPB7rw0FiHKqlWLfUZCSIOYmchMeU69SKWVzF3ini0yIVGpsIUl73Vu3ows4EKkeFlqd7nKhVzjZ0q5n6uo3PRdX74Y4kojuphrJ8tuep/rppgA5D6hENQXPZfaTubqZkXLeD8lQyi8fJh+5NLPZsopuzzbegrnlhJrZfYzBfDysij3UG8PhwrtpqnKsuj6abZu4V06SyIudH0rX19KxFXqXO5esGw/52NkCYnmRNOW549fVEaeUwAtOGd9YoyJRUExoDRe1khnqrxstHH+luP/xuML3s7zNBsXYkTqWL0wXnlMT3UmACJnQgqeo3zp9f4gSMAbpW3kZwHB9TeafymQ+bQOWVJLF0dipqIoVy6K3ER4XluNXZWxoGz6Eumlnp7IZELGc5JwaQnyCQXyRG8+N0SRC6G0Uo8bF35iPBVGsjiItep8uqQUr3oqiFzIom62+4b7pq6qqjqe+nGy1vuYGXETwIL/rpp2u22JuB1gpZvJnFcpBm/maZ6N8zIBADImVVFWZaGkCysJH208RlhgrTk4MyGGXEjIzy2FHkXN0qI9pMz1IQAf08QJMikFNaQSOTBQVHVdFdr4gI9Odly0plJacvB2RkgQX8mD4EI1XyeRYSt4/ndJFMUYvPefhcnA6jUtjJcJ6BgiPrLME5cVyJzBa9cf9inbWP0wmTJwBhxZAkDGckkV/dVz3No7l13GUzqW1T+gw5KsE34ND8tbk8W1MubSb+YMWYQlFk6lE1eU1CMdoXXTbmSsq6qqm/ah68Zptu5CiyAxSpXN7nBYKBMw014Rxpb6MwRH/dCsDyKRC6KLum7qfnbxTOELCIwmrqqq4MmMnPY/Pkc7ZnE679dFJX28YoqgJWYax2GcrROM2mRTcelkQvRnbCUuJpNUuixLzZOdOKQYXzGxMNNHtWNUVHUJ6z/Lbyml4B0ZPp+kJMSVSJLzkNGCjImVkD1TsRDTVWaGvF4+MqsoNQtRgjHgjMKRF+RyT0pnrrvy2uL4fBsXvaOyLV5kyoOf0CApJ+PWyTB3PiT+w4SMaKVj8B7D+zokK4aiKKtaQ1WWZd20m+OpH6bZWLdGbAEZk7KoN4e728OuKVWuYKJzara5+jK3/iIsCkOAhcWyHYxP4ImGD1f1UdZt2xQijEcIPibM1SPLc8xBMCkEY/SicsfKqtRKcPxEgILaB1AkU3Fc+Oi3p36yIUKIC1P9shelLqu6rQoRpg5j8CG+QvtDNUabKcjJ+pRWsVh+AS60Y+M4of1IlcyT9yWk1oVSLi5alecCmja3YYUUvTXjMAzUsuj68z7mbDqxkPIzQjgXVHVd/3EXPT2tl4c1mLEUD2UC/brO5I0/oUIWq2OVkGwDKCUjgzUr7oy5qm04wXG11loXPGhdVHW7PZ26fpgmY5wLIaSYshYv2+3h7tvtri2lyNzVq8JIae253g8UBcEltbHd9ZMNgCyzLdPOU0XVbLabtmS2Z94YF2Dt3XDuP6q01oVSPmEkanai4SdirI8/vYX2rBvGQnFkwIQsms2uG2YfAb0n8xUhR1WULpt2s2lK5gYZnbX+NfJqZEJVrYmyJaZ6fPYLUvJ+HvvuxOADm/bxLMiFLMqqqk0EFiJQIqve7Pb7XVufCYcpJ/7h3RzPiqJS8SIvvwQUxwwT/ciiU6KS5TPlClBEl3Oee5cKVVT1yrn/WfFYktXLHoJ8qKqiLMvSs4Bcal0oDn4eGSz8a68PBETOhZJKSqVEosLaTdf1/TBO07x0DwRgQuiiareHm9ubba0lEeR6O49r7760gsdP/dBoyZHljrK7YbIBmCDjNTuzuqjazXa3bTXMyk+95DHFcAFkgdX+qQcX0UdgTOp6s91u26ZQV3U7eP4IY7Dz0J1OJ1KDBKjYjuPsQkJu3KIxs/dRVE27222bAs0xmZHU/wsCQqUs1TaJarI+knw/EZAE0bupP/7gQI7VJ9a/gGy3s0duXUzAuNBVu7+9vdltqkIyxJRyh7Su/6C/kCAFbydy042WKeV4a4b6fspFJ6QOeV5LW8BcRySlELm9QlG32+22rS9oZD41FmHMsYls+VZNOzjQgQldVqUW0Q4SYgjv6hBceXAEFwIFbYnNMAzjOE2zsUurKk7sYdvtfrdtc/EMtYmgQ4WaF+Q3czq2DXVyyeiR2frE5DA5HxMici6lLupms93tNrWMQxpLTfVqC8OsSDlHUzab7WAiCheBZS9ot6nLT1mqOY4wdseHllpgMWBSV+1sbEhMqHwnCZExIfIi97ttrdLITFe80f4AmVBlZLo1lHB8Yl7lmIaZTrUEb61jH9chCKREm+04B5R6diHRDzaHb99u922lOMMMyz2djqfhw8d9Wvt/DnUhWY5aLCffp1x02rM+t+1IC4mzVEVRFjaxmPuq7vb7bVOqT7HVnOci3FwWRshucNlsB5OUjVwVdV0VPExHFpzz8YVM1ZOx8ouxTNWmirJup2meptmQgIREBN+kBJtM0Zqt0lPXUTNJAoQGN4/d8djWhSSuay6LxvmQuCq7kY48xklTtZvtdtuUzPG5XLs9zdM0GxdCZk3RdbtRZ7PAAAAXLElEQVQbTEA52QhMFtVmd3uz31SffI4pQfBm6o9tUxdaMBQMuSpb5wMwWZyWQiBEzqXSRd20291u11bcK3cqMmHpaz6IApSlzTilR+75EpTzdqxVstM4GfcpVkJEIctmZz3Ksp9siMiFLtvt4ZZMXyrft/NwOt4/dP0TMqP3nw/EQB1LT9sxn3GQFnbrU9d9zkWPwVlj1v4S5EtWddPMkVs6+Ort4Wa/qRTHJSz0uUHaarV8FtdwtEkNFrgu66bSzPYizNP8KvzuPHIxCAIkQGQJmZCqsNZYYww1aA4xpcymX5VVVWotOOISETwdO9IMKQE1CBu7432u8afCtCqExGTRdINxIQF5mWXdNJtN25Qqzb5QgjOAGKyZhmGcjKaadyZ1tTU2oiwnE4Grot5sD4dtrT99zqTo3dwf64qsXRDIhK5DTEwWVbO4oKTkyqpuN9vttq0VmNjrJYlFAvI43I7AAIDJwofVi3kU6IUsIDKZ/nSSYsm2P82gvfjn8zSMSV3bAKJshsmEuHSFvDnst5WWxJ07D93x/v7h9NQeepIheCl7l1KwZrFBGQIDTCm6bJe+aGG9fdWUKczneV5jzsQqtNnNkRcuIJO6arb7w7ZSjJB9+PK1nv/ghQ/kBkOZUB2QCV1bl0RN/ZHrqpQwF3Hqcnu6Ny+aX1wGVCEip/CS984563yOY2WdqLTWWkrBGGb9cXy4fzitLMYJIHg7ddStjDMEgcBkiokJXW+pVWeO1JZVXTd1VSrmEyU1MEeYum5Ta8kWD8b7gKLcjDYAV2XVtNttPmeePMcnN/jyU6WWvmN3XxRaCgaQBDBZREChyuY0jLPzgbxGVZR13bZt21SaRxwXGk8SEMIznCP7CQA5MnEOGp0F4yIUZEWcu7pYQgwLLCKv73m+ICeGF/oLICrIJqKsNuPkfEQui7LJ9rtghIDuu4cf338cu9E8azqxXis9vYP8Gep5dDpu6kIyiByBcujd6eF4okbYTx7uelUKQz676tK4bJxmKzmS5a2rzWiiKEcXgdqPbDa1wujtmkt/fq3zD9LFB+BispRi9M7OGUOAgMikrkLkxWYOIFRRloqHMY3l/6/uyZYb2XUDuPQm2cn/f2Husa2lF+4k8gCy1bI9Pj6p3FSla8o1M25RBAiA2NFrKUR5OkkG4HgGe/jb+6AQSAAKQVKVnHPiwonqxaqNcjmG3Yy2+/VyvS3G7xlxPPLvPgx1wBlJgVKPKPRw+o/N+pRLTe4YxnGchl7LAqKBWg3o+8vUa75+pB5LATW8LDZkkLofx3EcNeSUkhT4LVG143/Q3BOaqaRgl447sxARSZQdodD96XXZak2UkKrrhnE6nU7TNHaKosD2NQCKdV0q/KfdBQgCi9wjVE+8wU/JWMah77SSAmqSZml1f1SjBU+zIOpoCH6hdgQTsptIja8u1NkEw3Q6n07joLlCwK3z5f3941pHox7ExGOt/V/0HNQnpJK8Xe/n89RLKJ0USCX6bb7xxOHPFwgBZ3HWGrTm0D2qYZwEadZ1PY9aIggAobrpNRQ5vNpYQHb9ME3j2GFyUiolBYrHfmmflXRAej3k+l1HEKi6Vn2IWrIrVI+EerShgNRdrxUF6nlk32PRUgqVmh3y1MutDpVaR4VUatatFKRKe/jV2mldCh6aSjlyIvr75b7YvUUSYcHkTdf3HWvDpVOIskOh++nVcS8l7iva9Tx4k6hUjbFQoejMfHtpgUgEoXoCoceXzYUCqLRWWoocpBCtgWODqAk2qrHUI9HtCfmMj+ik1poNqZI7haJDVN30slkbQioF2IPVj8M4DJ0WmGrmH2sIqqb255xyzrL5Clnl/UY93y86BNnqYx6pAnUZAbj/vfkoefuZJ65lFkAppwKyF9055lzqlN9hGAatJccxtvny/vb2fp23Y3dQgj2zNJeMbdkv9SJUcnTbPE29whwGJZBytOv14/1yWzb/KTuYHiPhcilEJXPdzHPFUE7BrvN8Z7tfIqDsplREdzIuFhQ8yUSQT6mAkEIgCSgll5zyI9vyCelZNGjSIXOnemF4qNmgBUgAFIpA6DGkDEIqKSDlQ2r2wwmdOcK+HwFV/12w63wbFOTUd9zhExFB7HdNjaQ/OlnyWCa7XD/e3t4vT3KKoCThVqUUW4s5d0qAUCj1UJMYQdRQsFJSIOXgjDHWhVRKgei2+XSahk4KAC0RhOpR6OHsfMwEKABKCTHEgjX/7BmiehO1E8vERPd0ZJQxyFVWf09kj7EapBqmVxd45hpKqTmJUSuJlLzdtq31ugEFNZs8hBCjoN0eov3HE2c8NIRS9sHARIRUNeYYggQJlGPLwC2Pw+NBkFFCQSg8ECvEIvoegDjWrzrdaSWxQE7Rbcv1/V//+utyW5+N0LZWiFEQlhS/zfYloJy8mfteYfZc15mCXS5vbx+3gyx8rEqZ9fGoSFCKITYIdrouJXm7ztfT2EmgXgkEVN0JZP9ifSqAUkiEEmNGHYqQAkkhULUl9uq2I9KREEpkO+AJhD2iuUy9EpyDIRSg7FIqAIBAKXlnXXUZ0L5oUFAQ8j4hlZXaUqI389gJit5NY9cpJSTuVRrQ0rPZX4nQtDxvl/vHX3/99XFdrD+2waeSgpU1mh/DaeyURBRa6L4W77T+pAK5D9lyv90X42POBVKw623gHmVUlBQgFKDqxhBSzqXkFKKPRY6RUCAoAcBEFfa2PQc6kCSA0oHoqiKf0QsUCFRiCIHb+gstdB9j4rxILj9SSkpEyjGY5Xq58aBeIFAt0dhZO4jya4cBUQrO+SYpmBijd9ZYjVkC5WCtrZmUrNKXnIJ31hgFWSDU1IWQqQ3/eAzlpkQ5BmeW28fbX/96vy7mOeu2xnWtGVVRSNEaa9031f1UUrBaKyxhO4+dBG569PH2cf88M4cAgDL7Hq0GjZSdNdb68HTRUMnRrbdx7BWWnDolkUD1ILqpVU+XHIK1nrTLKJCyElCys8a2fMuqgyfvrTWDKAqhBGeMbWHqw3d5s95vZ+75IRGJAKUWUpVSKOfkzXKtJMdfHb2z1nSYJcLjCDi9kHJ0m1aYgzWvp9M48Pibmi6IRx26KWxsci3369tf//X2cd+eCvgJSo4oRA1qOD+NvZYSQaBQtAfhakJdDt5u9+v7x20x7F8Nbu262iV56pVi/heqiymmGHO02+aSnDwBUNYSIAdjHyfdfIrWWitJCyjJmZowf7TTERAox+CdfT1NvVYCUQnNDR0AAIVArlTJKTq73q9v79e5blK1wMCyDCL3vw1ZEpQUzHxoCtK6tc09JBbV2zwvm/OxNCU7B2/W5dRB1BKppliFLPQwDEJJJWsuPuVScvRuW++Xj7f394/71uaCV+ZsqbqTpkFhiXael7Xmohx1eKAcnRRYoplPQyex5GpuzubLSBDeod2WWUPUgpJb55nHex7N9Bxsd+87hSVFP2otBBHKXujIN30M0S7L6kpnM2CJA5/cOs8slWqBdEnebesyytxrhBztPLdsgaZgI3EZ6mnsFJTEWbXVzCspp+i9XefL+8d9dTGXItivvc69SL1EysHc90Xrfboh5Oi25fX1fJoqi8g61BZ2Txc0fT7F4M16v72/vb2931YT8hPSqCS0LIecNeZlGgfdWveKpm5Q4YWc2Zb75ePtMhufSgFCL1k9yzGENqqZAIUsWJLb1nlZbFKTL0Rp7D5DRKy0OLMtJ1WiklDifmQ7HxdIjZOs3baXc02RQ5SiGtlsIWTO3zfrcr9c3j/m6hVSLFjMchtEdv1vcyOISgrm/nGbN24fxDS73k89holF9Xa7XOdtb2NHJQW73k8dhFFLpBK93Vbjs+yn82ka+qIUCUSAUkqM3m3rfLtcPi5XTi7bNSxCopK8We+TIjcqLNEuH9f7yvMsnvdZEgqg7Lf71GuJJQdnlnleVxe/FIKwfXE/dRBHZpDrlaXzUcfKKWxKSSzR2/PEXn0qhEIBAuUUzDrf7osjbVLOwTYGuV7vy8apO+1yWG6jzG5QzCCXy30xIT5qQdkjsAxDpzBHDsc12yXFELy123K/XS61yVmNGl17kaZeIqRg5o/rnSNIxDYIQkneLPPr68v5fBrHvut0DR1iqy+hau3nnEJw23a/Xd/fPy63eQvPSOMxtQCUOS/09Xziqbd7/QYRFMolxeCd2ZbldrterosNKRNhjk4IVmuc5cEjSJS5usGsy3y/Lzark8852qljiO47RGzGervcJk1+1AJKsuv1cjseGWGBVPFtzbK8vpymoe/UIc+6WccxBme3bZnn2+3a3BGgmLKXsRfZnn7NIFBKCma+vN0W9pbyfyzXDsPaGGT++LjVo+PT9mYeNfm1Moiz67r5LIfzy8uZe8By8WVOwTuzLvPtervdeZjak7QvOfrtPijyZ75B1uvbdTbhcwMGAijJEyW3TX2nZD3Kaih+YaaSgplHDWFlBtlu71WtOL6Vg5ACKXm7vnJFdTVmc0rBO7PM99t9ddTZGIM5VwbZbh8f1cZtXs711stsT/UGWW5vl9m443VFJQertZaYgzuPnRYCaiuC6L2zdtuW5X6/L6uLuVApOZj11om0TfUGWS7v9QgAgCAzMdt1fnl5eTmfT+M07ANrRCtr2dkjRufMutxul+vlOje6e8ZvBoCSk3fbMr++nPlWaiVOVJ0pIThntnWd5/k+r41jSwJk/rDb+tJkTU4peGu25X6/z6sv6uRTsC9Tt0PU5AFhKUwHxTOao19vH5c759s3Ji6Q2JyrWzxN1fnaZmO1PXpn7LouyzIva9VHWMXK0a5aZDdPNbvu73iEAErJwa6362U2jH62hxXE7Twwg9jlxtKisNTP0W9aUdhOQ2OQbTW+yOH08npuwhgoszGzbcs835dltS5+KvtjbbqTxS+nXmFJbp0v9eb+dClAAaASvel1Dc1zIPzzkrCzsKK4nQYtKHkzXxnbBwczUkaPQDnY9T9fzuNYY65U9222Zb7NswnU2eDtPA1KQknezLeP29qcQEQp2kWL4uapV6z2zJdL1SUfm8/Jb1JACXZ94Y6QnH4SgnPOGLNt67pthkUylBTsrDCbF1ZIol3vl11ZgDb8OTizzKfz+XQ+T9M49k8sAgBQagw/OGfWdb7fbvf7sjn/tactAeRWkbDOL+fzaRqGwwgcKlU0O2u2dV3XbdtsVVqpAHgWdmadX05jrxViNWW3dZnnebWBlPHBLucdotv1UiECopzcqhWF9dRrASX57QnNO4ew0WrW+fxyPk01PMFcTEQ5pxS9d9aYbd221RhbgSVQACVHJ7H49TR08vcqVo7OrHO9igig5GAEJDdPva4uvWWe12rWsUItBSQ7j72SSCUGa40NRfbT6eV8HsdOcw4C21NmW9d127gk9Jk/gErym6Bo7kOngFIw61JjiZ9TR6iwYa0525ZKSpxs+IU/kEqqIIy9QkjRbst93j75B6Ckqi9uM0sjxXdIzik6Z7ZlWZbNJVDO25Vz3kuKdlvmZuQSQinRSVHCeho6KThgM8/VMjxcIQkRgZI383nqeXgGO+2cc9Yaa43jVPXCNrOVWPwycSe26M06z01Z4LmdlVS2cZxO02mapnEY+k5rLWVrw8JaHJP1tq7LPC/rZlz8ruSZAEpiN8+2TKcTM5yWHD2mUkq77owxLSmSpzchFQYxum25vUxsPlNOIThj1nVdN+MTKRe8mccniExsKlb0kumgVwJKCm5d5vmTyYqFGtgrg9zvxhe2Kzl476yxxljrnA8x1SiyIoASESi59Z8Mu2KflDHGVplHlCNiCXbuO8lwOms26/auGSVHCyVsI8f9CxfSpiL0OJ6m09izElpyjiHU47etK8AnYi4JEbLfhsqM3trN2m86+FSiSNKLJtPynybpVR09mLHXijnYGPPFsqGS9wjL2FxBDy+dZSGZQQbv1rHXErE0zS7kVtia0SMjXUskStFbY8wnbzZSSTyMdz2fapJfyTnG6L33zrUcqkeRH2KJdt4HoHg+gkouBAClJQP0/TjwcPShf3AIAtKDZKzdzLqtpoqp7zLXOOBbUvJ2q1MGu06pnUFyTjEG77yzzrtQy3sJGoewHbGeTtPQGCR6b61hMZ5BhuC38QDR1miOkCgHpBzWfaZesLbGMD7RQOGmEP3wGIT4zCDBe+ec4+lSae9SqACoQKQS3drtI0v/nj+glJyiD3srBaRcXTydEgKIcozeP3zWBFASUA6m00oiEuWUQoyJhNqGZRgGHs1CVeZwAl0LpHyjOEFJfuu0FMjtXXzY0+ie3wWigrmVjRN9jbkfyJFB0FIilQrBp/AKIWWqmcID5yvJ1rgmRu+ccy6EVFDE4PpOS4mPvhPHPA2AEt2m2fTOKQYf/CcQWEOkEt02jj3bIIVbgobALv+9EpAAqeRAObo65KbkB14e5IxUikjBa82Tn/uh58myUtXKyKZgec9iyjnvvxNTn8jPO913fdftQzihRV5TiCH4EGLkPoct0I18vafgtnEY+k6xyzeF4J13PoSYCUSKwRwg8qFCRI0Osu+7WtWVUvjuyGAXDLrrDoN0m08oJQ56hX2Puzt0LzdUO1S/4RCqt+dBWUHEuopExBq0aviAVvWr1EG6cGyZW4CwNwWhTkmPMX7e6uFBwEOXMlYiH2nhnx8EOHhogAi+YQ+AWi7bEFFbB6avgpNr6XkmbMtYqhuPNbiYCoEQ+3B3olyx0ZoZcKH23meNTfxvQMBWBtj1WktmkJJySimmxIH+w2HuE+UZLfWEypOGyo0yODimdae7ruu01lpxrTs0xSiFEIKvd9TPfV+50ZFQUmmluYZaCEQkIsoVMm4Y9LV9Crc70V3Xaa1EvSFTDLFdjPjobLJDtNMcPJHuXqzz9a7j9IC6R66J2TVKjs/HlFJMOXFGGjUnNTu/RW1s3zpM/P1DLZb+UFZa6emuy3C56K5SY+uff/BwlEKE2CKZtca2Zgykcjz8b46EQ7RNb/pxvuoh27LpGt++Vgc/onhAQN8oFkxiQqrWmGOnq5xSTtw4HdsGscGaD2thizKjgPrCt+A+KvcV46dQKZkHkuc9LYkO+zqi5fmEHsir6JOqVlBpqR5ztrkVckoxxRjSn8XU04q4jw/hwbJcXVCTo3Ld7de7m9EglZItNbKl4KTaOx4FSlGjmV9pDh4Af4vmT3sUKIUUjz02LxZPeD/scf88PuD7psnETw8RfFJW6hYQAdp00md8ILSpdtiUVyLY+WbPCt4z7p62+hlc2P2STW/64QjhiUF+eIdBgEfq+LfLtnwllgei9bGojWuqxlN3WLH1ea1PLY/+CEIj+p0PK36o7B/4fD2gAASs4YzvFsW6LIpGL1I8vFgVkD3H9zejnbEebmtL9DgYonLIMPyyDu6CdXcNt8zJvdL4gabvaO5ABz8d2S4YauD84dWmlgL5PUYfnz3K2b99qAaUPi3WloH260+yq2X5HN54MNYBqTuYfzwXPHzXl538j58nEL6B4PnrGzdxLVY7PWZrBDg2lf0WG1+w9Yfb8oifXbB8i6DvjuDP1MLMh5XLD3mnO5X+LKa+fvMuaI9RON7sz/C1D9Lx/Jk6foboCYs/HtmubB/2yM20f9gjPv3l9/cH1HW+kU0tdv/dr5++pr3R2OZhJfyO4ve16LHc/8LzvOxPesV+OM/8dFCj2o+/x8ZPX4Z/ws8fSB9+PILP+z9Q58FOo+bN+AeiB4/k3HZQ9/vTKkcjEZipnl7/G4h+icU/7fFnjOKP//zx+ZF0/naXz298urv+Hs7jZ/+3OOOfr3rYNj649IsS8Qts/M23/XP8/GLRtuyRzR9kTb8UU19X/ExFvznNJwC/Z338eZFfAfxlj61f0h/3+I/ujH/v85kA/r88RxT+Ozf+b8LPTi7PpEDwJ0L97ZrHlf7Zp/5Pjv8ruN8+/w1xtM5c/5zgcAAAAABJRU5ErkJggg==" />
+ <mask
+ id="mask12">
+ <use
+ xlink:href="#image838"
+ id="use297"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image837"
+ width="800"
+ height="109"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip28">
+ <path
+ d="M 435.19922,909 H 561 v 125.6133 H 435.19922 Z m 0,0"
+ id="path301" />
+ </clipPath>
+ <image
+ id="image844"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask13">
+ <use
+ xlink:href="#image844"
+ id="use305"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image843"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip29">
+ <path
+ d="M 0,0 H 1315.1992 V 1392.4063 H 0 Z m 0,0"
+ id="path309" />
+ </clipPath>
+ <image
+ id="image855"
+ width="1316"
+ height="1393"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABSQAAAVxCAYAAACeLII5AAAABmJLR0QA/wD/AP+gvaeTAAAfoklEQVR4nOzBAQEAAACAkP6v7ggKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA27uDmwhiIIqC7ZEXQt24yYIDSETB88FVEfj89HsGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA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AAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADI7Jl5n34EAAAAAHCFrz9PlwjQ7AoW/wAAAABJRU5ErkJggg==" />
+ <mask
+ id="mask14">
+ <g
+ filter="url(#alpha)"
+ id="g315">
+ <use
+ xlink:href="#image855"
+ id="use313"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </g>
+ </mask>
+ <clipPath
+ id="clip31">
+ <path
+ d="m 438,911 h 115 v 116 H 438 Z m 0,0"
+ id="path318" />
+ </clipPath>
+ <clipPath
+ id="clip32">
+ <path
+ d="m 452.13281,911.91406 h 86.65625 c 7.69141,0 13.87891,6.19141 13.87891,13.88281 v 86.65623 c 0,7.6875 -6.1875,13.8789 -13.87891,13.8789 h -86.65625 c -7.6875,0 -13.8789,-6.1914 -13.8789,-13.8789 v -86.65623 c 0,-7.6914 6.1914,-13.88281 13.8789,-13.88281 z m 0,0"
+ id="path321" />
+ </clipPath>
+ <clipPath
+ id="clip30">
+ <rect
+ x="0"
+ y="0"
+ width="1316"
+ height="1393"
+ id="rect324" />
+ </clipPath>
+ <g
+ id="surface854"
+ clip-path="url(#clip30)">
+ <g
+ clip-path="url(#clip31)"
+ clip-rule="nonzero"
+ id="g331">
+ <g
+ clip-path="url(#clip32)"
+ clip-rule="nonzero"
+ id="g329">
+ <path
+ style="fill:#303030;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="M 438.25391,911.91406 V 1026.332 H 552.66797 V 911.91406 Z m 0,0"
+ id="path327" />
+ </g>
+ </g>
+ </g>
+ <clipPath
+ id="clip33">
+ <path
+ d="m 562.39844,929 h 584.41016 v 87 H 562.39844 Z m 0,0"
+ id="path334" />
+ </clipPath>
+ <image
+ id="image860"
+ width="730"
+ height="109"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask15">
+ <use
+ xlink:href="#image860"
+ id="use338"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image859"
+ width="730"
+ height="109"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip34">
+ <path
+ d="M 291.19922,909 H 417 v 125.6133 H 291.19922 Z m 0,0"
+ id="path342" />
+ </clipPath>
+ <image
+ id="image866"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask16">
+ <use
+ xlink:href="#image866"
+ id="use346"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image865"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip35">
+ <path
+ d="M 0,0 H 1315.1992 V 1392.4063 H 0 Z m 0,0"
+ id="path350" />
+ </clipPath>
+ <image
+ id="image877"
+ width="1316"
+ height="1393"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABSQAAAVxCAYAAACeLII5AAAABmJLR0QA/wD/AP+gvaeTAAAfw0lEQVR4nOzBAQEAAACAkP6v7ggKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA27tjnAhiKAqCfwYDR+XcXIIAIUGyV9i2kKukyR23/DwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA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CQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAya2Y+dh8CAAAAADjC5x/qzArhTm/VnwAAAABJRU5ErkJggg==" />
+ <mask
+ id="mask17">
+ <g
+ filter="url(#alpha)"
+ id="g356">
+ <use
+ xlink:href="#image877"
+ id="use354"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </g>
+ </mask>
+ <clipPath
+ id="clip37">
+ <path
+ d="m 294,911 h 115 v 115 H 294 Z m 0,0"
+ id="path359" />
+ </clipPath>
+ <clipPath
+ id="clip38">
+ <path
+ d="m 308.13281,911.32422 h 86.65625 c 7.69141,0 13.87891,6.1914 13.87891,13.8789 v 86.66018 c 0,7.6875 -6.1875,13.8789 -13.87891,13.8789 h -86.65625 c -7.6875,0 -13.8789,-6.1914 -13.8789,-13.8789 v -86.66018 c 0,-7.6875 6.1914,-13.8789 13.8789,-13.8789 z m 0,0"
+ id="path362" />
+ </clipPath>
+ <clipPath
+ id="clip36">
+ <rect
+ x="0"
+ y="0"
+ width="1316"
+ height="1393"
+ id="rect365" />
+ </clipPath>
+ <g
+ id="surface876"
+ clip-path="url(#clip36)">
+ <g
+ clip-path="url(#clip37)"
+ clip-rule="nonzero"
+ id="g372">
+ <g
+ clip-path="url(#clip38)"
+ clip-rule="nonzero"
+ id="g370">
+ <path
+ style="fill:#303030;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="M 294.25391,911.32422 V 1025.7422 H 408.66797 V 911.32422 Z m 0,0"
+ id="path368" />
+ </g>
+ </g>
+ </g>
+ <clipPath
+ id="clip39">
+ <path
+ d="M 435.19922,531.07422 H 561 V 657 H 435.19922 Z m 0,0"
+ id="path375" />
+ </clipPath>
+ <image
+ id="image882"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask18">
+ <use
+ xlink:href="#image882"
+ id="use379"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image881"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip40">
+ <path
+ d="M 0,0 H 1315.1992 V 1392.4063 H 0 Z m 0,0"
+ id="path383" />
+ </clipPath>
+ <image
+ id="image893"
+ width="1316"
+ height="1393"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABSQAAAVxCAYAAACeLII5AAAABmJLR0QA/wD/AP+gvaeTAAAgAElEQVR4nOzdQU4AIAwAwUJQ//9dTYyfMMuBmRf0vGlhBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOD/rZnZt4cAAAAAAJ7we2bm4/YUAAAAAMATvgVJAAAAAKDyI0gCAAAAAJV1ZubcngIAAAAAeMKyIQkAAAAAVARJAAAAACAjSAIAAAAAGW9IAgAAAAAZG5IAAAAAQEaQBAAAAAAy68zM5+0pAAAAAIAnCJIAAAAAQEaQBAAAAAAy+8zM1+0pAAAAAIAn2JAEAAAAADKCJAAAAACQWU62AQAAAIDKtiEJAAAAAFScbAMAAAAAGSfbAAAAAEDGyTYAAAAAkHGyDQAAAABknGwDAAAAABkbkgAAAABAxhuSAAAAAEDGyTYAAAAAkHGyDQAAAABkBEkAAAAAILOdbAMAAAAAFRuSAAAAAEBGkAQAAAAAMn7ZBgAAAAAy24YkAAAAAFBxsg0AAAAAZARJAAAAACAjSAIAAAAAGW9IAgAAAAAZG5IAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMT20AAAAAgIwNSQAAAAAgs/btCQAAAACAdwiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAD8tWPHBACAMADDduAV3TjBRTlIFPQuABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCSFT1ioAAAMGSURBVAAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJAxJAEAAACAjCEJAAAAAGQMSQAAAAAgY0gCAAAAABlDEgAAAADIGJIAAAAAQMaQBAAAAAAyhiQAAAAAkDEkAQAAAICMIQkAAAAAZAxJAAAAACBjSAIAAAAAGUMSAAAAAMgYkgAAAABAxpAEAAAAADKGJAAAAACQMSQBAAAAgIwhCQAAAABkDEkAAAAAIGNIAgAAAAAZQxIAAAAAyBiSAAAAAEDGkAQAAAAAMoYkAAAAAJBZM7NfRwAAAAAAXzgXuNILze+fCBwAAAAASUVORK5CYII=" />
+ <mask
+ id="mask19">
+ <g
+ filter="url(#alpha)"
+ id="g389">
+ <use
+ xlink:href="#image893"
+ id="use387"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </g>
+ </mask>
+ <clipPath
+ id="clip42">
+ <path
+ d="M 438,533 H 553 V 649 H 438 Z m 0,0"
+ id="path392" />
+ </clipPath>
+ <clipPath
+ id="clip43">
+ <path
+ d="m 452.16016,533.73047 h 86.65625 c 7.6914,0 13.8789,6.1875 13.8789,13.8789 v 86.65625 c 0,7.6875 -6.1875,13.87891 -13.8789,13.87891 h -86.65625 c -7.6875,0 -13.87891,-6.19141 -13.87891,-13.87891 v -86.65625 c 0,-7.6914 6.19141,-13.8789 13.87891,-13.8789 z m 0,0"
+ id="path395" />
+ </clipPath>
+ <clipPath
+ id="clip41">
+ <rect
+ x="0"
+ y="0"
+ width="1316"
+ height="1393"
+ id="rect398" />
+ </clipPath>
+ <g
+ id="surface892"
+ clip-path="url(#clip41)">
+ <g
+ clip-path="url(#clip42)"
+ clip-rule="nonzero"
+ id="g405">
+ <g
+ clip-path="url(#clip43)"
+ clip-rule="nonzero"
+ id="g403">
+ <path
+ style="fill:#303030;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="M 438.28125,533.73047 V 648.14453 H 552.69531 V 533.73047 Z m 0,0"
+ id="path401" />
+ </g>
+ </g>
+ </g>
+ <clipPath
+ id="clip44">
+ <path
+ d="M 0,0 H 1315.1992 V 1392.4063 H 0 Z m 0,0"
+ id="path408" />
+ </clipPath>
+ <image
+ id="image903"
+ width="1316"
+ height="1393"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABSQAAAVxCAYAAACeLII5AAAABmJLR0QA/wD/AP+gvaeTAAAgAElEQVR4nOzdy21FIRAFwXkWITn/uPy9OAo3C6oimHXrIF4zswcAAAAA4P+9v52+AAAAAAC4hyAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgIwgCQAAAABkBEkAAAAAICNIAgAAAAAZQRIAAAAAyAiSAAAAAEBGkAQAAAAAMoIkAAAAAJARJAEAAACAjCAJAAAAAGQESQAAAAAgI0gCAAAAABlBEgAAAADICJIAAAAAQEaQBAAAAAAygiQAAAAAkBEkAQAAAICMIAkAAAAAZARJAAAAACAjSAIAAAAAGUESAAAAAMgIkgAAAABARpAEAAAAADKCJAAAAACQESQBAAAAgMyamX36CAAAAADgDmtmfk8fAQAAAABcYa+Z+Tl9BQAAAABwhW0hCQAAAABkBEkAAAAAoGIhCQAAAAB0BEkAAAAAoOJTGwAAAAAg48k2AAAAANARJAEAAACAioUkAAAAAJDZa2a+T18BAAAAAFxhr5n5PH0FAAAAAHAFQRIAAAAAyOw1Mx+nrwAAAAAArvBYSAIAAAAAFU+2AQAAAICMhSQAAAAAkLGQBAAAAAAyFpIAAAAAQMZCEgAAAADICJIAAAAAQMaTbQAAAAAgYyEJAAAAAGQsJAEAAACAjIUkAAAAAJARJAEAAACAzLNm5uP0FQAAAADAFSwkAQAAAICMT20AAAAAgIyFJAAAAACQsZAEAAAAADIWkgAAAABAZq+Z+Tp9BQAAAABwhUeQBAAAAAAqFpIAAAAAQEaQBAAAAAAygiQAAAAAkPHLNgAAAACQ8akNAAAAAJDxZBsAAAAAyAiSAAAAAEBGkAQAAAAAMs9rZl6nrwAAAAAArrBPHwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADw1x4cCAAAAAAI8rdeYIQKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI/CkxMAAAE5SURBVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgBELhV8rTwD7iAAAAAElFTkSuQmCC" />
+ <mask
+ id="mask20">
+ <g
+ filter="url(#alpha)"
+ id="g414">
+ <use
+ xlink:href="#image903"
+ id="use412"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </g>
+ </mask>
+ <clipPath
+ id="clip46">
+ <path
+ d="m 445,539 h 101 v 24 H 445 Z m 0,0"
+ id="path417" />
+ </clipPath>
+ <clipPath
+ id="clip47">
+ <path
+ d="m 457.50391,539.12109 h 76.10937 c 6.39063,0 11.53125,2.96094 11.53125,6.63672 v 9.61328 c 0,3.67969 -5.14062,6.64063 -11.53125,6.64063 h -76.10937 c -6.39063,0 -11.53125,-2.96094 -11.53125,-6.64063 v -9.61328 c 0,-3.67578 5.14062,-6.63672 11.53125,-6.63672 z m 0,0"
+ id="path420" />
+ </clipPath>
+ <clipPath
+ id="clip45">
+ <rect
+ x="0"
+ y="0"
+ width="1316"
+ height="1393"
+ id="rect423" />
+ </clipPath>
+ <g
+ id="surface902"
+ clip-path="url(#clip45)">
+ <g
+ clip-path="url(#clip46)"
+ clip-rule="nonzero"
+ id="g430">
+ <g
+ clip-path="url(#clip47)"
+ clip-rule="nonzero"
+ id="g428">
+ <path
+ style="fill:#d7f3ee;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 445.97266,539.12109 v 22.89063 h 99.17187 v -22.89063 z m 0,0"
+ id="path426" />
+ </g>
+ </g>
+ </g>
+ <clipPath
+ id="clip48">
+ <path
+ d="M 438.39844,1266.2773 H 564 V 1392 H 438.39844 Z m 0,0"
+ id="path433" />
+ </clipPath>
+ <image
+ id="image908"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask21">
+ <use
+ xlink:href="#image908"
+ id="use437"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image907"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip49">
+ <path
+ d="M 0,0 H 1315.1992 V 1392.4063 H 0 Z m 0,0"
+ id="path441" />
+ </clipPath>
+ <image
+ id="image919"
+ width="1316"
+ height="1393"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABSQAAAVxCAYAAACeLII5AAAABmJLR0QA/wD/AP+gvaeTAAAcg0lEQVR4nOzBAQEAAACAkP6v7ggKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA27ljm4piKIiC15aBxn+vlEBAAAktcF7gGcn5jVe7BgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADorL8HAAAAAPDffs4IJAEAAACAhkASAAAAAMgsgSQAAAAAkNlPHwAAAAAA3ENDEgAAAADICCQBAAAAgIxAEgAAAADI+EMSAAAAAMhoSAIAAAAAlXVm5u3pKwAAAACAK3yfmfl4+goAAAAA4ApfZ2ben74CAAAAALjCEkgCAAAAABWBJAAAAACQ2f6QBAAAAAAqGpIAAAAAQEYgCQAAAABklsk2AAAAAFDZGpIAAAAAQEVDEgAAAADI+EMSAAAAAMiYbAMAAAAAGZNtAAAAACBjsg0AAAAAZASSAAAAAEBmm2wDAAAAABUNSQAAAAAgI5AEAAAAADICSQAAAAAg4w9JAAAAACCjIQkAAAAAZASSAAAAAEBmmWwDAAAAAJWtIQkAAAAAVDQkAQAAAICMPyQBAAAAgIzJNgAAAACQMdkGAAAAADIm2wAAAABARiAJAAAAAGS2yTYAAAAAUFlnZl5PXwEAAAAAXOHzF7NXBvvky+6DAAAAAElFTkSuQmCC" />
+ <mask
+ id="mask22">
+ <g
+ filter="url(#alpha)"
+ id="g447">
+ <use
+ xlink:href="#image919"
+ id="use445"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </g>
+ </mask>
+ <clipPath
+ id="clip51">
+ <path
+ d="m 441,1269 h 116 v 115 H 441 Z m 0,0"
+ id="path450" />
+ </clipPath>
+ <clipPath
+ id="clip52">
+ <path
+ d="m 455.52734,1269.4297 h 86.65625 c 7.69141,0 13.87891,6.1875 13.87891,13.8789 v 86.6562 c 0,7.6875 -6.1875,13.879 -13.87891,13.879 h -86.65625 c -7.6875,0 -13.8789,-6.1915 -13.8789,-13.879 v -86.6562 c 0,-7.6914 6.1914,-13.8789 13.8789,-13.8789 z m 0,0"
+ id="path453" />
+ </clipPath>
+ <clipPath
+ id="clip50">
+ <rect
+ x="0"
+ y="0"
+ width="1316"
+ height="1393"
+ id="rect456" />
+ </clipPath>
+ <g
+ id="surface918"
+ clip-path="url(#clip50)">
+ <g
+ clip-path="url(#clip51)"
+ clip-rule="nonzero"
+ id="g463">
+ <g
+ clip-path="url(#clip52)"
+ clip-rule="nonzero"
+ id="g461">
+ <path
+ style="fill:#303030;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 441.64844,1269.4297 v 114.4141 H 556.0625 v -114.4141 z m 0,0"
+ id="path459" />
+ </g>
+ </g>
+ </g>
+ <clipPath
+ id="clip53">
+ <path
+ d="m 566,1286.2773 h 584 v 87.3243 H 566 Z m 0,0"
+ id="path466" />
+ </clipPath>
+ <image
+ id="image924"
+ width="730"
+ height="109"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask23">
+ <use
+ xlink:href="#image924"
+ id="use470"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image923"
+ width="730"
+ height="109"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip54">
+ <path
+ d="m 563.19922,24 h 341.4414 v 83.8125 h -341.4414 z m 0,0"
+ id="path474" />
+ </clipPath>
+ <image
+ id="image930"
+ width="426"
+ height="104"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask24">
+ <use
+ xlink:href="#image930"
+ id="use478"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image929"
+ width="426"
+ height="104"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip55">
+ <path
+ d="M 433,0 H 558.73828 V 125.8125 H 433 Z m 0,0"
+ id="path482" />
+ </clipPath>
+ <image
+ id="image936"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask25">
+ <use
+ xlink:href="#image936"
+ id="use486"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image935"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip56">
+ <path
+ d="M 0,0 H 1315.1992 V 1392.4063 H 0 Z m 0,0"
+ id="path490" />
+ </clipPath>
+ <image
+ id="image947"
+ width="1316"
+ height="1393"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABSQAAAVxCAYAAACeLII5AAAABmJLR0QA/wD/AP+gvaeTAAAcf0lEQVR4nOzdsRHDMBADQchibe6/DXcgJ5KrMBj8bgWIf47DI8knAAAAAAD/915Jrt0rAAAAAIAR7pXku3sFAAAAADDC4yAJAAAAALQoJAEAAACAGoUkAAAAAFCjkAQAAAAAahSSAAAAAECNgyQAAAAAUOPJNgAAAABQo5AEAAAAAGoUkgAAAABAjUISAAAAAKh5VpJr9woAAAAAYARPtgEAAACAGk+2AQAAAIAahSQAAAAAUKOQBAAAAABqFJIAAAAAQI1CEgAAAACocZAEAAAAAGo82QYAAAAAahSSAAAAAECNQhIAAAAAqFFIAgAAAAA1DpIAAAAAQM29kly7VwAAAAAAIygkAQAAAIAan9oAAAAAADUKSQAAAACgRiEJAAAAANQoJAEAAACAGgdJAAAAAKDmXkme3SsAAAAAgBkcJAEAAACAmtfuAQAAAADAHApJAAAAAKBGIQkAAAAA1JxJjt0jAAAAAIARHoUkAAAAAFBz7h4AAAAAAMyhkAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+LUHhwQAAAAAgv6/doQVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAI4KtEtzvY2/0AAAAASUVORK5CYII=" />
+ <mask
+ id="mask26">
+ <g
+ filter="url(#alpha)"
+ id="g496">
+ <use
+ xlink:href="#image947"
+ id="use494"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </g>
+ </mask>
+ <clipPath
+ id="clip58">
+ <path
+ d="M 443,8 H 543 V 32 H 443 Z m 0,0"
+ id="path499" />
+ </clipPath>
+ <clipPath
+ id="clip59">
+ <path
+ d="m 455.21484,8.351562 h 76.10938 c 6.38672,0 11.53125,2.960938 11.53125,6.636719 v 9.613281 c 0,3.679688 -5.14453,6.640626 -11.53125,6.640626 h -76.10938 c -6.39062,0 -11.53515,-2.960938 -11.53515,-6.640626 v -9.613281 c 0,-3.675781 5.14453,-6.636719 11.53515,-6.636719 z m 0,0"
+ id="path502" />
+ </clipPath>
+ <clipPath
+ id="clip57">
+ <rect
+ x="0"
+ y="0"
+ width="1316"
+ height="1393"
+ id="rect505" />
+ </clipPath>
+ <g
+ id="surface946"
+ clip-path="url(#clip57)">
+ <g
+ clip-path="url(#clip58)"
+ clip-rule="nonzero"
+ id="g512">
+ <g
+ clip-path="url(#clip59)"
+ clip-rule="nonzero"
+ id="g510">
+ <path
+ style="fill:#d7f3ee;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 443.67969,8.351562 v 22.890626 h 99.17578 V 8.351562 Z m 0,0"
+ id="path508" />
+ </g>
+ </g>
+ </g>
+ <image
+ id="image952"
+ width="800"
+ height="109"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAABtCAAAAAB8NVqbAAAAAmJLR0QA/4ePzL8AACAASURBVHic7H3pYttGk21Vr1i5SXLm/Z/uThJbIrH2fn9UA6R2SpbzeSbTTpxYJtENoKtrO3UK4f/Gv37g498gnX9P/5kV/T4D/9ML+L/xnx0IAJh/nXdDAkiQ6D//biER6/89EZVf9lQezfOTs7wg3j9xxa+92sen/Oc3IgIgICIgjVWDJEgppZQgpf8JiuTyMX7xUsU6w5MD5OunejbPT85Cb/fRj37iik+ewPlCv25vfOXD+Nz8CIjIkNE/JCNA0hHpV0wpJkj/9NI+Mh7tgy9/ZSLPQefH+rISnRxf/FBwnQgAgM6nhJ+bBAHz4Xf+2efX/Xhl56tB+nUmxqOH/qse+ZvTA0PGGGecc844YwwZIGTxCDHEEGKIIab4zy7tI+PRPvgFr0zkOZAxhgwBaZYYU4zw8c2L59+fH4kIyJAxhnlPxBRj/OTt4HL0IZ71XqIzL374ioj5CTy+Wj5Cf8nOuHzoNFuMMcXPnheXF15/f0srIQJDzrgQQkghhBCcM8YQAFKKMYYQfB4hhBjTLzwqfmJc7AM474AvXKqgfcs454IzhoAJUowhhBBCih97XY9dvbzK9QqIyBgXnHOGCCklmiR+ZlMg4rJkts6XUl53hA89IAS6Gq1sXX2MMQQfwsfl7bo5WZ4TEYGW7gPGnxTHt1/B5ceQcS6EVDSklIJzjgwAUooheu+dc9ZaZ53znmTktxORi31ATzGG4EMMnzjbXxkCAJBxIaWSUtDLitF766zDAB+RkCeu3urn5YeKiEwIqZQUnDFIMQTnrPUe4mfkg3OZr7UopBRj8M5Z5/0HhQ4ZE4IeAF+vlvLVnI/xV0gI41wqJaVgDGky66z3PyWO77yCi88x5FwopXVRFkVRaKWkEJwzBKSDxnvnrDFmns1srHXOh/AZm+KXjsf7ANZXBvHje+qVIQCRcamKoiy1FBwxpeidMdM0mw89j2ylcGTZ04spxhiXvYqATChdlKXWgjNI0VszT5MBgI9uQETkQumyLAolOFu2dAjOzfM8G/yQ0CEyLpQuykIrwfkqbsFbM00zgv/6k5PmLMqy0EowhBi8MdPEDfifsLIQkNE4nxkxPrcSEZFxKXVRVlVdVVVZFFpJSRokC0hwzhkzz9M0TtM0zcY6F8IXmIBfOBBoHxSl1oIjpEB7iqH7slcmAJAJVTZN25SF5AxTCs5MQ98xgHT93sWss0W2oCDG6L0PHiOpIUQudd20TV0owTAGZ8a+FwyWUOJHBuOyyNdat3SMzppx6IeBmQ8IHR0QZd3UTVkowdgqbnYe+65HgPRl59F5VsZVVTdtU2nJWYrezkPfd/j5qAUAAmNMCMEFORNZEzyzEhEZE1IXVd20bdM0VVlqrYQQJFkJYooheOesmadpHIZhGIZxmo3FEH4vCVn2QVUogRi9m8ehE5jSl70ygYhcFs12v982JZ1m3k3D6UFgiiFevVREzqVSOms7iDF4a61hDshQQyZU1e73u7bWkkMMZuqPDxxiCBE/JO6IjAldbfb77abSNB0kEuz+dHzgmA2LKy/HhKrazW67aUotOfk0KQZvxv5BcYgxpo8t8Io7QCaLmh665ixFZ8bTg0LIYYtPXRQYW18BR7oHZ4217pFayhqzajab7XazaeuqLLSUgvOseShgELxzdjbTOPR93526vh9+zVnx6YGItA92u7bSkkH0ZuqOiqUvfGWCpLDZ393tt5UWHFP0duruFUbvfLj6eSDjqiirqtSKc8QUo7PzNE4TQMKEgMiFrrY3dze7ppQcYpjHUyVT8D58eE8gl0W9u7077OpCrRokODN0D6Vg6QPBJ6T73x1uD/u20pKzVR/N/akQ4J0P4WPLu2LW/NBvD9tKS4Tg5v6h4NE799Hj4uKiyJUuq6ospOSIKQVvzTSODMFDPH+KcaHLut3u9/v9tm3qUmspBM+WGZLnEmMIzlszT+PQd6fjw4PkCBB/QsN9/WBc6Hp7c3ezbQrJIPp5OJY8BedDvPpsf3sIQMZV2e5u/7jd1SQgwYwnjcFMs3Xsun2GZEC1m01TFpIjpujdPHanDikhC4BM6Hpz+Pbt0JaSY/TzUEtwZpot++DNIDKpm+3Nt7tDU8rVawjODKdaMQjBx5jCNW8SIZ9C377dHTaVlmcTy81dLZKdZ+PYl5+bTKiy2d38cberC8FScFNX8JAn+9QV6RVU7WbT1Fpxhoms2JNkkNJ6oNKxW7bb/eH2cNhvmrrUSuYA3pJNIBEJ0XtnzTSO3em+LgSmGMIn7OFfNrIevvn2bd8WkmHwc1/J5MxknP8iORZIx/H2cHu3rwvBMCVvh4L5qesGwa89O0nM9jf7bVOq7IOP3VHzFEKMgc5MXba7m9vbtlQcoje9BDt0neQIHzk0kU6Oqt3d3JGwLVva27EuOATnrA9XBuCQcVU025tv//Xt0FZKXIjbXMpohq4YGOKX2liYz5PN4fZu32jBUnRTyeM8dL3gn361jMui3R/2u7bUggFEb6f+WJAVm/c1+bVlu7u5vbu72W/bqtSStMcKV8zZ6BQTicg8tm1VCAjOOn/VsfOPDARALnW12d/cHdpCcohurkQyQ1eM2RH++SGANG6z2e53TY6oWMXc0NaF5AhXPg8y5A/f7vabSgnEFJ0ZjyVPzjofYjZlyrrd7HabUjKM3kiwfXO2aq4fdAg2m+1u3xaSMQAASCl5V2oBwc7TbH24RoVQbC3vl31bSLEkQmJ0s0hz35Tq4wt8/w7Ycge7LCCahelUFVIwvPqhP70ml2Wzv7u72dRaMEzR2/FUyeSt9as3iYyrstnd3v3x7e6wa6tCSQpewerIL3mUBCnG6H1ZVaUS4OZxnDiLn1vdrxjImFBl3W53u7YUDJIzMpmuLpVg+EWvTAAiE1KXVV3XleYMIQaJfqxLvYZ03l8pIhOq3Oxv/7jZVkowSNGbvhTRjtNsGCaKYqmiquu6LiSD5AXYuir1x28GEZmQqijruq61XNaYUvBSYDDTMIyzdXiVicW41FW7O9zc7Le1lutaUvQ82bosPiHA19wCpzhSXddKIETH4lyXWRifL/wShv7aFbOo3/5xt621JA0y1BLsNE2GkZJGZEzqarO/+68/vt3u2rqgTBKlTOCsZjIOhqUkolJKsmj6EwVxfh/5yBkK2lOCQfI8mboqtRRf9soEUp5Qaa21VhwRUsCgixyOuvZ5IOOqrLf7m9ttnU0sU4o4D8eT5IiYABmj1JRWSjJIHLzWWskPTHIxG+O0ZKXkktqDFAXHaOeh67pxdlcFGJAJWdTtdrfbtnVF2xMAIKXEktNaK3n1MfGB9ZOEKK0LrZRAiBh1obWU/NlpgbAmxvFNRBQil0W92R9u9vViYpUy2f50UuuGoZj+7vbu2x93+02tFecIQDmrFCmPT+iNBcfDI+ccwlxXhRL89yqPwBy301orwSCx5M+v7GsEWZAciozG4RQHz3/8gHwg40KVdbvZbisSkDDzOHdVSXFYRCBQgKCMLSQQKwLo4+pwuRYXi9MAAIkxTMFM29ND04/Gh3etUFyNte2mqYpLZZZS4lxQdAfhKm30kfUDYn7oQnAETIKLnIl4BClGAMzwkaVC47WbouO0bjbbTa05OZMCTF+XWjBEzFtf6mqzv737dnvYkjO/QIsWOAlBghYAIyJHTEprLQVngF9lunzNyPtACM45g5TyH54+xZ8YAhaQFF/BeowxxjIu6eppkAmpdFGWZSlJQDCZstA5LQIAGfG0JHqRJvmMsYh4vtYFvhATQtRVvdlsmqqfHXs/4IKMC1017aatc3z68u8Yoy3yC8rKENnFHQCBENgL8oGXMPT4MnJk/SjhC8qyVGQr82SrolBiFXxkQhbN9nBze9hv6oKCXTE455x1LvgYEwBjTHAppBSS0r5ICwX4VFL31w18uqfyxv1C+QCxbDdcBiwG6AX+/Zq1IuNCSCGlEBwxRYxCSnHxblag0OdnuZxu/fLl1xmXuqrbtqnKybD3MgrkohdVs2mbinLylwKyrvVXHJqP7gAun8vlh7Lg0MJiijGEV1HWiAw5F0JKKQRnmBimvMnzfSElo9rd/rDftXVB0ZTgrJnn2RhD6QNkjEspldZaaSkFx5Sxmxnh9PuIyPM99dJT/KkhaJ4nG+080fVrXS4Ej/f/xfZ/etVPzPJshfjkp1yoom7ati717N5NFiFSzqxt61IJ/nQpP7PAd8fTO3hhMkRcjFJEgBiCJ2Dta8m6y/1yxr0v7wABGJO6arf73battKRgipunsR/GcZqtCyGReyR1UZRlVZRaSw4hZtBK/Fm08VePr9xTL46l5Panr5gy2DiEwIDAuoEqCf4ZcMKSBuNSl3VLGKd3MwoUwyJ5onwK4Sp/j4GIXCilC6UER0jRW2uMsfAKFDOdAf8BEi5/iGGFryAKWVTtdrttq0IJBpCCnYfudDx1/Tgb52NcgjZFVTV1U9dlITmB373/vfKE/8gQ73/kqpEB28YYwxNHTDFYY2br3MeBJJ+ZHgASIEVpirpp26ochH/7vMOsQJqmqclqB0gAmH4XEUEuVFlVdVUoziB5Z8Zx4Pgyk0JKkOgVzIYDOenOGGOsWyQEkRO4jyxKAqbO/fH+/sf9qRsn63xMAIxzqXRR1c1ms2nbupBgrbXWkgb5d40vFBBv53HoSx7JtPWm74dhNNb/eglZGDiQQE5l3TRNqeV7eAPK7tdZgTBc8wDpd5AQJCTFZrtpay0ZRmfH/qQYFXy+8IUYCIddiegEwxSDHfp+GGfr8+cZuVxN9riA4H33f/311/1DP87OhRQpIim1Luu23e7206YumJvnDHr8vSysXz++SEBSxl4dSx5mnfMg/cP9sR/NBxCPn52dCoNyMFmoMiOgrXvLTUdExlVRNW1DHgiVhSPi7yIhTKiy3d8edk2pGAZnFuBIeAmrmlJwZuyOlYhGC06JwuP9MeeEKE0oVFFVdVmQxxWDM8Px+5//76/vp36yy8cY40KqomzaUzeM06YWYRin2Tgf/m0W1tdpkBjs1D0ULEyVEgxT8GY4/v39YZit/xUVeZdzU2UQADJECmQ1TVOXeuZvu+mMS102JEwcIaWYIhDBx2+wDxAZV1V7uPt22FSKQ3RTV0vw1lr//LYSvYL+WIpoVlj2cPr+/aGfSIVgjgNX5aowg5v74/e//vzzRzcaRy4GlV0JqXR36vpxHKdGw9SRMfCvUyFfIyAJUvRm6hQL86lcoCbj6f77Qze5X3vspFzRHRMgZxwhB6aaSkvxpo1F8NeaFAjVvcYIyPkXhtE/P1aY2O23222lOAY31Sq5aRxn+1Liks4oxaPpqqxBzNQ9/H1/mhYzlzEutS4KJQVHhBSDm4bj/ffvP+6HyfmYXXBECvVO4zhO4zRuKjQPp2Gy/v+c9M+OFL0dOUbT1YXiDCAEM/en40M/2V9uYkGKwfsITAjgiNnOrsvR8NdPvJz8r1qqS2SYoqeLpK9D8vzUoHq5ze5ws60lx2gnCWY4lhke8nSk6N3EWbJDXSrOAaJ383A6PnRjfgWEYSMsBiK9NDN0p+Px1I8Xu58YV5ywxhgzz2NXCdc9dJPx/zof/csEBGJwMyY3nUolOAOIwZlpGPrR+JB+aVwoIUXQAvCYlnLD7Hm/JZyIXBZl0zRUwpJScM5G4BFYxN8gHYaQa4HbzaaSHKKTYPu61FIgPgdzJ0jRMQQ/dVQYCTF4Mw9DP8xZiZP7rWSGfJHen0dy5O2F+YQAiMTbYM08dpWI48Ppn3Anf7vxVU46QPSQop0ysCGl4J2dZ2PsLz92UkrRW+MTDwkZB0oWNk3dKeNfKwvAc1KxIpRqcNaYgCIxzn8PJ53QVWVZloXkEAW4sij0q+jJFABTdHOvJc/sG96YeaZXkJCohoSQBFYi/LMz8zwb63wMZ2WbEBISh453dupLmUx3Goz718nH12mQFAFSdGYQhIxLKXjvnKPk0i/ebyl4O7soInCODLksKPunBH+9rJ4xocu6aetKS85I5c2eKRQi8l+73qsGwTuJkEkKDgmkVEqJ1/BrCSC6FJ0RkjNGryB454gGiTQIEq/GmWgtEj1ceFILnwAo1RhjcGbUMvlpGG2I1/kgT5f3IbF6dm+/QigfT/LGDF/mg2CKkKK3Z3o+SuTGK58pfGDRzyaP0VszRx5QcMaQC1XWTdNUxWQZe7FuCnNOoG4bQrtSHmcKLHIVf71IXzMQEDknqj3OMGaiOc5ewVIkiClF71b+uxRjiCFcbGtEzjhbmZIgkxG+xHGxFt56OykOwV5nDKzo/KWu5vKCV3x3hfZ/+NvXjnP9wEWR2KszfJ0GwRRTjJ74XXElQL6SXySvegWbP/rPe1OnFIObJ88jE1IkRKE0pTcG8VrdFOUE6jbTEAGRHU1egroqs3neoe/gIa/62CtfJqpWlpGkDJGxS5bpp1dNADFhyNXlaWFPPb8BxIVhdX3gGUWML5Q9J4CYUgreCobRe+ffoVzBBfoF+OjGF5z+m7AGgIUU8hJrmCiK//xePzeWWS5X+Pb6vkxAIGFKmDAu7+7MoP/uV8/oRjh/Fz5AvJ9ScHZyLHGlBOeU32ibt9x0gmHVTVOXOWng5nEYo2TvV1pd1DAtC/iJj70yB2JmXT+LA+YNzhhipnbA9PgQTIgpnsXn8Ss4412XSajgSEkhXmSjpGMuEBNmfBtogo+wknCW4JQul/Hak1qrGPP/PPv2G5Uw1w5cZ2HrZnsyw7Mpvk5AYLVK8v9cy02FCMgSMMLcQy7/PB9911wkRm9mg4nrIkSGTKiyogTgy2465qRy9lQYpujNPPRTLJR7R4Esj/lcxJReSmxf+bFX50DG1yqd87IZ0SiLGFg++V58RGk1oR79NK+E/gKzFi2qqixs5jx9qkTILqCYcHojS5hlj2Udt1qBCTKhOFFzv7TYRbLY+msFIyciIFq+/BIE7fqBywGz6mEgJuqUOUBTfOEVfaGA0F2uHPEpQ4be4VFCRGQcAVkuYAPI9OIxRKJqf/eppJRCcPMMSRQEy+Uql4WsIc6n06443korTqCLcegngOIdyCoCvUS2vsKYXmCLQsAs8m9+7JUpgIiMc2XH2S6nohulPAaqIY/pks1+3WiIuE766BU8YqwnNoKmbfvZRVhhpZdLzC8QF63+6jPJ9WVrJeI5DBAXLvQXSeIXyaJSJ6p9WuQr5RKYQIzUP0WfjUtxzbpE4tVPcUFAe9pwT17R1wkI5hrBvMuJQj+Gd2Am9M5TYnzhzASAGJfgig94FWNySt7ZKUZRziWpEF3WTUMBquc8HBnUmBEpGZYxj8MwI38n8Y+5IHVh90xxwfSnT3zstWeSyUHLolCSLz51pk4sysqjC/lwDSHghYQgtfugV5CoxcfZeMoVCVk1IxEJbHb9ZAMg8z6etf5ZpACygLy6OWnBnAshpKASLc5p/9H28847518iiadiPU5fXmqwOcn40mfAO+ec8+6zrQBgEQ++1JPJXOoNuSbA5ylofY/u88sEZDndpOScMUgpeu+8c+EtbnSkdHYQSKYwhfhTjN47ay2Ril+xq1KK0ZspRFHVRsvEWI70VuU4v+SmI3KpM7uv5IuL3vdGqLeTYRQplZkIPi/VOQ9P6W9f+pi/irwdcwJEF03bEtR98V6RnCvLCxdjysWyfn3GSDT1kug2Mj+9dwHytsrtAYjLcoE+N9tpthE4l8aR7KQnTvE7EZMsHkJKqZVWOrdSoGAmnXXOWmONNfl9nnc5IiJnQggppVJKKikkz4cswsqAag3h9l0InwKDUQaICyWVVlorlVcIlOxxztIU1tJ+u9gwX6hB6KUWWhF5O1VyMvsGNzpmogGWhNJFUSglBLHYE3HyPM3GEPPnu1ZW9M7YIKrGuMAzyqppmqqXLznduNTmUqUUxODmcegHq4o3Id25hqkoCiUJvuWtnWdjntLf5o9pLTlfCPPn2V5F3o6MCarH2B52bUWlXAgr955jlXExphj9o2dMp40qCq2FIDCDNbOZlwqrlNl6nV8OAeSyaKxzAblUw0QVHzGHHtNFAOAdk5Pw8UVRlGVRaC2VWDp2RDoajJmneZrm2Rh3FtglMaOUKrQuCq21klJwwThpEPq2NfM0TdM4zcZ6Hz/VH4lqXHRRliV1exBiFRAqopmmcZrm2RLf7jrDVwkIEj9IXdeZvD06Mw/DwBAgvJJUWFiUU2CqLKuKdh2DSG92HIdhGMbZOHi3Q0dKMTgze16Ns/OCrzHcqlDmGcledtHrpllivJEUyODTOwVeyLgq6rqpzoI1DcPAAPzl1/LH6gv5m8ZejE8+9sqTRELst5vt4WbfLrVchI1pdp6Vk/GU8rDTMPQMIMVEEqTKqqnrQguOKXg7j/2Q61wSUFGCtcY6H1JKSPyOPkTkUlfdMM/OLVWg5wD92+tFYMTxRcxqVVUWKm+/rEG8d9bO0ziOwzCM43wuiUSiB9NFWZVVWZZloYlFe3VGYyBAxjgMfd8P4/TB1ha0wswSVVZVXdd1VdJRnPs4hRBcpiDu+2EcZ+suT+Sv0yBMqKLZ7rZtVUgOKdi5744CU4yvkIBmyoTGaxAlLVzJXFvq7TwPQ9edTl3PZ/TvdzNI0dvZ8n6YTKEiZ1wuWcAX3HQiM6nbtq6KvIHN1Pf9kNibubC8pba77aYuFWcpeTP1pwcOMcVLT4A+tt1umlKK5WOaA8Hy33uSKItmu9tvt9vd7rCpNEELMzNcwGJjXIgpRm/n/nTM/POJBKje7rZtU0iOKdp57I4SU4wp0DPK0BKbVQgyrmJMwERRNV03zsY4l5uuhUVK3gy9LX5RVTftpm2auiqL3CMJcen55Jwx0zT0fXfquo6IuCABAnKhiqpumqap66oqCq1WGm0ioEq0fcehP52Op5McED/YT4Zsf1mUVdO2m7ap67LIBPjLAp2z8zQM3el07FQ/oYOzhHydD8K4qjaHWyJvx+TtSBTx4ZUjGTMHjWGe6bpuiGVRMMT8Hse+Oz4QGSe8+1CoohFZOYxz5UVaSm+bqhiFf+ymr2QmFzFeOw19P4wo31Ugsmx3t7f7ttKCpejn4VQJus0LHmPGZdnsbm/2m+Vj4+mHhEj9wd5/kmW7v7097Dabzaat1EJHyrgqI8pmopRd8GY8VRIyQT4uX7zZt6XmmIKd+gfNEjm3kOgUMdM0zdYH8mw4JGLSrjddN0yTMYZ6rnlPkaMU3xIRBMaELMp6s9nutptNU1XFoj/OUSzvrZ2nse9Ox3stGJ2Z2WYs6812t920TbN0YaBeokuYmE7LaexOxzNN7cckhHGpyqrdbLe7bdvWVUnUchTIgEVCxr47PdQPWgo2wVlCvkhAlpYdh2/fDptScYzBDEfNgjXGspdVSEZzpzLKoq7zujPKLnhrpqHbNGUWkHcDfCkGZyKU/TiZIrALEdCGP0GbrDjF+jKL3nfDxIvwdmoTuSqb/d0fN9u6EAjRzf2DgmBme1nfi8hV0e7vvt1uay0YRDcPDwq9NebdNCRmS+r22+2+beq6vDSxRAFc1dbHmIjNvpLEP+/ppFRlu7/747CtlcAUzHQqeXTGWEoGJbK6xnGarRYREZCBJM3UbPt+GKd5JmeVWhO6pYHnqywqjA6i3W5/2O82bV1pLcVK9guwwFWcNdPQH9tScSreoZYxXJebw83NYbdpsgFxyRK1eOrOmmk4bRpS9/ChhAjmgP5mtz/sd7u2qYqCmgAuiO0EMXpv5rE/ES4PEQCWTkFfp0E41Yd+u9kuAlLwMA/DaNxL9UdLPVCQFmRZVkWhZVavQG/SjJschAW4WPGLIxGi16ei6yfjlKCuBrk23T5pX4CLAdaUOtN1z2Pf9+Ms30RTZN+l2d7c3e1WAdHopmGY+ErDhUTU3WwPd3f7LCAmf2x8zL31wmMBZEzqstnuD/u2LrWm6lgEQJYEAJOOOqrE6OaCh7nvlGBE7ko83He3OxIQO5Y8mLEfxNr0xNtp7Pt+rArCBCMTSPSrm2Ecp3mazWxmY2azCEom+3lpRy5G8mZ/uL252W/bptRKkvO7ZB1xDQ6YaWgqyaKzxnoEAEQmVbU5fPt2u9vUpSLivowNA0SWGOeMAYRgzdRk6ktI8QP9o6j9S9VuDze3JIdFbrmUst8GiAgpBDu3TVMR+9PalPsrnXRyi7f7wyIgBQvTqS7kM7qpi29UDgsPgiJvnDNkmcMvhWCrOvdNBOoX/Y6fHoO1Qff9MFst2Wpj1YUy7tJNX1z0tm2qInND2Wnou2F08M75jgux1GFXF5xBdEahG04P+hFtbabE3u4Pq4AosMPpOir7NYq1umUkHwmQcQmMUyIjRseTrQqdGeFXIu79YRGQgse5o0AxJkjktw/dqRvqIh/WyBZ2sM08k3TMZp7neaKw02yN9/6VfkQ5Tny4vbu7vdm1Taklz2128guj5DWIlEJZloWAMA/9QAINyLiu2v3tt7tdWyrBAVJO2cWUcmtZxgRCLFxZloWSHFKkbMh1KoRiR1W7v7m7u7vZb5tKk01NQFqK/HHOUaWirEp6mNSRO5Ed93UaBLnQRd00bVtKDiko5qe6KpSgA+XlL/jIdUiMcw4hMU9suIiMAUQKWFNLn3Be8SsjpRi88arvh7EpA8dccN7U1TDx8CTEtASBteQIKSuQYQo8vGf/LG5lW2uOEL0EN9Rl8bhIgz5W1k3bNErkj4319bTjmW/xAnWRf7z8FeS99+hqZDnWbdtWiiMEy+LcLOzI+RnZaehOpxOdxqRDyAopnHXWWmusMbOZcthpHMfJGOZfaq2drZfN4e7bH99u9pumoPas4Uwyl9NznHHGhZA8uXE5JQjpQiWT+yzRwTmy7UJKyIUsirIEzjkkpZSSgkMK3pEGveY5IhD38v722x/fbg87euEpBp9TbIBMCEqLSKWUlnRgXoDQv0ZAEKnemXrFaskwBQy5b+yL1QtkIil5agAAIABJREFUEcgiIvc+Ju8ShTGlUlonyRhmSkGWn8m7jdpSCt6i7Pt+nK0SOexMkV574aYjvZeFLo4hUDi068cZ3sGr4prtKQutOUIU6Iui0JJyFeniY0JqarAszh9TgmUa6bfvJHpvzTxPgoBDCYGOzBSDp8bU2cSaptnY5dHgeXEUxcJol4wNLS4l78x4emjJnEdgC9KDSxk8Zbxz++dpHIe+77q+H6bZupfAjGvR/H/9191+uwQ8grPGWmJpBJazgFJyJiFUFRlhi59BHn7dVIWASBSoM2U7Egqpq7p1DTLOgcpYIAVrDbGrXKdBMhfxt//649vNri214BCDM/nBxcS41EVZFoViMhOJp+Cd9ws/xVfmQTiXUkophWCQMNJjeVk+8jeEUMGHaK3zPqa82qqsCpAMgbBZFLu37jU1v44UvYu87/pxsj6yRQyaulDikZvOLmgdOIMYnRn7rh8mw95RIDl3QyApwRAYhgUw9cjRouJWehj84mOvPo3L+0jB26k/lQKC9yFdRHRCcPNMBIgJYvDz8HDsxtmGmCDheXFSCA4Qk5dyJTtP5I46M3bHpi7JngeGAIAMWeKEf/PBU+Z7nseh706n4+nUDdOMHp5ISI4mbA93f/zx7YbkA2J0ZprGaTLWhZWlUReF1oJRG4ELk5u2gFJSYAx2HodhHKZpNs7HxLgqm83kEjLGGSbGSNdP4zQbd11/vcXAuvv2xx+3+7ZSEiEEMw1934+TcTHlWE4Tks6FMik4a6xxgTowfSVYkTjiOWOMQQLGeMYfvuCikwpBpMaL47iIs9JV07bOJwCRMXck8LO1b6OtE6QUXWBD3/fjXAYOyGSuGCymSzedoIwEw5IMITPe9eNkxXs9ZhfUUcbjQUrn28RHH2MLMO3px158Ghe3ASkGN/eFwmDn2fqES8gUYnTz2PfjTAQ9wZvx9OPHcTCU6blAwxGmjaB5l/z0mRqoojwnrhKCCRhBG2MM0ece0OPQn44P91WhO86sC48yWrg0c7u9vbs77BqSj2Cnsev6bhyN89mC0WVZ1VWlJYY1S79eBBAZQnDBjn3XdV0/LgIiVNnu5wCcU5Eq5nxu1w+Tva7DHsWRtze3d3e3+w21S/XzcDo9HE/daGxMTOiy2W73gTYsQgrezNM0WecjfqEGIZz9Wr8A59oceOPMTMGZ4Xg8dSTOKGRZt9u98QkQBWTYiZnGYZxm92p9OV0LUgwWyFayXrCFFq595KYvnUyahX4zRm+noev60Th8NzpCEsJeus3HHyMRwWcfe1s+ACClCHYSHLwZh9EE5DzXyKfo3dQ9PHSDcSGmRMQxDw/90ixomZU9mvRSPiAGOw9HrRVFvnRadR8mSCyjfZcm0NOw2VBZDafuB48khML0+5vb28OuqUg+3NSd7h8eTt1orI8ZSlTWTbNp6kKEBY2VEiRAqvqJwVuGduyOD8dj14+Tsd6nxLiq2skBV1IJDoggY2jm4bQ5lnp+MTL6/F1xqat2f7i9vdltai0ZBG+G44/vP34cT+PsYmJCV+1psCEhY4xJSBkbMeWA/JdqEMyY++WP9JJe/UKKwc7d/fcf96eeBISrot52k43AGDKOKCAFZ8ae9u/bONuUYnBpGvp+mIyTHBkXBNit+tmu38381k22sBLp9r4fJhv4+0i4892td31x048eBlz8fP3YFb5lCm5GCHYexjmgUEpFlgjUb6fux0p0RRD9oRtml53Ws/t+MStcHlEpoBGdUlIyBiklnQSyhY0YIUHiK0zYWdM0TVNXiytp4fwCctql2R4ON7tNXSrBIQY79Q8//v77x0LnmMFEVbvZbttap3kkf2Z5zjknPLgwdQ/39w+nbphmRxzaXJWjA6HLslSSMWAiEhN/UyppWXzfTScR3u5ubg7bttKSQ/RmPH3/66+/vj900+xjYlyXbT/7hFwILjgU3k5Dd+oGIjlKXygg8Fwc3twOKQVn+uP3P//6cexn42NCLouqG20AJgizzbgsXDv2p2M3TNa/3fAjpeDjNPRdP7RWR0TKoC7Z9Nykj2BSTdtUxdKobB66rh9nm+Q1rt/zbf6qkwVXfOz5XURyp808Gs9UWVdh2U3eTv3D33/fD8bFFFMk4Mjqgyyre30ZCVJwMxdrQjaqKFLOQ+ZF5uKuGKkfUlWubZASXEQSl1D2br/bNKXKNQPj6cef//3X94d+tC6khIxxqYuq2Wx3u7Zk9tSNxD+Xb9V7M49d4nZ4+HF//9ANk7EuswDK2YEomk1bF4GnpZdm01TUMuv9V0UZ0M1+vyf5wBjc3D98/+///vPHkQjbGJfFMLvEpNJKcMZVUbfb0/ZY9bPzX+uDfGwkijn2D9///PPHcZydTwmZ0OVkPHCpyAbInvZmkylK3rlmDMFQxs86yeHMnHjBQroQVjdVsZCZjFnrvG9h/SMjRUofOxt50WwX4BSkGL0Zu4e/v/ezC1Rj4q3LIPCrLx4sdfaCGLz3FW1+zC3aACALCUtcSEVNJaXkDKhgapWQ3JZ9u9ttGupeGIOb+ofvf/6/P38ch8yYzZALqcrq1HXdthKhf+jGBU2QiLmrP/IZ5u7+/v7hNFBQLqYEyIRPomjpZEwAxCpblGX5amz00aAYb9Vsd7tNthaSN+Pp/u8///uvH91oXIyATE7WRyaLsiqUZHR6btqmKkbO4peaWB8aBAxy83B6+PH9Rzc6FyMA43K2HrgsqlJLkQBYOnfEkYa956aHyOah7/pxLj3nyKSuMrGP9QvCWlzWoucsetcNk/X8agKWXzkSpBBjCiFGUW4m43xKFOZNMbh57E7H07QUTIW1DutK/QQQ0DAESNF756yrtBLiopXd8p8ELHG+xOIYA8hIeKSQMlVbtZvt0lwl5gPvr7/+uu/GrCao9Gsoh6HvT7VM08NxWLi0IaXozXBSQcWpe7g/dsNknQ9U2ossAtMDNQgIKSFAhg0XV6WTMtavarfbTUvxyhScGU4/vv/99/djdtwQrQsRZdlkgk2W81xVQex6/zENAkAhwf50OnXd7D3xijsfgKt1uQiME5NovcRd3sKbxBgc5fxyNp0TqL0qR5PJ03Kz0bapCiUoMDL2XTeMV4YO/4FBLZljQj5Nsz2XnCWIIXgzT8MwOiq5jUvp/vUaBGKgYuFANSptVRDZ1kVPyuzKJIrYCc4ZQszpM8iamLKS1N2RqLCJC/jH9x/3x97QAimyJoSZ53HoKgWmP/azCwkgUUuguVcwiTB1x1M/0N0uFCMJRWa1CzHCAuFRaqVOfWcsxnTbUoPelAKdyPcPp37MHEYYYkJRNJvtdnZekoFRLf3J/3MahHxO78w8jeNkrA/kIcQITBTNthvaKqSEuKz4ujR0DDBPfdddZtPrtl2aheDSEqRpm6oQnAGRmfT9MBsf+O8hILn8G91C+nYuW0oxBGetWfYfFaZ/qIAoQQSbc45mGoexqYsiV9ixi8Q9RbYw14gT/fEFyiOzXtS5rBkh0VlzOh6P3XBuSQKI6Lmzdp6HQqGb+yXolvnoBbqOhWnsSVWs2S7MJqS1zmfSx5U6lRJK79z1ystxBnF5Ow3d8UiqKhLkPgHyaei6gfJJjPZbRVKPX+ykf2zk+h16BDlOmRLjI/nMLot4bt1avI9jSimFlNF4lE3PFHK5Nh3J0yvXh5bJabuuH2bn38ay/JMjJVzryS9BaClXoYZABsz6z4dGBLApxeDsPA59t2mrqlBLIcYSwMYEgAlh4U+I3hEI5Jy1F6qs6mpJOUIM3kw9BYDsRScRxEg7fdQCo50n4xa+mhTchNFIDGZa0vW5BJ6iDRc2JORZuZC5M+w7pyWS8ZGr1hgsFmrXDVPOtdJxwZyZpnGcjQsRgHEqb80g0f+ggCxv25+RLwkjemvGcRiXODS9iWIRkHeOjRQXk4lA73juQWhcTICMCV1Q9lAKXIPIw2Tce3Ua/+gg7XBRIJ5/nFYmnE+zqSWMAAAxeDuPQ7fdtk1dFYVSOdfPiXsHsttOE8TgnJkzygMSUrCjqKqqJPxMIm6ycRjG6QkWJOXC1llyTN7ZFfGfYnCY/MwxOOrwRm5LpkyivGoiF2tJYpHN9373cESqCSrrVRkQRHykosGUtQc9CSqI9hQM4EJpimwzwP+sBoFMSHTBExBDcHbOUAXqGkUd2Jd2329HjlP0GUlAoHcyqJrc05OIbpaeUksWnXwWH/F3cNEvxlPpuPjpy3917XUxkoPv7DR0p4e2reuqLDKZgRD8TCwCAMggV9tM0zRREx1c8JjFUgW62mzTRBvtUu3ljqKOM0wx5G7sJDgeojcMIhV9Ubp5IbBCoZfKWBoIa1v0K0ISlIMpy0UXUOLIzLN1PgGyZYFkfDnrCM1EZpxS2dH5TwrIUvry6F1T6ig37aZjg7G8Ys7xRWDwxYgxIw+HqdaSIeYma1VJhYVspe3NFpYlF322IbJfeq+/0SAi5RS8m6fh1DR1U9VUEa6VUgQ0W5gTSEJyt+hhmHIIagVGagrHZ4PZGGKKf6L3clSfgmcXBHQpgo/BLWoiy0X+xZhQNQHQz28GVyDDezuBTlZdFIXKaObMp+6pBwCDC7MNyJjLi+BCqqV46z8rIADw5CDMzT5cRiemRFhTQrS9U2xEFwguh23nQjG+gN6bUksX0tIluq4oxrtKk/XXoXv+d4yUSza8NVPflVVVlVVZFmWhC01SooiAJxdVgYzB5eiHcSEmzMeWVktKgoLQiz/59EkmTBFi/twaVbjgbYRMrclX0BrjTKhqs6mzDQeQ8UxIgLb30yCMC8riLDEvMk2BCRUv6hqQCUVOfFoOZC7EAkD9jwvI05EItEoFBfSjFUDL2XuHfEoxLVZTa7VI1Em5aeuqUDZEyjteKpBpIAvrX9YaJrPuememURNCviyKYvm9yBw8ApAtOcFmszsdT/1kPe1TskTEWmwSY1iLNV6YD1ZKWrj0ToDOwAzuFFwILgTngnFOcMVtLvukgUsZzBVRXuRnEQZcyhSFLqskL7g5kHFdXVb0YEYds99CgzwbCYjd7Fyzkhe8FBK/9/VVhVgf2ALdbetCCb+4JPVSKeXmcei6nhTIr7+332jQ6R2Cs7OUSmmltS50URRFWZZlWVfE4CM5sAVW0mw2m+akZ8bIVOcrRx2pgBxxCS/XJaRn/0OryAhLvtSNKLE6QkIW1fawqS/jl8/QZa8MRMhsdiIrICTihLKZobAXsD5ELqtNW2u1APFXUn34T+ZBXh3kQC5UrLAwk+ViivfCWBRMoUBxqQRQOWnT1FUxh3AOai0W1pDRv+Fj2YT/+SMt9iy3XAgpJNEOFrosyqqum7bdNL5KUgADBJ5TCvUS7ci1J0Ks8pGZTl+Rjzzlk7GSlhIto9ZKZyeIihBV2Wwz79E5R3wV4nM5V1f5AADCLjosjbtgX0JkUjf7bXNRHY64BJJ/QwGBzHi8ampceW6veTQprhW0tRfs3NOzUB5XYVlivNPQ9Rko/O/SIHAWkUzpnAlAtS6Ksm7a7Y7wrIg5lKjKKgN0WMwFcoKfA675XIsfia8tZ59SCy1jobVS2b5hyIWu6rq4tlD5ycVphRfywYSuNo4V0+NOQMiEKje7DbVnhscxwt9QQHKo/wJAcQ5dXAMWj9lwItA7Z2zxy0fPRFFmphPCKc5D33U55/LvUiAAkEUEIhXyZAZpqZQuqrrd9ZOlckZgC0dstSQVEiDL1ebZ/4W1V8H1mX0yeqTSRVnVVVVXZVlotZahJkCudHEVMvHZpc8xYZ6bgSByoWsPqnlSOJFhl5tKMUgRFi76ry25/dqRlrqzVUJWTv/3vwohW07DVOl40ZCtdFxmaNaSRZ+GvhuG98mq/teO7DzHHGDlZO7oomr6YXYRloZUQEwrZZVL3JeayctTK1OZXK8/qOK2KKu6aZtm4TzkORocQ0qZgOdTLyc7/kv1WEJkUvvI9HxRkAKw0MGUlWIQECFYY4xd6rp+SwFZm1PRWIoVrzxGUiIi0W6YTCHTSmRd987J6sJF99aMQ9f3U45c/jtHztnl+BBDzrgQehim2UVgXAjOWEJMGYJBOzgf0MvmWy71AcIqyIEwXdbNZrPZbtq6rgqliPjc++idDwlFAKH0586vJaty4VgIHRPX7knxNiV1BEYXHaZghwwH+PqCqV81EBaleZ2N5U120yvPGTKpi7ppmt5btRJWZ75RqpR6k9H9f/8gZyQ/Yc+QczPP1kXgUq/EdYiEpFWZdSE361lfSeY7vPrAJ5OnqNrNbrfbbTdtVWklGETvKFNsrY9MVp5JrWNk6cqCsydzXAaEMXtO8LQ0FZFBcMlzhhCDGR+Op340PvyuGgSebVe8+P3970a/gN6NlmntldN4qS6y6OEMSvk3WVhrefrjkQH1CBgwIPc+ROCqqKpSS8HS0uRAqpV393k64iPASaT4YrPZHQ6H/W6bOZiiN34exzGjWphuHFNFsUAzP3W/i5O00PAa/5zNHJEx6vkTvR1Of/84DpMN8XcVkKd5oBWpddUzIuDQ0HXD2DiVe3rWbdt7q9ql7TMk8lQ6UiCfOZ/+J45Lg+jp3y0PGSNSxlkWdbtpah/4UtixUhzBaxe5fiWMq7LZHW5ub2/2W6IRT966qeu6rh/GebYeRLFNqm78693u3xwXDWwhE68OZ5z4o9UgcUymRLTra+H/7ykgT8YSIrlOQtLKJdoNs9WCQa4LGIJT7VIdkBVIN4yz9Vf0JPjfMRZ+ifQaG3gWkpQSoChOp66n8yNjPHLgav3Q4xdC0neVq4iZUOHw7du328OurUrFMbowd8eH++OxG8bZ2giqDrKhnfpxJ3GJhi71Vyn4eTgeu+FRGuTi0eQiFDsPx4f77jfWICuegMYZ+n3l99Pa72Osy7jYWJsxOrVd6eKCM+PQ9cNk/kXyQUgmzAGnN/J5EajsgNCJkROA9yIq9Cgan+s3Fj6LqySECV22+7s//uvbzb6tCsUhRj+dfnz//v3h2I2TtSGywolm+rQJnNLaWhcAIAY79w/f70+Tfd46c91sMZ+c/eR+WwFZ8TbnHFSKV/BXLyOls5tuFefIpSrrdgInty1ZWCl6ojgbZkvtln7NLfxWAyFTNQP1DHm9mVmCFIKzS8FrhlFdEhflvGB6nFC49qaRWgxtDnd/fPu239QFUY5M3Y+//vzr+33Xz8aGgNyzyTzpiXb1SJTaz9UUCCkFP4+n73/fD8a/mvZKKQRnDPVQ+T2jWJhRz5eWbozUy+WqCyTMbjpt/6U2fWOZk5vN2rVznvq+76dX+kT/7E3A9ebGPzeQcSmV4BC9c97j6xsvQxndCj588rkVWXIWkYuo6lX14rJodofb29sDgTxS8BeUPNT0UaK+gnP2xfUvZVrh3MApRe+m/vTjeze9ZGMt3yNPfrnx31BA1sz5OYAYiRb/aisru+l9N4xzqTj1Jps996IlUBoxDPYdsTR9eRY9Z26uNTf+mYHUvKEotUBKhvkQ4HUlQqfSYzLWbOgm+uuwtE1eL/8OHmj9i6Vpz/5w2G+aUnFMMdjpdP/3X3/+dd+NxvuYgIEM19sNz25gaem7sCalRAWkp4fJvub1JzqNPW03+D0z6YiZ43dBMaS0JP+vO0rSAsha3HTkUlc2Si+qtlotrKGn8oZfATJZaVh/IwkBRCGLpqk1BjOO42Qtvh8/vdDjREuaoyUp5t231sleUgG/cEkkRzgBJCS2gGaz221b6vIViPPrx/fv98fBuBAjYPopHiZa4UVfACQoq5mmwbzl1Zxrmj8NNcGfie+9d+3c8f18Fi0Nv1/rdvjCOKdCxqaQjDGhvQftRdGUmkiW57Ffsui/4hYYu7Iy9MOX/rxrQ30kd5uKh6nvlBzZKwbUGWa79DuHrDPColNo9y1Nk3HBBq6FRi9ekSC5ibgBVEm1z4USfOFEOT48HLthtiFGAEQiDoLPHDMpQQzUFyDvm4yOwRSdtf5lHn8S4gRpxcy8ISCILzpdS95+SS198cBcaUCN3iGrg+CzNXzlVdJFnrwKApDLIjIdmS6prS0pkJ6oXr5YhSAAQ/6UWP2T4wkm4qfWhVzoqj0cGhWm7lgoKWbrl5Yojye6pNhhS9QqxhB8CFmDhOCsW5hIAHIxdy4QeZZnyKcGAsQYCUGb+acyaVDIr2SYDCWmFur3zypiIs0xdiE3WKsmEFJ40UvPEwJLCDErgdcEBNcyr8uvP1nBL5ARkg8plVwai1Gx/Wu1nC+PhDGe+Uq0YIwJDVxHJrUSDNdM4udDiG/fAnIuhHjD3rhupJTgQnzxaaBofSVXgjuYUOVmf7vTaT7VZVGocTLWx5BSetr7g1EH84sWSClGslgCkbLHpeX6wq2xsrrxZ1vlbIBBCsFHYJwLVRRFoSRnDHPB7rw0FiHKqlWLfUZCSIOYmchMeU69SKWVzF3ini0yIVGpsIUl73Vu3ows4EKkeFlqd7nKhVzjZ0q5n6uo3PRdX74Y4kojuphrJ8tuep/rppgA5D6hENQXPZfaTubqZkXLeD8lQyi8fJh+5NLPZsopuzzbegrnlhJrZfYzBfDysij3UG8PhwrtpqnKsuj6abZu4V06SyIudH0rX19KxFXqXO5esGw/52NkCYnmRNOW549fVEaeUwAtOGd9YoyJRUExoDRe1khnqrxstHH+luP/xuML3s7zNBsXYkTqWL0wXnlMT3UmACJnQgqeo3zp9f4gSMAbpW3kZwHB9TeafymQ+bQOWVJLF0dipqIoVy6K3ER4XluNXZWxoGz6Eumlnp7IZELGc5JwaQnyCQXyRG8+N0SRC6G0Uo8bF35iPBVGsjiItep8uqQUr3oqiFzIom62+4b7pq6qqjqe+nGy1vuYGXETwIL/rpp2u22JuB1gpZvJnFcpBm/maZ6N8zIBADImVVFWZaGkCysJH208RlhgrTk4MyGGXEjIzy2FHkXN0qI9pMz1IQAf08QJMikFNaQSOTBQVHVdFdr4gI9Odly0plJacvB2RkgQX8mD4EI1XyeRYSt4/ndJFMUYvPefhcnA6jUtjJcJ6BgiPrLME5cVyJzBa9cf9inbWP0wmTJwBhxZAkDGckkV/dVz3No7l13GUzqW1T+gw5KsE34ND8tbk8W1MubSb+YMWYQlFk6lE1eU1CMdoXXTbmSsq6qqm/ah68Zptu5CiyAxSpXN7nBYKBMw014Rxpb6MwRH/dCsDyKRC6KLum7qfnbxTOELCIwmrqqq4MmMnPY/Pkc7ZnE679dFJX28YoqgJWYax2GcrROM2mRTcelkQvRnbCUuJpNUuixLzZOdOKQYXzGxMNNHtWNUVHUJ6z/Lbyml4B0ZPp+kJMSVSJLzkNGCjImVkD1TsRDTVWaGvF4+MqsoNQtRgjHgjMKRF+RyT0pnrrvy2uL4fBsXvaOyLV5kyoOf0CApJ+PWyTB3PiT+w4SMaKVj8B7D+zokK4aiKKtaQ1WWZd20m+OpH6bZWLdGbAEZk7KoN4e728OuKVWuYKJzara5+jK3/iIsCkOAhcWyHYxP4ImGD1f1UdZt2xQijEcIPibM1SPLc8xBMCkEY/SicsfKqtRKcPxEgILaB1AkU3Fc+Oi3p36yIUKIC1P9shelLqu6rQoRpg5j8CG+QvtDNUabKcjJ+pRWsVh+AS60Y+M4of1IlcyT9yWk1oVSLi5alecCmja3YYUUvTXjMAzUsuj68z7mbDqxkPIzQjgXVHVd/3EXPT2tl4c1mLEUD2UC/brO5I0/oUIWq2OVkGwDKCUjgzUr7oy5qm04wXG11loXPGhdVHW7PZ26fpgmY5wLIaSYshYv2+3h7tvtri2lyNzVq8JIae253g8UBcEltbHd9ZMNgCyzLdPOU0XVbLabtmS2Z94YF2Dt3XDuP6q01oVSPmEkanai4SdirI8/vYX2rBvGQnFkwIQsms2uG2YfAb0n8xUhR1WULpt2s2lK5gYZnbX+NfJqZEJVrYmyJaZ6fPYLUvJ+HvvuxOADm/bxLMiFLMqqqk0EFiJQIqve7Pb7XVufCYcpJ/7h3RzPiqJS8SIvvwQUxwwT/ciiU6KS5TPlClBEl3Oee5cKVVT1yrn/WfFYktXLHoJ8qKqiLMvSs4Bcal0oDn4eGSz8a68PBETOhZJKSqVEosLaTdf1/TBO07x0DwRgQuiiareHm9ubba0lEeR6O49r7760gsdP/dBoyZHljrK7YbIBmCDjNTuzuqjazXa3bTXMyk+95DHFcAFkgdX+qQcX0UdgTOp6s91u26ZQV3U7eP4IY7Dz0J1OJ1KDBKjYjuPsQkJu3KIxs/dRVE27222bAs0xmZHU/wsCQqUs1TaJarI+knw/EZAE0bupP/7gQI7VJ9a/gGy3s0duXUzAuNBVu7+9vdltqkIyxJRyh7Su/6C/kCAFbydy042WKeV4a4b6fspFJ6QOeV5LW8BcRySlELm9QlG32+22rS9oZD41FmHMsYls+VZNOzjQgQldVqUW0Q4SYgjv6hBceXAEFwIFbYnNMAzjOE2zsUurKk7sYdvtfrdtc/EMtYmgQ4WaF+Q3czq2DXVyyeiR2frE5DA5HxMici6lLupms93tNrWMQxpLTfVqC8OsSDlHUzab7WAiCheBZS9ot6nLT1mqOY4wdseHllpgMWBSV+1sbEhMqHwnCZExIfIi97ttrdLITFe80f4AmVBlZLo1lHB8Yl7lmIaZTrUEb61jH9chCKREm+04B5R6diHRDzaHb99u922lOMMMyz2djqfhw8d9Wvt/DnUhWY5aLCffp1x02rM+t+1IC4mzVEVRFjaxmPuq7vb7bVOqT7HVnOci3FwWRshucNlsB5OUjVwVdV0VPExHFpzz8YVM1ZOx8ouxTNWmirJup2meptmQgIREBN+kBJtM0Zqt0lPXUTNJAoQGN4/d8djWhSSuay6LxvmQuCq7kY48xklTtZvtdtuUzPG5XLs9zdM0GxdCZk3RdbtRZ7PAAAAXLElEQVQbTEA52QhMFtVmd3uz31SffI4pQfBm6o9tUxdaMBQMuSpb5wMwWZyWQiBEzqXSRd20291u11bcK3cqMmHpaz6IApSlzTilR+75EpTzdqxVstM4GfcpVkJEIctmZz3Ksp9siMiFLtvt4ZZMXyrft/NwOt4/dP0TMqP3nw/EQB1LT9sxn3GQFnbrU9d9zkWPwVlj1v4S5EtWddPMkVs6+Ort4Wa/qRTHJSz0uUHaarV8FtdwtEkNFrgu66bSzPYizNP8KvzuPHIxCAIkQGQJmZCqsNZYYww1aA4xpcymX5VVVWotOOISETwdO9IMKQE1CBu7432u8afCtCqExGTRdINxIQF5mWXdNJtN25Qqzb5QgjOAGKyZhmGcjKaadyZ1tTU2oiwnE4Grot5sD4dtrT99zqTo3dwf64qsXRDIhK5DTEwWVbO4oKTkyqpuN9vttq0VmNjrJYlFAvI43I7AAIDJwofVi3kU6IUsIDKZ/nSSYsm2P82gvfjn8zSMSV3bAKJshsmEuHSFvDnst5WWxJ07D93x/v7h9NQeepIheCl7l1KwZrFBGQIDTCm6bJe+aGG9fdWUKczneV5jzsQqtNnNkRcuIJO6arb7w7ZSjJB9+PK1nv/ghQ/kBkOZUB2QCV1bl0RN/ZHrqpQwF3Hqcnu6Ny+aX1wGVCEip/CS984563yOY2WdqLTWWkrBGGb9cXy4fzitLMYJIHg7ddStjDMEgcBkiokJXW+pVWeO1JZVXTd1VSrmEyU1MEeYum5Ta8kWD8b7gKLcjDYAV2XVtNttPmeePMcnN/jyU6WWvmN3XxRaCgaQBDBZREChyuY0jLPzgbxGVZR13bZt21SaRxwXGk8SEMIznCP7CQA5MnEOGp0F4yIUZEWcu7pYQgwLLCKv73m+ICeGF/oLICrIJqKsNuPkfEQui7LJ9rtghIDuu4cf338cu9E8azqxXis9vYP8Gep5dDpu6kIyiByBcujd6eF4okbYTx7uelUKQz676tK4bJxmKzmS5a2rzWiiKEcXgdqPbDa1wujtmkt/fq3zD9LFB+BispRi9M7OGUOAgMikrkLkxWYOIFRRloqHMY3l/6/uyZYb2XUDuPQm2cn/f2Husa2lF+4k8gCy1bI9Pj6p3FSla8o1M25RBAiA2NFrKUR5OkkG4HgGe/jb+6AQSAAKQVKVnHPiwonqxaqNcjmG3Yy2+/VyvS3G7xlxPPLvPgx1wBlJgVKPKPRw+o/N+pRLTe4YxnGchl7LAqKBWg3o+8vUa75+pB5LATW8LDZkkLofx3EcNeSUkhT4LVG143/Q3BOaqaRgl447sxARSZQdodD96XXZak2UkKrrhnE6nU7TNHaKosD2NQCKdV0q/KfdBQgCi9wjVE+8wU/JWMah77SSAmqSZml1f1SjBU+zIOpoCH6hdgQTsptIja8u1NkEw3Q6n07joLlCwK3z5f3941pHox7ExGOt/V/0HNQnpJK8Xe/n89RLKJ0USCX6bb7xxOHPFwgBZ3HWGrTm0D2qYZwEadZ1PY9aIggAobrpNRQ5vNpYQHb9ME3j2GFyUiolBYrHfmmflXRAej3k+l1HEKi6Vn2IWrIrVI+EerShgNRdrxUF6nlk32PRUgqVmh3y1MutDpVaR4VUatatFKRKe/jV2mldCh6aSjlyIvr75b7YvUUSYcHkTdf3HWvDpVOIskOh++nVcS8l7iva9Tx4k6hUjbFQoejMfHtpgUgEoXoCoceXzYUCqLRWWoocpBCtgWODqAk2qrHUI9HtCfmMj+ik1poNqZI7haJDVN30slkbQioF2IPVj8M4DJ0WmGrmH2sIqqb255xyzrL5Clnl/UY93y86BNnqYx6pAnUZAbj/vfkoefuZJ65lFkAppwKyF9055lzqlN9hGAatJccxtvny/vb2fp23Y3dQgj2zNJeMbdkv9SJUcnTbPE29whwGJZBytOv14/1yWzb/KTuYHiPhcilEJXPdzHPFUE7BrvN8Z7tfIqDsplREdzIuFhQ8yUSQT6mAkEIgCSgll5zyI9vyCelZNGjSIXOnemF4qNmgBUgAFIpA6DGkDEIqKSDlQ2r2wwmdOcK+HwFV/12w63wbFOTUd9zhExFB7HdNjaQ/OlnyWCa7XD/e3t4vT3KKoCThVqUUW4s5d0qAUCj1UJMYQdRQsFJSIOXgjDHWhVRKgei2+XSahk4KAC0RhOpR6OHsfMwEKABKCTHEgjX/7BmiehO1E8vERPd0ZJQxyFVWf09kj7EapBqmVxd45hpKqTmJUSuJlLzdtq31ugEFNZs8hBCjoN0eov3HE2c8NIRS9sHARIRUNeYYggQJlGPLwC2Pw+NBkFFCQSg8ECvEIvoegDjWrzrdaSWxQE7Rbcv1/V//+utyW5+N0LZWiFEQlhS/zfYloJy8mfteYfZc15mCXS5vbx+3gyx8rEqZ9fGoSFCKITYIdrouJXm7ztfT2EmgXgkEVN0JZP9ifSqAUkiEEmNGHYqQAkkhULUl9uq2I9KREEpkO+AJhD2iuUy9EpyDIRSg7FIqAIBAKXlnXXUZ0L5oUFAQ8j4hlZXaUqI389gJit5NY9cpJSTuVRrQ0rPZX4nQtDxvl/vHX3/99XFdrD+2waeSgpU1mh/DaeyURBRa6L4W77T+pAK5D9lyv90X42POBVKw623gHmVUlBQgFKDqxhBSzqXkFKKPRY6RUCAoAcBEFfa2PQc6kCSA0oHoqiKf0QsUCFRiCIHb+gstdB9j4rxILj9SSkpEyjGY5Xq58aBeIFAt0dhZO4jya4cBUQrO+SYpmBijd9ZYjVkC5WCtrZmUrNKXnIJ31hgFWSDU1IWQqQ3/eAzlpkQ5BmeW28fbX/96vy7mOeu2xnWtGVVRSNEaa9031f1UUrBaKyxhO4+dBG569PH2cf88M4cAgDL7Hq0GjZSdNdb68HTRUMnRrbdx7BWWnDolkUD1ILqpVU+XHIK1nrTLKJCyElCys8a2fMuqgyfvrTWDKAqhBGeMbWHqw3d5s95vZ+75IRGJAKUWUpVSKOfkzXKtJMdfHb2z1nSYJcLjCDi9kHJ0m1aYgzWvp9M48Pibmi6IRx26KWxsci3369tf//X2cd+eCvgJSo4oRA1qOD+NvZYSQaBQtAfhakJdDt5u9+v7x20x7F8Nbu262iV56pVi/heqiymmGHO02+aSnDwBUNYSIAdjHyfdfIrWWitJCyjJmZowf7TTERAox+CdfT1NvVYCUQnNDR0AAIVArlTJKTq73q9v79e5blK1wMCyDCL3vw1ZEpQUzHxoCtK6tc09JBbV2zwvm/OxNCU7B2/W5dRB1BKppliFLPQwDEJJJWsuPuVScvRuW++Xj7f394/71uaCV+ZsqbqTpkFhiXael7Xmohx1eKAcnRRYoplPQyex5GpuzubLSBDeod2WWUPUgpJb55nHex7N9Bxsd+87hSVFP2otBBHKXujIN30M0S7L6kpnM2CJA5/cOs8slWqBdEnebesyytxrhBztPLdsgaZgI3EZ6mnsFJTEWbXVzCspp+i9XefL+8d9dTGXItivvc69SL1EysHc90Xrfboh5Oi25fX1fJoqi8g61BZ2Txc0fT7F4M16v72/vb2931YT8hPSqCS0LIecNeZlGgfdWveKpm5Q4YWc2Zb75ePtMhufSgFCL1k9yzGENqqZAIUsWJLb1nlZbFKTL0Rp7D5DRKy0OLMtJ1WiklDifmQ7HxdIjZOs3baXc02RQ5SiGtlsIWTO3zfrcr9c3j/m6hVSLFjMchtEdv1vcyOISgrm/nGbN24fxDS73k89holF9Xa7XOdtb2NHJQW73k8dhFFLpBK93Vbjs+yn82ka+qIUCUSAUkqM3m3rfLtcPi5XTi7bNSxCopK8We+TIjcqLNEuH9f7yvMsnvdZEgqg7Lf71GuJJQdnlnleVxe/FIKwfXE/dRBHZpDrlaXzUcfKKWxKSSzR2/PEXn0qhEIBAuUUzDrf7osjbVLOwTYGuV7vy8apO+1yWG6jzG5QzCCXy30xIT5qQdkjsAxDpzBHDsc12yXFELy123K/XS61yVmNGl17kaZeIqRg5o/rnSNIxDYIQkneLPPr68v5fBrHvut0DR1iqy+hau3nnEJw23a/Xd/fPy63eQvPSOMxtQCUOS/09Xziqbd7/QYRFMolxeCd2ZbldrterosNKRNhjk4IVmuc5cEjSJS5usGsy3y/Lzark8852qljiO47RGzGervcJk1+1AJKsuv1cjseGWGBVPFtzbK8vpymoe/UIc+6WccxBme3bZnn2+3a3BGgmLKXsRfZnn7NIFBKCma+vN0W9pbyfyzXDsPaGGT++LjVo+PT9mYeNfm1Moiz67r5LIfzy8uZe8By8WVOwTuzLvPtervdeZjak7QvOfrtPijyZ75B1uvbdTbhcwMGAijJEyW3TX2nZD3Kaih+YaaSgplHDWFlBtlu71WtOL6Vg5ACKXm7vnJFdTVmc0rBO7PM99t9ddTZGIM5VwbZbh8f1cZtXs711stsT/UGWW5vl9m443VFJQertZaYgzuPnRYCaiuC6L2zdtuW5X6/L6uLuVApOZj11om0TfUGWS7v9QgAgCAzMdt1fnl5eTmfT+M07ANrRCtr2dkjRufMutxul+vlOje6e8ZvBoCSk3fbMr++nPlWaiVOVJ0pIThntnWd5/k+r41jSwJk/rDb+tJkTU4peGu25X6/z6sv6uRTsC9Tt0PU5AFhKUwHxTOao19vH5c759s3Ji6Q2JyrWzxN1fnaZmO1PXpn7LouyzIva9VHWMXK0a5aZDdPNbvu73iEAErJwa6362U2jH62hxXE7Twwg9jlxtKisNTP0W9aUdhOQ2OQbTW+yOH08npuwhgoszGzbcs835dltS5+KvtjbbqTxS+nXmFJbp0v9eb+dClAAaASvel1Dc1zIPzzkrCzsKK4nQYtKHkzXxnbBwczUkaPQDnY9T9fzuNYY65U9222Zb7NswnU2eDtPA1KQknezLeP29qcQEQp2kWL4uapV6z2zJdL1SUfm8/Jb1JACXZ94Y6QnH4SgnPOGLNt67pthkUylBTsrDCbF1ZIol3vl11ZgDb8OTizzKfz+XQ+T9M49k8sAgBQagw/OGfWdb7fbvf7sjn/tactAeRWkbDOL+fzaRqGwwgcKlU0O2u2dV3XbdtsVVqpAHgWdmadX05jrxViNWW3dZnnebWBlPHBLucdotv1UiECopzcqhWF9dRrASX57QnNO4ew0WrW+fxyPk01PMFcTEQ5pxS9d9aYbd221RhbgSVQACVHJ7H49TR08vcqVo7OrHO9igig5GAEJDdPva4uvWWe12rWsUItBSQ7j72SSCUGa40NRfbT6eV8HsdOcw4C21NmW9d127gk9Jk/gErym6Bo7kOngFIw61JjiZ9TR6iwYa0525ZKSpxs+IU/kEqqIIy9QkjRbst93j75B6Ckqi9uM0sjxXdIzik6Z7ZlWZbNJVDO25Vz3kuKdlvmZuQSQinRSVHCeho6KThgM8/VMjxcIQkRgZI383nqeXgGO+2cc9Yaa43jVPXCNrOVWPwycSe26M06z01Z4LmdlVS2cZxO02mapnEY+k5rLWVrw8JaHJP1tq7LPC/rZlz8ruSZAEpiN8+2TKcTM5yWHD2mUkq77owxLSmSpzchFQYxum25vUxsPlNOIThj1nVdN+MTKRe8mccniExsKlb0kumgVwJKCm5d5vmTyYqFGtgrg9zvxhe2Kzl476yxxljrnA8x1SiyIoASESi59Z8Mu2KflDHGVplHlCNiCXbuO8lwOms26/auGSVHCyVsI8f9CxfSpiL0OJ6m09izElpyjiHU47etK8AnYi4JEbLfhsqM3trN2m86+FSiSNKLJtPynybpVR09mLHXijnYGPPFsqGS9wjL2FxBDy+dZSGZQQbv1rHXErE0zS7kVtia0SMjXUskStFbY8wnbzZSSTyMdz2fapJfyTnG6L33zrUcqkeRH2KJdt4HoHg+gkouBAClJQP0/TjwcPShf3AIAtKDZKzdzLqtpoqp7zLXOOBbUvJ2q1MGu06pnUFyTjEG77yzzrtQy3sJGoewHbGeTtPQGCR6b61hMZ5BhuC38QDR1miOkCgHpBzWfaZesLbGMD7RQOGmEP3wGIT4zCDBe+ec4+lSae9SqACoQKQS3drtI0v/nj+glJyiD3srBaRcXTydEgKIcozeP3zWBFASUA6m00oiEuWUQoyJhNqGZRgGHs1CVeZwAl0LpHyjOEFJfuu0FMjtXXzY0+ie3wWigrmVjRN9jbkfyJFB0FIilQrBp/AKIWWqmcID5yvJ1rgmRu+ccy6EVFDE4PpOS4mPvhPHPA2AEt2m2fTOKQYf/CcQWEOkEt02jj3bIIVbgobALv+9EpAAqeRAObo65KbkB14e5IxUikjBa82Tn/uh58myUtXKyKZgec9iyjnvvxNTn8jPO913fdftQzihRV5TiCH4EGLkPoct0I18vafgtnEY+k6xyzeF4J13PoSYCUSKwRwg8qFCRI0Osu+7WtWVUvjuyGAXDLrrDoN0m08oJQ56hX2Puzt0LzdUO1S/4RCqt+dBWUHEuopExBq0aviAVvWr1EG6cGyZW4CwNwWhTkmPMX7e6uFBwEOXMlYiH2nhnx8EOHhogAi+YQ+AWi7bEFFbB6avgpNr6XkmbMtYqhuPNbiYCoEQ+3B3olyx0ZoZcKH23meNTfxvQMBWBtj1WktmkJJySimmxIH+w2HuE+UZLfWEypOGyo0yODimdae7ruu01lpxrTs0xSiFEIKvd9TPfV+50ZFQUmmluYZaCEQkIsoVMm4Y9LV9Crc70V3Xaa1EvSFTDLFdjPjobLJDtNMcPJHuXqzz9a7j9IC6R66J2TVKjs/HlFJMOXFGGjUnNTu/RW1s3zpM/P1DLZb+UFZa6emuy3C56K5SY+uff/BwlEKE2CKZtca2Zgykcjz8b46EQ7RNb/pxvuoh27LpGt++Vgc/onhAQN8oFkxiQqrWmGOnq5xSTtw4HdsGscGaD2thizKjgPrCt+A+KvcV46dQKZkHkuc9LYkO+zqi5fmEHsir6JOqVlBpqR5ztrkVckoxxRjSn8XU04q4jw/hwbJcXVCTo3Ld7de7m9EglZItNbKl4KTaOx4FSlGjmV9pDh4Af4vmT3sUKIUUjz02LxZPeD/scf88PuD7psnETw8RfFJW6hYQAdp00md8ILSpdtiUVyLY+WbPCt4z7p62+hlc2P2STW/64QjhiUF+eIdBgEfq+LfLtnwllgei9bGojWuqxlN3WLH1ea1PLY/+CEIj+p0PK36o7B/4fD2gAASs4YzvFsW6LIpGL1I8vFgVkD3H9zejnbEebmtL9DgYonLIMPyyDu6CdXcNt8zJvdL4gabvaO5ABz8d2S4YauD84dWmlgL5PUYfnz3K2b99qAaUPi3WloH260+yq2X5HN54MNYBqTuYfzwXPHzXl538j58nEL6B4PnrGzdxLVY7PWZrBDg2lf0WG1+w9Yfb8oifXbB8i6DvjuDP1MLMh5XLD3mnO5X+LKa+fvMuaI9RON7sz/C1D9Lx/Jk6foboCYs/HtmubB/2yM20f9gjPv3l9/cH1HW+kU0tdv/dr5++pr3R2OZhJfyO4ve16LHc/8LzvOxPesV+OM/8dFCj2o+/x8ZPX4Z/ws8fSB9+PILP+z9Q58FOo+bN+AeiB4/k3HZQ9/vTKkcjEZipnl7/G4h+icU/7fFnjOKP//zx+ZF0/naXz298urv+Hs7jZ/+3OOOfr3rYNj649IsS8Qts/M23/XP8/GLRtuyRzR9kTb8UU19X/ExFvznNJwC/Z338eZFfAfxlj61f0h/3+I/ujH/v85kA/r88RxT+Ozf+b8LPTi7PpEDwJ0L97ZrHlf7Zp/5Pjv8ruN8+/w1xtM5c/5zgcAAAAABJRU5ErkJggg==" />
+ <mask
+ id="mask27">
+ <use
+ xlink:href="#image952"
+ id="use516"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image951"
+ width="800"
+ height="109"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip60">
+ <path
+ d="M 435.19922,148 H 561 V 273.8125 H 435.19922 Z m 0,0"
+ id="path520" />
+ </clipPath>
+ <image
+ id="image958"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <mask
+ id="mask28">
+ <use
+ xlink:href="#image958"
+ id="use524"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </mask>
+ <image
+ id="image957"
+ width="157"
+ height="157"
+ xlink:href="data:image/png;base64,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" />
+ <clipPath
+ id="clip61">
+ <path
+ d="M 0,0 H 1315.1992 V 1392.4063 H 0 Z m 0,0"
+ id="path528" />
+ </clipPath>
+ <image
+ id="image969"
+ width="1316"
+ height="1393"
+ xlink:href="data:image/png;base64,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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPDXHhwIAAAAAAjytx5hgQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgBf+71XNGFSbhwAAAABJRU5ErkJggg==" />
+ <mask
+ id="mask29">
+ <g
+ filter="url(#alpha)"
+ id="g534">
+ <use
+ xlink:href="#image969"
+ id="use532"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </g>
+ </mask>
+ <clipPath
+ id="clip63">
+ <path
+ d="m 445,155 h 101 v 24 H 445 Z m 0,0"
+ id="path537" />
+ </clipPath>
+ <clipPath
+ id="clip64">
+ <path
+ d="m 457.50391,155.92187 h 76.10937 c 6.39063,0 11.53125,2.95704 11.53125,6.63672 v 9.61328 c 0,3.67579 -5.14062,6.63672 -11.53125,6.63672 h -76.10937 c -6.39063,0 -11.53125,-2.96093 -11.53125,-6.63672 v -9.61328 c 0,-3.67968 5.14062,-6.63672 11.53125,-6.63672 z m 0,0"
+ id="path540" />
+ </clipPath>
+ <clipPath
+ id="clip62">
+ <rect
+ x="0"
+ y="0"
+ width="1316"
+ height="1393"
+ id="rect543" />
+ </clipPath>
+ <g
+ id="surface968"
+ clip-path="url(#clip62)">
+ <g
+ clip-path="url(#clip63)"
+ clip-rule="nonzero"
+ id="g550">
+ <g
+ clip-path="url(#clip64)"
+ clip-rule="nonzero"
+ id="g548">
+ <path
+ style="fill:#d7f3ee;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 445.97266,155.92187 v 22.88672 h 99.17187 v -22.88672 z m 0,0"
+ id="path546" />
+ </g>
+ </g>
+ </g>
+ <clipPath
+ id="clip65">
+ <path
+ d="m 0,1088 h 1315.1992 v 5 H 0 Z m 0,0"
+ id="path553" />
+ </clipPath>
+ <clipPath
+ id="clip66">
+ <path
+ d="m 0,705 h 1315.1992 v 5 H 0 Z m 0,0"
+ id="path556" />
+ </clipPath>
+ <clipPath
+ id="clip67">
+ <path
+ d="m 0,323 h 1315.1992 v 5 H 0 Z m 0,0"
+ id="path559" />
+ </clipPath>
+ </defs>
+ <g
+ id="surface741"
+ transform="translate(-433)">
+ <g
+ clip-path="url(#clip55)"
+ clip-rule="nonzero"
+ id="g928">
+ <use
+ xlink:href="#image935"
+ mask="url(#mask25)"
+ transform="matrix(0.802138,0,0,0.802138,432.8,-0.123972)"
+ id="use926"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </g>
+ <path
+ style="fill:#1e7866;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 449.87109,2.960938 h 86.65625 c 7.6875,0 13.87891,6.1875 13.87891,13.878906 v 86.656246 c 0,7.6875 -6.19141,13.87891 -13.87891,13.87891 h -86.65625 c -7.6914,0 -13.8789,-6.19141 -13.8789,-13.87891 V 16.839844 c 0,-7.691406 6.1875,-13.878906 13.8789,-13.878906 z m 0,0"
+ id="path930" />
+ <path
+ style="fill:#ff7d28;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 449.87109,2.960938 h 86.65625 c 7.6875,0 13.87891,6.1875 13.87891,13.878906 v 86.656246 c 0,7.6875 -6.19141,13.87891 -13.87891,13.87891 h -86.65625 c -7.6914,0 -13.8789,-6.19141 -13.8789,-13.87891 V 16.839844 c 0,-7.691406 6.1875,-13.878906 13.8789,-13.878906 z m 0,0"
+ id="path932" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.566698;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -469.08203,23.070312 h 4.52344 c 1.80078,0 3.25,1.449219 3.25,3.253907 v 4.523437 c 0,1.800782 -1.44922,3.25 -3.25,3.25 h -4.52344 c -1.80078,0 -3.25391,-1.449218 -3.25391,-3.25 v -4.523437 c 0,-1.804688 1.45313,-3.253907 3.25391,-3.253907 z m 0,0"
+ transform="scale(-1,1)"
+ id="path934" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.566698;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -469.08203,37.265625 h 4.52344 c 1.80078,0 3.25,1.449219 3.25,3.25 V 95.21875 c 0,1.800781 -1.44922,3.253906 -3.25,3.253906 h -4.52344 c -1.80078,0 -3.25391,-1.453125 -3.25391,-3.253906 V 40.515625 c 0,-1.800781 1.45313,-3.25 3.25391,-3.25 z m 0,0"
+ transform="scale(-1,1)"
+ id="path936" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.566698;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -502.07031,23.070312 h 22.76562 c 1.80469,0 3.25391,1.449219 3.25391,3.253907 v 4.523437 c 0,1.800782 -1.44922,3.25 -3.25391,3.25 h -22.76562 c -1.80078,0 -3.25,-1.449218 -3.25,-3.25 v -4.523437 c 0,-1.804688 1.44922,-3.253907 3.25,-3.253907 z m 0,0"
+ transform="scale(-1,1)"
+ id="path938" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.566698;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -46.828125,-525.47266 c 13.140625,0 23.789063,10.65235 23.789063,23.79297 h -23.789063 z m 0,0"
+ transform="matrix(0,-1,-1,0,0,0)"
+ id="path940" />
+ <path
+ style="fill:#ff7d28;fill-opacity:1;fill-rule:nonzero;stroke:#ff7d28;stroke-width:0.806224;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -47.03125,-513.82422 c 6.761719,0 12.246094,5.48438 12.246094,12.2461 H -47.03125 Z m 0,0"
+ transform="matrix(0,-1,-1,0,0,0)"
+ id="path942" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.566698;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -522.23437,44.191406 h 4.52343 c 1.80078,0 3.25,1.449219 3.25,3.253906 v 33.972657 c 0,1.800781 -1.44922,3.253906 -3.25,3.253906 h -4.52343 c -1.80079,0 -3.25391,-1.453125 -3.25391,-3.253906 V 47.445312 c 0,-1.804687 1.45312,-3.253906 3.25391,-3.253906 z m 0,0"
+ transform="scale(-1,1)"
+ id="path944" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.566698;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -522.23437,87.390625 h 4.52343 c 1.80078,0 3.25,1.449219 3.25,3.253906 v 4.523438 c 0,1.800781 -1.44922,3.25 -3.25,3.25 h -4.52343 c -1.80079,0 -3.25391,-1.449219 -3.25391,-3.25 v -4.523438 c 0,-1.804687 1.45312,-3.253906 3.25391,-3.253906 z m 0,0"
+ transform="scale(-1,1)"
+ id="path946" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.566929;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1"
+ d="m 58.601562,-522.98437 h 4.527344 c 1.800782,0 3.25,1.45312 3.25,3.2539 v 54.72656 c 0,1.80079 -1.449218,3.25 -3.25,3.25 h -4.527344 c -1.800781,0 -3.25,-1.44921 -3.25,-3.25 v -54.72656 c 0,-1.80078 1.449219,-3.2539 3.25,-3.2539 z m 0,0"
+ transform="rotate(90)"
+ id="path948" />
+ <g
+ clip-path="url(#clip56)"
+ clip-rule="nonzero"
+ id="g952">
+ <use
+ xlink:href="#surface946"
+ mask="url(#mask26)"
+ id="use950"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </g>
+ </g>
+</svg>
diff --git a/doc/dev-doc/changelog.rst b/doc/dev-doc/changelog.rst
new file mode 100644
index 000000000..03879c6fd
--- /dev/null
+++ b/doc/dev-doc/changelog.rst
@@ -0,0 +1,63 @@
+Version 4.0
+-------------
+
+features
+ * transferred the CI from jenkins to Gitlab CI/CD
+
+api
+ * breaking api changes to match the stl containers
+
+ * ``clear`` does not set to ``0`` anymore but empties containers
+ * ``empty`` does not empty containers but tells if the container is empty
+ * ``zero`` replace the old empty and set containers to ``0``
+
+Version 3.2
+-------------
+
+features
+ Activating PETSc solver back with the new solver interface
+
+api
+ deprecating old C++ 03 code
+
+Version 3.0
+-------------
+
+c++14
+ switch from C++ standard ``2003`` to ``2014``
+ Example of changes implied by this::
+
+ for (UInt g = _not_ghost; g <= _ghost; ++g) {
+ GhostType gt = (GhostType)g;
+ Mesh::type_iterator it = this->mesh.firstType(spatial_dimension, gt);
+ Mesh::type_iterator end = this->mesh.lastType(spatial_dimension, gt);
+ for (; it != end; ++it) {
+ ElementType & type = *it;
+ ...
+ }
+ }
+
+ becomes::
+
+ for (auto ghost_type : ghost_types) {
+ for (auto type : mesh.elementTypes(spatial_dimension,
+ ghost_type)) {
+ ...
+ }
+ }
+
+features
+ * Parallel cohesive elements
+ * Models using new interface for solvers
+
+ * Same configuration for all models
+ * Solver can be configured in input file
+ * PETSc interface temporary inactive
+
+ * Periodic boundary condition temporary inactive
+ * Element groups created by default for ``“physical_names”``
+
+api
+ * Named arguments for functions (e.g. ``model.initFull(_analysis_method = _static)``)
+ * Only one function to solve a step :cpp:func:`model.solveStep() <akantu::ModelSolver::solveStep>`
+ * Simplification of the parallel simulation with the :cpp:func:`mesh.distribute() <akantu::Mesh::distribute>` function
diff --git a/doc/dev-doc/conf.py.in b/doc/dev-doc/conf.py.in
index fb87b1f96..9649b901a 100644
--- a/doc/dev-doc/conf.py.in
+++ b/doc/dev-doc/conf.py.in
@@ -1,212 +1,242 @@
# -*- 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 subprocess
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- Project information -----------------------------------------------------
project = '@PROJECT_NAME@'
-copyright = '@AKANTU_COPYRING@'
+copyright = '@AKANTU_COPYRIGHT@'
author = '@AKANTU_MAINTAINER@'
-version = '@AKANTU_VERSION_MAJOR@.@AKANTU_VERSION_MINOR@'
+version = '@AKANTU_MAJOR_VERSION@.@AKANTU_MINOR_VERSION@'
# The full version, including alpha/beta/rc tags
release = '@AKANTU_VERSION@'
# -- 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',
]
read_the_docs_build = os.environ.get('READTHEDOCS', None) == 'True'
# Add any paths that contain templates here, relative to this directory.
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'
# The master toctree document.
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"]
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
primary_domain = 'cpp'
highlight_language = 'cpp'
+bibtex_bibfiles = ['manual/manual-bibliography.bib']
# -- 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'
else:
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'
# 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',
]}
+math_eqref_format = "Eq. {number}"
+
+# MathJax configuration
+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': ["AMSmath.js", "AMSsymbols.js", "sinuitx.js"],
+ },
+}
+
# -- Options for HTMLHelp output ---------------------------------------------
# Output file base name for HTML help builder.
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}''',
# 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'),
]
# -- 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)
]
# -- 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'),
]
# -- 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 = ''
# 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']
# -- Extension configuration -------------------------------------------------
if read_the_docs_build:
akantu_path = "."
else:
akantu_path = "@CMAKE_CURRENT_BINARY_DIR@"
- os.makedirs("@CMAKE_CURRENT_BINARY_DIR@/_static", exist_ok=True)
+ 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))
# 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_show_enumvalue_initializer = True
# -- Options for intersphinx extension ---------------------------------------
intersphinx_mapping = {
'numpy': ('https://docs.scipy.org/doc/numpy/', None),
'scipy': ('https://docs.scipy.org/doc/scipy/reference', None),
}
diff --git a/doc/dev-doc/index.rst b/doc/dev-doc/index.rst
index 9856890aa..c7116d93f 100644
--- a/doc/dev-doc/index.rst
+++ b/doc/dev-doc/index.rst
@@ -1,28 +1,49 @@
.. Akantu documentation master file, created by
sphinx-quickstart on Fri Apr 17 16:35:46 2020.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Akantu: a FEM library
=====================
.. toctree::
:maxdepth: 2
:caption: User Manual
./manual/getting_started.rst
./manual/fe_engine.rst
+ ./manual/solidmechanicsmodel.rst
+ ./manual/heattransfermodel.rst
+ ./manual/structuralmechanicsmodel.rst
+ ./manual/io.rst
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Changelog
+
+ ./changelog.rst
.. toctree::
:maxdepth: 2
:caption: API Reference
./reference.rst
+.. toctree::
+ :maxdepth: 2
+ :caption: Appendix
+
+ ./manual/appendix.rst
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Bibliography
+
+ ./manual/bibliography.rst
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
diff --git a/doc/dev-doc/manual/appendix.rst b/doc/dev-doc/manual/appendix.rst
new file mode 100644
index 000000000..ad20f3ee8
--- /dev/null
+++ b/doc/dev-doc/manual/appendix.rst
@@ -0,0 +1,3 @@
+.. include:: ./appendix/elements.rst
+
+.. include:: ./appendix/material-parameters.rst
diff --git a/doc/dev-doc/manual/appendix/elements.rst b/doc/dev-doc/manual/appendix/elements.rst
new file mode 100644
index 000000000..22ce3e818
--- /dev/null
+++ b/doc/dev-doc/manual/appendix/elements.rst
@@ -0,0 +1,743 @@
+.. _app-elements:
+
+Shape Functions
+===============
+
+Schematic overview of all the element types defined in `Akantu` is described in
+Section :ref:`sec-elements`. In this appendix, more detailed information (shape
+function, location of Gaussian quadrature points, and so on) of each of these
+types is listed. For each element type, the coordinates of the nodes are given
+in the iso-parametric frame of reference, together with the shape functions (and
+their derivatives) on these respective nodes. Also all the Gaussian quadrature
+points within each element are assigned (together with the weight that is
+applied on these points). The graphical representations of all the element types
+can be found in Section :ref:`sec-elements`.
+
+Iso-parametric Elements
+-----------------------
+
+1D-Shape Functions
+``````````````````
+
+Segment 2
+'''''''''
+
+.. list-table:: Elements properties
+ :header-rows: 1
+
+ * - Node (:math:`i`)
+ - Coord. (:math:`\xi`)
+ - Shape function (:math:`N_i`)
+ - Derivative (:math:`\frac{\partial N_i}{\partial \xi}`)
+ * - 1
+ - -1
+ - :math:`\frac{1}{2}\left(1-\xi\right)`
+ - :math:`-\frac{1}{2}`
+ * - 2
+ - 1
+ - :math:`\frac{1}{2}\left(1+\xi\right)`
+ - :math:`\frac{1}{2}`
+
+.. list-table:: Gaussian quadrature points
+ :align: center
+
+ * - Coord. (:math:`\xi`)
+ - Weight
+ * - 0
+ - 2
+
+Segment 3
+'''''''''
+
+.. list-table:: Elements properties
+ :header-rows: 1
+
+ * - Node (:math:`i`)
+ - Coord. (:math:`\xi`)
+ - Shape function (:math:`N_i`)
+ - Derivative (:math:`\frac{\partial N_i}{\partial \xi}`)
+ * - 1
+ - -1
+ - :math:`\frac{1}{2}\xi\left(\xi-1\right)`
+ - :math:`\xi-\frac{1}{2}`
+ * - 2
+ - 1
+ - :math:`\frac{1}{2}\xi\left(\xi+1\right)`
+ - :math:`\xi+\frac{1}{2}`
+ * - 3
+ - 0
+ - :math:`1-\xi^{2}`
+ - :math:`-2\xi`
+
+.. list-table:: Gaussian quadrature points
+ :align: center
+
+ * - Coord. (:math:`\xi`)
+ - Weight
+ * - :math:`-1/\sqrt{3}`
+ - 1
+ * - :math:`1/\sqrt{3}`
+ - 1
+
+
+2D-Shape Functions
+``````````````````
+
+Triangle 3
+''''''''''
+
+.. list-table:: Elements properties
+ :header-rows: 1
+
+ * - Node (:math:`i`)
+ - Coord. (:math:`\xi`, :math:`\eta`)
+ - Shape function (:math:`N_i`)
+ - Derivative (:math:`\frac{\partial N_i}{\partial \xi}`, :math:`\frac{\partial N_i}{\partial \eta}`)
+ * - 1
+ - (:math:`0`, :math:`0`)
+ - :math:`1-\xi-\eta`
+ - (:math:`-1`, :math:`-1`)
+ * - 2
+ - (:math:`1`, :math:`0`)
+ - :math:`\xi`
+ - (:math:`1`, :math:`0`)
+ * - 3
+ - (:math:`0`, :math:`1`)
+ - :math:`\eta`
+ - (:math:`0`, :math:`1`)
+
+.. list-table:: Gaussian quadrature points
+ :align: center
+
+ * - Coord. (:math:`\xi`, :math:`\eta`)
+ - Weight
+ * - (:math:`\frac{1}{3}`, :math:`\frac{1}{3}`)
+ - :math:`\frac{1}{2}`
+
+Triangle 6
+''''''''''
+
+.. list-table:: Elements properties
+ :header-rows: 1
+
+ * - Node (:math:`i`)
+ - Coord. (:math:`\xi`, :math:`\eta`)
+ - Shape function (:math:`N_i`)
+ - Derivative (:math:`\frac{\partial N_i}{\partial \xi}`, :math:`\frac{\partial N_i}{\partial \eta}`)
+ * - 1
+ - (:math:`0`, :math:`0`)
+ - :math:`-\left(1-\xi-\eta\right)\left(1-2\left(1-\xi-\eta\right)\right)`
+ - (:math:`1-4\left(1-\xi-\eta\right)`, :math:`1-4\left(1-\xi-\eta\right)`)
+ * - 2
+ - (:math:`1`, :math:`0`)
+ - :math:`-\xi\left(1-2\xi\right)`
+ - (:math:`4\xi-1`, :math:`0`)
+ * - 3
+ - (:math:`0`, :math:`1`)
+ - :math:`-\eta\left(1-2\eta\right)`
+ - (:math:`0`, :math:`4\eta-1`)
+ * - 4
+ - (:math:`\frac{1}{2}`, :math:`0`)
+ - :math:`4\xi\left(1-\xi-\eta\right)`
+ - (:math:`4\left(1-2\xi-\eta\right)`, :math:`-4\xi`)
+ * - 5
+ - (:math:`\frac{1}{2}`, :math:`\frac{1}{2}`)
+ - :math:`4\xi\eta`
+ - (:math:`4\eta`, :math:`4\xi`)
+ * - 6
+ - (:math:`0`, :math:`\frac{1}{2}`)
+ - :math:`4\eta\left(1-\xi-\eta\right)`
+ - (:math:`-4\eta`, :math:`4\left(1-\xi-2\eta\right)`)
+
+.. list-table:: Gaussian quadrature points
+ :align: center
+
+
+ * - Coord. (:math:`\xi`, :math:`\eta`)
+ - Weight
+ * - (:math:`\frac{1}{6}`, :math:`\frac{1}{6}`)
+ - :math:`\frac{1}{6}`
+ * - (:math:`\frac{2}{3}`, :math:`\frac{1}{6}`)
+ - :math:`\frac{1}{6}`
+ * - (:math:`\frac{1}{6}`, :math:`\frac{2}{3}`)
+ - :math:`\frac{1}{6}`
+
+Quadrangle 4
+''''''''''''
+
+.. list-table:: Elements properties
+ :header-rows: 1
+
+ * - Node (:math:`i`)
+ - Coord. (:math:`\xi`, :math:`\eta`)
+ - Shape function (:math:`N_i`)
+ - Derivative (:math:`\frac{\partial N_i}{\partial \xi}`, :math:`\frac{\partial N_i}{\partial \eta}`)
+ * - 1
+ - (:math:`-1`, :math:`-1`)
+ - :math:`\frac{1}{4}\left(1-\xi\right)\left(1-\eta\right)`
+ - (:math:`-\frac{1}{4}\left(1-\eta\right)`, :math:`-\frac{1}{4}\left(1-\xi\right)`)
+ * - 2
+ - (:math:`1`, :math:`-1`)
+ - :math:`\frac{1}{4}\left(1+\xi\right)\left(1-\eta\right)`
+ - (:math:`\frac{1}{4}\left(1-\eta\right)`, :math:`-\frac{1}{4}\left(1+\xi\right)`)
+ * - 3
+ - (:math:`1`, :math:`1`)
+ - :math:`\frac{1}{4}\left(1+\xi\right)\left(1+\eta\right)`
+ - (:math:`\frac{1}{4}\left(1+\eta\right)`, :math:`\frac{1}{4}\left(1+\xi\right)`)
+ * - 4
+ - (:math:`-1`, :math:`1`)
+ - :math:`\frac{1}{4}\left(1-\xi\right)\left(1+\eta\right)`
+ - (:math:`-\frac{1}{4}\left(1+\eta\right)`, :math:`\frac{1}{4}\left(1-\xi\right)`)
+
+.. list-table:: Gaussian quadrature points
+ :align: center
+
+ * - Coord. (:math:`\xi`, :math:`\eta`)
+ - Weight
+ * - (:math:`-\frac{1}{\sqrt{3}}`, :math:`-\frac{1}{\sqrt{3}}`)
+ - 1
+ * - (:math:`\frac{1}{\sqrt{3}}`, :math:`-\frac{1}{\sqrt{3}}`)
+ - 1
+ * - (:math:`\frac{1}{\sqrt{3}}`, :math:`\frac{1}{\sqrt{3}}`)
+ - 1
+ * - (:math:`-\frac{1}{\sqrt{3}}`, :math:`\frac{1}{\sqrt{3}}`)
+ - 1
+
+Quadrangle 8
+''''''''''''
+
+.. list-table:: Elements properties
+ :header-rows: 1
+
+ * - Node (:math:`i`)
+ - Coord. (:math:`\xi`, :math:`\eta`)
+ - Shape function (:math:`N_i`)
+ - Derivative (:math:`\frac{\partial N_i}{\partial \xi}`, :math:`\frac{\partial N_i}{\partial \eta}`)
+ * - 1
+ - (:math:`-1`, :math:`-1`)
+ - :math:`\frac{1}{4}\left(1-\xi\right)\left(1-\eta\right)\left(-1-\xi-\eta\right)`
+ - (:math:`\frac{1}{4}\left(1-\eta\right)\left(2\xi+\eta\right)`, :math:`\frac{1}{4}\left(1-\xi\right)\left(\xi+2\eta\right)`)
+ * - 2
+ - (:math:`1`, :math:`-1`)
+ - :math:`\frac{1}{4}\left(1+\xi\right)\left(1-\eta\right)\left(-1+\xi-\eta\right)`
+ - (:math:`\frac{1}{4}\left(1-\eta\right)\left(2\xi-\eta\right)`, :math:`-\frac{1}{4}\left(1+\xi\right)\left(\xi-2\eta\right)`)
+ * - 3
+ - (:math:`1`, :math:`1`)
+ - :math:`\frac{1}{4}\left(1+\xi\right)\left(1+\eta\right)\left(-1+\xi+\eta\right)`
+ - (:math:`\frac{1}{4}\left(1+\eta\right)\left(2\xi+\eta\right)`, :math:`\frac{1}{4}\left(1+\xi\right)\left(\xi+2\eta\right)`)
+ * - 4
+ - (:math:`-1`, :math:`1`)
+ - :math:`\frac{1}{4}\left(1-\xi\right)\left(1+\eta\right)\left(-1-\xi+\eta\right)`
+ - (:math:`\frac{1}{4}\left(1+\eta\right)\left(2\xi-\eta\right)`, :math:`-\frac{1}{4}\left(1-\xi\right)\left(\xi-2\eta\right)`)
+ * - 5
+ - (:math:`0`, :math:`-1`)
+ - :math:`\frac{1}{2}\left(1-\xi^{2}\right)\left(1-\eta\right)`
+ - (:math:`-\xi\left(1-\eta\right)`, :math:`-\frac{1}{2}\left(1-\xi^{2}\right)`)
+ * - 6
+ - (:math:`1`, :math:`0`)
+ - :math:`\frac{1}{2}\left(1+\xi\right)\left(1-\eta^{2}\right)`
+ - (:math:`\frac{1}{2}\left(1-\eta^{2}\right)`, :math:`-\eta\left(1+\xi\right)`)
+ * - 7
+ - (:math:`0`, :math:`1`)
+ - :math:`\frac{1}{2}\left(1-\xi^{2}\right)\left(1+\eta\right)`
+ - (:math:`-\xi\left(1+\eta\right)`, :math:`\frac{1}{2}\left(1-\xi^{2}\right)`)
+ * - 8
+ - (:math:`-1`, :math:`0`)
+ - :math:`\frac{1}{2}\left(1-\xi\right)\left(1-\eta^{2}\right)`
+ - (:math:`-\frac{1}{2}\left(1-\eta^{2}\right)`, :math:`-\eta\left(1-\xi\right)`)
+
+.. list-table:: Gaussian quadrature points
+ :align: center
+
+ * - Coord. (:math:`\xi`, :math:`\eta`)
+ - Weight
+ * - (:math:`0`, :math:`0`)
+ - :math:`\frac{64}{81}`
+ * - (:math:`\sqrt{\tfrac{3}{5}}`, :math:`\sqrt{\tfrac{3}{5}}`)
+ - :math:`\frac{25}{81}`
+ * - (:math:`-\sqrt{\tfrac{3}{5}}`, :math:`\sqrt{\tfrac{3}{5}}`)
+ - :math:`\frac{25}{81}`
+ * - (:math:`-\sqrt{\tfrac{3}{5}}`, :math:`-\sqrt{\tfrac{3}{5}}`)
+ - :math:`\frac{25}{81}`
+ * - (:math:`\sqrt{\tfrac{3}{5}}`, :math:`-\sqrt{\tfrac{3}{5}}`)
+ - :math:`\frac{25}{81}`
+ * - (:math:`0`, :math:`\sqrt{\tfrac{3}{5}}`)
+ - :math:`\frac{40}{81}`
+ * - (:math:`-\sqrt{\tfrac{3}{5}}`, :math:`0`)
+ - :math:`\frac{40}{81}`
+ * - (:math:`0`, :math:`-\sqrt{\tfrac{3}{5}}`)
+ - :math:`\frac{40}{81}`
+ * - (:math:`\sqrt{\tfrac{3}{5}}`, :math:`0`)
+ - :math:`\frac{40}{81}`
+
+
+3D-Shape Functions
+``````````````````
+
+Tetrahedron 4
+'''''''''''''
+
+.. list-table:: Elements properties
+ :header-rows: 1
+
+ * - Node (:math:`i`)
+ - Coord. (:math:`\xi`, :math:`\eta`, :math:`\zeta`)
+ - Shape function (:math:`N_i`)
+ - Derivative (:math:`\frac{\partial N_i}{\partial \xi}`, :math:`\frac{\partial N_i}{\partial \eta}`, :math:`\frac{\partial N_i}{\partial \zeta}`)
+ * - 1
+ - (:math:`0`, :math:`0`, :math:`0`)
+ - :math:`1-\xi-\eta-\zeta`
+ - (:math:`-1`, :math:`-1`, :math:`-1`)
+ * - 2
+ - (:math:`1`, :math:`0`, :math:`0`)
+ - :math:`\xi`
+ - (:math:`1`, :math:`0`, :math:`0`)
+ * - 3
+ - (:math:`0`, :math:`1`, :math:`0`)
+ - :math:`\eta`
+ - (:math:`0`, :math:`1`, :math:`0`)
+ * - 4
+ - (:math:`0`, :math:`0`, :math:`1`)
+ - :math:`\zeta`
+ - (:math:`0`, :math:`0`, :math:`1`)
+
+.. list-table:: Gaussian quadrature points
+ :align: center
+
+ * - Coord. (:math:`\xi`, :math:`\eta`, :math:`\zeta`)
+ - Weight
+ * - (:math:`\frac{1}{4}`, :math:`\frac{1}{4}`, :math:`\frac{1}{4}`)
+ - :math:`\frac{1}{6}`
+
+Tetrahedron 10
+''''''''''''''
+
+.. list-table:: Elements properties
+ :header-rows: 1
+
+ * - Node (:math:`i`)
+ - Coord. (:math:`\xi`, :math:`\eta`, :math:`\zeta`)
+ - Shape function (:math:`N_i`)
+ - Derivative (:math:`\frac{\partial N_i}{\partial \xi}`, :math:`\frac{\partial N_i}{\partial \eta}`, :math:`\frac{\partial N_i}{\partial \zeta}`)
+ * - 1
+ - (:math:`0`, :math:`0`, :math:`0`)
+ - :math:`\left(1-\xi-\eta-\zeta\right)\left(1-2\xi-2\eta-2\zeta\right)`
+ - :math:`4\xi+4\eta+4\zeta-3`, :math:`4\xi+4\eta+4\zeta-3`, :math:`4\xi+4\eta+4\zeta-3`
+ * - 2
+ - (:math:`1`, :math:`0`, :math:`0`)
+ - :math:`\xi\left(2\xi-1\right)`
+ - (:math:`4\xi-1`, :math:`0`, :math:`0`)
+ * - 3
+ - (:math:`0`, :math:`1`, :math:`0`)
+ - :math:`\eta\left(2\eta-1\right)`
+ - (:math:`0`, :math:`4\eta-1`, :math:`0`)
+ * - 4
+ - (:math:`0`, :math:`0`, :math:`1`)
+ - :math:`\zeta\left(2\zeta-1\right)`
+ - (:math:`0`, :math:`0`, :math:`4\zeta-1`)
+ * - 5
+ - (:math:`\frac{1}{2}`, :math:`0`, :math:`0`)
+ - :math:`4\xi\left(1-\xi-\eta-\zeta\right)`
+ - (:math:`4-8\xi-4\eta-4\zeta`, :math:`-4\xi`, :math:`-4\xi`)
+ * - 6
+ - (:math:`\frac{1}{2}`, :math:`\frac{1}{2}`, :math:`0`)
+ - :math:`4\xi\eta`
+ - (:math:`4\eta`, :math:`4\xi`, :math:`0`)
+ * - 7
+ - (:math:`0`, :math:`\frac{1}{2}`, :math:`0`)
+ - :math:`4\eta\left(1-\xi-\eta-\zeta\right)`
+ - (:math:`-4\eta`, :math:`4-4\xi-8\eta-4\zeta`, :math:`-4\eta`)
+ * - 8
+ - (:math:`0`, :math:`0`, :math:`\frac{1}{2}`)
+ - :math:`4\zeta\left(1-\xi-\eta-\zeta\right)`
+ - (:math:`-4\zeta`, :math:`-4\zeta`, :math:`4-4\xi-4\eta-8\zeta`)
+ * - 9
+ - (:math:`\frac{1}{2}`, :math:`0`, :math:`\frac{1}{2}`)
+ - :math:`4\xi\zeta`
+ - (:math:`4\zeta`, :math:`0`, :math:`4\xi`)
+ * - 10
+ - (:math:`0`, :math:`\frac{1}{2}`, :math:`\frac{1}{2}`)
+ - :math:`4\eta\zeta`
+ - (:math:`0`, :math:`4\zeta`, :math:`4\eta`)
+
+.. list-table:: Gaussian quadrature points
+ :align: center
+
+ * - Coord. (:math:`\xi`, :math:`\eta`, :math:`\zeta`)
+ - Weight
+ * - (:math:`\frac{5-\sqrt{5}}{20}`, :math:`\frac{5-\sqrt{5}}{20}`, :math:`\frac{5-\sqrt{5}}{20}`)
+ - :math:`\frac{1}{24}`
+ * - (:math:`\frac{5+3\sqrt{5}}{20}`, :math:`\frac{5-\sqrt{5}}{20}`, :math:`\frac{5-\sqrt{5}}{20}`)
+ - :math:`\frac{1}{24}`
+ * - (:math:`\frac{5-\sqrt{5}}{20}`, :math:`\frac{5+3\sqrt{5}}{20}`, :math:`\frac{5-\sqrt{5}}{20}`)
+ - :math:`\frac{1}{24}`
+ * - (:math:`\frac{5-\sqrt{5}}{20}`, :math:`\frac{5-\sqrt{5}}{20}`, :math:`\frac{5+3\sqrt{5}}{20}`)
+ - :math:`\frac{1}{24}`
+
+Hexahedron 8
+''''''''''''
+
+.. list-table:: Elements properties
+ :header-rows: 1
+
+ * - Node (:math:`i`)
+ - Coord. (:math:`\xi`, :math:`\eta`, :math:`\zeta`)
+ - Shape function (:math:`N_i`)
+ - Derivative (:math:`\frac{\partial N_i}{\partial \xi}`, :math:`\frac{\partial N_i}{\partial \eta}`, :math:`\frac{\partial N_i}{\partial \zeta}`)
+ * - 1
+ - (:math:`-1`, :math:`-1`, :math:`-1`)
+ - :math:`\frac{1}{8}\left(1-\xi\right)\left(1-\eta\right)\left(1-\zeta\right)`
+ - (:math:`-\frac{1}{8}\left(1-\eta\right)\left(1-\zeta\right)`, :math:`-\frac{1}{8}\left(1-\xi\right)\left(1-\zeta\right)`, :math:`3`)
+ * - 2
+ - (:math:`1`, :math:`-1`, :math:`-1`)
+ - :math:`\frac{1}{8}\left(1+\xi\right)\left(1-\eta\right)\left(1-\zeta\right)`
+ - (:math:`\frac{1}{8}\left(1-\eta\right)\left(1-\zeta\right)`, :math:`-\frac{1}{8}\left(1+\xi\right)\left(1-\zeta\right)`, :math:`3`)
+ * - 3
+ - (:math:`1`, :math:`1`, :math:`-1`)
+ - :math:`\frac{1}{8}\left(1+\xi\right)\left(1+\eta\right)\left(1-\zeta\right)`
+ - (:math:`\frac{1}{8}\left(1+\eta\right)\left(1-\zeta\right)`, :math:`\frac{1}{8}\left(1+\xi\right)\left(1-\zeta\right)`, :math:`3`)
+ * - 4
+ - (:math:`-1`, :math:`1`, :math:`-1`)
+ - :math:`\frac{1}{8}\left(1-\xi\right)\left(1+\eta\right)\left(1-\zeta\right)`
+ - (:math:`-\frac{1}{8}\left(1+\eta\right)\left(1-\zeta\right)`, :math:`\frac{1}{8}\left(1-\xi\right)\left(1-\zeta\right)`, :math:`3`)
+ * - 5
+ - (:math:`-1`, :math:`-1`, :math:`1`)
+ - :math:`\frac{1}{8}\left(1-\xi\right)\left(1-\eta\right)\left(1+\zeta\right)`
+ - (:math:`-\frac{1}{8}\left(1-\eta\right)\left(1+\zeta\right)`, :math:`-\frac{1}{8}\left(1-\xi\right)\left(1+\zeta\right)`, :math:`3`)
+ * - 6
+ - (:math:`1`, :math:`-1`, :math:`1`)
+ - :math:`\frac{1}{8}\left(1+\xi\right)\left(1-\eta\right)\left(1+\zeta\right)`
+ - (:math:`\frac{1}{8}\left(1-\eta\right)\left(1+\zeta\right)`, :math:`-\frac{1}{8}\left(1+\xi\right)\left(1+\zeta\right)`, :math:`3`)
+ * - 7
+ - (:math:`1`, :math:`1`, :math:`1`)
+ - :math:`\frac{1}{8}\left(1+\xi\right)\left(1+\eta\right)\left(1+\zeta\right)`
+ - (:math:`\frac{1}{8}\left(1+\eta\right)\left(1+\zeta\right)`, :math:`\frac{1}{8}\left(1+\xi\right)\left(1+\zeta\right)`, :math:`3`)
+ * - 8
+ - (:math:`-1`, :math:`1`, :math:`1`)
+ - :math:`\frac{1}{8}\left(1-\xi\right)\left(1+\eta\right)\left(1+\zeta\right)`
+ - (:math:`-\frac{1}{8}\left(1+\eta\right)\left(1+\zeta\right)`, :math:`\frac{1}{8}\left(1-\xi\right)\left(1+\zeta\right)`, :math:`3`)
+
+.. list-table:: Gaussian quadrature points
+ :align: center
+
+ * - Coord. (:math:`\xi`, :math:`\eta`, :math:`\zeta`)
+ - Weight
+ * - (:math:`-\frac{1}{\sqrt{3}}`, :math:`-\frac{1}{\sqrt{3}}`, :math:`-\frac{1}{\sqrt{3}}`)
+ - 1
+ * - (:math:`\frac{1}{\sqrt{3}}`, :math:`-\frac{1}{\sqrt{3}}`, :math:`-\frac{1}{\sqrt{3}}`)
+ - 1
+ * - (:math:`\frac{1}{\sqrt{3}}`, :math:`\frac{1}{\sqrt{3}}`, :math:`-\frac{1}{\sqrt{3}}`)
+ - 1
+ * - (:math:`-\frac{1}{\sqrt{3}}`, :math:`\frac{1}{\sqrt{3}}`, :math:`-\frac{1}{\sqrt{3}}`)
+ - 1
+ * - (:math:`-\frac{1}{\sqrt{3}}`, :math:`-\frac{1}{\sqrt{3}}`, :math:`\frac{1}{\sqrt{3}}`)
+ - 1
+ * - (:math:`\frac{1}{\sqrt{3}}`, :math:`-\frac{1}{\sqrt{3}}`, :math:`\frac{1}{\sqrt{3}}`)
+ - 1
+ * - (:math:`\frac{1}{\sqrt{3}}`, :math:`\frac{1}{\sqrt{3}}`, :math:`\frac{1}{\sqrt{3}}`)
+ - 1
+ * - (:math:`-\frac{1}{\sqrt{3}}`, :math:`\frac{1}{\sqrt{3}}`, :math:`\frac{1}{\sqrt{3}}`)
+ - 1
+
+Pentahedron 6
+'''''''''''''
+
+.. list-table:: Elements properties
+ :header-rows: 1
+
+ * - Node (:math:`i`)
+ - Coord. (:math:`\xi`, :math:`\eta`, :math:`\zeta`)
+ - Shape function (:math:`N_i`)
+ - Derivative (:math:`\frac{\partial N_i}{\partial \xi}`, :math:`\frac{\partial N_i}{\partial \eta}`, :math:`\frac{\partial N_i}{\partial \zeta}`)
+ * - 1
+ - (:math:`-1`, :math:`1`, :math:`0`)
+ - :math:`\frac{1}{2}\left(1-\xi\right)\eta`
+
+ - (:math:`-\frac{1}{2}\eta`, :math:`\frac{1}{2}\left(1-\xi\right)`, :math:`3`)
+ * - 2
+ - (:math:`-1`, :math:`0`, :math:`1`)
+ - :math:`\frac{1}{2}\left(1-\xi\right)\zeta`
+
+ - (:math:`-\frac{1}{2}\zeta`, :math:`0.0`, :math:`3`)
+ * - 3
+ - (:math:`-1`, :math:`0`, :math:`0`)
+ - :math:`\frac{1}{2}\left(1-\xi\right)\left(1-\eta-\zeta\right)`
+
+ - (:math:`-\frac{1}{2}\left(1-\eta-\zeta\right)`, :math:`-\frac{1}{2}\left(1-\xi\right)`, :math:`3`)
+ * - 4
+ - (:math:`1`, :math:`1`, :math:`0`)
+ - :math:`\frac{1}{2}\left(1+\xi\right)\eta`
+
+ - (:math:`\frac{1}{2}\eta`, :math:`\frac{1}{2}\left(1+\xi\right)`, :math:`3`)
+ * - 5
+ - (:math:`1`, :math:`0`, :math:`1`)
+ - :math:`\frac{1}{2}\left(1+\xi\right)\zeta`
+
+ - (:math:`\frac{1}{2}\zeta`, :math:`0.0`, :math:`3`)
+ * - 6
+ - (:math:`1`, :math:`0`, :math:`0`)
+ - :math:`\frac{1}{2}\left(1+\xi\right)\left(1-\eta-\zeta\right)`
+
+ - (:math:`\frac{1}{2}\left(1-\eta-\zeta\right)`, :math:`-\frac{1}{2}\left(1+\xi\right)`, :math:`3`)
+
+.. list-table:: Gaussian quadrature points
+ :align: center
+
+ * - Coord. (:math:`\xi`, :math:`\eta`, :math:`\zeta`)
+ - Weight
+ * - (:math:`-\frac{1}{\sqrt{3}}`, :math:`0.5`, :math:`0.5`)
+ - :math:`\frac{1}{6}`
+ * - (:math:`-\frac{1}{\sqrt{3}}`, :math:`0.0`, :math:`0.5`)
+ - :math:`\frac{1}{6}`
+ * - (:math:`-\frac{1}{\sqrt{3}}`, :math:`0.5`, :math:`0.0`)
+ - :math:`\frac{1}{6}`
+ * - (:math:`\frac{1}{\sqrt{3}}`, :math:`0.5`, :math:`0.5`)
+ - :math:`\frac{1}{6}`
+ * - (:math:`\frac{1}{\sqrt{3}}`, :math:`0.0`, :math:`0.5`)
+ - :math:`\frac{1}{6}`
+ * - (:math:`\frac{1}{\sqrt{3}}`, :math:`0.5`, :math:`0.0`)
+ - :math:`\frac{1}{6}`
+
+Hexahedron 20
+'''''''''''''
+
+.. list-table:: Elements properties
+ :header-rows: 1
+
+ * - Node (:math:`i`)
+ - Coord. (:math:`\xi`, :math:`\eta`, :math:`\zeta`)
+ - Shape function (:math:`N_i`)
+ - Derivative (:math:`\frac{\partial N_i}{\partial \xi}`, :math:`\frac{\partial N_i}{\partial \eta}`, :math:`\frac{\partial N_i}{\partial \zeta}`)
+ * - 1
+ - (:math:`-1`, :math:`-1`, :math:`-1`)
+ - :math:`\frac{1}{8}\left(1-\xi\right)\left(1-\eta\right)\left(1-\zeta\right)\left(-2-\xi-\eta-\zeta\right)`
+ - (:math:`\frac{1}{4}\left(\xi+\frac{1}{2}\left(\eta+\zeta+1\right)\right)\left(\eta-1\right)\left(\zeta-1\right)`, :math:`\frac{1}{4}\left(\eta+\frac{1}{2}\left(\xi+\zeta+1\right)\right)\left(\xi-1\right)\left(\zeta-1\right)`, :math:`3`)
+ * - 2
+ - (:math:`1`, :math:`-1`, :math:`-1`)
+ - :math:`\frac{1}{8}\left(1+\xi\right)\left(1-\eta\right)\left(1-\zeta\right)\left(-2+\xi-\eta-\zeta\right)`
+ - (:math:`\frac{1}{4}\left(\xi-\frac{1}{2}\left(\eta+\zeta+1\right)\right)\left(\eta-1\right)\left(\zeta-1\right)`, :math:`-\frac{1}{4}\left(\eta-\frac{1}{2}\left(\xi-\zeta-1\right)\right)\left(\xi+1\right)\left(\zeta-1\right)`, :math:`3`)
+ * - 3
+ - (:math:`1`, :math:`1`, :math:`-1`)
+ - :math:`\frac{1}{8}\left(1+\xi\right)\left(1+\eta\right)\left(1-\zeta\right)\left(-2+\xi+\eta-\zeta\right)`
+ - (:math:`-\frac{1}{4}\left(\xi+\frac{1}{2}\left(\eta-\zeta-1\right)\right)\left(\eta+1\right)\left(\zeta-1\right)`, :math:`-\frac{1}{4}\left(\eta+\frac{1}{2}\left(\xi-\zeta-1\right)\right)\left(\xi+1\right)\left(\zeta-1\right)`, :math:`3`)
+ * - 4
+ - (:math:`-1`, :math:`1`, :math:`-1`)
+ - :math:`\frac{1}{8}\left(1-\xi\right)\left(1+\eta\right)\left(1-\zeta\right)\left(-2-\xi+\eta-\zeta\right)`
+ - (:math:`-\frac{1}{4}\left(\xi-\frac{1}{2}\left(\eta-\zeta-1\right)\right)\left(\eta+1\right)\left(\zeta-1\right)`, :math:`\frac{1}{4}\left(\eta-\frac{1}{2}\left(\xi+\zeta+1\right)\right)\left(\xi-1\right)\left(\zeta-1\right)`, :math:`3`)
+ * - 5
+ - (:math:`-1`, :math:`-1`, :math:`1`)
+ - :math:`\frac{1}{8}\left(1-\xi\right)\left(1-\eta\right)\left(1+\zeta\right)\left(-2-\xi-\eta+\zeta\right)`
+ - (:math:`-\frac{1}{4}\left(\xi+\frac{1}{2}\left(\eta-\zeta+1\right)\right)\left(\eta-1\right)\left(\zeta+1\right)`, :math:`-\frac{1}{4}\left(\eta+\frac{1}{2}\left(\xi-\zeta+1\right)\right)\left(\xi-1\right)\left(\zeta+1\right)`, :math:`3`)
+ * - 6
+ - (:math:`1`, :math:`-1`, :math:`1`)
+ - :math:`\frac{1}{8}\left(1+\xi\right)\left(1-\eta\right)\left(1+\zeta\right)\left(-2+\xi-\eta+\zeta\right)`
+ - (:math:`-\frac{1}{4}\left(\xi-\frac{1}{2}\left(\eta-\zeta+1\right)\right)\left(\eta-1\right)\left(\zeta+1\right)`, :math:`\frac{1}{4}\left(\eta-\frac{1}{2}\left(\xi+\zeta-1\right)\right)\left(\xi+1\right)\left(\zeta+1\right)`, :math:`3`)
+ * - 7
+ - (:math:`1`, :math:`1`, :math:`1`)
+ - :math:`\frac{1}{8}\left(1+\xi\right)\left(1+\eta\right)\left(1+\zeta\right)\left(-2+\xi+\eta+\zeta\right)`
+ - (:math:`\frac{1}{4}\left(\xi+\frac{1}{2}\left(\eta+\zeta-1\right)\right)\left(\eta+1\right)\left(\zeta+1\right)`, :math:`\frac{1}{4}\left(\eta+\frac{1}{2}\left(\xi+\zeta-1\right)\right)\left(\xi+1\right)\left(\zeta+1\right)`, :math:`3`)
+ * - 8
+ - (:math:`-1`, :math:`1`, :math:`1`)
+ - :math:`\frac{1}{8}\left(1-\xi\right)\left(1+\eta\right)\left(1+\zeta\right)\left(-2-\xi+\eta+\zeta\right)`
+ - (:math:`\frac{1}{4}\left(\xi-\frac{1}{2}\left(\eta+\zeta-1\right)\right)\left(\eta+1\right)\left(\zeta+1\right)`, :math:`-\frac{1}{4}\left(\eta-\frac{1}{2}\left(\xi-\zeta+1\right)\right)\left(\xi-1\right)\left(\zeta+1\right)`, :math:`3`)
+ * - 9
+ - (:math:`0`, :math:`-1`, :math:`-1`)
+ - :math:`\frac{1}{4}\left(1-\xi^{2}\right)\left(1-\eta\right)\left(1-\zeta\right)`
+ - (:math:`-\frac{1}{2}\xi\left(\eta-1\right)\left(\zeta-1\right)`, :math:`-\frac{1}{4}\left(\xi^{2}-1\right)\left(\zeta-1\right)`, :math:`3`)
+ * - 10
+ - (:math:`1`, :math:`0`, :math:`-1`)
+ - :math:`\frac{1}{4}\left(1+\xi\right)\left(1-\eta^{2}\right)\left(1-\zeta\right)`
+ - (:math:`\frac{1}{4}\left(\eta^{2}-1\right)\left(\zeta-1\right)`, :math:`\frac{1}{2}\eta\left(\xi+1\right)\left(\zeta-1\right)`, :math:`3`)
+ * - 11
+ - (:math:`0`, :math:`1`, :math:`-1`)
+ - :math:`\frac{1}{4}\left(1-\xi^{2}\right)\left(1+\eta\right)\left(1-\zeta\right)`
+ - (:math:`\frac{1}{2}\xi\left(\eta+1\right)\left(\zeta-1\right)`, :math:`\frac{1}{4}\left(\xi^{2}-1\right)\left(\zeta-1\right)`, :math:`3`)
+ * - 12
+ - (:math:`-1`, :math:`0`, :math:`-1`)
+ - :math:`\frac{1}{4}\left(1-\xi\right)\left(1-\eta^{2}\right)\left(1-\zeta\right)`
+ - (:math:`-\frac{1}{4}\left(\eta^{2}-1\right)\left(\zeta-1\right)`, :math:`-\frac{1}{2}\eta\left(\xi-1\right)\left(\zeta-1\right)`, :math:`3`)
+ * - 13
+ - (:math:`-1`, :math:`-1`, :math:`0`)
+ - :math:`\frac{1}{4}\left(1-\xi\right)\left(1-\eta\right)\left(1-\zeta^{2}\right)`
+ - (:math:`-\frac{1}{4}\left(\eta-1\right)\left(\zeta^{2}-1\right)`, :math:`-\frac{1}{4}\left(\xi-1\right)\left(\zeta^{2}-1\right)`, :math:`3`)
+ * - 14
+ - (:math:`1`, :math:`-1`, :math:`0`)
+ - :math:`\frac{1}{4}\left(1+\xi\right)\left(1-\eta\right)\left(1-\zeta^{2}\right)`
+ - (:math:`\frac{1}{4}\left(\eta-1\right)\left(\zeta^{2}-1\right)`, :math:`\frac{1}{4}\left(\xi+1\right)\left(\zeta^{2}-1\right)`, :math:`3`)
+ * - 15
+ - (:math:`1`, :math:`1`, :math:`0`)
+ - :math:`\frac{1}{4}\left(1+\xi\right)\left(1+\eta\right)\left(1-\zeta^{2}\right)`
+ - (:math:`-\frac{1}{4}\left(\eta+1\right)\left(\zeta^{2}-1\right)`, :math:`-\frac{1}{4}\left(\xi+1\right)\left(\zeta^{2}-1\right)`, :math:`3`)
+ * - 16
+ - (:math:`-1`, :math:`1`, :math:`0`)
+ - :math:`\frac{1}{4}\left(1-\xi\right)\left(1+\eta\right)\left(1-\zeta^{2}\right)`
+ - (:math:`\frac{1}{4}\left(\eta+1\right)\left(\zeta^{2}-1\right)`, :math:`\frac{1}{4}\left(\xi-1\right)\left(\zeta^{2}-1\right)`, :math:`3`)
+ * - 17
+ - (:math:`0`, :math:`-1`, :math:`1`)
+ - :math:`\frac{1}{4}\left(1-\xi^{2}\right)\left(1-\eta\right)\left(1+\zeta\right)`
+ - (:math:`\frac{1}{2}\xi\left(\eta-1\right)\left(\zeta+1\right)`, :math:`\frac{1}{4}\left(\xi^{2}-1\right)\left(\zeta+1\right)`, :math:`3`)
+ * - 18
+ - (:math:`1`, :math:`0`, :math:`1`)
+ - :math:`\frac{1}{4}\left(1+\xi\right)\left(1-\eta^{2}\right)\left(1+\zeta\right)`
+ - (:math:`-\frac{1}{4}\left(\eta^{2}-1\right)\left(\zeta+1\right)`, :math:`-\frac{1}{2}\eta\left(\xi+1\right)\left(\zeta+1\right)`, :math:`3`)
+ * - 19
+ - (:math:`0`, :math:`1`, :math:`1`)
+ - :math:`\frac{1}{4}\left(1-\xi^{2}\right)\left(1+\eta\right)\left(1+\zeta\right)`
+ - (:math:`-\frac{1}{2}\xi\left(\eta+1\right)\left(\zeta+1\right)`, :math:`-\frac{1}{4}\left(\xi^{2}-1\right)\left(\zeta+1\right)`, :math:`3`)
+ * - 20
+ - (:math:`-1`, :math:`0`, :math:`1`)
+ - :math:`\frac{1}{4}\left(1-\xi\right)\left(1-\eta^{2}\right)\left(1+\zeta\right)`
+ - (:math:`\frac{1}{4}\left(\eta^{2}-1\right)\left(\zeta+1\right)`, :math:`\frac{1}{2}\eta\left(\xi-1\right)\left(\zeta+1\right)`, :math:`3`)
+
+.. list-table:: Gaussian quadrature points
+ :align: center
+
+ * - Coord. (:math:`\xi`, :math:`\eta`, :math:`\zeta`)
+ - Weight
+ * - (:math:`-\sqrt{\tfrac{3}{5}}`, :math:`-\sqrt{\tfrac{3}{5}}`, :math:`-\sqrt{\tfrac{3}{5}}`)
+ - :math:`\frac{125}{729}`
+ * - (:math:`-\sqrt{\tfrac{3}{5}}`, :math:`-\sqrt{\tfrac{3}{5}}`, :math:`0`)
+ - :math:`\frac{200}{729}`
+ * - (:math:`-\sqrt{\tfrac{3}{5}}`, :math:`-\sqrt{\tfrac{3}{5}}`, :math:`\sqrt{\tfrac{3}{5}}`)
+ - :math:`\frac{125}{729}`
+ * - (:math:`-\sqrt{\tfrac{3}{5}}`, :math:`0`, :math:`-\sqrt{\tfrac{3}{5}}`)
+ - :math:`\frac{200}{729}`
+ * - (:math:`-\sqrt{\tfrac{3}{5}}`, :math:`0`, :math:`0`)
+ - :math:`\frac{320}{729}`
+ * - (:math:`-\sqrt{\tfrac{3}{5}}`, :math:`0`, :math:`\sqrt{\tfrac{3}{5}}`)
+ - :math:`\frac{200}{729}`
+ * - (:math:`-\sqrt{\tfrac{3}{5}}`, :math:`\sqrt{\tfrac{3}{5}}`, :math:`-\sqrt{\tfrac{3}{5}}`)
+ - :math:`\frac{125}{729}`
+ * - (:math:`-\sqrt{\tfrac{3}{5}}`, :math:`\sqrt{\tfrac{3}{5}}`, :math:`0`)
+ - :math:`\frac{200}{729}`
+ * - (:math:`-\sqrt{\tfrac{3}{5}}`, :math:`\sqrt{\tfrac{3}{5}}`, :math:`\sqrt{\tfrac{3}{5}}`)
+ - :math:`\frac{125}{729}`
+ * - (:math:`0`, :math:`-\sqrt{\tfrac{3}{5}}`, :math:`-\sqrt{\tfrac{3}{5}}`)
+ - :math:`\frac{200}{729}`
+ * - (:math:`0`, :math:`-\sqrt{\tfrac{3}{5}}`, :math:`0`)
+ - :math:`\frac{320}{729}`
+ * - (:math:`0`, :math:`-\sqrt{\tfrac{3}{5}}`, :math:`\sqrt{\tfrac{3}{5}}`)
+ - :math:`\frac{200}{729}`
+ * - (:math:`0`, :math:`0`, :math:`-\sqrt{\tfrac{3}{5}}`)
+ - :math:`\frac{320}{729}`
+ * - (:math:`0`, :math:`0`, :math:`0`)
+ - :math:`\frac{512}{729}`
+ * - (:math:`0`, :math:`0`, :math:`\sqrt{\tfrac{3}{5}}`)
+ - :math:`\frac{320}{729}`
+ * - (:math:`0`, :math:`\sqrt{\tfrac{3}{5}}`, :math:`-\sqrt{\tfrac{3}{5}}`)
+ - :math:`\frac{200}{729}`
+ * - (:math:`0`, :math:`\sqrt{\tfrac{3}{5}}`, :math:`0`)
+ - :math:`\frac{320}{729}`
+ * - (:math:`0`, :math:`\sqrt{\tfrac{3}{5}}`, :math:`\sqrt{\tfrac{3}{5}}`)
+ - :math:`\frac{200}{729}`
+ * - (:math:`\sqrt{\tfrac{3}{5}}`, :math:`-\sqrt{\tfrac{3}{5}}`, :math:`-\sqrt{\tfrac{3}{5}}`)
+ - :math:`\frac{125}{729}`
+ * - (:math:`\sqrt{\tfrac{3}{5}}`, :math:`-\sqrt{\tfrac{3}{5}}`, :math:`0`)
+ - :math:`\frac{200}{729}`
+ * - (:math:`\sqrt{\tfrac{3}{5}}`, :math:`-\sqrt{\tfrac{3}{5}}`, :math:`\sqrt{\tfrac{3}{5}}`)
+ - :math:`\frac{125}{729}`
+ * - (:math:`\sqrt{\tfrac{3}{5}}`, :math:`0`, :math:`-\sqrt{\tfrac{3}{5}}`)
+ - :math:`\frac{200}{729}`
+ * - (:math:`\sqrt{\tfrac{3}{5}}`, :math:`0`, :math:`0`)
+ - :math:`\frac{320}{729}`
+ * - (:math:`\sqrt{\tfrac{3}{5}}`, :math:`0`, :math:`\sqrt{\tfrac{3}{5}}`)
+ - :math:`\frac{200}{729}`
+ * - (:math:`\sqrt{\tfrac{3}{5}}`, :math:`\sqrt{\tfrac{3}{5}}`, :math:`-\sqrt{\tfrac{3}{5}}`)
+ - :math:`\frac{125}{729}`
+ * - (:math:`\sqrt{\tfrac{3}{5}}`, :math:`\sqrt{\tfrac{3}{5}}`, :math:`0`)
+ - :math:`\frac{200}{729}`
+ * - (:math:`\sqrt{\tfrac{3}{5}}`, :math:`\sqrt{\tfrac{3}{5}}`, :math:`\sqrt{\tfrac{3}{5}}`)
+ - :math:`\frac{125}{729}`
+
+Pentahedron 15
+''''''''''''''
+
+.. list-table:: Elements properties
+ :header-rows: 1
+
+ * - Node (:math:`i`)
+ - Coord. (:math:`\xi`, :math:`\eta`, :math:`\zeta`)
+ - Shape function (:math:`N_i`)
+ - Derivative (:math:`\frac{\partial N_i}{\partial \xi}`, :math:`\frac{\partial N_i}{\partial \eta}`, :math:`\frac{\partial N_i}{\partial \zeta}`)
+ * - 1
+ - (:math:`-1`, :math:`1`, :math:`0`)
+ - :math:`\frac{1}{2}\eta\left(1-\xi\right)\left(2\eta-2-\xi\right)`
+ - (:math:`\frac{1}{2}\eta\left(2\xi-2\eta+1\right)`, :math:`-\frac{1}{2}\left(\xi-1\right)\left(4\eta-\xi-2\right)`, :math:`3`)
+ * - 2
+ - (:math:`-1`, :math:`0`, :math:`1`)
+ - :math:`\frac{1}{2}\zeta\left(1-\xi\right)\left(2\zeta-2-\xi\right)`
+ - (:math:`\frac{1}{2}\zeta\left(2\xi-2\zeta+1\right)`, :math:`0.0`, :math:`3`)
+ * - 3
+ - (:math:`-1`, :math:`0`, :math:`0`)
+ - :math:`\frac{1}{2}\left(\xi-1\right)\left(1-\eta-\zeta\right)\left(\xi+2\eta+2\zeta\right)`
+ - (:math:`-\frac{1}{2}\left(2\xi+2\eta+2\zeta-1\right)\left(\eta+\zeta-1\right)`, :math:`-\frac{1}{2}\left(\xi-1\right)\left(4\eta+\xi+2\left(2\zeta-1\right)\right)`, :math:`3`)
+ * - 4
+ - (:math:`1`, :math:`1`, :math:`0`)
+ - :math:`\frac{1}{2}\eta\left(1+\xi\right)\left(2\eta-2+\xi\right)`
+ - (:math:`\frac{1}{2}\eta\left(2\xi+2\eta-1\right)`, :math:`\frac{1}{2}\left(\xi+1\right)\left(4\eta+\xi-2\right)`, :math:`3`)
+ * - 5
+ - (:math:`1`, :math:`0`, :math:`1`)
+ - :math:`\frac{1}{2}\zeta\left(1+\xi\right)\left(2\zeta-2+\xi\right)`
+ - (:math:`\frac{1}{2}\zeta\left(2\xi+2\zeta-1\right)`, :math:`0.0`, :math:`3`)
+ * - 6
+ - (:math:`1`, :math:`0`, :math:`0`)
+ - :math:`\frac{1}{2}\left(-\xi-1\right)\left(1-\eta-\zeta\right)\left(-\xi+2\eta+2\zeta\right)`
+ - (:math:`-\frac{1}{2}\left(\eta+\zeta-1\right)\left(2\xi-2\eta-2\zeta+1\right)`, :math:`\frac{1}{2}\left(\xi+1\right)\left(4\eta-\xi+2\left(2\zeta-1\right)\right)`, :math:`3`)
+ * - 7
+ - (:math:`-1`, :math:`0.5`, :math:`0.5`)
+ - :math:`2\eta\zeta\left(1-\xi\right)`
+ - (:math:`-2\eta\zeta`, :math:`-2\left(\xi-1\right)\zeta`, :math:`3`)
+ * - 8
+ - (:math:`-1`, :math:`0`, :math:`0.5`)
+ - :math:`2\zeta\left(1-\eta-\zeta\right)\left(1-\xi\right)`
+ - (:math:`2\zeta\left(\eta+\zeta-1\right)`, :math:`2\zeta-\left(\xi-1\right)`, :math:`3`)
+ * - 9
+ - (:math:`-1`, :math:`0.5`, :math:`0`)
+ - :math:`2\eta\left(1-\xi\right)\left(1-\eta-\zeta\right)`
+ - (:math:`2\eta\left(\eta+\zeta-1\right)`, :math:`2\left(2\eta+\zeta-1\right)\left(\xi-1\right)`, :math:`3`)
+ * - 10
+ - (:math:`0`, :math:`1`, :math:`0`)
+ - :math:`\eta\left(1-\xi^{2}\right)`
+ - (:math:`-2\xi\eta`, :math:`-\left(\xi^{2}-1\right)`, :math:`3`)
+ * - 11
+ - (:math:`0`, :math:`0`, :math:`1`)
+ - :math:`\zeta\left(1-\xi^{2}\right)`
+ - (:math:`-2\xi\zeta`, :math:`0.0`, :math:`3`)
+ * - 12
+ - (:math:`0`, :math:`0`, :math:`0`)
+ - :math:`\left(1-\xi^{2}\right)\left(1-\eta-\zeta\right)`
+ - (:math:`2\xi\left(\eta+\zeta-1\right)`, :math:`\left(\xi^{2}-1\right)`, :math:`3`)
+ * - 13
+ - (:math:`1`, :math:`0.5`, :math:`0.5`)
+ - :math:`2\eta\zeta\left(1+\xi\right)`
+ - (:math:`2\eta\zeta`, :math:`2\zeta\left(\xi+1\right)`, :math:`3`)
+ * - 14
+ - (:math:`1`, :math:`0`, :math:`0.5`)
+ - :math:`2\zeta\left(1+\xi\right)\left(1-\eta-\zeta\right)`
+ - (:math:`-2\zeta\left(\eta+\zeta-1\right)`, :math:`-2\zeta\left(\xi+1\right)`, :math:`3`)
+ * - 15
+ - (:math:`1`, :math:`0.5`, :math:`0`)
+ - :math:`2\eta\left(1+\xi\right)\left(1-\eta-\zeta\right)`
+ - (:math:`-2\eta\left(\eta+\zeta-1\right)`, :math:`-2\left(2\eta+\zeta-1\right)\left(\xi+1\right)`, :math:`3`)
+
+.. list-table:: Gaussian quadrature points
+ :align: center
+
+ * - Coord. (:math:`\xi`, :math:`\eta`, :math:`\zeta`)
+ - Weight
+ * - (:math:`-{\tfrac{1}{\sqrt{3}}}`, :math:`\tfrac{1}{3}`, :math:`\tfrac{1}{3}`)
+ - -:math:`\frac{27}{96}`
+ * - (:math:`-{\tfrac{1}{\sqrt{3}}}`, :math:`0.6`, :math:`0.2`)
+ - :math:`\frac{25}{96}`
+ * - (:math:`-{\tfrac{1}{\sqrt{3}}}`, :math:`0.2`, :math:`0.6`)
+ - :math:`\frac{25}{96}`
+ * - (:math:`-{\tfrac{1}{\sqrt{3}}}`, :math:`0.2`, :math:`0.2`)
+ - :math:`\frac{25}{96}`
+ * - (:math:`{\tfrac{1}{\sqrt{3}}}`, :math:`\tfrac{1}{3}`, :math:`\tfrac{1}{3}`)
+ - -:math:`\frac{27}{96}`
+ * - (:math:`{\tfrac{1}{\sqrt{3}}}`, :math:`0.6`, :math:`0.2`)
+ - :math:`\frac{25}{96}`
+ * - (:math:`{\tfrac{1}{\sqrt{3}}}`, :math:`0.2`, :math:`0.6`)
+ - :math:`\frac{25}{96}`
+ * - (:math:`{\tfrac{1}{\sqrt{3}}}`, :math:`0.2`, :math:`0.2`)
+ - :math:`\frac{25}{96}`
diff --git a/doc/dev-doc/manual/appendix/material-parameters.rst b/doc/dev-doc/manual/appendix/material-parameters.rst
new file mode 100644
index 000000000..4ab43b393
--- /dev/null
+++ b/doc/dev-doc/manual/appendix/material-parameters.rst
@@ -0,0 +1,129 @@
+.. _app-material-parameters:
+
+Material Parameters
+===================
+
+Linear elastic isotropic
+------------------------
+
+Keyword: :ref:`elastic <sect-smm-linear-elastic-isotropic>`
+
+Parameters:
+
+- ``rho``: (*Real*) Density
+- ``E``: (*Real*) Young's modulus
+- ``nu``: (*Real*) Poisson's ratio
+- ``Plane_stress``: (*bool*) Plane stress simplification (only 2D problems)
+
+
+Linear elastic anisotropic
+--------------------------
+
+Keyword: :ref:`elastic_anisotropic <sect-smm-linear-elastic-anisotropic>`
+
+Parameters:
+
+- ``rho``: (*Real*) Density
+- ``n1``: (*Vector<Real>*) Direction of main material axis
+- ``n2``: (*Vector<Real>*) Direction of second material axis
+- ``n3``: (*Vector<Real>*) Direction of third material axis
+- ``C..``: (*Real*) Coefficient ij of material tensor C (all the 36 values in
+ Voigt notation can be entered)
+- ``alpha``: (*Real*) Viscous propertion (default is 0)
+
+
+Linear elastic anisotropic
+------------------------
+
+Keyword: :ref:`elastic_orthotropic <sect-smm-linear-elastic-orthotropic>`
+
+Parameters:
+
+- ``rho``: (*Real*) Density
+- ``n1``: (*Vector<Real>*) Direction of main material axis
+- ``n2``: (*Vector<Real>*) Direction of second material axis (if applicable)
+- ``n3``: (*Vector<Real>*) Direction of third material axis (if applicable)
+- ``E1``: (*Real*) Young's modulus (n1)
+- ``E2``: (*Real*) Young's modulus (n2)
+- ``E3``: (*Real*) Young's modulus (n3)
+- ``nu1``: (*Real*) Poisson's ratio (n1)
+- ``nu2``: (*Real*) Poisson's ratio (n2)
+- ``nu3``: (*Real*) Poisson's ratio (n3)
+- ``G12``: (*Real*) Shear modulus (12)
+- ``G13``: (*Real*) Shear modulus (13)
+- ``G23``: (*Real*) Shear modulus (23)
+
+
+Neohookean (finite strains)
+---------------------------
+
+Keyword: :ref:`neohookean <sect-smm-cl-neohookean>`
+
+Parameters:
+
+- ``rho``: (*Real*) Density
+- ``E``: (*Real*) Young's modulus
+- ``nu``: (*Real*) Poisson's ratio
+- ``Plane_stress``: (*bool*) Plane stress simplification (only 2D problems)
+
+
+Standard linear solid
+---------------------
+
+Keyword: :ref:`sls_deviatoric <sect-smm-cl-sls>`
+
+Parameters:
+
+- ``rho``: (*Real*) Density
+- ``E``: (*Real*) Young's modulus
+- ``nu``: (*Real*) Poisson's ratio
+- ``Plane_stress``: (*bool*) Plane stress simplification (only 2D problems)
+- ``Eta``: (*Real*) Viscosity
+- ``Ev``: (*Real*) Stiffness of viscous element
+
+
+Elasto-plastic linear isotropic hardening
+-----------------------------------------
+
+Keyword: :ref:`plastic_linear_isotropic_hardening <sect-smm-cl-plastic>`
+
+Parameters:
+
+- ``rho``: (*Real*) Density
+- ``E``: (*Real*) Young's modulus
+- ``nu``: (*Real*) Poisson's ratio
+- ``h``: (*Real*) Hardening modulus
+- ``sigma_y``: (*Real*) Yield stress
+
+
+Marigo
+------
+
+Keyword: :ref:`marigo <sect-smm-cl-damage-marigo>`
+
+Parameters:
+
+- ``rho``: (*Real*) Density
+- ``E``: (*Real*) Young's modulus
+- ``nu``: (*Real*) Poisson's ratio
+- ``Plane_stress``: (*bool*) Plane stress simplification (only 2D problems)
+- ``Yd``: (*Random*) Hardening modulus
+- ``Sd``: (*Real*) Damage energy
+
+
+Mazars
+------
+
+Keyword: :ref:`mazars <sect-smm-cl-damage-mazars>`
+
+Parameters:
+
+- ``rho``: (*Real*) Density
+- ``E``: (*Real*) Young's modulus
+- ``nu``: (*Real*) Poisson's ratio
+- ``At``: (*Real*) Traction post-peak asymptotic value
+- ``Bt``: (*Real*) Traction decay shape
+- ``Ac``: (*Real*) Compression post-peak asymptotic value
+- ``Bc``: (*Real*) Compression decay shape
+- ``K0``: (*Real*) Damage threshold
+- ``beta``: (*Real*) Shear parameter
diff --git a/doc/dev-doc/manual/bibliography.rst b/doc/dev-doc/manual/bibliography.rst
new file mode 100644
index 000000000..7a9e4db7d
--- /dev/null
+++ b/doc/dev-doc/manual/bibliography.rst
@@ -0,0 +1,7 @@
+.. _bibliography:
+
+Bibliography
+------------
+
+.. bibliography:: manual-bibliography.bib
+ :cited:
diff --git a/doc/dev-doc/manual/constitutive-laws.rst b/doc/dev-doc/manual/constitutive-laws.rst
new file mode 100644
index 000000000..3416daffe
--- /dev/null
+++ b/doc/dev-doc/manual/constitutive-laws.rst
@@ -0,0 +1,925 @@
+.. _sect-smm-cl:
+
+Constitutive Laws
+-----------------
+
+In order to compute an element’s response to deformation, one needs to
+use an appropriate constitutive relationship. The constitutive law is
+used to compute the element’s stresses from the element’s strains.
+
+In the finite-element discretization, the constitutive formulation is
+applied to every quadrature point of each element. When the implicit
+formulation is used, the tangent matrix has to be computed.
+
+| The chosen materials for the simulation have to be specified in the
+ mesh file or, as an alternative, they can be assigned using the at
+ ``element_material`` vector. For
+ every material assigned to the problem one has to specify the material
+ characteristics (constitutive behavior and material properties) using
+ the text input file (see :ref:`sect-io-material`).
+| In order to conveniently store values at each quadrature in a material point
+ ``Akantu`` provides a special data structure, the at :cpp:class:`InternalField
+ <akantu::InternalField>`. The internal fields are inheriting from the at
+ :cpp:class:`ElementTypeMapArray <akantu::ElementTypeMapArray>`. Furthermore,
+ it provides several functions for initialization, auto-resizing and auto
+ removal of quadrature points.
+
+Sometimes it is also desired to generate random distributions of
+internal parameters. An example might be the critical stress at which
+the material fails. To generate such a field, in the text input file, a
+random quantity needs be added to the base value:
+
+All parameters are real numbers. For the uniform distribution, minimum
+and maximum values have to be specified. Random parameters are defined
+as a :math:`base` value to which we add a random number that follows the
+chosen distribution.
+
+The
+`Uniform <http://en.wikipedia.org/wiki/Uniform_distribution_(continuous)>`__
+distribution is gives a random values between in :math:`[min, max)`. The
+`Weibull <http://en.wikipedia.org/wiki/Weibull_distribution>`__
+distribution is characterized by the following cumulative distribution
+function:
+
+.. math:: F(x) = 1- e^{-\left({x/\lambda}\right)^m}
+
+which depends on :math:`m` and :math:`\lambda`, which are the shape
+parameter and the scale parameter. These random distributions are
+different each time the code is executed. In order to obtain always the
+same one, it possible to manually set the *seed* that is the number from
+which these pseudo-random distributions are created. This can be done by
+adding the following line to the input file *outside* the material
+parameters environments:
+
+.. code-block::
+
+ seed = 1.0
+
+where the value 1.0 can be substituted with any number. Currently
+``Akantu`` can reproduce always the same distribution when the seed is
+specified *only* in serial. The value of the *seed* can be also
+specified directly in the code (for instance in the main file) with the
+command:
+
+.. code-block::
+
+ RandGenerator<Real>::seed(1.0)
+
+The same command, with empty brackets, can be used to check the value of
+the *seed* used in the simulation.
+
+The following sections describe the constitutive models implemented in
+``Akantu``. In Appendix `7 <#app:material-parameters>`__ a summary of
+the parameters for all materials of ``Akantu`` is provided.
+
+Elastic
+```````
+
+The elastic law is a commonly used constitutive relationship that can be
+used for a wide range of engineering materials (*e.g.*, metals,
+concrete, rock, wood, glass, rubber, etc.) provided that the strains
+remain small (*i.e.*, small deformation and stress lower than yield
+strength).
+
+The elastic laws are often expressed as
+:math:`\boldsymbol{\sigma} =
+\boldsymbol{C}:\boldsymbol{\varepsilon}` with
+where :math:`\boldsymbol{\sigma}` is the Cauchy stress
+tensor, :math:`\boldsymbol{\varepsilon}` represents the
+infinitesimal strain tensor and :math:`\boldsymbol{C}` is
+the elastic modulus tensor.
+
+.. _sect-smm-linear-elastic-isotropic:
+
+Linear isotropic
+''''''''''''''''
+
+The linear isotropic elastic behavior is described by Hooke’s law, which
+states that the stress is linearly proportional to the applied strain
+(material behaves like an ideal spring), as illustrated in
+ :numref:`fig:smm:cl:el`.
+
+.. figure:: figures/cl/stress_strain_el.svg
+ :alt: Elastic strain-stress curve
+ :name: fig:smm:cl:el
+ :width: 60.0%
+
+ Stress-strain curve of elastic material and schematic representation of
+ Hooke's law, denoted as a spring.
+
+The equation that relates the strains to the displacements is: point)
+from the displacements as follows:
+
+.. math::
+
+ \label{eqn:smm:strain_inf}
+ \boldsymbol{\varepsilon} =
+ \frac{1}{2} \left[ \nabla_0 \boldsymbol{u}+\nabla_0 \boldsymbol{u}^T \right]
+
+where :math:`\boldsymbol{\varepsilon}` represents the
+infinitesimal strain tensor,
+:math:`\nabla_{0}\boldsymbol{u}` the displacement gradient
+tensor according to the initial configuration. The constitutive equation
+for isotropic homogeneous media can be expressed as:
+
+.. math::
+
+ \label{eqn:smm:material:constitutive_elastic}
+ \boldsymbol{\sigma } =\lambda\mathrm{tr}(\boldsymbol{\varepsilon})\boldsymbol{I}+2 \mu\boldsymbol{\varepsilon}
+
+where :math:`\boldsymbol{\sigma}` is the Cauchy stress
+tensor (:math:`\lambda` and :math:`\mu` are the the first and second
+Lame’s coefficients).
+
+In Voigt notation this correspond to
+
+.. math::
+
+ \begin{aligned}
+ \left[\begin{array}{c}
+ \sigma_{11}\\
+ \sigma_{22}\\
+ \sigma_{33}\\
+ \sigma_{23}\\
+ \sigma_{13}\\
+ \sigma_{12}\\
+ \end{array}\right]
+ &= \frac{E}{(1+\nu)(1-2\nu)}\left[
+ \begin{array}{cccccc}
+ 1-\nu & \nu & \nu & 0 & 0 & 0\\
+ \nu & 1-\nu & \nu & 0 & 0 & 0\\
+ \nu & \nu & 1-\nu & 0 & 0 & 0\\
+ 0 & 0 & 0 & \frac{1-2\nu}{2} & 0 & 0 \\
+ 0 & 0 & 0 & 0 & \frac{1-2\nu}{2} & 0 \\
+ 0 & 0 & 0 & 0 & 0 & \frac{1-2\nu}{2} \\
+ \end{array}\right]
+ \left[\begin{array}{c}
+ \varepsilon_{11}\\
+ \varepsilon_{22}\\
+ \varepsilon_{33}\\
+ 2\varepsilon_{23}\\
+ 2\varepsilon_{13}\\
+ 2\varepsilon_{12}\\
+ \end{array}\right]\end{aligned}
+
+.. _sect-smm-linear-elastic-anisotropic:
+
+Linear anisotropic
+''''''''''''''''''
+
+This formulation is not sufficient to represent all elastic material
+behavior. Some materials have characteristic orientation that have to be
+taken into account. To represent this anisotropy a more general
+stress-strain law has to be used. For this we define the elastic modulus
+tensor as follow:
+
+.. math::
+
+ \begin{aligned}
+ \left[\begin{array}{c}
+ \sigma_{11}\\
+ \sigma_{22}\\
+ \sigma_{33}\\
+ \sigma_{23}\\
+ \sigma_{13}\\
+ \sigma_{12}\\
+ \end{array}\right]
+ &= \left[
+ \begin{array}{cccccc}
+ c_{11} & c_{12} & c_{13} & c_{14} & c_{15} & c_{16}\\
+ c_{21} & c_{22} & c_{23} & c_{24} & c_{25} & c_{26}\\
+ c_{31} & c_{32} & c_{33} & c_{34} & c_{35} & c_{36}\\
+ c_{41} & c_{42} & c_{43} & c_{44} & c_{45} & c_{46}\\
+ c_{51} & c_{52} & c_{53} & c_{54} & c_{55} & c_{56}\\
+ c_{61} & c_{62} & c_{63} & c_{64} & c_{65} & c_{66}\\
+ \end{array}\right]
+ \left[\begin{array}{c}
+ \varepsilon_{11}\\
+ \varepsilon_{22}\\
+ \varepsilon_{33}\\
+ 2\varepsilon_{23}\\
+ 2\varepsilon_{13}\\
+ 2\varepsilon_{12}\\
+ \end{array}\right]\end{aligned}
+
+To simplify the writing of input files the :math:`\boldsymbol{C}` tensor
+is expressed in the material basis. And this basis as to be given too.
+This basis :math:`\Omega_{{\mathrm{mat}}}
+= \{\boldsymbol{n_1}, \boldsymbol{n_2}, \boldsymbol{n_3}\}`
+is used to define the rotation :math:`R_{ij} =
+\boldsymbol{n_j} . \boldsymbol{e_i}`. And
+:math:`\boldsymbol{C}` can be rotated in the global basis
+:math:`\Omega
+= \{\boldsymbol{e_1}, \boldsymbol{e_2}, \boldsymbol{e_3}\}`
+as follow:
+
+.. math::
+
+ \begin{aligned}
+ \boldsymbol{C}_{\Omega} &= \boldsymbol{R}_1 \boldsymbol{C}_{\Omega_{{\mathrm{mat}}}} \boldsymbol{R}_2\\
+ \boldsymbol{R}_1 &= \left[
+ \begin{array}{cccccc}
+ R_{11} R_{11} & R_{12} R_{12} & R_{13} R_{13} & R_{12} R_{13} & R_{11} R_{13} & R_{11} R_{12}\\
+ R_{21} R_{21} & R_{22} R_{22} & R_{23} R_{23} & R_{22} R_{23} & R_{21} R_{23} & R_{21} R_{22}\\
+ R_{31} R_{31} & R_{32} R_{32} & R_{33} R_{33} & R_{32} R_{33} & R_{31} R_{33} & R_{31} R_{32}\\
+ R_{21} R_{31} & R_{22} R_{32} & R_{23} R_{33} & R_{22} R_{33} & R_{21} R_{33} & R_{21} R_{32}\\
+ R_{11} R_{31} & R_{12} R_{32} & R_{13} R_{33} & R_{12} R_{33} & R_{11} R_{33} & R_{11} R_{32}\\
+ R_{11} R_{21} & R_{12} R_{22} & R_{13} R_{23} & R_{12} R_{23} & R_{11} R_{23} & R_{11} R_{22}\\
+ \end{array}\right]\\
+ \boldsymbol{R}_2 &= \left[
+ \begin{array}{cccccc}
+ R_{11} R_{11} & R_{21} R_{21} & R_{31} R_{31} & R_{21} R_{31} & R_{11} R_{31} & R_{11} R_{21}\\
+ R_{12} R_{12} & R_{22} R_{22} & R_{32} R_{32} & R_{22} R_{32} & R_{12} R_{32} & R_{12} R_{22}\\
+ R_{13} R_{13} & R_{23} R_{23} & R_{33} R_{33} & R_{23} R_{33} & R_{13} R_{33} & R_{13} R_{23}\\
+ R_{12} R_{13} & R_{22} R_{23} & R_{32} R_{33} & R_{22} R_{33} & R_{12} R_{33} & R_{12} R_{23}\\
+ R_{11} R_{13} & R_{21} R_{23} & R_{31} R_{33} & R_{21} R_{33} & R_{11} R_{33} & R_{11} R_{23}\\
+ R_{11} R_{12} & R_{21} R_{22} & R_{31} R_{32} & R_{21} R_{32} & R_{11} R_{32} & R_{11} R_{22}\\
+ \end{array}\right]\\\end{aligned}
+
+.. _sect-smm-linear-elastic-orthotropic:
+
+Linear orthotropic
+''''''''''''''''''
+
+A particular case of anisotropy is when the material basis is orthogonal
+in which case the elastic modulus tensor can be simplified and rewritten
+in terms of 9 independents material parameters.
+
+.. math::
+
+ \begin{aligned}
+ \left[\begin{array}{c}
+ \sigma_{11}\\
+ \sigma_{22}\\
+ \sigma_{33}\\
+ \sigma_{23}\\
+ \sigma_{13}\\
+ \sigma_{12}\\
+ \end{array}\right]
+ &= \left[
+ \begin{array}{cccccc}
+ c_{11} & c_{12} & c_{13} & 0 & 0 & 0 \\
+ & c_{22} & c_{23} & 0 & 0 & 0 \\
+ & & c_{33} & 0 & 0 & 0 \\
+ & & & c_{44} & 0 & 0 \\
+ & \multicolumn{2}{l}{\text{sym.}} & & c_{55} & 0 \\
+ & & & & & c_{66}\\
+ \end{array}\right]
+ \left[\begin{array}{c}
+ \varepsilon_{11}\\
+ \varepsilon_{22}\\
+ \varepsilon_{33}\\
+ 2\varepsilon_{23}\\
+ 2\varepsilon_{13}\\
+ 2\varepsilon_{12}\\
+ \end{array}\right]\end{aligned}
+
+.. math::
+
+ \begin{aligned}
+ c_{11} &= E_1 (1 - \nu_{23}\nu_{32})\Gamma \qquad c_{22} = E_2 (1 - \nu_{13}\nu_{31})\Gamma \qquad c_{33} = E_3 (1 - \nu_{12}\nu_{21})\Gamma\\
+ c_{12} &= E_1 (\nu_{21} - \nu_{31}\nu_{23})\Gamma = E_2 (\nu_{12} - \nu_{32}\nu_{13})\Gamma\\
+ c_{13} &= E_1 (\nu_{31} - \nu_{21}\nu_{32})\Gamma = E_2 (\nu_{13} - \nu_{21}\nu_{23})\Gamma\\
+ c_{23} &= E_2 (\nu_{32} - \nu_{12}\nu_{31})\Gamma = E_3 (\nu_{23} - \nu_{21}\nu_{13})\Gamma\\
+ c_{44} &= \mu_{23} \qquad c_{55} = \mu_{13} \qquad c_{66} = \mu_{12} \\
+ \Gamma &= \frac{1}{1 - \nu_{12} \nu_{21} - \nu_{13} \nu_{31} - \nu_{32} \nu_{23} - 2 \nu_{21} \nu_{32} \nu_{13}}\end{aligned}
+
+The Poisson ratios follow the rule
+:math:`\nu_{ij} = \nu_{ji} E_i / E_j`.
+
+.. _sect-smm-cl-neohookean:
+
+Neo-Hookean
+'''''''''''
+
+The hyperelastic Neo-Hookean constitutive law results from an extension
+of the linear elastic relationship (Hooke’s Law) for large deformation.
+Thus, the model predicts nonlinear stress-strain behavior for bodies
+undergoing large deformations.
+
+.. figure:: figures/cl/stress_strain_neo.svg
+ :alt: Neo-hookean Stress-strain curve.
+ :name: fig:smm:cl:neo_hookean
+ :width: 40.0%
+
+ Neo-hookean Stress-strain curve.
+
+As illustrated in :numref:`fig:smm:cl:neo_hookean`, the behavior
+is initially linear and the mechanical behavior is very close to the
+corresponding linear elastic material. This constitutive relationship,
+which accounts for compressibility, is a modified version of the one
+proposed by Ronald Rivlin :cite:`Belytschko:2000`.
+
+The strain energy stored in the material is given by:
+
+.. math::
+
+ \label{eqn:smm:constitutive:neohookean_potential}
+ \Psi(\boldsymbol{C}) = \frac{1}{2}\lambda_0\left(\ln J\right)^2-\mu_0\ln J+\frac{1}{2}
+ \mu_0\left(\mathrm{tr}(\boldsymbol{C})-3\right)
+
+where :math:`\lambda_0` and :math:`\mu_0` are, respectively, Lamé’s
+first parameter and the shear modulus at the initial configuration.
+:math:`J` is the jacobian of the deformation gradient
+(:math:`\boldsymbol{F}=\nabla_{\!\!\boldsymbol{X}}\boldsymbol{x}`):
+:math:`J=\text{det}(\boldsymbol{F})`. Finally
+:math:`\boldsymbol{C}` is the right Cauchy-Green
+deformation tensor.
+
+Since this kind of material is used for large deformation problems, a
+finite deformation framework should be used. Therefore, the Cauchy
+stress (:math:`\boldsymbol{\sigma}`) should be computed
+through the second Piola-Kirchhoff stress tensor
+:math:`\boldsymbol{S}`:
+
+.. math:: \boldsymbol{\sigma } = \frac{1}{J}\boldsymbol{F}\boldsymbol{S}\boldsymbol{F}^T
+
+Finally the second Piola-Kirchhoff stress tensor is given by:
+
+.. math::
+
+ \boldsymbol{S} = 2\frac{\partial\Psi}{\partial\boldsymbol{C}} = \lambda_0\ln J
+ \boldsymbol{C}^{-1}+\mu_0\left(\boldsymbol{I}-\boldsymbol{C}^{-1}\right)
+
+The parameters to indicate in the material file are the same as those
+for the elastic case: ``E`` (Young’s modulus), ``nu`` (Poisson’s ratio).
+
+.. _sect-smm-cl-sls:
+
+Visco-Elasticity
+''''''''''''''''
+
+Visco-elasticity is characterized by strain rate dependent behavior.
+Moreover, when such a material undergoes a deformation it dissipates
+energy. This dissipation results in a hysteresis loop in the
+stress-strain curve at every loading cycle (see
+:numref:`fig:smm:cl:visco-elastic:hyst`).
+In principle, it can be applied to many materials, since all materials
+exhibit a visco-elastic behavior if subjected to particular conditions
+(such as high temperatures).
+
+.. figure:: figures/cl/stress_strain_visco.svg
+ :name: fig:smm:cl:visco-elastic:hyst
+ :align: center
+ :width: 40.0%
+
+ Characteristic stress-strain behavior of a visco-elastic material with hysteresis loop
+
+.. figure:: figures/cl/visco_elastic_law.svg
+ :name: fig:smm:cl:visco-elastic:model
+ :align: center
+ :width: 40.0%
+
+ Schematic representation of the standard rheological linear solid visco-elastic model
+
+The standard rheological linear solid model (see Sections 10.2 and 10.3
+of :cite:`simo92`) has been implemented in ``Akantu``. This
+model results from the combination of a spring mounted in parallel with
+a spring and a dashpot connected in series, as illustrated in
+:numref:`fig:smm:cl:visco-elastic:model`.
+The advantage of this model is that it allows to account for creep or
+stress relaxation. The equation that relates the stress to the strain is
+(in 1D):
+
+.. math:: \frac{d\varepsilon(t)}{dt} = \left ( E + E_V \right ) ^ {-1} \cdot \left [ \frac{d\sigma(t)}{dt} + \frac{E_V}{\eta}\sigma(t) - \frac{EE_V}{\eta}\varepsilon(t) \right ]
+
+where :math:`\eta` is the viscosity. The equilibrium condition is unique and is
+attained in the limit, as :math:`t \to \infty`. At this stage, the response is
+elastic and depends on the Young’s modulus :math:`E`. The mandatory parameters
+for the material file are the following: ``rho`` (density), ``E`` (Young’s
+modulus), ``nu`` (Poisson’s ratio), ``Plane_Stress`` (if set to zero plane
+strain, otherwise plane stress), ``eta`` (dashpot viscosity) and ``Ev``
+(stiffness of the viscous element).
+
+Note that the current standard linear solid model is applied only on the
+deviatoric part of the strain tensor. The spheric part of the strain
+tensor affects the stress tensor like an linear elastic material.
+
+.. _sect-smm-cl-plastic:
+
+Plastic
+```````
+
+Small-Deformation Plasticity
+''''''''''''''''''''''''''''
+
+The small-deformation plasticity is a simple plasticity material
+formulation which accounts for the additive decomposition of strain into
+elastic and plastic strain components. This formulation is applicable to
+infinitesimal deformation where the additive decomposition of the strain
+is a valid approximation. In this formulation, plastic strain is a
+shearing process where hydrostatic stress has no contribution to
+plasticity and consequently plasticity does not lead to volume change.
+:numref:`fig:smm:cl:Lin-strain-hard` shows the linear strain
+hardening elasto-plastic behavior according to the additive
+decomposition of strain into the elastic and plastic parts in
+infinitesimal deformation as
+
+.. math::
+
+ \boldsymbol{\varepsilon} &= \boldsymbol{\varepsilon}^e +\boldsymbol{\varepsilon}^p\\
+ \boldsymbol{\sigma} &= 2G(\boldsymbol{\varepsilon}^e) + \lambda \mathrm{tr}(\boldsymbol{\varepsilon}^e)\boldsymbol{I}
+
+.. figure:: figures/cl/isotropic_hardening_plasticity.svg
+ :name: fig:smm:cl:Lin-strain-hard
+ :align: center
+
+ Stress-strain curve for the small-deformation plasticity with linear isotropic hardening.
+
+In this class, the von Mises yield criterion is used. In the von Mises
+yield criterion, the yield is independent of the hydrostatic stress.
+Other yielding criteria such as Tresca and Gurson can be easily
+implemented in this class as well.
+
+In the von Mises yield criterion, the hydrostatic stresses have no
+effect on the plasticity and consequently the yielding occurs when a
+critical elastic shear energy is achieved.
+
+.. math::
+
+ \label{eqn:smm:constitutive:von Mises}
+ f = \sigma_{{\mathrm{eff}}} - \sigma_y = \left(\frac{3}{2} {\boldsymbol{\sigma}}^{{\mathrm{tr}}} : {\boldsymbol{\sigma}}^{{\mathrm{tr}}}\right)^\frac{1}{2}-\sigma_y (\boldsymbol{\varepsilon}^p)
+
+.. math::
+
+ \label{eqn:smm:constitutive:yielding}
+ f < 0 \quad \textrm{Elastic deformation,} \qquad f = 0 \quad \textrm{Plastic deformation}
+
+where :math:`\sigma_y` is the yield strength of the material which can
+be function of plastic strain in case of hardening type of materials and
+:math:`{\boldsymbol{\sigma}}^{{\mathrm{tr}}}` is the
+deviatoric part of stress given by
+
+.. math::
+
+ \label{eqn:smm:constitutive:deviatoric stress}
+ {\boldsymbol{\sigma}}^{{\mathrm{tr}}}=\boldsymbol{\sigma} - \frac{1}{3} \mathrm{tr}(\boldsymbol{\sigma}) \boldsymbol{I}
+
+After yielding :math:`(f = 0)`, the normality hypothesis of plasticity
+determines the direction of plastic flow which is normal to the tangent
+to the yielding surface at the load point. Then, the tensorial form of
+the plastic constitutive equation using the von Mises yielding criterion
+(see equation 4.34) may be written as
+
+.. math::
+
+ \label{eqn:smm:constitutive:plastic contitutive equation}
+ \Delta {\boldsymbol{\varepsilon}}^p = \Delta p \frac {\partial{f}}{\partial{\boldsymbol{\sigma}}}=\frac{3}{2} \Delta p \frac{{\boldsymbol{\sigma}}^{{\mathrm{tr}}}}{\sigma_{{\mathrm{eff}}}}
+
+In these expressions, the direction of the plastic strain increment (or
+equivalently, plastic strain rate) is given by
+:math:`\frac{{\boldsymbol{\sigma}}^{{\mathrm{tr}}}}{\sigma_{{\mathrm{eff}}}}`
+while the magnitude is defined by the plastic multiplier
+:math:`\Delta p`. This can be obtained using the *consistency condition*
+which impose the requirement for the load point to remain on the
+yielding surface in the plastic regime.
+
+Here, we summarize the implementation procedures for the
+small-deformation plasticity with linear isotropic hardening:
+
+#. Compute the trial stress:
+
+ .. math:: {\boldsymbol{\sigma}}^{{\mathrm{tr}}} = {\boldsymbol{\sigma}}_t + 2G\Delta \boldsymbol{\varepsilon} + \lambda \mathrm{tr}(\Delta \boldsymbol{\varepsilon})\boldsymbol{I}
+
+#. Check the Yielding criteria:
+
+ .. math:: f = (\frac{3}{2} {\boldsymbol{\sigma}}^{{\mathrm{tr}}} : {\boldsymbol{\sigma}}^{{\mathrm{tr}}})^{1/2}-\sigma_y (\boldsymbol{\varepsilon}^p)
+
+#. Compute the Plastic multiplier:
+
+ .. math::
+
+ \begin{aligned}
+ d \Delta p &= \frac{\sigma^{tr}_{eff} - 3G \Delta P^{(k)}- \sigma_y^{(k)}}{3G + h}\\
+ \Delta p^{(k+1)} &= \Delta p^{(k)}+ d\Delta p\\
+ \sigma_y^{(k+1)} &= (\sigma_y)_t+ h\Delta p
+ \end{aligned}
+
+#. Compute the plastic strain increment:
+
+ .. math:: \Delta {\boldsymbol{\varepsilon}}^p = \frac{3}{2} \Delta p \frac{{\boldsymbol{\sigma}}^{{\mathrm{tr}}}}{\sigma_{{\mathrm{eff}}}}
+
+#. Compute the stress increment:
+
+ .. math:: {\Delta \boldsymbol{\sigma}} = 2G(\Delta \boldsymbol{\varepsilon}-\Delta \boldsymbol{\varepsilon}^p) + \lambda \mathrm{tr}(\Delta \boldsymbol{\varepsilon}-\Delta \boldsymbol{\varepsilon}^p)\boldsymbol{I}
+
+#. Update the variables:
+
+ .. math::
+
+ \begin{aligned}
+ {\boldsymbol{\varepsilon^p}} &= {\boldsymbol{\varepsilon}}^p_t+{\Delta {\boldsymbol{\varepsilon}}^p}\\
+ {\boldsymbol{\sigma}} &= {\boldsymbol{\sigma}}_t+{\Delta \boldsymbol{\sigma}}
+ \end{aligned}
+
+We use an implicit integration technique called *the radial return method* to
+obtain the plastic multiplier. This method has the advantage of being
+unconditionally stable, however, the accuracy remains dependent on the step
+size. The plastic parameters to indicate in the material file are:
+:math:`\sigma_y` (Yield stress) and ``h`` (Hardening modulus). In addition, the
+elastic parameters need to be defined as previously mentioned: ``E`` (Young’s
+modulus), ``nu`` (Poisson’s ratio).
+
+Damage
+``````
+
+In the simplified case of a linear elastic and brittle material,
+isotropic damage can be represented by a scalar variable :math:`d`,
+which varies from :math:`0` to :math:`1` for no damage to fully broken
+material respectively. The stress-strain relationship then becomes:
+
+.. math:: \boldsymbol{\sigma} = (1-d)\, \boldsymbol{C}:\boldsymbol{\varepsilon}
+
+where :math:`\boldsymbol{\sigma}`,
+:math:`\boldsymbol{\varepsilon}` are the Cauchy stress and
+strain tensors, and :math:`\boldsymbol{C}` is the elastic
+stiffness tensor. This formulation relies on the definition of an
+evolution law for the damage variable. In ``Akantu``, many possibilities
+exist and they are listed below.
+
+.. _sect-smm-cl-damage-marigo:
+
+Marigo
+''''''
+
+This damage evolution law is energy based as defined by Marigo
+:cite:`marigo81a`, :cite:`lemaitre96a`. It is an isotropic damage law.
+
+.. math::
+
+ \begin{aligned}
+ Y &= \frac{1}{2}\boldsymbol{\varepsilon}:\boldsymbol{C}:\boldsymbol{\varepsilon}\\
+ F &= Y - Y_d - S d\\
+ d &= \left\{
+ \begin{array}{l l}
+ \mathrm{min}\left(\frac{Y-Y_d}{S},\;1\right) & \mathrm{if}\; F > 0\\
+ \mathrm{unchanged} & \mathrm{otherwise}
+ \end{array}
+ \right.\end{aligned}
+
+In this formulation, :math:`Y` is the strain energy release rate,
+:math:`Y_d` the rupture criterion and :math:`S` the damage energy. The
+non-local version of this damage evolution law is constructed by
+averaging the energy :math:`Y`.
+
+.. _sect-smm-cl-damage-mazars:
+
+Mazars
+''''''
+
+This law introduced by Mazars :cite:`mazars84a` is a
+behavioral model to represent damage evolution in concrete. This model
+does not rely on the computation of the tangent stiffness, the damage is
+directly evaluated from the strain.
+
+The governing variable in this damage law is the equivalent strain
+:math:`\varepsilon_{{\mathrm{eq}}} =
+\sqrt{<\boldsymbol{\varepsilon}>_+:<\boldsymbol{\varepsilon}>_+}`,
+with :math:`<.>_+` the positive part of the tensor. This part is defined
+in the principal coordinates (I, II, III) as
+:math:`\varepsilon_{{\mathrm{eq}}} =
+\sqrt{<\boldsymbol{\varepsilon_I}>_+^2 + <\boldsymbol{\varepsilon_{II}}>_+^2 + <\boldsymbol{\varepsilon_{III}}>_+^2}`.
+The damage is defined as:
+
+.. math::
+
+ \begin{aligned}
+ D &= \alpha_t^\beta D_t + (1-\alpha_t)^\beta D_c\\
+ D_t &= 1 - \frac{\kappa_0 (1- A_t)}{\varepsilon_{{\mathrm{eq}}}} - A_t \exp^{-B_t(\varepsilon_{{\mathrm{eq}}}-\kappa_0)}\\
+ D_c &= 1 - \frac{\kappa_0 (1- A_c)}{\varepsilon_{{\mathrm{eq}}}} - A_c
+ \exp^{-B_c(\varepsilon_{{\mathrm{eq}}}-\kappa_0)}\\
+ \alpha_t &= \frac{\sum_{i=1}^3<\varepsilon_i>_+\varepsilon_{{\mathrm{nd}}\;i}}{\varepsilon_{{\mathrm{eq}}}^2}\end{aligned}
+
+With :math:`\kappa_0` the damage threshold, :math:`A_t` and :math:`B_t`
+the damage parameter in traction, :math:`A_c` and :math:`B_c` the damage
+parameter in compression, :math:`\beta` is the shear parameter.
+:math:`\alpha_t` is the coupling parameter between traction and
+compression, the :math:`\varepsilon_i` are the eigenstrain and the
+:math:`\varepsilon_{{\mathrm{nd}}\;i}` are the eigenvalues of the strain
+if the material were undamaged.
+
+The coefficients :math:`A` and :math:`B` are the post-peak asymptotic
+value and the decay shape parameters.
+
+.. _sect:smm:CLNL:
+
+Non-Local Constitutive Laws
+```````````````````````````
+
+Continuum damage modeling of quasi-brittle materials undergo significant
+softening after the onset of damage. This fast growth of damage causes a loss of
+ellipticity of partial differential equations of equilibrium. Therefore, the
+numerical simulation results won't be objective anymore, because the dissipated
+energy will depend on mesh size used in the simulation. One way to avoid this
+effect is the use of non-local damage formulations. In this approach a local
+quantity such as the strain is replaced by its non-local average, where the size
+of the domain, over which the quantitiy is averaged, depends on the underlying
+material microstructure. ``Akantu`` provides non-local versions of many
+constitutive laws for damage. Examples are for instance the material
+:ref:`sect-smm-cl-damage-mazars` and the material
+:ref:`sect-smm-cl-damage-marigo`, that can be used in a non-local context. In
+order to use the corresponding non-local formulation the user has to define the
+non-local material he wishes to use in the text input file:
+
+.. code-block:: none
+
+ material constitutive_law_non_local [
+ name = material_name
+ rho = $value$
+ ...
+ ]
+
+where ``constitutive_law_non_local`` is the name of the non-local constitutive law, *e.g.* `marigo_non_local`.
+In addition to the material the non-local neighborhood, that should be used for the averaging process needs to be defined in the material file as well:
+
+.. code-block:: none
+
+ non_local neighborhood_name weight_function_type [
+ radius = $value$
+ ...
+ weight_function weight_parameter [
+ damage_limit = $value$
+ ...
+ ]
+ ]
+
+for the non-local averaging, *e.g.* ``base_wf``, followed by the properties of the non-local neighborhood, such as the radius, and the weight function parameters. It is important to notice that the non-local neighborhood must have the same name as the material to which the neighborhood belongs!
+The following two sections list the non-local constitutive laws and different type of weight functions available in ``Akantu``.
+\subsection{Non-local constitutive laws}
+Let us consider a body having a volume :math:`V` and a boundary :math:`\Gamma`. The stress-strain relation for a non-local damage model can be described as follows:
+
+.. _eq:non-local-const:
+ .. math:: \vec{\sigma} = (1-\bar{d}) \vec{D}:\epsilon
+
+with :math:`\vec{D}` the elastic moduli tensor, :math:`\sigma` the stress tensor, :math:`\epsilon` the strain tensor and :math:`\bar{d}` the non-local damage variable. Note that this stres-strain relationship is similar to the relationship defined in Damage model except :math:`\bar{d}`. The non-local damage model can be extended to the damage constitutive laws: :ref:`sect-smm-cl-damage-marigo` and :ref:`sect-smm-cl-damage-mazars`.
+
+The non-local damage variable :math:`\bar{d}` is defined as follows:
+
+.. _eq:non-local-const:
+ .. math:: \bar{d}(\vec{x}) = \int_{V}W(\vec{x}, \vec{y}) d(\vec{y}) dV(\vec{y})
+
+with :math:`W(\vec{x},\vec{y})` the weight function which averages local damage variables to describe the non-local interactions. A list of available weight functions and its functionalities in \akantu are explained in the next section.
+
+Non-local weight functions
+''''''''''''''''''''''''''
+
+The available weight functions in ``Akantu`` are follows:
+
+ - ``base_weight_function``: This weight function averages local damage variables by using a bell-shape function on spatial dimensions.
+ - ``damaged_weight_function``: A linear-shape weight function is applied to average local damage variables. Its slope is determined by damage variables. For example, the damage variables for an element which is highly damaged are averaged over large spatial dimension (linear function including a small slope).
+ - ``remove_damaged_weight_function``: This weight function averages damage values by using a bell-shape function as ``base_weight_function``, but excludes elements which are fully damaged.
+ - ``remove_damaged_with_damage_rate_weight_function``: A bell-shape function is applied to average local damage variables for elements having small damage rates.
+ - ``stress_based_weight_function``: Non local integral takes stress states, and use the states to construct weight function: an ellipsoid shape. Detailed explanations of this weight function are given in Giry et al. :cite:`giry13a`.
+
+
+
+.. _sec-cohesive-laws:
+
+Cohesive Constitutive laws
+``````````````````````````
+
+.. _ssect-smm-cl-coh-snozzi:
+
+Linear Irreversible Law
+'''''''''''''''''''''''
+
+.. figure:: figures/cl/linear_cohesive_law.svg
+ :alt: Irreversible cohesive laws for explicit simulations.
+ :name: fig:smm:coh:linear_cohesive_law
+ :align: center
+ :width: 60.0%
+
+ Irreversible cohesive laws for explicit simulations.
+
+
+`Akantu` includes the Snozzi-Molinari :cite:`snozzi_cohesive_2013`
+linear irreversible cohesive law (see
+:numref:`fig:smm:coh:linear_cohesive_law`). It is an extension to
+the Camacho-Ortiz :cite:`camacho_computational_1996` cohesive law in
+order to make dissipated fracture energy path-dependent. The concept
+of free potential energy is dropped and a new independent parameter
+:math:`\kappa` is introduced:
+
+.. math::
+ \kappa = \frac{G_\mathrm{c, II}}{G_\mathrm{c, I}}
+
+
+where :math:`G_\mathrm{c, I}` and :math:`G_\mathrm{c, II}` are the
+necessary works of separation per unit area to open completely a
+cohesive zone under mode I and mode II, respectively. Their model yields to the
+following equation for cohesive tractions :math:`\vec{T}` in case of crack
+opening :math:`{\delta}`:
+
+.. math::
+ \vec{T} = \left( \frac{\beta^2}{\kappa} \Delta_\mathrm{t} \vec{t} +
+ \Delta_\mathrm{n} \vec{n} \right)
+ \frac{\sigma_\mathrm{c}}{\delta}
+ \left( 1- \frac{\delta}{\delta_\mathrm{c}} \right)
+ = \hat{\vec T}\,
+ \frac{\sigma_\mathrm{c}}{\delta}
+ \left( 1- \frac{\delta}{\delta_\mathrm{c}} \right)
+ :label: eq-smm-coh-tractions
+
+where :math:`\sigma_\mathrm{c}` is the material strength along the fracture,
+:math:`\delta_\mathrm{c}` the critical effective displacement after which
+cohesive tractions are zero (complete decohesion), :math:`\Delta_\mathrm{t}`
+and :math:`\Delta_\mathrm{n}` are the tangential and normal components of
+the opening displacement vector :math:`\vec{\Delta}`, respectively. The
+parameter :math:`\beta` is a weight that indicates how big the tangential
+opening contribution is. The effective opening displacement is:
+
+.. math::
+ \delta = \sqrt{\frac{\beta^2}{\kappa^2} \Delta_\mathrm{t}^2 + \Delta_\mathrm{n}^2}
+
+In case of unloading or reloading :math:`\delta < \delta_\mathrm{max}`,
+tractions are calculated as:
+
+.. math::
+ \begin{eqnarray}
+ T_\mathrm{n} &= \Delta_\mathrm{n}\, \frac{\sigma_\mathrm{c}}{\delta_\mathrm{max}} \left( 1- \frac{\delta_\mathrm{max}}{\delta_\mathrm{c}} \right) \\
+ T_\mathrm{t} &= \frac{\beta^2}{\kappa}\, \Delta_\mathrm{t}\, \frac{\sigma_\mathrm{c}}{\delta_\mathrm{max}} \left( 1- \frac{\delta_\mathrm{max}}{\delta_\mathrm{c}} \right)
+ \end{eqnarray}
+
+so that they vary linearly between the origin and the maximum attained
+tractions. As shown in :numref:`fig:smm:coh:linear_cohesive_law`,
+in this law, the dissipated and reversible energies are:
+
+.. math::
+ \begin{eqnarray}
+ E_\mathrm{diss} &= \frac{1}{2} \sigma_\mathrm{c}\, \delta_\mathrm{max}\\[1ex]
+ E_\mathrm{rev} &= \frac{1}{2} T\, \delta
+ \end{eqnarray}
+
+Moreover, a damage parameter :math:`D` can be defined as:
+
+.. math::
+ D = \min \left(
+ \frac{\delta_\mathrm{max}}{\delta_\mathrm{c}},1 \right)
+
+which varies from 0 (undamaged condition) and 1 (fully
+damaged condition). This variable can only increase because damage is
+an irreversible process. A simple penalty contact model has been incorporated
+in the cohesive law so that normal tractions can be returned in
+case of compression:
+
+.. math::
+ T_\mathrm{n} = \alpha \Delta_\mathrm{n} \quad\text{if}\quad
+ \Delta_\mathrm{n}\quad <\quad 0
+
+where :math:`\alpha` is a stiffness parameter that defaults to zero. The
+relative contact energy is equivalent to reversible energy but in
+compression.
+
+The material name of the linear decreasing cohesive law is
+``material_cohesive_linear`` and its parameters with their respective default
+values are:
+
+- ``sigma_c = 0``
+- ``delta_c = 0``
+- ``beta = 0``
+- ``G_c = 0``
+- ``kappa = 1``
+- ``penalty = 0``
+
+where ``G_c`` corresponds to :math:`G_\mathrm{c, I}`. A random number
+generator can be used to assign a random :math:`\sigma_\mathrm{c}` to each
+facet following a given distribution (see
+Section :ref:`sect-smm-cl`). Only one parameter between ``delta_c``
+and ``G_c`` has to be specified. For random :math:`\sigma_\mathrm{c}`
+distributions, the chosen parameter of these two is kept fixed and the
+other one is varied.
+
+The bi-linear constitutive law works exactly the same way as the linear
+one, except for the additional parameter ``delta_0`` that by
+default is zero. Two examples for the extrinsic and intrinsic cohesive
+elements and also an example to assign different properties to
+inter-granular and trans-granular cohesive elements can be found in
+the folder ``examples/cohesive_element/``.
+
+.. _ssect:smm:cl:coh-friction:
+
+Linear Cohesive Law with Friction
+'''''''''''''''''''''''''''''''''
+
+This law represents a variation of the linear irreversible cohesive of
+the previous section, which adds friction. The friction behavior is
+approximated with an elasto-plastic law, which relates the friction
+force to the relative sliding between the two faces of the cohesive
+element. The slope of the elastic branch is called
+``penalty_for_friction``, and is defined by the user, together
+with the friction coefficient, as a material property. The friction
+contribution evolves with the damage of the cohesive law: it is null
+when the damage is zero, and it becomes maximum when the damage is
+equal to one. This is done by defining a current value of the
+friction coefficient (mu) that increases linearly with the damage, up
+to the value of the friction coefficient defined by the user. The
+yielding plateau of the friction law is given by the product of the
+current friction coefficient and the local compression stress acting
+in the cohesive element. Such an approach is equivalent to a
+node-to-node contact friction. Its accuracy is acceptable only for
+small displacements.
+
+The material name of the linear cohesive law with friction is
+``material_cohesive_linear_friction``. Its additional parameters
+with respect to those of the linear cohesive law without friction,
+with the respective default values, are:
+
+- ``mu = 0``
+- ``penalty_for_friction = 0``
+
+.. _ssect:smm:cl:coh-fatigue:
+
+Linear Cohesive Law with Fatigue
+''''''''''''''''''''''''''''''''
+
+This law represents a variation of the linear irreversible cohesive
+law of the previous section, that removes the hypothesis of elastic
+unloading-reloading cycles. With this law, some energy is dissipated
+also during unloading and reloading with hysteresis. The
+implementation follows the work of :cite:`nguyen2001`. During the
+unloading-reloading cycle, the traction increment is computed as
+
+.. math::
+ \dot{T} =
+ \begin{cases}
+ K^- \, \dot{\delta} & \text{if $\dot{\delta} < 0$} \\
+ K^+ \, \dot{\delta} & \text{if $\dot{\delta} > 0$} \\
+ \end{cases}
+
+where :math:`\dot{\delta}` and :math:`\dot{T}` are respectively the effective
+opening displacement and the cohesive traction increments with respect
+to time, while :math:`K^-` and :math:`K^+` are respectively the unloading and
+reloading incremental stiffness. The unloading path is linear and
+results in an unloading stiffness
+
+.. math::
+ K^- = \frac{T_\mathrm{max}}{\delta_\mathrm{max}}
+
+where :math:`T_\mathrm{max}` and :math:`\delta_\mathrm{max}` are the maximum
+cohesive traction and the effective opening displacement reached
+during the precedent loading phase. The unloading stiffness remains
+constant during the unloading phase. On the other hand the reloading
+stiffness increment :math:`\dot{K}^+` is calculated as
+
+.. math::
+ \dot{K}^+ =
+ \begin{cases}
+ - K^+ \, \dot{\delta} / \delta_\mathrm{f} & \text{if $\dot{\delta}
+ > 0$} \\
+ \left( K^+ - K^- \right) \, \dot{\delta} / \delta_\mathrm{f} &
+ \text{if $\dot{\delta}$ < $0$}
+ \end{cases}
+
+where :math:`\delta_\mathrm{f}` is a material parameter (refer
+to :cite:`vocialta15` for more details). During unloading the stiffness
+:math:`K^+` tends to :math:`K^-`, while during reloading :math:`K^+` gets decreased at
+every time step. If the cohesive traction during reloading exceeds the
+upper limit given by equation :eq:`eq-smm-coh-tractions`, it is
+recomputed following the behavior of the linear decreasing cohesive
+law for crack opening.
+
+.. _ssect:smm:cl:coh-exponential:
+
+Exponential Cohesive Law
+'''''''''''''''''''''''''
+
+Ortiz and Pandolfi proposed this cohesive law in 1999 :cite:`ortiz1999`. The
+traction-opening equation for this law is as follows:
+
+.. math::
+ T = e \sigma_c \frac{\delta}{\delta_c}e^{-\delta/ \delta_c}
+ :label: eq:exponential_law
+
+This equation is plotted in :numref:`fig:smm:cl:ecl`. The term
+:math:`\partial{\vec{T}}/ \partial{\delta}` after the necessary derivation
+can expressed as
+
+.. math::
+ \frac{\partial{\vec{T}}} {\partial{\delta}} = \hat{\vec{T}} \otimes
+ \frac {\partial{(T/\delta)}}{\partial{\delta}}
+ \frac{\hat{\vec{T}}}{\delta}+ \frac{T}{\delta} \left[ \beta^2 \mat{I} +
+ \left(1-\beta^2\right) \left(\vec{n} \otimes \vec{n}\right)\right]
+ :label: eq:tangent_cohesive
+
+where
+
+.. math::
+ \frac{\partial{(T/ \delta)}}{\partial{\delta}} = \left\{\begin{array} {l l}
+ -e \frac{\sigma_c}{\delta_c^2 }e^{-\delta / \delta_c} & \quad \text{if}
+ \delta \geq \delta_{max}\\
+ 0 & \quad \text{if} \delta < \delta_{max}, \delta_n > 0
+ \end{array} \right.
+
+
+As regards the behavior in compression, two options are available:
+a contact penalty approach with stiffness following the formulation of
+the exponential law and a contact penalty approach with constant
+stiffness. In the second case, the stiffness is defined as a function
+of the tangent of the exponential law at the origin.
+
+.. figure:: figures/cl/cohesive_exponential.png
+ :alt: Exponential cohesive law
+ :name: fig:smm:cl:ecl
+ :align: center
+
+ Exponential cohesive law
diff --git a/doc/dev-doc/manual/fe_engine.rst b/doc/dev-doc/manual/fe_engine.rst
index 5e693213f..34779e141 100644
--- a/doc/dev-doc/manual/fe_engine.rst
+++ b/doc/dev-doc/manual/fe_engine.rst
@@ -1,183 +1,233 @@
-``FEEngine``
-============
-
-The ``FEEngine`` interface is dedicated to handle the
-finite-element approximations and the numerical integration of the
-weak form. As we will see in Chapter sect:smm, ``Model``
-creates its own ``FEEngine`` object so the explicit creation of the
+FEEngine
+========
+
+The :cpp:class:`FEEngine<akantu::FEEngine>` interface is dedicated to handle the
+finite-element approximations and the numerical integration of the weak form. As
+we will see in Chapter :doc:`./solidmechanicsmodel`,
+:cpp:class:`Model<akantu::Model>` creates its own
+:cpp:class:`FEEngine<akantu::FEEngine>` object so the explicit creation of the
object is not required.
Mathematical Operations
-----------------------
-Using the ``FEEngine`` object, one can compute a interpolation, an
-integration or a gradient. A simple example is given below::
-
- // having a FEEngine object
- std::unique_ptr<FEEngine> fem =
- std::make_unique<FEEngineTemplate<IntegratorGauss,ShapeLagrange>>(
- my_mesh, dim, "my_fem");
- // instead of this, a FEEngine object can be get using the model:
- // model.getFEEngine()
-
- //compute the gradient
- Array<Real> u; //append the values you want
- Array<Real> nablauq; //gradient array to be computed
- // compute the gradient
- fem->gradientOnIntegrationPoints(const Array<Real> &u,
- Array<Real> &nablauq,
- const UInt nb_degree_of_freedom,
- ElementType type);
-
- // interpolate
- Array<Real> uq; //interpolated array to be computed
- // compute the interpolation
- fem->interpolateOnIntegrationPoints(const Array<Real> &u,
- Array<Real> &uq,
- UInt nb_degree_of_freedom,
- ElementType type);
-
- // interpolated function can be integrated over the elements
- Array<Real> int_val_on_elem;
- // integrate
- fem->integrate(const Array<Real> &uq,
- Array<Real> &int_uq,
- UInt nb_degree_of_freedom,
- ElementType type);
-
-Another example below shows how to integrate stress and strain fields
-over elements assigned to a particular material::
-
- UInt sp_dim = 3; //spatial dimension
- UInt m = 1; //material index of interest
- const ElementType type = _tetrahedron_4; //element type
-
- // get the stress and strain arrays associated to the material index m
- const Array<Real> & strain_vec = model.getMaterial(m).getGradU(type);
- const Array<Real> & stress_vec = model.getMaterial(m).getStress(type);
-
- // get the element filter for the material index
- const Array<UInt> & elem_filter = model.getMaterial(m).getElementFilter(type);
-
- // initialize the integrated stress and strain arrays
- Array<Real> int_strain_vec(elem_filter.getSize(),
- sp_dim*sp_dim, "int_of_strain");
- Array<Real> int_stress_vec(elem_filter.getSize(),
- sp_dim*sp_dim, "int_of_stress");
-
- // integrate the fields
- model.getFEEngine().integrate(strain_vec, int_strain_vec,
- sp_dim*sp_dim, type, _not_ghost, elem_filter);
- model.getFEEngine().integrate(stress_vec, int_stress_vec,
- sp_dim*sp_dim, type, _not_ghost, elem_filter);
+Using the :cpp:class:`FEEngine<akantu::FEEngine>` object, one can compute a interpolation,
+an integration or a gradient.A simple example is given below:
+
+.. code-block:: c++
+
+ // having a FEEngine object
+ auto fem = std::make_unique<FEEngineTemplate<IntegratorGauss, ShapeLagrange>>(my_mesh, dim, "my_fem");
+ // instead of this, a FEEngine object can be get using the model:
+ // model.getFEEngine()
+
+ // compute the gradient
+ Array<Real> u; // append the values you want
+ Array<Real> nablauq; // gradient array to be computed
+ // compute the gradient
+ fem->gradientOnIntegrationPoints(const Array<Real> & u, Array<Real> & nablauq,
+ const UInt nb_degree_of_freedom,
+ ElementType type);
+
+ // interpolate
+ Array<Real> uq; // interpolated array to be computed
+ // compute the interpolation
+ fem->interpolateOnIntegrationPoints(const Array<Real> & u, Array<Real> & uq,
+ UInt nb_degree_of_freedom,
+ ElementType type);
+
+ // interpolated function can be integrated over the elements
+ Array<Real> int_val_on_elem;
+ // integrate
+ fem->integrate(const Array<Real> & uq, Array<Real> & int_uq,
+ UInt nb_degree_of_freedom, ElementType type);
+
+
+Another example below shows how to integrate stress and strain fields over
+elements assigned to a particular material:
+
+.. code-block:: c++
+
+ UInt sp_dim{3}; // spatial dimension
+ UInt m{1}; // material index of interest
+ const auto type{_tetrahedron_4}; // element type
+
+ // get the stress and strain arrays associated to the material index m
+ const auto & strain_vec = model.getMaterial(m).getGradU(type);
+ const auto & stress_vec = model.getMaterial(m).getStress(type);
+
+ // get the element filter for the material index
+ const auto & elem_filter = model.getMaterial(m).getElementFilter(type);
+
+ // initialize the integrated stress and strain arrays
+ Array<Real> int_strain_vec(elem_filter.getSize(), sp_dim * sp_dim,
+ "int_of_strain");
+ Array<Real> int_stress_vec(elem_filter.getSize(), sp_dim * sp_dim,
+ "int_of_stress");
+
+ // integrate the fields
+ model.getFEEngine().integrate(strain_vec, int_strain_vec, sp_dim * sp_dim, type,
+ _not_ghost, elem_filter);
+ model.getFEEngine().integrate(stress_vec, int_stress_vec, sp_dim * sp_dim, type,
+ _not_ghost, elem_filter);
+
+
+.. _sec-elements:
Elements
--------
The base for every Finite-Elements computation is its mesh and the elements that
are used within that mesh. The element types that can be used depend on the
-mesh, but also on the dimensionality of the problem (1D, 2D or 3D). In Akantu,
-several isoparametric Lagrangian element types are supported (and one
-serendipity element). Each of these types is discussed in some detail below,
+mesh, but also on the dimensionality of the problem (1D, 2D or 3D). In
+``Akantu``, several iso-parametric Lagrangian element types are supported (and
+one serendipity element). Each of these types is discussed in some detail below,
starting with the 1D-elements all the way to the 3D-elements. More detailed
information (shape function, location of Gaussian quadrature points, and so on)
can be found in Appendix app:elements.
-Isoparametric Elements
-......................
+Iso-parametric Elements
+.......................
1D
````
-In Akantu, there are two types of isoparametric elements defined in 1D. These
-element types are called ``_segment_2`` and ``_segment_3``, and are
-depicted schematically in :numref:`fig:elements:1D`. Some of the basic
-properties of these elements are listed in :numref:`tab:elements:1D`.
+There are two types of iso-parametric elements defined in 1D. These element
+types are called :cpp:enumerator:`_segment_2 <akantu::_segment_2>` and
+:cpp:enumerator:`_segment_3 <akantu::_segment_3>`, and are depicted
+schematically in :numref:`fig-elements-1D`. Some of the basic properties of
+these elements are listed in :numref:`tab-elements-1D`.
-.. _fig:elements:1D:
+.. _fig-elements-1D:
.. figure:: figures/elements/segments.svg
:align: center
- Schematic overview of the two 1D element types in Akantu. In each
- element, the node numbering as used in Akantu is indicated and also the
+ Schematic overview of the two 1D element types in ``Akantu``. In each
+ element, the node numbering as used in ``Akantu`` is indicated and also the
quadrature points are highlighted (gray circles).
-.. _tab:elements:1D:
-.. table:: Some basic properties of the two 1D isoparametric elements in Akantu
+.. _tab-elements-1D:
+.. csv-table:: Some basic properties of the two 1D iso-parametric elements in ``Akantu``
+ :header: "Element type", "Order", "#nodes", "#quad points"
- +--------------+---------+------+------+
- |Element |Order |#nodes|#quad |
- |type | | |points|
- +--------------+---------+------+------+
- |``_segment_2``|linear |2 |1 |
- +--------------+---------+------+------+
- |``_segment_3``|quadratic|3 |2 |
- +--------------+---------+------+------+
+ ":cpp:enumerator:`_segment_2 <akantu::_segment_2>`", "linear", 2, 1
+ ":cpp:enumerator:`_segment_3 <akantu::_segment_3>`", "quadratic", 3, 2
2D
````
-In Akantu, there are four types of isoparametric elements defined in 2D. These
-element types are called ``_triangle_3``, ``_triangle_6``,
-``_quadrangle_4`` and ``_quadrangle_8``, and all of them are depicted
-in :numref:`fig:elements:2D`. As with the 1D elements, some of the most basic
-properties of these elements are listed in :numref:`tab:elements:2D`. It is
-important to note that the first element is linear, the next two quadratic and
-the last one cubic. Furthermore, the last element type (``_quadrangle_8``)
+
+There are four types of iso-parametric elements defined in 2D. These element
+types are called :cpp:enumerator:`_triangle_3 <akantu::_triangle_3>`,
+:cpp:enumerator:`_triangle_6 <akantu::_triangle_6>`,
+:cpp:enumerator:`_quadrangle_4 <akantu::_quadrangle_4>` and
+:cpp:enumerator:`_quadrangle_8 <akantu::_quadrangle_8>`, and all of them are
+depicted in :numref:`fig-elements-2D`. As with the 1D elements, some of the most
+basic properties of these elements are listed in :numref:`tab-elements-2D`. It
+is important to note that the first element is linear, the next two quadratic
+and the last one cubic. Furthermore, the last element type (``_quadrangle_8``)
is not a Lagrangian but a serendipity element.
-.. _fig:elements:2D:
+.. _fig-elements-2D:
.. figure:: figures/elements/elements_2d.svg
:align: center
- Schematic overview of the four 2D element types in Akantu. In each
- element, the node numbering as used in Akantu is indicated and also the
+ Schematic overview of the four 2D element types in ``Akantu``. In each
+ element, the node numbering as used in ``Akantu`` is indicated and also the
quadrature points are highlighted (gray circles).
-.. _tab:elements:2D:
-.. table:: Some basic properties of the 2D isoparametric elements in Akantu
+.. _tab-elements-2D:
+.. csv-table:: Some basic properties of the 2D iso-parametric elements in ``Akantu``
+ :header: "Element type", "Order", "#nodes", "#quad points"
- +--------------------+----------+------+------+
- |Element |Order |#nodes|#quad |
- |type | | |points|
- +--------------------+----------+------+------+
- |``_triangle_3`` |linear |3 |1 |
- +--------------------+----------+------+------+
- |``_triangle_6`` |quadratic |6 |3 |
- +--------------------+----------+------+------+
- |``_quadrangle_4`` |linear |4 |4 |
- +--------------------+----------+------+------+
- |``_quadrangle_8`` |quadratic |8 |9 |
- +--------------------+----------+------+------+
+ ":cpp:enumerator:`_triangle_3 <akantu::_triangle_3>`", "linear", 3, 1
+ ":cpp:enumerator:`_triangle_6 <akantu::_triangle_6>`", "quadratic", 6, 3
+ ":cpp:enumerator:`_quadrangle_4 <akantu::_quadrangle_4>`", "linear", 4, 4
+ ":cpp:enumerator:`_quadrangle_8 <akantu::_quadrangle_8>`", "quadratic", 8, 9
3D
````
-In Akantu, there are three types of isoparametric elements defined in 3D. These
-element types are called ``_tetrahedron_4``, ``_tetrahedron_10`` and
-``_hexahedron_8``, and all of them are depicted schematically in
-:numref:`fig:elements:3D`. As with the 1D and 2D elements some of the most
-basic properties of these elements are listed in :numref:`tab:elements:3D`.
+In ``Akantu``, there are three types of iso-parametric elements defined in 3D.
+These element types are called :cpp:enumerator:`_tetrahedron_4
+<akantu::_tetrahedron_4>`, :cpp:enumerator:`_tetrahedron_10
+<akantu::_tetrahedron_10>` and :cpp:enumerator:`_hexadedron_8
+<akantu::_hexadedron_8>`, and all of them are depicted schematically in
+:numref:`fig-elements-3D`. As with the 1D and 2D elements some of the most basic
+properties of these elements are listed in :numref:`tab-elements-3D`.
-.. _fig:elements:3D:
+.. _fig-elements-3D:
.. figure:: figures/elements/elements_3d.svg
:align: center
- Schematic overview of the three 3D element types in Akantu. In each
- element, the node numbering as used in Akantu is indicated and also the
+ Schematic overview of the three 3D element types in ``Akantu``. In each
+ element, the node numbering as used in ``Akantu`` is indicated and also the
quadrature points are highlighted (gray circles).
-.. _tab:elements:3D:
-.. table:: Some basic properties of the 3D isoparametric elements in Akantu
-
- +--------------------+----------+------+------+
- |Element |Order |#nodes|#quad |
- |type | | |points|
- +--------------------+----------+------+------+
- |``_tetrahedron_4`` |linear |4 |1 |
- +--------------------+----------+------+------+
- |``_tetrahedron_10`` |quadratic |10 |4 |
- +--------------------+----------+------+------+
- |``_hexadedron_8`` |cubic |8 |8 |
- +--------------------+----------+------+------+
+.. _tab-elements-3D:
+.. csv-table:: Some basic properties of the 3D iso-parametric elements in ``Akantu``
+ :header: "Element type", "Order", "#nodes", "#quad points"
+
+ ":cpp:enumerator:`_tetrahedron_4 <akantu::_tetrahedron_4>`", "linear", 4, 1
+ ":cpp:enumerator:`_tetrahedron_10 <akantu::_tetrahedron_10>`", "quadratic", 10, 4
+ ":cpp:enumerator:`_hexadedron_8 <akantu::_hexadedron_8>`", "cubic", 8, 8
+
+Cohesive Elements
+.................
+
+The cohesive elements that have been implemented in ``Akantu`` are based
+on the work of Ortiz and Pandolfi :cite:`ortiz1999`. Their main
+properties are reported in :numref:`tab-coh-cohesive_elements`.
+
+.. _fig-smm-coh-cohesive2d:
+.. figure:: figures/elements/cohesive_2d_6.svg
+ :align: center
+
+ Cohesive element in 2D for quadratic triangular elements T6.
+
+.. _tab-coh-cohesive_elements:
+.. csv-table:: Some basic properties of the cohesive elements in ``Akantu``.
+ :header: "Element type", "Facet type", "Order", "#nodes", "#quad points"
+
+ ":cpp:enumerator:`_cohesive_1d_2 <_cohesive_1d_2>`", ":cpp:enumerator:`_point_1 <akantu::_point_1>`", "linear", 2, 1
+ ":cpp:enumerator:`_cohesive_2d_4 <akantu::_cohesive_2d_4>`", ":cpp:enumerator:`_segment_2 <akantu::_segment_2>`", "linear", 4, 1
+ ":cpp:enumerator:`_cohesive_2d_6 <akantu::_cohesive_2d_6>`", ":cpp:enumerator:`_segment_3 <akantu::_segment_3>`", "quadratic", 6, 2
+ ":cpp:enumerator:`_cohesive_3d_6 <akantu::_cohesive_3d_6>`", ":cpp:enumerator:`_triangle_3 <akantu::_triangle_3>`","linear", 6, 1
+ ":cpp:enumerator:`_cohesive_3d_12 <akantu::_cohesive_3d_12>`", ":cpp:enumerator:`_triangle_6 <akantu::_triangle_6>`", "quadratic", 12, 3
+
+
+Structural Elements
+...................
+
+Bernoulli Beam Elements
+```````````````````````
+
+These elements allow to compute the displacements and rotations of
+structures constituted by Bernoulli beams. ``Akantu`` defines them for
+both 2D and 3D problems respectively in the element types
+:cpp:enumerator:`_bernoulli_beam_2 <akantu::_bernoulli_beam_2>` and :cpp:enumerator:`_bernoulli_beam_3 <akantu::_bernoulli_beam_3>`. A
+schematic depiction of a beam element is shown in
+:numref:`fig-elements-bernoulli` and some of its properties are
+listed in :numref:`tab-elements-bernoulli`.
+
+.. note::
+ Beam elements are of mixed order: the axial displacement is
+ linearly interpolated while transverse displacements and rotations
+ use cubic shape functions.
+
+.. _fig-elements-bernoulli:
+.. figure:: figures/elements/bernoulli_2.svg
+ :align: center
+
+ Schematic depiction of a Bernoulli beam element (applied to 2D and
+ 3D) in ``Akantu``. The node numbering as used in ``Akantu`` is
+ indicated, and also the quadrature points are highlighted (gray
+ circles).
+
+.. _tab-elements-bernoulli:
+.. csv-table:: Some basic properties of the beam elements in ``Akantu``
+ :header: "Element type", "Dimension", "# nodes", "# quad. points", "# d.o.f."
+
+ ":cpp:enumerator:`_bernoulli_beam_2 <akantu::_bernoulli_beam_2>`", "2D", 2, 3, 6
+ ":cpp:enumerator:`_bernoulli_beam_3 <akantu::_bernoulli_beam_3>`", "3D", 2, 3, 12
diff --git a/doc/dev-doc/manual/figures/bc_and_ic_example.svg b/doc/dev-doc/manual/figures/bc_and_ic_example.svg
new file mode 100644
index 000000000..a9ae246da
--- /dev/null
+++ b/doc/dev-doc/manual/figures/bc_and_ic_example.svg
@@ -0,0 +1,1014 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="221.11139"
+ height="145.30687"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
+ sodipodi:docname="bc_and_ic_example.svg">
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="DotS"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="DotS"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path1663"
+ d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+ style="fill:#aa3030;fill-opacity:1;fill-rule:evenodd;stroke:#aa3030;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(0.2,0,0,0.2,1.48,0.2)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path1605"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill:#aa3030;fill-opacity:1;fill-rule:evenodd;stroke:#aa3030;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lend"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path1599"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill:#aa3030;fill-opacity:1;fill-rule:evenodd;stroke:#aa3030;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(-0.8,0,0,-0.8,-10,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <pattern
+ inkscape:collect="always"
+ xlink:href="#Strips1_1-7"
+ id="pattern2347-2"
+ patternTransform="matrix(0.66149863,0.67220297,-1.2006385,1.1815192,104.26974,154.07196)" />
+ <pattern
+ inkscape:stockid="Stripes 1:1"
+ id="Strips1_1-7"
+ patternTransform="translate(0,0) scale(10,10)"
+ height="1"
+ width="2"
+ patternUnits="userSpaceOnUse"
+ inkscape:collect="always">
+ <rect
+ id="rect1629-0"
+ height="2"
+ width="1"
+ y="-0.5"
+ x="0"
+ style="fill:black;stroke:none" />
+ </pattern>
+ <pattern
+ inkscape:collect="always"
+ xlink:href="#Strips1_1-7-2"
+ id="pattern2347-2-2"
+ patternTransform="matrix(0.66149863,0.67220297,-1.2006385,1.1815192,104.26974,154.07196)" />
+ <pattern
+ inkscape:stockid="Stripes 1:1"
+ id="Strips1_1-7-2"
+ patternTransform="translate(0,0) scale(10,10)"
+ height="1"
+ width="2"
+ patternUnits="userSpaceOnUse"
+ inkscape:collect="always">
+ <rect
+ id="rect1629-0-8"
+ height="2"
+ width="1"
+ y="-0.5"
+ x="0"
+ style="fill:black;stroke:none" />
+ </pattern>
+ <pattern
+ inkscape:collect="always"
+ xlink:href="#Strips1_1-7-1"
+ id="pattern2347-2-3"
+ patternTransform="matrix(0.66149863,0.67220297,-1.2006385,1.1815192,104.26974,154.07196)" />
+ <pattern
+ inkscape:stockid="Stripes 1:1"
+ id="Strips1_1-7-1"
+ patternTransform="translate(0,0) scale(10,10)"
+ height="1"
+ width="2"
+ patternUnits="userSpaceOnUse"
+ inkscape:collect="always">
+ <rect
+ id="rect1629-0-9"
+ height="2"
+ width="1"
+ y="-0.5"
+ x="0"
+ style="fill:black;stroke:none" />
+ </pattern>
+ <pattern
+ inkscape:collect="always"
+ xlink:href="#Strips1_1-7-0"
+ id="pattern2347-2-1"
+ patternTransform="matrix(0.66149863,0.67220297,-1.2006385,1.1815192,104.26974,154.07196)" />
+ <pattern
+ inkscape:stockid="Stripes 1:1"
+ id="Strips1_1-7-0"
+ patternTransform="translate(0,0) scale(10,10)"
+ height="1"
+ width="2"
+ patternUnits="userSpaceOnUse"
+ inkscape:collect="always">
+ <rect
+ id="rect1629-0-6"
+ height="2"
+ width="1"
+ y="-0.5"
+ x="0"
+ style="fill:black;stroke:none" />
+ </pattern>
+ <pattern
+ inkscape:collect="always"
+ xlink:href="#Strips1_1-7-6"
+ id="pattern2347-2-7"
+ patternTransform="matrix(0.66149863,0.67220297,-1.2006385,1.1815192,104.26974,154.07196)" />
+ <pattern
+ inkscape:stockid="Stripes 1:1"
+ id="Strips1_1-7-6"
+ patternTransform="translate(0,0) scale(10,10)"
+ height="1"
+ width="2"
+ patternUnits="userSpaceOnUse"
+ inkscape:collect="always">
+ <rect
+ id="rect1629-0-5"
+ height="2"
+ width="1"
+ y="-0.5"
+ x="0"
+ style="fill:black;stroke:none" />
+ </pattern>
+ <pattern
+ inkscape:collect="always"
+ xlink:href="#Strips1_1-7-2-9"
+ id="pattern2347-2-2-6"
+ patternTransform="matrix(0.66149863,0.67220297,-1.2006385,1.1815192,104.26974,154.07196)" />
+ <pattern
+ inkscape:stockid="Stripes 1:1"
+ id="Strips1_1-7-2-9"
+ patternTransform="translate(0,0) scale(10,10)"
+ height="1"
+ width="2"
+ patternUnits="userSpaceOnUse"
+ inkscape:collect="always">
+ <rect
+ id="rect1629-0-8-3"
+ height="2"
+ width="1"
+ y="-0.5"
+ x="0"
+ style="fill:black;stroke:none" />
+ </pattern>
+ <pattern
+ inkscape:collect="always"
+ xlink:href="#Strips1_1-7-1-4"
+ id="pattern2347-2-3-7"
+ patternTransform="matrix(0.66149863,0.67220297,-1.2006385,1.1815192,104.26974,154.07196)" />
+ <pattern
+ inkscape:stockid="Stripes 1:1"
+ id="Strips1_1-7-1-4"
+ patternTransform="translate(0,0) scale(10,10)"
+ height="1"
+ width="2"
+ patternUnits="userSpaceOnUse"
+ inkscape:collect="always">
+ <rect
+ id="rect1629-0-9-5"
+ height="2"
+ width="1"
+ y="-0.5"
+ x="0"
+ style="fill:black;stroke:none" />
+ </pattern>
+ <pattern
+ inkscape:collect="always"
+ xlink:href="#Strips1_1-7-0-5"
+ id="pattern2347-2-1-2"
+ patternTransform="matrix(0.66149863,0.67220297,-1.2006385,1.1815192,104.26974,154.07196)" />
+ <pattern
+ inkscape:stockid="Stripes 1:1"
+ id="Strips1_1-7-0-5"
+ patternTransform="translate(0,0) scale(10,10)"
+ height="1"
+ width="2"
+ patternUnits="userSpaceOnUse"
+ inkscape:collect="always">
+ <rect
+ id="rect1629-0-6-4"
+ height="2"
+ width="1"
+ y="-0.5"
+ x="0"
+ style="fill:black;stroke:none" />
+ </pattern>
+ <marker
+ inkscape:stockid="DotS"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="DotS-3"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ inkscape:connector-curvature="0"
+ id="path1663-6"
+ d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+ style="fill:#aa3030;fill-opacity:1;fill-rule:evenodd;stroke:#aa3030;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(0.2,0,0,0.2,1.48,0.2)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-7"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ inkscape:connector-curvature="0"
+ id="path1605-5"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill:#aa3030;fill-opacity:1;fill-rule:evenodd;stroke:#aa3030;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="DotS"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="DotS-5"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ inkscape:connector-curvature="0"
+ id="path1663-62"
+ d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+ style="fill:#aa3030;fill-opacity:1;fill-rule:evenodd;stroke:#aa3030;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(0.2,0,0,0.2,1.48,0.2)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-9"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ inkscape:connector-curvature="0"
+ id="path1605-1"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill:#aa3030;fill-opacity:1;fill-rule:evenodd;stroke:#aa3030;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="DotS"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="DotS-7"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ inkscape:connector-curvature="0"
+ id="path1663-0"
+ d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+ style="fill:#aa3030;fill-opacity:1;fill-rule:evenodd;stroke:#aa3030;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(0.2,0,0,0.2,1.48,0.2)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-93"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ inkscape:connector-curvature="0"
+ id="path1605-6"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill:#aa3030;fill-opacity:1;fill-rule:evenodd;stroke:#aa3030;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="DotS"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="DotS-6"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ inkscape:connector-curvature="0"
+ id="path1663-2"
+ d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+ style="fill:#aa3030;fill-opacity:1;fill-rule:evenodd;stroke:#aa3030;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(0.2,0,0,0.2,1.48,0.2)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-6"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ inkscape:connector-curvature="0"
+ id="path1605-18"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill:#aa3030;fill-opacity:1;fill-rule:evenodd;stroke:#aa3030;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="DotS"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="DotS-9"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ inkscape:connector-curvature="0"
+ id="path1663-20"
+ d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+ style="fill:#aa3030;fill-opacity:1;fill-rule:evenodd;stroke:#aa3030;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(0.2,0,0,0.2,1.48,0.2)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-2"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ inkscape:connector-curvature="0"
+ id="path1605-3"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill:#aa3030;fill-opacity:1;fill-rule:evenodd;stroke:#aa3030;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="DotS"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="DotS-92"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ inkscape:connector-curvature="0"
+ id="path1663-28"
+ d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+ style="fill:#aa3030;fill-opacity:1;fill-rule:evenodd;stroke:#aa3030;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(0.2,0,0,0.2,1.48,0.2)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-97"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ inkscape:connector-curvature="0"
+ id="path1605-36"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill:#aa3030;fill-opacity:1;fill-rule:evenodd;stroke:#aa3030;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="DotS"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="DotS-2"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ inkscape:connector-curvature="0"
+ id="path1663-9"
+ d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+ style="fill:#aa3030;fill-opacity:1;fill-rule:evenodd;stroke:#aa3030;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(0.2,0,0,0.2,1.48,0.2)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-3"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ inkscape:connector-curvature="0"
+ id="path1605-19"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill:#aa3030;fill-opacity:1;fill-rule:evenodd;stroke:#aa3030;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)" />
+ </marker>
+ <path
+ id="g0-116"
+ d="m 2.247,-4.364 h 1.026 c 0.218,0 0.327,0 0.327,-0.218 0,-0.12 -0.11,-0.12 -0.305,-0.12 h -0.96 c 0.392,-1.549 0.447,-1.767 0.447,-1.833 0,-0.185 -0.131,-0.294 -0.317,-0.294 -0.032,0 -0.338,0.01 -0.436,0.393 L 1.604,-4.702 H 0.578 c -0.218,0 -0.327,0 -0.327,0.207 0,0.131 0.087,0.131 0.305,0.131 h 0.96 c -0.785,3.099 -0.829,3.284 -0.829,3.48 0,0.59 0.415,1.004 1.004,1.004 1.113,0 1.734,-1.593 1.734,-1.68 0,-0.11 -0.087,-0.11 -0.13,-0.11 -0.099,0 -0.11,0.034 -0.164,0.154 -0.471,1.134 -1.047,1.396 -1.418,1.396 -0.23,0 -0.338,-0.142 -0.338,-0.502 0,-0.262 0.021,-0.338 0.065,-0.523 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="g1-22"
+ d="M 4.69,-6.098 V -6.436 H 0.754 v 0.338 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="g1-40"
+ d="m 3.61,2.618 c 0,-0.033 0,-0.054 -0.185,-0.24 -1.363,-1.374 -1.712,-3.436 -1.712,-5.105 0,-1.898 0.414,-3.797 1.756,-5.16 0.142,-0.131 0.142,-0.153 0.142,-0.186 0,-0.076 -0.044,-0.109 -0.11,-0.109 -0.108,0 -1.09,0.742 -1.734,2.127 -0.556,1.2 -0.687,2.411 -0.687,3.328 0,0.85 0.12,2.17 0.72,3.403 0.655,1.342 1.593,2.051 1.702,2.051 0.065,0 0.109,-0.032 0.109,-0.109 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="g1-41"
+ d="m 3.153,-2.727 c 0,-0.851 -0.12,-2.171 -0.72,-3.404 C 1.778,-7.473 0.84,-8.18 0.73,-8.18 c -0.066,0 -0.11,0.043 -0.11,0.108 0,0.033 0,0.055 0.208,0.251 1.072,1.079 1.692,2.814 1.692,5.094 0,1.865 -0.404,3.785 -1.756,5.16 -0.142,0.13 -0.142,0.152 -0.142,0.185 0,0.066 0.043,0.11 0.109,0.11 0.109,0 1.09,-0.743 1.734,-2.128 0.557,-1.2 0.688,-2.41 0.688,-3.327 z"
+ inkscape:connector-curvature="0" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2.8"
+ inkscape:cx="123.97301"
+ inkscape:cy="64.871414"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1920"
+ inkscape:window-height="1152"
+ inkscape:window-x="0"
+ inkscape:window-y="24"
+ inkscape:window-maximized="1"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-17.392997,-29.66231)">
+ <rect
+ style="fill:#afafaf;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="rect3753"
+ width="99"
+ height="99"
+ x="50.5"
+ y="42.862186" />
+ <g
+ id="g1831">
+ <g
+ id="g1663">
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-5-9"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065521"
+ y="169.21198" />
+ <rect
+ style="fill:url(#pattern2347-2);fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-3"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065536"
+ y="169.21198" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.09244144;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 37.300383,169.7582 h 26.39924"
+ id="path2466"
+ inkscape:connector-curvature="0" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468"
+ cx="41.853683"
+ cy="166.79083"
+ r="2.9673686" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-6"
+ cx="59.144897"
+ cy="166.79083"
+ r="2.9673686" />
+ <path
+ sodipodi:type="star"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.88414478;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2355-2"
+ sodipodi:sides="3"
+ sodipodi:cx="50.5"
+ sodipodi:cy="156.21994"
+ sodipodi:r1="14.357757"
+ sodipodi:r2="7.1788788"
+ sodipodi:arg1="0.52359878"
+ sodipodi:arg2="1.5707963"
+ inkscape:flatsided="true"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ d="m 62.934182,163.39882 -24.868364,0 L 50.5,141.86218 Z"
+ inkscape:transform-center-y="-3.5894424"
+ inkscape:transform-center-x="8.1207044e-06" />
+ </g>
+ <g
+ id="g1663-9"
+ transform="translate(33.000004)">
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-5-9-7"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065521"
+ y="169.21198" />
+ <rect
+ style="fill:url(#pattern2347-2-2);fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-3-3"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065536"
+ y="169.21198" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.09244144;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 37.300383,169.7582 h 26.39924"
+ id="path2466-6"
+ inkscape:connector-curvature="0" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-1"
+ cx="41.853683"
+ cy="166.79083"
+ r="2.9673686" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-6-2"
+ cx="59.144897"
+ cy="166.79083"
+ r="2.9673686" />
+ <path
+ sodipodi:type="star"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.88414478;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2355-2-9"
+ sodipodi:sides="3"
+ sodipodi:cx="50.5"
+ sodipodi:cy="156.21994"
+ sodipodi:r1="14.357757"
+ sodipodi:r2="7.1788788"
+ sodipodi:arg1="0.52359878"
+ sodipodi:arg2="1.5707963"
+ inkscape:flatsided="true"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ d="m 62.934182,163.39882 -24.868364,0 L 50.5,141.86218 Z"
+ inkscape:transform-center-y="-3.5894424"
+ inkscape:transform-center-x="8.1207044e-06" />
+ </g>
+ <g
+ id="g1663-4"
+ transform="translate(65.999999)">
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-5-9-78"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065521"
+ y="169.21198" />
+ <rect
+ style="fill:url(#pattern2347-2-3);fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-3-4"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065536"
+ y="169.21198" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.09244144;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 37.300383,169.7582 h 26.39924"
+ id="path2466-5"
+ inkscape:connector-curvature="0" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-0"
+ cx="41.853683"
+ cy="166.79083"
+ r="2.9673686" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-6-3"
+ cx="59.144897"
+ cy="166.79083"
+ r="2.9673686" />
+ <path
+ sodipodi:type="star"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.88414478;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2355-2-6"
+ sodipodi:sides="3"
+ sodipodi:cx="50.5"
+ sodipodi:cy="156.21994"
+ sodipodi:r1="14.357757"
+ sodipodi:r2="7.1788788"
+ sodipodi:arg1="0.52359878"
+ sodipodi:arg2="1.5707963"
+ inkscape:flatsided="true"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ d="m 62.934182,163.39882 -24.868364,0 L 50.5,141.86218 Z"
+ inkscape:transform-center-y="-3.5894424"
+ inkscape:transform-center-x="8.1207044e-06" />
+ </g>
+ <g
+ id="g1663-3"
+ transform="translate(99)">
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-5-9-2"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065521"
+ y="169.21198" />
+ <rect
+ style="fill:url(#pattern2347-2-1);fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-3-0"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065536"
+ y="169.21198" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.09244144;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 37.300383,169.7582 h 26.39924"
+ id="path2466-61"
+ inkscape:connector-curvature="0" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-5"
+ cx="41.853683"
+ cy="166.79083"
+ r="2.9673686" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-6-5"
+ cx="59.144897"
+ cy="166.79083"
+ r="2.9673686" />
+ <path
+ sodipodi:type="star"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.88414478;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2355-2-4"
+ sodipodi:sides="3"
+ sodipodi:cx="50.5"
+ sodipodi:cy="156.21994"
+ sodipodi:r1="14.357757"
+ sodipodi:r2="7.1788788"
+ sodipodi:arg1="0.52359878"
+ sodipodi:arg2="1.5707963"
+ inkscape:flatsided="true"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ d="m 62.934182,163.39882 -24.868364,0 L 50.5,141.86218 Z"
+ inkscape:transform-center-y="-3.5894424"
+ inkscape:transform-center-x="8.1207044e-06" />
+ </g>
+ </g>
+ <g
+ transform="rotate(90,100,92.362184)"
+ id="g1831-7">
+ <g
+ id="g1663-44">
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-5-9-3"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065521"
+ y="169.21198" />
+ <rect
+ style="fill:url(#pattern2347-2-7);fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-3-07"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065536"
+ y="169.21198" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.09244144;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 37.300383,169.7582 h 26.39924"
+ id="path2466-8"
+ inkscape:connector-curvature="0" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-68"
+ cx="41.853683"
+ cy="166.79083"
+ r="2.9673686" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-6-8"
+ cx="59.144897"
+ cy="166.79083"
+ r="2.9673686" />
+ <path
+ sodipodi:type="star"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.88414478;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2355-2-43"
+ sodipodi:sides="3"
+ sodipodi:cx="50.5"
+ sodipodi:cy="156.21994"
+ sodipodi:r1="14.357757"
+ sodipodi:r2="7.1788788"
+ sodipodi:arg1="0.52359878"
+ sodipodi:arg2="1.5707963"
+ inkscape:flatsided="true"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ d="m 62.934182,163.39882 -24.868364,0 L 50.5,141.86218 Z"
+ inkscape:transform-center-y="-3.5894424"
+ inkscape:transform-center-x="8.1207044e-06" />
+ </g>
+ <g
+ id="g1663-9-1"
+ transform="translate(33.000004)">
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-5-9-7-4"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065521"
+ y="169.21198" />
+ <rect
+ style="fill:url(#pattern2347-2-2-6);fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-3-3-9"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065536"
+ y="169.21198" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.09244144;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 37.300383,169.7582 h 26.39924"
+ id="path2466-6-2"
+ inkscape:connector-curvature="0" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-1-0"
+ cx="41.853683"
+ cy="166.79083"
+ r="2.9673686" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-6-2-6"
+ cx="59.144897"
+ cy="166.79083"
+ r="2.9673686" />
+ <path
+ sodipodi:type="star"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.88414478;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2355-2-9-8"
+ sodipodi:sides="3"
+ sodipodi:cx="50.5"
+ sodipodi:cy="156.21994"
+ sodipodi:r1="14.357757"
+ sodipodi:r2="7.1788788"
+ sodipodi:arg1="0.52359878"
+ sodipodi:arg2="1.5707963"
+ inkscape:flatsided="true"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ d="m 62.934182,163.39882 -24.868364,0 L 50.5,141.86218 Z"
+ inkscape:transform-center-y="-3.5894424"
+ inkscape:transform-center-x="8.1207044e-06" />
+ </g>
+ <g
+ id="g1663-4-9"
+ transform="translate(65.999999)">
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-5-9-78-2"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065521"
+ y="169.21198" />
+ <rect
+ style="fill:url(#pattern2347-2-3-7);fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-3-4-6"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065536"
+ y="169.21198" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.09244144;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 37.300383,169.7582 h 26.39924"
+ id="path2466-5-6"
+ inkscape:connector-curvature="0" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-0-4"
+ cx="41.853683"
+ cy="166.79083"
+ r="2.9673686" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-6-3-9"
+ cx="59.144897"
+ cy="166.79083"
+ r="2.9673686" />
+ <path
+ sodipodi:type="star"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.88414478;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2355-2-6-5"
+ sodipodi:sides="3"
+ sodipodi:cx="50.5"
+ sodipodi:cy="156.21994"
+ sodipodi:r1="14.357757"
+ sodipodi:r2="7.1788788"
+ sodipodi:arg1="0.52359878"
+ sodipodi:arg2="1.5707963"
+ inkscape:flatsided="true"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ d="m 62.934182,163.39882 -24.868364,0 L 50.5,141.86218 Z"
+ inkscape:transform-center-y="-3.5894424"
+ inkscape:transform-center-x="8.1207044e-06" />
+ </g>
+ <g
+ id="g1663-3-0"
+ transform="translate(99)">
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-5-9-2-4"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065521"
+ y="169.21198" />
+ <rect
+ style="fill:url(#pattern2347-2-1-2);fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-3-0-8"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065536"
+ y="169.21198" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.09244144;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 37.300383,169.7582 h 26.39924"
+ id="path2466-61-7"
+ inkscape:connector-curvature="0" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-5-1"
+ cx="41.853683"
+ cy="166.79083"
+ r="2.9673686" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-6-5-7"
+ cx="59.144897"
+ cy="166.79083"
+ r="2.9673686" />
+ <path
+ sodipodi:type="star"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.88414478;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2355-2-4-2"
+ sodipodi:sides="3"
+ sodipodi:cx="50.5"
+ sodipodi:cy="156.21994"
+ sodipodi:r1="14.357757"
+ sodipodi:r2="7.1788788"
+ sodipodi:arg1="0.52359878"
+ sodipodi:arg2="1.5707963"
+ inkscape:flatsided="true"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ d="m 62.934182,163.39882 -24.868364,0 L 50.5,141.86218 Z"
+ inkscape:transform-center-y="-3.5894424"
+ inkscape:transform-center-x="8.1207044e-06" />
+ </g>
+ </g>
+ <path
+ style="fill:none;stroke:#aa3030;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#DotS);marker-end:url(#Arrow1Mend)"
+ d="m 149.5,42.862186 h 52.75091"
+ id="path1594"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;stroke:#aa3030;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#DotS-3);marker-end:url(#Arrow1Mend-7)"
+ d="m 149.5,59.362185 h 52.75091"
+ id="path1594-3"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;stroke:#aa3030;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#DotS-5);marker-end:url(#Arrow1Mend-9)"
+ d="m 149.5,75.862183 h 52.75091"
+ id="path1594-2"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;stroke:#aa3030;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#DotS-7);marker-end:url(#Arrow1Mend-93)"
+ d="m 149.5,92.362188 h 52.75091"
+ id="path1594-0"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;stroke:#aa3030;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#DotS-9);marker-end:url(#Arrow1Mend-2)"
+ d="m 149.5,108.86218 h 52.75091"
+ id="path1594-75"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;stroke:#aa3030;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#DotS-92);marker-end:url(#Arrow1Mend-97)"
+ d="m 149.5,125.36218 h 52.75091"
+ id="path1594-1"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;stroke:#aa3030;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#DotS-2);marker-end:url(#Arrow1Mend-3)"
+ d="m 149.5,141.86218 h 52.75091"
+ id="path1594-4"
+ inkscape:connector-curvature="0" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
+ x="239.53584"
+ y="93.540611"
+ id="text3207"><tspan
+ sodipodi:role="line"
+ id="tspan3205"
+ x="239.53584"
+ y="128.93124"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'DejaVu Math TeX Gyre';-inkscape-font-specification:'DejaVu Math TeX Gyre'" /></text>
+ <g
+ id="page1"
+ transform="matrix(1.6728997,0,0,1.6728997,-2918.6973,-2397.8511)">
+ <use
+ id="use3278"
+ y="1489.41"
+ xlink:href="#g1-22"
+ x="1872.17"
+ width="100%"
+ height="100%" />
+ <use
+ id="use3280"
+ y="1491.42"
+ xlink:href="#g0-116"
+ x="1872.02"
+ width="100%"
+ height="100%" />
+ <use
+ id="use3282"
+ y="1491.42"
+ xlink:href="#g1-40"
+ x="1875.96"
+ width="100%"
+ height="100%" />
+ <use
+ id="use3284"
+ y="1491.42"
+ xlink:href="#g0-116"
+ x="1880.1899"
+ width="100%"
+ height="100%" />
+ <use
+ id="use3286"
+ y="1491.42"
+ xlink:href="#g1-41"
+ x="1884.11"
+ width="100%"
+ height="100%" />
+ </g>
+ </g>
+</svg>
diff --git a/doc/dev-doc/manual/figures/beam_example.svg b/doc/dev-doc/manual/figures/beam_example.svg
new file mode 100644
index 000000000..eb5dc6dbd
--- /dev/null
+++ b/doc/dev-doc/manual/figures/beam_example.svg
@@ -0,0 +1,594 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="176.396pt"
+ height="76.207pt"
+ viewBox="0 0 176.396 76.207"
+ version="1.2"
+ id="svg227"
+ sodipodi:docname="beam_example.svg"
+ inkscape:version="1.0.2 (e86c870879, 2021-01-15)">
+ <metadata
+ id="metadata231">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="744"
+ inkscape:window-height="480"
+ id="namedview229"
+ showgrid="false"
+ inkscape:zoom="5.472063"
+ inkscape:cx="117.59733"
+ inkscape:cy="50.804667"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg227" />
+ <defs
+ id="defs52">
+ <g
+ id="g38">
+ <symbol
+ overflow="visible"
+ id="glyph0-0">
+ <path
+ style="stroke:none;"
+ d=""
+ id="path2" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-1">
+ <path
+ style="stroke:none;"
+ d="M 3.84375 -4.5625 C 3.40625 -4.734375 3.125 -4.8125 2.828125 -4.8125 C 2.5625 -4.8125 2.328125 -4.71875 2.046875 -4.5625 C 1.6875 -4.34375 1.25 -3.984375 1.03125 -3.734375 C 0.625 -3.265625 0.234375 -1.796875 0.234375 -0.78125 C 0.234375 -0.1875 0.359375 0.109375 0.59375 0.109375 C 0.8125 0.109375 1 0.03125 1.328125 -0.1875 L 1.40625 -0.25 C 2.046875 -0.703125 2.703125 -1.421875 3.078125 -2.078125 L 2.3125 2.046875 C 2.265625 2.28125 2.15625 2.484375 2.046875 2.609375 L 2.140625 2.75 C 2.4375 2.671875 2.75 2.609375 2.984375 2.578125 C 3.03125 2.046875 3.09375 1.5 3.1875 1 L 4.03125 -3.546875 C 4.140625 -4.15625 4.203125 -4.421875 4.3125 -4.71875 L 4.171875 -4.78125 Z M 3.296875 -3.09375 C 3.1875 -2.59375 2.953125 -2.15625 2.59375 -1.6875 C 2.078125 -1.0625 1.46875 -0.59375 1.171875 -0.59375 C 1.03125 -0.59375 0.953125 -0.796875 0.953125 -1.125 C 0.953125 -2.078125 1.390625 -3.578125 1.796875 -4.078125 C 1.921875 -4.234375 2.21875 -4.3125 2.53125 -4.3125 C 2.8125 -4.3125 3.015625 -4.25 3.5 -4.046875 Z M 3.296875 -3.09375 "
+ id="path5" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-2">
+ <path
+ style="stroke:none;"
+ d="M 1.03125 -6.625 L 1.5 -6.59375 C 1.734375 -6.59375 1.84375 -6.515625 1.84375 -6.34375 C 1.84375 -6.265625 1.78125 -5.734375 1.75 -5.578125 L 1.03125 -1.25 C 0.890625 -0.359375 0.859375 -0.3125 0.453125 -0.28125 L 0.09375 -0.25 L 0.0625 0.03125 L 0.390625 0.03125 L 1.328125 0 L 2.265625 0.03125 L 2.578125 0.03125 L 2.609375 -0.25 L 2.09375 -0.28125 C 1.828125 -0.296875 1.734375 -0.375 1.734375 -0.5625 C 1.734375 -0.625 1.75 -0.734375 1.75 -0.78125 L 2.640625 -6.109375 C 2.71875 -6.5 2.765625 -6.546875 3.15625 -6.59375 L 3.5 -6.625 L 3.53125 -6.890625 L 3.4375 -6.890625 L 2.46875 -6.875 C 2.296875 -6.875 2.125 -6.875 1.6875 -6.875 L 1.0625 -6.890625 Z M 1.03125 -6.625 "
+ id="path8" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-3">
+ <path
+ style="stroke:none;"
+ d="M 9.34375 -6.625 L 9.375 -6.890625 L 9.09375 -6.890625 C 8.8125 -6.875 8.65625 -6.875 8.53125 -6.875 L 8.296875 -6.875 L 7.703125 -6.890625 L 7.546875 -6.59375 C 7.28125 -6.078125 7.09375 -5.734375 6.859375 -5.34375 L 4.40625 -1.140625 L 2.90625 -6.890625 L 2.625 -6.890625 C 2.359375 -6.875 2.203125 -6.875 2.078125 -6.875 L 1.828125 -6.875 L 1.25 -6.890625 L 1.21875 -6.625 L 1.46875 -6.59375 C 2 -6.5625 2.171875 -6.453125 2.171875 -6.203125 C 2.171875 -6.140625 2.15625 -6.078125 2.140625 -6 L 0.703125 -0.921875 C 0.53125 -0.40625 0.390625 -0.28125 -0.15625 -0.234375 L -0.1875 0.03125 L 0.203125 0.015625 C 0.53125 0.015625 0.78125 0 0.921875 0 C 1.0625 0 1.328125 0.015625 1.671875 0.015625 L 2.03125 0.03125 L 2.046875 -0.25 L 1.671875 -0.28125 C 1.359375 -0.296875 1.1875 -0.40625 1.1875 -0.546875 C 1.1875 -0.640625 1.203125 -0.78125 1.25 -0.953125 L 2.5 -5.578125 L 3.984375 0.171875 L 4.203125 0.171875 L 4.78125 -0.859375 C 5.546875 -2.265625 6.484375 -3.875 7.15625 -4.984375 L 7.671875 -5.859375 L 7.3125 -0.671875 C 7.265625 -0.390625 7.140625 -0.3125 6.640625 -0.28125 L 6.328125 -0.25 L 6.296875 0.03125 L 6.765625 0.015625 C 7.1875 0.015625 7.484375 0 7.640625 0 C 7.796875 0 8.09375 0.015625 8.5 0.015625 L 8.96875 0.03125 L 8.984375 -0.25 L 8.625 -0.28125 C 8.265625 -0.3125 8.078125 -0.421875 8.078125 -0.625 L 8.09375 -1 L 8.5 -6.296875 C 8.515625 -6.484375 8.671875 -6.578125 8.96875 -6.59375 Z M 9.34375 -6.625 "
+ id="path11" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-0">
+ <path
+ style="stroke:none;"
+ d=""
+ id="path14" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-1">
+ <path
+ style="stroke:none;"
+ d="M 0.46875 -3.875 L 0.53125 -3.875 L 1.4375 -4.265625 C 1.4375 -4.28125 1.453125 -4.28125 1.453125 -4.28125 C 1.5 -4.28125 1.515625 -4.21875 1.515625 -4.046875 L 1.515625 -0.671875 C 1.515625 -0.3125 1.4375 -0.234375 1.0625 -0.203125 L 0.671875 -0.1875 L 0.671875 0.015625 C 1.734375 0 1.734375 0 1.8125 0 C 1.90625 0 2.0625 0 2.296875 0 C 2.390625 0.015625 2.625 0.015625 2.90625 0.015625 L 2.90625 -0.1875 L 2.546875 -0.203125 C 2.171875 -0.234375 2.09375 -0.3125 2.09375 -0.671875 L 2.09375 -4.796875 L 2 -4.84375 C 1.546875 -4.59375 1.046875 -4.390625 0.421875 -4.171875 Z M 0.46875 -3.875 "
+ id="path17" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-2">
+ <path
+ style="stroke:none;"
+ d="M 0.109375 -0.15625 L 0.109375 0.015625 C 1.421875 0 1.421875 0 1.671875 0 C 1.921875 0 1.921875 0 3.265625 0.015625 C 3.25 -0.125 3.25 -0.1875 3.25 -0.296875 C 3.25 -0.390625 3.25 -0.453125 3.265625 -0.609375 C 2.453125 -0.578125 2.140625 -0.5625 0.84375 -0.53125 L 2.125 -1.875 C 2.796875 -2.59375 3 -2.984375 3 -3.5 C 3 -4.3125 2.453125 -4.796875 1.578125 -4.796875 C 1.078125 -4.796875 0.734375 -4.65625 0.390625 -4.3125 L 0.265625 -3.359375 L 0.46875 -3.359375 L 0.5625 -3.6875 C 0.671875 -4.09375 0.921875 -4.265625 1.390625 -4.265625 C 2 -4.265625 2.375 -3.890625 2.375 -3.296875 C 2.375 -2.78125 2.078125 -2.265625 1.296875 -1.421875 Z M 0.109375 -0.15625 "
+ id="path20" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-0">
+ <path
+ style="stroke:none;"
+ d=""
+ id="path23" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-1">
+ <path
+ style="stroke:none;"
+ d="M 3.21875 -5.359375 L 3.21875 -5.890625 L 0.109375 -5.890625 L 0.109375 -5.359375 Z M 3.21875 -5.359375 "
+ id="path26" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-2">
+ <path
+ style="stroke:none;"
+ d="M 0.671875 -5.53125 L 0.765625 -5.53125 L 2.046875 -6.109375 C 2.0625 -6.125 2.078125 -6.125 2.078125 -6.125 C 2.140625 -6.125 2.15625 -6.03125 2.15625 -5.796875 L 2.15625 -0.953125 C 2.15625 -0.4375 2.046875 -0.328125 1.515625 -0.296875 L 0.953125 -0.265625 L 0.953125 0.03125 C 2.5 0 2.5 0 2.609375 0 C 2.734375 0 2.953125 0 3.296875 0.015625 C 3.40625 0.015625 3.765625 0.015625 4.171875 0.03125 L 4.171875 -0.265625 L 3.65625 -0.296875 C 3.09375 -0.328125 3 -0.4375 3 -0.953125 L 3 -6.875 L 2.859375 -6.921875 C 2.21875 -6.578125 1.5 -6.28125 0.59375 -5.96875 Z M 0.671875 -5.53125 "
+ id="path29" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-3">
+ <path
+ style="stroke:none;"
+ d="M 0.15625 -0.234375 L 0.15625 0.03125 C 2.03125 0 2.03125 0 2.375 0 C 2.734375 0 2.734375 0 4.671875 0.03125 C 4.640625 -0.171875 4.640625 -0.28125 4.640625 -0.421875 C 4.640625 -0.546875 4.640625 -0.640625 4.671875 -0.875 C 3.515625 -0.8125 3.0625 -0.8125 1.21875 -0.765625 L 3.03125 -2.6875 C 4 -3.71875 4.296875 -4.265625 4.296875 -5.015625 C 4.296875 -6.15625 3.515625 -6.875 2.25 -6.875 C 1.53125 -6.875 1.046875 -6.671875 0.5625 -6.171875 L 0.390625 -4.8125 L 0.671875 -4.8125 L 0.8125 -5.28125 C 0.96875 -5.859375 1.328125 -6.09375 2 -6.09375 C 2.84375 -6.09375 3.40625 -5.5625 3.40625 -4.71875 C 3.40625 -3.96875 2.984375 -3.234375 1.859375 -2.03125 Z M 0.15625 -0.234375 "
+ id="path32" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-4">
+ <path
+ style="stroke:none;"
+ d="M 0.421875 -4.96875 L 0.734375 -4.96875 L 0.921875 -5.515625 C 1.03125 -5.859375 1.65625 -6.203125 2.171875 -6.203125 C 2.828125 -6.203125 3.34375 -5.671875 3.34375 -5.046875 C 3.34375 -4.296875 2.765625 -3.671875 2.046875 -3.671875 C 1.96875 -3.671875 1.859375 -3.671875 1.734375 -3.6875 L 1.578125 -3.703125 L 1.46875 -3.171875 L 1.53125 -3.109375 C 1.90625 -3.28125 2.109375 -3.328125 2.375 -3.328125 C 3.203125 -3.328125 3.671875 -2.796875 3.671875 -1.890625 C 3.671875 -0.875 3.078125 -0.203125 2.140625 -0.203125 C 1.6875 -0.203125 1.28125 -0.359375 0.984375 -0.640625 C 0.734375 -0.859375 0.609375 -1.09375 0.421875 -1.625 L 0.15625 -1.53125 C 0.359375 -0.921875 0.4375 -0.5625 0.5 -0.0625 C 1.03125 0.125 1.46875 0.203125 1.828125 0.203125 C 2.625 0.203125 3.546875 -0.25 4.09375 -0.921875 C 4.4375 -1.328125 4.609375 -1.765625 4.609375 -2.234375 C 4.609375 -2.71875 4.40625 -3.125 4.046875 -3.375 C 3.796875 -3.5625 3.5625 -3.640625 3.078125 -3.734375 C 3.875 -4.34375 4.171875 -4.8125 4.171875 -5.390625 C 4.171875 -6.28125 3.421875 -6.875 2.328125 -6.875 C 1.640625 -6.875 1.203125 -6.671875 0.71875 -6.203125 Z M 0.421875 -4.96875 "
+ id="path35" />
+ </symbol>
+ </g>
+ <clipPath
+ id="clip1">
+ <path
+ d="M 172 34 L 176.394531 34 L 176.394531 42 L 172 42 Z M 172 34 "
+ id="path40" />
+ </clipPath>
+ <clipPath
+ id="clip2">
+ <path
+ d="M 8 59 L 9 59 L 9 76.207031 L 8 76.207031 Z M 8 59 "
+ id="path43" />
+ </clipPath>
+ <clipPath
+ id="clip3">
+ <path
+ d="M 88 59 L 89 59 L 89 76.207031 L 88 76.207031 Z M 88 59 "
+ id="path46" />
+ </clipPath>
+ <clipPath
+ id="clip4">
+ <path
+ d="M 152 59 L 153 59 L 153 76.207031 L 152 76.207031 Z M 152 59 "
+ id="path49" />
+ </clipPath>
+ </defs>
+ <g
+ id="surface429">
+ <g
+ style="fill:rgb(0%,0%,0%);fill-opacity:1;"
+ id="g56">
+ <use
+ xlink:href="#glyph0-1"
+ x="46.2"
+ y="12.007"
+ id="use54" />
+ </g>
+ <path
+ style="fill:none;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 103.999219 407.999188 L 95.999219 407.999188 L 95.999219 395.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path58" />
+ <path
+ style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 95.999219 395.999188 L 96.999219 398.999188 L 94.999219 398.999188 Z M 95.999219 395.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path60" />
+ <path
+ style="fill:none;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 111.999219 407.999188 L 103.999219 407.999188 L 103.999219 395.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path62" />
+ <path
+ style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 103.999219 395.999188 L 104.999219 398.999188 L 102.999219 398.999188 Z M 103.999219 395.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path64" />
+ <path
+ style="fill:none;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 119.999219 407.999188 L 111.999219 407.999188 L 111.999219 395.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path66" />
+ <path
+ style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 111.999219 395.999188 L 112.999219 398.999188 L 110.999219 398.999188 Z M 111.999219 395.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path68" />
+ <path
+ style="fill:none;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 127.999219 407.999188 L 119.999219 407.999188 L 119.999219 395.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path70" />
+ <path
+ style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 119.999219 395.999188 L 120.999219 398.999188 L 118.999219 398.999188 Z M 119.999219 395.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path72" />
+ <path
+ style="fill:none;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 135.999219 407.999188 L 127.999219 407.999188 L 127.999219 395.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path74" />
+ <path
+ style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 127.999219 395.999188 L 128.999219 398.999188 L 126.999219 398.999188 Z M 127.999219 395.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path76" />
+ <path
+ style="fill:none;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 143.999219 407.999188 L 135.999219 407.999188 L 135.999219 395.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path78" />
+ <path
+ style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 135.999219 395.999188 L 136.999219 398.999188 L 134.999219 398.999188 Z M 135.999219 395.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path80" />
+ <path
+ style="fill:none;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 151.999219 407.999188 L 143.999219 407.999188 L 143.999219 395.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path82" />
+ <path
+ style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 143.999219 395.999188 L 144.999219 398.999188 L 142.999219 398.999188 Z M 143.999219 395.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path84" />
+ <path
+ style="fill:none;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 159.999219 407.999188 L 151.999219 407.999188 L 151.999219 395.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path86" />
+ <path
+ style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 151.999219 395.999188 L 152.999219 398.999188 L 150.999219 398.999188 Z M 151.999219 395.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path88" />
+ <path
+ style="fill:none;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 167.999219 407.999188 L 159.999219 407.999188 L 159.999219 395.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path90" />
+ <path
+ style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 159.999219 395.999188 L 160.999219 398.999188 L 158.999219 398.999188 Z M 159.999219 395.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path92" />
+ <path
+ style="fill:none;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 175.999219 407.999188 L 167.999219 407.999188 L 167.999219 395.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path94" />
+ <path
+ style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 167.999219 395.999188 L 168.999219 398.999188 L 166.999219 398.999188 Z M 167.999219 395.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path96" />
+ <path
+ style="fill:none;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 175.999219 407.999188 L 175.999219 395.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path98" />
+ <path
+ style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 175.999219 395.999188 L 176.999219 398.999188 L 174.999219 398.999188 Z M 175.999219 395.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path100" />
+ <path
+ style="fill:none;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 95.999219 395.999188 L 239.999219 395.999188 L 239.999219 391.999188 L 95.999219 391.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path102" />
+ <path
+ style="fill:none;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 95.999219 403.999188 L 95.999219 383.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path104" />
+ <path
+ style="fill:none;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 95.999219 399.999188 L 91.999219 403.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path106" />
+ <path
+ style="fill:none;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 95.999219 395.999188 L 87.999219 403.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path108" />
+ <path
+ style="fill:none;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 95.999219 391.999188 L 87.999219 399.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path110" />
+ <path
+ style="fill:none;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 95.999219 387.999188 L 87.999219 395.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path112" />
+ <path
+ style="fill:none;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 95.999219 383.999188 L 87.999219 391.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path114" />
+ <path
+ style="fill:none;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 91.999219 383.999188 L 87.999219 387.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path116" />
+ <path
+ style="fill:none;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 175.999219 391.999188 L 171.999219 383.999188 L 179.999219 383.999188 L 175.999219 391.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path118" />
+ <path
+ style=" stroke:none;fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;"
+ d="M 88 46.007812 C 88 45.011719 87.195312 44.207031 86.199219 44.207031 C 85.207031 44.207031 84.398438 45.011719 84.398438 46.007812 C 84.398438 47 85.207031 47.808594 86.199219 47.808594 C 87.195312 47.808594 88 47 88 46.007812 M 87.398438 46.007812 C 87.398438 45.34375 86.863281 44.808594 86.199219 44.808594 C 85.535156 44.808594 85 45.34375 85 46.007812 C 85 46.671875 85.535156 47.207031 86.199219 47.207031 C 86.863281 47.207031 87.398438 46.671875 87.398438 46.007812 "
+ id="path120" />
+ <path
+ style=" stroke:none;fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;"
+ d="M 92 46.007812 C 92 45.011719 91.195312 44.207031 90.199219 44.207031 C 89.207031 44.207031 88.398438 45.011719 88.398438 46.007812 C 88.398438 47 89.207031 47.808594 90.199219 47.808594 C 91.195312 47.808594 92 47 92 46.007812 M 91.398438 46.007812 C 91.398438 45.34375 90.863281 44.808594 90.199219 44.808594 C 89.535156 44.808594 89 45.34375 89 46.007812 C 89 46.671875 89.535156 47.207031 90.199219 47.207031 C 90.863281 47.207031 91.398438 46.671875 91.398438 46.007812 "
+ id="path122" />
+ <path
+ style="fill:none;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 239.999219 391.999188 L 235.999219 383.999188 L 243.999219 383.999188 L 239.999219 391.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path124" />
+ <path
+ style=" stroke:none;fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;"
+ d="M 152 46.007812 C 152 45.011719 151.195312 44.207031 150.199219 44.207031 C 149.207031 44.207031 148.398438 45.011719 148.398438 46.007812 C 148.398438 47 149.207031 47.808594 150.199219 47.808594 C 151.195312 47.808594 152 47 152 46.007812 M 151.398438 46.007812 C 151.398438 45.34375 150.863281 44.808594 150.199219 44.808594 C 149.535156 44.808594 149 45.34375 149 46.007812 C 149 46.671875 149.535156 47.207031 150.199219 47.207031 C 150.863281 47.207031 151.398438 46.671875 151.398438 46.007812 "
+ id="path126" />
+ <path
+ style=" stroke:none;fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;"
+ d="M 156 46.007812 C 156 45.011719 155.195312 44.207031 154.199219 44.207031 C 153.207031 44.207031 152.398438 45.011719 152.398438 46.007812 C 152.398438 47 153.207031 47.808594 154.199219 47.808594 C 155.195312 47.808594 156 47 156 46.007812 M 155.398438 46.007812 C 155.398438 45.34375 154.863281 44.808594 154.199219 44.808594 C 153.535156 44.808594 153 45.34375 153 46.007812 C 153 46.671875 153.535156 47.207031 154.199219 47.207031 C 154.863281 47.207031 155.398438 46.671875 155.398438 46.007812 "
+ id="path128" />
+ <path
+ style="fill:none;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 165.999219 379.999188 L 185.999219 379.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path130" />
+ <path
+ style="fill:none;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 169.999219 379.999188 L 165.999219 375.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path132" />
+ <path
+ style="fill:none;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 173.999219 379.999188 L 165.999219 371.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path134" />
+ <path
+ style="fill:none;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 177.999219 379.999188 L 169.999219 371.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path136" />
+ <path
+ style="fill:none;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 181.999219 379.999188 L 173.999219 371.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path138" />
+ <path
+ style="fill:none;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 185.999219 379.999188 L 177.999219 371.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path140" />
+ <path
+ style="fill:none;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 185.999219 375.999188 L 181.999219 371.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path142" />
+ <path
+ style="fill:none;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 229.999219 379.999188 L 249.999219 379.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path144" />
+ <path
+ style="fill:none;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 233.999219 379.999188 L 229.999219 375.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path146" />
+ <path
+ style="fill:none;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 237.999219 379.999188 L 229.999219 371.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path148" />
+ <path
+ style="fill:none;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 241.999219 379.999188 L 233.999219 371.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path150" />
+ <path
+ style="fill:none;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 245.999219 379.999188 L 237.999219 371.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path152" />
+ <path
+ style="fill:none;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 249.999219 379.999188 L 241.999219 371.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path154" />
+ <path
+ style="fill:none;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 249.999219 375.999188 L 245.999219 371.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path156" />
+ <path
+ style="fill:none;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 231.999219 413.999188 C 243.046094 418.417156 255.58125 413.046063 259.999219 401.999188 C 261.89375 397.268719 262.046094 392.022625 260.436719 387.186688 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path158" />
+ <path
+ style=" stroke:none;fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;"
+ d="M 172.636719 40.820312 L 175.796875 36.601562 L 172.636719 35.550781 Z M 172.636719 40.820312 "
+ id="path160" />
+ <g
+ clip-path="url(#clip1)"
+ clip-rule="nonzero"
+ id="g164">
+ <path
+ style="fill:none;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 260.436719 387.186688 L 263.596875 391.405438 L 260.436719 392.456219 Z M 260.436719 387.186688 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path162" />
+ </g>
+ <g
+ clip-path="url(#clip2)"
+ clip-rule="nonzero"
+ id="g168">
+ <path
+ style="fill:none;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 95.999219 367.999188 L 95.999219 351.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path166" />
+ </g>
+ <g
+ clip-path="url(#clip3)"
+ clip-rule="nonzero"
+ id="g172">
+ <path
+ style="fill:none;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 175.999219 367.999188 L 175.999219 351.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path170" />
+ </g>
+ <g
+ clip-path="url(#clip4)"
+ clip-rule="nonzero"
+ id="g176">
+ <path
+ style="fill:none;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 239.999219 367.999188 L 239.999219 351.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path174" />
+ </g>
+ <path
+ style="fill:none;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 95.999219 355.999188 L 175.999219 355.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path178" />
+ <path
+ style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 175.999219 355.999188 L 172.999219 356.999188 L 172.999219 354.999188 Z M 175.999219 355.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path180" />
+ <path
+ style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 95.999219 355.999188 L 98.999219 354.999188 L 98.999219 356.999188 Z M 95.999219 355.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path182" />
+ <path
+ style="fill:none;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 175.999219 355.999188 L 239.999219 355.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path184" />
+ <path
+ style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 239.999219 355.999188 L 236.999219 356.999188 L 236.999219 354.999188 Z M 239.999219 355.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path186" />
+ <path
+ style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 175.999219 355.999188 L 178.999219 354.999188 L 178.999219 356.999188 Z M 175.999219 355.999188 "
+ transform="matrix(1,0,0,-1,-87.8,428.007)"
+ id="path188" />
+ <g
+ style="fill:rgb(0%,0%,0%);fill-opacity:1;"
+ id="g192">
+ <use
+ xlink:href="#glyph0-2"
+ x="44.2"
+ y="67.999"
+ id="use190" />
+ </g>
+ <g
+ style="fill:rgb(0%,0%,0%);fill-opacity:1;"
+ id="g196">
+ <use
+ xlink:href="#glyph1-1"
+ x="47.518"
+ y="69.494"
+ id="use194" />
+ </g>
+ <g
+ style="fill:rgb(0%,0%,0%);fill-opacity:1;"
+ id="g200">
+ <use
+ xlink:href="#glyph0-2"
+ x="116.2"
+ y="67.999"
+ id="use198" />
+ </g>
+ <g
+ style="fill:rgb(0%,0%,0%);fill-opacity:1;"
+ id="g204">
+ <use
+ xlink:href="#glyph1-2"
+ x="119.518"
+ y="69.494"
+ id="use202" />
+ </g>
+ <g
+ style="fill:rgb(0%,0%,0%);fill-opacity:1;"
+ id="g208">
+ <use
+ xlink:href="#glyph2-1"
+ x="155.991"
+ y="5.816"
+ id="use206" />
+ </g>
+ <g
+ style="fill:rgb(0%,0%,0%);fill-opacity:1;"
+ id="g212">
+ <use
+ xlink:href="#glyph0-3"
+ x="152.2"
+ y="7.998"
+ id="use210" />
+ </g>
+ <g
+ style="fill:rgb(0%,0%,0%);fill-opacity:1;"
+ id="g216">
+ <use
+ xlink:href="#glyph2-2"
+ x="10.2"
+ y="46"
+ id="use214" />
+ </g>
+ <g
+ style="fill:rgb(0%,0%,0%);fill-opacity:1;"
+ id="g220">
+ <use
+ xlink:href="#glyph2-3"
+ x="94.2"
+ y="46"
+ id="use218" />
+ </g>
+ <g
+ style="fill:rgb(0%,0%,0%);fill-opacity:1;"
+ id="g224">
+ <use
+ xlink:href="#glyph2-4"
+ x="158.2"
+ y="45.998"
+ id="use222" />
+ </g>
+ </g>
+</svg>
diff --git a/doc/dev-doc/manual/figures/cl/cohesive_exponential.png b/doc/dev-doc/manual/figures/cl/cohesive_exponential.png
new file mode 100644
index 000000000..d3d0ad6a6
Binary files /dev/null and b/doc/dev-doc/manual/figures/cl/cohesive_exponential.png differ
diff --git a/doc/dev-doc/manual/figures/cl/hooke_law.svg b/doc/dev-doc/manual/figures/cl/hooke_law.svg
new file mode 100644
index 000000000..94e4b0062
--- /dev/null
+++ b/doc/dev-doc/manual/figures/cl/hooke_law.svg
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ id="svg2"
+ xml:space="preserve"
+ width="377.95334"
+ height="188.976"
+ viewBox="0 0 377.95334 188.976"
+ sodipodi:docname="hooke_law.pdf"><metadata
+ id="metadata8"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+ id="defs6"><clipPath
+ clipPathUnits="userSpaceOnUse"
+ id="clipPath18"><path
+ d="M 0,141.732 H 283.465 V 0 H 0 Z"
+ id="path16" /></clipPath></defs><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="640"
+ inkscape:window-height="480"
+ id="namedview4" /><g
+ id="g10"
+ inkscape:groupmode="layer"
+ inkscape:label="hooke_law"
+ transform="matrix(1.3333333,0,0,-1.3333333,0,188.976)"><g
+ id="g12"><g
+ id="g14"
+ clip-path="url(#clipPath18)"><g
+ id="g20"
+ transform="translate(141.7324,56.6934)"><path
+ d="m 0,0 7.392,36.96 c 0.612,3.058 1.612,3.058 2.224,0 l 14.783,-73.92 c 0.613,-3.058 1.613,-3.058 2.225,0 L 34.016,0"
+ style="fill:none;stroke:#231f20;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path22" /></g><g
+ id="g24"
+ transform="translate(107.7163,56.6934)"><path
+ d="m 0,0 7.392,36.96 c 0.611,3.058 1.612,3.058 2.224,0 L 24.4,-36.96 c 0.611,-3.058 1.612,-3.058 2.224,0 L 34.016,0"
+ style="fill:none;stroke:#231f20;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path26" /></g><g
+ id="g28"
+ transform="translate(73.7002,56.6934)"><path
+ d="m 0,0 7.392,36.96 c 0.611,3.058 1.612,3.058 2.224,0 L 24.4,-36.96 c 0.611,-3.058 1.612,-3.058 2.224,0 L 34.016,0"
+ style="fill:none;stroke:#231f20;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path30" /></g><g
+ id="g32"
+ transform="translate(73.7002,56.6934)"><path
+ d="m 0,0 -7.392,-36.96 c -0.611,-3.058 -1.612,-3.058 -2.224,0 L -24.4,36.96 c -0.611,3.058 -1.612,3.058 -2.224,0 l -6.28,-31.4 C -33.515,2.502 -36.567,0 -39.685,0 h -11.338"
+ style="fill:none;stroke:#231f20;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path34" /></g><g
+ id="g36"
+ transform="translate(175.748,56.6934)"><path
+ d="m 0,0 7.392,36.96 c 0.612,3.058 1.612,3.058 2.224,0 l 14.783,-73.92 c 0.613,-3.058 1.613,-3.058 2.225,0 L 34.016,0"
+ style="fill:none;stroke:#231f20;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path38" /></g><g
+ id="g40"
+ transform="translate(209.7637,56.6934)"><path
+ d="m 0,0 7.392,36.96 c 0.612,3.058 1.612,3.058 2.224,0 l 14.783,-73.92 c 0.613,-3.058 1.613,-3.058 2.225,0 l 6.279,31.4 C 33.516,-2.502 36.566,0 39.685,0 h 11.338"
+ style="fill:none;stroke:#231f20;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path42" /></g><text
+ transform="matrix(1,0,0,-1,133.0312,116.7319)"
+ style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:30px;font-family:Cambria;-inkscape-font-specification:Cambria-Italic;writing-mode:lr-tb;fill:#231f20;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="text46"><tspan
+ x="0"
+ y="0"
+ id="tspan44">E</tspan></text></g></g></g></svg>
diff --git a/doc/dev-doc/manual/figures/cl/isotropic_hardening_plasticity.svg b/doc/dev-doc/manual/figures/cl/isotropic_hardening_plasticity.svg
new file mode 100644
index 000000000..54dd40d38
--- /dev/null
+++ b/doc/dev-doc/manual/figures/cl/isotropic_hardening_plasticity.svg
@@ -0,0 +1,181 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ id="svg2"
+ xml:space="preserve"
+ width="545.57861"
+ height="444.22092"
+ viewBox="0 0 545.5786 444.22092"
+ sodipodi:docname="isotropic_hardening_plasticity.svg"
+ inkscape:version="0.92.4 (5da689c313, 2019-01-14)"><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1920"
+ inkscape:window-height="1153"
+ id="namedview52"
+ showgrid="false"
+ inkscape:zoom="1.0625344"
+ inkscape:cx="275.33353"
+ inkscape:cy="177.93375"
+ inkscape:window-x="1920"
+ inkscape:window-y="24"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg2" /><metadata
+ id="metadata8"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
+ id="defs6"><clipPath
+ clipPathUnits="userSpaceOnUse"
+ id="clipPath22"><path
+ d="M 0,425.197 H 453.543 V 0 H 0 Z"
+ id="path20" /></clipPath><clipPath
+ clipPathUnits="userSpaceOnUse"
+ id="clipPath38"><path
+ d="M 0,425.197 H 453.543 V 0 H 0 Z"
+ id="path36" /></clipPath><clipPath
+ clipPathUnits="userSpaceOnUse"
+ id="clipPath18"><path
+ d="M 0,141.732 H 283.465 V 0 H 0 Z"
+ id="path16" /></clipPath>
+
+
+
+
+
+</defs><path
+ id="path1656"
+ d="M 298.60287,164.99053 V 117.56144 H 452.2464"
+ style="fill:none;stroke:#000000;stroke-width:2.0999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ id="path7302"
+ d="m 62.70012,377.11545 h 70.81293 v -86.02548 l -0.44465,0.14952"
+ style="fill:none;stroke:#000000;stroke-width:2.10205579;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ d="M 33.218145,411.4294 211.71436,192.84487"
+ style="fill:none;stroke:#ed1c24;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
+ id="path14" /><g
+ transform="matrix(1.3333333,0,0,-1.3333333,33.217487,403.16314)"
+ id="g40"><path
+ id="path42"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="M 0,0 H -5.102 V -11.339 H 6.236 v 5.103 C 2.792,-6.236 0,-3.444 0,0" /></g><g
+ transform="matrix(1.3333333,0,0,-1.3333333,39.693487,39.906273)"
+ id="g44"><path
+ id="path46"
+ style="fill:#110f0d;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 0,0 -0.821,2.907 -3.115,11.114 c -0.508,1.799 -1.329,1.799 -1.836,0 L -8.781,3.303 -9.709,0 c -0.254,-0.9 -0.044,-1.637 0.463,-1.637 h 0.92 6.944 0.92 C 0.045,-1.637 0.244,-0.9 0,0" /></g><g
+ transform="matrix(1.3333333,0,0,-1.3333333,511.62054,417.90008)"
+ id="g48"><path
+ id="path50"
+ style="fill:#110f0d;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 0,0 2.906,0.813 11.115,3.116 c 1.801,0.506 1.801,1.339 0,1.844 L 3.299,8.775 0,9.702 C -0.9,9.958 -1.637,9.756 -1.637,9.247 V 8.329 1.383 0.464 C -1.637,-0.044 -0.9,-0.255 0,0" /></g><g
+ style="stroke-width:1.14950716"
+ transform="matrix(1.3333333,0,0,-1.0090562,33.217487,42.446293)"
+ id="g52"><path
+ id="path54"
+ style="fill:none;stroke:#110f0d;stroke-width:2.29901433;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
+ d="m 0,0 v -358.425 c 0,-3.989 1.331,-7.246 2.958,-7.246 h 362.711" /></g><text
+ y="456.35873"
+ x="658.94214"
+ id="text58"
+ style="font-variant:normal;font-weight:normal;font-stretch:normal;font-size:36.31492996px;font-family:Symbol;-inkscape-font-specification:SymbolMT;writing-mode:lr-tb;fill:#231f20;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.23395932"
+ transform="matrix(1.0280423,0,-0.33268551,0.97272263,0,0)"><tspan
+ style="stroke-width:1.23395932"
+ id="tspan56"
+ y="456.35873"
+ x="658.94214">ε</tspan></text>
+
+<text
+ y="19.25"
+ x="-1.1119791"
+ id="text62"
+ style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:37.33333206px;font-family:'Times New Roman';-inkscape-font-specification:TimesNewRomanPS-ItalicMT;writing-mode:lr-tb;fill:#110f0d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.33333325"><tspan
+ style="stroke-width:1.33333325"
+ id="tspan60"
+ y="19.25"
+ x="-1.1119791">σ</tspan></text>
+
+<text
+ y="195.7561"
+ x="1.9271464"
+ id="text62-3"
+ style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:37.33333206px;font-family:'Times New Roman';-inkscape-font-specification:TimesNewRomanPS-ItalicMT;writing-mode:lr-tb;fill:#110f0d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.33333325"><tspan
+ style="stroke-width:1.33333325"
+ id="tspan60-6"
+ y="195.7561"
+ x="1.9271464">σ</tspan></text>
+
+<g
+ aria-label="y"
+ transform="matrix(0.49755151,0,0,0.49755151,40.34287,41.567186)"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;line-height:1.25;font-family:cmmi10;-inkscape-font-specification:cmmi10;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.00984216"
+ id="flowRoot1556"><path
+ d="m -41.732422,327.61017 q 0.839844,1.44531 2.929688,1.44531 1.894531,0 3.28125,-1.32812 1.386718,-1.32813 2.226562,-3.22266 0.859375,-1.875 1.328125,-3.84765 -1.777344,1.67968 -3.847656,1.67968 -1.582031,0 -2.695313,-0.54687 -1.113281,-0.54688 -1.738281,-1.62109 -0.605469,-1.09375 -0.605469,-2.63672 0,-1.3086 0.351563,-2.67578 0.351562,-1.38672 0.976562,-3.06641 0.644532,-1.69922 1.113282,-2.94922 0.527343,-1.46484 0.527343,-2.40234 0,-1.19141 -0.878906,-1.19141 -1.582031,0 -2.617187,1.64063 -1.015625,1.62109 -1.503907,3.63281 -0.07813,0.2539 -0.332031,0.2539 h -0.46875 q -0.332031,0 -0.332031,-0.37109 v -0.11719 q 0.644531,-2.38281 1.972656,-4.21875 1.328125,-1.85547 3.359375,-1.85547 1.425781,0 2.402344,0.9375 0.996094,0.9375 0.996094,2.38282 0,0.74218 -0.332032,1.5625 -0.175781,0.48828 -0.800781,2.1289 -0.625,1.64063 -0.957031,2.71485 -0.332031,1.07422 -0.546875,2.10937 -0.214844,1.03516 -0.214844,2.07031 0,1.32813 0.566406,2.2461 0.566407,0.91797 1.796875,0.91797 2.480469,0 4.453125,-3.02735 l 3.027344,-12.32422 q 0.136719,-0.52734 0.625,-0.89843 0.488281,-0.39063 1.054688,-0.39063 0.488281,0 0.839843,0.3125 0.371094,0.3125 0.371094,0.82031 0,0.23438 -0.03906,0.3125 l -3.964844,15.91797 q -0.527344,2.05078 -1.933594,3.92578 -1.40625,1.875 -3.398437,3.00782 -1.972657,1.15234 -4.121094,1.15234 -1.035156,0 -2.050781,-0.41016 -1.015625,-0.39062 -1.640625,-1.1914 -0.625,-0.80078 -0.625,-1.875 0,-1.09375 0.644531,-1.89453 0.644531,-0.80078 1.71875,-0.80078 0.644531,0 1.074219,0.39062 0.449218,0.41016 0.449218,1.05469 0,0.91797 -0.683593,1.60156 -0.683594,0.68359 -1.601563,0.68359 -0.03906,-0.0195 -0.07813,-0.0391 -0.03906,0 -0.07813,0 z"
+ style="stroke-width:2.00984216"
+ id="path933" /></g><path
+ id="path1564"
+ d="M 39.678089,192.0963 H 524.09175"
+ style="fill:none;stroke:#000000;stroke-width:2.50000024;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:2.50000005, 5.0000001;stroke-dashoffset:0;stroke-opacity:1" /><path
+ d="M 211.71436,192.84487 498.42907,104.52334"
+ style="fill:none;stroke:#ed1c24;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
+ id="path14-6" /><path
+ d="M 223.38239,409.88923 463.33741,116.3485"
+ style="fill:none;stroke:#ed1c24;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:4, 12;stroke-dashoffset:0;stroke-opacity:1"
+ id="path14-65" /><g
+ aria-label="E"
+ transform="translate(-99.526343,154.69903)"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;line-height:1.25;font-family:cmmi10;-inkscape-font-specification:cmmi10;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
+ id="flowRoot1676-9"><path
+ d="m 239.6875,196.30158 q -0.39063,0 -0.39063,-0.52735 0.0195,-0.0976 0.0781,-0.33203 0.0586,-0.2539 0.15625,-0.39062 0.11719,-0.15625 0.27344,-0.15625 2.4414,0 3.39843,-0.27344 0.52735,-0.17578 0.76172,-1.07422 l 5.48828,-21.97265 q 0.0781,-0.39063 0.0781,-0.54688 0,-0.42969 -0.48828,-0.48828 -0.74219,-0.15625 -2.8711,-0.15625 -0.39062,0 -0.39062,-0.52734 0.0195,-0.0977 0.0781,-0.33204 0.0586,-0.2539 0.15625,-0.39062 0.11719,-0.15625 0.27344,-0.15625 h 21.67969 q 0.41016,0 0.41016,0.52734 l -0.95704,8.28125 q 0,0.0977 -0.15625,0.23438 -0.13671,0.11719 -0.2539,0.11719 h -0.35156 q -0.41016,0 -0.41016,-0.50782 0.23437,-1.95312 0.23437,-2.89062 0,-1.60156 -0.54687,-2.48047 -0.54688,-0.89844 -1.46484,-1.28906 -0.89844,-0.39063 -2.01172,-0.48828 -1.09375,-0.0977 -2.89063,-0.0977 h -4.55078 q -1.09375,0 -1.44531,0.19531 -0.35156,0.17578 -0.64453,1.17188 l -2.40235,9.66797 h 3.125 q 2.03125,0 3.06641,-0.27344 1.03516,-0.29297 1.64062,-1.19141 0.60547,-0.89844 1.09375,-2.85156 0.0391,-0.13672 0.13672,-0.23438 0.11719,-0.11718 0.25391,-0.11718 h 0.37109 q 0.39063,0 0.39063,0.50781 l -2.48047,9.88281 q -0.0781,0.35156 -0.39063,0.35156 h -0.37109 q -0.39062,0 -0.39062,-0.50781 0.39062,-1.60156 0.39062,-2.36328 0,-1.17187 -1.05469,-1.48437 -1.05468,-0.3125 -2.8125,-0.3125 h -3.32031 l -2.71484,10.8789 q -0.17578,0.48828 -0.17578,0.87891 0,0.3125 1.36718,0.3125 h 4.84375 q 2.89063,0 4.6875,-0.44922 1.79688,-0.44922 2.96875,-1.42578 1.19141,-0.9961 2.05078,-2.51953 0.85938,-1.52344 2.08985,-4.4336 0.0781,-0.2539 0.35156,-0.2539 h 0.37109 q 0.15625,0 0.27344,0.13672 0.11719,0.13671 0.11719,0.3125 0,0.0781 -0.0391,0.15625 l -4.1211,9.64843 q -0.0781,0.23438 -0.3125,0.23438 z"
+ style=""
+ id="path936" /></g><flowRoot
+ transform="translate(-2.4214387e-7,-106.88244)"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;line-height:1.25;font-family:MathJax_Math;-inkscape-font-specification:MathJax_Math;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
+ id="flowRoot1728"
+ xml:space="preserve"><flowRegion
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:MathJax_Math;-inkscape-font-specification:MathJax_Math"
+ id="flowRegion1730"><rect
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:MathJax_Math;-inkscape-font-specification:MathJax_Math"
+ y="86.302933"
+ x="413.78577"
+ height="61.406563"
+ width="140.76274"
+ id="rect1732" /></flowRegion><flowPara
+ id="flowPara1734" /></flowRoot><g
+ aria-label="E+h "
+ transform="translate(-66.240327,42.374796)"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;line-height:1.25;font-family:MathJax_Math;-inkscape-font-specification:MathJax_Math;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
+ id="flowRoot1720-6"><path
+ d="m 278.42586,106.50973 c 0,0.24 0.08,0.24 0.28,0.4 h 0.48 c 0.56,0 0.72,-0.04 0.8,-0.28 0.04,-0.12 1.08,-8.599996 1.08,-8.879996 0,-0.2 -0.16,-0.32 -0.28,-0.44 h -22.16 c -0.28,0.16 -0.24,0.2 -0.4,0.84 -0.2,0.72 -0.12,0.72 0.16,1 h 1.24 c 1.08,0.04 1.92,0.12 2.12,0.2 0.12,0 0.16,0.12 0.16,0.28 0,0.24 -5.52,22.159996 -5.64,22.439996 -0.16,0.28 -0.36,0.4 -0.76,0.48 -0.44,0.04 -1.48,0.12 -2.4,0.12 -1.04,0 -1.04,0 -1.24,0.84 -0.08,0.32 -0.12,0.48 -0.12,0.56 0,0.08 0.04,0.12 0.08,0.2 0.12,0.24 -0.84,0.24 11.56,0.24 10.96,0 11.32,0 11.44,-0.08 0.2,-0.2 4.4,-10.36 4.4,-10.36 0,-0.12 -0.12,-0.36 -0.24,-0.44 -0.16,-0.08 -1,-0.08 -1.16,0 -0.12,0.08 -0.12,0.08 -0.52,0.96 -2,4.84 -3.44,6.64 -6.08,7.48 -1.6,0.56 -2.48,0.6 -7.36,0.6 -3.6,-0.04 -3.64,-0.04 -3.64,-0.24 0,-0.12 2.48,-10.08 2.76,-11.12 h 2.08 c 2.6,0 3.28,0.04 3.92,0.32 0.6,0.24 0.8,0.64 0.8,1.48 0,0.48 -0.04,0.92 -0.28,1.8 -0.16,0.68 -0.16,0.84 0.04,0.96 0.12,0.12 0.16,0.12 0.64,0.12 0.52,0 0.52,0 0.64,-0.12 0.12,-0.08 0.16,-0.24 1.44,-5.4 l 1.32,-5.28 c -0.2,-0.28 -0.12,-0.4 -0.8,-0.4 -0.92,0 -0.76,0.12 -0.92,0.84 -0.2,0.68 -0.4,1.32 -0.6,1.72 -0.92,2.08 -2.52,2.12 -5.52,2.12 h -0.36 c -1,0 -1.92,-0.04 -1.92,-0.04 0,-0.08 2.4,-9.639996 2.48,-9.799996 0.08,-0.2 0.2,-0.32 0.4,-0.4 0.2,-0.04 0.56,-0.04 3.56,-0.08 4.12,0 5,0.04 6.12,0.28 2,0.48 2.64,1.679996 2.64,3.959996 0,0.64 -0.24,2.32 -0.24,3.12 z"
+ style=""
+ id="path939" /><path
+ d="m 298.43555,99.431609 v 10.878911 h 10.8789 v 3.32031 h -10.8789 v 10.8789 h -3.28125 v -10.8789 h -10.87891 v -3.32031 H 295.1543 V 99.431609 Z"
+ style=""
+ id="path941" /><path
+ d="m 330.11273,124.90973 c 1.64,0 2.64,-0.72 3.44,-1.72 0.84,-0.92 1.64,-2.44 2.04,-3.88 0.08,-0.28 0.16,-0.48 0.16,-0.6 0,-0.36 -0.24,-0.32 -0.8,-0.32 -0.72,0 -0.72,0 -0.88,0.6 -0.76,2.72 -2.16,4.48 -3.64,4.48 -0.6,0 -0.76,-0.24 -0.8,-0.96 0,-0.72 0.12,-1.2 0.72,-2.84 1.16,-3.2 1.92,-5.56 2.2,-7.08 0.08,-0.4 0.12,-1 0.12,-1.52 0,-2.84 -2,-4.24 -4.84,-4.24 -2.32,0 -3.88,0.96 -5.2,2.16 -0.2,0.2 -0.28,0.28 -0.28,0.2 0,-0.04 0.68,-2.76 1.52,-6.04 1.28,-4.959996 1.44,-5.839996 1.44,-6.039996 0,-0.24 -0.24,-0.36 -0.48,-0.36 -0.08,0 -5.72,0.44 -5.8,0.44 -0.32,0 -0.4,0.28 -0.52,0.76 -0.2,0.88 -0.12,0.8 0.16,1.08 h 0.84 c 0.88,0.04 1.36,0.12 1.6,0.2 0.2,0.12 0.24,0.28 0.24,0.64 l -2.96,11.839996 c -2.44,9.68 -2.92,11.6 -2.92,12.16 0,0.68 0.84,1.08 1.4,1.08 0.8,0 1.48,-0.48 1.76,-1.08 0.08,-0.2 0.44,-1.48 1.48,-5.68 l 1.36,-5.4 0.28,-0.52 c 1.4,-2.24 2.84,-3.44 4.72,-3.84 0.32,-0.08 0.56,-0.08 1.04,-0.08 1.48,0 1.96,0.84 1.96,2.36 0,0.52 -0.04,1.12 -0.12,1.52 -0.32,1.68 -1.28,4.68 -2.4,7.56 -0.24,0.68 -0.4,1.24 -0.4,1.76 0,2 1.52,3.36 3.56,3.36 z"
+ style=""
+ id="path943" /></g><g
+ aria-label="Eh"
+ transform="translate(-258.96247,11.199156)"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;line-height:1.25;font-family:MathJax_Math;-inkscape-font-specification:MathJax_Math;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
+ id="flowRoot1739-7"><path
+ d="m 488.94148,96.419891 c 0,0.24 0.08,0.24 0.28,0.4 h 0.48 c 0.56,0 0.72,-0.04 0.8,-0.28 0.04,-0.12 1.08,-8.6 1.08,-8.88 0,-0.2 -0.16,-0.32 -0.28,-0.44 h -22.16 c -0.28,0.16 -0.24,0.2 -0.4,0.84 -0.2,0.72 -0.12,0.72 0.16,1 h 1.24 c 1.08,0.04 1.92,0.12 2.12,0.2 0.12,0 0.16,0.12 0.16,0.28 0,0.24 -5.52,22.159999 -5.64,22.439999 -0.16,0.28 -0.36,0.4 -0.76,0.48 -0.44,0.04 -1.48,0.12 -2.4,0.12 -1.04,0 -1.04,0 -1.24,0.84 -0.08,0.32 -0.12,0.48 -0.12,0.56 0,0.08 0.04,0.12 0.08,0.2 0.12,0.24 -0.84,0.24 11.56,0.24 10.96,0 11.32,0 11.44,-0.08 0.2,-0.2 4.4,-10.36 4.4,-10.36 0,-0.12 -0.12,-0.36 -0.24,-0.44 -0.16,-0.08 -1,-0.08 -1.16,0 -0.12,0.08 -0.12,0.08 -0.52,0.96 -2,4.84 -3.44,6.64 -6.08,7.48 -1.6,0.56 -2.48,0.6 -7.36,0.6 -3.6,-0.04 -3.64,-0.04 -3.64,-0.24 0,-0.12 2.48,-10.08 2.76,-11.12 h 2.08 c 2.6,0 3.28,0.04 3.92,0.32 0.6,0.24 0.8,0.64 0.8,1.48 0,0.48 -0.04,0.92 -0.28,1.8 -0.16,0.68 -0.16,0.84 0.04,0.96 0.12,0.12 0.16,0.12 0.64,0.12 0.52,0 0.52,0 0.64,-0.12 0.12,-0.08 0.16,-0.24 1.44,-5.4 l 1.32,-5.279999 c -0.2,-0.28 -0.12,-0.4 -0.8,-0.4 -0.92,0 -0.76,0.12 -0.92,0.84 -0.2,0.68 -0.4,1.32 -0.6,1.72 -0.92,2.08 -2.52,2.12 -5.52,2.12 h -0.36 c -1,0 -1.92,-0.04 -1.92,-0.04 0,-0.08 2.4,-9.64 2.48,-9.8 0.08,-0.2 0.2,-0.32 0.4,-0.4 0.2,-0.04 0.56,-0.04 3.56,-0.08 4.12,0 5,0.04 6.12,0.28 2,0.48 2.64,1.68 2.64,3.96 0,0.64 -0.24,2.32 -0.24,3.12 z"
+ style=""
+ id="path946" /><path
+ d="m 507.11273,114.81989 c 1.64,0 2.64,-0.72 3.44,-1.72 0.84,-0.92 1.64,-2.44 2.04,-3.88 0.08,-0.28 0.16,-0.48 0.16,-0.6 0,-0.36 -0.24,-0.32 -0.8,-0.32 -0.72,0 -0.72,0 -0.88,0.6 -0.76,2.72 -2.16,4.48 -3.64,4.48 -0.6,0 -0.76,-0.24 -0.8,-0.96 0,-0.72 0.12,-1.2 0.72,-2.84 1.16,-3.2 1.92,-5.56 2.2,-7.08 0.08,-0.4 0.12,-1 0.12,-1.52 0,-2.839999 -2,-4.239999 -4.84,-4.239999 -2.32,0 -3.88,0.96 -5.2,2.16 -0.2,0.2 -0.28,0.28 -0.28,0.2 0,-0.04 0.68,-2.76 1.52,-6.04 1.28,-4.96 1.44,-5.84 1.44,-6.04 0,-0.24 -0.24,-0.36 -0.48,-0.36 -0.08,0 -5.72,0.44 -5.8,0.44 -0.32,0 -0.4,0.28 -0.52,0.76 -0.2,0.88 -0.12,0.8 0.16,1.08 h 0.84 c 0.88,0.04 1.36,0.12 1.6,0.2 0.2,0.12 0.24,0.28 0.24,0.64 l -2.96,11.839999 c -2.44,9.68 -2.92,11.6 -2.92,12.16 0,0.68 0.84,1.08 1.4,1.08 0.8,0 1.48,-0.48 1.76,-1.08 0.08,-0.2 0.44,-1.48 1.48,-5.68 l 1.36,-5.4 0.28,-0.52 c 1.4,-2.239999 2.84,-3.439999 4.72,-3.839999 0.32,-0.08 0.56,-0.08 1.04,-0.08 1.48,0 1.96,0.84 1.96,2.359999 0,0.52 -0.04,1.12 -0.12,1.52 -0.32,1.68 -1.28,4.68 -2.4,7.56 -0.24,0.68 -0.4,1.24 -0.4,1.76 0,2 1.52,3.36 3.56,3.36 z"
+ style=""
+ id="path948" /></g><path
+ id="path1747-9"
+ d="m 182.1448,132.59181 h 93.96893"
+ style="fill:none;stroke:#000000;stroke-width:1.39999998;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /></svg>
\ No newline at end of file
diff --git a/doc/dev-doc/manual/figures/cl/linear_cohesive_law.svg b/doc/dev-doc/manual/figures/cl/linear_cohesive_law.svg
new file mode 100644
index 000000000..96390ca87
--- /dev/null
+++ b/doc/dev-doc/manual/figures/cl/linear_cohesive_law.svg
@@ -0,0 +1,553 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="153.73pt"
+ height="145.55pt"
+ viewBox="0 0 153.73 145.55"
+ version="1.2"
+ id="svg210"
+ sodipodi:docname="linear_cohesive_law.svg"
+ inkscape:version="1.0.2 (e86c870879, 2021-01-15)">
+ <metadata
+ id="metadata214">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1920"
+ inkscape:window-height="1053"
+ id="namedview212"
+ showgrid="false"
+ inkscape:zoom="2.2080041"
+ inkscape:cx="93.414115"
+ inkscape:cy="140.41857"
+ inkscape:window-x="0"
+ inkscape:window-y="-9"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg210" />
+ <defs
+ id="defs79">
+ <g
+ id="g68">
+ <symbol
+ overflow="visible"
+ id="glyph0-0">
+ <path
+ style="stroke:none;"
+ d=""
+ id="path2" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-1">
+ <path
+ style="stroke:none;"
+ d="M 4.25 -6.046875 C 4.328125 -6.328125 4.359375 -6.390625 4.484375 -6.421875 C 4.578125 -6.4375 4.90625 -6.4375 5.109375 -6.4375 C 6.125 -6.4375 6.5625 -6.40625 6.5625 -5.625 C 6.5625 -5.46875 6.53125 -5.078125 6.484375 -4.828125 C 6.484375 -4.78125 6.453125 -4.671875 6.453125 -4.640625 C 6.453125 -4.578125 6.484375 -4.5 6.578125 -4.5 C 6.6875 -4.5 6.703125 -4.578125 6.734375 -4.734375 L 7 -6.46875 C 7.015625 -6.515625 7.015625 -6.609375 7.015625 -6.640625 C 7.015625 -6.75 6.921875 -6.75 6.75 -6.75 L 1.21875 -6.75 C 0.984375 -6.75 0.96875 -6.734375 0.890625 -6.546875 L 0.296875 -4.796875 C 0.296875 -4.78125 0.234375 -4.640625 0.234375 -4.609375 C 0.234375 -4.5625 0.296875 -4.5 0.359375 -4.5 C 0.453125 -4.5 0.46875 -4.5625 0.53125 -4.71875 C 1.0625 -6.265625 1.328125 -6.4375 2.796875 -6.4375 L 3.1875 -6.4375 C 3.46875 -6.4375 3.46875 -6.40625 3.46875 -6.3125 C 3.46875 -6.265625 3.4375 -6.140625 3.421875 -6.109375 L 2.09375 -0.78125 C 2 -0.421875 1.96875 -0.3125 0.90625 -0.3125 C 0.546875 -0.3125 0.484375 -0.3125 0.484375 -0.125 C 0.484375 0 0.59375 0 0.65625 0 C 0.921875 0 1.203125 -0.015625 1.46875 -0.015625 C 1.75 -0.015625 2.046875 -0.03125 2.328125 -0.03125 C 2.609375 -0.03125 2.875 -0.015625 3.15625 -0.015625 C 3.4375 -0.015625 3.734375 0 4.015625 0 C 4.109375 0 4.234375 0 4.234375 -0.203125 C 4.234375 -0.3125 4.15625 -0.3125 3.890625 -0.3125 C 3.65625 -0.3125 3.515625 -0.3125 3.265625 -0.328125 C 2.96875 -0.359375 2.890625 -0.390625 2.890625 -0.546875 C 2.890625 -0.5625 2.890625 -0.609375 2.9375 -0.75 Z M 4.25 -6.046875 "
+ id="path5" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-2">
+ <path
+ style="stroke:none;"
+ d="M 2.625 -4.359375 C 1.390625 -4.0625 0.421875 -2.765625 0.421875 -1.5625 C 0.421875 -0.59375 1.0625 0.125 2 0.125 C 3.15625 0.125 3.984375 -1.453125 3.984375 -2.828125 C 3.984375 -3.734375 3.59375 -4.234375 3.25 -4.671875 C 2.890625 -5.125 2.296875 -5.875 2.296875 -6.3125 C 2.296875 -6.53125 2.5 -6.765625 2.84375 -6.765625 C 3.15625 -6.765625 3.34375 -6.640625 3.5625 -6.5 C 3.765625 -6.375 3.953125 -6.25 4.109375 -6.25 C 4.359375 -6.25 4.5 -6.484375 4.5 -6.65625 C 4.5 -6.875 4.34375 -6.890625 3.984375 -6.984375 C 3.46875 -7.09375 3.328125 -7.09375 3.171875 -7.09375 C 2.390625 -7.09375 2.03125 -6.65625 2.03125 -6.0625 C 2.03125 -5.515625 2.328125 -4.96875 2.625 -4.359375 Z M 2.75 -4.140625 C 3 -3.671875 3.296875 -3.140625 3.296875 -2.421875 C 3.296875 -1.765625 2.921875 -0.09375 2 -0.09375 C 1.453125 -0.09375 1.03125 -0.515625 1.03125 -1.28125 C 1.03125 -1.90625 1.40625 -3.78125 2.75 -4.140625 Z M 2.75 -4.140625 "
+ id="path8" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-0">
+ <path
+ style="stroke:none;"
+ d=""
+ id="path11" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-1">
+ <path
+ style="stroke:none;"
+ d="M 4.765625 -3.3125 C 4.890625 -3.3125 5.21875 -3.3125 5.21875 -3.625 C 5.21875 -3.859375 5.015625 -3.859375 4.859375 -3.859375 L 2.75 -3.859375 C 1.390625 -3.859375 0.34375 -2.453125 0.34375 -1.34375 C 0.34375 -0.4375 0.984375 0.09375 1.75 0.09375 C 2.84375 0.09375 4.046875 -0.984375 4.046875 -2.34375 C 4.046875 -2.921875 3.8125 -3.265625 3.78125 -3.3125 Z M 1.765625 -0.125 C 1.421875 -0.125 0.9375 -0.3125 0.9375 -1.09375 C 0.9375 -1.71875 1.296875 -3.3125 2.59375 -3.3125 C 3.03125 -3.3125 3.4375 -3.109375 3.4375 -2.453125 C 3.4375 -2.21875 3.34375 -1.40625 2.921875 -0.828125 C 2.625 -0.40625 2.171875 -0.125 1.765625 -0.125 Z M 1.765625 -0.125 "
+ id="path14" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-2">
+ <path
+ style="stroke:none;"
+ d="M 2.40625 -3.90625 C 1.375 -3.6875 0.390625 -2.609375 0.390625 -1.421875 C 0.390625 -0.4375 1.078125 0.109375 1.859375 0.109375 C 2.90625 0.109375 3.6875 -1.25 3.6875 -2.53125 C 3.6875 -3.359375 3.296875 -3.84375 3.015625 -4.171875 C 2.703125 -4.5625 2.109375 -5.28125 2.109375 -5.671875 C 2.109375 -5.828125 2.234375 -6.078125 2.609375 -6.078125 C 2.890625 -6.078125 3.0625 -5.96875 3.25 -5.84375 C 3.375 -5.765625 3.640625 -5.609375 3.78125 -5.609375 C 3.96875 -5.609375 4.140625 -5.78125 4.140625 -5.96875 C 4.140625 -6.171875 4.015625 -6.203125 3.59375 -6.28125 C 3.15625 -6.390625 3 -6.390625 2.875 -6.390625 C 1.96875 -6.390625 1.84375 -5.75 1.84375 -5.46875 C 1.84375 -5.015625 2.125 -4.40625 2.40625 -3.90625 Z M 2.53125 -3.6875 C 3 -2.859375 3.015625 -2.453125 3.015625 -2.15625 C 3.015625 -1.484375 2.65625 -0.109375 1.859375 -0.109375 C 1.46875 -0.109375 0.984375 -0.375 0.984375 -1.171875 C 0.984375 -1.75 1.296875 -3.359375 2.53125 -3.6875 Z M 2.53125 -3.6875 "
+ id="path17" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-3">
+ <path
+ style="stroke:none;"
+ d="M 3.9375 -5.46875 C 3.984375 -5.65625 4.015625 -5.75 4.15625 -5.78125 C 4.234375 -5.796875 4.515625 -5.796875 4.703125 -5.796875 C 5.734375 -5.796875 6.046875 -5.734375 6.046875 -5.03125 C 6.046875 -4.921875 6.046875 -4.78125 5.96875 -4.25 L 5.953125 -4.171875 C 5.953125 -4.109375 5.984375 -4.046875 6.078125 -4.046875 C 6.1875 -4.046875 6.203125 -4.140625 6.21875 -4.265625 L 6.453125 -5.828125 C 6.46875 -5.859375 6.46875 -5.921875 6.46875 -5.953125 C 6.46875 -6.078125 6.390625 -6.078125 6.21875 -6.078125 L 1.109375 -6.078125 C 0.890625 -6.078125 0.890625 -6.0625 0.828125 -5.890625 L 0.265625 -4.3125 C 0.234375 -4.203125 0.234375 -4.171875 0.234375 -4.171875 C 0.234375 -4.125 0.265625 -4.046875 0.34375 -4.046875 C 0.453125 -4.046875 0.46875 -4.109375 0.515625 -4.265625 C 1.015625 -5.671875 1.296875 -5.796875 2.59375 -5.796875 L 2.9375 -5.796875 C 3.1875 -5.796875 3.1875 -5.765625 3.1875 -5.6875 C 3.1875 -5.640625 3.171875 -5.578125 3.15625 -5.5 L 1.953125 -0.703125 C 1.875 -0.390625 1.84375 -0.28125 0.890625 -0.28125 C 0.609375 -0.28125 0.53125 -0.28125 0.53125 -0.109375 C 0.53125 0 0.640625 0 0.6875 0 C 0.921875 0 1.171875 -0.015625 1.421875 -0.015625 C 1.671875 -0.015625 1.921875 -0.03125 2.171875 -0.03125 C 2.578125 -0.03125 2.59375 -0.03125 2.921875 -0.015625 C 3.046875 -0.015625 3.625 0 3.703125 0 C 3.78125 0 3.890625 0 3.890625 -0.15625 C 3.890625 -0.28125 3.8125 -0.28125 3.59375 -0.28125 C 3.359375 -0.28125 3.25 -0.28125 3.015625 -0.296875 C 2.8125 -0.328125 2.703125 -0.328125 2.703125 -0.484375 C 2.703125 -0.53125 2.71875 -0.59375 2.734375 -0.65625 Z M 3.9375 -5.46875 "
+ id="path20" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-4">
+ <path
+ style="stroke:none;"
+ d="M 2.8125 -3.03125 L 3.734375 -3.03125 C 4.375 -3.03125 4.5 -2.921875 4.5 -2.625 C 4.5 -2.5625 4.46875 -2.375 4.4375 -2.21875 C 4.421875 -2.171875 4.40625 -2.125 4.40625 -2.09375 C 4.40625 -2.09375 4.40625 -1.984375 4.53125 -1.984375 C 4.640625 -1.984375 4.640625 -2.046875 4.6875 -2.171875 L 5.15625 -4.0625 C 5.1875 -4.1875 5.1875 -4.203125 5.1875 -4.25 C 5.1875 -4.3125 5.140625 -4.359375 5.0625 -4.359375 C 4.96875 -4.359375 4.953125 -4.3125 4.921875 -4.140625 C 4.734375 -3.421875 4.453125 -3.3125 3.734375 -3.3125 L 2.890625 -3.3125 C 3.015625 -3.859375 3.4375 -5.5625 3.484375 -5.671875 C 3.546875 -5.796875 3.609375 -5.828125 3.90625 -5.828125 L 5.21875 -5.828125 C 6.34375 -5.828125 6.59375 -5.546875 6.59375 -4.828125 C 6.59375 -4.546875 6.546875 -4.28125 6.546875 -4.21875 C 6.546875 -4.140625 6.59375 -4.078125 6.671875 -4.078125 C 6.78125 -4.078125 6.796875 -4.140625 6.8125 -4.3125 L 7 -5.96875 C 7 -6.09375 6.90625 -6.09375 6.734375 -6.09375 L 2.109375 -6.09375 C 1.953125 -6.09375 1.84375 -6.09375 1.84375 -5.9375 C 1.84375 -5.828125 1.9375 -5.828125 2.125 -5.828125 C 2.15625 -5.828125 2.328125 -5.828125 2.46875 -5.796875 C 2.65625 -5.78125 2.671875 -5.734375 2.671875 -5.65625 C 2.671875 -5.625 2.671875 -5.609375 2.625 -5.453125 L 1.4375 -0.6875 C 1.359375 -0.359375 1.34375 -0.28125 0.65625 -0.28125 C 0.5 -0.28125 0.390625 -0.28125 0.390625 -0.109375 C 0.390625 0 0.484375 0 0.640625 0 L 5.375 0 C 5.546875 0 5.5625 0 5.59375 -0.03125 C 5.609375 -0.046875 5.625 -0.046875 5.671875 -0.171875 C 5.78125 -0.40625 6.546875 -2.109375 6.546875 -2.203125 C 6.546875 -2.265625 6.5 -2.3125 6.421875 -2.3125 C 6.34375 -2.3125 6.34375 -2.3125 6.25 -2.09375 C 5.6875 -0.84375 5.3125 -0.28125 3.828125 -0.28125 L 2.453125 -0.28125 C 2.328125 -0.28125 2.3125 -0.28125 2.28125 -0.28125 C 2.1875 -0.296875 2.15625 -0.3125 2.15625 -0.375 C 2.15625 -0.390625 2.15625 -0.40625 2.203125 -0.578125 Z M 2.8125 -3.03125 "
+ id="path23" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-5">
+ <path
+ style="stroke:none;"
+ d="M 4.40625 -2.109375 C 4.40625 -3.4375 3.59375 -3.96875 2.828125 -3.96875 C 1.609375 -3.96875 0.390625 -2.71875 0.390625 -1.4375 C 0.390625 -0.515625 1.015625 0.09375 1.890625 0.09375 C 2.859375 0.09375 3.59375 -0.46875 3.828125 -0.65625 C 3.9375 -0.203125 4.234375 0.09375 4.671875 0.09375 C 5.15625 0.09375 5.453125 -0.375 5.453125 -0.53125 C 5.453125 -0.640625 5.359375 -0.640625 5.328125 -0.640625 C 5.234375 -0.640625 5.21875 -0.59375 5.1875 -0.515625 C 5.09375 -0.265625 4.859375 -0.125 4.703125 -0.125 C 4.40625 -0.125 4.40625 -0.921875 4.40625 -1.109375 C 4.40625 -1.21875 4.40625 -1.234375 4.5 -1.34375 C 4.640625 -1.53125 4.890625 -1.84375 5.125 -2.296875 C 5.390625 -2.828125 5.53125 -3.375 5.53125 -3.421875 C 5.53125 -3.4375 5.53125 -3.53125 5.421875 -3.53125 C 5.3125 -3.53125 5.3125 -3.5 5.265625 -3.359375 C 5.171875 -3.046875 4.96875 -2.359375 4.40625 -1.609375 Z M 3.78125 -0.921875 C 3.546875 -0.71875 2.828125 -0.125 1.90625 -0.125 C 1.40625 -0.125 1.078125 -0.453125 1.078125 -1.109375 C 1.078125 -1.484375 1.25 -2.375 1.609375 -2.953125 C 1.84375 -3.3125 2.3125 -3.734375 2.828125 -3.734375 C 3.4375 -3.734375 3.625 -3.203125 3.71875 -2.65625 C 3.78125 -2.171875 3.734375 -1.3125 3.78125 -0.921875 Z M 3.78125 -0.921875 "
+ id="path26" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-0">
+ <path
+ style="stroke:none;"
+ d=""
+ id="path29" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-1">
+ <path
+ style="stroke:none;"
+ d="M 1.984375 -0.78125 C 1.75 -0.421875 1.5625 -0.3125 1.28125 -0.3125 C 0.828125 -0.3125 0.515625 -0.703125 0.515625 -1.28125 C 0.515625 -1.796875 0.78125 -2.140625 1.1875 -2.140625 C 1.359375 -2.140625 1.4375 -2.09375 1.484375 -1.90625 L 1.515625 -1.796875 C 1.546875 -1.65625 1.640625 -1.5625 1.75 -1.5625 C 1.875 -1.5625 1.984375 -1.671875 1.984375 -1.78125 C 1.984375 -2.0625 1.640625 -2.296875 1.21875 -2.296875 C 0.96875 -2.296875 0.71875 -2.1875 0.515625 -2.015625 C 0.265625 -1.796875 0.125 -1.453125 0.125 -1.0625 C 0.125 -0.40625 0.515625 0.046875 1.078125 0.046875 C 1.296875 0.046875 1.5 -0.03125 1.671875 -0.1875 C 1.8125 -0.296875 1.90625 -0.4375 2.046875 -0.734375 Z M 1.984375 -0.78125 "
+ id="path32" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-2">
+ <path
+ style="stroke:none;"
+ d="M 0.09375 -1.984375 C 0.15625 -2 0.203125 -2 0.25 -2 C 0.390625 -2 0.421875 -1.921875 0.421875 -1.6875 L 0.421875 -0.421875 C 0.421875 -0.15625 0.359375 -0.078125 0.078125 -0.078125 L 0.078125 0 L 1.1875 0 L 1.1875 -0.078125 C 0.921875 -0.078125 0.84375 -0.140625 0.84375 -0.328125 L 0.84375 -1.734375 C 0.84375 -1.75 0.890625 -1.796875 0.921875 -1.828125 C 1.046875 -1.953125 1.265625 -2.03125 1.4375 -2.03125 C 1.65625 -2.03125 1.765625 -1.859375 1.765625 -1.515625 L 1.765625 -0.421875 C 1.765625 -0.15625 1.703125 -0.09375 1.421875 -0.078125 L 1.421875 0 L 2.546875 0 L 2.546875 -0.078125 C 2.265625 -0.078125 2.1875 -0.171875 2.1875 -0.46875 L 2.1875 -1.734375 C 2.328125 -1.9375 2.5 -2.03125 2.71875 -2.03125 C 3.015625 -2.03125 3.09375 -1.90625 3.09375 -1.484375 L 3.09375 -0.4375 C 3.09375 -0.15625 3.0625 -0.109375 2.765625 -0.078125 L 2.765625 0 L 3.859375 0 L 3.859375 -0.078125 L 3.734375 -0.078125 C 3.578125 -0.09375 3.515625 -0.1875 3.515625 -0.375 L 3.515625 -1.40625 C 3.515625 -2 3.328125 -2.296875 2.9375 -2.296875 C 2.65625 -2.296875 2.390625 -2.15625 2.125 -1.875 C 2.03125 -2.15625 1.875 -2.296875 1.59375 -2.296875 C 1.375 -2.296875 1.234375 -2.21875 0.828125 -1.90625 L 0.828125 -2.28125 L 0.796875 -2.296875 C 0.53125 -2.203125 0.375 -2.140625 0.09375 -2.0625 Z M 0.09375 -1.984375 "
+ id="path35" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-3">
+ <path
+ style="stroke:none;"
+ d="M 2.203125 -0.328125 C 2.125 -0.265625 2.0625 -0.234375 1.984375 -0.234375 C 1.875 -0.234375 1.828125 -0.296875 1.828125 -0.515625 L 1.828125 -1.5 C 1.828125 -1.75 1.8125 -1.90625 1.734375 -2.015625 C 1.625 -2.203125 1.40625 -2.296875 1.109375 -2.296875 C 0.640625 -2.296875 0.28125 -2.046875 0.28125 -1.734375 C 0.28125 -1.625 0.375 -1.515625 0.5 -1.515625 C 0.609375 -1.515625 0.71875 -1.625 0.71875 -1.734375 C 0.71875 -1.75 0.71875 -1.78125 0.703125 -1.8125 C 0.703125 -1.859375 0.6875 -1.890625 0.6875 -1.921875 C 0.6875 -2.0625 0.859375 -2.171875 1.046875 -2.171875 C 1.296875 -2.171875 1.4375 -2.03125 1.4375 -1.765625 L 1.4375 -1.453125 C 0.65625 -1.140625 0.578125 -1.109375 0.359375 -0.921875 C 0.25 -0.8125 0.1875 -0.640625 0.1875 -0.484375 C 0.1875 -0.171875 0.40625 0.046875 0.703125 0.046875 C 0.921875 0.046875 1.125 -0.0625 1.4375 -0.3125 C 1.453125 -0.0625 1.546875 0.046875 1.75 0.046875 C 1.921875 0.046875 2.03125 -0.015625 2.203125 -0.203125 Z M 1.4375 -0.609375 C 1.4375 -0.453125 1.40625 -0.40625 1.296875 -0.359375 C 1.1875 -0.28125 1.046875 -0.234375 0.9375 -0.234375 C 0.765625 -0.234375 0.625 -0.40625 0.625 -0.625 L 0.625 -0.640625 C 0.625 -0.9375 0.828125 -1.109375 1.4375 -1.328125 Z M 1.4375 -0.609375 "
+ id="path38" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-4">
+ <path
+ style="stroke:none;"
+ d="M 1.390625 0 L 2.390625 0 L 2.390625 -0.078125 C 2.234375 -0.078125 2.140625 -0.15625 1.984375 -0.375 L 1.34375 -1.34375 L 1.75 -1.953125 C 1.84375 -2.078125 2 -2.15625 2.15625 -2.171875 L 2.15625 -2.25 L 1.375 -2.25 L 1.375 -2.171875 C 1.515625 -2.15625 1.5625 -2.125 1.5625 -2.0625 C 1.5625 -2 1.515625 -1.890625 1.390625 -1.734375 C 1.359375 -1.703125 1.296875 -1.609375 1.234375 -1.515625 L 1.171875 -1.609375 C 1.03125 -1.828125 0.9375 -2 0.9375 -2.0625 C 0.9375 -2.125 1 -2.15625 1.15625 -2.171875 L 1.15625 -2.25 L 0.125 -2.25 L 0.125 -2.171875 L 0.171875 -2.171875 C 0.3125 -2.171875 0.390625 -2.109375 0.546875 -1.875 L 1.015625 -1.15625 L 0.453125 -0.328125 C 0.296875 -0.125 0.25 -0.09375 0.078125 -0.078125 L 0.078125 0 L 0.8125 0 L 0.8125 -0.078125 C 0.671875 -0.078125 0.609375 -0.09375 0.609375 -0.171875 C 0.609375 -0.1875 0.640625 -0.265625 0.703125 -0.375 L 1.09375 -0.984375 L 1.5625 -0.28125 C 1.578125 -0.25 1.578125 -0.21875 1.578125 -0.1875 C 1.578125 -0.109375 1.546875 -0.078125 1.390625 -0.078125 Z M 1.390625 0 "
+ id="path41" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-5">
+ <path
+ style="stroke:none;"
+ d="M 1.71875 0.046875 L 2.453125 -0.203125 L 2.453125 -0.296875 C 2.359375 -0.28125 2.34375 -0.28125 2.328125 -0.28125 C 2.15625 -0.28125 2.109375 -0.34375 2.109375 -0.5625 L 2.109375 -3.390625 L 2.09375 -3.40625 C 1.84375 -3.3125 1.671875 -3.265625 1.359375 -3.1875 L 1.359375 -3.109375 C 1.390625 -3.109375 1.421875 -3.109375 1.46875 -3.109375 C 1.65625 -3.109375 1.6875 -3.0625 1.6875 -2.859375 L 1.6875 -2.078125 C 1.5 -2.234375 1.375 -2.296875 1.171875 -2.296875 C 0.59375 -2.296875 0.140625 -1.734375 0.140625 -1.015625 C 0.140625 -0.390625 0.515625 0.046875 1.0625 0.046875 C 1.328125 0.046875 1.53125 -0.046875 1.6875 -0.28125 L 1.6875 0.03125 Z M 1.6875 -0.515625 C 1.6875 -0.46875 1.65625 -0.40625 1.609375 -0.359375 C 1.515625 -0.265625 1.390625 -0.203125 1.25 -0.203125 C 0.84375 -0.203125 0.5625 -0.609375 0.5625 -1.21875 C 0.5625 -1.78125 0.8125 -2.15625 1.1875 -2.15625 C 1.453125 -2.15625 1.6875 -1.921875 1.6875 -1.65625 Z M 1.6875 -0.515625 "
+ id="path44" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-6">
+ <path
+ style="stroke:none;"
+ d="M 0.875 -2.296875 L 0.09375 -2.015625 L 0.09375 -1.9375 L 0.140625 -1.953125 C 0.203125 -1.953125 0.265625 -1.96875 0.3125 -1.96875 C 0.421875 -1.96875 0.46875 -1.890625 0.46875 -1.671875 L 0.46875 -0.515625 C 0.46875 -0.15625 0.421875 -0.09375 0.078125 -0.078125 L 0.078125 0 L 1.265625 0 L 1.265625 -0.078125 C 0.9375 -0.09375 0.890625 -0.15625 0.890625 -0.515625 L 0.890625 -2.28125 Z M 0.640625 -3.40625 C 0.5 -3.40625 0.390625 -3.296875 0.390625 -3.15625 C 0.390625 -3.015625 0.5 -2.890625 0.640625 -2.890625 C 0.78125 -2.890625 0.890625 -3 0.890625 -3.15625 C 0.890625 -3.296875 0.78125 -3.40625 0.640625 -3.40625 Z M 0.640625 -3.40625 "
+ id="path47" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-7">
+ <path
+ style="stroke:none;"
+ d="M 1.5625 -1.5625 L 1.546875 -2.25 L 1.5 -2.25 L 1.484375 -2.234375 C 1.4375 -2.203125 1.4375 -2.1875 1.421875 -2.1875 C 1.390625 -2.1875 1.328125 -2.203125 1.28125 -2.234375 C 1.171875 -2.265625 1.0625 -2.28125 0.9375 -2.28125 C 0.53125 -2.28125 0.25 -2.03125 0.25 -1.671875 C 0.25 -1.390625 0.40625 -1.203125 0.84375 -0.953125 L 1.125 -0.796875 C 1.296875 -0.6875 1.390625 -0.578125 1.390625 -0.421875 C 1.390625 -0.203125 1.21875 -0.0625 0.96875 -0.0625 C 0.796875 -0.0625 0.640625 -0.125 0.546875 -0.234375 C 0.453125 -0.359375 0.40625 -0.46875 0.34375 -0.75 L 0.265625 -0.75 L 0.265625 0.015625 L 0.328125 0.015625 C 0.359375 -0.03125 0.375 -0.046875 0.4375 -0.046875 C 0.484375 -0.046875 0.546875 -0.03125 0.671875 0 C 0.8125 0.03125 0.9375 0.046875 1.03125 0.046875 C 1.421875 0.046875 1.734375 -0.234375 1.734375 -0.59375 C 1.734375 -0.84375 1.609375 -1 1.3125 -1.1875 L 0.78125 -1.5 C 0.640625 -1.578125 0.5625 -1.703125 0.5625 -1.84375 C 0.5625 -2.03125 0.71875 -2.171875 0.953125 -2.171875 C 1.234375 -2.171875 1.375 -2.015625 1.5 -1.5625 Z M 1.5625 -1.5625 "
+ id="path50" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-8">
+ <path
+ style="stroke:none;"
+ d="M 0.03125 -1.9375 C 0.109375 -1.953125 0.15625 -1.96875 0.203125 -1.96875 C 0.328125 -1.96875 0.375 -1.890625 0.375 -1.671875 L 0.375 -0.421875 C 0.375 -0.171875 0.34375 -0.140625 0.03125 -0.078125 L 0.03125 0 L 1.21875 0 L 1.21875 -0.078125 C 0.875 -0.09375 0.796875 -0.171875 0.796875 -0.453125 L 0.796875 -1.5625 C 0.796875 -1.734375 1.015625 -1.984375 1.140625 -1.984375 C 1.171875 -1.984375 1.21875 -1.953125 1.28125 -1.90625 C 1.359375 -1.828125 1.40625 -1.796875 1.46875 -1.796875 C 1.59375 -1.796875 1.671875 -1.890625 1.671875 -2.03125 C 1.671875 -2.1875 1.5625 -2.296875 1.390625 -2.296875 C 1.1875 -2.296875 1.046875 -2.171875 0.796875 -1.828125 L 0.796875 -2.28125 L 0.765625 -2.296875 C 0.515625 -2.1875 0.328125 -2.125 0.03125 -2.03125 Z M 0.03125 -1.9375 "
+ id="path53" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-9">
+ <path
+ style="stroke:none;"
+ d="M 2.03125 -0.8125 C 1.796875 -0.4375 1.578125 -0.296875 1.265625 -0.296875 C 0.984375 -0.296875 0.765625 -0.4375 0.625 -0.71875 C 0.53125 -0.90625 0.5 -1.078125 0.484375 -1.375 L 2.015625 -1.375 C 1.984375 -1.703125 1.921875 -1.84375 1.796875 -2.015625 C 1.65625 -2.1875 1.421875 -2.296875 1.171875 -2.296875 C 0.921875 -2.296875 0.6875 -2.203125 0.5 -2.03125 C 0.265625 -1.828125 0.125 -1.46875 0.125 -1.0625 C 0.125 -0.375 0.484375 0.046875 1.0625 0.046875 C 1.53125 0.046875 1.90625 -0.25 2.109375 -0.78125 Z M 0.5 -1.546875 C 0.546875 -1.921875 0.71875 -2.109375 1.015625 -2.109375 C 1.328125 -2.109375 1.453125 -1.96875 1.515625 -1.546875 Z M 0.5 -1.546875 "
+ id="path56" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-10">
+ <path
+ style="stroke:none;"
+ d="M 2.375 -2.25 L 1.6875 -2.25 L 1.6875 -2.171875 C 1.84375 -2.15625 1.921875 -2.109375 1.921875 -2.015625 C 1.921875 -1.953125 1.90625 -1.90625 1.890625 -1.859375 L 1.390625 -0.5625 L 0.890625 -1.84375 C 0.859375 -1.90625 0.84375 -1.984375 0.84375 -2.03125 C 0.84375 -2.125 0.890625 -2.15625 1.078125 -2.171875 L 1.078125 -2.25 L 0.09375 -2.25 L 0.09375 -2.171875 C 0.28125 -2.15625 0.3125 -2.109375 0.546875 -1.59375 L 1.140625 -0.171875 C 1.15625 -0.140625 1.171875 -0.09375 1.1875 -0.0625 C 1.21875 0.03125 1.25 0.0625 1.28125 0.0625 C 1.3125 0.0625 1.34375 0 1.421875 -0.171875 L 2.046875 -1.78125 C 2.203125 -2.125 2.234375 -2.15625 2.375 -2.171875 Z M 2.375 -2.25 "
+ id="path59" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-0">
+ <path
+ style="stroke:none;"
+ d=""
+ id="path62" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-1">
+ <path
+ style="stroke:none;"
+ d="M 2.609375 -6.0625 L 1 -5.25 L 1 -5.125 C 1.109375 -5.171875 1.203125 -5.203125 1.234375 -5.21875 C 1.40625 -5.28125 1.546875 -5.3125 1.640625 -5.3125 C 1.828125 -5.3125 1.90625 -5.1875 1.90625 -4.890625 L 1.90625 -0.828125 C 1.90625 -0.53125 1.84375 -0.328125 1.6875 -0.25 C 1.5625 -0.171875 1.4375 -0.140625 1.0625 -0.140625 L 1.0625 0 L 3.53125 0 L 3.53125 -0.140625 C 2.828125 -0.140625 2.6875 -0.234375 2.6875 -0.65625 L 2.6875 -6.046875 Z M 2.609375 -6.0625 "
+ id="path65" />
+ </symbol>
+ </g>
+ <clipPath
+ id="clip1">
+ <path
+ d="M 30 83 L 50 83 L 50 145.550781 L 30 145.550781 Z M 30 83 "
+ id="path70" />
+ </clipPath>
+ <clipPath
+ id="clip2">
+ <path
+ d="M 48 1 L 50 1 L 50 145.550781 L 48 145.550781 Z M 48 1 "
+ id="path73" />
+ </clipPath>
+ <clipPath
+ id="clip3">
+ <path
+ d="M 149 90 L 153.730469 90 L 153.730469 98 L 149 98 Z M 149 90 "
+ id="path76" />
+ </clipPath>
+ </defs>
+ <g
+ id="surface521">
+ <path
+ style=" stroke:none;fill-rule:evenodd;fill:rgb(87.799072%,100%,100%);fill-opacity:1;"
+ d="M 79.515625 59.316406 L 79.515625 83.621094 L 49.304688 83.621094 L 79.539062 59.273438 "
+ id="path81" />
+ <path
+ style=" stroke:none;fill-rule:evenodd;fill:rgb(67.799377%,84.698486%,90.19928%);fill-opacity:1;"
+ d="M 48.859375 83.800781 L 91.117188 50.019531 L 48.859375 18.527344 L 48.859375 83.800781 "
+ id="path83" />
+ <path
+ style="fill:none;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,54.499817%,54.499817%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 300.391063 646.078781 L 300.391063 621.320969 "
+ transform="matrix(1,0,0,-1,-220.852,705.153)"
+ id="path85" />
+ <path
+ style="fill:none;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,54.499817%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 311.855906 655.133469 L 269.707469 621.285813 "
+ transform="matrix(1,0,0,-1,-220.852,705.153)"
+ id="path87" />
+ <g
+ clip-path="url(#clip1)"
+ clip-rule="nonzero"
+ id="g91">
+ <path
+ style="fill:none;stroke-width:0.4;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(0%,0%,54.499817%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 269.711375 621.352219 L 251.785594 559.930344 "
+ transform="matrix(1,0,0,-1,-220.852,705.153)"
+ id="path89" />
+ </g>
+ <path
+ style="fill:none;stroke-width:0.4;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(17.999268%,54.499817%,34.098816%);stroke-opacity:1;stroke-dasharray:4;stroke-miterlimit:10;"
+ d="M 311.969188 655.133469 L 311.930125 621.375656 "
+ transform="matrix(1,0,0,-1,-220.852,705.153)"
+ id="path93" />
+ <path
+ style="fill:none;stroke-width:0.4;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(17.999268%,54.499817%,34.098816%);stroke-opacity:1;stroke-dasharray:4;stroke-miterlimit:10;"
+ d="M 311.992625 655.113938 L 269.711375 655.133469 "
+ transform="matrix(1,0,0,-1,-220.852,705.153)"
+ id="path95" />
+ <path
+ style="fill:none;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,54.499817%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 269.711375 686.625656 L 357.285594 621.352219 "
+ transform="matrix(1,0,0,-1,-220.852,705.153)"
+ id="path97" />
+ <path
+ style="fill:none;stroke-width:0.8;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 221.250438 621.30925 L 372.57075 621.352219 "
+ transform="matrix(1,0,0,-1,-220.852,705.153)"
+ id="path99" />
+ <path
+ style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.8;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 372.57075 621.352219 L 367.57075 623.016281 L 367.57075 619.688156 Z M 372.57075 621.352219 "
+ transform="matrix(1,0,0,-1,-220.852,705.153)"
+ id="path101" />
+ <g
+ clip-path="url(#clip2)"
+ clip-rule="nonzero"
+ id="g105">
+ <path
+ style="fill:none;stroke-width:0.8;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 269.711375 560.004563 L 269.711375 703.156906 "
+ transform="matrix(1,0,0,-1,-220.852,705.153)"
+ id="path103" />
+ </g>
+ <path
+ style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.8;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 269.711375 703.156906 L 268.047313 698.156906 L 271.379344 698.156906 Z M 269.711375 703.156906 "
+ transform="matrix(1,0,0,-1,-220.852,705.153)"
+ id="path107" />
+ <path
+ style="fill:none;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 278.305125 638.008469 L 294.50825 650.766281 "
+ transform="matrix(1,0,0,-1,-220.852,705.153)"
+ id="path109" />
+ <path
+ style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 294.50825 650.766281 L 291.531688 649.695969 L 292.766063 648.125656 Z M 294.50825 650.766281 "
+ transform="matrix(1,0,0,-1,-220.852,705.153)"
+ id="path111" />
+ <path
+ style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 278.305125 638.008469 L 281.281688 639.078781 L 280.043406 640.649094 Z M 278.305125 638.008469 "
+ transform="matrix(1,0,0,-1,-220.852,705.153)"
+ id="path113" />
+ <g
+ style="fill:rgb(0%,0%,0%);fill-opacity:1;"
+ id="g117">
+ <use
+ xlink:href="#glyph0-1"
+ x="35.514"
+ y="6.808"
+ id="use115" />
+ </g>
+ <g
+ clip-path="url(#clip3)"
+ clip-rule="nonzero"
+ id="g123">
+ <g
+ style="fill:rgb(0%,0%,0%);fill-opacity:1;"
+ id="g121">
+ <use
+ xlink:href="#glyph0-2"
+ x="148.925"
+ y="97.547"
+ id="use119" />
+ </g>
+ </g>
+ <g
+ style="fill:rgb(0%,0%,0%);fill-opacity:1;"
+ id="g127">
+ <use
+ xlink:href="#glyph1-1"
+ x="35.428"
+ y="20.532"
+ id="use125" />
+ </g>
+ <g
+ style="fill:rgb(0%,0%,0%);fill-opacity:1;"
+ id="g131">
+ <use
+ xlink:href="#glyph2-1"
+ x="40.697"
+ y="21.528"
+ id="use129" />
+ </g>
+ <g
+ style="fill:rgb(0%,0%,0%);fill-opacity:1;"
+ id="g135">
+ <use
+ xlink:href="#glyph1-2"
+ x="129.99"
+ y="94.426"
+ id="use133" />
+ </g>
+ <g
+ style="fill:rgb(0%,0%,0%);fill-opacity:1;"
+ id="g139">
+ <use
+ xlink:href="#glyph2-1"
+ x="134.077"
+ y="95.422"
+ id="use137" />
+ </g>
+ <g
+ style="fill:rgb(0%,39.199829%,0%);fill-opacity:1;"
+ id="g143">
+ <use
+ xlink:href="#glyph1-2"
+ x="84.013"
+ y="94.426"
+ id="use141" />
+ </g>
+ <g
+ style="fill:rgb(0%,39.199829%,0%);fill-opacity:1;"
+ id="g151">
+ <use
+ xlink:href="#glyph2-2"
+ x="88.1"
+ y="95.422"
+ id="use145" />
+ <use
+ xlink:href="#glyph2-3"
+ x="91.975451"
+ y="95.422"
+ id="use147" />
+ <use
+ xlink:href="#glyph2-4"
+ x="94.187149"
+ y="95.422"
+ id="use149" />
+ </g>
+ <g
+ style="fill:rgb(0%,39.199829%,0%);fill-opacity:1;"
+ id="g155">
+ <use
+ xlink:href="#glyph1-3"
+ x="27.115"
+ y="53.819"
+ id="use153" />
+ </g>
+ <g
+ style="fill:rgb(0%,39.199829%,0%);fill-opacity:1;"
+ id="g163">
+ <use
+ xlink:href="#glyph2-2"
+ x="32.491"
+ y="54.815"
+ id="use157" />
+ <use
+ xlink:href="#glyph2-3"
+ x="36.366451"
+ y="54.815"
+ id="use159" />
+ <use
+ xlink:href="#glyph2-4"
+ x="38.578149"
+ y="54.815"
+ id="use161" />
+ </g>
+ <g
+ style="fill:rgb(0%,0%,54.499817%);fill-opacity:1;"
+ id="g167">
+ <use
+ xlink:href="#glyph1-4"
+ x="52.908"
+ y="44.668"
+ id="use165" />
+ </g>
+ <g
+ style="fill:rgb(0%,0%,54.499817%);fill-opacity:1;"
+ id="g177">
+ <use
+ xlink:href="#glyph2-5"
+ x="59.689"
+ y="45.664"
+ id="use169" />
+ <use
+ xlink:href="#glyph2-6"
+ x="62.17965"
+ y="45.664"
+ id="use171" />
+ <use
+ xlink:href="#glyph2-7"
+ x="63.564451"
+ y="45.664"
+ id="use173" />
+ <use
+ xlink:href="#glyph2-7"
+ x="65.502177"
+ y="45.664"
+ id="use175" />
+ </g>
+ <g
+ style="fill:rgb(0%,54.499817%,54.499817%);fill-opacity:1;"
+ id="g181">
+ <use
+ xlink:href="#glyph1-4"
+ x="60.993"
+ y="80.846"
+ id="use179" />
+ </g>
+ <g
+ style="fill:rgb(0%,54.499817%,54.499817%);fill-opacity:1;"
+ id="g187">
+ <use
+ xlink:href="#glyph2-8"
+ x="67.774"
+ y="81.842"
+ id="use183" />
+ <use
+ xlink:href="#glyph2-9"
+ x="69.432773"
+ y="81.842"
+ id="use185" />
+ </g>
+ <g
+ style="fill:rgb(0%,54.499817%,54.499817%);fill-opacity:1;"
+ id="g191">
+ <use
+ xlink:href="#glyph2-10"
+ x="71.519938"
+ y="81.842"
+ id="use189" />
+ </g>
+ <path
+ style="fill:none;stroke-width:0.4;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(0%,39.199829%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 231.0395 583.649094 L 237.426219 583.649094 L 237.426219 605.528 L 231.0395 583.649094 "
+ transform="matrix(1,0,0,-1,-220.852,705.153)"
+ id="path193" />
+ <g
+ style="fill:rgb(0%,39.199829%,0%);fill-opacity:1;"
+ id="g197">
+ <use
+ xlink:href="#glyph3-1"
+ x="11.253"
+ y="130.607"
+ id="use195" />
+ </g>
+ <g
+ style="fill:rgb(0%,39.199829%,0%);fill-opacity:1;"
+ id="g201">
+ <use
+ xlink:href="#glyph1-5"
+ x="18.257"
+ y="113.294"
+ id="use199" />
+ </g>
+ <path
+ style="fill:none;stroke-width:0.8;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 299.312938 675.676438 L 324.734813 656.731125 "
+ transform="matrix(1,0,0,-1,-220.852,705.153)"
+ id="path203" />
+ <path
+ style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.8;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M 324.734813 656.731125 L 322.922313 659.324875 L 321.730906 657.723313 Z M 324.734813 656.731125 "
+ transform="matrix(1,0,0,-1,-220.852,705.153)"
+ id="path205" />
+ <path
+ style=" stroke:none;fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;"
+ d="M 80.765625 59.28125 C 80.765625 58.617188 80.226562 58.082031 79.566406 58.082031 C 78.902344 58.082031 78.363281 58.617188 78.363281 59.28125 C 78.363281 59.945312 78.902344 60.480469 79.566406 60.480469 C 80.226562 60.480469 80.765625 59.945312 80.765625 59.28125 "
+ id="path207" />
+ </g>
+</svg>
diff --git a/doc/dev-doc/manual/figures/cl/stress_strain_el.svg b/doc/dev-doc/manual/figures/cl/stress_strain_el.svg
new file mode 100644
index 000000000..340d82871
--- /dev/null
+++ b/doc/dev-doc/manual/figures/cl/stress_strain_el.svg
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ viewBox="0 0 1288.5931 551.1034"
+ height="551.10339"
+ width="1288.5931"
+ xml:space="preserve"
+ id="svg2"
+ version="1.1"><metadata
+ id="metadata8"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
+ id="defs6"><clipPath
+ id="clipPath22"
+ clipPathUnits="userSpaceOnUse"><path
+ id="path20"
+ d="M 0,425.197 H 453.543 V 0 H 0 Z" /></clipPath><clipPath
+ id="clipPath38"
+ clipPathUnits="userSpaceOnUse"><path
+ id="path36"
+ d="M 0,425.197 H 453.543 V 0 H 0 Z" /></clipPath><clipPath
+ id="clipPath18"
+ clipPathUnits="userSpaceOnUse"><path
+ id="path16"
+ d="M 0,141.732 H 283.465 V 0 H 0 Z" /></clipPath></defs><g
+ transform="matrix(1.3333333,0,0,-1.3333333,-23.475845,559.60545)"
+ id="g10"><g
+ transform="translate(42.52,30.9707)"
+ id="g12"><path
+ id="path14"
+ style="fill:none;stroke:#ed1c24;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
+ d="M 0,0 255.118,311.812" /></g><g
+ id="g16"><g
+ clip-path="url(#clipPath22)"
+ id="g18"><g
+ transform="translate(204.4868,239.8325)"
+ id="g24"><path
+ id="path26"
+ style="fill:#ed1c24;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 0,0 c -0.603,0.494 -0.504,1.133 0.221,1.42 l 13.641,5.413 c 0.725,0.288 1.204,-0.104 1.066,-0.871 L 12.317,-8.48 c -0.139,-0.768 -0.746,-0.991 -1.349,-0.498 z" /></g><g
+ transform="translate(177.8687,184.9092)"
+ id="g28"><path
+ id="path30"
+ style="fill:#ed1c24;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 0,0 c 0.603,-0.493 0.504,-1.133 -0.221,-1.42 l -13.641,-5.414 c -0.725,-0.287 -1.204,0.105 -1.066,0.872 l 2.611,14.442 c 0.139,0.767 0.746,0.991 1.349,0.498 z" /></g></g></g><g
+ id="g32"><g
+ clip-path="url(#clipPath38)"
+ id="g34"><g
+ transform="translate(42.52,37.1699)"
+ id="g40"><path
+ id="path42"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="M 0,0 H -5.102 V -11.339 H 6.236 v 5.103 C 2.792,-6.236 0,-3.444 0,0" /></g><g
+ transform="translate(47.377,389.7744)"
+ id="g44"><path
+ id="path46"
+ style="fill:#110f0d;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 0,0 -0.821,2.907 -3.115,11.114 c -0.508,1.799 -1.329,1.799 -1.836,0 L -8.781,3.303 -9.709,0 c -0.254,-0.9 -0.044,-1.637 0.463,-1.637 h 0.92 6.944 0.92 C 0.045,-1.637 0.244,-0.9 0,0" /></g><g
+ transform="translate(401.3223,26.1172)"
+ id="g48"><path
+ id="path50"
+ style="fill:#110f0d;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 0,0 2.906,0.813 11.115,3.116 c 1.801,0.506 1.801,1.339 0,1.844 L 3.299,8.775 0,9.702 C -0.9,9.958 -1.637,9.756 -1.637,9.247 V 8.329 1.383 0.464 C -1.637,-0.044 -0.9,-0.255 0,0" /></g><g
+ transform="translate(42.52,396.6416)"
+ id="g52"><path
+ id="path54"
+ style="fill:none;stroke:#110f0d;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
+ d="m 0,0 v -358.425 c 0,-3.989 1.331,-7.246 2.958,-7.246 h 362.711" /></g><text
+ id="text58"
+ style="font-variant:normal;font-weight:normal;font-stretch:normal;font-size:29.42959976px;font-family:Symbol;-inkscape-font-specification:SymbolMT;writing-mode:lr-tb;fill:#231f20;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ transform="matrix(0.95142186,0,-0.30789031,-0.90022517,411.8042,6.6094)"><tspan
+ id="tspan56"
+ y="0"
+ x="0">ε</tspan></text>
+
+<text
+ id="text62"
+ style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:28px;font-family:'Times New Roman';-inkscape-font-specification:TimesNewRomanPS-ItalicMT;writing-mode:lr-tb;fill:#110f0d;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ transform="matrix(1,0,0,-1,16.7729,405.2666)"><tspan
+ id="tspan60"
+ y="0"
+ x="0">σ</tspan></text>
+
+</g></g><g
+ transform="matrix(2.0404154,0,0,2.0404154,451.93853,68.063376)"
+ id="g12-3"><g
+ clip-path="url(#clipPath18)"
+ id="g14"><g
+ transform="translate(141.7324,56.6934)"
+ id="g20"><path
+ id="path22"
+ style="fill:none;stroke:#231f20;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 0,0 7.392,36.96 c 0.612,3.058 1.612,3.058 2.224,0 l 14.783,-73.92 c 0.613,-3.058 1.613,-3.058 2.225,0 L 34.016,0" /></g><g
+ transform="translate(107.7163,56.6934)"
+ id="g24-6"><path
+ id="path26-7"
+ style="fill:none;stroke:#231f20;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 0,0 7.392,36.96 c 0.611,3.058 1.612,3.058 2.224,0 L 24.4,-36.96 c 0.611,-3.058 1.612,-3.058 2.224,0 L 34.016,0" /></g><g
+ transform="translate(73.7002,56.6934)"
+ id="g28-5"><path
+ id="path30-3"
+ style="fill:none;stroke:#231f20;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 0,0 7.392,36.96 c 0.611,3.058 1.612,3.058 2.224,0 L 24.4,-36.96 c 0.611,-3.058 1.612,-3.058 2.224,0 L 34.016,0" /></g><g
+ transform="translate(73.7002,56.6934)"
+ id="g32-5"><path
+ id="path34"
+ style="fill:none;stroke:#231f20;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 0,0 -7.392,-36.96 c -0.611,-3.058 -1.612,-3.058 -2.224,0 L -24.4,36.96 c -0.611,3.058 -1.612,3.058 -2.224,0 l -6.28,-31.4 C -33.515,2.502 -36.567,0 -39.685,0 h -11.338" /></g><g
+ transform="translate(175.748,56.6934)"
+ id="g36"><path
+ id="path38"
+ style="fill:none;stroke:#231f20;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 0,0 7.392,36.96 c 0.612,3.058 1.612,3.058 2.224,0 l 14.783,-73.92 c 0.613,-3.058 1.613,-3.058 2.225,0 L 34.016,0" /></g><g
+ transform="translate(209.7637,56.6934)"
+ id="g40-6"><path
+ id="path42-2"
+ style="fill:none;stroke:#231f20;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 0,0 7.392,36.96 c 0.612,3.058 1.612,3.058 2.224,0 l 14.783,-73.92 c 0.613,-3.058 1.613,-3.058 2.225,0 l 6.279,31.4 C 33.516,-2.502 36.566,0 39.685,0 h 11.338" /></g><text
+ id="text46"
+ style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:30px;font-family:Cambria;-inkscape-font-specification:Cambria-Italic;writing-mode:lr-tb;fill:#231f20;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ transform="matrix(1,0,0,-1,133.0312,116.7319)"><tspan
+ id="tspan44"
+ y="0"
+ x="0">E</tspan></text>
+
+</g></g><text
+ transform="scale(1,-1)"
+ y="-190.20499"
+ x="269.05658"
+ id="text46-9"
+ style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:61.21250153px;font-family:Cambria;-inkscape-font-specification:Cambria-Italic;writing-mode:lr-tb;fill:#231f20;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.04041004"><tspan
+ style="stroke-width:2.04041004"
+ id="tspan44-1"
+ y="-190.20499"
+ x="269.05658">E</tspan></text>
+
+<path
+ id="path7302"
+ d="m 139.02839,144.80619 h 117.14513 v 142.31108 l -0.73559,-0.24734"
+ style="fill:none;stroke:#000000;stroke-width:3.47741008;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /></g></svg>
\ No newline at end of file
diff --git a/doc/dev-doc/manual/figures/cl/stress_strain_hook.tex b/doc/dev-doc/manual/figures/cl/stress_strain_hook.tex
new file mode 100644
index 000000000..71c671036
--- /dev/null
+++ b/doc/dev-doc/manual/figures/cl/stress_strain_hook.tex
@@ -0,0 +1,10 @@
+\documentclass[tikz,convert={outfile=\jobname.svg}]{standalone}
+%\usetikzlibrary{...}% tikz package already loaded by 'tikz' option
+\begin{document}
+\begin{tikzpicture}% Example:
+ \draw[thick,latex-latex] (0,5) node[left] {$\sigma$} |- (5,0) node (x) [right, below] {$\varepsilon$};
+ \draw[thin] (1.5,1.5) -- (2.5,1.5) -- (2.5,2.5) node [midway, right] {E};
+ \draw[very thick,color=red] (0,0) -- (4,4);
+ \draw[very thick,latex-latex,color=red] (1,1) -- (3,3);
+\end{tikzpicture}
+\end{document}
diff --git a/doc/dev-doc/manual/figures/cl/stress_strain_neo.svg b/doc/dev-doc/manual/figures/cl/stress_strain_neo.svg
new file mode 100644
index 000000000..2b8c92ac6
--- /dev/null
+++ b/doc/dev-doc/manual/figures/cl/stress_strain_neo.svg
@@ -0,0 +1,209 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ id="svg78"
+ version="1.2"
+ viewBox="0 0 408.828308 404.326477"
+ height="404.326477pt"
+ width="408.828308pt">
+ <metadata
+ id="metadata82">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs49">
+ <clipPath
+ id="clip1">
+ <path
+ id="path2"
+ d="M 0 0 L 408.828125 0 L 408.828125 404.328125 L 0 404.328125 Z M 0 0 " />
+ </clipPath>
+ <filter
+ height="100%"
+ width="100%"
+ y="0%"
+ x="0%"
+ filterUnits="objectBoundingBox"
+ id="alpha">
+ <feColorMatrix
+ id="feColorMatrix5"
+ values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
+ in="SourceGraphic"
+ type="matrix" />
+ </filter>
+ <clipPath
+ id="clip3">
+ <path
+ id="path8"
+ d="M 0 0 L 408.828125 0 L 408.828125 404.328125 L 0 404.328125 Z M 0 0 " />
+ </clipPath>
+ <clipPath
+ id="clip2">
+ <rect
+ id="rect11"
+ height="405"
+ width="409"
+ y="0"
+ x="0" />
+ </clipPath>
+ <g
+ clip-path="url(#clip2)"
+ id="surface703">
+ <g
+ id="g16"
+ clip-rule="nonzero"
+ clip-path="url(#clip3)">
+ <path
+ id="path14"
+ d="M 0 404.328125 L 408.828125 404.328125 L 408.828125 0 L 0 0 Z M 0 404.328125 "
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:0.98;" />
+ </g>
+ </g>
+ <mask
+ id="mask0">
+ <g
+ id="g21"
+ filter="url(#alpha)">
+ <use
+ id="use19"
+ xlink:href="#surface703" />
+ </g>
+ </mask>
+ <clipPath
+ id="clip5">
+ <path
+ id="path24"
+ d="M 0 0 L 408.828125 0 L 408.828125 404.328125 L 0 404.328125 Z M 0 0 " />
+ </clipPath>
+ <clipPath
+ id="clip7">
+ <path
+ id="path27"
+ d="M 1 259 L 206 259 L 206 404.328125 L 1 404.328125 Z M 1 259 " />
+ </clipPath>
+ <clipPath
+ id="clip6">
+ <rect
+ id="rect30"
+ height="405"
+ width="409"
+ y="0"
+ x="0" />
+ </clipPath>
+ <g
+ clip-path="url(#clip6)"
+ id="surface700">
+ <g
+ id="g35"
+ clip-rule="nonzero"
+ clip-path="url(#clip7)">
+ <path
+ id="path33"
+ transform="matrix(1,0,0,1,0,-0.000000000000056843)"
+ d="M 24.199219 381.753906 C 24.199219 381.753906 80.738281 301.085938 182.644531 282.164063 "
+ style="fill:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(100%,0%,0%);stroke-opacity:1;stroke-miterlimit:4;" />
+ </g>
+ </g>
+ <clipPath
+ id="clip4">
+ <rect
+ id="rect38"
+ height="405"
+ width="409"
+ y="0"
+ x="0" />
+ </clipPath>
+ <g
+ clip-path="url(#clip4)"
+ id="surface702">
+ <g
+ id="g43"
+ clip-rule="nonzero"
+ clip-path="url(#clip5)">
+ <use
+ id="use41"
+ xlink:href="#surface700" />
+ </g>
+ </g>
+ <clipPath
+ id="clip8">
+ <path
+ id="path46"
+ d="M 395 388 L 408.828125 388 L 408.828125 404.328125 L 395 404.328125 Z M 395 388 " />
+ </clipPath>
+ </defs>
+ <g
+ id="surface694">
+ <path
+ id="path51"
+ d="M 397.550781 381.753906 L 24.199219 381.753906 L 24.199219 9.210938 "
+ style="fill:none;stroke-width:2.4;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:4;" />
+ <path
+ id="path53"
+ transform="matrix(-1,0.000000000000000122,-0.000000000000000122,-1,0,0)"
+ d="M -387.953125 -381.753906 L -383.152344 -386.554688 L -399.953125 -381.753906 L -383.152344 -376.953125 Z M -387.953125 -381.753906 "
+ style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:4;" />
+ <path
+ id="path55"
+ transform="matrix(-0.000000000000000061,1,-1,-0.000000000000000061,0,0)"
+ d="M 18.808594 -24.199219 L 23.609375 -29 L 6.808594 -24.199219 L 23.609375 -19.398438 Z M 18.808594 -24.199219 "
+ style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:4;" />
+ <path
+ id="path57"
+ d="M 182.644531 282.164062 C 206.214844 277.789062 225.519531 264.542969 241.292969 246.574219 "
+ style="fill:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(100%,0%,0%);stroke-opacity:1;stroke-miterlimit:3.9;" />
+ <path
+ id="path59"
+ transform="matrix(1,-0.185647,0.185647,1,0,0)"
+ d="M 146.494515 315.059548 L 120.715589 305.578456 L 146.495008 296.098702 C 142.377469 301.697575 142.401325 309.354347 146.494515 315.059548 Z M 146.494515 315.059548 "
+ style="fill-rule:evenodd;fill:rgb(100%,0%,0%);fill-opacity:1;stroke-width:1.474801;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(100%,0%,0%);stroke-opacity:1;stroke-miterlimit:4;" />
+ <path
+ id="path61"
+ transform="matrix(-0.877909,1,-1,-0.877909,0,0)"
+ d="M 35.342355 -251.242215 L 15.636478 -258.489124 L 35.344467 -265.736257 C 32.19566 -261.456266 32.212468 -255.603835 35.342355 -251.242215 Z M 35.342355 -251.242215 "
+ style="fill-rule:evenodd;fill:rgb(100%,0%,0%);fill-opacity:1;stroke-width:1.127238;stroke-linecap:butt;stroke-linejoin:round;stroke:rgb(100%,0%,0%);stroke-opacity:1;stroke-miterlimit:4;" />
+ <path
+ id="path63"
+ d="M 241.292969 246.574219 C 296.648438 183.523438 308.527344 62.332031 308.527344 62.332031 "
+ style="fill:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(100%,0%,0%);stroke-opacity:1;stroke-miterlimit:4;" />
+ <g
+ id="g67"
+ clip-rule="nonzero"
+ clip-path="url(#clip1)">
+ <use
+ id="use65"
+ mask="url(#mask0)"
+ xlink:href="#surface702" />
+ </g>
+ <g
+ id="g71"
+ clip-rule="nonzero"
+ clip-path="url(#clip8)">
+ <path
+ id="path69"
+ d="M 398.957031 395.796875 C 400.082031 396.277344 401.101562 396.277344 401.90625 396.277344 C 402.765625 396.277344 404.695312 396.277344 404.695312 395.207031 C 404.695312 394.402344 403.464844 394.292969 402.175781 394.292969 C 401.476562 394.292969 400.242188 394.347656 399.011719 394.9375 C 398.207031 394.5625 397.613281 393.863281 397.613281 392.953125 C 397.613281 390.859375 400.996094 389.625 404.054688 389.625 C 404.589844 389.625 405.824219 389.625 407.21875 390.589844 C 407.59375 390.859375 407.648438 390.96875 407.863281 390.96875 C 408.34375 390.96875 408.828125 390.484375 408.828125 390 C 408.828125 389.359375 406.734375 388.070312 404.375 388.070312 C 400.457031 388.070312 396.757812 390.375 396.757812 392.953125 C 396.757812 394.453125 397.992188 395.3125 398.152344 395.421875 C 396.113281 396.546875 395.039062 398.425781 395.039062 400.035156 C 395.039062 402.234375 396.972656 404.328125 400.621094 404.328125 C 405.125 404.328125 407.058594 401.320312 407.058594 400.785156 C 407.058594 400.570312 406.84375 400.464844 406.683594 400.464844 C 406.46875 400.464844 406.359375 400.570312 406.308594 400.679688 C 405.824219 401.484375 405.019531 402.769531 400.886719 402.769531 C 398.796875 402.769531 395.898438 402.234375 395.898438 399.820312 C 395.898438 398.640625 396.863281 396.816406 398.957031 395.796875 Z M 400.082031 395.367188 C 400.886719 395.097656 401.585938 395.042969 402.175781 395.042969 C 403.035156 395.042969 403.140625 395.097656 403.785156 395.257812 C 403.25 395.472656 403.195312 395.527344 401.90625 395.527344 C 401.15625 395.527344 400.726562 395.527344 400.082031 395.367188 Z M 400.082031 395.367188 "
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" />
+ </g>
+ <path
+ id="path73"
+ d="M 16.417969 1.984375 C 16.902344 1.984375 18.132812 1.984375 18.132812 0.804688 C 18.132812 0 17.382812 0 16.792969 0 L 8.960938 0 C 3.808594 0 0 5.632812 0 9.710938 C 0 12.714844 2.039062 15.128906 5.152344 15.128906 C 9.175781 15.128906 13.734375 11 13.734375 5.742188 C 13.734375 5.152344 13.734375 3.488281 12.660156 1.984375 Z M 5.203125 14.378906 C 3.488281 14.378906 2.144531 13.144531 2.144531 10.675781 C 2.144531 9.65625 2.523438 6.867188 3.753906 4.828125 C 5.203125 2.46875 7.242188 1.984375 8.421875 1.984375 C 11.265625 1.984375 11.535156 4.238281 11.535156 5.3125 C 11.535156 6.921875 10.835938 9.710938 9.710938 11.480469 C 8.371094 13.464844 6.492188 14.378906 5.203125 14.378906 Z M 5.203125 14.378906 "
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" />
+ <path
+ id="path75"
+ d="M 24.199219 381.753906 L 174.875 217.328125 "
+ style="fill:none;stroke-width:2.4;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-dasharray:2.4,4.8;stroke-miterlimit:3.9;" />
+ </g>
+</svg>
diff --git a/doc/dev-doc/manual/figures/cl/stress_strain_visco.svg b/doc/dev-doc/manual/figures/cl/stress_strain_visco.svg
new file mode 100644
index 000000000..28aa90e1d
--- /dev/null
+++ b/doc/dev-doc/manual/figures/cl/stress_strain_visco.svg
@@ -0,0 +1,633 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="453.543pt"
+ height="425.197pt"
+ viewBox="0 0 453.543 425.197"
+ version="1.2"
+ id="svg278"
+ sodipodi:docname="stress_strain_visco.svg"
+ inkscape:version="1.0.2 (e86c870879, 2021-01-15)">
+ <metadata
+ id="metadata282">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="744"
+ inkscape:window-height="480"
+ id="namedview280"
+ showgrid="false"
+ inkscape:zoom="1.5116523"
+ inkscape:cx="302.362"
+ inkscape:cy="283.46466"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg278" />
+ <defs
+ id="defs239">
+ <g
+ id="g20">
+ <symbol
+ overflow="visible"
+ id="glyph0-0">
+ <path
+ style="stroke:none;"
+ d="M 3.890625 0 L 3.890625 -17.5 L 17.890625 -17.5 L 17.890625 0 Z M 4.328125 -0.4375 L 17.453125 -0.4375 L 17.453125 -17.0625 L 4.328125 -17.0625 Z M 4.328125 -0.4375 "
+ id="path2" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-1">
+ <path
+ style="stroke:none;"
+ d=""
+ id="path5" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-0">
+ <path
+ style="stroke:none;"
+ d="M 1.40625 0 L 7.078125 -16.5625 L 21.078125 -16.5625 L 15.40625 0 Z M 1.984375 -0.421875 L 15.109375 -0.421875 L 20.5 -16.15625 L 7.375 -16.15625 Z M 1.984375 -0.421875 "
+ id="path8" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-1">
+ <path
+ style="stroke:none;"
+ d="M 6.609375 -6.359375 C 5.660156 -6.734375 5.078125 -7.132812 4.859375 -7.5625 C 4.640625 -8 4.628906 -8.515625 4.828125 -9.109375 C 5.109375 -9.910156 5.695312 -10.578125 6.59375 -11.109375 C 7.800781 -11.847656 9.195312 -12.21875 10.78125 -12.21875 C 11.71875 -12.21875 12.523438 -12.113281 13.203125 -11.90625 C 13.878906 -11.695312 14.296875 -11.425781 14.453125 -11.09375 C 14.609375 -10.757812 14.632812 -10.445312 14.53125 -10.15625 C 14.4375 -9.882812 14.234375 -9.640625 13.921875 -9.421875 C 13.617188 -9.203125 13.3125 -9.09375 13 -9.09375 C 12.5 -9.09375 12.1875 -9.398438 12.0625 -10.015625 C 11.945312 -10.640625 11.800781 -11.019531 11.625 -11.15625 C 11.34375 -11.375 10.941406 -11.484375 10.421875 -11.484375 C 9.597656 -11.484375 8.867188 -11.253906 8.234375 -10.796875 C 7.597656 -10.347656 7.144531 -9.738281 6.875 -8.96875 C 6.613281 -8.207031 6.617188 -7.625 6.890625 -7.21875 C 7.148438 -6.8125 7.632812 -6.609375 8.34375 -6.609375 C 8.570312 -6.609375 8.835938 -6.625 9.140625 -6.65625 C 9.597656 -6.71875 9.957031 -6.75 10.21875 -6.75 C 10.613281 -6.75 10.863281 -6.703125 10.96875 -6.609375 C 11.082031 -6.515625 11.113281 -6.40625 11.0625 -6.28125 C 11.019531 -6.144531 10.925781 -6.046875 10.78125 -5.984375 C 10.582031 -5.867188 10.273438 -5.8125 9.859375 -5.8125 C 9.765625 -5.8125 9.617188 -5.820312 9.421875 -5.84375 C 8.972656 -5.90625 8.597656 -5.9375 8.296875 -5.9375 C 7.523438 -5.9375 6.816406 -5.695312 6.171875 -5.21875 C 5.523438 -4.75 5.066406 -4.125 4.796875 -3.34375 C 4.492188 -2.445312 4.507812 -1.742188 4.84375 -1.234375 C 5.175781 -0.734375 5.742188 -0.484375 6.546875 -0.484375 C 7.191406 -0.484375 7.78125 -0.664062 8.3125 -1.03125 C 8.625 -1.25 9.03125 -1.695312 9.53125 -2.375 C 9.851562 -2.820312 10.085938 -3.097656 10.234375 -3.203125 C 10.617188 -3.484375 10.972656 -3.625 11.296875 -3.625 C 11.679688 -3.625 11.960938 -3.5 12.140625 -3.25 C 12.328125 -3 12.359375 -2.6875 12.234375 -2.3125 C 12.023438 -1.71875 11.476562 -1.171875 10.59375 -0.671875 C 9.289062 0.046875 7.828125 0.40625 6.203125 0.40625 C 4.492188 0.40625 3.296875 0.046875 2.609375 -0.671875 C 1.929688 -1.390625 1.742188 -2.179688 2.046875 -3.046875 C 2.253906 -3.671875 2.703125 -4.265625 3.390625 -4.828125 C 4.078125 -5.390625 5.148438 -5.898438 6.609375 -6.359375 Z M 6.609375 -6.359375 "
+ id="path11" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-0">
+ <path
+ style="stroke:none;"
+ d="M 3.890625 0 L 3.890625 -17.5 L 17.890625 -17.5 L 17.890625 0 Z M 4.328125 -0.4375 L 17.453125 -0.4375 L 17.453125 -17.0625 L 4.328125 -17.0625 Z M 4.328125 -0.4375 "
+ id="path14" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-1">
+ <path
+ style="stroke:none;"
+ d="M 14.765625 -12.046875 L 14.21875 -10.125 L 9.78125 -10.125 C 11.0625 -9.050781 11.703125 -7.851562 11.703125 -6.53125 C 11.703125 -4.800781 11.03125 -3.222656 9.6875 -1.796875 C 8.34375 -0.378906 6.734375 0.328125 4.859375 0.328125 C 3.546875 0.328125 2.53125 -0.03125 1.8125 -0.75 C 1.09375 -1.476562 0.734375 -2.457031 0.734375 -3.6875 C 0.734375 -4.851562 1.003906 -6.070312 1.546875 -7.34375 C 2.097656 -8.613281 2.847656 -9.609375 3.796875 -10.328125 C 4.742188 -11.054688 5.734375 -11.523438 6.765625 -11.734375 C 7.796875 -11.941406 9.394531 -12.046875 11.5625 -12.046875 Z M 9.109375 -10.125 C 6.804688 -10.101562 5.179688 -9.273438 4.234375 -7.640625 C 3.296875 -6.015625 2.828125 -4.367188 2.828125 -2.703125 C 2.828125 -1.097656 3.519531 -0.296875 4.90625 -0.296875 C 6.238281 -0.296875 7.363281 -1.179688 8.28125 -2.953125 C 9.195312 -4.722656 9.65625 -6.382812 9.65625 -7.9375 C 9.65625 -8.863281 9.472656 -9.59375 9.109375 -10.125 Z M 9.109375 -10.125 "
+ id="path17" />
+ </symbol>
+ </g>
+ <clipPath
+ id="clip1">
+ <path
+ d="M 42 61 L 320 61 L 320 395 L 42 395 Z M 42 61 "
+ id="path22" />
+ </clipPath>
+ <clipPath
+ id="clip2">
+ <path
+ d="M 319.96875 61.296875 C 276.769531 204.195312 174.578125 322.585938 42.519531 394.226562 C 110.605469 266.503906 219.441406 127.34375 319.96875 61.296875 "
+ id="path25" />
+ </clipPath>
+ <clipPath
+ id="clip3">
+ <rect
+ x="0"
+ y="0"
+ width="69"
+ height="69"
+ id="rect28" />
+ </clipPath>
+ <g
+ id="surface388"
+ clip-path="url(#clip3)">
+ <rect
+ x="0"
+ y="0"
+ width="69"
+ height="69"
+ style="fill:rgb(85.888672%,85.385132%,84.693909%);fill-opacity:1;stroke:none;"
+ id="rect31" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 59.648438 69.757812 C 59.664062 69.683594 59.65625 69.628906 59.621094 69.566406 C 59.570312 69.464844 59.507812 69.503906 59.457031 69.4375 C 59.40625 69.375 59.359375 69.160156 59.378906 69.074219 C 59.207031 69.019531 59.367188 68.851562 59.363281 68.726562 C 59.359375 68.675781 59.320312 68.621094 59.316406 68.566406 C 59.308594 68.445312 59.335938 68.300781 59.386719 68.195312 C 59.484375 67.996094 59.652344 67.78125 59.820312 67.652344 C 60.011719 67.503906 60.183594 67.078125 60.429688 67.019531 C 60.605469 66.980469 60.8125 67.125 60.972656 67.164062 C 61.152344 67.207031 61.371094 67.152344 61.542969 67.214844 C 61.765625 67.292969 61.671875 67.554688 61.832031 67.664062 C 61.933594 67.738281 62.117188 67.675781 62.238281 67.71875 C 62.4375 67.785156 62.578125 67.980469 62.613281 68.175781 C 62.628906 68.257812 62.621094 68.320312 62.652344 68.398438 C 62.691406 68.5 62.761719 68.558594 62.742188 68.691406 C 62.734375 68.75 62.691406 68.8125 62.675781 68.875 C 62.65625 68.992188 62.679688 69.101562 62.679688 69.214844 C 62.675781 69.414062 62.695312 69.59375 62.601562 69.765625 C 62.21875 70.449219 61.363281 70.691406 60.625 70.554688 C 60.445312 70.519531 60.339844 70.425781 60.183594 70.347656 C 60.011719 70.257812 60.042969 70.179688 59.867188 70.121094 C 59.785156 70.097656 59.648438 69.996094 59.652344 69.898438 C 59.652344 69.78125 59.65625 69.847656 59.664062 69.730469 C 59.640625 69.71875 59.640625 69.734375 59.632812 69.753906 "
+ id="path33" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 51.980469 69.757812 C 52 69.683594 51.988281 69.628906 51.953125 69.566406 C 51.902344 69.464844 51.84375 69.503906 51.789062 69.4375 C 51.738281 69.375 51.691406 69.160156 51.710938 69.074219 C 51.542969 69.019531 51.699219 68.851562 51.695312 68.726562 C 51.691406 68.675781 51.65625 68.621094 51.652344 68.566406 C 51.640625 68.445312 51.667969 68.300781 51.71875 68.195312 C 51.816406 67.996094 51.988281 67.78125 52.152344 67.652344 C 52.34375 67.503906 52.519531 67.078125 52.761719 67.019531 C 52.941406 66.980469 53.144531 67.125 53.304688 67.164062 C 53.484375 67.207031 53.703125 67.152344 53.875 67.214844 C 54.097656 67.292969 54.003906 67.554688 54.164062 67.664062 C 54.269531 67.738281 54.453125 67.675781 54.570312 67.71875 C 54.769531 67.785156 54.910156 67.980469 54.945312 68.175781 C 54.960938 68.257812 54.957031 68.320312 54.984375 68.398438 C 55.023438 68.5 55.097656 68.558594 55.074219 68.691406 C 55.066406 68.75 55.023438 68.8125 55.011719 68.875 C 54.988281 68.992188 55.011719 69.101562 55.011719 69.214844 C 55.007812 69.414062 55.027344 69.59375 54.933594 69.765625 C 54.554688 70.449219 53.695312 70.691406 52.957031 70.554688 C 52.777344 70.519531 52.671875 70.425781 52.515625 70.347656 C 52.347656 70.257812 52.378906 70.179688 52.199219 70.121094 C 52.117188 70.097656 51.984375 69.996094 51.984375 69.898438 C 51.984375 69.78125 51.988281 69.847656 51.996094 69.730469 C 51.972656 69.71875 51.972656 69.734375 51.964844 69.753906 "
+ id="path35" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 44.3125 69.757812 C 44.332031 69.683594 44.320312 69.628906 44.289062 69.566406 C 44.234375 69.464844 44.175781 69.503906 44.121094 69.4375 C 44.074219 69.375 44.023438 69.160156 44.042969 69.074219 C 43.875 69.019531 44.035156 68.851562 44.027344 68.726562 C 44.027344 68.675781 43.988281 68.621094 43.984375 68.566406 C 43.972656 68.445312 44 68.300781 44.050781 68.195312 C 44.148438 67.996094 44.320312 67.78125 44.488281 67.652344 C 44.675781 67.503906 44.851562 67.078125 45.09375 67.019531 C 45.273438 66.980469 45.480469 67.125 45.636719 67.164062 C 45.816406 67.207031 46.035156 67.152344 46.210938 67.214844 C 46.429688 67.292969 46.335938 67.554688 46.496094 67.664062 C 46.601562 67.738281 46.785156 67.675781 46.902344 67.71875 C 47.101562 67.785156 47.242188 67.980469 47.28125 68.175781 C 47.296875 68.257812 47.289062 68.320312 47.316406 68.398438 C 47.355469 68.5 47.429688 68.558594 47.410156 68.691406 C 47.398438 68.75 47.355469 68.8125 47.34375 68.875 C 47.320312 68.992188 47.347656 69.101562 47.34375 69.214844 C 47.34375 69.414062 47.359375 69.59375 47.265625 69.765625 C 46.886719 70.449219 46.03125 70.691406 45.292969 70.554688 C 45.113281 70.519531 45.003906 70.425781 44.851562 70.347656 C 44.679688 70.257812 44.710938 70.179688 44.53125 70.121094 C 44.449219 70.097656 44.316406 69.996094 44.316406 69.898438 C 44.316406 69.78125 44.320312 69.847656 44.328125 69.730469 C 44.308594 69.71875 44.308594 69.734375 44.296875 69.753906 "
+ id="path37" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 36.648438 69.757812 C 36.664062 69.683594 36.65625 69.628906 36.621094 69.566406 C 36.570312 69.464844 36.507812 69.503906 36.457031 69.4375 C 36.40625 69.375 36.359375 69.160156 36.378906 69.074219 C 36.207031 69.019531 36.367188 68.851562 36.363281 68.726562 C 36.359375 68.675781 36.320312 68.621094 36.316406 68.566406 C 36.308594 68.445312 36.335938 68.300781 36.386719 68.195312 C 36.484375 67.996094 36.652344 67.78125 36.820312 67.652344 C 37.011719 67.503906 37.183594 67.078125 37.429688 67.019531 C 37.605469 66.980469 37.8125 67.125 37.972656 67.164062 C 38.152344 67.207031 38.371094 67.152344 38.542969 67.214844 C 38.765625 67.292969 38.671875 67.554688 38.832031 67.664062 C 38.933594 67.738281 39.117188 67.675781 39.238281 67.71875 C 39.4375 67.785156 39.578125 67.980469 39.613281 68.175781 C 39.628906 68.257812 39.621094 68.320312 39.652344 68.398438 C 39.691406 68.5 39.761719 68.558594 39.742188 68.691406 C 39.734375 68.75 39.691406 68.8125 39.675781 68.875 C 39.65625 68.992188 39.679688 69.101562 39.679688 69.214844 C 39.675781 69.414062 39.695312 69.59375 39.601562 69.765625 C 39.21875 70.449219 38.363281 70.691406 37.625 70.554688 C 37.445312 70.519531 37.339844 70.425781 37.183594 70.347656 C 37.011719 70.257812 37.042969 70.179688 36.867188 70.121094 C 36.785156 70.097656 36.648438 69.996094 36.652344 69.898438 C 36.652344 69.78125 36.65625 69.847656 36.664062 69.730469 C 36.640625 69.71875 36.640625 69.734375 36.632812 69.753906 "
+ id="path39" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 28.980469 69.757812 C 29 69.683594 28.988281 69.628906 28.953125 69.566406 C 28.902344 69.464844 28.84375 69.503906 28.789062 69.4375 C 28.738281 69.375 28.691406 69.160156 28.710938 69.074219 C 28.542969 69.019531 28.699219 68.851562 28.695312 68.726562 C 28.691406 68.675781 28.65625 68.621094 28.652344 68.566406 C 28.640625 68.445312 28.667969 68.300781 28.71875 68.195312 C 28.816406 67.996094 28.988281 67.78125 29.152344 67.652344 C 29.34375 67.503906 29.519531 67.078125 29.761719 67.019531 C 29.941406 66.980469 30.144531 67.125 30.304688 67.164062 C 30.484375 67.207031 30.703125 67.152344 30.875 67.214844 C 31.097656 67.292969 31.003906 67.554688 31.164062 67.664062 C 31.269531 67.738281 31.453125 67.675781 31.570312 67.71875 C 31.769531 67.785156 31.910156 67.980469 31.945312 68.175781 C 31.960938 68.257812 31.957031 68.320312 31.984375 68.398438 C 32.023438 68.5 32.097656 68.558594 32.074219 68.691406 C 32.066406 68.75 32.023438 68.8125 32.011719 68.875 C 31.988281 68.992188 32.011719 69.101562 32.011719 69.214844 C 32.007812 69.414062 32.027344 69.59375 31.933594 69.765625 C 31.554688 70.449219 30.695312 70.691406 29.957031 70.554688 C 29.777344 70.519531 29.671875 70.425781 29.515625 70.347656 C 29.347656 70.257812 29.378906 70.179688 29.199219 70.121094 C 29.117188 70.097656 28.984375 69.996094 28.984375 69.898438 C 28.984375 69.78125 28.988281 69.847656 28.996094 69.730469 C 28.972656 69.71875 28.972656 69.734375 28.964844 69.753906 "
+ id="path41" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 21.3125 69.757812 C 21.332031 69.683594 21.320312 69.628906 21.289062 69.566406 C 21.234375 69.464844 21.175781 69.503906 21.121094 69.4375 C 21.074219 69.375 21.023438 69.160156 21.042969 69.074219 C 20.875 69.019531 21.035156 68.851562 21.027344 68.726562 C 21.027344 68.675781 20.988281 68.621094 20.984375 68.566406 C 20.972656 68.445312 21 68.300781 21.050781 68.195312 C 21.148438 67.996094 21.320312 67.78125 21.488281 67.652344 C 21.675781 67.503906 21.851562 67.078125 22.09375 67.019531 C 22.273438 66.980469 22.480469 67.125 22.636719 67.164062 C 22.816406 67.207031 23.035156 67.152344 23.210938 67.214844 C 23.429688 67.292969 23.335938 67.554688 23.496094 67.664062 C 23.601562 67.738281 23.785156 67.675781 23.902344 67.71875 C 24.105469 67.785156 24.242188 67.980469 24.28125 68.175781 C 24.296875 68.257812 24.289062 68.320312 24.316406 68.398438 C 24.355469 68.5 24.429688 68.558594 24.410156 68.691406 C 24.398438 68.75 24.355469 68.8125 24.34375 68.875 C 24.320312 68.992188 24.347656 69.101562 24.34375 69.214844 C 24.34375 69.414062 24.359375 69.59375 24.265625 69.765625 C 23.886719 70.449219 23.03125 70.691406 22.292969 70.554688 C 22.113281 70.519531 22.003906 70.425781 21.851562 70.347656 C 21.679688 70.257812 21.710938 70.179688 21.53125 70.121094 C 21.449219 70.097656 21.316406 69.996094 21.316406 69.898438 C 21.316406 69.78125 21.320312 69.847656 21.328125 69.730469 C 21.308594 69.71875 21.308594 69.734375 21.296875 69.753906 "
+ id="path43" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 13.648438 69.757812 C 13.664062 69.683594 13.65625 69.628906 13.621094 69.566406 C 13.570312 69.464844 13.507812 69.503906 13.457031 69.4375 C 13.40625 69.375 13.359375 69.160156 13.378906 69.074219 C 13.207031 69.019531 13.367188 68.851562 13.363281 68.726562 C 13.359375 68.675781 13.320312 68.621094 13.316406 68.566406 C 13.308594 68.445312 13.335938 68.300781 13.386719 68.195312 C 13.484375 67.996094 13.652344 67.78125 13.820312 67.652344 C 14.011719 67.503906 14.183594 67.078125 14.429688 67.019531 C 14.605469 66.980469 14.8125 67.125 14.972656 67.164062 C 15.152344 67.207031 15.371094 67.152344 15.542969 67.214844 C 15.765625 67.292969 15.671875 67.554688 15.832031 67.664062 C 15.933594 67.738281 16.117188 67.675781 16.238281 67.71875 C 16.4375 67.785156 16.578125 67.980469 16.613281 68.175781 C 16.628906 68.257812 16.621094 68.320312 16.652344 68.398438 C 16.691406 68.5 16.761719 68.558594 16.742188 68.691406 C 16.734375 68.75 16.691406 68.8125 16.675781 68.875 C 16.65625 68.992188 16.679688 69.101562 16.679688 69.214844 C 16.675781 69.414062 16.695312 69.59375 16.601562 69.765625 C 16.21875 70.449219 15.363281 70.691406 14.625 70.554688 C 14.445312 70.519531 14.339844 70.425781 14.183594 70.347656 C 14.011719 70.257812 14.042969 70.179688 13.867188 70.121094 C 13.785156 70.097656 13.648438 69.996094 13.652344 69.898438 C 13.652344 69.78125 13.65625 69.847656 13.664062 69.730469 C 13.640625 69.71875 13.640625 69.734375 13.632812 69.753906 "
+ id="path45" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 5.980469 69.757812 C 6 69.683594 5.988281 69.628906 5.953125 69.566406 C 5.902344 69.464844 5.84375 69.503906 5.789062 69.4375 C 5.738281 69.375 5.691406 69.160156 5.710938 69.074219 C 5.542969 69.019531 5.699219 68.851562 5.695312 68.726562 C 5.691406 68.675781 5.65625 68.621094 5.652344 68.566406 C 5.640625 68.445312 5.667969 68.300781 5.71875 68.195312 C 5.816406 67.996094 5.988281 67.78125 6.152344 67.652344 C 6.34375 67.503906 6.519531 67.078125 6.761719 67.019531 C 6.9375 66.980469 7.144531 67.125 7.304688 67.164062 C 7.484375 67.207031 7.703125 67.152344 7.875 67.214844 C 8.097656 67.292969 8.003906 67.554688 8.164062 67.664062 C 8.269531 67.738281 8.453125 67.675781 8.570312 67.71875 C 8.769531 67.785156 8.910156 67.980469 8.945312 68.175781 C 8.960938 68.257812 8.957031 68.320312 8.984375 68.398438 C 9.023438 68.5 9.097656 68.558594 9.074219 68.691406 C 9.066406 68.75 9.023438 68.8125 9.011719 68.875 C 8.988281 68.992188 9.011719 69.101562 9.011719 69.214844 C 9.007812 69.414062 9.027344 69.59375 8.933594 69.765625 C 8.554688 70.449219 7.695312 70.691406 6.957031 70.554688 C 6.777344 70.519531 6.671875 70.425781 6.515625 70.347656 C 6.347656 70.257812 6.378906 70.179688 6.199219 70.121094 C 6.117188 70.097656 5.984375 69.996094 5.984375 69.898438 C 5.984375 69.78125 5.988281 69.847656 5.996094 69.730469 C 5.972656 69.71875 5.972656 69.734375 5.964844 69.753906 "
+ id="path47" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M -1.6875 69.757812 C -1.667969 69.683594 -1.679688 69.628906 -1.710938 69.566406 C -1.765625 69.464844 -1.824219 69.503906 -1.878906 69.4375 C -1.929688 69.375 -1.976562 69.160156 -1.957031 69.074219 C -2.125 69.019531 -1.964844 68.851562 -1.972656 68.726562 C -1.972656 68.675781 -2.011719 68.621094 -2.015625 68.566406 C -2.027344 68.445312 -2 68.300781 -1.949219 68.195312 C -1.851562 67.996094 -1.679688 67.78125 -1.511719 67.652344 C -1.324219 67.503906 -1.148438 67.078125 -0.90625 67.019531 C -0.726562 66.980469 -0.519531 67.125 -0.363281 67.164062 C -0.183594 67.207031 0.0351562 67.152344 0.210938 67.214844 C 0.429688 67.292969 0.335938 67.554688 0.496094 67.664062 C 0.601562 67.738281 0.785156 67.675781 0.902344 67.71875 C 1.105469 67.785156 1.242188 67.980469 1.28125 68.175781 C 1.296875 68.257812 1.289062 68.320312 1.316406 68.398438 C 1.355469 68.5 1.429688 68.558594 1.410156 68.691406 C 1.398438 68.75 1.355469 68.8125 1.34375 68.875 C 1.324219 68.992188 1.347656 69.101562 1.34375 69.214844 C 1.34375 69.414062 1.359375 69.59375 1.265625 69.765625 C 0.886719 70.449219 0.03125 70.691406 -0.707031 70.554688 C -0.886719 70.519531 -0.996094 70.425781 -1.148438 70.347656 C -1.320312 70.257812 -1.289062 70.179688 -1.46875 70.121094 C -1.550781 70.097656 -1.683594 69.996094 -1.683594 69.898438 C -1.679688 69.78125 -1.679688 69.847656 -1.671875 69.730469 C -1.691406 69.71875 -1.691406 69.734375 -1.703125 69.753906 "
+ id="path49" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 67.3125 69.757812 C 67.332031 69.683594 67.320312 69.628906 67.289062 69.566406 C 67.234375 69.464844 67.175781 69.503906 67.121094 69.4375 C 67.070312 69.375 67.023438 69.160156 67.042969 69.074219 C 66.875 69.019531 67.035156 68.851562 67.027344 68.726562 C 67.027344 68.675781 66.988281 68.621094 66.984375 68.566406 C 66.972656 68.445312 67 68.300781 67.050781 68.195312 C 67.148438 67.996094 67.320312 67.78125 67.488281 67.652344 C 67.675781 67.503906 67.851562 67.078125 68.09375 67.019531 C 68.273438 66.980469 68.480469 67.125 68.636719 67.164062 C 68.816406 67.207031 69.035156 67.152344 69.210938 67.214844 C 69.429688 67.292969 69.335938 67.554688 69.496094 67.664062 C 69.601562 67.738281 69.785156 67.675781 69.902344 67.71875 C 70.101562 67.785156 70.242188 67.980469 70.28125 68.175781 C 70.296875 68.257812 70.289062 68.320312 70.316406 68.398438 C 70.355469 68.5 70.429688 68.558594 70.410156 68.691406 C 70.398438 68.75 70.355469 68.8125 70.34375 68.875 C 70.324219 68.992188 70.347656 69.101562 70.34375 69.214844 C 70.34375 69.414062 70.359375 69.59375 70.265625 69.765625 C 69.886719 70.449219 69.03125 70.691406 68.292969 70.554688 C 68.113281 70.519531 68.003906 70.425781 67.851562 70.347656 C 67.679688 70.257812 67.710938 70.179688 67.53125 70.121094 C 67.449219 70.097656 67.316406 69.996094 67.316406 69.898438 C 67.320312 69.78125 67.320312 69.847656 67.328125 69.730469 C 67.308594 69.71875 67.308594 69.734375 67.296875 69.753906 "
+ id="path51" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M -1.628906 69.757812 C -1.613281 69.683594 -1.621094 69.628906 -1.65625 69.566406 C -1.707031 69.464844 -1.769531 69.503906 -1.820312 69.4375 C -1.871094 69.375 -1.917969 69.160156 -1.898438 69.074219 C -2.070312 69.019531 -1.910156 68.851562 -1.914062 68.726562 C -1.917969 68.675781 -1.957031 68.621094 -1.960938 68.566406 C -1.96875 68.445312 -1.941406 68.300781 -1.890625 68.195312 C -1.792969 67.996094 -1.625 67.78125 -1.457031 67.652344 C -1.265625 67.503906 -1.09375 67.078125 -0.847656 67.019531 C -0.671875 66.980469 -0.464844 67.125 -0.304688 67.164062 C -0.125 67.207031 0.09375 67.152344 0.265625 67.214844 C 0.488281 67.292969 0.394531 67.554688 0.554688 67.664062 C 0.65625 67.738281 0.839844 67.675781 0.960938 67.71875 C 1.160156 67.785156 1.300781 67.980469 1.335938 68.175781 C 1.351562 68.257812 1.347656 68.320312 1.375 68.398438 C 1.410156 68.5 1.484375 68.558594 1.464844 68.691406 C 1.457031 68.75 1.414062 68.8125 1.398438 68.875 C 1.378906 68.992188 1.402344 69.101562 1.402344 69.214844 C 1.398438 69.414062 1.417969 69.59375 1.324219 69.765625 C 0.941406 70.449219 0.0859375 70.691406 -0.652344 70.554688 C -0.832031 70.519531 -0.9375 70.425781 -1.09375 70.347656 C -1.265625 70.257812 -1.234375 70.179688 -1.410156 70.121094 C -1.492188 70.097656 -1.628906 69.996094 -1.625 69.898438 C -1.625 69.78125 -1.621094 69.847656 -1.613281 69.730469 C -1.636719 69.71875 -1.636719 69.734375 -1.644531 69.753906 "
+ id="path53" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 67.3125 62.105469 C 67.332031 62.03125 67.320312 61.976562 67.289062 61.914062 C 67.234375 61.8125 67.175781 61.851562 67.121094 61.785156 C 67.074219 61.722656 67.023438 61.507812 67.042969 61.421875 C 66.875 61.367188 67.035156 61.199219 67.027344 61.074219 C 67.027344 61.023438 66.988281 60.96875 66.984375 60.914062 C 66.972656 60.792969 67 60.648438 67.050781 60.542969 C 67.148438 60.34375 67.320312 60.128906 67.488281 60 C 67.675781 59.851562 67.851562 59.425781 68.09375 59.367188 C 68.273438 59.328125 68.480469 59.472656 68.636719 59.511719 C 68.816406 59.554688 69.035156 59.5 69.210938 59.5625 C 69.429688 59.640625 69.335938 59.902344 69.496094 60.011719 C 69.601562 60.085938 69.785156 60.023438 69.902344 60.066406 C 70.101562 60.132812 70.242188 60.328125 70.28125 60.523438 C 70.296875 60.605469 70.289062 60.667969 70.316406 60.746094 C 70.355469 60.847656 70.429688 60.90625 70.410156 61.039062 C 70.398438 61.097656 70.355469 61.160156 70.34375 61.222656 C 70.320312 61.339844 70.347656 61.449219 70.34375 61.5625 C 70.34375 61.761719 70.359375 61.941406 70.265625 62.113281 C 69.886719 62.796875 69.03125 63.039062 68.292969 62.902344 C 68.113281 62.867188 68.003906 62.773438 67.851562 62.695312 C 67.679688 62.605469 67.710938 62.527344 67.53125 62.46875 C 67.449219 62.445312 67.316406 62.34375 67.316406 62.246094 C 67.316406 62.128906 67.320312 62.195312 67.328125 62.078125 C 67.308594 62.066406 67.308594 62.082031 67.296875 62.101562 "
+ id="path55" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 59.652344 62.105469 C 59.671875 62.03125 59.660156 61.976562 59.628906 61.914062 C 59.574219 61.8125 59.515625 61.851562 59.460938 61.785156 C 59.414062 61.722656 59.363281 61.507812 59.382812 61.421875 C 59.214844 61.367188 59.375 61.199219 59.367188 61.074219 C 59.367188 61.023438 59.328125 60.96875 59.324219 60.914062 C 59.3125 60.792969 59.339844 60.648438 59.390625 60.542969 C 59.488281 60.34375 59.660156 60.128906 59.828125 60 C 60.015625 59.851562 60.191406 59.425781 60.433594 59.367188 C 60.609375 59.328125 60.816406 59.472656 60.976562 59.511719 C 61.15625 59.554688 61.375 59.5 61.546875 59.5625 C 61.769531 59.640625 61.675781 59.902344 61.835938 60.011719 C 61.941406 60.085938 62.125 60.023438 62.242188 60.066406 C 62.441406 60.132812 62.582031 60.328125 62.621094 60.523438 C 62.632812 60.605469 62.628906 60.667969 62.65625 60.746094 C 62.695312 60.847656 62.769531 60.90625 62.75 61.039062 C 62.738281 61.097656 62.695312 61.160156 62.683594 61.222656 C 62.660156 61.339844 62.683594 61.449219 62.683594 61.5625 C 62.679688 61.761719 62.699219 61.941406 62.605469 62.113281 C 62.226562 62.796875 61.371094 63.039062 60.632812 62.902344 C 60.453125 62.867188 60.34375 62.773438 60.1875 62.695312 C 60.019531 62.605469 60.050781 62.527344 59.871094 62.46875 C 59.789062 62.445312 59.65625 62.34375 59.65625 62.246094 C 59.65625 62.128906 59.660156 62.195312 59.667969 62.078125 C 59.648438 62.066406 59.644531 62.082031 59.636719 62.101562 "
+ id="path57" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 51.992188 62.105469 C 52.011719 62.03125 52 61.976562 51.96875 61.914062 C 51.914062 61.8125 51.855469 61.851562 51.800781 61.785156 C 51.753906 61.722656 51.703125 61.507812 51.722656 61.421875 C 51.554688 61.367188 51.714844 61.199219 51.707031 61.074219 C 51.707031 61.023438 51.667969 60.96875 51.664062 60.914062 C 51.652344 60.792969 51.679688 60.648438 51.730469 60.542969 C 51.828125 60.34375 52 60.128906 52.167969 60 C 52.355469 59.851562 52.53125 59.425781 52.773438 59.367188 C 52.953125 59.328125 53.15625 59.472656 53.316406 59.511719 C 53.496094 59.554688 53.714844 59.5 53.890625 59.5625 C 54.109375 59.640625 54.015625 59.902344 54.175781 60.011719 C 54.28125 60.085938 54.464844 60.023438 54.582031 60.066406 C 54.78125 60.132812 54.921875 60.328125 54.960938 60.523438 C 54.972656 60.605469 54.96875 60.667969 54.996094 60.746094 C 55.035156 60.847656 55.109375 60.90625 55.089844 61.039062 C 55.078125 61.097656 55.035156 61.160156 55.023438 61.222656 C 55 61.339844 55.023438 61.449219 55.023438 61.5625 C 55.019531 61.761719 55.039062 61.941406 54.945312 62.113281 C 54.566406 62.796875 53.710938 63.039062 52.972656 62.902344 C 52.792969 62.867188 52.683594 62.773438 52.527344 62.695312 C 52.359375 62.605469 52.390625 62.527344 52.210938 62.46875 C 52.128906 62.445312 51.996094 62.34375 51.996094 62.246094 C 51.996094 62.128906 52 62.195312 52.007812 62.078125 C 51.988281 62.066406 51.984375 62.082031 51.976562 62.101562 "
+ id="path59" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 44.332031 62.105469 C 44.351562 62.03125 44.339844 61.976562 44.304688 61.914062 C 44.253906 61.8125 44.195312 61.851562 44.140625 61.785156 C 44.089844 61.722656 44.042969 61.507812 44.0625 61.421875 C 43.894531 61.367188 44.054688 61.199219 44.046875 61.074219 C 44.042969 61.023438 44.007812 60.96875 44.003906 60.914062 C 43.992188 60.792969 44.019531 60.648438 44.070312 60.542969 C 44.167969 60.34375 44.339844 60.128906 44.503906 60 C 44.695312 59.851562 44.871094 59.425781 45.113281 59.367188 C 45.289062 59.328125 45.496094 59.472656 45.65625 59.511719 C 45.835938 59.554688 46.054688 59.5 46.226562 59.5625 C 46.449219 59.640625 46.355469 59.902344 46.515625 60.011719 C 46.621094 60.085938 46.804688 60.023438 46.921875 60.066406 C 47.121094 60.132812 47.261719 60.328125 47.296875 60.523438 C 47.316406 60.605469 47.308594 60.667969 47.335938 60.746094 C 47.375 60.847656 47.449219 60.90625 47.425781 61.039062 C 47.417969 61.097656 47.375 61.160156 47.363281 61.222656 C 47.339844 61.339844 47.363281 61.449219 47.363281 61.5625 C 47.359375 61.761719 47.378906 61.941406 47.285156 62.113281 C 46.90625 62.796875 46.046875 63.039062 45.308594 62.902344 C 45.128906 62.867188 45.023438 62.773438 44.867188 62.695312 C 44.699219 62.605469 44.730469 62.527344 44.550781 62.46875 C 44.46875 62.445312 44.335938 62.34375 44.335938 62.246094 C 44.335938 62.128906 44.339844 62.195312 44.347656 62.078125 C 44.328125 62.066406 44.324219 62.082031 44.316406 62.101562 "
+ id="path61" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 36.671875 62.105469 C 36.691406 62.03125 36.679688 61.976562 36.644531 61.914062 C 36.59375 61.8125 36.535156 61.851562 36.480469 61.785156 C 36.429688 61.722656 36.382812 61.507812 36.402344 61.421875 C 36.234375 61.367188 36.394531 61.199219 36.386719 61.074219 C 36.382812 61.023438 36.347656 60.96875 36.34375 60.914062 C 36.332031 60.792969 36.359375 60.648438 36.410156 60.542969 C 36.507812 60.34375 36.679688 60.128906 36.84375 60 C 37.035156 59.851562 37.210938 59.425781 37.453125 59.367188 C 37.632812 59.328125 37.835938 59.472656 37.996094 59.511719 C 38.175781 59.554688 38.394531 59.5 38.570312 59.5625 C 38.789062 59.640625 38.695312 59.902344 38.855469 60.011719 C 38.960938 60.085938 39.144531 60.023438 39.261719 60.066406 C 39.460938 60.132812 39.601562 60.328125 39.636719 60.523438 C 39.652344 60.605469 39.648438 60.667969 39.675781 60.746094 C 39.714844 60.847656 39.789062 60.90625 39.765625 61.039062 C 39.757812 61.097656 39.714844 61.160156 39.703125 61.222656 C 39.679688 61.339844 39.703125 61.449219 39.703125 61.5625 C 39.699219 61.761719 39.71875 61.941406 39.625 62.113281 C 39.246094 62.796875 38.386719 63.039062 37.652344 62.902344 C 37.46875 62.867188 37.363281 62.773438 37.207031 62.695312 C 37.039062 62.605469 37.070312 62.527344 36.890625 62.46875 C 36.808594 62.445312 36.675781 62.34375 36.675781 62.246094 C 36.675781 62.128906 36.679688 62.195312 36.6875 62.078125 C 36.667969 62.066406 36.664062 62.082031 36.65625 62.101562 "
+ id="path63" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 29.011719 62.105469 C 29.03125 62.03125 29.019531 61.976562 28.984375 61.914062 C 28.933594 61.8125 28.875 61.851562 28.820312 61.785156 C 28.769531 61.722656 28.722656 61.507812 28.742188 61.421875 C 28.574219 61.367188 28.734375 61.199219 28.726562 61.074219 C 28.722656 61.023438 28.6875 60.96875 28.683594 60.914062 C 28.671875 60.792969 28.699219 60.648438 28.75 60.542969 C 28.847656 60.34375 29.019531 60.128906 29.183594 60 C 29.375 59.851562 29.550781 59.425781 29.792969 59.367188 C 29.96875 59.328125 30.175781 59.472656 30.335938 59.511719 C 30.515625 59.554688 30.734375 59.5 30.90625 59.5625 C 31.128906 59.640625 31.035156 59.902344 31.195312 60.011719 C 31.300781 60.085938 31.484375 60.023438 31.601562 60.066406 C 31.800781 60.132812 31.941406 60.328125 31.976562 60.523438 C 31.992188 60.605469 31.988281 60.667969 32.015625 60.746094 C 32.054688 60.847656 32.128906 60.90625 32.105469 61.039062 C 32.097656 61.097656 32.054688 61.160156 32.042969 61.222656 C 32.019531 61.339844 32.042969 61.449219 32.042969 61.5625 C 32.039062 61.761719 32.058594 61.941406 31.964844 62.113281 C 31.585938 62.796875 30.726562 63.039062 29.988281 62.902344 C 29.808594 62.867188 29.703125 62.773438 29.546875 62.695312 C 29.378906 62.605469 29.410156 62.527344 29.230469 62.46875 C 29.148438 62.445312 29.015625 62.34375 29.015625 62.246094 C 29.015625 62.128906 29.019531 62.195312 29.027344 62.078125 C 29.003906 62.066406 29.003906 62.082031 28.996094 62.101562 "
+ id="path65" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 21.351562 62.105469 C 21.371094 62.03125 21.359375 61.976562 21.324219 61.914062 C 21.273438 61.8125 21.214844 61.851562 21.160156 61.785156 C 21.109375 61.722656 21.0625 61.507812 21.082031 61.421875 C 20.914062 61.367188 21.074219 61.199219 21.066406 61.074219 C 21.0625 61.023438 21.027344 60.96875 21.023438 60.914062 C 21.011719 60.792969 21.039062 60.648438 21.089844 60.542969 C 21.1875 60.34375 21.359375 60.128906 21.523438 60 C 21.714844 59.851562 21.890625 59.425781 22.132812 59.367188 C 22.3125 59.328125 22.515625 59.472656 22.675781 59.511719 C 22.855469 59.554688 23.074219 59.5 23.246094 59.5625 C 23.46875 59.640625 23.375 59.902344 23.535156 60.011719 C 23.640625 60.085938 23.824219 60.023438 23.941406 60.066406 C 24.140625 60.132812 24.28125 60.328125 24.316406 60.523438 C 24.332031 60.605469 24.328125 60.667969 24.355469 60.746094 C 24.394531 60.847656 24.46875 60.90625 24.445312 61.039062 C 24.4375 61.097656 24.394531 61.160156 24.382812 61.222656 C 24.359375 61.339844 24.382812 61.449219 24.382812 61.5625 C 24.378906 61.761719 24.398438 61.941406 24.304688 62.113281 C 23.925781 62.796875 23.066406 63.039062 22.328125 62.902344 C 22.148438 62.867188 22.042969 62.773438 21.886719 62.695312 C 21.71875 62.605469 21.75 62.527344 21.570312 62.46875 C 21.488281 62.445312 21.355469 62.34375 21.355469 62.246094 C 21.355469 62.128906 21.359375 62.195312 21.367188 62.078125 C 21.34375 62.066406 21.34375 62.082031 21.335938 62.101562 "
+ id="path67" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 13.691406 62.105469 C 13.707031 62.03125 13.699219 61.976562 13.664062 61.914062 C 13.613281 61.8125 13.554688 61.851562 13.5 61.785156 C 13.449219 61.722656 13.402344 61.507812 13.421875 61.421875 C 13.25 61.367188 13.410156 61.199219 13.40625 61.074219 C 13.402344 61.023438 13.363281 60.96875 13.359375 60.914062 C 13.351562 60.792969 13.378906 60.648438 13.429688 60.542969 C 13.527344 60.34375 13.695312 60.128906 13.863281 60 C 14.054688 59.851562 14.230469 59.425781 14.472656 59.367188 C 14.648438 59.328125 14.855469 59.472656 15.015625 59.511719 C 15.195312 59.554688 15.414062 59.5 15.585938 59.5625 C 15.808594 59.640625 15.714844 59.902344 15.875 60.011719 C 15.980469 60.085938 16.160156 60.023438 16.28125 60.066406 C 16.480469 60.132812 16.621094 60.328125 16.65625 60.523438 C 16.671875 60.605469 16.667969 60.667969 16.695312 60.746094 C 16.734375 60.847656 16.808594 60.90625 16.785156 61.039062 C 16.777344 61.097656 16.734375 61.160156 16.722656 61.222656 C 16.699219 61.339844 16.722656 61.449219 16.722656 61.5625 C 16.71875 61.761719 16.738281 61.941406 16.644531 62.113281 C 16.265625 62.796875 15.40625 63.039062 14.667969 62.902344 C 14.488281 62.867188 14.382812 62.773438 14.226562 62.695312 C 14.058594 62.605469 14.089844 62.527344 13.910156 62.46875 C 13.828125 62.445312 13.691406 62.34375 13.695312 62.246094 C 13.695312 62.128906 13.699219 62.195312 13.707031 62.078125 C 13.683594 62.066406 13.683594 62.082031 13.675781 62.101562 "
+ id="path69" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 6.03125 62.105469 C 6.046875 62.03125 6.039062 61.976562 6.003906 61.914062 C 5.953125 61.8125 5.894531 61.851562 5.839844 61.785156 C 5.789062 61.722656 5.742188 61.507812 5.761719 61.421875 C 5.59375 61.367188 5.75 61.199219 5.746094 61.074219 C 5.742188 61.023438 5.703125 60.96875 5.699219 60.914062 C 5.691406 60.792969 5.71875 60.648438 5.769531 60.542969 C 5.867188 60.34375 6.035156 60.128906 6.203125 60 C 6.394531 59.851562 6.570312 59.425781 6.8125 59.367188 C 6.988281 59.328125 7.195312 59.472656 7.355469 59.511719 C 7.535156 59.554688 7.753906 59.5 7.925781 59.5625 C 8.148438 59.640625 8.054688 59.902344 8.214844 60.011719 C 8.320312 60.085938 8.5 60.023438 8.621094 60.066406 C 8.820312 60.132812 8.960938 60.328125 8.996094 60.523438 C 9.011719 60.605469 9.007812 60.667969 9.035156 60.746094 C 9.074219 60.847656 9.148438 60.90625 9.125 61.039062 C 9.117188 61.097656 9.074219 61.160156 9.0625 61.222656 C 9.039062 61.339844 9.0625 61.449219 9.0625 61.5625 C 9.058594 61.761719 9.078125 61.941406 8.984375 62.113281 C 8.605469 62.796875 7.746094 63.039062 7.007812 62.902344 C 6.828125 62.867188 6.722656 62.773438 6.566406 62.695312 C 6.398438 62.605469 6.429688 62.527344 6.25 62.46875 C 6.167969 62.445312 6.03125 62.34375 6.035156 62.246094 C 6.035156 62.128906 6.039062 62.195312 6.046875 62.078125 C 6.023438 62.066406 6.023438 62.082031 6.015625 62.101562 "
+ id="path71" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M -1.628906 62.105469 C -1.613281 62.03125 -1.621094 61.976562 -1.65625 61.914062 C -1.707031 61.8125 -1.769531 61.851562 -1.820312 61.785156 C -1.871094 61.722656 -1.917969 61.507812 -1.898438 61.421875 C -2.070312 61.367188 -1.910156 61.199219 -1.914062 61.074219 C -1.917969 61.023438 -1.957031 60.96875 -1.960938 60.914062 C -1.96875 60.792969 -1.941406 60.648438 -1.890625 60.542969 C -1.792969 60.34375 -1.625 60.128906 -1.457031 60 C -1.265625 59.851562 -1.09375 59.425781 -0.847656 59.367188 C -0.671875 59.328125 -0.464844 59.472656 -0.304688 59.511719 C -0.125 59.554688 0.09375 59.5 0.265625 59.5625 C 0.488281 59.640625 0.394531 59.902344 0.554688 60.011719 C 0.65625 60.085938 0.839844 60.023438 0.960938 60.066406 C 1.160156 60.132812 1.300781 60.328125 1.335938 60.523438 C 1.351562 60.605469 1.347656 60.667969 1.375 60.746094 C 1.414062 60.847656 1.484375 60.90625 1.464844 61.039062 C 1.457031 61.097656 1.414062 61.160156 1.398438 61.222656 C 1.378906 61.339844 1.402344 61.449219 1.402344 61.5625 C 1.398438 61.761719 1.417969 61.941406 1.324219 62.113281 C 0.941406 62.796875 0.0859375 63.039062 -0.652344 62.902344 C -0.832031 62.867188 -0.9375 62.773438 -1.09375 62.695312 C -1.265625 62.605469 -1.234375 62.527344 -1.410156 62.46875 C -1.492188 62.445312 -1.628906 62.34375 -1.625 62.246094 C -1.625 62.128906 -1.621094 62.195312 -1.613281 62.078125 C -1.636719 62.066406 -1.636719 62.082031 -1.644531 62.101562 "
+ id="path73" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 67.3125 54.453125 C 67.332031 54.378906 67.320312 54.324219 67.289062 54.261719 C 67.234375 54.160156 67.175781 54.199219 67.121094 54.132812 C 67.074219 54.070312 67.023438 53.855469 67.042969 53.769531 C 66.875 53.714844 67.035156 53.542969 67.027344 53.421875 C 67.027344 53.371094 66.988281 53.316406 66.984375 53.261719 C 66.972656 53.140625 67 52.996094 67.050781 52.890625 C 67.148438 52.691406 67.320312 52.476562 67.488281 52.347656 C 67.675781 52.195312 67.851562 51.773438 68.09375 51.714844 C 68.273438 51.671875 68.480469 51.820312 68.636719 51.855469 C 68.816406 51.898438 69.035156 51.847656 69.210938 51.910156 C 69.429688 51.988281 69.335938 52.246094 69.496094 52.359375 C 69.601562 52.433594 69.785156 52.371094 69.902344 52.410156 C 70.101562 52.480469 70.242188 52.675781 70.28125 52.871094 C 70.296875 52.953125 70.289062 53.015625 70.316406 53.09375 C 70.355469 53.195312 70.429688 53.253906 70.410156 53.386719 C 70.398438 53.445312 70.355469 53.503906 70.34375 53.570312 C 70.320312 53.683594 70.347656 53.796875 70.34375 53.90625 C 70.34375 54.109375 70.359375 54.289062 70.265625 54.457031 C 69.886719 55.144531 69.03125 55.386719 68.292969 55.246094 C 68.113281 55.214844 68.003906 55.121094 67.851562 55.039062 C 67.679688 54.953125 67.710938 54.875 67.53125 54.816406 C 67.449219 54.789062 67.316406 54.691406 67.316406 54.59375 C 67.316406 54.476562 67.320312 54.542969 67.328125 54.425781 C 67.308594 54.414062 67.308594 54.429688 67.296875 54.449219 "
+ id="path75" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 59.652344 54.453125 C 59.671875 54.378906 59.660156 54.324219 59.628906 54.261719 C 59.574219 54.160156 59.515625 54.199219 59.460938 54.132812 C 59.414062 54.070312 59.363281 53.855469 59.382812 53.769531 C 59.214844 53.714844 59.375 53.542969 59.367188 53.421875 C 59.367188 53.371094 59.328125 53.316406 59.324219 53.261719 C 59.3125 53.140625 59.339844 52.996094 59.390625 52.890625 C 59.488281 52.691406 59.660156 52.476562 59.828125 52.347656 C 60.015625 52.195312 60.191406 51.773438 60.433594 51.714844 C 60.609375 51.671875 60.816406 51.820312 60.976562 51.855469 C 61.15625 51.898438 61.375 51.847656 61.546875 51.910156 C 61.769531 51.988281 61.675781 52.246094 61.835938 52.359375 C 61.941406 52.433594 62.125 52.371094 62.242188 52.410156 C 62.441406 52.480469 62.582031 52.675781 62.621094 52.871094 C 62.632812 52.953125 62.628906 53.015625 62.65625 53.09375 C 62.695312 53.195312 62.769531 53.253906 62.75 53.386719 C 62.738281 53.445312 62.695312 53.503906 62.683594 53.570312 C 62.660156 53.683594 62.683594 53.796875 62.683594 53.90625 C 62.679688 54.109375 62.699219 54.289062 62.605469 54.457031 C 62.226562 55.144531 61.371094 55.386719 60.632812 55.246094 C 60.453125 55.214844 60.34375 55.121094 60.1875 55.039062 C 60.019531 54.953125 60.050781 54.875 59.871094 54.816406 C 59.789062 54.789062 59.65625 54.691406 59.65625 54.59375 C 59.65625 54.476562 59.660156 54.542969 59.667969 54.425781 C 59.648438 54.414062 59.644531 54.429688 59.636719 54.449219 "
+ id="path77" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 51.992188 54.453125 C 52.011719 54.378906 52 54.324219 51.96875 54.261719 C 51.914062 54.160156 51.855469 54.199219 51.800781 54.132812 C 51.753906 54.070312 51.703125 53.855469 51.722656 53.769531 C 51.554688 53.714844 51.714844 53.542969 51.707031 53.421875 C 51.707031 53.371094 51.667969 53.316406 51.664062 53.261719 C 51.652344 53.140625 51.679688 52.996094 51.730469 52.890625 C 51.828125 52.691406 52 52.476562 52.167969 52.347656 C 52.355469 52.195312 52.53125 51.773438 52.773438 51.714844 C 52.953125 51.671875 53.15625 51.820312 53.316406 51.855469 C 53.496094 51.898438 53.714844 51.847656 53.890625 51.910156 C 54.109375 51.988281 54.015625 52.246094 54.175781 52.359375 C 54.28125 52.433594 54.464844 52.371094 54.582031 52.410156 C 54.78125 52.480469 54.921875 52.675781 54.960938 52.871094 C 54.972656 52.953125 54.96875 53.015625 54.996094 53.09375 C 55.035156 53.195312 55.109375 53.253906 55.089844 53.386719 C 55.078125 53.445312 55.035156 53.503906 55.023438 53.570312 C 55 53.683594 55.023438 53.796875 55.023438 53.90625 C 55.019531 54.109375 55.039062 54.289062 54.945312 54.457031 C 54.566406 55.144531 53.710938 55.386719 52.972656 55.246094 C 52.792969 55.214844 52.683594 55.121094 52.527344 55.039062 C 52.359375 54.953125 52.390625 54.875 52.210938 54.816406 C 52.128906 54.789062 51.996094 54.691406 51.996094 54.59375 C 51.996094 54.476562 52 54.542969 52.007812 54.425781 C 51.988281 54.414062 51.984375 54.429688 51.976562 54.449219 "
+ id="path79" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 44.332031 54.453125 C 44.351562 54.378906 44.339844 54.324219 44.304688 54.261719 C 44.253906 54.160156 44.195312 54.199219 44.140625 54.132812 C 44.089844 54.070312 44.042969 53.855469 44.0625 53.769531 C 43.894531 53.714844 44.054688 53.542969 44.046875 53.421875 C 44.042969 53.371094 44.007812 53.316406 44.003906 53.261719 C 43.992188 53.140625 44.019531 52.996094 44.070312 52.890625 C 44.167969 52.691406 44.339844 52.476562 44.503906 52.347656 C 44.695312 52.195312 44.871094 51.773438 45.113281 51.714844 C 45.289062 51.671875 45.496094 51.820312 45.65625 51.855469 C 45.835938 51.898438 46.054688 51.847656 46.226562 51.910156 C 46.449219 51.988281 46.355469 52.246094 46.515625 52.359375 C 46.621094 52.433594 46.804688 52.371094 46.921875 52.410156 C 47.121094 52.480469 47.261719 52.675781 47.296875 52.871094 C 47.316406 52.953125 47.308594 53.015625 47.335938 53.09375 C 47.375 53.195312 47.449219 53.253906 47.425781 53.386719 C 47.417969 53.445312 47.375 53.503906 47.363281 53.570312 C 47.339844 53.683594 47.363281 53.796875 47.363281 53.90625 C 47.359375 54.109375 47.378906 54.289062 47.285156 54.457031 C 46.90625 55.144531 46.046875 55.386719 45.308594 55.246094 C 45.128906 55.214844 45.023438 55.121094 44.867188 55.039062 C 44.699219 54.953125 44.730469 54.875 44.550781 54.816406 C 44.46875 54.789062 44.335938 54.691406 44.335938 54.59375 C 44.335938 54.476562 44.339844 54.542969 44.347656 54.425781 C 44.328125 54.414062 44.324219 54.429688 44.316406 54.449219 "
+ id="path81" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 36.671875 54.453125 C 36.691406 54.378906 36.679688 54.324219 36.644531 54.261719 C 36.59375 54.160156 36.535156 54.199219 36.480469 54.132812 C 36.429688 54.070312 36.382812 53.855469 36.402344 53.769531 C 36.234375 53.714844 36.394531 53.542969 36.386719 53.421875 C 36.382812 53.371094 36.347656 53.316406 36.34375 53.261719 C 36.332031 53.140625 36.359375 52.996094 36.410156 52.890625 C 36.507812 52.691406 36.679688 52.476562 36.84375 52.347656 C 37.035156 52.195312 37.210938 51.773438 37.453125 51.714844 C 37.632812 51.671875 37.835938 51.820312 37.996094 51.855469 C 38.175781 51.898438 38.394531 51.847656 38.570312 51.910156 C 38.789062 51.988281 38.695312 52.246094 38.855469 52.359375 C 38.960938 52.433594 39.144531 52.371094 39.261719 52.410156 C 39.460938 52.480469 39.601562 52.675781 39.636719 52.871094 C 39.652344 52.953125 39.648438 53.015625 39.675781 53.09375 C 39.714844 53.195312 39.789062 53.253906 39.765625 53.386719 C 39.757812 53.445312 39.714844 53.503906 39.703125 53.570312 C 39.679688 53.683594 39.703125 53.796875 39.703125 53.90625 C 39.699219 54.109375 39.71875 54.289062 39.625 54.457031 C 39.246094 55.144531 38.386719 55.386719 37.652344 55.246094 C 37.46875 55.214844 37.363281 55.121094 37.207031 55.039062 C 37.039062 54.953125 37.070312 54.875 36.890625 54.816406 C 36.808594 54.789062 36.675781 54.691406 36.675781 54.59375 C 36.675781 54.476562 36.679688 54.542969 36.6875 54.425781 C 36.667969 54.414062 36.664062 54.429688 36.65625 54.449219 "
+ id="path83" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 29.011719 54.453125 C 29.03125 54.378906 29.019531 54.324219 28.984375 54.261719 C 28.933594 54.160156 28.875 54.199219 28.820312 54.132812 C 28.769531 54.070312 28.722656 53.855469 28.742188 53.769531 C 28.574219 53.714844 28.734375 53.542969 28.726562 53.421875 C 28.722656 53.371094 28.6875 53.316406 28.683594 53.261719 C 28.671875 53.140625 28.699219 52.996094 28.75 52.890625 C 28.847656 52.691406 29.019531 52.476562 29.183594 52.347656 C 29.375 52.195312 29.550781 51.773438 29.792969 51.714844 C 29.96875 51.671875 30.175781 51.820312 30.335938 51.855469 C 30.515625 51.898438 30.734375 51.847656 30.90625 51.910156 C 31.128906 51.988281 31.035156 52.246094 31.195312 52.359375 C 31.300781 52.433594 31.484375 52.371094 31.601562 52.410156 C 31.800781 52.480469 31.941406 52.675781 31.976562 52.871094 C 31.992188 52.953125 31.988281 53.015625 32.015625 53.09375 C 32.054688 53.195312 32.128906 53.253906 32.105469 53.386719 C 32.097656 53.445312 32.054688 53.503906 32.042969 53.570312 C 32.019531 53.683594 32.042969 53.796875 32.042969 53.90625 C 32.039062 54.109375 32.058594 54.289062 31.964844 54.457031 C 31.585938 55.144531 30.726562 55.386719 29.988281 55.246094 C 29.808594 55.214844 29.703125 55.121094 29.546875 55.039062 C 29.378906 54.953125 29.410156 54.875 29.230469 54.816406 C 29.148438 54.789062 29.015625 54.691406 29.015625 54.59375 C 29.015625 54.476562 29.019531 54.542969 29.027344 54.425781 C 29.003906 54.414062 29.003906 54.429688 28.996094 54.449219 "
+ id="path85" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 21.351562 54.453125 C 21.371094 54.378906 21.359375 54.324219 21.324219 54.261719 C 21.273438 54.160156 21.214844 54.199219 21.160156 54.132812 C 21.109375 54.070312 21.0625 53.855469 21.082031 53.769531 C 20.914062 53.714844 21.074219 53.542969 21.066406 53.421875 C 21.0625 53.371094 21.027344 53.316406 21.023438 53.261719 C 21.011719 53.140625 21.039062 52.996094 21.089844 52.890625 C 21.1875 52.691406 21.359375 52.476562 21.523438 52.347656 C 21.714844 52.195312 21.890625 51.773438 22.132812 51.714844 C 22.3125 51.671875 22.515625 51.820312 22.675781 51.855469 C 22.855469 51.898438 23.074219 51.847656 23.246094 51.910156 C 23.46875 51.988281 23.375 52.246094 23.535156 52.359375 C 23.640625 52.433594 23.824219 52.371094 23.941406 52.410156 C 24.140625 52.480469 24.28125 52.675781 24.316406 52.871094 C 24.332031 52.953125 24.328125 53.015625 24.355469 53.09375 C 24.394531 53.195312 24.46875 53.253906 24.445312 53.386719 C 24.4375 53.445312 24.394531 53.503906 24.382812 53.570312 C 24.359375 53.683594 24.382812 53.796875 24.382812 53.90625 C 24.378906 54.109375 24.398438 54.289062 24.304688 54.457031 C 23.925781 55.144531 23.066406 55.386719 22.328125 55.246094 C 22.148438 55.214844 22.042969 55.121094 21.886719 55.039062 C 21.71875 54.953125 21.75 54.875 21.570312 54.816406 C 21.488281 54.789062 21.355469 54.691406 21.355469 54.59375 C 21.355469 54.476562 21.359375 54.542969 21.367188 54.425781 C 21.34375 54.414062 21.34375 54.429688 21.335938 54.449219 "
+ id="path87" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 13.691406 54.453125 C 13.707031 54.378906 13.699219 54.324219 13.664062 54.261719 C 13.613281 54.160156 13.554688 54.199219 13.5 54.132812 C 13.449219 54.070312 13.402344 53.855469 13.421875 53.769531 C 13.25 53.714844 13.410156 53.542969 13.40625 53.421875 C 13.402344 53.371094 13.363281 53.316406 13.359375 53.261719 C 13.351562 53.140625 13.378906 52.996094 13.429688 52.890625 C 13.527344 52.691406 13.695312 52.476562 13.863281 52.347656 C 14.054688 52.195312 14.230469 51.773438 14.472656 51.714844 C 14.648438 51.671875 14.855469 51.820312 15.015625 51.855469 C 15.195312 51.898438 15.414062 51.847656 15.585938 51.910156 C 15.808594 51.988281 15.714844 52.246094 15.875 52.359375 C 15.980469 52.433594 16.160156 52.371094 16.28125 52.410156 C 16.480469 52.480469 16.621094 52.675781 16.65625 52.871094 C 16.671875 52.953125 16.667969 53.015625 16.695312 53.09375 C 16.734375 53.195312 16.808594 53.253906 16.785156 53.386719 C 16.777344 53.445312 16.734375 53.503906 16.722656 53.570312 C 16.699219 53.683594 16.722656 53.796875 16.722656 53.90625 C 16.71875 54.109375 16.738281 54.289062 16.644531 54.457031 C 16.265625 55.144531 15.40625 55.386719 14.667969 55.246094 C 14.488281 55.214844 14.382812 55.121094 14.226562 55.039062 C 14.058594 54.953125 14.089844 54.875 13.910156 54.816406 C 13.828125 54.789062 13.691406 54.691406 13.695312 54.59375 C 13.695312 54.476562 13.699219 54.542969 13.707031 54.425781 C 13.683594 54.414062 13.683594 54.429688 13.675781 54.449219 "
+ id="path89" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 6.03125 54.453125 C 6.046875 54.378906 6.039062 54.324219 6.003906 54.261719 C 5.953125 54.160156 5.894531 54.199219 5.839844 54.132812 C 5.789062 54.070312 5.742188 53.855469 5.761719 53.769531 C 5.59375 53.714844 5.75 53.542969 5.746094 53.421875 C 5.742188 53.371094 5.703125 53.316406 5.699219 53.261719 C 5.691406 53.140625 5.71875 52.996094 5.769531 52.890625 C 5.867188 52.691406 6.035156 52.476562 6.203125 52.347656 C 6.394531 52.195312 6.570312 51.773438 6.8125 51.714844 C 6.988281 51.671875 7.195312 51.820312 7.355469 51.855469 C 7.535156 51.898438 7.753906 51.847656 7.925781 51.910156 C 8.148438 51.988281 8.054688 52.246094 8.214844 52.359375 C 8.320312 52.433594 8.5 52.371094 8.621094 52.410156 C 8.820312 52.480469 8.960938 52.675781 8.996094 52.871094 C 9.011719 52.953125 9.007812 53.015625 9.035156 53.09375 C 9.074219 53.195312 9.148438 53.253906 9.125 53.386719 C 9.117188 53.445312 9.074219 53.503906 9.0625 53.570312 C 9.039062 53.683594 9.0625 53.796875 9.0625 53.90625 C 9.058594 54.109375 9.078125 54.289062 8.984375 54.457031 C 8.605469 55.144531 7.746094 55.386719 7.007812 55.246094 C 6.828125 55.214844 6.722656 55.121094 6.566406 55.039062 C 6.398438 54.953125 6.429688 54.875 6.25 54.816406 C 6.167969 54.789062 6.03125 54.691406 6.035156 54.59375 C 6.035156 54.476562 6.039062 54.542969 6.046875 54.425781 C 6.023438 54.414062 6.023438 54.429688 6.015625 54.449219 "
+ id="path91" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M -1.628906 54.453125 C -1.613281 54.378906 -1.621094 54.324219 -1.65625 54.261719 C -1.707031 54.160156 -1.769531 54.199219 -1.820312 54.132812 C -1.871094 54.070312 -1.917969 53.855469 -1.898438 53.769531 C -2.070312 53.714844 -1.910156 53.542969 -1.914062 53.421875 C -1.917969 53.371094 -1.957031 53.316406 -1.960938 53.261719 C -1.96875 53.140625 -1.941406 52.996094 -1.890625 52.890625 C -1.792969 52.691406 -1.625 52.476562 -1.457031 52.347656 C -1.265625 52.195312 -1.09375 51.773438 -0.847656 51.714844 C -0.671875 51.671875 -0.464844 51.820312 -0.304688 51.855469 C -0.125 51.898438 0.09375 51.847656 0.265625 51.910156 C 0.488281 51.988281 0.394531 52.246094 0.554688 52.359375 C 0.65625 52.433594 0.839844 52.371094 0.960938 52.410156 C 1.160156 52.480469 1.300781 52.675781 1.335938 52.871094 C 1.351562 52.953125 1.347656 53.015625 1.375 53.09375 C 1.414062 53.195312 1.484375 53.253906 1.464844 53.386719 C 1.457031 53.445312 1.414062 53.503906 1.398438 53.570312 C 1.378906 53.683594 1.402344 53.796875 1.402344 53.90625 C 1.398438 54.109375 1.417969 54.289062 1.324219 54.457031 C 0.941406 55.144531 0.0859375 55.386719 -0.652344 55.246094 C -0.832031 55.214844 -0.9375 55.121094 -1.09375 55.039062 C -1.265625 54.953125 -1.234375 54.875 -1.410156 54.816406 C -1.492188 54.789062 -1.628906 54.691406 -1.625 54.59375 C -1.625 54.476562 -1.621094 54.542969 -1.613281 54.425781 C -1.636719 54.414062 -1.636719 54.429688 -1.644531 54.449219 "
+ id="path93" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 67.3125 46.796875 C 67.332031 46.726562 67.320312 46.671875 67.289062 46.609375 C 67.234375 46.507812 67.175781 46.546875 67.121094 46.480469 C 67.074219 46.417969 67.023438 46.203125 67.042969 46.117188 C 66.875 46.0625 67.035156 45.890625 67.027344 45.769531 C 67.027344 45.71875 66.988281 45.664062 66.984375 45.609375 C 66.972656 45.488281 67 45.34375 67.050781 45.238281 C 67.148438 45.039062 67.320312 44.824219 67.488281 44.695312 C 67.675781 44.542969 67.851562 44.121094 68.09375 44.0625 C 68.273438 44.019531 68.480469 44.167969 68.636719 44.203125 C 68.816406 44.246094 69.035156 44.195312 69.210938 44.257812 C 69.429688 44.335938 69.335938 44.59375 69.496094 44.707031 C 69.601562 44.78125 69.785156 44.71875 69.902344 44.757812 C 70.101562 44.828125 70.242188 45.023438 70.28125 45.21875 C 70.296875 45.300781 70.289062 45.363281 70.316406 45.441406 C 70.355469 45.542969 70.429688 45.601562 70.410156 45.734375 C 70.398438 45.792969 70.355469 45.851562 70.34375 45.917969 C 70.320312 46.03125 70.347656 46.144531 70.34375 46.253906 C 70.34375 46.457031 70.359375 46.636719 70.265625 46.804688 C 69.886719 47.492188 69.03125 47.734375 68.292969 47.59375 C 68.113281 47.5625 68.003906 47.46875 67.851562 47.386719 C 67.679688 47.300781 67.710938 47.222656 67.53125 47.164062 C 67.449219 47.136719 67.316406 47.039062 67.316406 46.941406 C 67.316406 46.824219 67.320312 46.890625 67.328125 46.773438 C 67.308594 46.761719 67.308594 46.777344 67.296875 46.796875 "
+ id="path95" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 59.652344 46.796875 C 59.671875 46.726562 59.660156 46.671875 59.628906 46.609375 C 59.574219 46.507812 59.515625 46.546875 59.460938 46.480469 C 59.414062 46.417969 59.363281 46.203125 59.382812 46.117188 C 59.214844 46.0625 59.375 45.890625 59.367188 45.769531 C 59.367188 45.71875 59.328125 45.664062 59.324219 45.609375 C 59.3125 45.488281 59.339844 45.34375 59.390625 45.238281 C 59.488281 45.039062 59.660156 44.824219 59.828125 44.695312 C 60.015625 44.542969 60.191406 44.121094 60.433594 44.0625 C 60.609375 44.019531 60.816406 44.167969 60.976562 44.203125 C 61.15625 44.246094 61.375 44.195312 61.546875 44.257812 C 61.769531 44.335938 61.675781 44.59375 61.835938 44.707031 C 61.941406 44.78125 62.125 44.71875 62.242188 44.757812 C 62.441406 44.828125 62.582031 45.023438 62.621094 45.21875 C 62.632812 45.300781 62.628906 45.363281 62.65625 45.441406 C 62.695312 45.542969 62.769531 45.601562 62.75 45.734375 C 62.738281 45.792969 62.695312 45.851562 62.683594 45.917969 C 62.660156 46.03125 62.683594 46.144531 62.683594 46.253906 C 62.679688 46.457031 62.699219 46.636719 62.605469 46.804688 C 62.226562 47.492188 61.371094 47.734375 60.632812 47.59375 C 60.453125 47.5625 60.34375 47.46875 60.1875 47.386719 C 60.019531 47.300781 60.050781 47.222656 59.871094 47.164062 C 59.789062 47.136719 59.65625 47.039062 59.65625 46.941406 C 59.65625 46.824219 59.660156 46.890625 59.667969 46.773438 C 59.648438 46.761719 59.644531 46.777344 59.636719 46.796875 "
+ id="path97" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 51.992188 46.796875 C 52.011719 46.726562 52 46.671875 51.96875 46.609375 C 51.914062 46.507812 51.855469 46.546875 51.800781 46.480469 C 51.753906 46.417969 51.703125 46.203125 51.722656 46.117188 C 51.554688 46.0625 51.714844 45.890625 51.707031 45.769531 C 51.707031 45.71875 51.667969 45.664062 51.664062 45.609375 C 51.652344 45.488281 51.679688 45.34375 51.730469 45.238281 C 51.828125 45.039062 52 44.824219 52.167969 44.695312 C 52.355469 44.542969 52.53125 44.121094 52.773438 44.0625 C 52.953125 44.019531 53.15625 44.167969 53.316406 44.203125 C 53.496094 44.246094 53.714844 44.195312 53.890625 44.257812 C 54.109375 44.335938 54.015625 44.59375 54.175781 44.707031 C 54.28125 44.78125 54.464844 44.71875 54.582031 44.757812 C 54.78125 44.828125 54.921875 45.023438 54.960938 45.21875 C 54.972656 45.300781 54.96875 45.363281 54.996094 45.441406 C 55.035156 45.542969 55.109375 45.601562 55.089844 45.734375 C 55.078125 45.792969 55.035156 45.851562 55.023438 45.917969 C 55 46.03125 55.023438 46.144531 55.023438 46.253906 C 55.019531 46.457031 55.039062 46.636719 54.945312 46.804688 C 54.566406 47.492188 53.710938 47.734375 52.972656 47.59375 C 52.792969 47.5625 52.683594 47.46875 52.527344 47.386719 C 52.359375 47.300781 52.390625 47.222656 52.210938 47.164062 C 52.128906 47.136719 51.996094 47.039062 51.996094 46.941406 C 51.996094 46.824219 52 46.890625 52.007812 46.773438 C 51.988281 46.761719 51.984375 46.777344 51.976562 46.796875 "
+ id="path99" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 44.332031 46.796875 C 44.351562 46.726562 44.339844 46.671875 44.304688 46.609375 C 44.253906 46.507812 44.195312 46.546875 44.140625 46.480469 C 44.089844 46.417969 44.042969 46.203125 44.0625 46.117188 C 43.894531 46.0625 44.054688 45.890625 44.046875 45.769531 C 44.042969 45.71875 44.007812 45.664062 44.003906 45.609375 C 43.992188 45.488281 44.019531 45.34375 44.070312 45.238281 C 44.167969 45.039062 44.339844 44.824219 44.503906 44.695312 C 44.695312 44.542969 44.871094 44.121094 45.113281 44.0625 C 45.289062 44.019531 45.496094 44.167969 45.65625 44.203125 C 45.835938 44.246094 46.054688 44.195312 46.226562 44.257812 C 46.449219 44.335938 46.355469 44.59375 46.515625 44.707031 C 46.621094 44.78125 46.804688 44.71875 46.921875 44.757812 C 47.121094 44.828125 47.261719 45.023438 47.296875 45.21875 C 47.316406 45.300781 47.308594 45.363281 47.335938 45.441406 C 47.375 45.542969 47.449219 45.601562 47.425781 45.734375 C 47.417969 45.792969 47.375 45.851562 47.363281 45.917969 C 47.339844 46.03125 47.363281 46.144531 47.363281 46.253906 C 47.359375 46.457031 47.378906 46.636719 47.285156 46.804688 C 46.90625 47.492188 46.046875 47.734375 45.308594 47.59375 C 45.128906 47.5625 45.023438 47.46875 44.867188 47.386719 C 44.699219 47.300781 44.730469 47.222656 44.550781 47.164062 C 44.46875 47.136719 44.335938 47.039062 44.335938 46.941406 C 44.335938 46.824219 44.339844 46.890625 44.347656 46.773438 C 44.328125 46.761719 44.324219 46.777344 44.316406 46.796875 "
+ id="path101" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 36.671875 46.796875 C 36.691406 46.726562 36.679688 46.671875 36.644531 46.609375 C 36.59375 46.507812 36.535156 46.546875 36.480469 46.480469 C 36.429688 46.417969 36.382812 46.203125 36.402344 46.117188 C 36.234375 46.0625 36.394531 45.890625 36.386719 45.769531 C 36.382812 45.71875 36.347656 45.664062 36.34375 45.609375 C 36.332031 45.488281 36.359375 45.34375 36.410156 45.238281 C 36.507812 45.039062 36.679688 44.824219 36.84375 44.695312 C 37.035156 44.542969 37.210938 44.121094 37.453125 44.0625 C 37.632812 44.019531 37.835938 44.167969 37.996094 44.203125 C 38.175781 44.246094 38.394531 44.195312 38.570312 44.257812 C 38.789062 44.335938 38.695312 44.59375 38.855469 44.707031 C 38.960938 44.78125 39.144531 44.71875 39.261719 44.757812 C 39.460938 44.828125 39.601562 45.023438 39.636719 45.21875 C 39.652344 45.300781 39.648438 45.363281 39.675781 45.441406 C 39.714844 45.542969 39.789062 45.601562 39.765625 45.734375 C 39.757812 45.792969 39.714844 45.851562 39.703125 45.917969 C 39.679688 46.03125 39.703125 46.144531 39.703125 46.253906 C 39.699219 46.457031 39.71875 46.636719 39.625 46.804688 C 39.246094 47.492188 38.386719 47.734375 37.652344 47.59375 C 37.46875 47.5625 37.363281 47.46875 37.207031 47.386719 C 37.039062 47.300781 37.070312 47.222656 36.890625 47.164062 C 36.808594 47.136719 36.675781 47.039062 36.675781 46.941406 C 36.675781 46.824219 36.679688 46.890625 36.6875 46.773438 C 36.667969 46.761719 36.664062 46.777344 36.65625 46.796875 "
+ id="path103" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 29.011719 46.796875 C 29.03125 46.726562 29.019531 46.671875 28.984375 46.609375 C 28.933594 46.507812 28.875 46.546875 28.820312 46.480469 C 28.769531 46.417969 28.722656 46.203125 28.742188 46.117188 C 28.574219 46.0625 28.734375 45.890625 28.726562 45.769531 C 28.722656 45.71875 28.6875 45.664062 28.683594 45.609375 C 28.671875 45.488281 28.699219 45.34375 28.75 45.238281 C 28.847656 45.039062 29.019531 44.824219 29.183594 44.695312 C 29.375 44.542969 29.550781 44.121094 29.792969 44.0625 C 29.96875 44.019531 30.175781 44.167969 30.335938 44.203125 C 30.515625 44.246094 30.734375 44.195312 30.90625 44.257812 C 31.128906 44.335938 31.035156 44.59375 31.195312 44.707031 C 31.300781 44.78125 31.484375 44.71875 31.601562 44.757812 C 31.800781 44.828125 31.941406 45.023438 31.976562 45.21875 C 31.992188 45.300781 31.988281 45.363281 32.015625 45.441406 C 32.054688 45.542969 32.128906 45.601562 32.105469 45.734375 C 32.097656 45.792969 32.054688 45.851562 32.042969 45.917969 C 32.019531 46.03125 32.042969 46.144531 32.042969 46.253906 C 32.039062 46.457031 32.058594 46.636719 31.964844 46.804688 C 31.585938 47.492188 30.726562 47.734375 29.988281 47.59375 C 29.808594 47.5625 29.703125 47.46875 29.546875 47.386719 C 29.378906 47.300781 29.410156 47.222656 29.230469 47.164062 C 29.148438 47.136719 29.015625 47.039062 29.015625 46.941406 C 29.015625 46.824219 29.019531 46.890625 29.027344 46.773438 C 29.003906 46.761719 29.003906 46.777344 28.996094 46.796875 "
+ id="path105" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 21.351562 46.796875 C 21.371094 46.726562 21.359375 46.671875 21.324219 46.609375 C 21.273438 46.507812 21.214844 46.546875 21.160156 46.480469 C 21.109375 46.417969 21.0625 46.203125 21.082031 46.117188 C 20.914062 46.0625 21.074219 45.890625 21.066406 45.769531 C 21.0625 45.71875 21.027344 45.664062 21.023438 45.609375 C 21.011719 45.488281 21.039062 45.34375 21.089844 45.238281 C 21.1875 45.039062 21.359375 44.824219 21.523438 44.695312 C 21.714844 44.542969 21.890625 44.121094 22.132812 44.0625 C 22.3125 44.019531 22.515625 44.167969 22.675781 44.203125 C 22.855469 44.246094 23.074219 44.195312 23.246094 44.257812 C 23.46875 44.335938 23.375 44.59375 23.535156 44.707031 C 23.640625 44.78125 23.824219 44.71875 23.941406 44.757812 C 24.140625 44.828125 24.28125 45.023438 24.316406 45.21875 C 24.332031 45.300781 24.328125 45.363281 24.355469 45.441406 C 24.394531 45.542969 24.46875 45.601562 24.445312 45.734375 C 24.4375 45.792969 24.394531 45.851562 24.382812 45.917969 C 24.359375 46.03125 24.382812 46.144531 24.382812 46.253906 C 24.378906 46.457031 24.398438 46.636719 24.304688 46.804688 C 23.925781 47.492188 23.066406 47.734375 22.328125 47.59375 C 22.148438 47.5625 22.042969 47.46875 21.886719 47.386719 C 21.71875 47.300781 21.75 47.222656 21.570312 47.164062 C 21.488281 47.136719 21.355469 47.039062 21.355469 46.941406 C 21.355469 46.824219 21.359375 46.890625 21.367188 46.773438 C 21.34375 46.761719 21.34375 46.777344 21.335938 46.796875 "
+ id="path107" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 13.691406 46.796875 C 13.707031 46.726562 13.699219 46.671875 13.664062 46.609375 C 13.613281 46.507812 13.554688 46.546875 13.5 46.480469 C 13.449219 46.417969 13.402344 46.203125 13.421875 46.117188 C 13.25 46.0625 13.410156 45.890625 13.40625 45.769531 C 13.402344 45.71875 13.363281 45.664062 13.359375 45.609375 C 13.351562 45.488281 13.378906 45.34375 13.429688 45.238281 C 13.527344 45.039062 13.695312 44.824219 13.863281 44.695312 C 14.054688 44.542969 14.230469 44.121094 14.472656 44.0625 C 14.648438 44.019531 14.855469 44.167969 15.015625 44.203125 C 15.195312 44.246094 15.414062 44.195312 15.585938 44.257812 C 15.808594 44.335938 15.714844 44.59375 15.875 44.707031 C 15.980469 44.78125 16.160156 44.71875 16.28125 44.757812 C 16.480469 44.828125 16.621094 45.023438 16.65625 45.21875 C 16.671875 45.300781 16.667969 45.363281 16.695312 45.441406 C 16.734375 45.542969 16.808594 45.601562 16.785156 45.734375 C 16.777344 45.792969 16.734375 45.851562 16.722656 45.917969 C 16.699219 46.03125 16.722656 46.144531 16.722656 46.253906 C 16.71875 46.457031 16.738281 46.636719 16.644531 46.804688 C 16.265625 47.492188 15.40625 47.734375 14.667969 47.59375 C 14.488281 47.5625 14.382812 47.46875 14.226562 47.386719 C 14.058594 47.300781 14.089844 47.222656 13.910156 47.164062 C 13.828125 47.136719 13.691406 47.039062 13.695312 46.941406 C 13.695312 46.824219 13.699219 46.890625 13.707031 46.773438 C 13.683594 46.761719 13.683594 46.777344 13.675781 46.796875 "
+ id="path109" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 6.03125 46.796875 C 6.046875 46.726562 6.039062 46.671875 6.003906 46.609375 C 5.953125 46.507812 5.894531 46.546875 5.839844 46.480469 C 5.789062 46.417969 5.742188 46.203125 5.761719 46.117188 C 5.59375 46.0625 5.75 45.890625 5.746094 45.769531 C 5.742188 45.71875 5.703125 45.664062 5.699219 45.609375 C 5.691406 45.488281 5.71875 45.34375 5.769531 45.238281 C 5.867188 45.039062 6.035156 44.824219 6.203125 44.695312 C 6.394531 44.542969 6.570312 44.121094 6.8125 44.0625 C 6.988281 44.019531 7.195312 44.167969 7.355469 44.203125 C 7.535156 44.246094 7.753906 44.195312 7.925781 44.257812 C 8.148438 44.335938 8.054688 44.59375 8.214844 44.707031 C 8.320312 44.78125 8.5 44.71875 8.621094 44.757812 C 8.820312 44.828125 8.960938 45.023438 8.996094 45.21875 C 9.011719 45.300781 9.007812 45.363281 9.035156 45.441406 C 9.074219 45.542969 9.148438 45.601562 9.125 45.734375 C 9.117188 45.792969 9.074219 45.851562 9.0625 45.917969 C 9.039062 46.03125 9.0625 46.144531 9.0625 46.253906 C 9.058594 46.457031 9.078125 46.636719 8.984375 46.804688 C 8.605469 47.492188 7.746094 47.734375 7.007812 47.59375 C 6.828125 47.5625 6.722656 47.46875 6.566406 47.386719 C 6.398438 47.300781 6.429688 47.222656 6.25 47.164062 C 6.167969 47.136719 6.03125 47.039062 6.035156 46.941406 C 6.035156 46.824219 6.039062 46.890625 6.046875 46.773438 C 6.023438 46.761719 6.023438 46.777344 6.015625 46.796875 "
+ id="path111" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M -1.628906 46.796875 C -1.613281 46.726562 -1.621094 46.671875 -1.65625 46.609375 C -1.707031 46.507812 -1.769531 46.546875 -1.820312 46.480469 C -1.871094 46.417969 -1.917969 46.203125 -1.898438 46.117188 C -2.070312 46.0625 -1.910156 45.890625 -1.914062 45.769531 C -1.917969 45.71875 -1.957031 45.664062 -1.960938 45.609375 C -1.96875 45.488281 -1.941406 45.34375 -1.890625 45.238281 C -1.792969 45.039062 -1.625 44.824219 -1.457031 44.695312 C -1.265625 44.542969 -1.09375 44.121094 -0.847656 44.0625 C -0.671875 44.019531 -0.464844 44.167969 -0.304688 44.203125 C -0.125 44.246094 0.09375 44.195312 0.265625 44.257812 C 0.488281 44.335938 0.394531 44.59375 0.554688 44.707031 C 0.65625 44.78125 0.839844 44.71875 0.960938 44.757812 C 1.160156 44.828125 1.300781 45.023438 1.335938 45.21875 C 1.351562 45.300781 1.347656 45.363281 1.375 45.441406 C 1.414062 45.542969 1.484375 45.601562 1.464844 45.734375 C 1.457031 45.792969 1.414062 45.851562 1.398438 45.917969 C 1.378906 46.03125 1.402344 46.144531 1.402344 46.253906 C 1.398438 46.457031 1.417969 46.636719 1.324219 46.804688 C 0.941406 47.492188 0.0859375 47.734375 -0.652344 47.59375 C -0.832031 47.5625 -0.9375 47.46875 -1.09375 47.386719 C -1.265625 47.300781 -1.234375 47.222656 -1.410156 47.164062 C -1.492188 47.136719 -1.628906 47.039062 -1.625 46.941406 C -1.625 46.824219 -1.621094 46.890625 -1.613281 46.773438 C -1.636719 46.761719 -1.636719 46.777344 -1.644531 46.796875 "
+ id="path113" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 67.3125 39.144531 C 67.332031 39.074219 67.320312 39.019531 67.289062 38.957031 C 67.234375 38.855469 67.175781 38.890625 67.121094 38.828125 C 67.074219 38.765625 67.023438 38.550781 67.042969 38.464844 C 66.875 38.40625 67.035156 38.238281 67.027344 38.117188 C 67.027344 38.066406 66.988281 38.007812 66.984375 37.953125 C 66.972656 37.835938 67 37.6875 67.050781 37.582031 C 67.148438 37.386719 67.320312 37.171875 67.488281 37.039062 C 67.675781 36.890625 67.851562 36.46875 68.09375 36.410156 C 68.273438 36.367188 68.480469 36.511719 68.636719 36.550781 C 68.816406 36.59375 69.035156 36.542969 69.210938 36.605469 C 69.429688 36.683594 69.335938 36.941406 69.496094 37.054688 C 69.601562 37.128906 69.785156 37.066406 69.902344 37.105469 C 70.101562 37.175781 70.242188 37.367188 70.28125 37.566406 C 70.296875 37.648438 70.289062 37.710938 70.316406 37.785156 C 70.355469 37.886719 70.429688 37.945312 70.410156 38.078125 C 70.398438 38.140625 70.355469 38.199219 70.34375 38.265625 C 70.320312 38.378906 70.347656 38.488281 70.34375 38.601562 C 70.34375 38.800781 70.359375 38.980469 70.265625 39.152344 C 69.886719 39.839844 69.03125 40.082031 68.292969 39.941406 C 68.113281 39.90625 68.003906 39.816406 67.851562 39.734375 C 67.679688 39.648438 67.710938 39.570312 67.53125 39.511719 C 67.449219 39.484375 67.316406 39.386719 67.316406 39.289062 C 67.316406 39.171875 67.320312 39.238281 67.328125 39.117188 C 67.308594 39.109375 67.308594 39.121094 67.296875 39.144531 "
+ id="path115" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 59.652344 39.144531 C 59.671875 39.074219 59.660156 39.019531 59.628906 38.957031 C 59.574219 38.855469 59.515625 38.890625 59.460938 38.828125 C 59.414062 38.765625 59.363281 38.550781 59.382812 38.464844 C 59.214844 38.40625 59.375 38.238281 59.367188 38.117188 C 59.367188 38.066406 59.328125 38.007812 59.324219 37.953125 C 59.3125 37.835938 59.339844 37.6875 59.390625 37.582031 C 59.488281 37.386719 59.660156 37.171875 59.828125 37.039062 C 60.015625 36.890625 60.191406 36.46875 60.433594 36.410156 C 60.609375 36.367188 60.816406 36.511719 60.976562 36.550781 C 61.15625 36.59375 61.375 36.542969 61.546875 36.605469 C 61.769531 36.683594 61.675781 36.941406 61.835938 37.054688 C 61.941406 37.128906 62.125 37.066406 62.242188 37.105469 C 62.441406 37.175781 62.582031 37.367188 62.621094 37.566406 C 62.632812 37.648438 62.628906 37.710938 62.65625 37.785156 C 62.695312 37.886719 62.769531 37.945312 62.75 38.078125 C 62.738281 38.140625 62.695312 38.199219 62.683594 38.265625 C 62.660156 38.378906 62.683594 38.488281 62.683594 38.601562 C 62.679688 38.800781 62.699219 38.980469 62.605469 39.152344 C 62.226562 39.839844 61.371094 40.082031 60.632812 39.941406 C 60.453125 39.90625 60.34375 39.816406 60.1875 39.734375 C 60.019531 39.648438 60.050781 39.570312 59.871094 39.511719 C 59.789062 39.484375 59.65625 39.386719 59.65625 39.289062 C 59.65625 39.171875 59.660156 39.238281 59.667969 39.117188 C 59.648438 39.109375 59.644531 39.121094 59.636719 39.144531 "
+ id="path117" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 51.992188 39.144531 C 52.011719 39.074219 52 39.019531 51.96875 38.957031 C 51.914062 38.855469 51.855469 38.890625 51.800781 38.828125 C 51.753906 38.765625 51.703125 38.550781 51.722656 38.464844 C 51.554688 38.40625 51.714844 38.238281 51.707031 38.117188 C 51.707031 38.066406 51.667969 38.007812 51.664062 37.953125 C 51.652344 37.835938 51.679688 37.6875 51.730469 37.582031 C 51.828125 37.386719 52 37.171875 52.167969 37.039062 C 52.355469 36.890625 52.53125 36.46875 52.773438 36.410156 C 52.953125 36.367188 53.15625 36.511719 53.316406 36.550781 C 53.496094 36.59375 53.714844 36.542969 53.890625 36.605469 C 54.109375 36.683594 54.015625 36.941406 54.175781 37.054688 C 54.28125 37.128906 54.464844 37.066406 54.582031 37.105469 C 54.78125 37.175781 54.921875 37.367188 54.960938 37.566406 C 54.972656 37.648438 54.96875 37.710938 54.996094 37.785156 C 55.035156 37.886719 55.109375 37.945312 55.089844 38.078125 C 55.078125 38.140625 55.035156 38.199219 55.023438 38.265625 C 55 38.378906 55.023438 38.488281 55.023438 38.601562 C 55.019531 38.800781 55.039062 38.980469 54.945312 39.152344 C 54.566406 39.839844 53.710938 40.082031 52.972656 39.941406 C 52.792969 39.90625 52.683594 39.816406 52.527344 39.734375 C 52.359375 39.648438 52.390625 39.570312 52.210938 39.511719 C 52.128906 39.484375 51.996094 39.386719 51.996094 39.289062 C 51.996094 39.171875 52 39.238281 52.007812 39.117188 C 51.988281 39.109375 51.984375 39.121094 51.976562 39.144531 "
+ id="path119" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 44.332031 39.144531 C 44.351562 39.074219 44.339844 39.019531 44.304688 38.957031 C 44.253906 38.855469 44.195312 38.890625 44.140625 38.828125 C 44.089844 38.765625 44.042969 38.550781 44.0625 38.464844 C 43.894531 38.40625 44.054688 38.238281 44.046875 38.117188 C 44.042969 38.066406 44.007812 38.007812 44.003906 37.953125 C 43.992188 37.835938 44.019531 37.6875 44.070312 37.582031 C 44.167969 37.386719 44.339844 37.171875 44.503906 37.039062 C 44.695312 36.890625 44.871094 36.46875 45.113281 36.410156 C 45.289062 36.367188 45.496094 36.511719 45.65625 36.550781 C 45.835938 36.59375 46.054688 36.542969 46.226562 36.605469 C 46.449219 36.683594 46.355469 36.941406 46.515625 37.054688 C 46.621094 37.128906 46.804688 37.066406 46.921875 37.105469 C 47.121094 37.175781 47.261719 37.367188 47.296875 37.566406 C 47.316406 37.648438 47.308594 37.710938 47.335938 37.785156 C 47.375 37.886719 47.449219 37.945312 47.425781 38.078125 C 47.417969 38.140625 47.375 38.199219 47.363281 38.265625 C 47.339844 38.378906 47.363281 38.488281 47.363281 38.601562 C 47.359375 38.800781 47.378906 38.980469 47.285156 39.152344 C 46.90625 39.839844 46.046875 40.082031 45.308594 39.941406 C 45.128906 39.90625 45.023438 39.816406 44.867188 39.734375 C 44.699219 39.648438 44.730469 39.570312 44.550781 39.511719 C 44.46875 39.484375 44.335938 39.386719 44.335938 39.289062 C 44.335938 39.171875 44.339844 39.238281 44.347656 39.117188 C 44.328125 39.109375 44.324219 39.121094 44.316406 39.144531 "
+ id="path121" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 36.671875 39.144531 C 36.691406 39.074219 36.679688 39.019531 36.644531 38.957031 C 36.59375 38.855469 36.535156 38.890625 36.480469 38.828125 C 36.429688 38.765625 36.382812 38.550781 36.402344 38.464844 C 36.234375 38.40625 36.394531 38.238281 36.386719 38.117188 C 36.382812 38.066406 36.347656 38.007812 36.34375 37.953125 C 36.332031 37.835938 36.359375 37.6875 36.410156 37.582031 C 36.507812 37.386719 36.679688 37.171875 36.84375 37.039062 C 37.035156 36.890625 37.210938 36.46875 37.453125 36.410156 C 37.632812 36.367188 37.835938 36.511719 37.996094 36.550781 C 38.175781 36.59375 38.394531 36.542969 38.570312 36.605469 C 38.789062 36.683594 38.695312 36.941406 38.855469 37.054688 C 38.960938 37.128906 39.144531 37.066406 39.261719 37.105469 C 39.460938 37.175781 39.601562 37.367188 39.636719 37.566406 C 39.652344 37.648438 39.648438 37.710938 39.675781 37.785156 C 39.714844 37.886719 39.789062 37.945312 39.765625 38.078125 C 39.757812 38.140625 39.714844 38.199219 39.703125 38.265625 C 39.679688 38.378906 39.703125 38.488281 39.703125 38.601562 C 39.699219 38.800781 39.71875 38.980469 39.625 39.152344 C 39.246094 39.839844 38.386719 40.082031 37.652344 39.941406 C 37.46875 39.90625 37.363281 39.816406 37.207031 39.734375 C 37.039062 39.648438 37.070312 39.570312 36.890625 39.511719 C 36.808594 39.484375 36.675781 39.386719 36.675781 39.289062 C 36.675781 39.171875 36.679688 39.238281 36.6875 39.117188 C 36.667969 39.109375 36.664062 39.121094 36.65625 39.144531 "
+ id="path123" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 29.011719 39.144531 C 29.03125 39.074219 29.019531 39.019531 28.984375 38.957031 C 28.933594 38.855469 28.875 38.890625 28.820312 38.828125 C 28.769531 38.765625 28.722656 38.550781 28.742188 38.464844 C 28.574219 38.40625 28.734375 38.238281 28.726562 38.117188 C 28.722656 38.066406 28.6875 38.007812 28.683594 37.953125 C 28.671875 37.835938 28.699219 37.6875 28.75 37.582031 C 28.847656 37.386719 29.019531 37.171875 29.183594 37.039062 C 29.375 36.890625 29.550781 36.46875 29.792969 36.410156 C 29.96875 36.367188 30.175781 36.511719 30.335938 36.550781 C 30.515625 36.59375 30.734375 36.542969 30.90625 36.605469 C 31.128906 36.683594 31.035156 36.941406 31.195312 37.054688 C 31.300781 37.128906 31.484375 37.066406 31.601562 37.105469 C 31.800781 37.175781 31.941406 37.367188 31.976562 37.566406 C 31.992188 37.648438 31.988281 37.710938 32.015625 37.785156 C 32.054688 37.886719 32.128906 37.945312 32.105469 38.078125 C 32.097656 38.140625 32.054688 38.199219 32.042969 38.265625 C 32.019531 38.378906 32.042969 38.488281 32.042969 38.601562 C 32.039062 38.800781 32.058594 38.980469 31.964844 39.152344 C 31.585938 39.839844 30.726562 40.082031 29.988281 39.941406 C 29.808594 39.90625 29.703125 39.816406 29.546875 39.734375 C 29.378906 39.648438 29.410156 39.570312 29.230469 39.511719 C 29.148438 39.484375 29.015625 39.386719 29.015625 39.289062 C 29.015625 39.171875 29.019531 39.238281 29.027344 39.117188 C 29.003906 39.109375 29.003906 39.121094 28.996094 39.144531 "
+ id="path125" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 21.351562 39.144531 C 21.371094 39.074219 21.359375 39.019531 21.324219 38.957031 C 21.273438 38.855469 21.214844 38.890625 21.160156 38.828125 C 21.109375 38.765625 21.0625 38.550781 21.082031 38.464844 C 20.914062 38.40625 21.074219 38.238281 21.066406 38.117188 C 21.0625 38.066406 21.027344 38.007812 21.023438 37.953125 C 21.011719 37.835938 21.039062 37.6875 21.089844 37.582031 C 21.1875 37.386719 21.359375 37.171875 21.523438 37.039062 C 21.714844 36.890625 21.890625 36.46875 22.132812 36.410156 C 22.3125 36.367188 22.515625 36.511719 22.675781 36.550781 C 22.855469 36.59375 23.074219 36.542969 23.246094 36.605469 C 23.46875 36.683594 23.375 36.941406 23.535156 37.054688 C 23.640625 37.128906 23.824219 37.066406 23.941406 37.105469 C 24.140625 37.175781 24.28125 37.367188 24.316406 37.566406 C 24.332031 37.648438 24.328125 37.710938 24.355469 37.785156 C 24.394531 37.886719 24.46875 37.945312 24.445312 38.078125 C 24.4375 38.140625 24.394531 38.199219 24.382812 38.265625 C 24.359375 38.378906 24.382812 38.488281 24.382812 38.601562 C 24.378906 38.800781 24.398438 38.980469 24.304688 39.152344 C 23.925781 39.839844 23.066406 40.082031 22.328125 39.941406 C 22.148438 39.90625 22.042969 39.816406 21.886719 39.734375 C 21.71875 39.648438 21.75 39.570312 21.570312 39.511719 C 21.488281 39.484375 21.355469 39.386719 21.355469 39.289062 C 21.355469 39.171875 21.359375 39.238281 21.367188 39.117188 C 21.34375 39.109375 21.34375 39.121094 21.335938 39.144531 "
+ id="path127" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 13.691406 39.144531 C 13.707031 39.074219 13.699219 39.019531 13.664062 38.957031 C 13.613281 38.855469 13.554688 38.890625 13.5 38.828125 C 13.449219 38.765625 13.402344 38.550781 13.421875 38.464844 C 13.25 38.40625 13.410156 38.238281 13.40625 38.117188 C 13.402344 38.066406 13.363281 38.007812 13.359375 37.953125 C 13.351562 37.835938 13.378906 37.6875 13.429688 37.582031 C 13.527344 37.386719 13.695312 37.171875 13.863281 37.039062 C 14.054688 36.890625 14.230469 36.46875 14.472656 36.410156 C 14.648438 36.367188 14.855469 36.511719 15.015625 36.550781 C 15.195312 36.59375 15.414062 36.542969 15.585938 36.605469 C 15.808594 36.683594 15.714844 36.941406 15.875 37.054688 C 15.980469 37.128906 16.160156 37.066406 16.28125 37.105469 C 16.480469 37.175781 16.621094 37.367188 16.65625 37.566406 C 16.671875 37.648438 16.667969 37.710938 16.695312 37.785156 C 16.734375 37.886719 16.808594 37.945312 16.785156 38.078125 C 16.777344 38.140625 16.734375 38.199219 16.722656 38.265625 C 16.699219 38.378906 16.722656 38.488281 16.722656 38.601562 C 16.71875 38.800781 16.738281 38.980469 16.644531 39.152344 C 16.265625 39.839844 15.40625 40.082031 14.667969 39.941406 C 14.488281 39.90625 14.382812 39.816406 14.226562 39.734375 C 14.058594 39.648438 14.089844 39.570312 13.910156 39.511719 C 13.828125 39.484375 13.691406 39.386719 13.695312 39.289062 C 13.695312 39.171875 13.699219 39.238281 13.707031 39.117188 C 13.683594 39.109375 13.683594 39.121094 13.675781 39.144531 "
+ id="path129" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 6.03125 39.144531 C 6.046875 39.074219 6.039062 39.019531 6.003906 38.957031 C 5.953125 38.855469 5.894531 38.890625 5.839844 38.828125 C 5.789062 38.765625 5.742188 38.550781 5.761719 38.464844 C 5.59375 38.40625 5.75 38.238281 5.746094 38.117188 C 5.742188 38.066406 5.703125 38.007812 5.699219 37.953125 C 5.691406 37.835938 5.71875 37.6875 5.769531 37.582031 C 5.867188 37.386719 6.035156 37.171875 6.203125 37.039062 C 6.394531 36.890625 6.570312 36.46875 6.8125 36.410156 C 6.988281 36.367188 7.195312 36.511719 7.355469 36.550781 C 7.535156 36.59375 7.753906 36.542969 7.925781 36.605469 C 8.148438 36.683594 8.054688 36.941406 8.214844 37.054688 C 8.320312 37.128906 8.5 37.066406 8.621094 37.105469 C 8.820312 37.175781 8.960938 37.367188 8.996094 37.566406 C 9.011719 37.648438 9.007812 37.710938 9.035156 37.785156 C 9.074219 37.886719 9.148438 37.945312 9.125 38.078125 C 9.117188 38.140625 9.074219 38.199219 9.0625 38.265625 C 9.039062 38.378906 9.0625 38.488281 9.0625 38.601562 C 9.058594 38.800781 9.078125 38.980469 8.984375 39.152344 C 8.605469 39.839844 7.746094 40.082031 7.007812 39.941406 C 6.828125 39.90625 6.722656 39.816406 6.566406 39.734375 C 6.398438 39.648438 6.429688 39.570312 6.25 39.511719 C 6.167969 39.484375 6.03125 39.386719 6.035156 39.289062 C 6.035156 39.171875 6.039062 39.238281 6.046875 39.117188 C 6.023438 39.109375 6.023438 39.121094 6.015625 39.144531 "
+ id="path131" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M -1.628906 39.144531 C -1.613281 39.074219 -1.621094 39.019531 -1.65625 38.957031 C -1.707031 38.855469 -1.769531 38.890625 -1.820312 38.828125 C -1.871094 38.765625 -1.917969 38.550781 -1.898438 38.464844 C -2.070312 38.40625 -1.910156 38.238281 -1.914062 38.117188 C -1.917969 38.066406 -1.957031 38.007812 -1.960938 37.953125 C -1.96875 37.835938 -1.941406 37.6875 -1.890625 37.582031 C -1.792969 37.386719 -1.625 37.171875 -1.457031 37.039062 C -1.265625 36.890625 -1.09375 36.46875 -0.847656 36.410156 C -0.671875 36.367188 -0.464844 36.511719 -0.304688 36.550781 C -0.125 36.59375 0.09375 36.542969 0.265625 36.605469 C 0.488281 36.683594 0.394531 36.941406 0.554688 37.054688 C 0.65625 37.128906 0.839844 37.066406 0.960938 37.105469 C 1.160156 37.175781 1.300781 37.367188 1.335938 37.566406 C 1.351562 37.648438 1.347656 37.710938 1.375 37.785156 C 1.414062 37.886719 1.484375 37.945312 1.464844 38.078125 C 1.457031 38.140625 1.414062 38.199219 1.398438 38.265625 C 1.378906 38.378906 1.402344 38.488281 1.402344 38.601562 C 1.398438 38.800781 1.417969 38.980469 1.324219 39.152344 C 0.941406 39.839844 0.0859375 40.082031 -0.652344 39.941406 C -0.832031 39.90625 -0.9375 39.816406 -1.09375 39.734375 C -1.265625 39.648438 -1.234375 39.570312 -1.410156 39.511719 C -1.492188 39.484375 -1.628906 39.386719 -1.625 39.289062 C -1.625 39.171875 -1.621094 39.238281 -1.613281 39.117188 C -1.636719 39.109375 -1.636719 39.121094 -1.644531 39.144531 "
+ id="path133" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 67.3125 31.492188 C 67.332031 31.421875 67.320312 31.367188 67.289062 31.304688 C 67.234375 31.203125 67.175781 31.238281 67.121094 31.175781 C 67.074219 31.113281 67.023438 30.898438 67.042969 30.8125 C 66.875 30.753906 67.035156 30.585938 67.027344 30.464844 C 67.027344 30.414062 66.988281 30.355469 66.984375 30.300781 C 66.972656 30.183594 67 30.035156 67.050781 29.933594 C 67.148438 29.734375 67.320312 29.519531 67.488281 29.386719 C 67.675781 29.238281 67.851562 28.816406 68.09375 28.757812 C 68.273438 28.714844 68.480469 28.859375 68.636719 28.898438 C 68.816406 28.941406 69.035156 28.890625 69.210938 28.953125 C 69.429688 29.03125 69.335938 29.289062 69.496094 29.402344 C 69.601562 29.476562 69.785156 29.410156 69.902344 29.453125 C 70.101562 29.523438 70.242188 29.714844 70.28125 29.914062 C 70.296875 29.996094 70.289062 30.058594 70.316406 30.132812 C 70.355469 30.234375 70.429688 30.292969 70.410156 30.425781 C 70.398438 30.488281 70.355469 30.546875 70.34375 30.613281 C 70.320312 30.726562 70.347656 30.835938 70.34375 30.949219 C 70.34375 31.148438 70.359375 31.328125 70.265625 31.5 C 69.886719 32.1875 69.03125 32.429688 68.292969 32.289062 C 68.113281 32.253906 68.003906 32.164062 67.851562 32.082031 C 67.679688 31.992188 67.710938 31.917969 67.53125 31.859375 C 67.449219 31.832031 67.316406 31.734375 67.316406 31.636719 C 67.316406 31.519531 67.320312 31.585938 67.328125 31.464844 C 67.308594 31.457031 67.308594 31.472656 67.296875 31.492188 "
+ id="path135" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 59.652344 31.492188 C 59.671875 31.421875 59.660156 31.367188 59.628906 31.304688 C 59.574219 31.203125 59.515625 31.238281 59.460938 31.175781 C 59.414062 31.113281 59.363281 30.898438 59.382812 30.8125 C 59.214844 30.753906 59.375 30.585938 59.367188 30.464844 C 59.367188 30.414062 59.328125 30.355469 59.324219 30.300781 C 59.3125 30.183594 59.339844 30.035156 59.390625 29.933594 C 59.488281 29.734375 59.660156 29.519531 59.828125 29.386719 C 60.015625 29.238281 60.191406 28.816406 60.433594 28.757812 C 60.609375 28.714844 60.816406 28.859375 60.976562 28.898438 C 61.15625 28.941406 61.375 28.890625 61.546875 28.953125 C 61.769531 29.03125 61.675781 29.289062 61.835938 29.402344 C 61.941406 29.476562 62.125 29.410156 62.242188 29.453125 C 62.441406 29.523438 62.582031 29.714844 62.621094 29.914062 C 62.632812 29.996094 62.628906 30.058594 62.65625 30.132812 C 62.695312 30.234375 62.769531 30.292969 62.75 30.425781 C 62.738281 30.488281 62.695312 30.546875 62.683594 30.613281 C 62.660156 30.726562 62.683594 30.835938 62.683594 30.949219 C 62.679688 31.148438 62.699219 31.328125 62.605469 31.5 C 62.226562 32.1875 61.371094 32.429688 60.632812 32.289062 C 60.453125 32.253906 60.34375 32.164062 60.1875 32.082031 C 60.019531 31.992188 60.050781 31.917969 59.871094 31.859375 C 59.789062 31.832031 59.65625 31.734375 59.65625 31.636719 C 59.65625 31.519531 59.660156 31.585938 59.667969 31.464844 C 59.648438 31.457031 59.644531 31.472656 59.636719 31.492188 "
+ id="path137" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 51.992188 31.492188 C 52.011719 31.421875 52 31.367188 51.96875 31.304688 C 51.914062 31.203125 51.855469 31.238281 51.800781 31.175781 C 51.753906 31.113281 51.703125 30.898438 51.722656 30.8125 C 51.554688 30.753906 51.714844 30.585938 51.707031 30.464844 C 51.707031 30.414062 51.667969 30.355469 51.664062 30.300781 C 51.652344 30.183594 51.679688 30.035156 51.730469 29.933594 C 51.828125 29.734375 52 29.519531 52.167969 29.386719 C 52.355469 29.238281 52.53125 28.816406 52.773438 28.757812 C 52.953125 28.714844 53.15625 28.859375 53.316406 28.898438 C 53.496094 28.941406 53.714844 28.890625 53.890625 28.953125 C 54.109375 29.03125 54.015625 29.289062 54.175781 29.402344 C 54.28125 29.476562 54.464844 29.410156 54.582031 29.453125 C 54.78125 29.523438 54.921875 29.714844 54.960938 29.914062 C 54.972656 29.996094 54.96875 30.058594 54.996094 30.132812 C 55.035156 30.234375 55.109375 30.292969 55.089844 30.425781 C 55.078125 30.488281 55.035156 30.546875 55.023438 30.613281 C 55 30.726562 55.023438 30.835938 55.023438 30.949219 C 55.019531 31.148438 55.039062 31.328125 54.945312 31.5 C 54.566406 32.1875 53.710938 32.429688 52.972656 32.289062 C 52.792969 32.253906 52.683594 32.164062 52.527344 32.082031 C 52.359375 31.992188 52.390625 31.917969 52.210938 31.859375 C 52.128906 31.832031 51.996094 31.734375 51.996094 31.636719 C 51.996094 31.519531 52 31.585938 52.007812 31.464844 C 51.988281 31.457031 51.984375 31.472656 51.976562 31.492188 "
+ id="path139" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 44.332031 31.492188 C 44.351562 31.421875 44.339844 31.367188 44.304688 31.304688 C 44.253906 31.203125 44.195312 31.238281 44.140625 31.175781 C 44.089844 31.113281 44.042969 30.898438 44.0625 30.8125 C 43.894531 30.753906 44.054688 30.585938 44.046875 30.464844 C 44.042969 30.414062 44.007812 30.355469 44.003906 30.300781 C 43.992188 30.183594 44.019531 30.035156 44.070312 29.933594 C 44.167969 29.734375 44.339844 29.519531 44.503906 29.386719 C 44.695312 29.238281 44.871094 28.816406 45.113281 28.757812 C 45.289062 28.714844 45.496094 28.859375 45.65625 28.898438 C 45.835938 28.941406 46.054688 28.890625 46.226562 28.953125 C 46.449219 29.03125 46.355469 29.289062 46.515625 29.402344 C 46.621094 29.476562 46.804688 29.410156 46.921875 29.453125 C 47.121094 29.523438 47.261719 29.714844 47.296875 29.914062 C 47.316406 29.996094 47.308594 30.058594 47.335938 30.132812 C 47.375 30.234375 47.449219 30.292969 47.425781 30.425781 C 47.417969 30.488281 47.375 30.546875 47.363281 30.613281 C 47.339844 30.726562 47.363281 30.835938 47.363281 30.949219 C 47.359375 31.148438 47.378906 31.328125 47.285156 31.5 C 46.90625 32.1875 46.046875 32.429688 45.308594 32.289062 C 45.128906 32.253906 45.023438 32.164062 44.867188 32.082031 C 44.699219 31.992188 44.730469 31.917969 44.550781 31.859375 C 44.46875 31.832031 44.335938 31.734375 44.335938 31.636719 C 44.335938 31.519531 44.339844 31.585938 44.347656 31.464844 C 44.328125 31.457031 44.324219 31.472656 44.316406 31.492188 "
+ id="path141" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 36.671875 31.492188 C 36.691406 31.421875 36.679688 31.367188 36.644531 31.304688 C 36.59375 31.203125 36.535156 31.238281 36.480469 31.175781 C 36.429688 31.113281 36.382812 30.898438 36.402344 30.8125 C 36.234375 30.753906 36.394531 30.585938 36.386719 30.464844 C 36.382812 30.414062 36.347656 30.355469 36.34375 30.300781 C 36.332031 30.183594 36.359375 30.035156 36.410156 29.933594 C 36.507812 29.734375 36.679688 29.519531 36.84375 29.386719 C 37.035156 29.238281 37.210938 28.816406 37.453125 28.757812 C 37.632812 28.714844 37.835938 28.859375 37.996094 28.898438 C 38.175781 28.941406 38.394531 28.890625 38.570312 28.953125 C 38.789062 29.03125 38.695312 29.289062 38.855469 29.402344 C 38.960938 29.476562 39.144531 29.410156 39.261719 29.453125 C 39.460938 29.523438 39.601562 29.714844 39.636719 29.914062 C 39.652344 29.996094 39.648438 30.058594 39.675781 30.132812 C 39.714844 30.234375 39.789062 30.292969 39.765625 30.425781 C 39.757812 30.488281 39.714844 30.546875 39.703125 30.613281 C 39.679688 30.726562 39.703125 30.835938 39.703125 30.949219 C 39.699219 31.148438 39.71875 31.328125 39.625 31.5 C 39.246094 32.1875 38.386719 32.429688 37.652344 32.289062 C 37.46875 32.253906 37.363281 32.164062 37.207031 32.082031 C 37.039062 31.992188 37.070312 31.917969 36.890625 31.859375 C 36.808594 31.832031 36.675781 31.734375 36.675781 31.636719 C 36.675781 31.519531 36.679688 31.585938 36.6875 31.464844 C 36.667969 31.457031 36.664062 31.472656 36.65625 31.492188 "
+ id="path143" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 29.011719 31.492188 C 29.03125 31.421875 29.019531 31.367188 28.984375 31.304688 C 28.933594 31.203125 28.875 31.238281 28.820312 31.175781 C 28.769531 31.113281 28.722656 30.898438 28.742188 30.8125 C 28.574219 30.753906 28.734375 30.585938 28.726562 30.464844 C 28.722656 30.414062 28.6875 30.355469 28.683594 30.300781 C 28.671875 30.183594 28.699219 30.035156 28.75 29.933594 C 28.847656 29.734375 29.019531 29.519531 29.183594 29.386719 C 29.375 29.238281 29.550781 28.816406 29.792969 28.757812 C 29.96875 28.714844 30.175781 28.859375 30.335938 28.898438 C 30.515625 28.941406 30.734375 28.890625 30.90625 28.953125 C 31.128906 29.03125 31.035156 29.289062 31.195312 29.402344 C 31.300781 29.476562 31.484375 29.410156 31.601562 29.453125 C 31.800781 29.523438 31.941406 29.714844 31.976562 29.914062 C 31.992188 29.996094 31.988281 30.058594 32.015625 30.132812 C 32.054688 30.234375 32.128906 30.292969 32.105469 30.425781 C 32.097656 30.488281 32.054688 30.546875 32.042969 30.613281 C 32.019531 30.726562 32.042969 30.835938 32.042969 30.949219 C 32.039062 31.148438 32.058594 31.328125 31.964844 31.5 C 31.585938 32.1875 30.726562 32.429688 29.988281 32.289062 C 29.808594 32.253906 29.703125 32.164062 29.546875 32.082031 C 29.378906 31.992188 29.410156 31.917969 29.230469 31.859375 C 29.148438 31.832031 29.015625 31.734375 29.015625 31.636719 C 29.015625 31.519531 29.019531 31.585938 29.027344 31.464844 C 29.003906 31.457031 29.003906 31.472656 28.996094 31.492188 "
+ id="path145" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 21.351562 31.492188 C 21.371094 31.421875 21.359375 31.367188 21.324219 31.304688 C 21.273438 31.203125 21.214844 31.238281 21.160156 31.175781 C 21.109375 31.113281 21.0625 30.898438 21.082031 30.8125 C 20.914062 30.753906 21.074219 30.585938 21.066406 30.464844 C 21.0625 30.414062 21.027344 30.355469 21.023438 30.300781 C 21.011719 30.183594 21.039062 30.035156 21.089844 29.933594 C 21.1875 29.734375 21.359375 29.519531 21.523438 29.386719 C 21.714844 29.238281 21.890625 28.816406 22.132812 28.757812 C 22.3125 28.714844 22.515625 28.859375 22.675781 28.898438 C 22.855469 28.941406 23.074219 28.890625 23.246094 28.953125 C 23.46875 29.03125 23.375 29.289062 23.535156 29.402344 C 23.640625 29.476562 23.824219 29.410156 23.941406 29.453125 C 24.140625 29.523438 24.28125 29.714844 24.316406 29.914062 C 24.332031 29.996094 24.328125 30.058594 24.355469 30.132812 C 24.394531 30.234375 24.46875 30.292969 24.445312 30.425781 C 24.4375 30.488281 24.394531 30.546875 24.382812 30.613281 C 24.359375 30.726562 24.382812 30.835938 24.382812 30.949219 C 24.378906 31.148438 24.398438 31.328125 24.304688 31.5 C 23.925781 32.1875 23.066406 32.429688 22.328125 32.289062 C 22.148438 32.253906 22.042969 32.164062 21.886719 32.082031 C 21.71875 31.992188 21.75 31.917969 21.570312 31.859375 C 21.488281 31.832031 21.355469 31.734375 21.355469 31.636719 C 21.355469 31.519531 21.359375 31.585938 21.367188 31.464844 C 21.34375 31.457031 21.34375 31.472656 21.335938 31.492188 "
+ id="path147" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 13.691406 31.492188 C 13.707031 31.421875 13.699219 31.367188 13.664062 31.304688 C 13.613281 31.203125 13.554688 31.238281 13.5 31.175781 C 13.449219 31.113281 13.402344 30.898438 13.421875 30.8125 C 13.25 30.753906 13.410156 30.585938 13.40625 30.464844 C 13.402344 30.414062 13.363281 30.355469 13.359375 30.300781 C 13.351562 30.183594 13.378906 30.035156 13.429688 29.933594 C 13.527344 29.734375 13.695312 29.519531 13.863281 29.386719 C 14.054688 29.238281 14.230469 28.816406 14.472656 28.757812 C 14.648438 28.714844 14.855469 28.859375 15.015625 28.898438 C 15.195312 28.941406 15.414062 28.890625 15.585938 28.953125 C 15.808594 29.03125 15.714844 29.289062 15.875 29.402344 C 15.980469 29.476562 16.160156 29.410156 16.28125 29.453125 C 16.480469 29.523438 16.621094 29.714844 16.65625 29.914062 C 16.671875 29.996094 16.667969 30.058594 16.695312 30.132812 C 16.734375 30.234375 16.808594 30.292969 16.785156 30.425781 C 16.777344 30.488281 16.734375 30.546875 16.722656 30.613281 C 16.699219 30.726562 16.722656 30.835938 16.722656 30.949219 C 16.71875 31.148438 16.738281 31.328125 16.644531 31.5 C 16.265625 32.1875 15.40625 32.429688 14.667969 32.289062 C 14.488281 32.253906 14.382812 32.164062 14.226562 32.082031 C 14.058594 31.992188 14.089844 31.917969 13.910156 31.859375 C 13.828125 31.832031 13.691406 31.734375 13.695312 31.636719 C 13.695312 31.519531 13.699219 31.585938 13.707031 31.464844 C 13.683594 31.457031 13.683594 31.472656 13.675781 31.492188 "
+ id="path149" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 6.03125 31.492188 C 6.046875 31.421875 6.039062 31.367188 6.003906 31.304688 C 5.953125 31.203125 5.894531 31.238281 5.839844 31.175781 C 5.789062 31.113281 5.742188 30.898438 5.761719 30.8125 C 5.59375 30.753906 5.75 30.585938 5.746094 30.464844 C 5.742188 30.414062 5.703125 30.355469 5.699219 30.300781 C 5.691406 30.183594 5.71875 30.035156 5.769531 29.933594 C 5.867188 29.734375 6.035156 29.519531 6.203125 29.386719 C 6.394531 29.238281 6.570312 28.816406 6.8125 28.757812 C 6.988281 28.714844 7.195312 28.859375 7.355469 28.898438 C 7.535156 28.941406 7.753906 28.890625 7.925781 28.953125 C 8.148438 29.03125 8.054688 29.289062 8.214844 29.402344 C 8.320312 29.476562 8.5 29.410156 8.621094 29.453125 C 8.820312 29.523438 8.960938 29.714844 8.996094 29.914062 C 9.011719 29.996094 9.007812 30.058594 9.035156 30.132812 C 9.074219 30.234375 9.148438 30.292969 9.125 30.425781 C 9.117188 30.488281 9.074219 30.546875 9.0625 30.613281 C 9.039062 30.726562 9.0625 30.835938 9.0625 30.949219 C 9.058594 31.148438 9.078125 31.328125 8.984375 31.5 C 8.605469 32.1875 7.746094 32.429688 7.007812 32.289062 C 6.828125 32.253906 6.722656 32.164062 6.566406 32.082031 C 6.398438 31.992188 6.429688 31.917969 6.25 31.859375 C 6.167969 31.832031 6.03125 31.734375 6.035156 31.636719 C 6.035156 31.519531 6.039062 31.585938 6.046875 31.464844 C 6.023438 31.457031 6.023438 31.472656 6.015625 31.492188 "
+ id="path151" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M -1.628906 31.492188 C -1.613281 31.421875 -1.621094 31.367188 -1.65625 31.304688 C -1.707031 31.203125 -1.769531 31.238281 -1.820312 31.175781 C -1.871094 31.113281 -1.917969 30.898438 -1.898438 30.8125 C -2.070312 30.753906 -1.910156 30.585938 -1.914062 30.464844 C -1.917969 30.414062 -1.957031 30.355469 -1.960938 30.300781 C -1.96875 30.183594 -1.941406 30.035156 -1.890625 29.933594 C -1.792969 29.734375 -1.625 29.519531 -1.457031 29.386719 C -1.265625 29.238281 -1.09375 28.816406 -0.847656 28.757812 C -0.671875 28.714844 -0.464844 28.859375 -0.304688 28.898438 C -0.125 28.941406 0.09375 28.890625 0.265625 28.953125 C 0.488281 29.03125 0.394531 29.289062 0.554688 29.402344 C 0.65625 29.476562 0.839844 29.410156 0.960938 29.453125 C 1.160156 29.523438 1.300781 29.714844 1.335938 29.914062 C 1.351562 29.996094 1.347656 30.058594 1.375 30.132812 C 1.414062 30.234375 1.484375 30.292969 1.464844 30.425781 C 1.457031 30.488281 1.414062 30.546875 1.398438 30.613281 C 1.378906 30.726562 1.402344 30.835938 1.402344 30.949219 C 1.398438 31.148438 1.417969 31.328125 1.324219 31.5 C 0.941406 32.1875 0.0859375 32.429688 -0.652344 32.289062 C -0.832031 32.253906 -0.9375 32.164062 -1.09375 32.082031 C -1.265625 31.992188 -1.234375 31.917969 -1.410156 31.859375 C -1.492188 31.832031 -1.628906 31.734375 -1.625 31.636719 C -1.625 31.519531 -1.621094 31.585938 -1.613281 31.464844 C -1.636719 31.457031 -1.636719 31.472656 -1.644531 31.492188 "
+ id="path153" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 67.3125 23.839844 C 67.332031 23.769531 67.320312 23.710938 67.289062 23.648438 C 67.234375 23.546875 67.175781 23.585938 67.121094 23.523438 C 67.074219 23.457031 67.023438 23.246094 67.042969 23.160156 C 66.875 23.101562 67.035156 22.933594 67.027344 22.808594 C 67.027344 22.761719 66.988281 22.703125 66.984375 22.648438 C 66.972656 22.53125 67 22.382812 67.050781 22.277344 C 67.148438 22.078125 67.320312 21.863281 67.488281 21.734375 C 67.675781 21.585938 67.851562 21.160156 68.09375 21.105469 C 68.273438 21.0625 68.480469 21.207031 68.636719 21.246094 C 68.816406 21.289062 69.035156 21.234375 69.210938 21.296875 C 69.429688 21.378906 69.335938 21.636719 69.496094 21.75 C 69.601562 21.824219 69.785156 21.757812 69.902344 21.800781 C 70.101562 21.867188 70.242188 22.0625 70.28125 22.261719 C 70.296875 22.34375 70.289062 22.40625 70.316406 22.480469 C 70.355469 22.582031 70.429688 22.640625 70.410156 22.773438 C 70.398438 22.835938 70.355469 22.894531 70.34375 22.960938 C 70.320312 23.074219 70.347656 23.183594 70.34375 23.296875 C 70.34375 23.496094 70.359375 23.675781 70.265625 23.847656 C 69.886719 24.535156 69.03125 24.777344 68.292969 24.636719 C 68.113281 24.601562 68.003906 24.511719 67.851562 24.429688 C 67.679688 24.339844 67.710938 24.261719 67.53125 24.203125 C 67.449219 24.179688 67.316406 24.082031 67.316406 23.980469 C 67.316406 23.867188 67.320312 23.929688 67.328125 23.8125 C 67.308594 23.800781 67.308594 23.816406 67.296875 23.835938 "
+ id="path155" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 59.652344 23.839844 C 59.671875 23.769531 59.660156 23.710938 59.628906 23.648438 C 59.574219 23.546875 59.515625 23.585938 59.460938 23.523438 C 59.414062 23.457031 59.363281 23.246094 59.382812 23.160156 C 59.214844 23.101562 59.375 22.933594 59.367188 22.808594 C 59.367188 22.761719 59.328125 22.703125 59.324219 22.648438 C 59.3125 22.53125 59.339844 22.382812 59.390625 22.277344 C 59.488281 22.078125 59.660156 21.863281 59.828125 21.734375 C 60.015625 21.585938 60.191406 21.160156 60.433594 21.105469 C 60.609375 21.0625 60.816406 21.207031 60.976562 21.246094 C 61.15625 21.289062 61.375 21.234375 61.546875 21.296875 C 61.769531 21.378906 61.675781 21.636719 61.835938 21.75 C 61.941406 21.824219 62.125 21.757812 62.242188 21.800781 C 62.441406 21.867188 62.582031 22.0625 62.621094 22.261719 C 62.632812 22.34375 62.628906 22.40625 62.65625 22.480469 C 62.695312 22.582031 62.769531 22.640625 62.75 22.773438 C 62.738281 22.835938 62.695312 22.894531 62.683594 22.960938 C 62.660156 23.074219 62.683594 23.183594 62.683594 23.296875 C 62.679688 23.496094 62.699219 23.675781 62.605469 23.847656 C 62.226562 24.535156 61.371094 24.777344 60.632812 24.636719 C 60.453125 24.601562 60.34375 24.511719 60.1875 24.429688 C 60.019531 24.339844 60.050781 24.261719 59.871094 24.203125 C 59.789062 24.179688 59.65625 24.082031 59.65625 23.980469 C 59.65625 23.867188 59.660156 23.929688 59.667969 23.8125 C 59.648438 23.800781 59.644531 23.816406 59.636719 23.835938 "
+ id="path157" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 51.992188 23.839844 C 52.011719 23.769531 52 23.710938 51.96875 23.648438 C 51.914062 23.546875 51.855469 23.585938 51.800781 23.523438 C 51.753906 23.457031 51.703125 23.246094 51.722656 23.160156 C 51.554688 23.101562 51.714844 22.933594 51.707031 22.808594 C 51.707031 22.761719 51.667969 22.703125 51.664062 22.648438 C 51.652344 22.53125 51.679688 22.382812 51.730469 22.277344 C 51.828125 22.078125 52 21.863281 52.167969 21.734375 C 52.355469 21.585938 52.53125 21.160156 52.773438 21.105469 C 52.953125 21.0625 53.15625 21.207031 53.316406 21.246094 C 53.496094 21.289062 53.714844 21.234375 53.890625 21.296875 C 54.109375 21.378906 54.015625 21.636719 54.175781 21.75 C 54.28125 21.824219 54.464844 21.757812 54.582031 21.800781 C 54.78125 21.867188 54.921875 22.0625 54.960938 22.261719 C 54.972656 22.34375 54.96875 22.40625 54.996094 22.480469 C 55.035156 22.582031 55.109375 22.640625 55.089844 22.773438 C 55.078125 22.835938 55.035156 22.894531 55.023438 22.960938 C 55 23.074219 55.023438 23.183594 55.023438 23.296875 C 55.019531 23.496094 55.039062 23.675781 54.945312 23.847656 C 54.566406 24.535156 53.710938 24.777344 52.972656 24.636719 C 52.792969 24.601562 52.683594 24.511719 52.527344 24.429688 C 52.359375 24.339844 52.390625 24.261719 52.210938 24.203125 C 52.128906 24.179688 51.996094 24.082031 51.996094 23.980469 C 51.996094 23.867188 52 23.929688 52.007812 23.8125 C 51.988281 23.800781 51.984375 23.816406 51.976562 23.835938 "
+ id="path159" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 44.332031 23.839844 C 44.351562 23.769531 44.339844 23.710938 44.304688 23.648438 C 44.253906 23.546875 44.195312 23.585938 44.140625 23.523438 C 44.089844 23.457031 44.042969 23.246094 44.0625 23.160156 C 43.894531 23.101562 44.054688 22.933594 44.046875 22.808594 C 44.042969 22.761719 44.007812 22.703125 44.003906 22.648438 C 43.992188 22.53125 44.019531 22.382812 44.070312 22.277344 C 44.167969 22.078125 44.339844 21.863281 44.503906 21.734375 C 44.695312 21.585938 44.871094 21.160156 45.113281 21.105469 C 45.289062 21.0625 45.496094 21.207031 45.65625 21.246094 C 45.835938 21.289062 46.054688 21.234375 46.226562 21.296875 C 46.449219 21.378906 46.355469 21.636719 46.515625 21.75 C 46.621094 21.824219 46.804688 21.757812 46.921875 21.800781 C 47.121094 21.867188 47.261719 22.0625 47.296875 22.261719 C 47.316406 22.34375 47.308594 22.40625 47.335938 22.480469 C 47.375 22.582031 47.449219 22.640625 47.425781 22.773438 C 47.417969 22.835938 47.375 22.894531 47.363281 22.960938 C 47.339844 23.074219 47.363281 23.183594 47.363281 23.296875 C 47.359375 23.496094 47.378906 23.675781 47.285156 23.847656 C 46.90625 24.535156 46.046875 24.777344 45.308594 24.636719 C 45.128906 24.601562 45.023438 24.511719 44.867188 24.429688 C 44.699219 24.339844 44.730469 24.261719 44.550781 24.203125 C 44.46875 24.179688 44.335938 24.082031 44.335938 23.980469 C 44.335938 23.867188 44.339844 23.929688 44.347656 23.8125 C 44.328125 23.800781 44.324219 23.816406 44.316406 23.835938 "
+ id="path161" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 36.671875 23.839844 C 36.691406 23.769531 36.679688 23.710938 36.644531 23.648438 C 36.59375 23.546875 36.535156 23.585938 36.480469 23.523438 C 36.429688 23.457031 36.382812 23.246094 36.402344 23.160156 C 36.234375 23.101562 36.394531 22.933594 36.386719 22.808594 C 36.382812 22.761719 36.347656 22.703125 36.34375 22.648438 C 36.332031 22.53125 36.359375 22.382812 36.410156 22.277344 C 36.507812 22.078125 36.679688 21.863281 36.84375 21.734375 C 37.035156 21.585938 37.210938 21.160156 37.453125 21.105469 C 37.632812 21.0625 37.835938 21.207031 37.996094 21.246094 C 38.175781 21.289062 38.394531 21.234375 38.570312 21.296875 C 38.789062 21.378906 38.695312 21.636719 38.855469 21.75 C 38.960938 21.824219 39.144531 21.757812 39.261719 21.800781 C 39.460938 21.867188 39.601562 22.0625 39.636719 22.261719 C 39.652344 22.34375 39.648438 22.40625 39.675781 22.480469 C 39.714844 22.582031 39.789062 22.640625 39.765625 22.773438 C 39.757812 22.835938 39.714844 22.894531 39.703125 22.960938 C 39.679688 23.074219 39.703125 23.183594 39.703125 23.296875 C 39.699219 23.496094 39.71875 23.675781 39.625 23.847656 C 39.246094 24.535156 38.386719 24.777344 37.652344 24.636719 C 37.46875 24.601562 37.363281 24.511719 37.207031 24.429688 C 37.039062 24.339844 37.070312 24.261719 36.890625 24.203125 C 36.808594 24.179688 36.675781 24.082031 36.675781 23.980469 C 36.675781 23.867188 36.679688 23.929688 36.6875 23.8125 C 36.667969 23.800781 36.664062 23.816406 36.65625 23.835938 "
+ id="path163" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 29.011719 23.839844 C 29.03125 23.769531 29.019531 23.710938 28.984375 23.648438 C 28.933594 23.546875 28.875 23.585938 28.820312 23.523438 C 28.769531 23.457031 28.722656 23.246094 28.742188 23.160156 C 28.574219 23.101562 28.734375 22.933594 28.726562 22.808594 C 28.722656 22.761719 28.6875 22.703125 28.683594 22.648438 C 28.671875 22.53125 28.699219 22.382812 28.75 22.277344 C 28.847656 22.078125 29.019531 21.863281 29.183594 21.734375 C 29.375 21.585938 29.550781 21.160156 29.792969 21.105469 C 29.96875 21.0625 30.175781 21.207031 30.335938 21.246094 C 30.515625 21.289062 30.734375 21.234375 30.90625 21.296875 C 31.128906 21.378906 31.035156 21.636719 31.195312 21.75 C 31.300781 21.824219 31.484375 21.757812 31.601562 21.800781 C 31.800781 21.867188 31.941406 22.0625 31.976562 22.261719 C 31.992188 22.34375 31.988281 22.40625 32.015625 22.480469 C 32.054688 22.582031 32.128906 22.640625 32.105469 22.773438 C 32.097656 22.835938 32.054688 22.894531 32.042969 22.960938 C 32.019531 23.074219 32.042969 23.183594 32.042969 23.296875 C 32.039062 23.496094 32.058594 23.675781 31.964844 23.847656 C 31.585938 24.535156 30.726562 24.777344 29.988281 24.636719 C 29.808594 24.601562 29.703125 24.511719 29.546875 24.429688 C 29.378906 24.339844 29.410156 24.261719 29.230469 24.203125 C 29.148438 24.179688 29.015625 24.082031 29.015625 23.980469 C 29.015625 23.867188 29.019531 23.929688 29.027344 23.8125 C 29.003906 23.800781 29.003906 23.816406 28.996094 23.835938 "
+ id="path165" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 21.351562 23.839844 C 21.371094 23.769531 21.359375 23.710938 21.324219 23.648438 C 21.273438 23.546875 21.214844 23.585938 21.160156 23.523438 C 21.109375 23.457031 21.0625 23.246094 21.082031 23.160156 C 20.914062 23.101562 21.074219 22.933594 21.066406 22.808594 C 21.0625 22.761719 21.027344 22.703125 21.023438 22.648438 C 21.011719 22.53125 21.039062 22.382812 21.089844 22.277344 C 21.1875 22.078125 21.359375 21.863281 21.523438 21.734375 C 21.714844 21.585938 21.890625 21.160156 22.132812 21.105469 C 22.3125 21.0625 22.515625 21.207031 22.675781 21.246094 C 22.855469 21.289062 23.074219 21.234375 23.246094 21.296875 C 23.46875 21.378906 23.375 21.636719 23.535156 21.75 C 23.640625 21.824219 23.824219 21.757812 23.941406 21.800781 C 24.140625 21.867188 24.28125 22.0625 24.316406 22.261719 C 24.332031 22.34375 24.328125 22.40625 24.355469 22.480469 C 24.394531 22.582031 24.46875 22.640625 24.445312 22.773438 C 24.4375 22.835938 24.394531 22.894531 24.382812 22.960938 C 24.359375 23.074219 24.382812 23.183594 24.382812 23.296875 C 24.378906 23.496094 24.398438 23.675781 24.304688 23.847656 C 23.925781 24.535156 23.066406 24.777344 22.328125 24.636719 C 22.148438 24.601562 22.042969 24.511719 21.886719 24.429688 C 21.71875 24.339844 21.75 24.261719 21.570312 24.203125 C 21.488281 24.179688 21.355469 24.082031 21.355469 23.980469 C 21.355469 23.867188 21.359375 23.929688 21.367188 23.8125 C 21.34375 23.800781 21.34375 23.816406 21.335938 23.835938 "
+ id="path167" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 13.691406 23.839844 C 13.707031 23.769531 13.699219 23.710938 13.664062 23.648438 C 13.613281 23.546875 13.554688 23.585938 13.5 23.523438 C 13.449219 23.457031 13.402344 23.246094 13.421875 23.160156 C 13.25 23.101562 13.410156 22.933594 13.40625 22.808594 C 13.402344 22.761719 13.363281 22.703125 13.359375 22.648438 C 13.351562 22.53125 13.378906 22.382812 13.429688 22.277344 C 13.527344 22.078125 13.695312 21.863281 13.863281 21.734375 C 14.054688 21.585938 14.230469 21.160156 14.472656 21.105469 C 14.648438 21.0625 14.855469 21.207031 15.015625 21.246094 C 15.195312 21.289062 15.414062 21.234375 15.585938 21.296875 C 15.808594 21.378906 15.714844 21.636719 15.875 21.75 C 15.980469 21.824219 16.160156 21.757812 16.28125 21.800781 C 16.480469 21.867188 16.621094 22.0625 16.65625 22.261719 C 16.671875 22.34375 16.667969 22.40625 16.695312 22.480469 C 16.734375 22.582031 16.808594 22.640625 16.785156 22.773438 C 16.777344 22.835938 16.734375 22.894531 16.722656 22.960938 C 16.699219 23.074219 16.722656 23.183594 16.722656 23.296875 C 16.71875 23.496094 16.738281 23.675781 16.644531 23.847656 C 16.265625 24.535156 15.40625 24.777344 14.667969 24.636719 C 14.488281 24.601562 14.382812 24.511719 14.226562 24.429688 C 14.058594 24.339844 14.089844 24.261719 13.910156 24.203125 C 13.828125 24.179688 13.691406 24.082031 13.695312 23.980469 C 13.695312 23.867188 13.699219 23.929688 13.707031 23.8125 C 13.683594 23.800781 13.683594 23.816406 13.675781 23.835938 "
+ id="path169" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 6.03125 23.839844 C 6.046875 23.769531 6.039062 23.710938 6.003906 23.648438 C 5.953125 23.546875 5.894531 23.585938 5.839844 23.523438 C 5.789062 23.457031 5.742188 23.246094 5.761719 23.160156 C 5.59375 23.101562 5.75 22.933594 5.746094 22.808594 C 5.742188 22.761719 5.703125 22.703125 5.699219 22.648438 C 5.691406 22.53125 5.71875 22.382812 5.769531 22.277344 C 5.867188 22.078125 6.035156 21.863281 6.203125 21.734375 C 6.394531 21.585938 6.570312 21.160156 6.8125 21.105469 C 6.988281 21.0625 7.195312 21.207031 7.355469 21.246094 C 7.535156 21.289062 7.753906 21.234375 7.925781 21.296875 C 8.148438 21.378906 8.054688 21.636719 8.214844 21.75 C 8.320312 21.824219 8.5 21.757812 8.621094 21.800781 C 8.820312 21.867188 8.960938 22.0625 8.996094 22.261719 C 9.011719 22.34375 9.007812 22.40625 9.035156 22.480469 C 9.074219 22.582031 9.148438 22.640625 9.125 22.773438 C 9.117188 22.835938 9.074219 22.894531 9.0625 22.960938 C 9.039062 23.074219 9.0625 23.183594 9.0625 23.296875 C 9.058594 23.496094 9.078125 23.675781 8.984375 23.847656 C 8.605469 24.535156 7.746094 24.777344 7.007812 24.636719 C 6.828125 24.601562 6.722656 24.511719 6.566406 24.429688 C 6.398438 24.339844 6.429688 24.261719 6.25 24.203125 C 6.167969 24.179688 6.03125 24.082031 6.035156 23.980469 C 6.035156 23.867188 6.039062 23.929688 6.046875 23.8125 C 6.023438 23.800781 6.023438 23.816406 6.015625 23.835938 "
+ id="path171" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M -1.628906 23.839844 C -1.613281 23.769531 -1.621094 23.710938 -1.65625 23.648438 C -1.707031 23.546875 -1.769531 23.585938 -1.820312 23.523438 C -1.871094 23.457031 -1.917969 23.246094 -1.898438 23.160156 C -2.070312 23.101562 -1.910156 22.933594 -1.914062 22.808594 C -1.917969 22.761719 -1.957031 22.703125 -1.960938 22.648438 C -1.96875 22.53125 -1.941406 22.382812 -1.890625 22.277344 C -1.792969 22.078125 -1.625 21.863281 -1.457031 21.734375 C -1.265625 21.585938 -1.09375 21.160156 -0.847656 21.105469 C -0.671875 21.0625 -0.464844 21.207031 -0.304688 21.246094 C -0.125 21.289062 0.09375 21.234375 0.265625 21.296875 C 0.488281 21.378906 0.394531 21.636719 0.554688 21.75 C 0.65625 21.824219 0.839844 21.757812 0.960938 21.800781 C 1.160156 21.867188 1.300781 22.0625 1.335938 22.261719 C 1.351562 22.34375 1.347656 22.40625 1.375 22.480469 C 1.414062 22.582031 1.484375 22.640625 1.464844 22.773438 C 1.457031 22.835938 1.414062 22.894531 1.398438 22.960938 C 1.378906 23.074219 1.402344 23.183594 1.402344 23.296875 C 1.398438 23.496094 1.417969 23.675781 1.324219 23.847656 C 0.941406 24.535156 0.0859375 24.777344 -0.652344 24.636719 C -0.832031 24.601562 -0.9375 24.511719 -1.09375 24.429688 C -1.265625 24.339844 -1.234375 24.261719 -1.410156 24.203125 C -1.492188 24.179688 -1.628906 24.082031 -1.625 23.980469 C -1.625 23.867188 -1.621094 23.929688 -1.613281 23.8125 C -1.636719 23.800781 -1.636719 23.816406 -1.644531 23.835938 "
+ id="path173" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 67.3125 16.1875 C 67.332031 16.117188 67.320312 16.058594 67.289062 15.996094 C 67.234375 15.898438 67.175781 15.933594 67.121094 15.871094 C 67.074219 15.804688 67.023438 15.59375 67.042969 15.507812 C 66.875 15.449219 67.035156 15.28125 67.027344 15.15625 C 67.027344 15.109375 66.988281 15.050781 66.984375 14.996094 C 66.972656 14.875 67 14.730469 67.050781 14.625 C 67.148438 14.425781 67.320312 14.210938 67.488281 14.082031 C 67.675781 13.933594 67.851562 13.507812 68.09375 13.453125 C 68.273438 13.410156 68.480469 13.554688 68.636719 13.59375 C 68.816406 13.636719 69.035156 13.582031 69.210938 13.648438 C 69.429688 13.726562 69.335938 13.984375 69.496094 14.097656 C 69.601562 14.171875 69.785156 14.105469 69.902344 14.148438 C 70.101562 14.214844 70.242188 14.410156 70.28125 14.609375 C 70.296875 14.691406 70.289062 14.75 70.316406 14.828125 C 70.355469 14.929688 70.429688 14.988281 70.410156 15.121094 C 70.398438 15.183594 70.355469 15.242188 70.34375 15.308594 C 70.320312 15.421875 70.347656 15.53125 70.34375 15.644531 C 70.34375 15.84375 70.359375 16.023438 70.265625 16.195312 C 69.886719 16.882812 69.03125 17.125 68.292969 16.984375 C 68.113281 16.949219 68.003906 16.859375 67.851562 16.777344 C 67.679688 16.6875 67.710938 16.609375 67.53125 16.550781 C 67.449219 16.527344 67.316406 16.429688 67.316406 16.328125 C 67.316406 16.214844 67.320312 16.277344 67.328125 16.160156 C 67.308594 16.148438 67.308594 16.164062 67.296875 16.183594 "
+ id="path175" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 59.652344 16.1875 C 59.671875 16.117188 59.660156 16.058594 59.628906 15.996094 C 59.574219 15.898438 59.515625 15.933594 59.460938 15.871094 C 59.414062 15.804688 59.363281 15.59375 59.382812 15.507812 C 59.214844 15.449219 59.375 15.28125 59.367188 15.15625 C 59.367188 15.109375 59.328125 15.050781 59.324219 14.996094 C 59.3125 14.875 59.339844 14.730469 59.390625 14.625 C 59.488281 14.425781 59.660156 14.210938 59.828125 14.082031 C 60.015625 13.933594 60.191406 13.507812 60.433594 13.453125 C 60.609375 13.410156 60.816406 13.554688 60.976562 13.59375 C 61.15625 13.636719 61.375 13.582031 61.546875 13.648438 C 61.769531 13.726562 61.675781 13.984375 61.835938 14.097656 C 61.941406 14.171875 62.125 14.105469 62.242188 14.148438 C 62.441406 14.214844 62.582031 14.410156 62.621094 14.609375 C 62.632812 14.691406 62.628906 14.75 62.65625 14.828125 C 62.695312 14.929688 62.769531 14.988281 62.75 15.121094 C 62.738281 15.183594 62.695312 15.242188 62.683594 15.308594 C 62.660156 15.421875 62.683594 15.53125 62.683594 15.644531 C 62.679688 15.84375 62.699219 16.023438 62.605469 16.195312 C 62.226562 16.882812 61.371094 17.125 60.632812 16.984375 C 60.453125 16.949219 60.34375 16.859375 60.1875 16.777344 C 60.019531 16.6875 60.050781 16.609375 59.871094 16.550781 C 59.789062 16.527344 59.65625 16.429688 59.65625 16.328125 C 59.65625 16.214844 59.660156 16.277344 59.667969 16.160156 C 59.648438 16.148438 59.644531 16.164062 59.636719 16.183594 "
+ id="path177" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 51.992188 16.1875 C 52.011719 16.117188 52 16.058594 51.96875 15.996094 C 51.914062 15.898438 51.855469 15.933594 51.800781 15.871094 C 51.753906 15.804688 51.703125 15.59375 51.722656 15.507812 C 51.554688 15.449219 51.714844 15.28125 51.707031 15.15625 C 51.707031 15.109375 51.667969 15.050781 51.664062 14.996094 C 51.652344 14.875 51.679688 14.730469 51.730469 14.625 C 51.828125 14.425781 52 14.210938 52.167969 14.082031 C 52.355469 13.933594 52.53125 13.507812 52.773438 13.453125 C 52.953125 13.410156 53.15625 13.554688 53.316406 13.59375 C 53.496094 13.636719 53.714844 13.582031 53.890625 13.648438 C 54.109375 13.726562 54.015625 13.984375 54.175781 14.097656 C 54.28125 14.171875 54.464844 14.105469 54.582031 14.148438 C 54.78125 14.214844 54.921875 14.410156 54.960938 14.609375 C 54.972656 14.691406 54.96875 14.75 54.996094 14.828125 C 55.035156 14.929688 55.109375 14.988281 55.089844 15.121094 C 55.078125 15.183594 55.035156 15.242188 55.023438 15.308594 C 55 15.421875 55.023438 15.53125 55.023438 15.644531 C 55.019531 15.84375 55.039062 16.023438 54.945312 16.195312 C 54.566406 16.882812 53.710938 17.125 52.972656 16.984375 C 52.792969 16.949219 52.683594 16.859375 52.527344 16.777344 C 52.359375 16.6875 52.390625 16.609375 52.210938 16.550781 C 52.128906 16.527344 51.996094 16.429688 51.996094 16.328125 C 51.996094 16.214844 52 16.277344 52.007812 16.160156 C 51.988281 16.148438 51.984375 16.164062 51.976562 16.183594 "
+ id="path179" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 44.332031 16.1875 C 44.351562 16.117188 44.339844 16.058594 44.304688 15.996094 C 44.253906 15.898438 44.195312 15.933594 44.140625 15.871094 C 44.089844 15.804688 44.042969 15.59375 44.0625 15.507812 C 43.894531 15.449219 44.054688 15.28125 44.046875 15.15625 C 44.042969 15.109375 44.007812 15.050781 44.003906 14.996094 C 43.992188 14.875 44.019531 14.730469 44.070312 14.625 C 44.167969 14.425781 44.339844 14.210938 44.503906 14.082031 C 44.695312 13.933594 44.871094 13.507812 45.113281 13.453125 C 45.289062 13.410156 45.496094 13.554688 45.65625 13.59375 C 45.835938 13.636719 46.054688 13.582031 46.226562 13.648438 C 46.449219 13.726562 46.355469 13.984375 46.515625 14.097656 C 46.621094 14.171875 46.804688 14.105469 46.921875 14.148438 C 47.121094 14.214844 47.261719 14.410156 47.296875 14.609375 C 47.316406 14.691406 47.308594 14.75 47.335938 14.828125 C 47.375 14.929688 47.449219 14.988281 47.425781 15.121094 C 47.417969 15.183594 47.375 15.242188 47.363281 15.308594 C 47.339844 15.421875 47.363281 15.53125 47.363281 15.644531 C 47.359375 15.84375 47.378906 16.023438 47.285156 16.195312 C 46.90625 16.882812 46.046875 17.125 45.308594 16.984375 C 45.128906 16.949219 45.023438 16.859375 44.867188 16.777344 C 44.699219 16.6875 44.730469 16.609375 44.550781 16.550781 C 44.46875 16.527344 44.335938 16.429688 44.335938 16.328125 C 44.335938 16.214844 44.339844 16.277344 44.347656 16.160156 C 44.328125 16.148438 44.324219 16.164062 44.316406 16.183594 "
+ id="path181" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 36.671875 16.1875 C 36.691406 16.117188 36.679688 16.058594 36.644531 15.996094 C 36.59375 15.898438 36.535156 15.933594 36.480469 15.871094 C 36.429688 15.804688 36.382812 15.59375 36.402344 15.507812 C 36.234375 15.449219 36.394531 15.28125 36.386719 15.15625 C 36.382812 15.109375 36.347656 15.050781 36.34375 14.996094 C 36.332031 14.875 36.359375 14.730469 36.410156 14.625 C 36.507812 14.425781 36.679688 14.210938 36.84375 14.082031 C 37.035156 13.933594 37.210938 13.507812 37.453125 13.453125 C 37.632812 13.410156 37.835938 13.554688 37.996094 13.59375 C 38.175781 13.636719 38.394531 13.582031 38.570312 13.648438 C 38.789062 13.726562 38.695312 13.984375 38.855469 14.097656 C 38.960938 14.171875 39.144531 14.105469 39.261719 14.148438 C 39.460938 14.214844 39.601562 14.410156 39.636719 14.609375 C 39.652344 14.691406 39.648438 14.75 39.675781 14.828125 C 39.714844 14.929688 39.789062 14.988281 39.765625 15.121094 C 39.757812 15.183594 39.714844 15.242188 39.703125 15.308594 C 39.679688 15.421875 39.703125 15.53125 39.703125 15.644531 C 39.699219 15.84375 39.71875 16.023438 39.625 16.195312 C 39.246094 16.882812 38.386719 17.125 37.652344 16.984375 C 37.46875 16.949219 37.363281 16.859375 37.207031 16.777344 C 37.039062 16.6875 37.070312 16.609375 36.890625 16.550781 C 36.808594 16.527344 36.675781 16.429688 36.675781 16.328125 C 36.675781 16.214844 36.679688 16.277344 36.6875 16.160156 C 36.667969 16.148438 36.664062 16.164062 36.65625 16.183594 "
+ id="path183" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 29.011719 16.1875 C 29.03125 16.117188 29.019531 16.058594 28.984375 15.996094 C 28.933594 15.898438 28.875 15.933594 28.820312 15.871094 C 28.769531 15.804688 28.722656 15.59375 28.742188 15.507812 C 28.574219 15.449219 28.734375 15.28125 28.726562 15.15625 C 28.722656 15.109375 28.6875 15.050781 28.683594 14.996094 C 28.671875 14.875 28.699219 14.730469 28.75 14.625 C 28.847656 14.425781 29.019531 14.210938 29.183594 14.082031 C 29.375 13.933594 29.550781 13.507812 29.792969 13.453125 C 29.96875 13.410156 30.175781 13.554688 30.335938 13.59375 C 30.515625 13.636719 30.734375 13.582031 30.90625 13.648438 C 31.128906 13.726562 31.035156 13.984375 31.195312 14.097656 C 31.300781 14.171875 31.484375 14.105469 31.601562 14.148438 C 31.800781 14.214844 31.941406 14.410156 31.976562 14.609375 C 31.992188 14.691406 31.988281 14.75 32.015625 14.828125 C 32.054688 14.929688 32.128906 14.988281 32.105469 15.121094 C 32.097656 15.183594 32.054688 15.242188 32.042969 15.308594 C 32.019531 15.421875 32.042969 15.53125 32.042969 15.644531 C 32.039062 15.84375 32.058594 16.023438 31.964844 16.195312 C 31.585938 16.882812 30.726562 17.125 29.988281 16.984375 C 29.808594 16.949219 29.703125 16.859375 29.546875 16.777344 C 29.378906 16.6875 29.410156 16.609375 29.230469 16.550781 C 29.148438 16.527344 29.015625 16.429688 29.015625 16.328125 C 29.015625 16.214844 29.019531 16.277344 29.027344 16.160156 C 29.003906 16.148438 29.003906 16.164062 28.996094 16.183594 "
+ id="path185" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 21.351562 16.1875 C 21.371094 16.117188 21.359375 16.058594 21.324219 15.996094 C 21.273438 15.898438 21.214844 15.933594 21.160156 15.871094 C 21.109375 15.804688 21.0625 15.59375 21.082031 15.507812 C 20.914062 15.449219 21.074219 15.28125 21.066406 15.15625 C 21.0625 15.109375 21.027344 15.050781 21.023438 14.996094 C 21.011719 14.875 21.039062 14.730469 21.089844 14.625 C 21.1875 14.425781 21.359375 14.210938 21.523438 14.082031 C 21.714844 13.933594 21.890625 13.507812 22.132812 13.453125 C 22.3125 13.410156 22.515625 13.554688 22.675781 13.59375 C 22.855469 13.636719 23.074219 13.582031 23.246094 13.648438 C 23.46875 13.726562 23.375 13.984375 23.535156 14.097656 C 23.640625 14.171875 23.824219 14.105469 23.941406 14.148438 C 24.140625 14.214844 24.28125 14.410156 24.316406 14.609375 C 24.332031 14.691406 24.328125 14.75 24.355469 14.828125 C 24.394531 14.929688 24.46875 14.988281 24.445312 15.121094 C 24.4375 15.183594 24.394531 15.242188 24.382812 15.308594 C 24.359375 15.421875 24.382812 15.53125 24.382812 15.644531 C 24.378906 15.84375 24.398438 16.023438 24.304688 16.195312 C 23.925781 16.882812 23.066406 17.125 22.328125 16.984375 C 22.148438 16.949219 22.042969 16.859375 21.886719 16.777344 C 21.71875 16.6875 21.75 16.609375 21.570312 16.550781 C 21.488281 16.527344 21.355469 16.429688 21.355469 16.328125 C 21.355469 16.214844 21.359375 16.277344 21.367188 16.160156 C 21.34375 16.148438 21.34375 16.164062 21.335938 16.183594 "
+ id="path187" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 13.691406 16.1875 C 13.707031 16.117188 13.699219 16.058594 13.664062 15.996094 C 13.613281 15.898438 13.554688 15.933594 13.5 15.871094 C 13.449219 15.804688 13.402344 15.59375 13.421875 15.507812 C 13.25 15.449219 13.410156 15.28125 13.40625 15.15625 C 13.402344 15.109375 13.363281 15.050781 13.359375 14.996094 C 13.351562 14.875 13.378906 14.730469 13.429688 14.625 C 13.527344 14.425781 13.695312 14.210938 13.863281 14.082031 C 14.054688 13.933594 14.230469 13.507812 14.472656 13.453125 C 14.648438 13.410156 14.855469 13.554688 15.015625 13.59375 C 15.195312 13.636719 15.414062 13.582031 15.585938 13.648438 C 15.808594 13.726562 15.714844 13.984375 15.875 14.097656 C 15.980469 14.171875 16.160156 14.105469 16.28125 14.148438 C 16.480469 14.214844 16.621094 14.410156 16.65625 14.609375 C 16.671875 14.691406 16.667969 14.75 16.695312 14.828125 C 16.734375 14.929688 16.808594 14.988281 16.785156 15.121094 C 16.777344 15.183594 16.734375 15.242188 16.722656 15.308594 C 16.699219 15.421875 16.722656 15.53125 16.722656 15.644531 C 16.71875 15.84375 16.738281 16.023438 16.644531 16.195312 C 16.265625 16.882812 15.40625 17.125 14.667969 16.984375 C 14.488281 16.949219 14.382812 16.859375 14.226562 16.777344 C 14.058594 16.6875 14.089844 16.609375 13.910156 16.550781 C 13.828125 16.527344 13.691406 16.429688 13.695312 16.328125 C 13.695312 16.214844 13.699219 16.277344 13.707031 16.160156 C 13.683594 16.148438 13.683594 16.164062 13.675781 16.183594 "
+ id="path189" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 6.03125 16.1875 C 6.046875 16.117188 6.039062 16.058594 6.003906 15.996094 C 5.953125 15.898438 5.894531 15.933594 5.839844 15.871094 C 5.789062 15.804688 5.742188 15.59375 5.761719 15.507812 C 5.59375 15.449219 5.75 15.28125 5.746094 15.15625 C 5.742188 15.109375 5.703125 15.050781 5.699219 14.996094 C 5.691406 14.875 5.71875 14.730469 5.769531 14.625 C 5.867188 14.425781 6.035156 14.210938 6.203125 14.082031 C 6.394531 13.933594 6.570312 13.507812 6.8125 13.453125 C 6.988281 13.410156 7.195312 13.554688 7.355469 13.59375 C 7.535156 13.636719 7.753906 13.582031 7.925781 13.648438 C 8.148438 13.726562 8.054688 13.984375 8.214844 14.097656 C 8.320312 14.171875 8.5 14.105469 8.621094 14.148438 C 8.820312 14.214844 8.960938 14.410156 8.996094 14.609375 C 9.011719 14.691406 9.007812 14.75 9.035156 14.828125 C 9.074219 14.929688 9.148438 14.988281 9.125 15.121094 C 9.117188 15.183594 9.074219 15.242188 9.0625 15.308594 C 9.039062 15.421875 9.0625 15.53125 9.0625 15.644531 C 9.058594 15.84375 9.078125 16.023438 8.984375 16.195312 C 8.605469 16.882812 7.746094 17.125 7.007812 16.984375 C 6.828125 16.949219 6.722656 16.859375 6.566406 16.777344 C 6.398438 16.6875 6.429688 16.609375 6.25 16.550781 C 6.167969 16.527344 6.03125 16.429688 6.035156 16.328125 C 6.035156 16.214844 6.039062 16.277344 6.046875 16.160156 C 6.023438 16.148438 6.023438 16.164062 6.015625 16.183594 "
+ id="path191" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M -1.628906 16.1875 C -1.613281 16.117188 -1.621094 16.058594 -1.65625 15.996094 C -1.707031 15.898438 -1.769531 15.933594 -1.820312 15.871094 C -1.871094 15.804688 -1.917969 15.59375 -1.898438 15.507812 C -2.070312 15.449219 -1.910156 15.28125 -1.914062 15.15625 C -1.917969 15.109375 -1.957031 15.050781 -1.960938 14.996094 C -1.96875 14.875 -1.941406 14.730469 -1.890625 14.625 C -1.792969 14.425781 -1.625 14.210938 -1.457031 14.082031 C -1.265625 13.933594 -1.09375 13.507812 -0.847656 13.453125 C -0.671875 13.410156 -0.464844 13.554688 -0.304688 13.59375 C -0.125 13.636719 0.09375 13.582031 0.265625 13.648438 C 0.488281 13.726562 0.394531 13.984375 0.554688 14.097656 C 0.65625 14.171875 0.839844 14.105469 0.960938 14.148438 C 1.160156 14.214844 1.300781 14.410156 1.335938 14.609375 C 1.351562 14.691406 1.347656 14.75 1.375 14.828125 C 1.414062 14.929688 1.484375 14.988281 1.464844 15.121094 C 1.457031 15.183594 1.414062 15.242188 1.398438 15.308594 C 1.378906 15.421875 1.402344 15.53125 1.402344 15.644531 C 1.398438 15.84375 1.417969 16.023438 1.324219 16.195312 C 0.941406 16.882812 0.0859375 17.125 -0.652344 16.984375 C -0.832031 16.949219 -0.9375 16.859375 -1.09375 16.777344 C -1.265625 16.6875 -1.234375 16.609375 -1.410156 16.550781 C -1.492188 16.527344 -1.628906 16.429688 -1.625 16.328125 C -1.625 16.214844 -1.621094 16.277344 -1.613281 16.160156 C -1.636719 16.148438 -1.636719 16.164062 -1.644531 16.183594 "
+ id="path193" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 67.3125 8.535156 C 67.332031 8.460938 67.320312 8.40625 67.289062 8.34375 C 67.234375 8.242188 67.175781 8.28125 67.121094 8.214844 C 67.074219 8.152344 67.023438 7.9375 67.042969 7.851562 C 66.875 7.796875 67.035156 7.628906 67.027344 7.503906 C 67.027344 7.453125 66.988281 7.398438 66.984375 7.34375 C 66.972656 7.222656 67 7.078125 67.050781 6.972656 C 67.148438 6.773438 67.320312 6.558594 67.488281 6.429688 C 67.675781 6.28125 67.851562 5.855469 68.09375 5.796875 C 68.273438 5.757812 68.480469 5.902344 68.636719 5.941406 C 68.816406 5.984375 69.035156 5.929688 69.210938 5.992188 C 69.429688 6.070312 69.335938 6.332031 69.496094 6.441406 C 69.601562 6.515625 69.785156 6.453125 69.902344 6.496094 C 70.101562 6.5625 70.242188 6.757812 70.28125 6.953125 C 70.296875 7.035156 70.289062 7.097656 70.316406 7.175781 C 70.355469 7.277344 70.429688 7.335938 70.410156 7.46875 C 70.398438 7.527344 70.355469 7.589844 70.34375 7.652344 C 70.320312 7.769531 70.347656 7.878906 70.34375 7.992188 C 70.34375 8.191406 70.359375 8.371094 70.265625 8.542969 C 69.886719 9.226562 69.03125 9.46875 68.292969 9.332031 C 68.113281 9.296875 68.003906 9.203125 67.851562 9.125 C 67.679688 9.035156 67.710938 8.957031 67.53125 8.898438 C 67.449219 8.875 67.316406 8.773438 67.316406 8.675781 C 67.316406 8.558594 67.320312 8.625 67.328125 8.507812 C 67.308594 8.496094 67.308594 8.511719 67.296875 8.53125 "
+ id="path195" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 59.652344 8.535156 C 59.671875 8.460938 59.660156 8.40625 59.628906 8.34375 C 59.574219 8.242188 59.515625 8.28125 59.460938 8.214844 C 59.414062 8.152344 59.363281 7.9375 59.382812 7.851562 C 59.214844 7.796875 59.375 7.628906 59.367188 7.503906 C 59.367188 7.453125 59.328125 7.398438 59.324219 7.34375 C 59.3125 7.222656 59.339844 7.078125 59.390625 6.972656 C 59.488281 6.773438 59.660156 6.558594 59.828125 6.429688 C 60.015625 6.28125 60.191406 5.855469 60.433594 5.796875 C 60.609375 5.757812 60.816406 5.902344 60.976562 5.941406 C 61.15625 5.984375 61.375 5.929688 61.546875 5.992188 C 61.769531 6.070312 61.675781 6.332031 61.835938 6.441406 C 61.941406 6.515625 62.125 6.453125 62.242188 6.496094 C 62.441406 6.5625 62.582031 6.757812 62.621094 6.953125 C 62.632812 7.035156 62.628906 7.097656 62.65625 7.175781 C 62.695312 7.277344 62.769531 7.335938 62.75 7.46875 C 62.738281 7.527344 62.695312 7.589844 62.683594 7.652344 C 62.660156 7.769531 62.683594 7.878906 62.683594 7.992188 C 62.679688 8.191406 62.699219 8.371094 62.605469 8.542969 C 62.226562 9.226562 61.371094 9.46875 60.632812 9.332031 C 60.453125 9.296875 60.34375 9.203125 60.1875 9.125 C 60.019531 9.035156 60.050781 8.957031 59.871094 8.898438 C 59.789062 8.875 59.65625 8.773438 59.65625 8.675781 C 59.65625 8.558594 59.660156 8.625 59.667969 8.507812 C 59.648438 8.496094 59.644531 8.511719 59.636719 8.53125 "
+ id="path197" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 51.992188 8.535156 C 52.011719 8.460938 52 8.40625 51.96875 8.34375 C 51.914062 8.242188 51.855469 8.28125 51.800781 8.214844 C 51.753906 8.152344 51.703125 7.9375 51.722656 7.851562 C 51.554688 7.796875 51.714844 7.628906 51.707031 7.503906 C 51.707031 7.453125 51.667969 7.398438 51.664062 7.34375 C 51.652344 7.222656 51.679688 7.078125 51.730469 6.972656 C 51.828125 6.773438 52 6.558594 52.167969 6.429688 C 52.355469 6.28125 52.53125 5.855469 52.773438 5.796875 C 52.953125 5.757812 53.15625 5.902344 53.316406 5.941406 C 53.496094 5.984375 53.714844 5.929688 53.890625 5.992188 C 54.109375 6.070312 54.015625 6.332031 54.175781 6.441406 C 54.28125 6.515625 54.464844 6.453125 54.582031 6.496094 C 54.78125 6.5625 54.921875 6.757812 54.960938 6.953125 C 54.972656 7.035156 54.96875 7.097656 54.996094 7.175781 C 55.035156 7.277344 55.109375 7.335938 55.089844 7.46875 C 55.078125 7.527344 55.035156 7.589844 55.023438 7.652344 C 55 7.769531 55.023438 7.878906 55.023438 7.992188 C 55.019531 8.191406 55.039062 8.371094 54.945312 8.542969 C 54.566406 9.226562 53.710938 9.46875 52.972656 9.332031 C 52.792969 9.296875 52.683594 9.203125 52.527344 9.125 C 52.359375 9.035156 52.390625 8.957031 52.210938 8.898438 C 52.128906 8.875 51.996094 8.773438 51.996094 8.675781 C 51.996094 8.558594 52 8.625 52.007812 8.507812 C 51.988281 8.496094 51.984375 8.511719 51.976562 8.53125 "
+ id="path199" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 44.332031 8.535156 C 44.351562 8.460938 44.339844 8.40625 44.304688 8.34375 C 44.253906 8.242188 44.195312 8.28125 44.140625 8.214844 C 44.089844 8.152344 44.042969 7.9375 44.0625 7.851562 C 43.894531 7.796875 44.054688 7.628906 44.046875 7.503906 C 44.042969 7.453125 44.007812 7.398438 44.003906 7.34375 C 43.992188 7.222656 44.019531 7.078125 44.070312 6.972656 C 44.167969 6.773438 44.339844 6.558594 44.503906 6.429688 C 44.695312 6.28125 44.871094 5.855469 45.113281 5.796875 C 45.289062 5.757812 45.496094 5.902344 45.65625 5.941406 C 45.835938 5.984375 46.054688 5.929688 46.226562 5.992188 C 46.449219 6.070312 46.355469 6.332031 46.515625 6.441406 C 46.621094 6.515625 46.804688 6.453125 46.921875 6.496094 C 47.121094 6.5625 47.261719 6.757812 47.296875 6.953125 C 47.316406 7.035156 47.308594 7.097656 47.335938 7.175781 C 47.375 7.277344 47.449219 7.335938 47.425781 7.46875 C 47.417969 7.527344 47.375 7.589844 47.363281 7.652344 C 47.339844 7.769531 47.363281 7.878906 47.363281 7.992188 C 47.359375 8.191406 47.378906 8.371094 47.285156 8.542969 C 46.90625 9.226562 46.046875 9.46875 45.308594 9.332031 C 45.128906 9.296875 45.023438 9.203125 44.867188 9.125 C 44.699219 9.035156 44.730469 8.957031 44.550781 8.898438 C 44.46875 8.875 44.335938 8.773438 44.335938 8.675781 C 44.335938 8.558594 44.339844 8.625 44.347656 8.507812 C 44.328125 8.496094 44.324219 8.511719 44.316406 8.53125 "
+ id="path201" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 36.671875 8.535156 C 36.691406 8.460938 36.679688 8.40625 36.644531 8.34375 C 36.59375 8.242188 36.535156 8.28125 36.480469 8.214844 C 36.429688 8.152344 36.382812 7.9375 36.402344 7.851562 C 36.234375 7.796875 36.394531 7.628906 36.386719 7.503906 C 36.382812 7.453125 36.347656 7.398438 36.34375 7.34375 C 36.332031 7.222656 36.359375 7.078125 36.410156 6.972656 C 36.507812 6.773438 36.679688 6.558594 36.84375 6.429688 C 37.035156 6.28125 37.210938 5.855469 37.453125 5.796875 C 37.632812 5.757812 37.835938 5.902344 37.996094 5.941406 C 38.175781 5.984375 38.394531 5.929688 38.570312 5.992188 C 38.789062 6.070312 38.695312 6.332031 38.855469 6.441406 C 38.960938 6.515625 39.144531 6.453125 39.261719 6.496094 C 39.460938 6.5625 39.601562 6.757812 39.636719 6.953125 C 39.652344 7.035156 39.648438 7.097656 39.675781 7.175781 C 39.714844 7.277344 39.789062 7.335938 39.765625 7.46875 C 39.757812 7.527344 39.714844 7.589844 39.703125 7.652344 C 39.679688 7.769531 39.703125 7.878906 39.703125 7.992188 C 39.699219 8.191406 39.71875 8.371094 39.625 8.542969 C 39.246094 9.226562 38.386719 9.46875 37.652344 9.332031 C 37.46875 9.296875 37.363281 9.203125 37.207031 9.125 C 37.039062 9.035156 37.070312 8.957031 36.890625 8.898438 C 36.808594 8.875 36.675781 8.773438 36.675781 8.675781 C 36.675781 8.558594 36.679688 8.625 36.6875 8.507812 C 36.667969 8.496094 36.664062 8.511719 36.65625 8.53125 "
+ id="path203" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 29.011719 8.535156 C 29.03125 8.460938 29.019531 8.40625 28.984375 8.34375 C 28.933594 8.242188 28.875 8.28125 28.820312 8.214844 C 28.769531 8.152344 28.722656 7.9375 28.742188 7.851562 C 28.574219 7.796875 28.734375 7.628906 28.726562 7.503906 C 28.722656 7.453125 28.6875 7.398438 28.683594 7.34375 C 28.671875 7.222656 28.699219 7.078125 28.75 6.972656 C 28.847656 6.773438 29.019531 6.558594 29.183594 6.429688 C 29.375 6.28125 29.550781 5.855469 29.792969 5.796875 C 29.96875 5.757812 30.175781 5.902344 30.335938 5.941406 C 30.515625 5.984375 30.734375 5.929688 30.90625 5.992188 C 31.128906 6.070312 31.035156 6.332031 31.195312 6.441406 C 31.300781 6.515625 31.484375 6.453125 31.601562 6.496094 C 31.800781 6.5625 31.941406 6.757812 31.976562 6.953125 C 31.992188 7.035156 31.988281 7.097656 32.015625 7.175781 C 32.054688 7.277344 32.128906 7.335938 32.105469 7.46875 C 32.097656 7.527344 32.054688 7.589844 32.042969 7.652344 C 32.019531 7.769531 32.042969 7.878906 32.042969 7.992188 C 32.039062 8.191406 32.058594 8.371094 31.964844 8.542969 C 31.585938 9.226562 30.726562 9.46875 29.988281 9.332031 C 29.808594 9.296875 29.703125 9.203125 29.546875 9.125 C 29.378906 9.035156 29.410156 8.957031 29.230469 8.898438 C 29.148438 8.875 29.015625 8.773438 29.015625 8.675781 C 29.015625 8.558594 29.019531 8.625 29.027344 8.507812 C 29.003906 8.496094 29.003906 8.511719 28.996094 8.53125 "
+ id="path205" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 21.351562 8.535156 C 21.371094 8.460938 21.359375 8.40625 21.324219 8.34375 C 21.273438 8.242188 21.214844 8.28125 21.160156 8.214844 C 21.109375 8.152344 21.0625 7.9375 21.082031 7.851562 C 20.914062 7.796875 21.074219 7.628906 21.066406 7.503906 C 21.0625 7.453125 21.027344 7.398438 21.023438 7.34375 C 21.011719 7.222656 21.039062 7.078125 21.089844 6.972656 C 21.1875 6.773438 21.359375 6.558594 21.523438 6.429688 C 21.714844 6.28125 21.890625 5.855469 22.132812 5.796875 C 22.3125 5.757812 22.515625 5.902344 22.675781 5.941406 C 22.855469 5.984375 23.074219 5.929688 23.246094 5.992188 C 23.46875 6.070312 23.375 6.332031 23.535156 6.441406 C 23.640625 6.515625 23.824219 6.453125 23.941406 6.496094 C 24.140625 6.5625 24.28125 6.757812 24.316406 6.953125 C 24.332031 7.035156 24.328125 7.097656 24.355469 7.175781 C 24.394531 7.277344 24.46875 7.335938 24.445312 7.46875 C 24.4375 7.527344 24.394531 7.589844 24.382812 7.652344 C 24.359375 7.769531 24.382812 7.878906 24.382812 7.992188 C 24.378906 8.191406 24.398438 8.371094 24.304688 8.542969 C 23.925781 9.226562 23.066406 9.46875 22.328125 9.332031 C 22.148438 9.296875 22.042969 9.203125 21.886719 9.125 C 21.71875 9.035156 21.75 8.957031 21.570312 8.898438 C 21.488281 8.875 21.355469 8.773438 21.355469 8.675781 C 21.355469 8.558594 21.359375 8.625 21.367188 8.507812 C 21.34375 8.496094 21.34375 8.511719 21.335938 8.53125 "
+ id="path207" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 13.691406 8.535156 C 13.707031 8.460938 13.699219 8.40625 13.664062 8.34375 C 13.613281 8.242188 13.554688 8.28125 13.5 8.214844 C 13.449219 8.152344 13.402344 7.9375 13.421875 7.851562 C 13.25 7.796875 13.410156 7.628906 13.40625 7.503906 C 13.402344 7.453125 13.363281 7.398438 13.359375 7.34375 C 13.351562 7.222656 13.378906 7.078125 13.429688 6.972656 C 13.527344 6.773438 13.695312 6.558594 13.863281 6.429688 C 14.054688 6.28125 14.230469 5.855469 14.472656 5.796875 C 14.648438 5.757812 14.855469 5.902344 15.015625 5.941406 C 15.195312 5.984375 15.414062 5.929688 15.585938 5.992188 C 15.808594 6.070312 15.714844 6.332031 15.875 6.441406 C 15.980469 6.515625 16.160156 6.453125 16.28125 6.496094 C 16.480469 6.5625 16.621094 6.757812 16.65625 6.953125 C 16.671875 7.035156 16.667969 7.097656 16.695312 7.175781 C 16.734375 7.277344 16.808594 7.335938 16.785156 7.46875 C 16.777344 7.527344 16.734375 7.589844 16.722656 7.652344 C 16.699219 7.769531 16.722656 7.878906 16.722656 7.992188 C 16.71875 8.191406 16.738281 8.371094 16.644531 8.542969 C 16.265625 9.226562 15.40625 9.46875 14.667969 9.332031 C 14.488281 9.296875 14.382812 9.203125 14.226562 9.125 C 14.058594 9.035156 14.089844 8.957031 13.910156 8.898438 C 13.828125 8.875 13.691406 8.773438 13.695312 8.675781 C 13.695312 8.558594 13.699219 8.625 13.707031 8.507812 C 13.683594 8.496094 13.683594 8.511719 13.675781 8.53125 "
+ id="path209" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 6.03125 8.535156 C 6.046875 8.460938 6.039062 8.40625 6.003906 8.34375 C 5.953125 8.242188 5.894531 8.28125 5.839844 8.214844 C 5.789062 8.152344 5.742188 7.9375 5.761719 7.851562 C 5.59375 7.796875 5.75 7.628906 5.746094 7.503906 C 5.742188 7.453125 5.703125 7.398438 5.699219 7.34375 C 5.691406 7.222656 5.71875 7.078125 5.769531 6.972656 C 5.867188 6.773438 6.035156 6.558594 6.203125 6.429688 C 6.394531 6.28125 6.570312 5.855469 6.8125 5.796875 C 6.988281 5.757812 7.195312 5.902344 7.355469 5.941406 C 7.535156 5.984375 7.753906 5.929688 7.925781 5.992188 C 8.148438 6.070312 8.054688 6.332031 8.214844 6.441406 C 8.320312 6.515625 8.5 6.453125 8.621094 6.496094 C 8.820312 6.5625 8.960938 6.757812 8.996094 6.953125 C 9.011719 7.035156 9.007812 7.097656 9.035156 7.175781 C 9.074219 7.277344 9.148438 7.335938 9.125 7.46875 C 9.117188 7.527344 9.074219 7.589844 9.0625 7.652344 C 9.039062 7.769531 9.0625 7.878906 9.0625 7.992188 C 9.058594 8.191406 9.078125 8.371094 8.984375 8.542969 C 8.605469 9.226562 7.746094 9.46875 7.007812 9.332031 C 6.828125 9.296875 6.722656 9.203125 6.566406 9.125 C 6.398438 9.035156 6.429688 8.957031 6.25 8.898438 C 6.167969 8.875 6.03125 8.773438 6.035156 8.675781 C 6.035156 8.558594 6.039062 8.625 6.046875 8.507812 C 6.023438 8.496094 6.023438 8.511719 6.015625 8.53125 "
+ id="path211" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M -1.628906 8.535156 C -1.613281 8.460938 -1.621094 8.40625 -1.65625 8.34375 C -1.707031 8.242188 -1.769531 8.28125 -1.820312 8.214844 C -1.871094 8.152344 -1.917969 7.9375 -1.898438 7.851562 C -2.070312 7.796875 -1.910156 7.628906 -1.914062 7.503906 C -1.917969 7.453125 -1.957031 7.398438 -1.960938 7.34375 C -1.96875 7.222656 -1.941406 7.078125 -1.890625 6.972656 C -1.792969 6.773438 -1.625 6.558594 -1.457031 6.429688 C -1.265625 6.28125 -1.09375 5.855469 -0.847656 5.796875 C -0.671875 5.757812 -0.464844 5.902344 -0.304688 5.941406 C -0.125 5.984375 0.09375 5.929688 0.265625 5.992188 C 0.488281 6.070312 0.394531 6.332031 0.554688 6.441406 C 0.65625 6.515625 0.839844 6.453125 0.960938 6.496094 C 1.160156 6.5625 1.300781 6.757812 1.335938 6.953125 C 1.351562 7.035156 1.347656 7.097656 1.375 7.175781 C 1.414062 7.277344 1.484375 7.335938 1.464844 7.46875 C 1.457031 7.527344 1.414062 7.589844 1.398438 7.652344 C 1.378906 7.769531 1.402344 7.878906 1.402344 7.992188 C 1.398438 8.191406 1.417969 8.371094 1.324219 8.542969 C 0.941406 9.226562 0.0859375 9.46875 -0.652344 9.332031 C -0.832031 9.296875 -0.9375 9.203125 -1.09375 9.125 C -1.265625 9.035156 -1.234375 8.957031 -1.410156 8.898438 C -1.492188 8.875 -1.628906 8.773438 -1.625 8.675781 C -1.625 8.558594 -1.621094 8.625 -1.613281 8.507812 C -1.636719 8.496094 -1.636719 8.511719 -1.644531 8.53125 "
+ id="path213" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 67.3125 0.882812 C 67.332031 0.808594 67.320312 0.753906 67.289062 0.691406 C 67.234375 0.589844 67.175781 0.628906 67.121094 0.5625 C 67.070312 0.5 67.023438 0.285156 67.042969 0.199219 C 66.875 0.144531 67.035156 -0.0234375 67.027344 -0.148438 C 67.027344 -0.199219 66.988281 -0.253906 66.984375 -0.308594 C 66.972656 -0.429688 67 -0.574219 67.050781 -0.679688 C 67.148438 -0.878906 67.320312 -1.09375 67.488281 -1.222656 C 67.675781 -1.371094 67.851562 -1.796875 68.09375 -1.855469 C 68.273438 -1.894531 68.480469 -1.75 68.636719 -1.710938 C 68.816406 -1.667969 69.035156 -1.722656 69.210938 -1.660156 C 69.429688 -1.582031 69.335938 -1.320312 69.496094 -1.210938 C 69.601562 -1.136719 69.785156 -1.199219 69.902344 -1.15625 C 70.101562 -1.089844 70.242188 -0.894531 70.28125 -0.699219 C 70.296875 -0.617188 70.289062 -0.554688 70.316406 -0.476562 C 70.355469 -0.375 70.429688 -0.316406 70.410156 -0.183594 C 70.398438 -0.125 70.355469 -0.0625 70.34375 0 C 70.324219 0.117188 70.347656 0.226562 70.34375 0.339844 C 70.34375 0.539062 70.359375 0.71875 70.265625 0.890625 C 69.886719 1.574219 69.03125 1.816406 68.292969 1.679688 C 68.113281 1.644531 68.003906 1.550781 67.851562 1.472656 C 67.679688 1.382812 67.710938 1.304688 67.53125 1.246094 C 67.449219 1.222656 67.316406 1.121094 67.316406 1.023438 C 67.320312 0.90625 67.320312 0.972656 67.328125 0.855469 C 67.308594 0.84375 67.308594 0.859375 67.296875 0.878906 "
+ id="path215" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 59.652344 0.882812 C 59.671875 0.808594 59.660156 0.753906 59.628906 0.691406 C 59.574219 0.589844 59.515625 0.628906 59.460938 0.5625 C 59.414062 0.5 59.363281 0.285156 59.382812 0.199219 C 59.214844 0.144531 59.375 -0.0234375 59.367188 -0.148438 C 59.367188 -0.199219 59.328125 -0.253906 59.324219 -0.308594 C 59.3125 -0.429688 59.339844 -0.574219 59.390625 -0.679688 C 59.488281 -0.878906 59.660156 -1.09375 59.828125 -1.222656 C 60.015625 -1.371094 60.191406 -1.796875 60.433594 -1.855469 C 60.609375 -1.894531 60.816406 -1.75 60.976562 -1.710938 C 61.15625 -1.667969 61.375 -1.722656 61.546875 -1.660156 C 61.769531 -1.582031 61.675781 -1.320312 61.835938 -1.210938 C 61.941406 -1.136719 62.125 -1.199219 62.242188 -1.15625 C 62.441406 -1.089844 62.582031 -0.894531 62.621094 -0.699219 C 62.632812 -0.617188 62.628906 -0.554688 62.65625 -0.476562 C 62.695312 -0.375 62.769531 -0.316406 62.75 -0.183594 C 62.738281 -0.125 62.695312 -0.0625 62.683594 0 C 62.660156 0.117188 62.683594 0.226562 62.683594 0.339844 C 62.679688 0.539062 62.699219 0.71875 62.605469 0.890625 C 62.226562 1.574219 61.371094 1.816406 60.632812 1.679688 C 60.453125 1.644531 60.34375 1.550781 60.1875 1.472656 C 60.019531 1.382812 60.050781 1.304688 59.871094 1.246094 C 59.789062 1.222656 59.65625 1.121094 59.65625 1.023438 C 59.65625 0.90625 59.660156 0.972656 59.667969 0.855469 C 59.648438 0.84375 59.644531 0.859375 59.636719 0.878906 "
+ id="path217" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 51.992188 0.882812 C 52.011719 0.808594 52 0.753906 51.96875 0.691406 C 51.914062 0.589844 51.855469 0.628906 51.800781 0.5625 C 51.753906 0.5 51.703125 0.285156 51.722656 0.199219 C 51.554688 0.144531 51.714844 -0.0234375 51.707031 -0.148438 C 51.707031 -0.199219 51.667969 -0.253906 51.664062 -0.308594 C 51.652344 -0.429688 51.679688 -0.574219 51.730469 -0.679688 C 51.828125 -0.878906 52 -1.09375 52.167969 -1.222656 C 52.355469 -1.371094 52.53125 -1.796875 52.773438 -1.855469 C 52.953125 -1.894531 53.15625 -1.75 53.316406 -1.710938 C 53.496094 -1.667969 53.714844 -1.722656 53.890625 -1.660156 C 54.109375 -1.582031 54.015625 -1.320312 54.175781 -1.210938 C 54.28125 -1.136719 54.464844 -1.199219 54.582031 -1.15625 C 54.78125 -1.089844 54.921875 -0.894531 54.960938 -0.699219 C 54.972656 -0.617188 54.96875 -0.554688 54.996094 -0.476562 C 55.035156 -0.375 55.109375 -0.316406 55.089844 -0.183594 C 55.078125 -0.125 55.035156 -0.0625 55.023438 0 C 55 0.117188 55.023438 0.226562 55.023438 0.339844 C 55.019531 0.539062 55.039062 0.71875 54.945312 0.890625 C 54.566406 1.574219 53.710938 1.816406 52.972656 1.679688 C 52.792969 1.644531 52.683594 1.550781 52.527344 1.472656 C 52.359375 1.382812 52.390625 1.304688 52.210938 1.246094 C 52.128906 1.222656 51.996094 1.121094 51.996094 1.023438 C 51.996094 0.90625 52 0.972656 52.007812 0.855469 C 51.988281 0.84375 51.984375 0.859375 51.976562 0.878906 "
+ id="path219" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 44.332031 0.882812 C 44.351562 0.808594 44.339844 0.753906 44.304688 0.691406 C 44.253906 0.589844 44.195312 0.628906 44.140625 0.5625 C 44.089844 0.5 44.042969 0.285156 44.0625 0.199219 C 43.894531 0.144531 44.050781 -0.0234375 44.046875 -0.148438 C 44.042969 -0.199219 44.007812 -0.253906 44.003906 -0.308594 C 43.992188 -0.429688 44.019531 -0.574219 44.070312 -0.679688 C 44.167969 -0.878906 44.339844 -1.09375 44.503906 -1.222656 C 44.695312 -1.371094 44.871094 -1.796875 45.113281 -1.855469 C 45.289062 -1.894531 45.496094 -1.75 45.65625 -1.710938 C 45.835938 -1.667969 46.054688 -1.722656 46.226562 -1.660156 C 46.449219 -1.582031 46.355469 -1.320312 46.515625 -1.210938 C 46.621094 -1.136719 46.804688 -1.199219 46.921875 -1.15625 C 47.121094 -1.089844 47.261719 -0.894531 47.296875 -0.699219 C 47.316406 -0.617188 47.308594 -0.554688 47.335938 -0.476562 C 47.375 -0.375 47.449219 -0.316406 47.425781 -0.183594 C 47.417969 -0.125 47.375 -0.0625 47.363281 0 C 47.339844 0.117188 47.363281 0.226562 47.363281 0.339844 C 47.359375 0.539062 47.378906 0.71875 47.285156 0.890625 C 46.90625 1.574219 46.046875 1.816406 45.308594 1.679688 C 45.128906 1.644531 45.023438 1.550781 44.867188 1.472656 C 44.699219 1.382812 44.730469 1.304688 44.550781 1.246094 C 44.46875 1.222656 44.335938 1.121094 44.335938 1.023438 C 44.335938 0.90625 44.339844 0.972656 44.347656 0.855469 C 44.328125 0.84375 44.324219 0.859375 44.316406 0.878906 "
+ id="path221" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 36.671875 0.882812 C 36.691406 0.808594 36.679688 0.753906 36.644531 0.691406 C 36.59375 0.589844 36.535156 0.628906 36.480469 0.5625 C 36.429688 0.5 36.382812 0.285156 36.402344 0.199219 C 36.234375 0.144531 36.390625 -0.0234375 36.386719 -0.148438 C 36.382812 -0.199219 36.347656 -0.253906 36.34375 -0.308594 C 36.332031 -0.429688 36.359375 -0.574219 36.410156 -0.679688 C 36.507812 -0.878906 36.679688 -1.09375 36.84375 -1.222656 C 37.035156 -1.371094 37.210938 -1.796875 37.453125 -1.855469 C 37.632812 -1.894531 37.835938 -1.75 37.996094 -1.710938 C 38.175781 -1.667969 38.394531 -1.722656 38.570312 -1.660156 C 38.789062 -1.582031 38.695312 -1.320312 38.855469 -1.210938 C 38.960938 -1.136719 39.144531 -1.199219 39.261719 -1.15625 C 39.460938 -1.089844 39.601562 -0.894531 39.636719 -0.699219 C 39.652344 -0.617188 39.648438 -0.554688 39.675781 -0.476562 C 39.714844 -0.375 39.789062 -0.316406 39.765625 -0.183594 C 39.757812 -0.125 39.714844 -0.0625 39.703125 0 C 39.679688 0.117188 39.703125 0.226562 39.703125 0.339844 C 39.699219 0.539062 39.71875 0.71875 39.625 0.890625 C 39.246094 1.574219 38.386719 1.816406 37.652344 1.679688 C 37.46875 1.644531 37.363281 1.550781 37.207031 1.472656 C 37.039062 1.382812 37.070312 1.304688 36.890625 1.246094 C 36.808594 1.222656 36.675781 1.121094 36.675781 1.023438 C 36.675781 0.90625 36.679688 0.972656 36.6875 0.855469 C 36.667969 0.84375 36.664062 0.859375 36.65625 0.878906 "
+ id="path223" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 29.011719 0.882812 C 29.03125 0.808594 29.019531 0.753906 28.984375 0.691406 C 28.933594 0.589844 28.875 0.628906 28.820312 0.5625 C 28.769531 0.5 28.722656 0.285156 28.742188 0.199219 C 28.574219 0.144531 28.730469 -0.0234375 28.726562 -0.148438 C 28.722656 -0.199219 28.6875 -0.253906 28.683594 -0.308594 C 28.671875 -0.429688 28.699219 -0.574219 28.75 -0.679688 C 28.847656 -0.878906 29.019531 -1.09375 29.183594 -1.222656 C 29.375 -1.371094 29.550781 -1.796875 29.792969 -1.855469 C 29.96875 -1.894531 30.175781 -1.75 30.335938 -1.710938 C 30.515625 -1.667969 30.734375 -1.722656 30.90625 -1.660156 C 31.128906 -1.582031 31.035156 -1.320312 31.195312 -1.210938 C 31.300781 -1.136719 31.484375 -1.199219 31.601562 -1.15625 C 31.800781 -1.089844 31.941406 -0.894531 31.976562 -0.699219 C 31.992188 -0.617188 31.988281 -0.554688 32.015625 -0.476562 C 32.054688 -0.375 32.128906 -0.316406 32.105469 -0.183594 C 32.097656 -0.125 32.054688 -0.0625 32.042969 0 C 32.019531 0.117188 32.042969 0.226562 32.042969 0.339844 C 32.039062 0.539062 32.058594 0.71875 31.964844 0.890625 C 31.585938 1.574219 30.726562 1.816406 29.988281 1.679688 C 29.808594 1.644531 29.703125 1.550781 29.546875 1.472656 C 29.378906 1.382812 29.410156 1.304688 29.230469 1.246094 C 29.148438 1.222656 29.015625 1.121094 29.015625 1.023438 C 29.015625 0.90625 29.019531 0.972656 29.027344 0.855469 C 29.003906 0.84375 29.003906 0.859375 28.996094 0.878906 "
+ id="path225" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 21.351562 0.882812 C 21.371094 0.808594 21.359375 0.753906 21.324219 0.691406 C 21.273438 0.589844 21.214844 0.628906 21.160156 0.5625 C 21.109375 0.5 21.0625 0.285156 21.082031 0.199219 C 20.914062 0.144531 21.070312 -0.0234375 21.066406 -0.148438 C 21.0625 -0.199219 21.027344 -0.253906 21.023438 -0.308594 C 21.011719 -0.429688 21.039062 -0.574219 21.089844 -0.679688 C 21.1875 -0.878906 21.359375 -1.09375 21.523438 -1.222656 C 21.714844 -1.371094 21.890625 -1.796875 22.132812 -1.855469 C 22.3125 -1.894531 22.515625 -1.75 22.675781 -1.710938 C 22.855469 -1.667969 23.074219 -1.722656 23.246094 -1.660156 C 23.46875 -1.582031 23.375 -1.320312 23.535156 -1.210938 C 23.640625 -1.136719 23.824219 -1.199219 23.941406 -1.15625 C 24.140625 -1.089844 24.28125 -0.894531 24.316406 -0.699219 C 24.332031 -0.617188 24.328125 -0.554688 24.355469 -0.476562 C 24.394531 -0.375 24.46875 -0.316406 24.445312 -0.183594 C 24.4375 -0.125 24.394531 -0.0625 24.382812 0 C 24.359375 0.117188 24.382812 0.226562 24.382812 0.339844 C 24.378906 0.539062 24.398438 0.71875 24.304688 0.890625 C 23.925781 1.574219 23.066406 1.816406 22.328125 1.679688 C 22.148438 1.644531 22.042969 1.550781 21.886719 1.472656 C 21.71875 1.382812 21.75 1.304688 21.570312 1.246094 C 21.488281 1.222656 21.355469 1.121094 21.355469 1.023438 C 21.355469 0.90625 21.359375 0.972656 21.367188 0.855469 C 21.34375 0.84375 21.34375 0.859375 21.335938 0.878906 "
+ id="path227" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 13.691406 0.882812 C 13.707031 0.808594 13.699219 0.753906 13.664062 0.691406 C 13.613281 0.589844 13.554688 0.628906 13.5 0.5625 C 13.449219 0.5 13.402344 0.285156 13.421875 0.199219 C 13.25 0.144531 13.410156 -0.0234375 13.40625 -0.148438 C 13.402344 -0.199219 13.363281 -0.253906 13.359375 -0.308594 C 13.351562 -0.429688 13.378906 -0.574219 13.429688 -0.679688 C 13.527344 -0.878906 13.695312 -1.09375 13.863281 -1.222656 C 14.054688 -1.371094 14.230469 -1.796875 14.472656 -1.855469 C 14.648438 -1.894531 14.855469 -1.75 15.015625 -1.710938 C 15.195312 -1.667969 15.414062 -1.722656 15.585938 -1.660156 C 15.808594 -1.582031 15.714844 -1.320312 15.875 -1.210938 C 15.980469 -1.136719 16.160156 -1.199219 16.28125 -1.15625 C 16.480469 -1.089844 16.621094 -0.894531 16.65625 -0.699219 C 16.671875 -0.617188 16.667969 -0.554688 16.695312 -0.476562 C 16.734375 -0.375 16.808594 -0.316406 16.785156 -0.183594 C 16.777344 -0.125 16.734375 -0.0625 16.722656 0 C 16.699219 0.117188 16.722656 0.226562 16.722656 0.339844 C 16.71875 0.539062 16.738281 0.71875 16.644531 0.890625 C 16.265625 1.574219 15.40625 1.816406 14.667969 1.679688 C 14.488281 1.644531 14.382812 1.550781 14.226562 1.472656 C 14.058594 1.382812 14.089844 1.304688 13.910156 1.246094 C 13.828125 1.222656 13.691406 1.121094 13.695312 1.023438 C 13.695312 0.90625 13.699219 0.972656 13.707031 0.855469 C 13.683594 0.84375 13.683594 0.859375 13.675781 0.878906 "
+ id="path229" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 6.03125 0.882812 C 6.046875 0.808594 6.039062 0.753906 6.003906 0.691406 C 5.953125 0.589844 5.894531 0.628906 5.839844 0.5625 C 5.789062 0.5 5.742188 0.285156 5.761719 0.199219 C 5.59375 0.144531 5.75 -0.0234375 5.746094 -0.148438 C 5.742188 -0.199219 5.703125 -0.253906 5.699219 -0.308594 C 5.691406 -0.429688 5.71875 -0.574219 5.769531 -0.679688 C 5.867188 -0.878906 6.035156 -1.09375 6.203125 -1.222656 C 6.394531 -1.371094 6.570312 -1.796875 6.8125 -1.855469 C 6.988281 -1.894531 7.195312 -1.75 7.355469 -1.710938 C 7.535156 -1.667969 7.753906 -1.722656 7.925781 -1.660156 C 8.148438 -1.582031 8.054688 -1.320312 8.214844 -1.210938 C 8.320312 -1.136719 8.5 -1.199219 8.621094 -1.15625 C 8.820312 -1.089844 8.960938 -0.894531 8.996094 -0.699219 C 9.011719 -0.617188 9.007812 -0.554688 9.035156 -0.476562 C 9.074219 -0.375 9.148438 -0.316406 9.125 -0.183594 C 9.117188 -0.125 9.074219 -0.0625 9.0625 0 C 9.039062 0.117188 9.0625 0.226562 9.0625 0.339844 C 9.058594 0.539062 9.078125 0.71875 8.984375 0.890625 C 8.605469 1.574219 7.746094 1.816406 7.007812 1.679688 C 6.828125 1.644531 6.722656 1.550781 6.566406 1.472656 C 6.398438 1.382812 6.429688 1.304688 6.25 1.246094 C 6.167969 1.222656 6.03125 1.121094 6.035156 1.023438 C 6.035156 0.90625 6.039062 0.972656 6.046875 0.855469 C 6.023438 0.84375 6.023438 0.859375 6.015625 0.878906 "
+ id="path231" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M -1.628906 0.882812 C -1.613281 0.808594 -1.621094 0.753906 -1.65625 0.691406 C -1.707031 0.589844 -1.769531 0.628906 -1.820312 0.5625 C -1.871094 0.5 -1.917969 0.285156 -1.898438 0.199219 C -2.070312 0.144531 -1.910156 -0.0234375 -1.914062 -0.148438 C -1.917969 -0.199219 -1.957031 -0.253906 -1.960938 -0.308594 C -1.96875 -0.429688 -1.941406 -0.574219 -1.890625 -0.679688 C -1.792969 -0.878906 -1.625 -1.09375 -1.457031 -1.222656 C -1.265625 -1.371094 -1.09375 -1.796875 -0.847656 -1.855469 C -0.671875 -1.894531 -0.464844 -1.75 -0.304688 -1.710938 C -0.125 -1.667969 0.09375 -1.722656 0.265625 -1.660156 C 0.488281 -1.582031 0.394531 -1.320312 0.554688 -1.210938 C 0.65625 -1.136719 0.839844 -1.199219 0.960938 -1.15625 C 1.160156 -1.089844 1.300781 -0.894531 1.335938 -0.699219 C 1.351562 -0.617188 1.347656 -0.554688 1.375 -0.476562 C 1.410156 -0.375 1.484375 -0.316406 1.464844 -0.183594 C 1.457031 -0.125 1.414062 -0.0625 1.398438 0 C 1.378906 0.117188 1.402344 0.226562 1.402344 0.339844 C 1.398438 0.539062 1.417969 0.71875 1.324219 0.890625 C 0.941406 1.574219 0.0859375 1.816406 -0.652344 1.679688 C -0.832031 1.644531 -0.9375 1.550781 -1.09375 1.472656 C -1.265625 1.382812 -1.234375 1.304688 -1.410156 1.246094 C -1.492188 1.222656 -1.628906 1.121094 -1.625 1.023438 C -1.625 0.90625 -1.621094 0.972656 -1.613281 0.855469 C -1.636719 0.84375 -1.636719 0.859375 -1.644531 0.878906 "
+ id="path233" />
+ </g>
+ <pattern
+ id="pattern0"
+ patternUnits="userSpaceOnUse"
+ width="69"
+ height="69"
+ patternTransform="matrix(1,0,0,-1,-26.48,394.2263)">
+ <use
+ xlink:href="#surface388"
+ id="use236" />
+ </pattern>
+ </defs>
+ <g
+ id="surface384">
+ <g
+ clip-path="url(#clip1)"
+ clip-rule="nonzero"
+ id="g245">
+ <g
+ clip-path="url(#clip2)"
+ clip-rule="nonzero"
+ id="g243">
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:url(#pattern0);"
+ d="M 42.519531 394.226562 L 319.96875 394.226562 L 319.96875 61.296875 L 42.519531 61.296875 Z M 42.519531 394.226562 "
+ id="path241" />
+ </g>
+ </g>
+ <path
+ style="fill:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(92.939758%,10.978699%,14.118958%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M -0.00095 0.000225 C -43.200169 -142.898212 -145.391575 -261.288837 -277.450169 -332.929462 C -209.364231 -205.206806 -100.528294 -66.04665 -0.00095 0.000225 Z M -0.00095 0.000225 "
+ transform="matrix(1,0,0,-1,319.9697,61.2971)"
+ id="path247" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(92.939758%,10.978699%,14.118958%);fill-opacity:1;"
+ d="M 169.175781 303.578125 C 168.414062 303.746094 167.972656 303.273438 168.191406 302.527344 L 172.335938 288.445312 C 172.558594 287.699219 173.160156 287.5625 173.679688 288.144531 L 183.445312 299.101562 C 183.960938 299.683594 183.765625 300.300781 183.003906 300.472656 Z M 169.175781 303.578125 "
+ id="path249" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(92.939758%,10.978699%,14.118958%);fill-opacity:1;"
+ d="M 213.800781 143.300781 C 213.25 142.746094 213.414062 142.121094 214.164062 141.90625 L 228.28125 137.890625 C 229.03125 137.675781 229.46875 138.113281 229.25 138.863281 L 225.207031 152.972656 C 224.992188 153.71875 224.363281 153.882812 223.8125 153.332031 Z M 213.800781 143.300781 "
+ id="path251" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;"
+ d="M 42.519531 388.027344 L 37.417969 388.027344 L 37.417969 399.367188 L 48.757812 399.367188 L 48.757812 394.261719 C 45.3125 394.261719 42.519531 391.472656 42.519531 388.027344 "
+ id="path253" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(6.47583%,5.92804%,5.235291%);fill-opacity:1;"
+ d="M 47.378906 35.421875 L 46.554688 32.515625 L 43.441406 21.402344 C 42.933594 19.601562 42.113281 19.601562 41.605469 21.402344 L 38.597656 32.121094 L 37.667969 35.421875 C 37.414062 36.324219 37.625 37.058594 38.132812 37.058594 L 46.914062 37.058594 C 47.421875 37.058594 47.621094 36.324219 47.378906 35.421875 "
+ id="path255" />
+ <path
+ style=" stroke:none;fill-rule:nonzero;fill:rgb(6.47583%,5.92804%,5.235291%);fill-opacity:1;"
+ d="M 401.324219 399.078125 L 404.226562 398.265625 L 415.34375 395.152344 C 417.144531 394.644531 417.144531 393.8125 415.34375 393.308594 L 404.621094 390.304688 L 401.324219 389.378906 C 400.421875 389.121094 399.683594 389.324219 399.683594 389.832031 L 399.683594 398.617188 C 399.683594 399.125 400.421875 399.335938 401.324219 399.078125 "
+ id="path257" />
+ <path
+ style="fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(6.47583%,5.92804%,5.235291%);stroke-opacity:1;stroke-miterlimit:10;"
+ d="M -0.00046875 0.0007125 L -0.00046875 -358.425069 C -0.00046875 -362.41335 1.331562 -365.671163 2.956562 -365.671163 L 365.6675 -365.671163 "
+ transform="matrix(1,0,0,-1,42.52,28.5554)"
+ id="path259" />
+ <g
+ style="fill:rgb(6.47583%,5.92804%,5.235291%);fill-opacity:1;"
+ id="g263">
+ <use
+ xlink:href="#glyph0-1"
+ x="33.1689"
+ y="27.0173"
+ id="use261" />
+ </g>
+ <g
+ style="fill:rgb(13.729858%,12.159729%,12.548828%);fill-opacity:1;"
+ id="g267">
+ <use
+ xlink:href="#glyph1-1"
+ x="411.8042"
+ y="418.5876"
+ id="use265" />
+ </g>
+ <g
+ style="fill:rgb(6.47583%,5.92804%,5.235291%);fill-opacity:1;"
+ id="g271">
+ <use
+ xlink:href="#glyph2-1"
+ x="16.7729"
+ y="27.0173"
+ id="use269" />
+ </g>
+ <g
+ style="fill:rgb(6.47583%,5.92804%,5.235291%);fill-opacity:1;"
+ id="g275">
+ <use
+ xlink:href="#glyph0-1"
+ x="30.7169"
+ y="27.0173"
+ id="use273" />
+ </g>
+ </g>
+</svg>
diff --git a/doc/dev-doc/manual/figures/cl/visco_elastic_law.svg b/doc/dev-doc/manual/figures/cl/visco_elastic_law.svg
new file mode 100644
index 000000000..c1fbc8cee
--- /dev/null
+++ b/doc/dev-doc/manual/figures/cl/visco_elastic_law.svg
@@ -0,0 +1,238 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="283.465pt"
+ height="283.465pt"
+ viewBox="0 0 283.465 283.465"
+ version="1.2"
+ id="svg79"
+ sodipodi:docname="visco_elastic_law.svg"
+ inkscape:version="1.0.2 (e86c870879, 2021-01-15)">
+ <metadata
+ id="metadata83">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1738"
+ inkscape:window-height="1007"
+ id="namedview81"
+ showgrid="false"
+ inkscape:zoom="2.2674757"
+ inkscape:cx="188.97666"
+ inkscape:cy="188.97666"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg79" />
+ <defs
+ id="defs22">
+ <g
+ id="g20">
+ <symbol
+ overflow="visible"
+ id="glyph0-0">
+ <path
+ style="stroke:none;"
+ d="M 7.75 -6.65625 L 7.75 -6.953125 C 7.75 -7.160156 7.757812 -7.347656 7.78125 -7.515625 C 7.8125 -7.679688 7.878906 -7.859375 7.984375 -8.046875 C 8.085938 -8.242188 8.234375 -8.460938 8.421875 -8.703125 C 8.609375 -8.941406 8.867188 -9.238281 9.203125 -9.59375 C 9.410156 -9.820312 9.597656 -10.039062 9.765625 -10.25 C 9.941406 -10.46875 10.09375 -10.691406 10.21875 -10.921875 C 10.34375 -11.160156 10.4375 -11.410156 10.5 -11.671875 C 10.570312 -11.929688 10.609375 -12.21875 10.609375 -12.53125 C 10.609375 -13.21875 10.457031 -13.75 10.15625 -14.125 C 9.863281 -14.5 9.4375 -14.6875 8.875 -14.6875 C 8.582031 -14.6875 8.328125 -14.640625 8.109375 -14.546875 C 7.890625 -14.453125 7.695312 -14.320312 7.53125 -14.15625 C 7.363281 -14 7.222656 -13.804688 7.109375 -13.578125 C 7.003906 -13.347656 6.910156 -13.097656 6.828125 -12.828125 L 5.515625 -12.828125 L 5.515625 -14.609375 C 6.054688 -14.878906 6.640625 -15.117188 7.265625 -15.328125 C 7.890625 -15.535156 8.507812 -15.640625 9.125 -15.640625 C 9.757812 -15.640625 10.300781 -15.570312 10.75 -15.4375 C 11.207031 -15.3125 11.578125 -15.128906 11.859375 -14.890625 C 12.140625 -14.660156 12.34375 -14.382812 12.46875 -14.0625 C 12.59375 -13.75 12.65625 -13.40625 12.65625 -13.03125 C 12.65625 -12.6875 12.617188 -12.367188 12.546875 -12.078125 C 12.484375 -11.796875 12.367188 -11.515625 12.203125 -11.234375 C 12.046875 -10.960938 11.828125 -10.664062 11.546875 -10.34375 C 11.273438 -10.03125 10.941406 -9.664062 10.546875 -9.25 C 10.253906 -8.957031 10.019531 -8.707031 9.84375 -8.5 C 9.664062 -8.300781 9.53125 -8.113281 9.4375 -7.9375 C 9.34375 -7.757812 9.28125 -7.578125 9.25 -7.390625 C 9.21875 -7.210938 9.203125 -6.992188 9.203125 -6.734375 L 9.203125 -6.65625 Z M 9.453125 -5.15625 L 9.453125 -2.953125 L 7.484375 -2.953125 L 7.484375 -5.15625 Z M 14.421875 -1.265625 L 14.421875 -17.375 L 4 -17.375 L 4 -1.265625 Z M 15.828125 -18.65625 L 15.828125 0 L 2.59375 0 L 2.59375 -18.65625 Z M 15.828125 -18.65625 "
+ id="path2" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-1">
+ <path
+ style="stroke:none;"
+ d="M 16.734375 -18.65625 L 15.796875 -14.46875 L 14.484375 -14.46875 C 14.453125 -15.125 14.398438 -15.632812 14.328125 -16 C 14.253906 -16.375 14.171875 -16.648438 14.078125 -16.828125 C 13.992188 -17.003906 13.890625 -17.132812 13.765625 -17.21875 C 13.648438 -17.300781 13.503906 -17.359375 13.328125 -17.390625 C 13.148438 -17.429688 12.894531 -17.453125 12.5625 -17.453125 L 8.109375 -17.453125 L 6.546875 -10.296875 L 9.15625 -10.296875 C 9.5625 -10.296875 9.894531 -10.347656 10.15625 -10.453125 C 10.425781 -10.566406 10.675781 -10.757812 10.90625 -11.03125 C 11.132812 -11.300781 11.390625 -11.710938 11.671875 -12.265625 L 12.921875 -12.265625 L 11.6875 -7.015625 L 10.53125 -7.015625 C 10.53125 -7.515625 10.507812 -7.882812 10.46875 -8.125 C 10.4375 -8.363281 10.390625 -8.535156 10.328125 -8.640625 C 10.265625 -8.753906 10.1875 -8.84375 10.09375 -8.90625 C 10.007812 -8.976562 9.894531 -9.023438 9.75 -9.046875 C 9.601562 -9.078125 9.363281 -9.09375 9.03125 -9.09375 L 6.296875 -9.09375 L 4.546875 -1.203125 L 8.59375 -1.203125 C 8.988281 -1.203125 9.320312 -1.226562 9.59375 -1.28125 C 9.875 -1.34375 10.128906 -1.445312 10.359375 -1.59375 C 10.585938 -1.738281 10.804688 -1.925781 11.015625 -2.15625 C 11.222656 -2.394531 11.4375 -2.695312 11.65625 -3.0625 C 11.882812 -3.425781 12.191406 -3.984375 12.578125 -4.734375 L 13.90625 -4.734375 L 12.703125 0 L 0.578125 0 L 0.71875 -0.671875 C 1.070312 -0.722656 1.332031 -0.8125 1.5 -0.9375 C 1.675781 -1.0625 1.820312 -1.234375 1.9375 -1.453125 C 2.0625 -1.671875 2.171875 -1.9375 2.265625 -2.25 C 2.367188 -2.5625 2.519531 -3.171875 2.71875 -4.078125 L 5.046875 -14.609375 C 5.242188 -15.515625 5.34375 -16.242188 5.34375 -16.796875 C 5.34375 -17.234375 5.234375 -17.535156 5.015625 -17.703125 C 4.796875 -17.867188 4.457031 -17.96875 4 -18 L 4.15625 -18.65625 Z M 16.734375 -18.65625 "
+ id="path5" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-0">
+ <path
+ style="stroke:none;"
+ d="M 1.609375 0 L 8.078125 -18.921875 L 24.078125 -18.921875 L 17.609375 0 Z M 2.265625 -0.46875 L 17.265625 -0.46875 L 23.421875 -18.453125 L 8.421875 -18.453125 Z M 2.265625 -0.46875 "
+ id="path8" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-1">
+ <path
+ style="stroke:none;"
+ d="M 4.25 0 L 7.640625 -9.90625 C 7.929688 -10.757812 8.070312 -11.347656 8.0625 -11.671875 C 8.0625 -11.992188 7.988281 -12.222656 7.84375 -12.359375 C 7.695312 -12.492188 7.488281 -12.5625 7.21875 -12.5625 C 6.851562 -12.5625 6.484375 -12.414062 6.109375 -12.125 C 5.742188 -11.84375 5.335938 -11.253906 4.890625 -10.359375 L 4.25 -10.359375 C 4.863281 -11.660156 5.546875 -12.582031 6.296875 -13.125 C 7.054688 -13.664062 7.890625 -13.9375 8.796875 -13.9375 C 9.660156 -13.9375 10.238281 -13.703125 10.53125 -13.234375 C 10.832031 -12.765625 10.804688 -11.898438 10.453125 -10.640625 C 12.285156 -12.078125 13.632812 -12.984375 14.5 -13.359375 C 15.363281 -13.742188 16.1875 -13.9375 16.96875 -13.9375 C 17.945312 -13.9375 18.671875 -13.707031 19.140625 -13.25 C 19.617188 -12.800781 19.84375 -12.21875 19.8125 -11.5 C 19.789062 -10.789062 19.476562 -9.539062 18.875 -7.75 L 15.796875 1.234375 C 15.285156 2.710938 14.957031 3.765625 14.8125 4.390625 C 14.675781 5.015625 14.59375 5.726562 14.5625 6.53125 L 11.890625 6.53125 C 11.984375 5.59375 12.054688 4.984375 12.109375 4.703125 C 12.273438 3.941406 12.472656 3.222656 12.703125 2.546875 L 16.421875 -8.296875 C 16.878906 -9.648438 17.050781 -10.539062 16.9375 -10.96875 C 16.75 -11.738281 16.175781 -12.125 15.21875 -12.125 C 14.570312 -12.125 13.867188 -11.941406 13.109375 -11.578125 C 12.347656 -11.222656 11.335938 -10.550781 10.078125 -9.5625 L 6.8125 0 Z M 4.25 0 "
+ id="path11" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-0">
+ <path
+ style="stroke:none;"
+ d="M 4.4375 -3.8125 L 4.4375 -3.984375 C 4.4375 -4.097656 4.441406 -4.203125 4.453125 -4.296875 C 4.472656 -4.390625 4.507812 -4.488281 4.5625 -4.59375 C 4.625 -4.707031 4.707031 -4.832031 4.8125 -4.96875 C 4.925781 -5.113281 5.078125 -5.285156 5.265625 -5.484375 C 5.378906 -5.617188 5.484375 -5.742188 5.578125 -5.859375 C 5.679688 -5.984375 5.769531 -6.113281 5.84375 -6.25 C 5.914062 -6.382812 5.96875 -6.523438 6 -6.671875 C 6.039062 -6.816406 6.0625 -6.984375 6.0625 -7.171875 C 6.0625 -7.554688 5.976562 -7.851562 5.8125 -8.0625 C 5.644531 -8.28125 5.398438 -8.390625 5.078125 -8.390625 C 4.910156 -8.390625 4.765625 -8.363281 4.640625 -8.3125 C 4.515625 -8.257812 4.40625 -8.1875 4.3125 -8.09375 C 4.21875 -8 4.132812 -7.882812 4.0625 -7.75 C 4 -7.625 3.945312 -7.484375 3.90625 -7.328125 L 3.15625 -7.328125 L 3.15625 -8.34375 C 3.46875 -8.5 3.800781 -8.632812 4.15625 -8.75 C 4.507812 -8.875 4.863281 -8.9375 5.21875 -8.9375 C 5.582031 -8.9375 5.890625 -8.898438 6.140625 -8.828125 C 6.398438 -8.753906 6.609375 -8.648438 6.765625 -8.515625 C 6.929688 -8.378906 7.050781 -8.21875 7.125 -8.03125 C 7.195312 -7.851562 7.234375 -7.660156 7.234375 -7.453125 C 7.234375 -7.253906 7.210938 -7.070312 7.171875 -6.90625 C 7.140625 -6.75 7.078125 -6.585938 6.984375 -6.421875 C 6.890625 -6.265625 6.765625 -6.09375 6.609375 -5.90625 C 6.453125 -5.726562 6.257812 -5.523438 6.03125 -5.296875 C 5.863281 -5.117188 5.726562 -4.972656 5.625 -4.859375 C 5.53125 -4.742188 5.453125 -4.632812 5.390625 -4.53125 C 5.335938 -4.4375 5.300781 -4.335938 5.28125 -4.234375 C 5.269531 -4.128906 5.265625 -4.003906 5.265625 -3.859375 L 5.265625 -3.8125 Z M 5.40625 -2.953125 L 5.40625 -1.6875 L 4.28125 -1.6875 L 4.28125 -2.953125 Z M 8.25 -0.734375 L 8.25 -9.9375 L 2.296875 -9.9375 L 2.296875 -0.734375 Z M 9.046875 -10.65625 L 9.046875 0 L 1.484375 0 L 1.484375 -10.65625 Z M 9.046875 -10.65625 "
+ id="path14" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-1">
+ <path
+ style="stroke:none;"
+ d="M 4.8125 -10.671875 L 4.71875 -10.28125 C 4.488281 -10.257812 4.3125 -10.191406 4.1875 -10.078125 C 4.0625 -9.972656 3.972656 -9.820312 3.921875 -9.625 C 3.867188 -9.425781 3.84375 -9.164062 3.84375 -8.84375 C 3.84375 -8.738281 3.84375 -8.628906 3.84375 -8.515625 L 4.078125 -2.421875 L 4.078125 -1.796875 L 4.15625 -1.796875 L 7.546875 -7.890625 C 7.742188 -8.253906 7.890625 -8.5625 7.984375 -8.8125 C 8.085938 -9.070312 8.140625 -9.300781 8.140625 -9.5 C 8.140625 -9.707031 8.078125 -9.878906 7.953125 -10.015625 C 7.828125 -10.148438 7.628906 -10.238281 7.359375 -10.28125 L 7.4375 -10.671875 L 10.640625 -10.671875 L 10.546875 -10.28125 C 10.410156 -10.257812 10.273438 -10.207031 10.140625 -10.125 C 10.015625 -10.039062 9.875 -9.894531 9.71875 -9.6875 C 9.5625 -9.488281 9.316406 -9.101562 8.984375 -8.53125 L 3.984375 0.125 L 2.9375 0.125 L 2.421875 -8.59375 C 2.398438 -8.925781 2.359375 -9.210938 2.296875 -9.453125 C 2.242188 -9.703125 2.160156 -9.894531 2.046875 -10.03125 C 1.929688 -10.164062 1.742188 -10.25 1.484375 -10.28125 L 1.578125 -10.671875 Z M 4.8125 -10.671875 "
+ id="path17" />
+ </symbol>
+ </g>
+ </defs>
+ <g
+ id="surface381">
+ <path
+ style="fill:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(13.729858%,12.159729%,12.548828%);stroke-opacity:1;stroke-miterlimit:4;"
+ d="M -0.00193125 0.0002 L 5.4551 27.71895 C 5.908225 30.011919 6.646506 30.011919 7.099631 27.71895 L 18.009787 -27.71855 C 18.462912 -30.011519 19.197287 -30.011519 19.650412 -27.71855 L 25.107444 0.0002 "
+ transform="matrix(1,0,0,-1,141.7324,71.0002)"
+ id="path24" />
+ <path
+ style="fill:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(13.729858%,12.159729%,12.548828%);stroke-opacity:1;stroke-miterlimit:4;"
+ d="M 0.000000000000014211 0.0002 L 5.457031 27.71895 C 5.90625 30.011919 6.644531 30.011919 7.097656 27.71895 L 18.007813 -27.71855 C 18.460938 -30.011519 19.199219 -30.011519 19.652344 -27.71855 L 25.105469 0.0002 "
+ transform="matrix(1,0,0,-1,116.625,71.0002)"
+ id="path26" />
+ <path
+ style="fill:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(13.729858%,12.159729%,12.548828%);stroke-opacity:1;stroke-miterlimit:4;"
+ d="M 0.00143125 0.0002 L 5.454556 27.71895 C 5.907681 30.011919 6.645962 30.011919 7.099087 27.71895 L 18.009244 -27.71855 C 18.458462 -30.011519 19.20065 -30.011519 19.649869 -27.71855 L 25.1069 0.0002 "
+ transform="matrix(1,0,0,-1,91.5181,71.0002)"
+ id="path28" />
+ <path
+ style="fill:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(13.729858%,12.159729%,12.548828%);stroke-opacity:1;stroke-miterlimit:4;"
+ d="M 0.00143125 0.0002 L -5.4556 -27.71855 C -5.908725 -30.011519 -6.647006 -30.011519 -7.096225 -27.71855 L -18.010288 27.71895 C -18.459506 30.011919 -19.201694 30.011919 -19.650913 27.71895 L -24.287631 4.172075 C -24.73685 1.8752 -27.022006 0.0002 -29.357944 0.0002 L -66.006381 0.0002 "
+ transform="matrix(1,0,0,-1,91.5181,71.0002)"
+ id="path30" />
+ <path
+ style="fill:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(13.729858%,12.159729%,12.548828%);stroke-opacity:1;stroke-miterlimit:4;"
+ d="M 0.00094375 0.0002 L 5.454069 27.71895 C 5.907194 30.011919 6.645475 30.011919 7.0986 27.71895 L 18.008756 -27.71855 C 18.461881 -30.011519 19.200162 -30.011519 19.649381 -27.71855 L 25.106412 0.0002 "
+ transform="matrix(1,0,0,-1,166.8389,71.0002)"
+ id="path32" />
+ <path
+ style="fill:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(13.729858%,12.159729%,12.548828%);stroke-opacity:1;stroke-miterlimit:4;"
+ d="M -0.0009875 0.0002 L 5.456044 27.71895 C 5.905262 30.011919 6.64745 30.011919 7.096669 27.71895 L 18.010731 -27.71855 C 18.45995 -30.011519 19.198231 -30.011519 19.651356 -27.71855 L 24.284169 -4.171675 C 24.737294 -1.878706 27.018544 0.0002 29.358387 0.0002 L 66.006825 0.0002 "
+ transform="matrix(1,0,0,-1,191.9463,71.0002)"
+ id="path34" />
+ <path
+ style="fill:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(13.729858%,12.159729%,12.548828%);stroke-opacity:1;stroke-miterlimit:4;"
+ d="M -0.0005125 0.00010625 L 5.456519 27.718856 C 5.905738 30.011825 6.647925 30.011825 7.097144 27.718856 L 18.0073 -27.718644 C 18.460425 -30.011613 19.198706 -30.011613 19.651831 -27.718644 L 25.104956 0.00010625 "
+ transform="matrix(1,0,0,-1,119.0552,198.5587)"
+ id="path36" />
+ <path
+ style="fill:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(13.729858%,12.159729%,12.548828%);stroke-opacity:1;stroke-miterlimit:4;"
+ d="M 0.00141875 0.00010625 L 5.454544 27.718856 C 5.907669 30.011825 6.64595 30.011825 7.099075 27.718856 L 18.009231 -27.718644 C 18.45845 -30.011613 19.200637 -30.011613 19.649856 -27.718644 L 25.106887 0.00010625 "
+ transform="matrix(1,0,0,-1,93.9478,198.5587)"
+ id="path38" />
+ <path
+ style="fill:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(13.729858%,12.159729%,12.548828%);stroke-opacity:1;stroke-miterlimit:4;"
+ d="M -0.00095625 0.00010625 L 5.456075 27.718856 C 5.905294 30.011825 6.647481 30.011825 7.0967 27.718856 L 18.010762 -27.718644 C 18.459981 -30.011613 19.198262 -30.011613 19.651387 -27.718644 L 25.104512 0.00010625 "
+ transform="matrix(1,0,0,-1,68.8408,198.5587)"
+ id="path40" />
+ <path
+ style="fill:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(13.729858%,12.159729%,12.548828%);stroke-opacity:1;stroke-miterlimit:4;"
+ d="M -0.00095625 0.00010625 L -5.454081 -27.718644 C -5.907206 -30.011613 -6.645488 -30.011613 -7.098613 -27.718644 L -18.008769 27.718856 C -18.461894 30.011825 -19.200175 30.011825 -19.649394 27.718856 L -24.286113 4.171981 C -24.735331 1.875106 -27.020488 0.00010625 -29.360331 0.00010625 L -43.329081 0.00010625 "
+ transform="matrix(1,0,0,-1,68.8408,198.5587)"
+ id="path42" />
+ <path
+ style="fill:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(13.729858%,12.159729%,12.548828%);stroke-opacity:1;stroke-miterlimit:4;"
+ d="M -0.00094375 0.00010625 L 5.456087 27.718856 C 5.909212 30.011825 6.647494 30.011825 7.100619 27.718856 L 18.010775 -27.718644 C 18.459994 -30.011613 19.198275 -30.011613 19.6514 -27.718644 L 24.284212 -4.171769 C 24.737337 -1.8788 27.022494 0.00010625 29.358431 0.00010625 L 34.768587 0.00010625 "
+ transform="matrix(1,0,0,-1,144.1611,198.5587)"
+ id="path44" />
+ <g
+ style="fill:rgb(13.729858%,12.159729%,12.548828%);fill-opacity:1;"
+ id="g48">
+ <use
+ xlink:href="#glyph0-1"
+ x="133.3359"
+ y="29.6676"
+ id="use46" />
+ </g>
+ <path
+ style="fill:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(13.729858%,12.159729%,12.548828%);stroke-opacity:1;stroke-miterlimit:4;"
+ d="M 0.00093125 0.00110625 L 14.172806 0.00110625 "
+ transform="matrix(1,0,0,-1,174.3311,198.5597)"
+ id="path50" />
+ <path
+ style="fill:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(13.729858%,12.159729%,12.548828%);stroke-opacity:1;stroke-miterlimit:4;"
+ d="M 0.0000375 -0.00184375 L -36.851525 -0.00184375 C -39.968713 -0.00184375 -42.519494 -2.552625 -42.519494 -5.669813 L -42.519494 -51.021375 C -42.519494 -54.142469 -39.968713 -56.69325 -36.851525 -56.69325 L 0.0000375 -56.69325 "
+ transform="matrix(1,0,0,-1,231.0234,170.213)"
+ id="path52" />
+ <path
+ style="fill:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(13.729858%,12.159729%,12.548828%);stroke-opacity:1;stroke-miterlimit:4;"
+ d="M 0.00099375 0.00015625 L 0.00099375 -48.187344 "
+ transform="matrix(1,0,0,-1,215.4326,174.465)"
+ id="path54" />
+ <path
+ style="fill:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(13.729858%,12.159729%,12.548828%);stroke-opacity:1;stroke-miterlimit:4;"
+ d="M 0.001025 0.00110625 L -42.518506 0.00110625 "
+ transform="matrix(1,0,0,-1,257.9521,198.5597)"
+ id="path56" />
+ <path
+ style="fill:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(13.729858%,12.159729%,12.548828%);stroke-opacity:1;stroke-miterlimit:4;"
+ d="M 0.000025 0.0002 L 0.000025 -63.78105 L 14.175806 -63.78105 "
+ transform="matrix(1,0,0,-1,257.9531,71.0002)"
+ id="path58" />
+ <path
+ style="fill:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(13.729858%,12.159729%,12.548828%);stroke-opacity:1;stroke-miterlimit:4;"
+ d="M 0.000025 0.00110625 L 0.000025 70.868294 "
+ transform="matrix(1,0,0,-1,257.9531,198.5597)"
+ id="path60" />
+ <path
+ style="fill:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(13.729858%,12.159729%,12.548828%);stroke-opacity:1;stroke-miterlimit:4;"
+ d="M 0.00001875 0.0002 L 0.00001875 -63.78105 L -14.175763 -63.78105 "
+ transform="matrix(1,0,0,-1,25.5117,71.0002)"
+ id="path62" />
+ <path
+ style="fill:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(13.729858%,12.159729%,12.548828%);stroke-opacity:1;stroke-miterlimit:4;"
+ d="M 0.00001875 0.00110625 L 0.00001875 70.868294 "
+ transform="matrix(1,0,0,-1,25.5117,198.5597)"
+ id="path64" />
+ <g
+ style="fill:rgb(13.729858%,12.159729%,12.548828%);fill-opacity:1;"
+ id="g68">
+ <use
+ xlink:href="#glyph1-1"
+ x="192.3643"
+ y="261.0939"
+ id="use66" />
+ </g>
+ <g
+ style="fill:rgb(13.729858%,12.159729%,12.548828%);fill-opacity:1;"
+ id="g72">
+ <use
+ xlink:href="#glyph0-1"
+ x="89.0786"
+ y="261.4421"
+ id="use70" />
+ </g>
+ <g
+ style="fill:rgb(13.729858%,12.159729%,12.548828%);fill-opacity:1;"
+ id="g76">
+ <use
+ xlink:href="#glyph2-1"
+ x="104.9927"
+ y="265.4421"
+ id="use74" />
+ </g>
+ </g>
+</svg>
diff --git a/doc/dev-doc/manual/figures/dirichlet.svg b/doc/dev-doc/manual/figures/dirichlet.svg
new file mode 100644
index 000000000..3d7782d4c
--- /dev/null
+++ b/doc/dev-doc/manual/figures/dirichlet.svg
@@ -0,0 +1,335 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="635.24872"
+ height="100"
+ id="svg4245"
+ version="1.1"
+ inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
+ sodipodi:docname="dirichlet.svg">
+ <defs
+ id="defs4247">
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2558">
+ <stop
+ style="stop-color:#afafaf;stop-opacity:1"
+ offset="0"
+ id="stop2554" />
+ <stop
+ id="stop2562"
+ offset="0.48881748"
+ style="stop-color:#cdcdcd;stop-opacity:0.98431373" />
+ <stop
+ style="stop-color:#e9e9e9;stop-opacity:0.97254902"
+ offset="1"
+ id="stop2556" />
+ </linearGradient>
+ <pattern
+ inkscape:collect="always"
+ xlink:href="#Strips1_1"
+ id="pattern2347"
+ patternTransform="matrix(0.90828475,0.92298257,-1.6485622,1.62231,94.330095,111.14521)" />
+ <pattern
+ inkscape:stockid="Stripes 1:1"
+ id="Strips1_1"
+ patternTransform="translate(0,0) scale(10,10)"
+ height="1"
+ width="2"
+ patternUnits="userSpaceOnUse"
+ inkscape:collect="always">
+ <rect
+ id="rect1629"
+ height="2"
+ width="1"
+ y="-0.5"
+ x="0"
+ style="fill:black;stroke:none" />
+ </pattern>
+ <marker
+ inkscape:stockid="TriangleOutL"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="TriangleOutL"
+ style="overflow:visible">
+ <path
+ id="path5011"
+ d="M 5.77,0 -2.88,5 V -5 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;marker-start:none"
+ transform="scale(0.8)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="TriangleOutM"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="TriangleOutM"
+ style="overflow:visible">
+ <path
+ id="path5014"
+ d="M 5.77,0 -2.88,5 V -5 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;marker-start:none"
+ transform="scale(0.4)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend"
+ style="overflow:visible">
+ <path
+ id="path4889"
+ style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend"
+ style="overflow:visible">
+ <path
+ id="path4877"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;marker-start:none"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Mend"
+ style="overflow:visible">
+ <path
+ id="path4895"
+ style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="scale(-0.6)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lend"
+ style="overflow:visible">
+ <path
+ id="path4871"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;marker-start:none"
+ transform="matrix(-0.8,0,0,-0.8,-10,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <pattern
+ inkscape:collect="always"
+ xlink:href="#Strips1_1-7"
+ id="pattern2347-2"
+ patternTransform="matrix(0.90828475,0.92298257,-1.6485622,1.62231,183.20176,207.07339)" />
+ <pattern
+ inkscape:stockid="Stripes 1:1"
+ id="Strips1_1-7"
+ patternTransform="translate(0,0) scale(10,10)"
+ height="1"
+ width="2"
+ patternUnits="userSpaceOnUse"
+ inkscape:collect="always">
+ <rect
+ id="rect1629-0"
+ height="2"
+ width="1"
+ y="-0.5"
+ x="0"
+ style="fill:black;stroke:none" />
+ </pattern>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2558"
+ id="linearGradient2560"
+ x1="320"
+ y1="151.86218"
+ x2="320"
+ y2="52.862183"
+ gradientUnits="userSpaceOnUse" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.2885298"
+ inkscape:cx="345.63817"
+ inkscape:cy="30.822573"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1920"
+ inkscape:window-height="1152"
+ inkscape:window-x="0"
+ inkscape:window-y="24"
+ inkscape:window-maximized="1"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:snap-smooth-nodes="true"
+ inkscape:snap-object-midpoints="true"
+ inkscape:object-paths="true" />
+ <metadata
+ id="metadata4250">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-2.3756318,-52.362183)">
+ <rect
+ style="fill:url(#linearGradient2560);fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="rect4253"
+ width="599"
+ height="99"
+ x="20.5"
+ y="52.862183" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:0.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.2, 2.4;stroke-dashoffset:0;stroke-opacity:1"
+ d="m 20.5,102.36218 h 599"
+ id="path6455"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <g
+ id="g2511">
+ <rect
+ y="131.93352"
+ x="3.4269824"
+ height="7.9050622"
+ width="34.146034"
+ id="rect911-5"
+ style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.11845864;stroke-miterlimit:4;stroke-dasharray:0.11845865, 0.11845865;stroke-dashoffset:0;stroke-opacity:1" />
+ <rect
+ y="131.93352"
+ x="3.4269879"
+ height="7.9050622"
+ width="34.146034"
+ id="rect911"
+ style="fill:url(#pattern2347);fill-opacity:1;stroke:none;stroke-width:0.11845864;stroke-miterlimit:4;stroke-dasharray:0.11845865, 0.11845865;stroke-dashoffset:0;stroke-opacity:1" />
+ <path
+ inkscape:transform-center-x="3.1575168e-06"
+ inkscape:transform-center-y="-4.9285578"
+ d="m 37.573019,131.93351 -34.1460377,0 L 20.5,102.36218 Z"
+ inkscape:randomized="0"
+ inkscape:rounded="0"
+ inkscape:flatsided="true"
+ sodipodi:arg2="1.5707963"
+ sodipodi:arg1="0.52359878"
+ sodipodi:r2="9.8571119"
+ sodipodi:r1="19.714224"
+ sodipodi:cy="122.0764"
+ sodipodi:cx="20.5"
+ sodipodi:sides="3"
+ id="path2355"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.21399379;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="star" />
+ <circle
+ r="2.1285522"
+ cy="102.36218"
+ cx="20.5"
+ id="path2421"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.68048555;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </g>
+ <g
+ id="g2533"
+ transform="translate(458.40634,-86.563398)">
+ <g
+ transform="translate(51.721594,-1.3829021)"
+ id="g2518">
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.11845864;stroke-miterlimit:4;stroke-dasharray:0.11845865, 0.11845865;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-5-9"
+ width="34.146034"
+ height="7.9050622"
+ x="92.298653"
+ y="227.86171" />
+ <rect
+ style="fill:url(#pattern2347-2);fill-opacity:1;stroke:none;stroke-width:0.11845864;stroke-miterlimit:4;stroke-dasharray:0.11845865, 0.11845865;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-3"
+ width="34.146034"
+ height="7.9050622"
+ x="92.29866"
+ y="227.86171" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 91.248047,228.61171 H 127.49609"
+ id="path2466"
+ inkscape:connector-curvature="0" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.86301315;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468"
+ cx="97.500069"
+ cy="224.53729"
+ r="4.0744085" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.86301315;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-6"
+ cx="121.24213"
+ cy="224.53729"
+ r="4.0744085" />
+ </g>
+ <g
+ transform="translate(0.40481977,-0.58306837)"
+ id="g2522">
+ <path
+ sodipodi:type="star"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.21399379;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2355-2"
+ sodipodi:sides="3"
+ sodipodi:cx="160.68884"
+ sodipodi:cy="209.22287"
+ sodipodi:r1="19.714224"
+ sodipodi:r2="9.8571119"
+ sodipodi:arg1="0.52359878"
+ sodipodi:arg2="1.5707963"
+ inkscape:flatsided="true"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ d="m 177.76186,219.07998 -34.14604,0 17.07302,-29.57133 z"
+ inkscape:transform-center-y="-4.9285578"
+ inkscape:transform-center-x="3.1575168e-06" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.68048555;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2421-6"
+ cx="160.68883"
+ cy="189.50867"
+ r="2.1285522" />
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/doc/dev-doc/manual/figures/dynamic_analysis.png b/doc/dev-doc/manual/figures/dynamic_analysis.png
new file mode 100644
index 000000000..0e058f70d
Binary files /dev/null and b/doc/dev-doc/manual/figures/dynamic_analysis.png differ
diff --git a/doc/dev-doc/manual/figures/elements/bernoulli_2.svg b/doc/dev-doc/manual/figures/elements/bernoulli_2.svg
new file mode 100644
index 000000000..9e96665f2
--- /dev/null
+++ b/doc/dev-doc/manual/figures/elements/bernoulli_2.svg
@@ -0,0 +1,878 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="350"
+ height="100"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.47 r22583"
+ sodipodi:docname="bernouille_2.svg">
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="Arrow1Send"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1Send"
+ style="overflow:visible;">
+ <path
+ id="path4238"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;"
+ transform="scale(0.2) rotate(180) translate(6,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Sstart"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1Sstart"
+ style="overflow:visible">
+ <path
+ id="path4235"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
+ transform="scale(0.2) translate(6,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mstart"
+ style="overflow:visible">
+ <path
+ id="path3617"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(0.4,0,0,0.4,4,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend"
+ style="overflow:visible">
+ <path
+ id="path3620"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lend"
+ style="overflow:visible">
+ <path
+ id="path3614"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(-0.8,0,0,-0.8,-10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lstart"
+ style="overflow:visible">
+ <path
+ id="path3611"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(0.8,0,0,0.8,10,0)" />
+ </marker>
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ id="perspective10" />
+ <inkscape:perspective
+ id="perspective4996"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective5434"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective5456"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective5480"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective5505"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective5537"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective5559"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective5583"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective6607"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective7066"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <linearGradient
+ id="linearGradient5079">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="0"
+ id="stop5081" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0.39249146;"
+ offset="1"
+ id="stop5083" />
+ </linearGradient>
+ <inkscape:perspective
+ id="perspective7190"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <linearGradient
+ id="linearGradient5079-4">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="0"
+ id="stop5081-7" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0.39249146;"
+ offset="1"
+ id="stop5083-3" />
+ </linearGradient>
+ <inkscape:perspective
+ id="perspective7190-5"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <linearGradient
+ id="linearGradient5079-9">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="0"
+ id="stop5081-6" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0.39249146;"
+ offset="1"
+ id="stop5083-9" />
+ </linearGradient>
+ <inkscape:perspective
+ id="perspective7190-58"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <linearGradient
+ id="linearGradient5079-5">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="0"
+ id="stop5081-5" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0.39249146;"
+ offset="1"
+ id="stop5083-0" />
+ </linearGradient>
+ <inkscape:perspective
+ id="perspective7190-9"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <linearGradient
+ id="linearGradient5079-53">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="0"
+ id="stop5081-4" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0.39249146;"
+ offset="1"
+ id="stop5083-2" />
+ </linearGradient>
+ <inkscape:perspective
+ id="perspective7190-6"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <linearGradient
+ id="linearGradient5079-1">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="0"
+ id="stop5081-77" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0.39249146;"
+ offset="1"
+ id="stop5083-6" />
+ </linearGradient>
+ <inkscape:perspective
+ id="perspective7190-3"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <linearGradient
+ id="linearGradient5079-90">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="0"
+ id="stop5081-58" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0.39249146;"
+ offset="1"
+ id="stop5083-91" />
+ </linearGradient>
+ <inkscape:perspective
+ id="perspective7190-95"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <linearGradient
+ id="linearGradient5079-0">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="0"
+ id="stop5081-1" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0.39249146;"
+ offset="1"
+ id="stop5083-5" />
+ </linearGradient>
+ <inkscape:perspective
+ id="perspective9892"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <linearGradient
+ id="linearGradient5079-12">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="0"
+ id="stop5081-0" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0.39249146;"
+ offset="1"
+ id="stop5083-7" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient9863"
+ id="linearGradient9882"
+ gradientUnits="userSpaceOnUse"
+ x1="93.202484"
+ y1="-428.55365"
+ x2="97"
+ y2="-451.54465" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient9863">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop9865" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop9867" />
+ </linearGradient>
+ <inkscape:perspective
+ id="perspective9892-2"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <linearGradient
+ id="linearGradient5079-12-7">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="0"
+ id="stop5081-0-9" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0.39249146;"
+ offset="1"
+ id="stop5083-7-8" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient9863-4"
+ id="linearGradient9882-2"
+ gradientUnits="userSpaceOnUse"
+ x1="93.202484"
+ y1="-428.55365"
+ x2="97"
+ y2="-451.54465" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient9863-4">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop9865-4" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop9867-9" />
+ </linearGradient>
+ <inkscape:perspective
+ id="perspective9892-0"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <linearGradient
+ id="linearGradient5079-12-3">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="0"
+ id="stop5081-0-94" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0.39249146;"
+ offset="1"
+ id="stop5083-7-4" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient9863-46"
+ id="linearGradient9882-1"
+ gradientUnits="userSpaceOnUse"
+ x1="93.202484"
+ y1="-428.55365"
+ x2="97"
+ y2="-451.54465" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient9863-46">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop9865-7" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop9867-8" />
+ </linearGradient>
+ <inkscape:perspective
+ id="perspective9892-22"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <linearGradient
+ id="linearGradient5079-12-5">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="0"
+ id="stop5081-0-3" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0.39249146;"
+ offset="1"
+ id="stop5083-7-9" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient9863-2"
+ id="linearGradient9882-19"
+ gradientUnits="userSpaceOnUse"
+ x1="93.202484"
+ y1="-428.55365"
+ x2="97"
+ y2="-451.54465" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient9863-2">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop9865-9" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop9867-90" />
+ </linearGradient>
+ <inkscape:perspective
+ id="perspective9892-1"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <linearGradient
+ id="linearGradient5079-12-2">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="0"
+ id="stop5081-0-4" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0.39249146;"
+ offset="1"
+ id="stop5083-7-1" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient9863-8"
+ id="linearGradient9882-4"
+ gradientUnits="userSpaceOnUse"
+ x1="93.202484"
+ y1="-428.55365"
+ x2="97"
+ y2="-451.54465" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient9863-8">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop9865-74" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop9867-6" />
+ </linearGradient>
+ <inkscape:perspective
+ id="perspective9892-6"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <linearGradient
+ id="linearGradient5079-12-25">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="0"
+ id="stop5081-0-1" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0.39249146;"
+ offset="1"
+ id="stop5083-7-0" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient9863-1"
+ id="linearGradient9882-9"
+ gradientUnits="userSpaceOnUse"
+ x1="93.202484"
+ y1="-428.55365"
+ x2="97"
+ y2="-451.54465" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient9863-1">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop9865-1" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop9867-89" />
+ </linearGradient>
+ <inkscape:perspective
+ id="perspective9892-9"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <linearGradient
+ id="linearGradient5079-12-8">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="0"
+ id="stop5081-0-6" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0.39249146;"
+ offset="1"
+ id="stop5083-7-5" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient9863-9"
+ id="linearGradient9882-5"
+ gradientUnits="userSpaceOnUse"
+ x1="93.202484"
+ y1="-428.55365"
+ x2="97"
+ y2="-451.54465" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient9863-9">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop9865-8" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop9867-0" />
+ </linearGradient>
+ <inkscape:perspective
+ id="perspective9892-93"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <linearGradient
+ id="linearGradient5079-12-50">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="0"
+ id="stop5081-0-91" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0.39249146;"
+ offset="1"
+ id="stop5083-7-7" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient9863-42"
+ id="linearGradient9882-48"
+ gradientUnits="userSpaceOnUse"
+ x1="93.202484"
+ y1="-428.55365"
+ x2="97"
+ y2="-451.54465" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient9863-42">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop9865-84" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop9867-5" />
+ </linearGradient>
+ <inkscape:perspective
+ id="perspective4665"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective5774"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective4425"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective4425-1"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective4425-9"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective4425-2"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective4647"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective2953"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective2953-6"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective2953-1"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective2959"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective2978"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective3986"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective4008"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-3"
+ style="overflow:visible">
+ <path
+ id="path3620-1"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)" />
+ </marker>
+ <inkscape:perspective
+ id="perspective5856"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective5878"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="3.4828571"
+ inkscape:cx="175"
+ inkscape:cy="50"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1680"
+ inkscape:window-height="976"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ inkscape:window-maximized="1" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-200.4496,-283.7173)">
+ <path
+ style="fill:#000000"
+ d="m 491.42359,353.51508 c -0.14567,-0.12144 -0.46255,-0.35559 -0.70417,-0.52034 -0.6065,-0.41353 -0.54646,-0.48494 0.38654,-0.45965 0.59214,0.0161 0.82719,-0.009 1.36859,-0.14834 0.84511,-0.21684 1.65479,-0.61164 2.11271,-1.03015 0.60705,-0.55482 0.73332,-1.01725 0.38441,-1.40795 -0.19048,-0.21331 -0.20218,-0.21656 -0.86012,-0.23898 -0.41772,-0.0142 -1.15604,0.0342 -1.97545,0.12969 -1.72274,0.2007 -3.00722,0.20258 -3.80656,0.006 -1.4445,-0.35603 -2.38,-1.16258 -2.74178,-2.36386 -0.0873,-0.28987 -0.11831,-0.6032 -0.11464,-1.15878 0.004,-0.65209 0.0319,-0.8414 0.19222,-1.31777 0.63798,-1.896 2.49674,-3.32955 5.72514,-4.41544 1.04619,-0.3519 1.06057,-0.40719 0.19446,-0.74786 -1.24105,-0.48815 -1.95356,-1.03901 -2.29992,-1.77814 -0.16334,-0.34856 -0.17898,-0.45141 -0.17898,-1.17654 0,-0.73127 0.0147,-0.82605 0.18484,-1.18933 0.47994,-1.02496 1.50773,-1.86589 2.87993,-2.35633 0.22887,-0.0818 0.40525,-0.18161 0.39194,-0.22179 -0.0155,-0.0469 -0.40136,-0.0872 -1.07828,-0.11262 -1.36104,-0.051 -1.86822,-0.1904 -2.06964,-0.5686 -0.1143,-0.21459 -0.0957,-0.31967 0.0973,-0.54905 0.14229,-0.16911 0.84713,-0.59752 0.98305,-0.59752 0.0195,0 0.0831,0.11881 0.14117,0.26402 0.14286,0.35704 0.47838,0.63546 0.93648,0.77711 0.53731,0.16615 2.40056,0.16341 3.1779,-0.005 0.6145,-0.13287 1.64619,-0.4708 2.21906,-0.72685 l 0.38141,-0.17048 0.10276,0.15684 c 0.0956,0.14583 0.0896,0.18855 -0.0852,0.60879 -0.20985,0.50466 -0.25339,0.52945 -0.95592,0.54416 -0.54097,0.0113 -1.79023,0.23714 -2.44135,0.44129 -0.90199,0.28282 -1.5559,0.65395 -2.08894,1.18558 -0.6643,0.66256 -0.95325,1.33558 -0.95486,2.22412 -0.004,1.87291 1.29826,3.03044 3.7266,3.31393 0.37178,0.0434 0.70343,0.10372 0.737,0.13403 0.0354,0.032 -0.0887,0.21338 -0.29569,0.43228 l -0.35672,0.37718 -1.06173,0.0121 c -1.14868,0.0131 -1.78631,0.13448 -2.78823,0.53083 -1.81475,0.71791 -3.07162,2.10825 -3.38058,3.73957 -0.32978,1.74133 0.37754,2.82625 2.05041,3.14498 0.74073,0.14113 2.30851,0.12498 3.90097,-0.0402 1.96211,-0.20349 2.54537,-0.14369 2.9631,0.30382 0.14239,0.15256 0.16549,0.23908 0.16518,0.61879 -2.7e-4,0.35093 -0.0392,0.52063 -0.18962,0.82761 -0.41934,0.85563 -1.42156,1.85338 -2.72352,2.71136 -0.5488,0.36166 -1.81673,1.04269 -1.93561,1.03967 -0.028,-7.1e-4 -0.17005,-0.10065 -0.31573,-0.22209 z"
+ id="path5002-5" />
+ <rect
+ style="fill:#b4b4b4;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="rect4413"
+ width="168.72569"
+ height="7.0822434"
+ x="266.08752"
+ y="338.22803" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:none;marker-end:url(#Arrow1Mend)"
+ d="m 349.74527,341.79533 127.66991,0"
+ id="path4031"
+ sodipodi:nodetypes="cc" />
+ <path
+ transform="matrix(1.0652537,0,0,1.0652537,298.41021,174.47413)"
+ sodipodi:type="arc"
+ style="fill:#808080;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.40811527;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="path4415-8-2-2"
+ sodipodi:cx="48.300972"
+ sodipodi:cy="157.52428"
+ sodipodi:rx="6.5533981"
+ sodipodi:ry="6.5533981"
+ d="m 54.85437,157.52428 a 6.5533981,6.5533981 0 1 1 -13.106796,0 6.5533981,6.5533981 0 1 1 13.106796,0 z" />
+ <path
+ transform="matrix(1.0652537,0,0,1.0652537,380.41462,174.22413)"
+ sodipodi:type="arc"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.87748706;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="path4415-2"
+ sodipodi:cx="48.300972"
+ sodipodi:cy="157.52428"
+ sodipodi:rx="6.5533981"
+ sodipodi:ry="6.5533981"
+ d="m 54.85437,157.52428 a 6.5533981,6.5533981 0 1 1 -13.106796,0 6.5533981,6.5533981 0 1 1 13.106796,0 z" />
+ <path
+ transform="matrix(1.0652537,0,0,1.0652537,216.90583,174.22413)"
+ sodipodi:type="arc"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.87748706;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="path4415-64"
+ sodipodi:cx="48.300972"
+ sodipodi:cy="157.52428"
+ sodipodi:rx="6.5533981"
+ sodipodi:ry="6.5533981"
+ d="m 54.85437,157.52428 a 6.5533981,6.5533981 0 1 1 -13.106796,0 6.5533981,6.5533981 0 1 1 13.106796,0 z" />
+ <path
+ style="fill:#000000"
+ d="m 263.78265,375.3333 c -5e-5,-0.37195 0.0238,-0.384 0.76106,-0.384 0.41862,0 1.08069,-0.0685 1.47125,-0.15229 1.24885,-0.26784 1.18252,0.16752 1.2311,-8.07986 0.0233,-3.95865 0.005,-7.40177 -0.0412,-7.65136 -0.046,-0.2496 -0.16586,-0.45382 -0.26636,-0.45382 -0.1005,0 -0.94394,0.3456 -1.8743,0.76799 -0.93036,0.4224 -1.78783,0.768 -1.90547,0.768 -0.13314,0 -0.21391,-0.12749 -0.21391,-0.33767 0,-0.18571 -0.034,-0.42642 -0.0756,-0.53491 -0.0554,-0.14441 0.30927,-0.3398 1.36144,-0.7294 0.79042,-0.29269 2.22599,-0.88865 3.19015,-1.32437 0.96417,-0.43571 1.82816,-0.76716 1.91999,-0.73655 0.12715,0.0424 0.16696,2.13215 0.16696,8.76344 l 0,8.70778 0.31419,0.36418 c 0.35409,0.41046 1.08117,0.62058 2.16434,0.62551 0.70583,0.003 0.73308,0.0176 0.73311,0.38733 l 10e-6,0.384 -4.46829,0 -4.46829,0 -6e-5,-0.384 z"
+ id="path6625-6" />
+ <path
+ style="fill:#000000"
+ d="m 425.52249,375.39459 c -4e-5,-0.22692 0.80419,-1.18072 2.70799,-3.21162 5.50321,-5.87061 6.49492,-7.44405 6.33882,-10.05714 -0.1288,-2.15597 -1.35252,-3.40386 -3.5394,-3.60933 -1.9629,-0.18443 -3.158,0.63125 -3.71907,2.53833 -0.29235,0.99371 -0.32944,1.04809 -0.71482,1.04809 -0.38172,0 -0.40166,-0.0276 -0.3274,-0.45382 0.0435,-0.24959 0.15466,-1.09953 0.24704,-1.88874 0.16555,-1.41427 0.17597,-1.44217 0.72262,-1.93602 2.14614,-1.93892 6.51265,-1.87537 8.5119,0.12388 1.50729,1.50728 1.78564,4.24499 0.6598,6.48933 -0.56362,1.12353 -1.55548,2.32694 -4.56064,5.53326 -1.42854,1.52417 -2.75846,2.9627 -2.9554,3.19675 l -0.35806,0.42553 3.90415,-0.0932 c 2.14728,-0.0512 4.29688,-0.13041 4.77688,-0.17594 l 0.87272,-0.0828 10e-6,1.23807 2e-5,1.23808 -6.28355,0 -6.28355,0 -6e-5,-0.32277 z"
+ id="path6623-9" />
+ <path
+ transform="matrix(1.0652537,0,0,1.0652537,243.78302,174.47413)"
+ sodipodi:type="arc"
+ style="fill:#808080;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.40811527;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="path4415-8"
+ sodipodi:cx="48.300972"
+ sodipodi:cy="157.52428"
+ sodipodi:rx="6.5533981"
+ sodipodi:ry="6.5533981"
+ d="m 54.85437,157.52428 a 6.5533981,6.5533981 0 1 1 -13.106796,0 6.5533981,6.5533981 0 1 1 13.106796,0 z" />
+ <path
+ transform="matrix(1.0652537,0,0,1.0652537,353.53742,174.47413)"
+ sodipodi:type="arc"
+ style="fill:#808080;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.40811527;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="path4415-8-2"
+ sodipodi:cx="48.300972"
+ sodipodi:cy="157.52428"
+ sodipodi:rx="6.5533981"
+ sodipodi:ry="6.5533981"
+ d="m 54.85437,157.52428 a 6.5533981,6.5533981 0 1 1 -13.106796,0 6.5533981,6.5533981 0 1 1 13.106796,0 z" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow1Sstart);marker-mid:url(#Arrow1Lstart);marker-end:url(#Arrow1Send)"
+ d="m 272.91208,315.69788 154.43039,0"
+ id="path4031-5"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ d="m 268.35862,306.39691 0,17.96117"
+ id="path5846" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ d="m 431.86741,306.39691 0,17.96117"
+ id="path5846-8" />
+ <path
+ style="fill:#000000"
+ d="m 351.94856,308.69236 c -0.4997,-0.50252 -0.6319,-1.62999 -0.38871,-3.31483 0.25344,-1.75571 0.86049,-3.78735 1.623,-5.43173 0.49802,-1.07399 1.14565,-1.84451 2.06619,-2.45823 0.58928,-0.39288 1.57489,-0.87552 2.0171,-0.98777 0.80154,-0.20345 1.65249,-0.15649 2.65123,0.1463 l 0.56887,0.17246 0.421,-0.22493 0.42098,-0.22494 0.12457,0.11522 0.12455,0.11522 -1.20059,4.92639 c -0.87291,3.58178 -1.1941,4.98236 -1.17678,5.13148 0.0292,0.25132 0.18353,0.47005 0.34889,0.49437 0.29895,0.044 0.80895,-0.2333 1.51021,-0.82108 0.21251,-0.17812 0.40401,-0.32386 0.42556,-0.32386 0.0216,0 0.0701,0.11028 0.10783,0.24504 l 0.0685,0.24505 -0.20592,0.17836 c -0.44379,0.38441 -1.43937,1.13916 -1.9878,1.50695 -0.7505,0.5033 -1.12508,0.67433 -1.48499,0.678 -0.24186,0.002 -0.29633,-0.0166 -0.40372,-0.14135 -0.23062,-0.26792 -0.28669,-0.73171 -0.17897,-1.48045 0.029,-0.20178 0.20414,-1.0272 0.38915,-1.8343 0.48444,-2.11334 0.44942,-1.93874 0.37204,-1.85469 -0.0368,0.0401 -0.35211,0.45191 -0.70051,0.91526 -1.74174,2.31635 -3.47656,3.90521 -4.71272,4.31623 -0.46301,0.15396 -0.56999,0.14215 -0.79901,-0.0881 z m 2.27789,-1.68195 c 0.62569,-0.29633 1.53073,-1.14108 2.42324,-2.26177 1.44199,-1.81072 1.98941,-3.00574 2.60941,-5.69637 0.10761,-0.46672 0.18317,-0.88082 0.16805,-0.92024 -0.0278,-0.0724 -0.87936,-0.35085 -1.39056,-0.45458 -0.38844,-0.0788 -1.18938,-0.0691 -1.518,0.0184 -0.94978,0.25283 -1.50167,1.0814 -2.25589,3.38683 -0.62568,1.91252 -1.0058,3.79929 -1.0058,4.99237 0,0.55109 0.0534,0.78708 0.21751,0.96181 0.15453,0.16448 0.3646,0.15712 0.75204,-0.0264 z m -15.22464,1.34233 0,-0.34154 2.28785,-2.43051 c 3.15531,-3.35207 3.51128,-3.75083 4.30512,-4.82272 1.19253,-1.61021 1.68756,-2.85214 1.68756,-4.23369 0,-1.27134 -0.50222,-2.31175 -1.39871,-2.89752 -0.67144,-0.43873 -1.31368,-0.61761 -2.21747,-0.61761 -1.33121,0 -2.2345,0.44695 -2.7098,1.34083 -0.0924,0.17376 -0.27066,0.66835 -0.39617,1.09911 l -0.22819,0.78318 -0.36721,0.0152 -0.36722,0.0152 0.2166,-1.73385 0.2166,-1.73384 0.27977,-0.24667 c 0.77663,-0.68476 1.48401,-1.07868 2.33366,-1.29955 0.6368,-0.16554 1.96787,-0.23034 2.70721,-0.13179 1.90676,0.25418 3.23308,1.17916 3.8614,2.69298 0.26075,0.62823 0.34168,1.11338 0.33846,2.02925 -0.002,0.69702 -0.0218,0.88454 -0.13652,1.32099 -0.29643,1.12795 -0.99959,2.26374 -2.31136,3.73343 -0.33368,0.37385 -3.72038,3.9828 -5.13551,5.47253 l -0.26704,0.28111 1.8605,-0.0349 c 1.74064,-0.0327 3.59611,-0.0848 5.94836,-0.167 l 0.99576,-0.0348 0,1.14071 0,1.14073 -0.64943,-0.0371 c -0.82758,-0.0472 -8.87284,-0.0445 -10.00258,0.003 l -0.85164,0.0361 0,-0.34154 z"
+ id="path5884" />
+ </g>
+</svg>
diff --git a/doc/dev-doc/manual/figures/elements/cohesive_2d_6.svg b/doc/dev-doc/manual/figures/elements/cohesive_2d_6.svg
new file mode 100644
index 000000000..7bf0eca89
--- /dev/null
+++ b/doc/dev-doc/manual/figures/elements/cohesive_2d_6.svg
@@ -0,0 +1,192 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ id="svg2"
+ xml:space="preserve"
+ width="237.168"
+ height="94.254669"
+ viewBox="0 0 237.168 94.254669"
+ sodipodi:docname="cohesive_2d_6.svg"
+ inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"><metadata
+ id="metadata8"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
+ id="defs6" /><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="780"
+ inkscape:window-height="480"
+ id="namedview4"
+ showgrid="false"
+ inkscape:zoom="5.4265331"
+ inkscape:cx="118.584"
+ inkscape:cy="47.127335"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="g10" /><g
+ id="g10"
+ inkscape:groupmode="layer"
+ inkscape:label="cohesive2d"
+ transform="matrix(1.3333333,0,0,-1.3333333,-158.98667,944.14933)"><g
+ id="g12"><path
+ d="m 174.476,701.342 68.319,-0.236"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#949494;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:4;stroke-dashoffset:0;stroke-opacity:1"
+ id="path14" /></g><g
+ id="g16"><path
+ d="m 183.391,670.608 50.056,-0.064"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#949494;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:4;stroke-dashoffset:0;stroke-opacity:1"
+ id="path18" /></g><g
+ id="g20"><path
+ d="m 192,640 h 32"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#949494;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:4;stroke-dashoffset:0;stroke-opacity:1"
+ id="path22" /></g><g
+ id="g24"><path
+ d="m 224.053,639.921 70.563,11.865 -51.83,49.332"
+ style="fill:none;stroke:#000000;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
+ id="path26" /></g><g
+ id="g28"><path
+ d="m 192.089,639.947 -70.349,13.077 52.671,48.433"
+ style="fill:none;stroke:#000000;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
+ id="path30" /></g><g
+ id="g32"><path
+ d="m 174.411,701.457 17.678,-61.51"
+ style="fill:none;stroke:#2e8b57;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
+ id="path34" /></g><g
+ id="g36"><path
+ d="m 224.053,639.921 18.733,61.197"
+ style="fill:none;stroke:#2e8b57;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
+ id="path38" /></g><g
+ id="g40"><g
+ id="g42"><path
+ d="m 123.54,653.024 c 0,0.994 -0.806,1.8 -1.8,1.8 -0.994,0 -1.8,-0.806 -1.8,-1.8 0,-0.995 0.806,-1.8 1.8,-1.8 0.994,0 1.8,0.805 1.8,1.8"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ id="path44" /></g></g><g
+ id="g46"><g
+ id="g48"><path
+ d="m 158.715,646.485 c 0,0.994 -0.806,1.8 -1.8,1.8 -0.994,0 -1.8,-0.806 -1.8,-1.8 0,-0.994 0.806,-1.8 1.8,-1.8 0.994,0 1.8,0.806 1.8,1.8"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ id="path50" /></g></g><g
+ id="g52"><g
+ id="g54"><path
+ d="m 149.876,677.24 c 0,0.994 -0.806,1.8 -1.8,1.8 -0.994,0 -1.8,-0.806 -1.8,-1.8 0,-0.994 0.806,-1.8 1.8,-1.8 0.994,0 1.8,0.806 1.8,1.8"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ id="path56" /></g></g><g
+ id="g58"><g
+ id="g60"><path
+ d="m 270.501,676.452 c 0,0.994 -0.806,1.8 -1.8,1.8 -0.994,0 -1.8,-0.806 -1.8,-1.8 0,-0.994 0.806,-1.8 1.8,-1.8 0.994,0 1.8,0.806 1.8,1.8"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ id="path62" /></g></g><g
+ id="g64"><g
+ id="g66"><path
+ d="m 261.135,645.853 c 0,0.995 -0.806,1.8 -1.8,1.8 -0.995,0 -1.8,-0.805 -1.8,-1.8 0,-0.994 0.805,-1.8 1.8,-1.8 0.994,0 1.8,0.806 1.8,1.8"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ id="path68" /></g></g><g
+ id="g70"><g
+ id="g72"><path
+ d="m 296.416,651.786 c 0,0.994 -0.806,1.8 -1.8,1.8 -0.994,0 -1.8,-0.806 -1.8,-1.8 0,-0.994 0.806,-1.8 1.8,-1.8 0.994,0 1.8,0.806 1.8,1.8"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ id="path74" /></g></g><g
+ id="g76"><g
+ id="g78"><path
+ d="m 176.211,701.457 c 0,0.994 -0.806,1.8 -1.8,1.8 -0.994,0 -1.8,-0.806 -1.8,-1.8 0,-0.994 0.806,-1.8 1.8,-1.8 0.994,0 1.8,0.806 1.8,1.8"
+ style="fill:#2e8b57;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ id="path80" /></g></g><g
+ id="g82"><g
+ id="g84"><path
+ d="m 185.05,670.702 c 0,0.994 -0.806,1.8 -1.8,1.8 -0.994,0 -1.8,-0.806 -1.8,-1.8 0,-0.994 0.806,-1.8 1.8,-1.8 0.994,0 1.8,0.806 1.8,1.8"
+ style="fill:#2e8b57;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ id="path86" /></g></g><g
+ id="g88"><g
+ id="g90"><path
+ d="m 193.889,639.947 c 0,0.994 -0.806,1.8 -1.8,1.8 -0.994,0 -1.8,-0.806 -1.8,-1.8 0,-0.994 0.806,-1.8 1.8,-1.8 0.994,0 1.8,0.806 1.8,1.8"
+ style="fill:#2e8b57;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ id="path92" /></g></g><g
+ id="g94"><g
+ id="g96"><path
+ d="m 225.853,639.921 c 0,0.994 -0.806,1.8 -1.8,1.8 -0.994,0 -1.8,-0.806 -1.8,-1.8 0,-0.994 0.806,-1.8 1.8,-1.8 0.994,0 1.8,0.806 1.8,1.8"
+ style="fill:#2e8b57;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ id="path98" /></g></g><g
+ id="g100"><g
+ id="g102"><path
+ d="m 235.219,670.519 c 0,0.994 -0.806,1.8 -1.8,1.8 -0.994,0 -1.8,-0.806 -1.8,-1.8 0,-0.994 0.806,-1.8 1.8,-1.8 0.994,0 1.8,0.806 1.8,1.8"
+ style="fill:#2e8b57;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ id="path104" /></g></g><g
+ id="g106"><g
+ id="g108"><path
+ d="m 244.586,701.118 c 0,0.994 -0.806,1.8 -1.8,1.8 -0.994,0 -1.8,-0.806 -1.8,-1.8 0,-0.994 0.806,-1.8 1.8,-1.8 0.994,0 1.8,0.806 1.8,1.8"
+ style="fill:#2e8b57;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ id="path110" /></g></g><g
+ id="g112"
+ transform="translate(244.798,703.568)"><g
+ id="g114"
+ transform="translate(0,-788.493)"><text
+ transform="matrix(1,0,0,-1,0,788.523)"
+ style="font-variant:normal;font-size:4.9813px;font-family:'URW Gothic';-inkscape-font-specification:URWPalladioL-Roma;writing-mode:lr-tb;fill:#2e8b57;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="text118"><tspan
+ x="0"
+ y="0"
+ id="tspan116">1</tspan></text></g></g><g
+ id="g120"
+ transform="translate(229.997,672.848)"><g
+ id="g122"
+ transform="translate(0,-788.518)"><text
+ transform="matrix(1,0,0,-1,0,788.598)"
+ style="font-variant:normal;font-size:4.9813px;font-family:'URW Gothic';-inkscape-font-specification:URWPalladioL-Roma;writing-mode:lr-tb;fill:#2e8b57;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="text126"><tspan
+ x="0"
+ y="0"
+ id="tspan124">3</tspan></text></g></g><g
+ id="g128"
+ transform="translate(221.274,642.228)"><g
+ id="g130"
+ transform="translate(0,-788.568)"><text
+ transform="matrix(1,0,0,-1,0,788.598)"
+ style="font-variant:normal;font-size:4.9813px;font-family:'URW Gothic';-inkscape-font-specification:URWPalladioL-Roma;writing-mode:lr-tb;fill:#2e8b57;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="text134"><tspan
+ x="0"
+ y="0"
+ id="tspan132">2</tspan></text></g></g><g
+ id="g136"
+ transform="translate(174.75,704.605)"><g
+ id="g138"
+ transform="translate(0,-788.493)"><text
+ transform="matrix(1,0,0,-1,0,788.523)"
+ style="font-variant:normal;font-size:4.9813px;font-family:'URW Gothic';-inkscape-font-specification:URWPalladioL-Roma;writing-mode:lr-tb;fill:#2e8b57;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="text142"><tspan
+ x="0"
+ y="0"
+ id="tspan140">4</tspan></text></g></g><g
+ id="g144"
+ transform="translate(183.589,673.8)"><g
+ id="g146"
+ transform="translate(0,-788.518)"><text
+ transform="matrix(1,0,0,-1,0,788.598)"
+ style="font-variant:normal;font-size:4.9813px;font-family:'URW Gothic';-inkscape-font-specification:URWPalladioL-Roma;writing-mode:lr-tb;fill:#2e8b57;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="text150"><tspan
+ x="0"
+ y="0"
+ id="tspan148">6</tspan></text></g></g><g
+ id="g152"
+ transform="translate(192.428,643.044)"><g
+ id="g154"
+ transform="translate(0,-788.518)"><text
+ transform="matrix(1,0,0,-1,0,788.598)"
+ style="font-variant:normal;font-size:4.9813px;font-family:'URW Gothic';-inkscape-font-specification:URWPalladioL-Roma;writing-mode:lr-tb;fill:#2e8b57;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="text158"><tspan
+ x="0"
+ y="0"
+ id="tspan156">5</tspan></text></g></g></g></svg>
diff --git a/doc/dev-doc/manual/figures/explicit.svg b/doc/dev-doc/manual/figures/explicit.svg
new file mode 100644
index 000000000..bac613bf4
--- /dev/null
+++ b/doc/dev-doc/manual/figures/explicit.svg
@@ -0,0 +1,2330 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="342.71356pt"
+ height="107.73483pt"
+ viewBox="0 0 342.71356 107.73483"
+ version="1.2"
+ id="svg4483"
+ sodipodi:docname="explicit.svg"
+ inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
+ <metadata
+ id="metadata4487">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1920"
+ inkscape:window-height="1152"
+ id="namedview4485"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:zoom="1.6819299"
+ inkscape:cx="243.66869"
+ inkscape:cy="9.2156239"
+ inkscape:window-x="0"
+ inkscape:window-y="24"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg4483" />
+ <defs
+ id="defs646">
+ <g
+ id="g644">
+ <symbol
+ overflow="visible"
+ id="glyph0-0"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d=""
+ id="path2"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-1"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 0.46875,-5.4375 H 0.8125 L 1,-6.046875 C 1.125,-6.40625 1.8125,-6.78125 2.375,-6.78125 c 0.71875,0 1.296875,0.578125 1.296875,1.265625 0,0.8125 -0.65625,1.5 -1.421875,1.5 -0.09375,0 -0.203125,-0.015625 -0.359375,-0.015625 l -0.15625,-0.015625 -0.125,0.578125 0.0625,0.0625 c 0.421875,-0.1875 0.625,-0.234375 0.921875,-0.234375 0.90625,0 1.4375,0.578125 1.4375,1.5625 0,1.125 -0.671875,1.84375 -1.6875,1.84375 -0.5,0 -0.953125,-0.15625 -1.28125,-0.46875 C 0.8125,-0.9375 0.671875,-1.1875 0.453125,-1.78125 L 0.15625,-1.671875 C 0.390625,-1 0.484375,-0.609375 0.546875,-0.0625 1.125,0.125 1.609375,0.21875 2,0.21875 c 0.875,0 1.875,-0.484375 2.484375,-1.21875 0.375,-0.453125 0.546875,-0.9375 0.546875,-1.4375 0,-0.53125 -0.203125,-0.984375 -0.609375,-1.265625 -0.265625,-0.1875 -0.515625,-0.28125 -1.0625,-0.375 C 4.234375,-4.75 4.5625,-5.25 4.5625,-5.90625 4.5625,-6.875 3.75,-7.515625 2.546875,-7.515625 c -0.75,0 -1.234375,0.203125 -1.765625,0.734375 z m 0,0"
+ id="path5"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-2"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 0.171875,-0.25 V 0.03125 C 2.21875,0 2.21875,0 2.609375,0 3,0 3,0 5.109375,0.03125 5.078125,-0.203125 5.078125,-0.3125 5.078125,-0.453125 c 0,-0.140625 0,-0.25 0.03125,-0.5 -1.265625,0.0625 -1.765625,0.0625 -3.78125,0.109375 L 3.3125,-2.9375 c 1.0625,-1.125 1.390625,-1.734375 1.390625,-2.546875 0,-1.25 -0.859375,-2.03125 -2.234375,-2.03125 -0.796875,0 -1.328125,0.21875 -1.859375,0.765625 l -0.1875,1.484375 h 0.3125 l 0.15625,-0.5 c 0.171875,-0.640625 0.5625,-0.90625 1.296875,-0.90625 0.9375,0 1.53125,0.59375 1.53125,1.515625 0,0.8125 -0.453125,1.625 -1.6875,2.9375 z m 0,0"
+ id="path8"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-3"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 2.78125,-7.015625 c 0.03125,0.375 0.046875,0.625 0.046875,1.09375 V -1.3125 c 0,0.859375 -0.0625,0.953125 -0.5625,0.984375 L 1.75,-0.296875 V 0.03125 C 2.390625,0.015625 2.765625,0 3.34375,0 3.90625,0 4.296875,0.015625 4.9375,0.03125 v -0.328125 l -0.515625,-0.03125 c -0.5,-0.03125 -0.5625,-0.125 -0.5625,-0.984375 v -4.609375 c 0,-0.484375 0.015625,-0.734375 0.046875,-1.09375 h 1.6875 c 0.3125,0 0.421875,0.09375 0.4375,0.359375 l 0.046875,0.859375 H 6.40625 c 0,-0.703125 0.03125,-1.125 0.078125,-1.75 C 5.703125,-7.53125 5.34375,-7.53125 5.03125,-7.53125 4.609375,-7.515625 4.296875,-7.515625 4.140625,-7.515625 H 2.40625 c -0.109375,0 -0.6875,-0.015625 -1.46875,-0.015625 L 0.203125,-7.546875 c 0.046875,0.625 0.0625,1.046875 0.0625,1.75 h 0.34375 L 0.65625,-6.65625 c 0.015625,-0.265625 0.125,-0.359375 0.4375,-0.359375 z m 0,0"
+ id="path11"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-4"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 0.078125,-7.203125 H 0.6875 c 0.203125,0 0.265625,0.109375 0.265625,0.4375 v 5.65625 c 0,0.6875 -0.03125,0.765625 -0.390625,0.78125 l -0.5,0.03125 V 0.03125 C 0.8125,0.015625 1.09375,0 1.421875,0 1.734375,0 2.03125,0.015625 2.765625,0.03125 V -0.296875 L 2.28125,-0.328125 C 1.90625,-0.34375 1.875,-0.421875 1.875,-1.109375 v -2.3125 c 0,-0.5 0.71875,-1.03125 1.390625,-1.03125 0.71875,0 1.203125,0.546875 1.203125,1.390625 V 0.03125 C 5.140625,0 5.140625,0 5.328125,0 c 0.15625,0 0.15625,0 0.90625,0.03125 v -0.328125 l -0.4375,-0.03125 c -0.375,-0.015625 -0.40625,-0.09375 -0.40625,-0.78125 V -3.21875 c 0,-1.28125 -0.609375,-1.890625 -1.828125,-1.890625 -0.40625,0 -0.625,0.078125 -0.859375,0.265625 L 1.875,-4.125 v -3.703125 l -0.09375,-0.09375 c -0.484375,0.1875 -0.84375,0.28125 -1.703125,0.40625 z m 0,0"
+ id="path14"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-5"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 4.765625,-0.765625 4.625,-0.875 c -0.703125,0.421875 -0.953125,0.515625 -1.421875,0.515625 -0.703125,0 -1.296875,-0.3125 -1.59375,-0.84375 -0.21875,-0.375 -0.296875,-0.671875 -0.3125,-1.3125 h 1.59375 c 0.75,0 1.21875,-0.03125 1.96875,-0.140625 0.015625,-0.15625 0.03125,-0.25 0.03125,-0.390625 0,-1.25 -0.8125,-2.0625 -2.03125,-2.0625 -0.40625,0 -0.875,0.140625 -1.328125,0.390625 -0.890625,0.515625 -1.25,1.203125 -1.25,2.328125 0,0.6875 0.171875,1.28125 0.453125,1.6875 C 1.171875,-0.125 1.90625,0.21875 2.71875,0.21875 3.125,0.21875 3.53125,0.125 3.984375,-0.0625 4.28125,-0.1875 4.5,-0.328125 4.546875,-0.390625 Z M 3.921875,-2.90625 C 3.34375,-2.890625 3.09375,-2.875 2.6875,-2.875 c -0.5,0 -0.78125,-0.015625 -1.375,-0.0625 0,-0.515625 0.046875,-0.765625 0.1875,-1.046875 C 1.734375,-4.453125 2.203125,-4.75 2.71875,-4.75 c 0.375,0 0.65625,0.140625 0.84375,0.4375 0.25,0.359375 0.3125,0.671875 0.359375,1.40625 z m 0,0"
+ id="path17"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-6"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 2.046875,-5.078125 -0.09375,-0.03125 c -0.5625,0.21875 -1.125,0.375 -1.703125,0.4375 v 0.3125 h 0.40625 c 0.4375,0 0.484375,0.078125 0.484375,0.796875 v 2.453125 c 0,0.6875 -0.046875,0.765625 -0.40625,0.78125 l -0.5,0.03125 V 0.03125 C 1.3125,0 1.3125,0 1.59375,0 1.859375,0 1.859375,0 2.953125,0.03125 v -0.328125 l -0.5,-0.03125 c -0.375,-0.015625 -0.40625,-0.09375 -0.40625,-0.78125 z M 1.546875,-7.5 C 1.21875,-7.5 0.9375,-7.203125 0.9375,-6.875 c 0,0.3125 0.28125,0.59375 0.59375,0.59375 0.328125,0 0.625,-0.28125 0.625,-0.59375 0,-0.3125 -0.296875,-0.625 -0.609375,-0.625 z m 0,0"
+ id="path20"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-7"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 4.46875,0.03125 C 5.140625,0 5.140625,0 5.328125,0 c 0.15625,0 0.15625,0 0.90625,0.03125 v -0.328125 l -0.4375,-0.03125 c -0.375,-0.015625 -0.40625,-0.09375 -0.40625,-0.78125 V -3.21875 c 0,-1.28125 -0.609375,-1.890625 -1.828125,-1.890625 -0.40625,0 -0.625,0.078125 -0.859375,0.265625 L 1.875,-4.125 v -0.953125 l -0.09375,-0.03125 c -0.5625,0.21875 -1.125,0.375 -1.703125,0.4375 v 0.3125 h 0.40625 c 0.4375,0 0.46875,0.078125 0.46875,0.796875 v 2.453125 c 0,0.6875 -0.03125,0.765625 -0.390625,0.78125 l -0.5,0.03125 V 0.03125 C 0.8125,0.015625 1.09375,0 1.421875,0 1.734375,0 2.03125,0.015625 2.765625,0.03125 V -0.296875 L 2.28125,-0.328125 C 1.90625,-0.34375 1.875,-0.421875 1.875,-1.109375 v -2.3125 c 0,-0.5 0.71875,-1.03125 1.390625,-1.03125 0.71875,0 1.203125,0.546875 1.203125,1.390625 z m 0,0"
+ id="path23"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-8"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 1.0625,-4.171875 v 3.140625 c 0,0.8125 0.359375,1.15625 1.203125,1.15625 0.25,0 0.515625,-0.046875 0.578125,-0.125 L 3.375,-0.578125 3.234375,-0.765625 c -0.28125,0.15625 -0.4375,0.203125 -0.65625,0.203125 -0.421875,0 -0.59375,-0.21875 -0.59375,-0.75 V -4.171875 H 3.375 L 3.484375,-4.75 l -1.5,0.0625 v -0.421875 c 0,-0.453125 0.03125,-0.84375 0.125,-1.546875 L 1.96875,-6.765625 c -0.25,0.15625 -0.578125,0.296875 -0.921875,0.40625 C 1.078125,-6.03125 1.09375,-5.828125 1.09375,-5.5 v 0.765625 l -0.859375,0.375 V -4.125 Z m 0,0"
+ id="path26"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-9"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 3.53125,-0.890625 3.484375,0.03125 C 4.1875,0 4.1875,0 4.328125,0 c 0.0625,0 0.328125,0.015625 0.8125,0.03125 V -0.296875 L 4.71875,-0.3125 C 4.453125,-0.328125 4.40625,-0.421875 4.40625,-0.953125 V -2.9375 c 0,-1.59375 -0.453125,-2.171875 -1.71875,-2.171875 C 2.21875,-5.109375 1.78125,-5 1.359375,-4.75 L 0.78125,-4.390625 V -3.6875 l 0.28125,0.078125 0.140625,-0.34375 c 0.234375,-0.5 0.34375,-0.578125 0.84375,-0.578125 1.03125,0 1.453125,0.390625 1.484375,1.390625 L 2.453125,-2.9375 C 0.96875,-2.671875 0.34375,-2.15625 0.34375,-1.203125 c 0,0.84375 0.515625,1.328125 1.375,1.328125 0.203125,0 0.390625,-0.03125 0.46875,-0.078125 z m 0,-0.46875 c -0.3125,0.453125 -1,0.875 -1.421875,0.875 -0.4375,0 -0.828125,-0.40625 -0.828125,-0.875 0,-0.40625 0.21875,-0.796875 0.546875,-1 0.28125,-0.15625 0.875,-0.3125 1.703125,-0.4375 z m 0,0"
+ id="path29"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-10"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 0.25,-7.203125 h 0.609375 c 0.203125,0 0.28125,0.109375 0.28125,0.4375 v 5.65625 c 0,0.6875 -0.046875,0.765625 -0.40625,0.78125 l -0.5,0.03125 V 0.03125 C 1.3125,0 1.3125,0 1.59375,0 1.859375,0 1.859375,0 2.953125,0.03125 v -0.328125 l -0.5,-0.03125 c -0.375,-0.015625 -0.40625,-0.09375 -0.40625,-0.78125 v -6.71875 l -0.09375,-0.09375 c -0.484375,0.1875 -0.84375,0.28125 -1.703125,0.40625 z m 0,0"
+ id="path32"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-11"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 4.5,-4.765625 c -0.625,-0.265625 -0.921875,-0.34375 -1.296875,-0.34375 -0.296875,0 -0.5625,0.0625 -0.84375,0.203125 l -0.953125,0.5 c -0.625,0.328125 -1.03125,1.125 -1.03125,2.046875 0,0.875 0.296875,1.59375 0.828125,2.03125 0.375,0.3125 0.890625,0.453125 1.59375,0.453125 0.21875,0 0.46875,-0.03125 0.53125,-0.078125 l 1.203125,-1.015625 -0.046875,1 C 5.015625,0.015625 5.1875,0 5.328125,0 5.40625,0 5.5625,0 5.796875,0.015625 c 0.078125,0 0.28125,0 0.515625,0.015625 v -0.328125 l -0.5,-0.03125 C 5.4375,-0.34375 5.40625,-0.421875 5.40625,-1.109375 v -6.71875 L 5.3125,-7.921875 c -0.484375,0.1875 -0.84375,0.28125 -1.703125,0.40625 v 0.3125 H 4.21875 c 0.203125,0 0.28125,0.109375 0.28125,0.4375 z m 0,2.359375 c 0,0.703125 -0.03125,0.8125 -0.171875,1.0625 -0.296875,0.515625 -0.84375,0.859375 -1.34375,0.859375 -0.96875,0 -1.65625,-0.9375 -1.65625,-2.234375 0,-1.1875 0.59375,-1.890625 1.578125,-1.890625 0.40625,0 0.84375,0.15625 1.234375,0.40625 C 4.359375,-4.046875 4.5,-3.90625 4.5,-3.8125 Z m 0,0"
+ id="path35"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-12"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 0.453125,-1.5625 c 0,0.75 -0.03125,1.078125 -0.125,1.515625 0.5625,0.1875 0.984375,0.265625 1.5,0.265625 1.421875,0 2.4375,-0.765625 2.4375,-1.828125 0,-0.34375 -0.09375,-0.59375 -0.3125,-0.8125 C 3.671875,-2.6875 3.3125,-2.828125 2.359375,-3.015625 1.46875,-3.1875 1.171875,-3.40625 1.171875,-3.90625 1.171875,-4.453125 1.546875,-4.75 2.21875,-4.75 2.9375,-4.75 3.5,-4.375 3.5,-3.859375 v 0.25 H 3.8125 C 3.8125,-4.25 3.828125,-4.5 3.859375,-4.84375 3.28125,-5.03125 2.890625,-5.109375 2.4375,-5.109375 c -1.265625,0 -2.03125,0.59375 -2.03125,1.578125 0,0.515625 0.234375,0.890625 0.734375,1.125 0.296875,0.125 0.875,0.296875 1.625,0.46875 0.484375,0.109375 0.6875,0.328125 0.6875,0.75 0,0.59375 -0.546875,1 -1.3125,1 -0.796875,0 -1.359375,-0.375 -1.359375,-0.921875 V -1.5625 Z m 0,0"
+ id="path38"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-13"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 0.109375,-4.359375 h 0.40625 C 0.953125,-4.359375 1,-4.28125 1,-3.5625 V 1.921875 C 1,2.609375 0.953125,2.6875 0.59375,2.703125 l -0.5,0.03125 V 3.0625 C 1.171875,3.03125 1.171875,3.03125 1.453125,3.03125 c 0.265625,0 0.265625,0 1.359375,0.03125 V 2.734375 l -0.5,-0.03125 C 1.9375,2.6875 1.90625,2.609375 1.90625,1.921875 v -2.0625 C 2.234375,0.046875 2.578125,0.125 3.078125,0.125 c 0.3125,0 0.59375,-0.0625 0.765625,-0.171875 l 1.25,-0.796875 c 0.4375,-0.296875 0.953125,-1.40625 0.953125,-2.109375 0,-1.21875 -1.015625,-2.15625 -2.265625,-2.15625 C 3.375,-5.109375 2.984375,-5 2.796875,-4.84375 L 1.90625,-4.03125 V -5.078125 L 1.8125,-5.109375 c -0.5625,0.21875 -1.125,0.375 -1.703125,0.4375 z M 1.90625,-3.34375 c 0,-0.140625 0.0625,-0.265625 0.203125,-0.453125 0.3125,-0.40625 0.765625,-0.609375 1.296875,-0.609375 1.03125,0 1.6875,0.71875 1.6875,1.859375 0,1.21875 -0.734375,2.125 -1.71875,2.125 -0.59375,0 -1.046875,-0.203125 -1.46875,-0.640625 z m 0,0"
+ id="path41"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-14"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 4.359375,-3.390625 c 0,-0.5 0.046875,-0.953125 0.140625,-1.34375 -0.4375,-0.265625 -0.8125,-0.375 -1.265625,-0.375 -0.515625,0 -1.125,0.203125 -1.78125,0.609375 -0.765625,0.484375 -1.171875,1.25 -1.171875,2.203125 0,1.484375 1.03125,2.515625 2.5,2.515625 0.5625,0 1.390625,-0.203125 1.5,-0.375 L 4.5,-0.5 4.390625,-0.671875 c -0.40625,0.21875 -0.75,0.328125 -1.140625,0.328125 -1.203125,0 -2,-0.9375 -2,-2.375 0,-1.171875 0.53125,-1.828125 1.515625,-1.828125 0.484375,0 1.015625,0.21875 1.203125,0.484375 l 0.078125,0.671875 z m 0,0"
+ id="path44"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-15"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 0.203125,-4.359375 H 0.59375 c 0.4375,0 0.484375,0.078125 0.484375,0.796875 v 2.453125 c 0,0.6875 -0.03125,0.765625 -0.40625,0.78125 l -0.5,0.03125 V 0.03125 C 1.21875,0 1.25,0 1.5625,0 1.859375,0 2.15625,0.015625 2.859375,0.03125 V -0.296875 L 2.40625,-0.328125 C 2.03125,-0.34375 2,-0.421875 2,-1.109375 v -2.3125 c 0,-0.5 0.71875,-1.03125 1.375,-1.03125 0.578125,0 0.984375,0.5625 0.984375,1.390625 v 1.953125 c 0,0.6875 -0.03125,0.765625 -0.40625,0.78125 l -0.5,0.03125 V 0.03125 C 4.546875,0 4.546875,0 4.828125,0 5.09375,0 5.09375,0 6.1875,0.03125 v -0.328125 l -0.5,-0.03125 C 5.3125,-0.34375 5.28125,-0.421875 5.28125,-1.109375 v -2.3125 c 0,-0.5 0.71875,-1.03125 1.375,-1.03125 0.578125,0 0.984375,0.5625 0.984375,1.390625 V 0.03125 C 8.3125,0 8.328125,0 8.5,0 8.65625,0 8.65625,0 9.484375,0.03125 V -0.296875 L 8.96875,-0.328125 C 8.59375,-0.34375 8.5625,-0.421875 8.5625,-1.109375 v -2.25 c 0,-1.078125 -0.640625,-1.75 -1.640625,-1.75 -0.375,0 -0.6875,0.09375 -0.890625,0.265625 l -0.828125,0.75 C 4.859375,-4.828125 4.40625,-5.109375 3.65625,-5.109375 3.25,-5.109375 2.9375,-5.015625 2.75,-4.84375 L 2,-4.140625 v -0.9375 l -0.109375,-0.03125 c -0.546875,0.21875 -1.109375,0.375 -1.6875,0.4375 z m 0,0"
+ id="path47"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-16"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 5.875,-4.703125 V -5 c -0.65625,0.015625 -0.84375,0.03125 -1.015625,0.03125 -0.1875,0 -0.375,-0.015625 -1.03125,-0.03125 v 0.296875 l 0.453125,0.03125 c 0.171875,0 0.25,0.109375 0.25,0.28125 0,0.171875 -0.078125,0.46875 -0.203125,0.796875 L 3.875,-2.4375 C 3.734375,-2.125 3.609375,-1.828125 3.203125,-0.921875 L 1.890625,-4.0625 c -0.0625,-0.140625 -0.09375,-0.296875 -0.09375,-0.40625 0,-0.125 0.09375,-0.203125 0.28125,-0.203125 l 0.53125,-0.03125 V -5 C 1.53125,-4.96875 1.53125,-4.96875 1.328125,-4.96875 1.125,-4.96875 0.59375,-4.984375 0.0625,-5 v 0.296875 l 0.375,0.03125 C 0.59375,-4.65625 0.65625,-4.59375 0.78125,-4.3125 l 1.875,4.390625 h 0.546875 c 0.0625,-0.1875 0.171875,-0.46875 0.171875,-0.5 0.109375,-0.3125 0.25,-0.6875 0.265625,-0.6875 l 1.28125,-2.84375 c 0.25,-0.5 0.40625,-0.703125 0.625,-0.71875 z m 0,0"
+ id="path50"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-17"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 3.046875,-5.109375 c -1.59375,0 -2.703125,1.125 -2.703125,2.75 0,1.53125 0.96875,2.578125 2.375,2.578125 1.6875,0 2.890625,-1.171875 2.890625,-2.796875 0,-1.484375 -1.078125,-2.53125 -2.5625,-2.53125 z M 2.84375,-4.75 c 1,0 1.75,1.078125 1.75,2.5625 0,1.265625 -0.5625,2.046875 -1.4375,2.046875 -1.078125,0 -1.796875,-1.046875 -1.796875,-2.59375 0,-1.3125 0.53125,-2.015625 1.484375,-2.015625 z m 0,0"
+ id="path53"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-18"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 3.234375,-0.9375 C 3,-1.359375 2.875,-1.609375 2.6875,-2.140625 L 1.984375,-3.96875 C 1.921875,-4.15625 1.875,-4.3125 1.875,-4.421875 c 0,-0.15625 0.125,-0.25 0.375,-0.25 l 0.40625,-0.03125 V -5 C 1.609375,-4.96875 1.609375,-4.96875 1.390625,-4.96875 1.1875,-4.96875 1.1875,-4.96875 0.125,-5 v 0.296875 l 0.203125,0.03125 c 0.265625,0.03125 0.40625,0.140625 0.5625,0.53125 l 1.1875,2.859375 c 0.3125,0.734375 0.421875,1 0.609375,1.546875 l -0.25,0.65625 c -0.34375,0.921875 -0.765625,1.4375 -1.1875,1.4375 -0.171875,0 -0.359375,-0.09375 -0.546875,-0.25 H 0.5625 l -0.265625,0.8125 C 0.5,3.03125 0.671875,3.09375 0.890625,3.09375 1.609375,3.09375 2.078125,2.6875 2.5,1.75 l 2.21875,-5.109375 c 0.421875,-1 0.640625,-1.28125 0.90625,-1.3125 l 0.3125,-0.03125 V -5 c -0.859375,0.03125 -0.859375,0.03125 -1.046875,0.03125 -0.171875,0 -0.171875,0 -1.03125,-0.03125 v 0.296875 l 0.28125,0.03125 c 0.3125,0.015625 0.46875,0.109375 0.46875,0.265625 0,0.078125 -0.03125,0.171875 -0.078125,0.28125 z m 0,0"
+ id="path56"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-19"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 3.71875,-7.8125 c -0.234375,-0.09375 -0.359375,-0.125 -0.5,-0.125 -0.34375,0 -0.734375,0.1875 -0.96875,0.453125 l -0.640625,0.71875 C 1.296875,-6.40625 1.15625,-5.90625 1.15625,-5.1875 v 0.453125 l -0.75,0.34375 v 0.234375 l 0.75,-0.046875 v 3.09375 c 0,0.6875 -0.03125,0.765625 -0.40625,0.78125 l -0.5,0.03125 V 0.03125 C 1.34375,0 1.34375,0 1.625,0 1.890625,0 2.1875,0.015625 3.046875,0.03125 V -0.296875 L 2.46875,-0.328125 C 2.109375,-0.34375 2.078125,-0.421875 2.078125,-1.109375 v -3.09375 H 3.3125 l 0.078125,-0.578125 -1.3125,0.046875 V -5.65625 c 0,-1.265625 0.140625,-1.5625 0.75,-1.5625 0.296875,0 0.5,0.078125 0.765625,0.296875 l 0.125,-0.0625 z M 5.5,-5.03125 5.40625,-5.109375 C 4.921875,-4.875 4.34375,-4.71875 3.703125,-4.640625 V -4.34375 H 4.09375 c 0.4375,0 0.484375,0.078125 0.484375,0.796875 v 2.4375 c 0,0.6875 -0.03125,0.765625 -0.40625,0.78125 l -0.5,0.03125 V 0.03125 C 4.765625,0 4.765625,0 5.03125,0 5.3125,0 5.3125,0 6.40625,0.03125 v -0.328125 l -0.5,-0.03125 C 5.53125,-0.34375 5.5,-0.421875 5.5,-1.109375 Z M 5.09375,-7.5 c -0.328125,0 -0.609375,0.296875 -0.609375,0.625 0,0.3125 0.28125,0.59375 0.59375,0.59375 0.328125,0 0.609375,-0.28125 0.609375,-0.59375 0,-0.3125 -0.28125,-0.625 -0.59375,-0.625 z m 0,0"
+ id="path59"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-20"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 0.25,-4.359375 h 0.40625 c 0.4375,0 0.484375,0.078125 0.484375,0.796875 v 2.453125 c 0,0.6875 -0.046875,0.765625 -0.40625,0.78125 l -0.5,0.03125 V 0.03125 C 1.015625,0.015625 1.296875,0 1.578125,0 1.78125,0 1.78125,0 3.125,0.03125 v -0.328125 l -0.5625,-0.03125 c -0.484375,-0.03125 -0.515625,-0.0625 -0.515625,-0.78125 v -1.875 c 0,-0.65625 0.4375,-1.203125 0.96875,-1.203125 0.328125,0 0.5625,0.15625 0.734375,0.5 H 3.984375 L 4.078125,-5 c -0.125,-0.078125 -0.3125,-0.109375 -0.515625,-0.109375 -0.328125,0 -0.671875,0.171875 -0.90625,0.4375 l -0.609375,0.71875 v -1.125 l -0.09375,-0.03125 c -0.5625,0.21875 -1.125,0.375 -1.703125,0.4375 z m 0,0"
+ id="path62"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-21"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 2.21875,-1.34375 C 1.9375,-1.25 1.734375,-1.171875 1.15625,-1.015625 1.078125,-0.1875 0.8125,0.53125 0.171875,1.578125 L 0.328125,1.6875 0.78125,1.484375 C 1.65625,0.34375 2.078125,-0.34375 2.375,-1.1875 Z m 0,0"
+ id="path65"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-22"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 1.640625,-7.828125 1.53125,-7.921875 c -0.46875,0.1875 -0.828125,0.28125 -1.6875,0.40625 v 0.3125 h 0.609375 c 0.1875,0 0.265625,0.109375 0.265625,0.4375 v 5.09375 C 0.71875,-1 0.703125,-0.703125 0.625,0.0625 L 0.8125,0.125 1.25,-0.265625 C 1.671875,0.015625 2.078125,0.125 2.65625,0.125 c 0.421875,0 0.703125,-0.078125 0.9375,-0.234375 l 1.078125,-0.78125 c 0.484375,-0.359375 0.875,-1.265625 0.875,-2.046875 0,-1.265625 -0.828125,-2.171875 -1.984375,-2.171875 -0.515625,0 -1.046875,0.1875 -1.328125,0.46875 l -0.59375,0.609375 z m 0,4.46875 c 0,-0.15625 0.109375,-0.34375 0.28125,-0.546875 0.296875,-0.3125 0.734375,-0.5 1.171875,-0.5 0.921875,0 1.5,0.734375 1.5,1.90625 0,1.21875 -0.65625,2.078125 -1.578125,2.078125 -0.578125,0 -1.375,-0.359375 -1.375,-0.59375 z m 0,0"
+ id="path68"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-23"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 3.71875,-7.8125 c -0.234375,-0.09375 -0.359375,-0.125 -0.5,-0.125 -0.34375,0 -0.734375,0.1875 -0.96875,0.453125 l -0.640625,0.71875 C 1.296875,-6.40625 1.15625,-5.90625 1.15625,-5.1875 v 0.453125 l -0.75,0.34375 v 0.234375 l 0.75,-0.046875 v 3.09375 c 0,0.6875 -0.03125,0.765625 -0.40625,0.78125 l -0.5,0.03125 V 0.03125 C 1.34375,0 1.34375,0 1.609375,0 c 0.28125,0 0.28125,0 1.375,0.03125 V -0.296875 L 2.46875,-0.328125 C 2.109375,-0.34375 2.078125,-0.421875 2.078125,-1.109375 v -3.09375 h 1.34375 L 3.5,-4.78125 2.078125,-4.734375 V -5.65625 c 0,-1.265625 0.140625,-1.5625 0.75,-1.5625 0.296875,0 0.5,0.078125 0.765625,0.296875 l 0.125,-0.0625 z m 0,0"
+ id="path71"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-24"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 5.484375,-5.078125 -0.09375,-0.03125 c -0.5625,0.21875 -1.125,0.375 -1.703125,0.4375 v 0.3125 h 0.40625 c 0.4375,0 0.46875,0.078125 0.46875,0.796875 v 1.78125 C 4.5625,-1.546875 4.421875,-1.25 4.171875,-1 c -0.3125,0.3125 -0.65625,0.46875 -1.078125,0.46875 -0.390625,0 -0.6875,-0.125 -0.875,-0.328125 C 2.078125,-1.03125 2,-1.375 2,-1.859375 v -3.21875 l -0.109375,-0.03125 c -0.546875,0.21875 -1.109375,0.375 -1.6875,0.4375 v 0.3125 H 0.59375 c 0.4375,0 0.484375,0.078125 0.484375,0.796875 v 1.828125 c 0,0.765625 0.0625,1.109375 0.265625,1.375 C 1.578125,-0.03125 2,0.125 2.625,0.125 c 0.484375,0 0.921875,-0.140625 1.21875,-0.421875 L 4.5625,-0.96875 c 0,0.3125 0,0.484375 -0.03125,1 C 5.25,0 5.265625,0 5.4375,0 5.578125,0 5.578125,0 6.34375,0.03125 v -0.328125 l -0.453125,-0.03125 c -0.375,-0.015625 -0.40625,-0.09375 -0.40625,-0.78125 z m 0,0"
+ id="path74"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-25"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 4.28125,-0.71875 V 1.921875 C 4.28125,2.609375 4.25,2.6875 3.875,2.703125 L 3.25,2.734375 V 3.0625 C 4.515625,3.03125 4.515625,3.03125 4.765625,3.03125 c 0.265625,0 0.5625,0.015625 1.34375,0.03125 V 2.734375 l -0.5,-0.03125 C 5.234375,2.6875 5.203125,2.609375 5.203125,1.921875 V -3.46875 c 0,-0.671875 0.0625,-1.09375 0.25,-1.5 L 5.25,-5.09375 4.71875,-4.75 c -0.5625,-0.25 -1.03125,-0.359375 -1.5,-0.359375 -0.3125,0 -0.703125,0.078125 -0.859375,0.1875 L 1.4375,-4.34375 C 0.734375,-3.890625 0.375,-3.171875 0.375,-2.21875 0.375,-0.796875 1.25,0.125 2.59375,0.125 2.828125,0.125 3.015625,0.09375 3.15625,0 Z m 0,-0.59375 c 0,0.15625 -0.25,0.4375 -0.546875,0.609375 -0.265625,0.140625 -0.515625,0.21875 -0.78125,0.21875 -0.953125,0 -1.625,-0.90625 -1.625,-2.21875 0,-1.203125 0.609375,-1.90625 1.65625,-1.90625 0.53125,0 0.921875,0.140625 1.296875,0.453125 z m 0,0"
+ id="path77"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-26"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 4.96875,-4.71875 v -0.3125 C 4.203125,-5.015625 3.875,-5 3.453125,-5 L 0.75,-5.03125 c -0.015625,0.546875 -0.03125,0.671875 -0.09375,1.4375 h 0.328125 l 0.125,-0.75 C 1.15625,-4.5625 1.359375,-4.625 2.15625,-4.625 h 1.5625 c -0.234375,0.359375 -0.421875,0.609375 -0.703125,0.984375 l -1.859375,2.3125 c -0.265625,0.3125 -0.421875,0.5 -0.984375,1.140625 l 0.0625,0.21875 C 1.375,0 1.4375,0 1.90625,0 l 3.015625,0.03125 c 0,-0.25 0.015625,-0.515625 0.09375,-1.03125 l 0.0625,-0.515625 H 4.75 L 4.5625,-0.65625 C 4.515625,-0.453125 4.0625,-0.375 3.015625,-0.375 H 1.53125 Z m 0,0"
+ id="path80"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-27"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 5.65625,-4.125 5.9375,-4.5625 5.890625,-4.703125 4.34375,-4.65625 C 3.9375,-4.984375 3.546875,-5.109375 3.015625,-5.109375 c -0.484375,0 -1.046875,0.140625 -1.484375,0.40625 -0.609375,0.34375 -0.9375,0.890625 -0.9375,1.546875 0,0.8125 0.5,1.375 1.3125,1.453125 L 1.046875,-1.03125 C 0.9375,-0.875 0.890625,-0.734375 0.890625,-0.5625 c 0,0.296875 0.1875,0.46875 0.71875,0.625 L 0.65625,0.578125 C 0.5,0.671875 0.34375,1.0625 0.34375,1.40625 c 0,1 0.96875,1.6875 2.359375,1.6875 1.65625,0 2.953125,-1.046875 2.953125,-2.375 0,-0.78125 -0.53125,-1.328125 -1.296875,-1.328125 H 2.625 c -0.578125,0 -0.84375,-0.125 -0.84375,-0.421875 0,-0.140625 0.09375,-0.265625 0.375,-0.5 0.0625,-0.0625 0.09375,-0.078125 0.140625,-0.125 0.109375,0.015625 0.171875,0.015625 0.25,0.015625 0.46875,0 1.046875,-0.203125 1.5,-0.515625 C 4.53125,-2.515625 4.78125,-2.953125 4.78125,-3.53125 4.78125,-3.75 4.75,-3.890625 4.65625,-4.125 Z M 2.625,-4.75 c 0.734375,0 1.234375,0.609375 1.234375,1.546875 C 3.859375,-2.5 3.40625,-2 2.734375,-2 2,-2 1.515625,-2.578125 1.515625,-3.40625 1.515625,-4.25 1.921875,-4.75 2.625,-4.75 Z m 0.5,4.90625 c 1.234375,0 1.65625,0.25 1.65625,0.953125 0,0.9375 -0.859375,1.640625 -2,1.640625 -0.953125,0 -1.609375,-0.5625 -1.609375,-1.359375 0,-0.5 0.296875,-0.9375 0.71875,-1.125 C 2.078125,0.171875 2.375,0.15625 3.125,0.15625 Z m 0,0"
+ id="path83"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-28"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 3.28125,2.109375 C 2.5,1.140625 2.21875,0.625 1.9375,-0.359375 1.703125,-1.140625 1.59375,-1.96875 1.59375,-2.90625 c 0,-0.90625 0.109375,-1.671875 0.3125,-2.390625 0.28125,-0.890625 0.578125,-1.40625 1.375,-2.34375 l -0.203125,-0.28125 c -1.140625,1.015625 -1.578125,1.625 -2,2.6875 -0.28125,0.734375 -0.421875,1.5 -0.421875,2.328125 0,1.171875 0.265625,2.28125 0.796875,3.25 0.375,0.734375 0.734375,1.15625 1.625,2 z m 0,0"
+ id="path86"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-29"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 3.046875,-1.96875 v 0.8125 c 0,0.65625 -0.109375,0.8125 -0.640625,0.828125 l -0.6875,0.03125 V 0.03125 C 3.171875,0 3.171875,0 3.4375,0 3.703125,0 3.703125,0 5.140625,0.03125 V -0.296875 L 4.625,-0.328125 C 4.09375,-0.359375 3.96875,-0.5 3.96875,-1.15625 v -0.8125 c 0.640625,0 0.875,0 1.171875,0.03125 v -0.765625 c -0.5,0.046875 -0.71875,0.046875 -0.875,0.03125 H 3.96875 V -4.1875 c 0,-1.9375 0.015625,-2.875 0.078125,-3.328125 l -0.125,-0.046875 -0.8125,0.28125 -3.09375,5.0625 0.125,0.25 z m 0,-0.703125 h -2.34375 l 2.34375,-3.84375 z m 0,0"
+ id="path89"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-30"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 1.359375,-1.203125 c -0.328125,0 -0.625,0.296875 -0.625,0.625 0,0.328125 0.296875,0.625 0.609375,0.625 0.34375,0 0.65625,-0.28125 0.65625,-0.625 0,-0.328125 -0.3125,-0.625 -0.640625,-0.625 z m 0,0"
+ id="path92"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-31"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 0.734375,-6.046875 H 0.84375 L 2.25,-6.6875 c 0,-0.015625 0.015625,-0.015625 0.03125,-0.015625 0.0625,0 0.078125,0.109375 0.078125,0.359375 v 5.296875 c 0,0.5625 -0.109375,0.6875 -0.703125,0.71875 l -0.609375,0.03125 V 0.03125 C 2.71875,0 2.71875,0 2.84375,0 c 0.140625,0 0.390625,0 0.75,0.015625 0.140625,0 0.515625,0 0.96875,0.015625 V -0.296875 L 3.984375,-0.328125 C 3.390625,-0.359375 3.28125,-0.484375 3.28125,-1.046875 v -6.46875 L 3.125,-7.5625 C 2.421875,-7.203125 1.640625,-6.875 0.65625,-6.515625 Z m 0,0"
+ id="path95"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-32"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 0.5625,2.34375 c 0.75,-0.703125 1.0625,-1.0625 1.40625,-1.609375 0.65625,-1.046875 1.015625,-2.3125 1.015625,-3.625 0,-0.84375 -0.15625,-1.609375 -0.4375,-2.34375 -0.40625,-1.0625 -0.859375,-1.65625 -1.984375,-2.6875 l -0.21875,0.28125 c 0.796875,0.9375 1.09375,1.453125 1.375,2.34375 0.21875,0.71875 0.328125,1.484375 0.328125,2.390625 0,0.9375 -0.109375,1.765625 -0.34375,2.546875 -0.28125,0.984375 -0.5625,1.5 -1.359375,2.46875 z m 0,0"
+ id="path98"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-33"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 2.359375,-6.203125 C 2.359375,-7.0625 2.40625,-7.15625 2.90625,-7.1875 L 3.4375,-7.21875 v -0.328125 c -0.796875,0.03125 -1.03125,0.03125 -1.609375,0.03125 -0.546875,0 -0.796875,-0.015625 -1.59375,-0.03125 v 0.328125 l 0.53125,0.03125 c 0.5,0.03125 0.546875,0.125 0.546875,0.984375 v 5.171875 c 0,0.3125 -0.09375,0.546875 -0.265625,0.640625 l -0.34375,0.171875 v 0.25 C 1.421875,0 1.5,0 1.78125,0 1.890625,0 2.0625,0 2.296875,0.015625 c 0.53125,0 1.734375,0.015625 2.03125,0.015625 0.34375,0 0.34375,0 1.828125,-0.03125 0.078125,-0.46875 0.125,-0.8125 0.234375,-1.875 H 6.046875 L 5.90625,-1.328125 c 0,0.03125 -0.015625,0.109375 -0.03125,0.125 -0.0625,0.3125 -0.140625,0.53125 -0.203125,0.578125 -0.140625,0.125 -1.09375,0.203125 -2.390625,0.203125 -0.390625,0 -0.640625,-0.03125 -0.921875,-0.09375 z m 0,0"
+ id="path101"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-34"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 0.25,-7.203125 h 0.609375 c 0.203125,0 0.28125,0.109375 0.28125,0.4375 v 5.65625 c 0,0.6875 -0.046875,0.765625 -0.40625,0.78125 l -0.5,0.03125 V 0.03125 C 1.3125,0 1.3125,0 1.609375,0 1.90625,0 1.96875,0 2.9375,0.03125 v -0.328125 l -0.484375,-0.03125 c -0.375,-0.015625 -0.40625,-0.09375 -0.40625,-0.78125 V -2.625 L 4.9375,0.125 5.984375,-0.1875 v -0.25 C 5.71875,-0.4375 5.40625,-0.625 5.0625,-0.953125 l -1.375,-1.375 C 3.296875,-2.703125 3.171875,-2.84375 2.984375,-3.0625 l 1.40625,-1.109375 c 0.515625,-0.390625 0.8125,-0.515625 1.1875,-0.5 V -5 H 4.484375 C 3.78125,-4.3125 3.265625,-3.828125 2.84375,-3.484375 L 2.046875,-2.84375 v -4.984375 l -0.09375,-0.09375 c -0.484375,0.1875 -0.84375,0.28125 -1.703125,0.40625 z m 0,0"
+ id="path104"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-35"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 4.5,-5.109375 C 3.609375,-3.03125 3.453125,-2.65625 2.765625,-1.140625 l -0.90625,-3 C 1.828125,-4.25 1.8125,-4.328125 1.8125,-4.421875 c 0,-0.15625 0.140625,-0.25 0.40625,-0.25 l 0.4375,-0.03125 V -5 c -1.078125,0.03125 -1.078125,0.03125 -1.296875,0.03125 -0.21875,0 -0.21875,0 -1.296875,-0.03125 v 0.296875 l 0.3125,0.03125 C 0.671875,-4.65625 0.8125,-4.4375 1.09375,-3.59375 L 2.203125,0.078125 H 2.71875 L 3.78125,-2.4375 c 0.0625,-0.125 0.171875,-0.390625 0.359375,-0.765625 l 0.21875,-0.453125 1.46875,3.734375 H 6.34375 C 6.40625,-0.15625 6.5,-0.40625 6.59375,-0.640625 6.8125,-1.28125 7.046875,-1.96875 7.09375,-2.03125 l 0.734375,-1.703125 c 0.328125,-0.71875 0.453125,-0.90625 0.703125,-0.9375 L 8.8125,-4.703125 V -5 c -0.890625,0.03125 -0.890625,0.03125 -1.0625,0.03125 -0.1875,0 -0.1875,0 -1.078125,-0.03125 v 0.296875 l 0.375,0.03125 c 0.265625,0 0.421875,0.140625 0.421875,0.328125 0,0.125 -0.015625,0.25 -0.078125,0.390625 L 6.84375,-2.421875 C 6.765625,-2.1875 6.671875,-1.96875 6.328125,-1.0625 l -1.25,-3.171875 c -0.15625,-0.375 -0.234375,-0.59375 -0.328125,-0.875 z m 0,0"
+ id="path107"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-36"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 3.078125,-2.34375 3.40625,-3.078125 3.34375,-3.125 H 0.484375 l -0.296875,0.71875 0.046875,0.0625 z m 0,0"
+ id="path110"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-37"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 1.5,-7.515625 C 0.890625,-7.53125 0.78125,-7.53125 0.1875,-7.546875 v 0.328125 l 0.484375,0.03125 c 0.5,0.03125 0.546875,0.125 0.546875,0.984375 V -1.3125 c 0,0.859375 -0.046875,0.953125 -0.546875,0.984375 L 0.1875,-0.296875 V 0.03125 C 1.265625,0 1.265625,0 1.453125,0 c 0.15625,0 0.15625,0 1.3125,0.03125 V -0.296875 L 2.28125,-0.328125 c -0.5,-0.03125 -0.5625,-0.125 -0.5625,-0.984375 V -6.359375 L 6.875,0.046875 7.859375,0.21875 c 0,-0.046875 0,-0.0625 0,-0.125 C 7.84375,-0.03125 7.828125,-0.125 7.828125,-0.1875 v -6.015625 c 0,-0.859375 0.0625,-0.953125 0.5625,-0.984375 l 0.46875,-0.03125 v -0.328125 c -1.140625,0.03125 -1.140625,0.03125 -1.296875,0.03125 -0.1875,0 -0.1875,0 -1.265625,-0.03125 v 0.328125 l 0.46875,0.03125 c 0.515625,0.03125 0.5625,0.125 0.5625,0.984375 V -1.0625 l -5.25,-6.484375 z m 0,0"
+ id="path113"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-38"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 2.875,-7.515625 c -1.703125,0 -2.5625,1.34375 -2.5625,3.984375 0,1.28125 0.234375,2.375 0.609375,2.90625 0.390625,0.53125 1,0.84375 1.671875,0.84375 1.65625,0 2.484375,-1.421875 2.484375,-4.203125 0,-2.390625 -0.71875,-3.53125 -2.203125,-3.53125 z M 2.671875,-7.125 c 1.0625,0 1.484375,1.0625 1.484375,3.671875 0,2.328125 -0.421875,3.296875 -1.421875,3.296875 -1.0625,0 -1.5,-1.109375 -1.5,-3.765625 0,-2.3125 0.40625,-3.203125 1.4375,-3.203125 z m 0,0"
+ id="path116"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-39"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 2.359375,-6.203125 C 2.359375,-7.0625 2.40625,-7.15625 2.90625,-7.1875 L 3.4375,-7.21875 v -0.328125 c -0.796875,0.03125 -1.03125,0.03125 -1.609375,0.03125 -0.546875,0 -0.796875,-0.015625 -1.59375,-0.03125 v 0.328125 l 0.53125,0.03125 c 0.5,0.03125 0.546875,0.125 0.546875,0.984375 V -1.3125 c 0,0.859375 -0.046875,0.953125 -0.546875,0.984375 l -0.53125,0.03125 V 0.03125 C 0.890625,0.015625 1.265625,0 1.828125,0 2.40625,0 2.796875,0.015625 3.4375,0.03125 v -0.328125 l -0.53125,-0.03125 c -0.5,-0.03125 -0.546875,-0.125 -0.546875,-0.984375 z m 0,0"
+ id="path119"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-40"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 1.34375,-1.203125 c -0.328125,0 -0.625,0.296875 -0.625,0.625 0,0.328125 0.296875,0.625 0.609375,0.625 0.34375,0 0.65625,-0.28125 0.65625,-0.625 0,-0.328125 -0.3125,-0.625 -0.640625,-0.625 z m 0,-3.765625 c -0.328125,0 -0.625,0.296875 -0.625,0.625 0,0.328125 0.296875,0.640625 0.609375,0.640625 0.34375,0 0.65625,-0.296875 0.65625,-0.625 0,-0.34375 -0.3125,-0.640625 -0.640625,-0.640625 z m 0,0"
+ id="path122"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-41"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 1.390625,-6.59375 c 1.40625,0.046875 2.015625,0.046875 3.328125,0.046875 L 4.765625,-6.59375 C 4.75,-6.796875 4.75,-6.875 4.75,-7.03125 c 0,-0.140625 0,-0.234375 0.015625,-0.421875 l -0.046875,-0.0625 c -0.828125,0.046875 -1.3125,0.0625 -1.90625,0.0625 -0.59375,0 -1.078125,-0.015625 -1.890625,-0.0625 l -0.0625,0.0625 c 0.015625,0.65625 0.03125,1.140625 0.03125,1.484375 0,0.9375 -0.046875,1.984375 -0.078125,2.453125 l 0.21875,0.0625 c 0.515625,-0.5 0.8125,-0.65625 1.34375,-0.65625 1.046875,0 1.703125,0.734375 1.703125,1.921875 0,1.203125 -0.703125,1.921875 -1.890625,1.921875 -0.578125,0 -1.125,-0.203125 -1.28125,-0.484375 l -0.5,-0.890625 L 0.140625,-1.5 c 0.25,0.625 0.390625,0.96875 0.53125,1.453125 0.3125,0.171875 0.75,0.265625 1.21875,0.265625 0.734375,0 1.5,-0.3125 2.09375,-0.84375 C 4.65625,-1.203125 5,-1.953125 5,-2.765625 c 0,-1.21875 -0.875,-2.09375 -2.125,-2.09375 -0.515625,0 -0.90625,0.140625 -1.59375,0.546875 z m 0,0"
+ id="path125"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-42"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 4.53125,-7.328125 -0.390625,-0.1875 c -1.046875,0.34375 -1.5,0.5625 -2.0625,1.046875 -1.125,0.953125 -1.734375,2.28125 -1.734375,3.78125 0,1.796875 0.875,2.90625 2.28125,2.90625 1.421875,0 2.484375,-1.125 2.484375,-2.609375 0,-1.25 -0.75,-2.0625 -1.90625,-2.0625 -0.546875,0 -0.875,0.140625 -1.53125,0.640625 -0.109375,0.109375 -0.125,0.109375 -0.25,0.203125 0.234375,-1.96875 1.15625,-3.046875 3.109375,-3.625 z m -1.75,3.515625 c 0.828125,0 1.390625,0.71875 1.390625,1.8125 0,1.125 -0.5625,1.859375 -1.40625,1.859375 -0.921875,0 -1.421875,-0.796875 -1.421875,-2.265625 0,-0.375 0.046875,-0.578125 0.15625,-0.765625 0.25,-0.375 0.75,-0.640625 1.28125,-0.640625 z m 0,0"
+ id="path128"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-43"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 3.140625,-2.765625 1.546875,-1.8125 c 0.0625,-0.078125 0.1875,-0.125 0.375,-0.125 H 5.21875 V -5 H 4.3125 L 2.96875,-3.09375 2.140625,-4.359375 c -0.171875,-0.25 -0.28125,-0.390625 -0.65625,-0.75 L 0.21875,-4.890625 0.265625,-4.609375 0.5,-4.59375 c 0.203125,0.015625 0.4375,0.171875 0.546875,0.3125 l 1.3125,1.90625 -1.625,1.921875 c -0.078125,0.09375 -0.21875,0.15625 -0.328125,0.15625 H 0.21875 V 0 h 0.9375 C 1.234375,-0.125 1.25,-0.15625 1.390625,-0.359375 1.5625,-0.671875 1.734375,-0.9375 1.8125,-1.03125 l 0.8125,-1.0625 1.15625,1.765625 0.015625,0.015625 c 0.09375,0.15625 0.1875,0.265625 0.265625,0.34375 C 4.640625,0 4.640625,0 4.734375,0 4.84375,0 4.84375,0 5.40625,0.03125 V -0.296875 L 5.140625,-0.328125 C 5,-0.34375 4.875,-0.421875 4.75,-0.59375 Z m 0,0"
+ id="path131"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-44"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 2.359375,-3.5625 c 0.296875,-0.03125 0.625,-0.03125 1.125,-0.03125 0.796875,0 1.09375,0.03125 1.296875,0.125 l 0.078125,0.265625 0.0625,0.65625 H 5.25 C 5.21875,-3.5625 5.21875,-3.5625 5.21875,-3.765625 c 0,-0.171875 0,-0.171875 0.03125,-1.265625 H 4.921875 l -0.0625,0.578125 c 0,0.09375 -0.03125,0.1875 -0.078125,0.28125 -0.203125,0.078125 -0.5,0.109375 -1.21875,0.109375 -0.46875,0 -0.84375,0 -1.203125,-0.03125 V -7 C 2.6875,-7.078125 2.875,-7.09375 3.515625,-7.09375 c 0.53125,0 1.140625,0.0625 1.484375,0.15625 0.25,0.0625 0.296875,0.125 0.296875,0.375 v 0.703125 h 0.375 c 0,-0.65625 0.046875,-1.171875 0.171875,-1.65625 C 5.3125,-7.53125 4.984375,-7.546875 4.515625,-7.546875 L 3.34375,-7.53125 c -0.53125,0.015625 -0.90625,0.015625 -1.1875,0.015625 -0.3125,0 -0.3125,0 -1.921875,-0.03125 v 0.328125 l 0.53125,0.03125 c 0.5,0.03125 0.546875,0.125 0.546875,0.984375 V -1.3125 c 0,0.859375 -0.046875,0.953125 -0.546875,0.984375 l -0.53125,0.03125 V 0.03125 C 0.890625,0.015625 1.265625,0 1.828125,0 2.40625,0 2.796875,0.015625 3.4375,0.03125 v -0.328125 l -0.53125,-0.03125 c -0.5,-0.03125 -0.546875,-0.125 -0.546875,-0.984375 z m 0,0"
+ id="path134"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph0-45"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 1.828125,-3.765625 c -0.4375,0.203125 -0.625,0.328125 -0.875,0.5625 -0.40625,0.40625 -0.625,0.90625 -0.625,1.46875 0,1.125 0.90625,1.953125 2.140625,1.953125 1.40625,0 2.59375,-1.109375 2.59375,-2.46875 0,-0.875 -0.40625,-1.34375 -1.65625,-1.90625 1,-0.671875 1.34375,-1.140625 1.34375,-1.78125 0,-0.9375 -0.765625,-1.578125 -1.921875,-1.578125 -1.296875,0 -2.25,0.828125 -2.25,1.96875 0,0.75 0.296875,1.15625 1.25,1.78125 z M 3.09375,-3.21875 c 0.6875,0.3125 1.109375,0.84375 1.109375,1.4375 0,0.90625 -0.6875,1.625 -1.578125,1.625 -0.921875,0 -1.53125,-0.65625 -1.53125,-1.671875 0,-0.796875 0.328125,-1.296875 1.125,-1.78125 z M 2.4375,-4.609375 C 1.75,-4.953125 1.390625,-5.390625 1.390625,-5.9375 c 0,-0.71875 0.53125,-1.203125 1.296875,-1.203125 0.796875,0 1.328125,0.515625 1.328125,1.3125 0,0.625 -0.28125,1.046875 -0.984375,1.515625 z m 0,0"
+ id="path137"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-0"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d=""
+ id="path140"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-1"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 7.453125,-0.921875 7.390625,-1.03125 C 6.625,-0.59375 5.84375,-0.375 5.015625,-0.375 c -2.125,0 -3.3125,-1.40625 -2.953125,-3.5625 0.34375,-2 1.796875,-3.34375 3.671875,-3.34375 1.078125,0 2.109375,0.4375 2.03125,0.875 l -0.125,0.78125 H 7.96875 c 0.109375,-0.546875 0.21875,-0.9375 0.5,-1.65625 C 7.578125,-7.59375 6.8125,-7.734375 6.015625,-7.734375 5.0625,-7.734375 4.125,-7.5 3.3125,-7.078125 1.953125,-6.34375 1.109375,-5.203125 0.859375,-3.734375 0.46875,-1.375 1.9375,0.21875 4.5,0.21875 c 0.90625,0 1.75,-0.203125 2.5625,-0.59375 z m 0,0"
+ id="path143"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-2"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 6.9375,-1.3125 C 6.796875,-0.453125 6.734375,-0.359375 6.21875,-0.328125 L 5.6875,-0.296875 5.625,0.03125 C 6.28125,0.015625 6.65625,0 7.234375,0 7.796875,0 8.1875,0.015625 8.828125,0.03125 l 0.0625,-0.328125 -0.53125,-0.03125 C 7.875,-0.359375 7.828125,-0.453125 7.96875,-1.3125 L 8.796875,-6.203125 C 8.9375,-7.0625 9.015625,-7.15625 9.515625,-7.1875 l 0.53125,-0.03125 0.04687,-0.328125 c -0.796875,0.03125 -1.046875,0.03125 -1.609375,0.03125 -0.546875,0 -0.8125,-0.015625 -1.578125,-0.03125 l -0.0625,0.328125 0.515625,0.03125 c 0.5,0.03125 0.53125,0.125 0.390625,0.984375 l -0.328125,1.9375 C 7.203125,-4.25 6.96875,-4.25 6.515625,-4.25 H 3.96875 c -0.453125,0 -0.6875,0 -0.90625,-0.015625 l 0.328125,-1.9375 C 3.53125,-7.0625 3.609375,-7.15625 4.109375,-7.1875 l 0.53125,-0.03125 0.0625,-0.328125 c -0.8125,0.03125 -1.046875,0.03125 -1.609375,0.03125 -0.546875,0 -0.8125,-0.015625 -1.59375,-0.03125 L 1.453125,-7.21875 1.96875,-7.1875 c 0.5,0.03125 0.53125,0.125 0.390625,0.984375 L 1.53125,-1.3125 C 1.390625,-0.453125 1.328125,-0.359375 0.8125,-0.328125 l -0.515625,0.03125 -0.0625,0.328125 C 0.890625,0.015625 1.265625,0 1.828125,0 2.40625,0 2.796875,0.015625 3.421875,0.03125 l 0.0625,-0.328125 -0.515625,-0.03125 c -0.5,-0.03125 -0.53125,-0.125 -0.390625,-0.984375 l 0.40625,-2.421875 c 0.296875,-0.03125 0.46875,-0.03125 0.90625,-0.03125 h 2.5625 c 0.421875,0 0.609375,0 0.890625,0.03125 z m 0,0"
+ id="path146"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-3"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 5.71875,-7.640625 H 5.375 c -0.265625,0.46875 -0.5,0.875 -0.59375,1.046875 l -0.796875,1.3125 -2.6875,4.421875 c -0.1875,0.3125 -0.4375,0.515625 -0.671875,0.53125 L 0.21875,-0.296875 0.15625,0.03125 C 1.171875,0 1.171875,0 1.359375,0 1.5,0 1.5,0 2.65625,0.03125 l 0.0625,-0.328125 -0.390625,-0.03125 c -0.375,-0.03125 -0.5,-0.109375 -0.484375,-0.28125 C 1.875,-0.75 2.109375,-1.21875 2.421875,-1.765625 L 2.84375,-2.5 h 3.140625 l 0.296875,1.25 c 0.109375,0.46875 0.125,0.609375 0.109375,0.703125 C 6.375,-0.421875 6.1875,-0.34375 5.90625,-0.328125 l -0.5,0.03125 L 5.328125,0.03125 C 6.6875,0 6.6875,0 6.859375,0 7.0625,0 7.0625,0 8.234375,0.03125 l 0.0625,-0.328125 -0.375,-0.03125 C 7.65625,-0.375 7.5625,-0.5625 7.328125,-1.4375 Z m -2.609375,4.703125 1.921875,-3.25 0.84375,3.25 z m 0,0"
+ id="path149"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-4"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 3.515625,-7 c 0.40625,-0.09375 0.734375,-0.125 1.09375,-0.125 1.1875,0 1.734375,0.53125 1.546875,1.578125 C 6,-4.5625 5.21875,-3.875 4.265625,-3.875 4.03125,-3.875 3.875,-3.90625 3.609375,-3.984375 L 3.65625,-3.59375 c 0.25,0.046875 0.40625,0.0625 0.625,0.0625 1.484375,0 2.8125,-1.015625 3.03125,-2.328125 0.171875,-1.046875 -0.578125,-1.6875 -1.953125,-1.6875 -0.0625,0 -0.171875,0 -0.328125,0.015625 -0.390625,0 -1.21875,0.015625 -1.546875,0.015625 -0.234375,0 -0.234375,0 -1.96875,-0.03125 L 1.46875,-7.21875 1.9375,-7.1875 c 0.5,0.03125 0.546875,0.171875 0.421875,0.984375 L 1.53125,-1.3125 C 1.390625,-0.453125 1.328125,-0.359375 0.8125,-0.328125 l -0.515625,0.03125 -0.0625,0.328125 C 0.890625,0.015625 1.265625,0 1.828125,0 2.40625,0 2.796875,0.015625 3.421875,0.03125 l 0.0625,-0.328125 -0.515625,-0.03125 c -0.5,-0.03125 -0.53125,-0.125 -0.390625,-0.984375 z m 0,0"
+ id="path152"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-5"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 3.953125,-7.015625 c -0.03125,0.375 -0.0625,0.625 -0.140625,1.09375 L 3.046875,-1.3125 c -0.140625,0.859375 -0.21875,0.953125 -0.71875,0.984375 l -0.53125,0.03125 -0.0625,0.328125 C 2.390625,0.015625 2.765625,0 3.34375,0 3.90625,0 4.296875,0.015625 4.921875,0.03125 L 5,-0.296875 4.46875,-0.328125 C 3.984375,-0.359375 3.9375,-0.453125 4.078125,-1.3125 l 0.78125,-4.609375 c 0.0625,-0.484375 0.125,-0.734375 0.21875,-1.09375 h 1.6875 c 0.296875,0 0.40625,0.09375 0.375,0.359375 l -0.09375,0.859375 h 0.34375 C 7.5,-6.5 7.59375,-6.921875 7.75,-7.546875 6.953125,-7.53125 6.59375,-7.53125 6.296875,-7.53125 5.875,-7.515625 5.546875,-7.515625 5.390625,-7.515625 H 3.671875 C 3.5625,-7.515625 2.96875,-7.53125 2.1875,-7.53125 L 1.46875,-7.546875 c -0.0625,0.625 -0.109375,1.046875 -0.21875,1.75 h 0.328125 l 0.1875,-0.859375 c 0.0625,-0.265625 0.1875,-0.359375 0.484375,-0.359375 z m 0,0"
+ id="path155"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-6"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 1.453125,-7.21875 1.96875,-7.1875 c 0.5,0.03125 0.53125,0.125 0.390625,0.984375 L 1.53125,-1.3125 C 1.390625,-0.453125 1.34375,-0.375 0.8125,-0.328125 l -0.390625,0.03125 -0.0625,0.328125 C 1.09375,0.015625 1.40625,0 1.71875,0 c 0.046875,0 0.359375,0 0.765625,0.015625 1.5,0.015625 1.5,0.015625 1.75,0.015625 0.328125,0 0.328125,0 1.828125,-0.03125 0.109375,-0.625 0.28125,-1.21875 0.46875,-1.78125 h -0.375 c -0.046875,0.171875 -0.109375,0.3125 -0.109375,0.359375 -0.140625,0.46875 -0.359375,0.8125 -0.5,0.859375 -0.25,0.078125 -1.046875,0.140625 -2.03125,0.140625 -0.578125,0 -0.765625,-0.03125 -1.078125,-0.09375 L 2.953125,-3.5625 c 0.3125,-0.03125 0.640625,-0.03125 1.125,-0.03125 0.8125,0 1.109375,0.03125 1.28125,0.125 l 0.046875,0.265625 -0.0625,0.65625 H 5.671875 C 5.8125,-3.5625 5.8125,-3.5625 5.84375,-3.765625 5.875,-3.9375 5.875,-3.9375 6.078125,-5.03125 h -0.3125 l -0.15625,0.578125 c -0.03125,0.09375 -0.078125,0.1875 -0.140625,0.28125 C 5.265625,-4.09375 4.953125,-4.0625 4.234375,-4.0625 c -0.453125,0 -0.828125,0 -1.1875,-0.03125 L 3.515625,-7 C 3.875,-7.078125 4.0625,-7.09375 4.828125,-7.09375 c 0.609375,0 1.21875,0.0625 1.546875,0.15625 0.25,0.0625 0.265625,0.125 0.234375,0.375 l -0.125,0.703125 H 6.875 c 0.109375,-0.65625 0.25,-1.171875 0.4375,-1.65625 -0.53125,-0.015625 -0.859375,-0.03125 -1.328125,-0.03125 -0.28125,0 -0.640625,0 -1.09375,0.015625 -0.6875,0 -1.15625,0.015625 -1.453125,0.015625 -0.375,0 -0.859375,-0.015625 -1.9375,-0.03125 z m 0,0"
+ id="path158"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-7"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 3.515625,-7 c 0.40625,-0.09375 0.734375,-0.125 1.09375,-0.125 1.140625,0 1.6875,0.515625 1.53125,1.4375 -0.15625,1.015625 -1.0625,1.640625 -2.328125,1.640625 -0.078125,0 -0.1875,0 -0.328125,-0.015625 L 3.40625,-3.921875 C 3.5,-3.78125 3.515625,-3.734375 3.625,-3.59375 3.75,-3.421875 3.78125,-3.375 3.859375,-3.25 l 1.5625,2.6875 c 0.046875,0.09375 0.09375,0.1875 0.15625,0.25 C 5.625,-0.203125 5.6875,-0.109375 5.765625,0.03125 6.28125,0 6.390625,0 6.515625,0 6.609375,0 6.609375,0 7.28125,0.03125 l 0.0625,-0.328125 C 7,-0.328125 6.84375,-0.390625 6.734375,-0.578125 L 4.78125,-3.8125 C 5.4375,-3.953125 5.71875,-4.078125 6.125,-4.328125 6.765625,-4.734375 7.171875,-5.28125 7.28125,-5.90625 7.453125,-6.953125 6.78125,-7.546875 5.4375,-7.546875 H 5.25 c -1.421875,0.03125 -1.421875,0.03125 -1.796875,0.03125 -0.359375,0 -0.359375,0 -1.875,-0.03125 L 1.53125,-7.21875 1.96875,-7.1875 c 0.5,0.03125 0.53125,0.125 0.390625,0.984375 L 1.53125,-1.3125 C 1.390625,-0.453125 1.328125,-0.359375 0.8125,-0.328125 l -0.515625,0.03125 -0.0625,0.328125 C 0.890625,0.015625 1.265625,0 1.828125,0 2.40625,0 2.796875,0.015625 3.421875,0.03125 l 0.0625,-0.328125 -0.515625,-0.03125 c -0.5,-0.03125 -0.53125,-0.125 -0.390625,-0.984375 z m 0,0"
+ id="path161"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-8"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 3.375,-1.96875 3.25,-1.15625 C 3.140625,-0.5 2.984375,-0.34375 2.453125,-0.328125 L 1.78125,-0.296875 1.71875,0.03125 C 3.171875,0 3.171875,0 3.4375,0 3.703125,0 3.703125,0 5.140625,0.03125 l 0.0625,-0.328125 -0.53125,-0.03125 C 4.15625,-0.359375 4.0625,-0.5 4.171875,-1.15625 l 0.125,-0.8125 c 0.640625,0 0.875,0 1.171875,0.03125 l 0.125,-0.765625 C 5.078125,-2.65625 4.875,-2.65625 4.71875,-2.671875 H 4.421875 l 0.25,-1.515625 c 0.328125,-1.9375 0.5,-2.875 0.625,-3.328125 L 5.1875,-7.5625 l -0.875,0.28125 -3.921875,5.0625 0.078125,0.25 z M 3.5,-2.671875 H 1.15625 l 2.984375,-3.84375 z m 0,0"
+ id="path164"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-9"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 1.5625,-1.203125 c -0.328125,0 -0.671875,0.296875 -0.734375,0.625 -0.046875,0.328125 0.1875,0.625 0.5,0.625 0.34375,0 0.71875,-0.28125 0.765625,-0.625 0.0625,-0.328125 -0.203125,-0.625 -0.53125,-0.625 z m 0,0"
+ id="path167"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-10"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 5.828125,-5.734375 C 5.9375,-6.3125 6.03125,-6.6875 6.234375,-7.28125 5.4375,-7.625 4.953125,-7.734375 4.359375,-7.734375 c -1.640625,0 -2.96875,1.03125 -3.21875,2.453125 -0.078125,0.53125 0,0.921875 0.25,1.21875 0.328125,0.34375 0.796875,0.515625 1.828125,0.65625 0.8125,0.125 1.171875,0.25 1.421875,0.5 0.25,0.21875 0.328125,0.546875 0.25,0.953125 -0.15625,0.984375 -1.09375,1.6875 -2.234375,1.6875 -0.875,0 -1.640625,-0.421875 -1.625,-0.875 L 1.0625,-1.859375 H 0.71875 c -0.015625,0.1875 -0.046875,0.359375 -0.0625,0.4375 -0.09375,0.5 -0.140625,0.734375 -0.296875,1.21875 0.578125,0.28125 1.1875,0.421875 1.828125,0.421875 1.890625,0 3.453125,-1.140625 3.71875,-2.734375 0.078125,-0.5 0,-0.859375 -0.25,-1.15625 -0.328125,-0.359375 -0.765625,-0.5 -1.953125,-0.65625 C 2.40625,-4.5 1.984375,-4.859375 2.125,-5.703125 2.28125,-6.625 3.078125,-7.28125 4.03125,-7.28125 c 0.421875,0 0.828125,0.09375 1.09375,0.25 0.328125,0.1875 0.421875,0.359375 0.390625,0.671875 l -0.03125,0.625 z m 0,0"
+ id="path170"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-11"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 5.671875,-7.734375 c -2.484375,0 -4.40625,1.609375 -4.8125,4.015625 C 0.484375,-1.421875 1.796875,0.21875 4,0.21875 6.453125,0.21875 8.609375,-1.609375 9,-4 9.375,-6.296875 8.09375,-7.734375 5.671875,-7.734375 Z M 5.40625,-7.28125 c 1.875,0 2.75,1.34375 2.375,3.609375 -0.359375,2.15625 -1.578125,3.4375 -3.265625,3.4375 -1.828125,0 -2.8125,-1.546875 -2.421875,-3.890625 0.34375,-2.015625 1.53125,-3.15625 3.3125,-3.15625 z m 0,0"
+ id="path173"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-12"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 3.390625,-6.203125 C 3.53125,-7.0625 3.609375,-7.15625 4.109375,-7.1875 l 0.53125,-0.03125 0.0625,-0.328125 c -0.8125,0.03125 -1.046875,0.03125 -1.609375,0.03125 -0.546875,0 -0.8125,-0.015625 -1.59375,-0.03125 L 1.453125,-7.21875 1.96875,-7.1875 c 0.5,0.03125 0.53125,0.125 0.390625,0.984375 L 1.5,-1.03125 c -0.0625,0.3125 -0.203125,0.546875 -0.390625,0.640625 l -0.375,0.171875 -0.03125,0.25 C 1.421875,0 1.5,0 1.78125,0 1.890625,0 2.0625,0 2.296875,0.015625 c 0.53125,0 1.71875,0.015625 2.015625,0.015625 0.359375,0 0.359375,0 1.84375,-0.03125 0.15625,-0.46875 0.265625,-0.8125 0.546875,-1.875 H 6.359375 L 6.125,-1.328125 c 0,0.03125 -0.03125,0.109375 -0.046875,0.125 -0.109375,0.3125 -0.234375,0.53125 -0.296875,0.578125 -0.171875,0.125 -1.125,0.203125 -2.421875,0.203125 -0.390625,0 -0.640625,-0.03125 -0.921875,-0.09375 z m 0,0"
+ id="path176"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-13"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 3.390625,-6.203125 C 3.53125,-7.0625 3.609375,-7.15625 4.109375,-7.1875 l 0.53125,-0.03125 0.0625,-0.328125 c -0.8125,0.03125 -1.046875,0.03125 -1.609375,0.03125 -0.546875,0 -0.8125,-0.015625 -1.59375,-0.03125 L 1.453125,-7.21875 1.96875,-7.1875 c 0.5,0.03125 0.53125,0.125 0.390625,0.984375 L 1.53125,-1.3125 C 1.390625,-0.453125 1.328125,-0.359375 0.8125,-0.328125 l -0.515625,0.03125 -0.0625,0.328125 C 0.890625,0.015625 1.265625,0 1.828125,0 2.40625,0 2.796875,0.015625 3.421875,0.03125 l 0.0625,-0.328125 -0.515625,-0.03125 c -0.5,-0.03125 -0.53125,-0.125 -0.390625,-0.984375 z m 0,0"
+ id="path179"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-14"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 0.65625,-0.21875 -0.046875,0.25 C 1.890625,0 1.890625,0 2.140625,0 c 0.1875,0 0.375,0 0.5625,0.015625 0.859375,0.015625 0.859375,0.015625 1,0.015625 1.40625,0 2.515625,-0.359375 3.40625,-1.109375 0.90625,-0.78125 1.578125,-1.953125 1.78125,-3.125 0.34375,-2.03125 -0.9375,-3.34375 -3.234375,-3.34375 -0.078125,0 -0.21875,0 -0.4375,0.015625 -0.546875,0 -1.015625,0.015625 -1.484375,0.015625 -0.390625,0 -1,-0.015625 -2.234375,-0.03125 L 1.453125,-7.21875 1.875,-7.1875 c 0.5,0.03125 0.53125,0.125 0.390625,0.984375 L 1.40625,-1.03125 c -0.046875,0.3125 -0.203125,0.546875 -0.375,0.640625 z m 2.796875,-6.8125 c 0.28125,-0.046875 0.671875,-0.078125 1.21875,-0.078125 0.796875,0 1.640625,0.140625 2.0625,0.34375 0.21875,0.109375 0.40625,0.25 0.578125,0.453125 C 7.75,-5.796875 7.859375,-5 7.671875,-3.921875 7.46875,-2.6875 6.953125,-1.75 6.125,-1.171875 5.390625,-0.625 4.703125,-0.453125 3.46875,-0.453125 c -0.5,0 -0.84375,-0.015625 -1.109375,-0.078125 z m 0,0"
+ id="path182"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-15"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 8.203125,-1.3125 C 8.0625,-0.453125 8,-0.375 7.484375,-0.328125 l -0.375,0.03125 -0.0625,0.328125 C 7.5625,0.015625 7.890625,0 8.453125,0 c 0.609375,0 1,0.015625 1.640625,0.03125 L 10.15625,-0.296875 9.625,-0.328125 C 9.140625,-0.359375 9.09375,-0.453125 9.234375,-1.3125 L 10.0625,-6.203125 C 10.203125,-7.0625 10.28125,-7.15625 10.78125,-7.1875 l 0.46875,-0.03125 0.04687,-0.328125 c -0.84375,0.03125 -0.84375,0.03125 -1.015625,0.03125 -0.171875,0 -0.171875,0 -1.03125,-0.03125 l -3.875,6.0625 -1.859375,-6.0625 C 2.625,-7.515625 2.625,-7.515625 2.46875,-7.515625 c -0.171875,0 -0.171875,0 -1.03125,-0.03125 l -0.046875,0.328125 0.46875,0.03125 c 0.5,0.03125 0.53125,0.125 0.390625,0.984375 L 1.421875,-1.3125 C 1.28125,-0.453125 1.21875,-0.359375 0.703125,-0.328125 L 0.234375,-0.296875 0.15625,0.03125 C 1.25,0 1.25,0 1.4375,0 1.609375,0 1.609375,0 2.734375,0.03125 l 0.0625,-0.328125 -0.46875,-0.03125 c -0.5,-0.03125 -0.546875,-0.125 -0.390625,-0.984375 l 0.828125,-4.984375 1.96875,6.4375 h 0.21875 c 0.09375,-0.15625 0.1875,-0.34375 0.296875,-0.5 C 5.484375,-0.78125 5.671875,-1.09375 5.765625,-1.25 l 2.625,-4.109375 C 8.609375,-5.6875 8.78125,-5.9375 9.046875,-6.296875 Z m 0,0"
+ id="path185"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph1-16"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 2.75,-7.515625 C 2.140625,-7.53125 2.046875,-7.53125 1.453125,-7.546875 l -0.0625,0.328125 0.46875,0.03125 c 0.5,0.03125 0.546875,0.125 0.390625,0.984375 L 1.4375,-1.3125 c -0.140625,0.859375 -0.203125,0.953125 -0.71875,0.984375 l -0.484375,0.03125 -0.0625,0.328125 C 1.265625,0 1.265625,0 1.453125,0 1.609375,0 1.609375,0 2.75,0.03125 L 2.8125,-0.296875 2.328125,-0.328125 C 1.84375,-0.359375 1.796875,-0.453125 1.9375,-1.3125 L 2.78125,-6.359375 6.875,0.046875 7.828125,0.21875 c 0,-0.046875 0,-0.0625 0.015625,-0.125 0.015625,-0.125 0.015625,-0.21875 0.015625,-0.28125 l 1,-6.015625 C 9.015625,-7.0625 9.078125,-7.15625 9.59375,-7.1875 l 0.484375,-0.03125 0.04687,-0.328125 c -1.15625,0.03125 -1.15625,0.03125 -1.3125,0.03125 -0.1875,0 -0.1875,0 -1.25,-0.03125 L 7.5,-7.21875 7.96875,-7.1875 c 0.5,0.03125 0.53125,0.125 0.390625,0.984375 L 7.5,-1.0625 3.34375,-7.546875 Z m 0,0"
+ id="path188"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-0"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d=""
+ id="path191"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-1"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 4.8125,-0.1875 v -1.015625 c -0.609375,0.21875 -1.03125,0.34375 -1.4375,0.34375 -0.984375,0 -1.765625,-0.625 -1.765625,-1.609375 0,-0.921875 0.609375,-1.625 1.53125,-1.625 0.21875,0 0.421875,0.03125 0.640625,0.09375 v 0.453125 H 4.734375 V -4.75 C 4.15625,-4.921875 3.671875,-5.015625 3.15625,-5.015625 c -1.53125,0 -2.734375,0.96875 -2.734375,2.578125 0,1.640625 1.25,2.53125 2.765625,2.53125 0.515625,0 1.03125,-0.09375 1.625,-0.28125 z m 0,0"
+ id="path194"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-2"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 4.921875,-2.453125 C 4.921875,-3.875 4.171875,-5 2.609375,-5 c -1.546875,0 -2.3125,1.125 -2.3125,2.546875 0,1.421875 0.765625,2.546875 2.3125,2.546875 1.5625,0 2.3125,-1.125 2.3125,-2.546875 z m -1.171875,0 C 3.75,-1.625 3.453125,-0.8125 2.609375,-0.8125 c -0.828125,0 -1.125,-0.828125 -1.125,-1.640625 0,-0.8125 0.296875,-1.625 1.125,-1.625 0.84375,0 1.140625,0.796875 1.140625,1.625 z m 0,0"
+ id="path197"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-3"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 5.28125,0 V -0.921875 H 4.578125 v -2.375 C 4.578125,-4.234375 4.21875,-5 3.109375,-5 c -0.59375,0 -1,0.25 -1.3125,0.640625 v -0.53125 h -1.84375 V -3.96875 H 0.65625 v 3.046875 H -0.046875 V 0 H 2.5 V -0.921875 H 1.796875 v -2.1875 C 2.09375,-3.515625 2.40625,-3.90625 2.84375,-3.90625 c 0.5,0 0.578125,0.296875 0.578125,0.84375 V 0 Z m 0,0"
+ id="path200"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-4"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 4.828125,-1.46875 c 0,-1.15625 -1.359375,-1.390625 -2.203125,-1.625 -0.25,-0.0625 -0.859375,-0.21875 -0.859375,-0.5625 0,-0.421875 0.59375,-0.4375 0.921875,-0.4375 0.234375,0 0.546875,0.046875 0.796875,0.09375 v 0.453125 H 4.4375 v -1.1875 c -0.609375,-0.1875 -1.234375,-0.28125 -1.765625,-0.28125 -0.859375,0 -2,0.34375 -2,1.484375 0,1.125 1.375,1.375 2.171875,1.609375 0.25,0.078125 0.859375,0.234375 0.859375,0.5625 0,0.484375 -0.75,0.546875 -1.125,0.546875 -0.296875,0 -0.671875,-0.0625 -0.96875,-0.140625 V -1.46875 h -0.96875 v 1.296875 C 1.3125,-0.015625 1.96875,0.09375 2.5625,0.09375 c 0.875,0 2.265625,-0.328125 2.265625,-1.5625 z m 0,0"
+ id="path203"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-5"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 4.8125,-0.015625 V -0.96875 C 4.3125,-0.875 3.984375,-0.8125 3.640625,-0.8125 2.90625,-0.8125 2.6875,-0.921875 2.6875,-1.65625 V -3.875 H 4.28125 V -4.796875 H 2.6875 V -5.78125 h -1.125 v 0.984375 H 0.40625 V -3.875 H 1.5625 v 2.078125 c 0,1.4375 0.453125,1.890625 1.890625,1.890625 0.421875,0 0.828125,-0.03125 1.359375,-0.109375 z m 0,0"
+ id="path206"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-6"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 5.328125,0 V -0.921875 H 4.78125 l -1.3125,-2.03125 c 0.6875,-0.390625 1.171875,-1.0625 1.171875,-1.921875 0,-1.359375 -1.328125,-1.65625 -2.25,-1.65625 H 0.09375 v 0.921875 h 0.625 v 4.6875 h -0.625 V 0 h 2.390625 v -0.921875 h -0.625 V -2.65625 H 2.375 L 4.09375,0 Z m -1.875,-4.734375 c 0,0.703125 -0.671875,1.140625 -1.3125,1.140625 H 1.859375 V -5.609375 H 2.25 c 0.609375,0 1.203125,0.203125 1.203125,0.875 z m 0,0"
+ id="path209"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-7"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 4.796875,-0.21875 V -1.203125 C 4.140625,-0.9375 3.65625,-0.8125 3.171875,-0.8125 2.265625,-0.8125 1.75,-1.21875 1.640625,-2.078125 H 4.78125 C 4.78125,-3.5625 4.5625,-5 2.765625,-5 c -1.484375,0 -2.3125,1.265625 -2.3125,2.609375 0,1.640625 1.015625,2.484375 2.609375,2.484375 0.5625,0 1.109375,-0.109375 1.734375,-0.3125 z m -1.21875,-2.796875 h -1.90625 c 0.109375,-0.578125 0.4375,-1.0625 1.03125,-1.0625 0.640625,0 0.859375,0.421875 0.875,1.0625 z m 0,0"
+ id="path212"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-8"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 5.171875,-0.09375 -0.125,-0.796875 C 4.96875,-0.875 4.75,-0.8125 4.671875,-0.8125 c -0.328125,0 -0.28125,-0.328125 -0.28125,-0.578125 V -3.3125 C 4.390625,-4.5625 3.84375,-5 2.640625,-5 2.046875,-5 1.46875,-4.875 0.8125,-4.625 v 1.0625 C 1.5,-3.921875 1.96875,-4.078125 2.46875,-4.078125 c 0.578125,0 0.796875,0.1875 0.796875,0.765625 v 0.234375 h -0.375 c -0.984375,0 -2.46875,0.375 -2.46875,1.75 0,0.90625 0.796875,1.390625 1.609375,1.390625 0.546875,0 1.046875,-0.234375 1.453125,-0.53125 0.171875,0.328125 0.46875,0.53125 0.875,0.53125 0.140625,0 0.25,-0.015625 0.359375,-0.03125 C 4.828125,0 4.9375,-0.03125 5.171875,-0.09375 Z M 3.265625,-1.3125 c -0.3125,0.25 -0.65625,0.421875 -1.015625,0.421875 -0.390625,0 -0.671875,-0.21875 -0.671875,-0.578125 0,-0.765625 1.015625,-0.78125 1.578125,-0.78125 h 0.109375 z m 0,0"
+ id="path215"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-9"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 4.640625,0 V -0.921875 H 3.1875 v -6.0625 H 0.578125 V -6.0625 h 1.46875 v 5.140625 H 0.578125 V 0 Z m 0,0"
+ id="path218"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-10"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 5.546875,-5.890625 V -6.875 c -0.53125,-0.15625 -0.984375,-0.21875 -1.4375,-0.21875 -1.84375,0 -2.046875,1.25 -2.0625,2.640625 H 0.8125 v 0.921875 h 1.234375 v 2.609375 H 0.59375 V 0 H 4.640625 V -0.921875 H 3.1875 V -3.53125 H 4.859375 V -4.453125 H 3.1875 V -4.8125 c 0,-0.84375 0.125,-1.359375 1,-1.359375 0.390625,0 0.78125,0.078125 1.359375,0.28125 z m 0,0"
+ id="path221"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-11"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 5,-3.296875 v -1.5625 C 4.578125,-4.953125 4.28125,-5 3.984375,-5 3.34375,-5 2.828125,-4.734375 2.4375,-4.3125 V -4.890625 H 0.421875 V -3.96875 H 1.28125 v 3.046875 H 0.1875 V 0 H 3.734375 V -0.921875 H 2.4375 V -3.09375 c 0.390625,-0.53125 0.78125,-0.875 1.4375,-0.875 0.0625,0 0.125,0.015625 0.1875,0.015625 v 0.65625 z m 0,0"
+ id="path224"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-12"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 5.296875,-5.609375 V -6.53125 H 3.15625 v 0.921875 H 3.703125 V -2.3125 c 0,0.875 -0.15625,1.515625 -1.0625,1.515625 -0.84375,0 -1,-0.65625 -1,-1.484375 V -5.609375 H 2.21875 V -6.53125 H -0.0625 v 0.921875 h 0.578125 v 3.3125 c 0,1.40625 0.546875,2.421875 2.140625,2.421875 1.65625,0 2.078125,-1.1875 2.078125,-2.5625 v -3.171875 z m 0,0"
+ id="path227"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph2-13"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 4.640625,0 V -0.921875 H 3.1875 v -4.6875 H 4.640625 V -6.53125 h -4.0625 v 0.921875 h 1.46875 v 4.6875 H 0.578125 V 0 Z m 0,0"
+ id="path230"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-0"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d=""
+ id="path233"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-1"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 4.859375,0.578125 v -2.03125 h -0.625 V 0 h -3.25 V -1.453125 H 0.375 v 2.03125 z m 0,0"
+ id="path236"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-2"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 4.6875,-1.3125 C 4.6875,-2.953125 1.609375,-2.5 1.609375,-3.5 c 0,-0.5 0.6875,-0.59375 1.078125,-0.59375 0.28125,0 0.65625,0.0625 0.9375,0.109375 v 0.4375 H 4.28125 V -4.46875 c -0.484375,-0.140625 -1.09375,-0.25 -1.609375,-0.25 -0.859375,0 -1.84375,0.34375 -1.84375,1.34375 0,1.5625 3.015625,1.1875 3.015625,2.171875 0,0.5625 -0.828125,0.6875 -1.265625,0.6875 -0.34375,0 -0.796875,-0.078125 -1.125,-0.1875 v -0.46875 H 0.796875 V -0.15625 C 1.34375,-0.015625 2,0.09375 2.5625,0.09375 c 0.890625,0 2.125,-0.328125 2.125,-1.40625 z m 0,0"
+ id="path239"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-3"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 5,-0.046875 4.921875,-0.5625 c -0.078125,0.03125 -0.171875,0.046875 -0.25,0.046875 -0.4375,0 -0.4375,-0.40625 -0.4375,-0.71875 V -3.15625 c 0,-1.15625 -0.453125,-1.546875 -1.59375,-1.546875 -0.5625,0 -1.140625,0.125 -1.671875,0.328125 v 0.703125 c 0.46875,-0.234375 0.96875,-0.40625 1.5,-0.40625 0.65625,0 0.9375,0.25 0.9375,0.90625 v 0.40625 H 2.890625 c -1,0 -2.328125,0.359375 -2.328125,1.578125 0,0.8125 0.71875,1.25 1.46875,1.25 0.5625,0 1.078125,-0.265625 1.5,-0.625 0.140625,0.390625 0.421875,0.625 0.828125,0.625 C 4.578125,0.0625 4.796875,0 5,-0.046875 Z M 3.40625,-1.09375 c -0.328125,0.28125 -0.71875,0.5 -1.15625,0.5 -0.453125,0 -0.8125,-0.265625 -0.8125,-0.734375 0,-0.84375 1.09375,-0.921875 1.71875,-0.921875 h 0.25 z m 0,0"
+ id="path242"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-4"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 5.40625,-5.953125 v -0.65625 C 4.984375,-6.734375 4.5625,-6.78125 4.109375,-6.78125 c -1.75,0 -1.90625,1.171875 -1.90625,2.625 h -1.25 v 0.625 h 1.25 V -0.625 H 0.734375 V 0 H 4.5 V -0.625 H 3.03125 v -2.90625 h 1.6875 v -0.625 H 3.046875 v -0.515625 c 0,-0.84375 0.140625,-1.5 1.140625,-1.5 0.421875,0 0.828125,0.078125 1.21875,0.21875 z m 0,0"
+ id="path245"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-5"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 4.640625,-0.171875 V -0.84375 c -0.46875,0.203125 -0.96875,0.328125 -1.46875,0.328125 -1.03125,0 -1.625,-0.515625 -1.6875,-1.5625 H 4.625 c 0,-1.34375 -0.203125,-2.625 -1.859375,-2.625 -1.375,0 -2.171875,1.171875 -2.171875,2.453125 0,1.5625 0.953125,2.34375 2.46875,2.34375 0.53125,0 1.078125,-0.109375 1.578125,-0.265625 z M 3.734375,-2.71875 H 1.5 c 0.078125,-0.6875 0.421875,-1.359375 1.203125,-1.359375 0.78125,0 1.03125,0.546875 1.03125,1.25 z m 0,0"
+ id="path248"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-6"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 4.671875,0 v -0.640625 c -0.34375,0.078125 -0.6875,0.125 -1.03125,0.125 -0.78125,0 -1.109375,-0.15625 -1.109375,-0.984375 V -3.875 H 4.140625 V -4.5 H 2.53125 V -5.484375 H 1.703125 V -4.5 h -1.15625 v 0.625 h 1.15625 v 2.234375 c 0,1.34375 0.390625,1.734375 1.75,1.734375 0.40625,0 0.8125,-0.03125 1.21875,-0.09375 z m 0,0"
+ id="path251"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-7"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 4.984375,-3.984375 V -4.59375 h -1.625 v 0.609375 h 0.375 L 2.65625,-1.21875 1.609375,-3.984375 H 1.90625 V -4.59375 H 0.234375 v 0.609375 H 0.75 l 1.5,3.78125 C 1.828125,0.875 1.75,1.03125 0.546875,1.03125 v 0.640625 c 1.015625,0 1.671875,-0.0625 2.078125,-1.09375 l 1.875,-4.5625 z m 0,0"
+ id="path254"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-8"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 5.03125,0.625 V 0 H 0.203125 v 0.625 z m 0,0"
+ id="path257"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-9"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 3.140625,-5.625 V -6.6875 H 2.09375 V -5.625 Z M 4.34375,0 V -0.625 H 3.03125 V -4.59375 H 0.828125 v 0.625 H 2.1875 V -0.625 H 0.734375 V 0 Z m 0,0"
+ id="path260"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-10"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 5.296875,0 v -0.625 h -0.5 v -2.75 c 0,-0.5625 -0.015625,-1.328125 -0.765625,-1.328125 -0.515625,0 -0.84375,0.546875 -1.046875,0.96875 H 2.953125 C 2.875,-4.15625 2.734375,-4.703125 2.21875,-4.703125 1.75,-4.703125 1.375,-4.09375 1.1875,-3.734375 V -4.59375 h -1.234375 v 0.625 h 0.5 V -0.625 h -0.5 V 0 H 1.6875 v -0.625 h -0.5 v -2.328125 c 0.125,-0.28125 0.5,-1.015625 0.84375,-1.015625 0.21875,0 0.21875,0.25 0.21875,0.40625 V 0 h 1.234375 v -0.625 h -0.5 v -2.34375 c 0.140625,-0.3125 0.46875,-1.015625 0.859375,-1.015625 0.21875,0 0.21875,0.21875 0.21875,0.390625 V 0 Z m 0,0"
+ id="path263"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-11"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 4.65625,-0.15625 v -0.6875 C 4.25,-0.6875 3.8125,-0.5625 3.375,-0.5625 c -1.046875,0 -1.90625,-0.671875 -1.90625,-1.765625 0,-0.984375 0.65625,-1.765625 1.671875,-1.765625 0.265625,0 0.53125,0.046875 0.78125,0.109375 v 0.4375 h 0.65625 v -0.9375 C 4.125,-4.625 3.65625,-4.71875 3.15625,-4.71875 c -1.46875,0 -2.578125,0.921875 -2.578125,2.4375 0,1.53125 1.15625,2.375 2.609375,2.375 0.5,0 0.984375,-0.09375 1.46875,-0.25 z m 0,0"
+ id="path266"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-12"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 4.78125,-2.296875 c 0,-1.375 -0.71875,-2.40625 -2.171875,-2.40625 -1.4375,0 -2.171875,1.03125 -2.171875,2.40625 0,1.359375 0.734375,2.390625 2.171875,2.390625 1.453125,0 2.171875,-1.03125 2.171875,-2.390625 z m -0.890625,0 c 0,0.828125 -0.296875,1.78125 -1.28125,1.78125 -0.984375,0 -1.28125,-0.96875 -1.28125,-1.78125 0,-0.828125 0.296875,-1.78125 1.28125,-1.78125 0.984375,0 1.28125,0.953125 1.28125,1.78125 z m 0,0"
+ id="path269"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-13"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 4.84375,-3.296875 V -4.59375 C 4.5625,-4.65625 4.265625,-4.703125 3.984375,-4.703125 c -0.734375,0 -1.296875,0.375 -1.703125,0.96875 V -4.59375 H 0.578125 v 0.625 H 1.4375 V -0.625 H 0.34375 V 0 H 3.578125 V -0.625 H 2.28125 V -2.984375 C 2.6875,-3.5625 3.125,-3.96875 3.875,-3.96875 c 0.109375,0 0.234375,0.015625 0.34375,0.046875 v 0.625 z m 0,0"
+ id="path272"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-14"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 4.875,-3.296875 V -3.84375 h -4.5 v 0.546875 z m 0,2.03125 v -0.5625 h -4.5 v 0.5625 z m 0,0"
+ id="path275"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-15"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 4.75,-3.140625 c 0,-1.34375 -0.453125,-3.296875 -2.140625,-3.296875 -1.6875,0 -2.140625,1.953125 -2.140625,3.296875 0,1.359375 0.453125,3.296875 2.140625,3.296875 C 4.3125,0.15625 4.75,-1.78125 4.75,-3.140625 Z m -0.875,0 c 0,0.46875 -0.03125,1.234375 -0.234375,1.8125 l -1.5625,-4.3125 c 0.15625,-0.09375 0.328125,-0.15625 0.53125,-0.15625 1.203125,0 1.265625,1.8125 1.265625,2.65625 z M 3.140625,-0.625 C 3,-0.53125 2.828125,-0.46875 2.609375,-0.46875 c -1.1875,0 -1.25,-1.828125 -1.25,-2.671875 0,-0.453125 0.03125,-1.203125 0.234375,-1.78125 z m 0,0"
+ id="path278"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-16"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 3.234375,0 v -1.25 h -1.25 V 0 Z m 0,0"
+ id="path281"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-17"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 4.671875,-1.640625 c 0,-0.90625 -0.65625,-1.40625 -1.359375,-1.8125 0.578125,-0.359375 1.109375,-0.890625 1.109375,-1.625 0,-0.96875 -0.75,-1.359375 -1.625,-1.359375 C 1.828125,-6.4375 1,-5.890625 1,-4.859375 1,-4.25 1.375,-3.765625 1.84375,-3.421875 1.171875,-3 0.671875,-2.390625 0.671875,-1.578125 c 0,1.125 0.90625,1.734375 1.953125,1.734375 1.078125,0 2.046875,-0.65625 2.046875,-1.796875 z M 3.65625,-4.9375 c 0,0.53125 -0.390625,0.9375 -0.8125,1.203125 C 2.375,-4 1.78125,-4.359375 1.78125,-4.96875 c 0,-0.546875 0.421875,-0.828125 0.9375,-0.828125 0.515625,0 0.9375,0.3125 0.9375,0.859375 z m 0.171875,3.46875 c 0,0.59375 -0.5625,1 -1.125,1 C 1.984375,-0.46875 1.5,-0.984375 1.5,-1.6875 c 0,-0.5625 0.390625,-1.046875 0.796875,-1.390625 0.59375,0.359375 1.53125,0.8125 1.53125,1.609375 z m 0,0"
+ id="path284"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-18"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 3.234375,-3.34375 v -1.25 h -1.25 v 1.25 z m 0,2.84375 v -0.75 h -1.25 V 0 H 2.5 c 0,0.328125 -0.09375,0.890625 -0.515625,0.921875 v 0.4375 C 3.015625,1.3125 3.234375,0.375 3.234375,-0.5 Z m 0,0"
+ id="path287"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-19"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 4.8125,-2.453125 c 0,-1.0625 -0.421875,-2.25 -1.671875,-2.25 -0.65625,0 -1.171875,0.421875 -1.5,0.96875 V -4.59375 h -1.4375 v 0.625 h 0.59375 v 5 h -0.59375 v 0.640625 h 2.03125 V 1.03125 h -0.59375 v -1.546875 c 0.3125,0.390625 0.75,0.609375 1.25,0.609375 1.375,0 1.921875,-1.375 1.921875,-2.546875 z m -0.875,0.09375 c 0,0.75 -0.203125,1.84375 -1.171875,1.84375 -0.4375,0 -0.84375,-0.3125 -1.125,-0.625 V -3.09375 C 1.9375,-3.546875 2.375,-3.984375 2.953125,-3.984375 c 0.859375,0 0.984375,0.96875 0.984375,1.625 z m 0,0"
+ id="path290"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-20"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 4.5,0 V -0.625 H 3.03125 V -6.6875 H 0.734375 v 0.625 H 2.1875 V -0.625 H 0.734375 V 0 Z m 0,0"
+ id="path293"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-21"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 5.09375,0 v -0.625 h -0.625 V -6.6875 H 3 v 0.625 h 0.625 v 1.984375 c -0.3125,-0.375 -0.75,-0.625 -1.25,-0.625 -1.375,0 -1.9375,1.359375 -1.9375,2.546875 0,1.0625 0.4375,2.25 1.6875,2.25 0.65625,0 1.171875,-0.4375 1.5,-0.96875 V 0 Z M 3.625,-1.5 c -0.3125,0.453125 -0.75,0.890625 -1.328125,0.890625 -0.859375,0 -0.96875,-0.96875 -0.96875,-1.625 0,-0.75 0.203125,-1.84375 1.15625,-1.84375 0.453125,0 0.859375,0.3125 1.140625,0.640625 z m 0,0"
+ id="path296"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-22"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 4.53125,-4.859375 -0.25,-0.8125 -1.3125,0.59375 c 0.125,0.15625 0.171875,0.296875 0.171875,0.5 z m -1.5,-1.703125 H 2.1875 l 0.171875,1.40625 c 0.078125,-0.03125 0.15625,-0.046875 0.25,-0.046875 0.09375,0 0.1875,0.015625 0.265625,0.046875 z M 4.0625,-3.40625 3.09375,-4.46875 C 3,-4.3125 2.84375,-4.203125 2.6875,-4.140625 l 0.6875,1.21875 z M 2.25,-5.078125 l -1.296875,-0.59375 -0.25,0.8125 1.375,0.28125 c 0,-0.203125 0.046875,-0.34375 0.171875,-0.5 z m 0.296875,0.9375 C 2.375,-4.203125 2.21875,-4.3125 2.125,-4.46875 l -0.953125,1.0625 0.671875,0.484375 z m 0,0"
+ id="path299"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-23"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 4.921875,-4.921875 v -1.3125 H 0.3125 v 1.3125 h 0.625 v -0.6875 H 2.203125 V -0.625 H 1.46875 V 0 h 2.296875 v -0.625 h -0.71875 v -4.984375 h 1.25 v 0.6875 z m 0,0"
+ id="path302"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-24"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 4.71875,-1.53125 c 0,-1.921875 -3.21875,-2.125 -3.21875,-3.4375 0,-0.640625 0.625,-0.765625 1.140625,-0.765625 0.34375,0 0.671875,0.0625 1.015625,0.15625 v 0.5 h 0.625 V -6.09375 C 3.734375,-6.265625 3.1875,-6.375 2.625,-6.375 c -0.984375,0 -1.890625,0.421875 -1.890625,1.546875 0,1.84375 3.15625,2.1875 3.15625,3.4375 C 3.890625,-0.6875 3.09375,-0.5 2.53125,-0.5 2.125,-0.5 1.71875,-0.59375 1.328125,-0.734375 v -0.5625 h -0.625 v 1.125 c 0.578125,0.15625 1.1875,0.296875 1.8125,0.296875 1.03125,0 2.203125,-0.4375 2.203125,-1.65625 z m 0,0"
+ id="path305"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-25"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 4.59375,1.25 V 0.671875 C 2.90625,0.3125 2.296875,-1.140625 2.296875,-2.71875 c 0,-1.5625 0.59375,-3.046875 2.296875,-3.390625 V -6.6875 c -2.03125,0.234375 -3.140625,2.046875 -3.140625,3.96875 0,1.9375 1.109375,3.734375 3.140625,3.96875 z m 0,0"
+ id="path308"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-26"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 3.765625,-2.71875 c 0,-1.890625 -1.125,-3.734375 -3.140625,-3.96875 v 0.578125 c 1.703125,0.34375 2.296875,1.828125 2.296875,3.390625 0,1.578125 -0.59375,3.03125 -2.296875,3.390625 V 1.25 C 2.65625,1.015625 3.765625,-0.796875 3.765625,-2.71875 Z m 0,0"
+ id="path311"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-27"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 4.453125,0 V -0.625 H 3.296875 V -6.4375 L 1.34375,-5.609375 V -5 L 2.453125,-5.40625 V -0.625 H 1.296875 V 0 Z m 0,0"
+ id="path314"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-28"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 4.34375,-2.28125 v -0.5625 H 0.890625 v 0.5625 z m 0,0"
+ id="path317"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-29"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 4.859375,-0.0625 V -0.625 L 1.28125,-2.5625 4.859375,-4.5 V -5.0625 L 0.375,-2.65625 v 0.1875 z m 0,0"
+ id="path320"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-30"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 4.875,-2.28125 v -0.5625 H 2.90625 V -4.796875 H 2.34375 V -2.84375 H 0.375 v 0.5625 h 1.96875 v 1.984375 h 0.5625 V -2.28125 Z m 0,0"
+ id="path323"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-31"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 4.34375,1.25 V 0.625 c -0.53125,0 -1.5,0.09375 -1.5,-0.6875 0,-0.484375 0.203125,-0.96875 0.203125,-1.453125 0,-0.59375 -0.28125,-1.015625 -0.84375,-1.203125 0.5625,-0.1875 0.84375,-0.59375 0.84375,-1.171875 0,-0.5 -0.203125,-0.984375 -0.203125,-1.484375 0,-0.78125 0.953125,-0.6875 1.5,-0.6875 v -0.625 H 3.875 c -0.828125,0 -1.78125,0.203125 -1.78125,1.21875 0,0.5625 0.234375,1.109375 0.234375,1.65625 0,0.828125 -0.75,0.78125 -1.34375,0.78125 v 0.625 c 0.59375,0 1.34375,-0.03125 1.34375,0.765625 0,0.578125 -0.234375,1.109375 -0.234375,1.671875 0,1 0.984375,1.21875 1.78125,1.21875 z m 0,0"
+ id="path326"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-32"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 4.984375,-3.984375 V -4.59375 h -1.625 v 0.609375 h 0.4375 l -1.15625,3.0625 -1.109375,-3.0625 h 0.375 V -4.59375 H 0.234375 v 0.609375 H 0.6875 l 1.46875,3.96875 h 0.859375 l 1.515625,-3.96875 z m 0,0"
+ id="path329"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph3-33"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 4.234375,-2.40625 v -0.625 C 3.640625,-3.03125 2.90625,-3 2.90625,-3.8125 2.90625,-4.359375 3.125,-4.90625 3.125,-5.46875 3.125,-6.453125 2.15625,-6.6875 1.34375,-6.6875 H 0.890625 v 0.625 c 0.546875,0 1.484375,-0.09375 1.484375,0.6875 0,0.5 -0.203125,0.984375 -0.203125,1.484375 0,0.578125 0.28125,0.984375 0.84375,1.171875 -0.5625,0.1875 -0.84375,0.59375 -0.84375,1.1875 0,0.5 0.203125,0.96875 0.203125,1.46875 0,0.765625 -0.953125,0.6875 -1.484375,0.6875 V 1.25 H 1.34375 c 0.8125,0 1.78125,-0.203125 1.78125,-1.21875 0,-0.5625 -0.21875,-1.09375 -0.21875,-1.671875 0,-0.796875 0.75,-0.765625 1.328125,-0.765625 z m 0,0"
+ id="path332"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph4-0"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d=""
+ id="path335"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph4-1"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 6.59375,-2.625 V -3.265625 H 0.703125 V -2.625 Z m 0,0"
+ id="path338"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph4-2"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 5,-2.953125 c 0,-1.1875 -0.953125,-2.125 -2.140625,-2.125 -1.203125,0 -2.15625,0.9375 -2.15625,2.125 0,1.203125 0.953125,2.140625 2.15625,2.140625 C 4.046875,-0.8125 5,-1.75 5,-2.953125 Z m -0.5625,0 c 0,0.890625 -0.71875,1.59375 -1.578125,1.59375 -0.890625,0 -1.59375,-0.703125 -1.59375,-1.59375 0,-0.875 0.703125,-1.578125 1.59375,-1.578125 0.859375,0 1.578125,0.703125 1.578125,1.578125 z m 0,0"
+ id="path341"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph5-0"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d=""
+ id="path344"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph5-1"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 5.140625,-6.34375 c 0,-0.6875 -0.390625,-1.078125 -1.078125,-1.078125 -0.34375,0 -0.6875,0.078125 -0.984375,0.21875 -1.765625,0.71875 -1.875,2.421875 -2.125,4.046875 C 0.640625,-1.109375 0.375,0.953125 -0.0625,2.984375 L 0.015625,3.09375 C 0.28125,2.984375 0.5625,2.890625 0.84375,2.828125 0.90625,1.765625 1.03125,0.71875 1.171875,-0.3125 1.53125,0.015625 2,0.15625 2.46875,0.15625 3.796875,0.15625 5,-0.875 5,-2.234375 5,-3.28125 4.203125,-3.90625 3.21875,-3.921875 L 3.234375,-4.03125 c 0.96875,-0.328125 1.90625,-1.1875 1.90625,-2.3125 z m -0.78125,0.265625 c 0,1.21875 -1.015625,2.03125 -2.171875,2.03125 -0.03125,0.125 -0.0625,0.265625 -0.109375,0.375 L 2.15625,-3.59375 C 2.328125,-3.671875 2.5,-3.703125 2.6875,-3.703125 c 0.890625,0 1.4375,0.53125 1.4375,1.4375 C 4.125,-1.25 3.546875,-0.125 2.40625,-0.125 c -0.640625,0 -1.03125,-0.359375 -1.03125,-1 0,-1.109375 0.59375,-5.109375 1.578125,-5.734375 0.171875,-0.109375 0.375,-0.1875 0.578125,-0.1875 0.578125,0 0.828125,0.4375 0.828125,0.96875 z m 0,0"
+ id="path347"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph5-2"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 4.359375,-7.78125 H 3.75 l -3.046875,9.625 H 1.3125 Z m 0,0"
+ id="path350"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph5-3"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 6.046875,-4.5 C 5.96875,-4.671875 5.890625,-4.84375 5.796875,-5 H 5.6875 C 5.53125,-4.265625 5.03125,-3.4375 4.640625,-2.796875 L 4.5625,-2.828125 c 0.015625,-0.84375 0.234375,-2.328125 -0.984375,-2.328125 -1.828125,0 -3.28125,2.5 -3.28125,4.140625 0,0.65625 0.21875,1.1875 0.953125,1.1875 1,0 2,-0.875 2.625,-1.59375 l 0.046875,0.03125 c 0,0.359375 -0.078125,1.5625 0.484375,1.5625 0.515625,0 1.25,-0.765625 1.5625,-1.09375 V -1 L 5.90625,-1.0625 H 5.828125 C 5.625,-0.859375 5.375,-0.59375 5.078125,-0.59375 4.59375,-0.59375 4.625,-2 4.625,-2.359375 5.15625,-3.03125 5.65625,-3.734375 6.046875,-4.5 Z M 3.90625,-1.78125 c -0.453125,0.546875 -1.328125,1.46875 -2.109375,1.46875 -0.5625,0 -0.703125,-0.46875 -0.703125,-0.9375 0,-0.953125 0.765625,-3.515625 1.953125,-3.515625 0.9375,0 0.859375,2.015625 0.859375,2.671875 z m 0,0"
+ id="path353"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph5-4"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 4.984375,-3.125 c 0,-1.984375 -2.5625,-2.390625 -2.5625,-3.65625 0,-0.453125 0.578125,-0.625 0.953125,-0.625 0.375,0 0.75,0.125 1.0625,0.328125 l 0.125,-0.0625 C 4.53125,-7.28125 4.484375,-7.421875 4.421875,-7.5625 4.15625,-7.625 3.875,-7.65625 3.609375,-7.65625 c -0.671875,0 -1.96875,0.28125 -1.96875,1.140625 0,0.640625 0.78125,1.140625 1.25,1.453125 L 2.875,-5 c -1.546875,0.359375 -2.53125,1.6875 -2.53125,3.25 0,1.171875 0.484375,1.921875 1.71875,1.921875 1.71875,0 2.921875,-1.6875 2.921875,-3.296875 z M 4.15625,-2.9375 c 0,1.046875 -0.53125,2.8125 -1.8125,2.8125 -0.921875,0 -1.15625,-0.953125 -1.15625,-1.703125 0,-1.390625 0.46875,-2.65625 1.921875,-3.0625 0.671875,0.53125 1.046875,1.0625 1.046875,1.953125 z m 0,0"
+ id="path356"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph6-0"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d=""
+ id="path359"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph6-1"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 6.59375,-3.78125 V -4.421875 H 0.703125 v 0.640625 z m 0,2.3125 V -2.109375 H 0.703125 v 0.640625 z m 0,0"
+ id="path362"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph6-2"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 6.59375,-2.625 v -0.640625 h -2.625 V -5.875 H 3.328125 v 2.609375 h -2.625 V -2.625 h 2.625 V 0 H 3.96875 v -2.625 z m 0,0"
+ id="path365"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph6-3"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 7.21875,-0.0625 c -0.671875,-1.203125 -1.1875,-2.53125 -1.734375,-3.796875 -0.53125,-1.21875 -1.109375,-2.46875 -1.53125,-3.75 h -0.28125 c -0.546875,1.375 -1.15625,2.703125 -1.75,4.046875 -0.53125,1.15625 -1.03125,2.375 -1.625,3.5 L 0.375,0.046875 C 1.53125,0 2.6875,0 3.84375,0 4.953125,0 6.046875,0.015625 7.15625,0.046875 Z M 5.859375,-0.40625 h -4.71875 c 0.71875,-1.9375 1.5,-3.84375 2.328125,-5.75 h 0.078125 c 0.8125,1.890625 1.59375,3.8125 2.3125,5.75 z m 0,0"
+ id="path368"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph7-0"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d=""
+ id="path371"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph7-1"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 5.71875,0 v -1.015625 h -0.5625 l -1.6875,-6.125 H 2.25 l -1.6875,6.125 H 0.015625 V 0 h 2.4375 v -1.015625 h -0.6875 C 1.890625,-1.40625 2,-1.8125 2.109375,-2.203125 H 3.5625 c 0.109375,0.390625 0.21875,0.796875 0.34375,1.1875 H 3.21875 V 0 Z M 3.265625,-3.3125 H 2.40625 C 2.546875,-3.875 2.6875,-4.453125 2.828125,-5.03125 2.984375,-4.453125 3.125,-3.875 3.265625,-3.3125 Z m 0,0"
+ id="path374"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph7-2"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 5.671875,0 V -1.015625 H 4.96875 L 3.234375,-2.9375 4.46875,-4.34375 h 0.890625 v -1.015625 h -2.5625 v 1.015625 h 0.25 L 2.0625,-3.234375 v -4.40625 H 0.15625 V -6.625 H 0.8125 v 5.609375 H 0.15625 V 0 h 2.5625 V -1.015625 H 2.0625 v -1.34375 l 1.203125,1.34375 H 3.125 V 0 Z m 0,0"
+ id="path377"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph7-3"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 5.65625,-0.09375 5.515625,-0.984375 c -0.078125,0.03125 -0.3125,0.09375 -0.40625,0.09375 -0.359375,0 -0.3125,-0.359375 -0.3125,-0.625 V -3.625 c 0,-1.359375 -0.59375,-1.84375 -1.90625,-1.84375 -0.640625,0 -1.28125,0.140625 -2,0.40625 v 1.15625 c 0.75,-0.375 1.265625,-0.546875 1.8125,-0.546875 0.640625,0 0.859375,0.203125 0.859375,0.828125 v 0.265625 H 3.15625 c -1.078125,0 -2.703125,0.40625 -2.703125,1.890625 0,1.015625 0.890625,1.546875 1.765625,1.546875 0.59375,0 1.140625,-0.265625 1.59375,-0.59375 0.171875,0.359375 0.5,0.59375 0.96875,0.59375 0.140625,0 0.265625,-0.03125 0.390625,-0.046875 C 5.28125,0 5.40625,-0.03125 5.65625,-0.09375 Z M 3.5625,-1.4375 c -0.328125,0.265625 -0.703125,0.46875 -1.109375,0.46875 -0.421875,0 -0.71875,-0.234375 -0.71875,-0.640625 0,-0.828125 1.09375,-0.859375 1.71875,-0.859375 H 3.5625 Z m 0,0"
+ id="path380"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph7-4"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 5.765625,0 V -1.015625 H 5 v -2.59375 C 5,-4.625 4.609375,-5.46875 3.40625,-5.46875 c -0.640625,0 -1.09375,0.28125 -1.4375,0.6875 V -5.359375 H -0.046875 V -4.34375 H 0.71875 v 3.328125 H -0.046875 V 0 H 2.71875 v -1.015625 h -0.75 V -3.40625 c 0.328125,-0.4375 0.65625,-0.875 1.15625,-0.875 0.546875,0 0.625,0.328125 0.625,0.9375 V 0 Z m 0,0"
+ id="path383"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph7-5"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 5.265625,-0.015625 V -1.0625 c -0.546875,0.109375 -0.90625,0.171875 -1.28125,0.171875 -0.796875,0 -1.046875,-0.125 -1.046875,-0.921875 v -2.421875 h 1.75 V -5.25 h -1.75 V -6.328125 H 1.703125 V -5.25 H 0.4375 v 1.015625 h 1.265625 v 2.265625 c 0,1.578125 0.5,2.078125 2.078125,2.078125 0.453125,0 0.890625,-0.046875 1.484375,-0.125 z m 0,0"
+ id="path386"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph7-6"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 5.703125,0 v -1.015625 h -0.65625 v -4.34375 H 3.15625 V -4.34375 H 3.8125 v 2.375 C 3.484375,-1.515625 3.03125,-1.078125 2.53125,-1.078125 1.984375,-1.078125 1.890625,-1.40625 1.890625,-2 v -3.359375 h -1.875 v 1.015625 h 0.65625 V -1.75 c 0,1.015625 0.375,1.859375 1.578125,1.859375 0.65625,0 1.15625,-0.28125 1.5625,-0.734375 V 0 Z m 0,0"
+ id="path389"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph8-0"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d=""
+ id="path392"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph8-1"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 5.125,-1.4375 c 0,-1.796875 -3.359375,-1.28125 -3.359375,-2.390625 0,-0.5625 0.75,-0.640625 1.171875,-0.640625 0.3125,0 0.734375,0.046875 1.03125,0.109375 V -3.875 H 4.6875 V -4.890625 C 4.15625,-5.046875 3.484375,-5.15625 2.921875,-5.15625 c -0.9375,0 -2.015625,0.375 -2.015625,1.453125 0,1.71875 3.296875,1.296875 3.296875,2.390625 0,0.609375 -0.90625,0.75 -1.375,0.75 -0.390625,0 -0.875,-0.078125 -1.234375,-0.203125 V -1.28125 H 0.875 v 1.125 c 0.59375,0.140625 1.3125,0.265625 1.921875,0.265625 0.984375,0 2.328125,-0.375 2.328125,-1.546875 z m 0,0"
+ id="path395"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph8-2"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 5.21875,-2.515625 c 0,-1.5 -0.765625,-2.625 -2.359375,-2.625 -1.578125,0 -2.375,1.125 -2.375,2.625 0,1.484375 0.796875,2.625 2.375,2.625 1.59375,0 2.359375,-1.140625 2.359375,-2.625 z m -0.953125,0 c 0,0.90625 -0.328125,1.953125 -1.40625,1.953125 -1.078125,0 -1.390625,-1.0625 -1.390625,-1.953125 0,-0.890625 0.3125,-1.9375 1.390625,-1.9375 1.078125,0 1.40625,1.03125 1.40625,1.9375 z m 0,0"
+ id="path398"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph8-3"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 4.921875,0 V -0.6875 H 3.3125 v -6.625 H 0.796875 V -6.625 H 2.40625 v 5.9375 H 0.796875 V 0 Z m 0,0"
+ id="path401"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph8-4"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 5.453125,-4.359375 V -5.03125 h -1.78125 v 0.671875 H 4.15625 l -1.265625,3.34375 -1.21875,-3.34375 h 0.40625 V -5.03125 h -1.8125 v 0.671875 H 0.75 l 1.609375,4.34375 h 0.9375 l 1.65625,-4.34375 z m 0,0"
+ id="path404"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph8-5"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 5.078125,-0.203125 v -0.71875 C 4.5625,-0.703125 4.03125,-0.5625 3.46875,-0.5625 2.328125,-0.5625 1.6875,-1.140625 1.625,-2.28125 h 3.4375 c 0,-1.46875 -0.234375,-2.859375 -2.03125,-2.859375 -1.515625,0 -2.375,1.28125 -2.375,2.6875 0,1.703125 1.03125,2.5625 2.6875,2.5625 0.59375,0 1.1875,-0.125 1.734375,-0.3125 z m -1,-2.765625 h -2.4375 c 0.078125,-0.75 0.46875,-1.484375 1.3125,-1.484375 0.859375,0 1.125,0.59375 1.125,1.359375 z m 0,0"
+ id="path407"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph8-6"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 5.171875,-1.671875 C 5.171875,-3.78125 1.640625,-4 1.640625,-5.4375 c 0,-0.703125 0.6875,-0.828125 1.25,-0.828125 0.375,0 0.734375,0.0625 1.09375,0.15625 V -5.5625 h 0.6875 V -6.671875 C 4.09375,-6.84375 3.484375,-6.96875 2.875,-6.96875 c -1.09375,0 -2.078125,0.453125 -2.078125,1.6875 0,2.015625 3.46875,2.390625 3.46875,3.75 0,0.78125 -0.875,0.984375 -1.5,0.984375 -0.4375,0 -0.890625,-0.109375 -1.3125,-0.265625 v -0.609375 h -0.6875 V -0.1875 C 1.40625,-0.015625 2.078125,0.125 2.75,0.125 c 1.125,0 2.421875,-0.46875 2.421875,-1.796875 z m 0,0"
+ id="path410"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph8-7"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 5.109375,0 v -0.703125 c -0.375,0.078125 -0.75,0.140625 -1.125,0.140625 -0.859375,0 -1.21875,-0.171875 -1.21875,-1.078125 v -2.59375 H 4.53125 v -0.6875 H 2.765625 V -6 h -0.90625 v 1.078125 H 0.59375 v 0.6875 h 1.265625 v 2.4375 c 0,1.484375 0.4375,1.90625 1.921875,1.90625 C 4.21875,0.109375 4.671875,0.0625 5.109375,0 Z m 0,0"
+ id="path413"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph8-8"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 5.265625,-2.6875 c 0,-1.15625 -0.453125,-2.453125 -1.828125,-2.453125 -0.71875,0 -1.28125,0.46875 -1.65625,1.046875 v -0.9375 h -1.5625 v 0.6875 H 0.875 V 1.140625 H 0.21875 v 0.6875 H 2.4375 v -0.6875 H 1.78125 V -0.5625 c 0.359375,0.421875 0.84375,0.671875 1.375,0.671875 1.515625,0 2.109375,-1.5 2.109375,-2.796875 z M 4.3125,-2.578125 c 0,0.828125 -0.234375,2.015625 -1.28125,2.015625 -0.484375,0 -0.921875,-0.34375 -1.25,-0.6875 V -3.390625 C 2.125,-3.875 2.609375,-4.359375 3.234375,-4.359375 c 0.9375,0 1.078125,1.0625 1.078125,1.78125 z m 0,0"
+ id="path416"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph8-9"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 5.796875,0 V -0.6875 H 5.25 v -3 c 0,-0.625 -0.015625,-1.453125 -0.84375,-1.453125 -0.5625,0 -0.921875,0.59375 -1.140625,1.046875 H 3.234375 C 3.15625,-4.546875 3,-5.140625 2.421875,-5.140625 c -0.5,0 -0.921875,0.671875 -1.125,1.046875 v -0.9375 h -1.34375 v 0.6875 h 0.53125 v 3.65625 h -0.53125 V 0 H 1.84375 V -0.6875 H 1.296875 v -2.546875 c 0.125,-0.3125 0.546875,-1.109375 0.921875,-1.109375 0.234375,0 0.25,0.28125 0.25,0.453125 V 0 H 3.8125 V -0.6875 H 3.265625 V -3.25 c 0.15625,-0.34375 0.515625,-1.109375 0.9375,-1.109375 0.234375,0 0.25,0.25 0.25,0.421875 V 0 Z m 0,0"
+ id="path419"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph8-10"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 5.578125,0 v -0.6875 h -0.6875 v -6.625 H 3.28125 v 0.6875 h 0.6875 v 2.171875 c -0.34375,-0.421875 -0.828125,-0.6875 -1.375,-0.6875 -1.5,0 -2.109375,1.484375 -2.109375,2.78125 0,1.171875 0.46875,2.46875 1.84375,2.46875 0.71875,0 1.28125,-0.484375 1.640625,-1.0625 V 0 Z M 3.96875,-1.640625 c -0.34375,0.5 -0.828125,0.96875 -1.453125,0.96875 -0.9375,0 -1.046875,-1.0625 -1.046875,-1.765625 0,-0.828125 0.203125,-2.015625 1.25,-2.015625 0.5,0 0.9375,0.328125 1.25,0.6875 z m 0,0"
+ id="path422"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph8-11"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 3.546875,0 v -1.375 h -1.375 V 0 Z m 0,0"
+ id="path425"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph8-12"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 5.359375,0 v -0.671875 h -0.625 L 3.34375,-2.65625 4.53125,-4.359375 H 5.171875 V -5.03125 H 3.125 v 0.671875 h 0.5 l -0.75,1.0625 -0.71875,-1.0625 H 2.625 V -5.03125 H 0.59375 v 0.671875 h 0.625 L 2.421875,-2.65625 1,-0.671875 H 0.359375 V 0 H 2.40625 V -0.671875 H 1.921875 L 2.875,-2 3.8125,-0.671875 H 3.328125 V 0 Z m 0,0"
+ id="path428"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph8-13"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 3.4375,-6.15625 V -7.3125 H 2.296875 v 1.15625 z M 4.75,0 V -0.6875 H 3.3125 V -5.03125 H 0.90625 v 0.6875 h 1.5 V -0.6875 H 0.796875 V 0 Z m 0,0"
+ id="path431"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph8-14"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 5.09375,-0.15625 v -0.765625 c -0.453125,0.171875 -0.921875,0.3125 -1.40625,0.3125 -1.140625,0 -2.078125,-0.75 -2.078125,-1.9375 0,-1.078125 0.71875,-1.921875 1.828125,-1.921875 0.296875,0 0.578125,0.046875 0.859375,0.109375 V -3.875 h 0.71875 v -1.03125 c -0.5,-0.15625 -1.03125,-0.25 -1.5625,-0.25 -1.59375,0 -2.828125,1 -2.828125,2.65625 0,1.6875 1.265625,2.609375 2.859375,2.609375 C 4.03125,0.109375 4.5625,0 5.09375,-0.15625 Z m 0,0"
+ id="path434"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph8-15"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 5.296875,-4.921875 c 0,-1.515625 -1.140625,-1.890625 -2.375,-1.890625 H 0.3125 V -6.125 H 1 v 5.4375 H 0.3125 V 0 h 2.28125 v -0.6875 h -0.6875 v -2 c 1.40625,0 3.390625,-0.328125 3.390625,-2.234375 z m -0.96875,0.03125 c 0,1.078125 -0.96875,1.515625 -1.859375,1.515625 h -0.5625 v -2.75 H 2.8125 c 0.734375,0 1.515625,0.3125 1.515625,1.234375 z m 0,0"
+ id="path437"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph8-16"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 5.296875,-3.59375 v -1.4375 c -0.3125,-0.0625 -0.625,-0.109375 -0.9375,-0.109375 -0.796875,0 -1.421875,0.40625 -1.859375,1.046875 v -0.9375 H 0.625 v 0.6875 H 1.578125 V -0.6875 H 0.375 V 0 H 3.921875 V -0.6875 H 2.5 V -3.265625 C 2.9375,-3.90625 3.421875,-4.34375 4.25,-4.34375 c 0.109375,0 0.25,0.03125 0.359375,0.046875 v 0.703125 z m 0,0"
+ id="path440"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph8-17"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 5.03125,1.359375 v -0.625 C 3.171875,0.34375 2.515625,-1.25 2.515625,-2.96875 c 0,-1.71875 0.640625,-3.34375 2.515625,-3.71875 v -0.625 c -2.234375,0.25 -3.4375,2.234375 -3.4375,4.34375 0,2.109375 1.203125,4.078125 3.4375,4.328125 z m 0,0"
+ id="path443"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph8-18"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 4.125,-2.96875 c 0,-2.078125 -1.234375,-4.09375 -3.4375,-4.34375 v 0.625 c 1.859375,0.375 2.515625,2 2.515625,3.71875 0,1.71875 -0.65625,3.3125 -2.515625,3.703125 v 0.625 C 2.90625,1.109375 4.125,-0.875 4.125,-2.96875 Z m 0,0"
+ id="path446"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph8-19"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 5.296875,-0.1875 v -0.75 C 4.828125,-0.75 4.3125,-0.59375 3.78125,-0.59375 c -1.40625,0 -2.390625,-1.4375 -2.390625,-2.84375 0,-1.25 0.765625,-2.828125 2.109375,-2.828125 0.34375,0 0.703125,0.0625 1.03125,0.15625 V -5.5625 h 0.6875 v -1.125 c -0.546875,-0.15625 -1.109375,-0.28125 -1.6875,-0.28125 -1.875,0 -3.109375,1.71875 -3.109375,3.59375 0,1.875 1.28125,3.5 3.140625,3.5 0.59375,0 1.1875,-0.109375 1.734375,-0.3125 z m 0,0"
+ id="path449"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph8-20"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 5.546875,0 V -0.6875 H 4.890625 V -5.03125 H 3.3125 v 0.6875 H 3.96875 V -1.75 c -0.328125,0.46875 -0.828125,1 -1.4375,1 -0.703125,0 -0.796875,-0.5 -0.796875,-1.09375 v -3.1875 h -1.5625 v 0.6875 h 0.65625 v 2.765625 c 0,0.953125 0.34375,1.6875 1.421875,1.6875 0.765625,0 1.328125,-0.421875 1.71875,-1.0625 V 0 Z m 0,0"
+ id="path452"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph8-21"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 5.46875,-0.046875 -0.078125,-0.5625 c -0.09375,0.015625 -0.1875,0.046875 -0.28125,0.046875 -0.46875,0 -0.46875,-0.4375 -0.46875,-0.796875 v -2.09375 c 0,-1.265625 -0.5,-1.6875 -1.75,-1.6875 -0.625,0 -1.25,0.140625 -1.828125,0.359375 v 0.765625 c 0.515625,-0.265625 1.0625,-0.4375 1.640625,-0.4375 0.703125,0 1.03125,0.265625 1.03125,0.984375 v 0.4375 H 3.15625 c -1.078125,0 -2.53125,0.40625 -2.53125,1.734375 0,0.875 0.765625,1.375 1.59375,1.375 0.609375,0 1.1875,-0.3125 1.65625,-0.6875 0.15625,0.421875 0.4375,0.6875 0.90625,0.6875 0.234375,0 0.46875,-0.078125 0.6875,-0.125 z m -1.734375,-1.15625 c -0.359375,0.3125 -0.796875,0.5625 -1.28125,0.5625 -0.484375,0 -0.875,-0.296875 -0.875,-0.8125 0,-0.921875 1.1875,-1.015625 1.875,-1.015625 h 0.28125 z m 0,0"
+ id="path455"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph8-22"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 5.671875,0 V -0.6875 H 5.140625 L 3.5625,-3.140625 C 4.359375,-3.515625 4.921875,-4.25 4.921875,-5.171875 4.921875,-6.484375 3.625,-6.8125 2.625,-6.8125 H 0.265625 v 0.6875 h 0.6875 v 5.4375 h -0.6875 V 0 h 2.28125 v -0.6875 h -0.6875 V -2.90625 H 2.6875 L 4.5625,0 Z M 3.9375,-5.015625 c 0,0.875 -0.84375,1.421875 -1.59375,1.421875 H 1.859375 V -6.125 H 2.46875 c 0.65625,0 1.46875,0.21875 1.46875,1.109375 z m 0,0"
+ id="path458"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph8-23"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 5.546875,0 V -0.6875 H 5.03125 l -1.6875,-6.125 H 2.375 l -1.6875,6.125 H 0.171875 V 0 h 2.125 v -0.6875 h -0.75 c 0.171875,-0.5 0.296875,-1 0.4375,-1.515625 H 3.6875 C 3.828125,-1.6875 3.953125,-1.1875 4.125,-0.6875 H 3.375 V 0 Z m -2.0625,-2.984375 H 2.1875 C 2.40625,-3.84375 2.625,-4.703125 2.828125,-5.5625 c 0.21875,0.859375 0.4375,1.71875 0.65625,2.578125 z m 0,0"
+ id="path461"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph8-24"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 5.609375,0 V -0.6875 H 4.84375 v -2.765625 c 0,-0.96875 -0.359375,-1.6875 -1.4375,-1.6875 -0.765625,0 -1.234375,0.421875 -1.609375,1.046875 v -0.9375 H 0.125 v 0.6875 H 0.890625 V -0.6875 H 0.125 V 0 H 2.5625 V -0.6875 H 1.796875 v -2.609375 c 0.3125,-0.4375 0.71875,-0.984375 1.328125,-0.984375 0.703125,0 0.796875,0.5 0.796875,1.09375 V 0 Z m 0,0"
+ id="path464"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph8-25"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 5.359375,-7.3125 h -0.75 L 0.34375,1.359375 h 0.765625 z m 0,0"
+ id="path467"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph8-26"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 5.5,0.671875 V 0 H 0.21875 v 0.671875 z m 0,0"
+ id="path470"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph8-27"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 5.453125,-4.359375 V -5.03125 h -1.78125 v 0.671875 H 4.09375 L 2.90625,-1.328125 1.75,-4.359375 H 2.078125 V -5.03125 h -1.8125 v 0.671875 h 0.5625 l 1.625,4.140625 C 2,0.953125 1.90625,1.140625 0.59375,1.140625 v 0.6875 C 1.703125,1.828125 2.421875,1.75 2.875,0.625 l 2.046875,-4.984375 z m 0,0"
+ id="path473"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph9-0"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d=""
+ id="path476"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph9-1"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 1.359375,-4.25 0.75,-1.171875 C 0.734375,-1.09375 0.734375,-1.0625 0.703125,-0.9375 0.625,-0.625 0.609375,-0.46875 0.609375,-0.34375 c 0,0.28125 0.125,0.4375 0.328125,0.4375 0.375,0 0.765625,-0.21875 1.609375,-0.90625 L 2.71875,-0.9375 2.890625,-1.078125 2.78125,-1.28125 2.296875,-0.9375 C 1.96875,-0.71875 1.75,-0.609375 1.640625,-0.609375 c -0.109375,0 -0.15625,-0.09375 -0.15625,-0.21875 0,-0.28125 0.15625,-1.171875 0.46875,-2.75 L 2.09375,-4.25 H 3.265625 L 3.375,-4.796875 c -0.40625,0.046875 -0.78125,0.0625 -1.1875,0.0625 0.171875,-1.03125 0.28125,-1.5625 0.484375,-2.140625 l -0.125,-0.171875 c -0.21875,0.125 -0.5,0.265625 -0.84375,0.390625 l -0.28125,1.859375 c -0.46875,0.234375 -0.75,0.34375 -0.953125,0.40625 L 0.453125,-4.25 Z m 0,0"
+ id="path479"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph9-2"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 4.25,-7.046875 h 0.796875 c 0.859375,0 1.25,0.0625 1.28125,0.1875 0.03125,0.09375 0.046875,0.296875 0.046875,0.40625 l -0.046875,0.6875 H 6.625 L 6.921875,-7.546875 5.9375,-7.53125 c -0.96875,0 -1.703125,0.015625 -2.09375,0.015625 -0.390625,0 -1.09375,-0.015625 -2.03125,-0.015625 l -1.03125,-0.015625 -0.203125,1.78125 h 0.3125 L 1.0625,-6.40625 c 0.046875,-0.25 0.125,-0.421875 0.1875,-0.5 0.0625,-0.078125 0.484375,-0.140625 0.890625,-0.140625 h 1.21875 l -0.9375,5.6875 C 2.25,-0.375 2.21875,-0.34375 1.78125,-0.3125 L 1.234375,-0.265625 1.1875,0.03125 1.8125,0.015625 C 2.25,0.015625 2.5625,0 2.734375,0 2.9375,0 3.28125,0.015625 3.703125,0.015625 L 4.1875,0.03125 4.21875,-0.265625 3.578125,-0.3125 c -0.28125,0 -0.390625,-0.09375 -0.390625,-0.3125 0,-0.109375 0.015625,-0.1875 0.046875,-0.390625 z m 0,0"
+ id="path482"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph9-3"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 2.578125,-7.875 2.4375,-8 C 1.875,-7.703125 1.484375,-7.609375 0.703125,-7.53125 L 0.65625,-7.3125 h 0.515625 c 0.265625,0 0.375,0.078125 0.375,0.265625 0,0.09375 -0.015625,0.1875 -0.015625,0.265625 L 1.0625,-4.234375 c -0.296875,1.65625 -0.71875,3.53125 -0.953125,4.21875 L 0.1875,0.09375 l 0.75,-0.171875 c 0.171875,-1.25 0.359375,-1.921875 0.671875,-2.5 0.484375,-0.875 1.625,-1.9375 2.078125,-1.9375 0.140625,0 0.234375,0.09375 0.234375,0.234375 0,0.203125 -0.078125,0.609375 -0.15625,0.984375 l -0.5625,2.125 C 3.109375,-0.78125 3.0625,-0.53125 3.0625,-0.328125 c 0,0.265625 0.125,0.421875 0.328125,0.421875 0.28125,0 0.671875,-0.21875 1.75,-1.015625 L 5.03125,-1.125 4.75,-0.9375 C 4.421875,-0.734375 4.1875,-0.609375 4.078125,-0.609375 4,-0.609375 3.9375,-0.703125 3.9375,-0.828125 c 0,-0.109375 0.015625,-0.203125 0.078125,-0.4375 L 4.71875,-4.0625 c 0.0625,-0.28125 0.109375,-0.609375 0.109375,-0.796875 0,-0.25 -0.109375,-0.390625 -0.3125,-0.390625 -0.375,0 -1.03125,0.359375 -1.71875,0.9375 C 2.40625,-4 2.21875,-3.8125 1.71875,-3.15625 L 1.5,-2.890625 Z m 0,0"
+ id="path485"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph9-4"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 3.578125,-1.203125 3.3125,-1.03125 C 2.734375,-0.609375 2.21875,-0.390625 1.828125,-0.390625 1.3125,-0.390625 1,-0.796875 1,-1.453125 c 0,-0.265625 0.03125,-0.5625 0.078125,-0.875 l 0.890625,-0.21875 c 0.1875,-0.046875 0.484375,-0.15625 0.75,-0.28125 0.9375,-0.40625 1.359375,-0.90625 1.359375,-1.578125 0,-0.515625 -0.375,-0.84375 -0.921875,-0.84375 -0.703125,0 -1.90625,0.75 -2.34375,1.4375 -0.328125,0.546875 -0.65625,1.84375 -0.65625,2.578125 0,0.859375 0.484375,1.359375 1.265625,1.359375 0.625,0 1.234375,-0.3125 2.25,-1.125 z m -2.34375,-1.78125 c 0.1875,-0.75 0.40625,-1.21875 0.71875,-1.515625 0.203125,-0.171875 0.53125,-0.296875 0.796875,-0.296875 0.3125,0 0.515625,0.21875 0.515625,0.5625 0,0.484375 -0.375,1 -0.9375,1.265625 -0.3125,0.15625 -0.703125,0.28125 -1.1875,0.390625 z m 0,0"
+ id="path488"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph9-5"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 0.375,-4.234375 0.453125,-4.015625 0.796875,-4.25 c 0.40625,-0.25 0.4375,-0.265625 0.515625,-0.265625 0.109375,0 0.1875,0.109375 0.1875,0.25 0,0.078125 -0.03125,0.328125 -0.078125,0.484375 l -0.71875,2.609375 C 0.609375,-0.828125 0.5625,-0.53125 0.5625,-0.328125 0.5625,-0.0625 0.671875,0.09375 0.890625,0.09375 1.171875,0.09375 1.5625,-0.125 2.625,-0.921875 L 2.515625,-1.125 2.234375,-0.9375 c -0.3125,0.203125 -0.5625,0.328125 -0.65625,0.328125 -0.078125,0 -0.15625,-0.109375 -0.15625,-0.21875 0,-0.109375 0.03125,-0.203125 0.078125,-0.4375 l 0.84375,-3.3125 c 0.046875,-0.1875 0.0625,-0.3125 0.0625,-0.390625 0,-0.1875 -0.09375,-0.28125 -0.265625,-0.28125 -0.25,0 -0.640625,0.21875 -1.46875,0.796875 z m 2.109375,-3.53125 c -0.3125,0 -0.625,0.359375 -0.625,0.734375 0,0.265625 0.15625,0.4375 0.421875,0.4375 0.34375,0 0.59375,-0.3125 0.59375,-0.71875 0,-0.265625 -0.15625,-0.453125 -0.390625,-0.453125 z m 0,0"
+ id="path491"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph9-6"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 0.265625,-4.234375 0.34375,-4.015625 0.6875,-4.25 c 0.40625,-0.25 0.4375,-0.265625 0.515625,-0.265625 0.109375,0 0.1875,0.109375 0.1875,0.265625 0,0.5625 -0.4375,2.671875 -0.890625,4.234375 L 0.578125,0.09375 C 0.84375,0.015625 1.09375,-0.046875 1.34375,-0.09375 c 0.203125,-1.375 0.4375,-2.078125 0.9375,-2.828125 0.59375,-0.921875 1.40625,-1.59375 1.890625,-1.59375 0.125,0 0.1875,0.09375 0.1875,0.265625 0,0.1875 -0.03125,0.421875 -0.109375,0.765625 l -0.578125,2.3125 c -0.09375,0.40625 -0.140625,0.65625 -0.140625,0.828125 0,0.28125 0.125,0.4375 0.328125,0.4375 0.28125,0 0.671875,-0.21875 1.75,-1.015625 L 5.5,-1.125 5.21875,-0.9375 c -0.328125,0.203125 -0.5625,0.328125 -0.671875,0.328125 -0.078125,0 -0.140625,-0.09375 -0.140625,-0.21875 0,-0.0625 0.015625,-0.171875 0.015625,-0.21875 L 5.140625,-4.0625 c 0.078125,-0.3125 0.125,-0.609375 0.125,-0.796875 0,-0.25 -0.125,-0.390625 -0.34375,-0.390625 -0.453125,0 -1.203125,0.40625 -1.84375,1 -0.421875,0.390625 -0.71875,0.765625 -1.296875,1.5625 l 0.421875,-1.765625 c 0.046875,-0.1875 0.0625,-0.328125 0.0625,-0.4375 C 2.265625,-5.125 2.1875,-5.25 2.015625,-5.25 1.78125,-5.25 1.359375,-5.015625 0.5625,-4.453125 Z m 0,0"
+ id="path494"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph9-7"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 4.015625,-3.984375 C 4.046875,-4.390625 4.09375,-4.75 4.1875,-5.1875 4.0625,-5.25 4.03125,-5.25 3.96875,-5.25 3.625,-5.25 3.296875,-5 2.90625,-4.4375 L 2.609375,-4.015625 C 2.1875,-3.40625 2.046875,-3.171875 1.875,-2.796875 l 0.09375,-0.4375 c 0.03125,-0.25 0.125,-0.6875 0.1875,-0.9375 l 0.0625,-0.28125 c 0.046875,-0.1875 0.078125,-0.328125 0.078125,-0.453125 0,-0.21875 -0.09375,-0.34375 -0.25,-0.34375 -0.234375,0 -0.65625,0.21875 -1.453125,0.796875 l -0.3125,0.21875 0.078125,0.21875 L 0.703125,-4.25 c 0.3125,-0.1875 0.4375,-0.25 0.53125,-0.25 0.109375,0 0.1875,0.109375 0.1875,0.25 0,0.625 -0.46875,2.875 -0.90625,4.265625 L 0.625,0.09375 C 0.78125,0.046875 0.953125,0.015625 1.203125,-0.046875 L 1.359375,-0.0625 1.640625,-1.375 c 0.1875,-0.90625 0.4375,-1.484375 0.921875,-2.109375 0.375,-0.46875 0.671875,-0.703125 0.90625,-0.703125 0.15625,0 0.265625,0.046875 0.390625,0.203125 z m 0,0"
+ id="path497"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph9-8"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 4.390625,-5.140625 4.28125,-5.25 3.921875,-5.046875 C 3.46875,-5.21875 3.28125,-5.25 2.984375,-5.25 c -0.296875,0 -0.515625,0.046875 -0.8125,0.1875 -0.671875,0.34375 -1.03125,0.671875 -1.3125,1.203125 -0.484375,0.96875 -0.8125,2.28125 -0.8125,3.125 0,0.484375 0.15625,0.859375 0.375,0.859375 0.234375,0 0.640625,-0.25 1.0625,-0.609375 0.453125,-0.421875 0.875,-0.90625 1.46875,-1.734375 l -0.3125,1.375 c -0.046875,0.1875 -0.0625,0.390625 -0.0625,0.5625 0,0.234375 0.09375,0.375 0.25,0.375 0.265625,0 0.703125,-0.28125 1.59375,-1.015625 l -0.0625,-0.234375 c -0.078125,0.0625 -0.109375,0.078125 -0.15625,0.125 -0.34375,0.296875 -0.5,0.390625 -0.671875,0.390625 -0.09375,0 -0.15625,-0.09375 -0.15625,-0.25 0,-0.0625 0,-0.09375 0,-0.125 z m -0.90625,0.625 c -0.25,1.1875 -0.453125,1.75 -0.828125,2.328125 -0.609375,0.90625 -1.28125,1.546875 -1.625,1.546875 -0.140625,0 -0.203125,-0.140625 -0.203125,-0.4375 0,-0.703125 0.3125,-1.96875 0.6875,-2.84375 0.265625,-0.609375 0.515625,-0.796875 1.03125,-0.796875 0.25,0 0.453125,0.046875 0.9375,0.203125 z m 0,0"
+ id="path500"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph9-9"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 2.734375,-7.875 2.609375,-8 c -0.5625,0.296875 -0.96875,0.390625 -1.75,0.46875 L 0.8125,-7.3125 h 0.53125 c 0.265625,0 0.375,0.078125 0.375,0.25 0,0.03125 0,0.078125 -0.046875,0.28125 l -0.03125,0.234375 C 1.578125,-5.9375 0.921875,-2.546875 0.75,-2 l -0.15625,0.5625 c -0.140625,0.546875 -0.203125,0.875 -0.203125,1.09375 0,0.28125 0.125,0.4375 0.328125,0.4375 0.28125,0 0.671875,-0.21875 1.75,-1.015625 L 2.359375,-1.125 2.078125,-0.9375 C 1.75,-0.734375 1.515625,-0.609375 1.40625,-0.609375 c -0.078125,0 -0.140625,-0.109375 -0.140625,-0.21875 0,-0.0625 0.015625,-0.140625 0.046875,-0.3125 l 0.015625,-0.09375 z m 0,0"
+ id="path503"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph9-10"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 3.125,-4.171875 c 0.03125,-0.21875 0.109375,-0.46875 0.171875,-0.609375 L 3.265625,-4.859375 3.1875,-4.84375 c -0.265625,0.0625 -0.390625,0.078125 -0.921875,0.078125 H 2.09375 l 0.234375,-1.21875 C 2.5,-6.921875 2.796875,-7.34375 3.296875,-7.34375 c 0.328125,0 0.625,0.171875 0.796875,0.421875 l 0.109375,-0.03125 C 4.25,-7.125 4.359375,-7.46875 4.453125,-7.6875 L 4.5,-7.859375 C 4.328125,-7.921875 3.984375,-8 3.734375,-8 3.625,-8 3.453125,-7.96875 3.359375,-7.921875 3.09375,-7.796875 2.203125,-7.125 1.953125,-6.875 c -0.234375,0.25 -0.375,0.578125 -0.484375,1.1875 l -0.171875,0.859375 c -0.453125,0.21875 -0.671875,0.3125 -0.9375,0.40625 l -0.046875,0.25 h 0.875 L 1.09375,-3.5625 C 0.765625,-1.4375 0.359375,0.59375 0.125,1.34375 c -0.203125,0.640625 -0.53125,0.984375 -0.921875,0.984375 -0.25,0 -0.375,-0.078125 -0.5625,-0.328125 l -0.15625,0.046875 c -0.046875,0.25 -0.203125,0.78125 -0.25,0.875 0.09375,0.0625 0.265625,0.09375 0.375,0.09375 0.453125,0 1.046875,-0.34375 1.46875,-0.859375 0.65625,-0.78125 0.90625,-1.546875 1.703125,-5.375 0.03125,-0.125 0.109375,-0.546875 0.203125,-0.953125 z m 0,0"
+ id="path506"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph9-11"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 3.03125,-5.25 c -0.46875,0 -0.984375,0.171875 -1.46875,0.53125 -0.890625,0.625 -1.375,1.734375 -1.375,3.0625 0,1.15625 0.484375,1.78125 1.390625,1.78125 0.609375,0 1.25,-0.28125 1.75,-0.75 C 4,-1.265625 4.484375,-2.515625 4.484375,-3.609375 4.484375,-4.640625 3.9375,-5.25 3.03125,-5.25 Z M 2.625,-4.84375 c 0.671875,0 1.03125,0.484375 1.03125,1.40625 0,1.046875 -0.34375,2.203125 -0.828125,2.78125 -0.1875,0.234375 -0.46875,0.359375 -0.8125,0.359375 -0.625,0 -1,-0.484375 -1,-1.34375 0,-1.234375 0.421875,-2.578125 0.953125,-3.015625 0.140625,-0.125 0.390625,-0.1875 0.65625,-0.1875 z m 0,0"
+ id="path509"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph9-12"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 3.734375,-3.59375 h 0.25 C 4.0625,-4.3125 4.140625,-4.71875 4.25,-5 3.984375,-5.15625 3.59375,-5.25 3.203125,-5.25 c -0.5,0 -1.3125,0.390625 -1.921875,0.9375 -0.59375,0.53125 -1.015625,1.6875 -1.015625,2.828125 0,1.046875 0.46875,1.609375 1.34375,1.609375 0.578125,0 1.109375,-0.21875 1.703125,-0.71875 L 3.859375,-1.03125 3.78125,-1.25 3.609375,-1.140625 C 2.828125,-0.625 2.40625,-0.4375 2.03125,-0.4375 c -0.609375,0 -0.9375,-0.4375 -0.9375,-1.296875 0,-1.171875 0.390625,-2.3125 0.921875,-2.71875 0.234375,-0.1875 0.5,-0.265625 0.828125,-0.265625 0.5,0 0.890625,0.203125 0.890625,0.46875 z m 0,0"
+ id="path512"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph9-13"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 0.34375,-1.609375 c -0.0625,0.546875 -0.125,0.9375 -0.25,1.4375 0.421875,0.1875 0.796875,0.296875 1.171875,0.296875 0.53125,0 1.03125,-0.203125 1.59375,-0.640625 0.546875,-0.4375 0.78125,-0.84375 0.78125,-1.375 0,-0.59375 -0.34375,-0.90625 -1.15625,-1.0625 l -0.46875,-0.09375 c -0.65625,-0.109375 -0.859375,-0.296875 -0.859375,-0.75 0,-0.578125 0.484375,-1.03125 1.109375,-1.03125 0.453125,0 0.859375,0.203125 1.03125,0.5 v 0.59375 H 3.5625 C 3.59375,-4.109375 3.640625,-4.40625 3.765625,-4.96875 3.34375,-5.171875 3.03125,-5.25 2.671875,-5.25 c -0.5625,0 -1.234375,0.328125 -1.75,0.84375 -0.296875,0.296875 -0.40625,0.5625 -0.40625,0.953125 0,0.625 0.328125,0.96875 1.0625,1.109375 L 2.25,-2.21875 C 2.734375,-2.125 2.90625,-1.9375 2.90625,-1.5 c 0,0.703125 -0.5,1.1875 -1.265625,1.1875 -0.390625,0 -0.71875,-0.125 -1.03125,-0.390625 v -0.90625 z m 0,0"
+ id="path515"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph9-14"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 7.96875,-1.125 -0.296875,0.1875 c -0.3125,0.203125 -0.546875,0.328125 -0.65625,0.328125 -0.078125,0 -0.140625,-0.09375 -0.140625,-0.21875 0,-0.109375 0.015625,-0.203125 0.078125,-0.4375 L 7.765625,-4.5 C 7.8125,-4.6875 7.84375,-4.859375 7.84375,-4.953125 7.84375,-5.140625 7.734375,-5.25 7.5625,-5.25 c -0.328125,0 -0.734375,0.1875 -1.40625,0.65625 -0.6875,0.5 -1.0625,0.875 -1.59375,1.625 L 4.890625,-4.453125 C 4.921875,-4.671875 4.96875,-4.84375 4.96875,-4.921875 4.96875,-5.125 4.859375,-5.25 4.671875,-5.25 4.359375,-5.25 3.84375,-5.015625 3.25,-4.5625 2.765625,-4.1875 2.546875,-3.953125 1.84375,-3 L 2.203125,-4.453125 C 2.25,-4.625 2.265625,-4.78125 2.265625,-4.921875 2.265625,-5.125 2.171875,-5.25 2.015625,-5.25 1.78125,-5.25 1.375,-5.03125 0.5625,-4.453125 L 0.265625,-4.234375 0.34375,-4.015625 0.6875,-4.25 c 0.40625,-0.25 0.4375,-0.265625 0.515625,-0.265625 0.109375,0 0.1875,0.109375 0.1875,0.265625 0,0.5625 -0.4375,2.671875 -0.890625,4.234375 l 0.03125,0.109375 0.75,-0.15625 0.234375,-1.109375 c 0.265625,-1.21875 0.5625,-1.859375 1.15625,-2.515625 0.453125,-0.5 0.953125,-0.828125 1.25,-0.828125 0.078125,0 0.125,0.09375 0.125,0.234375 0,0.375 -0.234375,1.625 -0.6875,3.53125 L 3.1875,-0.015625 3.234375,0.09375 3.984375,-0.0625 4.21875,-1.234375 C 4.390625,-2.109375 4.765625,-2.9375 5.25,-3.46875 c 0.59375,-0.671875 1.140625,-1.046875 1.484375,-1.046875 0.09375,0 0.140625,0.09375 0.140625,0.265625 0,0.265625 -0.03125,0.421875 -0.265625,1.375 C 6.4375,-2.1875 6.25,-1.53125 6.140625,-1.171875 6.046875,-0.859375 6,-0.578125 6,-0.34375 c 0,0.28125 0.125,0.4375 0.328125,0.4375 0.28125,0 0.671875,-0.21875 1.75,-1.015625 z m 0,0"
+ id="path518"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph9-15"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 1.25,-0.03125 1.359375,0 c 0.28125,0.09375 0.4375,0.125 0.5625,0.125 0.609375,0 1.859375,-0.796875 2.328125,-1.484375 0.453125,-0.671875 0.828125,-1.875 0.828125,-2.6875 0,-0.734375 -0.25,-1.203125 -0.65625,-1.203125 -0.453125,0 -1.03125,0.28125 -1.59375,0.796875 -0.453125,0.375 -0.671875,0.65625 -1.0625,1.296875 L 2.03125,-4.453125 c 0.03125,-0.1875 0.046875,-0.34375 0.046875,-0.46875 C 2.078125,-5.140625 2,-5.25 1.84375,-5.25 c -0.234375,0 -0.640625,0.21875 -1.453125,0.796875 l -0.296875,0.21875 0.0625,0.21875 L 0.515625,-4.25 C 0.8125,-4.4375 0.9375,-4.5 1.03125,-4.5 c 0.109375,0 0.1875,0.109375 0.1875,0.25 0,0.09375 -0.015625,0.3125 -0.046875,0.421875 l -0.625,3.734375 C 0.4375,0.5625 0.234375,1.5625 0.015625,2.546875 L -0.078125,2.9375 0,3.015625 C 0.234375,2.9375 0.453125,2.875 0.796875,2.828125 Z M 1.5625,-1.796875 C 1.796875,-3.203125 2.96875,-4.625 3.875,-4.625 c 0.28125,0 0.40625,0.234375 0.40625,0.75 0,0.875 -0.4375,2.171875 -1.015625,3 -0.21875,0.3125 -0.5625,0.484375 -1.015625,0.484375 -0.328125,0 -0.609375,-0.078125 -0.890625,-0.25 z m 0,0"
+ id="path521"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph9-16"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 3.625,-1.0625 C 3.5625,-0.796875 3.515625,-0.5 3.515625,-0.34375 c 0,0.28125 0.109375,0.4375 0.328125,0.4375 0.28125,0 0.671875,-0.21875 1.734375,-1.015625 L 5.46875,-1.125 5.1875,-0.9375 c -0.265625,0.171875 -0.546875,0.296875 -0.65625,0.296875 -0.09375,0 -0.140625,-0.078125 -0.140625,-0.1875 0,-0.09375 0.015625,-0.203125 0.046875,-0.375 l 0.015625,-0.0625 c 0.25,-1.1875 0.578125,-2.640625 0.90625,-3.890625 l -0.0625,-0.09375 -0.75,0.171875 -0.109375,0.5625 C 4.28125,-3.671875 4,-2.921875 3.671875,-2.40625 c -0.65625,0.984375 -1.5,1.765625 -1.9375,1.765625 -0.09375,0 -0.140625,-0.09375 -0.140625,-0.28125 0,-0.15625 0.015625,-0.296875 0.078125,-0.578125 l 0.625,-2.953125 C 2.328125,-4.625 2.34375,-4.78125 2.34375,-4.921875 2.34375,-5.125 2.25,-5.25 2.09375,-5.25 c -0.21875,0 -0.625,0.21875 -1.4375,0.796875 l -0.3125,0.21875 0.078125,0.21875 L 0.78125,-4.25 c 0.296875,-0.1875 0.421875,-0.25 0.515625,-0.25 0.09375,0 0.1875,0.109375 0.1875,0.21875 0,0.046875 -0.015625,0.15625 -0.015625,0.203125 L 0.78125,-0.84375 C 0.75,-0.734375 0.734375,-0.484375 0.734375,-0.328125 c 0,0.25 0.15625,0.453125 0.375,0.453125 0.6875,0 2.015625,-1.21875 2.921875,-2.734375 z m 0,0"
+ id="path524"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph9-17"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 5.265625,-7.875 5.140625,-8 c -0.578125,0.296875 -0.96875,0.390625 -1.75,0.46875 L 3.34375,-7.3125 H 3.875 c 0.265625,0 0.375,0.078125 0.375,0.265625 0,0.09375 -0.015625,0.1875 -0.03125,0.265625 L 3.921875,-5.109375 C 3.59375,-5.203125 3.296875,-5.25 3.015625,-5.25 c -0.75,0 -1.765625,0.78125 -2.28125,1.734375 -0.328125,0.625 -0.546875,1.65625 -0.546875,2.578125 0,0.703125 0.15625,1.0625 0.484375,1.0625 0.28125,0 0.6875,-0.1875 1.0625,-0.484375 0.59375,-0.484375 0.953125,-0.90625 1.671875,-2 l -0.25,0.984375 c -0.109375,0.484375 -0.171875,0.828125 -0.171875,1.109375 0,0.234375 0.109375,0.359375 0.28125,0.359375 0.1875,0 0.453125,-0.125 0.828125,-0.40625 l 0.875,-0.640625 -0.109375,-0.21875 -0.46875,0.34375 c -0.15625,0.109375 -0.328125,0.1875 -0.4375,0.1875 -0.078125,0 -0.140625,-0.09375 -0.140625,-0.25 0,-0.09375 0,-0.1875 0.078125,-0.5 z m -1.59375,4.046875 c -0.1875,0.953125 -0.734375,1.921875 -1.40625,2.5625 -0.375,0.359375 -0.8125,0.625 -1.015625,0.625 -0.1875,0 -0.28125,-0.15625 -0.28125,-0.421875 0,-1.375 0.5,-3.078125 1.03125,-3.5 0.15625,-0.109375 0.359375,-0.15625 0.65625,-0.15625 0.46875,0 0.796875,0.0625 1.15625,0.25 z m 0,0"
+ id="path527"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph9-18"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 2.5625,-7.875 2.4375,-8 c -0.578125,0.296875 -0.96875,0.390625 -1.75,0.46875 L 0.640625,-7.3125 h 0.53125 c 0.25,0 0.359375,0.078125 0.359375,0.265625 0,0.078125 0,0.203125 -0.015625,0.265625 l -1.09375,6 C 0.40625,-0.734375 0.40625,-0.703125 0.40625,-0.671875 c 0,0.4375 0.515625,0.796875 1.125,0.796875 0.40625,0 0.953125,-0.21875 1.421875,-0.546875 C 4,-1.171875 4.71875,-2.65625 4.71875,-4.09375 4.71875,-4.515625 4.625,-4.9375 4.5,-5.109375 4.421875,-5.203125 4.28125,-5.25 4.109375,-5.25 c -0.265625,0 -0.59375,0.078125 -0.890625,0.234375 -0.5625,0.296875 -0.921875,0.625 -1.59375,1.484375 z m 0.953125,3.25 c 0.28125,0 0.421875,0.25 0.421875,0.78125 0,0.703125 -0.234375,1.640625 -0.5625,2.34375 -0.359375,0.765625 -0.796875,1.109375 -1.375,1.109375 -0.5,0 -0.78125,-0.25 -0.78125,-0.703125 0,-0.28125 0.046875,-0.578125 0.140625,-0.984375 0.21875,-0.875 0.46875,-1.40625 0.90625,-1.859375 0.359375,-0.375 0.9375,-0.6875 1.25,-0.6875 z m 0,0"
+ id="path530"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph9-19"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 3.625,-4.609375 3.734375,-4.40625 C 3.84375,-4.46875 3.9375,-4.5 4.03125,-4.5 4.328125,-4.5 4.5,-4.25 4.5,-3.8125 c 0,1.609375 -1.046875,3.390625 -2.015625,3.390625 -0.53125,0 -0.875,-0.46875 -0.875,-1.171875 0,-1.140625 0.4375,-2.140625 1.5,-3.46875 L 3,-5.25 c -0.21875,0.09375 -0.359375,0.125 -0.671875,0.125 -0.296875,0 -0.75,-0.015625 -1.078125,-0.0625 H 1.125 C 1.0625,-5.203125 1,-5.203125 1,-5.203125 0.859375,-5.203125 0.765625,-5.1875 0.640625,-5.125 0.484375,-4.828125 0.375,-4.4375 0.234375,-3.84375 H 0.46875 l 0.1875,-0.453125 c 0.078125,-0.1875 0.296875,-0.3125 0.546875,-0.3125 0.0625,0 0.15625,0 0.296875,0.015625 0.09375,0.015625 0.171875,0.015625 0.3125,0.015625 0.234375,0 0.40625,-0.015625 0.6875,-0.046875 -1.21875,1.3125 -1.671875,2.21875 -1.671875,3.3125 0,0.828125 0.5,1.4375 1.203125,1.4375 0.453125,0 0.875,-0.203125 1.375,-0.65625 C 3.921875,-1 4.4375,-1.71875 4.78125,-2.46875 5.015625,-3.015625 5.203125,-3.75 5.203125,-4.1875 c 0,-0.625 -0.28125,-1.0625 -0.671875,-1.0625 -0.140625,0 -0.28125,0.03125 -0.359375,0.125 z m 0,0"
+ id="path533"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph9-20"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 6.046875,-4.609375 6.15625,-4.40625 C 6.265625,-4.46875 6.359375,-4.5 6.453125,-4.5 c 0.296875,0 0.46875,0.25 0.46875,0.6875 0,1.578125 -1.046875,3.390625 -1.984375,3.390625 -0.3125,0 -0.546875,-0.4375 -0.546875,-1.046875 0,-0.5 0.109375,-1.21875 0.234375,-1.6875 L 5.1875,-5.171875 5.109375,-5.25 4.3125,-5.015625 l -0.328125,1.625 c -0.125,0.671875 -0.28125,1.109375 -0.59375,1.703125 -0.4375,0.8125 -0.84375,1.265625 -1.171875,1.265625 -0.390625,0 -0.640625,-0.421875 -0.640625,-1.0625 0,-1.296875 0.453125,-2.359375 1.53125,-3.578125 L 3,-5.25 c -0.21875,0.09375 -0.359375,0.125 -0.671875,0.125 -0.296875,0 -0.75,-0.015625 -1.078125,-0.0625 H 1.125 C 1.0625,-5.203125 1,-5.203125 1,-5.203125 0.859375,-5.203125 0.765625,-5.1875 0.640625,-5.125 0.484375,-4.828125 0.375,-4.4375 0.234375,-3.84375 H 0.46875 l 0.1875,-0.453125 c 0.078125,-0.1875 0.296875,-0.3125 0.546875,-0.3125 0.0625,0 0.15625,0 0.296875,0.015625 0.09375,0.015625 0.171875,0.015625 0.328125,0.015625 0.21875,0 0.359375,-0.015625 0.640625,-0.046875 L 2.4375,-4.578125 C 2.359375,-4.5 2.328125,-4.4375 2.140625,-4.234375 1.15625,-3.09375 0.78125,-2.296875 0.78125,-1.375 c 0,0.890625 0.390625,1.5 0.96875,1.5 0.6875,0 1.265625,-0.609375 2.0625,-2.171875 C 3.71875,-1.5625 3.6875,-1.28125 3.6875,-1.03125 3.6875,-0.34375 4,0.125 4.5,0.125 5.375,0.125 6.515625,-0.984375 7.203125,-2.46875 7.4375,-3.015625 7.625,-3.75 7.625,-4.1875 7.625,-4.8125 7.34375,-5.25 6.953125,-5.25 6.8125,-5.25 6.671875,-5.21875 6.59375,-5.125 Z m 0,0"
+ id="path536"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph9-21"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 1.234375,-1.21875 c -0.359375,0 -0.65625,0.34375 -0.65625,0.765625 0,0.3125 0.1875,0.5 0.484375,0.5 0.359375,0 0.65625,-0.34375 0.65625,-0.75 C 1.71875,-1 1.5,-1.21875 1.234375,-1.21875 Z m 0,0"
+ id="path539"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph9-22"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 0.234375,-0.953125 c 0,0.21875 -0.03125,0.359375 -0.09375,0.703125 C 0.125,-0.125 0.109375,-0.09375 0.09375,-0.015625 0.265625,0.078125 0.4375,0.125 0.5625,0.125 c 0.359375,0 0.796875,-0.328125 1.125,-0.828125 l 0.828125,-1.28125 0.125,0.75 C 2.78125,-0.3125 3.03125,0.125 3.421875,0.125 c 0.25,0 0.59375,-0.1875 0.9375,-0.5 L 4.890625,-0.859375 4.796875,-1.0625 c -0.390625,0.328125 -0.65625,0.484375 -0.84375,0.484375 -0.15625,0 -0.296875,-0.109375 -0.40625,-0.328125 -0.09375,-0.203125 -0.21875,-0.609375 -0.28125,-0.921875 l -0.1875,-1.109375 0.375,-0.53125 c 0.515625,-0.703125 0.8125,-0.953125 1.15625,-0.953125 0.171875,0 0.296875,0.078125 0.359375,0.25 L 5.109375,-4.21875 5.28125,-5.140625 C 5.140625,-5.21875 5.046875,-5.25 4.953125,-5.25 c -0.4375,0 -0.875,0.390625 -1.546875,1.390625 L 3,-3.265625 2.9375,-3.78125 C 2.796875,-4.859375 2.515625,-5.25 1.859375,-5.25 1.578125,-5.25 1.34375,-5.171875 1.25,-5.03125 l -0.640625,0.90625 0.1875,0.109375 c 0.328125,-0.375 0.546875,-0.515625 0.75,-0.515625 0.359375,0 0.609375,0.4375 0.78125,1.515625 l 0.125,0.671875 -0.4375,0.671875 C 1.546875,-0.9375 1.171875,-0.59375 0.875,-0.59375 0.703125,-0.59375 0.59375,-0.625 0.5625,-0.6875 L 0.453125,-1 Z m 0,0"
+ id="path542"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph9-23"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 5.03125,-7.6875 -4.0625,6.46875 C 0.578125,-0.609375 0.375,-0.4375 0.015625,-0.34375 l -0.21875,0.046875 V 0.03125 C 0.796875,0 0.796875,0 1.015625,0 1.21875,0 1.25,0 2.1875,0.03125 V -0.265625 L 1.609375,-0.3125 c -0.171875,0 -0.328125,-0.109375 -0.328125,-0.203125 0,-0.078125 0.078125,-0.234375 0.28125,-0.59375 l 1.109375,-1.90625 h 2.6875 L 5.625,-0.96875 v 0.03125 c 0,0.015625 0.015625,0.046875 0.03125,0.09375 0,0.09375 0.015625,0.203125 0.015625,0.25 0,0.171875 -0.171875,0.265625 -0.46875,0.28125 L 4.71875,-0.265625 V 0.03125 C 5.828125,0 5.828125,0 6.046875,0 c 0.21875,0 0.21875,0 1.34375,0.03125 V -0.265625 L 7.09375,-0.3125 C 6.59375,-0.375 6.578125,-0.375 6.484375,-0.875 l -0.9375,-6.8125 z m -0.140625,1.140625 0.40625,3.140625 H 2.9375 Z m 0,0"
+ id="path545"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph9-24"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 3.8125,-7.234375 0.03125,-0.3125 H 3.578125 l -0.984375,0.03125 c -0.171875,0 -0.375,0 -0.84375,-0.015625 l -0.6875,-0.015625 -0.03125,0.3125 0.5,0.015625 c 0.265625,0.015625 0.390625,0.09375 0.390625,0.296875 0,0.15625 -0.046875,0.484375 -0.09375,0.828125 l -0.9375,5.5 C 0.84375,-0.40625 0.703125,-0.328125 0.234375,-0.25 L 0.171875,0.03125 0.625,0.015625 C 0.953125,0.015625 1.640625,0 1.859375,0 l 2.34375,0.03125 H 4.3125 c 0.140625,0 0.421875,-0.015625 0.78125,-0.015625 L 5.453125,0 C 5.4375,-0.265625 5.5625,-1.125 5.703125,-1.796875 H 5.359375 L 5.1875,-1.125 C 5.078125,-0.78125 4.984375,-0.59375 4.890625,-0.53125 4.75,-0.484375 4.046875,-0.421875 3.3125,-0.421875 2.78125,-0.421875 2.484375,-0.4375 1.765625,-0.5 v -0.046875 c 0,-0.15625 0.015625,-0.234375 0.015625,-0.34375 L 2.796875,-6.6875 C 2.875,-7.09375 2.9375,-7.171875 3.34375,-7.203125 Z m 0,0"
+ id="path548"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph10-0"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d=""
+ id="path551"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph10-1"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 1.828125,-7.3125 c -0.40625,0 -0.734375,0.328125 -0.734375,0.71875 0,0.40625 0.328125,0.734375 0.734375,0.734375 0.390625,0 0.71875,-0.328125 0.71875,-0.734375 0,-0.390625 -0.328125,-0.71875 -0.71875,-0.71875 z m 0,0"
+ id="path554"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph10-2"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 3,-7.3125 c -0.421875,0 -0.75,0.328125 -0.75,0.71875 0,0.40625 0.328125,0.734375 0.734375,0.734375 0.40625,0 0.734375,-0.328125 0.734375,-0.734375 C 3.71875,-6.984375 3.390625,-7.3125 3,-7.3125 Z m -2.34375,0 c -0.421875,0 -0.75,0.328125 -0.75,0.71875 0,0.40625 0.328125,0.734375 0.734375,0.734375 0.40625,0 0.734375,-0.328125 0.734375,-0.734375 0,-0.390625 -0.328125,-0.71875 -0.71875,-0.71875 z m 0,0"
+ id="path557"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph10-3"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 1.6875,-7.390625 H 1.46875 C 1.359375,-7.390625 1,-7.40625 0.375,-7.421875 v 0.390625 l 0.40625,0.046875 c 0.5,0.03125 0.53125,0.109375 0.53125,1.0625 v 4.453125 c 0,0.953125 -0.03125,1.015625 -0.53125,1.0625 L 0.375,-0.375 V 0.03125 C 1.421875,0 1.421875,0 1.640625,0 1.859375,0 2.359375,0.015625 2.875,0.03125 V -0.375 L 2.46875,-0.40625 C 1.984375,-0.453125 1.953125,-0.5 1.953125,-1.46875 V -5.765625 L 5.5,-1.484375 c 0.296875,0.34375 0.5625,0.65625 1.34375,1.515625 L 7.734375,0.171875 7.8125,0.125 C 7.78125,-0.484375 7.78125,-0.71875 7.78125,-0.96875 v -4.953125 c 0,-0.953125 0.03125,-1.03125 0.515625,-1.0625 l 0.40625,-0.046875 v -0.390625 l -1.25,0.03125 c -0.109375,0 -0.28125,0 -1.25,-0.03125 v 0.390625 l 0.40625,0.046875 C 7.09375,-6.953125 7.125,-6.875 7.125,-5.921875 V -2 L 3.734375,-5.984375 C 3.5,-6.234375 3.296875,-6.5 2.609375,-7.421875 Z m 0,0"
+ id="path560"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph10-4"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 3.125,-5.140625 c -1.734375,0 -2.6875,0.96875 -2.6875,2.703125 0,1.671875 0.890625,2.625 2.46875,2.625 1.75,0 2.734375,-1 2.734375,-2.765625 0,-1.65625 -0.890625,-2.5625 -2.515625,-2.5625 z M 3,-4.609375 c 0.78125,0 1.15625,0.765625 1.15625,2.375 0,1.328125 -0.3125,1.890625 -1.046875,1.890625 -0.78125,0 -1.1875,-0.796875 -1.1875,-2.359375 C 1.921875,-4.03125 2.25,-4.609375 3,-4.609375 Z m 0,0"
+ id="path563"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph10-5"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 3.53125,-0.65625 3.453125,-0.859375 C 3.234375,-0.75 3.125,-0.71875 2.9375,-0.71875 c -0.5,0 -0.671875,-0.171875 -0.671875,-0.703125 V -4.03125 h 1.15625 l 0.09375,-0.703125 -1.25,0.0625 v -0.625 c 0,-0.578125 0.03125,-0.9375 0.140625,-1.484375 L 2.25,-6.890625 c -0.4375,0.203125 -0.78125,0.34375 -1.328125,0.546875 0.03125,0.5625 0.03125,0.765625 0.03125,1 v 0.65625 l -0.71875,0.46875 v 0.234375 L 0.9375,-4.03125 v 2.828125 c 0,0.96875 0.40625,1.390625 1.359375,1.390625 C 2.625,0.1875 2.90625,0.109375 3,-0.015625 Z m 0,0"
+ id="path566"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph10-6"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 5.03125,-3.21875 c 0,-1.171875 -0.828125,-1.921875 -2.0625,-1.921875 -0.390625,0 -0.671875,0.078125 -0.96875,0.25 l -0.5,0.3125 C 0.78125,-4.15625 0.453125,-3.5 0.453125,-2.46875 c 0,1.734375 0.875,2.65625 2.5,2.65625 0.625,0 1.03125,-0.109375 1.71875,-0.46875 l 0.25,-0.53125 -0.140625,-0.15625 c -0.5,0.28125 -0.828125,0.375 -1.265625,0.375 -0.578125,0 -1.109375,-0.296875 -1.375,-0.765625 -0.171875,-0.3125 -0.25,-0.5625 -0.265625,-1.078125 h 1.421875 c 0.625,0 1.125,-0.0625 1.734375,-0.21875 z m -2.3125,0.234375 H 2.6875 c -0.0625,0 -0.359375,0 -0.53125,-0.015625 L 1.8125,-3.015625 C 1.84375,-4.09375 2.109375,-4.53125 2.734375,-4.53125 c 0.625,0 0.890625,0.4375 0.90625,1.515625 z m 0,0"
+ id="path569"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph10-7"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 1.375,-1.578125 c -0.453125,0 -0.859375,0.40625 -0.859375,0.875 0,0.453125 0.390625,0.828125 0.84375,0.828125 0.453125,0 0.859375,-0.390625 0.859375,-0.828125 0,-0.453125 -0.40625,-0.875 -0.84375,-0.875 z m 0,-3.375 c -0.453125,0 -0.859375,0.40625 -0.859375,0.859375 0,0.46875 0.390625,0.84375 0.84375,0.84375 0.453125,0 0.859375,-0.390625 0.859375,-0.84375 0,-0.4375 -0.40625,-0.859375 -0.84375,-0.859375 z m 0,0"
+ id="path572"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph11-0"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d=""
+ id="path575"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph11-1"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 0.234375,-3.984375 0.140625,0.28125 0.40625,-0.21875 c 0.109375,-0.0625 0.25,-0.109375 0.359375,-0.109375 0.109375,0 0.171875,0.0625 0.171875,0.171875 0,0.234375 -0.109375,0.78125 -0.359375,1.75 -0.046875,0.171875 -0.109375,0.375 -0.171875,0.625 v 0.0625 L 0.71875,-1.25 c -0.09375,0.40625 -0.140625,0.625 -0.140625,0.8125 0,0.375 0.203125,0.625 0.484375,0.625 0.765625,0 1.78125,-0.921875 2.859375,-2.625 l -0.34375,1.265625 c -0.125,0.4375 -0.171875,0.71875 -0.171875,0.890625 0,0.265625 0.15625,0.46875 0.375,0.46875 0.21875,0 0.875,-0.375 1.515625,-0.859375 0.0625,-0.046875 0.25,-0.1875 0.390625,-0.28125 l -0.109375,-0.3125 -0.25,0.1875 c -0.21875,0.15625 -0.4375,0.265625 -0.59375,0.265625 -0.09375,0 -0.15625,-0.046875 -0.15625,-0.125 0,-0.140625 0.0625,-0.421875 0.171875,-0.78125 0,-0.03125 0,-0.078125 0.03125,-0.140625 0,-0.03125 0.25,-0.96875 0.390625,-1.546875 0.09375,-0.359375 0.1875,-0.671875 0.453125,-1.546875 l -0.09375,-0.125 C 5.0625,-4.921875 4.6875,-4.859375 4.328125,-4.875 4.3125,-4.0625 3.984375,-3.046875 3.453125,-2.203125 2.9375,-1.359375 2.34375,-0.78125 2.03125,-0.78125 c -0.09375,0 -0.171875,-0.0625 -0.171875,-0.140625 0,-0.171875 0.03125,-0.4375 0.109375,-0.71875 L 2.34375,-3.125 c 0.28125,-1.078125 0.3125,-1.296875 0.3125,-1.609375 0,-0.234375 -0.109375,-0.375 -0.296875,-0.375 -0.28125,0 -0.765625,0.203125 -1.265625,0.546875 z m 0,0"
+ id="path578"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph11-2"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 4.0625,0.1875 h 0.265625 c 0.5,-0.890625 0.640625,-1.125 0.78125,-1.359375 L 8.28125,-6.390625 7.390625,-1.09375 c 0,0.015625 -0.015625,0.0625 -0.03125,0.109375 -0.015625,0.0625 -0.015625,0.09375 -0.015625,0.09375 C 7.265625,-0.484375 7.21875,-0.4375 6.875,-0.375 l -0.53125,0.03125 -0.046875,0.375 C 7.71875,0 7.71875,0 7.984375,0 8.25,0 8.25,0 9.640625,0.03125 l 0.0625,-0.375 L 9.09375,-0.375 C 8.8125,-0.390625 8.703125,-0.484375 8.703125,-0.703125 8.703125,-0.78125 8.71875,-0.90625 8.75,-1.109375 8.765625,-1.15625 8.78125,-1.1875 8.78125,-1.203125 l 0.875,-5.09375 c 0.109375,-0.578125 0.203125,-0.71875 0.609375,-0.75 l 0.421875,-0.03125 0.0625,-0.34375 c -1.046875,0.03125 -1.046875,0.03125 -1.25,0.03125 -0.203125,0 -0.71875,-0.015625 -1.234375,-0.03125 -0.859375,1.546875 -1.5,2.609375 -3.359375,5.53125 l -1.390625,-5.53125 c -1,0.03125 -1,0.03125 -1.203125,0.03125 -0.203125,0 -0.703125,-0.015625 -1.203125,-0.03125 l -0.046875,0.34375 0.484375,0.03125 c 0.40625,0.015625 0.578125,0.125 0.578125,0.359375 0,0.203125 -0.03125,0.421875 -0.09375,0.671875 L 0.671875,-1 c -0.109375,0.40625 -0.25,0.59375 -0.4375,0.625 L -0.234375,-0.34375 -0.25,0.03125 C 0.734375,0 0.734375,0 0.9375,0 1.140625,0 1.625,0.015625 2.109375,0.03125 l 0.0625,-0.375 -0.5625,-0.03125 C 1.34375,-0.390625 1.25,-0.484375 1.25,-0.703125 c 0,-0.109375 0,-0.125 0.078125,-0.46875 L 2.5,-5.984375 Z m 0,0"
+ id="path581"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph11-3"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 6.78125,-5.28125 h 0.390625 c 0.046875,-0.515625 0.125,-0.828125 0.40625,-1.828125 C 6.875,-7.4375 6.25,-7.578125 5.5,-7.578125 c -2.625,0 -4.75,2.15625 -4.75,4.859375 0,1.046875 0.3125,1.796875 0.90625,2.296875 C 2.140625,-0.03125 2.875,0.1875 3.703125,0.1875 c 1.03125,0 1.8125,-0.28125 3.015625,-1.03125 L 6.859375,-1.265625 6.75,-1.359375 6.515625,-1.21875 c -0.828125,0.484375 -1.484375,0.6875 -2.15625,0.6875 -1.4375,0 -2.109375,-0.765625 -2.109375,-2.4375 0,-2.421875 1.046875,-3.96875 2.671875,-3.96875 1.015625,0 1.9375,0.34375 1.921875,0.703125 z m 0,0"
+ id="path584"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph11-4"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 3.953125,-4.390625 C 4.03125,-4.671875 4.0625,-4.796875 4.125,-4.953125 L 4.0625,-5.03125 c -0.53125,0.046875 -0.859375,0.0625 -1.34375,0.078125 0.046875,-0.140625 0.0625,-0.265625 0.078125,-0.3125 0.25,-1.4375 0.40625,-1.78125 0.828125,-1.78125 0.296875,0 0.46875,0.125 0.796875,0.609375 L 4.59375,-6.46875 C 4.671875,-6.984375 4.75,-7.3125 4.890625,-7.828125 c -0.234375,-0.0625 -0.359375,-0.09375 -0.5,-0.09375 -0.234375,0 -0.359375,0.046875 -0.625,0.21875 -0.3125,0.1875 -0.703125,0.46875 -0.859375,0.609375 L 2.296875,-6.5625 C 1.8125,-6.125 1.6875,-5.90625 1.5625,-5.265625 c -0.03125,0.125 -0.03125,0.15625 -0.046875,0.21875 C 1.40625,-5 1.25,-4.921875 0.96875,-4.84375 L 0.421875,-4.671875 0.375,-4.390625 H 1.359375 L 0.59375,-0.3125 C 0.5625,-0.125 0.515625,0.0625 0.5,0.15625 0.1875,1.828125 0.0625,2.140625 -0.328125,2.140625 -0.5625,2.140625 -0.734375,2 -0.890625,1.65625 L -1.0625,1.6875 C -1.125,2 -1.296875,2.59375 -1.421875,2.859375 -1.3125,2.90625 -1.140625,2.953125 -1,2.953125 -0.625,2.953125 0.046875,2.515625 0.484375,2 1.15625,1.203125 1.484375,0.390625 2,-1.828125 l 0.609375,-2.5625 z m 0,0"
+ id="path587"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph12-0"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d=""
+ id="path590"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph12-1"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 1.6875,-3.75 1.125,-3.578125 C 0.859375,-3.5 0.71875,-3.46875 0.3125,-3.421875 L 0.234375,-3.40625 V -3.125 l 0.359375,0.03125 c 0.171875,0.015625 0.203125,0.078125 0.203125,0.546875 v 3.78125 c 0,0.4375 -0.046875,0.5 -0.28125,0.515625 L 0.234375,1.765625 V 2.0625 l 1.03125,-0.03125 c 0.09375,0 0.109375,0 1.15625,0.03125 V 1.765625 L 2.0625,1.75 C 1.8125,1.734375 1.765625,1.671875 1.765625,1.234375 V 0 C 2.15625,0.109375 2.28125,0.140625 2.484375,0.140625 2.75,0.140625 3.03125,-0.015625 3.5625,-0.4375 3.625,-0.46875 3.671875,-0.515625 3.703125,-0.546875 4.234375,-0.9375 4.515625,-1.5625 4.515625,-2.28125 4.515625,-3.125 3.90625,-3.75 3.078125,-3.75 2.765625,-3.75 2.53125,-3.671875 2.25,-3.4375 l -0.484375,0.390625 v -0.65625 z m 0.078125,1.109375 C 1.9375,-2.90625 2.203125,-3.03125 2.53125,-3.03125 c 0.609375,0 0.984375,0.46875 0.984375,1.265625 0,0.828125 -0.359375,1.359375 -0.9375,1.359375 -0.34375,0 -0.59375,-0.140625 -0.8125,-0.4375 z m 0,0"
+ id="path593"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph12-2"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 3.671875,-2.34375 c 0,-0.859375 -0.59375,-1.40625 -1.5,-1.40625 -0.28125,0 -0.484375,0.046875 -0.703125,0.1875 l -0.375,0.21875 c -0.53125,0.3125 -0.765625,0.78125 -0.765625,1.546875 0,1.265625 0.640625,1.9375 1.828125,1.9375 0.453125,0 0.75,-0.078125 1.265625,-0.34375 L 3.59375,-0.59375 3.5,-0.703125 C 3.125,-0.5 2.890625,-0.4375 2.5625,-0.4375 2.15625,-0.4375 1.765625,-0.65625 1.5625,-1 1.4375,-1.21875 1.390625,-1.40625 1.375,-1.78125 h 1.046875 c 0.453125,0 0.8125,-0.046875 1.25,-0.15625 z m -1.6875,0.171875 h -0.03125 c -0.046875,0 -0.25,-0.015625 -0.375,-0.015625 l -0.25,-0.015625 C 1.34375,-2.984375 1.53125,-3.3125 2,-3.3125 c 0.453125,0 0.640625,0.328125 0.65625,1.109375 z m 0,0"
+ id="path596"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph12-3"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 1.59375,-1.671875 0.71875,-0.5625 C 0.5625,-0.359375 0.421875,-0.28125 0.171875,-0.265625 V 0 h 0.71875 C 1.125,-0.359375 1.21875,-0.484375 1.3125,-0.625 l 0.5,-0.703125 L 2.609375,0.03125 3.234375,0 C 3.546875,0.015625 3.625,0.015625 3.84375,0.03125 V -0.265625 C 3.609375,-0.296875 3.4375,-0.453125 3.15625,-0.90625 l -0.703125,-1.203125 0.78125,-0.90625 C 3.46875,-3.3125 3.546875,-3.359375 3.84375,-3.359375 V -3.65625 h -0.75 L 2.234375,-2.46875 1.8125,-3.140625 C 1.625,-3.4375 1.484375,-3.625 1.328125,-3.75 c -0.28125,0.0625 -0.875,0.203125 -1.171875,0.25 l 0.0625,0.28125 c 0.046875,0 0.09375,0 0.125,0 0.265625,0 0.40625,0.109375 0.609375,0.46875 z m 0,0"
+ id="path599"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph12-4"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 2.578125,-0.484375 2.515625,-0.625 c -0.15625,0.078125 -0.234375,0.09375 -0.359375,0.09375 -0.375,0 -0.5,-0.125 -0.5,-0.515625 V -2.9375 H 2.5 L 2.5625,-3.453125 1.65625,-3.40625 V -3.875 c 0,-0.421875 0.03125,-0.6875 0.09375,-1.078125 L 1.640625,-5.03125 c -0.3125,0.140625 -0.5625,0.234375 -0.96875,0.390625 0.015625,0.421875 0.03125,0.5625 0.03125,0.734375 v 0.484375 l -0.53125,0.34375 V -2.90625 L 0.6875,-2.9375 v 2.0625 c 0,0.703125 0.296875,1.015625 0.984375,1.015625 0.25,0 0.453125,-0.0625 0.515625,-0.15625 z m 0,0"
+ id="path602"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph13-0"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d=""
+ id="path605"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph13-1"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 0.1875,-3.09375 0.25,-2.9375 0.5,-3.09375 c 0.296875,-0.1875 0.328125,-0.203125 0.375,-0.203125 0.09375,0 0.140625,0.078125 0.140625,0.203125 0,0.40625 -0.328125,1.9375 -0.65625,3.078125 l 0.0625,0.09375 C 0.625,0.015625 0.8125,-0.03125 0.984375,-0.0625 c 0.140625,-1 0.3125,-1.515625 0.6875,-2.078125 0.421875,-0.671875 1.015625,-1.15625 1.375,-1.15625 0.09375,0 0.140625,0.0625 0.140625,0.1875 0,0.140625 -0.03125,0.3125 -0.09375,0.5625 l -0.40625,1.6875 C 2.609375,-0.5625 2.578125,-0.375 2.578125,-0.25 c 0,0.203125 0.09375,0.328125 0.25,0.328125 0.203125,0 0.484375,-0.171875 1.265625,-0.75 L 4.015625,-0.828125 3.8125,-0.6875 c -0.234375,0.15625 -0.40625,0.234375 -0.484375,0.234375 -0.0625,0 -0.109375,-0.0625 -0.109375,-0.15625 0,-0.03125 0.015625,-0.125 0.015625,-0.15625 L 3.765625,-2.96875 C 3.8125,-3.203125 3.84375,-3.421875 3.84375,-3.546875 c 0,-0.1875 -0.078125,-0.296875 -0.234375,-0.296875 -0.34375,0 -0.890625,0.3125 -1.359375,0.75 -0.3125,0.265625 -0.53125,0.546875 -0.9375,1.125 L 1.609375,-3.25 C 1.640625,-3.390625 1.65625,-3.484375 1.65625,-3.578125 1.65625,-3.75 1.59375,-3.84375 1.46875,-3.84375 1.3125,-3.84375 1,-3.671875 0.421875,-3.25 Z m 0,0"
+ id="path608"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph14-0"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d=""
+ id="path611"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph14-1"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 4.8125,-1.921875 v -0.46875 H 2.90625 V -4.28125 H 2.4375 v 1.890625 H 0.515625 v 0.46875 H 2.4375 V 0 h 0.46875 v -1.921875 z m 0,0"
+ id="path614"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph15-0"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d=""
+ id="path617"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph15-1"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="m 0.53125,-4.421875 h 0.078125 l 1.03125,-0.46875 c 0.015625,0 0.015625,0 0.03125,0 0.046875,0 0.0625,0.0625 0.0625,0.265625 v 3.859375 c 0,0.421875 -0.09375,0.5 -0.515625,0.53125 L 0.765625,-0.21875 v 0.25 C 1.984375,0 1.984375,0 2.078125,0 2.1875,0 2.359375,0 2.625,0.015625 c 0.09375,0 0.375,0 0.703125,0.015625 v -0.25 L 2.921875,-0.234375 C 2.484375,-0.265625 2.40625,-0.34375 2.40625,-0.765625 v -4.71875 l -0.125,-0.046875 c -0.515625,0.265625 -1.078125,0.515625 -1.796875,0.765625 z m 0,0"
+ id="path620"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph15-2"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 0.125,-0.1875 V 0.03125 C 1.625,0 1.625,0 1.90625,0 2.1875,0 2.1875,0 3.734375,0.03125 3.71875,-0.140625 3.71875,-0.21875 3.71875,-0.328125 c 0,-0.109375 0,-0.1875 0.015625,-0.359375 C 2.8125,-0.65625 2.453125,-0.640625 0.96875,-0.609375 L 2.421875,-2.15625 c 0.78125,-0.8125 1.015625,-1.25 1.015625,-1.859375 0,-0.90625 -0.625,-1.46875 -1.640625,-1.46875 -0.5625,0 -0.953125,0.15625 -1.34375,0.546875 L 0.3125,-3.84375 h 0.234375 l 0.09375,-0.375 C 0.765625,-4.671875 1.0625,-4.875 1.59375,-4.875 c 0.6875,0 1.125,0.421875 1.125,1.109375 0,0.59375 -0.34375,1.1875 -1.234375,2.140625 z m 0,0"
+ id="path623"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph15-3"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 1.5,-3.703125 1.421875,-3.734375 C 1.015625,-3.5625 0.609375,-3.453125 0.1875,-3.40625 v 0.21875 h 0.296875 c 0.3125,0 0.34375,0.0625 0.34375,0.578125 V -0.8125 c 0,0.5 -0.015625,0.5625 -0.296875,0.578125 L 0.171875,-0.21875 v 0.25 C 0.96875,0 0.96875,0 1.15625,0 c 0.203125,0 0.203125,0 1,0.03125 v -0.25 L 1.796875,-0.234375 C 1.515625,-0.25 1.5,-0.3125 1.5,-0.8125 Z M 1.125,-5.46875 c -0.234375,0 -0.4375,0.203125 -0.4375,0.4375 0,0.234375 0.203125,0.4375 0.4375,0.4375 0.234375,0 0.4375,-0.203125 0.4375,-0.4375 0,-0.21875 -0.203125,-0.4375 -0.4375,-0.4375 z m 0,0"
+ id="path626"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph15-4"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 3.265625,0.03125 C 3.75,0 3.765625,0 3.890625,0 c 0.125,0 0.125,0 0.671875,0.03125 v -0.25 L 4.234375,-0.234375 C 3.953125,-0.25 3.9375,-0.296875 3.9375,-0.8125 v -1.53125 c 0,-0.9375 -0.4375,-1.390625 -1.328125,-1.390625 -0.296875,0 -0.46875,0.046875 -0.640625,0.203125 L 1.375,-3.015625 v -0.6875 L 1.296875,-3.734375 C 0.890625,-3.5625 0.484375,-3.453125 0.0625,-3.40625 v 0.21875 h 0.28125 c 0.328125,0 0.359375,0.0625 0.359375,0.578125 V -0.8125 c 0,0.5 -0.03125,0.5625 -0.296875,0.578125 L 0.046875,-0.21875 v 0.25 C 0.59375,0.015625 0.8125,0 1.03125,0 c 0.234375,0 0.453125,0.015625 1,0.03125 v -0.25 L 1.671875,-0.234375 C 1.390625,-0.25 1.375,-0.3125 1.375,-0.8125 V -2.5 c 0,-0.359375 0.515625,-0.75 1,-0.75 0.546875,0 0.890625,0.390625 0.890625,1.015625 z m 0,0"
+ id="path629"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph15-5"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 0.78125,-3.046875 V -0.75 c 0,0.59375 0.265625,0.84375 0.875,0.84375 0.1875,0 0.375,-0.03125 0.421875,-0.09375 L 2.46875,-0.421875 2.359375,-0.5625 c -0.203125,0.109375 -0.3125,0.15625 -0.46875,0.15625 -0.3125,0 -0.4375,-0.15625 -0.4375,-0.546875 v -2.09375 H 2.46875 l 0.078125,-0.421875 -1.09375,0.046875 v -0.3125 c 0,-0.328125 0.015625,-0.609375 0.078125,-1.125 L 1.4375,-4.953125 c -0.1875,0.125 -0.421875,0.234375 -0.671875,0.3125 0.015625,0.234375 0.03125,0.390625 0.03125,0.625 v 0.5625 l -0.625,0.28125 v 0.15625 z m 0,0"
+ id="path632"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph16-0"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d=""
+ id="path635"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph16-1"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 3.890625,21.140625 C 2.15625,18.53125 1.6875,14.734375 1.6875,10.484375 1.6875,6.25 2.15625,2.4375 3.890625,-0.171875 l -0.15625,-0.46875 c -0.59375,0.90625 -3.15625,3.515625 -3.15625,11.125 0,7.609375 2.5625,10.21875 3.15625,11.125 z m 0,0"
+ id="path638"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ <symbol
+ overflow="visible"
+ id="glyph16-2"
+ style="overflow:visible">
+ <path
+ style="stroke:none"
+ d="M 3.890625,10.484375 C 3.890625,2.875 1.3125,0.265625 0.71875,-0.640625 l -0.140625,0.46875 C 2.3125,2.4375 2.78125,6.25 2.78125,10.484375 c 0,4.25 -0.46875,8.046875 -2.203125,10.65625 l 0.140625,0.46875 c 0.59375,-0.90625 3.171875,-3.515625 3.171875,-11.125 z m 0,0"
+ id="path641"
+ inkscape:connector-curvature="0" />
+ </symbol>
+ </g>
+ </defs>
+ <path
+ inkscape:connector-curvature="0"
+ id="path4370"
+ d="m 28.34765,31.3598 5.78516,0.17969 5.78516,0.25 5.78515,0.34766 5.78125,0.46093 5.78516,0.59766 5.78516,0.76562 5.78515,0.95313 5.78516,1.14453 5.78515,1.32813 5.78516,1.5 5.78516,1.6289 5.78515,1.69141 5.78516,1.66406 5.78125,1.53125 5.78516,1.28125 5.78515,0.89063 5.78516,0.38281 5.78515,-0.23047 5.78516,-0.91406 5.78516,-1.60938 5.78515,-2.30859 5.78516,-2.91406 5.78515,-3.39063 5.78125,-3.69141 5.78516,-3.78125 5.78516,-3.69531 5.78515,-3.39062 5.78516,-2.91407 5.78516,-2.3125 5.78515,-1.625 5.78516,-0.91406 5.78515,-0.23047 5.78516,0.38282 5.78125,0.89453 5.78516,1.27734 5.78515,1.53125 5.78516,1.66406 5.78516,1.69141 5.78515,1.62891 5.78516,1.5039 5.78515,1.33203 5.78516,1.13672 5.78516,0.95313 5.78125,0.76562 5.78515,0.60547 5.78516,0.46094 5.78516,0.34375 5.78515,0.2539 5.78516,0.17579"
+ style="fill:none;stroke:#0000ff;stroke-width:0.79701;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4372"
+ d="M 0,31.00043 H 336.57422"
+ style="fill:none;stroke:#000000;stroke-width:0.3985;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4374"
+ d="m 340.16406,31.00043 c -1.06641,-0.19922 -2.79297,-0.79688 -3.98828,-1.4961 v 2.98828 c 1.19531,-0.69531 2.92187,-1.29296 3.98828,-1.49218"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+ <use
+ style="fill:#000000;fill-opacity:1"
+ xlink:href="#glyph9-22"
+ x="454.112"
+ y="693.37201"
+ id="use4376"
+ width="100%"
+ height="100%"
+ transform="translate(-116.67969,-653.27301)" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4380"
+ d="M 170.08203,50.84027 V 6.2387097"
+ style="fill:none;stroke:#000000;stroke-width:0.3985;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4382"
+ d="m 170.08203,2.6527697 c -0.19922,1.0625 -0.79688,2.78906 -1.4961,3.98438 h 2.98829 c -0.69532,-1.19532 -1.29297,-2.92188 -1.49219,-3.98438"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+ <use
+ style="fill:#000000;fill-opacity:1"
+ xlink:href="#glyph9-16"
+ x="290.59601"
+ y="658.52301"
+ id="use4384"
+ width="100%"
+ height="100%"
+ transform="translate(-116.67969,-653.27301)" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4388"
+ d="m 167.24609,13.99261 h 45.35547"
+ style="fill:none;stroke:#000000;stroke-width:0.3985;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1" />
+ <use
+ style="fill:#000000;fill-opacity:1"
+ xlink:href="#glyph9-23"
+ x="272.21399"
+ y="671.08301"
+ id="use4390"
+ width="100%"
+ height="100%"
+ transform="translate(-116.67969,-653.27301)" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4394"
+ d="M 28.34765,107.53558 H 311.8164 V 79.18793 H 28.34765 Z m 0,0"
+ style="fill:none;stroke:#000000;stroke-width:0.3985;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4396"
+ d="M 31.93359,70.68402 H 308.23047"
+ style="fill:none;stroke:#000000;stroke-width:0.3985;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4398"
+ d="m 28.34765,70.68402 c 1.0625,0.19922 2.78907,0.79688 3.98438,1.49609 v -2.98828 c -1.19531,0.69532 -2.92188,1.29297 -3.98438,1.49219"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4400"
+ d="m 311.8164,70.68402 c -1.0625,-0.19922 -2.78906,-0.79687 -3.98437,-1.49219 v 2.98828 c 1.19531,-0.69921 2.92187,-1.29687 3.98437,-1.49609"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+ <use
+ style="fill:#000000;fill-opacity:1"
+ xlink:href="#glyph9-24"
+ x="283.728"
+ y="720.052"
+ id="use4402"
+ width="100%"
+ height="100%"
+ transform="translate(-116.67969,-653.27301)" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4406"
+ d="M 311.8164,65.01605 V 76.3559"
+ style="fill:none;stroke:#000000;stroke-width:0.3985;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4408"
+ d="M 28.34765,65.01605 V 76.3559"
+ style="fill:none;stroke:#000000;stroke-width:0.3985;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4410"
+ d="M 320.32031,103.94965 V 82.77386"
+ style="fill:none;stroke:#000000;stroke-width:0.3985;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4412"
+ d="m 320.32031,107.53558 c 0.19922,-1.0625 0.79687,-2.78906 1.49219,-3.98437 h -2.98828 c 0.69921,1.19531 1.29687,2.92187 1.49609,3.98437"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4414"
+ d="m 320.32031,79.18793 c -0.19922,1.0625 -0.79688,2.78906 -1.49609,3.98437 h 2.98828 c -0.69532,-1.19531 -1.29297,-2.92187 -1.49219,-3.98437"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+ <use
+ style="fill:#000000;fill-opacity:1"
+ xlink:href="#glyph9-3"
+ x="440.832"
+ y="750.59802"
+ id="use4416"
+ width="100%"
+ height="100%"
+ transform="translate(-116.67969,-653.27301)" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4420"
+ d="m 314.64843,79.18793 h 11.33985"
+ style="fill:none;stroke:#000000;stroke-width:0.3985;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4422"
+ d="m 314.64843,107.53558 h 11.33985"
+ style="fill:none;stroke:#000000;stroke-width:0.3985;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1" />
+</svg>
diff --git a/doc/dev-doc/manual/figures/hot-point-1.png b/doc/dev-doc/manual/figures/hot-point-1.png
new file mode 100644
index 000000000..6349ba329
Binary files /dev/null and b/doc/dev-doc/manual/figures/hot-point-1.png differ
diff --git a/doc/dev-doc/manual/figures/hot-point-2.png b/doc/dev-doc/manual/figures/hot-point-2.png
new file mode 100644
index 000000000..2325ee893
Binary files /dev/null and b/doc/dev-doc/manual/figures/hot-point-2.png differ
diff --git a/doc/dev-doc/manual/figures/implicit_dynamic.svg b/doc/dev-doc/manual/figures/implicit_dynamic.svg
new file mode 100644
index 000000000..2404b812e
--- /dev/null
+++ b/doc/dev-doc/manual/figures/implicit_dynamic.svg
@@ -0,0 +1,381 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="635.24872"
+ height="100"
+ id="svg4245"
+ version="1.1"
+ inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
+ sodipodi:docname="implicit_dynamic.svg">
+ <defs
+ id="defs4247">
+ <marker
+ inkscape:stockid="DotS"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="DotS"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path1614"
+ d="M -2.5,-1.0 C -2.5,1.7600000 -4.7400000,4.0 -7.5,4.0 C -10.260000,4.0 -12.5,1.7600000 -12.5,-1.0 C -12.5,-3.7600000 -10.260000,-6.0 -7.5,-6.0 C -4.7400000,-6.0 -2.5,-3.7600000 -2.5,-1.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
+ transform="scale(0.2) translate(7.4, 1)" />
+ </marker>
+ <marker
+ inkscape:stockid="DotM"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="DotM"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path1611"
+ d="M -2.5,-1.0 C -2.5,1.7600000 -4.7400000,4.0 -7.5,4.0 C -10.260000,4.0 -12.5,1.7600000 -12.5,-1.0 C -12.5,-3.7600000 -10.260000,-6.0 -7.5,-6.0 C -4.7400000,-6.0 -2.5,-3.7600000 -2.5,-1.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
+ transform="scale(0.4) translate(7.4, 1)" />
+ </marker>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2558">
+ <stop
+ style="stop-color:#afafaf;stop-opacity:1"
+ offset="0"
+ id="stop2554" />
+ <stop
+ id="stop2562"
+ offset="0.48881748"
+ style="stop-color:#cdcdcd;stop-opacity:0.98431373" />
+ <stop
+ style="stop-color:#e9e9e9;stop-opacity:0.97254902"
+ offset="1"
+ id="stop2556" />
+ </linearGradient>
+ <pattern
+ inkscape:collect="always"
+ xlink:href="#Strips1_1"
+ id="pattern2347"
+ patternTransform="matrix(0.90828475,0.92298257,-1.6485622,1.62231,94.330095,111.14521)" />
+ <pattern
+ inkscape:stockid="Stripes 1:1"
+ id="Strips1_1"
+ patternTransform="translate(0,0) scale(10,10)"
+ height="1"
+ width="2"
+ patternUnits="userSpaceOnUse"
+ inkscape:collect="always">
+ <rect
+ id="rect1629"
+ height="2"
+ width="1"
+ y="-0.5"
+ x="0"
+ style="fill:black;stroke:none" />
+ </pattern>
+ <marker
+ inkscape:stockid="TriangleOutL"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="TriangleOutL"
+ style="overflow:visible">
+ <path
+ id="path5011"
+ d="M 5.77,0 -2.88,5 V -5 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;marker-start:none"
+ transform="scale(0.8)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="TriangleOutM"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="TriangleOutM"
+ style="overflow:visible">
+ <path
+ id="path5014"
+ d="M 5.77,0 -2.88,5 V -5 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;marker-start:none"
+ transform="scale(0.4)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend"
+ style="overflow:visible">
+ <path
+ id="path4889"
+ style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend"
+ style="overflow:visible">
+ <path
+ id="path4877"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;marker-start:none"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Mend"
+ style="overflow:visible">
+ <path
+ id="path4895"
+ style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="scale(-0.6)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lend"
+ style="overflow:visible">
+ <path
+ id="path4871"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;marker-start:none"
+ transform="matrix(-0.8,0,0,-0.8,-10,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <pattern
+ inkscape:collect="always"
+ xlink:href="#Strips1_1-7"
+ id="pattern2347-2"
+ patternTransform="matrix(0.90828475,0.92298257,-1.6485622,1.62231,183.20176,207.07339)" />
+ <pattern
+ inkscape:stockid="Stripes 1:1"
+ id="Strips1_1-7"
+ patternTransform="translate(0,0) scale(10,10)"
+ height="1"
+ width="2"
+ patternUnits="userSpaceOnUse"
+ inkscape:collect="always">
+ <rect
+ id="rect1629-0"
+ height="2"
+ width="1"
+ y="-0.5"
+ x="0"
+ style="fill:black;stroke:none" />
+ </pattern>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2558"
+ id="linearGradient2560"
+ x1="320"
+ y1="151.86218"
+ x2="320"
+ y2="52.862183"
+ gradientUnits="userSpaceOnUse" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.2885298"
+ inkscape:cx="309.57814"
+ inkscape:cy="76.161052"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1920"
+ inkscape:window-height="1152"
+ inkscape:window-x="0"
+ inkscape:window-y="24"
+ inkscape:window-maximized="1"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:snap-smooth-nodes="true"
+ inkscape:snap-object-midpoints="true"
+ inkscape:object-paths="true" />
+ <metadata
+ id="metadata4250">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-2.3756318,-52.362183)">
+ <rect
+ style="fill:url(#linearGradient2560);fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="rect4253"
+ width="599"
+ height="99"
+ x="20.5"
+ y="52.862183" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:0.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.2, 2.4;stroke-dashoffset:0;stroke-opacity:1"
+ d="m 20.5,102.36218 h 599"
+ id="path6455"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <g
+ id="g2511">
+ <rect
+ y="131.93352"
+ x="3.4269824"
+ height="7.9050622"
+ width="34.146034"
+ id="rect911-5"
+ style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.11845864;stroke-miterlimit:4;stroke-dasharray:0.11845865, 0.11845865;stroke-dashoffset:0;stroke-opacity:1" />
+ <rect
+ y="131.93352"
+ x="3.4269879"
+ height="7.9050622"
+ width="34.146034"
+ id="rect911"
+ style="fill:url(#pattern2347);fill-opacity:1;stroke:none;stroke-width:0.11845864;stroke-miterlimit:4;stroke-dasharray:0.11845865, 0.11845865;stroke-dashoffset:0;stroke-opacity:1" />
+ <path
+ inkscape:transform-center-x="3.1575168e-06"
+ inkscape:transform-center-y="-4.9285578"
+ d="m 37.573019,131.93351 -34.1460377,0 L 20.5,102.36218 Z"
+ inkscape:randomized="0"
+ inkscape:rounded="0"
+ inkscape:flatsided="true"
+ sodipodi:arg2="1.5707963"
+ sodipodi:arg1="0.52359878"
+ sodipodi:r2="9.8571119"
+ sodipodi:r1="19.714224"
+ sodipodi:cy="122.0764"
+ sodipodi:cx="20.5"
+ sodipodi:sides="3"
+ id="path2355"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.21399379;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="star" />
+ <circle
+ r="2.1285522"
+ cy="102.36218"
+ cx="20.5"
+ id="path2421"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.68048555;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </g>
+ <g
+ id="g2533"
+ transform="translate(458.40634,-86.563398)">
+ <g
+ transform="translate(51.721594,-1.3829021)"
+ id="g2518">
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.11845864;stroke-miterlimit:4;stroke-dasharray:0.11845865, 0.11845865;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-5-9"
+ width="34.146034"
+ height="7.9050622"
+ x="92.298653"
+ y="227.86171" />
+ <rect
+ style="fill:url(#pattern2347-2);fill-opacity:1;stroke:none;stroke-width:0.11845864;stroke-miterlimit:4;stroke-dasharray:0.11845865, 0.11845865;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-3"
+ width="34.146034"
+ height="7.9050622"
+ x="92.29866"
+ y="227.86171" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 91.248047,228.61171 H 127.49609"
+ id="path2466"
+ inkscape:connector-curvature="0" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.86301315;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468"
+ cx="97.500069"
+ cy="224.53729"
+ r="4.0744085" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.86301315;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-6"
+ cx="121.24213"
+ cy="224.53729"
+ r="4.0744085" />
+ </g>
+ <g
+ transform="translate(0.40481977,-0.58306837)"
+ id="g2522">
+ <path
+ sodipodi:type="star"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.21399379;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2355-2"
+ sodipodi:sides="3"
+ sodipodi:cx="160.68884"
+ sodipodi:cy="209.22287"
+ sodipodi:r1="19.714224"
+ sodipodi:r2="9.8571119"
+ sodipodi:arg1="0.52359878"
+ sodipodi:arg2="1.5707963"
+ inkscape:flatsided="true"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ d="m 177.76186,219.07998 -34.14604,0 17.07302,-29.57133 z"
+ inkscape:transform-center-y="-4.9285578"
+ inkscape:transform-center-x="3.1575168e-06" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.68048555;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2421-6"
+ cx="160.68883"
+ cy="189.50867"
+ r="2.1285522" />
+ </g>
+ </g>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;marker-end:url(#TriangleOutM);marker-start:url(#DotS)"
+ d="M 320,102.36218 V 71.650041"
+ id="path1545"
+ inkscape:connector-curvature="0" />
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot1927"
+ style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:cmmi10;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;-inkscape-font-specification:cmmi10;font-stretch:normal;font-variant:normal;"
+ transform="translate(2.3756318,52.362183)"><flowRegion
+ id="flowRegion1929"
+ style="-inkscape-font-specification:cmmi10;font-family:cmmi10;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;"><rect
+ id="rect1931"
+ width="35.6996"
+ height="49.499996"
+ x="334.48975"
+ y="0.49999961"
+ style="-inkscape-font-specification:cmmi10;font-family:cmmi10;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;" /></flowRegion><flowPara
+ id="flowPara1933">F</flowPara></flowRoot> </g>
+</svg>
diff --git a/doc/dev-doc/manual/figures/insertion.svg b/doc/dev-doc/manual/figures/insertion.svg
new file mode 100644
index 000000000..455503347
--- /dev/null
+++ b/doc/dev-doc/manual/figures/insertion.svg
@@ -0,0 +1,258 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ id="svg2"
+ xml:space="preserve"
+ width="1003.437"
+ height="419.80017"
+ viewBox="0 0 1003.437 419.80017"
+ sodipodi:docname="insertion.pdf"><metadata
+ id="metadata8"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+ id="defs6" /><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="640"
+ inkscape:window-height="480"
+ id="namedview4" /><g
+ id="g10"
+ inkscape:groupmode="layer"
+ inkscape:label="insertion"
+ transform="matrix(1.3333333,0,0,-1.3333333,0,419.80017)"><g
+ id="g12"><g
+ id="g14"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="m 127.961,182.59 c -0.156,-0.402 8.328,-12.039 18.848,-25.86 10.523,-13.82 19.574,-25.734 20.113,-26.476 l 0.988,-1.348 3.324,2.914 c 9.075,7.938 20.746,17.254 24.649,28.965 1.504,4.504 3.488,11.844 3.09,12.242 0,0 -67.993,10.289 -69.633,10.289 -0.606,0 -1.227,-0.328 -1.379,-0.726 z"
+ style="fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#ffccaa;stroke-width:0.571429;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path16" /></g><g
+ id="g18"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="M 139.832,283.281 C 74.145,279.25 19.164,275.938 17.648,275.922 c -2.41,-0.027 -2.73,-0.172 -2.57,-1.156 0.113,-0.692 20.375,-17.332 53.238,-43.711 49.481,-39.723 53.204,-42.594 55.239,-42.594 1.199,0 2.543,-0.137 2.98,-0.305 0.922,-0.355 133.766,100.504 134.051,101.774 0.102,0.449 -0.152,0.785 -0.57,0.75 -0.414,-0.035 -54.5,-3.368 -120.184,-7.399 z"
+ style="fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#ffccaa;stroke-width:0.571429;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path20" /></g><g
+ id="g22"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="m 194.688,237.316 c -36.458,-27.793 -66.368,-50.925 -66.469,-51.41 -0.102,-0.48 0.027,-0.89 0.285,-0.91 0.258,-0.016 37.754,-5.105 83.324,-11.305 l 82.856,-11.273 2.523,3 -16,60.785 c -8.801,33.434 -16.051,60.836 -16.117,60.899 -0.063,0.062 -1.016,0.257 -2.117,0.429 -1.926,0.305 -4.418,-1.527 -68.285,-50.215 z"
+ style="fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#ffccaa;stroke-width:0.571429;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path24" /></g><g
+ id="g26"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="m 198.973,173.027 c 0,-0.547 -0.618,-3.425 -1.371,-6.398 -3.657,-14.426 -10.938,-26.762 -21.77,-36.895 l -4.953,-4.636 29.047,-38.133 C 227.617,50.609 229.062,48.828 230.82,48.789 l 1.848,-0.043 31.73,54.395 c 33.844,58.027 31.981,54.472 30.27,57.746 -0.039,0.082 -21.008,2.968 -46.598,6.425 -25.59,3.454 -47.101,6.376 -47.808,6.493 -0.922,0.152 -1.289,-0.071 -1.289,-0.778 z"
+ style="fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#ffccaa;stroke-width:0.571429;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path28" /></g><g
+ id="g30"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="m 166.688,122.375 c -4.778,-3.324 -15.086,-8.063 -21.426,-9.848 -7.922,-2.234 -14.809,-3.082 -23.004,-2.839 -5.668,0.171 -6.715,0.066 -6.735,-0.657 -0.011,-0.469 -2.293,-18.597 -5.066,-40.285 -2.848,-22.238 -4.816,-39.516 -4.519,-39.629 0.292,-0.109 1.019,-0.726 1.617,-1.367 l 1.086,-1.164 59.168,9.062 c 49.726,7.618 59.164,9.207 59.164,9.965 0,0.496 0.238,1.149 0.523,1.449 0.422,0.438 -20.668,28.579 -57.004,76.063 l -0.945,1.238 z"
+ style="fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#ffccaa;stroke-width:0.571429;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path32" /></g><g
+ id="g34"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="M 71.48,115.219 C 43.703,79.332 20.793,49.676 20.57,49.312 c -0.222,-0.367 -0.203,-1.41 0.039,-2.324 l 0.446,-1.66 39.386,-9.203 39.391,-9.199 1.551,1.285 c 0.851,0.707 1.812,1.371 2.129,1.48 0.32,0.11 2.773,17.555 5.449,38.77 2.672,21.215 4.984,39.269 5.133,40.121 0.246,1.41 0.082,1.582 -1.852,1.914 -4.137,0.711 -10.742,2.516 -14.195,3.875 l -3.5,1.379 -1.609,-3.371 c -0.887,-1.852 -1.883,-3.277 -2.215,-3.168 -0.766,0.25 -10.645,15.973 -10.27,16.348 0.266,0.265 19.192,1.086 19.559,0.847 0.101,-0.066 -0.477,-1.422 -1.285,-3.011 -0.805,-1.594 -1.465,-3.149 -1.465,-3.461 0,-0.981 16.726,-5.793 17.492,-5.028 0.187,0.188 8.219,63.36 8.219,64.653 0,0.496 -0.223,0.902 -0.493,0.902 -0.273,0 -23.222,-29.359 -51,-65.242 z"
+ style="fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#ffccaa;stroke-width:0.571429;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path36" /></g><g
+ id="g38"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="M 124.141,184.477 16.715,45.617"
+ style="fill:none;stroke:#c83737;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path40" /></g><path
+ d="m 20.715,269.233 c 0,-2.211 -1.793,-4 -4,-4 -2.211,0 -4,1.789 -4,4 0,2.207 1.789,4 4,4 2.207,0 4,-1.793 4,-4 z"
+ style="fill:#501616;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path42" /><g
+ id="g44"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="M 124.395,184.328 104.215,25.219"
+ style="fill:none;stroke:#c83737;stroke-width:1.6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path46" /></g><path
+ d="m 108.215,289.631 c 0,-2.207 -1.789,-4 -4,-4 -2.207,0 -4,1.793 -4,4 0,2.211 1.793,4 4,4 2.211,0 4,-1.789 4,-4 z"
+ style="fill:#501616;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path48" /><g
+ id="g50"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="M 124.824,184.387 231.145,44.68"
+ style="fill:none;stroke:#c83737;stroke-width:1.6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path52" /></g><path
+ d="m 235.145,270.17 c 0,-2.207 -1.79,-4 -4,-4 -2.207,0 -4,1.793 -4,4 0,2.211 1.793,4 4,4 2.21,0 4,-1.789 4,-4 z"
+ style="fill:#501616;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path54" /><g
+ id="g56"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="M 125.09,184.73 299.055,161.121"
+ style="fill:none;stroke:#ff6600;stroke-width:2.4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path58" /></g><path
+ d="m 303.055,153.729 c 0,-2.211 -1.789,-4 -4,-4 -2.207,0 -4,1.789 -4,4 0,2.207 1.793,4 4,4 2.211,0 4,-1.793 4,-4 z"
+ style="fill:#501616;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path60" /><g
+ id="g62"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="M 124.141,185.273 11,276.133"
+ style="fill:none;stroke:#c83737;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path64" /></g><path
+ d="m 15,38.717 c 0,-2.207 -1.793,-4 -4,-4 -2.211,0 -4,1.793 -4,4 0,2.211 1.789,4 4,4 2.207,0 4,-1.789 4,-4 z"
+ style="fill:#501616;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path66" /><g
+ id="g68"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="M 125.031,185.156 264.738,291.48"
+ style="fill:none;stroke:#c83737;stroke-width:1.6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path70" /></g><path
+ d="m 268.738,23.37 c 0,-2.207 -1.789,-4 -4,-4 -2.211,0 -4,1.793 -4,4 0,2.211 1.789,4 4,4 2.211,0 4,-1.789 4,-4 z"
+ style="fill:#501616;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path72" /><g
+ id="g74"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="m 90.355,120.258 c 35.688,-18.883 79.926,-5.258 98.805,30.43 3.695,6.988 6.235,14.523 7.516,22.32"
+ style="fill:none;stroke:#ff0000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path76" /></g><g
+ id="g78"
+ transform="matrix(-1,-0.529035,-0.529035,1,0,314.85013)"><path
+ d="m -12.729,-131.308 -12.233,7.071 0.001,-14.14 z"
+ style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:#ff0000;stroke-width:1.76785;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path80" /></g><g
+ id="g82"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="m 124.66,180.316 c -0.012,-0.078 -1.902,-14.796 -4.199,-32.714 -2.977,-23.215 -3.996,-32.7 -3.547,-33.028 1.059,-0.769 17.438,-0.004 22.059,1.031 9.043,2.024 21.148,7.25 25.816,11.145 l 1.813,1.512 -19.868,26.101 c -13.98,18.367 -20.187,26.098 -20.953,26.098 -0.601,0 -1.105,-0.066 -1.121,-0.145 z"
+ style="fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#ffccaa;stroke-width:0.571429;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path84" /></g><path
+ d="m 128.141,130.374 c 0,-2.207 -1.789,-4 -4,-4 -2.207,0 -4,1.793 -4,4 0,2.211 1.793,4 4,4 2.211,0 4,-1.789 4,-4 z"
+ style="fill:#501616;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path86" /><text
+ transform="matrix(1,0,0,-1,-1.904,120.27637)"
+ style="font-variant:normal;font-weight:normal;font-size:22.4px;font-family:LMSans8;-inkscape-font-specification:LMSans8;writing-mode:lr-tb;fill:#c83737;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="text90"><tspan
+ x="0 12.2976 24.191999 36.489601 48.787201 61.084801 72.508797 80.639999"
+ y="0"
+ sodipodi:role="line"
+ id="tspan88">boundary</tspan></text><g
+ id="g92"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="m 577.035,162.859 c -0.058,-0.425 10.871,-9.8 24.285,-20.836 13.414,-11.035 24.957,-20.554 25.657,-21.152 l 3.289,-16.035 15.5,10.672 c 0,0 1.273,29.433 2.386,41.73 0.426,4.727 0.676,12.328 0.196,12.621 0,0 -68.539,-5.597 -70.137,-5.972 -0.59,-0.141 -1.117,-0.602 -1.176,-1.028 z"
+ style="fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#ffccaa;stroke-width:0.571429;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path94" /></g><g
+ id="g96"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="m 589.352,302.652 c -65.684,-4.031 -120.664,-7.343 -122.18,-7.359 -2.41,-0.027 -2.731,-0.172 -2.574,-1.156 0.113,-0.692 20.379,-17.332 53.238,-43.711 49.48,-39.723 53.203,-42.594 55.242,-42.594 1.199,0 2.539,-0.137 2.981,-0.305 0.918,-0.355 133.761,100.504 134.05,101.774 0.102,0.449 -0.156,0.785 -0.57,0.75 -0.418,-0.035 -54.5,-3.363 -120.187,-7.399 z"
+ style="fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#ffccaa;stroke-width:0.571429;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path98" /></g><g
+ id="g100"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="m 644.211,256.688 c -36.457,-27.793 -66.371,-50.926 -66.473,-51.411 -0.101,-0.48 0.028,-0.89 0.289,-0.906 0.258,-0.019 37.754,-5.109 83.325,-11.309 l 82.851,-11.273 2.524,3 -15.997,60.785 c -8.8,33.434 -16.054,60.836 -16.117,60.899 -0.066,0.062 -1.015,0.257 -2.117,0.429 -1.926,0.309 -4.418,-1.527 -68.285,-50.214 z"
+ style="fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#ffccaa;stroke-width:0.571429;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path102" /></g><g
+ id="g104"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="m 648.348,169.859 c 0.125,-0.527 0.187,-3.472 0.132,-6.539 -0.246,-14.879 -4.5,-28.558 -12.714,-40.91 l -3.758,-5.648 37.031,-30.446 C 704.34,57.297 706.152,55.891 707.875,56.258 l 1.809,0.383 18.386,60.226 c 19.614,64.246 18.618,60.36 16.203,63.156 -0.058,0.067 -21.132,-1.933 -46.828,-4.449 -25.699,-2.515 -47.308,-4.613 -48.023,-4.66 -0.934,-0.062 -1.238,-0.363 -1.074,-1.055 z"
+ style="fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#ffccaa;stroke-width:0.571429;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path106" /></g><g
+ id="g108"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="m 622.137,122.961 c -4.489,3.703 -12.473,-4.961 -18.235,-8.156 -7.199,-3.993 -15.332,-11.075 -23.359,-12.715 -5.559,-1.141 1.133,-12.969 1.281,-13.68 0.094,-0.461 2.039,-18.629 4.321,-40.371 2.335,-22.297 4.386,-39.566 4.703,-39.609 0.308,-0.039 1.156,-0.473 1.886,-0.957 l 1.325,-0.887 55.503,22.41 c 46.653,18.836 55.473,22.551 55.297,23.289 -0.113,0.485 -0.035,1.172 0.176,1.527 0.309,0.524 -26.68,23.071 -72.949,60.942 l -1.203,0.988 z"
+ style="fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#ffccaa;stroke-width:0.571429;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path110" /></g><g
+ id="g112"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="M 537.535,84.316 C 518.742,43.012 503.258,8.887 503.125,8.48 c -0.133,-0.406 0.125,-1.418 0.57,-2.25 l 0.817,-1.515 40.445,0.09 40.449,0.093 1.215,1.606 c 0.668,0.883 1.449,1.75 1.734,1.93 0.286,0.179 -1.335,17.722 -3.601,38.984 12.305,19.824 4.965,81 -14.703,68.441 -19.11,-21.425 -13.063,-33.386 9.676,-21.906 0.14,0.227 -6.551,63.551 -6.848,64.809 -0.113,0.484 -0.426,0.828 -0.688,0.765 -0.265,-0.062 -15.863,-33.906 -34.656,-75.211 z"
+ style="fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#ffccaa;stroke-width:0.571429;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path114" /></g><g
+ id="g116"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="M 572.887,163.816 500.219,4"
+ style="fill:none;stroke:#c83737;stroke-width:3.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path118" /></g><g
+ id="g120"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="M 573.168,163.734 590.066,4.242"
+ style="fill:none;stroke:#c83737;stroke-width:1.6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path122" /></g><g
+ id="g124"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="M 573.57,163.887 709.133,52.332"
+ style="fill:none;stroke:#c83737;stroke-width:1.6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path126" /></g><g
+ id="g128"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="m 574.609,204.102 173.969,-23.61"
+ style="fill:none;stroke:#c83737;stroke-width:3.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path130" /></g><g
+ id="g132"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="M 573.664,204.645 460.52,295.504"
+ style="fill:none;stroke:#c83737;stroke-width:3.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path134" /></g><g
+ id="g136"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="M 574.555,204.527 714.262,310.852"
+ style="fill:none;stroke:#c83737;stroke-width:1.6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path138" /></g><g
+ id="g140"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="m 574.348,159.891 c 0,-0.079 1.543,-14.84 3.422,-32.805 2.437,-23.277 3.625,-32.746 4.136,-32.961 1.207,-0.504 16.973,4 21.235,6.07 8.332,4.043 18.918,11.91 22.562,16.774 l 1.418,1.89 -25.328,20.84 c -17.828,14.668 -25.645,20.766 -26.391,20.59 -0.582,-0.141 -1.058,-0.316 -1.054,-0.398 z"
+ style="fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#ffccaa;stroke-width:0.571429;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path142" /></g><g
+ id="g144"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="m 573.543,163.785 174.793,16.406"
+ style="fill:none;stroke:#c83737;stroke-width:3.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path146" /></g><path
+ d="m 504.109,309.932 c -0.507,-2.152 -2.66,-3.484 -4.812,-2.976 -2.149,0.507 -3.481,2.664 -2.973,4.812 0.508,2.149 2.66,3.481 4.813,2.973 2.148,-0.504 3.48,-2.66 2.972,-4.809 z"
+ style="fill:#501616;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path148" /><path
+ d="m 593.961,309.69 c -0.508,-2.148 -2.664,-3.48 -4.813,-2.973 -2.148,0.504 -3.48,2.66 -2.972,4.809 0.504,2.152 2.66,3.484 4.808,2.976 2.153,-0.507 3.485,-2.664 2.977,-4.812 z"
+ style="fill:#501616;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path150" /><path
+ d="m 713.133,262.518 c 0,-2.207 -1.789,-4 -4,-4 -2.207,0 -4,1.793 -4,4 0,2.211 1.793,4 4,4 2.211,0 4,-1.789 4,-4 z"
+ style="fill:#501616;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path152" /><path
+ d="m 752.578,134.358 c 0,-2.211 -1.793,-4 -4,-4 -2.211,0 -4,1.789 -4,4 0,2.207 1.789,4 4,4 2.207,0 4,-1.793 4,-4 z"
+ style="fill:#501616;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path154" /><path
+ d="m 464.52,19.346 c 0,-2.207 -1.79,-4 -4,-4 -2.208,0 -4,1.793 -4,4 0,2.211 1.792,4 4,4 2.21,0 4,-1.789 4,-4 z"
+ style="fill:#501616;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path156" /><path
+ d="m 718.262,3.999 c 0,-2.207 -1.793,-4 -4,-4 -2.211,0 -4,1.793 -4,4 0,2.211 1.789,4 4,4 2.207,0 4,-1.789 4,-4 z"
+ style="fill:#501616;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path158" /><g
+ id="g160"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="m 577.664,203.848 c 0,2.207 -1.793,4 -4,4 -2.211,0 -4,-1.793 -4,-4 0,-2.211 1.789,-4 4,-4 2.207,0 4,1.789 4,4 z"
+ style="fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:#501616;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path162" /></g><g
+ id="g164"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="m 577.68,163.797 c 0,2.211 -1.789,4 -4,4 -2.207,0 -4,-1.789 -4,-4 0,-2.211 1.793,-4 4,-4 2.211,0 4,1.789 4,4 z"
+ style="fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:#501616;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path166" /></g><text
+ transform="matrix(1,0,0,-1,127.80108,213.18491)"
+ style="font-variant:normal;font-weight:normal;font-size:22.4px;font-family:LMSans8;-inkscape-font-specification:LMSans8;writing-mode:lr-tb;fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="text172"><tspan
+ x="0 5.6896 17.584 29.500799"
+ y="0"
+ sodipodi:role="line"
+ id="tspan168">loop</tspan><tspan
+ x="354.8912 367.18881 379.08322 391.38083 403.67841 409.36801 419.94083 432.23843 440.16803 452.46561 464.36002 476.65762 487.25281"
+ y="90.053917"
+ sodipodi:role="line"
+ id="tspan170">doubled nodes</tspan></text><g
+ id="g174"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="m 340.449,161.523 h 64.024"
+ style="fill:none;stroke:#ff0000;stroke-width:24.4002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path176" /></g><g
+ id="g178"
+ transform="matrix(1,0,0,-1,0,314.85013)"><path
+ d="m 432.629,161.523 -42.211,24.399 v -48.801 z"
+ style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:#ff0000;stroke-width:6.10006;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path180" /></g></g></g></svg>
diff --git a/doc/dev-doc/manual/figures/problem_domain.svg b/doc/dev-doc/manual/figures/problem_domain.svg
new file mode 100644
index 000000000..c87491d98
--- /dev/null
+++ b/doc/dev-doc/manual/figures/problem_domain.svg
@@ -0,0 +1,423 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ id="svg2"
+ xml:space="preserve"
+ width="116.56955"
+ height="115.81045"
+ viewBox="0 0 116.56955 115.81045"
+ sodipodi:docname="problem_domain.svg"
+ inkscape:version="0.92.4 (5da689c313, 2019-01-14)"><metadata
+ id="metadata8"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
+ id="defs6"><linearGradient
+ inkscape:collect="always"
+ id="linearGradient1811"><stop
+ style="stop-color:#e5e6e6;stop-opacity:1;"
+ offset="0"
+ id="stop1807" /><stop
+ style="stop-color:#848989;stop-opacity:1"
+ offset="1"
+ id="stop1809" /></linearGradient><marker
+ inkscape:stockid="Arrow1Lstart"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1Lstart"
+ style="overflow:visible"
+ inkscape:isstock="true"><path
+ id="path1100"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="fill-rule:evenodd;stroke:#727272;stroke-width:1pt;stroke-opacity:1;fill:#a8a8a8;fill-opacity:1"
+ transform="scale(0.8) translate(12.5,0)" /></marker><marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1Lend"
+ style="overflow:visible;"
+ inkscape:isstock="true"><path
+ id="path1103"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="fill-rule:evenodd;stroke:#727272;stroke-width:1pt;stroke-opacity:1;fill:#a8a8a8;fill-opacity:1"
+ transform="scale(0.8) rotate(180) translate(12.5,0)" /></marker><clipPath
+ clipPathUnits="userSpaceOnUse"
+ id="clipPath92"><path
+ d="m 65,41.78 h 24 v -29 H 65 Z"
+ id="path90"
+ inkscape:connector-curvature="0" /></clipPath><clipPath
+ clipPathUnits="userSpaceOnUse"
+ id="clipPath98"><path
+ d="m 75.184,40.905 13.578,-6.07 -9.649,-21.223 -13.582,6.07 z"
+ id="path96"
+ inkscape:connector-curvature="0" /></clipPath><clipPath
+ clipPathUnits="userSpaceOnUse"
+ id="clipPath106"><path
+ d="M 27,13 H 88 V 83 H 27 Z"
+ id="path104"
+ inkscape:connector-curvature="0" /></clipPath><radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient1811"
+ id="radialGradient1813"
+ cx="48.072891"
+ cy="66.983139"
+ fx="48.072891"
+ fy="66.983139"
+ r="30.261484"
+ gradientTransform="matrix(1.2859017,0.57609144,-0.6593038,1.4716411,21.512737,-61.59447)"
+ gradientUnits="userSpaceOnUse" /></defs><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1920"
+ inkscape:window-height="1152"
+ id="namedview4"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:zoom="5.4547007"
+ inkscape:cx="56.191308"
+ inkscape:cy="62.127178"
+ inkscape:window-x="0"
+ inkscape:window-y="24"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="g10" /><g
+ id="g10"
+ inkscape:groupmode="layer"
+ inkscape:label="problemDomain"
+ transform="matrix(1.3333333,0,0,-1.3333333,-3.3552026,121.37542)"><g
+ transform="matrix(1,0,0,-1,0,91.779846)"
+ id="g14"><path
+ inkscape:connector-curvature="0"
+ id="path16"
+ style="fill:none;stroke:#000000;stroke-width:0.23999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 21.758,27.02 58.375,4.121" /></g><g
+ transform="matrix(1,0,0,-1,0,91.779846)"
+ id="g18"><path
+ inkscape:connector-curvature="0"
+ id="path20"
+ style="fill:none;stroke:#000000;stroke-width:0.23999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 23.344,28.949 61.32,5.141" /></g><g
+ transform="matrix(1,0,0,-1,0,91.779846)"
+ id="g22"><path
+ inkscape:connector-curvature="0"
+ id="path24"
+ style="fill:none;stroke:#000000;stroke-width:0.23999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 19.035,17.273 43.297,2.082" /></g><g
+ transform="matrix(1,0,0,-1,0,91.779846)"
+ id="g26"><path
+ inkscape:connector-curvature="0"
+ id="path28"
+ style="fill:none;stroke:#000000;stroke-width:0.23999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 18.605,20.332 47.719,2.309" /></g><g
+ transform="matrix(1,0,0,-1,0,91.779846)"
+ id="g30"><path
+ inkscape:connector-curvature="0"
+ id="path32"
+ style="fill:none;stroke:#000000;stroke-width:0.23999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 19.148,22.941 51.684,2.648" /></g><g
+ transform="matrix(1,0,0,-1,0,91.779846)"
+ id="g34"><path
+ inkscape:connector-curvature="0"
+ id="path36"
+ style="fill:none;stroke:#000000;stroke-width:0.23999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 20.281,24.98 54.82,3.285" /></g><g
+ transform="matrix(1,0,0,-1,0,91.779846)"
+ id="g38"><path
+ inkscape:connector-curvature="0"
+ id="path40"
+ style="fill:none;stroke:#000000;stroke-width:0.23999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 25.219,30.426 64.043,6.273" /></g><g
+ transform="matrix(1,0,0,-1,0,91.779846)"
+ id="g42"><path
+ inkscape:connector-curvature="0"
+ id="path44"
+ style="fill:none;stroke:#000000;stroke-width:0.23999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 27.086,32.348 66.535,7.41" /></g><g
+ transform="matrix(1,0,0,-1,0,91.779846)"
+ id="g46"><path
+ inkscape:connector-curvature="0"
+ id="path48"
+ style="fill:none;stroke:#000000;stroke-width:0.23999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 28.785,34.164 68.578,8.883" /></g><g
+ transform="matrix(1,0,0,-1,0,91.779846)"
+ id="g50"><path
+ inkscape:connector-curvature="0"
+ id="path52"
+ style="fill:none;stroke:#000000;stroke-width:0.23999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 30.371,35.637 70.617,10.469" /></g><path
+ inkscape:connector-curvature="0"
+ id="path54"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.16;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 9.805,90.975 20,-20" /><g
+ transform="matrix(1,0,0,-1,0,91.779846)"
+ id="g56"><path
+ inkscape:connector-curvature="0"
+ id="path58"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.16;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 9.805,40.805 20,20" /></g><g
+ transform="matrix(1,0,0,-1,0,91.779846)"
+ id="g60"><path
+ inkscape:connector-curvature="0"
+ id="path62"
+ style="fill:none;stroke:#ffffff;stroke-width:0.16;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 9.805,60.805 20,20" /></g><path
+ inkscape:connector-curvature="0"
+ id="path64"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.16;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 29.793,90.924 20,-20" /><path
+ inkscape:connector-curvature="0"
+ id="path66"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.16;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 29.793,50.924 20,-20" /><path
+ inkscape:connector-curvature="0"
+ id="path68"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.16;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 49.793,70.917 20,-20" /><path
+ inkscape:connector-curvature="0"
+ id="path70"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.16;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 49.793,50.917 20,-20" /><path
+ inkscape:connector-curvature="0"
+ id="path72"
+ style="fill:none;stroke:#ffffff;stroke-width:0.16;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 49.793,30.917 20,-20" /><path
+ inkscape:connector-curvature="0"
+ id="path74"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.16;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 69.887,50.917 20,-20" /><path
+ inkscape:connector-curvature="0"
+ id="path76"
+ style="fill:none;stroke:#ffffff;stroke-width:0.16;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 69.887,30.917 20,-20" /><path
+ inkscape:connector-curvature="0"
+ id="path78"
+ style="fill:url(#radialGradient1813);fill-opacity:1;fill-rule:nonzero;stroke:#231f20;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
+ d="M 79.508,61.276 C 78.375,65.835 76.746,70.495 74.59,73.092 67.957,81.081 64.129,83.479 54.41,85.764 47.102,87.483 40.141,87.331 32.852,84.815 26.91,82.764 21.242,77.014 20.961,70.272 c -0.164,-3.937 3.523,-7.16 6.105,-9.582 3.043,-2.863 6.563,-5.93 8.508,-9.676 3.391,-6.519 2.145,-13.148 2.641,-20.258 0.246,-3.57 1.422,-6.867 4.492,-8.91 1.723,-1.144 3.148,-2 5.352,-2.828 5.207,-1.957 11.605,0.301 15.144,1.844 2.82,1.23 4.199,2.328 6.649,4.461 3.656,3.191 6.14,7.515 7.734,12.215 1.41,4.16 2.332,8.105 2.777,12.289 0.336,3.183 -0.101,7.273 -0.855,11.449" /><path
+ inkscape:connector-curvature="0"
+ id="path80"
+ style="fill:none;stroke:#000000;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 76.895,33.401 6.093,-2.746" /><g
+ transform="matrix(1,-0.450795,0.450795,1,0,91.779846)"
+ id="g82"><path
+ inkscape:connector-curvature="0"
+ id="path84"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.18233;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 87.236,-19.709 0.731,-0.729 -2.551,0.728 2.55,0.732 z" /></g><g
+ id="g86"><g
+ id="g88"
+ clip-path="url(#clipPath92)"><g
+ id="g94"
+ clip-path="url(#clipPath98)"><g
+ id="g100"
+ transform="translate(0,-0.220154)"><g
+ id="g102" /><g
+ id="g114"><g
+ clip-path="url(#clipPath106)"
+ id="g112"><g
+ id="g110"><path
+ d="m 85.871,56.766 c -1.129,4.558 -2.762,9.218 -4.914,11.816 -6.633,7.988 -10.461,10.387 -20.18,12.672 -7.312,1.719 -14.269,1.566 -21.558,-0.949 -5.942,-2.051 -11.61,-7.801 -11.891,-14.543 -0.168,-3.938 3.524,-7.16 6.106,-9.582 3.043,-2.86 6.558,-5.93 8.507,-9.676 3.391,-6.52 2.145,-13.149 2.641,-20.258 0.246,-3.57 1.422,-6.867 4.492,-8.91 1.723,-1.145 3.145,-2 5.352,-2.828 5.207,-1.957 11.605,0.301 15.144,1.844 2.821,1.23 4.2,2.328 6.649,4.46 3.652,3.192 6.136,7.516 7.73,12.215 1.41,4.161 2.336,8.106 2.778,12.289 0.339,3.184 -0.102,7.274 -0.856,11.45"
+ style="fill:none;stroke:#231f20;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
+ id="path108"
+ inkscape:connector-curvature="0" /></g></g></g></g></g></g></g><path
+ inkscape:connector-curvature="0"
+ id="path118"
+ style="fill:none;stroke:#000000;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 74.434,29.327 5.613,-3.953" /><g
+ transform="matrix(1,-0.704274,0.704274,1,0,91.779846)"
+ id="g120"><path
+ inkscape:connector-curvature="0"
+ id="path122"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.163517;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 80.462,-6.708 0.655,-0.652 -2.289,0.654 2.289,0.655 z" /></g><path
+ inkscape:connector-curvature="0"
+ id="path124"
+ style="fill:none;stroke:#000000;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 68.406,22.698 3.035,-5.813" /><g
+ transform="matrix(0.521714,-1,1,0.521714,0,91.779846)"
+ id="g126"><path
+ inkscape:connector-curvature="0"
+ id="path128"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.17731901;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 83.774,25.442 0.711,-0.71 -2.485,0.71 2.483,0.709 z" /></g><path
+ inkscape:connector-curvature="0"
+ id="path130"
+ style="fill:none;stroke:#000000;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 26.91,39.983 11.547,0.848" /><g
+ transform="matrix(1,0.0736683,-0.0736683,1,0,91.779846)"
+ id="g132"><path
+ inkscape:connector-curvature="0"
+ id="path134"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.19946;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 24.567,-53.49 0.797,-0.796 -2.793,0.795 2.793,0.798 z" /></g><path
+ inkscape:connector-curvature="0"
+ id="path136"
+ style="fill:none;stroke:#000000;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 78.691,38.237 85.32,36.292" /><g
+ transform="matrix(1,-0.2932,0.2932,1,0,91.779846)"
+ id="g138"><path
+ inkscape:connector-curvature="0"
+ id="path140"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.191921;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 88.454,-28.061 0.768,-0.767 -2.686,0.767 2.685,0.768 z" /></g><path
+ inkscape:connector-curvature="0"
+ id="path142"
+ style="fill:none;stroke:#000000;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 71.742,25.706 4.465,-5.102" /><g
+ transform="matrix(0.874864,-1,1,0.874864,0,91.779846)"
+ id="g144"><path
+ inkscape:connector-curvature="0"
+ id="path146"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.150526;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 74.185,7.895 74.787,7.294 72.68,7.895 74.786,8.498 Z" /></g><path
+ inkscape:connector-curvature="0"
+ id="path148"
+ style="fill:none;stroke:#000000;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 8.891,24.088 8.941,11.553" /><g
+ transform="matrix(0.00410474,-1,1,0.00410474,0,91.779846)"
+ id="g150"><path
+ inkscape:connector-curvature="0"
+ id="path152"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.19999801;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 69.324,8.61 0.802,-0.8 -2.802,0.8 2.801,0.801 z" /></g><path
+ inkscape:connector-curvature="0"
+ id="path154"
+ style="fill:none;stroke:#000000;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 21.234,11.772 8.699,11.768" /><g
+ transform="rotate(-179.98789,0.00485004,45.889922)"
+ id="g156"><path
+ inkscape:connector-curvature="0"
+ id="path158"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m -19.616,80.016 0.797,-0.801 -2.797,0.797 2.797,0.801 z" /></g><g
+ transform="matrix(1,0.340349,0.340349,-1,0,91.779846)"
+ id="g166"><path
+ inkscape:connector-curvature="0"
+ id="path168"
+ style="fill:none;stroke:#000000;stroke-width:0.37866899;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 19.026,38.968 c 0.001,1.594 -1.357,2.886 -3.036,2.884 -0.504,0.001 -0.998,-0.121 -1.443,-0.35" /></g><g
+ transform="matrix(-1,-0.350619,-0.350619,1,0,91.779846)"
+ id="g170"><path
+ inkscape:connector-curvature="0"
+ id="path172"
+ style="fill:none;stroke:#000000;stroke-width:0.37746999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m -15.772,-43.979 c 0.003,1.587 -1.354,2.873 -3.026,2.872 -0.5,10e-4 -0.997,-0.119 -1.442,-0.345" /></g><g
+ transform="matrix(1,-0.414704,-0.414704,-1,0,91.779846)"
+ id="g174"><path
+ inkscape:connector-curvature="0"
+ id="path176"
+ style="fill:none;stroke:#000000;stroke-width:0.369488;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 82.747,0.2 c 0,1.555 -1.325,2.815 -2.962,2.814 -0.492,0.001 -0.978,-0.117 -1.411,-0.34" /></g><g
+ transform="matrix(-1,0.403992,0.403992,1,0,91.779846)"
+ id="g178"><path
+ inkscape:connector-curvature="0"
+ id="path180"
+ style="fill:none;stroke:#000000;stroke-width:0.37087801;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m -80.265,-5.839 c 0,1.558 -1.329,2.822 -2.972,2.825 -0.494,-0.003 -0.982,-0.119 -1.416,-0.342" /></g><path
+ inkscape:connector-curvature="0"
+ id="path182"
+ style="fill:none;stroke:#000000;stroke-width:0.23999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 24.59,83.35 6.89,4.121" /><g
+ transform="matrix(1,0,0,-1,0,91.779846)"
+ id="g184"><path
+ inkscape:connector-curvature="0"
+ id="path186"
+ style="fill:none;stroke:#000000;stroke-width:0.23999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 38.195,2.422 -17.8,11.109" /></g><g
+ transform="matrix(1,0,0,-1,0,91.779846)"
+ id="g188"><path
+ inkscape:connector-curvature="0"
+ id="path190"
+ style="fill:none;stroke:#000000;stroke-width:0.23999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 70.262,13.695 2.203,-1.363" /></g><g
+ transform="matrix(1,0,0,-1,0,91.779846)"
+ id="g192"><path
+ inkscape:connector-curvature="0"
+ id="path194"
+ style="fill:none;stroke:#000000;stroke-width:0.23999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 72.145,15.379 2.003,-1.324" /></g><g
+ transform="matrix(1,0,0,-1,0,91.779846)"
+ id="g196"><path
+ inkscape:connector-curvature="0"
+ id="path198"
+ style="fill:none;stroke:#000000;stroke-width:0.23999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 73.629,17.262 c 0.199,-0.121 2.125,-1.324 2.125,-1.324" /></g><g
+ transform="matrix(1,0,0,-1,0,91.779846)"
+ id="g200"><path
+ inkscape:connector-curvature="0"
+ id="path202"
+ style="fill:none;stroke:#000000;stroke-width:0.23999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 75.191,19.145 77.277,17.82" /></g><g
+ transform="matrix(1,0,0,-1,0,91.779846)"
+ id="g204"><path
+ inkscape:connector-curvature="0"
+ id="path206"
+ style="fill:none;stroke:#000000;stroke-width:0.23999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 76.395,21.148 78.52,19.867" /></g><g
+ transform="matrix(1,0,0,-1,0,91.779846)"
+ id="g208"><path
+ inkscape:connector-curvature="0"
+ id="path210"
+ style="fill:none;stroke:#000000;stroke-width:0.23999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 77.398,23.352 79.48,22.07" /></g><g
+ transform="matrix(1,0,0,-1,0,91.779846)"
+ id="g212"><path
+ inkscape:connector-curvature="0"
+ id="path214"
+ style="fill:none;stroke:#000000;stroke-width:0.23999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 78.32,25.598 2.004,-1.243" /></g><path
+ inkscape:connector-curvature="0"
+ id="path216"
+ style="fill:none;stroke:#000000;stroke-width:0.23999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 79,63.776 2.125,1.203" /><g
+ transform="matrix(1,0,0,-1,0,91.779846)"
+ id="g218"><path
+ inkscape:connector-curvature="0"
+ id="path220"
+ style="fill:none;stroke:#000000;stroke-width:0.23999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 79.641,30.488 c 0.281,-0.199 2.203,-1.363 2.203,-1.363" /></g><g
+ transform="matrix(1,0,0,-1,0,91.779846)"
+ id="g222"><path
+ inkscape:connector-curvature="0"
+ id="path224"
+ style="fill:none;stroke:#000000;stroke-width:0.23999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 80.16,33.012 82.328,31.57" /></g><g
+ transform="matrix(1,0,0,-1,0,91.779846)"
+ id="g226"><path
+ inkscape:connector-curvature="0"
+ id="path228"
+ style="fill:none;stroke:#000000;stroke-width:0.23999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 80.402,35.656 2.203,-1.402" /></g><path
+ inkscape:connector-curvature="0"
+ id="path230"
+ style="fill:none;stroke:#000000;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 3.008,4.76 5.957,7.11" /><g
+ transform="matrix(0.837873,1,-1,0.837873,0,91.779846)"
+ id="g232"><path
+ inkscape:connector-curvature="0"
+ id="path234"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.153301;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m -48.42,-44.605 0.611,-0.613 -2.145,0.613 2.145,0.614 z" /></g><path
+ inkscape:connector-curvature="0"
+ id="path160"
+ style="fill:#a8a8a8;fill-opacity:1;stroke:#727272;stroke-width:0.30000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:0.30000002, 0.30000002;stroke-dashoffset:0;stroke-opacity:1;marker-start:url(#Arrow1Lstart)"
+ d="M 50.085297,35.063945 8.812,11.881"
+ sodipodi:nodetypes="cc" /></g></svg>
\ No newline at end of file
diff --git a/doc/dev-doc/manual/figures/static.svg b/doc/dev-doc/manual/figures/static.svg
new file mode 100644
index 000000000..39c08a593
--- /dev/null
+++ b/doc/dev-doc/manual/figures/static.svg
@@ -0,0 +1,658 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="145.30687"
+ height="145.30687"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
+ sodipodi:docname="static.svg">
+ <defs
+ id="defs4">
+ <pattern
+ inkscape:collect="always"
+ xlink:href="#Strips1_1-7"
+ id="pattern2347-2"
+ patternTransform="matrix(0.66149863,0.67220297,-1.2006385,1.1815192,104.26974,154.07196)" />
+ <pattern
+ inkscape:stockid="Stripes 1:1"
+ id="Strips1_1-7"
+ patternTransform="translate(0,0) scale(10,10)"
+ height="1"
+ width="2"
+ patternUnits="userSpaceOnUse"
+ inkscape:collect="always">
+ <rect
+ id="rect1629-0"
+ height="2"
+ width="1"
+ y="-0.5"
+ x="0"
+ style="fill:black;stroke:none" />
+ </pattern>
+ <pattern
+ inkscape:collect="always"
+ xlink:href="#Strips1_1-7-2"
+ id="pattern2347-2-2"
+ patternTransform="matrix(0.66149863,0.67220297,-1.2006385,1.1815192,104.26974,154.07196)" />
+ <pattern
+ inkscape:stockid="Stripes 1:1"
+ id="Strips1_1-7-2"
+ patternTransform="translate(0,0) scale(10,10)"
+ height="1"
+ width="2"
+ patternUnits="userSpaceOnUse"
+ inkscape:collect="always">
+ <rect
+ id="rect1629-0-8"
+ height="2"
+ width="1"
+ y="-0.5"
+ x="0"
+ style="fill:black;stroke:none" />
+ </pattern>
+ <pattern
+ inkscape:collect="always"
+ xlink:href="#Strips1_1-7-1"
+ id="pattern2347-2-3"
+ patternTransform="matrix(0.66149863,0.67220297,-1.2006385,1.1815192,104.26974,154.07196)" />
+ <pattern
+ inkscape:stockid="Stripes 1:1"
+ id="Strips1_1-7-1"
+ patternTransform="translate(0,0) scale(10,10)"
+ height="1"
+ width="2"
+ patternUnits="userSpaceOnUse"
+ inkscape:collect="always">
+ <rect
+ id="rect1629-0-9"
+ height="2"
+ width="1"
+ y="-0.5"
+ x="0"
+ style="fill:black;stroke:none" />
+ </pattern>
+ <pattern
+ inkscape:collect="always"
+ xlink:href="#Strips1_1-7-0"
+ id="pattern2347-2-1"
+ patternTransform="matrix(0.66149863,0.67220297,-1.2006385,1.1815192,104.26974,154.07196)" />
+ <pattern
+ inkscape:stockid="Stripes 1:1"
+ id="Strips1_1-7-0"
+ patternTransform="translate(0,0) scale(10,10)"
+ height="1"
+ width="2"
+ patternUnits="userSpaceOnUse"
+ inkscape:collect="always">
+ <rect
+ id="rect1629-0-6"
+ height="2"
+ width="1"
+ y="-0.5"
+ x="0"
+ style="fill:black;stroke:none" />
+ </pattern>
+ <pattern
+ inkscape:collect="always"
+ xlink:href="#Strips1_1-7-6"
+ id="pattern2347-2-7"
+ patternTransform="matrix(0.66149863,0.67220297,-1.2006385,1.1815192,104.26974,154.07196)" />
+ <pattern
+ inkscape:stockid="Stripes 1:1"
+ id="Strips1_1-7-6"
+ patternTransform="translate(0,0) scale(10,10)"
+ height="1"
+ width="2"
+ patternUnits="userSpaceOnUse"
+ inkscape:collect="always">
+ <rect
+ id="rect1629-0-5"
+ height="2"
+ width="1"
+ y="-0.5"
+ x="0"
+ style="fill:black;stroke:none" />
+ </pattern>
+ <pattern
+ inkscape:collect="always"
+ xlink:href="#Strips1_1-7-2-9"
+ id="pattern2347-2-2-6"
+ patternTransform="matrix(0.66149863,0.67220297,-1.2006385,1.1815192,104.26974,154.07196)" />
+ <pattern
+ inkscape:stockid="Stripes 1:1"
+ id="Strips1_1-7-2-9"
+ patternTransform="translate(0,0) scale(10,10)"
+ height="1"
+ width="2"
+ patternUnits="userSpaceOnUse"
+ inkscape:collect="always">
+ <rect
+ id="rect1629-0-8-3"
+ height="2"
+ width="1"
+ y="-0.5"
+ x="0"
+ style="fill:black;stroke:none" />
+ </pattern>
+ <pattern
+ inkscape:collect="always"
+ xlink:href="#Strips1_1-7-1-4"
+ id="pattern2347-2-3-7"
+ patternTransform="matrix(0.66149863,0.67220297,-1.2006385,1.1815192,104.26974,154.07196)" />
+ <pattern
+ inkscape:stockid="Stripes 1:1"
+ id="Strips1_1-7-1-4"
+ patternTransform="translate(0,0) scale(10,10)"
+ height="1"
+ width="2"
+ patternUnits="userSpaceOnUse"
+ inkscape:collect="always">
+ <rect
+ id="rect1629-0-9-5"
+ height="2"
+ width="1"
+ y="-0.5"
+ x="0"
+ style="fill:black;stroke:none" />
+ </pattern>
+ <pattern
+ inkscape:collect="always"
+ xlink:href="#Strips1_1-7-0-5"
+ id="pattern2347-2-1-2"
+ patternTransform="matrix(0.66149863,0.67220297,-1.2006385,1.1815192,104.26974,154.07196)" />
+ <pattern
+ inkscape:stockid="Stripes 1:1"
+ id="Strips1_1-7-0-5"
+ patternTransform="translate(0,0) scale(10,10)"
+ height="1"
+ width="2"
+ patternUnits="userSpaceOnUse"
+ inkscape:collect="always">
+ <rect
+ id="rect1629-0-6-4"
+ height="2"
+ width="1"
+ y="-0.5"
+ x="0"
+ style="fill:black;stroke:none" />
+ </pattern>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="3.959798"
+ inkscape:cx="67.350766"
+ inkscape:cy="59.715204"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1920"
+ inkscape:window-height="1152"
+ inkscape:window-x="0"
+ inkscape:window-y="24"
+ inkscape:window-maximized="1"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-17.392997,-29.66231)">
+ <rect
+ style="fill:#afafaf;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="rect3753"
+ width="99"
+ height="99"
+ x="50.5"
+ y="42.862186" />
+ <g
+ id="g1831">
+ <g
+ id="g1663">
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-5-9"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065521"
+ y="169.21198" />
+ <rect
+ style="fill:url(#pattern2347-2);fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-3"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065536"
+ y="169.21198" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.09244144;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 37.300383,169.7582 h 26.39924"
+ id="path2466"
+ inkscape:connector-curvature="0" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468"
+ cx="41.853683"
+ cy="166.79083"
+ r="2.9673686" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-6"
+ cx="59.144897"
+ cy="166.79083"
+ r="2.9673686" />
+ <path
+ sodipodi:type="star"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.88414478;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2355-2"
+ sodipodi:sides="3"
+ sodipodi:cx="50.5"
+ sodipodi:cy="156.21994"
+ sodipodi:r1="14.357757"
+ sodipodi:r2="7.1788788"
+ sodipodi:arg1="0.52359878"
+ sodipodi:arg2="1.5707963"
+ inkscape:flatsided="true"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ d="m 62.934182,163.39882 -24.868364,0 L 50.5,141.86218 Z"
+ inkscape:transform-center-y="-3.5894424"
+ inkscape:transform-center-x="8.1207044e-06" />
+ </g>
+ <g
+ id="g1663-9"
+ transform="translate(33.000004)">
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-5-9-7"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065521"
+ y="169.21198" />
+ <rect
+ style="fill:url(#pattern2347-2-2);fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-3-3"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065536"
+ y="169.21198" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.09244144;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 37.300383,169.7582 h 26.39924"
+ id="path2466-6"
+ inkscape:connector-curvature="0" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-1"
+ cx="41.853683"
+ cy="166.79083"
+ r="2.9673686" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-6-2"
+ cx="59.144897"
+ cy="166.79083"
+ r="2.9673686" />
+ <path
+ sodipodi:type="star"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.88414478;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2355-2-9"
+ sodipodi:sides="3"
+ sodipodi:cx="50.5"
+ sodipodi:cy="156.21994"
+ sodipodi:r1="14.357757"
+ sodipodi:r2="7.1788788"
+ sodipodi:arg1="0.52359878"
+ sodipodi:arg2="1.5707963"
+ inkscape:flatsided="true"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ d="m 62.934182,163.39882 -24.868364,0 L 50.5,141.86218 Z"
+ inkscape:transform-center-y="-3.5894424"
+ inkscape:transform-center-x="8.1207044e-06" />
+ </g>
+ <g
+ id="g1663-4"
+ transform="translate(65.999999)">
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-5-9-78"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065521"
+ y="169.21198" />
+ <rect
+ style="fill:url(#pattern2347-2-3);fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-3-4"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065536"
+ y="169.21198" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.09244144;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 37.300383,169.7582 h 26.39924"
+ id="path2466-5"
+ inkscape:connector-curvature="0" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-0"
+ cx="41.853683"
+ cy="166.79083"
+ r="2.9673686" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-6-3"
+ cx="59.144897"
+ cy="166.79083"
+ r="2.9673686" />
+ <path
+ sodipodi:type="star"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.88414478;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2355-2-6"
+ sodipodi:sides="3"
+ sodipodi:cx="50.5"
+ sodipodi:cy="156.21994"
+ sodipodi:r1="14.357757"
+ sodipodi:r2="7.1788788"
+ sodipodi:arg1="0.52359878"
+ sodipodi:arg2="1.5707963"
+ inkscape:flatsided="true"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ d="m 62.934182,163.39882 -24.868364,0 L 50.5,141.86218 Z"
+ inkscape:transform-center-y="-3.5894424"
+ inkscape:transform-center-x="8.1207044e-06" />
+ </g>
+ <g
+ id="g1663-3"
+ transform="translate(99)">
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-5-9-2"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065521"
+ y="169.21198" />
+ <rect
+ style="fill:url(#pattern2347-2-1);fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-3-0"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065536"
+ y="169.21198" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.09244144;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 37.300383,169.7582 h 26.39924"
+ id="path2466-61"
+ inkscape:connector-curvature="0" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-5"
+ cx="41.853683"
+ cy="166.79083"
+ r="2.9673686" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-6-5"
+ cx="59.144897"
+ cy="166.79083"
+ r="2.9673686" />
+ <path
+ sodipodi:type="star"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.88414478;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2355-2-4"
+ sodipodi:sides="3"
+ sodipodi:cx="50.5"
+ sodipodi:cy="156.21994"
+ sodipodi:r1="14.357757"
+ sodipodi:r2="7.1788788"
+ sodipodi:arg1="0.52359878"
+ sodipodi:arg2="1.5707963"
+ inkscape:flatsided="true"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ d="m 62.934182,163.39882 -24.868364,0 L 50.5,141.86218 Z"
+ inkscape:transform-center-y="-3.5894424"
+ inkscape:transform-center-x="8.1207044e-06" />
+ </g>
+ </g>
+ <g
+ transform="rotate(90,100,92.362184)"
+ id="g1831-7">
+ <g
+ id="g1663-44">
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-5-9-3"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065521"
+ y="169.21198" />
+ <rect
+ style="fill:url(#pattern2347-2-7);fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-3-07"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065536"
+ y="169.21198" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.09244144;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 37.300383,169.7582 h 26.39924"
+ id="path2466-8"
+ inkscape:connector-curvature="0" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-68"
+ cx="41.853683"
+ cy="166.79083"
+ r="2.9673686" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-6-8"
+ cx="59.144897"
+ cy="166.79083"
+ r="2.9673686" />
+ <path
+ sodipodi:type="star"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.88414478;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2355-2-43"
+ sodipodi:sides="3"
+ sodipodi:cx="50.5"
+ sodipodi:cy="156.21994"
+ sodipodi:r1="14.357757"
+ sodipodi:r2="7.1788788"
+ sodipodi:arg1="0.52359878"
+ sodipodi:arg2="1.5707963"
+ inkscape:flatsided="true"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ d="m 62.934182,163.39882 -24.868364,0 L 50.5,141.86218 Z"
+ inkscape:transform-center-y="-3.5894424"
+ inkscape:transform-center-x="8.1207044e-06" />
+ </g>
+ <g
+ id="g1663-9-1"
+ transform="translate(33.000004)">
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-5-9-7-4"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065521"
+ y="169.21198" />
+ <rect
+ style="fill:url(#pattern2347-2-2-6);fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-3-3-9"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065536"
+ y="169.21198" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.09244144;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 37.300383,169.7582 h 26.39924"
+ id="path2466-6-2"
+ inkscape:connector-curvature="0" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-1-0"
+ cx="41.853683"
+ cy="166.79083"
+ r="2.9673686" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-6-2-6"
+ cx="59.144897"
+ cy="166.79083"
+ r="2.9673686" />
+ <path
+ sodipodi:type="star"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.88414478;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2355-2-9-8"
+ sodipodi:sides="3"
+ sodipodi:cx="50.5"
+ sodipodi:cy="156.21994"
+ sodipodi:r1="14.357757"
+ sodipodi:r2="7.1788788"
+ sodipodi:arg1="0.52359878"
+ sodipodi:arg2="1.5707963"
+ inkscape:flatsided="true"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ d="m 62.934182,163.39882 -24.868364,0 L 50.5,141.86218 Z"
+ inkscape:transform-center-y="-3.5894424"
+ inkscape:transform-center-x="8.1207044e-06" />
+ </g>
+ <g
+ id="g1663-4-9"
+ transform="translate(65.999999)">
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-5-9-78-2"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065521"
+ y="169.21198" />
+ <rect
+ style="fill:url(#pattern2347-2-3-7);fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-3-4-6"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065536"
+ y="169.21198" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.09244144;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 37.300383,169.7582 h 26.39924"
+ id="path2466-5-6"
+ inkscape:connector-curvature="0" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-0-4"
+ cx="41.853683"
+ cy="166.79083"
+ r="2.9673686" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-6-3-9"
+ cx="59.144897"
+ cy="166.79083"
+ r="2.9673686" />
+ <path
+ sodipodi:type="star"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.88414478;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2355-2-6-5"
+ sodipodi:sides="3"
+ sodipodi:cx="50.5"
+ sodipodi:cy="156.21994"
+ sodipodi:r1="14.357757"
+ sodipodi:r2="7.1788788"
+ sodipodi:arg1="0.52359878"
+ sodipodi:arg2="1.5707963"
+ inkscape:flatsided="true"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ d="m 62.934182,163.39882 -24.868364,0 L 50.5,141.86218 Z"
+ inkscape:transform-center-y="-3.5894424"
+ inkscape:transform-center-x="8.1207044e-06" />
+ </g>
+ <g
+ id="g1663-3-0"
+ transform="translate(99)">
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-5-9-2-4"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065521"
+ y="169.21198" />
+ <rect
+ style="fill:url(#pattern2347-2-1-2);fill-opacity:1;stroke:none;stroke-width:0.08627276;stroke-miterlimit:4;stroke-dasharray:0.08627276, 0.08627276;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect911-3-0-8"
+ width="24.868362"
+ height="5.7572122"
+ x="38.065536"
+ y="169.21198" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.09244144;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 37.300383,169.7582 h 26.39924"
+ id="path2466-61-7"
+ inkscape:connector-curvature="0" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-5-1"
+ cx="41.853683"
+ cy="166.79083"
+ r="2.9673686" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.62852758;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2468-6-5-7"
+ cx="59.144897"
+ cy="166.79083"
+ r="2.9673686" />
+ <path
+ sodipodi:type="star"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.88414478;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2355-2-4-2"
+ sodipodi:sides="3"
+ sodipodi:cx="50.5"
+ sodipodi:cy="156.21994"
+ sodipodi:r1="14.357757"
+ sodipodi:r2="7.1788788"
+ sodipodi:arg1="0.52359878"
+ sodipodi:arg2="1.5707963"
+ inkscape:flatsided="true"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ d="m 62.934182,163.39882 -24.868364,0 L 50.5,141.86218 Z"
+ inkscape:transform-center-y="-3.5894424"
+ inkscape:transform-center-x="8.1207044e-06" />
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/doc/dev-doc/manual/figures/static_analysis.png b/doc/dev-doc/manual/figures/static_analysis.png
new file mode 100644
index 000000000..b4cc12be3
Binary files /dev/null and b/doc/dev-doc/manual/figures/static_analysis.png differ
diff --git a/doc/dev-doc/manual/figures/tangent.svg b/doc/dev-doc/manual/figures/tangent.svg
new file mode 100644
index 000000000..6e6710e1f
--- /dev/null
+++ b/doc/dev-doc/manual/figures/tangent.svg
@@ -0,0 +1,185 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="281.59772"
+ height="257.2486"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
+ sodipodi:docname="tangent.svg">
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path870"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mstart"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path867"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(0.4,0,0,0.4,4,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lstart"
+ style="overflow:visible">
+ <path
+ id="path3938"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;marker-start:none"
+ transform="matrix(0.8,0,0,0.8,10,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lend"
+ style="overflow:visible">
+ <path
+ id="path3941"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;marker-start:none"
+ transform="matrix(-0.8,0,0,-0.8,-10,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <path
+ id="g0-27"
+ d="m 5.65,-4.07 c 0.143,0 0.535,0 0.535,-0.37 0,-0.262 -0.229,-0.262 -0.425,-0.262 H 3.273 c -1.648,0 -2.858,1.8 -2.858,3.098 0,0.96 0.643,1.724 1.635,1.724 1.287,0 2.738,-1.32 2.738,-3 0,-0.185 0,-0.71 -0.338,-1.19 z m -3.587,3.95 c -0.535,0 -0.971,-0.393 -0.971,-1.178 0,-0.327 0.13,-1.222 0.513,-1.866 0.458,-0.752 1.112,-0.905 1.483,-0.905 0.917,0 1.004,0.72 1.004,1.058 0,0.513 -0.218,1.407 -0.59,1.964 -0.425,0.643 -1.014,0.927 -1.44,0.927 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="g0-34"
+ d="m 1.538,-2.476 c 0.36,0.152 0.677,0.152 0.938,0.152 0.284,0 0.884,0 0.884,-0.338 0,-0.262 -0.382,-0.294 -0.796,-0.294 -0.23,0 -0.611,0.021 -1.015,0.207 -0.25,-0.131 -0.436,-0.338 -0.436,-0.633 0,-0.665 1.069,-1.058 2.05,-1.058 0.175,0 0.568,0 1.004,0.305 0.12,0.088 0.142,0.11 0.218,0.11 0.142,0 0.295,-0.142 0.295,-0.295 0,-0.207 -0.665,-0.622 -1.407,-0.622 -1.255,0 -2.433,0.731 -2.433,1.56 0,0.47 0.393,0.742 0.436,0.775 -0.643,0.36 -0.992,0.97 -0.992,1.472 0,0.71 0.621,1.375 1.778,1.375 1.44,0 2.05,-0.96 2.05,-1.124 0,-0.065 -0.054,-0.109 -0.12,-0.109 -0.054,0 -0.087,0.044 -0.108,0.077 -0.153,0.25 -0.415,0.654 -1.724,0.654 -0.665,0 -1.593,-0.163 -1.593,-0.938 0,-0.37 0.306,-0.95 0.971,-1.276 z m 0.36,-0.142 c 0.251,-0.087 0.48,-0.098 0.666,-0.098 0.272,0 0.316,0.01 0.512,0.065 -0.163,0.076 -0.185,0.087 -0.6,0.087 -0.24,0 -0.37,0 -0.578,-0.054 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="g0-68"
+ d="m 1.735,-0.85 c -0.11,0.425 -0.131,0.512 -0.993,0.512 -0.186,0 -0.306,0 -0.306,0.207 C 0.436,0 0.535,0 0.742,0 h 3.61 c 2.27,0 4.419,-2.302 4.419,-4.69 0,-1.54 -0.927,-2.76 -2.564,-2.76 H 2.542 c -0.207,0 -0.327,0 -0.327,0.206 0,0.131 0.098,0.131 0.316,0.131 0.142,0 0.338,0.011 0.469,0.022 0.175,0.022 0.24,0.055 0.24,0.175 0,0.043 -0.01,0.076 -0.044,0.207 L 1.735,-0.851 Z M 4.09,-6.699 c 0.098,-0.382 0.12,-0.415 0.589,-0.415 h 1.167 c 1.07,0 1.975,0.578 1.975,2.018 0,0.535 -0.218,2.324 -1.146,3.524 -0.316,0.404 -1.178,1.233 -2.52,1.233 H 2.924 c -0.153,0 -0.175,0 -0.24,-0.011 -0.11,-0.011 -0.142,-0.022 -0.142,-0.11 0,-0.032 0,-0.054 0.054,-0.25 L 4.091,-6.7 Z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="g1-49"
+ d="m 3.207,-6.982 c 0,-0.262 0,-0.283 -0.25,-0.283 -0.677,0.698 -1.637,0.698 -1.986,0.698 v 0.338 c 0.218,0 0.862,0 1.429,-0.284 v 5.651 c 0,0.393 -0.033,0.524 -1.015,0.524 H 1.036 V 0 c 0.382,-0.033 1.331,-0.033 1.768,-0.033 0.437,0 1.385,0 1.767,0.033 v -0.338 h -0.35 c -0.981,0 -1.014,-0.12 -1.014,-0.524 z"
+ inkscape:connector-curvature="0" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2.0081833"
+ inkscape:cx="208.55015"
+ inkscape:cy="131.11945"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1920"
+ inkscape:window-height="1152"
+ inkscape:window-x="0"
+ inkscape:window-y="24"
+ inkscape:window-maximized="1"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:snap-intersection-paths="true"
+ inkscape:object-paths="false" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-250.97409,-458.03924)">
+ <path
+ style="fill:none;stroke:#050505;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 259.04324,702.38225 C 331.721,597.3439 388.17358,522.01205 515.49394,510.66668"
+ id="path1203"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#Arrow1Mstart);marker-end:url(#Arrow1Mend)"
+ d="m 259.04324,473.81745 v 228.5648 h 258.44255"
+ id="path859"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#000000;fill-opacity:0.14220186;stroke:#5f5f5f;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 357.28377,579.51529 h 58.83888 v -52.38982 z"
+ id="path1257"
+ inkscape:connector-curvature="0" />
+ <use
+ height="100%"
+ width="100%"
+ x="1872.02"
+ xlink:href="#g0-27"
+ y="1490.67"
+ id="use2727"
+ transform="matrix(1.6728644,0,0,1.6728644,-2881.3558,-2027.7838)" />
+ <use
+ height="100%"
+ width="100%"
+ x="1878.62"
+ xlink:href="#g0-34"
+ y="1490.67"
+ id="use2729"
+ transform="matrix(1.6728644,0,0,1.6728644,-2617.9337,-1778.8025)" />
+ <use
+ height="100%"
+ width="100%"
+ x="1883.6899"
+ xlink:href="#g0-68"
+ y="1490.67"
+ id="use2731"
+ transform="matrix(1.6728644,0,0,1.6728644,-2725.4936,-1934.4444)" />
+ <use
+ height="100%"
+ width="100%"
+ x="1892.99"
+ xlink:href="#g1-49"
+ y="1490.67"
+ id="use2733"
+ transform="matrix(1.6728644,0,0,1.6728644,-2785.0546,-1897.3176)" />
+ </g>
+</svg>
diff --git a/doc/dev-doc/manual/getting_started.rst b/doc/dev-doc/manual/getting_started.rst
index ffd4b4bb3..ea7a75556 100644
--- a/doc/dev-doc/manual/getting_started.rst
+++ b/doc/dev-doc/manual/getting_started.rst
@@ -1,302 +1,308 @@
Getting Started
===============
-Compiling Akantu
-----------------
+Compiling ``Akantu``
+--------------------
-Akantu is a `CMake <https://cmake.org/>`_ project, so to configure it, you can either
+``Akantu`` is a `CMake <https://cmake.org/>`_ project, so to configure it, you can either
follow the usual way::
> cd akantu
> mkdir build
> cd build
> ccmake ..
[ Set the options that you need ]
> make
> make install
-All the Akantu options are documented in Appendix app:package-dependencies.
+All the ``Akantu`` options are documented in Appendix app:package-dependencies.
Writing a ``main`` function
---------------------------
-Akantu first needs to be initialized. The memory management included in the core
-library handles the correct allocation and de-allocation of vectors, structures
-and/or objects. Moreover, in parallel computations, the initialization procedure
-performs the communication setup. This is achieved by the function
-:cpp:func:`initialize <akantu::initialize>` that is used as follows::
+``Akantu`` first needs to be initialized. The memory management included in the
+core library handles the correct allocation and de-allocation of vectors,
+structures and/or objects. Moreover, in parallel computations, the
+initialization procedure performs the communication setup. This is achieved by
+the function :cpp:func:`initialize <akantu::initialize>` that is used as
+follows::
#include "aka_common.hh"
#include "..."
using namespace akantu;
int main(int argc, char *argv[]) {
- initialize("input_file.dat", argc, argv);
+ initialize("input_file.dat", argc, argv);
- // your code ...
+ // your code ...
}
The :cpp:func:`initialize <akantu::initialize>` function takes the text inpute
-file and the program parameters which can be parsed by Akantu in due form (see
+file and the program parameters which can be parsed by ``Akantu`` in due form (see
sect:parser). Obviously it is necessary to include all files needed in main. In
this manual all provided code implies the usage of ``akantu`` as
namespace.
+.. _loading mesh:
+
Creating and Loading a Mesh
---------------------------
-In its current state, Akantu supports three types of meshes: Gmsh, Abaqus and
+In its current state, ``Akantu`` supports three types of meshes: Gmsh, Abaqus and
Diana. Once a :cpp:class:`akantu::Mesh` object is created with a given spatial
dimension, it can be filled by reading a mesh input file. The method
:cpp:func:`read <akantu::Mesh::read>` of the class :cpp:class:`Mesh
<akantu::Mesh>` infers the mesh type from the file extension. If a non-standard
file extension is used, the mesh type has to be specified. ::
UInt spatial_dimension = 2;
Mesh mesh(spatial_dimension);
// Reading Gmsh files
mesh.read("my_gmsh_mesh.msh");
mesh.read("my_gmsh_mesh", _miot_gmsh);
The Gmsh reader adds the geometrical and physical tags as mesh data. The
physical values are stored as a :cpp:type:`UInt <akantu::UInt>` data called
``tag_0``, if a string name is provided it is stored as a ``std::string`` data
named ``physical_names``. The geometrical tag is stored as a :cpp:type:`UInt
<akantu::UInt>` data named ``tag_1``.
Using Arrays
------------
-Data in Akantu can be stored in data containers implemented by the
+Data in ``Akantu`` can be stored in data containers implemented by the
:cpp:class:`akantu::Array` class. In its most basic usage, the :cpp:class:`Array
<akantu::Array>` class implemented in \akantu is similar to the ``std::vector``
class of the Standard Template Library (STL) for C++. A simple :cpp:class:`Array
<akantu::Array>` containing a sequence of ``nb_element`` values (of a given
type) can be generated with::
Array<type> example_array(nb_element);
-where ``type`` usually is ``Real``, ``Int``, ``UInt`` or ``bool``.
-Each value is associated to an index, so that data can be accessed by
-typing::
+where ``type`` usually is :cpp:type:`Real <akantu::Real>`, :cpp:type:`Int
+<akantu::Int>`, :cpp:type:`UInt <akantu::UInt>` or ``bool``. Each value is
+associated to an index, so that data can be accessed by typing::
auto & val = example_array(index);
``Arrays`` can also contain tuples of values for each index. In that case, the
number of components per tuple must be specified at the :cpp:class:`Array
<akantu::Array>` creation. For example, if we want to create an
:cpp:class:`Array <akantu::Array>` to store the coordinates (sequences of three
values) of ten nodes, the appropriate code is the following::
UInt nb_nodes = 10;
UInt spatial_dimension = 3;
Array<Real> position(nb_nodes, spatial_dimension);
In this case the :math:`x` position of the eighth node number will be given
by ``position(7, 0)`` (in C++, numbering starts at 0 and not 1). If
the number of components for the sequences is not specified, the
default value of 1 is used. Here is a list of some basic operations
that can be performed on :cpp:class:`Array <akantu::Array>`:
- - ``resize(size)`` change the size of the :cpp:class:`Array <akantu::Array>`.
- - ``clear()`` set all entries of the :cpp:class:`Array <akantu::Array>` to
- zero.
- - ``set(t)`` set all entries of the :cpp:class:`Array <akantu::Array>` to
- ``t``.
- - ``copy(const Array<T> & other)`` copy another :cpp:class:`Array
- <akantu::Array>` into the current one. The two :cpp:class:`Array
- <akantu::Array>` should have the same number of components.
- - ``push_back(tuple)`` append a tuple with the correct number of components at
- the end of the :cpp:class:`Array <akantu::Array>`.
- - ``erase(i)`` erase the value at the i-th position.
- - ``find(value)`` search ``value`` in the current :cpp:class:`Array
- <akantu::Array>`. Return position index of the first occurence or -1 if not
- found.
- - ``storage()`` Return the address of the allocated memory of the
- :cpp:class:`Array <akantu::Array>`.
+ - :cpp:func:`resize(size) <akantu::ArrayDataLayer::resize>` change the size of
+ the :cpp:class:`Array <akantu::Array>`.
+ - :cpp:func:`clear <akantu::Array::clear>` reset the size of the
+ :cpp:class:`Array <akantu::Array>` to zero. (*warning* this changed in >
+ v4.0)
+ - :cpp:func:`set(t) <akantu::Array::set>` set all entries of the
+ :cpp:class:`Array <akantu::Array>` to ``t``.
+ - :cpp:func:`copy(const Array & other) <akantu::Array::copy>` copy another
+ :cpp:class:`Array <akantu::Array>` into the current one. The two
+ :cpp:class:`Arrays <akantu::Array>` should have the same number of
+ components.
+ - :cpp:func:`push_back(tuple) <akantu::Array::push_back>` append a tuple with
+ the correct number of components at the end of the :cpp:class:`Array <akantu::Array>`.
+ - :cpp:func:`erase(i) <akantu::Array::erase>` erase the value at the i-th position.
+ - :cpp:func:`find(value) <akantu::Array::find>` search ``value`` in the
+ current :cpp:class:`Array <akantu::Array>`. Return position index of the
+ first occurence or -1 if not found.
+ - :cpp:func:`storage() <akantu::Array::storage>` Return the address of the
+ allocated memory of the :cpp:class:`Array <akantu::Array>`.
Array iterators
-------------------
-It is very common in Akantu to loop over arrays to perform a specific treatment.
+It is very common in ``Akantu`` to loop over arrays to perform a specific treatment.
This ranges from geometric calculation on nodal quantities to tensor algebra (in
constitutive laws for example). The :cpp:class:`Array <akantu::Array>` object
has the possibility to request iterators in order to make the writing of loops
easier and enhance readability. For instance, a loop over the nodal coordinates
can be performed like::
// accessing the nodal coordinates Array
// with spatial_dimension components
const auto & nodes = mesh.getNodes();
for (const auto & coords : make_view(nodes, spatial_dimension)) {
// do what you need ....
}
-In that example, each ``coords`` is a ``Vector<Real>`` containing
-geometrical array of size ``spatial_dimension`` and the iteration is
+In that example, each ``coords`` is a :cpp:class:`Vector\<Real\> <akantu::Vector>`
+containing geometrical array of size ``spatial_dimension`` and the iteration is
conveniently performed by the :cpp:class:`Array <akantu::Array>` iterator.
The :cpp:class:`Array <akantu::Array>` object is intensively used to store
second order tensor values. In that case, it should be specified that the
returned object type is a matrix when constructing the iterator. This is done
when calling the :cpp:func:`make_view <akantu::make_view>`. For instance,
assuming that we have a :cpp:class:`Array <akantu::Array>` storing stresses, we
can loop over the stored tensors by::
for (const auto & stress :
make_view(stresses, spatial_dimension, spatial_dimension)) {
// stress is of type `const Matrix<Real>&`
}
-In that last example, the :cpp:class:`Matrix <akantu::Matrix>` objects are
+In that last example, the :cpp:class:`Matrix\<Real\> <akantu::Matrix>` objects are
``spatial_dimension`` :math:`\times` ``spatial_dimension`` matrices. The light
-objects :cpp:class:`Matrix <akantu::Matrix>` and :cpp:class:`Vector
-<akantu::Vector>` can be used and combined to do most common linear algebra. If
-the number of component is 1, it is possible to use :cpp:func:`make_view
-<akantu::make_view>` to this effect.
+objects :cpp:class:`Matrix\<T\> <akantu::Matrix>` and
+:cpp:class:`Vector\<T\> <akantu::Vector>` can be used and combined to do most
+common linear algebra. If the number of component is 1, it is possible to use
+:cpp:func:`make_view <akantu::make_view>` to this effect.
In general, a mesh consists of several kinds of elements. Consequently, the
amount of data to be stored can differ for each element type. The
straightforward example is the connectivity array, namely the sequences of nodes
belonging to each element (linear triangular elements have fewer nodes than,
say, rectangular quadratic elements etc.). A particular data structure called
-:cpp:class:`ElementTypeMapArray <akantu::ElementTypeMapArray>` is provided to
-easily manage this kind of data. It consists of a group of ``Arrays``, each
+:cpp:class:`ElementTypeMapArray\<T\> <akantu::ElementTypeMapArray>` is provided
+to easily manage this kind of data. It consists of a group of ``Arrays``, each
associated to an element type. The following code can retrieve the
-``ElementTypeMapArray`` which stores the connectivity arrays for a mesh::
+:cpp:class:`ElementTypeMapArray\<UInt\> <akantu::ElementTypeMapArray>` which
+stores the connectivity arrays for a mesh::
const ElementTypeMapArray<UInt> & connectivities =
mesh.getConnectivities();
Then, the specific array associated to a given element type can be obtained by::
const Array<UInt> & connectivity_triangle =
connectivities(_triangle_3);
where the first order 3-node triangular element was used in the presented piece
of code.
Vector & Matrix
```````````````
-The :cpp:class:`Array <akantu::Array>` iterators as presented in the previous
-section can be shaped as :cpp:class:`Vector <akantu::Vector>` or
-:cpp:class:`Matrix <akantu::Matrix>`. This objects represent 1st and 2nd order
+The :cpp:class:`Array\<T\> <akantu::Array>` iterators as presented in the previous
+section can be shaped as :cpp:class:`Vector\<T\> <akantu::Vector>` or
+:cpp:class:`Matrix\<T\> <akantu::Matrix>`. This objects represent 1st and 2nd order
tensors. As such they come with some functionalities that we will present a bit
more into detail in this here.
``Vector<T>``
'''''''''''''
- Accessors:
- ``v(i)`` gives the ``i`` -th component of the vector ``v``
- ``v[i]`` gives the ``i`` -th component of the vector ``v``
- ``v.size()`` gives the number of component
- Level 1: (results are scalars)
- ``v.norm()`` returns the geometrical norm (:math:`L_2`)
- ``v.norm<N>()`` returns the :math:`L_N` norm defined as :math:`\left(\sum_i
|v(i)|^N\right)^{1/N}`. N can take any positive integer value.
There are also some particular values for the most commonly used
norms, ``L_1`` for the Manhattan norm, ``L_2`` for the geometrical
norm and ``L_inf`` for the norm infinity.
- ``v.dot(x)`` return the dot product of ``v`` and ``x``
- ``v.distance(x)`` return the geometrical norm of :math:`v - x`
- Level 2: (results are vectors)
- ``v += s``, ``v -= s``, ``v *= s``, ``v /= s`` those are
element-wise operators that sum, substract, multiply or divide all the
component of ``v`` by the scalar ``s``
- ``v += x``, ``v -= x`` sums or substracts the vector ``x`` to/from ``v``
- - ``v.mul(A, x, alpha)`` stores the result of :math:`\alpha \boldsymbol{A}
- \vec{x}` in ``v``, :math:`\alpha` is equal to 1 by default
+ - ``v.mul(A, x, alpha)`` stores the result of :math:`\alpha \boldsymbol{A} \vec{x}` in ``v``, :math:`\alpha` is equal to 1 by default
- ``v.solve(A, b)`` stores the result of the resolution of the system
:math:`\boldsymbol{A} \vec{x} = \vec{b}` in ``v``
- ``v.crossProduct(v1, v2)`` computes the cross product of ``v1`` and ``v2``
and stores the result in ``v``
``Matrix<T>``
'''''''''''''
- Accessors:
- ``A(i, j)`` gives the component :math:`A_{ij}` of the matrix ``A``
- ``A(i)`` gives the :math:`i^{th}` column of the matrix as a ``Vector``
- ``A[k]`` gives the :math:`k^{th}` component of the matrix, matrices are
stored in a column major way, which means that to access :math:`A_{ij}`,
:math:`k = i + j M`
- ``A.rows()`` gives the number of rows of ``A`` (:math:`M`)
- ``A.cols()`` gives the number of columns of ``A`` (:math:`N`)
- ``A.size()`` gives the number of component in the matrix (:math:`M \times
N`)
- Level 1: (results are scalars)
- ``A.norm()`` is equivalent to ``A.norm<L_2>()``
- ``A.norm<N>()`` returns the :math:`L_N` norm defined as
:math:`\left(\sum_i\sum_j |A(i,j)|^N\right)^{1/N}`. N can take
any positive integer value. There are also some particular values
for the most commonly used norms, ``L_1`` for the Manhattan
norm, ``L_2`` for the geometrical norm and ``L_inf`` for
the norm infinity.
- ``A.trace()`` return the trace of ``A``
- ``A.det()`` return the determinant of ``A``
- ``A.doubleDot(B)`` return the double dot product of ``A`` and
``B``, :math:`\mat{A}:\mat{B}`
- Level 3: (results are matrices)
- ``A.eye(s)``, ``Matrix<T>::eye(s)`` fills/creates a matrix with
the :math:`s\mat{I}` with :math:`\mat{I}` the identity matrix
- ``A.inverse(B)`` stores :math:`\mat{B}^{-1}` in ``A``
- ``A.transpose()`` returns :math:`\mat{A}^{t}`
- ``A.outerProduct(v1, v2)`` stores :math:`\vec{v_1} \vec{v_2}^{t}` in
``A``
- ``C.mul<t_A, t_B>(A, B, alpha)``: stores the result of the product of
``A`` and code{B} time the scalar ``alpha`` in ``C``. ``t_A``
and ``t_B`` are boolean defining if ``A`` and ``B`` should be
transposed or not.
+----------+----------+--------------+
|``t_A`` |``t_B`` |result |
| | | |
+----------+----------+--------------+
|false |false |:math:`\mat{C}|
| | |= \alpha |
| | |\mat{A} |
| | |\mat{B}` |
| | | |
+----------+----------+--------------+
|false |true |:math:`\mat{C}|
| | |= \alpha |
| | |\mat{A} |
| | |\mat{B}^t` |
| | | |
+----------+----------+--------------+
|true |false |:math:`\mat{C}|
| | |= \alpha |
| | |\mat{A}^t |
| | |\mat{B}` |
| | | |
+----------+----------+--------------+
|true |true |:math:`\mat{C}|
| | |= \alpha |
| | |\mat{A}^t |
| | |\mat{B}^t` |
+----------+----------+--------------+
- ``A.eigs(d, V)`` this method computes the eigenvalues and eigenvectors of
``A`` and store the results in ``d`` and ``V`` such that :math:`d(i) =
\lambda_i` and :math:`V(i) = \vec{v_i}` with :math:`\mat{A}\vec{v_i} =
\lambda_i\vec{v_i}` and :math:`\lambda_1 > ... > \lambda_i > ... >
\lambda_N`
diff --git a/doc/dev-doc/manual/heattransfermodel.rst b/doc/dev-doc/manual/heattransfermodel.rst
new file mode 100644
index 000000000..38f14392b
--- /dev/null
+++ b/doc/dev-doc/manual/heattransfermodel.rst
@@ -0,0 +1,169 @@
+Heat Transfer Model
+===================
+
+The heat transfer model is a specific implementation of the :cpp:class:`Model
+<akantu::Model>` interface dedicated to handle the dynamic heat equation.
+
+Theory
+------
+
+The strong form of the dynamic heat equation
+can be expressed as
+
+.. math::
+ \rho c_v \dot{T} + \nabla \cdot \vec{\kappa} \nabla T = b
+
+with :math:`T` the scalar temperature field, :math:`c_v` the specific heat capacity, :math:`\rho`
+the mass density, :math:`\mat{\kappa}` the conductivity tensor, and :math:`b` the heat
+generation per unit of volume. The discretized weak form with a finite number of
+elements is
+
+.. math::
+ \forall i \quad
+ \sum_j \left( \int_\Omega \rho c_v N_j N_i d\Omega \right) \dot{T}_j
+ - \sum_j \left( \int_\Omega \vec{\kappa} \nabla N_j \nabla N_i d\Omega \right) T_j =
+ - \int_{\Gamma} N_i \vec{q} \cdot \vec{n} d\Gamma + \int_\Omega b N_i d\Omega
+
+with :math:`i` and :math:`j` the node indices, :math:`\vec{n}` the normal field to the surface
+:math:`\Gamma = \partial \Omega`.
+To simplify, we can define the capacity and the conductivity matrices as
+
+.. math::
+ C_{ij} = \int_\Omega \rho c_v N_j N_i d\Omega \qquad \textrm{and} \qquad
+ K_{ij} = - \int_\Omega \vec{\kappa} \nabla N_j \nabla N_i d\Omega
+
+and the system to solve can be written
+
+.. math::
+ \mat{C} \cdot \vec{\dot{T}} = \vec{Q}^{\text{ext}} -\mat{K} \cdot \vec{T}~,
+
+with :math:`\vec{Q}^{\text{ext}}` the consistent heat generated.
+
+Using the Heat Transfer Model
+-----------------------------
+
+A material file name has to be provided during initialization.
+Currently, the :cpp:class:`HeatTransferModel <akantu::HeatTransferModel>` object uses dynamic analysis
+with an explicit time integration scheme. It can simply be created
+like this
+
+.. code-block:: c++
+
+ HeatTransferModel model(mesh, spatial_dimension);
+
+while an existing mesh has been used (see \ref{sect:common:mesh}).
+Then the model object can be initialized with:
+
+.. code-block:: c++
+
+ model.initFull()
+
+This function will load the material properties, and allocate / initialize the nodes and element :cpp:class:`Arrays <akantu::Array>`
+More precisely, the heat transfer model contains 4 :cpp:class:`Arrays <akantu::Array>`:
+
+- **temperature** contains the nodal temperature :math:`T` (zero by default after the initialization).
+
+- **temperature_rate** contains the variations of temperature :math:`\dot{T}` (zero by default after the initialization).
+
+- **blocked_dofs** contains a Boolean value for each degree of freedom specifying whether the degree is blocked or not. A Dirichlet boundary condition (:math:`T_d`) can be prescribed by setting the **blocked_dofs** value of a degree of freedom to ``true``. The **temperature** and the **temperature_rate** are computed for all degrees of freedom where the **blocked_dofs** value is set to ``false``. For the remaining degrees of freedom, the imposed values (zero by default after initialization) are kept.
+
+- **external_heat_rate** contains the external heat generations. :math:`\vec{Q^{ext}}` on the nodes.
+
+- **internal_heat_rate** contains the internal heat generations. :math:`\vec{Q^{int}} = -\mat{K} \cdot \vec{T}` on the nodes.
+
+Only a single material can be specified on the domain. A material text file (*e.g.* material.dat) provides the material properties as follows:
+
+.. code-block::
+
+ model heat_transfer_model [
+ capacity = %\emph{XXX}%
+ density = %\emph{XXX}%
+ conductivity = [%\emph{XXX}% ... %\emph{XXX}%]
+ ]
+
+where the ``capacity`` and ``density`` are scalars, and the ``conductivity`` is specified as a :math:`3\times 3` tensor.
+
+Explicit Dynamic
+----------------
+
+The explicit time integration scheme in ``Akantu`` uses a lumped capacity
+matrix :math:`\mat{C}` (reducing the computational cost, see Chapter :ref:`sect-smm`).
+This matrix is assembled by distributing the capacity of each element onto its nodes. Therefore, the resulting :math:`\mat{C}` is a diagonal matrix stored in the ``capacity`` :cpp:class:`Array <akantu::Array>` of the model.
+
+
+.. code-block:: c++
+
+ model.assembleCapacityLumped();
+
+.. note::
+ Currently, only the explicit time integration with lumped capacity
+ matrix is implemented within ``Akantu``.
+
+The explicit integration scheme is *Forward Euler* :cite:`curnier92a`.
+
+- Predictor: :math:`\vec{T}_{n+1} = \vec{T}_{n} + \Delta t \dot{\vec{T}}_{n}`
+
+- Update residual: :math:`\vec{R}_{n+1} = \left( \vec{Q^{ext}_{n+1}} - \vec{K}\vec{T}_{n+1} \right)`
+
+- Corrector : :math:`\dot{\vec{T}}_{n+1} = \mat{C}^{-1} \vec{R}_{n+1}`
+
+The explicit integration scheme is conditionally stable. The time step has to be
+smaller than the stable time step, and it can be obtained in ``Akantu`` as
+follows:
+
+.. code-block:: c++
+
+ time_step = model.getStableTimeStep();
+
+The stable time step is defined as:
+
+.. math::
+ \Delta t_{\st{crit}} = 2 \Delta x^2 \frac{\rho c_v}{\mid\mid \mat{\kappa} \mid\mid^\infty}
+ :label: eqn:htm:explicit:stabletime
+
+where :math:`\Delta x` is the characteristic length (*e.g* the in-radius in the
+case of linear triangle element), :math:`\rho` is the density,
+:math:`\mat{\kappa}` is the conductivity tensor, and :math:`c_v` is the specific
+heat capacity. It is necessary to impose a time step which is smaller than the
+stable time step, for instance, by multiplying the stable time step by a safety
+factor smaller than one.
+
+.. code-block:: c++
+
+ const Real safety_time_factor = 0.1;
+ Real applied_time_step = time_step * safety_time_factor;
+ model.setTimeStep(applied_time_step);
+
+
+The following loop allows, for each time step, to update the ``temperature``,
+``residual`` and ``temperature_rate`` fields following the previously described
+integration scheme.
+
+.. code-block:: c++
+
+ for (UInt s = 1; (s-1)*applied_time_step < total_time; ++s) {
+ model.solveStep();
+ }
+
+An example of explicit dynamic heat propagation is presented in
+``examples/heat_transfer/explicit_heat_transfer.cc``. This example consists
+of a square 2D plate of :math:`1 \text{m}^2` having an initial temperature of
+:math:`100 \text{K}` everywhere but a none centered hot point maintained at
+:math:`300 \text{K}`. :numref:`fig:htm:explicit:dynamic-1` presents the geometry
+of this case. The material used is a linear fictitious elastic material with a
+density of :math:`8940 \text{kg}/\text{m}^3`, a conductivity of
+:math:`401 \text{W}/\text{m}/\text{K}` and a specific heat capacity of
+:math:`385 \text{J}/\text{K}/\text{kg}`. The time step used is
+:math:`0.12 \text{s}`.
+
+.. _fig:htm:explicit:dynamic-1:
+.. figure:: figures/hot-point-1.png
+ :align: center
+
+ Initial temperature field
+
+.. _fig:htm:explicit:dynamic-2:
+.. figure:: figures/hot-point-2.png
+ :align: center
+
+ Temperature field after 15000 time steps = 30 minutes. The lines represent iso-surfaces.
diff --git a/doc/dev-doc/manual/io.rst b/doc/dev-doc/manual/io.rst
new file mode 100644
index 000000000..230fdb671
--- /dev/null
+++ b/doc/dev-doc/manual/io.rst
@@ -0,0 +1,273 @@
+.. _sect-io:
+
+Input/Output
+============
+
+Input file
+----------
+
+The text input file of a simulation should be precised using the method
+:cpp:func:`initialize <akantu::initialize>` which will instantiate the static
+:cpp:class:`Parser <akantu::Parser>` object of ``Akantu``. This section
+explains how to manipulate at :cpp:class:`Parser <akantu::Parser>` objects to
+input data in ``Akantu``.
+
+Akantu Parser
+~~~~~~~~~~~~~
+
+``Akantu`` file parser has a tree organization.
+
+- :cpp:class:`Parser <akantu::Parser>`, the root of the tree, can be accessed
+ using::
+
+ auto & parser = getStaticParser();
+
+- :cpp:class:`ParserSection <akantu::ParserSection>`, branch of the tree,
+ contains map a of sub-sections (:cpp:enum:`SectionType
+ <akantu::SectionType>`, :cpp:class:`ParserSection <akantu::ParserSection>`)
+ and a :cpp:class:`ParserSection * <akantu::ParserSection>` pointing to the
+ parent section. The user section of the input file can directly be accessed
+ by::
+
+ const auto & usersect = getUserParser();
+
+- :cpp:class:`ParserParameter <akantu::ParserParameter>`, the leaf of the tree,
+ carries data of the input file which can be cast to the correct type with the
+ assignment operator::
+
+ Real mass = usersect.getParameter("mass");
+
+ or used directly within an expression
+
+Grammar
+~~~~~~~
+
+The structure of text input files consists of different sections
+containing a list of parameters. As example, the file parsed in the
+previous section will look like::
+
+ user parameters [
+ mass = 10.5
+ ]
+
+Basically every standard arithmetic operations can be used inside of input files
+as well as the constant ``pi`` and ``e`` and the exponent operator ``^``.
+Operations between :cpp:class:`ParserParameter <akantu::ParserParameter>` are
+also possible with the convention that only parameters of the current and the
+parent sections are available. :cpp:class:`Vector <akantu::Vector>` and
+:cpp:class:`Matrix <akantu::Matrix>` can also be read according to the ``NumPy``
+:cite:`numpy` writing convention (a.e. cauchy_stress_tensor = [[:math:`\sigma_{xx}`,
+:math:`\sigma_{xy}`],[:math:`\sigma_{yx}`,\ :math:`\sigma_{yy}`]]). An example
+illustrating how to parse the following input file can be found in
+``example\io\parser\example_parser.cc``::
+
+ user parameters [
+ spatial_dimension = 2
+ mesh_file = swiss_cheese.msh
+ inner_holes = holes
+ outter_crust = crust
+ lactostatic_p = 30e3
+ stress = [[lactostatic_p, 0 ],
+ [0, lactostatic_p]]
+ max_nb_iterations = 100
+ precision = 1e-9
+ ]
+
+.. _sect-io-material:
+
+Material section
+~~~~~~~~~~~~~~~~
+
+The input file should also be used to specify material characteristics
+(constitutive behavior and material properties). The dedicated material section
+is then read by :cpp:func:`initFull <akantu::SolidMechanicsModel::initFull>`
+method of :cpp:class:`SolidMechanicsModel <akantu::SolidMechanicsModel>` which
+initializes the different materials specified with the following convention::
+
+ material constitutive_law [
+ name = value
+ rho = value
+ ...
+ ]
+
+where *constitutive_law* is the adopted constitutive law, followed by
+the material properties listed one by line in the bracket (*e.g.*,
+``name`` and density :math:``rho``. Some constitutive laws can also
+have an *optional flavor*. More information can be found in sections
+relative to material :ref:`sect-smm-cl`
+or in Appendix :ref:`app-material-parameters`.
+
+Output data
+-----------
+
+Generic data
+~~~~~~~~~~~~
+
+In this section, we address ways to get the internal data in human-readable
+formats. The models in ``Akantu`` handle data associated to the mesh, but this
+data can be split into several :cpp:class:`Arrays <akantu::Array>`. For example,
+the data stored per element type in a :cpp:class:`ElementTypeMapArray
+<akantu::ElementTypeMapArray>` is composed of as many :cpp:class:`Arrays
+<akantu::Array>` as types in the mesh.
+
+In order to get this data in a visualization software, the models contain a
+object to dump ``VTK`` files. These files can be visualized in software such
+as ``ParaView`` :cite:`paraview`, ``ViSit`` :cite:`visit` or ``Mayavi``
+:cite:`mayavi`.
+
+The internal dumper of the model can be configured to specify which data
+fields are to be written. This is done with the :cpp:func:`addDumpField <akantu::Model::addDumpField>` method. By default all the
+files are generated in a folder called ``paraview/``::
+
+ model.setBaseName("output"); // prefix for all generated files
+ model.addDumpField("displacement"); model.addDumpField("stress"); ...
+ model.dump()
+
+The fields are dumped with the number of components of the memory. For example,
+in 2D, the memory has :cpp:class:`Vectors <akantu::Vector>` of 2 components, or
+the :math:`2^{nd}` order tensors with :math:`2\times2` components. This memory
+can be dealt with :cpp:func:`addDumpFieldVector
+<akantu::Model::addDumpFieldVector>` which always dumps :cpp:class:`Vectors
+<akantu::Vector>` with 3 components or :cpp:func:`addDumpFieldTensor
+<akantu::Model::addDumpFieldTensor>` which dumps :math:`2^{nd}` order tensors
+with :math:`3\times3` components respectively. The routines :cpp:func:`addDumpFieldVector <akantu::Model::addDumpFieldVector>` and
+:cpp:func:`addDumpFieldTensor <akantu::Model::addDumpFieldTensor>` were
+introduced because of ``ParaView`` which mostly manipulate 3D data.
+
+Those fields which are stored by quadrature point are modified to be seen in the
+``VTK`` file as elemental data. To do this, the default is to average the
+values of all the quadrature points.
+
+The list of fields depends on the models (for :cpp:class:`SolidMechanicsModel
+<akantu::SolidMechanicsModel>` see table :ref:`tab-io-smm-field-list`.
+
+.. container::
+ :name: tab-io-smm-field-list
+
+ .. table:: List of dumpable fields for :cpp:class:`SolidMechanicsModel <akantu::SolidMechanicsModel>`.
+
+ ====================== ================ =================
+ key type support
+ ====================== ================ =================
+ displacement ``Vector<Real>`` nodes
+ mass ``Vector<Real>`` nodes
+ velocity ``Vector<Real>`` nodes
+ acceleration ``Vector<Real>`` nodes
+ force ``Vector<Real>`` nodes
+ residual ``Vector<Real>`` nodes
+ increment ``Vector<Real>`` nodes
+ blocked_dofs ``Vector<bool>`` nodes
+ partitions ``Real`` elements
+ material_index variable elements
+ strain ``Matrix<Real>`` quadrature points
+ Green strain ``Matrix<Real>`` quadrature points
+ principal strain ``Vector<Real>`` quadrature points
+ principal Green strain ``Vector<Real>`` quadrature points
+ grad_u ``Matrix<Real>`` quadrature points
+ stress ``Matrix<Real>`` quadrature points
+ Von Mises stress ``Real`` quadrature points
+ material_index variable quadrature points
+ ====================== ================ =================
+
+Cohesive elements’ data
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Cohesive elements and their relative data can be easily dumped thanks to
+a specific dumper contained in :cpp:class:`SolidMechanicsModelCohesive <akantu::SolidMechanicsModelCohesive>`. In order to use it, one has
+just to add the string ``cohesive elements`` when calling each method already
+illustrated. Here is an example on how to dump displacement and damage::
+
+ model.addDumpFieldVectorToDumper("cohesive elements", "displacement");
+ model.addDumpFieldToDumper("cohesive elements", "damage");
+ model.dump("cohesive elements");
+
+
+Fragmentation data
+^^^^^^^^^^^^^^^^^^
+
+Whenever the :cpp:class:`SolidMechanicsModelCohesive
+<akantu::SolidMechanicsModelCohesive>` is used, it is possible to dump
+additional data about the fragments that get formed in the simulation both in
+serial and parallel. This task is carried out by the
+:cpp:class:`FragmentManager <akantu::FragmentManager>` class, that takes care of
+computing the following quantities for each fragment:
+
+- index;
+
+- mass;
+
+- moments of inertia;
+
+- velocity;
+
+- number of elements.
+
+These computations can be realized at once by calling the function
+:cpp:class:`computeAllData <akantu::FragmentManager::computeAllData>`, or
+individually by calling the other public functions of the class. The data can be
+dumped to be visualized in ``ParaView``, or can be accessed within the
+simulation. An example of usage is:
+
+At the end of this example the velocities of the fragments are accessed with a
+reference to a :cpp:class:`const Array\<Real\> <akantu::Array>`. The size of this
+array is the number of fragments, and its number of components is the spatial
+dimension in this case.
+
+Advanced dumping
+~~~~~~~~~~~~~~~~
+
+Arbitrary fields
+^^^^^^^^^^^^^^^^
+
+In addition to the predetermined fields from the models and materials,
+the user can add any data to a dumper as long as the support is the
+same. That is to say data that have the size of the full mesh on if the
+dumper is dumping the mesh, or of the size of an element group if it is
+a filtered dumper.
+
+For this the easiest is to use the “external” fields register functions
+
+The simple case force nodal and elemental data are to pass directly the
+data container itself if it as the good size.
+
+- For nodal fields:
+
+ It is assumed that the array as the same size as the number of nodes
+ in the mesh
+
+- For elemental fields:
+
+ It is assumed that the arrays in the map have the same sizes as the element
+ numbers in the mesh for element types of dimension ``spatial_dimension``.
+
+If some changes have to be applied on the data as for example a padding for
+``ParaView`` vectors, this can be done by using the field interface.
+
+All these functions use the default dumper registered in the mesh but also have
+the ``ToDumper`` variation with the dumper name specified. For example:
+
+An example of code presenting this interface is present in the
+``examples/io/dumper``. This interface is part of the :cpp:class:`Dumpable
+<akantu::Dumpable>` class from which the :cpp:class:`Mesh <akantu::Mesh>`
+inherits.
+
+Creating a new dumper
+^^^^^^^^^^^^^^^^^^^^^
+
+You can also create you own dumpers, ``Akantu`` uses a third-party library in
+order to write the output files, ``IOHelper``. ``Akantu`` supports the
+``ParaView`` format and a Text format defined by ``IOHelper``.
+
+This two files format are handled by the classes :cpp:class:`DumperParaview
+<akantu::DumperParaview>` and :cpp:class:`DumperText <akantu::DumperText>`.
+
+In order to use them you can instantiate on of this object in your code. This
+dumper have a simple interface. You can register a mesh :cpp:func:`registerMesh
+<akantu::DumperIOHelper::registerMesh>`, :cpp:func:`registerFilteredMesh
+<akantu::DumperIOHelper::registerFilteredMesh>` or a field,
+:cpp:class:`registerField <akantu::DumperIOHelper::registerField>`.
+
+An example of code presenting this low level interface is present in the
+``examples/io/dumper``. The different types of :cpp:class:`Field
+<akantu::Field>` that can be created are present in the source folder
+``src/io/dumper``.
diff --git a/doc/dev-doc/manual/manual-bibliography.bib b/doc/dev-doc/manual/manual-bibliography.bib
new file mode 100644
index 000000000..7913a9f6e
--- /dev/null
+++ b/doc/dev-doc/manual/manual-bibliography.bib
@@ -0,0 +1,594 @@
+% This file was created with JabRef 2.10b2.
+% Encoding: UTF-8
+
+
+@Article{aifantis84a,
+ Title = {On the microstructural origin of certain inelastic models},
+ Author = {E. C. Aifantis},
+ Journal = {Journal of Engineering Materials and Technology},
+ Year = {1984},
+ Pages = {326 - 330},
+ Volume = {106}
+}
+
+@Article{Aragon:2013d,
+ Title = {A hierarchical detection framework for computational contact mechanics},
+ Author = {Alejandro M. Arag{\'o}n and Jean-Fran{\c c}ois Molinari},
+ Journal = {Computer Methods in Applied Mechanics and Engineering},
+ Year = {2014},
+ Number = {0},
+ Pages = {574 - 588},
+ Volume = {268},
+
+ Bdsk-url-1 = {http://www.sciencedirect.com/science/article/pii/S0045782513002533},
+ Bdsk-url-2 = {http://dx.doi.org/10.1016/j.cma.2013.10.001},
+ Date-added = {2014-05-22 09:14:48 +0000},
+ Date-modified = {2014-05-22 09:14:48 +0000},
+ Doi = {10.1016/j.cma.2013.10.001},
+ ISSN = {0045-7825},
+ Url = {http://dx.doi.org/10.1016/j.cma.2013.10.001}
+}
+
+@Article{bazant86a,
+ Title = {Mechanics of distributed cracking},
+ Author = {Zden\v{e}k P. Ba\v{z}ant},
+ Journal = {Applied Mechanics Reviews},
+ Year = {1986},
+ Number = {5},
+ Pages = {675 - 705},
+ Volume = {39}
+}
+
+@Article{bazant76a,
+ Title = {Instability, Ductility, and Size Effect in Strain-Softening Concrete},
+ Author = {Zden\v{e}k P. Ba\v{z}ant},
+ Journal = {Journal of the Engineering Mechanics Division},
+ Year = {1976},
+ Number = {5},
+ Pages = {331 - 344},
+ Volume = {102}
+}
+
+@Article{bazant85a,
+ Title = {Wave Propagation in a Strain-Softening Bar: Exact Solution},
+ Author = {Zden\v{e}k P. Ba\v{z}ant and Ted Belytschko},
+ Journal = {Journal of Engineering Mechanics},
+ Year = {1985},
+ Number = {3},
+ Pages = {381 - 389},
+ Volume = {111}
+}
+
+@Article{bazant88a,
+ Title = {{Nonlocal Continuum Damage, Localization Instability and Convergence}},
+ Author = {Zden\v{e}k P. Ba\v{z}ant and Gilles Pijaudier-Cabot},
+ Journal = {Journal of Applied Mechanics},
+ Year = {1988},
+ Number = {2},
+ Pages = {287-293},
+ Volume = {55},
+
+ Optdoi = {10.1115/1.3173674},
+ Publisher = {ASME}
+}
+
+@Article{belytschko03a,
+ Title = {Dynamic crack propagation based on loss of hyperbolicity and a new discontinuous enrichment},
+ Author = {T Belytschko and H Chen and J Xu and G Zi},
+ Journal = {International Journal for Numerical Methods in Engineering},
+ Year = {2003},
+ Pages = {1875-1905},
+ Volume = {58}
+}
+
+@Article{camacho_computational_1996,
+ Title = {Computational modelling of impact damage in brittle materials},
+ Author = {Camacho, {G.T.} and Ortiz, M.},
+ Journal = {International Journal of Solids and Structures},
+ Year = {1996},
+
+ Month = aug,
+ Number = {20{\textendash}22},
+ Pages = {2899--2938},
+ Volume = {33},
+
+ Urldate = {2012-01-17}
+}
+
+@Article{caughey1960,
+ Title = {Classical normal modes in damped linear dynamic systems},
+ Author = {Caughey, TK},
+ Journal = {Journal of Applied Mechanics},
+ Year = {1960},
+ Number = {2},
+ Pages = {269--271},
+ Volume = {27},
+
+ Publisher = {American Society of Mechanical Engineers}
+}
+
+@Book{courant56a,
+ Title = {On the partial difference equations of mathematical physics},
+ Author = {Courant, Richard and Friedrichs, Kurt Otto and Lewy, H.},
+ Publisher = {Courant Institute of Mathematical Sciences, New York University},
+ Year = {1956},
+
+ Address = {New York}
+}
+
+@Book{curnier92a,
+ Title = {M{\'e}thodes num{\'e}riques en m{\'e}canique des solides},
+ Author = {Curnier, A.},
+ Publisher = {EPFL},
+ Year = {1992},
+
+ Bdsk-url-1 = {http://books.google.ch/books?id=-Z2zygAACAAJ},
+ Url = {http://books.google.ch/books?id=-Z2zygAACAAJ}
+}
+
+@incollection{giry13a,
+ title = {{Stress-based Non-local Damage Model}},
+ author = {GIRY, C{\'e}dric and Dufour, Fr{\'e}d{\'e}ric},
+ url = {https://hal.archives-ouvertes.fr/hal-02535143},
+ booktitle = {{Damage Mechanics of Cementitious Materials and Structures}},
+ publisher = {{John Wiley \& Sons, Inc.}},
+ pages = {51-88},
+ year = {2013},
+ month = Mar,
+ doi = {10.1002/9781118562086.ch3},
+}
+
+@Article{youn10a,
+ Title = {Studies of dynamic crack propagation and crack branching with peridynamics},
+ Author = {Youn Doh-Ha and Florin Bobaru},
+ Journal = {International Journal of Fracture},
+ Year = {2010},
+ Pages = {229-1244},
+ Volume = {162}
+}
+
+@Book{frey2009,
+ Title = {M{\'e}thodes des {\'e}l{\'e}ments finis: Analyse des structures et milieux continus},
+ Author = {Frey, F. and Jirousek, J.},
+ Publisher = {PPUR},
+ Year = {2009},
+ Series = {Trait{\'e} de g{\'e}nie civil de l'Ecole Polytechnique F{\'e}d{\'e}rale de Lausanne},
+
+ Bdsk-url-1 = {http://books.google.fr/books?id=bCBtQgAACAAJ},
+ ISBN = {9782880748524},
+ Url = {http://books.google.fr/books?id=bCBtQgAACAAJ}
+}
+
+@Article{gmsh,
+ Title = {Gmsh: A 3-D finite element mesh generator with built-in pre- and post-processing facilities},
+ Author = {Geuzaine, Christophe and Remacle, Jean-Fran{\c c}ois},
+ Journal = {International Journal for Numerical Methods in Engineering},
+ Year = {2009},
+ Number = {11},
+ Pages = {1309--1331},
+ Volume = {79},
+
+ Bdsk-url-1 = {http://dx.doi.org/10.1002/nme.2579},
+ Doi = {10.1002/nme.2579},
+ ISSN = {1097-0207},
+ Keywords = {computer-aided design, mesh generation, post-processing, finite element method, open-source software},
+ Publisher = {John Wiley \& Sons, Ltd.},
+ Url = {http://dx.doi.org/10.1002/nme.2579}
+}
+
+@Article{giry11a,
+ Title = {Stress-based nonlocal damage model},
+ Author = {C. Giry and F. Dufour and J. Mazars},
+ Journal = {International Journal of Solids and Structures},
+ Year = {2011},
+ Pages = {3431 - 3443},
+ Volume = {48}
+}
+
+@Article{hughes-83a,
+ Title = {A precis of developments in computational methods for transient analysis},
+ Author = {Hughes, T.J.R. and Belytschko, T.},
+ Journal = {Journal. of Applied Mechanics (ASME)},
+ Year = {1983},
+ Pages = {1033-1041},
+ Volume = {50}
+}
+
+@Article{hughes83a,
+ Title = {{A Pr{\'e}cis of Developments in Computational Methods for Transient Analysis}},
+ Author = {{Hughes}, T.~J.~R. and {Belytschko}, T.},
+ Journal = {Journal of Applied Mechanics},
+ Year = {1983},
+
+ Month = {December},
+ Pages = {1033},
+ Volume = {50},
+
+ Optdoi = {10.1115/1.3167186}
+}
+
+@Article{jirasek07a,
+ Title = {Mathematical analysis of strain localization},
+ Author = {Milan Jir\'asek},
+ Journal = {Revue Europeenne de Genie Civil},
+ Year = {2007},
+ Pages = {977 - 991},
+ Volume = {11}
+}
+
+@Article{jirasek07b,
+ Title = {Nonlocal damage mechanics},
+ Author = {Milan Jir\'asek},
+ Journal = {Revue Europeenne de Genie Civil},
+ Year = {2007},
+ Pages = {993 - 1021},
+ Volume = {11}
+}
+
+@Article{jirasek98a,
+ Title = {Nonlocal models for damage and fracture: comparison of approaches},
+ Author = {Milan Jir\'asek},
+ Journal = {International Journal of Solids and Structures},
+ Year = {1998},
+ Pages = {4133 - 4145},
+ Volume = {35}
+}
+
+@Article{jirasek03a,
+ Title = {Comparison of integral-type nonlocal plasticity models for strain-softening materials},
+ Author = {Milan Jir\'asek and Simon Rolshoven},
+ Journal = {International Journal of Engineering Science},
+ Year = {2003},
+ Pages = {1553-1602},
+ Volume = {41}
+}
+
+@Article{jirasek04a,
+ Title = {Size effect on fracture energy induced by non-locality},
+ Author = {Milan Jir\'asek and S Rolshoven and P Grassl},
+ Journal = {International Journal for numerical and analytical methods in geomechanics},
+ Year = {2004},
+ Pages = {653 - 670},
+ Volume = {28}
+}
+
+@Article{ladeveze92a,
+ Title = {A damage computational method for composite structures},
+ Author = {P. Ladeveze},
+ Journal = {Computational \& Structures},
+ Year = {1992},
+ Pages = {79-87},
+ Volume = {44}
+}
+
+@Book{Laursen:2002,
+ Title = {Computational Contact and Impact Mechanics: Fundamentals of Modeling Interfacial Phenomena in Nonlinear Finite Element Analysis},
+ Author = {Laursen, T.A.},
+ Publisher = {Springer},
+ Year = {2002},
+ Series = {Engineering online library},
+
+ Bdsk-url-1 = {http://books.google.ch/books?id=umzsErNuyFgC},
+ Date-added = {2014-03-21 13:24:56 +0000},
+ Date-modified = {2014-03-21 13:24:56 +0000},
+ ISBN = {9783540429067},
+ Lccn = {2002511027}
+}
+
+@Book{lemaitre96a,
+ Title = {A Course on Damage Mechanics},
+ Author = {Lemaitre, Jean},
+ Publisher = {Springer Berlin Heidelberg},
+ Year = {1996},
+
+ ISBN = {978-3-540-60980-3}
+}
+
+@PhdThesis{levy10a,
+ Title = {Exploring the {P}hysics behind {D}ynamic {F}ragmentation through {P}arallel {S}imulations},
+ Author = {Levy, Sarah},
+ School = {ENAC},
+ Year = {2010},
+
+ Address = {Lausanne},
+
+ Affiliation = {EPFL},
+ Doctoral = {EDME},
+ Institute = {IIC},
+ Original-unit = {LSMS},
+ Publisher = {EPFL},
+ Unit = {LSMS}
+}
+
+@Article{marigo81a,
+ Title = {{Formulation d'une loi d'endommagement d'un mat\'eriau \'elastique}},
+ Author = {Marigo, Jean-Jacques},
+ Journal = {{C. R. Acad. Sci., Paris, S\'er. II}},
+ Year = {1981},
+ Number = {19},
+ Pages = {1309-1312},
+ Volume = {292},
+
+ ISSN = {0249-6305}
+}
+
+@PhdThesis{mazars84a,
+ Title = {{Application de la m\'ecanique de l'endommagement au comportement non lin\'eaire et \`a la rupture du b\'eton de structure}},
+ Author = {Mazars, Jacky},
+ School = {Universit\'e Paris 6},
+ Year = {1984}
+}
+
+@Article{needleman88a,
+ Title = {Material rate dependence and mesh sensitivity in localization problems},
+ Author = {A. Needleman},
+ Journal = {Computer Methods in Applied Mechanics and Engineering},
+ Year = {1988},
+ Number = {1},
+ Pages = {69 - 85},
+ Volume = {67},
+
+ ISSN = {0045-7825},
+ Optdoi = {10.1016/0045-7825(88)90069-2}
+}
+
+@Article{newmark59a,
+ Title = {{A Method of Computation for Structural Dynamics}},
+ Author = {Nathan M. Newmark},
+ Journal = {Journal of the Engineering Mechanics Division},
+ Year = {1959},
+
+ Month = {July},
+ Number = {3},
+ Pages = {67-94},
+ Volume = {85},
+
+ Editor = {ASCE}
+}
+
+@Article{nguyen2001,
+ Title = {A cohesive model of fatigue crack growth},
+ Author = {Nguyen, O. and Repetto, E. A. and Ortiz, M. and Radovitzky, R. A.},
+ Journal = {International Journal of Fracture},
+ Year = {2001},
+
+ Month = aug,
+ Number = {4},
+ Pages = {351--369},
+ Volume = {110},
+
+ Doi = {10.1023/A:1010839522926},
+ ISSN = {0376-9429, 1573-2673},
+ Language = {en},
+ Urldate = {2015-02-17}
+}
+
+@TechReport{Omohundro:1989,
+ Title = {Five Balltree Construction Algorithms},
+ Author = {Stephen M. Omohundro},
+ Institution = {International Computer Science Institute, University of California at Berkeley},
+ Year = {1989},
+ Number = {TR-89-063},
+ Type = {Technical Report},
+
+ Bdsk-url-1 = {http://ftp.icsi.berkeley.edu/ftp/pub/techreports/1989/tr-89-063.pdf},
+ Date-added = {2014-05-22 09:47:58 +0000},
+ Date-modified = {2014-05-22 09:47:58 +0000},
+ Url = {http://ftp.icsi.berkeley.edu/ftp/pub/techreports/1989/tr-89-063.pdf}
+}
+
+@Article{ortiz1999,
+ Title = {Finite-deformation irreversible cohesive elements for three-dimensional crack-propagation analysis},
+ Author = {Ortiz, M. and Pandolfi, A.},
+ Journal = {International Journal for Numerical Methods in Engineering (IJNME)},
+ Year = {1999},
+ Pages = {1267-1282},
+ Volume = {44}
+}
+
+@Article{ortiz99a,
+ Title = {Finite-deformation irreversible cohesive elements for three-dimensional crack-propagation analysis},
+ Author = {M Ortiz and A Pandolfi},
+ Journal = {International Journal for Numerical Methods in Engineering},
+ Year = {1999},
+ Pages = {1267-1282},
+ Volume = {44}
+}
+
+@Article{pandolfi12a,
+ Title = {An eigenerosion approach to brittle fracture},
+ Author = {A Pandolfi and M Ortiz},
+ Journal = {International Journal for Numerical Methods in Engineering},
+ Year = {2012},
+ Pages = {694-714},
+ Volume = {92}
+}
+
+@Article{patzak01a,
+ Title = {Parallel explicit finite element dynamics with nonlocal constitutive models},
+ Author = {B. Patz{\'a}k and D. Rypl and Z. Bittnar},
+ Journal = {{Computers \& Structures}},
+ Year = {2001},
+ Number = {26--28},
+ Pages = {2287 - 2297},
+ Volume = {79},
+
+ ISSN = {0045-7949}
+}
+
+@PhdThesis{Pietrzak:1997,
+ Title = {Continuum mechanics modelling and augmented Lagrangian formulation of large deformation frictional contact problems},
+ Author = {Pietrzak, Grzegorz},
+ School = {{\'E}cole {P}olytechnique {F}{\'e}d{\'e}rale de {L}ausanne},
+ Year = {1997},
+
+ Date-added = {2014-09-16 12:23:01 +0000},
+ Date-modified = {2014-09-16 12:41:09 +0000},
+ Doi = {10.5075/epfl-thesis-1656}
+}
+
+@Article{pijaudier87a,
+ Title = {Nonlocal damage theory},
+ Author = {Gilles Pijaudier-Cabot and Zden\v{e}k P. Ba\v{z}ant},
+ Journal = {Journal of Engineering Mechanics},
+ Year = {1987},
+ Number = {10},
+ Pages = {1512 - 1533},
+ Volume = {113}
+}
+
+@Article{rice76a,
+ Title = {The Localisation of Plastic Deformation},
+ Author = {James R. Rice},
+ Journal = {Theoretical and Applied Mechanics (14th International Congress on Theoretical and Applied Mechanics, Delft, 1976, ed. W.T. Koiter)},
+ Year = {1976},
+ Pages = {207 - 220},
+ Volume = {1}
+}
+
+@Article{sharon96a,
+ Title = {Microbranching instability and the dynamic fracture of brittle materials},
+ Author = {Eran Sharon and Jay Fineberg},
+ Journal = {Physical Review B},
+ Year = {1996},
+ Number = {10},
+ Pages = {7128 - 7139},
+ Volume = {54}
+}
+
+@Article{silling00a,
+ Title = {Reformulation of elasticity theory for discontinuities and long-range forces},
+ Author = {S A Silling},
+ Journal = {Journal of the Mechanics and Physics of Solids},
+ Year = {2000},
+ Pages = {175-209},
+ Volume = {48}
+}
+
+@Book{simo92,
+ Title = {Computational Inelasticity},
+ Author = {Simo, J.C. and Hughes, T.J.R.},
+ Publisher = {Springer},
+ Year = {1992}
+}
+
+@Article{snozzi_cohesive_2013,
+ Title = {A cohesive element model for mixed mode loading with frictional contact capability},
+ Author = {Snozzi, Leonardo and Molinari, Jean-Francois},
+ Journal = {International Journal for Numerical Methods in Engineering},
+ Year = {2013},
+
+ Month = feb,
+ Number = {5},
+ Pages = {510--526},
+ Volume = {93}
+}
+
+@Book{Belytschko:2000,
+ Title = {Nonlinear Finite Elements for Continua and Structures},
+ Author = {Ted Belytschko, Wing Kam Liu, Brian Moran},
+ Publisher = {Wiley},
+ Year = {2000}
+}
+
+@Article{devree95,
+ Title = {Comparison of nonlocal approaches in continuum damage mechanics},
+ Author = {J.H.P. de Vree and W.A.M. Brekelmans and M.A.J. van Gils},
+ Journal = {Computers \&; Structures},
+ Year = {1995},
+ Number = {4},
+ Pages = {581 - 588},
+ Volume = {55},
+
+ ISSN = {0045-7949},
+ Optdoi = {10.1016/0045-7949(94)00501-S}
+}
+
+@Unpublished{vocialta15,
+ Title = {3D dynamic fragmentation with parallel dynamic insertion of cohesive elements},
+ Author = {M. Vocialta and N. Richart and J.-F. Molinari},
+ Note = {Submitted to IJNME},
+ Year = {2015}
+}
+
+@Unpublished{wolff14a,
+ Title = {A non-local continuum damage approach to model dynamic crack branching},
+ Author = {C. Wolff and N. Richart and J.-F. Molinari},
+ Note = {Submitted to IJNME},
+ Year = {2014}
+}
+
+@Article{Zhou_Molinari_2004,
+ Title = {Dynamic crack propagation with cohesive elements: a methodology to address mesh dependency},
+ Author = {F. Zhou and J. F. Molinari},
+ Journal = {International Journal for Numerical Methods in Engineering},
+ Year = {2004},
+
+ Timestamp = {2015.07.30}
+}
+
+@Misc{abaqus,
+ Title = {Simulia ABAQUS FEA},
+
+ Bdsk-url-1 = {http://www.3ds.com/products-services/simulia/portfolio/abaqus/},
+ Key = {Unified FEA},
+ Url = {\url{http://www.3ds.com/products-services/simulia/portfolio/abaqus/}}
+}
+
+@Misc{cmake,
+ Title = {CMake - Cross Platform Make},
+
+ Bdsk-url-1 = {http://www.cmake.org/},
+ Url = {\url{http://www.cmake.org/}}
+}
+
+@Misc{diana,
+ Title = {TNO DIANA},
+
+ Bdsk-url-1 = {http://tnodiana.com/content/DIANA},
+ Key = {FEM},
+ Url = {\url{http://tnodiana.com/content/DIANA}}
+}
+
+@Misc{mayavi,
+ Title = {The MayaVi Data Visualizer},
+
+ Bdsk-url-1 = {http://mayavi.sourceforge.net/},
+ Url = {\url{http://mayavi.sourceforge.net/}}
+}
+
+@Misc{mumps,
+ Title = {MUMPS : a parallel sparse direct solver},
+
+ Bdsk-url-1 = {http://graal.ens-lyon.fr/MUMPS/},
+ Key = {sparse matrix, direct solver, parallelisme},
+ Url = {\url{http://graal.ens-lyon.fr/MUMPS/}}
+}
+
+@Misc{numpy,
+ Title = {NumPy - Fundamental package for scientific computing with Python},
+
+ Bdsk-url-1 = {http://www.numpy.org/},
+ Url = {\url{http://www.numpy.org/}}
+}
+@Misc{paraview,
+ Title = {ParaView - Open Source Scientific Visualization},
+
+ Bdsk-url-1 = {http://www.paraview.org/},
+ Url = {\url{http://www.paraview.org/}}
+}
+
+@Misc{scotch,
+ Title = {SCOTCH: Static Mapping, Graph, Mesh and Hypergraph Partitioning},
+
+ Bdsk-url-1 = {http://www.labri.fr/perso/pelegrin/scotch/},
+ Url = {\url{http://www.labri.fr/perso/pelegrin/scotch/}}
+}
+
+@Misc{visit,
+ Title = {VisIt Visualization Tool},
+
+ Bdsk-url-1 = {http://wci.llnl.gov/codes/visit/},
+ Url = {\url{http://wci.llnl.gov/codes/visit/}}
+}
+
diff --git a/doc/dev-doc/manual/new-constitutive-laws.rst b/doc/dev-doc/manual/new-constitutive-laws.rst
new file mode 100644
index 000000000..f8925e0fe
--- /dev/null
+++ b/doc/dev-doc/manual/new-constitutive-laws.rst
@@ -0,0 +1,373 @@
+Adding a New Constitutive Law
+-----------------------------
+
+There are several constitutive laws in ``Akantu`` as described in the previous
+Section :ref:`sect-smm-cl`. It is also possible to use a user-defined material
+for the simulation. These materials are referred to as local materials since
+they are local to the example of the user and not part of the ``Akantu``
+library. To define a new local material, two files (``material_XXX.hh`` and
+``material_XXX.cc``) have to be provided where ``XXX`` is the name of the new
+material. The header file ``material_XXX.hh`` defines the interface of your
+custom material. Its implementation is provided in the ``material_XXX.cc``. The
+new law must inherit from the :cpp:class:`Material <akantu::Material>` class or
+any other existing material class. It is therefore necessary to include the
+interface of the parent material in the header file of your local material and
+indicate the inheritance in the declaration of the class::
+
+ auto & solver = model.getNonLinearSolver();
+ solver.set("max_iterations", 1);
+ solver.set("threshold", 1e-4);
+ solver.set("convergence_type", SolveConvergenceCriteria::_residual);
+
+ model.solveStep();
+
+
+ /* ---------------------------------------------------------------------- */
+ #include "material.hh"
+ /* ---------------------------------------------------------------------- */
+
+ #ifndef __AKANTU_MATERIAL_XXX_HH__
+ #define __AKANTU_MATERIAL_XXX_HH__
+
+ namespace akantu {
+
+ class MaterialXXX : public Material {
+
+ /// declare here the interface of your material
+
+ };
+
+In the header file the user also needs to declare all the members of the new
+material. These include the parameters that a read from the
+material input file, as well as any other material parameters that will be
+computed during the simulation and internal variables.
+
+
+In the following the example of adding a new damage material will be
+presented. In this case the parameters in the material will consist of the
+Young's modulus, the Poisson coefficient, the resistance to damage and the
+damage threshold. The material will then from these values compute its Lamé
+coefficients and its bulk modulus. Furthermore, the user has to add a new
+internal variable ``damage`` in order to store the amount of damage at each
+quadrature point in each step of the simulation. For this specific material the
+member declaration inside the class will look as follows::
+
+ class LocalMaterialDamage : public Material {
+
+ /// declare constructors/destructors here
+
+ /// declare methods and accessors here
+
+ /* -------------------------------------------------------------------- */
+ /* Class Members */
+ /* -------------------------------------------------------------------- */
+
+ AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(Damage, damage, Real);
+ private:
+
+ /// the young modulus
+ Real E;
+
+ /// Poisson coefficient
+ Real nu;
+
+ /// First Lame coefficient
+ Real lambda;
+
+ /// Second Lame coefficient (shear modulus)
+ Real mu;
+
+ /// resistance to damage
+ Real Yd;
+
+ /// damage threshold
+ Real Sd;
+
+ /// Bulk modulus
+ Real kpa;
+
+ /// damage internal variable
+ InternalField<Real> damage;
+
+ };
+
+In order to enable to print the material parameters at any point in
+the user's example file using the standard output stream by typing::
+
+ for (UInt m = 0; m < model.getNbMaterials(); ++m)
+ std::cout << model.getMaterial(m) << std::endl;
+
+the standard output stream operator has to be redefined. This should be done at the end of the header file::
+
+ class LocalMaterialDamage : public Material {
+
+ /// declare here the interace of your material
+
+ }:
+ /* ---------------------------------------------------------------------- */
+ /* inline functions */
+ /* ---------------------------------------------------------------------- */
+ /// standard output stream operator
+ inline std::ostream & operator <<(std::ostream & stream, const LocalMaterialDamage & _this)
+ {
+ _this.printself(stream);
+ return stream;
+ }
+
+However, the user still needs to register the material parameters that
+should be printed out. The registration is done during the call of the
+constructor. Like all definitions the implementation of the
+constructor has to be written in the ``material_XXX.cc``
+file. However, the declaration has to be provided in the
+``material_XXX.hh`` file::
+
+ class LocalMaterialDamage : public Material {
+ /* -------------------------------------------------------------------- */
+ /* Constructors/Destructors */
+ /* -------------------------------------------------------------------- */
+ public:
+
+ LocalMaterialDamage(SolidMechanicsModel & model, const ID & id = "");
+ };
+
+The user can now define the implementation of the constructor in the
+``material_XXX.cc`` file::
+
+ /* ---------------------------------------------------------------------- */
+ #include "local_material_damage.hh"
+ #include "solid_mechanics_model.hh"
+
+ namespace akantu {
+
+ /* ---------------------------------------------------------------------- */
+ LocalMaterialDamage::LocalMaterialDamage(SolidMechanicsModel & model,
+ const ID & id) :
+ Material(model, id),
+ damage("damage", *this) {
+ AKANTU_DEBUG_IN();
+
+ this->registerParam("E", E, 0., _pat_parsable, "Young's modulus");
+ this->registerParam("nu", nu, 0.5, _pat_parsable, "Poisson's ratio");
+ this->registerParam("lambda", lambda, _pat_readable, "First Lame coefficient");
+ this->registerParam("mu", mu, _pat_readable, "Second Lame coefficient");
+ this->registerParam("kapa", kpa, _pat_readable, "Bulk coefficient");
+ this->registerParam("Yd", Yd, 50., _pat_parsmod);
+ this->registerParam("Sd", Sd, 5000., _pat_parsmod);
+
+ damage.initialize(1);
+
+ AKANTU_DEBUG_OUT();
+ }
+
+During the intializer list the reference to the model and the material id are
+assigned and the constructor of the internal field is called. Inside the scope
+of the constructor the internal values have to be initialized and the
+parameters, that should be printed out, are registered with the function:
+``registerParam``::
+
+ void registerParam(name of the parameter (key in the material file),
+ member variable,
+ default value (optional parameter),
+ access permissions,
+ description);
+
+The available access permissions are as follows:
+- ``_pat_internal``: Parameter can only be output when the material is printed.
+- ``_pat_writable``: User can write into the parameter. The parameter is output when the material is printed.
+- ``_pat_readable``: User can read the parameter. The parameter is output when the material is printed.
+- ``_pat_modifiable``: Parameter is writable and readable.
+- ``_pat_parsable``: Parameter can be parsed, *i.e.* read from the input file.
+- ``_pat_parsmod``: Parameter is modifiable and parsable.
+
+In order to implement the new constitutive law the user needs to
+specify how the additional material parameters, that are not
+defined in the input material file, should be calculated. Furthermore,
+it has to be defined how stresses and the stable time step should be
+computed for the new local material. In the case of implicit
+simulations, in addition, the computation of the tangent stiffness needs
+to be defined. Therefore, the user needs to redefine the following
+functions of the parent material::
+
+ void initMaterial();
+
+ // for explicit and implicit simulations void
+ computeStress(ElementType el_type, GhostType ghost_type = _not_ghost);
+
+ // for implicit simulations
+ void computeTangentStiffness(const ElementType & el_type,
+ Array<Real> & tangent_matrix,
+ GhostType ghost_type = _not_ghost);
+
+ // for explicit and implicit simulations
+ Real getStableTimeStep(Real h, const Element & element);
+
+In the following a detailed description of these functions is provided:
+
+- ``initMaterial``: This method is called after the material file is fully read
+ and the elements corresponding to each material are assigned. Some of the
+ frequently used constant parameters are calculated in this method. For
+ example, the Lam\'{e} constants of elastic materials can be considered as such
+ parameters.
+
+- ``computeStress``: In this method, the stresses are computed based on the
+ constitutive law as a function of the strains of the quadrature points. For
+ example, the stresses for the elastic material are calculated based on the
+ following formula:
+
+ .. math::
+
+ \mat{\sigma } =\lambda\mathrm{tr}(\mat{\varepsilon})\mat{I}+2 \mu \mat{\varepsilon}
+
+ Therefore, this method contains a loop on all quadrature points assigned to
+ the material using the two macros:
+ ``MATERIAL_STRESS_QUADRATURE_POINT_LOOP_BEGIN`` and
+ ``MATERIAL_STRESS_QUADRATURE_POINT_LOOP_END``
+
+ .. code::
+
+ MATERIAL_STRESS_QUADRATURE_POINT_LOOP_BEGIN(element_type);
+
+ // sigma <- f(grad_u)
+
+ MATERIAL_STRESS_QUADRATURE_POINT_LOOP_END;
+
+ The strain vector in Akantu contains the values of :math:`\nabla \vec{u}`,
+ i.e. it is really the *displacement gradient*,
+
+- ``computeTangentStiffness``: This method is called when the tangent to the
+ stress-strain curve is desired (see Fig \ref {fig:smm:AL:K}). For example,
+ it is called in the implicit solver when the stiffness matrix for the
+ regular elements is assembled based on the following formula:
+
+ .. math::
+ \label{eqn:smm:constitutive_elasc} \mat{K }
+ =\int{\mat{B^T}\mat{D(\varepsilon)}\mat{B}}
+
+ Therefore, in this method, the ``tangent`` matrix (\mat{D}) is
+ computed for a given strain.
+
+ The ``tangent`` matrix is a :math:`4^{th}` order tensor which is stored as
+ a matrix in Voigt notation.
+
+ .. _fig:smm:AL:K:
+ .. figure:: figures/tangent.svg
+ :align: center
+ :width: 60%
+
+ Tangent to the stress-strain curve.
+
+..
+ \begin{figure}[!htb]
+ \begin{center}
+ \includegraphics[width=0.4\textwidth,keepaspectratio=true]{figures/tangent.pdf}
+ \caption{Tangent to the stress-strain curve.}
+ \label{fig:smm:AL:K}
+ \end{center}
+ \end{figure}
+
+- ``getCelerity``: The stability criterion of the explicit integration scheme
+ depend on the fastest wave celerity~\eqref{eqn:smm:explicit:stabletime}. This
+ celerity depend on the material, and therefore the value of this velocity
+ should be defined in this method for each new material. By default, the
+ fastest wave speed is the compressive wave whose celerity can be defined in ``getPushWaveSpeed``.
+
+Once the declaration and implementation of the new material has been
+completed, this material can be used in the user's example by including the header file::
+
+ #include "material_XXX.hh"
+
+For existing materials, as mentioned in Section~\ref{sect:smm:CL}, by
+default, the materials are initialized inside the method
+``initFull``. If a local material should be used instead, the
+initialization of the material has to be postponed until the local
+material is registered in the model. Therefore, the model is
+initialized with the boolean for skipping the material initialization
+equal to true::
+
+ /// model initialization
+ model.initFull(_analysis_method = _explicit_lumped_mass);
+
+Once the model has been initialized, the local material needs
+to be registered in the model::
+
+ model.registerNewCustomMaterials<XXX>("name_of_local_material");
+
+Only at this point the material can be initialized::
+
+ model.initMaterials();
+
+A full example for adding a new damage law can be found in
+``examples/new_material``.
+
+Adding a New Non-Local Constitutive Law
+```````````````````````````````````````
+
+In order to add a new non-local material we first have to add the local
+constitutive law in Akantu (see above). We can then add the non-local version
+of the constitutive law by adding the two files (``material_XXX_non_local.hh``
+and ``material_XXX_non_local.cc``) where ``XXX`` is the name of the
+corresponding local material. The new law must inherit from the two classes,
+non-local parent class, such as the ``MaterialNonLocal`` class, and from the
+local version of the constitutive law, *i.e.* ``MaterialXXX``. It is therefore
+necessary to include the interface of those classes in the header file of your
+custom material and indicate the inheritance in the declaration of the class::
+
+ /* ---------------------------------------------------------------------- */
+ #include "material_non_local.hh" // the non-local parent
+ #include "material_XXX.hh"
+ /* ---------------------------------------------------------------------- */
+
+ #ifndef __AKANTU_MATERIAL_XXX_HH__
+ #define __AKANTU_MATERIAL_XXX_HH__
+
+ namespace akantu {
+
+ class MaterialXXXNonLocal : public MaterialXXX,
+ public MaterialNonLocal {
+
+ /// declare here the interface of your material
+
+ };
+
+As members of the class we only need to add the internal fields to store the
+non-local quantities, which are obtained from the averaging process::
+
+ /* -------------------------------------------------------------------------- */
+ /* Class members */
+ /* -------------------------------------------------------------------------- */
+ protected:
+ InternalField<Real> grad_u_nl;
+
+The following four functions need to be implemented in the non-local material::
+
+ /// initialization of the material
+ void initMaterial();
+ /// loop over all element and invoke stress computation
+ virtual void computeNonLocalStresses(GhostType ghost_type);
+ /// compute stresses after local quantities have been averaged
+ virtual void computeNonLocalStress(ElementType el_type, GhostType ghost_type)
+ /// compute all local quantities
+ void computeStress(ElementType el_type, GhostType ghost_type);
+
+In the intialization of the non-local material we need to register the local
+quantity for the averaging process. In our example the internal field
+*grad_u_nl* is the non-local counterpart of the gradient of the displacement
+field (*grad_u_nl*)::
+
+ void MaterialXXXNonLocal::initMaterial() {
+ MaterialXXX::initMaterial();
+ MaterialNonLocal::initMaterial();
+ /// register the non-local variable in the manager
+ this->model->getNonLocalManager().registerNonLocalVariable(
+ this->grad_u.getName(),
+ this->grad_u_nl.getName(),
+ spatial_dimension * spatial_dimension);
+ }
+
+The function to register the non-local variable takes as parameters the name of
+the local internal field, the name of the non-local counterpart and the number
+of components of the field we want to average. In the *computeStress* we now
+need to compute all the quantities we want to average. We can then write a loop
+for the stress computation in the function *computeNonLocalStresses* and then
+provide the constitutive law on each integration point in the function
+*computeNonLocalStress*.
diff --git a/doc/dev-doc/manual/solidmechanicsmodel.rst b/doc/dev-doc/manual/solidmechanicsmodel.rst
new file mode 100644
index 000000000..374e930f3
--- /dev/null
+++ b/doc/dev-doc/manual/solidmechanicsmodel.rst
@@ -0,0 +1,880 @@
+.. _sect-smm:
+
+Solid Mechanics Model
+=====================
+
+The solid mechanics model is a specific implementation of the :cpp:class:`Model
+<akantu::Model>` interface dedicated to handle the equations of motion or
+equations of equilibrium. The model is created for a given mesh. It will create
+its own :cpp:class:`FEEngine <akantu::FEEngine>` object to compute the
+interpolation, gradient, integration and assembly operations. A
+:cpp:class:`SolidMechanicsModel <akantu::SolidMechanicsModel>` object can simply
+be created like this::
+
+ SolidMechanicsModel model(mesh);
+
+where ``mesh`` is the mesh for which the equations are to be
+solved. A second parameter called ``spatial_dimension`` can be
+added after ``mesh`` if the spatial dimension of the problem is
+different than that of the mesh.
+
+This model contains at least the following six ``Arrays``:
+
+:cpp:func:`blocked_dofs <akantu::SolidMechanicsModel::getBlockedDOFs>`:
+ contains a Boolean value for each degree of freedom specifying whether that
+ degree is blocked or not. A Dirichlet boundary condition can be prescribed
+ by setting the **blocked_dofs** value of a degree of freedom to
+ ``true``. A Neumann boundary condition can be applied by setting the
+ **blocked_dofs** value of a degree of freedom to ``false``. The
+ **displacement**, **velocity** and **acceleration** are
+ computed for all degrees of freedom for which the **blocked_dofs**
+ value is set to ``false``. For the remaining degrees of freedom, the imposed
+ values (zero by default after initialization) are kept.
+
+:cpp:func:`displacement <akantu::SolidMechanicsModel::getDisplacement>`:
+ contains the displacements of all degrees of freedom. It can be either a
+ computed displacement for free degrees of freedom or an imposed displacement
+ in case of blocked ones (:math:`\vec{u}` in the following).
+
+:cpp:func:`velocity <akantu::SolidMechanicsModel::getVelocity>`:
+ contains the velocities of all degrees of freedom. As **displacement**,
+ it contains computed or imposed velocities depending on the nature of the
+ degrees of freedom (:math:`\dot{\vec{u}}` in the following).
+
+:cpp:func:`acceleration <akantu::SolidMechanicsModel::getAcceleration>`:
+ contains the accelerations of all degrees of freedom. As **displacement**,
+ it contains computed or imposed accelerations depending on the nature of the
+ degrees of freedom (:math:`\ddot{\vec{u}}` in the following).
+
+:cpp:func:`external_force <akantu::SolidMechanicsModel::getExternalForce>`:
+ contains the external forces applied on the nodes
+ (:math:`\vec{f}_{\st{ext}}` in the following).
+
+:cpp:func:`internal_force <akantu::SolidMechanicsModel::getInternalForce>`:
+ contains the internal forces on the nodes (:math:`\vec{f}_{\mathrm{int}}` in
+ the following).
+
+
+Some examples to help to understand how to use this model will be
+presented in the next sections.
+
+
+Model Setup
+-----------
+
+
+Setting Initial Conditions
+``````````````````````````
+
+For a unique solution of the equations of motion, initial
+displacements and velocities for all degrees of freedom must be
+specified:
+
+.. math::
+ \vec{u}(t=0) & = \vec{u}_0\\
+ \dot{\vec u}(t=0) & = \vec{v}_0
+
+The solid mechanics model can be initialized as
+follows::
+
+ model.initFull()
+
+This function initializes the internal arrays and sets them to zero. Initial
+displacements and velocities that are not equal to zero can be prescribed by
+running a loop over the total number of nodes. Here, the initial displacement in
+:math:`x`-direction and the initial velocity in :math:`y`-direction for all
+nodes is set to :math:`0.1` and :math:`1`, respectively::
+
+ auto & disp = model.getDisplacement();
+ auto & velo = model.getVelocity();
+
+ for (UInt node = 0; node < mesh.getNbNodes(); ++node) {
+ disp(node, 0) = 0.1;
+ velo(node, 1) = 1.;
+ }
+
+.. _sect-smm-boundary:
+
+Setting Boundary Conditions
+```````````````````````````
+
+This section explains how to impose Dirichlet or Neumann boundary
+conditions. A Dirichlet boundary condition specifies the values that
+the displacement needs to take for every point :math:`x` at the boundary
+(:math:`\Gamma_u`) of the problem domain (:numref:`fig-smm-boundaries`):
+
+.. math::
+ \vec{u} = \bar{\vec u} \quad \forall \vec{x}\in \Gamma_{u}
+
+
+A Neumann boundary condition imposes the value of the gradient of the
+solution at the boundary :math:`\Gamma_t` of the problem domain
+(:numref:`fig-smm-boundaries`):
+
+.. math::
+ \vec{t} = \mat{\sigma} \vec{n} = \bar{\vec t} \quad
+ \forall \vec{x}\in \Gamma_{t}
+
+.. _fig-smm-boundaries:
+.. figure:: figures/problem_domain.svg
+ :align: center
+
+ Problem domain :math:`\Omega` with boundary in three dimensions. The
+ Dirchelet and the Neumann regions of the boundary are denoted with
+ :math:`\Gamma_u` and :math:`\Gamma_t`, respecitvely.
+
+Different ways of imposing these boundary conditions exist. A basic
+way is to loop over nodes or elements at the boundary and apply local
+values. A more advanced method consists of using the notion of the
+boundary of the mesh. In the following both ways are presented.
+
+Starting with the basic approach, as mentioned, the Dirichlet boundary
+conditions can be applied by looping over the nodes and assigning the
+required values. :numref:`fig-smm-dirichlet_bc` shows a beam with a
+fixed support on the left side. On the right end of the beam, a load
+is applied. At the fixed support, the displacement has a given
+value. For this example, the displacements in both the :math:`x` and the
+:math:`y`-direction are set to zero. Implementing this displacement boundary
+condition is similar to the implementation of initial displacement
+conditions described above. However, in order to impose a displacement
+boundary condition for all time steps, the corresponding nodes need to
+be marked as boundary nodes using the function ``blocked``. While,
+in order to impose a load on the right side, the nodes are not marked.
+The detail codes are shown as follows
+
+.. code-block:: c++
+
+ auto & blocked = model.getBlockedDOFs();
+ const auto & pos = mesh.getNodes();
+
+ UInt nb_nodes = mesh.getNbNodes();
+
+ for (UInt node = 0; node < nb_nodes; ++node) {
+ if(Math::are_float_equal(pos(node, _x), 0)) {
+ blocked(node, _x) = true; // block dof in x-direction
+ blocked(node, _y) = true; // block dof in y-direction
+ disp(node, _x) = 0.; // fixed displacement in x-direction
+ disp(node, _y) = 0.; // fixed displacement in y-direction
+ } else if (Math::are_float_equal(pos(node, _y), 0)) {
+ blocked(node, _x) = false; // unblock dof in x-direction
+ forces(node, _x) = 10.; // force in x-direction
+ }
+ }
+
+
+.. _fig-smm-dirichlet_bc:
+.. figure:: figures/dirichlet.svg
+ :align: center
+
+ Beam with fixed support and load.
+
+
+For the more advanced approach, one needs the notion of a boundary in
+the mesh. Therefore, the boundary should be created before boundary
+condition functors can be applied. Generally the boundary can be
+specified from the mesh file or the geometry. For the first case, the
+function ``createGroupsFromMeshData`` is called. This function
+can read any types of mesh data which are provided in the mesh
+file. If the mesh file is created with Gmsh, the function takes one
+input strings which is either ``tag_0``, ``tag_1`` or
+``physical_names``. The first two tags are assigned by Gmsh to
+each element which shows the physical group that they belong to. In
+Gmsh, it is also possible to consider strings for different groups of
+elements. These elements can be separated by giving a string
+``physical_names`` to the function
+``createGroupsFromMeshData``
+
+.. code-block:: c++
+
+ mesh.createGroupsFromMeshData<std::string>("physical_names").
+
+Boundary conditions support can also be created from the geometry by calling
+``createBoundaryGroupFromGeometry``. This function gathers all the elements on
+the boundary of the geometry.
+
+To apply the required boundary conditions, the function :cpp:func:`applyBC
+<akantu::BoundaryCondition::applyBC>` needs to be called on a
+:cpp:class:`SolidMechanicsModel <akantu::SolidMechanicsModel>`. This function
+gets a Dirichlet or Neumann functor and a string which specifies the desired
+boundary on which the boundary conditions is to be applied. The functors specify
+the type of conditions to apply. Three built-in functors for Dirichlet exist:
+:cpp:class:`FlagOnly <akantu::BC::Dirichlet::FlagOnly>`, :cpp:class:`FixedValue
+<akantu::BC::Dirichlet::FixedValue>` and :cpp:class:`IncrementValue
+<akantu::BC::Dirichlet::IncrementValue>`. The functor ``FlagOnly`` is used if a
+point is fixed in a given direction. Therefore, the input parameter to this
+functor is only the fixed direction. The ``FixedValue`` functor is used when a
+displacement value is applied in a fixed direction. The ``IncrementValue``
+applies an increment to the displacement in a given direction. The following
+code shows the utilization of three functors for the top, bottom and side
+surface of the mesh which were already defined in the Gmsh
+
+.. code-block:: c++
+
+ model.applyBC(BC::Dirichlet::FixedValue(13.0, _y), "Top");
+ model.applyBC(BC::Dirichlet::FlagOnly(_x), "Bottom");
+ model.applyBC(BC::Dirichlet::IncrementValue(13.0, _x), "Side");
+
+To apply a Neumann boundary condition, the applied traction or stress should be
+specified before. In case of specifying the traction on the surface, the functor
+:cpp:class:`FromTraction <akantu::BC::Neumann::FromTraction>` of Neumann
+boundary conditions is called. Otherwise, the functor :cpp:class:`FromStress
+<akantu::BC::Neumann::FromStress>` should be called which gets the stress tensor
+as an input parameter
+
+.. code-block:: c++
+
+ Vector<Real> surface_traction{0., 0., 1.};
+ auto surface_stress(3, 3) = Matrix<Real>::eye(3);
+
+ model.applyBC(BC::Neumann::FromTraction(surface_traction), "Bottom");
+ model.applyBC(BC::Neumann::FromStress(surface_stress), "Top");
+
+If the boundary conditions need to be removed during the simulation, a
+functor is called from the Neumann boundary condition to free those
+boundary conditions from the desired boundary
+
+.. code-block:: c++
+
+ model.applyBC(BC::Neumann::FreeBoundary(), "Side");
+
+User specified functors can also be implemented. A full example for
+setting both initial and boundary conditions can be found in
+``examples/boundary_conditions.cc``. The problem solved
+in this example is shown in :numref:`fig-smm-bc_and_ic`. It consists
+of a plate that is fixed with movable supports on the left and bottom
+side. On the right side, a traction, which increases linearly with the
+number of time steps, is applied. The initial displacement and
+velocity in :math:`x`-direction at all free nodes is zero and two
+respectively.
+
+.. _fig-smm-bc_and_ic:
+.. figure:: figures/bc_and_ic_example.svg
+ :align: center
+ :width: 75%
+
+ Plate on movable supports.
+
+..
+ \begin{figure}[!htb]
+ \centering
+ \includegraphics[scale=0.8]{figures/bc_and_ic_example}
+ \caption{Plate on movable supports.\label{fig-smm-bc_and_ic}}
+ \end{figure}
+
+As it is mentioned in Section \ref{sect:common:groups}, node and
+element groups can be used to assign the boundary conditions. A
+generic example is given below with a Dirichlet boundary condition::
+
+ // create a node group
+ NodeGroup & node_group = mesh.createNodeGroup("nodes_fix");
+
+ /* fill the node group with the nodes you want */
+
+ // create an element group using the existing node group
+ mesh.createElementGroupFromNodeGroup("el_fix",
+ "nodes_fix",
+ spatial_dimension-1);
+
+ // boundary condition can be applied using the element group name
+ model.applyBC(BC::Dirichlet::FixedValue(0.0, _x), "el_fix");
+
+Material Selector
+`````````````````
+
+If the user wants to assign different materials to different
+finite elements groups in ``Akantu``, a material selector has to be
+used. By default, ``Akantu`` assigns the first valid material in the
+material file to all elements present in the model (regular continuum
+materials are assigned to the regular elements and cohesive materials
+are assigned to cohesive elements or element facets).
+
+To assign different materials to specific elements, mesh data information such
+as tag information or specified physical names can be used.
+:cpp:class:`MeshDataMaterialSelector <akantu::MeshDataMaterialSelector>` class
+uses this information to assign different materials. With the proper physical
+name or tag name and index, different materials can be assigned as demonstrated
+in the examples below::
+
+ auto mat_selector =
+ std::make_shared<MeshDataMaterialSelector<std::string>>("physical_names",
+ model);
+ model.setMaterialSelector(mat_selector);
+
+In this example the physical names specified in a GMSH geometry file will by
+used to match the material names in the input file.
+
+Another example would be to use the first (``tag_0``) or the second
+(``tag_1``) tag associated to each elements in the mesh::
+
+ auto mat_selector = std::make_shared<MeshDataMaterialSelector<UInt>>(
+ "tag_1", model, first_index);
+ model.setMaterialSelector(*mat_selector);
+
+where ``first_index`` (default is 1) is the value of ``tag_1`` that will
+be associated to the first material in the material input file. The following
+values of the tag will be associated with the following materials.
+
+There are four different material selectors pre-defined in ``Akantu``.
+:cpp:class:`MaterialSelector <akantu::MaterialSelector>` and
+:cpp:class:`DefaultMaterialSelector <akantu::DefaultMaterialSelector>` is used
+to assign a material to regular elements by default. For the regular elements,
+as in the example above, :cpp:class:`MeshDataMaterialSelector
+<akantu::MeshDataMaterialSelector>` can be used to assign different materials to
+different elements.
+
+Apart from the ``Akantu``'s default material selectors, users can always
+develop their own classes in the main code to tackle various
+multi-material assignment situations.
+
+For cohesive material, ``Akantu`` has a pre-defined material selector to assign
+the first cohesive material by default to the cohesive elements which is called
+:cpp:class:`DefaultMaterialCohesiveSelector
+<akantu::DefaultMaterialCohesiveSelector>` and it inherits its properties from
+:cpp:class:`DefaultMaterialSelector <akantu::DefaultMaterialSelector>`. Multiple
+cohesive materials can be assigned using mesh data information (for more
+details, see :ref:`sect-smm-intrinsic-insertion`).
+
+
+Insertion of Cohesive Elements
+``````````````````````````````
+Cohesive elements are currently compatible only with static simulation
+and dynamic simulation with an explicit time integration scheme (see
+section :ref:`ssect-smm-expl-time-integration`). They do not have to be
+inserted when the mesh is generated (intrinsic) but can be added
+during the simulation (extrinsic). At any time during the simulation,
+it is possible to access the following energies with the relative
+function:
+
+.. code-block:: c++
+
+ Real Ed = model.getEnergy("dissipated");
+ Real Er = model.getEnergy("reversible");
+ Real Ec = model.getEnergy("contact");
+
+A new model have to be call in a very similar way that the solid
+mechanics model:
+
+.. code-block:: c++
+
+ SolidMechanicsModelCohesive model(mesh);
+ model.initFull(_analysis_method = _explicit_lumped_mass,
+ _is_extrinsic = true);
+
+Cohesive element insertion can be either realized at the beginning of
+the simulation or it can be carried out dynamically during the
+simulation. The first approach is called ``intrinsic``, the second
+one ``extrinsic``. When an element is present from the beginning, a
+bi-linear or exponential cohesive law should be used instead of a
+linear one. A bi-linear law works exactly like a linear one except for
+an additional parameter :math:`\delta_0` separating an initial linear
+elastic part from the linear irreversible one. For additional details
+concerning cohesive laws see Section~\ref{sec:cohesive-laws}.
+
+.. _fig-smm-coh-insertion:
+.. figure:: figures/insertion.svg
+ :align: center
+
+ Insertion of a cohesive element.
+
+Extrinsic cohesive elements are dynamically inserted between two
+standard elements when
+
+.. math::
+ \sigma_\mathrm{eff} > \sigma_\mathrm{c} \quad\text {with} \quad \sigma_\mathrm{eff} = \sqrt{\sigma_\mathrm{n} ^ 2 + \frac{\tau ^ 2} {\beta ^ 2 }}
+
+in which :math:`\sigma_\mathrm { n }
+` is the tensile normal traction and $\tau$ the resulting tangential one( :numref:`fig-smm-coh-insertion`).
+
+Extrinsic approach
+''''''''''''''''''
+
+During the simulation, stress has to be checked along each facet in order to
+insert cohesive elements where the stress criterion is reached. This check is
+performed by calling the method :cpp:func:`checkCohesiveStress
+<akantu::SolidMechanicsModelCohesive::checkCohesiveStress>`, as example before
+each step resolution:
+
+.. code-block:: c++
+
+ model.checkCohesiveStress();
+ model.solveStep();
+
+The area where stresses are checked and cohesive elements inserted can be
+limited using the method :cpp:func:`setLimit
+<akantu::CohesiveInserter::setLimit>` on the :cpp:class:`CohesiveInserter
+<akantu::CohesiveInserter>` during initialization. As example, to limit
+insertion in the range :math:`[-1.5, 1.5]` in the $x$ direction:
+
+.. code-block:: c++
+
+ auto & inserter = model.getElementInserter();
+ inserter.setLimit(_x, -1.5, 1.5);
+
+Additional restrictions with respect to :math:`_y` and :math:`_z` directions can
+be added as well.
+
+.. _sect-smm-intrinsic-insertion:
+
+Intrinsic approach
+''''''''''''''''''
+
+Intrinsic cohesive elements are inserted in the mesh with the method
+:cpp:func:`initFull <akantu::SolidMechanicsModelCohesive::initFull>`.
+Similarly, the range of insertion can be limited with :cpp:func:`setLimit
+<akantu::CohesiveInserter::setLimit>` before the :cpp:func:`initFull
+<akantu::SolidMechanicsModelCohesive::initFull>` call.
+
+In both cases extrinsic and intrinsic the insertion can be restricted to
+surfaces or element groups. To do so the list of groups should be specified in
+the input file.
+
+.. code-block::
+
+ model solid_mechanics_model_cohesive [
+ cohesive_inserter [
+ cohesive_surfaces = [surface1, surface2, ...]
+ cohesive_zones = [group1, group2, ...]
+ ]
+
+ material cohesive_linear [
+ name = insertion
+ beta = 1
+ G_c = 10
+ sigma_c = 1e6
+ ]
+ ]
+
+
+
+Static Analysis
+---------------
+
+The :cpp:class:`SolidMechanicsModel <akantu::SolidMechanicsModel>` class can
+handle different analysis methods, the first one being presented is the static
+case. In this case, the equation to solve is
+
+.. math::
+ \mat{K} \vec{u} = \vec{f}_{\mathrm{ext}}
+ :label: eqn-smm-static
+
+where :math:`\mat{K}` is the global stiffness matrix, :math:`\vec{u}` the
+displacement vector and :math:`\vec{f}_{\st{ext}}` the vector of external
+forces applied to the system.
+
+To solve such a problem, the static solver of the
+:cpp:class:`SolidMechanicsModel <akantu::SolidMechanicsModel>` object is used.
+First, a model has to be created and initialized. To create the model, a mesh
+(which can be read from a file) is needed, as explained in
+Section~\ref{sect:common:mesh}. Once an instance of a
+:cpp:class:`SolidMechanicsModel <akantu::SolidMechanicsModel>` is obtained, the
+easiest way to initialize it is to use the :cpp:func:`initFull
+<akantu::SolidMechanicsModel::initFull>` method by giving the
+:cpp:class:`SolidMechanicsModelOptions <akantu::SolidMechanicsModelOptions>`.
+These options specify the type of analysis to be performed and whether the
+materials should be initialized with :cpp:func:`initMaterials
+<akantu::SolidMechanicsModel::initMaterials>` or not
+
+.. code-block:: c++
+
+ SolidMechanicsModel model(mesh);
+ model.initFull(_analysis_method = _static);
+
+Here, a static analysis is chosen by passing the argument
+:cpp:enumerator:`_static <akantu::_static>` to the method. By default, the
+Boolean for no initialization of the materials is set to false, so that they are
+initialized during the ``initFull``. The method ``initFull`` also initializes
+all appropriate vectors to zero. Once the model is created and initialized, the
+boundary conditions can be set as explained in Section :ref:`sect-smm-boundary`.
+Boundary conditions will prescribe the external forces for some free degrees of
+freedom :math:`\vec{f}_{\st{ext}}` and displacements for some others. At this
+point of the analysis, the function
+:cpp:func:`solveStep <akantu::SolidMechanicsModel::solveStep>` can be called
+
+.. code-block:: c++
+
+ auto & solver = model.getNonLinearSolver();
+ solver.set("max_iterations", 1);
+ solver.set("threshold", 1e-4);
+ solver.set("convergence_type", SolveConvergenceCriteria::_residual);
+
+ model.solveStep();
+
+This function is templated by the solving method and the convergence criterion
+and takes two arguments: the tolerance and the maximum number of iterations (100
+by default), which are :math:`10^{-4}` and :math:`1` for this example. The
+modified Newton-Raphson method is chosen to solve the system. In this method,
+the equilibrium equation (:eq:`eqn-smm-static`) is modified in order to apply a
+Newton-Raphson convergence algorithm:
+
+.. math::
+ \mat{K}^{i+1}\delta\vec{u}^{i+1} &= \vec{r} \\
+ &= \vec{f}_{\st{ext}} -\vec{f}_{\st{int}}\\
+ &= \vec{f}_{\st{ext}} - \mat{K}^{i} \vec{u}^{i}\\
+ \vec{u}^{i+1} &= \vec{u}^{i} + \delta\vec{u}^{i+1}~,
+
+where :math:`\delta\vec{u}` is the increment of displacement to be added from
+one iteration to the other, and :math:`i` is the Newton-Raphson iteration
+counter. By invoking the ``solveStep`` method in the first step, the global
+stiffness matrix :math:`\mat{K}` from (:eq:`eqn-smm-static`) is automatically
+assembled. A Newton-Raphson iteration is subsequently started, :math:`\mat{K}`
+is updated according to the displacement computed at the previous iteration and
+one loops until the forces are balanced
+(:cpp:enumerator:`SolveConvergenceCriteria::_residual
+<akantu::SolveConvergenceCriteria::_residual>`), i.e. :math:`||\vec{r}|| <`
+:cpp:member:`threshold
+<akantu::NonLinearSolverNewtonRaphson::convergence_criteria>`. One can also
+iterate until the increment of displacement is zero
+(:cpp:enumerator:`SolveConvergenceCriteria::_solution
+<akantu::SolveConvergenceCriteria::_solution>`) which also means that the
+equilibrium is found. For a linear elastic problem, the solution is obtained in
+one iteration and therefore the maximum number of iterations can be set to one.
+But for a non-linear case, one needs to iterate as long as the norm of the
+residual exceeds the tolerance threshold and therefore the maximum number of
+iterations has to be higher, e.g. :math:`100`
+
+.. code-block:: c++
+
+ solver.set("max_iterations", 100);
+ model.solveStep();
+
+At the end of the analysis, the final solution is stored in the
+**displacement** vector. A full example of how to solve a static
+problem is presented in the code ``examples/static/static.cc``.
+This example is composed of a 2D plate of steel, blocked with rollers
+on the left and bottom sides as shown in :numref:`fig-smm-static`.
+The nodes from the right side of the sample are displaced by :math:`0.01\%`
+of the length of the plate.
+
+.. _fig-smm-static:
+.. figure:: figures/static.svg
+ :align: center
+ :width: 75%
+
+ Numerical setup.
+
+The results of this analysis is depicted in
+:numref:`fig-smm-implicit:static_solution`.
+
+.. _fig-smm-implicit:static_solution:
+.. figure:: figures/static_analysis.png
+ :align: center
+ :width: 75%
+
+ Solution of the static analysis. Left: the initial condition, right:
+ the solution (deformation magnified 50 times).
+
+
+Dynamic Methods
+---------------
+
+Different ways to solve the equations of motion are implemented in the
+solid mechanics model. The complete equations that should be solved
+are:
+
+.. math:: \mat{M}\ddot{\vec{u}} + \mat{C}\dot{\vec{u}} + \mat{K}\vec{u} = \vec{f}_{\mathrm{ext}}
+ :label: eqn-equation-motion
+
+where :math:`\mat{M}`, :math:`\mat{C}` and :math:`\mat{K}` are the mass,
+damping and stiffness matrices, respectively.
+
+In the previous section, it has already been discussed how to solve this
+equation in the static case, where :math:`\ddot{\vec{u}} = \dot{\vec{u}} = 0`.
+Here the method to solve this equation in the general case will be presented.
+For this purpose, a time discretization has to be specified. The most common
+discretization method in solid mechanics is the Newmark-:math:`\beta` method,
+which is also the default in ``Akantu``.
+
+For the Newmark-:math:`\beta` method, (:eq:`eqn-equation-motion`) becomes a
+system of three equations (see :cite:`curnier92a,hughes-83a` for more details):
+
+.. math::
+ \begin{eqnarray}
+ \mat{M} \ddot{\vec{u}}_{n+1} + \mat{C}\dot{\vec{u}}_{n+1} + \mat{K} \vec{u}_{n+1} &={\vec{f}_{\st{ext}}}_{\, n+1}\\
+ \vec{u}_{n+1} &= \vec{u}_{n}
+ + \left(1 - \alpha\right) \Delta t \dot{\vec{u}}_{n}
+ + \alpha \Delta t \dot{\vec{u}}_{n+1}
+ + \left(\frac{1}{2} - \alpha\right) \Delta t^2 \ddot{\vec{u}}_{n}\\
+ \dot{\vec{u}}_{n+1} &= \dot{\vec{u}}_{n}
+ + \left(1 - \beta\right) \Delta t \ddot{\vec{u}}_{n}
+ + \beta \Delta t \ddot{\vec{u}}_{n+1}
+ \end{eqnarray}
+ :label: eqn-equation-motion-discret
+
+In these new equations, :math:`\ddot{\vec{u}}_{n}`, :math:`\dot{\vec{u}}_{n}`
+and :math:`\vec{u}_{n}` are the approximations of :math:`\ddot{\vec{u}}(t_n)`,
+:math:`\dot{\vec{u}}(t_n)` and :math:`\vec{u}(t_n)`.
+Equation~(:eq:`eqn-equation-motion-discret`) is the equation of motion
+discretized in space (finite-element discretization), and the equations above
+are discretized in both space and time (Newmark discretization). The
+:math:`\alpha` and :math:`\beta` parameters determine the stability and the
+accuracy of the algorithm. Classical values for :math:`\alpha` and :math:`\beta`
+are usually :math:`\beta = 1/2` for no numerical damping and :math:`0 < \alpha <
+1/2`.
+
+.. csv-table::
+ :header: ":math:`\\alpha`", "Method (:math:`\\beta = 1/2`)", "Type"
+
+ ":math:`0`", "central difference", "explicit"
+ ":math:`\frac{1}{6}`", "Fox-Goodwin(royal road)", "implicit"
+ ":math:`\frac{1}{3}`", "Linear acceleration", "implicit"
+ ":math:`\frac{1}{2}`", "Average acceleration (trapeziodal rule)", "implicit"
+
+
+The solution of this system of equations,
+(:eq:`eqn-equation-motion-discret`) is
+split into a predictor and a corrector system of equations. Moreover,
+in the case of a non-linear equations, an iterative algorithm such as
+the Newton-Raphson method is applied. The system of equations can be
+written as:
+
+- *Predictor*:
+
+ .. math:: \vec{u}_{n+1}^{0} &= \vec{u}_{n} + \Delta t \dot{\vec{u}}_{n} + \frac{\Delta t^2}{2} \ddot{\vec{u}}_{n} \\
+ \dot{\vec{u}}_{n+1}^{0} &= \dot{\vec{u}}_{n} + \Delta t \ddot{\vec{u}}_{n} \\
+ \ddot{\vec{u}}_{n+1}^{0} &= \ddot{\vec{u}}_{n}
+
+- *Solve*:
+
+ .. math:: \left(c \mat{M} + d \mat{C} + e \mat{K}_{n+1}^i\right)
+ \vec{w} &= {\vec{f}_{\st{ext}}}_{\,n+1} - {\vec{f}_{\st{int}}}_{\,n+1}^i -
+ \mat{C} \dot{\vec{u}}_{n+1}^i - \mat{M} \ddot{\vec{u}}_{n+1}^i\\
+ &= \vec{r}_{n+1}^i
+
+- *Corrector*:
+
+ .. math:: \ddot{\vec{u}}_{n+1}^{i+1} &= \ddot{\vec{u}}_{n+1}^{i} +c \vec{w} \\
+ \dot{\vec{u}}_{n+1}^{i+1} &= \dot{\vec{u}}_{n+1}^{i} + d\vec{w} \\
+ \vec{u}_{n+1}^{i+1} &= \vec{u}_{n+1}^{i} + e \vec{w}
+
+where :math:`i` is the Newton-Raphson iteration counter and :math:`c`, :math:`d` and :math:`e`
+are parameters depending on the method used to solve the equations
+
+
+.. csv-table::
+ :header: "", ":math:`\\vec{w}`", ":math:`e`", ":math:`d`", ":math:`c`"
+
+ "in acceleration", ":math:`\delta\ddot{\vec{u}}`", ":math:`\alpha\beta\Delta t^2`", ":math:`\beta\Delta t`", ":math:`1`"
+ "in velocity", ":math:`\delta\dot{\vec{u}}`", ":math:`\alpha\Delta t`", ":math:`1`", ":math:`\frac{1}{\beta\Delta t}`"
+ "in displacement", ":math:`\delta\vec{u}`", ":math:`1`", ":math:`\frac{1}{\alpha\Delta t}`", ":math:`\frac{1}{\alpha\beta \Delta t^2}`"
+
+
+.. note:: If you want to use the implicit solver ``Akantu`` should be compiled at
+ least with one sparse matrix solver such as `Mumps
+ <http://mumps.enseeiht.fr/>`_ :cite:`mumps`.
+
+
+Implicit Time Integration
+`````````````````````````
+
+To solve a problem with an implicit time integration scheme, first a
+:cpp:class:`SolidMechanicsModel <akantu::SolidMechanicsModel>` object has to be
+created and initialized. Then the initial and boundary conditions have to be
+set. Everything is similar to the example in the static case
+(Section~\ref{sect:smm:static}), however, in this case the implicit dynamic
+scheme is selected at the initialization of the model::
+
+ SolidMechanicsModel model(mesh);
+ model.initFull(_analysis_method = _implicit_dynamic);
+
+Because a dynamic simulation is conducted, an integration time step
+:math:`\Delta t` has to be specified. In the case of implicit simulations,
+``Akantu`` implements a trapezoidal rule by default. That is to say
+:math:`\alpha = 1/2` and :math:`\beta = 1/2` which is unconditionally
+stable. Therefore the value of the time step can be chosen arbitrarily
+within reason::
+
+ model.setTimeStep(time_step);
+
+Since the system has to be solved for a given amount of time steps, the
+method ``solveStep()``, (which has already been used in the static
+example in Section~\ref{sect:smm:static}), is called inside a time
+loop::
+
+ /// time loop
+ Real time = 0.;
+
+ auto & solver = model.getNonLinearSolver();
+ solver.set("max_iterations", 100);
+ solver.set("threshold", 1e-12);
+ solver.set("convergence_type", SolveConvergenceCriteria::_solution);
+
+ for (UInt s = 1; time <max_time; ++s, time += time_step) {
+ model.solveStep();
+ }
+
+An example of solid mechanics with an implicit time integration scheme is
+presented in ``examples/implicit/implicit_dynamic.cc``. This example consists of
+a 3D beam of
+:math:`10\mathrm{m}\times1\mathrm{m}\times1\mathrm{m}` blocked
+on one side and is on a roller on the other side. A constant force of
+:math:`5\mathrm{kN}` is applied in its middle.
+:numref:`fig-smm-implicit-dynamic` presents the geometry of this case. The
+material used is a fictitious linear elastic material with a density of
+:math:`1000 \mathrm{kg/m}^3`, a Young's Modulus of
+:math:`120 \mathrm{MPa}` and Poisson's ratio of :math:`0.3`. These values
+were chosen to simplify the analytical solution.
+
+An approximation of the dynamic response of the middle point of the
+beam is given by:
+
+.. math::
+
+ u\left(\frac{L}{2}, t\right)
+ = \frac{1}{\pi^4} \left(1 - cos\left(\pi^2 t\right) +
+ \frac{1}{81}\left(1 - cos\left(3^2 \pi^2 t\right)\right) +
+ \frac{1}{625}\left(1 - cos\left(5^2 \pi^2 t\right)\right)\right)
+
+.. _fig-smm-implicit-dynamic:
+.. figure:: figures/implicit_dynamic.svg
+ :align: center
+ :width: 75%
+
+ Numerical setup.
+
+..
+ \begin{figure}[!htb]
+ \centering
+ \includegraphics[scale=.6]{figures/implicit_dynamic}
+ \caption{Numerical setup}
+ \label{fig-smm-implicit:dynamic}
+ \end{figure}
+
+:numref:`fig-smm-implicit-dynamic_solution` presents the deformed
+beam at 3 different times during the simulation: time steps 0, 1000 and
+2000.
+
+.. _fig-smm-implicit-dynamic_solution:
+.. figure:: figures/dynamic_analysis.png
+ :align: center
+ :width: 60%
+
+ Deformed beam at three different times (displacement :math:`\times
+ 10`).
+
+.. _ssect-smm-expl-time-integration:
+
+Explicit Time Integration
+`````````````````````````
+
+The explicit dynamic time integration scheme is based on the
+Newmark-:math:`\beta` scheme with :math:`\alpha=0` (see equations
+\ref{eqn:equation-motion-discret}-\ref{eqn:finite-difference-2}). In
+``Akantu``, :math:`\beta` is defaults to :math:`\beta=1/2`, see section
+\ref{sect:smm:Dynamic_methods}.
+
+The initialization of the simulation is similar to the static and implicit
+dynamic version. The model is created from the :cpp:class:`SolidMechanicsModel
+<akantu::SolidMechanicsModel>` class. In the initialization, the explicit scheme
+is selected using the ``_explicit_lumped_mass`` constant::
+
+ SolidMechanicsModel model(mesh);
+ model.initFull(_analysis_method = _explicit_lumped_mass);
+
+
+.. note::
+ Writing ``model.initFull()`` or ``model.initFull();`` is
+ equivalent to use the ``_explicit_lumped_mass`` keyword, as this
+ is the default case.
+
+The explicit time integration scheme implemented in ``Akantu`` uses a
+lumped mass matrix :math:`\mat{M}` (reducing the computational cost). This
+matrix is assembled by distributing the mass of each element onto its
+nodes. The resulting :math:`\mat{M}` is therefore a diagonal matrix stored
+in the **mass** vector of the model.
+
+The explicit integration scheme is conditionally stable. The time step
+has to be smaller than the stable time step which is obtained in
+Akantu as follows::
+
+ critical_time_step = model.getStableTimeStep();
+
+The stable time step corresponds to the time the fastest wave (the compressive
+wave) needs to travel the characteristic length of the mesh:
+
+.. math::
+ \Delta t_{\st{crit}} = \frac{\Delta x}{c}
+
+where :math:`\Delta x` is a characteristic length (\eg the inradius in the case
+of linear triangle element) and :math:`c` is the celerity of the fastest wave in
+the material. It is generally the compressive wave of celerity :math:`c =
+\sqrt{\frac{2 \mu + \lambda}{\rho}}`, :math:`\mu` and :math:`\lambda` are the
+first and second Lame's coefficients and :math:`\rho` is the density. However,
+it is recommended to impose a time step that is smaller than the stable time
+step, for instance, by multiplying the stable time step by a safety factor
+smaller than one::
+
+ const Real safety_time_factor = 0.8;
+ Real applied_time_step = critical_time_step * safety_time_factor;
+ model.setTimeStep(applied_time_step);
+
+The initial displacement and velocity fields are, by default, equal to zero if
+not given specifically by the user (see \ref{sect:smm:initial_condition}).
+
+Like in implicit dynamics, a time loop is used in which the
+displacement, velocity and acceleration fields are updated at each
+time step. The values of these fields are obtained from the
+Newmark:math:`-\beta` equations with :math:`\beta=1/2` and :math:`\alpha=0`. In ``Akantu``
+these computations at each time step are invoked by calling the
+function ``solveStep``::
+
+ for (UInt s = 1; (s-1)*applied_time_step < total_time; ++s) {
+ model.solveStep();
+ }
+
+The method ``solveStep`` wraps the four following functions:
+
+- ``model.explicitPred()`` allows to compute the displacement
+ field at :math:`t+1` and a part of the velocity field at :math:`t+1`, denoted by
+ :math:`\vec{\dot{u}^{\st{p}}}_{n+1}`, which will be used later in the method
+ ``model.explicitCorr()``. The equations are:
+
+ .. math::
+ \vec{u}_{n+1} &= \vec{u}_{n} + \Delta t
+ \vec{\dot{u}}_{n} + \frac{\Delta t^2}{2} \vec{\ddot{u}}_{n}\\
+ \vec{\dot{u}^{\st{p}}}_{n+1} &= \vec{\dot{u}}_{n} + \Delta t
+ \vec{\ddot{u}}_{n}
+
+- ``model.updateResidual()`` and ``model.updateAcceleration()`` compute the acceleration increment
+ :math:`\delta \vec{\ddot{u}}`:
+
+ .. math::
+ \left(\mat{M} + \frac{1}{2} \Delta t \mat{C}\right)
+ \delta \vec{\ddot{u}} = \vec{f_{\st{ext}}} - \vec{f}_{\st{int}\, n+1}
+ - \mat{C} \vec{\dot{u}^{\st{p}}}_{n+1} - \mat{M} \vec{\ddot{u}}_{n}
+
+ The internal force :math:`\vec{f}_{\st{int}\, n+1}` is computed from the
+ displacement :math:`\vec{u}_{n+1}` based on the constitutive law.
+
+- ``model.explicitCorr()`` computes the velocity and
+ acceleration fields at :math:`t+1`:
+
+ .. math::
+ \vec{\dot{u}}_{n+1} &= \vec{\dot{u}^{\st{p}}}_{n+1} + \frac{\Delta t}{2}
+ \delta \vec{\ddot{u}} \\ \vec{\ddot{u}}_{n+1} &=
+ \vec{\ddot{u}}_{n} + \delta \vec{\ddot{u}}
+
+The use of an explicit time integration scheme is illustrated by the example:
+``examples/explicit/explicit_dynamic.cc``. This example models the propagation
+of a wave in a steel beam. The beam and the applied displacement in the
+:math:`x` direction are shown in :numref:`fig-smm-explicit`.
+
+
+.. _fig-smm-explicit:
+.. figure:: figures/explicit.svg
+ :align: center
+ :width: 90%
+
+ Numerical setup.
+
+
+The length and height of the beam are :math:`L={10}\textrm{m}` and :math:`h =
+{1}\textrm{m}`, respectively. The material is linear elastic, homogeneous and
+isotropic (density: :math:`7800\mathrm{kg/m}^3`, Young's modulus:
+:math:`210\mathrm{GPa}` and Poisson's ratio: :math:`0.3`). The imposed
+displacement follow a Gaussian function with a maximum amplitude of :math:`A =
+{0.01}\textrm{m}`. The potential, kinetic and total energies are computed. The
+safety factor is equal to :math:`0.8`.
+
+.. include:: ./constitutive-laws.rst
+
+.. include:: ./new-constitutive-laws.rst
diff --git a/doc/dev-doc/manual/structuralmechanicsmodel.rst b/doc/dev-doc/manual/structuralmechanicsmodel.rst
new file mode 100644
index 000000000..92a8c3f40
--- /dev/null
+++ b/doc/dev-doc/manual/structuralmechanicsmodel.rst
@@ -0,0 +1,214 @@
+Structural Mechanics Model
+==========================
+
+Static structural mechanics problems can be handled using the class
+:cpp:class:`StructuralMechanicsModel <akantu::StructuralMechanicsModel>`. So
+far, ``Akantu`` provides 2D and 3D Bernoulli beam elements :cite:`frey2009`.
+This model is instantiated for a given :cpp:class:`Mesh <akantu::Mesh>`, as for
+the :cpp:class:`SolidMechanicsModel <akantu::SolidMechanicsModel>`. The model
+will create its own :cpp:class:`FEEngine <akantu::FEEngine>` object to compute
+the interpolation, gradient, integration and assembly operations. The
+:cpp:class:`StructuralMechanicsModel <akantu::StructuralMechanicsModel>`
+constructor is called in the following way:
+
+.. code-block:: c++
+
+ StructuralMechanicsModel model(mesh, spatial_dimension);
+
+where ``mesh`` is a :cpp:class:`Mesh <akantu::Mesh>` object defining the structure for
+which the equations of statics are to be solved, and
+``spatial_dimension`` is the dimensionality of the problem. If
+``spatial_dimension`` is omitted, the problem is assumed to have
+the same dimensionality as the one specified by the mesh.
+
+.. warning::
+ Dynamic computations are not supported to date.
+
+.. note::
+ Structural meshes are created and loaded
+ with ``_miot_gmsh_struct`` instead of ``_miot_gmsh`` (cf. :ref:`loading mesh`)
+
+ .. code-block:: c++
+
+ Mesh mesh;
+ mesh.read("structural_mesh.msh", _miot_gmsh_struct);
+
+
+This model contains at least the following :cpp:class:`Arrays <akantu::Arrays>`:
+
+- **blocked_dofs** contains a Boolean value for each degree of
+ freedom specifying whether that degree is blocked or not. A
+ Dirichlet boundary condition can be prescribed by setting the
+ **blocked_dofs** value of a degree of freedom to
+ ``true``. The **displacement** is computed for all degrees
+ of freedom for which the **blocked_dofs** value is set to
+ ``false``. For the remaining degrees of freedom, the imposed
+ values (zero by default after initialization) are kept.
+
+- **displacement_rotation** contains the generalized displacements (*i.e.* displacements and rotations) of all degrees of freedom. It can be either a computed displacement for free degrees of freedom or an imposed displacement in case of blocked ones (:math:`\vec{u}` in the following).
+
+- **external_force** contains the generalized external forces (forces and moments) applied to the nodes (:math:`\vec{f_{\st{ext}}}` in the following).
+
+- **internal_force** contains the generalized internal forces (forces and moments) applied to the nodes (:math:`\vec{f_{\st{int}}}` in the following).
+
+An example to help understand how to use this model will be presented in the
+next section.
+
+.. _sec:structMechMod:setup:
+
+Model Setup
+-----------
+
+Initialization
+``````````````
+
+The easiest way to initialize the structural mechanics model is:
+
+.. code-block:: c++
+
+ model.initFull();
+
+The method :cpp:class:`initFull <akantu::StructuralMechanicsModel::initFull>` computes the shape
+functions, initializes the internal vectors mentioned above and allocates the
+memory for the stiffness matrix, unlike the solid mechanics model, its default
+argument is ``_static``.
+
+Material properties are defined using the :cpp:class:`StructuralMaterial
+<akantu::StructuralMaterial>` structure described in
+:numref:`tab-structmechmod-strucmaterial`. Such a definition could, for
+instance, look like
+
+.. code-block:: c++
+
+ StructuralMaterial mat1;
+ mat.E=3e10;
+ mat.I=0.0025;
+ mat.A=0.01;
+
+.. _tab-structmechmod-strucmaterial:
+
+.. table:: Material properties for structural elements defined in the class :cpp:class:`StructuralMaterial <akantu::StructuralMaterial>`.
+ :align: center
+
+ ====== ======
+ Field Description
+ ====== ======
+ ``E`` Young's modulus
+ ``A`` Cross section area
+ ``I`` Second cross sectional moment of inertia (for 2D elements)
+ ``Iy`` ``I`` around beam :math:`y`--axis (for 3D elements)
+ ``Iz`` ``I`` around beam :math:`z`--axis (for 3D elements)
+ ``GJ`` Polar moment of inertia of beam cross section (for 3D elements)
+ ====== ======
+
+Materials can be added to the model's ``element_material`` vector using
+
+.. code-block:: c++
+
+ model.addMaterial(mat1);
+
+They are successively numbered and then assigned to specific elements.
+
+.. code-block:: c++
+
+ for (UInt i = 0; i < nb_element_mat_1; ++i) {
+ model.getElementMaterial(_bernoulli_beam_2)(i,0) = 1;
+ }
+
+
+.. _sect:structMechMod:boundary:
+
+Setting Boundary Conditions
+```````````````````````````
+As explained before, the Dirichlet boundary conditions are applied through the
+array **blocked_dofs**. Two options exist to define Neumann conditions.
+If a nodal force is applied, it has to be directly set in the array
+**force_momentum**. For loads distributed along the beam length, the
+method :cpp:class:`computeForcesFromFunction <akantu::computeForcesFromFunction>` integrates them into nodal forces. The
+method takes as input a function describing the distribution of loads along the
+beam and a functor :cpp:class:`BoundaryFunctionType <akantu::BoundaryFunctionType>` specifing if the function is expressed in the local coordinates (``_bft_traction_local``) or in the
+global system of coordinates (``_bft_traction``).
+
+.. code-block:: c++
+
+ static void lin_load(double * position, double * load,
+ Real * normal, UInt surface_id){
+ memset(load,0,sizeof(Real)*3);
+ load[1] = position[0]*position[0]-250;
+ }
+ int main(){
+ ...
+ model.computeForcesFromFunction<_bernoulli_beam_2>(lin_load,
+ _bft_traction_local);
+ ...
+ }
+
+
+.. _sect:structMechMod:static:
+
+Static Analysis
+---------------
+
+The :cpp:class:`StructuralMechanicsModel <akantu::StructuralMechanicsModel>` class can perform static analyses of structures. In this case, the equation to solve is the same as for the :cpp:class:`SolidMechanicsModel <akantu::SolidMechanicsModel>` used for static analyses
+
+.. math:: \mat{K} \vec{u} = \vec{f_{\st{ext}}}~,
+ :label: eqn-structmechmod-static
+
+where :math:`\mat{K}` is the global stiffness matrix, :math:`\vec{u}` the
+generalized displacement vector and :math:`\vec{f_{\st{ext}}}` the vector of
+generalized external forces applied to the system.
+
+To solve such a problem, the static solver of the
+:cpp:class:`StructuralMechanicsModel <akantu::StructuralMechanicsModel>` object
+is used. First a model has to be created and initialized.
+
+.. code-block:: c++
+
+ StructuralMechanicsModel model(mesh);
+ model.initFull();
+
+- :cpp:func:`model.initFull <akantu::StructuralMechanicsModel::initFull>` initializes all
+ internal vectors to zero.
+
+Once the model is created and initialized, the boundary conditions can be set as explained in Section :ref:`sect:structMechMod:boundary`. Boundary conditions will prescribe the external forces or moments for the free degrees of freedom :math:`\vec{f_{\st{ext}}}` and displacements or rotations for the others. To completely define the system represented by equation (:eq:`eqn-structmechmod-static`), the global stiffness matrix :math:`\mat{K}` must be assembled.
+
+.. code-block:: c++
+
+ model.assembleStiffnessMatrix();
+
+The computation of the static equilibrium is performed using the same
+Newton-Raphson algorithm as described in
+Section~\ref{sect:smm:static}.
+
+\note{To date, :cpp:class:`StructuralMechanicsModel
+<akantu::StructuralMechanicsModel>` handles only constitutively and
+geometrically linear problems, the algorithm is therefore guaranteed to converge
+in two iterations.}
+
+.. code-block:: c++
+
+ model.solveStep();
+
+- :cpp:func:`model.solveStep <akantu::StructuralMechanicsModel::solveStep>` solves the :eq:`eqn-structmechmod-static`.
+ The **increment** vector of the model will contain the new
+ increment of displacements, and the **displacement_rotation**
+ vector is also updated to the new displacements.
+
+At the end of the analysis, the final solution is stored in the
+**displacement_rotation** vector. A full example of how to solve a structural
+mechanics problem is presented in the code
+``example/structural_mechanics/bernoulli_beam_2_example.cc``. This example is
+composed of a 2D beam, clamped at the left end and supported by two rollers as
+shown in :numref:`fig-structmechmod-exam1-1`. The problem is defined by the
+applied load :math:`q=6 \text{\kN/m}`, moment :math:`\bar{M} = 3.6 \text{kN m}`,
+moments of inertia :math:`I_1 = 250\,000 \text{cm}^4` and :math:`I_2 = 128\,000
+\text{cm}^4` and lengths :math:`L_1 = 10\text{m}` and :math:`L_2 = 8\text{m}`.
+The resulting rotations at node two and three are :math:`\varphi_2 = 0.001\,167`
+and :math:`\varphi_3 = -0.000\,771`.
+
+.. _fig-structmechmod-exam1-1:
+
+.. figure:: figures/beam_example.svg
+ :align: center
+
+ 2D beam example
diff --git a/doc/dev-doc/reference.rst b/doc/dev-doc/reference.rst
index b3f054143..0550062f5 100644
--- a/doc/dev-doc/reference.rst
+++ b/doc/dev-doc/reference.rst
@@ -1,7 +1,101 @@
.. _reference:
Reference
---------
-.. doxygennamespace:: akantu
- :project: Akantu
+Common
+``````
+
+.. doxygenfunction:: akantu::initialize(const std::string &input_file, int &argc, char **&argv)
+.. doxygenfunction:: akantu::initialize(int &argc, char **&argv)
+
+.. doxygentypedef:: akantu::UInt
+.. doxygentypedef:: akantu::Int
+.. doxygentypedef:: akantu::Real
+
+.. doxygenenum:: akantu::ElementType
+.. doxygenenum:: akantu::ModelType
+.. doxygenenum:: akantu::AnalysisMethod
+.. doxygenenum:: akantu::SolveConvergenceCriteria
+
+.. doxygenclass:: akantu::ArrayBase
+.. doxygenclass:: akantu::ArrayDataLayer
+.. doxygenclass:: akantu::Array
+
+.. doxygenclass:: akantu::ElementTypeMapArray
+
+.. doxygenclass:: akantu::Vector
+.. doxygenclass:: akantu::Matrix
+
+Mesh
+````
+.. doxygenclass:: akantu::Mesh
+.. doxygenclass:: akantu::FEEngine
+
+Models
+``````
+
+Common
+......
+
+.. doxygenclass:: akantu::BC::Dirichlet::FixedValue
+.. doxygenclass:: akantu::BC::Dirichlet::FlagOnly
+.. doxygenclass:: akantu::BC::Dirichlet::IncrementValue
+.. doxygenclass:: akantu::BC::Neumann::FromStress
+.. doxygenclass:: akantu::BC::Neumann::FromTraction
+.. doxygenclass:: akantu::BoundaryCondition
+.. doxygenclass:: akantu::BoundaryConditionFunctor
+.. doxygenclass:: akantu::EventHandlerManager
+.. doxygenclass:: akantu::Model
+.. doxygenclass:: akantu::NonLocalManagerCallback
+
+Solvers
+.......
+
+.. doxygenclass:: akantu::ModelSolver
+.. doxygenclass:: akantu::DOFManager
+.. doxygenclass:: akantu::NonLinearSolver
+.. doxygenclass:: akantu::NonLinearSolverNewtonRaphson
+
+Solid Mechanics Model
+.....................
+
+.. doxygenclass:: akantu::SolidMechanicsModel
+.. doxygenclass:: akantu::SolidMechanicsModelOptions
+.. doxygenclass:: akantu::MaterialSelector
+.. doxygenclass:: akantu::MeshDataMaterialSelector
+.. doxygenclass:: akantu::Material
+.. doxygenclass:: akantu::InternalField
+
+Solid Mechanics Model Cohesive
+..............................
+
+.. doxygenclass:: akantu::SolidMechanicsModelCohesive
+.. doxygenclass:: akantu::FragmentManager
+
+Heat Transfer Model
+...................
+
+.. doxygenclass:: akantu::HeatTransferModel
+
+Structural Mechanics Model
+..........................
+
+.. doxygenclass:: akantu::StructuralMaterial
+.. doxygenclass:: akantu::StructuralMechanicsModel
+
+Synchronizers
+`````````````
+.. doxygenclass:: akantu::DataAccessor
+
+Input/Output
+````````````
+.. doxygenclass:: akantu::Dumpable
+.. doxygenclass:: akantu::DumperIOHelper
+.. doxygenclass:: akantu::DumperParaview
+.. doxygenclass:: akantu::DumperText
+.. doxygenclass:: akantu::Field
+.. doxygenclass:: akantu::Parser
+.. doxygenclass:: akantu::ParserParameter
+.. doxygenclass:: akantu::ParserSection
+.. doxygenenum:: akantu::SectionType
diff --git a/doc/manual/manual-appendix-elements.tex b/doc/manual/manual-appendix-elements.tex
index fb776a13d..c91c05f22 100644
--- a/doc/manual/manual-appendix-elements.tex
+++ b/doc/manual/manual-appendix-elements.tex
@@ -1,608 +1,616 @@
\chapter{Shape Functions\index{Elements}}
\label{app:elements}
-Schmatic overview of all the element types defined in \akantu is described in Section~\ref{sec:elements}. In this appendix, more detailed information (shape function, location of Gaussian quadrature points, and so on) of each of these types is listed. For each element type, the coordinates of the nodes are given in the isoparametric frame of reference, together with the shape functions (and their derivatives) on these respective nodes. Also all the Gaussian quadrature points within each element are assigned (together with the weight that is applied on these points). The graphical representations of all the element types can be found in Section~\ref{sec:elements}.
+Schmatic overview of all the element types defined in \akantu is described in
+Section~\ref{sec:elements}. In this appendix, more detailed information (shape
+function, location of Gaussian quadrature points, and so on) of each of these
+types is listed. For each element type, the coordinates of the nodes are given
+in the isoparametric frame of reference, together with the shape functions (and
+their derivatives) on these respective nodes. Also all the Gaussian quadrature
+points within each element are assigned (together with the weight that is
+applied on these points). The graphical representations of all the element types
+can be found in Section~\ref{sec:elements}.
%%%%%%%%%% 1D %%%%%%%%%
%\section{Isoparametric Elements in 1D\index{Elements!1D}}
\section{1D-Shape Functions\index{Elements!1D}}
\subsection{Segment 2\index{Elements!1D!Segment 2}}
\begin{Element}{1D}
1 & \inelemone{-1} & $\half\left(1-\xi\right)$ & \inelemone{-\half} \\
\elemline
2 & \inelemone{1} & $\half\left(1+\xi\right)$ & \inelemone{\half} \\
\end{Element}
\begin{QuadPoints}{lc}
Coord. \elemcooroned & 0 \\
\elemline
Weight & 2 \\
\end{QuadPoints}
\subsection{Segment 3\index{Elements!1D!Segment 3}}
\begin{Element}{1D}
1 & \inelemone{-1} & $\half\xi\left(\xi-1\right)$ & \inelemone{\xi-\half} \\
\elemline
2 & \inelemone{1} & $\half\xi\left(\xi+1\right)$ & \inelemone{\xi+\half} \\
\elemline
3 & \inelemone{0} & $1-\xi^{2}$ & \inelemone{-2\xi} \\
\end{Element}
\begin{QuadPoints}{lcc}
Coord. \elemcooroned & $-1/\sqrt{3}$ & $1/\sqrt{3}$ \\
\elemline
Weight & 1 & 1 \\
\end{QuadPoints}
%%%%%%%%%% 2D %%%%%%%%%
%\section{Isoparametric Elements in 2D\index{Elements!2D}}
\section{2D-Shape Functions\index{Elements!2D}}
\subsection{Triangle 3\index{Elements!2D!Triangle 3}}
\begin{Element}{2D}
1 & \inelemtwo{0}{0} & $1-\xi-\eta$ & \inelemtwo{-1}{-1} \\
\elemline
2 & \inelemtwo{1}{0} & $\xi$ & \inelemtwo{1}{0} \\
\elemline
3 & \inelemtwo{0}{1} & $\eta$ & \inelemtwo{0}{1} \\
\end{Element}
\begin{QuadPoints}{lc}
Coord. \elemcoortwod & \inquadtwo{\third}{\third} \\
\elemline
Weight & \half \\
\end{QuadPoints}
\subsection{Triangle 6\index{Elements!2D!Triangle 6}}
\begin{Element}{2D}
1 & \inelemtwo{0}{0} & $-\left(1-\xi-\eta\right)\left(1-2\left(1-\xi-\eta\right)\right)$ & \inelemtwo{1-4\left(1-\xi-\eta\right)}{1-4\left(1-\xi-\eta\right)} \\
\elemline
2 & \inelemtwo{1}{0} & $-\xi\left(1-2\xi\right)$ & \inelemtwo{4\xi-1}{0} \\
\elemline
3 & \inelemtwo{0}{1} & $-\eta\left(1-2\eta\right)$ & \inelemtwo{0}{4\eta-1} \\
\elemline
4 & \inelemtwo{\half}{0} & $4\xi\left(1-\xi-\eta\right)$ & \inelemtwo{4\left(1-2\xi-\eta\right)}{-4\xi} \\
\elemline
5 & \inelemtwo{\half}{\half} & $4\xi\eta$ & \inelemtwo{4\eta}{4\xi} \\
\elemline
6 & \inelemtwo{0}{\half} & $4\eta\left(1-\xi-\eta\right)$ & \inelemtwo{-4\eta}{4\left(1-\xi-2\eta\right)} \\
\elemline
\end{Element}
\begin{QuadPoints}{lccc}
Coord. \elemcoortwod & \inquadtwo{\sixth}{\sixth} & \inquadtwo{\twothird}{\sixth} & \inquadtwo{\sixth}{\twothird} \\
\elemline
Weight & \sixth & \sixth & \sixth \\
\end{QuadPoints}
\clearpage
\subsection{Quadrangle 4\index{Elements!2D!Quadrangle 4}}
\begin{Element}{2D}
1 & \inelemtwo{-1}{-1} & $\quart\left(1-\xi\right)\left(1-\eta\right)$ & \inelemtwo{-\quart\left(1-\eta\right)}{-\quart\left(1-\xi\right)} \\
\elemline
2 & \inelemtwo{1}{-1} & $\quart\left(1+\xi\right)\left(1-\eta\right)$ & \inelemtwo{\quart\left(1-\eta\right)}{-\quart\left(1+\xi\right)} \\
\elemline
3 & \inelemtwo{1}{1} & $\quart\left(1+\xi\right)\left(1+\eta\right)$ & \inelemtwo{\quart\left(1+\eta\right)}{\quart\left(1+\xi\right)} \\
\elemline
4 & \inelemtwo{-1}{1} & $\quart\left(1-\xi\right)\left(1+\eta\right)$ & \inelemtwo{-\quart\left(1+\eta\right)}{\quart\left(1-\xi\right)} \\
\end{Element}
\begin{QuadPoints}{lcccc}
\elemcoortwod & \inquadtwo{-\invsqrtthree}{-\invsqrtthree} & \inquadtwo{\invsqrtthree}{-\invsqrtthree}
& \inquadtwo{\invsqrtthree}{\invsqrtthree} & \inquadtwo{-\invsqrtthree}{\invsqrtthree} \\
\elemline
Weight & 1 & 1 & 1 & 1 \\
\end{QuadPoints}
\subsection{Quadrangle 8\index{Elements!2D!Quadrangle 8}}
\begin{Element}{2D}
1 & \inelemtwo{-1}{-1} & $\quart\left(1-\xi\right)\left(1-\eta\right)\left(-1-\xi-\eta\right)$
& \inelemtwo{\quart\left(1-\eta\right)\left(2\xi+\eta\right)}
{\quart\left(1-\xi\right)\left(\xi+2\eta\right)} \\
\elemline
2 & \inelemtwo{1}{-1} & $\quart\left(1+\xi\right)\left(1-\eta\right)\left(-1+\xi-\eta\right)$
& \inelemtwo{\quart\left(1-\eta\right)\left(2\xi-\eta\right)}
{-\quart\left(1+\xi\right)\left(\xi-2\eta\right)} \\
\elemline
3 & \inelemtwo{1}{1} & $\quart\left(1+\xi\right)\left(1+\eta\right)\left(-1+\xi+\eta\right)$
& \inelemtwo{\quart\left(1+\eta\right)\left(2\xi+\eta\right)}
{\quart\left(1+\xi\right)\left(\xi+2\eta\right)} \\
\elemline
4 & \inelemtwo{-1}{1} & $\quart\left(1-\xi\right)\left(1+\eta\right)\left(-1-\xi+\eta\right)$
& \inelemtwo{\quart\left(1+\eta\right)\left(2\xi-\eta\right)}
{-\quart\left(1-\xi\right)\left(\xi-2\eta\right)} \\
\elemline
5 & \inelemtwo{0}{-1} & $\half\left(1-\xi^{2}\right)\left(1-\eta\right)$
& \inelemtwo{-\xi\left(1-\eta\right)}
{-\half\left(1-\xi^{2}\right)} \\
\elemline
6 & \inelemtwo{1}{0} & $\half\left(1+\xi\right)\left(1-\eta^{2}\right)$
& \inelemtwo{\half\left(1-\eta^{2}\right)}
{-\eta\left(1+\xi\right)} \\
\elemline
7 & \inelemtwo{0}{1} & $\half\left(1-\xi^{2}\right)\left(1+\eta\right)$
& \inelemtwo{-\xi\left(1+\eta\right)}
{\half\left(1-\xi^{2}\right)} \\
\elemline
8 & \inelemtwo{-1}{0} & $\half\left(1-\xi\right)\left(1-\eta^{2}\right)$
& \inelemtwo{-\half\left(1-\eta^{2}\right)}
{-\eta\left(1-\xi\right)} \\
\end{Element}
\begin{QuadPoints}{lccccc}
Coord. \elemcoortwod & \inquadtwo{0}{0} & \inquadtwo{\sqrt{\tfrac{3}{5}}}{\sqrt{\tfrac{3}{5}}} & \inquadtwo{-\sqrt{\tfrac{3}{5}}}{\sqrt{\tfrac{3}{5}}}
& \inquadtwo{-\sqrt{\tfrac{3}{5}}}{-\sqrt{\tfrac{3}{5}}} & \inquadtwo{\sqrt{\tfrac{3}{5}}}{-\sqrt{\tfrac{3}{5}}} \\
\elemline
Weight & 64/81 & 25/81 & 25/81 & 25/81 & 25/81 \\
\elemline
Coord. \elemcoortwod & \inquadtwo{0}{\sqrt{\tfrac{3}{5}}} & \inquadtwo{-\sqrt{\tfrac{3}{5}}}{0}
& \inquadtwo{0}{-\sqrt{\tfrac{3}{5}}} & \inquadtwo{\sqrt{\tfrac{3}{5}}}{0} & \\
\elemline
Weight & 40/81 & 40/81 & 40/81 & 40/81 & \\
\end{QuadPoints}
\clearpage
%%%%%%%%%% 3D %%%%%%%%%
\section{3D-Shape Functions\index{Elements!3D}}
%\section{Isoparametric Elements in 3D\index{Elements!3D}}
\subsection{Tetrahedron 4\index{Elements!3D!Tetrahedron 4}}
\begin{Element}{3D}
1 & \inelemthree{0}{0}{0} & $1-\xi-\eta-\zeta$ & \inelemthree{-1}{-1}{-1} \\
\elemline
2 & \inelemthree{1}{0}{0} & $\xi$ & \inelemthree{1}{0}{0} \\
\elemline
3 & \inelemthree{0}{1}{0} & $\eta$ & \inelemthree{0}{1}{0} \\
\elemline
4 & \inelemthree{0}{0}{1} & $\zeta$ & \inelemthree{0}{0}{1} \\
\end{Element}
\begin{QuadPoints}{lc}
Coord. \elemcoorthreed & \inquadthree{\quart}{\quart}{\quart} \\
\elemline
Weight & \sixth \\
\end{QuadPoints}
\subsection{Tetrahedron 10\index{Elements!3D!Tetrahedron 10}}
\begin{Element}{3D}
1 & \inelemthree{0}{0}{0} & $\left(1-\xi-\eta-\zeta\right)\left(1-2\xi-2\eta-2\zeta\right)$
& \inelemthreecolumn{4\xi+4\eta+4\zeta-3}{4\xi+4\eta+4\zeta-3}{4\xi+4\eta+4\zeta-3}\\
\elemline
2 & \inelemthree{1}{0}{0} & $\xi\left(2\xi-1\right)$
& \inelemthree{4\xi-1}{0}{0} \\
\elemline
3 & \inelemthree{0}{1}{0} & $\eta\left(2\eta-1\right)$
& \inelemthree{0}{4\eta-1}{0} \\
\elemline
4 & \inelemthree{0}{0}{1} & $\zeta\left(2\zeta-1\right)$
& \inelemthree{0}{0}{4\zeta-1} \\
\elemline
5 & \inelemthree{\half}{0}{0} & $4\xi\left(1-\xi-\eta-\zeta\right)$
& \inelemthree{4-8\xi-4\eta-4\zeta}{-4\xi}{-4\xi} \\
\elemline
6 & \inelemthree{\half}{\half}{0} & $4\xi\eta$
& \inelemthree{4\eta}{4\xi}{0} \\
\elemline
7 & \inelemthree{0}{\half}{0} & $4\eta\left(1-\xi-\eta-\zeta\right)$
& \inelemthree{-4\eta}{4-4\xi-8\eta-4\zeta}{-4\eta} \\
\elemline
8 & \inelemthree{0}{0}{\half} & $4\zeta\left(1-\xi-\eta-\zeta\right)$
& \inelemthree{-4\zeta}{-4\zeta}{4-4\xi-4\eta-8\zeta} \\
\elemline
9 & \inelemthree{\half}{0}{\half} & $4\xi\zeta$
& \inelemthree{4\zeta}{0}{4\xi} \\
\elemline
10 & \inelemthree{0}{\half}{\half} & $4\eta\zeta$
& \inelemthree{0}{4\zeta}{4\eta} \\
\end{Element}
\begin{QuadPoints}{lcc}
Coord. \elemcoorthreed & \inquadthree{\quada}{\quada}{\quada} & \inquadthree{\quadb}{\quada}{\quada} \\
\elemline
Weight & 1/24 & 1/24 \\
\elemline
Coord. \elemcoorthreed & \inquadthree{\quada}{\quadb}{\quada} & \inquadthree{\quada}{\quada}{\quadb} \\
\elemline
Weight & 1/24 & 1/24 \\
\end{QuadPoints}
\clearpage
\subsection{Hexahedron 8\index{Elements!3D!Hexahedron 8}}
\begin{Element}{3D}
1 & \inelemthree{-1}{-1}{-1} & $\eighth\left(1-\xi\right)\left(1-\eta\right)\left(1-\zeta\right)$
& \inelemthree{-\eighth\left(1-\eta\right)\left(1-\zeta\right)}
{-\eighth\left(1-\xi\right)\left(1-\zeta\right)}
{-\eighth\left(1-\xi\right)\left(1-\eta\right)} \\
\elemline
2 & \inelemthree{1}{-1}{-1} & $\eighth\left(1+\xi\right)\left(1-\eta\right)\left(1-\zeta\right)$
& \inelemthree{ \eighth\left(1-\eta\right)\left(1-\zeta\right)}
{-\eighth\left(1+\xi\right)\left(1-\zeta\right)}
{-\eighth\left(1+\xi\right)\left(1-\eta\right)} \\
\elemline
3 & \inelemthree{1}{1}{-1} & $\eighth\left(1+\xi\right)\left(1+\eta\right)\left(1-\zeta\right)$
& \inelemthree{ \eighth\left(1+\eta\right)\left(1-\zeta\right)}
{ \eighth\left(1+\xi\right)\left(1-\zeta\right)}
{-\eighth\left(1+\xi\right)\left(1+\eta\right)} \\
\elemline
4 & \inelemthree{-1}{1}{-1} & $\eighth\left(1-\xi\right)\left(1+\eta\right)\left(1-\zeta\right)$
& \inelemthree{-\eighth\left(1+\eta\right)\left(1-\zeta\right)}
{ \eighth\left(1-\xi\right)\left(1-\zeta\right)}
{-\eighth\left(1-\xi\right)\left(1+\eta\right)} \\
\elemline
5 & \inelemthree{-1}{-1}{1} & $\eighth\left(1-\xi\right)\left(1-\eta\right)\left(1+\zeta\right)$
& \inelemthree{-\eighth\left(1-\eta\right)\left(1+\zeta\right)}
{-\eighth\left(1-\xi\right)\left(1+\zeta\right)}
{ \eighth\left(1-\xi\right)\left(1-\eta\right)} \\
\elemline
6 & \inelemthree{1}{-1}{1} & $\eighth\left(1+\xi\right)\left(1-\eta\right)\left(1+\zeta\right)$
& \inelemthree{ \eighth\left(1-\eta\right)\left(1+\zeta\right)}
{-\eighth\left(1+\xi\right)\left(1+\zeta\right)}
{ \eighth\left(1+\xi\right)\left(1-\eta\right)} \\
\elemline
7 & \inelemthree{1}{1}{1} & $\eighth\left(1+\xi\right)\left(1+\eta\right)\left(1+\zeta\right)$
& \inelemthree{ \eighth\left(1+\eta\right)\left(1+\zeta\right)}
{ \eighth\left(1+\xi\right)\left(1+\zeta\right)}
{ \eighth\left(1+\xi\right)\left(1+\eta\right)} \\
\elemline
8 & \inelemthree{-1}{1}{1} & $\eighth\left(1-\xi\right)\left(1+\eta\right)\left(1+\zeta\right)$
& \inelemthree{-\eighth\left(1+\eta\right)\left(1+\zeta\right)}
{ \eighth\left(1-\xi\right)\left(1+\zeta\right)}
{ \eighth\left(1-\xi\right)\left(1+\eta\right)} \\
\end{Element}
\begin{QuadPoints}{lcccc}
\elemcoorthreed & \inquadthree{-\invsqrtthree}{-\invsqrtthree}{-\invsqrtthree} & \inquadthree{\invsqrtthree}{-\invsqrtthree}{-\invsqrtthree}
& \inquadthree{\invsqrtthree}{\invsqrtthree}{-\invsqrtthree} & \inquadthree{-\invsqrtthree}{\invsqrtthree}{-\invsqrtthree} \\
\elemline
Weight & 1 & 1 & 1 & 1 \\
\elemline
\elemcoorthreed & \inquadthree{-\invsqrtthree}{-\invsqrtthree}{\invsqrtthree} & \inquadthree{\invsqrtthree}{-\invsqrtthree}{\invsqrtthree}
& \inquadthree{\invsqrtthree}{\invsqrtthree}{\invsqrtthree} & \inquadthree{-\invsqrtthree}{\invsqrtthree}{\invsqrtthree} \\
\elemline
Weight & 1 & 1 & 1 & 1 \\
\end{QuadPoints}
\subsection{Pentahedron 6\index{Elements!3D!Pentahedron 6}}
\begin{Element}{3D}
1 & \inelemthree{-1}{1}{0} & $\half\left(1-\xi\right)\eta$
& \inelemthree{-\half\eta}
{ \half\left(1-\xi\right)}
{0.0} \\
\elemline
2 & \inelemthree{-1}{0}{1} & $\half\left(1-\xi\right)\zeta$
& \inelemthree{-\half\zeta}
{0.0}
{\half\left(1-\xi\right)} \\
\elemline
3 & \inelemthree{-1}{0}{0} & $\half\left(1-\xi\right)\left(1-\eta-\zeta\right)$
& \inelemthree{-\half\left(1-\eta-\zeta\right)}
{-\half\left(1-\xi\right)}
{-\half\left(1-\xi\right)} \\
\elemline
4 & \inelemthree{1}{1}{0} & $\half\left(1+\xi\right)\eta$
& \inelemthree{ \half\eta}
{ \half\left(1+\xi\right)}
{0.0} \\
\elemline
5 & \inelemthree{1}{0}{1} & $\half\left(1+\xi\right)\zeta$
& \inelemthree{ \half\zeta}
{0.0}
{ \half\left(1+\xi\right)} \\
\elemline
6 & \inelemthree{1}{0}{0} & $\half\left(1+\xi\right)\left(1-\eta-\zeta\right)$
& \inelemthree{ \half\left(1-\eta-\zeta\right)}
{-\half\left(1+\xi\right)}
{-\half\left(1+\xi\right)} \\
\end{Element}
\begin{QuadPoints}{lcccccc}
\elemcoorthreed & \inquadthree{-\invsqrtthree}{0.5}{0.5}
& \inquadthree{-\invsqrtthree}{0.0}{0.5}
& \inquadthree{-\invsqrtthree}{0.5}{0.0}
& \inquadthree{\invsqrtthree}{0.5}{0.5}
& \inquadthree{\invsqrtthree}{0.0}{0.5}
& \inquadthree{\invsqrtthree}{0.5}{0.0} \\
\elemline
Weight & 1/6 & 1/6 & 1/6 & 1/6 & 1/6 & 1/6 \\
\end{QuadPoints}
\clearpage
\subsection{Hexahedron 20\index{Elements!3D!Hexahedron 20}}
\begin{Element_part1}{3D}
1 & \inelemthree{-1}{-1}{-1} & $\eighth\left(1-\xi\right)\left(1-\eta\right)\left(1-\zeta\right)\left(-2-\xi-\eta-\zeta\right)$ \\
\elemline
2 & \inelemthree{1}{-1}{-1} & $\eighth\left(1+\xi\right)\left(1-\eta\right)\left(1-\zeta\right)\left(-2+\xi-\eta-\zeta\right)$ \\
\elemline
3 & \inelemthree{1}{1}{-1} & $\eighth\left(1+\xi\right)\left(1+\eta\right)\left(1-\zeta\right)\left(-2+\xi+\eta-\zeta\right)$ \\
\elemline
4 & \inelemthree{-1}{1}{-1} & $\eighth\left(1-\xi\right)\left(1+\eta\right)\left(1-\zeta\right)\left(-2-\xi+\eta-\zeta\right)$ \\
\elemline
5 & \inelemthree{-1}{-1}{1} & $\eighth\left(1-\xi\right)\left(1-\eta\right)\left(1+\zeta\right)\left(-2-\xi-\eta+\zeta\right)$ \\
\elemline
6 & \inelemthree{1}{-1}{1} & $\eighth\left(1+\xi\right)\left(1-\eta\right)\left(1+\zeta\right)\left(-2+\xi-\eta+\zeta\right)$ \\
\elemline
7 & \inelemthree{1}{1}{1} & $\eighth\left(1+\xi\right)\left(1+\eta\right)\left(1+\zeta\right)\left(-2+\xi+\eta+\zeta\right)$ \\
\elemline
8 & \inelemthree{-1}{1}{1} & $\eighth\left(1-\xi\right)\left(1+\eta\right)\left(1+\zeta\right)\left(-2-\xi+\eta+\zeta\right)$ \\
\elemline
9 & \inelemthree{0}{-1}{-1} & $\quart\left(1-\xi^{2}\right)\left(1-\eta\right)\left(1-\zeta\right)$ \\
\elemline
10 & \inelemthree{1}{0}{-1} & $\quart\left(1+\xi\right)\left(1-\eta^{2}\right)\left(1-\zeta\right)$ \\
\elemline
11 & \inelemthree{0}{1}{-1} & $\quart\left(1-\xi^{2}\right)\left(1+\eta\right)\left(1-\zeta\right)$ \\
\elemline
12 & \inelemthree{-1}{0}{-1} & $\quart\left(1-\xi\right)\left(1-\eta^{2}\right)\left(1-\zeta\right)$ \\
\elemline
13 & \inelemthree{-1}{-1}{0} & $\quart\left(1-\xi\right)\left(1-\eta\right)\left(1-\zeta^{2}\right)$ \\
\elemline
14 & \inelemthree{1}{-1}{0} & $\quart\left(1+\xi\right)\left(1-\eta\right)\left(1-\zeta^{2}\right)$ \\
\elemline
15 & \inelemthree{1}{1}{0} & $\quart\left(1+\xi\right)\left(1+\eta\right)\left(1-\zeta^{2}\right)$ \\
\elemline
16 & \inelemthree{-1}{1}{0} & $\quart\left(1-\xi\right)\left(1+\eta\right)\left(1-\zeta^{2}\right)$ \\
\elemline
17 & \inelemthree{0}{-1}{1} & $\quart\left(1-\xi^{2}\right)\left(1-\eta\right)\left(1+\zeta\right)$ \\
\elemline
18 & \inelemthree{1}{0}{1} & $\quart\left(1+\xi\right)\left(1-\eta^{2}\right)\left(1+\zeta\right)$ \\
\elemline
19 & \inelemthree{0}{1}{1} & $\quart\left(1-\xi^{2}\right)\left(1+\eta\right)\left(1+\zeta\right)$ \\
\elemline
20 & \inelemthree{-1}{0}{1} & $\quart\left(1-\xi\right)\left(1-\eta^{2}\right)\left(1+\zeta\right)$ \\
\end{Element_part1}
\clearpage
\begin{Element_part2}{3D}
1 & \inelemthree{ \quart\left(\xi+\half\left(\eta+\zeta+1\right)\right)\left(\eta-1\right)\left(\zeta-1\right)}
{ \quart\left(\eta+\half\left(\xi+\zeta+1\right)\right)\left(\xi-1\right)\left(\zeta-1\right)}
{ \quart\left(\zeta+\half\left(\xi+\eta+1\right)\right)\left(\xi-1\right)\left(\eta-1\right)} \\
\elemline
2 & \inelemthree{ \quart\left(\xi-\half\left(\eta+\zeta+1\right)\right)\left(\eta-1\right)\left(\zeta-1\right)}
{-\quart\left(\eta-\half\left(\xi-\zeta-1\right)\right)\left(\xi+1\right)\left(\zeta-1\right)}
{-\quart\left(\zeta-\half\left(\xi-\eta-1\right)\right)\left(\xi+1\right)\left(\eta-1\right)} \\
\elemline
3 & \inelemthree{-\quart\left(\xi+\half\left(\eta-\zeta-1\right)\right)\left(\eta+1\right)\left(\zeta-1\right)}
{-\quart\left(\eta+\half\left(\xi-\zeta-1\right)\right)\left(\xi+1\right)\left(\zeta-1\right)}
{ \quart\left(\zeta-\half\left(\xi+\eta-1\right)\right)\left(\xi+1\right)\left(\eta+1\right)} \\
\elemline
4 & \inelemthree{-\quart\left(\xi-\half\left(\eta-\zeta-1\right)\right)\left(\eta+1\right)\left(\zeta-1\right)}
{ \quart\left(\eta-\half\left(\xi+\zeta+1\right)\right)\left(\xi-1\right)\left(\zeta-1\right)}
{-\quart\left(\zeta+\half\left(\xi-\eta+1\right)\right)\left(\xi-1\right)\left(\eta+1\right)} \\
\elemline
5 & \inelemthree{-\quart\left(\xi+\half\left(\eta-\zeta+1\right)\right)\left(\eta-1\right)\left(\zeta+1\right)}
{-\quart\left(\eta+\half\left(\xi-\zeta+1\right)\right)\left(\xi-1\right)\left(\zeta+1\right)}
{ \quart\left(\zeta-\half\left(\xi+\eta+1\right)\right)\left(\xi-1\right)\left(\eta-1\right)} \\
\elemline
6 & \inelemthree{-\quart\left(\xi-\half\left(\eta-\zeta+1\right)\right)\left(\eta-1\right)\left(\zeta+1\right)}
{ \quart\left(\eta-\half\left(\xi+\zeta-1\right)\right)\left(\xi+1\right)\left(\zeta+1\right)}
{-\quart\left(\zeta+\half\left(\xi-\eta-1\right)\right)\left(\xi+1\right)\left(\eta-1\right)} \\
\elemline
7 & \inelemthree{ \quart\left(\xi+\half\left(\eta+\zeta-1\right)\right)\left(\eta+1\right)\left(\zeta+1\right)}
{ \quart\left(\eta+\half\left(\xi+\zeta-1\right)\right)\left(\xi+1\right)\left(\zeta+1\right)}
{ \quart\left(\zeta+\half\left(\xi+\eta-1\right)\right)\left(\xi+1\right)\left(\eta+1\right)} \\
\elemline
8 & \inelemthree{ \quart\left(\xi-\half\left(\eta+\zeta-1\right)\right)\left(\eta+1\right)\left(\zeta+1\right)}
{-\quart\left(\eta-\half\left(\xi-\zeta+1\right)\right)\left(\xi-1\right)\left(\zeta+1\right)}
{-\quart\left(\zeta-\half\left(\xi-\eta+1\right)\right)\left(\xi-1\right)\left(\eta+1\right)} \\
\elemline
9 & \inelemthree{-\half\xi\left(\eta-1\right)\left(\zeta-1\right)}
{-\quart\left(\xi^{2}-1\right)\left(\zeta-1\right)}
{-\quart\left(\xi^{2}-1\right)\left(\eta-1\right)} \\
\elemline
10 & \inelemthree{ \quart\left(\eta^{2}-1\right)\left(\zeta-1\right)}
{ \half\eta\left(\xi+1\right)\left(\zeta-1\right)}
{ \quart\left(\xi+1\right)\left(\eta^{2}-1\right)} \\
\elemline
11 & \inelemthree{ \half\xi\left(\eta+1\right)\left(\zeta-1\right)}
{ \quart\left(\xi^{2}-1\right)\left(\zeta-1\right)}
{ \quart\left(\xi^{2}-1\right)\left(\eta+1\right)} \\
\elemline
12 & \inelemthree{-\quart\left(\eta^{2}-1\right)\left(\zeta-1\right)}
{-\half\eta\left(\xi-1\right)\left(\zeta-1\right)}
{-\quart\left(\xi-1\right)\left(\eta^{2}-1\right)} \\
\elemline
13 & \inelemthree{-\quart\left(\eta-1\right)\left(\zeta^{2}-1\right)}
{-\quart\left(\xi-1\right)\left(\zeta^{2}-1\right)}
{-\half\zeta\left(\xi-1\right)\left(\eta-1\right)} \\
\elemline
14 & \inelemthree{ \quart\left(\eta-1\right)\left(\zeta^{2}-1\right)}
{ \quart\left(\xi+1\right)\left(\zeta^{2}-1\right)}
{ \half\zeta\left(\xi+1\right)\left(\eta-1\right)} \\
\elemline
15 & \inelemthree{-\quart\left(\eta+1\right)\left(\zeta^{2}-1\right)}
{-\quart\left(\xi+1\right)\left(\zeta^{2}-1\right)}
{-\half\zeta\left(\xi+1\right)\left(\eta+1\right)} \\
\elemline
16 & \inelemthree{ \quart\left(\eta+1\right)\left(\zeta^{2}-1\right)}
{ \quart\left(\xi-1\right)\left(\zeta^{2}-1\right)}
{ \half\zeta\left(\xi-1\right)\left(\eta+1\right)} \\
\elemline
17 & \inelemthree{ \half\xi\left(\eta-1\right)\left(\zeta+1\right)}
{ \quart\left(\xi^{2}-1\right)\left(\zeta+1\right)}
{ \quart\left(\xi^{2}-1\right)\left(\eta-1\right)} \\
\elemline
18 & \inelemthree{-\quart\left(\eta^{2}-1\right)\left(\zeta+1\right)}
{-\half\eta\left(\xi+1\right)\left(\zeta+1\right)}
{-\quart\left(\xi+1\right)\left(\eta^{2}-1\right)} \\
\elemline
19 & \inelemthree{-\half\xi\left(\eta+1\right)\left(\zeta+1\right)}
{-\quart\left(\xi^{2}-1\right)\left(\zeta+1\right)}
{-\quart\left(\xi^{2}-1\right)\left(\eta+1\right)} \\
\elemline
20 & \inelemthree{ \quart\left(\eta^{2}-1\right)\left(\zeta+1\right)}
{ \half\eta\left(\xi-1\right)\left(\zeta+1\right)}
{ \quart\left(\xi-1\right)\left(\eta^{2}-1\right)} \\
\end{Element_part2}
\begin{QuadPoints}{lcccc}
Coord. \elemcoorthreed & \inquadthree{-\sqrt{\tfrac{3}{5}}}{-\sqrt{\tfrac{3}{5}}} {-\sqrt{\tfrac{3}{5}}} & \inquadthree{-\sqrt{\tfrac{3}{5}}}{-\sqrt{\tfrac{3}{5}}} {0}
& \inquadthree{-\sqrt{\tfrac{3}{5}}}{-\sqrt{\tfrac{3}{5}}} {\sqrt{\tfrac{3}{5}}} & \inquadthree{-\sqrt{\tfrac{3}{5}}}{0} {-\sqrt{\tfrac{3}{5}}} \\
\elemline
Weight & 125/729 & 200/729 & 125/729 & 200/729 \\
\elemline
Coord. \elemcoorthreed & \inquadthree{-\sqrt{\tfrac{3}{5}}}{0}{0} & \inquadthree{-\sqrt{\tfrac{3}{5}}}{0}{\sqrt{\tfrac{3}{5}}}
& \inquadthree{-\sqrt{\tfrac{3}{5}}}{\sqrt{\tfrac{3}{5}}}{-\sqrt{\tfrac{3}{5}}} & \inquadthree{-\sqrt{\tfrac{3}{5}}}{\sqrt{\tfrac{3}{5}}}{0} \\
\elemline
Weight & 320/729 & 200/729 & 125/729 & 200/729 \\
\elemline
Coord. \elemcoorthreed & \inquadthree{-\sqrt{\tfrac{3}{5}}}{\sqrt{\tfrac{3}{5}}}{\sqrt{\tfrac{3}{5}}} & \inquadthree{0}{-\sqrt{\tfrac{3}{5}}}{-\sqrt{\tfrac{3}{5}}}
& \inquadthree{0}{-\sqrt{\tfrac{3}{5}}}{0} & \inquadthree{0}{-\sqrt{\tfrac{3}{5}}}{\sqrt{\tfrac{3}{5}}} \\
\elemline
Weight & 125/729 & 200/729 & 320/729 & 200/729 \\
\elemline
Coord. \elemcoorthreed & \inquadthree{0}{0}{-\sqrt{\tfrac{3}{5}}} & \inquadthree{0}{0}{0}
& \inquadthree{0}{0}{\sqrt{\tfrac{3}{5}}} & \inquadthree{0}{\sqrt{\tfrac{3}{5}}}{-\sqrt{\tfrac{3}{5}}} \\
\elemline
Weight & 320/729 & 512/729 & 320/729 & 200/729 \\
\elemline
Coord. \elemcoorthreed & \inquadthree{0}{\sqrt{\tfrac{3}{5}}}{0} & \inquadthree{0}{\sqrt{\tfrac{3}{5}}}{\sqrt{\tfrac{3}{5}}}
& \inquadthree{\sqrt{\tfrac{3}{5}}}{-\sqrt{\tfrac{3}{5}}}{-\sqrt{\tfrac{3}{5}}} & \inquadthree{\sqrt{\tfrac{3}{5}}}{-\sqrt{\tfrac{3}{5}}}{0} \\
\elemline
Weight & 320/729 & 200/729 & 125/729 & 200/729 \\
\elemline
Coord. \elemcoorthreed & \inquadthree{\sqrt{\tfrac{3}{5}}}{-\sqrt{\tfrac{3}{5}}}{\sqrt{\tfrac{3}{5}}} & \inquadthree{\sqrt{\tfrac{3}{5}}}{0}{-\sqrt{\tfrac{3}{5}}}
& \inquadthree{\sqrt{\tfrac{3}{5}}}{0}{0} & \inquadthree{\sqrt{\tfrac{3}{5}}}{0}{\sqrt{\tfrac{3}{5}}} \\
\elemline
Weight & 125/729 & 200/729 & 320/729 & 200/729 \\
\elemline
Coord. \elemcoorthreed & \inquadthree{\sqrt{\tfrac{3}{5}}}{\sqrt{\tfrac{3}{5}}}{-\sqrt{\tfrac{3}{5}}} & \inquadthree{\sqrt{\tfrac{3}{5}}}{\sqrt{\tfrac{3}{5}}}{0}
& \inquadthree{\sqrt{\tfrac{3}{5}}}{\sqrt{\tfrac{3}{5}}}{\sqrt{\tfrac{3}{5}}} & \\
\elemline
Weight & 125/729 & 200/729 & 125/729 & \\
\end{QuadPoints}
\clearpage
\subsection{Pentahedron 15\index{Elements!3D!Pentahedron 15}}
\begin{Element_part1}{3D}
1 & \inelemthree{-1}{1}{0} & $\half\eta\left(1-\xi\right)\left(2\eta-2-\xi\right)$ \\
\elemline
2 & \inelemthree{-1}{0}{1} & $\half\zeta\left(1-\xi\right)\left(2\zeta-2-\xi\right)$ \\
\elemline
3 & \inelemthree{-1}{0}{0} & $\half\left(\xi-1\right)\left(1-\eta-\zeta\right)\left(\xi+2\eta+2\zeta\right)$ \\
\elemline
4 & \inelemthree{1}{1}{0} & $\half\eta\left(1+\xi\right)\left(2\eta-2+\xi\right)$ \\
\elemline
5 & \inelemthree{1}{0}{1} & $\half\zeta\left(1+\xi\right)\left(2\zeta-2+\xi\right)$ \\
\elemline
6 & \inelemthree{1}{0}{0} & $\half\left(-\xi-1\right)\left(1-\eta-\zeta\right)\left(-\xi+2\eta+2\zeta\right)$ \\
\elemline
7 & \inelemthree{-1}{0.5}{0.5} & $2\eta\zeta\left(1-\xi\right)$ \\
\elemline
8 & \inelemthree{-1}{0}{0.5} & $2\zeta\left(1-\eta-\zeta\right)\left(1-\xi\right)$ \\
\elemline
9 & \inelemthree{-1}{0.5}{0} & $2\eta\left(1-\xi\right)\left(1-\eta-\zeta\right)$ \\
\elemline
10 & \inelemthree{0}{1}{0} & $\eta\left(1-\xi^{2}\right)$ \\
\elemline
11 & \inelemthree{0}{0}{1} & $\zeta\left(1-\xi^{2}\right)$ \\
\elemline
12 & \inelemthree{0}{0}{0} & $\left(1-\xi^{2}\right)\left(1-\eta-\zeta\right)$ \\
\elemline
13 & \inelemthree{1}{0.5}{0.5} & $2\eta\zeta\left(1+\xi\right)$ \\
\elemline
14 & \inelemthree{1}{0}{0.5} & $2\zeta\left(1+\xi\right)\left(1-\eta-\zeta\right)$ \\
\elemline
15 & \inelemthree{1}{0.5}{0} & $2\eta\left(1+\xi\right)\left(1-\eta-\zeta\right)$ \\
\end{Element_part1}
\clearpage
\begin{Element_part2}{3D}
1 & \inelemthree{ \half\eta\left(2\xi-2\eta+1\right)}
{-\half\left(\xi-1\right)\left(4\eta-\xi-2\right)}
{ 0.0} \\
\elemline
2 & \inelemthree{ \half\zeta\left(2\xi-2\zeta+1\right)}
{ 0.0}
{-\half\left(\xi-1\right)\left(4\zeta-\xi-2\right)} \\
\elemline
3 & \inelemthree{-\half\left(2\xi+2\eta+2\zeta-1\right)\left(\eta+\zeta-1\right)}
{-\half\left(\xi-1\right)\left(4\eta+\xi+2\left(2\zeta-1\right)\right)}
{-\half\left(\xi-1\right)\left(4\zeta+\xi+2\left(2\eta-1\right)\right)} \\
\elemline
4 & \inelemthree{ \half\eta\left(2\xi+2\eta-1\right)}
{ \half\left(\xi+1\right)\left(4\eta+\xi-2\right)}
{ 0.0} \\
\elemline
5 & \inelemthree{ \half\zeta\left(2\xi+2\zeta-1\right)}
{ 0.0}
{ \half\left(\xi+1\right)\left(4\zeta+\xi-2\right)} \\
\elemline
6 & \inelemthree{-\half\left(\eta+\zeta-1\right)\left(2\xi-2\eta-2\zeta+1\right)}
{ \half\left(\xi+1\right)\left(4\eta-\xi+2\left(2\zeta-1\right)\right)}
{ \half\left(\xi+1\right)\left(4\zeta-\xi+2\left(2\eta-1\right)\right)} \\
\elemline
7 & \inelemthree{-2\eta\zeta}
{-2\left(\xi-1\right)\zeta}
{-2\left(\xi-1\right)\eta} \\
\elemline
8 & \inelemthree{ 2\zeta\left(\eta+\zeta-1\right)}
{ 2\zeta-\left(\xi-1\right)}
{ 2\left(\xi-1\right)\left(2\zeta+\eta-1\right)} \\
\elemline
9 & \inelemthree{ 2\eta\left(\eta+\zeta-1\right)}
{ 2\left(2\eta+\zeta-1\right)\left(\xi-1\right)}
{ 2\eta\left(\xi-1\right)} \\
\elemline
10 & \inelemthree{-2\xi\eta}
{-\left(\xi^{2}-1\right)}
{ 0.0} \\
\elemline
11 & \inelemthree{-2\xi\zeta}
{ 0.0}
{-\left(\xi^{2}-1\right)} \\
\elemline
12 & \inelemthree{ 2\xi\left(\eta+\zeta-1\right)}
{ \left(\xi^{2}-1\right)}
{ \left(\xi^{2}-1\right)} \\
\elemline
13 & \inelemthree{ 2\eta\zeta}
{ 2\zeta\left(\xi+1\right)}
{ 2\eta\left(\xi+1\right)} \\
\elemline
14 & \inelemthree{-2\zeta\left(\eta+\zeta-1\right)}
{-2\zeta\left(\xi+1\right)}
{-2\left(\xi+1\right)\left(2\zeta+\eta-1\right)} \\
\elemline
15 & \inelemthree{-2\eta\left(\eta+\zeta-1\right)}
{-2\left(2\eta+\zeta-1\right)\left(\xi+1\right)}
{-2\eta\left(\xi+1\right)} \\
\end{Element_part2}
\begin{QuadPoints}{lcccc}
Coord. \elemcoorthreed & \inquadthree{-{\tfrac{1}{\sqrt{3}}}}{\tfrac{1}{3}}{\tfrac{1}{3}} & \inquadthree{-{\tfrac{1}{\sqrt{3}}}}{0.6}{0.2}
& \inquadthree{-{\tfrac{1}{\sqrt{3}}}}{0.2}{0.6} & \inquadthree{-{\tfrac{1}{\sqrt{3}}}}{0.2}{0.2} \\
\elemline
Weight & -27/96 & 25/96 & 25/96 & 25/96 \\
\elemline
Coord. \elemcoorthreed & \inquadthree{{\tfrac{1}{\sqrt{3}}}}{\tfrac{1}{3}}{\tfrac{1}{3}} & \inquadthree{{\tfrac{1}{\sqrt{3}}}}{0.6}{0.2}
& \inquadthree{{\tfrac{1}{\sqrt{3}}}}{0.2}{0.6} & \inquadthree{{\tfrac{1}{\sqrt{3}}}}{0.2}{0.2} \\
\elemline
Weight & -27/96 & 25/96 & 25/96 & 25/96 \\
\end{QuadPoints}
%%% Local Variables:
%%% mode: latex
%%% TeX-master: "manual"
%%% End:
diff --git a/examples/cohesive_element/cohesive_extrinsic/CMakeLists.txt b/examples/cohesive_element/cohesive_extrinsic/CMakeLists.txt
index 9c563e853..b3de41926 100644
--- a/examples/cohesive_element/cohesive_extrinsic/CMakeLists.txt
+++ b/examples/cohesive_element/cohesive_extrinsic/CMakeLists.txt
@@ -1,39 +1,39 @@
#===============================================================================
# @file CMakeLists.txt
#
# @author Seyedeh Mohadeseh Taheri Mousavi <mohadeseh.taherimousavi@epfl.ch>
#
# @date creation: Mon Jan 18 2016
#
# @brief configuration for extrinsic cohesive elements
#
# @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 <http://www.gnu.org/licenses/>.
#
# @section DESCRIPTION
#
#===============================================================================
-add_mesh(cohesive_extrinsic_mesh triangle.geo 2 2)
+#add_mesh(cohesive_extrinsic_mesh triangle.geo 2 2)
register_example(cohesive_extrinsic
SOURCES cohesive_extrinsic.cc
- DEPENDS cohesive_extrinsic_mesh
- FILES_TO_COPY material.dat
+ #DEPENDS cohesive_extrinsic_mesh
+ FILES_TO_COPY material.dat triangle.msh
)
diff --git a/examples/cohesive_element/cohesive_extrinsic/triangle.msh b/examples/cohesive_element/cohesive_extrinsic/triangle.msh
new file mode 100644
index 000000000..9acaf5dc7
--- /dev/null
+++ b/examples/cohesive_element/cohesive_extrinsic/triangle.msh
@@ -0,0 +1,66 @@
+$MeshFormat
+2.2 0 8
+$EndMeshFormat
+$Nodes
+41
+1 1 1 0
+2 -1 1 0
+3 -1 -1 0
+4 1 -1 0
+5 2.750244476601438e-12 1 0
+6 0.5000000000011042 1 0
+7 -0.4999999999986249 1 0
+8 -1 2.750244476601438e-12 0
+9 -1 0.5000000000011042 0
+10 -1 -0.4999999999986249 0
+11 -2.750244476601438e-12 -1 0
+12 -0.5000000000011042 -1 0
+13 0.4999999999986249 -1 0
+14 1 -2.750244476601438e-12 0
+15 1 -0.5000000000011042 0
+16 1 0.4999999999986249 0
+17 0 0 0
+18 0.5000000000013751 0.4999999999986249 0
+19 0.4999999999986249 -0.5000000000013751 0
+20 -0.4999999999986249 0.5000000000013751 0
+21 -0.5000000000013751 -0.4999999999986249 0
+22 1.375122238300719e-12 0.5 0
+23 0.2500000000020627 0.7499999999993124 0
+24 -0.2499999999979373 0.7500000000006876 0
+25 -0.7499999999993124 0.2500000000020627 0
+26 -0.7500000000006876 -0.2499999999979373 0
+27 -0.5 1.375122238300719e-12 0
+28 -0.2500000000020627 -0.7499999999993124 0
+29 0.2499999999979373 -0.7500000000006876 0
+30 -1.375122238300719e-12 -0.5 0
+31 0.5 -1.375122238300719e-12 0
+32 0.7499999999993124 -0.2500000000020627 0
+33 0.7500000000006876 0.2499999999979373 0
+34 0.2499999999993124 -0.2500000000006876 0
+35 0.2500000000006876 0.2499999999993124 0
+36 -0.2499999999993124 0.2500000000006876 0
+37 -0.2500000000006876 -0.2499999999993124 0
+38 0.7500000000006876 0.7499999999993124 0
+39 -0.7499999999993124 0.7500000000006876 0
+40 -0.7500000000006876 -0.7499999999993124 0
+41 0.7499999999993124 -0.7500000000006876 0
+$EndNodes
+$Elements
+16
+1 9 2 7 6 20 18 5 22 23 24
+2 9 2 7 6 20 8 21 25 26 27
+3 9 2 7 6 21 11 19 28 29 30
+4 9 2 7 6 18 19 14 31 32 33
+5 9 2 7 6 17 19 18 34 31 35
+6 9 2 7 6 17 18 20 35 22 36
+7 9 2 7 6 17 21 19 37 30 34
+8 9 2 7 6 17 20 21 36 27 37
+9 9 2 7 6 1 18 14 38 33 16
+10 9 2 7 6 2 20 5 39 24 7
+11 9 2 7 6 3 21 8 40 26 10
+12 9 2 7 6 4 19 11 41 29 13
+13 9 2 7 6 1 5 18 6 23 38
+14 9 2 7 6 2 8 20 9 25 39
+15 9 2 7 6 3 11 21 12 28 40
+16 9 2 7 6 4 14 19 15 32 41
+$EndElements
diff --git a/examples/cohesive_element/cohesive_extrinsic_ig_tg/cohesive_extrinsic_ig_tg.cc b/examples/cohesive_element/cohesive_extrinsic_ig_tg/cohesive_extrinsic_ig_tg.cc
index fd81db9fe..abdd6fb08 100644
--- a/examples/cohesive_element/cohesive_extrinsic_ig_tg/cohesive_extrinsic_ig_tg.cc
+++ b/examples/cohesive_element/cohesive_extrinsic_ig_tg/cohesive_extrinsic_ig_tg.cc
@@ -1,149 +1,149 @@
/**
* @file cohesive_extrinsic_ig_tg.cc
*
* @author Seyedeh Mohadeseh Taheri Mousavi <mohadeseh.taherimousavi@epfl.ch>
* @author Marco Vocialta <marco.vocialta@epfl.ch>
*
* @date creation: Mon Jan 18 2016
*
* @brief Test for considering different cohesive properties for intergranular
* (IG) and
* transgranular (TG) fractures in extrinsic cohesive elements
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "solid_mechanics_model_cohesive.hh"
/* -------------------------------------------------------------------------- */
#include <iostream>
/* -------------------------------------------------------------------------- */
using namespace akantu;
/* -------------------------------------------------------------------------- */
class Velocity : public BC::Dirichlet::DirichletFunctor {
public:
explicit Velocity(SolidMechanicsModel & model, Real vel, BC::Axis ax = _x)
: DirichletFunctor(ax), model(model), vel(vel) {
disp = vel * model.getTimeStep();
}
public:
inline void operator()(UInt node, Vector<bool> & /*flags*/,
Vector<Real> & disp,
const Vector<Real> & coord) const {
Real sign = std::signbit(coord(axis)) ? -1. : 1.;
disp(axis) += sign * this->disp;
model.getVelocity()(node, axis) = sign * vel;
}
private:
SolidMechanicsModel & model;
Real vel, disp;
};
/* -------------------------------------------------------------------------- */
int main(int argc, char * argv[]) {
initialize("material.dat", argc, argv);
const UInt spatial_dimension = 2;
const UInt max_steps = 1000;
Mesh mesh(spatial_dimension);
mesh.read("square.msh");
SolidMechanicsModelCohesive model(mesh);
- MaterialCohesiveRules rules{{{"top", "bottom"}, "tg_cohesive"},
- {{"top", "top"}, "ig_cohesive"},
- {{"bottom", "bottom"}, "ig_cohesive"}};
+ MaterialCohesiveRules rules{{{"btop", "bbottom"}, "tg_cohesive"},
+ {{"btop", "btop"}, "ig_cohesive"},
+ {{"bbottom", "bbottom"}, "ig_cohesive"}};
/// model initialization
auto material_selector =
std::make_shared<MaterialCohesiveRulesSelector>(model, rules);
auto && current_selector = model.getMaterialSelector();
material_selector->setFallback(current_selector);
current_selector.setFallback(
std::make_shared<MeshDataMaterialSelector<std::string>>("physical_names",
model));
model.setMaterialSelector(material_selector);
model.initFull(_analysis_method = _explicit_lumped_mass,
_is_extrinsic = true);
Real time_step = model.getStableTimeStep() * 0.05;
model.setTimeStep(time_step);
std::cout << "Time step: " << time_step << std::endl;
model.assembleMassLumped();
auto & position = mesh.getNodes();
auto & velocity = model.getVelocity();
model.applyBC(BC::Dirichlet::FlagOnly(_y), "top");
model.applyBC(BC::Dirichlet::FlagOnly(_y), "bottom");
model.applyBC(BC::Dirichlet::FlagOnly(_x), "left");
model.applyBC(BC::Dirichlet::FlagOnly(_x), "right");
model.setBaseName("extrinsic");
model.addDumpFieldVector("displacement");
model.addDumpField("velocity");
model.addDumpField("acceleration");
model.addDumpField("internal_force");
model.addDumpField("stress");
model.addDumpField("grad_u");
model.addDumpField("material_index");
model.dump();
/// initial conditions
Real loading_rate = 0.1;
// bar_height = 2
Real VI = loading_rate * 2 * 0.5;
for (auto && data : zip(make_view(position, spatial_dimension),
make_view(velocity, spatial_dimension))) {
std::get<1>(data) = loading_rate * std::get<0>(data);
}
model.dump();
Velocity vely(model, VI, _y);
Velocity velx(model, VI, _x);
/// Main loop
for (UInt s = 1; s <= max_steps; ++s) {
model.applyBC(vely, "top");
model.applyBC(vely, "bottom");
model.applyBC(velx, "left");
model.applyBC(velx, "right");
model.checkCohesiveStress();
model.solveStep();
if (s % 10 == 0) {
model.dump();
std::cout << "passing step " << s << "/" << max_steps << std::endl;
}
}
return 0;
}
diff --git a/examples/cohesive_element/cohesive_intrinsic/CMakeLists.txt b/examples/cohesive_element/cohesive_intrinsic/CMakeLists.txt
index afbdccbd5..a149423a4 100644
--- a/examples/cohesive_element/cohesive_intrinsic/CMakeLists.txt
+++ b/examples/cohesive_element/cohesive_intrinsic/CMakeLists.txt
@@ -1,38 +1,37 @@
#===============================================================================
# @file CMakeLists.txt
#
# @author Seyedeh Mohadeseh Taheri Mousavi <mohadeseh.taherimousavi@epfl.ch>
#
# @date creation: Mon Jan 18 2016
#
# @brief Intrinsic cohesive element configuration
#
# @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 <http://www.gnu.org/licenses/>.
#
# @section DESCRIPTION
#
#===============================================================================
-add_mesh( cohesive_intrinsic_mesh triangle.geo 2 2)
+#add_mesh( cohesive_intrinsic_mesh triangle.geo 2 2)
register_example(cohesive_intrinsic
SOURCES cohesive_intrinsic.cc
- DEPENDS cohesive_intrinsic_mesh
- FILES_TO_COPY material.dat)
-
+ #DEPENDS cohesive_intrinsic_mesh
+ FILES_TO_COPY material.dat triangle.msh)
diff --git a/examples/cohesive_element/cohesive_intrinsic/triangle.msh b/examples/cohesive_element/cohesive_intrinsic/triangle.msh
new file mode 100644
index 000000000..9acaf5dc7
--- /dev/null
+++ b/examples/cohesive_element/cohesive_intrinsic/triangle.msh
@@ -0,0 +1,66 @@
+$MeshFormat
+2.2 0 8
+$EndMeshFormat
+$Nodes
+41
+1 1 1 0
+2 -1 1 0
+3 -1 -1 0
+4 1 -1 0
+5 2.750244476601438e-12 1 0
+6 0.5000000000011042 1 0
+7 -0.4999999999986249 1 0
+8 -1 2.750244476601438e-12 0
+9 -1 0.5000000000011042 0
+10 -1 -0.4999999999986249 0
+11 -2.750244476601438e-12 -1 0
+12 -0.5000000000011042 -1 0
+13 0.4999999999986249 -1 0
+14 1 -2.750244476601438e-12 0
+15 1 -0.5000000000011042 0
+16 1 0.4999999999986249 0
+17 0 0 0
+18 0.5000000000013751 0.4999999999986249 0
+19 0.4999999999986249 -0.5000000000013751 0
+20 -0.4999999999986249 0.5000000000013751 0
+21 -0.5000000000013751 -0.4999999999986249 0
+22 1.375122238300719e-12 0.5 0
+23 0.2500000000020627 0.7499999999993124 0
+24 -0.2499999999979373 0.7500000000006876 0
+25 -0.7499999999993124 0.2500000000020627 0
+26 -0.7500000000006876 -0.2499999999979373 0
+27 -0.5 1.375122238300719e-12 0
+28 -0.2500000000020627 -0.7499999999993124 0
+29 0.2499999999979373 -0.7500000000006876 0
+30 -1.375122238300719e-12 -0.5 0
+31 0.5 -1.375122238300719e-12 0
+32 0.7499999999993124 -0.2500000000020627 0
+33 0.7500000000006876 0.2499999999979373 0
+34 0.2499999999993124 -0.2500000000006876 0
+35 0.2500000000006876 0.2499999999993124 0
+36 -0.2499999999993124 0.2500000000006876 0
+37 -0.2500000000006876 -0.2499999999993124 0
+38 0.7500000000006876 0.7499999999993124 0
+39 -0.7499999999993124 0.7500000000006876 0
+40 -0.7500000000006876 -0.7499999999993124 0
+41 0.7499999999993124 -0.7500000000006876 0
+$EndNodes
+$Elements
+16
+1 9 2 7 6 20 18 5 22 23 24
+2 9 2 7 6 20 8 21 25 26 27
+3 9 2 7 6 21 11 19 28 29 30
+4 9 2 7 6 18 19 14 31 32 33
+5 9 2 7 6 17 19 18 34 31 35
+6 9 2 7 6 17 18 20 35 22 36
+7 9 2 7 6 17 21 19 37 30 34
+8 9 2 7 6 17 20 21 36 27 37
+9 9 2 7 6 1 18 14 38 33 16
+10 9 2 7 6 2 20 5 39 24 7
+11 9 2 7 6 3 21 8 40 26 10
+12 9 2 7 6 4 19 11 41 29 13
+13 9 2 7 6 1 5 18 6 23 38
+14 9 2 7 6 2 8 20 9 25 39
+15 9 2 7 6 3 11 21 12 28 40
+16 9 2 7 6 4 14 19 15 32 41
+$EndElements
diff --git a/examples/explicit/explicit_dynamic.cc b/examples/explicit/explicit_dynamic.cc
index dbb57c5b1..39e61bb08 100644
--- a/examples/explicit/explicit_dynamic.cc
+++ b/examples/explicit/explicit_dynamic.cc
@@ -1,106 +1,107 @@
/**
* @file explicit_dynamic.cc
*
* @author Seyedeh Mohadeseh Taheri Mousavi <mohadeseh.taherimousavi@epfl.ch>
*
* @date creation: Sun Jul 12 2015
* @date last modification: Mon Jan 18 2016
*
* @brief This code refers to the explicit dynamic example from the user manual
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "solid_mechanics_model.hh"
/* -------------------------------------------------------------------------- */
#include <fstream>
/* -------------------------------------------------------------------------- */
using namespace akantu;
int main(int argc, char * argv[]) {
initialize("material.dat", argc, argv);
const UInt spatial_dimension = 3;
const Real pulse_width = 2.;
const Real A = 0.01;
Real time_step;
Real time_factor = 0.8;
UInt max_steps = 1000;
Mesh mesh(spatial_dimension);
if (Communicator::getStaticCommunicator().whoAmI() == 0)
mesh.read("bar.msh");
SolidMechanicsModel model(mesh);
/// model initialization
model.initFull(_analysis_method = _explicit_lumped_mass);
time_step = model.getStableTimeStep();
std::cout << "Time Step = " << time_step * time_factor << "s (" << time_step
<< "s)" << std::endl;
- model.setTimeStep(time_step * time_factor);
+ time_step *= time_factor;
+ model.setTimeStep(time_step);
/// boundary and initial conditions
Array<Real> & displacement = model.getDisplacement();
const Array<Real> & nodes = mesh.getNodes();
for (UInt n = 0; n < mesh.getNbNodes(); ++n) {
Real x = nodes(n) - 2;
// Sinus * Gaussian
Real L = pulse_width;
Real k = 0.1 * 2 * M_PI * 3 / L;
displacement(n) = A * sin(k * x) * exp(-(k * x) * (k * x) / (L * L));
}
std::ofstream energy;
energy.open("energy.csv");
energy << "id,rtime,epot,ekin,tot" << std::endl;
model.setBaseName("explicit_dynamic");
model.addDumpField("displacement");
model.addDumpField("velocity");
model.addDumpField("acceleration");
model.addDumpField("stress");
model.dump();
for (UInt s = 1; s <= max_steps; ++s) {
model.solveStep();
Real epot = model.getEnergy("potential");
Real ekin = model.getEnergy("kinetic");
energy << s << "," << s * time_step << "," << epot << "," << ekin << ","
<< epot + ekin << "," << std::endl;
if (s % 10 == 0)
std::cout << "passing step " << s << "/" << max_steps << std::endl;
model.dump();
}
energy.close();
finalize();
return EXIT_SUCCESS;
}
diff --git a/examples/python/CMakeLists.txt b/examples/python/CMakeLists.txt
index 343e776e5..260733f51 100644
--- a/examples/python/CMakeLists.txt
+++ b/examples/python/CMakeLists.txt
@@ -1,9 +1,11 @@
add_subdirectory(custom-material)
add_subdirectory(dynamics)
add_subdirectory(eigen_modes)
add_subdirectory(plate-hole)
add_subdirectory(stiffness_matrix)
+add_subdirectory(structural_mechanics)
+add_subdirectory(fragmentation)
package_add_files_to_package(
examples/python/README.rst
)
diff --git a/examples/python/cohesive/plate.py b/examples/python/cohesive/plate.py
index d8090a1cc..214f08c52 100644
--- a/examples/python/cohesive/plate.py
+++ b/examples/python/cohesive/plate.py
@@ -1,84 +1,84 @@
#!/usr/bin/env python3
import akantu as aka
import numpy as np
def solve(material_file, mesh_file, traction):
aka.parseInput(material_file)
spatial_dimension = 2
# -------------------------------------------------------------------------
# Initialization
# -------------------------------------------------------------------------
mesh = aka.Mesh(spatial_dimension)
mesh.read(mesh_file)
model = aka.SolidMechanicsModelCohesive(mesh)
model.initFull(_analysis_method=aka._static,
_is_extrinsic=True)
model.initNewSolver(aka._explicit_lumped_mass)
model.setBaseName('plate')
model.addDumpFieldVector('displacement')
model.addDumpFieldVector('external_force')
model.addDumpField('strain')
model.addDumpField('stress')
model.addDumpField('blocked_dofs')
model.setBaseNameToDumper('cohesive elements', 'cohesive')
model.addDumpFieldVectorToDumper('cohesive elements', 'displacement')
model.addDumpFieldToDumper('cohesive elements', 'damage')
model.addDumpFieldVectorToDumper('cohesive elements', 'traction')
model.addDumpFieldVectorToDumper('cohesive elements', 'opening')
# -------------------------------------------------------------------------
# Boundary conditions
# -------------------------------------------------------------------------
model.applyBC(aka.FixedValue(0.0, aka._x), 'XBlocked')
model.applyBC(aka.FixedValue(0.0, aka._y), 'YBlocked')
trac = np.zeros(spatial_dimension)
trac[int(aka._y)] = traction
print('Solve for traction ', traction)
model.getExternalForce()[:] = 0
model.applyBC(aka.FromTraction(trac), 'Traction')
solver = model.getNonLinearSolver('static')
solver.set('max_iterations', 100)
solver.set('threshold', 1e-10)
- solver.set('convergence_type', aka._scc_residual)
+ solver.set("convergence_type", aka.SolveConvergenceCriteria.residual)
model.solveStep('static')
model.dump()
model.dump('cohesive elements')
model.setTimeStep(model.getStableTimeStep()*0.1)
maxsteps = 100
for i in range(0, maxsteps):
print('{0}/{1}'.format(i, maxsteps))
model.checkCohesiveStress()
model.solveStep('explicit_lumped')
if i % 10 == 0:
model.dump()
model.dump('cohesive elements')
# -----------------------------------------------------------------------------
# main
# -----------------------------------------------------------------------------
def main():
mesh_file = 'plate.msh'
material_file = 'material.dat'
traction = .095
solve(material_file, mesh_file, traction)
# -----------------------------------------------------------------------------
if __name__ == '__main__':
main()
diff --git a/examples/python/custom-material/bi-material.py b/examples/python/custom-material/bi-material.py
index d58c12058..823f7dee8 100644
--- a/examples/python/custom-material/bi-material.py
+++ b/examples/python/custom-material/bi-material.py
@@ -1,178 +1,180 @@
import akantu as aka
import numpy as np
# ------------------------------------------------------------------------------
class LocalElastic(aka.Material):
def __init__(self, model, _id):
super().__init__(model, _id)
super().registerParamReal('E',
aka._pat_readable | aka._pat_parsable,
'Youngs modulus')
super().registerParamReal('nu',
aka._pat_readable | aka._pat_parsable,
'Poisson ratio')
# change it to have the initialize wrapped
- super().registerInternal('factor', 1)
- super().registerInternal('quad_coordinates', 2)
+ super().registerInternalReal('factor', 1)
+ super().registerInternalReal('quad_coordinates', 2)
def initMaterial(self):
nu = self.getReal('nu')
E = self.getReal('E')
self.mu = E / (2 * (1 + nu))
self.lame_lambda = nu * E / (
(1. + nu) * (1. - 2. * nu))
# Second Lame coefficient (shear modulus)
self.lame_mu = E / (2. * (1. + nu))
super().initMaterial()
- quad_coords = self.internals["quad_coordinates"]
- factor = self.internals["factor"]
+ quad_coords = self.getInternalReal("quad_coordinates")
+ factor = self.getInternalReal("factor")
model = self.getModel()
model.getFEEngine().computeIntegrationPointsCoordinates(
- quad_coords, self.element_filter)
+ quad_coords, self.getElementFilter())
for elem_type in factor.elementTypes():
factor = factor(elem_type)
coords = quad_coords(elem_type)
factor[:] = 1.
factor[coords[:, 1] < 0.5] = .5
# declares all the parameters that are needed
def getPushWaveSpeed(self, params):
return np.sqrt((self.lame_lambda + 2 * self.lame_mu) / self.rho)
# compute small deformation tensor
@staticmethod
def computeEpsilon(grad_u):
return 0.5 * (grad_u + np.einsum('aij->aji', grad_u))
# constitutive law
def computeStress(self, el_type, ghost_type):
grad_u = self.getGradU(el_type, ghost_type)
sigma = self.getStress(el_type, ghost_type)
n_quads = grad_u.shape[0]
grad_u = grad_u.reshape((n_quads, 2, 2))
- factor = self.internals['factor'](el_type, ghost_type).reshape(n_quads)
+ factor = self.getInternalReal('factor')(
+ el_type, ghost_type).reshape(n_quads)
epsilon = self.computeEpsilon(grad_u)
sigma = sigma.reshape((n_quads, 2, 2))
trace = np.einsum('aii->a', grad_u)
sigma[:, :, :] = (
np.einsum('a,ij->aij', trace,
self.lame_lambda * np.eye(2))
+ 2. * self.lame_mu * epsilon)
sigma[:, :, :] = np.einsum('aij, a->aij', sigma, factor)
# constitutive law tangent modulii
def computeTangentModuli(self, el_type, tangent_matrix, ghost_type):
n_quads = tangent_matrix.shape[0]
tangent = tangent_matrix.reshape(n_quads, 3, 3)
- factor = self.internals['factor'](el_type, ghost_type).reshape(n_quads)
+ factor = self.getInternalReal('factor')(
+ el_type, ghost_type).reshape(n_quads)
Miiii = self.lame_lambda + 2 * self.lame_mu
Miijj = self.lame_lambda
Mijij = self.lame_mu
tangent[:, 0, 0] = Miiii
tangent[:, 1, 1] = Miiii
tangent[:, 0, 1] = Miijj
tangent[:, 1, 0] = Miijj
tangent[:, 2, 2] = Mijij
tangent[:, :, :] = np.einsum('aij, a->aij', tangent, factor)
# computes the energy density
def computePotentialEnergy(self, el_type):
sigma = self.getStress(el_type)
grad_u = self.getGradU(el_type)
nquads = sigma.shape[0]
stress = sigma.reshape(nquads, 2, 2)
grad_u = grad_u.reshape((nquads, 2, 2))
epsilon = self.computeEpsilon(grad_u)
energy_density = self.getPotentialEnergy(el_type)
energy_density[:, 0] = 0.5 * np.einsum('aij,aij->a', stress, epsilon)
# ------------------------------------------------------------------------------
# applies manually the boundary conditions
def applyBC(model):
nbNodes = model.getMesh().getNbNodes()
position = model.getMesh().getNodes()
displacement = model.getDisplacement()
blocked_dofs = model.getBlockedDOFs()
width = 1.
height = 1.
epsilon = 1e-8
for node in range(0, nbNodes):
if((np.abs(position[node, 0]) < epsilon) or # left side
(np.abs(position[node, 0] - width) < epsilon)): # right side
blocked_dofs[node, 0] = True
displacement[node, 0] = 0 * position[node, 0] + 0.
if(np.abs(position[node, 1]) < epsilon): # lower side
blocked_dofs[node, 1] = True
displacement[node, 1] = - 1.
if(np.abs(position[node, 1] - height) < epsilon): # upper side
blocked_dofs[node, 1] = True
displacement[node, 1] = 1.
# register the material to the material factory
def allocator(dim, option, model, id):
return LocalElastic(model, id)
mat_factory = aka.MaterialFactory.getInstance()
mat_factory.registerAllocator("local_elastic", allocator)
# main parameters
spatial_dimension = 2
mesh_file = 'square.msh'
# read mesh
mesh = aka.Mesh(spatial_dimension)
mesh.read(mesh_file)
# parse input file
aka.parseInput('material.dat')
# init the SolidMechanicsModel
model = aka.SolidMechanicsModel(mesh)
model.initFull(_analysis_method=aka._static)
# configure the solver
solver = model.getNonLinearSolver()
solver.set("max_iterations", 2)
solver.set("threshold", 1e-3)
solver.set("convergence_type", aka.SolveConvergenceCriteria.solution)
# prepare the dumper
model.setBaseName("bimaterial")
model.addDumpFieldVector("displacement")
model.addDumpFieldVector("internal_force")
model.addDumpFieldVector("external_force")
model.addDumpField("strain")
model.addDumpField("stress")
# model.addDumpField("factor")
model.addDumpField("blocked_dofs")
# Boundary conditions
applyBC(model)
# solve the problem
model.solveStep()
# dump paraview files
model.dump()
epot = model.getEnergy('potential')
print('Potential energy: ' + str(epot))
diff --git a/examples/python/eigen_modes/eigen_modes.py b/examples/python/eigen_modes/eigen_modes.py
index acee26aa6..5f7b72047 100644
--- a/examples/python/eigen_modes/eigen_modes.py
+++ b/examples/python/eigen_modes/eigen_modes.py
@@ -1,257 +1,264 @@
-import sys
+#!/usr/bin/env python
+
import subprocess
import argparse
import akantu as aka
import numpy as np
-from image_saver import ImageSaver
-import matplotlib.pyplot as plt
from scipy.sparse.linalg import eigsh
from scipy.sparse import csr_matrix
+try:
+ import matplotlib.pyplot as plt
+ from image_saver import ImageSaver
+ has_matplotlib = True
+except ImportError:
+ has_matplotlib = False
# -----------------------------------------------------------------------------
# parser
# -----------------------------------------------------------------------------
parser = argparse.ArgumentParser(description='Eigen mode exo')
parser.add_argument('-m', '--mode_number', type=int,
help='precise the mode to study', default=2)
parser.add_argument('-wL', '--wave_width', type=float,
help='precise the width of the wave for '
'the initial displacement', default=5)
parser.add_argument('-L', '--Lbar', type=float,
help='precise the length of the bar', default=10)
parser.add_argument('-t', '--time_step', type=float,
help='precise the timestep',
default=None)
parser.add_argument('-n', '--max_steps', type=int,
help='precise the number of timesteps',
default=500)
parser.add_argument('-mh', '--mesh_h', type=float,
help='characteristic mesh size',
default=.2)
parser.add_argument('-p', '--plot', action='store_true',
help='plot the results')
args = parser.parse_args()
mode = args.mode_number
wave_width = args.wave_width
time_step = args.time_step
max_steps = args.max_steps
mesh_h = args.mesh_h
Lbar = args.Lbar
plot = args.plot
# -----------------------------------------------------------------------------
# Mesh Generation
# -----------------------------------------------------------------------------
geo_content = """
// Mesh size
h = {0};
""".format(mesh_h)
geo_content += """
h1 = h;
h2 = h;
// Dimensions of the bar
Lx = 10;
Ly = 1;
// ------------------------------------------
// Geometry
// ------------------------------------------
Point(101) = { 0.0, -Ly/2, 0.0, h1};
Point(102) = { Lx, -Ly/2, 0.0, h2};
Point(103) = { Lx, 0., 0.0, h2};
Point(104) = { Lx, Ly/2., 0.0, h2};
Point(105) = { 0.0, Ly/2., 0.0, h1};
Point(106) = { 0.0, 0., 0.0, h1};
Line(101) = {101,102};
Line(102) = {102,103};
Line(103) = {103,104};
Line(104) = {104,105};
Line(105) = {105,106};
Line(106) = {106,101};
Line(107) = {106,103};
Line Loop(108) = {101, 102, -107, 106};
Plane Surface(109) = {108};
Line Loop(110) = {103, 104, 105, 107};
Plane Surface(111) = {110};
Physical Surface(112) = {109, 111};
Transfinite Surface "*";
Recombine Surface "*";
Physical Surface(113) = {111, 109};
Physical Line("XBlocked") = {103, 102};
Physical Line("ImposedVelocity") = {105, 106};
Physical Line("YBlocked") = {104, 101};
"""
mesh_file = 'bar'
with open(mesh_file + '.geo', 'w') as f:
f.write(geo_content)
subprocess.call(['gmsh', '-2', mesh_file + '.geo'])
mesh_file = mesh_file + '.msh'
# -----------------------------------------------------------------------------
# Initialization
# -----------------------------------------------------------------------------
spatial_dimension = 2
aka.parseInput('material.dat')
mesh = aka.Mesh(spatial_dimension)
mesh.read(mesh_file)
model = aka.SolidMechanicsModel(mesh)
model.initFull(aka._implicit_dynamic)
model.setBaseName("waves-{0}".format(mode))
model.addDumpFieldVector("displacement")
model.addDumpFieldVector("acceleration")
model.addDumpFieldVector("velocity")
model.addDumpField("blocked_dofs")
# -----------------------------------------------------------------------------
# Boundary conditions
# -----------------------------------------------------------------------------
internal_force = model.getInternalForce()
displacement = model.getDisplacement()
acceleration = model.getAcceleration()
velocity = model.getVelocity()
blocked_dofs = model.getBlockedDOFs()
nbNodes = mesh.getNbNodes()
position = mesh.getNodes()
model.applyBC(aka.FixedValue(0.0, aka._x), "XBlocked")
model.applyBC(aka.FixedValue(0.0, aka._y), "YBlocked")
# ------------------------------------------------------------------------
# timestep value computation
# ------------------------------------------------------------------------
time_factor = 0.8
stable_time_step = model.getStableTimeStep() * time_factor
if time_step:
print("Required Time Step = {0}".format(time_step))
if stable_time_step * time_factor < time_step:
print("Stable Time Step = {0}".format(stable_time_step))
raise RuntimeError("required time_step too large")
print("Required Time Step = {0}".format(time_step))
else:
print("Stable Time Step = {0}".format(stable_time_step))
time_step = stable_time_step * time_factor
model.setTimeStep(time_step)
-disp_sav = ImageSaver(mesh, displacement, 0, Lbar)
-velo_sav = ImageSaver(mesh, velocity, 0, Lbar)
-
# ------------------------------------------------------------------------
# compute the eigen modes
# ------------------------------------------------------------------------
model.assembleStiffnessMatrix()
model.assembleMass()
stiff = model.getDOFManager().getMatrix('K')
stiff = aka.AkantuSparseMatrix(stiff).toarray()
mass = model.getDOFManager().getMatrix('M')
mass = aka.AkantuSparseMatrix(mass).toarray()
# select the non blocked DOFs by index in the mask
mask = np.equal(blocked_dofs.flatten(), False)
Mass_star = mass[mask, :]
Mass_star = csr_matrix(Mass_star[:, mask].copy())
K_star = stiff[mask, :]
K_star = csr_matrix(K_star[:, mask].copy())
print('getting the eigen values')
vals, vects = eigsh(K_star, M=Mass_star, which='SM', k=20)
# -----------------------------------------------------------------------------
# import the initial conditions in displacement
# -----------------------------------------------------------------------------
displacement.reshape(nbNodes*2)[mask] = vects[:, mode]
with open('modes.txt', 'a') as f:
f.write('{0} {1}\n'.format(mode, vals[mode]))
model.dump()
# -----------------------------------------------------------------------------
# prepare the storage of the dynamical evolution
# -----------------------------------------------------------------------------
e_p = np.zeros(max_steps + 1)
e_k = np.zeros(max_steps + 1)
e_t = np.zeros(max_steps + 1)
time = np.zeros(max_steps + 1)
norm = np.zeros(max_steps + 1)
epot = model.getEnergy('potential')
ekin = model.getEnergy('kinetic')
e_p[0] = epot
e_k[0] = ekin
e_t[0] = epot + ekin
time[0] = 0
+if has_matplotlib:
+ disp_sav = ImageSaver(mesh, displacement, 0, Lbar)
+ velo_sav = ImageSaver(mesh, velocity, 0, Lbar)
+
+
# -----------------------------------------------------------------------------
# loop for evolution of motion dynamics
# -----------------------------------------------------------------------------
for step in range(1, max_steps + 1):
model.solveStep()
# outputs
epot = model.getEnergy('potential')
ekin = model.getEnergy('kinetic')
print(step, '/', max_steps, epot, ekin, epot + ekin)
e_p[step] = epot
e_k[step] = ekin
e_t[step] = epot + ekin
time[step] = (step + 1) * time_step
- disp_sav.storeStep()
- velo_sav.storeStep()
+ if has_matplotlib:
+ disp_sav.storeStep()
+ velo_sav.storeStep()
+
if step % 10 == 0:
model.dump()
-if not plot:
- sys.exit(0)
-
-# -----------------------------------------------------------------------------
-# plot figures for global evolution
-# -----------------------------------------------------------------------------
-# energy norms
-plt.figure(1)
-plt.plot(time, e_t, 'r', time, e_p, 'b', time, e_k, 'g')
-
-# space-time diagram for diplacements
-plt.figure(2)
-plt.imshow(disp_sav.getImage(), extent=(0, Lbar, max_steps * time_step, 0))
-plt.xlabel("Space ")
-plt.ylabel("Time ")
-
-# space-time diagram for velocities
-plt.figure(3)
-plt.imshow(velo_sav.getImage(), extent=(0, Lbar, max_steps * time_step, 0))
-plt.xlabel("Velocity")
-plt.ylabel("Time")
-plt.show()
+if plot and has_matplotlib:
+ # --------------------------------------------------------------------------
+ # plot figures for global evolution
+ # --------------------------------------------------------------------------
+ # energy norms
+ plt.figure(1)
+ plt.plot(time, e_t, 'r', time, e_p, 'b', time, e_k, 'g')
+
+ # space-time diagram for diplacements
+ plt.figure(2)
+ plt.imshow(disp_sav.getImage(), extent=(0, Lbar, max_steps * time_step, 0))
+ plt.xlabel("Space ")
+ plt.ylabel("Time ")
+
+ # space-time diagram for velocities
+ plt.figure(3)
+ plt.imshow(velo_sav.getImage(), extent=(0, Lbar, max_steps * time_step, 0))
+ plt.xlabel("Velocity")
+ plt.ylabel("Time")
+ plt.show()
diff --git a/examples/python/fragmentation/CMakeLists.txt b/examples/python/fragmentation/CMakeLists.txt
new file mode 100644
index 000000000..6c63393a0
--- /dev/null
+++ b/examples/python/fragmentation/CMakeLists.txt
@@ -0,0 +1,8 @@
+add_mesh(fragmentation_mesh fragmentation_mesh.geo DIM 2 ORDER 1)
+
+register_example(fragmentation
+ SCRIPT fragmentation.py
+ FILES_TO_COPY material.dat
+ DEPENDS fragmentation_mesh
+ PYTHON
+ )
diff --git a/examples/python/fragmentation/fragmentation.py b/examples/python/fragmentation/fragmentation.py
new file mode 100644
index 000000000..021551a0e
--- /dev/null
+++ b/examples/python/fragmentation/fragmentation.py
@@ -0,0 +1,180 @@
+#!/usr/bin/env python
+# coding: utf-8
+
+# import akantu
+import akantu as aka
+# import numpy for vector manipulation
+import numpy as np
+
+
+# ### Setting up the *SolidMechanicsModelCohesive*
+# We need to read again the material file and the mesh
+# Create the Solid Mechanics Cohesive Model in Akantu
+
+# reading material file
+aka.parseInput('material.dat')
+# creating mesh
+spatial_dimension = 2
+mesh = aka.Mesh(spatial_dimension)
+
+# geometry (as defined in geo file)
+L = 10
+length = L/100
+
+mesh.read('fragmentation_mesh.msh')
+
+# creates the model
+model = aka.SolidMechanicsModelCohesive(mesh)
+model.initFull(_analysis_method=aka._static, _is_extrinsic=True)
+
+
+# Initialize the solver
+# configures the static solver
+solver = model.getNonLinearSolver('static')
+solver.set('max_iterations', 100)
+solver.set('threshold', 1e-10)
+solver.set("convergence_type", aka.SolveConvergenceCriteria.residual)
+
+# Initilize a new solver (explicit Newmark with lumped mass)
+model.initNewSolver(aka._explicit_lumped_mass)
+# Dynamic insertion of cohesive elements
+model.updateAutomaticInsertion()
+
+
+# Implement Boundary and initial conditions
+# Dirichlet Boundary condition
+# model.applyBC(aka.FixedValue(0., aka._x), 'left')
+model.applyBC(aka.FixedValue(0., aka._y), 'bottom')
+
+model.getExternalForce()[:] = 0
+
+
+# ### Generate paraview files
+# Initialization for bulk vizualisation
+model.setBaseName('plate')
+model.addDumpFieldVector('displacement')
+model.addDumpFieldVector('velocity')
+model.addDumpFieldVector('external_force')
+model.addDumpField('strain')
+model.addDumpField('stress')
+model.addDumpField('blocked_dofs')
+
+# Initialization of vizualisation for Cohesive model
+model.setBaseNameToDumper('cohesive elements', 'cohesive')
+model.addDumpFieldVectorToDumper('cohesive elements', 'displacement')
+model.addDumpFieldToDumper('cohesive elements', 'damage')
+model.addDumpFieldVectorToDumper('cohesive elements', 'tractions')
+model.addDumpFieldVectorToDumper('cohesive elements', 'opening')
+
+
+# Custom Dirichlet Boundary Condition to impose constant velocity
+# Boundary functor fixing the displacement as it is
+
+class FixedDisplacement (aka.DirichletFunctor):
+ '''
+ Fix the displacement at its current value
+ '''
+
+ def __init__(self, axis, vel):
+ super().__init__(axis)
+ self.axis = axis
+ self.time = 0
+ self.vel = vel
+
+ def set_time(self, t):
+ self.time = t
+
+ def __call__(self, node, flags, disp, coord):
+ # sets the blocked dofs vector to true in the desired axis
+ flags[int(self.axis)] = True
+ disp[int(self.axis)] = self.vel*self.time
+
+
+functor_r = FixedDisplacement(aka._x, 1e-1)
+model.applyBC(functor_r, 'right')
+functor_l = FixedDisplacement(aka._x, -1e-1)
+model.applyBC(functor_l, 'left')
+
+# Initial condition : velocity gradient:
+# in x = 0 we have -v and in x = L we have +v
+
+nodes = model.getMesh().getNodes()
+vel_field = np.zeros(nodes.shape)
+vel_field[:, 0] = (2*nodes[:, 0]-L)/L*1e-1
+model.getVelocity()[:] = vel_field
+
+
+# ### Run the dynamical simulation
+# Initialize data arrays
+# Energies :
+E_pot = []
+E_kin = []
+E_dis = []
+E_rev = []
+E_con = []
+
+# Stress :
+Stress = []
+
+dt = model.getStableTimeStep()*0.1
+# choose the timestep
+model.setTimeStep(dt)
+# set maximum number of iteration
+maxsteps = 5000
+# solve
+for i in range(0, maxsteps):
+ time = dt*i
+ functor_r.set_time(time)
+ # fix displacements of the right boundary
+ model.applyBC(functor_r, 'right')
+
+ functor_l.set_time(time)
+ # fix displacements of the left boundary
+ model.applyBC(functor_l, 'left')
+
+ if i % 10 == 0:
+ model.dump()
+ model.dump('cohesive elements')
+ pass
+ if i % 50 == 0:
+ print('step {0}/{1}'.format(i, maxsteps))
+ model.checkCohesiveStress()
+ model.solveStep('explicit_lumped')
+
+ Ep = model.getEnergy("potential")
+ Ek = model.getEnergy("kinetic")
+ Ed = model.getEnergy("dissipated")
+ Er = model.getEnergy("reversible")
+ Ec = model.getEnergy("contact")
+
+ E_pot.append(Ep)
+ E_kin.append(Ek)
+ E_dis.append(Ed)
+ E_rev.append(Er)
+ E_con.append(Ec)
+
+ Stress_field = model.getMaterial(0).getStress(aka._triangle_3)
+ Stress_mean = np.mean(Stress_field)
+ Stress.append(Stress_mean)
+
+
+# Use the fragment Manager
+fragment_manager = aka.FragmentManager(model)
+fragment_manager.computeAllData()
+Nb_elem_per_frag = fragment_manager.getNbElementsPerFragment()
+Nb_frag = fragment_manager.getNbFragment()
+print('Nb_frag:', Nb_frag)
+# Average number of elements per fragment
+Nb_elem_mean = np.mean(Nb_elem_per_frag)
+print('average Nb elem / fragment:', Nb_elem_mean)
+# knowing the element size we can get the average fragment size
+s_mean = Nb_elem_mean*length
+print('average fragment size:', s_mean)
+
+
+# ## Plots
+# Plot stress as a function of time
+
+Time = [i*dt for i in range(0, maxsteps)]
+
+Stress_MPa = [x/10**6 for x in Stress]
diff --git a/examples/python/fragmentation/fragmentation_mesh.geo b/examples/python/fragmentation/fragmentation_mesh.geo
new file mode 100644
index 000000000..962715b2e
--- /dev/null
+++ b/examples/python/fragmentation/fragmentation_mesh.geo
@@ -0,0 +1,26 @@
+
+L = 10;
+l = 0.1;
+h = l;
+
+Point(1) = {0, 0, 0, h};
+Point(2) = {L, 0, 0, h};
+Point(3) = {L, l, 0, h};
+Point(4) = {0, l, 0, h};
+
+Line(1) = {1, 2};
+Line(2) = {2, 3};
+Line(3) = {3, 4};
+Line(4) = {4, 1};
+
+Line Loop(1) = {1, 2, 3, 4};
+Plane Surface(1) = {1};
+
+Transfinite Surface "*";
+
+
+Physical Surface("bulk") = {1};
+Physical Line("left") = {4};
+Physical Line("bottom") = {1};
+Physical Line("top") = {3};
+Physical Line("right") = {2};
diff --git a/examples/python/fragmentation/material.dat b/examples/python/fragmentation/material.dat
new file mode 100644
index 000000000..0f9c62397
--- /dev/null
+++ b/examples/python/fragmentation/material.dat
@@ -0,0 +1,24 @@
+
+model solid_mechanics_model_cohesive [
+
+ cohesive_inserter [
+ bounding_box = [[0,10],[-10, 10]]
+ ]
+
+
+ material elastic [
+ name = virtual
+ rho = 1 # density
+ E = 1 # young's modulus
+ nu = 0.3 # poisson's ratio
+ finite_deformation = true
+ ]
+
+ material cohesive_linear [
+ name = cohesive
+ sigma_c = 0.1
+ G_c = 1e-2
+ beta = 1.
+ penalty = 10.
+ ]
+]
diff --git a/examples/python/structural_mechanics/CMakeLists.txt b/examples/python/structural_mechanics/CMakeLists.txt
new file mode 100644
index 000000000..7496e58f4
--- /dev/null
+++ b/examples/python/structural_mechanics/CMakeLists.txt
@@ -0,0 +1,14 @@
+register_example(structural_mechanics.py
+ SCRIPT structural_mechanics.py
+ PYTHON
+ )
+
+register_example(structural_mechanics_softening.py
+ SCRIPT structural_mechanics_softening.py
+ PYTHON
+ )
+
+register_example(structural_mechanics_dynamics.py
+ SCRIPT structural_mechanics_dynamics.py
+ PYTHON
+ )
diff --git a/examples/python/structural_mechanics/structural_mechanics.py b/examples/python/structural_mechanics/structural_mechanics.py
new file mode 100644
index 000000000..d0706beda
--- /dev/null
+++ b/examples/python/structural_mechanics/structural_mechanics.py
@@ -0,0 +1,141 @@
+#!/usr/bin/env python
+# coding: utf-8
+
+# # Test of Structural Mechanics
+# In this example a beam, consisting of two elements, three nodes, is created.
+# The left most node is fixed and a force is applied at the right most node.
+import akantu as aka
+import numpy
+import numpy as np
+try:
+ import matplotlib.pyplot as plt
+ has_matplotlib = True
+except ImportError:
+ has_matplotlib = False
+
+# ### Creating the Mesh
+# Create a mesh for the two dimensional case
+beam = aka.Mesh(2)
+
+# We now create the connectivity array for the beam.
+beam.addConnectivityType(aka._bernoulli_beam_2)
+
+# We need a `MeshAccessor` in order to change the size of the mesh entities.
+beamAcc = aka.MeshAccessor(beam)
+
+# Now we create the array to store the nodes and the connectivities and give
+# them their size.
+beamAcc.resizeConnectivity(2, aka._bernoulli_beam_2)
+beamAcc.resizeNodes(3)
+
+Nodes = beam.getNodes()
+Nodes[0, :] = [0., 0.]
+Nodes[1, :] = [1., 0.]
+Nodes[2, :] = [2., 0.]
+
+# #### Setting the Connections
+Conn = beam.getConnectivity(aka._bernoulli_beam_2)
+Conn[0, :] = [0, 1]
+Conn[1, :] = [1, 2]
+
+# #### Ready
+# We have to make the mesh ready.
+beamAcc.makeReady()
+
+
+# ### Creating the Model
+model = aka.StructuralMechanicsModel(beam)
+
+# #### Setting up the Modell
+# ##### Creating and Inserting the Materials
+mat1 = aka.StructuralMaterial()
+mat1.E = 1e9
+mat1.rho = 1.
+mat1.I = 1. # noqa: E741
+mat1.Iz = 1.
+mat1.Iy = 1.
+mat1.A = 1.
+mat1.GJ = 1.
+model.addMaterial(mat1)
+
+mat2 = aka.StructuralMaterial()
+mat2.E = 1e9
+mat2.rho = 1.
+mat2.I = 1. # noqa: E741
+mat2.Iz = 1.
+mat2.Iy = 1.
+mat2.A = 1.
+mat2.GJ = 1.
+model.addMaterial(mat2)
+
+# ##### Initializing the Model
+model.initFull(aka._implicit_dynamic)
+
+# ##### Assigning the Materials
+materials = model.getElementMaterial(aka._bernoulli_beam_2)
+materials[0][0] = 0
+materials[1][0] = 1
+
+# ##### Setting Boundaries
+
+# Neumann
+# Apply a force of `10` at the last (right most) node.
+forces = model.getExternalForce()
+forces[:] = 0
+forces[2, 0] = 100.
+
+# Dirichlets
+# Block all dofs of the first node, since it is fixed.
+# All other nodes have no restrictions
+boundary = model.getBlockedDOFs()
+boundary[0, :] = True
+boundary[1, :] = False
+boundary[2, :] = False
+
+# ### Solving the System
+
+# Set up the system
+deltaT = 1e-10
+model.setTimeStep(deltaT)
+solver = model.getNonLinearSolver()
+solver.set("max_iterations", 100)
+solver.set("threshold", 1e-8)
+solver.set("convergence_type", aka.SolveConvergenceCriteria.solution)
+
+# Perform N time steps.
+# At each step records the displacement of all three nodes in x direction.
+N = 1000000
+
+disp1 = np.zeros(N)
+disp2 = np.zeros(N)
+disp0 = np.zeros(N)
+times = np.zeros(N)
+
+for i in range(N):
+ model.solveStep()
+ disp = model.getDisplacement()
+ disp0[i] = disp[0, 0]
+ disp1[i] = disp[1, 0]
+ disp2[i] = disp[2, 0]
+ times[i] = deltaT * i
+
+disps = [disp0, disp1, disp2]
+maxMin = [-1.0, 1.0]
+
+for d in disps:
+ maxMin[0] = max(np.max(d), maxMin[0])
+ maxMin[1] = min(np.min(d), maxMin[1])
+
+if has_matplotlib:
+ plt.plot(disp1, times, color='g', label="middle node")
+ plt.plot(disp2, times, color='b', label="right node")
+
+ plt.title("Displacement in $x$ of the nodes")
+ plt.ylabel("Time [S]")
+ plt.xlabel("displacement [m]")
+
+ plt.xlim((maxMin[1] * 1.3, maxMin[0] * 1.1))
+
+ plt.legend()
+
+ plt.show()
diff --git a/examples/python/structural_mechanics/structural_mechanics_dynamics.py b/examples/python/structural_mechanics/structural_mechanics_dynamics.py
new file mode 100644
index 000000000..4a9745a3a
--- /dev/null
+++ b/examples/python/structural_mechanics/structural_mechanics_dynamics.py
@@ -0,0 +1,175 @@
+#!/usr/bin/env python
+# coding: utf-8
+
+import akantu as aka
+import numpy
+import numpy as np
+try:
+ import matplotlib.pyplot as plt
+ has_matplotlib = True
+except ImportError:
+ has_matplotlib = False
+
+# ### Creating the Mesh
+# Create a mesh for the two dimensional case
+el_type = aka._bernoulli_beam_2
+beam = aka.Mesh(2)
+
+# We now create the connectivity array for the beam.
+beam.addConnectivityType(el_type)
+
+# We need a `MeshAccessor` in order to change the size of the mesh entities.
+beamAcc = aka.MeshAccessor(beam)
+
+# Now we create the array to store the nodes and the connectivities and give
+# them their size.
+nb_elem = 40
+L = 2
+beamAcc.resizeConnectivity(nb_elem, el_type)
+beamAcc.resizeNodes(nb_elem + 1)
+
+# #### Setting the Nodes
+Nodes = beam.getNodes()
+length = L / nb_elem
+Nodes[:, :] = 0.
+Nodes[:, 0] = np.arange(nb_elem+1) * length
+
+# #### Setting the Connections
+Conn = beam.getConnectivity(el_type)
+
+for e in range(nb_elem):
+ Conn[e, :] = [e, e + 1]
+
+# #### Ready
+# We have to make the mesh ready.
+beamAcc.makeReady()
+
+# ### Creating the Model
+model = aka.StructuralMechanicsModel(beam)
+
+if el_type == aka._bernoulli_beam_3:
+ normal = beam.getDataReal("extra_normal", el_type)
+
+ for e in range(nb_elem):
+ normal[e, :] = [0, 0, 1]
+
+# #### Setting up the Modell
+# ##### Creating and Inserting the Materials
+mat1 = aka.StructuralMaterial()
+mat1.E = 1e9
+mat1.rho = 10.
+mat1.I = 1. # noqa: E741
+mat1.Iz = 1.
+mat1.Iy = 1.
+mat1.A = 1.
+mat1.GJ = 1.
+model.addMaterial(mat1, 'mat1')
+
+# ##### Initializing the Model
+model.initFull(aka.AnalysisMethod._implicit_dynamic)
+
+# ##### Assigning the Materials
+materials = model.getElementMaterial(el_type)
+materials[:, :] = 0
+
+# ##### Setting Boundaries
+# Neumann
+F = 1e4
+no_print = int(nb_elem / 2)
+
+# Apply a force of `10` at the last (right most) node.
+forces = model.getExternalForce()
+forces[:, :] = 0
+forces[no_print, 1] = F
+
+# Dirichlets
+# Block all dofs of the first node, since it is fixed.
+# All other nodes have no restrictions
+boundary = model.getBlockedDOFs()
+boundary[:, :] = False
+
+boundary[0, 0] = True
+boundary[0, 1] = True
+
+if el_type == aka._bernoulli_beam_3:
+ boundary[0, 2] = True
+
+boundary[nb_elem, 1] = True
+
+# ### Solving the System
+# Set up the system
+deltaT = 1e-6
+model.setTimeStep(deltaT)
+solver = model.getNonLinearSolver()
+solver.set("max_iterations", 100)
+solver.set("threshold", 1e-8)
+solver.set("convergence_type", aka.SolveConvergenceCriteria.solution)
+
+model.assembleMatrix("M")
+M_ = model.getDOFManager().getMatrix("M")
+M = aka.AkantuSparseMatrix(M_)
+
+model.assembleMatrix("K")
+K_ = model.getDOFManager().getMatrix("K")
+K = aka.AkantuSparseMatrix(K_)
+
+C_ = model.getDOFManager().getMatrix("C")
+C_.add(M_, 0.00001)
+C_.add(K_, 0.00001)
+
+
+def analytical_solution(time, L, rho, E, A, I, F): # noqa: E741
+ omega = np.pi**2 / L**2 * np.sqrt(E * I / rho)
+ sum = 0.
+ N = 110
+ for n in range(1, N, 2):
+ sum += (1. - np.cos(n * n * omega * time)) / n**4
+
+ return 2. * F * L**3 / np.pi**4 / E / I * sum
+
+
+# Perform N time steps.
+# At each step records the displacement of all three nodes in x direction.
+N = 900
+
+mat1 = model.getMaterial('mat1')
+
+disp = model.getDisplacement()
+velo = model.getVelocity()
+disp[:, :] = 0.
+
+displs = np.zeros(N)
+
+ekin = np.zeros(N)
+epot = np.zeros(N)
+ework = np.zeros(N)
+_ework = 0.
+
+for i in range(1, N):
+ model.solveStep()
+ displs[i] = disp[no_print, 1]
+
+ _ework += F * velo[no_print, 1] * deltaT
+
+ ekin[i] = model.getEnergy("kinetic")
+ epot[i] = model.getEnergy("potential")
+ ework[i] = _ework
+
+
+def sol(x):
+ return analytical_solution(x, L, mat1.rho, mat1.E,
+ mat1.A, mat1.I, F)
+
+
+if has_matplotlib:
+ times = np.arange(N) * deltaT
+ plt.plot(times, sol(times))
+ plt.plot(times, displs)
+ plt.plot(times, displs - sol(times))
+
+ # What I do not fully understand is why the middle node first go backwards
+ # until it goes forward. I could imagine that there is some vibration,
+ # because everything is in rest.
+ np.max(displs - sol(times))
+ plt.plot(times, ekin+epot)
+ plt.plot(times, ework)
diff --git a/examples/python/structural_mechanics/structural_mechanics_softening.py b/examples/python/structural_mechanics/structural_mechanics_softening.py
new file mode 100644
index 000000000..602852693
--- /dev/null
+++ b/examples/python/structural_mechanics/structural_mechanics_softening.py
@@ -0,0 +1,189 @@
+#!/usr/bin/env python
+# coding: utf-8
+
+# # Test of Structural Mechanics
+# In this test there is a beam consisting of three parts, all have the same materials.
+# The left most node is fixed.
+# On the right most node a force is applied in x direction.
+#
+# After a certain time, the material of the middle _element_ is waekened, lower Young's modulus.
+# In each step the modulus is lowered by a coinstant factor.
+
+import akantu as aka
+import numpy
+import numpy as np
+try:
+ import matplotlib.pyplot as plt
+ has_matplotlib = True
+except ImportError:
+ has_matplotlib = False
+
+
+# ### Creating the Mesh
+# Create a mesh for the two dimensional case
+beam = aka.Mesh(2)
+
+# We now create the connectivity array for the beam.
+beam.addConnectivityType(aka._bernoulli_beam_2)
+
+# We need a `MeshAccessor` in order to change the size of the mesh entities.
+beamAcc = aka.MeshAccessor(beam)
+
+# Now we create the array to store the nodes and the connectivities and give them their size.
+beamAcc.resizeConnectivity(3, aka._bernoulli_beam_2)
+beamAcc.resizeNodes(4)
+
+# #### Setting the Nodes
+Nodes = beam.getNodes()
+Nodes[0, :] = [0., 0.]
+Nodes[1, :] = [1., 0.]
+Nodes[2, :] = [2., 0.]
+Nodes[3, :] = [3., 0.]
+
+
+# Setting the Connections
+Conn = beam.getConnectivity(aka._bernoulli_beam_2)
+Conn[0, :] = [0, 1]
+Conn[1, :] = [1, 2]
+Conn[2, :] = [2, 3]
+
+# Ready
+# We have to make the mesh ready.
+beamAcc.makeReady()
+
+# Creating the Model
+model = aka.StructuralMechanicsModel(beam)
+
+# Setting up the Modell
+# Creating and Inserting the Materials
+mat1 = aka.StructuralMaterial()
+mat1.E = 1e9
+mat1.rho = 1.
+mat1.I = 1. # noqa: E741
+mat1.A = 1.
+mat1.GJ = 1.
+mat1ID = model.addMaterial(mat1, 'mat1')
+
+mat2 = aka.StructuralMaterial()
+mat2.E = 1e9
+mat2.rho = 1.
+mat2.I = 1. # noqa: E741
+mat2.A = 1.
+mat2.GJ = 1.
+mat2ID = model.addMaterial(mat2, 'mat2')
+
+mat3 = aka.StructuralMaterial()
+mat3.E = mat2.E / 100000
+mat3.rho = 1.
+mat3.I = 1. # noqa: E741
+mat3.A = mat2.A / 100
+mat3.GJ = 1.
+mat3ID = model.addMaterial(mat3, 'mat3')
+
+# ##### Initializing the Model
+model.initFull(aka.AnalysisMethod._implicit_dynamic)
+
+# ##### Assigning the Materials
+materials = model.getElementMaterial(aka._bernoulli_beam_2)
+
+materials[0][0] = mat1ID
+materials[1][0] = mat2ID
+materials[2][0] = mat1ID
+
+
+# ##### Setting Boundaries
+# Neumann
+# Apply a force of `10` at the last (right most) node.
+forces = model.getExternalForce()
+forces[:] = 0
+forces[2, 0] = 100.
+
+# Dirichlets
+# Block all dofs of the first node, since it is fixed.
+# All other nodes have no restrictions
+boundary = model.getBlockedDOFs()
+boundary[0, :] = True
+boundary[1, :] = False
+boundary[2, :] = False
+boundary[3, :] = False
+
+# ### Solving the System
+# Set up the system
+deltaT = 1e-9
+model.setTimeStep(deltaT)
+solver = model.getNonLinearSolver()
+solver.set("max_iterations", 100)
+solver.set("threshold", 1e-8)
+solver.set("convergence_type", aka.SolveConvergenceCriteria.solution)
+
+# Perform N time steps.
+# At each step records the displacement of all three nodes in x direction.
+N = 10000 * 60
+
+disp0 = np.zeros(N)
+disp1 = np.zeros(N)
+disp2 = np.zeros(N)
+disp3 = np.zeros(N)
+times = np.zeros(N)
+switchT = None
+switchEnd = None
+
+softDuration = 1000
+SoftStart = (N // 2) - softDuration // 2
+SoftEnd = SoftStart + softDuration
+if(softDuration > 0):
+ softFactor = (model.getMaterial('mat3').E
+ / model.getMaterial('mat2').E) ** (1.0 / softDuration)
+
+mat2 = model.getMaterial('mat2')
+
+for i in range(N):
+ times[i] = deltaT * i
+
+ if((SoftStart <= i <= SoftEnd) and (softDuration > 0)):
+ if switchT is None:
+ switchT = times[i]
+ elif(i == SoftEnd):
+ switchEnd = times[i]
+ #
+ mat2.E *= softFactor
+ #
+
+ model.solveStep()
+ disp = model.getDisplacement()
+ disp0[i] = disp[0, 0]
+ disp1[i] = disp[1, 0]
+ disp2[i] = disp[2, 0]
+ disp3[i] = disp[3, 0]
+
+disps = [disp0, disp1, disp2, disp3]
+maxMin = [-1.0, 1.0]
+
+for d in disps:
+ maxMin[0] = max(np.max(d), maxMin[0])
+ maxMin[1] = min(np.min(d), maxMin[1])
+
+if has_matplotlib:
+ # plt.plot(disp0, times, color='k', label = "left node (fix)")
+ plt.plot(disp1, times, color='g', label="middle, left node")
+ plt.plot(disp2, times, color='g', linestyle='--',
+ label="middle, right node")
+ plt.plot(disp3, times, color='b', label="right node")
+
+ if(softDuration > 0):
+ plt.plot((maxMin[1], maxMin[0]), (switchT, switchT),)
+ plt.plot((maxMin[1], maxMin[0]), (switchEnd, switchEnd), )
+
+ plt.title("Displacement in $x$ of the nodes")
+ plt.ylabel("Time [S]")
+ plt.xlabel("displacement [m]")
+ plt.xlim((maxMin[1] * 1.3, maxMin[0] * 1.1))
+ plt.legend()
+ plt.show()
+
+# If the softening is disabled, then the displacement looks wierd.
+# Because the displacement first increases and then decreases.
+# In this case `softDuration > 0` holds.
+#
+# However if the softening is enabled, it looks rather good. The left middle
+# node will start to vibrate, because it is not pulled in the other direction.
diff --git a/examples/structural_mechanics/CMakeLists.txt b/examples/structural_mechanics/CMakeLists.txt
index 75d257f84..fb1688a8d 100644
--- a/examples/structural_mechanics/CMakeLists.txt
+++ b/examples/structural_mechanics/CMakeLists.txt
@@ -1,37 +1,37 @@
#===============================================================================
# @file CMakeLists.txt
#
# @author Fabian Barras <fabian.barras@epfl.ch>
#
# @date creation: Mon Jan 18 2016
# @date last modification: Tue Jan 19 2016
#
# @brief configuration for structural mechanics example
#
# @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 <http://www.gnu.org/licenses/>.
#
# @section DESCRIPTION
#
#===============================================================================
#===============================================================================
-#register_example(bernoulli_beam_2_exemple
-# SOURCES bernoulli_beam_2_exemple.cc
-# )
+register_example(bernoulli_beam_2_example
+ SOURCES bernoulli_beam_2_example.cc
+ )
diff --git a/examples/structural_mechanics/bernoulli_beam_2_exemple.cc b/examples/structural_mechanics/bernoulli_beam_2_example.cc
similarity index 78%
rename from examples/structural_mechanics/bernoulli_beam_2_exemple.cc
rename to examples/structural_mechanics/bernoulli_beam_2_example.cc
index b825f93e7..adc1d0be5 100644
--- a/examples/structural_mechanics/bernoulli_beam_2_exemple.cc
+++ b/examples/structural_mechanics/bernoulli_beam_2_example.cc
@@ -1,164 +1,153 @@
/**
* @file bernoulli_beam_2_exemple.cc
*
* @author Fabian Barras <fabian.barras@epfl.ch>
*
* @date creation: Mon Jan 18 2016
*
* @brief Computation of the analytical exemple 1.1 in the TGC vol 6
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "structural_mechanics_model.hh"
+#include "mesh_accessor.hh"
/* -------------------------------------------------------------------------- */
#include <iostream>
/* -------------------------------------------------------------------------- */
#define TYPE _bernoulli_beam_2
using namespace akantu;
// Linear load function
-static void lin_load(double * position, double * load,
- __attribute__((unused)) Real * normal,
- __attribute__((unused)) UInt surface_id) {
- memset(load, 0, sizeof(Real) * 3);
- if (position[0] <= 10) {
- load[1] = -6000;
+static void lin_load(const Array<Real> & nodes, Array<Real>& load) {
+ for(auto &&data : zip(make_view(nodes, 2), make_view(load, 3))) {
+ if (std::get<0>(data)[_y] <= 10) {
+ std::get<1>(data)[_y] = -6000;
+ }
}
}
/* -------------------------------------------------------------------------- */
int main(int argc, char * argv[]) {
initialize(argc, argv);
// Defining the mesh
Mesh beams(2);
UInt nb_nodes = 3;
UInt nb_nodes_1 = 1;
UInt nb_nodes_2 = nb_nodes - nb_nodes_1 - 1;
UInt nb_element = nb_nodes - 1;
- Array<Real> & nodes = const_cast<Array<Real> &>(beams.getNodes());
+ MeshAccessor mesh_accessor(beams);
+ Array<Real> & nodes = mesh_accessor.getNodes();
nodes.resize(nb_nodes);
beams.addConnectivityType(_bernoulli_beam_2);
- Array<UInt> & connectivity =
- const_cast<Array<UInt> &>(beams.getConnectivity(_bernoulli_beam_2));
+ Array<UInt> & connectivity = mesh_accessor.getConnectivity(_bernoulli_beam_2);
connectivity.resize(nb_element);
for (UInt i = 0; i < nb_nodes; ++i) {
nodes(i, 1) = 0;
}
for (UInt i = 0; i < nb_nodes_1; ++i) {
nodes(i, 0) = 10. * i / ((Real)nb_nodes_1);
}
nodes(nb_nodes_1, 0) = 10;
for (UInt i = 0; i < nb_nodes_2; ++i) {
nodes(nb_nodes_1 + i + 1, 0) = 10 + 8. * (i + 1) / ((Real)nb_nodes_2);
}
for (UInt i = 0; i < nb_element; ++i) {
connectivity(i, 0) = i;
connectivity(i, 1) = i + 1;
}
+ mesh_accessor.makeReady();
+
// Defining the materials
StructuralMechanicsModel model(beams);
StructuralMaterial mat1;
mat1.E = 3e10;
mat1.I = 0.0025;
mat1.A = 0.01;
model.addMaterial(mat1);
StructuralMaterial mat2;
mat2.E = 3e10;
mat2.I = 0.00128;
mat2.A = 0.01;
model.addMaterial(mat2);
// Defining the forces
model.initFull();
const Real M = -3600; // Momentum at 3
- Array<Real> & forces = model.getForce();
+ Array<Real> & forces = model.getExternalForce();
Array<Real> & displacement = model.getDisplacement();
Array<bool> & boundary = model.getBlockedDOFs();
const Array<Real> & N_M = model.getStress(_bernoulli_beam_2);
Array<UInt> & element_material = model.getElementMaterial(_bernoulli_beam_2);
forces.zero();
displacement.zero();
for (UInt i = 0; i < nb_nodes_2; ++i) {
element_material(i + nb_nodes_1) = 1;
}
forces(nb_nodes - 1, 2) += M;
- model.computeForcesFromFunction<_bernoulli_beam_2>(lin_load, _bft_traction);
+ Array<Real> load(nodes.size(), 3);
+ lin_load(nodes, load);
+
+ model.computeForcesByGlobalTractionArray(load, _bernoulli_beam_2);
// Defining the boundary conditions
boundary(0, 0) = true;
boundary(0, 1) = true;
boundary(0, 2) = true;
boundary(nb_nodes_1, 1) = true;
boundary(nb_nodes - 1, 1) = true;
- // Solve
- Real error;
- model.assembleStiffnessMatrix();
-
- UInt count = 0;
model.addDumpFieldVector("displacement");
model.addDumpField("rotation");
model.addDumpFieldVector("force");
model.addDumpField("momentum");
- do {
- if (count != 0)
- std::cerr << count << " - " << error << std::endl;
- model.updateResidual();
- model.solve();
- count++;
- } while (!model.testConvergenceIncrement(1e-10, error) && count < 10);
- std::cerr << count << " - " << error << std::endl;
-
- /* --------------------------------------------------------------------------
- */
- // Post-Processing
+ model.solveStep();
- model.computeStresses();
+ // Post-Processing
std::cout << " d1 = " << displacement(nb_nodes_1, 2) << std::endl;
std::cout << " d2 = " << displacement(nb_nodes - 1, 2) << std::endl;
std::cout << " M1 = " << N_M(0, 1) << std::endl;
std::cout << " M2 = " << N_M(2 * (nb_nodes - 2), 1) << std::endl;
model.dump();
finalize();
}
diff --git a/extra_packages/extra-materials/src/material_FE2/solid_mechanics_model_RVE.cc b/extra_packages/extra-materials/src/material_FE2/solid_mechanics_model_RVE.cc
index a7eaafa2e..87262a9f1 100644
--- a/extra_packages/extra-materials/src/material_FE2/solid_mechanics_model_RVE.cc
+++ b/extra_packages/extra-materials/src/material_FE2/solid_mechanics_model_RVE.cc
@@ -1,603 +1,602 @@
/**
* @file solid_mechanics_model_RVE.cc
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
* @date Wed Jan 13 15:32:35 2016
*
* @brief Implementation of SolidMechanicsModelRVE
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "solid_mechanics_model_RVE.hh"
#include "element_group.hh"
#include "material_damage_iterative.hh"
#include "node_group.hh"
#include "non_linear_solver.hh"
#include "non_local_manager.hh"
#include "parser.hh"
#include "sparse_matrix.hh"
/* -------------------------------------------------------------------------- */
#include <string>
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
SolidMechanicsModelRVE::SolidMechanicsModelRVE(Mesh & mesh,
bool use_RVE_mat_selector,
UInt nb_gel_pockets, UInt dim,
- const ID & id,
- const MemoryID & memory_id)
- : SolidMechanicsModel(mesh, dim, id, memory_id), volume(0.),
+ const ID & id)
+ : SolidMechanicsModel(mesh, dim, id), volume(0.),
use_RVE_mat_selector(use_RVE_mat_selector),
nb_gel_pockets(nb_gel_pockets), nb_dumps(0) {
AKANTU_DEBUG_IN();
/// find the four corner nodes of the RVE
findCornerNodes();
/// remove the corner nodes from the surface node groups:
/// This most be done because corner nodes a not periodic
mesh.getElementGroup("top").removeNode(corner_nodes(2));
mesh.getElementGroup("top").removeNode(corner_nodes(3));
mesh.getElementGroup("left").removeNode(corner_nodes(3));
mesh.getElementGroup("left").removeNode(corner_nodes(0));
mesh.getElementGroup("bottom").removeNode(corner_nodes(1));
mesh.getElementGroup("bottom").removeNode(corner_nodes(0));
mesh.getElementGroup("right").removeNode(corner_nodes(2));
mesh.getElementGroup("right").removeNode(corner_nodes(1));
const auto & bottom = mesh.getElementGroup("bottom").getNodeGroup();
bottom_nodes.insert(bottom.begin(), bottom.end());
const auto & left = mesh.getElementGroup("left").getNodeGroup();
left_nodes.insert(left.begin(), left.end());
// /// enforce periodicity on the displacement fluctuations
// auto surface_pair_1 = std::make_pair("top", "bottom");
// auto surface_pair_2 = std::make_pair("right", "left");
// SurfacePairList surface_pairs_list;
// surface_pairs_list.push_back(surface_pair_1);
// surface_pairs_list.push_back(surface_pair_2);
// TODO: To Nicolas correct the PBCs
// this->setPBC(surface_pairs_list);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
SolidMechanicsModelRVE::~SolidMechanicsModelRVE() = default;
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelRVE::initFullImpl(const ModelOptions & options) {
AKANTU_DEBUG_IN();
auto options_cp(options);
options_cp.analysis_method = AnalysisMethod::_static;
SolidMechanicsModel::initFullImpl(options_cp);
// this->initMaterials();
auto & fem = this->getFEEngine("SolidMechanicsFEEngine");
/// compute the volume of the RVE
GhostType gt = _not_ghost;
for (auto element_type :
this->mesh.elementTypes(spatial_dimension, gt, _ek_not_defined)) {
Array<Real> Volume(this->mesh.getNbElement(element_type) *
fem.getNbIntegrationPoints(element_type),
1, 1.);
this->volume = fem.integrate(Volume, element_type);
}
std::cout << "The volume of the RVE is " << this->volume << std::endl;
/// dumping
std::stringstream base_name;
- base_name << this->id; // << this->memory_id - 1;
+ base_name << this->id;
this->setBaseName(base_name.str());
this->addDumpFieldVector("displacement");
this->addDumpField("stress");
this->addDumpField("grad_u");
this->addDumpField("eigen_grad_u");
this->addDumpField("blocked_dofs");
this->addDumpField("material_index");
this->addDumpField("damage");
this->addDumpField("Sc");
this->addDumpField("external_force");
this->addDumpField("equivalent_stress");
this->addDumpField("internal_force");
this->addDumpField("delta_T");
this->dump();
this->nb_dumps += 1;
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelRVE::applyBoundaryConditions(
const Matrix<Real> & displacement_gradient) {
AKANTU_DEBUG_IN();
/// get the position of the nodes
const Array<Real> & pos = mesh.getNodes();
/// storage for the coordinates of a given node and the displacement that will
/// be applied
Vector<Real> x(spatial_dimension);
Vector<Real> appl_disp(spatial_dimension);
/// fix top right node
UInt node = this->corner_nodes(2);
x(0) = pos(node, 0);
x(1) = pos(node, 1);
appl_disp.mul<false>(displacement_gradient, x);
(*this->blocked_dofs)(node, 0) = true;
(*this->displacement)(node, 0) = appl_disp(0);
(*this->blocked_dofs)(node, 1) = true;
(*this->displacement)(node, 1) = appl_disp(1);
// (*this->blocked_dofs)(node,0) = true; (*this->displacement)(node,0) = 0.;
// (*this->blocked_dofs)(node,1) = true; (*this->displacement)(node,1) = 0.;
/// apply Hx at all the other corner nodes; H: displ. gradient
node = this->corner_nodes(0);
x(0) = pos(node, 0);
x(1) = pos(node, 1);
appl_disp.mul<false>(displacement_gradient, x);
(*this->blocked_dofs)(node, 0) = true;
(*this->displacement)(node, 0) = appl_disp(0);
(*this->blocked_dofs)(node, 1) = true;
(*this->displacement)(node, 1) = appl_disp(1);
node = this->corner_nodes(1);
x(0) = pos(node, 0);
x(1) = pos(node, 1);
appl_disp.mul<false>(displacement_gradient, x);
(*this->blocked_dofs)(node, 0) = true;
(*this->displacement)(node, 0) = appl_disp(0);
(*this->blocked_dofs)(node, 1) = true;
(*this->displacement)(node, 1) = appl_disp(1);
node = this->corner_nodes(3);
x(0) = pos(node, 0);
x(1) = pos(node, 1);
appl_disp.mul<false>(displacement_gradient, x);
(*this->blocked_dofs)(node, 0) = true;
(*this->displacement)(node, 0) = appl_disp(0);
(*this->blocked_dofs)(node, 1) = true;
(*this->displacement)(node, 1) = appl_disp(1);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelRVE::applyHomogeneousTemperature(
const Real & temperature) {
for (UInt m = 0; m < this->getNbMaterials(); ++m) {
Material & mat = this->getMaterial(m);
const ElementTypeMapArray<UInt> & filter_map = mat.getElementFilter();
// Loop over all element types
for (auto && type : filter_map.elementTypes(spatial_dimension)) {
const Array<UInt> & filter = filter_map(type);
if (filter.size() == 0)
continue;
Array<Real> & delta_T = mat.getArray<Real>("delta_T", type);
Array<Real>::scalar_iterator delta_T_it = delta_T.begin();
Array<Real>::scalar_iterator delta_T_end = delta_T.end();
for (; delta_T_it != delta_T_end; ++delta_T_it) {
*delta_T_it = temperature;
}
}
}
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelRVE::findCornerNodes() {
AKANTU_DEBUG_IN();
// find corner nodes
const auto & position = mesh.getNodes();
const auto & lower_bounds = mesh.getLowerBounds();
const auto & upper_bounds = mesh.getUpperBounds();
AKANTU_DEBUG_ASSERT(spatial_dimension == 2, "This is 2D only!");
corner_nodes.resize(4);
corner_nodes.set(UInt(-1));
for (auto && data : enumerate(make_view(position, spatial_dimension))) {
auto node = std::get<0>(data);
const auto & X = std::get<1>(data);
auto distance = X.distance(lower_bounds);
// node 1
if (Math::are_float_equal(distance, 0)) {
corner_nodes(0) = node;
}
// node 2
else if (Math::are_float_equal(X(_x), upper_bounds(_x)) &&
Math::are_float_equal(X(_y), lower_bounds(_y))) {
corner_nodes(1) = node;
}
// node 3
else if (Math::are_float_equal(X(_x), upper_bounds(_x)) &&
Math::are_float_equal(X(_y), upper_bounds(_y))) {
corner_nodes(2) = node;
}
// node 4
else if (Math::are_float_equal(X(_x), lower_bounds(_x)) &&
Math::are_float_equal(X(_y), upper_bounds(_y))) {
corner_nodes(3) = node;
}
}
for (UInt i = 0; i < corner_nodes.size(); ++i) {
if (corner_nodes(i) == UInt(-1))
AKANTU_ERROR("The corner node " << i + 1 << " wasn't found");
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelRVE::advanceASR(const Matrix<Real> & prestrain) {
AKANTU_DEBUG_IN();
AKANTU_DEBUG_ASSERT(spatial_dimension == 2, "This is 2D only!");
/// apply the new eigenstrain
for (auto element_type :
mesh.elementTypes(spatial_dimension, _not_ghost, _ek_not_defined)) {
Array<Real> & prestrain_vect =
const_cast<Array<Real> &>(this->getMaterial("gel").getInternal<Real>(
"eigen_grad_u")(element_type));
auto prestrain_it =
prestrain_vect.begin(spatial_dimension, spatial_dimension);
auto prestrain_end =
prestrain_vect.end(spatial_dimension, spatial_dimension);
for (; prestrain_it != prestrain_end; ++prestrain_it)
(*prestrain_it) = prestrain;
}
/// advance the damage
MaterialDamageIterative<2> & mat_paste =
dynamic_cast<MaterialDamageIterative<2> &>(*this->materials[1]);
MaterialDamageIterative<2> & mat_aggregate =
dynamic_cast<MaterialDamageIterative<2> &>(*this->materials[0]);
UInt nb_damaged_elements = 0;
Real max_eq_stress_aggregate = 0;
Real max_eq_stress_paste = 0;
auto & solver = this->getNonLinearSolver();
solver.set("max_iterations", 2);
solver.set("threshold", 1e-6);
solver.set("convergence_type", SolveConvergenceCriteria::_solution);
do {
this->solveStep();
/// compute damage
max_eq_stress_aggregate = mat_aggregate.getNormMaxEquivalentStress();
max_eq_stress_paste = mat_paste.getNormMaxEquivalentStress();
nb_damaged_elements = 0;
if (max_eq_stress_aggregate > max_eq_stress_paste)
nb_damaged_elements = mat_aggregate.updateDamage();
else if (max_eq_stress_aggregate < max_eq_stress_paste)
nb_damaged_elements = mat_paste.updateDamage();
else
nb_damaged_elements =
(mat_paste.updateDamage() + mat_aggregate.updateDamage());
std::cout << "the number of damaged elements is " << nb_damaged_elements
<< std::endl;
} while (nb_damaged_elements);
if (this->nb_dumps % 10 == 0) {
this->dump();
}
this->nb_dumps += 1;
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
Real SolidMechanicsModelRVE::averageTensorField(UInt row_index, UInt col_index,
const ID & field_type) {
AKANTU_DEBUG_IN();
auto & fem = this->getFEEngine("SolidMechanicsFEEngine");
Real average = 0;
GhostType gt = _not_ghost;
for (auto element_type :
mesh.elementTypes(spatial_dimension, gt, _ek_not_defined)) {
if (field_type == "stress") {
for (UInt m = 0; m < this->materials.size(); ++m) {
const auto & stress_vec = this->materials[m]->getStress(element_type);
const auto & elem_filter =
this->materials[m]->getElementFilter(element_type);
Array<Real> int_stress_vec(elem_filter.size(),
spatial_dimension * spatial_dimension,
"int_of_stress");
fem.integrate(stress_vec, int_stress_vec,
spatial_dimension * spatial_dimension, element_type,
_not_ghost, elem_filter);
for (UInt k = 0; k < elem_filter.size(); ++k)
average += int_stress_vec(k, row_index * spatial_dimension +
col_index); // 3 is the value
// for the yy (in
// 3D, the value is
// 4)
}
} else if (field_type == "strain") {
for (UInt m = 0; m < this->materials.size(); ++m) {
const auto & gradu_vec = this->materials[m]->getGradU(element_type);
const auto & elem_filter =
this->materials[m]->getElementFilter(element_type);
Array<Real> int_gradu_vec(elem_filter.size(),
spatial_dimension * spatial_dimension,
"int_of_gradu");
fem.integrate(gradu_vec, int_gradu_vec,
spatial_dimension * spatial_dimension, element_type,
_not_ghost, elem_filter);
for (UInt k = 0; k < elem_filter.size(); ++k)
/// averaging is done only for normal components, so stress and strain
/// are equal
average +=
0.5 *
(int_gradu_vec(k, row_index * spatial_dimension + col_index) +
int_gradu_vec(k, col_index * spatial_dimension + row_index));
}
} else if (field_type == "eigen_grad_u") {
for (UInt m = 0; m < this->materials.size(); ++m) {
const auto & eigen_gradu_vec =
this->materials[m]->getInternal<Real>("eigen_grad_u")(element_type);
const auto & elem_filter =
this->materials[m]->getElementFilter(element_type);
Array<Real> int_eigen_gradu_vec(elem_filter.size(),
spatial_dimension * spatial_dimension,
"int_of_gradu");
fem.integrate(eigen_gradu_vec, int_eigen_gradu_vec,
spatial_dimension * spatial_dimension, element_type,
_not_ghost, elem_filter);
for (UInt k = 0; k < elem_filter.size(); ++k)
/// averaging is done only for normal components, so stress and strain
/// are equal
average +=
int_eigen_gradu_vec(k, row_index * spatial_dimension + col_index);
}
} else {
AKANTU_ERROR("Averaging not implemented for this field!!!");
}
}
return average / this->volume;
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelRVE::homogenizeStiffness(Matrix<Real> & C_macro) {
AKANTU_DEBUG_IN();
const UInt dim = 2;
AKANTU_DEBUG_ASSERT(this->spatial_dimension == dim,
"Is only implemented for 2D!!!");
/// apply three independent loading states to determine C
/// 1. eps_el = (1;0;0) 2. eps_el = (0,1,0) 3. eps_el = (0,0,0.5)
/// clear the eigenstrain
Matrix<Real> zero_eigengradu(dim, dim, 0.);
GhostType gt = _not_ghost;
for (auto element_type : mesh.elementTypes(dim, gt, _ek_not_defined)) {
auto & prestrain_vect =
const_cast<Array<Real> &>(this->getMaterial("gel").getInternal<Real>(
"eigen_grad_u")(element_type));
auto prestrain_it =
prestrain_vect.begin(spatial_dimension, spatial_dimension);
auto prestrain_end =
prestrain_vect.end(spatial_dimension, spatial_dimension);
for (; prestrain_it != prestrain_end; ++prestrain_it)
(*prestrain_it) = zero_eigengradu;
}
/// storage for results of 3 different loading states
UInt voigt_size = VoigtHelper<dim>::size;
Matrix<Real> stresses(voigt_size, voigt_size, 0.);
Matrix<Real> strains(voigt_size, voigt_size, 0.);
Matrix<Real> H(dim, dim, 0.);
/// save the damage state before filling up cracks
// ElementTypeMapReal saved_damage("saved_damage");
// saved_damage.initialize(getFEEngine(), _nb_component = 1, _default_value =
// 0);
// this->fillCracks(saved_damage);
/// virtual test 1:
H(0, 0) = 0.01;
this->performVirtualTesting(H, stresses, strains, 0);
/// virtual test 2:
H.zero();
H(1, 1) = 0.01;
this->performVirtualTesting(H, stresses, strains, 1);
/// virtual test 3:
H.zero();
H(0, 1) = 0.01;
this->performVirtualTesting(H, stresses, strains, 2);
/// drain cracks
// this->drainCracks(saved_damage);
/// compute effective stiffness
Matrix<Real> eps_inverse(voigt_size, voigt_size);
eps_inverse.inverse(strains);
C_macro.mul<false, false>(stresses, eps_inverse);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelRVE::performVirtualTesting(const Matrix<Real> & H,
Matrix<Real> & eff_stresses,
Matrix<Real> & eff_strains,
const UInt test_no) {
AKANTU_DEBUG_IN();
this->applyBoundaryConditions(H);
auto & solver = this->getNonLinearSolver();
solver.set("max_iterations", 2);
solver.set("threshold", 1e-6);
solver.set("convergence_type", SolveConvergenceCriteria::_solution);
this->solveStep();
/// get average stress and strain
eff_stresses(0, test_no) = this->averageTensorField(0, 0, "stress");
eff_strains(0, test_no) = this->averageTensorField(0, 0, "strain");
eff_stresses(1, test_no) = this->averageTensorField(1, 1, "stress");
eff_strains(1, test_no) = this->averageTensorField(1, 1, "strain");
eff_stresses(2, test_no) = this->averageTensorField(1, 0, "stress");
eff_strains(2, test_no) = 2. * this->averageTensorField(1, 0, "strain");
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelRVE::homogenizeEigenGradU(
Matrix<Real> & eigen_gradu_macro) {
AKANTU_DEBUG_IN();
eigen_gradu_macro(0, 0) = this->averageTensorField(0, 0, "eigen_grad_u");
eigen_gradu_macro(1, 1) = this->averageTensorField(1, 1, "eigen_grad_u");
eigen_gradu_macro(0, 1) = this->averageTensorField(0, 1, "eigen_grad_u");
eigen_gradu_macro(1, 0) = this->averageTensorField(1, 0, "eigen_grad_u");
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelRVE::initMaterials() {
AKANTU_DEBUG_IN();
// make sure the material are instantiated
if (!are_materials_instantiated)
instantiateMaterials();
if (use_RVE_mat_selector) {
const Vector<Real> & lowerBounds = mesh.getLowerBounds();
const Vector<Real> & upperBounds = mesh.getUpperBounds();
Real bottom = lowerBounds(1);
Real top = upperBounds(1);
Real box_size = std::abs(top - bottom);
Real eps = box_size * 1e-6;
auto tmp = std::make_shared<GelMaterialSelector>(*this, box_size, "gel",
this->nb_gel_pockets, eps);
tmp->setFallback(material_selector);
material_selector = tmp;
}
this->assignMaterialToElements();
// synchronize the element material arrays
this->synchronize(SynchronizationTag::_material_id);
for (auto & material : materials) {
/// init internals properties
const auto type = material->getID();
if (type.find("material_FE2") != std::string::npos)
continue;
material->initMaterial();
}
this->synchronize(SynchronizationTag::_smm_init_mat);
if (this->non_local_manager) {
this->non_local_manager->initialize();
}
// SolidMechanicsModel::initMaterials();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelRVE::fillCracks(ElementTypeMapReal & saved_damage) {
const auto & mat_gel = this->getMaterial("gel");
Real E_gel = mat_gel.get("E");
Real E_homogenized = 0.;
for (auto && mat : materials) {
if (mat->getName() == "gel" || mat->getName() == "FE2_mat")
continue;
Real E = mat->get("E");
auto & damage = mat->getInternal<Real>("damage");
for (auto && type : damage.elementTypes()) {
const auto & elem_filter = mat->getElementFilter(type);
auto nb_integration_point = getFEEngine().getNbIntegrationPoints(type);
auto sav_dam_it =
make_view(saved_damage(type), nb_integration_point).begin();
for (auto && data :
zip(elem_filter, make_view(damage(type), nb_integration_point))) {
auto el = std::get<0>(data);
auto & dam = std::get<1>(data);
Vector<Real> sav_dam = sav_dam_it[el];
sav_dam = dam;
for (auto q : arange(dam.size())) {
E_homogenized = (E_gel - E) * dam(q) + E;
dam(q) = 1. - (E_homogenized / E);
}
}
}
}
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelRVE::drainCracks(
const ElementTypeMapReal & saved_damage) {
for (auto && mat : materials) {
if (mat->getName() == "gel" || mat->getName() == "FE2_mat")
continue;
auto & damage = mat->getInternal<Real>("damage");
for (auto && type : damage.elementTypes()) {
const auto & elem_filter = mat->getElementFilter(type);
auto nb_integration_point = getFEEngine().getNbIntegrationPoints(type);
auto sav_dam_it =
make_view(saved_damage(type), nb_integration_point).begin();
for (auto && data :
zip(elem_filter, make_view(damage(type), nb_integration_point))) {
auto el = std::get<0>(data);
auto & dam = std::get<1>(data);
Vector<Real> sav_dam = sav_dam_it[el];
dam = sav_dam;
}
}
}
}
} // namespace akantu
diff --git a/extra_packages/extra-materials/src/material_FE2/solid_mechanics_model_RVE.hh b/extra_packages/extra-materials/src/material_FE2/solid_mechanics_model_RVE.hh
index 68c37ee23..771229c53 100644
--- a/extra_packages/extra-materials/src/material_FE2/solid_mechanics_model_RVE.hh
+++ b/extra_packages/extra-materials/src/material_FE2/solid_mechanics_model_RVE.hh
@@ -1,232 +1,231 @@
/**
* @file solid_mechanics_model_RVE.hh
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
* @date Wed Jan 13 14:54:18 2016
*
* @brief SMM for RVE computations in FE2 simulations
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_SOLID_MECHANICS_MODEL_RVE_HH_
#define AKANTU_SOLID_MECHANICS_MODEL_RVE_HH_
/* -------------------------------------------------------------------------- */
#include "aka_grid_dynamic.hh"
#include "solid_mechanics_model.hh"
#include <unordered_set>
/* -------------------------------------------------------------------------- */
namespace akantu {
class SolidMechanicsModelRVE : public SolidMechanicsModel {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
SolidMechanicsModelRVE(Mesh & mesh, bool use_RVE_mat_selector = true,
UInt nb_gel_pockets = 400,
UInt spatial_dimension = _all_dimensions,
- const ID & id = "solid_mechanics_model",
- const MemoryID & memory_id = 0);
+ const ID & id = "solid_mechanics_model");
virtual ~SolidMechanicsModelRVE();
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
protected:
void initFullImpl(const ModelOptions & option) override;
/// initialize the materials
void initMaterials() override;
public:
/// apply boundary contions based on macroscopic deformation gradient
virtual void
applyBoundaryConditions(const Matrix<Real> & displacement_gradient);
/// apply homogeneous temperature field from the macroscale level to the RVEs
virtual void applyHomogeneousTemperature(const Real & temperature);
/// advance the reactions -> grow gel and apply homogenized properties
void advanceASR(const Matrix<Real> & prestrain);
/// compute average stress or strain in the model
Real averageTensorField(UInt row_index, UInt col_index,
const ID & field_type);
/// compute effective stiffness of the RVE
void homogenizeStiffness(Matrix<Real> & C_macro);
/// compute average eigenstrain
void homogenizeEigenGradU(Matrix<Real> & eigen_gradu_macro);
/* ------------------------------------------------------------------------ */
/* Data Accessor inherited members */
/* ------------------------------------------------------------------------ */
inline void unpackData(CommunicationBuffer & buffer,
const Array<UInt> & index,
const SynchronizationTag & tag) override;
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
AKANTU_GET_MACRO(CornerNodes, corner_nodes, const Array<UInt> &);
AKANTU_GET_MACRO(Volume, volume, Real);
private:
/// find the corner nodes
void findCornerNodes();
/// perform virtual testing
void performVirtualTesting(const Matrix<Real> & H,
Matrix<Real> & eff_stresses,
Matrix<Real> & eff_strains, const UInt test_no);
void fillCracks(ElementTypeMapReal & saved_damage);
void drainCracks(const ElementTypeMapReal & saved_damage);
/* ------------------------------------------------------------------------ */
/* Members */
/* ------------------------------------------------------------------------ */
/// volume of the RVE
Real volume;
/// corner nodes 1, 2, 3, 4 (see Leonardo's thesis, page 98)
Array<UInt> corner_nodes;
/// bottom nodes
std::unordered_set<UInt> bottom_nodes;
/// left nodes
std::unordered_set<UInt> left_nodes;
/// standard mat selector or user one
bool use_RVE_mat_selector;
/// the number of gel pockets inside the RVE
UInt nb_gel_pockets;
/// dump counter
UInt nb_dumps;
};
inline void SolidMechanicsModelRVE::unpackData(CommunicationBuffer & buffer,
const Array<UInt> & index,
const SynchronizationTag & tag) {
SolidMechanicsModel::unpackData(buffer, index, tag);
// if (tag == SynchronizationTag::_smm_uv) {
// auto disp_it = displacement->begin(spatial_dimension);
//
// for (auto node : index) {
// Vector<Real> current_disp(disp_it[node]);
//
// // if node is at the bottom, u_bottom = u_top +u_2 -u_3
// if (bottom_nodes.count(node)) {
// current_disp += Vector<Real>(disp_it[corner_nodes(1)]);
// current_disp -= Vector<Real>(disp_it[corner_nodes(2)]);
// }
// // if node is at the left, u_left = u_right +u_4 -u_3
// else if (left_nodes.count(node)) {
// current_disp += Vector<Real>(disp_it[corner_nodes(3)]);
// current_disp -= Vector<Real>(disp_it[corner_nodes(2)]);
// }
// }
// }
}
/* -------------------------------------------------------------------------- */
/* ASR material selector */
/* -------------------------------------------------------------------------- */
class GelMaterialSelector : public MeshDataMaterialSelector<std::string> {
public:
GelMaterialSelector(SolidMechanicsModel & model, const Real box_size,
const std::string & gel_material,
const UInt nb_gel_pockets, Real /*tolerance*/ = 0.)
: MeshDataMaterialSelector<std::string>("physical_names", model),
model(model), gel_material(gel_material),
nb_gel_pockets(nb_gel_pockets), nb_placed_gel_pockets(0),
box_size(box_size) {
Mesh & mesh = this->model.getMesh();
UInt spatial_dimension = model.getSpatialDimension();
Element el{_triangle_3, 0, _not_ghost};
UInt nb_element = mesh.getNbElement(el.type, el.ghost_type);
Array<Real> barycenter(nb_element, spatial_dimension);
for (auto && data : enumerate(make_view(barycenter, spatial_dimension))) {
el.element = std::get<0>(data);
auto & bary = std::get<1>(data);
mesh.getBarycenter(el, bary);
}
/// generate the gel pockets
srand(0.);
Vector<Real> center(spatial_dimension);
UInt placed_gel_pockets = 0;
std::set<int> checked_baries;
while (placed_gel_pockets != nb_gel_pockets) {
/// get a random bary center
UInt bary_id = rand() % nb_element;
if (checked_baries.find(bary_id) != checked_baries.end())
continue;
checked_baries.insert(bary_id);
el.element = bary_id;
if (MeshDataMaterialSelector<std::string>::operator()(el) == 1)
continue; /// element belongs to paste
gel_pockets.push_back(el);
placed_gel_pockets += 1;
}
}
UInt operator()(const Element & elem) {
UInt temp_index = MeshDataMaterialSelector<std::string>::operator()(elem);
if (temp_index == 1)
return temp_index;
std::vector<Element>::const_iterator iit = gel_pockets.begin();
std::vector<Element>::const_iterator eit = gel_pockets.end();
if (std::find(iit, eit, elem) != eit) {
nb_placed_gel_pockets += 1;
std::cout << nb_placed_gel_pockets << " gelpockets placed" << std::endl;
return model.getMaterialIndex(gel_material);
;
}
return 0;
}
protected:
SolidMechanicsModel & model;
std::string gel_material;
std::vector<Element> gel_pockets;
UInt nb_gel_pockets;
UInt nb_placed_gel_pockets;
Real box_size;
};
} // namespace akantu
///#include "material_selector_tmpl.hh"
#endif /* AKANTU_SOLID_MECHANICS_MODEL_RVE_HH_ */
diff --git a/extra_packages/igfem/src/integrator_gauss_igfem.hh b/extra_packages/igfem/src/integrator_gauss_igfem.hh
index bee55b2f7..c32092989 100644
--- a/extra_packages/igfem/src/integrator_gauss_igfem.hh
+++ b/extra_packages/igfem/src/integrator_gauss_igfem.hh
@@ -1,124 +1,123 @@
/**
* @file integrator_gauss_igfem.hh
*
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
*
*
* @brief Gauss integration facilities for IGFEM
*
*
* Copyright (©) 2010-2012, 2014 EPFL (Ecole Polytechnique Fédérale de Lausanne)
* Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)
*
*/
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_INTEGRATOR_IGFEM_HH_
#define AKANTU_INTEGRATOR_IGFEM_HH_
/* -------------------------------------------------------------------------- */
#include "integrator.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
template <class IOF> class IntegratorGauss<_ek_igfem, IOF> : public Integrator {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
- IntegratorGauss(const Mesh & mesh, const ID & id = "integrator_gauss",
- const MemoryID & memory_id = 0);
+ IntegratorGauss(const Mesh & mesh, const ID & id = "integrator_gauss");
virtual ~IntegratorGauss(){};
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
inline void initIntegrator(const Array<Real> & nodes,
ElementType type,
GhostType ghost_type);
/// precompute jacobians on elements of type "type"
template <ElementType type>
void precomputeJacobiansOnQuadraturePoints(const Array<Real> & nodes,
GhostType ghost_type);
/// integrate f on the element "elem" of type "type"
template <ElementType type>
inline void integrateOnElement(const Array<Real> & f, Real * intf,
- UInt nb_degree_of_freedom, const UInt elem,
+ UInt nb_degree_of_freedom, UInt elem,
GhostType ghost_type) const;
/// integrate f for all elements of type "type"
template <ElementType type>
void integrate(const Array<Real> & in_f, Array<Real> & intf,
UInt nb_degree_of_freedom, GhostType ghost_type,
const Array<UInt> & filter_elements) const;
/// integrate one element scalar value on all elements of type "type"
template <ElementType type>
Real integrate(const Vector<Real> & in_f, UInt index,
GhostType ghost_type) const;
/// integrate scalar field in_f
template <ElementType type>
Real integrate(const Array<Real> & in_f, GhostType ghost_type,
const Array<UInt> & filter_elements) const;
/// integrate partially around a quadrature point (@f$ intf_q = f_q * J_q *
/// w_q @f$)
template <ElementType type>
void integrateOnIntegrationPoints(const Array<Real> & in_f,
Array<Real> & intf,
UInt nb_degree_of_freedom,
GhostType ghost_type,
const Array<UInt> & filter_elements) const;
/// return a vector with quadrature points natural coordinates
template <ElementType type>
const Matrix<Real> & getIntegrationPoints(GhostType ghost_type) const;
/// return the number of quadrature points for a given element type
template <ElementType type>
inline UInt
getNbIntegrationPoints(GhostType ghost_type = _not_ghost) const;
/// compute the vector of quadrature points natural coordinates
template <ElementType type>
void computeQuadraturePoints(GhostType ghost_type);
/// check that the jacobians are not negative
template <ElementType type>
void checkJacobians(GhostType ghost_type) const;
public:
/// compute the jacobians on quad points for a given element
template <ElementType type>
void computeJacobianOnQuadPointsByElement(const Matrix<Real> & node_coords,
Vector<Real> & jacobians);
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
inline void integrate(Real * f, Real * jac, Real * inte,
UInt nb_degree_of_freedom,
UInt nb_quadrature_points) const;
private:
ElementTypeMap<Matrix<Real>> quadrature_points;
};
/* -------------------------------------------------------------------------- */
/* inline functions */
/* -------------------------------------------------------------------------- */
#include "integrator_gauss_igfem_inline_impl.hh"
} // namespace akantu
#endif /*AKANTU_INTEGRATOR_IGFEM_HH_ */
/* -------------------------------------------------------------------------- */
/* inline functions */
/* -------------------------------------------------------------------------- */
diff --git a/extra_packages/igfem/src/integrator_gauss_igfem_inline_impl.hh b/extra_packages/igfem/src/integrator_gauss_igfem_inline_impl.hh
index 9b92acc8e..376ccba79 100644
--- a/extra_packages/igfem/src/integrator_gauss_igfem_inline_impl.hh
+++ b/extra_packages/igfem/src/integrator_gauss_igfem_inline_impl.hh
@@ -1,451 +1,451 @@
/**
* @file integrator_gauss_igfem.hh
*
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
*
*
* @brief Inline functions of gauss integrator for the case of IGFEM
*
*
* Copyright (©) 2010-2012, 2014 EPFL (Ecole Polytechnique Fédérale de Lausanne)
* Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)
*
*/
/* -------------------------------------------------------------------------- */
} // namespace akantu
#include "fe_engine.hh"
#if defined(AKANTU_DEBUG_TOOLS)
#include "aka_debug_tools.hh"
#endif
namespace akantu {
/* -------------------------------------------------------------------------- */
#define INIT_INTEGRATOR(type) \
computeQuadraturePoints<type>(ghost_type); \
precomputeJacobiansOnQuadraturePoints<type>(nodes, ghost_type); \
checkJacobians<type>(ghost_type);
template <class IOF>
inline void
IntegratorGauss<_ek_igfem, IOF>::initIntegrator(const Array<Real> & nodes,
ElementType type,
GhostType ghost_type) {
AKANTU_BOOST_IGFEM_ELEMENT_SWITCH(INIT_INTEGRATOR);
}
#undef INIT_INTEGRATOR
/* -------------------------------------------------------------------------- */
template <class IOF>
template <ElementType type>
inline UInt IntegratorGauss<_ek_igfem, IOF>::getNbIntegrationPoints(
GhostType) const {
const ElementType sub_type_1 = ElementClassProperty<type>::sub_element_type_1;
const ElementType sub_type_2 = ElementClassProperty<type>::sub_element_type_2;
UInt nb_quad_points_sub_1 =
GaussIntegrationElement<sub_type_1>::getNbQuadraturePoints();
UInt nb_quad_points_sub_2 =
GaussIntegrationElement<sub_type_2>::getNbQuadraturePoints();
return (nb_quad_points_sub_1 + nb_quad_points_sub_2);
}
/* -------------------------------------------------------------------------- */
template <class IOF>
template <ElementType type>
inline void IntegratorGauss<_ek_igfem, IOF>::integrateOnElement(
const Array<Real> & f, Real * intf, UInt nb_degree_of_freedom,
const UInt elem, GhostType ghost_type) const {
Array<Real> & jac_loc = jacobians(type, ghost_type);
UInt nb_quadrature_points = getNbIntegrationPoints<type>();
AKANTU_DEBUG_ASSERT(f.getNbComponent() == nb_degree_of_freedom,
"The vector f do not have the good number of component.");
Real * f_val = f.storage() + elem * f.getNbComponent();
Real * jac_val = jac_loc.storage() + elem * nb_quadrature_points;
integrate(f_val, jac_val, intf, nb_degree_of_freedom, nb_quadrature_points);
}
/* -------------------------------------------------------------------------- */
template <class IOF>
template <ElementType type>
inline Real IntegratorGauss<_ek_igfem, IOF>::integrate(
const Vector<Real> & in_f, UInt index, GhostType ghost_type) const {
const Array<Real> & jac_loc = jacobians(type, ghost_type);
UInt nb_quadrature_points = getNbIntegrationPoints<type>();
AKANTU_DEBUG_ASSERT(in_f.size() == nb_quadrature_points,
"The vector f do not have nb_quadrature_points entries.");
Real * jac_val = jac_loc.storage() + index * nb_quadrature_points;
Real intf;
integrate(in_f.storage(), jac_val, &intf, 1, nb_quadrature_points);
return intf;
return 0.;
}
/* -------------------------------------------------------------------------- */
template <class IOF>
inline void
IntegratorGauss<_ek_igfem, IOF>::integrate(Real * f, Real * jac, Real * inte,
UInt nb_degree_of_freedom,
UInt nb_quadrature_points) const {
memset(inte, 0, nb_degree_of_freedom * sizeof(Real));
Real * cjac = jac;
for (UInt q = 0; q < nb_quadrature_points; ++q) {
for (UInt dof = 0; dof < nb_degree_of_freedom; ++dof) {
inte[dof] += *f * *cjac;
++f;
}
++cjac;
}
}
/* -------------------------------------------------------------------------- */
template <class IOF>
template <ElementType type>
inline const Matrix<Real> &
IntegratorGauss<_ek_igfem, IOF>::getIntegrationPoints(
GhostType ghost_type) const {
AKANTU_DEBUG_ASSERT(
quadrature_points.exists(type, ghost_type),
"Quadrature points for type "
<< quadrature_points.printType(type, ghost_type)
<< " have not been initialized."
<< " Did you use 'computeQuadraturePoints' function ?");
return quadrature_points(type, ghost_type);
}
/* -------------------------------------------------------------------------- */
template <class IOF>
template <ElementType type>
inline void IntegratorGauss<_ek_igfem, IOF>::computeQuadraturePoints(
GhostType ghost_type) {
/// typedef for the two subelement_types and the parent element type
const ElementType sub_type_1 = ElementClassProperty<type>::sub_element_type_1;
const ElementType sub_type_2 = ElementClassProperty<type>::sub_element_type_2;
/// store the quadrature points on the two subelements
Matrix<Real> & quads_sub_1 = quadrature_points(sub_type_1, ghost_type);
Matrix<Real> & quads_sub_2 = quadrature_points(sub_type_2, ghost_type);
quads_sub_1 = GaussIntegrationElement<sub_type_1>::getQuadraturePoints();
quads_sub_2 = GaussIntegrationElement<sub_type_2>::getQuadraturePoints();
/// store all quad points for the current type
UInt nb_quad_points_sub_1 =
GaussIntegrationElement<sub_type_1>::getNbQuadraturePoints();
UInt nb_quad_points_sub_2 =
GaussIntegrationElement<sub_type_2>::getNbQuadraturePoints();
UInt spatial_dimension = mesh.getSpatialDimension();
Matrix<Real> & quads = quadrature_points(type, ghost_type);
quads = Matrix<Real>(spatial_dimension,
nb_quad_points_sub_1 + nb_quad_points_sub_2);
Matrix<Real> quads_1(quads.storage(), quads.rows(), nb_quad_points_sub_1);
quads_1 = quads_sub_1;
Matrix<Real> quads_2(quads.storage() + quads.rows() * nb_quad_points_sub_1,
quads.rows(), nb_quad_points_sub_2);
quads_2 = quads_sub_2;
}
/* -------------------------------------------------------------------------- */
template <class IOF>
template <ElementType type>
inline void
IntegratorGauss<_ek_igfem, IOF>::computeJacobianOnQuadPointsByElement(
const Matrix<Real> & node_coords, Vector<Real> & jacobians) {
/// optimize: get the matrix from the ElementTypeMap
Matrix<Real> quad = GaussIntegrationElement<type>::getQuadraturePoints();
// jacobian
ElementClass<type>::computeJacobian(quad, node_coords, jacobians);
}
/* -------------------------------------------------------------------------- */
template <class IOF>
inline IntegratorGauss<_ek_igfem, IOF>::IntegratorGauss(
- const Mesh & mesh, const ID & id, const MemoryID & memory_id)
- : Integrator(mesh, id, memory_id) {
+ const Mesh & mesh, const ID & id)
+ : Integrator(mesh, id) {
AKANTU_DEBUG_IN();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class IOF>
template <ElementType type>
inline void IntegratorGauss<_ek_igfem, IOF>::checkJacobians(
GhostType ghost_type) const {
AKANTU_DEBUG_IN();
/// typedef for the two subelement_types and the parent element type
const ElementType sub_type_1 = ElementClassProperty<type>::sub_element_type_1;
const ElementType sub_type_2 = ElementClassProperty<type>::sub_element_type_2;
UInt nb_quad_points_sub_1 =
GaussIntegrationElement<sub_type_1>::getNbQuadraturePoints();
UInt nb_quad_points_sub_2 =
GaussIntegrationElement<sub_type_2>::getNbQuadraturePoints();
UInt nb_quadrature_points = nb_quad_points_sub_1 + nb_quad_points_sub_2;
UInt nb_element;
nb_element = mesh.getConnectivity(type, ghost_type).getSize();
Real * jacobians_val = jacobians(type, ghost_type).storage();
for (UInt i = 0; i < nb_element * nb_quadrature_points;
++i, ++jacobians_val) {
if (*jacobians_val < 0)
AKANTU_ERROR(
"Negative jacobian computed,"
<< " possible problem in the element node ordering (Quadrature Point "
<< i % nb_quadrature_points << ":" << i / nb_quadrature_points << ":"
<< type << ":" << ghost_type << ")");
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class IOF>
template <ElementType type>
inline void
IntegratorGauss<_ek_igfem, IOF>::precomputeJacobiansOnQuadraturePoints(
const Array<Real> & nodes, GhostType ghost_type) {
AKANTU_DEBUG_IN();
/// typedef for the two subelement_types and the parent element type
const ElementType sub_type_1 = ElementClassProperty<type>::sub_element_type_1;
const ElementType sub_type_2 = ElementClassProperty<type>::sub_element_type_2;
UInt spatial_dimension = mesh.getSpatialDimension();
UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
/// get the number of nodes for the subelements and the parent element
UInt nb_nodes_sub_1 =
ElementClass<sub_type_1>::getNbNodesPerInterpolationElement();
UInt nb_nodes_sub_2 =
ElementClass<sub_type_2>::getNbNodesPerInterpolationElement();
UInt nb_quadrature_points_sub_1 =
GaussIntegrationElement<sub_type_1>::getNbQuadraturePoints();
UInt nb_quadrature_points_sub_2 =
GaussIntegrationElement<sub_type_2>::getNbQuadraturePoints();
UInt nb_quadrature_points =
nb_quadrature_points_sub_1 + nb_quadrature_points_sub_2;
UInt nb_element = mesh.getNbElement(type, ghost_type);
Array<Real> * jacobians_tmp;
if (!jacobians.exists(type, ghost_type))
jacobians_tmp = &jacobians.alloc(nb_element * nb_quadrature_points, 1, type,
ghost_type);
else {
jacobians_tmp = &jacobians(type, ghost_type);
jacobians_tmp->resize(nb_element * nb_quadrature_points);
}
Array<Real>::vector_iterator jacobians_it =
jacobians_tmp->begin_reinterpret(nb_quadrature_points, nb_element);
Vector<Real> weights_sub_1 =
GaussIntegrationElement<sub_type_1>::getWeights();
Vector<Real> weights_sub_2 =
GaussIntegrationElement<sub_type_2>::getWeights();
Array<Real> x_el(0, spatial_dimension * nb_nodes_per_element);
FEEngine::extractNodalToElementField(mesh, nodes, x_el, type, ghost_type);
Array<Real>::const_matrix_iterator x_it =
x_el.begin(spatial_dimension, nb_nodes_per_element);
// Matrix<Real> local_coord(spatial_dimension, nb_nodes_per_element);
for (UInt elem = 0; elem < nb_element; ++elem, ++jacobians_it, ++x_it) {
const Matrix<Real> & X = *x_it;
Matrix<Real> sub_1_coords(spatial_dimension, nb_nodes_sub_1);
Matrix<Real> sub_2_coords(spatial_dimension, nb_nodes_sub_2);
ElementClass<type>::getSubElementCoords(X, sub_1_coords, 0);
ElementClass<type>::getSubElementCoords(X, sub_2_coords, 1);
Vector<Real> & J = *jacobians_it;
/// initialize vectors to store the jacobians for each subelement
Vector<Real> J_sub_1(nb_quadrature_points_sub_1);
Vector<Real> J_sub_2(nb_quadrature_points_sub_2);
computeJacobianOnQuadPointsByElement<sub_type_1>(sub_1_coords, J_sub_1);
computeJacobianOnQuadPointsByElement<sub_type_2>(sub_2_coords, J_sub_2);
J_sub_1 *= weights_sub_1;
J_sub_2 *= weights_sub_2;
/// copy results into the jacobian vector for this element
for (UInt i = 0; i < nb_quadrature_points_sub_1; ++i) {
J(i) = J_sub_1(i);
}
for (UInt i = 0; i < nb_quadrature_points_sub_2; ++i) {
J(i + nb_quadrature_points_sub_1) = J_sub_2(i);
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class IOF>
template <ElementType type>
inline void IntegratorGauss<_ek_igfem, IOF>::integrate(
const Array<Real> & in_f, Array<Real> & intf, UInt nb_degree_of_freedom,
GhostType ghost_type, const Array<UInt> & filter_elements) const {
AKANTU_DEBUG_IN();
AKANTU_DEBUG_ASSERT(jacobians.exists(type, ghost_type),
"No jacobians for the type "
<< jacobians.printType(type, ghost_type));
const Matrix<Real> & quads = quadrature_points(type, ghost_type);
UInt nb_points = quads.cols();
const Array<Real> & jac_loc = jacobians(type, ghost_type);
Array<Real>::const_matrix_iterator J_it;
Array<Real>::matrix_iterator inte_it;
Array<Real>::const_matrix_iterator f_it;
UInt nb_element;
Array<Real> * filtered_J = NULL;
if (filter_elements != empty_filter) {
nb_element = filter_elements.getSize();
filtered_J = new Array<Real>(0, jac_loc.getNbComponent());
FEEngine::filterElementalData(mesh, jac_loc, *filtered_J, type, ghost_type,
filter_elements);
const Array<Real> & cfiltered_J = *filtered_J; // \todo temporary patch
J_it = cfiltered_J.begin_reinterpret(nb_points, 1, nb_element);
} else {
nb_element = mesh.getNbElement(type, ghost_type);
J_it = jac_loc.begin_reinterpret(nb_points, 1, nb_element);
}
intf.resize(nb_element);
f_it = in_f.begin_reinterpret(nb_degree_of_freedom, nb_points, nb_element);
inte_it = intf.begin_reinterpret(nb_degree_of_freedom, 1, nb_element);
for (UInt el = 0; el < nb_element; ++el, ++J_it, ++f_it, ++inte_it) {
const Matrix<Real> & f = *f_it;
const Matrix<Real> & J = *J_it;
Matrix<Real> & inte_f = *inte_it;
inte_f.mul<false, false>(f, J);
}
delete filtered_J;
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class IOF>
template <ElementType type>
inline Real IntegratorGauss<_ek_igfem, IOF>::integrate(
const Array<Real> & in_f, GhostType ghost_type,
const Array<UInt> & filter_elements) const {
AKANTU_DEBUG_IN();
AKANTU_DEBUG_ASSERT(jacobians.exists(type, ghost_type),
"No jacobians for the type "
<< jacobians.printType(type, ghost_type));
Array<Real> intfv(0, 1);
integrate<type>(in_f, intfv, 1, ghost_type, filter_elements);
UInt nb_values = intfv.getSize();
if (nb_values == 0)
return 0.;
UInt nb_values_to_sum = nb_values >> 1;
std::sort(intfv.begin(), intfv.end());
// as long as the half is not empty
while (nb_values_to_sum) {
UInt remaining = (nb_values - 2 * nb_values_to_sum);
if (remaining)
intfv(nb_values - 2) += intfv(nb_values - 1);
// sum to consecutive values and store the sum in the first half
for (UInt i = 0; i < nb_values_to_sum; ++i) {
intfv(i) = intfv(2 * i) + intfv(2 * i + 1);
}
nb_values = nb_values_to_sum;
nb_values_to_sum >>= 1;
}
AKANTU_DEBUG_OUT();
return intfv(0);
}
/* -------------------------------------------------------------------------- */
template <class IOF>
template <ElementType type>
inline void IntegratorGauss<_ek_igfem, IOF>::integrateOnIntegrationPoints(
const Array<Real> & in_f, Array<Real> & intf, UInt nb_degree_of_freedom,
GhostType ghost_type, const Array<UInt> & filter_elements) const {
AKANTU_DEBUG_IN();
AKANTU_DEBUG_ASSERT(jacobians.exists(type, ghost_type),
"No jacobians for the type "
<< jacobians.printType(type, ghost_type));
UInt nb_element;
const Matrix<Real> & quads = quadrature_points(type, ghost_type);
UInt nb_points = quads.cols();
const Array<Real> & jac_loc = jacobians(type, ghost_type);
Array<Real>::const_scalar_iterator J_it;
Array<Real>::vector_iterator inte_it;
Array<Real>::const_vector_iterator f_it;
Array<Real> * filtered_J = NULL;
if (filter_elements != empty_filter) {
nb_element = filter_elements.getSize();
filtered_J = new Array<Real>(0, jac_loc.getNbComponent());
FEEngine::filterElementalData(mesh, jac_loc, *filtered_J, type, ghost_type,
filter_elements);
J_it = filtered_J->begin();
} else {
nb_element = mesh.getNbElement(type, ghost_type);
J_it = jac_loc.begin();
}
intf.resize(nb_element * nb_points);
f_it = in_f.begin(nb_degree_of_freedom);
inte_it = intf.begin(nb_degree_of_freedom);
for (UInt el = 0; el < nb_element; ++el, ++J_it, ++f_it, ++inte_it) {
const Real & J = *J_it;
const Vector<Real> & f = *f_it;
Vector<Real> & inte_f = *inte_it;
inte_f = f;
inte_f *= J;
}
delete filtered_J;
AKANTU_DEBUG_OUT();
}
diff --git a/extra_packages/igfem/src/non_local_manager_igfem.cc b/extra_packages/igfem/src/non_local_manager_igfem.cc
index 5f1b2b851..c530c2e90 100644
--- a/extra_packages/igfem/src/non_local_manager_igfem.cc
+++ b/extra_packages/igfem/src/non_local_manager_igfem.cc
@@ -1,307 +1,306 @@
/**
* @file non_local_manager_igfem.cc
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
* @date Mon Sep 21 15:32:10 2015
*
* @brief Implementation of non-local manager igfem
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#ifdef AKANTU_DAMAGE_NON_LOCAL
#include "non_local_manager_igfem.hh"
#include "material_non_local.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
NonLocalManagerIGFEM::NonLocalManagerIGFEM(SolidMechanicsModelIGFEM & model,
- const ID & id,
- const MemoryID & memory_id)
- : NonLocalManager(model, id, memory_id) {
+ const ID & id)
+ : NonLocalManager(model, id) {
Mesh & mesh = this->model.getMesh();
/// initialize the element type map array
/// it will be resized to nb_quad * nb_element during the computation of
/// coords
mesh.initElementTypeMapArray(quad_positions, spatial_dimension,
spatial_dimension, false, _ek_igfem, true);
}
/* -------------------------------------------------------------------------- */
-NonLocalManagerIGFEM::~NonLocalManagerIGFEM() {}
+NonLocalManagerIGFEM::~NonLocalManagerIGFEM() =default;
/* -------------------------------------------------------------------------- */
void NonLocalManagerIGFEM::init() {
/// store the number of current ghost elements for each type in the mesh
ElementTypeMap<UInt> nb_ghost_protected;
Mesh & mesh = this->model.getMesh();
for (UInt k = _ek_regular; k <= _ek_igfem; ++k) {
ElementKind el_kind = (ElementKind)k;
Mesh::type_iterator it = mesh.firstType(spatial_dimension, _ghost, el_kind);
Mesh::type_iterator last_type =
mesh.lastType(spatial_dimension, _ghost, el_kind);
for (; it != last_type; ++it)
nb_ghost_protected(mesh.getNbElement(*it, _ghost), *it, _ghost);
}
/// exchange the missing ghosts for the non-local neighborhoods
this->createNeighborhoodSynchronizers();
/// insert the ghost quadrature points of the non-local materials into the
/// non-local neighborhoods
for (UInt m = 0; m < this->non_local_materials.size(); ++m) {
switch (spatial_dimension) {
case 1:
dynamic_cast<MaterialNonLocal<1> &>(*(this->non_local_materials[m]))
.insertQuadsInNeighborhoods(_ghost);
break;
case 2:
dynamic_cast<MaterialNonLocal<2> &>(*(this->non_local_materials[m]))
.insertQuadsInNeighborhoods(_ghost);
break;
case 3:
dynamic_cast<MaterialNonLocal<3> &>(*(this->non_local_materials[m]))
.insertQuadsInNeighborhoods(_ghost);
break;
}
}
FEEngine & fee_regular = this->model.getFEEngine();
FEEngine & fee_igfem = this->model.getFEEngine("IGFEMFEEngine");
this->updatePairLists();
/// cleanup the unneccessary ghost elements
this->cleanupExtraGhostElements(nb_ghost_protected);
this->initElementTypeMap(1, volumes, fee_regular, _ek_regular);
this->initElementTypeMap(1, volumes, fee_igfem, _ek_igfem);
this->setJacobians(fee_regular, _ek_regular);
this->setJacobians(fee_igfem, _ek_igfem);
this->initNonLocalVariables();
this->computeWeights();
}
/* -------------------------------------------------------------------------- */
void NonLocalManagerIGFEM::computeAllNonLocalStresses() {
/// update the flattened version of the internals
std::map<ID, NonLocalVariable *>::iterator non_local_variable_it =
non_local_variables.begin();
std::map<ID, NonLocalVariable *>::iterator non_local_variable_end =
non_local_variables.end();
for (; non_local_variable_it != non_local_variable_end;
++non_local_variable_it) {
non_local_variable_it->second->local.zero();
non_local_variable_it->second->non_local.zero();
for (UInt gt = _not_ghost; gt <= _ghost; ++gt) {
GhostType ghost_type = (GhostType)gt;
this->flattenInternal(non_local_variable_it->second->local, ghost_type,
_ek_regular);
this->flattenInternal(non_local_variable_it->second->local, ghost_type,
_ek_igfem);
}
}
this->volumes.zero();
/// loop over all the neighborhoods and compute intiate the
/// exchange of the non-local_variables
std::set<ID>::const_iterator global_neighborhood_it =
global_neighborhoods.begin();
NeighborhoodMap::iterator it;
for (; global_neighborhood_it != global_neighborhoods.end();
++global_neighborhood_it) {
it = neighborhoods.find(*global_neighborhood_it);
if (it != neighborhoods.end())
it->second->getSynchronizerRegistry().asynchronousSynchronize(
SynchronizationTag::_mnl_for_average);
else
dummy_synchronizers[*global_neighborhood_it]->asynchronousSynchronize(
dummy_accessor, SynchronizationTag::_mnl_for_average);
}
this->averageInternals(_not_ghost);
AKANTU_DEBUG_INFO("Wait distant non local stresses");
/// loop over all the neighborhoods and block until all non-local
/// variables have been exchanged
global_neighborhood_it = global_neighborhoods.begin();
it = neighborhoods.begin();
for (; global_neighborhood_it != global_neighborhoods.end();
++global_neighborhood_it) {
it = neighborhoods.find(*global_neighborhood_it);
if (it != neighborhoods.end())
it->second->getSynchronizerRegistry().waitEndSynchronize(
SynchronizationTag::_mnl_for_average);
else
dummy_synchronizers[*global_neighborhood_it]->waitEndSynchronize(
dummy_accessor, SynchronizationTag::_mnl_for_average);
}
this->averageInternals(_ghost);
/// copy the results in the materials
this->distributeInternals(_ek_regular);
/// loop over all the materials and update the weights
for (UInt m = 0; m < this->non_local_materials.size(); ++m) {
switch (spatial_dimension) {
case 1:
dynamic_cast<MaterialNonLocal<1> &>(*(this->non_local_materials[m]))
.computeNonLocalStresses(_not_ghost);
break;
case 2:
dynamic_cast<MaterialNonLocal<2> &>(*(this->non_local_materials[m]))
.computeNonLocalStresses(_not_ghost);
break;
case 3:
dynamic_cast<MaterialNonLocal<3> &>(*(this->non_local_materials[m]))
.computeNonLocalStresses(_not_ghost);
break;
}
}
++this->compute_stress_calls;
}
/* -------------------------------------------------------------------------- */
void NonLocalManagerIGFEM::cleanupExtraGhostElements(
ElementTypeMap<UInt> & nb_ghost_protected) {
typedef std::set<Element> ElementSet;
ElementSet relevant_ghost_elements;
ElementSet to_keep_per_neighborhood;
/// loop over all the neighborhoods and get their protected ghosts
NeighborhoodMap::iterator neighborhood_it = neighborhoods.begin();
NeighborhoodMap::iterator neighborhood_end = neighborhoods.end();
for (; neighborhood_it != neighborhood_end; ++neighborhood_it) {
neighborhood_it->second->cleanupExtraGhostElements(
to_keep_per_neighborhood);
ElementSet::const_iterator it = to_keep_per_neighborhood.begin();
for (; it != to_keep_per_neighborhood.end(); ++it)
relevant_ghost_elements.insert(*it);
to_keep_per_neighborhood.zero();
}
/// remove all unneccessary ghosts from the mesh
/// Create list of element to remove and new numbering for element to keep
Mesh & mesh = this->model.getMesh();
ElementSet ghost_to_erase;
RemovedElementsEvent remove_elem(mesh);
Element element;
for (UInt k = _ek_regular; k < _ek_igfem; ++k) {
ElementKind el_kind = (ElementKind)k;
element.kind = _ek_igfem;
Mesh::type_iterator it = mesh.firstType(spatial_dimension, _ghost, el_kind);
Mesh::type_iterator last_type =
mesh.lastType(spatial_dimension, _ghost, el_kind);
element.ghost_type = _ghost;
for (; it != last_type; ++it) {
element.type = *it;
UInt nb_ghost_elem = mesh.getNbElement(*it, _ghost);
UInt nb_ghost_elem_protected = 0;
try {
nb_ghost_elem_protected = nb_ghost_protected(*it, _ghost);
} catch (...) {
}
if (!remove_elem.getNewNumbering().exists(*it, _ghost))
remove_elem.getNewNumbering().alloc(nb_ghost_elem, 1, *it, _ghost);
else
remove_elem.getNewNumbering(*it, _ghost).resize(nb_ghost_elem);
Array<UInt> & new_numbering = remove_elem.getNewNumbering(*it, _ghost);
for (UInt g = 0; g < nb_ghost_elem; ++g) {
element.element = g;
if (element.element >= nb_ghost_elem_protected &&
relevant_ghost_elements.find(element) ==
relevant_ghost_elements.end()) {
remove_elem.getList().push_back(element);
new_numbering(element.element) = UInt(-1);
}
}
/// renumber remaining ghosts
UInt ng = 0;
for (UInt g = 0; g < nb_ghost_elem; ++g) {
if (new_numbering(g) != UInt(-1)) {
new_numbering(g) = ng;
++ng;
}
}
}
}
for (UInt k = _ek_regular; k < _ek_igfem; ++k) {
ElementKind el_kind = (ElementKind)k;
Mesh::type_iterator it = mesh.firstType(spatial_dimension, _ghost, el_kind);
Mesh::type_iterator last_type =
mesh.lastType(spatial_dimension, _ghost, el_kind);
for (; it != last_type; ++it) {
UInt nb_elem = mesh.getNbElement(*it, _not_ghost);
if (!remove_elem.getNewNumbering().exists(*it, _not_ghost))
remove_elem.getNewNumbering().alloc(nb_elem, 1, *it, _not_ghost);
Array<UInt> & new_numbering =
remove_elem.getNewNumbering(*it, _not_ghost);
for (UInt e = 0; e < nb_elem; ++e) {
new_numbering(e) = e;
}
}
}
mesh.sendEvent(remove_elem);
}
/* -------------------------------------------------------------------------- */
void NonLocalManagerIGFEM::onElementsAdded(__attribute__((unused))
const Array<Element> & element_list,
__attribute__((unused))
const NewElementsEvent & event) {
FEEngine & fee = this->model.getFEEngine("IGFEMFEEngine");
this->resizeElementTypeMap(1, volumes, fee, _ek_igfem);
this->resizeElementTypeMap(spatial_dimension, quad_positions, fee, _ek_igfem);
NonLocalManager::onElementsAdded(element_list, event);
}
/* -------------------------------------------------------------------------- */
void NonLocalManagerIGFEM::onElementsRemoved(
const Array<Element> & element_list,
const ElementTypeMapArray<UInt> & new_numbering,
__attribute__((unused)) const RemovedElementsEvent & event) {
FEEngine & fee = this->model.getFEEngine("IGFEMFEEngine");
this->removeIntegrationPointsFromMap(event.getNewNumbering(),
spatial_dimension, quad_positions, fee,
_ek_igfem);
this->removeIntegrationPointsFromMap(event.getNewNumbering(), 1, volumes, fee,
_ek_igfem);
NonLocalManager::onElementsRemoved(element_list, new_numbering, event);
}
} // namespace akantu
#endif /* AKANTU_DAMAGE_NON_LOCAL */
diff --git a/extra_packages/igfem/src/non_local_manager_igfem.hh b/extra_packages/igfem/src/non_local_manager_igfem.hh
index 1937a2c86..ae2838523 100644
--- a/extra_packages/igfem/src/non_local_manager_igfem.hh
+++ b/extra_packages/igfem/src/non_local_manager_igfem.hh
@@ -1,97 +1,96 @@
/**
* @file non_local_manager_igfem.hh
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
* @date Mon Sep 21 14:21:33 2015
*
* @brief Class that manages all the non-local neighborhoods for IGFEM
* simulations
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#ifdef AKANTU_DAMAGE_NON_LOCAL
#ifndef AKANTU_NON_LOCAL_MANAGER_IGFEM_HH_
#define AKANTU_NON_LOCAL_MANAGER_IGFEM_HH_
/* -------------------------------------------------------------------------- */
#include "solid_mechanics_model_igfem.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
class NonLocalManagerIGFEM : public NonLocalManager {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
NonLocalManagerIGFEM(SolidMechanicsModelIGFEM & model,
- const ID & id = "non_local_manager_igfem",
- const MemoryID & memory_id = 0);
+ const ID & id = "non_local_manager_igfem");
virtual ~NonLocalManagerIGFEM();
/* --------------------------------------------------------------------------
*/
/* Methods */
/* --------------------------------------------------------------------------
*/
public:
/// initialize the non-local manager: compute pair lists and weights for all
/// neighborhoods
virtual void init();
/// average the internals and compute the non-local stresses
virtual void computeAllNonLocalStresses();
/* --------------------------------------------------------------------------
*/
/* MeshEventHandler inherited members */
/* --------------------------------------------------------------------------
*/
virtual void
onElementsRemoved(const Array<Element> & element_list,
const ElementTypeMapArray<UInt> & new_numbering,
const RemovedElementsEvent & event);
virtual void onElementsAdded(const Array<Element> & element_list,
const NewElementsEvent & event);
private:
/// cleanup unneccessary ghosts
virtual void
cleanupExtraGhostElements(ElementTypeMap<UInt> & nb_ghost_protected);
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
private:
};
} // namespace akantu
/* -------------------------------------------------------------------------- */
/* inline functions */
/* -------------------------------------------------------------------------- */
#endif /* AKANTU_NON_LOCAL_MANAGER_IGFEM_HH_ */
#endif /* AKANTU_DAMAGE_NON_LOCAL */
diff --git a/extra_packages/igfem/src/shape_igfem.cc b/extra_packages/igfem/src/shape_igfem.cc
index d698a9cdb..f75a5407e 100644
--- a/extra_packages/igfem/src/shape_igfem.cc
+++ b/extra_packages/igfem/src/shape_igfem.cc
@@ -1,96 +1,94 @@
/**
* @file shape_igfem_inline_impl.hh
*
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
*
*
* @brief ShapeIGFEM inline implementation
*
*
* Copyright (©) 2010-2012, 2014 EPFL (Ecole Polytechnique Fédérale de Lausanne)
* Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)
*
*/
/* -------------------------------------------------------------------------- */
-#include "aka_memory.hh"
//#include "mesh.hh"
#include "shape_igfem.hh"
/* -------------------------------------------------------------------------- */
#if defined(AKANTU_IGFEM)
namespace akantu {
/* -------------------------------------------------------------------------- */
-ShapeLagrange<_ek_igfem>::ShapeLagrange(const Mesh & mesh, const ID & id,
- const MemoryID & memory_id)
- : ShapeFunctions(mesh, id, memory_id),
- shapes("shapes_generic", id, memory_id),
- shapes_derivatives("shapes_derivatives_generic", id, memory_id),
- igfem_integration_points("igfem_integration_points", id, memory_id),
- shapes_at_enrichments("shapes_at_enrichments", id, memory_id) {
+ShapeLagrange<_ek_igfem>::ShapeLagrange(const Mesh & mesh, const ID & id)
+ : ShapeFunctions(mesh, id),
+ shapes("shapes_generic", id),
+ shapes_derivatives("shapes_derivatives_generic", id),
+ igfem_integration_points("igfem_integration_points", id),
+ shapes_at_enrichments("shapes_at_enrichments", id) {
AKANTU_DEBUG_IN();
AKANTU_DEBUG_OUT();
}
/*-------------------------------------------------------------------------- */
void ShapeLagrange<_ek_igfem>::extractValuesAtStandardNodes(
const Array<Real> & nodal_values, Array<Real> & extracted_values,
GhostType ghost_type) const {
AKANTU_DEBUG_ASSERT(nodal_values.getNbComponent() ==
extracted_values.getNbComponent(),
"The arrays are not of the same size!!!!!");
extracted_values.zero();
UInt spatial_dimension = mesh.getSpatialDimension();
Mesh::type_iterator it =
mesh.firstType(spatial_dimension, ghost_type, _ek_igfem);
Mesh::type_iterator end =
mesh.lastType(spatial_dimension, ghost_type, _ek_igfem);
for (; it != end; ++it) {
ElementType type = *it;
UInt nb_elements = mesh.getNbElement(type, ghost_type);
UInt nb_parent_nodes = 0;
UInt nb_nodes_per_element = 0;
#define GET_NODES_INFO(type) \
const ElementType parent_type = \
ElementClassProperty<type>::parent_element_type; \
nb_parent_nodes = \
ElementClass<parent_type>::getNbNodesPerInterpolationElement(); \
nb_nodes_per_element = \
ElementClass<type>::getNbNodesPerInterpolationElement();
AKANTU_BOOST_IGFEM_ELEMENT_SWITCH(GET_NODES_INFO);
#undef GET_NODES_INFO
UInt * conn_val = mesh.getConnectivity(type, ghost_type).storage();
for (UInt e = 0; e < nb_elements; ++e) {
/// copy the value at standard nodes
UInt offset = e * nb_nodes_per_element;
for (UInt n = 0; n < nb_parent_nodes; ++n) {
UInt node = conn_val[offset + n];
for (UInt i = 0; i < nodal_values.getNbComponent(); ++i)
extracted_values(node, i) = nodal_values(node, i);
}
}
}
}
/* -------------------------------------------------------------------------- */
void ShapeLagrange<_ek_igfem>::printself(std::ostream & stream,
int indent) const {
std::string space;
for (Int i = 0; i < indent; i++, space += AKANTU_INDENT)
;
stream << space << "Shapes Lagrange [" << std::endl;
ShapeFunctions::printself(stream, indent + 1);
shapes.printself(stream, indent + 1);
shapes_derivatives.printself(stream, indent + 1);
stream << space << "]" << std::endl;
}
} // namespace akantu
#endif
diff --git a/extra_packages/igfem/src/shape_igfem.hh b/extra_packages/igfem/src/shape_igfem.hh
index a3db5d3dd..537f6b401 100644
--- a/extra_packages/igfem/src/shape_igfem.hh
+++ b/extra_packages/igfem/src/shape_igfem.hh
@@ -1,200 +1,199 @@
/**
* @file shape_igfem.hh
*
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
*
*
* @brief shape functions for interface-enriched generalized FEM
*
*
* Copyright (©) 2010-2012, 2014 EPFL (Ecole Polytechnique Fédérale de Lausanne)
* Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_array.hh"
#include "shape_functions.hh"
#ifndef AKANTU_SHAPE_IGFEM_HH_
#define AKANTU_SHAPE_IGFEM_HH_
namespace akantu {
/* -------------------------------------------------------------------------- */
template <> class ShapeLagrange<_ek_igfem> : public ShapeFunctions {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
- ShapeLagrange(const Mesh & mesh, const ID & id = "shape_igfem",
- const MemoryID & memory_id = 0);
+ ShapeLagrange(const Mesh & mesh, const ID & id = "shape_igfem");
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
inline void initShapeFunctions(const Array<Real> & nodes,
const Matrix<Real> & integration_points,
const Matrix<Real> & integration_points_1,
const Matrix<Real> & integration_points_2,
ElementType type,
GhostType ghost_type);
inline void
interpolateEnrichmentsAllTypes(const Array<Real> & src, Array<Real> & dst,
ElementType type,
GhostType ghost_type) const;
template <ElementType type>
inline void precomputeShapesOnEnrichedNodes(const Array<Real> & nodes,
GhostType ghost_type);
template <ElementType type>
void interpolateAtEnrichedNodes(const Array<Real> & src, Array<Real> & dst,
GhostType ghost_type) const;
/// pre compute all shapes on the element integration points from natural
/// coordinates
template <ElementType type>
void precomputeShapesOnIntegrationPoints(const Array<Real> & nodes,
GhostType ghost_type);
/// pre compute all shape derivatives on the element integration points from
/// natural coordinates
template <ElementType type>
void precomputeShapeDerivativesOnIntegrationPoints(const Array<Real> & nodes,
GhostType ghost_type);
/// interpolate nodal values on the integration points
template <ElementType type>
void interpolateOnIntegrationPoints(
const Array<Real> & u, Array<Real> & uq, UInt nb_degree_of_freedom,
GhostType ghost_type = _not_ghost,
const Array<UInt> & filter_elements = empty_filter) const;
/// interpolate on physical point
template <ElementType type>
void interpolate(const Vector<Real> & real_coords, UInt elem,
const Matrix<Real> & nodal_values,
Vector<Real> & interpolated,
GhostType ghost_type) const;
/// compute the gradient of u on the integration points
template <ElementType type>
void gradientOnIntegrationPoints(
const Array<Real> & u, Array<Real> & nablauq, UInt nb_degree_of_freedom,
GhostType ghost_type = _not_ghost,
const Array<UInt> & filter_elements = empty_filter) const;
/// multiply a field by shape functions @f$ fts_{ij} = f_i * \varphi_j @f$
template <ElementType type>
void fieldTimesShapes(const Array<Real> & field,
Array<Real> & field_times_shapes,
GhostType ghost_type) const;
/// find natural coords in parent element from real coords provided an element
template <ElementType type>
void inverseMap(const Vector<Real> & real_coords, UInt element,
Vector<Real> & natural_coords,
GhostType ghost_type = _not_ghost) const;
/// find natural coords in sub-element from real coords provided an element
template <ElementType type>
void inverseMap(const Vector<Real> & real_coords, UInt element,
Vector<Real> & natural_coords, UInt sub_element,
GhostType ghost_type = _not_ghost) const;
/// return true if the coordinates provided are inside the element, false
/// otherwise
template <ElementType type>
bool contains(const Vector<Real> & real_coords, UInt elem,
GhostType ghost_type) const;
/// compute the shape on a provided point
template <ElementType type>
void computeShapes(const Vector<Real> & real_coords, UInt elem,
Vector<Real> & shapes, GhostType ghost_type) const;
/// compute the shape derivatives on a provided point
template <ElementType type>
void computeShapeDerivatives(const Matrix<Real> & real_coords, UInt elem,
Tensor3<Real> & shapes,
GhostType ghost_type) const;
/// interpolate a field on a given physical point
template <ElementType type>
void interpolateOnPhysicalPoint(const Vector<Real> & real_coords, UInt elem,
const Array<Real> & field,
Vector<Real> & interpolated,
GhostType ghost_type) const;
/// function to extract values at standard nodes and zero-out enriched values
/// of a nodal field
void extractValuesAtStandardNodes(const Array<Real> & nodal_values,
Array<Real> & extracted_values,
GhostType ghost_type) const;
/// function to print the containt of the class
virtual void printself(std::ostream & stream, int indent = 0) const;
protected:
/// compute the shape derivatives on integration points for a given element
template <ElementType type>
inline void
computeShapeDerivativesOnCPointsByElement(const Matrix<Real> & node_coords,
const Matrix<Real> & natural_coords,
Tensor3<Real> & shapesd) const;
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
/// get a the shapes vector
inline const Array<Real> &
getShapes(ElementType el_type,
GhostType ghost_type = _not_ghost) const;
/// get a the shapes derivatives vector
inline const Array<Real> &
getShapesDerivatives(ElementType el_type,
GhostType ghost_type = _not_ghost) const;
/// get a the shapes vector
inline const Array<Real> &
getShapesAtEnrichedNodes(ElementType el_type,
GhostType ghost_type = _not_ghost) const;
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
/// shape functions for all elements
ElementTypeMapArray<Real, InterpolationType> shapes;
/// shape functions derivatives for all elements
ElementTypeMapArray<Real, InterpolationType> shapes_derivatives;
/// additional integration points for the IGFEM formulation
ElementTypeMapArray<Real> igfem_integration_points;
/// values of shape functions for all elements on the enriched nodes
ElementTypeMapArray<Real, InterpolationType> shapes_at_enrichments;
};
} // namespace akantu
/* -------------------------------------------------------------------------- */
/* inline functions */
/* -------------------------------------------------------------------------- */
#include "shape_igfem_inline_impl.hh"
/// standard output stream operator
// template <class ShapeFunction>
// inline std::ostream & operator <<(std::ostream & stream, const
// ShapeIGFEM<ShapeFunction> & _this)
// {
// _this.printself(stream);
// return stream;
// }
#endif /* AKANTU_SHAPE_IGFEM_HH_ */
diff --git a/extra_packages/igfem/src/solid_mechanics_model_igfem.cc b/extra_packages/igfem/src/solid_mechanics_model_igfem.cc
index f7dfab8e3..8422cb2d2 100644
--- a/extra_packages/igfem/src/solid_mechanics_model_igfem.cc
+++ b/extra_packages/igfem/src/solid_mechanics_model_igfem.cc
@@ -1,586 +1,585 @@
/**
* @file solid_mechanics_model_igfem.hh
*
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
*
*
* @brief solid mechanics model for IGFEM analysis
*
*
* Copyright (©) 2010-2012, 2014 EPFL (Ecole Polytechnique Fédérale de Lausanne)
* Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)
*
*/
/* -------------------------------------------------------------------------- */
#include "solid_mechanics_model_igfem.hh"
#include "dumpable_inline_impl.hh"
#include "group_manager_inline_impl.hh"
#include "igfem_helper.hh"
#include "material_igfem.hh"
#ifdef AKANTU_USE_IOHELPER
#include "dumper_igfem_element_partition.hh"
#include "dumper_igfem_elemental_field.hh"
#include "dumper_igfem_material_internal_field.hh"
#include "dumper_material_padders.hh"
#include "dumper_paraview.hh"
#endif
/* -------------------------------------------------------------------------- */
namespace akantu {
const SolidMechanicsModelIGFEMOptions
default_solid_mechanics_model_igfem_options(_static, false);
SolidMechanicsModelIGFEM::SolidMechanicsModelIGFEM(Mesh & mesh, UInt dim,
- const ID & id,
- const MemoryID & memory_id)
- : SolidMechanicsModel(mesh, dim, id, memory_id), IGFEMEnrichment(mesh),
+ const ID & id)
+ : SolidMechanicsModel(mesh, dim, id), IGFEMEnrichment(mesh),
global_ids_updater(NULL) {
AKANTU_DEBUG_IN();
delete material_selector;
material_selector = new DefaultMaterialIGFEMSelector(*this);
this->registerEventHandler(*this);
#if defined(AKANTU_USE_IOHELPER)
this->mesh.registerDumper<DumperParaview>("igfem elements", id);
this->mesh.addDumpMeshToDumper("igfem elements", mesh, spatial_dimension,
_not_ghost, _ek_igfem);
#endif
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
SolidMechanicsModelIGFEM::~SolidMechanicsModelIGFEM() {
AKANTU_DEBUG_IN();
if (global_ids_updater)
delete global_ids_updater;
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelIGFEM::initFull(const ModelOptions & options) {
AKANTU_DEBUG_IN();
/// intialize the IGFEM enrichment
this->initialize();
SolidMechanicsModel::initFull(options);
// set the initial condition to 0
real_force->clear();
real_displacement->clear();
real_residual->clear();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
/**
* Allocate all the needed vectors. By default their are not necessarily set to
* 0
*
*/
void SolidMechanicsModelIGFEM::initArrays() {
AKANTU_DEBUG_IN();
UInt nb_nodes = mesh.getNbNodes();
std::stringstream sstr_rdisp;
sstr_rdisp << id << ":real_displacement";
std::stringstream sstr_rforc;
sstr_rforc << id << ":real_force";
std::stringstream sstr_rresi;
sstr_rresi << id << ":real_residual";
real_displacement = &(alloc<Real>(sstr_rdisp.str(), nb_nodes,
spatial_dimension, REAL_INIT_VALUE));
real_force = &(alloc<Real>(sstr_rforc.str(), nb_nodes, spatial_dimension,
REAL_INIT_VALUE));
real_residual = &(alloc<Real>(sstr_rresi.str(), nb_nodes, spatial_dimension,
REAL_INIT_VALUE));
SolidMechanicsModel::initArrays();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelIGFEM::initParallel(MeshPartition * partition,
DataAccessor * data_accessor) {
SolidMechanicsModel::initParallel(partition, data_accessor);
this->intersector_sphere.setDistributedSynchronizer(synch_parallel);
if (mesh.isDistributed())
global_ids_updater = new GlobalIdsUpdater(mesh, synch_parallel);
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelIGFEM::initMaterials() {
AKANTU_DEBUG_IN();
// make sure the material are instantiated
if (!are_materials_instantiated)
instantiateMaterials();
/// find the first igfem material
UInt igfem_index = 0;
while ((dynamic_cast<MaterialIGFEM *>(materials[igfem_index]) == NULL) &&
igfem_index <= materials.size())
++igfem_index;
AKANTU_DEBUG_ASSERT(igfem_index != materials.size(),
"No igfem materials in the material input file");
DefaultMaterialIGFEMSelector * igfem_mat_selector =
dynamic_cast<DefaultMaterialIGFEMSelector *>(material_selector);
if (igfem_mat_selector != NULL)
igfem_mat_selector->setIGFEMFallback(igfem_index);
SolidMechanicsModel::initMaterials();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
/**
* Initialize the model, basically pre-compute the shapes, shapes derivatives
* and jacobian
*
*/
void SolidMechanicsModelIGFEM::initModel() {
AKANTU_DEBUG_IN();
SolidMechanicsModel::initModel();
registerFEEngineObject<MyFEEngineIGFEMType>("IGFEMFEEngine", mesh,
spatial_dimension);
/// insert the two feengines associated with the model in the map
this->fe_engines_per_kind[_ek_regular] = &(this->getFEEngine());
this->fe_engines_per_kind[_ek_igfem] = &(this->getFEEngine("IGFEMFEEngine"));
/// add the igfem type connectivities
for (ghost_type_t::iterator gt = ghost_type_t::begin();
gt != ghost_type_t::end(); ++gt) {
GhostType type_ghost = *gt;
Mesh::type_iterator it = mesh.firstType(spatial_dimension, type_ghost);
Mesh::type_iterator last = mesh.lastType(spatial_dimension, type_ghost);
for (; it != last; ++it) {
const Array<UInt> & connectivity = mesh.getConnectivity(*it, type_ghost);
if (connectivity.getSize() != 0) {
ElementType type = *it;
Vector<ElementType> types_igfem = FEEngine::getIGFEMElementTypes(type);
for (UInt i = 0; i < types_igfem.size(); ++i)
mesh.addConnectivityType(types_igfem(i), type_ghost);
}
}
}
getFEEngine("IGFEMFEEngine").initShapeFunctions(_not_ghost);
getFEEngine("IGFEMFEEngine").initShapeFunctions(_ghost);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelIGFEM::onElementsAdded(const Array<Element> & elements,
const NewElementsEvent & event) {
AKANTU_DEBUG_IN();
const NewIGFEMElementsEvent * igfem_event =
dynamic_cast<const NewIGFEMElementsEvent *>(&event);
/// insert the new and old elements in the map
if (igfem_event != NULL) {
this->element_map.zero();
const Array<Element> & old_elements = igfem_event->getOldElementsList();
for (UInt e = 0; e < elements.getSize(); ++e) {
this->element_map[elements(e)] = old_elements(e);
}
}
/// update shape functions
getFEEngine("IGFEMFEEngine").initShapeFunctions(_not_ghost);
getFEEngine("IGFEMFEEngine").initShapeFunctions(_ghost);
SolidMechanicsModel::onElementsAdded(elements, event);
this->reassignMaterial();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelIGFEM::onElementsRemoved(
const Array<Element> & element_list,
const ElementTypeMapArray<UInt> & new_numbering,
const RemovedElementsEvent & event) {
this->getFEEngine("IGFEMFEEngine").initShapeFunctions(_not_ghost);
this->getFEEngine("IGFEMFEEngine").initShapeFunctions(_ghost);
SolidMechanicsModel::onElementsRemoved(element_list, new_numbering, event);
if (synch_parallel)
synch_parallel->computeAllBufferSizes(*this);
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelIGFEM::onNodesAdded(const Array<UInt> & nodes_list,
const NewNodesEvent & event) {
AKANTU_DEBUG_IN();
const NewIGFEMNodesEvent * igfem_event =
dynamic_cast<const NewIGFEMNodesEvent *>(&event);
// update the node type
if (igfem_event != NULL) {
intersector_sphere.updateNodeType(
nodes_list, igfem_event->getNewNodePerElem(),
igfem_event->getElementType(), igfem_event->getGhostType());
}
UInt nb_nodes = mesh.getNbNodes();
if (real_displacement)
real_displacement->resize(nb_nodes);
if (real_force)
real_force->resize(nb_nodes);
if (real_residual)
real_residual->resize(nb_nodes);
if (mesh.isDistributed())
mesh.getGlobalNodesIds().resize(mesh.getNbNodes());
if (displacement)
displacement->resize(nb_nodes);
if (mass)
mass->resize(nb_nodes);
if (velocity)
velocity->resize(nb_nodes);
if (acceleration)
acceleration->resize(nb_nodes);
if (force)
force->resize(nb_nodes);
if (residual)
residual->resize(nb_nodes);
if (blocked_dofs)
blocked_dofs->resize(nb_nodes);
if (previous_displacement)
previous_displacement->resize(nb_nodes);
if (increment_acceleration)
increment_acceleration->resize(nb_nodes);
if (increment)
increment->resize(nb_nodes);
if (current_position)
current_position->resize(nb_nodes);
std::vector<Material *>::iterator mat_it;
for (mat_it = materials.begin(); mat_it != materials.end(); ++mat_it) {
(*mat_it)->onNodesAdded(nodes_list, event);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelIGFEM::onNodesRemoved(const Array<UInt> & nodes_list,
const Array<UInt> & new_numbering,
const RemovedNodesEvent & event) {
if (real_displacement)
mesh.removeNodesFromArray(*real_displacement, new_numbering);
if (real_force)
mesh.removeNodesFromArray(*real_force, new_numbering);
if (real_residual)
mesh.removeNodesFromArray(*real_residual, new_numbering);
// communicate global connectivity for slave nodes
if (global_ids_updater)
global_ids_updater->updateGlobalIDs(
mesh.getNbNodes() - intersector_sphere.getNbStandardNodes());
SolidMechanicsModel::onNodesRemoved(nodes_list, new_numbering, event);
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelIGFEM::addDumpGroupFieldToDumper(
const std::string & dumper_name, const std::string & field_id,
const std::string & group_name, ElementKind element_kind,
bool padding_flag) {
AKANTU_DEBUG_IN();
ElementKind _element_kind = element_kind;
if (dumper_name == "igfem elements") {
_element_kind = _ek_igfem;
}
SolidMechanicsModel::addDumpGroupFieldToDumper(
dumper_name, field_id, group_name, _element_kind, padding_flag);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelIGFEM::onDump() {
this->computeValuesOnEnrichedNodes();
this->flattenAllRegisteredInternals(_ek_igfem);
SolidMechanicsModel::onDump();
}
/* -------------------------------------------------------------------------- */
#ifdef AKANTU_USE_IOHELPER
dumpers::Field * SolidMechanicsModelIGFEM::createElementalField(
const std::string & field_name, const std::string & group_name,
bool padding_flag, const UInt & spatial_dimension,
ElementKind kind) {
dumpers::Field * field = NULL;
if (kind != _ek_igfem)
field = SolidMechanicsModel::createElementalField(
field_name, group_name, padding_flag, spatial_dimension, kind);
else {
if (field_name == "partitions")
field =
mesh.createElementalField<UInt, dumpers::IGFEMElementPartitionField>(
mesh.getConnectivities(), group_name, spatial_dimension, kind);
else if (field_name == "material_index")
field =
mesh.createElementalField<UInt, Vector, dumpers::IGFEMElementalField>(
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) {
ElementTypeMap<UInt> nb_data_per_elem =
this->getInternalDataPerElem(field_name_copy, kind);
ElementTypeMapArray<Real> & internal_flat =
this->flattenInternal(field_name_copy, kind);
field =
mesh.createElementalField<Real, dumpers::IGFEMInternalMaterialField>(
internal_flat, group_name, spatial_dimension, kind,
nb_data_per_elem);
if (field_name == "strain") {
dumpers::ComputeStrain<false> * foo =
new dumpers::ComputeStrain<false>(*this);
field = dumpers::FieldComputeProxy::createFieldCompute(field, *foo);
} else if (field_name == "Von Mises stress") {
dumpers::ComputeVonMisesStress * foo =
new dumpers::ComputeVonMisesStress(*this);
field = dumpers::FieldComputeProxy::createFieldCompute(field, *foo);
} else if (field_name == "Green strain") {
dumpers::ComputeStrain<true> * foo =
new dumpers::ComputeStrain<true>(*this);
field = dumpers::FieldComputeProxy::createFieldCompute(field, *foo);
} else if (field_name == "principal strain") {
dumpers::ComputePrincipalStrain<false> * foo =
new dumpers::ComputePrincipalStrain<false>(*this);
field = dumpers::FieldComputeProxy::createFieldCompute(field, *foo);
} else if (field_name == "principal Green strain") {
dumpers::ComputePrincipalStrain<true> * foo =
new dumpers::ComputePrincipalStrain<true>(*this);
field = dumpers::FieldComputeProxy::createFieldCompute(field, *foo);
}
/// treat the paddings
if (padding_flag) {
if (field_name == "stress") {
if (spatial_dimension == 2) {
dumpers::StressPadder<2> * foo =
new dumpers::StressPadder<2>(*this);
field =
dumpers::FieldComputeProxy::createFieldCompute(field, *foo);
}
} else if (field_name == "strain" || field_name == "Green strain") {
if (spatial_dimension == 2) {
dumpers::StrainPadder<2> * foo =
new dumpers::StrainPadder<2>(*this);
field =
dumpers::FieldComputeProxy::createFieldCompute(field, *foo);
}
}
}
// homogenize the field
dumpers::ComputeFunctorInterface * foo =
dumpers::HomogenizerProxy::createHomogenizer(*field);
field = dumpers::FieldComputeProxy::createFieldCompute(field, *foo);
}
}
}
// }
return field;
}
/* -------------------------------------------------------------------------- */
dumpers::Field *
SolidMechanicsModelIGFEM::createNodalFieldReal(const std::string & field_name,
const std::string & group_name,
bool padding_flag) {
std::map<std::string, Array<Real> *> real_nodal_fields;
real_nodal_fields["real_displacement"] = real_displacement;
dumpers::Field * field = NULL;
if (padding_flag)
field = mesh.createNodalField(real_nodal_fields[field_name], group_name, 3);
else
field = mesh.createNodalField(real_nodal_fields[field_name], group_name);
if (field == NULL)
return SolidMechanicsModel::createNodalFieldReal(field_name, group_name,
padding_flag);
return field;
}
#else
/* -------------------------------------------------------------------------- */
dumpers::Field * SolidMechanicsModelIGFEM::createElementalField(
const std::string & field_name, const std::string & group_name,
bool padding_flag, const UInt & spatial_dimension,
ElementKind kind) {
return NULL;
}
/* -------------------------------------------------------------------------- */
dumpers::Field *
SolidMechanicsModelIGFEM::createNodalFieldReal(const std::string & field_name,
const std::string & group_name,
bool padding_flag) {
return NULL;
}
#endif
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelIGFEM::computeValuesOnEnrichedNodes() {
for (UInt n = 0; n < mesh.getNbNodes(); ++n) {
for (UInt s = 0; s < spatial_dimension; ++s)
(*real_displacement)(n, s) = (*displacement)(n, s);
}
Element element;
Vector<Real> real_coords(spatial_dimension);
Vector<Real> interpolated(spatial_dimension);
Array<Real>::const_vector_iterator r_displ_it =
this->real_displacement->begin(spatial_dimension);
for (ghost_type_t::iterator gt = ghost_type_t::begin();
gt != ghost_type_t::end(); ++gt) {
element.ghost_type = *gt;
Mesh::type_iterator it = mesh.firstType(spatial_dimension, *gt, _ek_igfem);
Mesh::type_iterator last = mesh.lastType(spatial_dimension, *gt, _ek_igfem);
for (; it != last; ++it) {
element.type = *it;
UInt nb_element = mesh.getNbElement(*it, *gt);
if (!nb_element)
continue;
UInt * elem_val = mesh.getConnectivity(*it, *gt).storage();
UInt nb_nodes_per_element = mesh.getNbNodesPerElement(*it);
Matrix<Real> nodes_coord(spatial_dimension, nb_nodes_per_element);
Matrix<Real> displ_val(spatial_dimension, nb_nodes_per_element);
UInt nb_enriched_nodes = IGFEMHelper::getNbEnrichedNodes(*it);
UInt nb_parent_nodes = IGFEMHelper::getNbParentNodes(*it);
for (UInt el = 0; el < nb_element; ++el) {
element.element = el;
/// get the node coordinates of the element
mesh.extractNodalValuesFromElement(
mesh.getNodes(), nodes_coord.storage(),
elem_val + el * nb_nodes_per_element, nb_nodes_per_element,
spatial_dimension);
/// get the displacement values at the nodes of the element
mesh.extractNodalValuesFromElement(
*(this->displacement), displ_val.storage(),
elem_val + el * nb_nodes_per_element, nb_nodes_per_element,
spatial_dimension);
for (UInt i = 0; i < nb_enriched_nodes; ++i) {
/// coordinates of enriched node
real_coords = nodes_coord(nb_parent_nodes + i);
/// global index of the enriched node
UInt idx = elem_val[el * nb_nodes_per_element + nb_parent_nodes + i];
/// compute the real displacement value
this->getFEEngine("IGFEMFEEngine")
.interpolate(real_coords, displ_val, interpolated, element);
r_displ_it[idx] = interpolated;
}
}
}
}
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelIGFEM::transferInternalValues(
const ID & internal, std::vector<Element> & new_elements,
Array<Real> & added_quads, Array<Real> & internal_values) {
/// @todo sort the new elements by their corresponding old element type and
/// old material!!!
/// get the number of elements for which iternals need to be transfered
UInt nb_new_elements = new_elements.size();
UInt nb_new_quads = added_quads.getSize() / nb_new_elements;
Array<Real>::const_matrix_iterator quad_coords =
added_quads.begin_reinterpret(this->spatial_dimension, nb_new_quads,
nb_new_elements);
UInt nb_internal_component = internal_values.getNbComponent();
Array<Real>::matrix_iterator internal_val = internal_values.begin_reinterpret(
nb_internal_component, nb_new_quads, nb_new_elements);
Vector<Real> default_values(nb_internal_component, 0.);
for (UInt e = 0; e < nb_new_elements; ++e, ++quad_coords, ++internal_val) {
Element new_element = new_elements[e];
Element old_element = this->element_map[new_element];
UInt mat_idx = (this->material_index(
old_element.type, old_element.ghost_type))(old_element.element);
Material & old_material = *(this->materials[mat_idx]);
old_material.extrapolateInternal(internal, old_element, *quad_coords,
*internal_val);
}
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelIGFEM::applyEigenGradU(
const Matrix<Real> & 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");
std::vector<Material *>::iterator mat_it;
for (mat_it = this->materials.begin(); mat_it != this->materials.end();
++mat_it) {
MaterialIGFEM * mat_igfem = dynamic_cast<MaterialIGFEM *>(*mat_it);
if (mat_igfem != NULL)
mat_igfem->applyEigenGradU(prescribed_eigen_grad_u, material_name,
ghost_type);
else if ((*mat_it)->getName() == material_name)
(*mat_it)->applyEigenGradU(prescribed_eigen_grad_u, ghost_type);
}
}
} // namespace akantu
diff --git a/extra_packages/igfem/src/solid_mechanics_model_igfem.hh b/extra_packages/igfem/src/solid_mechanics_model_igfem.hh
index 101e40b67..4933932f8 100644
--- a/extra_packages/igfem/src/solid_mechanics_model_igfem.hh
+++ b/extra_packages/igfem/src/solid_mechanics_model_igfem.hh
@@ -1,197 +1,196 @@
/**
* @file solid_mechanics_model_igfem.hh
*
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
*
*
* @brief solid mechanics model for IGFEM analysis
*
*
* Copyright (©) 2010-2012, 2014 EPFL (Ecole Polytechnique Fédérale de Lausanne)
* Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)
*
*/
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_SOLID_MECHANICS_MODEL_IGFEM_HH_
#define AKANTU_SOLID_MECHANICS_MODEL_IGFEM_HH_
#include "global_ids_updater.hh"
#include "igfem_enrichment.hh"
#include "solid_mechanics_model.hh"
#include "solid_mechanics_model_event_handler.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
struct SolidMechanicsModelIGFEMOptions : public SolidMechanicsModelOptions {
SolidMechanicsModelIGFEMOptions(AnalysisMethod analysis_method = _static,
bool no_init_materials = false)
: SolidMechanicsModelOptions(analysis_method, no_init_materials) {}
};
extern const SolidMechanicsModelIGFEMOptions
default_solid_mechanics_model_igfem_options;
/* -------------------------------------------------------------------------- */
/* Solid Mechanics Model for IGFEM analysis */
/* -------------------------------------------------------------------------- */
class SolidMechanicsModelIGFEM : public SolidMechanicsModel,
public SolidMechanicsModelEventHandler,
public IGFEMEnrichment {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
typedef FEEngineTemplate<IntegratorGauss, ShapeLagrange, _ek_igfem>
MyFEEngineIGFEMType;
typedef std::map<Element, Element> ElementMap;
typedef std::map<ElementKind, FEEngine *> FEEnginesPerKindMap;
SolidMechanicsModelIGFEM(Mesh & mesh,
UInt spatial_dimension = _all_dimensions,
- const ID & id = "solid_mechanics_model_igfem",
- const MemoryID & memory_id = 0);
+ const ID & id = "solid_mechanics_model_igfem");
virtual ~SolidMechanicsModelIGFEM();
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// initialize the cohesive model
virtual void initFull(const ModelOptions & options =
default_solid_mechanics_model_igfem_options);
/// initialize the model
virtual void initModel();
/// initialize igfem material
virtual void initMaterials();
/// register the tags associated with the parallel synchronizer
virtual void initParallel(MeshPartition * partition,
DataAccessor * data_accessor = NULL);
/// allocate all vectors
virtual void initArrays();
/// transfer internals from old to new elements
void transferInternalValues(const ID & internal,
std::vector<Element> & new_elements,
Array<Real> & added_quads,
Array<Real> & internal_values);
/// compute the barycenter for a sub-element
inline void getSubElementBarycenter(UInt element, UInt sub_element,
ElementType type,
Vector<Real> & barycenter,
GhostType ghost_type) const;
/// apply a constant eigen_grad_u on all quadrature points of a given material
virtual void applyEigenGradU(const Matrix<Real> & prescribed_eigen_grad_u,
const ID & material_name,
const GhostType ghost_type = _not_ghost);
private:
/// compute the real values of displacement, force, etc. on the enriched nodes
void computeValuesOnEnrichedNodes();
/* ------------------------------------------------------------------------ */
/* Mesh Event Handler inherited members */
/* ------------------------------------------------------------------------ */
protected:
virtual void onNodesAdded(const Array<UInt> & nodes_list,
const NewNodesEvent & event);
virtual void onNodesRemoved(const Array<UInt> & element_list,
const Array<UInt> & new_numbering,
const RemovedNodesEvent & event);
virtual void onElementsAdded(const Array<Element> & nodes_list,
const NewElementsEvent & event);
virtual void
onElementsRemoved(const Array<Element> & element_list,
const ElementTypeMapArray<UInt> & new_numbering,
const RemovedElementsEvent & event);
/* ------------------------------------------------------------------------ */
/* Dumpable interface */
/* ------------------------------------------------------------------------ */
public:
virtual void onDump();
virtual void addDumpGroupFieldToDumper(const std::string & dumper_name,
const std::string & field_id,
const std::string & group_name,
ElementKind element_kind,
bool padding_flag);
virtual dumpers::Field * createElementalField(const std::string & field_name,
const std::string & group_name,
bool padding_flag,
const UInt & spatial_dimension,
ElementKind kind);
virtual dumpers::Field * createNodalFieldReal(const std::string & field_name,
const std::string & group_name,
bool padding_flag);
/* --------------------------------------------------------------------------
*/
/* Accessors */
/* --------------------------------------------------------------------------
*/
public:
/// get the fe-engines per kind
AKANTU_GET_MACRO(FEEnginesPerKind, fe_engines_per_kind,
const FEEnginesPerKindMap &);
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
private:
/// real displacements array
Array<Real> * real_displacement;
/// real forces array
Array<Real> * real_force;
/// real residuals array
Array<Real> * real_residual;
/// map between and new elements (needed when the interface is moving)
ElementMap element_map;
/// global connectivity ids updater
GlobalIdsUpdater * global_ids_updater;
/// map between element kind and corresponding FEEngine object
FEEnginesPerKindMap fe_engines_per_kind;
};
/* -------------------------------------------------------------------------- */
/* IGFEMMaterialSelector */
/* -------------------------------------------------------------------------- */
class DefaultMaterialIGFEMSelector : public DefaultMaterialSelector {
public:
DefaultMaterialIGFEMSelector(const SolidMechanicsModelIGFEM & model)
: DefaultMaterialSelector(model.getMaterialByElement()),
fallback_value_igfem(0) {}
virtual UInt operator()(const Element & element) {
if (Mesh::getKind(element.type) == _ek_igfem)
return fallback_value_igfem;
else
return DefaultMaterialSelector::operator()(element);
}
void setIGFEMFallback(UInt f) { this->fallback_value_igfem = f; }
protected:
UInt fallback_value_igfem;
};
} // namespace akantu
#if defined(AKANTU_INCLUDE_INLINE_IMPL)
#include "solid_mechanics_model_igfem_inline_impl.hh"
#endif
#endif /* AKANTU_SOLID_MECHANICS_MODEL_IGFEM_HH_ */
diff --git a/extra_packages/students-extra-package b/extra_packages/students-extra-package
deleted file mode 160000
index 32620a252..000000000
--- a/extra_packages/students-extra-package
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 32620a252a16d3d2f8d2f7d46a42d3d19fcd34e9
diff --git a/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_coulomb.hh b/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_coulomb.hh
index 29ac24991..cdb31ae9b 100644
--- a/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_coulomb.hh
+++ b/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_coulomb.hh
@@ -1,107 +1,106 @@
/**
* @file ntn_friclaw_coulomb.hh
*
* @author David Simon Kammer <david.kammer@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Fri Feb 23 2018
*
* @brief coulomb friction with \mu_s = \mu_k (constant)
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#ifndef AST_NTN_FRICLAW_COULOMB_HH_
#define AST_NTN_FRICLAW_COULOMB_HH_
/* -------------------------------------------------------------------------- */
// simtools
#include "ntn_fricreg_no_regularisation.hh"
namespace akantu {
/* -------------------------------------------------------------------------- */
template <class Regularisation = NTNFricRegNoRegularisation>
class NTNFricLawCoulomb : public Regularisation {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
- NTNFricLawCoulomb(NTNBaseContact & contact, const ID & id = "coulomb",
- const MemoryID & memory_id = 0);
+ NTNFricLawCoulomb(NTNBaseContact & contact, const ID & id = "coulomb");
virtual ~NTNFricLawCoulomb(){};
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// register synchronizedarrays for sync
virtual void registerSynchronizedArray(SynchronizedArrayBase & array);
/// dump restart file
virtual void dumpRestart(const std::string & file_name) const;
/// read restart file
virtual void readRestart(const std::string & file_name);
/// function to print the contain of the class
virtual void printself(std::ostream & stream, int indent = 0) const;
protected:
/// compute frictional strength according to friction law
virtual void computeFrictionalStrength();
/* ------------------------------------------------------------------------ */
/* Dumpable */
/* ------------------------------------------------------------------------ */
public:
virtual void addDumpFieldToDumper(const std::string & dumper_name,
const std::string & field_id);
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
// friction coefficient
SynchronizedArray<Real> mu;
};
/* -------------------------------------------------------------------------- */
/* inline functions */
/* -------------------------------------------------------------------------- */
/// standard output stream operator
template <class Regularisation>
inline std::ostream &
operator<<(std::ostream & stream,
const NTNFricLawCoulomb<Regularisation> & _this) {
_this.printself(stream);
return stream;
}
} // namespace akantu
#include "ntn_friclaw_coulomb_tmpl.hh"
#endif /* AST_NTN_FRICLAW_COULOMB_HH_ */
diff --git a/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_coulomb_tmpl.hh b/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_coulomb_tmpl.hh
index 733a6c548..a1b626b67 100644
--- a/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_coulomb_tmpl.hh
+++ b/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_coulomb_tmpl.hh
@@ -1,167 +1,166 @@
/**
* @file ntn_friclaw_coulomb_tmpl.hh
*
* @author David Simon Kammer <david.kammer@epfl.ch>
*
* @date creation: Tue Dec 02 2014
* @date last modification: Fri Feb 23 2018
*
* @brief implementation of coulomb friction
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "dumper_nodal_field.hh"
namespace akantu {
/* -------------------------------------------------------------------------- */
template <class Regularisation>
NTNFricLawCoulomb<Regularisation>::NTNFricLawCoulomb(NTNBaseContact & contact,
- const ID & id,
- const MemoryID & memory_id)
- : Regularisation(contact, id, memory_id),
+ const ID & id)
+ : Regularisation(contact, id),
mu(0, 1, 0., id + ":mu", 0., "mu") {
AKANTU_DEBUG_IN();
Regularisation::registerSynchronizedArray(this->mu);
this->registerParam("mu", this->mu, _pat_parsmod, "friction coefficient");
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class Regularisation>
void NTNFricLawCoulomb<Regularisation>::computeFrictionalStrength() {
AKANTU_DEBUG_IN();
// get contact arrays
const SynchronizedArray<bool> & is_in_contact =
this->internalGetIsInContact();
const SynchronizedArray<Real> & pressure = this->internalGetContactPressure();
// array to fill
SynchronizedArray<Real> & strength = this->internalGetFrictionalStrength();
UInt nb_contact_nodes = this->contact.getNbContactNodes();
for (UInt n = 0; n < nb_contact_nodes; ++n) {
// node pair is NOT in contact
if (!is_in_contact(n))
strength(n) = 0.;
// node pair is in contact
else {
// compute frictional strength
strength(n) = this->mu(n) * pressure(n);
}
}
Regularisation::computeFrictionalStrength();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class Regularisation>
void NTNFricLawCoulomb<Regularisation>::registerSynchronizedArray(
SynchronizedArrayBase & array) {
AKANTU_DEBUG_IN();
this->mu.registerDependingArray(array);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class Regularisation>
void NTNFricLawCoulomb<Regularisation>::dumpRestart(
const std::string & file_name) const {
AKANTU_DEBUG_IN();
this->mu.dumpRestartFile(file_name);
Regularisation::dumpRestart(file_name);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class Regularisation>
void NTNFricLawCoulomb<Regularisation>::readRestart(
const std::string & file_name) {
AKANTU_DEBUG_IN();
this->mu.readRestartFile(file_name);
Regularisation::readRestart(file_name);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class Regularisation>
void NTNFricLawCoulomb<Regularisation>::printself(std::ostream & stream,
int indent) const {
AKANTU_DEBUG_IN();
std::string space;
for (Int i = 0; i < indent; i++, space += AKANTU_INDENT)
;
stream << space << "NTNFricLawCoulomb [" << std::endl;
Regularisation::printself(stream, ++indent);
stream << space << "]" << std::endl;
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class Regularisation>
void NTNFricLawCoulomb<Regularisation>::addDumpFieldToDumper(
const std::string & dumper_name, const std::string & field_id) {
AKANTU_DEBUG_IN();
#ifdef AKANTU_USE_IOHELPER
// const SynchronizedArray<UInt> * nodal_filter =
// &(this->contact.getSlaves());
if (field_id == "mu") {
this->internalAddDumpFieldToDumper(
dumper_name, field_id,
std::make_unique<dumpers::NodalField<Real>>(this->mu.getArray()));
}
/*
else if (field_id == "frictional_contact_pressure") {
this->internalAddDumpFieldToDumper(dumper_name,
field_id,
new
DumperIOHelper::NodalField<Real>(this->frictional_contact_pressure.getArray()));
}
*/
else {
Regularisation::addDumpFieldToDumper(dumper_name, field_id);
}
#endif
AKANTU_DEBUG_OUT();
}
} // namespace akantu
diff --git a/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_linear_cohesive.hh b/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_linear_cohesive.hh
index 5d86d0636..c67547a1e 100644
--- a/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_linear_cohesive.hh
+++ b/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_linear_cohesive.hh
@@ -1,114 +1,113 @@
/**
* @file ntn_friclaw_linear_cohesive.hh
*
* @author David Simon Kammer <david.kammer@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Fri Feb 23 2018
*
* @brief linear cohesive law
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#ifndef AST_NTN_FRICLAW_LINEAR_COHESIVE_HH_
#define AST_NTN_FRICLAW_LINEAR_COHESIVE_HH_
/* -------------------------------------------------------------------------- */
// simtools
#include "ntn_fricreg_no_regularisation.hh"
namespace akantu {
/* -------------------------------------------------------------------------- */
template <class Regularisation = NTNFricRegNoRegularisation>
class NTNFricLawLinearCohesive : public Regularisation {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
NTNFricLawLinearCohesive(NTNBaseContact & contact,
- const ID & id = "linear_cohesive",
- const MemoryID & memory_id = 0);
+ const ID & id = "linear_cohesive");
virtual ~NTNFricLawLinearCohesive(){};
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// register synchronizedarrays for sync
virtual void registerSynchronizedArray(SynchronizedArrayBase & array);
/// dump restart file
virtual void dumpRestart(const std::string & file_name) const;
/// read restart file
virtual void readRestart(const std::string & file_name);
/// function to print the contain of the class
virtual void printself(std::ostream & stream, int indent = 0) const;
protected:
/// compute frictional strength according to friction law
virtual void computeFrictionalStrength();
/* ------------------------------------------------------------------------ */
/* Dumpable */
/* ------------------------------------------------------------------------ */
public:
virtual void addDumpFieldToDumper(const std::string & dumper_name,
const std::string & field_id);
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
// fracture energy
SynchronizedArray<Real> G_c;
// peak value of cohesive law
SynchronizedArray<Real> tau_c;
// residual value of cohesive law (for slip > d_c)
SynchronizedArray<Real> tau_r;
};
/* -------------------------------------------------------------------------- */
/* inline functions */
/* -------------------------------------------------------------------------- */
/// standard output stream operator
template <class Regularisation>
inline std::ostream &
operator<<(std::ostream & stream,
const NTNFricLawLinearCohesive<Regularisation> & _this) {
_this.printself(stream);
return stream;
}
} // namespace akantu
#include "ntn_friclaw_linear_cohesive_tmpl.hh"
#endif /* AST_NTN_FRICLAW_LINEAR_COHESIVE_HH_ */
diff --git a/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_linear_cohesive_tmpl.hh b/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_linear_cohesive_tmpl.hh
index 887bc2e20..ba169ee22 100644
--- a/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_linear_cohesive_tmpl.hh
+++ b/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_linear_cohesive_tmpl.hh
@@ -1,188 +1,188 @@
/**
* @file ntn_friclaw_linear_cohesive_tmpl.hh
*
* @author David Simon Kammer <david.kammer@epfl.ch>
*
* @date creation: Tue Dec 02 2014
* @date last modification: Fri Feb 23 2018
*
* @brief implementation of linear cohesive law
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
//#include "dumper_text.hh"
namespace akantu {
/* -------------------------------------------------------------------------- */
template <class Regularisation>
NTNFricLawLinearCohesive<Regularisation>::NTNFricLawLinearCohesive(
- NTNBaseContact & contact, const ID & id, const MemoryID & memory_id)
- : Regularisation(contact, id, memory_id),
+ NTNBaseContact & contact, const ID & id)
+ : Regularisation(contact, id),
G_c(0, 1, 0., id + ":G_c", 0., "G_c"),
tau_c(0, 1, 0., id + ":tau_c", 0., "tau_c"),
tau_r(0, 1, 0., id + ":tau_r", 0., "tau_r") {
AKANTU_DEBUG_IN();
Regularisation::registerSynchronizedArray(this->G_c);
Regularisation::registerSynchronizedArray(this->tau_c);
Regularisation::registerSynchronizedArray(this->tau_r);
this->registerParam("G_c", this->G_c, _pat_parsmod, "fracture energy");
this->registerParam("tau_c", this->tau_c, _pat_parsmod,
"peak shear strength");
this->registerParam("tau_r", this->tau_r, _pat_parsmod,
"residual shear strength");
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class Regularisation>
void NTNFricLawLinearCohesive<Regularisation>::computeFrictionalStrength() {
AKANTU_DEBUG_IN();
// get arrays
const SynchronizedArray<bool> & is_in_contact =
this->internalGetIsInContact();
// const SynchronizedArray<Real> & slip = this->internalGetSlip();
const SynchronizedArray<Real> & slip = this->internalGetCumulativeSlip();
// array to fill
SynchronizedArray<Real> & strength = this->internalGetFrictionalStrength();
UInt nb_contact_nodes = this->contact.getNbContactNodes();
for (UInt n = 0; n < nb_contact_nodes; ++n) {
// node pair is NOT in contact
if (!is_in_contact(n))
strength(n) = 0.;
// node pair is in contact
else {
if (this->G_c(n) == 0.) {
// strength(n) = 0.;
strength(n) = this->tau_r(n);
} else {
Real slope = (this->tau_c(n) - this->tau_r(n)) *
(this->tau_c(n) - this->tau_r(n)) / (2 * this->G_c(n));
// no strength < tau_r
strength(n) =
std::max(this->tau_c(n) - slope * slip(n), this->tau_r(n));
// strength(n) = std::max(this->tau_c(n) - slope * slip(n), 0.); // no
// negative strength
}
}
}
Regularisation::computeFrictionalStrength();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class Regularisation>
void NTNFricLawLinearCohesive<Regularisation>::registerSynchronizedArray(
SynchronizedArrayBase & array) {
AKANTU_DEBUG_IN();
this->G_c.registerDependingArray(array);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class Regularisation>
void NTNFricLawLinearCohesive<Regularisation>::dumpRestart(
const std::string & file_name) const {
AKANTU_DEBUG_IN();
this->G_c.dumpRestartFile(file_name);
this->tau_c.dumpRestartFile(file_name);
this->tau_r.dumpRestartFile(file_name);
Regularisation::dumpRestart(file_name);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class Regularisation>
void NTNFricLawLinearCohesive<Regularisation>::readRestart(
const std::string & file_name) {
AKANTU_DEBUG_IN();
this->G_c.readRestartFile(file_name);
this->tau_c.readRestartFile(file_name);
this->tau_r.readRestartFile(file_name);
Regularisation::readRestart(file_name);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class Regularisation>
void NTNFricLawLinearCohesive<Regularisation>::printself(std::ostream & stream,
int indent) const {
AKANTU_DEBUG_IN();
std::string space;
for (Int i = 0; i < indent; i++, space += AKANTU_INDENT)
;
stream << space << "NTNFricLawLinearCohesive [" << std::endl;
Regularisation::printself(stream, ++indent);
stream << space << "]" << std::endl;
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class Regularisation>
void NTNFricLawLinearCohesive<Regularisation>::addDumpFieldToDumper(
const std::string & dumper_name, const std::string & field_id) {
AKANTU_DEBUG_IN();
#ifdef AKANTU_USE_IOHELPER
// const SynchronizedArray<UInt> * nodal_filter =
// &(this->contact.getSlaves());
if (field_id == "G_c") {
this->internalAddDumpFieldToDumper(
dumper_name, field_id,
std::make_unique<dumpers::NodalField<Real>>(this->G_c.getArray()));
} else if (field_id == "tau_c") {
this->internalAddDumpFieldToDumper(
dumper_name, field_id,
std::make_unique<dumpers::NodalField<Real>>(this->tau_c.getArray()));
} else if (field_id == "tau_r") {
this->internalAddDumpFieldToDumper(
dumper_name, field_id,
std::make_unique<dumpers::NodalField<Real>>(this->tau_r.getArray()));
} else {
Regularisation::addDumpFieldToDumper(dumper_name, field_id);
}
#endif
AKANTU_DEBUG_OUT();
}
} // namespace akantu
diff --git a/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_linear_slip_weakening.hh b/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_linear_slip_weakening.hh
index d12963771..60890c82c 100644
--- a/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_linear_slip_weakening.hh
+++ b/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_linear_slip_weakening.hh
@@ -1,116 +1,115 @@
/**
* @file ntn_friclaw_linear_slip_weakening.hh
*
* @author David Simon Kammer <david.kammer@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Fri Feb 23 2018
*
* @brief linear slip weakening
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#ifndef AST_NTN_FRICLAW_LINEAR_SLIP_WEAKENING_HH_
#define AST_NTN_FRICLAW_LINEAR_SLIP_WEAKENING_HH_
/* -------------------------------------------------------------------------- */
// simtools
#include "ntn_friclaw_coulomb.hh"
namespace akantu {
/* -------------------------------------------------------------------------- */
template <class Regularisation = NTNFricRegNoRegularisation>
class NTNFricLawLinearSlipWeakening : public NTNFricLawCoulomb<Regularisation> {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
NTNFricLawLinearSlipWeakening(NTNBaseContact & contact,
- const ID & id = "linear_slip_weakening",
- const MemoryID & memory_id = 0);
+ const ID & id = "linear_slip_weakening");
virtual ~NTNFricLawLinearSlipWeakening(){};
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// register synchronizedarrays for sync
virtual void registerSynchronizedArray(SynchronizedArrayBase & array);
/// dump restart file
virtual void dumpRestart(const std::string & file_name) const;
/// read restart file
virtual void readRestart(const std::string & file_name);
/// function to print the contain of the class
virtual void printself(std::ostream & stream, int indent = 0) const;
protected:
/// compute frictional strength according to friction law
virtual void computeFrictionalStrength();
/// computes the friction coefficient as a function of slip
virtual void computeFrictionCoefficient();
/* ------------------------------------------------------------------------ */
/* Dumpable */
/* ------------------------------------------------------------------------ */
public:
virtual void addDumpFieldToDumper(const std::string & dumper_name,
const std::string & field_id);
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
// static coefficient of friction
SynchronizedArray<Real> mu_s;
// kinetic coefficient of friction
SynchronizedArray<Real> mu_k;
// Dc the length over which slip weakening happens
SynchronizedArray<Real> d_c;
};
/* -------------------------------------------------------------------------- */
/* inline functions */
/* -------------------------------------------------------------------------- */
/// standard output stream operator
template <class Regularisation>
inline std::ostream &
operator<<(std::ostream & stream,
const NTNFricLawLinearSlipWeakening<Regularisation> & _this) {
_this.printself(stream);
return stream;
}
} // namespace akantu
#include "ntn_friclaw_linear_slip_weakening_tmpl.hh"
#endif /* AST_NTN_FRICLAW_LINEAR_SLIP_WEAKENING_HH_ */
diff --git a/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_linear_slip_weakening_no_healing.hh b/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_linear_slip_weakening_no_healing.hh
index e6e44ba10..cc2c63f4f 100644
--- a/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_linear_slip_weakening_no_healing.hh
+++ b/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_linear_slip_weakening_no_healing.hh
@@ -1,95 +1,94 @@
/**
* @file ntn_friclaw_linear_slip_weakening_no_healing.hh
*
* @author David Simon Kammer <david.kammer@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Fri Feb 23 2018
*
* @brief linear slip weakening
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#ifndef AST_NTN_FRICLAW_LINEAR_SLIP_WEAKENING_NO_HEALING_HH_
#define AST_NTN_FRICLAW_LINEAR_SLIP_WEAKENING_NO_HEALING_HH_
/* -------------------------------------------------------------------------- */
#include "ntn_friclaw_linear_slip_weakening.hh"
namespace akantu {
/* -------------------------------------------------------------------------- */
template <class Regularisation = NTNFricRegNoRegularisation>
class NTNFricLawLinearSlipWeakeningNoHealing
: public NTNFricLawLinearSlipWeakening<Regularisation> {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
NTNFricLawLinearSlipWeakeningNoHealing(
NTNBaseContact & contact,
- const ID & id = "linear_slip_weakening_no_healing",
- const MemoryID & memory_id = 0);
+ const ID & id = "linear_slip_weakening_no_healing");
virtual ~NTNFricLawLinearSlipWeakeningNoHealing(){};
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// function to print the contain of the class
virtual void printself(std::ostream & stream, int indent = 0) const;
protected:
/// computes the friction coefficient as a function of slip
virtual void computeFrictionCoefficient();
/* ------------------------------------------------------------------------ */
/* Dumpable */
/* ------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
};
/* -------------------------------------------------------------------------- */
/* inline functions */
/* -------------------------------------------------------------------------- */
/// standard output stream operator
template <class Regularisation>
inline std::ostream & operator<<(
std::ostream & stream,
const NTNFricLawLinearSlipWeakeningNoHealing<Regularisation> & _this) {
_this.printself(stream);
return stream;
}
} // namespace akantu
#include "ntn_friclaw_linear_slip_weakening_no_healing_tmpl.hh"
#endif /* AST_NTN_FRICLAW_LINEAR_SLIP_WEAKENING_NO_HEALING_HH_ */
diff --git a/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_linear_slip_weakening_no_healing_tmpl.hh b/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_linear_slip_weakening_no_healing_tmpl.hh
index dee74841e..a8cc4cc41 100644
--- a/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_linear_slip_weakening_no_healing_tmpl.hh
+++ b/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_linear_slip_weakening_no_healing_tmpl.hh
@@ -1,85 +1,84 @@
/**
* @file ntn_friclaw_linear_slip_weakening_no_healing_tmpl.hh
*
* @author David Simon Kammer <david.kammer@epfl.ch>
*
* @date creation: Tue Dec 02 2014
* @date last modification: Fri Feb 23 2018
*
* @brief implementation of linear slip weakening
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
template <class Regularisation>
NTNFricLawLinearSlipWeakeningNoHealing<Regularisation>::
NTNFricLawLinearSlipWeakeningNoHealing(NTNBaseContact & contact,
- const ID & id,
- const MemoryID & memory_id)
- : NTNFricLawLinearSlipWeakening<Regularisation>(contact, id, memory_id) {
+ const ID & id)
+ : NTNFricLawLinearSlipWeakening<Regularisation>(contact, id) {
AKANTU_DEBUG_IN();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class Regularisation>
void NTNFricLawLinearSlipWeakeningNoHealing<
Regularisation>::computeFrictionCoefficient() {
AKANTU_DEBUG_IN();
// get arrays
const SynchronizedArray<Real> & slip = this->internalGetCumulativeSlip();
UInt nb_contact_nodes = this->contact.getNbContactNodes();
for (UInt n = 0; n < nb_contact_nodes; ++n) {
if (slip(n) >= this->d_c(n)) {
this->mu(n) = this->mu_k(n);
} else {
// mu = mu_k + (1 - slip / Dc) * (mu_s - mu_k)
this->mu(n) = this->mu_k(n) + (1 - (slip(n) / this->d_c(n))) *
(this->mu_s(n) - this->mu_k(n));
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class Regularisation>
void NTNFricLawLinearSlipWeakeningNoHealing<Regularisation>::printself(
std::ostream & stream, int indent) const {
AKANTU_DEBUG_IN();
std::string space;
for (Int i = 0; i < indent; i++, space += AKANTU_INDENT)
;
stream << space << "NTNFricLawLinearSlipWeakeningNoHealing [" << std::endl;
NTNFricLawLinearSlipWeakening<Regularisation>::printself(stream, ++indent);
stream << space << "]" << std::endl;
AKANTU_DEBUG_OUT();
}
} // namespace akantu
diff --git a/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_linear_slip_weakening_tmpl.hh b/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_linear_slip_weakening_tmpl.hh
index 35c3d5d60..114143b36 100644
--- a/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_linear_slip_weakening_tmpl.hh
+++ b/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_laws/ntn_friclaw_linear_slip_weakening_tmpl.hh
@@ -1,189 +1,189 @@
/**
* @file ntn_friclaw_linear_slip_weakening_tmpl.hh
*
* @author David Simon Kammer <david.kammer@epfl.ch>
*
* @date creation: Tue Dec 02 2014
* @date last modification: Fri Feb 23 2018
*
* @brief implementation of linear slip weakening
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "dumper_text.hh"
namespace akantu {
/* -------------------------------------------------------------------------- */
template <class Regularisation>
NTNFricLawLinearSlipWeakening<Regularisation>::NTNFricLawLinearSlipWeakening(
- NTNBaseContact & contact, const ID & id, const MemoryID & memory_id)
- : NTNFricLawCoulomb<Regularisation>(contact, id, memory_id),
+ NTNBaseContact & contact, const ID & id)
+ : NTNFricLawCoulomb<Regularisation>(contact, id),
mu_s(0, 1, 0., id + ":mu_s", 0., "mu_s"),
mu_k(0, 1, 0., id + ":mu_k", 0., "mu_k"),
d_c(0, 1, 0., id + ":d_c", 0., "d_c") {
AKANTU_DEBUG_IN();
NTNFricLawCoulomb<Regularisation>::registerSynchronizedArray(this->mu_s);
NTNFricLawCoulomb<Regularisation>::registerSynchronizedArray(this->mu_k);
NTNFricLawCoulomb<Regularisation>::registerSynchronizedArray(this->d_c);
this->registerParam("mu_s", this->mu_s, _pat_parsmod,
"static friction coefficient");
this->registerParam("mu_k", this->mu_k, _pat_parsmod,
"kinetic friction coefficient");
this->registerParam("d_c", this->d_c, _pat_parsmod, "slip weakening length");
this->setParameterAccessType("mu", _pat_readable);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class Regularisation>
void NTNFricLawLinearSlipWeakening<
Regularisation>::computeFrictionalStrength() {
AKANTU_DEBUG_IN();
computeFrictionCoefficient();
NTNFricLawCoulomb<Regularisation>::computeFrictionalStrength();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class Regularisation>
void NTNFricLawLinearSlipWeakening<
Regularisation>::computeFrictionCoefficient() {
AKANTU_DEBUG_IN();
// get arrays
const SynchronizedArray<bool> & stick = this->internalGetIsSticking();
const SynchronizedArray<Real> & slip = this->internalGetSlip();
UInt nb_contact_nodes = this->contact.getNbContactNodes();
for (UInt n = 0; n < nb_contact_nodes; ++n) {
if (stick(n)) {
this->mu(n) = this->mu_s(n);
} else {
if (slip(n) >= this->d_c(n)) {
this->mu(n) = this->mu_k(n);
} else {
// mu = mu_k + (1 - slip / Dc) * (mu_s - mu_k)
this->mu(n) = this->mu_k(n) + (1 - (slip(n) / this->d_c(n))) *
(this->mu_s(n) - this->mu_k(n));
}
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class Regularisation>
void NTNFricLawLinearSlipWeakening<Regularisation>::registerSynchronizedArray(
SynchronizedArrayBase & array) {
AKANTU_DEBUG_IN();
this->mu_s.registerDependingArray(array);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class Regularisation>
void NTNFricLawLinearSlipWeakening<Regularisation>::dumpRestart(
const std::string & file_name) const {
AKANTU_DEBUG_IN();
this->mu_s.dumpRestartFile(file_name);
this->mu_k.dumpRestartFile(file_name);
this->d_c.dumpRestartFile(file_name);
NTNFricLawCoulomb<Regularisation>::dumpRestart(file_name);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class Regularisation>
void NTNFricLawLinearSlipWeakening<Regularisation>::readRestart(
const std::string & file_name) {
AKANTU_DEBUG_IN();
this->mu_s.readRestartFile(file_name);
this->mu_k.readRestartFile(file_name);
this->d_c.readRestartFile(file_name);
NTNFricLawCoulomb<Regularisation>::readRestart(file_name);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class Regularisation>
void NTNFricLawLinearSlipWeakening<Regularisation>::printself(
std::ostream & stream, int indent) const {
AKANTU_DEBUG_IN();
std::string space;
for (Int i = 0; i < indent; i++, space += AKANTU_INDENT)
;
stream << space << "NTNFricLawLinearSlipWeakening [" << std::endl;
NTNFricLawCoulomb<Regularisation>::printself(stream, ++indent);
stream << space << "]" << std::endl;
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class Regularisation>
void NTNFricLawLinearSlipWeakening<Regularisation>::addDumpFieldToDumper(
const std::string & dumper_name, const std::string & field_id) {
AKANTU_DEBUG_IN();
#ifdef AKANTU_USE_IOHELPER
// const SynchronizedArray<UInt> * nodal_filter =
// &(this->contact.getSlaves());
if (field_id == "mu_s") {
this->internalAddDumpFieldToDumper(
dumper_name, field_id,
std::make_unique<dumpers::NodalField<Real>>(this->mu_s.getArray()));
} else if (field_id == "mu_k") {
this->internalAddDumpFieldToDumper(
dumper_name, field_id,
std::make_unique<dumpers::NodalField<Real>>(this->mu_k.getArray()));
} else if (field_id == "d_c") {
this->internalAddDumpFieldToDumper(
dumper_name, field_id,
std::make_unique<dumpers::NodalField<Real>>(this->d_c.getArray()));
} else {
NTNFricLawCoulomb<Regularisation>::addDumpFieldToDumper(dumper_name,
field_id);
}
#endif
AKANTU_DEBUG_OUT();
}
} // namespace akantu
diff --git a/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_regularisations/ntn_fricreg_no_regularisation.cc b/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_regularisations/ntn_fricreg_no_regularisation.cc
index 85cab8044..76080511e 100644
--- a/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_regularisations/ntn_fricreg_no_regularisation.cc
+++ b/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_regularisations/ntn_fricreg_no_regularisation.cc
@@ -1,167 +1,167 @@
/**
* @file ntn_fricreg_no_regularisation.cc
*
* @author David Simon Kammer <david.kammer@epfl.ch>
*
* @date creation: Tue Dec 02 2014
* @date last modification: Fri Feb 23 2018
*
* @brief implementation of no regularisation
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
// simtools
#include "ntn_fricreg_no_regularisation.hh"
#include "dumper_nodal_field.hh"
#include "dumper_text.hh"
namespace akantu {
/* -------------------------------------------------------------------------- */
NTNFricRegNoRegularisation::NTNFricRegNoRegularisation(
- NTNBaseContact & contact, const ID & id, const MemoryID & memory_id)
- : NTNBaseFriction(contact, id, memory_id),
+ NTNBaseContact & contact, const ID & id)
+ : NTNBaseFriction(contact, id),
frictional_contact_pressure(0, 1, 0., id + ":frictional_contact_pressure",
0., "frictional_contact_pressure") {
AKANTU_DEBUG_IN();
NTNBaseFriction::registerSynchronizedArray(this->frictional_contact_pressure);
this->registerParam("frictional_contact_pressure",
this->frictional_contact_pressure, _pat_internal,
"contact pressure used for friction law");
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
const SynchronizedArray<Real> &
NTNFricRegNoRegularisation::internalGetContactPressure() {
AKANTU_DEBUG_IN();
this->computeFrictionalContactPressure();
AKANTU_DEBUG_OUT();
return this->frictional_contact_pressure;
}
/* -------------------------------------------------------------------------- */
void NTNFricRegNoRegularisation::computeFrictionalContactPressure() {
AKANTU_DEBUG_IN();
SolidMechanicsModel & model = this->contact.getModel();
UInt dim = model.getSpatialDimension();
// get contact arrays
const SynchronizedArray<bool> & is_in_contact =
this->internalGetIsInContact();
const Array<Real> & pressure = this->contact.getContactPressure().getArray();
Array<Real>::const_iterator<Vector<Real>> it = pressure.begin(dim);
UInt nb_contact_nodes = this->contact.getNbContactNodes();
for (UInt n = 0; n < nb_contact_nodes; ++n) {
// node pair is NOT in contact
if (!is_in_contact(n))
this->frictional_contact_pressure(n) = 0.;
// node pair is in contact
else {
// compute frictional contact pressure
const Vector<Real> & pres = it[n];
this->frictional_contact_pressure(n) = pres.norm();
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNFricRegNoRegularisation::registerSynchronizedArray(
SynchronizedArrayBase & array) {
AKANTU_DEBUG_IN();
this->frictional_contact_pressure.registerDependingArray(array);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNFricRegNoRegularisation::dumpRestart(
const std::string & file_name) const {
AKANTU_DEBUG_IN();
this->frictional_contact_pressure.dumpRestartFile(file_name);
NTNBaseFriction::dumpRestart(file_name);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNFricRegNoRegularisation::readRestart(const std::string & file_name) {
AKANTU_DEBUG_IN();
this->frictional_contact_pressure.readRestartFile(file_name);
NTNBaseFriction::readRestart(file_name);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNFricRegNoRegularisation::printself(std::ostream & stream,
int indent) const {
AKANTU_DEBUG_IN();
std::string space;
for (Int i = 0; i < indent; i++, space += AKANTU_INDENT)
;
stream << space << "NTNFricRegNoRegularisation [" << std::endl;
NTNBaseFriction::printself(stream, ++indent);
stream << space << "]" << std::endl;
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNFricRegNoRegularisation::addDumpFieldToDumper(
const std::string & dumper_name, const std::string & field_id) {
AKANTU_DEBUG_IN();
#ifdef AKANTU_USE_IOHELPER
// const SynchronizedArray<UInt> * nodal_filter =
// &(this->contact.getSlaves());
if (field_id == "frictional_contact_pressure") {
this->internalAddDumpFieldToDumper(
dumper_name, field_id,
std::make_unique<dumpers::NodalField<Real>>(
this->frictional_contact_pressure.getArray()));
} else {
NTNBaseFriction::addDumpFieldToDumper(dumper_name, field_id);
}
#endif
AKANTU_DEBUG_OUT();
}
} // namespace akantu
diff --git a/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_regularisations/ntn_fricreg_no_regularisation.hh b/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_regularisations/ntn_fricreg_no_regularisation.hh
index f7df9f108..75f399819 100644
--- a/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_regularisations/ntn_fricreg_no_regularisation.hh
+++ b/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_regularisations/ntn_fricreg_no_regularisation.hh
@@ -1,133 +1,132 @@
/**
* @file ntn_fricreg_no_regularisation.hh
*
* @author David Simon Kammer <david.kammer@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Fri Feb 23 2018
*
* @brief regularisation that does nothing
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#ifndef AST_NTN_FRICREG_NO_REGULARISATION_HH_
#define AST_NTN_FRICREG_NO_REGULARISATION_HH_
/* -------------------------------------------------------------------------- */
// simtools
#include "ntn_base_friction.hh"
namespace akantu {
/* -------------------------------------------------------------------------- */
class NTNFricRegNoRegularisation : public NTNBaseFriction {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
NTNFricRegNoRegularisation(NTNBaseContact & contact,
- const ID & id = "no_regularisation",
- const MemoryID & memory_id = 0);
+ const ID & id = "no_regularisation");
virtual ~NTNFricRegNoRegularisation(){};
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// set to steady state for no regularisation -> do nothing
virtual void setToSteadyState(){};
virtual void registerSynchronizedArray(SynchronizedArrayBase & array);
virtual void dumpRestart(const std::string & file_name) const;
virtual void readRestart(const std::string & file_name);
/// function to print the contain of the class
virtual void printself(std::ostream & stream, int indent = 0) const;
protected:
virtual void computeFrictionalContactPressure();
/// compute frictional strength according to friction law
virtual void computeFrictionalStrength(){};
/* ------------------------------------------------------------------------ */
/* Dumpable */
/* ------------------------------------------------------------------------ */
public:
virtual void addDumpFieldToDumper(const std::string & dumper_name,
const std::string & field_id);
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
protected:
/// get the is_in_contact array
virtual const SynchronizedArray<bool> & internalGetIsInContact() {
return this->contact.getIsInContact();
};
/// get the contact pressure (the norm: scalar value)
virtual const SynchronizedArray<Real> & internalGetContactPressure();
/// get the frictional strength array
virtual SynchronizedArray<Real> & internalGetFrictionalStrength() {
return this->frictional_strength;
};
/// get the is_sticking array
virtual SynchronizedArray<bool> & internalGetIsSticking() {
return this->is_sticking;
}
/// get the slip array
virtual SynchronizedArray<Real> & internalGetSlip() { return this->slip; }
/// get the slip array
virtual SynchronizedArray<Real> & internalGetCumulativeSlip() {
return this->cumulative_slip;
}
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
// contact pressure (absolut value) for computation of friction
SynchronizedArray<Real> frictional_contact_pressure;
};
/* -------------------------------------------------------------------------- */
/* inline functions */
/* -------------------------------------------------------------------------- */
//#include "ntn_fricreg_no_regularisation_inline_impl.hh"
/// standard output stream operator
inline std::ostream & operator<<(std::ostream & stream,
const NTNFricRegNoRegularisation & _this) {
_this.printself(stream);
return stream;
}
} // namespace akantu
#endif /* AST_NTN_FRICREG_NO_REGULARISATION_HH_ */
diff --git a/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_regularisations/ntn_fricreg_rubin_ampuero.cc b/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_regularisations/ntn_fricreg_rubin_ampuero.cc
index dfecefbb4..6877a5bf0 100644
--- a/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_regularisations/ntn_fricreg_rubin_ampuero.cc
+++ b/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_regularisations/ntn_fricreg_rubin_ampuero.cc
@@ -1,175 +1,174 @@
/**
* @file ntn_fricreg_rubin_ampuero.cc
*
* @author David Simon Kammer <david.kammer@epfl.ch>
*
* @date creation: Tue Dec 02 2014
* @date last modification: Fri Feb 23 2018
*
* @brief implementation of no regularisation
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
// simtools
#include "ntn_fricreg_rubin_ampuero.hh"
#include "dumper_nodal_field.hh"
#include "dumper_text.hh"
namespace akantu {
/* -------------------------------------------------------------------------- */
NTNFricRegRubinAmpuero::NTNFricRegRubinAmpuero(NTNBaseContact & contact,
- const ID & id,
- const MemoryID & memory_id)
- : NTNFricRegNoRegularisation(contact, id, memory_id),
+ const ID & id)
+ : NTNFricRegNoRegularisation(contact, id),
t_star(0, 1, 0., id + ":t_star", 0., "t_star") {
AKANTU_DEBUG_IN();
NTNFricRegNoRegularisation::registerSynchronizedArray(this->t_star);
this->registerParam("t_star", this->t_star, _pat_parsmod,
"time scale of regularization");
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
const SynchronizedArray<Real> &
NTNFricRegRubinAmpuero::internalGetContactPressure() {
AKANTU_DEBUG_IN();
SolidMechanicsModel & model = this->contact.getModel();
UInt dim = model.getSpatialDimension();
Real delta_t = model.getTimeStep();
// get contact arrays
const SynchronizedArray<bool> & is_in_contact =
this->internalGetIsInContact();
const Array<Real> & pressure = this->contact.getContactPressure().getArray();
Array<Real>::const_iterator<Vector<Real>> it = pressure.begin(dim);
UInt nb_contact_nodes = this->contact.getNbContactNodes();
for (UInt n = 0; n < nb_contact_nodes; ++n) {
// node pair is NOT in contact
if (!is_in_contact(n))
this->frictional_contact_pressure(n) = 0.;
// if t_star is too small compute like Coulomb friction (without
// regularization)
else if (Math::are_float_equal(this->t_star(n), 0.)) {
const Vector<Real> & pres = it[n];
this->frictional_contact_pressure(n) = pres.norm();
}
else {
// compute frictional contact pressure
// backward euler method: first order implicit numerical integration
// method
// \reg_pres_n+1 = (\reg_pres_n + \delta_t / \t_star * \cur_pres)
// / (1 + \delta_t / \t_star)
Real alpha = delta_t / this->t_star(n);
const Vector<Real> & pres = it[n];
this->frictional_contact_pressure(n) += alpha * pres.norm();
this->frictional_contact_pressure(n) /= 1 + alpha;
}
}
AKANTU_DEBUG_OUT();
return this->frictional_contact_pressure;
}
/* -------------------------------------------------------------------------- */
void NTNFricRegRubinAmpuero::setToSteadyState() {
AKANTU_DEBUG_IN();
NTNFricRegNoRegularisation::computeFrictionalContactPressure();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNFricRegRubinAmpuero::registerSynchronizedArray(
SynchronizedArrayBase & array) {
AKANTU_DEBUG_IN();
this->t_star.registerDependingArray(array);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNFricRegRubinAmpuero::dumpRestart(const std::string & file_name) const {
AKANTU_DEBUG_IN();
this->t_star.dumpRestartFile(file_name);
NTNFricRegNoRegularisation::dumpRestart(file_name);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNFricRegRubinAmpuero::readRestart(const std::string & file_name) {
AKANTU_DEBUG_IN();
this->t_star.readRestartFile(file_name);
NTNFricRegNoRegularisation::readRestart(file_name);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNFricRegRubinAmpuero::printself(std::ostream & stream,
int indent) const {
AKANTU_DEBUG_IN();
std::string space;
for (Int i = 0; i < indent; i++, space += AKANTU_INDENT)
;
stream << space << "NTNFricRegRubinAmpuero [" << std::endl;
NTNFricRegNoRegularisation::printself(stream, ++indent);
stream << space << "]" << std::endl;
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNFricRegRubinAmpuero::addDumpFieldToDumper(
const std::string & dumper_name, const std::string & field_id) {
AKANTU_DEBUG_IN();
#ifdef AKANTU_USE_IOHELPER
// const SynchronizedArray<UInt> * nodal_filter =
// &(this->contact.getSlaves());
if (field_id == "t_star") {
this->internalAddDumpFieldToDumper(
dumper_name, field_id,
std::make_unique<dumpers::NodalField<Real>>(this->t_star.getArray()));
} else {
NTNFricRegNoRegularisation::addDumpFieldToDumper(dumper_name, field_id);
}
#endif
AKANTU_DEBUG_OUT();
}
} // namespace akantu
diff --git a/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_regularisations/ntn_fricreg_rubin_ampuero.hh b/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_regularisations/ntn_fricreg_rubin_ampuero.hh
index 71593c4b8..2a6b85580 100644
--- a/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_regularisations/ntn_fricreg_rubin_ampuero.hh
+++ b/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_regularisations/ntn_fricreg_rubin_ampuero.hh
@@ -1,101 +1,100 @@
/**
* @file ntn_fricreg_rubin_ampuero.hh
*
* @author David Simon Kammer <david.kammer@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Fri Feb 23 2018
*
* @brief regularisation that regularizes the contact pressure
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#ifndef AST_NTN_FRICREG_RUBIN_AMPUERO_HH_
#define AST_NTN_FRICREG_RUBIN_AMPUERO_HH_
/* -------------------------------------------------------------------------- */
// simtools
#include "ntn_fricreg_no_regularisation.hh"
namespace akantu {
/* -------------------------------------------------------------------------- */
class NTNFricRegRubinAmpuero : public NTNFricRegNoRegularisation {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
NTNFricRegRubinAmpuero(NTNBaseContact & contact,
- const ID & id = "rubin_ampuero",
- const MemoryID & memory_id = 0);
+ const ID & id = "rubin_ampuero");
virtual ~NTNFricRegRubinAmpuero(){};
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
virtual void registerSynchronizedArray(SynchronizedArrayBase & array);
virtual void dumpRestart(const std::string & file_name) const;
virtual void readRestart(const std::string & file_name);
virtual void setToSteadyState();
/// function to print the contain of the class
virtual void printself(std::ostream & stream, int indent = 0) const;
/* ------------------------------------------------------------------------ */
/* Dumpable */
/* ------------------------------------------------------------------------ */
public:
virtual void addDumpFieldToDumper(const std::string & dumper_name,
const std::string & field_id);
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
protected:
/// get the contact pressure (the norm: scalar value)
virtual const SynchronizedArray<Real> & internalGetContactPressure();
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
private:
SynchronizedArray<Real> t_star;
};
/* -------------------------------------------------------------------------- */
/* inline functions */
/* -------------------------------------------------------------------------- */
//#include "ntn_fricreg_rubin_ampuero_inline_impl.hh"
/// standard output stream operator
inline std::ostream & operator<<(std::ostream & stream,
const NTNFricRegRubinAmpuero & _this) {
_this.printself(stream);
return stream;
}
} // namespace akantu
#endif /* AST_NTN_FRICREG_RUBIN_AMPUERO_HH_ */
diff --git a/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_regularisations/ntn_fricreg_simplified_prakash_clifton.cc b/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_regularisations/ntn_fricreg_simplified_prakash_clifton.cc
index 5812f7779..dfacc1585 100644
--- a/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_regularisations/ntn_fricreg_simplified_prakash_clifton.cc
+++ b/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_regularisations/ntn_fricreg_simplified_prakash_clifton.cc
@@ -1,162 +1,162 @@
/**
* @file ntn_fricreg_simplified_prakash_clifton.cc
*
* @author David Simon Kammer <david.kammer@epfl.ch>
*
* @date creation: Tue Dec 02 2014
* @date last modification: Fri Feb 23 2018
*
* @brief implementation of simplified prakash clifton with one parameter
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
// simtools
#include "ntn_fricreg_simplified_prakash_clifton.hh"
#include "dumper_nodal_field.hh"
#include "dumper_text.hh"
namespace akantu {
/* -------------------------------------------------------------------------- */
NTNFricRegSimplifiedPrakashClifton::NTNFricRegSimplifiedPrakashClifton(
- NTNBaseContact & contact, const ID & id, const MemoryID & memory_id)
- : NTNFricRegNoRegularisation(contact, id, memory_id),
+ NTNBaseContact & contact, const ID & id)
+ : NTNFricRegNoRegularisation(contact, id),
t_star(0, 1, 0., id + ":t_star", 0., "t_star"),
spc_internal(0, 1, 0., id + ":spc_internal", 0., "spc_internal") {
AKANTU_DEBUG_IN();
NTNFricRegNoRegularisation::registerSynchronizedArray(this->t_star);
NTNFricRegNoRegularisation::registerSynchronizedArray(this->spc_internal);
this->registerParam("t_star", this->t_star, _pat_parsmod,
"time scale of regularisation");
this->registerParam("spc_internal", this->spc_internal, _pat_internal, "");
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNFricRegSimplifiedPrakashClifton::computeFrictionalStrength() {
AKANTU_DEBUG_IN();
SolidMechanicsModel & model = this->contact.getModel();
Real delta_t = model.getTimeStep();
UInt nb_contact_nodes = this->contact.getNbContactNodes();
for (UInt n = 0; n < nb_contact_nodes; ++n) {
Real alpha = delta_t / this->t_star(n);
this->frictional_strength(n) += alpha * this->spc_internal(n);
this->frictional_strength(n) /= 1 + alpha;
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNFricRegSimplifiedPrakashClifton::setToSteadyState() {
AKANTU_DEBUG_IN();
/// fill the spc_internal array
computeFrictionalStrength();
/// set strength without regularisation
UInt nb_contact_nodes = this->contact.getNbContactNodes();
for (UInt n = 0; n < nb_contact_nodes; ++n) {
this->frictional_strength(n) = this->spc_internal(n);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNFricRegSimplifiedPrakashClifton::registerSynchronizedArray(
SynchronizedArrayBase & array) {
AKANTU_DEBUG_IN();
this->t_star.registerDependingArray(array);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNFricRegSimplifiedPrakashClifton::dumpRestart(
const std::string & file_name) const {
AKANTU_DEBUG_IN();
this->t_star.dumpRestartFile(file_name);
this->spc_internal.dumpRestartFile(file_name);
NTNFricRegNoRegularisation::dumpRestart(file_name);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNFricRegSimplifiedPrakashClifton::readRestart(
const std::string & file_name) {
AKANTU_DEBUG_IN();
this->t_star.readRestartFile(file_name);
this->spc_internal.readRestartFile(file_name);
NTNFricRegNoRegularisation::readRestart(file_name);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNFricRegSimplifiedPrakashClifton::printself(std::ostream & stream,
int indent) const {
AKANTU_DEBUG_IN();
std::string space;
for (Int i = 0; i < indent; i++, space += AKANTU_INDENT)
;
stream << space << "NTNFricRegSimplifiedPrakashClifton [" << std::endl;
NTNFricRegNoRegularisation::printself(stream, ++indent);
stream << space << "]" << std::endl;
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNFricRegSimplifiedPrakashClifton::addDumpFieldToDumper(
const std::string & dumper_name, const std::string & field_id) {
AKANTU_DEBUG_IN();
#ifdef AKANTU_USE_IOHELPER
// const SynchronizedArray<UInt> * nodal_filter =
// &(this->contact.getSlaves());
if (field_id == "t_star") {
this->internalAddDumpFieldToDumper(
dumper_name, field_id,
std::make_unique<dumpers::NodalField<Real>>(this->t_star.getArray()));
} else {
NTNFricRegNoRegularisation::addDumpFieldToDumper(dumper_name, field_id);
}
#endif
AKANTU_DEBUG_OUT();
}
} // namespace akantu
diff --git a/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_regularisations/ntn_fricreg_simplified_prakash_clifton.hh b/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_regularisations/ntn_fricreg_simplified_prakash_clifton.hh
index c513fdeca..e8d285288 100644
--- a/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_regularisations/ntn_fricreg_simplified_prakash_clifton.hh
+++ b/extra_packages/traction-at-split-node-contact/src/ntn_contact/friction_regularisations/ntn_fricreg_simplified_prakash_clifton.hh
@@ -1,112 +1,111 @@
/**
* @file ntn_fricreg_simplified_prakash_clifton.hh
*
* @author David Simon Kammer <david.kammer@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Fri Feb 23 2018
*
* @brief regularisation that regularizes the frictional strength with one
* parameter
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#ifndef AST_NTN_FRICREG_SIMPLIFIED_PRAKASH_CLIFTON_HH_
#define AST_NTN_FRICREG_SIMPLIFIED_PRAKASH_CLIFTON_HH_
/* -------------------------------------------------------------------------- */
// simtools
#include "ntn_fricreg_no_regularisation.hh"
namespace akantu {
/* -------------------------------------------------------------------------- */
class NTNFricRegSimplifiedPrakashClifton : public NTNFricRegNoRegularisation {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
NTNFricRegSimplifiedPrakashClifton(
- NTNBaseContact & contact, const ID & id = "simplified_prakash_clifton",
- const MemoryID & memory_id = 0);
+ NTNBaseContact & contact, const ID & id = "simplified_prakash_clifton");
virtual ~NTNFricRegSimplifiedPrakashClifton(){};
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
virtual void registerSynchronizedArray(SynchronizedArrayBase & array);
virtual void dumpRestart(const std::string & file_name) const;
virtual void readRestart(const std::string & file_name);
virtual void setToSteadyState();
/// function to print the contain of the class
virtual void printself(std::ostream & stream, int indent = 0) const;
protected:
/// compute frictional strength according to friction law
virtual void computeFrictionalStrength();
/* ------------------------------------------------------------------------ */
/* Dumpable */
/* ------------------------------------------------------------------------ */
public:
virtual void addDumpFieldToDumper(const std::string & dumper_name,
const std::string & field_id);
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
protected:
/// get the frictional strength array
virtual SynchronizedArray<Real> & internalGetFrictionalStrength() {
return this->spc_internal;
};
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
private:
SynchronizedArray<Real> t_star;
// to get the incremental frictional strength
SynchronizedArray<Real> spc_internal;
};
/* -------------------------------------------------------------------------- */
/* inline functions */
/* -------------------------------------------------------------------------- */
//#include "ntn_fricreg_simplified_prakash_clifton_inline_impl.hh"
/// standard output stream operator
inline std::ostream &
operator<<(std::ostream & stream,
const NTNFricRegSimplifiedPrakashClifton & _this) {
_this.printself(stream);
return stream;
}
} // namespace akantu
#endif /* AST_NTN_FRICREG_SIMPLIFIED_PRAKASH_CLIFTON_HH_ */
diff --git a/extra_packages/traction-at-split-node-contact/src/ntn_contact/mIIasym_contact.cc b/extra_packages/traction-at-split-node-contact/src/ntn_contact/mIIasym_contact.cc
index 5e221e535..014b71886 100644
--- a/extra_packages/traction-at-split-node-contact/src/ntn_contact/mIIasym_contact.cc
+++ b/extra_packages/traction-at-split-node-contact/src/ntn_contact/mIIasym_contact.cc
@@ -1,119 +1,118 @@
/**
* @file mIIasym_contact.cc
*
* @author David Simon Kammer <david.kammer@epfl.ch>
*
* @date creation: Tue Dec 02 2014
* @date last modification: Fri Feb 23 2018
*
* @brief
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
// simtools
#include "mIIasym_contact.hh"
namespace akantu {
/* -------------------------------------------------------------------------- */
-MIIASYMContact::MIIASYMContact(SolidMechanicsModel & model, const ID & id,
- const MemoryID & memory_id)
- : NTRFContact(model, id, memory_id) {
+MIIASYMContact::MIIASYMContact(SolidMechanicsModel & model, const ID & id)
+ : NTRFContact(model, id) {
AKANTU_DEBUG_IN();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void MIIASYMContact::updateImpedance() {
AKANTU_DEBUG_IN();
NTRFContact::updateImpedance();
for (UInt i = 0; i < this->impedance.size(); ++i) {
this->impedance(i) *= 0.5;
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
/// WARNING: this is only valid for the acceleration in equilibrium
void MIIASYMContact::computeRelativeNormalField(
const Array<Real> & field, Array<Real> & rel_normal_field) const {
AKANTU_DEBUG_IN();
NTRFContact::computeRelativeNormalField(field, rel_normal_field);
for (auto it_rtfield = rel_normal_field.begin();
it_rtfield != rel_normal_field.end(); ++it_rtfield) {
// in the anti-symmetric case
*it_rtfield *= 2.;
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void MIIASYMContact::computeRelativeTangentialField(
const Array<Real> & field, Array<Real> & rel_tang_field) const {
AKANTU_DEBUG_IN();
NTRFContact::computeRelativeTangentialField(field, rel_tang_field);
UInt dim = this->model.getSpatialDimension();
for (Array<Real>::iterator<Vector<Real>> it_rtfield =
rel_tang_field.begin(dim);
it_rtfield != rel_tang_field.end(dim); ++it_rtfield) {
// in the anti-symmetric case, the tangential fields become twice as large
*it_rtfield *= 2.;
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void MIIASYMContact::computeContactPressureInEquilibrium() {
AKANTU_DEBUG_IN();
NTRFContact::computeContactPressure();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void MIIASYMContact::printself(std::ostream & stream, int indent) const {
AKANTU_DEBUG_IN();
std::string space;
for (Int i = 0; i < indent; i++, space += AKANTU_INDENT)
;
stream << space << "MIIASYMContact [" << std::endl;
NTRFContact::printself(stream, indent);
stream << space << "]" << std::endl;
AKANTU_DEBUG_OUT();
}
} // namespace akantu
diff --git a/extra_packages/traction-at-split-node-contact/src/ntn_contact/mIIasym_contact.hh b/extra_packages/traction-at-split-node-contact/src/ntn_contact/mIIasym_contact.hh
index 3d2855cc6..5de74ea4d 100644
--- a/extra_packages/traction-at-split-node-contact/src/ntn_contact/mIIasym_contact.hh
+++ b/extra_packages/traction-at-split-node-contact/src/ntn_contact/mIIasym_contact.hh
@@ -1,90 +1,90 @@
/**
* @file mIIasym_contact.hh
*
* @author David Simon Kammer <david.kammer@epfl.ch>
*
* @date creation: Tue Dec 02 2014
* @date last modification: Fri Feb 23 2018
*
* @brief contact for mode II anti-symmetric simulations
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#ifndef AST_MIIASYM_CONTACT_HH_
#define AST_MIIASYM_CONTACT_HH_
/* -------------------------------------------------------------------------- */
// simtools
#include "ntrf_contact.hh"
namespace akantu {
/* -------------------------------------------------------------------------- */
class MIIASYMContact : public NTRFContact {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
- MIIASYMContact(SolidMechanicsModel & model, const ID & id = "contact",
- const MemoryID & memory_id = 0);
- virtual ~MIIASYMContact() = default;
+ MIIASYMContact(SolidMechanicsModel & model, const ID & id = "contact");
+ ~MIIASYMContact() override = default;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// update the impedance matrix
virtual void updateImpedance();
/// compute contact pressure -> do nothing because can only compute it in
/// equilibrium
- virtual void computeContactPressure(){};
+ void computeContactPressure() override{};
/// compute relative normal field (only value that has to be multiplied with
/// the normal)
/// WARNING: this is only valid for the acceleration in equilibrium
- virtual void computeRelativeNormalField(const Array<Real> & field,
- Array<Real> & rel_normal_field) const;
+ void
+ computeRelativeNormalField(const Array<Real> & field,
+ Array<Real> & rel_normal_field) const override;
/// compute relative tangential field (complet array)
/// relative to master nodes
- virtual void
+ void
computeRelativeTangentialField(const Array<Real> & field,
- Array<Real> & rel_tang_field) const;
+ Array<Real> & rel_tang_field) const override;
/// compute contact pressure that is used over the entire time
virtual void computeContactPressureInEquilibrium();
/// function to print the contain of the class
- virtual void printself(std::ostream & stream, int indent = 0) const;
+ void printself(std::ostream & stream, int indent = 0) const override;
};
/// standard output stream operator
inline std::ostream & operator<<(std::ostream & stream,
const MIIASYMContact & _this) {
_this.printself(stream);
return stream;
}
} // namespace akantu
#endif /* AST_MIIASYM_CONTACT_HH_ */
diff --git a/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_base_contact.cc b/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_base_contact.cc
index 4f9c385a6..b2a7c43c2 100644
--- a/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_base_contact.cc
+++ b/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_base_contact.cc
@@ -1,567 +1,565 @@
/**
* @file ntn_base_contact.cc
*
* @author David Simon Kammer <david.kammer@epfl.ch>
*
* @date creation: Tue Dec 02 2014
* @date last modification: Fri Feb 23 2018
*
* @brief implementation of ntn base contact
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "ntn_base_contact.hh"
#include "dof_manager_default.hh"
#include "dumpable_inline_impl.hh"
#include "dumper_nodal_field.hh"
#include "dumper_text.hh"
#include "element_synchronizer.hh"
#include "mesh_utils.hh"
#include "non_linear_solver_lumped.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
// NTNContactSynchElementFilter::NTNContactSynchElementFilter(
// NTNBaseContact & contact)
// : contact(contact),
// connectivity(contact.getModel().getMesh().getConnectivities()) {
// AKANTU_DEBUG_IN();
// AKANTU_DEBUG_OUT();
// }
/* -------------------------------------------------------------------------- */
// bool NTNContactSynchElementFilter::operator()(const Element & e) {
// AKANTU_DEBUG_IN();
// ElementType type = e.type;
// UInt element = e.element;
// GhostType ghost_type = e.ghost_type;
// // loop over all nodes of this element
// bool need_element = false;
// UInt nb_nodes = Mesh::getNbNodesPerElement(type);
// for (UInt n = 0; n < nb_nodes; ++n) {
// UInt nn = this->connectivity(type, ghost_type)(element, n);
// // if one nodes is in this contact, we need this element
// if (this->contact.getNodeIndex(nn) >= 0) {
// need_element = true;
// break;
// }
// }
// AKANTU_DEBUG_OUT();
// return need_element;
// }
/* -------------------------------------------------------------------------- */
-NTNBaseContact::NTNBaseContact(SolidMechanicsModel & model, const ID & id,
- const MemoryID & memory_id)
- : Memory(id, memory_id), Dumpable(), model(model),
+NTNBaseContact::NTNBaseContact(SolidMechanicsModel & model, const ID & id)
+ : id(id), model(model),
slaves(0, 1, 0, id + ":slaves", std::numeric_limits<UInt>::quiet_NaN(),
"slaves"),
normals(0, model.getSpatialDimension(), 0, id + ":normals",
std::numeric_limits<Real>::quiet_NaN(), "normals"),
contact_pressure(
0, model.getSpatialDimension(), 0, id + ":contact_pressure",
std::numeric_limits<Real>::quiet_NaN(), "contact_pressure"),
is_in_contact(0, 1, false, id + ":is_in_contact", false, "is_in_contact"),
lumped_boundary_slaves(0, 1, 0, id + ":lumped_boundary_slaves",
std::numeric_limits<Real>::quiet_NaN(),
"lumped_boundary_slaves"),
impedance(0, 1, 0, id + ":impedance",
std::numeric_limits<Real>::quiet_NaN(), "impedance"),
- contact_surfaces(), slave_elements("slave_elements", id, memory_id),
- node_to_elements() {
+ slave_elements("slave_elements", id) {
AKANTU_DEBUG_IN();
auto & boundary_fem = this->model.getFEEngineBoundary();
for (auto && ghost_type : ghost_types) {
boundary_fem.initShapeFunctions(ghost_type);
}
auto & mesh = this->model.getMesh();
auto spatial_dimension = this->model.getSpatialDimension();
this->slave_elements.initialize(mesh,
_spatial_dimension = spatial_dimension - 1);
MeshUtils::buildNode2Elements(mesh, this->node_to_elements,
spatial_dimension - 1);
this->registerDumper<DumperText>("text_all", id, true);
this->addDumpFilteredMesh(mesh, slave_elements, slaves.getArray(),
spatial_dimension - 1, _not_ghost, _ek_regular);
// parallelisation
this->synch_registry = std::make_unique<SynchronizerRegistry>();
this->synch_registry->registerDataAccessor(*this);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
NTNBaseContact::~NTNBaseContact() = default;
/* -------------------------------------------------------------------------- */
void NTNBaseContact::initParallel() {
AKANTU_DEBUG_IN();
this->synchronizer = std::make_unique<ElementSynchronizer>(
this->model.getMesh().getElementSynchronizer());
this->synchronizer->filterScheme([&](auto && element) {
// loop over all nodes of this element
Vector<UInt> conn = const_cast<const Mesh &>(this->model.getMesh())
.getConnectivity(element);
for (auto & node : conn) {
// if one nodes is in this contact, we need this element
if (this->getNodeIndex(node) >= 0) {
return true;
}
}
return false;
});
this->synch_registry->registerSynchronizer(*(this->synchronizer),
SynchronizationTag::_cf_nodal);
this->synch_registry->registerSynchronizer(*(this->synchronizer),
SynchronizationTag::_cf_incr);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNBaseContact::findBoundaryElements(
const Array<UInt> & interface_nodes, ElementTypeMapArray<UInt> & elements) {
AKANTU_DEBUG_IN();
// add connected boundary elements that have all nodes on this contact
for (const auto & node : interface_nodes) {
for (const auto & element : this->node_to_elements.getRow(node)) {
Vector<UInt> conn = const_cast<const Mesh &>(this->model.getMesh())
.getConnectivity(element);
auto nb_nodes = conn.size();
decltype(nb_nodes) nb_found_nodes = 0;
for (auto & nn : conn) {
if (interface_nodes.find(nn) != UInt(-1)) {
nb_found_nodes++;
} else {
break;
}
}
// this is an element between all contact nodes
// and is not already in the elements
if ((nb_found_nodes == nb_nodes) &&
(elements(element.type, element.ghost_type).find(element.element) ==
UInt(-1))) {
elements(element.type, element.ghost_type).push_back(element.element);
}
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNBaseContact::addSplitNode(UInt node, UInt) {
AKANTU_DEBUG_IN();
UInt dim = this->model.getSpatialDimension();
// add to node arrays
this->slaves.push_back(node);
// set contact as false
this->is_in_contact.push_back(false);
// before initializing
// set contact pressure, normal, lumped_boundary to Nan
this->contact_pressure.push_back(std::numeric_limits<Real>::quiet_NaN());
this->impedance.push_back(std::numeric_limits<Real>::quiet_NaN());
this->lumped_boundary_slaves.push_back(
std::numeric_limits<Real>::quiet_NaN());
Vector<Real> nan_normal(dim, std::numeric_limits<Real>::quiet_NaN());
this->normals.push_back(nan_normal);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNBaseContact::registerSynchronizedArray(SynchronizedArrayBase & array) {
AKANTU_DEBUG_IN();
this->slaves.registerDependingArray(array);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNBaseContact::dumpRestart(const std::string & file_name) const {
AKANTU_DEBUG_IN();
this->slaves.dumpRestartFile(file_name);
this->normals.dumpRestartFile(file_name);
this->is_in_contact.dumpRestartFile(file_name);
this->contact_pressure.dumpRestartFile(file_name);
this->lumped_boundary_slaves.dumpRestartFile(file_name);
this->impedance.dumpRestartFile(file_name);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNBaseContact::readRestart(const std::string & file_name) {
AKANTU_DEBUG_IN();
this->slaves.readRestartFile(file_name);
this->normals.readRestartFile(file_name);
this->is_in_contact.readRestartFile(file_name);
this->contact_pressure.readRestartFile(file_name);
this->lumped_boundary_slaves.readRestartFile(file_name);
this->impedance.readRestartFile(file_name);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
UInt NTNBaseContact::getNbNodesInContact() const {
AKANTU_DEBUG_IN();
UInt nb_contact = 0;
UInt nb_nodes = this->getNbContactNodes();
const Mesh & mesh = this->model.getMesh();
for (UInt n = 0; n < nb_nodes; ++n) {
bool is_local_node = mesh.isLocalOrMasterNode(this->slaves(n));
bool is_pbc_slave_node = mesh.isPeriodicSlave(this->slaves(n));
if (is_local_node && !is_pbc_slave_node && this->is_in_contact(n)) {
nb_contact++;
}
}
mesh.getCommunicator().allReduce(nb_contact, SynchronizerOperation::_sum);
AKANTU_DEBUG_OUT();
return nb_contact;
}
/* -------------------------------------------------------------------------- */
void NTNBaseContact::updateInternalData() {
AKANTU_DEBUG_IN();
updateNormals();
updateLumpedBoundary();
updateImpedance();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNBaseContact::updateLumpedBoundary() {
AKANTU_DEBUG_IN();
this->internalUpdateLumpedBoundary(this->slaves.getArray(),
this->slave_elements,
this->lumped_boundary_slaves);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNBaseContact::internalUpdateLumpedBoundary(
const Array<UInt> & nodes, const ElementTypeMapArray<UInt> & elements,
SynchronizedArray<Real> & boundary) {
AKANTU_DEBUG_IN();
// set all values in lumped_boundary to zero
boundary.zero();
UInt dim = this->model.getSpatialDimension();
// UInt nb_contact_nodes = getNbContactNodes();
const FEEngine & boundary_fem = this->model.getFEEngineBoundary();
const Mesh & mesh = this->model.getMesh();
for (auto ghost_type : ghost_types) {
for (auto & type : mesh.elementTypes(dim - 1, ghost_type)) {
UInt nb_elements = mesh.getNbElement(type, ghost_type);
UInt nb_nodes_per_element = mesh.getNbNodesPerElement(type);
const Array<UInt> & connectivity = mesh.getConnectivity(type, ghost_type);
// get shapes and compute integral
const Array<Real> & shapes = boundary_fem.getShapes(type, ghost_type);
Array<Real> area(nb_elements, nb_nodes_per_element);
boundary_fem.integrate(shapes, area, nb_nodes_per_element, type,
ghost_type);
if (this->contact_surfaces.size() == 0) {
AKANTU_DEBUG_WARNING(
"No surfaces in ntn base contact."
<< " You have to define the lumped boundary by yourself.");
}
Array<UInt>::const_iterator<UInt> elem_it =
(elements)(type, ghost_type).begin();
Array<UInt>::const_iterator<UInt> elem_it_end =
(elements)(type, ghost_type).end();
// loop over contact nodes
for (; elem_it != elem_it_end; ++elem_it) {
for (UInt q = 0; q < nb_nodes_per_element; ++q) {
UInt node = connectivity(*elem_it, q);
UInt node_index = nodes.find(node);
AKANTU_DEBUG_ASSERT(node_index != UInt(-1), "Could not find node "
<< node
<< " in the array!");
Real area_to_add = area(*elem_it, q);
boundary(node_index) += area_to_add;
}
}
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNBaseContact::computeAcceleration(Array<Real> & acceleration) const {
auto && dof_manager =
dynamic_cast<DOFManagerDefault &>(model.getDOFManager());
const auto & b = dof_manager.getResidual();
acceleration.resize(b.size());
const auto & blocked_dofs = dof_manager.getGlobalBlockedDOFs();
const auto & A = dof_manager.getLumpedMatrix("M");
Array<bool> blocked_dofs_bool(blocked_dofs.size());
for (auto && data : zip(blocked_dofs, blocked_dofs_bool)) {
std::get<1>(data) = std::get<0>(data);
}
// pre-compute the acceleration
// (not increment acceleration, because residual is still Kf)
NonLinearSolverLumped::solveLumped(A, acceleration, b, this->model.getF_M2A(),
blocked_dofs_bool);
}
/* -------------------------------------------------------------------------- */
void NTNBaseContact::computeContactPressure() {
AKANTU_DEBUG_IN();
UInt dim = this->model.getSpatialDimension();
Real delta_t = this->model.getTimeStep();
UInt nb_contact_nodes = getNbContactNodes();
AKANTU_DEBUG_ASSERT(delta_t > 0.,
"Cannot compute contact pressure if no time step is set");
// synchronize data
this->synch_registry->synchronize(SynchronizationTag::_cf_nodal);
Array<Real> acceleration(0, dim);
this->computeAcceleration(acceleration);
// compute relative normal fields of displacement, velocity and acceleration
Array<Real> r_disp(0, 1);
Array<Real> r_velo(0, 1);
Array<Real> r_acce(0, 1);
Array<Real> r_old_acce(0, 1);
computeNormalGap(r_disp);
// computeRelativeNormalField(this->model.getCurrentPosition(), r_disp);
computeRelativeNormalField(this->model.getVelocity(), r_velo);
computeRelativeNormalField(acceleration, r_acce);
computeRelativeNormalField(this->model.getAcceleration(), r_old_acce);
AKANTU_DEBUG_ASSERT(r_disp.size() == nb_contact_nodes,
"computeRelativeNormalField does not give back arrays "
<< "size == nb_contact_nodes. nb_contact_nodes = "
<< nb_contact_nodes
<< " | array size = " << r_disp.size());
// compute gap array for all nodes
Array<Real> gap(nb_contact_nodes, 1);
Real * gap_p = gap.storage();
Real * r_disp_p = r_disp.storage();
Real * r_velo_p = r_velo.storage();
Real * r_acce_p = r_acce.storage();
Real * r_old_acce_p = r_old_acce.storage();
for (UInt i = 0; i < nb_contact_nodes; ++i) {
*gap_p = *r_disp_p + delta_t * *r_velo_p + delta_t * delta_t * *r_acce_p -
0.5 * delta_t * delta_t * *r_old_acce_p;
// increment pointers
gap_p++;
r_disp_p++;
r_velo_p++;
r_acce_p++;
r_old_acce_p++;
}
// check if gap is negative -> is in contact
for (UInt n = 0; n < nb_contact_nodes; ++n) {
if (gap(n) <= 0.) {
for (UInt d = 0; d < dim; ++d) {
this->contact_pressure(n, d) =
this->impedance(n) * gap(n) / (2 * delta_t) * this->normals(n, d);
}
this->is_in_contact(n) = true;
} else {
for (UInt d = 0; d < dim; ++d)
this->contact_pressure(n, d) = 0.;
this->is_in_contact(n) = false;
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNBaseContact::applyContactPressure() {
AKANTU_DEBUG_IN();
UInt nb_contact_nodes = getNbContactNodes();
UInt dim = this->model.getSpatialDimension();
Array<Real> & residual = this->model.getInternalForce();
for (UInt n = 0; n < nb_contact_nodes; ++n) {
UInt slave = this->slaves(n);
for (UInt d = 0; d < dim; ++d) {
// residual(master,d) += this->lumped_boundary(n,0) *
// this->contact_pressure(n,d);
residual(slave, d) -=
this->lumped_boundary_slaves(n) * this->contact_pressure(n, d);
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
Int NTNBaseContact::getNodeIndex(UInt node) const {
return this->slaves.find(node);
}
/* -------------------------------------------------------------------------- */
void NTNBaseContact::printself(std::ostream & stream, int indent) const {
AKANTU_DEBUG_IN();
std::string space;
for (Int i = 0; i < indent; i++, space += AKANTU_INDENT)
;
stream << space << "NTNBaseContact [" << std::endl;
stream << space << " + id : " << id << std::endl;
stream << space << " + slaves : " << std::endl;
this->slaves.printself(stream, indent + 2);
stream << space << " + normals : " << std::endl;
this->normals.printself(stream, indent + 2);
stream << space << " + contact_pressure : " << std::endl;
this->contact_pressure.printself(stream, indent + 2);
stream << space << "]" << std::endl;
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNBaseContact::syncArrays(SyncChoice sync_choice) {
AKANTU_DEBUG_IN();
this->slaves.syncElements(sync_choice);
this->normals.syncElements(sync_choice);
this->is_in_contact.syncElements(sync_choice);
this->contact_pressure.syncElements(sync_choice);
this->lumped_boundary_slaves.syncElements(sync_choice);
this->impedance.syncElements(sync_choice);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNBaseContact::addDumpFieldToDumper(const std::string & dumper_name,
const std::string & field_id) {
AKANTU_DEBUG_IN();
#ifdef AKANTU_USE_IOHELPER
const Array<UInt> & nodal_filter = this->slaves.getArray();
#define ADD_FIELD(field_id, field, type) \
internalAddDumpFieldToDumper( \
dumper_name, field_id, \
std::make_unique< \
- dumpers::NodalField<type, true, Array<type>, Array<UInt>>>( \
+ dumpers::NodalField<type, true, Array<type>, Array<UInt>>>( \
field, 0, 0, &nodal_filter))
if (field_id == "displacement") {
ADD_FIELD(field_id, this->model.getDisplacement(), Real);
} else if (field_id == "mass") {
ADD_FIELD(field_id, this->model.getMass(), Real);
} else if (field_id == "velocity") {
ADD_FIELD(field_id, this->model.getVelocity(), Real);
} else if (field_id == "acceleration") {
ADD_FIELD(field_id, this->model.getAcceleration(), Real);
} else if (field_id == "external_force") {
ADD_FIELD(field_id, this->model.getExternalForce(), Real);
} else if (field_id == "internal_force") {
ADD_FIELD(field_id, this->model.getInternalForce(), Real);
} else if (field_id == "blocked_dofs") {
ADD_FIELD(field_id, this->model.getBlockedDOFs(), bool);
} else if (field_id == "increment") {
ADD_FIELD(field_id, this->model.getIncrement(), Real);
} else if (field_id == "normal") {
internalAddDumpFieldToDumper(
dumper_name, field_id,
std::make_unique<dumpers::NodalField<Real>>(this->normals.getArray()));
} else if (field_id == "contact_pressure") {
internalAddDumpFieldToDumper(dumper_name, field_id,
std::make_unique<dumpers::NodalField<Real>>(
this->contact_pressure.getArray()));
} else if (field_id == "is_in_contact") {
internalAddDumpFieldToDumper(dumper_name, field_id,
std::make_unique<dumpers::NodalField<bool>>(
this->is_in_contact.getArray()));
} else if (field_id == "lumped_boundary_slave") {
internalAddDumpFieldToDumper(dumper_name, field_id,
std::make_unique<dumpers::NodalField<Real>>(
this->lumped_boundary_slaves.getArray()));
} else if (field_id == "impedance") {
- internalAddDumpFieldToDumper(
- dumper_name, field_id,
- std::make_unique<dumpers::NodalField<Real>>(this->impedance.getArray()));
+ internalAddDumpFieldToDumper(dumper_name, field_id,
+ std::make_unique<dumpers::NodalField<Real>>(
+ this->impedance.getArray()));
} else {
std::cerr << "Could not add field '" << field_id
<< "' to the dumper. Just ignored it." << std::endl;
}
#undef ADD_FIELD
#endif
AKANTU_DEBUG_OUT();
}
} // namespace akantu
diff --git a/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_base_contact.hh b/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_base_contact.hh
index 8c9c3cf9d..d786ccab5 100644
--- a/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_base_contact.hh
+++ b/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_base_contact.hh
@@ -1,251 +1,250 @@
/**
* @file ntn_base_contact.hh
*
* @author David Simon Kammer <david.kammer@epfl.ch>
*
* @date creation: Tue Dec 02 2014
* @date last modification: Fri Feb 23 2018
*
* @brief base contact for ntn and ntrf contact
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#ifndef AST_NTN_BASE_CONTACT_HH_
#define AST_NTN_BASE_CONTACT_HH_
/* -------------------------------------------------------------------------- */
// akantu
#include "aka_csr.hh"
#include "solid_mechanics_model.hh"
// simtools
#include "synchronized_array.hh"
namespace akantu {
class NTNBaseContact;
/* -------------------------------------------------------------------------- */
// class NTNContactSynchElementFilter : public SynchElementFilter {
// public:
// // constructor
// NTNContactSynchElementFilter(NTNBaseContact & contact);
// // answer to: do we need this element ?
// virtual bool operator()(const Element & e);
// private:
// const NTNBaseContact & contact;
// const ElementTypeMapArray<UInt> & connectivity;
// };
/* -------------------------------------------------------------------------- */
-class NTNBaseContact : protected Memory,
- public DataAccessor<Element>,
- public Dumpable {
+class NTNBaseContact : public DataAccessor<Element>, public Dumpable {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
- NTNBaseContact(SolidMechanicsModel & model, const ID & id = "contact",
- const MemoryID & memory_id = 0);
- virtual ~NTNBaseContact();
+ NTNBaseContact(SolidMechanicsModel & model, const ID & id = "contact");
+ ~NTNBaseContact() override;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// initializes ntn contact parallel
virtual void initParallel();
/// add split node
virtual void addSplitNode(UInt node, UInt = 0);
/// update normals, lumped boundary, and impedance
virtual void updateInternalData();
/// update (compute the normals)
virtual void updateNormals() = 0;
/// update the lumped boundary B matrix
virtual void updateLumpedBoundary();
/// update the impedance matrix
virtual void updateImpedance() = 0;
/// compute the normal contact force
virtual void computeContactPressure();
/// impose the normal contact force
- virtual void applyContactPressure() ;
+ virtual void applyContactPressure();
/// register synchronizedarrays for sync
virtual void registerSynchronizedArray(SynchronizedArrayBase & array);
/// dump restart file
virtual void dumpRestart(const std::string & file_name) const;
/// read restart file
virtual void readRestart(const std::string & file_name);
/// compute the normal gap
virtual void computeNormalGap(Array<Real> & gap) const = 0;
/// compute relative normal field (only value that has to be multiplied with
/// the normal)
/// relative to master nodes
virtual void
computeRelativeNormalField(const Array<Real> & field,
Array<Real> & rel_normal_field) const = 0;
/// compute relative tangential field (complet array)
/// relative to master nodes
virtual void
computeRelativeTangentialField(const Array<Real> & field,
Array<Real> & rel_tang_field) const = 0;
/// function to print the contain of the class
virtual void printself(std::ostream & stream, int indent = 0) const;
/// computes the acceleration
void computeAcceleration(Array<Real> & acceleration) const;
protected:
/// updateLumpedBoundary
virtual void
internalUpdateLumpedBoundary(const Array<UInt> & nodes,
const ElementTypeMapArray<UInt> & elements,
SynchronizedArray<Real> & boundary);
// to find the slave_elements or master_elements
virtual void findBoundaryElements(const Array<UInt> & interface_nodes,
ElementTypeMapArray<UInt> & elements);
/// synchronize arrays
virtual void syncArrays(SyncChoice sync_choice);
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
inline UInt getNbData(const Array<Element> & elements,
const SynchronizationTag & tag) const override;
inline void packData(CommunicationBuffer & buffer,
const Array<Element> & elements,
const SynchronizationTag & tag) const override;
inline void unpackData(CommunicationBuffer & buffer,
const Array<Element> & elements,
const SynchronizationTag & tag) override;
/* ------------------------------------------------------------------------ */
/* Dumpable */
/* ------------------------------------------------------------------------ */
public:
void addDumpFieldToDumper(const std::string & dumper_name,
- const std::string & field_id) override;
+ const std::string & field_id) override;
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
AKANTU_GET_MACRO(Model, model, SolidMechanicsModel &)
AKANTU_GET_MACRO(Slaves, slaves, const SynchronizedArray<UInt> &)
AKANTU_GET_MACRO(Normals, normals, const SynchronizedArray<Real> &)
AKANTU_GET_MACRO(ContactPressure, contact_pressure,
const SynchronizedArray<Real> &)
AKANTU_GET_MACRO(LumpedBoundarySlaves, lumped_boundary_slaves,
const SynchronizedArray<Real> &)
AKANTU_GET_MACRO(Impedance, impedance, const SynchronizedArray<Real> &)
AKANTU_GET_MACRO(IsInContact, is_in_contact, const SynchronizedArray<bool> &)
AKANTU_GET_MACRO(SlaveElements, slave_elements,
const ElementTypeMapArray<UInt> &)
AKANTU_GET_MACRO(SynchronizerRegistry, *synch_registry,
SynchronizerRegistry &)
/// get number of nodes that are in contact (globally, on all procs together)
/// is_in_contact = true
virtual UInt getNbNodesInContact() const;
/// get index of node in either slaves or masters array
/// if node is in neither of them, return -1
virtual Int getNodeIndex(UInt node) const;
/// get number of contact nodes: nodes in the system locally (on this proc)
/// is_in_contact = true and false, because just in the system
virtual UInt getNbContactNodes() const { return this->slaves.size(); }
bool isNTNContact() const { return this->is_ntn_contact; }
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
- typedef std::set<const ElementGroup *> SurfacePtrSet;
+ using SurfacePtrSet = std::set<const ElementGroup *>;
+
+ ID id;
SolidMechanicsModel & model;
/// array of slave nodes
SynchronizedArray<UInt> slaves;
/// array of normals
SynchronizedArray<Real> normals;
/// array indicating if nodes are in contact
SynchronizedArray<Real> contact_pressure;
/// array indicating if nodes are in contact
SynchronizedArray<bool> is_in_contact;
/// boundary matrix for slave nodes
SynchronizedArray<Real> lumped_boundary_slaves;
/// impedance matrix
SynchronizedArray<Real> impedance;
/// contact surface
SurfacePtrSet contact_surfaces;
/// element list for dump and lumped_boundary
ElementTypeMapArray<UInt> slave_elements;
CSR<Element> node_to_elements;
/// parallelisation
std::unique_ptr<SynchronizerRegistry> synch_registry;
std::unique_ptr<ElementSynchronizer> synchronizer;
bool is_ntn_contact{true};
};
/// standard output stream operator
inline std::ostream & operator<<(std::ostream & stream,
const NTNBaseContact & _this) {
_this.printself(stream);
return stream;
}
} // namespace akantu
/* -------------------------------------------------------------------------- */
/* inline functions */
/* -------------------------------------------------------------------------- */
#include "ntn_base_contact_inline_impl.hh"
#endif /* AST_NTN_BASE_CONTACT_HH_ */
diff --git a/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_base_friction.cc b/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_base_friction.cc
index efa895782..e28e034fe 100644
--- a/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_base_friction.cc
+++ b/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_base_friction.cc
@@ -1,380 +1,379 @@
/**
* @file ntn_base_friction.cc
*
* @author David Simon Kammer <david.kammer@epfl.ch>
*
* @date creation: Tue Dec 02 2014
* @date last modification: Fri Feb 23 2018
*
* @brief implementation of ntn base friction
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
// simtools
#include "ntn_base_friction.hh"
#include "dof_manager_default.hh"
#include "dumper_nodal_field.hh"
#include "dumper_text.hh"
#include "non_linear_solver_lumped.hh"
namespace akantu {
/* -------------------------------------------------------------------------- */
-NTNBaseFriction::NTNBaseFriction(NTNBaseContact & contact, const ID & id,
- const MemoryID & memory_id)
- : Memory(id, memory_id), Parsable(ParserType::_friction, id), Dumpable(),
+NTNBaseFriction::NTNBaseFriction(NTNBaseContact & contact, const ID & id)
+ : Parsable(ParserType::_friction, id), Dumpable(),
contact(contact),
is_sticking(0, 1, true, id + ":is_sticking", true, "is_sticking"),
frictional_strength(0, 1, 0., id + ":frictional_strength", 0.,
"frictional_strength"),
friction_traction(0, contact.getModel().getSpatialDimension(), 0.,
id + ":friction_traction", 0., "friction_traction"),
slip(0, 1, 0., id + ":slip", 0., "slip"),
cumulative_slip(0, 1, 0., id + ":cumulative_slip", 0., "cumulative_slip"),
slip_velocity(0, contact.getModel().getSpatialDimension(), 0.,
id + ":slip_velocity", 0., "slip_velocity") {
AKANTU_DEBUG_IN();
this->contact.registerSynchronizedArray(this->is_sticking);
this->contact.registerSynchronizedArray(this->frictional_strength);
this->contact.registerSynchronizedArray(this->friction_traction);
this->contact.registerSynchronizedArray(this->slip);
this->contact.registerSynchronizedArray(this->cumulative_slip);
this->contact.registerSynchronizedArray(this->slip_velocity);
this->registerExternalDumper(contact.getDumper().shared_from_this(),
contact.getDefaultDumperName(), true);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNBaseFriction::updateSlip() {
AKANTU_DEBUG_IN();
SolidMechanicsModel & model = this->contact.getModel();
UInt dim = model.getSpatialDimension();
// synchronize increment
this->contact.getSynchronizerRegistry().synchronize(
SynchronizationTag::_cf_incr);
Array<Real> rel_tan_incr(0, dim);
this->contact.computeRelativeTangentialField(model.getIncrement(),
rel_tan_incr);
Array<Real>::const_iterator<Vector<Real>> it = rel_tan_incr.begin(dim);
UInt nb_nodes = this->contact.getNbContactNodes();
for (UInt n = 0; n < nb_nodes; ++n) {
if (this->is_sticking(n)) {
this->slip(n) = 0.;
} else {
const Vector<Real> & rti = it[n];
this->slip(n) += rti.norm();
this->cumulative_slip(n) += rti.norm();
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNBaseFriction::computeFrictionTraction() {
AKANTU_DEBUG_IN();
this->computeStickTraction();
this->computeFrictionalStrength();
SolidMechanicsModel & model = this->contact.getModel();
UInt dim = model.getSpatialDimension();
// get contact arrays
const SynchronizedArray<bool> & is_in_contact =
this->contact.getIsInContact();
Array<Real> & traction =
const_cast<Array<Real> &>(this->friction_traction.getArray());
Array<Real>::iterator<Vector<Real>> it_fric_trac = traction.begin(dim);
this->is_sticking.zero(); // set to not sticking
UInt nb_contact_nodes = this->contact.getNbContactNodes();
for (UInt n = 0; n < nb_contact_nodes; ++n) {
// node pair is in contact
if (is_in_contact(n)) {
Vector<Real> fric_trac = it_fric_trac[n];
// check if it is larger than frictional strength
Real abs_fric = fric_trac.norm();
if (abs_fric != 0.) {
Real alpha = this->frictional_strength(n) / abs_fric;
// larger -> sliding
if (alpha < 1.) {
fric_trac *= alpha;
} else
this->is_sticking(n) = true;
} else {
// frictional traction is already zero
this->is_sticking(n) = true;
}
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNBaseFriction::computeStickTraction() {
AKANTU_DEBUG_IN();
SolidMechanicsModel & model = this->contact.getModel();
UInt dim = model.getSpatialDimension();
Real delta_t = model.getTimeStep();
UInt nb_contact_nodes = this->contact.getNbContactNodes();
// get contact arrays
const SynchronizedArray<Real> & impedance = this->contact.getImpedance();
const SynchronizedArray<bool> & is_in_contact =
this->contact.getIsInContact();
Array<Real> acceleration(0, dim);
this->contact.computeAcceleration(acceleration);
// compute relative normal fields of velocity and acceleration
Array<Real> r_velo(0, dim);
Array<Real> r_acce(0, dim);
Array<Real> r_old_acce(0, dim);
this->contact.computeRelativeTangentialField(model.getVelocity(), r_velo);
this->contact.computeRelativeTangentialField(acceleration, r_acce);
this->contact.computeRelativeTangentialField(model.getAcceleration(),
r_old_acce);
AKANTU_DEBUG_ASSERT(r_velo.size() == nb_contact_nodes,
"computeRelativeNormalField does not give back arrays "
<< "size == nb_contact_nodes. nb_contact_nodes = "
<< nb_contact_nodes
<< " | array size = " << r_velo.size());
// compute tangential gap_dot array for all nodes
Array<Real> gap_dot(nb_contact_nodes, dim);
for (auto && data : zip(make_view(gap_dot), make_view(r_velo),
make_view(r_acce), make_view(r_old_acce))) {
auto & gap_dot = std::get<0>(data);
auto & r_velo = std::get<1>(data);
auto & r_acce = std::get<2>(data);
auto & r_old_acce = std::get<3>(data);
gap_dot = r_velo + delta_t * r_acce - 1. / 2. * delta_t * r_old_acce;
}
// compute friction traction to stop sliding
Array<Real> & traction =
const_cast<Array<Real> &>(this->friction_traction.getArray());
auto it_fric_trac = traction.begin(dim);
for (UInt n = 0; n < nb_contact_nodes; ++n) {
Vector<Real> fric_trac = it_fric_trac[n];
// node pair is NOT in contact
if (!is_in_contact(n)) {
fric_trac.zero(); // set to zero
}
// node pair is in contact
else {
// compute friction traction
for (UInt d = 0; d < dim; ++d)
fric_trac(d) = impedance(n) * gap_dot(n, d) / 2.;
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNBaseFriction::applyFrictionTraction() {
AKANTU_DEBUG_IN();
SolidMechanicsModel & model = this->contact.getModel();
Array<Real> & residual = model.getInternalForce();
UInt dim = model.getSpatialDimension();
const SynchronizedArray<UInt> & slaves = this->contact.getSlaves();
const SynchronizedArray<Real> & lumped_boundary_slaves =
this->contact.getLumpedBoundarySlaves();
UInt nb_contact_nodes = this->contact.getNbContactNodes();
for (UInt n = 0; n < nb_contact_nodes; ++n) {
UInt slave = slaves(n);
for (UInt d = 0; d < dim; ++d) {
residual(slave, d) -=
lumped_boundary_slaves(n) * this->friction_traction(n, d);
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNBaseFriction::registerSynchronizedArray(SynchronizedArrayBase & array) {
AKANTU_DEBUG_IN();
this->frictional_strength.registerDependingArray(array);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNBaseFriction::dumpRestart(const std::string & file_name) const {
AKANTU_DEBUG_IN();
this->is_sticking.dumpRestartFile(file_name);
this->frictional_strength.dumpRestartFile(file_name);
this->friction_traction.dumpRestartFile(file_name);
this->slip.dumpRestartFile(file_name);
this->cumulative_slip.dumpRestartFile(file_name);
this->slip_velocity.dumpRestartFile(file_name);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNBaseFriction::readRestart(const std::string & file_name) {
AKANTU_DEBUG_IN();
this->is_sticking.readRestartFile(file_name);
this->frictional_strength.readRestartFile(file_name);
this->friction_traction.readRestartFile(file_name);
this->cumulative_slip.readRestartFile(file_name);
this->slip_velocity.readRestartFile(file_name);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNBaseFriction::setParam(const std::string & name, UInt node,
Real value) {
AKANTU_DEBUG_IN();
SynchronizedArray<Real> & array =
this->get(name).get<SynchronizedArray<Real>>();
Int index = this->contact.getNodeIndex(node);
if (index < 0) {
AKANTU_DEBUG_WARNING("Node "
<< node << " is not a contact node. "
<< "Therefore, cannot set interface parameter!!");
} else {
array(index) = value; // put value
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
UInt NTNBaseFriction::getNbStickingNodes() const {
AKANTU_DEBUG_IN();
UInt nb_stick = 0;
UInt nb_nodes = this->contact.getNbContactNodes();
const SynchronizedArray<UInt> & nodes = this->contact.getSlaves();
const SynchronizedArray<bool> & is_in_contact =
this->contact.getIsInContact();
const Mesh & mesh = this->contact.getModel().getMesh();
for (UInt n = 0; n < nb_nodes; ++n) {
bool is_local_node = mesh.isLocalOrMasterNode(nodes(n));
bool is_pbc_slave_node = mesh.isPeriodicSlave(nodes(n));
if (is_local_node && !is_pbc_slave_node && is_in_contact(n) &&
this->is_sticking(n)) {
nb_stick++;
}
}
mesh.getCommunicator().allReduce(nb_stick, SynchronizerOperation::_sum);
AKANTU_DEBUG_OUT();
return nb_stick;
}
/* -------------------------------------------------------------------------- */
void NTNBaseFriction::printself(std::ostream & stream, int indent) const {
AKANTU_DEBUG_IN();
std::string space;
for (Int i = 0; i < indent; i++, space += AKANTU_INDENT)
;
stream << space << "NTNBaseFriction [" << std::endl;
Parsable::printself(stream, indent);
stream << space << "]" << std::endl;
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNBaseFriction::addDumpFieldToDumper(const std::string & dumper_name,
const std::string & field_id) {
AKANTU_DEBUG_IN();
#ifdef AKANTU_USE_IOHELPER
// const SynchronizedArray<UInt> * nodal_filter =
// &(this->contact.getSlaves());
if (field_id == "is_sticking") {
this->internalAddDumpFieldToDumper(
dumper_name, field_id,
std::make_unique<dumpers::NodalField<bool>>(
this->is_sticking.getArray()));
} else if (field_id == "frictional_strength") {
this->internalAddDumpFieldToDumper(
dumper_name, field_id,
std::make_unique<dumpers::NodalField<Real>>(
this->frictional_strength.getArray()));
} else if (field_id == "friction_traction") {
this->internalAddDumpFieldToDumper(
dumper_name, field_id,
std::make_unique<dumpers::NodalField<Real>>(
this->friction_traction.getArray()));
} else if (field_id == "slip") {
this->internalAddDumpFieldToDumper(
dumper_name, field_id,
std::make_unique<dumpers::NodalField<Real>>(this->slip.getArray()));
} else if (field_id == "cumulative_slip") {
this->internalAddDumpFieldToDumper(
dumper_name, field_id,
std::make_unique<dumpers::NodalField<Real>>(
this->cumulative_slip.getArray()));
} else if (field_id == "slip_velocity") {
this->internalAddDumpFieldToDumper(
dumper_name, field_id,
std::make_unique<dumpers::NodalField<Real>>(
this->slip_velocity.getArray()));
} else {
this->contact.addDumpFieldToDumper(dumper_name, field_id);
}
#endif
AKANTU_DEBUG_OUT();
}
} // namespace akantu
diff --git a/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_base_friction.hh b/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_base_friction.hh
index 538d02f65..7b6ec0b96 100644
--- a/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_base_friction.hh
+++ b/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_base_friction.hh
@@ -1,177 +1,176 @@
/**
* @file ntn_base_friction.hh
*
* @author David Simon Kammer <david.kammer@epfl.ch>
*
* @date creation: Tue Dec 02 2014
* @date last modification: Fri Feb 23 2018
*
* @brief base class for ntn and ntrf friction
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#ifndef AST_NTN_BASE_FRICTION_HH_
#define AST_NTN_BASE_FRICTION_HH_
/* -------------------------------------------------------------------------- */
// akantu
#include "parsable.hh"
// simtools
#include "ntn_base_contact.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
template <>
inline void ParameterTyped<akantu::SynchronizedArray<Real>>::setAuto(
const ParserParameter & in_param) {
Parameter::setAuto(in_param);
Real r = in_param;
param.setAndChangeDefault(r);
}
/* -------------------------------------------------------------------------- */
template <>
template <>
inline void ParameterTyped<akantu::SynchronizedArray<Real>>::setTyped<Real>(
const Real & value) {
param.setAndChangeDefault(value);
}
/* -------------------------------------------------------------------------- */
-class NTNBaseFriction : protected Memory, public Parsable, public Dumpable {
+class NTNBaseFriction : public Parsable, public Dumpable {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
- NTNBaseFriction(NTNBaseContact & contact, const ID & id = "friction",
- const MemoryID & memory_id = 0);
+ NTNBaseFriction(NTNBaseContact & contact, const ID & id = "friction");
virtual ~NTNBaseFriction() = default;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// compute friction traction
virtual void computeFrictionTraction();
/// compute stick traction (friction traction needed to stick the nodes)
virtual void computeStickTraction();
/// apply the friction force
virtual void applyFrictionTraction();
/// compute slip
virtual void updateSlip();
/// register Syncronizedarrays for sync
virtual void registerSynchronizedArray(SynchronizedArrayBase & array);
/// dump restart file
virtual void dumpRestart(const std::string & file_name) const;
/// read restart file
virtual void readRestart(const std::string & file_name);
/// set to steady state
virtual void setToSteadyState() { AKANTU_TO_IMPLEMENT(); };
/// get the number of sticking nodes (in parallel)
/// a node that is not in contact does not count as sticking
virtual UInt getNbStickingNodes() const;
/// function to print the contain of the class
virtual void printself(std::ostream & stream, int indent = 0) const;
protected:
/// compute frictional strength according to friction law
virtual void computeFrictionalStrength() = 0;
/* ------------------------------------------------------------------------ */
/* Dumpable */
/* ------------------------------------------------------------------------ */
public:
virtual void addDumpFieldToDumper(const std::string & dumper_name,
const std::string & field_id);
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
AKANTU_GET_MACRO(Contact, contact, const NTNBaseContact &)
AKANTU_GET_MACRO(IsSticking, is_sticking, const SynchronizedArray<bool> &)
AKANTU_GET_MACRO(FrictionalStrength, frictional_strength,
const SynchronizedArray<Real> &)
AKANTU_GET_MACRO(FrictionTraction, friction_traction,
const SynchronizedArray<Real> &)
AKANTU_GET_MACRO(Slip, slip, const SynchronizedArray<Real> &)
AKANTU_GET_MACRO(CumulativeSlip, cumulative_slip,
const SynchronizedArray<Real> &)
AKANTU_GET_MACRO(SlipVelocity, slip_velocity, const SynchronizedArray<Real> &)
/// set parameter of a given node
/// (if you need to set to all: used the setMixed function of the Parsable).
virtual void setParam(const std::string & name, UInt node, Real value);
// replaced by the setMixed of the Parsable
// virtual void setParam(const std::string & param, Real value) {
// AKANTU_ERROR("Friction does not know the following parameter: " <<
// param);
// };
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
NTNBaseContact & contact;
// if node is sticking
SynchronizedArray<bool> is_sticking;
// frictional strength
SynchronizedArray<Real> frictional_strength;
// friction force
SynchronizedArray<Real> friction_traction;
// slip
SynchronizedArray<Real> slip;
SynchronizedArray<Real> cumulative_slip;
// slip velocity (tangential vector)
SynchronizedArray<Real> slip_velocity;
};
/* -------------------------------------------------------------------------- */
/* inline functions */
/* -------------------------------------------------------------------------- */
//#include "ntn_base_friction_inline_impl.hh"
/// standard output stream operator
inline std::ostream & operator<<(std::ostream & stream,
const NTNBaseFriction & _this) {
_this.printself(stream);
return stream;
}
} // namespace akantu
#endif /* AST_NTN_BASE_FRICTION_HH_ */
diff --git a/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_contact.cc b/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_contact.cc
index 17a44cac8..e5ad24f75 100644
--- a/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_contact.cc
+++ b/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_contact.cc
@@ -1,555 +1,554 @@
/**
* @file ntn_contact.cc
*
* @author David Simon Kammer <david.kammer@epfl.ch>
*
* @date creation: Tue Dec 02 2014
* @date last modification: Fri Feb 23 2018
*
* @brief implementation of ntn_contact
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
// simtools
#include "ntn_contact.hh"
#include "dumper_nodal_field.hh"
#include "dumper_text.hh"
namespace akantu {
/* -------------------------------------------------------------------------- */
-NTNContact::NTNContact(SolidMechanicsModel & model, const ID & id,
- const MemoryID & memory_id)
- : NTNBaseContact(model, id, memory_id),
+NTNContact::NTNContact(SolidMechanicsModel & model, const ID & id)
+ : NTNBaseContact(model, id),
masters(0, 1, 0, id + ":masters", std::numeric_limits<UInt>::quiet_NaN(),
"masters"),
lumped_boundary_masters(0, 1, 0, id + ":lumped_boundary_masters",
std::numeric_limits<Real>::quiet_NaN(),
"lumped_boundary_masters"),
- master_elements("master_elements", id, memory_id) {
+ master_elements("master_elements", id) {
AKANTU_DEBUG_IN();
const Mesh & mesh = this->model.getMesh();
UInt spatial_dimension = this->model.getSpatialDimension();
this->master_elements.initialize(mesh, _nb_component = 1,
_spatial_dimension = spatial_dimension - 1);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNContact::pairInterfaceNodes(const ElementGroup & slave_boundary,
const ElementGroup & master_boundary,
UInt surface_normal_dir, const Mesh & mesh,
Array<UInt> & pairs) {
AKANTU_DEBUG_IN();
pairs.resize(0);
AKANTU_DEBUG_ASSERT(pairs.getNbComponent() == 2,
"Array of node pairs should have nb_component = 2,"
<< " but has nb_component = "
<< pairs.getNbComponent());
UInt dim = mesh.getSpatialDimension();
AKANTU_DEBUG_ASSERT(surface_normal_dir < dim,
"Mesh is of " << dim << " dimensions"
<< " and cannot have direction "
<< surface_normal_dir
<< " for surface normal");
// offset for projection computation
Vector<UInt> offset(dim - 1);
for (UInt i = 0, j = 0; i < dim; ++i) {
if (surface_normal_dir != i) {
offset(j) = i;
++j;
}
}
// find projected node coordinates
const Array<Real> & coordinates = mesh.getNodes();
// find slave nodes
Array<Real> proj_slave_coord(slave_boundary.getNbNodes(), dim - 1, 0.);
Array<UInt> slave_nodes(slave_boundary.getNbNodes());
UInt n(0);
for (auto && slave_node : slave_boundary.getNodeGroup().getNodes()) {
for (UInt d = 0; d < dim - 1; ++d) {
proj_slave_coord(n, d) = coordinates(slave_node, offset[d]);
slave_nodes(n) = slave_node;
}
++n;
}
// find master nodes
Array<Real> proj_master_coord(master_boundary.getNbNodes(), dim - 1, 0.);
Array<UInt> master_nodes(master_boundary.getNbNodes());
n = 0;
for (auto && master_node : master_boundary.getNodeGroup().getNodes()) {
for (UInt d = 0; d < dim - 1; ++d) {
proj_master_coord(n, d) = coordinates(master_node, offset[d]);
master_nodes(n) = master_node;
}
++n;
}
// find minimum distance between slave nodes to define tolerance
Real min_dist = std::numeric_limits<Real>::max();
for (UInt i = 0; i < proj_slave_coord.size(); ++i) {
for (UInt j = i + 1; j < proj_slave_coord.size(); ++j) {
Real dist = 0.;
for (UInt d = 0; d < dim - 1; ++d) {
dist += (proj_slave_coord(i, d) - proj_slave_coord(j, d)) *
(proj_slave_coord(i, d) - proj_slave_coord(j, d));
}
if (dist < min_dist) {
min_dist = dist;
}
}
}
min_dist = std::sqrt(min_dist);
Real local_tol = 0.1 * min_dist;
// find master slave node pairs
for (UInt i = 0; i < proj_slave_coord.size(); ++i) {
for (UInt j = 0; j < proj_master_coord.size(); ++j) {
Real dist = 0.;
for (UInt d = 0; d < dim - 1; ++d) {
dist += (proj_slave_coord(i, d) - proj_master_coord(j, d)) *
(proj_slave_coord(i, d) - proj_master_coord(j, d));
}
dist = std::sqrt(dist);
if (dist < local_tol) { // it is a pair
Vector<UInt> pair(2);
pair[0] = slave_nodes(i);
pair[1] = master_nodes(j);
pairs.push_back(pair);
continue; // found master do not need to search further for this slave
}
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNContact::addSurfacePair(const ID & slave, const ID & master,
UInt surface_normal_dir) {
AKANTU_DEBUG_IN();
const Mesh & mesh = this->model.getMesh();
const ElementGroup & slave_boundary = mesh.getElementGroup(slave);
const ElementGroup & master_boundary = mesh.getElementGroup(master);
this->contact_surfaces.insert(&slave_boundary);
this->contact_surfaces.insert(&master_boundary);
Array<UInt> pairs(0, 2);
NTNContact::pairInterfaceNodes(slave_boundary, master_boundary,
surface_normal_dir, this->model.getMesh(),
pairs);
// eliminate pairs which contain a pbc slave node
Array<UInt> pairs_no_PBC_slaves(0, 2);
Array<UInt>::const_vector_iterator it = pairs.begin(2);
Array<UInt>::const_vector_iterator end = pairs.end(2);
for (; it != end; ++it) {
const Vector<UInt> & pair = *it;
if (not mesh.isPeriodicSlave(pair(0)) and
not mesh.isPeriodicSlave(pair(1))) {
pairs_no_PBC_slaves.push_back(pair);
}
}
this->addNodePairs(pairs_no_PBC_slaves);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNContact::addNodePairs(const Array<UInt> & pairs) {
AKANTU_DEBUG_IN();
AKANTU_DEBUG_ASSERT(pairs.getNbComponent() == 2,
"Array of node pairs should have nb_component = 2,"
<< " but has nb_component = "
<< pairs.getNbComponent());
UInt nb_pairs = pairs.size();
for (UInt n = 0; n < nb_pairs; ++n) {
this->addSplitNode(pairs(n, 0), pairs(n, 1));
}
// synchronize with depending nodes
findBoundaryElements(this->slaves.getArray(), this->slave_elements);
findBoundaryElements(this->masters.getArray(), this->master_elements);
updateInternalData();
syncArrays(_added);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNContact::getNodePairs(Array<UInt> & pairs) const {
AKANTU_DEBUG_IN();
pairs.resize(0);
AKANTU_DEBUG_ASSERT(pairs.getNbComponent() == 2,
"Array of node pairs should have nb_component = 2,"
<< " but has nb_component = "
<< pairs.getNbComponent());
UInt nb_pairs = this->getNbContactNodes();
for (UInt n = 0; n < nb_pairs; ++n) {
Vector<UInt> pair{this->slaves(n), this->masters(n)};
pairs.push_back(pair);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNContact::addSplitNode(UInt slave, UInt master) {
AKANTU_DEBUG_IN();
NTNBaseContact::addSplitNode(slave);
this->masters.push_back(master);
this->lumped_boundary_masters.push_back(
std::numeric_limits<Real>::quiet_NaN());
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
/*
This function only works for surface elements with one quad point. For
surface elements with more quad points, it computes still, but the result
might not be what you are looking for.
*/
void NTNContact::updateNormals() {
AKANTU_DEBUG_IN();
// set normals to zero
this->normals.zero();
// contact information
UInt dim = this->model.getSpatialDimension();
UInt nb_contact_nodes = this->getNbContactNodes();
this->synch_registry->synchronize(
SynchronizationTag::_cf_nodal); // synchronize current pos
const Array<Real> & cur_pos = this->model.getCurrentPosition();
FEEngine & boundary_fem = this->model.getFEEngineBoundary();
const Mesh & mesh = this->model.getMesh();
for (auto ghost_type : ghost_types) {
for (auto & type : mesh.elementTypes(dim - 1, ghost_type)) {
// compute the normals
Array<Real> quad_normals(0, dim);
boundary_fem.computeNormalsOnIntegrationPoints(cur_pos, quad_normals,
type, ghost_type);
UInt nb_quad_points =
boundary_fem.getNbIntegrationPoints(type, ghost_type);
// new version: compute normals only based on master elements (and not all
// boundary elements)
// -------------------------------------------------------------------------------------
UInt nb_nodes_per_element = mesh.getNbNodesPerElement(type);
const Array<UInt> & connectivity = mesh.getConnectivity(type, ghost_type);
// loop over contact nodes
for (auto & element : (this->master_elements)(type, ghost_type)) {
for (UInt q = 0; q < nb_nodes_per_element; ++q) {
UInt node = connectivity(element, q);
UInt node_index = this->masters.find(node);
AKANTU_DEBUG_ASSERT(node_index != UInt(-1), "Could not find node "
<< node
<< " in the array!");
for (UInt q = 0; q < nb_quad_points; ++q) {
// add quad normal to master normal
for (UInt d = 0; d < dim; ++d) {
this->normals(node_index, d) +=
quad_normals(element * nb_quad_points + q, d);
}
}
}
}
}
}
Real * master_normals = this->normals.storage();
for (UInt n = 0; n < nb_contact_nodes; ++n) {
if (dim == 2)
Math::normalize2(&(master_normals[n * dim]));
else if (dim == 3)
Math::normalize3(&(master_normals[n * dim]));
}
// // normalize normals
// auto nit = this->normals.begin();
// auto nend = this->normals.end();
// for (; nit != nend; ++nit) {
// nit->normalize();
// }
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNContact::dumpRestart(const std::string & file_name) const {
AKANTU_DEBUG_IN();
NTNBaseContact::dumpRestart(file_name);
this->masters.dumpRestartFile(file_name);
this->lumped_boundary_masters.dumpRestartFile(file_name);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNContact::readRestart(const std::string & file_name) {
AKANTU_DEBUG_IN();
NTNBaseContact::readRestart(file_name);
this->masters.readRestartFile(file_name);
this->lumped_boundary_masters.readRestartFile(file_name);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNContact::updateImpedance() {
AKANTU_DEBUG_IN();
UInt nb_contact_nodes = getNbContactNodes();
Real delta_t = this->model.getTimeStep();
AKANTU_DEBUG_ASSERT(delta_t != NAN,
"Time step is NAN. Have you set it already?");
const Array<Real> & mass = this->model.getMass();
for (UInt n = 0; n < nb_contact_nodes; ++n) {
UInt master = this->masters(n);
UInt slave = this->slaves(n);
Real imp = (this->lumped_boundary_masters(n) / mass(master)) +
(this->lumped_boundary_slaves(n) / mass(slave));
imp = 2 / delta_t / imp;
this->impedance(n) = imp;
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNContact::updateLumpedBoundary() {
AKANTU_DEBUG_IN();
internalUpdateLumpedBoundary(this->slaves.getArray(), this->slave_elements,
this->lumped_boundary_slaves);
internalUpdateLumpedBoundary(this->masters.getArray(), this->master_elements,
this->lumped_boundary_masters);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNContact::applyContactPressure() {
AKANTU_DEBUG_IN();
UInt nb_ntn_pairs = getNbContactNodes();
UInt dim = this->model.getSpatialDimension();
Array<Real> & residual = this->model.getInternalForce();
for (UInt n = 0; n < nb_ntn_pairs; ++n) {
UInt master = this->masters(n);
UInt slave = this->slaves(n);
for (UInt d = 0; d < dim; ++d) {
residual(master, d) +=
this->lumped_boundary_masters(n) * this->contact_pressure(n, d);
residual(slave, d) -=
this->lumped_boundary_slaves(n) * this->contact_pressure(n, d);
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNContact::computeRelativeTangentialField(
const Array<Real> & field, Array<Real> & rel_tang_field) const {
AKANTU_DEBUG_IN();
// resize arrays to zero
rel_tang_field.resize(0);
UInt dim = this->model.getSpatialDimension();
auto it_field = field.begin(dim);
auto it_normal = this->normals.getArray().begin(dim);
Vector<Real> rfv(dim);
Vector<Real> np_rfv(dim);
UInt nb_contact_nodes = this->slaves.size();
for (UInt n = 0; n < nb_contact_nodes; ++n) {
// nodes
UInt slave = this->slaves(n);
UInt master = this->masters(n);
// relative field vector (slave - master)
rfv = Vector<Real>(it_field[slave]);
rfv -= Vector<Real>(it_field[master]);
// normal projection of relative field
const Vector<Real> normal_v = it_normal[n];
np_rfv = normal_v;
np_rfv *= rfv.dot(normal_v);
// subract normal projection from relative field to get the tangential
// projection
rfv -= np_rfv;
rel_tang_field.push_back(rfv);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNContact::computeRelativeNormalField(
const Array<Real> & field, Array<Real> & rel_normal_field) const {
AKANTU_DEBUG_IN();
// resize arrays to zero
rel_normal_field.resize(0);
UInt dim = this->model.getSpatialDimension();
// Real * field_p = field.storage();
// Real * normals_p = this->normals.storage();
Array<Real>::const_iterator<Vector<Real>> it_field = field.begin(dim);
Array<Real>::const_iterator<Vector<Real>> it_normal =
this->normals.getArray().begin(dim);
Vector<Real> rfv(dim);
UInt nb_contact_nodes = this->getNbContactNodes();
for (UInt n = 0; n < nb_contact_nodes; ++n) {
// nodes
UInt slave = this->slaves(n);
UInt master = this->masters(n);
// relative field vector (slave - master)
rfv = Vector<Real>(it_field[slave]);
rfv -= Vector<Real>(it_field[master]);
// length of normal projection of relative field
const Vector<Real> normal_v = it_normal[n];
rel_normal_field.push_back(rfv.dot(normal_v));
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
Int NTNContact::getNodeIndex(UInt node) const {
AKANTU_DEBUG_IN();
Int slave_i = NTNBaseContact::getNodeIndex(node);
Int master_i = this->masters.find(node);
AKANTU_DEBUG_OUT();
return std::max(slave_i, master_i);
}
/* -------------------------------------------------------------------------- */
void NTNContact::printself(std::ostream & stream, int indent) const {
AKANTU_DEBUG_IN();
std::string space;
for (Int i = 0; i < indent; i++, space += AKANTU_INDENT)
;
stream << space << "NTNContact [" << std::endl;
NTNBaseContact::printself(stream, indent);
stream << space << " + masters : " << std::endl;
this->masters.printself(stream, indent + 2);
stream << space << " + lumped_boundary_mastres : " << std::endl;
this->lumped_boundary_masters.printself(stream, indent + 2);
stream << space << "]" << std::endl;
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNContact::syncArrays(SyncChoice sync_choice) {
AKANTU_DEBUG_IN();
NTNBaseContact::syncArrays(sync_choice);
this->masters.syncElements(sync_choice);
this->lumped_boundary_masters.syncElements(sync_choice);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTNContact::addDumpFieldToDumper(const std::string & dumper_name,
const std::string & field_id) {
AKANTU_DEBUG_IN();
/*
#ifdef AKANTU_USE_IOHELPER
const Array<UInt> & nodal_filter = this->slaves.getArray();
#define ADD_FIELD(field_id, field, type) \
internalAddDumpFieldToDumper(dumper_name, \
field_id, \
new DumperIOHelper::NodalField< type, true, \
Array<type>, \
Array<UInt> >(field, 0, 0, &nodal_filter))
*/
if (field_id == "lumped_boundary_master") {
internalAddDumpFieldToDumper(dumper_name, field_id,
std::make_unique<dumpers::NodalField<Real>>(
this->lumped_boundary_masters.getArray()));
} else {
NTNBaseContact::addDumpFieldToDumper(dumper_name, field_id);
}
/*
#undef ADD_FIELD
#endif
*/
AKANTU_DEBUG_OUT();
}
} // namespace akantu
diff --git a/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_contact.hh b/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_contact.hh
index eb445dc20..dcd3beb69 100644
--- a/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_contact.hh
+++ b/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_contact.hh
@@ -1,166 +1,165 @@
/**
* @file ntn_contact.hh
*
* @author David Simon Kammer <david.kammer@epfl.ch>
*
* @date creation: Tue Dec 02 2014
* @date last modification: Fri Feb 23 2018
*
* @brief contact for node to node discretization
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#ifndef AST_NTN_CONTACT_HH_
#define AST_NTN_CONTACT_HH_
/* -------------------------------------------------------------------------- */
// simtools
#include "ntn_base_contact.hh"
namespace akantu {
/* -------------------------------------------------------------------------- */
class NTNContact : public NTNBaseContact {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
- NTNContact(SolidMechanicsModel & model, const ID & id = "contact",
- const MemoryID & memory_id = 0);
- virtual ~NTNContact(){};
+ NTNContact(SolidMechanicsModel & model, const ID & id = "contact");
+ ~NTNContact() override = default;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// add surface pair and pair nodes according to the surface normal
void addSurfacePair(const ID & slave, const ID & master,
UInt surface_normal_dir);
/// fills the pairs vector with interface node pairs (*,0)=slaves,
/// (*,1)=masters
static void pairInterfaceNodes(const ElementGroup & slave_boundary,
const ElementGroup & master_boundary,
UInt surface_normal_dir, const Mesh & mesh,
Array<UInt> & pairs);
// add node pairs from a list with pairs(*,0)=slaves and pairs(*,1)=masters
void addNodePairs(const Array<UInt> & pairs);
/// add node pair
void addSplitNode(UInt slave, UInt master) override;
/// update (compute the normals on the master nodes)
void updateNormals() override;
/// update the lumped boundary B matrix
void updateLumpedBoundary() override;
/// update the impedance matrix
void updateImpedance() override;
/// impose the normal contact force
void applyContactPressure() override;
/// dump restart file
void dumpRestart(const std::string & file_name) const override;
/// read restart file
void readRestart(const std::string & file_name) override;
/// compute the normal gap
void computeNormalGap(Array<Real> & gap) const override {
this->computeRelativeNormalField(this->model.getCurrentPosition(), gap);
};
/// compute relative normal field (only value that has to be multiplied with
/// the normal)
/// relative to master nodes
void
computeRelativeNormalField(const Array<Real> & field,
Array<Real> & rel_normal_field) const override;
/// compute relative tangential field (complet array)
/// relative to master nodes
void
computeRelativeTangentialField(const Array<Real> & field,
Array<Real> & rel_tang_field) const override;
/// function to print the contain of the class
void printself(std::ostream & stream, int indent = 0) const override;
protected:
/// synchronize arrays
void syncArrays(SyncChoice sync_choice) override;
/* ------------------------------------------------------------------------ */
/* Dumpable */
/* ------------------------------------------------------------------------ */
public:
void addDumpFieldToDumper(const std::string & dumper_name,
const std::string & field_id) override;
// virtual void addDumpFieldVector(const std::string & field_id);
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
AKANTU_GET_MACRO(Masters, masters, const SynchronizedArray<UInt> &)
AKANTU_GET_MACRO(LumpedBoundaryMasters, lumped_boundary_masters,
const SynchronizedArray<Real> &)
/// get interface node pairs (*,0) are slaves, (*,1) are masters
void getNodePairs(Array<UInt> & pairs) const;
/// get index of node in either slaves or masters array
/// if node is in neither of them, return -1
Int getNodeIndex(UInt node) const override;
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
private:
/// array of master nodes
SynchronizedArray<UInt> masters;
/// lumped boundary of master nodes
SynchronizedArray<Real> lumped_boundary_masters;
// element list for dump and lumped_boundary
ElementTypeMapArray<UInt> master_elements;
};
/* -------------------------------------------------------------------------- */
/* inline functions */
/* -------------------------------------------------------------------------- */
//#include "ntn_contact_inline_impl.hh"
/// standard output stream operator
inline std::ostream & operator<<(std::ostream & stream,
const NTNContact & _this) {
_this.printself(stream);
return stream;
}
} // namespace akantu
#endif /* AST_NTN_CONTACT_HH_ */
diff --git a/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_friction.hh b/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_friction.hh
index dec3a550a..a5f43b29a 100644
--- a/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_friction.hh
+++ b/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_friction.hh
@@ -1,99 +1,98 @@
/**
* @file ntn_friction.hh
*
* @author David Simon Kammer <david.kammer@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Fri Feb 23 2018
*
* @brief implementation of friction for node to node contact
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#ifndef AST_NTN_FRICTION_HH_
#define AST_NTN_FRICTION_HH_
/* -------------------------------------------------------------------------- */
// simtools
#include "ntn_base_friction.hh"
#include "ntn_friclaw_coulomb.hh"
namespace akantu {
/* -------------------------------------------------------------------------- */
template <template <class> class FrictionLaw = NTNFricLawCoulomb,
class Regularisation = NTNFricRegNoRegularisation>
class NTNFriction : public FrictionLaw<Regularisation> {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
- NTNFriction(NTNBaseContact & contact, const ID & id = "friction",
- const MemoryID & memory_id = 0);
+ NTNFriction(NTNBaseContact & contact, const ID & id = "friction");
virtual ~NTNFriction(){};
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// apply the friction force
virtual void applyFrictionTraction();
/// function to print the contain of the class
virtual void printself(std::ostream & stream, int indent = 0) const;
protected:
/* ------------------------------------------------------------------------ */
/* Dumpable */
/* ------------------------------------------------------------------------ */
public:
// virtual void addDumpFieldToDumper(const std::string & dumper_name,
// const std::string & field_id);
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
};
/* -------------------------------------------------------------------------- */
/* inline functions */
/* -------------------------------------------------------------------------- */
/// standard output stream operator
template <template <class> class FrictionLaw, class Regularisation>
inline std::ostream &
operator<<(std::ostream & stream,
const NTNFriction<FrictionLaw, Regularisation> & _this) {
_this.printself(stream);
return stream;
}
} // namespace akantu
#include "ntn_friction_tmpl.hh"
#endif /* AST_NTN_FRICTION_HH_ */
diff --git a/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_friction_tmpl.hh b/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_friction_tmpl.hh
index 7ccc86ff6..af48546d3 100644
--- a/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_friction_tmpl.hh
+++ b/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_friction_tmpl.hh
@@ -1,95 +1,95 @@
/**
* @file ntn_friction_tmpl.hh
*
* @author David Simon Kammer <david.kammer@epfl.ch>
*
* @date creation: Tue Dec 02 2014
* @date last modification: Fri Feb 23 2018
*
* @brief
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
// simtools
#include "ntn_contact.hh"
namespace akantu {
/* -------------------------------------------------------------------------- */
template <template <class> class FrictionLaw, class Regularisation>
NTNFriction<FrictionLaw, Regularisation>::NTNFriction(
- NTNBaseContact & contact, const ID & id, const MemoryID & memory_id)
- : FrictionLaw<Regularisation>(contact, id, memory_id) {
+ NTNBaseContact & contact, const ID & id)
+ : FrictionLaw<Regularisation>(contact, id) {
AKANTU_DEBUG_IN();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <template <class> class FrictionLaw, class Regularisation>
void NTNFriction<FrictionLaw, Regularisation>::applyFrictionTraction() {
AKANTU_DEBUG_IN();
NTNContact & ntn_contact = dynamic_cast<NTNContact &>(this->contact);
SolidMechanicsModel & model = ntn_contact.getModel();
Array<Real> & residual = model.getInternalForce();
UInt dim = model.getSpatialDimension();
const SynchronizedArray<UInt> & masters = ntn_contact.getMasters();
const SynchronizedArray<UInt> & slaves = ntn_contact.getSlaves();
const SynchronizedArray<Real> & l_boundary_slaves =
ntn_contact.getLumpedBoundarySlaves();
const SynchronizedArray<Real> & l_boundary_masters =
ntn_contact.getLumpedBoundaryMasters();
UInt nb_contact_nodes = ntn_contact.getNbContactNodes();
for (UInt n = 0; n < nb_contact_nodes; ++n) {
UInt master = masters(n);
UInt slave = slaves(n);
for (UInt d = 0; d < dim; ++d) {
residual(master, d) +=
l_boundary_masters(n) * this->friction_traction(n, d);
residual(slave, d) -=
l_boundary_slaves(n) * this->friction_traction(n, d);
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <template <class> class FrictionLaw, class Regularisation>
void NTNFriction<FrictionLaw, Regularisation>::printself(std::ostream & stream,
int indent) const {
AKANTU_DEBUG_IN();
std::string space;
for (Int i = 0; i < indent; i++, space += AKANTU_INDENT)
;
stream << space << "NTNFriction [" << std::endl;
FrictionLaw<Regularisation>::printself(stream, ++indent);
stream << space << "]" << std::endl;
AKANTU_DEBUG_OUT();
}
} // namespace akantu
diff --git a/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntrf_contact.cc b/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntrf_contact.cc
index 2771390d2..c1ff7288f 100644
--- a/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntrf_contact.cc
+++ b/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntrf_contact.cc
@@ -1,321 +1,320 @@
/**
* @file ntrf_contact.cc
*
* @author David Simon Kammer <david.kammer@epfl.ch>
*
* @date creation: Tue Dec 02 2014
* @date last modification: Fri Feb 23 2018
*
* @brief
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
// simtools
#include "ntrf_contact.hh"
namespace akantu {
/* -------------------------------------------------------------------------- */
-NTRFContact::NTRFContact(SolidMechanicsModel & model, const ID & id,
- const MemoryID & memory_id)
- : NTNBaseContact(model, id, memory_id),
+NTRFContact::NTRFContact(SolidMechanicsModel & model, const ID & id)
+ : NTNBaseContact(model, id),
reference_point(model.getSpatialDimension()),
normal(model.getSpatialDimension()) {
AKANTU_DEBUG_IN();
is_ntn_contact = false;
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTRFContact::setReferencePoint(Real x, Real y, Real z) {
AKANTU_DEBUG_IN();
Real coord[3];
coord[0] = x;
coord[1] = y;
coord[2] = z;
UInt dim = this->model.getSpatialDimension();
for (UInt d = 0; d < dim; ++d)
this->reference_point(d) = coord[d];
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTRFContact::setNormal(Real x, Real y, Real z) {
AKANTU_DEBUG_IN();
UInt dim = this->model.getSpatialDimension();
Real coord[3];
coord[0] = x;
coord[1] = y;
coord[2] = z;
for (UInt d = 0; d < dim; ++d)
this->normal(d) = coord[d];
this->normal.normalize();
this->updateNormals();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTRFContact::addSurface(const ID & surf) {
AKANTU_DEBUG_IN();
const Mesh & mesh_ref = this->model.getMesh();
try {
const ElementGroup & boundary = mesh_ref.getElementGroup(surf);
this->contact_surfaces.insert(&boundary);
// find slave nodes
for (auto && node : boundary.getNodeGroup().getNodes()) {
if (not mesh_ref.isPeriodicSlave(node)) {
this->addSplitNode(node);
}
}
} catch (debug::Exception & e) {
AKANTU_DEBUG_INFO("NTRFContact addSurface did not found subboundary "
<< surf
<< " and ignored it. Other procs might have it :)");
}
// synchronize with depending nodes
findBoundaryElements(this->slaves.getArray(), this->slave_elements);
updateInternalData();
syncArrays(_added);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTRFContact::addNodes(Array<UInt> & nodes) {
AKANTU_DEBUG_IN();
UInt nb_nodes = nodes.size();
UInt nb_compo = nodes.getNbComponent();
for (UInt n = 0; n < nb_nodes; ++n) {
for (UInt c = 0; c < nb_compo; ++c) {
this->addSplitNode(nodes(n, c));
}
}
// synchronize with depending nodes
findBoundaryElements(this->slaves.getArray(), this->slave_elements);
updateInternalData();
syncArrays(_added);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTRFContact::updateNormals() {
AKANTU_DEBUG_IN();
// normal is the same for all slaves
this->normals.set(this->normal);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTRFContact::updateImpedance() {
AKANTU_DEBUG_IN();
UInt nb_contact_nodes = getNbContactNodes();
Real delta_t = this->model.getTimeStep();
AKANTU_DEBUG_ASSERT(delta_t != NAN,
"Time step is NAN. Have you set it already?");
const Array<Real> & mass = this->model.getMass();
for (UInt n = 0; n < nb_contact_nodes; ++n) {
UInt slave = this->slaves(n);
Real imp = this->lumped_boundary_slaves(n) / mass(slave);
imp = 2 / delta_t / imp;
this->impedance(n) = imp;
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTRFContact::computeRelativeTangentialField(
const Array<Real> & field, Array<Real> & rel_tang_field) const {
AKANTU_DEBUG_IN();
// resize arrays to zero
rel_tang_field.resize(0);
UInt dim = this->model.getSpatialDimension();
Array<Real>::const_iterator<Vector<Real>> it_field = field.begin(dim);
Array<Real>::const_iterator<Vector<Real>> it_normal =
this->normals.getArray().begin(dim);
Vector<Real> rfv(dim);
Vector<Real> np_rfv(dim);
UInt nb_contact_nodes = this->slaves.size();
for (UInt n = 0; n < nb_contact_nodes; ++n) {
// nodes
UInt node = this->slaves(n);
// relative field vector
rfv = it_field[node];
;
// normal projection of relative field
const Vector<Real> & normal_v = it_normal[n];
np_rfv = normal_v;
np_rfv *= rfv.dot(normal_v);
// subtract normal projection from relative field to get the tangential
// projection
rfv -= np_rfv;
rel_tang_field.push_back(rfv);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTRFContact::computeNormalGap(Array<Real> & gap) const {
AKANTU_DEBUG_IN();
gap.resize(0);
UInt dim = this->model.getSpatialDimension();
Array<Real>::const_iterator<Vector<Real>> it_cur_pos =
this->model.getCurrentPosition().begin(dim);
Array<Real>::const_iterator<Vector<Real>> it_normal =
this->normals.getArray().begin(dim);
Vector<Real> gap_v(dim);
UInt nb_contact_nodes = this->getNbContactNodes();
for (UInt n = 0; n < nb_contact_nodes; ++n) {
// nodes
UInt node = this->slaves(n);
// gap vector
gap_v = it_cur_pos[node];
gap_v -= this->reference_point;
// length of normal projection of gap vector
const Vector<Real> & normal_v = it_normal[n];
gap.push_back(gap_v.dot(normal_v));
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTRFContact::computeRelativeNormalField(
const Array<Real> & field, Array<Real> & rel_normal_field) const {
AKANTU_DEBUG_IN();
// resize arrays to zero
rel_normal_field.resize(0);
UInt dim = this->model.getSpatialDimension();
Array<Real>::const_iterator<Vector<Real>> it_field = field.begin(dim);
Array<Real>::const_iterator<Vector<Real>> it_normal =
this->normals.getArray().begin(dim);
UInt nb_contact_nodes = this->getNbContactNodes();
for (UInt n = 0; n < nb_contact_nodes; ++n) {
// nodes
UInt node = this->slaves(n);
const Vector<Real> & field_v = it_field[node];
const Vector<Real> & normal_v = it_normal[n];
rel_normal_field.push_back(field_v.dot(normal_v));
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTRFContact::printself(std::ostream & stream, int indent) const {
AKANTU_DEBUG_IN();
std::string space;
for (Int i = 0; i < indent; i++, space += AKANTU_INDENT)
;
stream << space << "NTRFContact [" << std::endl;
NTNBaseContact::printself(stream, indent);
stream << space << "]" << std::endl;
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NTRFContact::addDumpFieldToDumper(const std::string & dumper_name,
const std::string & field_id) {
AKANTU_DEBUG_IN();
/*
#ifdef AKANTU_USE_IOHELPER
const Array<UInt> & nodal_filter = this->slaves.getArray();
#define ADD_FIELD(field_id, field, type) \
internalAddDumpFieldToDumper(dumper_name, \
field_id, \
new DumperIOHelper::NodalField< type, true, \
Array<type>, \
Array<UInt> >(field, 0, 0, &nodal_filter))
*/
/*
if(field_id == "displacement") {
ADD_FIELD(field_id, this->model.getDisplacement(), Real);
}
else if(field_id == "contact_pressure") {
internalAddDumpFieldToDumper(dumper_name,
field_id,
new
DumperIOHelper::NodalField<Real>(this->contact_pressure.getArray()));
}
else {*/
NTNBaseContact::addDumpFieldToDumper(dumper_name, field_id);
//}
/*
#undef ADD_FIELD
#endif
*/
AKANTU_DEBUG_OUT();
}
} // namespace akantu
diff --git a/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntrf_contact.hh b/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntrf_contact.hh
index a56ec2067..bd60a8761 100644
--- a/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntrf_contact.hh
+++ b/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntrf_contact.hh
@@ -1,125 +1,125 @@
/**
* @file ntrf_contact.hh
*
* @author David Simon Kammer <david.kammer@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Fri Feb 23 2018
*
* @brief contact for node to rigid flat interface
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#ifndef AST_NTRF_CONTACT_HH_
#define AST_NTRF_CONTACT_HH_
/* -------------------------------------------------------------------------- */
// simtools
#include "ntn_base_contact.hh"
namespace akantu {
/* -------------------------------------------------------------------------- */
class NTRFContact : public NTNBaseContact {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
- NTRFContact(SolidMechanicsModel & model, const ID & id = "contact",
- const MemoryID & memory_id = 0);
- virtual ~NTRFContact(){};
+ NTRFContact(SolidMechanicsModel & model, const ID & id = "contact");
+ ~NTRFContact() override = default;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
void setReferencePoint(Real x = 0., Real y = 0., Real z = 0.);
void setNormal(Real x = 1., Real y = 0., Real z = 0.);
/// add surface and nodes according to the surface normal
void addSurface(const ID & surf);
// add nodes from a list
void addNodes(Array<UInt> & nodes);
/// update (copy the normal to all normals)
- virtual void updateNormals();
+ void updateNormals() override;
/// update the impedance matrix
- virtual void updateImpedance();
+ void updateImpedance() override;
/// compute the normal gap
- virtual void computeNormalGap(Array<Real> & gap) const;
+ void computeNormalGap(Array<Real> & gap) const override;
/// compute relative normal field (only value that has to be multiplied with
/// the normal)
/// relative to master nodes
- virtual void computeRelativeNormalField(const Array<Real> & field,
- Array<Real> & rel_normal_field) const;
+ void
+ computeRelativeNormalField(const Array<Real> & field,
+ Array<Real> & rel_normal_field) const override;
/// compute relative tangential field (complet array)
/// relative to master nodes
- virtual void
+ void
computeRelativeTangentialField(const Array<Real> & field,
- Array<Real> & rel_tang_field) const;
+ Array<Real> & rel_tang_field) const override;
/// function to print the contain of the class
- virtual void printself(std::ostream & stream, int indent = 0) const;
+ void printself(std::ostream & stream, int indent = 0) const override;
/* ------------------------------------------------------------------------ */
/* Dumpable */
/* ------------------------------------------------------------------------ */
public:
- virtual void addDumpFieldToDumper(const std::string & dumper_name,
- const std::string & field_id);
+ void addDumpFieldToDumper(const std::string & dumper_name,
+ const std::string & field_id) override;
// virtual void addDumpFieldVector(const std::string & field_id);
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
private:
/// reference point for rigid flat surface
Vector<Real> reference_point;
/// outpointing normal of rigid flat surface
Vector<Real> normal;
};
/* -------------------------------------------------------------------------- */
/* inline functions */
/* -------------------------------------------------------------------------- */
//#include "ntrf_contact_inline_impl.hh"
/// standard output stream operator
inline std::ostream & operator<<(std::ostream & stream,
const NTRFContact & _this) {
_this.printself(stream);
return stream;
}
} // namespace akantu
#endif /* AST_NTRF_CONTACT_HH_ */
diff --git a/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntrf_friction.hh b/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntrf_friction.hh
index bddaa5f3d..55492b58b 100644
--- a/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntrf_friction.hh
+++ b/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntrf_friction.hh
@@ -1,91 +1,90 @@
/**
* @file ntrf_friction.hh
*
* @author David Simon Kammer <david.kammer@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Fri Feb 23 2018
*
* @brief friction for node to rigid flat interface
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#ifndef AST_NTRF_FRICTION_HH_
#define AST_NTRF_FRICTION_HH_
/* -------------------------------------------------------------------------- */
// simtools
#include "ntn_friclaw_coulomb.hh"
namespace akantu {
/* -------------------------------------------------------------------------- */
template <template <class> class FrictionLaw = NTNFricLawCoulomb,
class Regularisation = NTNFricRegNoRegularisation>
class NTRFFriction : public FrictionLaw<Regularisation> {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
- NTRFFriction(NTNBaseContact & contact, const ID & id = "friction",
- const MemoryID & memory_id = 0);
+ NTRFFriction(NTNBaseContact & contact, const ID & id = "friction");
virtual ~NTRFFriction(){};
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// function to print the contain of the class
virtual void printself(std::ostream & stream, int indent = 0) const;
/* ------------------------------------------------------------------------ */
/* Dumpable */
/* ------------------------------------------------------------------------ */
public:
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
};
/* -------------------------------------------------------------------------- */
/* inline functions */
/* -------------------------------------------------------------------------- */
/// standard output stream operato
template <template <class> class FrictionLaw, class Regularisation>
inline std::ostream &
operator<<(std::ostream & stream,
const NTRFFriction<FrictionLaw, Regularisation> & _this) {
_this.printself(stream);
return stream;
}
} // namespace akantu
#include "ntrf_friction_tmpl.hh"
#endif /* AST_NTRF_FRICTION_HH_ */
diff --git a/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntrf_friction_tmpl.hh b/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntrf_friction_tmpl.hh
index 7f871c082..21debe0f7 100644
--- a/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntrf_friction_tmpl.hh
+++ b/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntrf_friction_tmpl.hh
@@ -1,104 +1,104 @@
/**
* @file ntrf_friction_tmpl.hh
*
* @author David Simon Kammer <david.kammer@epfl.ch>
*
* @date creation: Tue Dec 02 2014
* @date last modification: Fri Feb 23 2018
*
* @brief implementation of node to rigid flat interface friction
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
//#include "ntrf_friction.hh"
namespace akantu {
/* -------------------------------------------------------------------------- */
template <template <class> class FrictionLaw, class Regularisation>
NTRFFriction<FrictionLaw, Regularisation>::NTRFFriction(
- NTNBaseContact & contact, const ID & id, const MemoryID & memory_id)
- : FrictionLaw<Regularisation>(contact, id, memory_id) {
+ NTNBaseContact & contact, const ID & id)
+ : FrictionLaw<Regularisation>(contact, id) {
AKANTU_DEBUG_IN();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <template <class> class FrictionLaw, class Regularisation>
void NTRFFriction<FrictionLaw, Regularisation>::printself(std::ostream & stream,
int indent) const {
AKANTU_DEBUG_IN();
std::string space;
for (Int i = 0; i < indent; i++, space += AKANTU_INDENT)
;
stream << space << "NTRFFriction [" << std::endl;
FrictionLaw<Regularisation>::printself(stream, ++indent);
stream << space << "]" << std::endl;
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
/*
void NTRFFriction::addDumpFieldToDumper(const std::string & dumper_name,
const std::string & field_id) {
AKANTU_DEBUG_IN();
#ifdef AKANTU_USE_IOHELPER
// const SynchronizedArray<UInt> * nodal_filter =
&(this->contact.getSlaves());
if(field_id == "is_sticking") {
this->internalAddDumpFieldToDumper(dumper_name,
field_id,
new
DumperIOHelper::NodalField<bool>(this->is_sticking.getArray()));
}
else if(field_id == "frictional_strength") {
this->internalAddDumpFieldToDumper(dumper_name,
field_id,
new
DumperIOHelper::NodalField<Real>(this->frictional_strength.getArray()));
}
else if(field_id == "friction_traction") {
this->internalAddDumpFieldToDumper(dumper_name,
field_id,
new
DumperIOHelper::NodalField<Real>(this->friction_traction.getArray()));
}
else if(field_id == "slip") {
this->internalAddDumpFieldToDumper(dumper_name,
field_id,
new DumperIOHelper::NodalField<Real>(this->slip.getArray()));
}
else {
this->contact.addDumpFieldToDumper(dumper_name, field_id);
}
#endif
AKANTU_DEBUG_OUT();
}
*/
} // namespace akantu
diff --git a/packages/core.cmake b/packages/core.cmake
index 24b09898a..96f024337 100644
--- a/packages/core.cmake
+++ b/packages/core.cmake
@@ -1,534 +1,527 @@
#===============================================================================
# @file core.cmake
#
# @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
# @author Nicolas Richart <nicolas.richart@epfl.ch>
#
# @date creation: Mon Nov 21 2011
# @date last modification: Mon Jan 18 2016
#
# @brief package description for core
#
# @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 <http://www.gnu.org/licenses/>.
#
#===============================================================================
package_declare(core NOT_OPTIONAL
DESCRIPTION "core package for Akantu"
FEATURES_PUBLIC cxx_strong_enums cxx_defaulted_functions
cxx_deleted_functions cxx_auto_type cxx_decltype_auto
FEATURES_PRIVATE cxx_lambdas cxx_nullptr cxx_range_for
cxx_delegating_constructors
DEPENDS INTERFACE akantu_iterators Boost)
package_declare_sources(core
common/aka_array.cc
common/aka_array.hh
common/aka_array_tmpl.hh
common/aka_array_printer.hh
common/aka_bbox.hh
common/aka_blas_lapack.hh
common/aka_circular_array.hh
common/aka_circular_array_inline_impl.hh
common/aka_common.cc
common/aka_common.hh
common/aka_common_inline_impl.hh
common/aka_csr.hh
common/aka_element_classes_info_inline_impl.hh
common/aka_enum_macros.hh
common/aka_error.cc
common/aka_error.hh
common/aka_event_handler_manager.hh
common/aka_extern.cc
common/aka_factory.hh
common/aka_fwd.hh
common/aka_grid_dynamic.hh
common/aka_math.cc
common/aka_math.hh
common/aka_math_tmpl.hh
- common/aka_memory.cc
- common/aka_memory.hh
- common/aka_memory_inline_impl.hh
common/aka_named_argument.hh
common/aka_random_generator.hh
common/aka_safe_enum.hh
- common/aka_static_memory.cc
- common/aka_static_memory.hh
- common/aka_static_memory_inline_impl.hh
- common/aka_static_memory_tmpl.hh
common/aka_typelist.hh
common/aka_types.hh
common/aka_visitor.hh
common/aka_voigthelper.hh
common/aka_voigthelper_tmpl.hh
common/aka_voigthelper.cc
common/aka_warning.hh
common/aka_warning_restore.hh
fe_engine/element_class.hh
fe_engine/element_class_helper.hh
fe_engine/element_class_tmpl.hh
fe_engine/element_classes/element_class_hexahedron_8_inline_impl.hh
fe_engine/element_classes/element_class_hexahedron_20_inline_impl.hh
fe_engine/element_classes/element_class_pentahedron_6_inline_impl.hh
fe_engine/element_classes/element_class_pentahedron_15_inline_impl.hh
fe_engine/element_classes/element_class_point_1_inline_impl.hh
fe_engine/element_classes/element_class_quadrangle_4_inline_impl.hh
fe_engine/element_classes/element_class_quadrangle_8_inline_impl.hh
fe_engine/element_classes/element_class_segment_2_inline_impl.hh
fe_engine/element_classes/element_class_segment_3_inline_impl.hh
fe_engine/element_classes/element_class_tetrahedron_10_inline_impl.hh
fe_engine/element_classes/element_class_tetrahedron_4_inline_impl.hh
fe_engine/element_classes/element_class_triangle_3_inline_impl.hh
fe_engine/element_classes/element_class_triangle_6_inline_impl.hh
fe_engine/element_type_conversion.hh
fe_engine/fe_engine.cc
fe_engine/fe_engine.hh
fe_engine/fe_engine_inline_impl.hh
fe_engine/fe_engine_template.hh
fe_engine/fe_engine_template_tmpl_field.hh
fe_engine/fe_engine_template_tmpl.hh
fe_engine/geometrical_element_property.hh
fe_engine/geometrical_element_property.cc
fe_engine/gauss_integration.cc
fe_engine/gauss_integration_tmpl.hh
fe_engine/integrator.hh
fe_engine/integrator_gauss.hh
fe_engine/integrator_gauss_inline_impl.hh
fe_engine/interpolation_element_tmpl.hh
fe_engine/integration_point.hh
fe_engine/shape_functions.hh
fe_engine/shape_functions.cc
fe_engine/shape_functions_inline_impl.hh
fe_engine/shape_lagrange_base.cc
fe_engine/shape_lagrange_base.hh
fe_engine/shape_lagrange_base_inline_impl.hh
fe_engine/shape_lagrange.hh
fe_engine/shape_lagrange_inline_impl.hh
fe_engine/element.hh
io/dumper/dumpable.hh
io/dumper/dumpable.cc
io/dumper/dumpable_dummy.hh
io/dumper/dumpable_inline_impl.hh
io/dumper/dumper_field.hh
io/dumper/dumper_material_padders.hh
io/dumper/dumper_filtered_connectivity.hh
io/dumper/dumper_element_partition.hh
io/mesh_io.cc
io/mesh_io.hh
io/mesh_io/mesh_io_diana.cc
io/mesh_io/mesh_io_diana.hh
io/mesh_io/mesh_io_msh.cc
io/mesh_io/mesh_io_msh.hh
#io/model_io.cc
#io/model_io.hh
io/parser/algebraic_parser.hh
io/parser/input_file_parser.hh
io/parser/parsable.cc
io/parser/parsable.hh
io/parser/parser.cc
io/parser/parser_real.cc
io/parser/parser_random.cc
io/parser/parser_types.cc
io/parser/parser_input_files.cc
io/parser/parser.hh
io/parser/parser_tmpl.hh
io/parser/parser_grammar_tmpl.hh
io/parser/cppargparse/cppargparse.hh
io/parser/cppargparse/cppargparse.cc
io/parser/cppargparse/cppargparse_tmpl.hh
io/parser/parameter_registry.cc
io/parser/parameter_registry.hh
io/parser/parameter_registry_tmpl.hh
mesh/element_group.cc
mesh/element_group.hh
mesh/element_group_inline_impl.hh
mesh/element_type_map.cc
mesh/element_type_map.hh
mesh/element_type_map_tmpl.hh
mesh/element_type_map_filter.hh
mesh/group_manager.cc
mesh/group_manager.hh
mesh/group_manager_inline_impl.hh
mesh/mesh.cc
mesh/mesh.hh
mesh/mesh_periodic.cc
mesh/mesh_accessor.hh
mesh/mesh_events.hh
mesh/mesh_filter.hh
mesh/mesh_global_data_updater.hh
mesh/mesh_data.cc
mesh/mesh_data.hh
mesh/mesh_data_tmpl.hh
mesh/mesh_inline_impl.hh
mesh/node_group.cc
mesh/node_group.hh
mesh/node_group_inline_impl.hh
mesh/mesh_iterators.hh
mesh_utils/mesh_partition.cc
mesh_utils/mesh_partition.hh
mesh_utils/mesh_partition/mesh_partition_mesh_data.cc
mesh_utils/mesh_partition/mesh_partition_mesh_data.hh
mesh_utils/mesh_partition/mesh_partition_scotch.hh
mesh_utils/mesh_utils_pbc.cc
mesh_utils/mesh_utils.cc
mesh_utils/mesh_utils.hh
mesh_utils/mesh_utils_distribution.cc
mesh_utils/mesh_utils_distribution.hh
mesh_utils/mesh_utils.hh
mesh_utils/mesh_utils_inline_impl.hh
mesh_utils/global_ids_updater.hh
mesh_utils/global_ids_updater.cc
mesh_utils/global_ids_updater_inline_impl.hh
model/common/boundary_condition/boundary_condition.hh
model/common/boundary_condition/boundary_condition_functor.hh
model/common/boundary_condition/boundary_condition_functor_inline_impl.hh
model/common/boundary_condition/boundary_condition_tmpl.hh
model/common/non_local_toolbox/neighborhood_base.hh
model/common/non_local_toolbox/neighborhood_base.cc
model/common/non_local_toolbox/neighborhood_base_inline_impl.hh
model/common/non_local_toolbox/neighborhoods_criterion_evaluation/neighborhood_max_criterion.hh
model/common/non_local_toolbox/neighborhoods_criterion_evaluation/neighborhood_max_criterion.cc
model/common/non_local_toolbox/neighborhoods_criterion_evaluation/neighborhood_max_criterion_inline_impl.hh
model/common/non_local_toolbox/non_local_manager.hh
model/common/non_local_toolbox/non_local_manager.cc
model/common/non_local_toolbox/non_local_manager_inline_impl.hh
model/common/non_local_toolbox/non_local_manager_callback.hh
model/common/non_local_toolbox/non_local_neighborhood_base.hh
model/common/non_local_toolbox/non_local_neighborhood_base.cc
model/common/non_local_toolbox/non_local_neighborhood.hh
model/common/non_local_toolbox/non_local_neighborhood_tmpl.hh
model/common/non_local_toolbox/non_local_neighborhood_inline_impl.hh
model/common/non_local_toolbox/base_weight_function.hh
model/common/non_local_toolbox/base_weight_function_inline_impl.hh
model/common/model_solver.cc
model/common/model_solver.hh
model/common/solver_callback.hh
model/common/solver_callback.cc
model/common/dof_manager/dof_manager.cc
model/common/dof_manager/dof_manager.hh
model/common/dof_manager/dof_manager_default.cc
model/common/dof_manager/dof_manager_default.hh
model/common/dof_manager/dof_manager_default_inline_impl.hh
model/common/dof_manager/dof_manager_inline_impl.hh
model/common/non_linear_solver/non_linear_solver.cc
model/common/non_linear_solver/non_linear_solver.hh
model/common/non_linear_solver/non_linear_solver_default.hh
model/common/non_linear_solver/non_linear_solver_lumped.cc
model/common/non_linear_solver/non_linear_solver_lumped.hh
model/common/time_step_solvers/time_step_solver.hh
model/common/time_step_solvers/time_step_solver.cc
model/common/time_step_solvers/time_step_solver_default.cc
model/common/time_step_solvers/time_step_solver_default.hh
model/common/time_step_solvers/time_step_solver_default_explicit.hh
model/common/integration_scheme/generalized_trapezoidal.cc
model/common/integration_scheme/generalized_trapezoidal.hh
model/common/integration_scheme/integration_scheme.cc
model/common/integration_scheme/integration_scheme.hh
model/common/integration_scheme/integration_scheme_1st_order.cc
model/common/integration_scheme/integration_scheme_1st_order.hh
model/common/integration_scheme/integration_scheme_2nd_order.cc
model/common/integration_scheme/integration_scheme_2nd_order.hh
model/common/integration_scheme/newmark-beta.cc
model/common/integration_scheme/newmark-beta.hh
model/common/integration_scheme/pseudo_time.cc
model/common/integration_scheme/pseudo_time.hh
model/model.cc
model/model.hh
model/model_inline_impl.hh
model/model_options.hh
solver/solver_vector.hh
solver/solver_vector_default.cc
solver/solver_vector_default.hh
solver/solver_vector_default_tmpl.hh
solver/solver_vector_distributed.cc
solver/solver_vector_distributed.hh
solver/sparse_matrix.cc
solver/sparse_matrix.hh
solver/sparse_matrix_aij.cc
solver/sparse_matrix_aij.hh
solver/sparse_matrix_aij_inline_impl.hh
solver/sparse_matrix_inline_impl.hh
solver/sparse_solver.cc
solver/sparse_solver.hh
solver/sparse_solver_inline_impl.hh
solver/terms_to_assemble.hh
synchronizer/communication_buffer_inline_impl.hh
synchronizer/communication_descriptor.hh
synchronizer/communication_descriptor_tmpl.hh
synchronizer/communication_request.hh
synchronizer/communication_tag.hh
synchronizer/communications.hh
synchronizer/communications_tmpl.hh
synchronizer/communicator.cc
synchronizer/communicator.hh
synchronizer/communicator_dummy_inline_impl.hh
synchronizer/communicator_event_handler.hh
synchronizer/communicator_inline_impl.hh
synchronizer/data_accessor.cc
synchronizer/data_accessor.hh
synchronizer/dof_synchronizer.cc
synchronizer/dof_synchronizer.hh
synchronizer/dof_synchronizer_inline_impl.hh
synchronizer/element_info_per_processor.cc
synchronizer/element_info_per_processor.hh
synchronizer/element_info_per_processor_tmpl.hh
synchronizer/element_synchronizer.cc
synchronizer/element_synchronizer.hh
synchronizer/facet_synchronizer.cc
synchronizer/facet_synchronizer.hh
synchronizer/facet_synchronizer_inline_impl.hh
synchronizer/grid_synchronizer.cc
synchronizer/grid_synchronizer.hh
synchronizer/grid_synchronizer_tmpl.hh
synchronizer/master_element_info_per_processor.cc
synchronizer/node_info_per_processor.cc
synchronizer/node_info_per_processor.hh
synchronizer/node_synchronizer.cc
synchronizer/node_synchronizer.hh
synchronizer/node_synchronizer_inline_impl.hh
synchronizer/periodic_node_synchronizer.cc
synchronizer/periodic_node_synchronizer.hh
synchronizer/slave_element_info_per_processor.cc
synchronizer/synchronizer.cc
synchronizer/synchronizer.hh
synchronizer/synchronizer_impl.hh
synchronizer/synchronizer_impl_tmpl.hh
synchronizer/synchronizer_registry.cc
synchronizer/synchronizer_registry.hh
synchronizer/synchronizer_tmpl.hh
synchronizer/communication_buffer.hh
)
set(AKANTU_SPIRIT_SOURCES
io/mesh_io/mesh_io_abaqus.cc
io/parser/parser_real.cc
io/parser/parser_random.cc
io/parser/parser_types.cc
io/parser/parser_input_files.cc
PARENT_SCOPE
)
package_declare_elements(core
ELEMENT_TYPES
_point_1
_segment_2
_segment_3
_triangle_3
_triangle_6
_quadrangle_4
_quadrangle_8
_tetrahedron_4
_tetrahedron_10
_pentahedron_6
_pentahedron_15
_hexahedron_8
_hexahedron_20
KIND regular
GEOMETRICAL_TYPES
_gt_point
_gt_segment_2
_gt_segment_3
_gt_triangle_3
_gt_triangle_6
_gt_quadrangle_4
_gt_quadrangle_8
_gt_tetrahedron_4
_gt_tetrahedron_10
_gt_hexahedron_8
_gt_hexahedron_20
_gt_pentahedron_6
_gt_pentahedron_15
INTERPOLATION_TYPES
_itp_lagrange_point_1
_itp_lagrange_segment_2
_itp_lagrange_segment_3
_itp_lagrange_triangle_3
_itp_lagrange_triangle_6
_itp_lagrange_quadrangle_4
_itp_serendip_quadrangle_8
_itp_lagrange_tetrahedron_4
_itp_lagrange_tetrahedron_10
_itp_lagrange_hexahedron_8
_itp_serendip_hexahedron_20
_itp_lagrange_pentahedron_6
_itp_lagrange_pentahedron_15
GEOMETRICAL_SHAPES
_gst_point
_gst_triangle
_gst_square
_gst_prism
GAUSS_INTEGRATION_TYPES
_git_point
_git_segment
_git_triangle
_git_tetrahedron
_git_pentahedron
INTERPOLATION_KIND _itk_lagrangian
FE_ENGINE_LISTS
gradient_on_integration_points
interpolate_on_integration_points
interpolate
compute_normals_on_integration_points
inverse_map
contains
compute_shapes
compute_shapes_derivatives
get_N
compute_dnds
compute_d2nds2
compute_jmat
get_shapes_derivatives
lagrange_base
assemble_fields
)
package_declare_documentation_files(core
manual.sty
manual.cls
manual.tex
manual-macros.sty
manual-titlepages.tex
manual-authors.tex
manual-changelog.tex
manual-introduction.tex
manual-gettingstarted.tex
manual-io.tex
manual-feengine.tex
manual-elements.tex
manual-appendix-elements.tex
manual-appendix-packages.tex
manual-backmatter.tex
manual-bibliography.bib
manual-bibliographystyle.bst
figures/bc_and_ic_example.pdf
figures/boundary.pdf
figures/boundary.svg
figures/dirichlet.pdf
figures/dirichlet.svg
# figures/doc_wheel.pdf
# figures/doc_wheel.svg
figures/hot-point-1.png
figures/hot-point-2.png
figures/insertion.pdf
figures/interpolate.pdf
figures/interpolate.svg
figures/vectors.pdf
figures/vectors.svg
figures/elements/hexahedron_8.pdf
figures/elements/hexahedron_8.svg
figures/elements/quadrangle_4.pdf
figures/elements/quadrangle_4.svg
figures/elements/quadrangle_8.pdf
figures/elements/quadrangle_8.svg
figures/elements/segment_2.pdf
figures/elements/segment_2.svg
figures/elements/segment_3.pdf
figures/elements/segment_3.svg
figures/elements/tetrahedron_10.pdf
figures/elements/tetrahedron_10.svg
figures/elements/tetrahedron_4.pdf
figures/elements/tetrahedron_4.svg
figures/elements/triangle_3.pdf
figures/elements/triangle_3.svg
figures/elements/triangle_6.pdf
figures/elements/triangle_6.svg
figures/elements/xtemp.pdf
)
package_declare_documentation(core
"This package is the core engine of \\akantu. It depends on:"
"\\begin{itemize}"
"\\item A C++ compiler (\\href{http://gcc.gnu.org/}{GCC} >= 4, or \\href{https://software.intel.com/en-us/intel-compilers}{Intel})."
"\\item The cross-platform, open-source \\href{http://www.cmake.org/}{CMake} build system."
"\\item The \\href{http://www.boost.org/}{Boost} C++ portable libraries."
"\\item The \\href{http://www.zlib.net/}{zlib} compression library."
"\\end{itemize}"
""
"Under Ubuntu (14.04 LTS) the installation can be performed using the commands:"
"\\begin{command}"
" > sudo apt-get install cmake libboost-dev zlib1g-dev g++"
"\\end{command}"
""
"Under Mac OS X the installation requires the following steps:"
"\\begin{itemize}"
"\\item Install Xcode"
"\\item Install the command line tools."
"\\item Install the MacPorts project which allows to automatically"
"download and install opensource packages."
"\\end{itemize}"
"Then the following commands should be typed in a terminal:"
"\\begin{command}"
" > sudo port install cmake gcc48 boost"
"\\end{command}"
)
find_program(READLINK_COMMAND readlink)
find_program(ADDR2LINE_COMMAND addr2line)
find_program(PATCH_COMMAND patch)
mark_as_advanced(READLINK_COMMAND)
mark_as_advanced(ADDR2LINE_COMMAND)
package_declare_extra_files_to_package(core
SOURCES
common/aka_element_classes_info.hh.in
common/aka_config.hh.in
)
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.9))
package_set_compile_flags(core CXX "-Wno-undefined-var-template")
endif()
if(DEFINED AKANTU_CXX11_FLAGS)
package_declare(core_cxx11 NOT_OPTIONAL
DESCRIPTION "C++ 11 additions for Akantu core"
COMPILE_FLAGS CXX "${AKANTU_CXX11_FLAGS}")
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.6")
set(AKANTU_CORE_CXX11 OFF CACHE BOOL "C++ 11 additions for Akantu core - not supported by the selected compiler" FORCE)
endif()
endif()
package_declare_documentation(core_cxx11
"This option activates some features of the C++11 standard. This is usable with GCC>=4.7 or Intel>=13.")
else()
if(CMAKE_VERSION VERSION_LESS 3.1)
message(FATAL_ERROR "Since version 3.0 Akantu requires at least c++11 capable compiler")
endif()
endif()
diff --git a/packages/documentation_developer_manual.cmake b/packages/documentation_developer_manual.cmake
index 02b9651f3..100f73fbd 100644
--- a/packages/documentation_developer_manual.cmake
+++ b/packages/documentation_developer_manual.cmake
@@ -1,58 +1,58 @@
#===============================================================================
# @file documentation_doxygen.cmake
#
# @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
# @author Nicolas Richart <nicolas.richart@epfl.ch>
#
# @date creation: Wed Jun 11 2014
# @date last modification: Mon Jan 18 2016
#
# @brief Doxygen documentation of the code
#
# @section LICENSE
#
# Copyright (©) 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 <http://www.gnu.org/licenses/>.
#
#===============================================================================
-package_declare(documentation_developer_manual
+package_declare(documentation
DESCRIPTION "Build source documentation using Sphinx/Doxygen.")
-package_declare_documentation(documentation_developer_manual
+package_declare_documentation(documentation
"This generates the Doxygen documantation of the source code."
"It depends on:"
"\\begin{itemize}"
"\\item \\href{http://www.stack.nl/~dimitri/doxygen/}{Doxygen} an automated source code documentations system."
"\\item Optional: \\href{http://www.graphviz.org/}{Graphviz} to generate the dependencies graph"
"\\end{itemize}"
""
"Under Ubuntu (14.04 LTS), the installation of the dependencies can be performed using the following command:"
"\\begin{command}"
" > sudo apt-get install doxygen"
" > sudo apt-get install graphviz"
"\\end{command}"
)
-package_set_package_system_dependency(documentation_developer_manual deb-src
+package_set_package_system_dependency(documentation deb-src
python3-sphinx python3-breathe doxygen graphviz)
-package_declare_extra_files_to_package(documentation_developer_manual
+package_declare_extra_files_to_package(documentation
PROJECT doc/dev-doc/akantu.dox.in
doc/dev-doc/conf.py.in
doc/dev-doc/index.rst
doc/dev-doc/reference.rst
cmake/Modules/FindSphinx.cmake
)
diff --git a/packages/documentation_user_manual.cmake b/packages/documentation_user_manual.cmake
index 1fb82dd6f..d62b6a0bb 100644
--- a/packages/documentation_user_manual.cmake
+++ b/packages/documentation_user_manual.cmake
@@ -1,55 +1,55 @@
#===============================================================================
# @file documentation_manual.cmake
#
# @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
# @author Nicolas Richart <nicolas.richart@epfl.ch>
#
# @date creation: Tue Jun 10 2014
# @date last modification: Mon Jan 18 2016
#
# @brief Akantu's manual package
#
# @section LICENSE
#
# Copyright (©) 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 <http://www.gnu.org/licenses/>.
#
#===============================================================================
-package_declare(documentation_user_manual
+package_declare(documentation_manual
DESCRIPTION "Build the user manual.")
package_declare_documentation(documentation_manual
"This package alows to compile the user manual in the build folder \\shellcode{build/doc/manual/manual.pdf}."
""
"Under Ubuntu (14.04 LTS), the installation of the dependencies can be performed using the following command:"
"\\begin{command}"
" > sudo apt-get install install rubber texlive texlive-science texlive-latex-extra"
"\\end{command}")
-package_set_package_system_dependency(documentation_user_manual deb-src
+package_set_package_system_dependency(documentation_manual deb-src
rubber
texlive-fonts-recommended
texlive-science
texlive-picture
texlive-extra
texlive-math-extra
texlive-latex-extra
texlive-bibtex-extra
)
-package_declare_extra_files_to_package(documentation_user_manual
+package_declare_extra_files_to_package(documentation_manual
MANUAL version-definition.tex.in
PROJECT cmake/Modules/FindInkscape.cmake)
diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt
index f8ad9811e..904979ae1 100644
--- a/python/CMakeLists.txt
+++ b/python/CMakeLists.txt
@@ -1,100 +1,133 @@
#===============================================================================
# @file CMakeLists.txt
#
# @author Nicolas Richart <nicolas.richart@epfl.ch>
#
# @date creation: Fri Dec 12 2014
# @date last modification: Mon Jan 18 2016
#
# @brief CMake file for the python wrapping of akantu
#
# @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 <http://www.gnu.org/licenses/>.
#
#===============================================================================
+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
+ )
+
+
set(PYAKANTU_SRCS
py_aka_common.cc
py_aka_error.cc
py_akantu.cc
py_boundary_conditions.cc
py_fe_engine.cc
py_group_manager.cc
py_mesh.cc
py_model.cc
py_parser.cc
+ py_solver.cc
)
package_is_activated(iohelper _is_activated)
if (_is_activated)
list(APPEND PYAKANTU_SRCS
py_dumpable.cc
)
endif()
package_is_activated(solid_mechanics _is_activated)
if (_is_activated)
list(APPEND PYAKANTU_SRCS
py_solid_mechanics_model.cc
py_material.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
)
endif()
package_is_activated(model_couplers _is_activated)
if(_is_activated)
list(APPEND PYAKANTU_SRCS
py_model_couplers.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 "")
set(_python_install_dir
${CMAKE_INSTALL_LIBDIR}/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages)
install(TARGETS py11_akantu
LIBRARY DESTINATION ${_python_install_dir})
install(DIRECTORY akantu
DESTINATION ${_python_install_dir}
FILES_MATCHING PATTERN "*.py")
+
+
+configure_file(setup.py.in
+ "${CMAKE_CURRENT_BINARY_DIR}/setup.py" @ONLY)
+
diff --git a/python/py_akantu.cc b/python/py_akantu.cc
index b621bff13..1efc20773 100644
--- a/python/py_akantu.cc
+++ b/python/py_akantu.cc
@@ -1,120 +1,132 @@
/* -------------------------------------------------------------------------- */
#include "aka_config.hh"
/* -------------------------------------------------------------------------- */
#include "py_aka_common.hh"
#include "py_aka_error.hh"
#include "py_boundary_conditions.hh"
#include "py_fe_engine.hh"
#include "py_group_manager.hh"
#include "py_mesh.hh"
#include "py_model.hh"
#include "py_parser.hh"
+#include "py_solver.hh"
#if defined(AKANTU_USE_IOHELPER)
#include "py_dumpable.hh"
#endif
#if defined(AKANTU_SOLID_MECHANICS)
#include "py_material.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"
#endif
#if defined(AKANTU_MODEL_COUPLERS)
#include "py_model_couplers.hh"
#endif
+#if defined(AKANTU_STRUCTURAL_MECHANICS)
+#include "py_structural_mechanics_model.hh"
+#endif
+
/* -------------------------------------------------------------------------- */
#include <aka_error.hh>
/* -------------------------------------------------------------------------- */
#include <pybind11/pybind11.h>
/* -------------------------------------------------------------------------- */
#include <iostream>
/* -------------------------------------------------------------------------- */
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);
#if defined(AKANTU_USE_IOHELPER)
register_dumpable(mod);
#endif
register_mesh(mod);
register_fe_engine(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);
#endif
#if defined(AKANTU_MODEL_COUPLERS)
register_model_couplers(mod);
#endif
}
} // namespace akantu
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
PYBIND11_MODULE(py11_akantu, mod) {
mod.doc() = "Akantu python interface";
- static py::exception<akantu::debug::Exception>
- akantu_exception(mod, "Exception");
+ static py::exception<akantu::debug::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", []() {
#if defined(AKANTU_USE_MPI)
return true;
#else
return false;
#endif
});
} // Module akantu
diff --git a/python/py_fragment_manager.cc b/python/py_fragment_manager.cc
new file mode 100644
index 000000000..cd2a48e9b
--- /dev/null
+++ b/python/py_fragment_manager.cc
@@ -0,0 +1,50 @@
+/* -------------------------------------------------------------------------- */
+#include "py_aka_array.hh"
+#include <fragment_manager.hh>
+#include <solid_mechanics_model_cohesive.hh>
+/* -------------------------------------------------------------------------- */
+#include <pybind11/pybind11.h>
+/* -------------------------------------------------------------------------- */
+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, [](FragmentManager & self) -> decltype(auto) { \
+ return self.func_name(); \
+ })
+
+void register_fragment_manager(py::module & mod) {
+ py::class_<FragmentManager, GroupManager>(mod, "FragmentManager")
+ .def(py::init<SolidMechanicsModelCohesive &, bool, const ID &>(),
+ py::arg("model"), py::arg("dump_data") = true,
+ py::arg("ID") = "fragment_manager")
+ .def("buildFragments", &FragmentManager::buildFragments,
+ py::arg("damage_limit") = 1.)
+ .def_function(computeCenterOfMass)
+ .def_function(computeVelocity)
+ .def_function(computeInertiaMoments)
+ .def("computeAllData", &FragmentManager::computeAllData,
+ py::arg("damage_limit") = 1.)
+ .def_function(computeNbElementsPerFragment)
+ .def_function(getNbFragment)
+ .def_function(getMass)
+ .def_function(getVelocity)
+ .def_function(getMomentsOfInertia)
+ .def_function(getPrincipalDirections)
+ .def_function(getNbElementsPerFragment);
+}
+} // namespace akantu
diff --git a/python/py_fragment_manager.hh b/python/py_fragment_manager.hh
new file mode 100644
index 000000000..2cbbed1d8
--- /dev/null
+++ b/python/py_fragment_manager.hh
@@ -0,0 +1,12 @@
+#include <pybind11/pybind11.h>
+
+#ifndef AKANTU_PY_FRAGMENT_MANAGER_HH_
+#define AKANTU_PY_FRAGMENT_MANAGER_HH_
+
+namespace akantu {
+
+void register_fragment_manager(pybind11::module & mod);
+
+} // namespace akantu
+
+#endif // AKANTU_PY_FRAGMENT_MANAGER_HH_
diff --git a/python/py_heat_transfer_model.cc b/python/py_heat_transfer_model.cc
index 0ade5ed10..d37cae96b 100644
--- a/python/py_heat_transfer_model.cc
+++ b/python/py_heat_transfer_model.cc
@@ -1,68 +1,68 @@
/* -------------------------------------------------------------------------- */
#include "py_aka_array.hh"
/* -------------------------------------------------------------------------- */
#include <heat_transfer_model.hh>
#include <non_linear_solver.hh>
/* -------------------------------------------------------------------------- */
//#include <pybind11/operators.h>
#include <pybind11/pybind11.h>
//#include <pybind11/stl.h>
/* -------------------------------------------------------------------------- */
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, \
[](HeatTransferModel & self) -> decltype(auto) { \
return self.func_name(); \
}, \
py::return_value_policy::reference)
#define def_function(func_name) \
def(#func_name, [](HeatTransferModel & self) -> decltype(auto) { \
return self.func_name(); \
})
/* -------------------------------------------------------------------------- */
void register_heat_transfer_model(py::module & mod) {
py::class_<HeatTransferModelOptions>(mod, "HeatTransferModelOptions")
.def(py::init<AnalysisMethod>(),
py::arg("analysis_method") = _explicit_lumped_mass);
py::class_<HeatTransferModel, Model>(mod, "HeatTransferModel",
py::multiple_inheritance())
- .def(py::init<Mesh &, UInt, const ID &, const MemoryID &>(),
+ .def(py::init<Mesh &, UInt, const ID &>(),
py::arg("mesh"), py::arg("spatial_dimension") = _all_dimensions,
- py::arg("id") = "heat_transfer_model", py::arg("memory_id") = 0)
+ py::arg("id") = "heat_transfer_model")
.def("initFull",
[](HeatTransferModel & self,
const HeatTransferModelOptions & options) {
self.initFull(options);
},
py::arg("_analysis_method") = HeatTransferModelOptions())
.def("initFull",
[](HeatTransferModel & self,
const AnalysisMethod & _analysis_method) {
self.initFull(HeatTransferModelOptions(_analysis_method));
},
py::arg("_analysis_method"))
.def("setTimeStep", &HeatTransferModel::setTimeStep, py::arg("time_step"),
py::arg("solver_id") = "")
.def_function(getStableTimeStep)
.def_function_nocopy(getTemperature)
.def_function_nocopy(getBlockedDOFs)
.def("getTemperatureGradient", &HeatTransferModel::getTemperatureGradient,
py::arg("el_type"), py::arg("ghost_type") = _not_ghost,
py::return_value_policy::reference)
.def("getKgradT", &HeatTransferModel::getKgradT, py::arg("el_type"),
py::arg("ghost_type") = _not_ghost,
py::return_value_policy::reference);
}
} // namespace akantu
diff --git a/python/py_material.cc b/python/py_material.cc
index a6f163006..ec4f1632e 100644
--- a/python/py_material.cc
+++ b/python/py_material.cc
@@ -1,246 +1,222 @@
/* -------------------------------------------------------------------------- */
#include "py_aka_array.hh"
/* -------------------------------------------------------------------------- */
#include <material_selector.hh>
#include <solid_mechanics_model.hh>
#if defined(AKANTU_COHESIVE_ELEMENT)
#include <solid_mechanics_model_cohesive.hh>
#endif
/* -------------------------------------------------------------------------- */
#include <pybind11/operators.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
/* -------------------------------------------------------------------------- */
namespace py = pybind11;
/* -------------------------------------------------------------------------- */
#if not defined(PYBIND11_OVERRIDE)
#define PYBIND11_OVERRIDE PYBIND11_OVERLOAD
#define PYBIND11_OVERRIDE_PURE PYBIND11_OVERLOAD_PURE
#endif
namespace akantu {
template <typename _Material> class PyMaterial : public _Material {
public:
/* Inherit the constructors */
using _Material::_Material;
~PyMaterial() override = default;
void initMaterial() override {
PYBIND11_OVERRIDE(void, _Material, initMaterial, ); // NOLINT
};
void computeStress(ElementType el_type,
GhostType ghost_type = _not_ghost) override {
PYBIND11_OVERRIDE_PURE(void, _Material, computeStress, el_type, ghost_type);
}
void computeTangentModuli(ElementType el_type, Array<Real> & tangent_matrix,
GhostType ghost_type = _not_ghost) override {
PYBIND11_OVERRIDE(void, _Material, computeTangentModuli, el_type,
tangent_matrix, ghost_type);
}
void computePotentialEnergy(ElementType el_type) override {
PYBIND11_OVERRIDE(void, _Material, computePotentialEnergy, el_type);
}
Real getPushWaveSpeed(const Element & element) const override {
PYBIND11_OVERRIDE(Real, _Material, getPushWaveSpeed, element);
}
Real getShearWaveSpeed(const Element & element) const override {
PYBIND11_OVERRIDE(Real, _Material, getShearWaveSpeed, element);
}
+ template <typename T>
void registerInternal(const std::string & name, UInt nb_component) {
- this->internals[name] = std::make_shared<InternalField<Real>>(name, *this);
+ auto && internal = std::make_shared<InternalField<T>>(name, *this);
AKANTU_DEBUG_INFO("alloc internal " << name << " "
<< &this->internals[name]);
- this->internals[name]->initialize(nb_component);
+ internal->initialize(nb_component);
+ this->internals[name] = internal;
}
- auto & getInternals() { return this->internals; }
-
protected:
- std::map<std::string, std::shared_ptr<InternalField<Real>>> internals;
+ std::map<std::string, std::shared_ptr<ElementTypeMapBase>> internals;
};
/* -------------------------------------------------------------------------- */
template <typename T>
-void register_element_type_map_array(py::module & mod,
- const std::string & name) {
- py::class_<ElementTypeMapArray<T>, std::shared_ptr<ElementTypeMapArray<T>>>(
- mod, ("ElementTypeMapArray" + name).c_str())
- .def(
- "__call__",
- [](ElementTypeMapArray<T> & 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)
- .def(
- "elementTypes",
- [](ElementTypeMapArray<T> & self, UInt _dim, GhostType _ghost_type,
- ElementKind _kind) -> decltype(auto) {
- auto types = self.elementTypes(_dim, _ghost_type, _kind);
- std::vector<ElementType> _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);
-
+void register_internal_field(py::module & mod, const std::string & name) {
py::class_<InternalField<T>, ElementTypeMapArray<T>,
std::shared_ptr<InternalField<T>>>(
mod, ("InternalField" + name).c_str());
}
/* -------------------------------------------------------------------------- */
template <typename _Material>
void define_material(py::module & mod, const std::string & name) {
py::class_<_Material, PyMaterial<_Material>, Parsable>(
mod, name.c_str(), py::multiple_inheritance())
.def(py::init<SolidMechanicsModel &, const ID &>())
.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)
.def("initMaterial", &Material::initMaterial)
.def("getModel", &Material::getModel)
- .def("registerInternal",
+ .def("registerInternalReal",
+ [](Material & self, const std::string & name, UInt nb_component) {
+ return dynamic_cast<PyMaterial<Material> &>(self)
+ .registerInternal<Real>(name, nb_component);
+ })
+ .def("registerInternalUInt",
[](Material & self, const std::string & name, UInt nb_component) {
- return dynamic_cast<PyMaterial<Material> &>(self).registerInternal(
- name, nb_component);
+ return dynamic_cast<PyMaterial<Material> &>(self)
+ .registerInternal<UInt>(name, nb_component);
})
.def(
- "getInternalFieldReal",
- [](Material & self, const ID & id, const ElementType & type,
- const GhostType & ghost_type) -> Array<Real> & {
- return self.getArray<Real>(id, type, ghost_type);
+ "getInternalReal",
+ [](Material & self, const ID & id) -> decltype(auto) {
+ return self.getInternal<Real>(id);
},
- py::arg("id"), py::arg("type"), py::arg("ghost_type") = _not_ghost)
+ py::arg("id"), py::return_value_policy::reference)
.def(
- "getInternalFieldUInt",
- [](Material & self, const ID & id, const ElementType & type,
- const GhostType & ghost_type) -> Array<UInt> & {
- return self.getArray<UInt>(id, type, ghost_type);
+ "getInternalUInt",
+ [](Material & self, const ID & id) -> decltype(auto) {
+ return self.getInternal<UInt>(id);
},
- py::arg("id"), py::arg("type"), py::arg("ghost_type") = _not_ghost)
+ py::arg("id"), py::return_value_policy::reference)
.def(
"getElementFilter",
- [](Material & self, const ElementType & type,
- const GhostType & ghost_type) -> const Array<UInt> & {
- return self.getElementFilter()(type, ghost_type);
+ [](Material & self) -> decltype(auto) {
+ return self.getElementFilter();
},
- py::arg("type"), py::arg("ghost_type") = _not_ghost);
+ py::return_value_policy::reference);
}
/* -------------------------------------------------------------------------- */
void register_material(py::module & mod) {
py::class_<MaterialFactory>(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](UInt dim, const ID & /*unused*/,
SolidMechanicsModel & model,
const ID & option) -> std::unique_ptr<Material> {
py::object obj = func(dim, id, model, option);
auto & ptr = py::cast<Material &>(obj);
obj.release();
return std::unique_ptr<Material>(&ptr);
});
- });
+ })
+ .def("getPossibleAllocators", &MaterialFactory::getPossibleAllocators);
- register_element_type_map_array<Real>(mod, "Real");
- register_element_type_map_array<UInt>(mod, "UInt");
+ register_internal_field<Real>(mod, "Real");
+ register_internal_field<UInt>(mod, "UInt");
define_material<Material>(mod, "Material");
}
/* -------------------------------------------------------------------------- */
template <typename T>
void register_data_material_selector(py::module & mod,
const std::string & name) {
py::class_<ElementDataMaterialSelector<T>, MaterialSelector,
std::shared_ptr<ElementDataMaterialSelector<T>>>(
mod, ("ElementDataMaterialSelector" + name).c_str());
py::class_<MeshDataMaterialSelector<T>, ElementDataMaterialSelector<T>,
std::shared_ptr<MeshDataMaterialSelector<T>>>(
mod, ("MeshDataMaterialSelector" + name).c_str())
.def(py::init<const std::string &, SolidMechanicsModel &, UInt>(),
py::arg("name"), py::arg("model"), py::arg("first_index") = 1);
}
/* -------------------------------------------------------------------------- */
void register_material_selector(py::module & mod) {
py::class_<MaterialSelector, std::shared_ptr<MaterialSelector>>(
mod, "MaterialSelector")
.def("setFallback",
[](MaterialSelector & self, UInt f) { self.setFallback(f); })
.def("setFallback",
[](MaterialSelector & self,
const std::shared_ptr<MaterialSelector> & fallback_selector) {
self.setFallback(fallback_selector);
})
.def("setFallback",
[](MaterialSelector & self, MaterialSelector & fallback_selector) {
self.setFallback(fallback_selector);
});
#if defined(AKANTU_COHESIVE_ELEMENT)
py::class_<DefaultMaterialCohesiveSelector, MaterialSelector,
std::shared_ptr<DefaultMaterialCohesiveSelector>>(
mod, "DefaultMaterialCohesiveSelector")
.def(py::init<const SolidMechanicsModelCohesive &>());
py::class_<MeshDataMaterialCohesiveSelector, MaterialSelector,
std::shared_ptr<MeshDataMaterialCohesiveSelector>>(
mod, "MeshDataMaterialCohesiveSelector")
.def(py::init<const SolidMechanicsModelCohesive &>());
py::class_<MaterialCohesiveRulesSelector, MaterialSelector,
std::shared_ptr<MaterialCohesiveRulesSelector>>(
mod, "MaterialCohesiveRulesSelector")
.def(py::init<const SolidMechanicsModelCohesive &,
const MaterialCohesiveRules &, const ID &>(),
py::arg("model"), py::arg("rules"),
py::arg("mesh_data_id") = "physical_names");
#endif
register_data_material_selector<std::string>(mod, "String");
}
-/* -------------------------------------------------------------------------- */
-
} // namespace akantu
diff --git a/python/py_mesh.cc b/python/py_mesh.cc
index 24a173bc6..fb2b536a0 100644
--- a/python/py_mesh.cc
+++ b/python/py_mesh.cc
@@ -1,81 +1,157 @@
/* -------------------------------------------------------------------------- */
#include "aka_config.hh"
/* -------------------------------------------------------------------------- */
#include "py_aka_array.hh"
/* -------------------------------------------------------------------------- */
#include <mesh.hh>
+#include <mesh_accessor.hh>
#include <mesh_utils.hh>
/* -------------------------------------------------------------------------- */
#include <pybind11/pybind11.h>
+#include <pybind11/stl.h>
/* -------------------------------------------------------------------------- */
namespace py = pybind11;
/* -------------------------------------------------------------------------- */
namespace akantu {
+/* -------------------------------------------------------------------------- */
+template <typename T>
+void register_element_type_map_array(py::module & mod,
+ const std::string & name) {
+ py::class_<ElementTypeMapArray<T>, std::shared_ptr<ElementTypeMapArray<T>>>(
+ mod, ("ElementTypeMapArray" + name).c_str())
+ .def(
+ "__call__",
+ [](ElementTypeMapArray<T> & 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)
+ .def(
+ "elementTypes",
+ [](ElementTypeMapArray<T> & self, UInt _dim, GhostType _ghost_type,
+ ElementKind _kind) -> std::vector<ElementType> {
+ auto types = self.elementTypes(_dim, _ghost_type, _kind);
+ std::vector<ElementType> _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);
+}
+
/* -------------------------------------------------------------------------- */
void register_mesh(py::module & mod) {
+
+ register_element_type_map_array<Real>(mod, "Real");
+ register_element_type_map_array<UInt>(mod, "UInt");
+
py::class_<MeshData>(mod, "MeshData")
.def(
"getElementalDataUInt",
- [](MeshData & _this, const ID & name) -> ElementTypeMapArray<UInt> & {
+ [](MeshData & _this, const ID & name) -> decltype(auto) {
return _this.getElementalData<UInt>(name);
},
+ py::return_value_policy::reference)
+ .def(
+ "getElementalDataReal",
+ [](MeshData & _this, const ID & name) -> decltype(auto) {
+ return _this.getElementalData<Real>(name);
+ },
py::return_value_policy::reference);
py::class_<Mesh, GroupManager, Dumpable, MeshData>(mod, "Mesh",
py::multiple_inheritance())
- .def(py::init<UInt, const ID &, const MemoryID &>(),
- py::arg("spatial_dimension"), py::arg("id") = "mesh",
- py::arg("memory_id") = 0)
+ .def(py::init<UInt, const ID &>(),
+ 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("fillNodesToElements", &Mesh::fillNodesToElements,
py::arg("dimension") = _all_dimensions)
.def("getAssociatedElements", [](Mesh & self, const UInt & node, py::list l) {
Array<Element> elements;
self.getAssociatedElements(node, elements);
for (auto && element : elements) {
l.append(element);
}
})
.def("makePeriodic",
[](Mesh & self, const SpatialDirection & direction) {
self.makePeriodic(direction);
})
.def(
"getNbElement",
- [](Mesh & self, const UInt spatial_dimension,
- GhostType ghost_type, ElementKind kind) {
+ [](Mesh & self, const UInt 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) {
+ [](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_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<Real>(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<Real>(name, type, ghost_type);
+ },
+ py::arg("name"), py::arg("type"), py::arg("ghost_type") = _not_ghost);
/* ------------------------------------------------------------------------ */
py::class_<MeshUtils>(mod, "MeshUtils")
.def_static("buildFacets", &MeshUtils::buildFacets);
+
+ py::class_<MeshAccessor>(mod, "MeshAccessor")
+ .def(py::init<Mesh &>(), 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);
}
} // namespace akantu
diff --git a/python/py_model.cc b/python/py_model.cc
index 185fb0698..8d60f89f3 100644
--- a/python/py_model.cc
+++ b/python/py_model.cc
@@ -1,81 +1,94 @@
/* -------------------------------------------------------------------------- */
#include "py_aka_array.hh"
/* -------------------------------------------------------------------------- */
#include <model.hh>
#include <non_linear_solver.hh>
#include <sparse_matrix_aij.hh>
/* -------------------------------------------------------------------------- */
#include <pybind11/operators.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
/* -------------------------------------------------------------------------- */
namespace py = pybind11;
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
void register_model(py::module & mod) {
py::class_<DOFManager>(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::class_<NonLinearSolver>(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_<ModelSolver, Parsable>(mod, "ModelSolver",
py::multiple_inheritance())
.def("getNonLinearSolver",
(NonLinearSolver & (ModelSolver::*)(const ID &)) &
ModelSolver::getNonLinearSolver,
py::arg("solver_id") = "", py::return_value_policy::reference)
.def("solveStep", [](ModelSolver & self) { self.solveStep(); })
.def("solveStep", [](ModelSolver & self, const ID & solver_id) {
self.solveStep(solver_id);
});
py::class_<Model, ModelSolver>(mod, "Model", py::multiple_inheritance())
.def("setBaseName", &Model::setBaseName)
.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::dump)
+ .def("dump", py::overload_cast<>(&Model::dump))
+ .def("dump", py::overload_cast<UInt>(&Model::dump))
+ .def("dump", py::overload_cast<Real, UInt>(&Model::dump))
+ .def("dump", py::overload_cast<const std::string &>(&Model::dump))
+ .def("dump", py::overload_cast<const std::string &, UInt>(&Model::dump))
+ .def("dump",
+ py::overload_cast<const std::string &, Real, UInt>(&Model::dump))
.def("initNewSolver", &Model::initNewSolver)
.def("getDOFManager", &Model::getDOFManager,
- py::return_value_policy::reference);
+ py::return_value_policy::reference)
+ .def("assembleMatrix", &Model::assembleMatrix);
}
} // namespace akantu
diff --git a/python/py_solid_mechanics_model.cc b/python/py_solid_mechanics_model.cc
index 9f4252e27..5b748c053 100644
--- a/python/py_solid_mechanics_model.cc
+++ b/python/py_solid_mechanics_model.cc
@@ -1,113 +1,110 @@
/* -------------------------------------------------------------------------- */
#include "py_aka_array.hh"
/* -------------------------------------------------------------------------- */
#include <non_linear_solver.hh>
#include <solid_mechanics_model.hh>
/* -------------------------------------------------------------------------- */
#include <pybind11/pybind11.h>
/* -------------------------------------------------------------------------- */
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) {
-void
-register_solid_mechanics_model(py::module & mod) {
py::class_<SolidMechanicsModelOptions>(mod, "SolidMechanicsModelOptions")
.def(py::init<AnalysisMethod>(),
py::arg("_analysis_method") = _explicit_lumped_mass);
py::class_<SolidMechanicsModel, Model>(mod, "SolidMechanicsModel",
py::multiple_inheritance())
- .def(py::init<Mesh &, UInt, const ID &, const MemoryID &,
+ .def(py::init<Mesh &, UInt, const ID &,
std::shared_ptr<DOFManager>, const ModelType>(),
py::arg("mesh"), py::arg("spatial_dimension") = _all_dimensions,
- py::arg("id") = "solid_mechanics_model", py::arg("memory_id") = 0,
+ 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",
py::overload_cast<const std::string &>(
&SolidMechanicsModel::getEnergy),
py::arg("energy_id"))
+ .def("getEnergy",
+ py::overload_cast<const std::string &, const std::string &>(
+ &SolidMechanicsModel::getEnergy),
+ 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(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("dump", py::overload_cast<>(&SolidMechanicsModel::dump))
- .def("dump",
- py::overload_cast<const std::string &>(&SolidMechanicsModel::dump))
- .def("dump", py::overload_cast<const std::string &, UInt>(
- &SolidMechanicsModel::dump))
- .def("dump", py::overload_cast<const std::string &, Real, UInt>(
- &SolidMechanicsModel::dump))
.def("getMaterial",
py::overload_cast<UInt>(&SolidMechanicsModel::getMaterial),
py::return_value_policy::reference)
.def("getMaterial",
py::overload_cast<const std::string &>(
&SolidMechanicsModel::getMaterial),
py::return_value_policy::reference)
.def("getMaterialIndex", &SolidMechanicsModel::getMaterialIndex)
.def("setMaterialSelector", &SolidMechanicsModel::setMaterialSelector)
.def("getMaterialSelector", &SolidMechanicsModel::getMaterialSelector);
}
} // namespace akantu
diff --git a/python/py_solid_mechanics_model_cohesive.cc b/python/py_solid_mechanics_model_cohesive.cc
index f46c3a8d9..d5495117d 100644
--- a/python/py_solid_mechanics_model_cohesive.cc
+++ b/python/py_solid_mechanics_model_cohesive.cc
@@ -1,64 +1,64 @@
/* -------------------------------------------------------------------------- */
#include "py_aka_array.hh"
/* -------------------------------------------------------------------------- */
#include <non_linear_solver.hh>
#include <solid_mechanics_model_cohesive.hh>
/* -------------------------------------------------------------------------- */
#include <pybind11/pybind11.h>
/* -------------------------------------------------------------------------- */
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_<CohesiveElementInserter>(mod, "CohesiveElementInserter")
.def("setLimit", &CohesiveElementInserter::setLimit);
py::class_<SolidMechanicsModelCohesiveOptions, SolidMechanicsModelOptions>(
mod, "SolidMechanicsModelCohesiveOptions")
.def(py::init<AnalysisMethod, bool>(),
py::arg("analysis_method") = _explicit_lumped_mass,
py::arg("is_extrinsic") = false);
py::class_<SolidMechanicsModelCohesive, SolidMechanicsModel>(
mod, "SolidMechanicsModelCohesive")
- .def(py::init<Mesh &, UInt, const ID &, const MemoryID &>(),
+ .def(py::init<Mesh &, UInt, const ID &>(),
py::arg("mesh"), py::arg("spatial_dimension") = _all_dimensions,
- py::arg("id") = "solid_mechanics_model", py::arg("memory_id") = 0)
+ 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)
.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 6e292867d..a0146642d 100644
--- a/python/py_solver.cc
+++ b/python/py_solver.cc
@@ -1,45 +1,53 @@
/* -------------------------------------------------------------------------- */
+#include "py_solver.hh"
#include "py_aka_array.hh"
/* -------------------------------------------------------------------------- */
-#include "py_solver.h"
-/* -------------------------------------------------------------------------- */#include <model.hh>
+#include <model.hh>
#include <non_linear_solver.hh>
#include <sparse_matrix_aij.hh>
/* -------------------------------------------------------------------------- */
#include <pybind11/operators.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
/* -------------------------------------------------------------------------- */
namespace py = pybind11;
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
-void register_solver(py::module & mod) {
+void register_solvers(py::module & mod) {
py::class_<SparseMatrix>(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("__call__", [](const SparseMatrix & self, UInt i, UInt j) {
return self(i, j);
});
py::class_<SparseMatrixAIJ, SparseMatrix>(mod, "SparseMatrixAIJ")
.def("getIRN", &SparseMatrixAIJ::getIRN)
.def("getJCN", &SparseMatrixAIJ::getJCN)
.def("getA", &SparseMatrixAIJ::getA);
py::class_<SolverVector>(mod, "SolverVector");
}
+
+} // namespace akantu
diff --git a/python/py_solver.hh b/python/py_solver.hh
index 892606998..41e1124e8 100644
--- a/python/py_solver.hh
+++ b/python/py_solver.hh
@@ -1,12 +1,12 @@
#include <pybind11/pybind11.h>
#ifndef AKANTU_PY_AKA_SOLVER_HH_
#define AKANTU_PY_AKA_SOLVER_HH_
namespace akantu {
-void register_solver(pybind11::module & mod);
+void register_solvers(pybind11::module & mod);
}
#endif
diff --git a/python/py_structural_mechanics_model.cc b/python/py_structural_mechanics_model.cc
new file mode 100644
index 000000000..05ec73e83
--- /dev/null
+++ b/python/py_structural_mechanics_model.cc
@@ -0,0 +1,129 @@
+/* -------------------------------------------------------------------------- */
+#include "py_aka_array.hh"
+/* -------------------------------------------------------------------------- */
+#include <structural_mechanics_model.hh>
+/* -------------------------------------------------------------------------- */
+#include <pybind11/pybind11.h>
+/* -------------------------------------------------------------------------- */
+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_<StructuralMaterial>(mod, "StructuralMaterial")
+ .def(py::init<>())
+ .def(py::init<const StructuralMaterial &>())
+ .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_<StructuralMechanicsModel, Model>(mod, "StructuralMechanicsModel")
+ .def(py::init<Mesh &, UInt, const ID &>(),
+ 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, const 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); },
+ "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)
+ .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)
+ .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");
+
+} // End: register structural mechanical model
+
+} // namespace akantu
diff --git a/python/py_structural_mechanics_model.hh b/python/py_structural_mechanics_model.hh
new file mode 100644
index 000000000..a859cd1f4
--- /dev/null
+++ b/python/py_structural_mechanics_model.hh
@@ -0,0 +1,12 @@
+#include <pybind11/pybind11.h>
+
+#ifndef AKANTU_PY_STRUCTURAL_MECHANICS_MODEL_HH_
+#define AKANTU_PY_STRUCTURAL_MECHANICS_MODEL_HH_
+
+namespace akantu {
+
+void register_structural_mechanics_model(pybind11::module & mod);
+
+} // namespace akantu
+
+#endif // AKANTU_PY_STRUCTURAL_MECHANICS_MODEL_HH_
diff --git a/python/setup.py.in b/python/setup.py.in
new file mode 100644
index 000000000..b7ac576cc
--- /dev/null
+++ b/python/setup.py.in
@@ -0,0 +1,51 @@
+#!/usr/bin/env python
+# -*- coding:utf-8 -*-
+
+from setuptools import setup, find_packages, Extension
+
+
+name = 'Akantu'
+version = '4.0.0'
+
+aka_inc_dirs = "@AKANTU_LIBRARY_INCLUDE_DIRS@".split(';')
+aka_inc_dirs += "@AKANTU_PRIVATE_EXTERNAL_INCLUDE_DIR@".split(';')
+aka_inc_dirs += "@AKANTU_INTERFACE_EXTERNAL_INCLUDE_DIR@".split(';')
+aka_inc_dirs.append(
+ "@PROJECT_SOURCE_DIR@/third-party/akantu_iterators/include")
+aka_inc_dirs.append(
+ "@PROJECT_BINARY_DIR@/src")
+aka_inc_dirs.append('@PROJECT_SOURCE_DIR@/python')
+aka_inc_dirs += '@PYBIND11_INCLUDE_DIR@'.split(';')
+
+
+pybind_extension = Extension(
+ "py11_akantu",
+ ["@PROJECT_SOURCE_DIR@/python/py_aka_common.cc",
+ "@PROJECT_SOURCE_DIR@/python/py_aka_error.cc",
+ "@PROJECT_SOURCE_DIR@/python/py_akantu.cc",
+ "@PROJECT_SOURCE_DIR@/python/py_boundary_conditions.cc",
+ "@PROJECT_SOURCE_DIR@/python/py_fe_engine.cc",
+ "@PROJECT_SOURCE_DIR@/python/py_group_manager.cc",
+ "@PROJECT_SOURCE_DIR@/python/py_mesh.cc",
+ "@PROJECT_SOURCE_DIR@/python/py_model.cc",
+ "@PROJECT_SOURCE_DIR@/python/py_parser.cc"],
+ version=version,
+ extra_compile_args=['-std=c++17'],
+ libraries=['akantu'],
+ library_dirs=["@PROJECT_BINARY_DIR@/src/"],
+ # '-g', '-fno-omit-frame-pointer'], # , '-O0'],
+ include_dirs=aka_inc_dirs
+)
+
+
+setup(name=name,
+ packages=find_packages(),
+ version="0.0.0",
+ author="Guillaume Anciaux",
+ author_email="guillaume.anciaux@epfl.ch",
+ description=("Akantu: Swiss-Made Open-Source Finite-Element Library"),
+ license="GPL",
+ tests_require=["pytest"],
+ install_requires=['numpy'],
+ ext_modules=[pybind_extension],
+ )
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index c5829f302..1e52131e6 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,258 +1,264 @@
#===============================================================================
# @file CMakeLists.txt
#
# @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
# @author Nicolas Richart <nicolas.richart@epfl.ch>
#
# @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 <http://www.gnu.org/licenses/>.
#
#===============================================================================
#===============================================================================
# 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")
#===============================================================================
# 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 $<BUILD_INTERFACE:${AKANTU_INCLUDE_DIRS}>
INTERFACE $<INSTALL_INTERFACE:include/akantu>
)
# small trick for build includes in public
set_property(TARGET akantu APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES
$<BUILD_INTERFACE:${AKANTU_INCLUDE_DIRS}>)
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})
set_target_properties(akantu
PROPERTIES
${AKANTU_LIBRARY_PROPERTIES} # this contains the version
COMPILE_FLAGS "${_cxx_flags}"
#PRIVATE_HEADER ${AKANTU_LIBRARY_PRIVATE_HDRS}
)
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 ${CMAKE_INSTALL_DATAROOTDIR}/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 d85d525a6..6390db524 100644
--- a/src/common/aka_array.hh
+++ b/src/common/aka_array.hh
@@ -1,439 +1,442 @@
/**
* @file aka_array.hh
*
* @author Till Junge <till.junge@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Tue Jan 16 2018
*
* @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
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_common.hh"
#include "aka_types.hh"
/* -------------------------------------------------------------------------- */
#include <typeinfo>
#include <vector>
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
#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:
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 UInt 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
UInt size() const { return size_; }
/// Get the number of components
AKANTU_GET_MACRO(NbComponent, nb_component, UInt);
/// Get the name of th array
AKANTU_GET_MACRO(ID, id, const 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
UInt size_{0};
/// number of components
UInt nb_component{1};
};
/* -------------------------------------------------------------------------- */
namespace {
template <std::size_t dim, typename T> struct IteratorHelper {};
template <typename T> struct IteratorHelper<0, T> { using type = T; };
template <typename T> struct IteratorHelper<1, T> { using type = Vector<T>; };
template <typename T> struct IteratorHelper<2, T> { using type = Matrix<T>; };
template <typename T> struct IteratorHelper<3, T> {
using type = Tensor3<T>;
};
template <std::size_t dim, typename T>
using IteratorHelper_t = typename IteratorHelper<dim, T>::type;
} // namespace
/* -------------------------------------------------------------------------- */
/* Memory handling layer */
/* -------------------------------------------------------------------------- */
enum class ArrayAllocationType {
_default,
_pod,
};
template <typename T>
struct ArrayAllocationTrait
: public std::conditional_t<
std::is_scalar<T>::value,
std::integral_constant<ArrayAllocationType,
ArrayAllocationType::_pod>,
std::integral_constant<ArrayAllocationType,
ArrayAllocationType::_default>> {};
/* -------------------------------------------------------------------------- */
template <typename T,
ArrayAllocationType allocation_trait = ArrayAllocationTrait<T>::value>
class ArrayDataLayer : public ArrayBase {
public:
using value_type = T;
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(UInt size = 0, UInt nb_component = 1,
const ID & id = "");
/// Allocation of a new vector with a default value
ArrayDataLayer(UInt size, UInt 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<value_type> & 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(UInt size, UInt nb_component);
// allocate and initialize the memory
virtual void allocate(UInt size, UInt 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 <template <typename> class C,
typename = std::enable_if_t<aka::is_tensor<C<T>>::value or
aka::is_tensor_proxy<C<T>>::value>>
inline void push_back(const C<T> & 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(UInt size, UInt new_size = UInt(-1));
/// change the size of the Array
virtual void resize(UInt size);
/// change the size of the Array and initialize the values
virtual void resize(UInt size, const T & val);
/// get the amount of space allocated in bytes
inline UInt getMemorySize() const override;
/// Get the real size allocated in memory
inline UInt getAllocatedSize() const;
/// give the address of the memory allocated for this vector
T * storage() const { return values; };
protected:
/// allocation type agnostic data access
T * values{nullptr};
/// data storage
std::vector<T> data_storage;
};
/* -------------------------------------------------------------------------- */
/* Actual Array */
/* -------------------------------------------------------------------------- */
template <typename T, bool is_scal> class Array : public ArrayDataLayer<T> {
private:
using parent = ArrayDataLayer<T>;
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
using value_type = typename parent::value_type;
using reference = typename parent::reference;
using pointer_type = typename parent::pointer_type;
using const_reference = typename parent::const_reference;
~Array() override;
Array() : Array(0){};
/// Allocation of a new vector
explicit Array(UInt size, UInt nb_component = 1, const ID & id = "");
/// Allocation of a new vector with a default value
explicit Array(UInt size, UInt 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<T> & vect);
// copy operator
Array & operator=(const Array & other);
// move constructor
Array(Array && other) noexcept = default;
// move assign
Array & operator=(Array && other) noexcept = default;
/* ------------------------------------------------------------------------ */
/* Iterator */
/* ------------------------------------------------------------------------ */
/// \todo protected: does not compile with intel check why
public:
template <class R, class it, class IR = R,
bool is_tensor_ = aka::is_tensor<std::decay_t<R>>::value>
class iterator_internal;
public:
/* ------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------ */
template <typename R = T> class const_iterator;
template <typename R = T> class iterator;
/* ------------------------------------------------------------------------ */
/// iterator for Array of nb_component = 1
using scalar_iterator = iterator<T>;
/// const_iterator for Array of nb_component = 1
using const_scalar_iterator = const_iterator<T>;
/// iterator returning Vectors of size n on entries of Array with
/// nb_component = n
using vector_iterator = iterator<Vector<T>>;
/// const_iterator returning Vectors of n size on entries of Array with
/// nb_component = n
using const_vector_iterator = const_iterator<Vector<T>>;
/// iterator returning Matrices of size (m, n) on entries of Array with
/// nb_component = m*n
using matrix_iterator = iterator<Matrix<T>>;
/// const iterator returning Matrices of size (m, n) on entries of Array with
/// nb_component = m*n
using const_matrix_iterator = const_iterator<Matrix<T>>;
/// iterator returning Tensor3 of size (m, n, k) on entries of Array with
/// nb_component = m*n*k
using tensor3_iterator = iterator<Tensor3<T>>;
/// const iterator returning Tensor3 of size (m, n, k) on entries of Array
/// with nb_component = m*n*k
using const_tensor3_iterator = const_iterator<Tensor3<T>>;
/* ------------------------------------------------------------------------ */
template <typename... Ns> inline decltype(auto) begin(Ns &&... n);
template <typename... Ns> inline decltype(auto) end(Ns &&... n);
template <typename... Ns> inline decltype(auto) begin(Ns &&... n) const;
template <typename... Ns> inline decltype(auto) end(Ns &&... n) const;
template <typename... Ns> inline decltype(auto) begin_reinterpret(Ns &&... n);
template <typename... Ns> inline decltype(auto) end_reinterpret(Ns &&... n);
template <typename... Ns>
inline decltype(auto) begin_reinterpret(Ns &&... n) const;
template <typename... Ns>
inline decltype(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
UInt find(const_reference elem) const;
/// @see Array::find(const_reference elem) const
-// UInt find(T elem[]) const;
+ // UInt 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 <template <typename> class C,
typename = std::enable_if_t<aka::is_tensor<C<T>>::value or
aka::is_tensor_proxy<C<T>>::value>>
inline void push_back(const C<T> & new_elem) {
parent::push_back(new_elem);
}
+ /// append the content of the iterator at the end of the Array
template <typename Ret> inline void push_back(const iterator<Ret> & it) {
push_back(*it);
}
/// erase the value at position i
inline void erase(UInt i);
/// ask Nico, clarify
template <typename R> inline iterator<R> erase(const iterator<R> & it);
/// @see Array::find(const_reference elem) const
template <template <typename> class C,
typename = std::enable_if_t<aka::is_tensor<C<T>>::value or
aka::is_tensor_proxy<C<T>>::value>>
inline UInt find(const C<T> & 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 the array to T{}
inline void zero() { this->set({}); }
/// resize the array to 0
inline void clear() { this->resize(0); }
/// set all tuples of the array to a given vector or matrix
/// @param vm Matrix or Vector to fill the array with
template <template <typename> class C,
typename = std::enable_if_t<aka::is_tensor<C<T>>::value or
aka::is_tensor_proxy<C<T>>::value>>
inline void set(const C<T> & vm);
/// Append the content of the other array to the current one
void append(const Array<T> & 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<T, is_scal> & other, bool no_sanity_check = false);
/// function to print the containt of the class
void printself(std::ostream & stream, int indent = 0) const override;
/* ------------------------------------------------------------------------ */
/* Operators */
/* ------------------------------------------------------------------------ */
public:
/// substraction entry-wise
Array<T, is_scal> & operator-=(const Array<T, is_scal> & other);
/// addition entry-wise
Array<T, is_scal> & operator+=(const Array<T, is_scal> & other);
/// multiply evry entry by alpha
Array<T, is_scal> & operator*=(const T & alpha);
/// check if the array are identical entry-wise
bool operator==(const Array<T, is_scal> & other) const;
/// @see Array::operator==(const Array<T, is_scal> & other) const
bool operator!=(const Array<T, is_scal> & other) const;
/// return a reference to the j-th entry of the i-th tuple
inline reference operator()(UInt i, UInt j = 0);
/// return a const reference to the j-th entry of the i-th tuple
inline const_reference operator()(UInt i, UInt j = 0) const;
/// return a reference to the ith component of the 1D array
inline reference operator[](UInt i);
/// return a const reference to the ith component of the 1D array
inline const_reference operator[](UInt i) const;
};
/* -------------------------------------------------------------------------- */
/* Inline Functions Array<T, is_scal> */
/* -------------------------------------------------------------------------- */
template <typename T, bool is_scal>
inline std::ostream & operator<<(std::ostream & stream,
const Array<T, is_scal> & _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 bdbb0a45d..50788d2ae 100644
--- a/src/common/aka_array_tmpl.hh
+++ b/src/common/aka_array_tmpl.hh
@@ -1,1364 +1,1361 @@
/**
* @file aka_array_tmpl.hh
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Thu Jul 15 2010
* @date last modification: Tue Feb 20 2018
*
* @brief Inline functions of the classes Array<T> and ArrayBase
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
/* Inline Functions Array<T> */
/* -------------------------------------------------------------------------- */
#include "aka_array.hh" // NOLINT
-#include "aka_static_memory.hh"
/* -------------------------------------------------------------------------- */
#include <memory>
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_AKA_ARRAY_TMPL_HH_
#define AKANTU_AKA_ARRAY_TMPL_HH_
namespace akantu {
namespace debug {
struct ArrayException : public Exception {};
} // namespace debug
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
template <typename T, ArrayAllocationType allocation_trait>
ArrayDataLayer<T, allocation_trait>::ArrayDataLayer(UInt size,
UInt nb_component,
const ID & id)
: ArrayBase(id) {
allocate(size, nb_component);
}
/* -------------------------------------------------------------------------- */
template <typename T, ArrayAllocationType allocation_trait>
ArrayDataLayer<T, allocation_trait>::ArrayDataLayer(UInt size,
UInt nb_component,
const_reference value,
const ID & id)
: ArrayBase(id) {
allocate(size, nb_component, value);
}
/* -------------------------------------------------------------------------- */
template <typename T, ArrayAllocationType allocation_trait>
ArrayDataLayer<T, allocation_trait>::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 <typename T, ArrayAllocationType allocation_trait>
ArrayDataLayer<T, allocation_trait>::ArrayDataLayer(
const std::vector<value_type> & vect) {
this->data_storage = vect;
this->size_ = vect.size();
this->nb_component = 1;
this->values = this->data_storage.data();
}
/* -------------------------------------------------------------------------- */
template <typename T, ArrayAllocationType allocation_trait>
ArrayDataLayer<T, allocation_trait> &
ArrayDataLayer<T, allocation_trait>::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 <typename T, ArrayAllocationType allocation_trait>
void ArrayDataLayer<T, allocation_trait>::allocate(UInt new_size,
UInt nb_component) {
this->nb_component = nb_component;
this->resize(new_size);
}
/* -------------------------------------------------------------------------- */
template <typename T, ArrayAllocationType allocation_trait>
void ArrayDataLayer<T, allocation_trait>::allocate(UInt new_size,
UInt nb_component,
const T & val) {
this->nb_component = nb_component;
this->resize(new_size, val);
}
/* -------------------------------------------------------------------------- */
template <typename T, ArrayAllocationType allocation_trait>
void ArrayDataLayer<T, allocation_trait>::resize(UInt new_size) {
this->data_storage.resize(new_size * this->nb_component);
this->values = this->data_storage.data();
this->size_ = new_size;
}
/* -------------------------------------------------------------------------- */
template <typename T, ArrayAllocationType allocation_trait>
void ArrayDataLayer<T, allocation_trait>::resize(UInt 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 <typename T, ArrayAllocationType allocation_trait>
void ArrayDataLayer<T, allocation_trait>::reserve(UInt size, UInt new_size) {
if (new_size != UInt(-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 <typename T, ArrayAllocationType allocation_trait>
inline void ArrayDataLayer<T, allocation_trait>::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<T> or Vector<T> */
template <typename T, ArrayAllocationType allocation_trait>
template <template <typename> class C, typename>
inline void
ArrayDataLayer<T, allocation_trait>::push_back(const C<T> & 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 (UInt i = 0; i < new_elem.size(); ++i) {
this->data_storage.push_back(new_elem[i]);
}
this->values = this->data_storage.data();
this->size_ += 1;
}
/* -------------------------------------------------------------------------- */
template <typename T, ArrayAllocationType allocation_trait>
inline UInt ArrayDataLayer<T, allocation_trait>::getAllocatedSize() const {
return this->data_storage.capacity() / this->nb_component;
}
/* -------------------------------------------------------------------------- */
template <typename T, ArrayAllocationType allocation_trait>
inline UInt ArrayDataLayer<T, allocation_trait>::getMemorySize() const {
return this->data_storage.capacity() * sizeof(T);
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
template <typename T>
class ArrayDataLayer<T, ArrayAllocationType::_pod> : public ArrayBase {
public:
using value_type = T;
using reference = value_type &;
using pointer_type = value_type *;
using const_reference = const value_type &;
public:
~ArrayDataLayer() override { deallocate(); }
/// Allocation of a new vector
ArrayDataLayer(UInt size = 0, UInt nb_component = 1, const ID & id = "")
: ArrayBase(id) {
allocate(size, nb_component);
}
/// Allocation of a new vector with a default value
ArrayDataLayer(UInt size, UInt 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.storage(), this->size_ * this->nb_component, values);
}
/// Copy constructor (deep copy)
explicit ArrayDataLayer(const std::vector<value_type> & 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.storage(), 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(UInt size, UInt nb_component) {
if (size != 0) { // malloc can return a non NULL pointer in case size is 0
this->values = static_cast<T *>( // 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(UInt size, UInt 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 <template <typename> class C,
typename = std::enable_if_t<aka::is_tensor<C<T>>::value or
aka::is_tensor_proxy<C<T>>::value>>
inline void push_back(const C<T> & 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);
std::copy_n(new_elem.storage(), new_elem.size(),
values + this->nb_component * (this->size_ - 1));
}
/// changes the allocated size but not the size
virtual void reserve(UInt size, UInt new_size = UInt(-1)) {
UInt tmp_size = this->size_;
if (new_size != UInt(-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(UInt 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;
UInt 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<T *>( // NOLINT
realloc(this->values,
size_to_allocate * this->nb_component * sizeof(T)));
if (tmp_ptr == nullptr) {
- StaticMemory::getStaticMemory().printself(std::cerr);
-
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(UInt size, const T & val) {
UInt 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 UInt getMemorySize() const final {
return this->allocated_size * this->nb_component * sizeof(T);
}
/// Get the real size allocated in memory
inline UInt getAllocatedSize() const { return this->allocated_size; }
/// give the address of the memory allocated for this vector
T * storage() const { return values; };
protected:
/// allocation type agnostic data access
T * values{nullptr};
UInt allocated_size{0};
};
/* -------------------------------------------------------------------------- */
template <class T, bool is_scal>
inline auto Array<T, is_scal>::operator()(UInt i, UInt 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 <class T, bool is_scal>
inline auto Array<T, is_scal>::operator()(UInt i, UInt 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 <class T, bool is_scal>
inline auto Array<T, is_scal>::operator[](UInt 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 <class T, bool is_scal>
inline auto Array<T, is_scal>::operator[](UInt 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 <class T, bool is_scal> inline void Array<T, is_scal>::erase(UInt 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 (UInt j = 0; j < this->nb_component; ++j) {
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
this->values[i * this->nb_component + j] =
this->values[(this->size_ - 1) * this->nb_component + j];
}
}
this->resize(this->size_ - 1);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
/**
* 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 <class T, bool is_scal>
Array<T, is_scal> &
Array<T, is_scal>::operator-=(const Array<T, is_scal> & other) {
AKANTU_DEBUG_ASSERT((this->size_ == other.size_) &&
(this->nb_component == other.nb_component),
"The too array don't have the same sizes");
T * a = this->values;
T * b = other.storage();
for (UInt 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 <class T, bool is_scal>
Array<T, is_scal> &
Array<T, is_scal>::operator+=(const Array<T, is_scal> & other) {
AKANTU_DEBUG_ASSERT((this->size_ == other.size()) &&
(this->nb_component == other.nb_component),
"The too array don't have the same sizes");
T * a = this->values;
T * b = other.storage();
for (UInt 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 <class T, bool is_scal>
Array<T, is_scal> & Array<T, is_scal>::operator*=(const T & alpha) {
T * a = this->values;
for (UInt 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 <class T, bool is_scal>
bool Array<T, is_scal>::operator==(const Array<T, is_scal> & 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.storage()) {
return true;
}
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
return std::equal(this->values,
this->values + this->size_ * this->nb_component,
other.storage());
}
/* --------------------------------------------------------------------------
*/
template <class T, bool is_scal>
bool Array<T, is_scal>::operator!=(const Array<T, is_scal> & 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 <class T, bool is_scal>
template <template <typename> class C, typename>
inline void Array<T, is_scal>::set(const C<T> & vm) {
AKANTU_DEBUG_ASSERT(this->nb_component == vm.size(),
"The size of the object does not "
"match the number of components");
for (T * it = this->values;
it < this->values + this->nb_component * this->size_;
it += this->nb_component) {
std::copy_n(vm.storage(), this->nb_component, it);
}
}
/* --------------------------------------------------------------------------
*/
template <class T, bool is_scal>
void Array<T, is_scal>::append(const Array<T> & other) {
AKANTU_DEBUG_ASSERT(this->nb_component == other.nb_component,
"Cannot append an array with a "
"different number of component");
UInt old_size = this->size_;
this->resize(this->size_ + other.size());
T * tmp = this->values + this->nb_component * old_size;
std::copy_n(other.storage(), other.size() * this->nb_component, tmp);
}
/* --------------------------------------------------------------------------
*/
/* Functions Array<T, is_scal> */
/* --------------------------------------------------------------------------
*/
template <class T, bool is_scal>
Array<T, is_scal>::Array(UInt size, UInt nb_component, const ID & id)
: parent(size, nb_component, id) {}
template <>
inline Array<std::string, false>::Array(UInt size, UInt nb_component,
const ID & id)
: parent(size, nb_component, "", id) {}
/* --------------------------------------------------------------------------
*/
template <class T, bool is_scal>
Array<T, is_scal>::Array(UInt size, UInt nb_component, const_reference value,
const ID & id)
: parent(size, nb_component, value, id) {}
/* --------------------------------------------------------------------------
*/
template <class T, bool is_scal>
Array<T, is_scal>::Array(const Array & vect, const ID & id)
: parent(vect, id) {}
/* --------------------------------------------------------------------------
*/
template <class T, bool is_scal>
Array<T, is_scal> &
Array<T, is_scal>::operator=(const Array<T, is_scal> & other) {
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 <class T, bool is_scal>
Array<T, is_scal>::Array(const std::vector<T> & vect) : parent(vect) {}
/* --------------------------------------------------------------------------
*/
template <class T, bool is_scal> Array<T, is_scal>::~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 <class T, bool is_scal>
UInt Array<T, is_scal>::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 : UInt(-1);
}
/* --------------------------------------------------------------------------
*/
// template <class T, bool is_scal> UInt Array<T,
// is_scal>::find(T elem[]) const
// {
// AKANTU_DEBUG_IN();
// T * it = this->values;
// UInt i = 0;
// for (; i < this->size_; ++i) {
// if (*it == elem[0]) {
// T * cit = it;
// UInt c = 0;
// for (; (c < this->nb_component) && (*cit ==
// elem[c]); ++c, ++cit)
// ;
// if (c == this->nb_component) {
// AKANTU_DEBUG_OUT();
// return i;
// }
// }
// it += this->nb_component;
// }
// return UInt(-1);
// }
/* --------------------------------------------------------------------------
*/
template <class T, bool is_scal>
template <template <typename> class C, typename>
inline UInt Array<T, is_scal>::find(const C<T> & elem) {
AKANTU_DEBUG_ASSERT(elem.size() == this->nb_component,
"Cannot find an element with a wrong size ("
<< elem.size() << ") != " << this->nb_component);
return this->find(*elem.storage());
}
/* --------------------------------------------------------------------------
*/
/**
* 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 <class T, bool is_scal>
void Array<T, is_scal>::copy(const Array<T, is_scal> & 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.storage(), this->size_ * this->nb_component, this->values);
AKANTU_DEBUG_OUT();
}
/* --------------------------------------------------------------------------
*/
template <bool is_scal> class ArrayPrintHelper {
public:
template <typename T>
static void print_content(const Array<T> & vect, std::ostream & stream,
int indent) {
std::string space(indent, AKANTU_INDENT);
stream << space << " + values : {";
for (UInt i = 0; i < vect.size(); ++i) {
stream << "{";
for (UInt 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<false> {
public:
template <typename T>
static void print_content(__attribute__((unused)) const Array<T> & vect,
__attribute__((unused)) std::ostream & stream,
__attribute__((unused)) int indent) {}
};
/* --------------------------------------------------------------------------
*/
template <class T, bool is_scal>
void Array<T, is_scal>::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<T>(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<is_scal or std::is_enum<T>::value>::print_content(
*this, stream, indent);
}
stream << space << "]" << std::endl;
}
/* --------------------------------------------------------------------------
*/
/* Inline Functions ArrayBase */
/* --------------------------------------------------------------------------
*/
// inline bool ArrayBase::empty() { return (this->size_ ==
// 0); }
/* --------------------------------------------------------------------------
*/
/* Iterators */
/* --------------------------------------------------------------------------
*/
template <class T, bool is_scal>
template <class R, class daughter, class IR, bool is_tensor>
class Array<T, is_scal>::iterator_internal {
public:
using value_type = R;
using pointer = R *;
using reference = R &;
using const_reference = const R &;
using internal_value_type = IR;
using internal_pointer = IR *;
using difference_type = std::ptrdiff_t;
using iterator_category = std::random_access_iterator_tag;
static_assert(not is_tensor, "Cannot handle tensors");
public:
iterator_internal(pointer data = nullptr) : ret(data), initial(data){};
iterator_internal(const iterator_internal & it) = default;
iterator_internal(iterator_internal && it) noexcept = default;
virtual ~iterator_internal() = default;
inline iterator_internal & operator=(const iterator_internal & it) = default;
inline iterator_internal &
operator=(iterator_internal && it) noexcept = default;
UInt getCurrentIndex() { return (this->ret - this->initial); };
inline reference operator*() { return *ret; };
inline const_reference operator*() const { return *ret; };
inline pointer operator->() { return ret; };
inline daughter & operator++() {
++ret;
return static_cast<daughter &>(*this);
};
inline daughter & operator--() {
--ret;
return static_cast<daughter &>(*this);
};
inline daughter & operator+=(const UInt n) {
ret += n;
return static_cast<daughter &>(*this);
}
inline daughter & operator-=(const UInt n) {
ret -= n;
return static_cast<daughter &>(*this);
}
inline reference operator[](const UInt n) { return ret[n]; }
inline bool operator==(const iterator_internal & other) const {
return ret == other.ret;
}
inline bool operator!=(const iterator_internal & other) const {
return ret != other.ret;
}
inline bool operator<(const iterator_internal & other) const {
return ret < other.ret;
}
inline bool operator<=(const iterator_internal & other) const {
return ret <= other.ret;
}
inline bool operator>(const iterator_internal & other) const {
return ret > other.ret;
}
inline bool operator>=(const iterator_internal & other) const {
return ret >= other.ret;
}
inline daughter operator-(difference_type n) { return daughter(ret - n); }
inline daughter operator+(difference_type n) { return daughter(ret + n); }
inline difference_type operator-(const iterator_internal & b) {
return ret - b.ret;
}
inline pointer data() const { return ret; }
protected:
pointer ret{nullptr};
pointer initial{nullptr};
};
/* --------------------------------------------------------------------------
*/
/**
* Specialization for scalar types
*/
template <class T, bool is_scal>
template <class R, class daughter, class IR>
class Array<T, is_scal>::iterator_internal<R, daughter, IR, true> {
public:
using value_type = R;
using pointer = R *;
using reference = R &;
using proxy = typename R::proxy;
using const_proxy = const typename R::proxy;
using const_reference = const R &;
using internal_value_type = IR;
using internal_pointer = IR *;
using difference_type = std::ptrdiff_t;
using iterator_category = std::random_access_iterator_tag;
using pointer_type = typename Array<T, is_scal>::pointer_type;
public:
iterator_internal() = default;
iterator_internal(pointer_type data, UInt _offset)
: _offset(_offset), initial(data), ret(nullptr), ret_ptr(data) {
AKANTU_ERROR("The constructor should never be called "
"it is just an ugly trick...");
}
iterator_internal(std::unique_ptr<internal_value_type> && wrapped)
: _offset(wrapped->size()), initial(wrapped->storage()),
ret(std::move(wrapped)), ret_ptr(ret->storage()) {}
iterator_internal(const iterator_internal & it) {
if (this != &it) {
this->_offset = it._offset;
this->initial = it.initial;
this->ret_ptr = it.ret_ptr;
this->ret = std::make_unique<internal_value_type>(*it.ret, false);
}
}
iterator_internal(iterator_internal && it) noexcept = default;
virtual ~iterator_internal() = default;
inline iterator_internal & operator=(const iterator_internal & it) {
if (this != &it) {
this->_offset = it._offset;
this->initial = it.initial;
this->ret_ptr = it.ret_ptr;
if (this->ret) {
this->ret->shallowCopy(*it.ret);
} else {
this->ret = std::make_unique<internal_value_type>(*it.ret, false);
}
}
return *this;
}
inline iterator_internal &
operator=(iterator_internal && it) noexcept = default;
UInt getCurrentIndex() {
return (this->ret_ptr - this->initial) / this->_offset;
};
inline reference operator*() {
ret->values = ret_ptr;
return *ret;
};
inline const_reference operator*() const {
ret->values = ret_ptr;
return *ret;
};
inline pointer operator->() {
ret->values = ret_ptr;
return ret.get();
};
inline daughter & operator++() {
ret_ptr += _offset;
return static_cast<daughter &>(*this);
};
inline daughter & operator--() {
ret_ptr -= _offset;
return static_cast<daughter &>(*this);
};
inline daughter & operator+=(const UInt n) {
ret_ptr += _offset * n;
return static_cast<daughter &>(*this);
}
inline daughter & operator-=(const UInt n) {
ret_ptr -= _offset * n;
return static_cast<daughter &>(*this);
}
inline proxy operator[](const UInt n) {
ret->values = ret_ptr + n * _offset;
return proxy(*ret);
}
inline const_proxy operator[](const UInt n) const { // NOLINT
ret->values = ret_ptr + n * _offset;
return const_proxy(*ret);
}
inline bool operator==(const iterator_internal & other) const {
return this->ret_ptr == other.ret_ptr;
}
inline bool operator!=(const iterator_internal & other) const {
return this->ret_ptr != other.ret_ptr;
}
inline bool operator<(const iterator_internal & other) const {
return this->ret_ptr < other.ret_ptr;
}
inline bool operator<=(const iterator_internal & other) const {
return this->ret_ptr <= other.ret_ptr;
}
inline bool operator>(const iterator_internal & other) const {
return this->ret_ptr > other.ret_ptr;
}
inline bool operator>=(const iterator_internal & other) const {
return this->ret_ptr >= other.ret_ptr;
}
inline daughter operator+(difference_type n) {
daughter tmp(static_cast<daughter &>(*this));
tmp += n;
return tmp;
}
inline daughter operator-(difference_type n) {
daughter tmp(static_cast<daughter &>(*this));
tmp -= n;
return tmp;
}
inline difference_type operator-(const iterator_internal & b) {
return (this->ret_ptr - b.ret_ptr) / _offset;
}
inline pointer_type data() const { return ret_ptr; }
inline difference_type offset() const { return _offset; }
protected:
UInt _offset{0};
pointer_type initial{nullptr};
std::unique_ptr<internal_value_type> ret{nullptr};
pointer_type ret_ptr{nullptr};
};
/* -------------------------------------------------------------------------- */
/* Iterators */
/* -------------------------------------------------------------------------- */
template <class T, bool is_scal>
template <typename R>
class Array<T, is_scal>::const_iterator
: public iterator_internal<const R, Array<T, is_scal>::const_iterator<R>,
R> {
public:
using parent = iterator_internal<const R, const_iterator, R>;
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:
~const_iterator() override = default;
const_iterator() = default;
const_iterator(const const_iterator & it) = default;
const_iterator(const_iterator && it) noexcept = default;
const_iterator & operator=(const const_iterator & it) = default;
const_iterator & operator=(const_iterator && it) noexcept = default;
template <typename P,
typename = std::enable_if_t<not aka::is_tensor<P>::value>>
const_iterator(P * data) : parent(data) {}
template <typename UP_P, typename = std::enable_if_t<aka::is_tensor<
typename UP_P::element_type>::value>>
const_iterator(UP_P && tensor) : parent(std::forward<UP_P>(tensor)) {}
};
/* -------------------------------------------------------------------------- */
template <class T, class R, bool is_tensor_ = aka::is_tensor<R>::value>
struct ConstConverterIteratorHelper {
using const_iterator = typename Array<T>::template const_iterator<R>;
using iterator = typename Array<T>::template iterator<R>;
static inline const_iterator convert(const iterator & it) {
return const_iterator(std::unique_ptr<R>(new R(*it, false)));
}
};
template <class T, class R> struct ConstConverterIteratorHelper<T, R, false> {
using const_iterator = typename Array<T>::template const_iterator<R>;
using iterator = typename Array<T>::template iterator<R>;
static inline const_iterator convert(const iterator & it) {
return const_iterator(it.data());
}
};
/* -------------------------------------------------------------------------- */
template <class T, bool is_scal>
template <typename R>
class Array<T, is_scal>::iterator
: public iterator_internal<R, Array<T, is_scal>::iterator<R>> {
public:
using parent = iterator_internal<R, 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:
~iterator() override = default;
iterator() = default;
iterator(const iterator & it) = default;
iterator(iterator && it) noexcept = default;
iterator & operator=(const iterator & it) = default;
iterator & operator=(iterator && it) noexcept = default;
template <typename P,
typename = std::enable_if_t<not aka::is_tensor<P>::value>>
iterator(P * data) : parent(data) {}
template <typename UP_P, typename = std::enable_if_t<aka::is_tensor<
typename UP_P::element_type>::value>>
iterator(UP_P && tensor) : parent(std::forward<UP_P>(tensor)) {}
operator const_iterator<R>() {
return ConstConverterIteratorHelper<T, R>::convert(*this);
}
};
/* -------------------------------------------------------------------------- */
/* Begin/End functions implementation */
/* -------------------------------------------------------------------------- */
namespace detail {
template <class Tuple, size_t... Is>
constexpr auto take_front_impl(Tuple && t,
std::index_sequence<Is...> /*idxs*/) {
return std::make_tuple(std::get<Is>(std::forward<Tuple>(t))...);
}
template <size_t N, class Tuple> constexpr auto take_front(Tuple && t) {
return take_front_impl(std::forward<Tuple>(t),
std::make_index_sequence<N>{});
}
template <typename... V> constexpr auto product_all(V &&... v) {
std::common_type_t<int, V...> result = 1;
(void)std::initializer_list<int>{(result *= v, 0)...};
return result;
}
template <typename... T> std::string to_string_all(T &&... t) {
if (sizeof...(T) == 0) {
return "";
}
std::stringstream ss;
bool noComma = true;
ss << "(";
(void)std::initializer_list<bool>{
(ss << (noComma ? "" : ", ") << t, noComma = false)...};
ss << ")";
return ss.str();
}
template <std::size_t N> struct InstantiationHelper {
template <typename type, typename T, typename... Ns>
static auto instantiate(T && data, Ns... ns) {
return std::make_unique<type>(data, ns...);
}
};
template <> struct InstantiationHelper<0> {
template <typename type, typename T> static auto instantiate(T && data) {
return data;
}
};
template <typename Arr, typename T, typename... Ns>
decltype(auto) get_iterator(Arr && array, T * data, Ns &&... ns) {
using type = IteratorHelper_t<sizeof...(Ns) - 1, T>;
using array_type = std::decay_t<Arr>;
using iterator =
std::conditional_t<std::is_const<std::remove_reference_t<Arr>>::value,
typename array_type::template const_iterator<type>,
typename array_type::template iterator<type>>;
static_assert(sizeof...(Ns), "You should provide a least one size");
if (array.getNbComponent() * array.size() !=
product_all(std::forward<Ns>(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...));
}
auto && wrapped = aka::apply(
[&](auto... n) {
return InstantiationHelper<sizeof...(n)>::template instantiate<type>(
data, n...);
},
take_front<sizeof...(Ns) - 1>(std::make_tuple(ns...)));
return iterator(std::move(wrapped));
}
} // namespace detail
/* -------------------------------------------------------------------------- */
template <class T, bool is_scal>
template <typename... Ns>
inline decltype(auto) Array<T, is_scal>::begin(Ns &&... ns) {
return detail::get_iterator(*this, this->values, std::forward<Ns>(ns)...,
this->size_);
}
template <class T, bool is_scal>
template <typename... Ns>
inline decltype(auto) Array<T, is_scal>::end(Ns &&... ns) {
return detail::get_iterator(*this,
this->values + this->nb_component * this->size_,
std::forward<Ns>(ns)..., this->size_);
}
template <class T, bool is_scal>
template <typename... Ns>
inline decltype(auto) Array<T, is_scal>::begin(Ns &&... ns) const {
return detail::get_iterator(*this, this->values, std::forward<Ns>(ns)...,
this->size_);
}
template <class T, bool is_scal>
template <typename... Ns>
inline decltype(auto) Array<T, is_scal>::end(Ns &&... ns) const {
return detail::get_iterator(*this,
this->values + this->nb_component * this->size_,
std::forward<Ns>(ns)..., this->size_);
}
template <class T, bool is_scal>
template <typename... Ns>
inline decltype(auto) Array<T, is_scal>::begin_reinterpret(Ns &&... ns) {
return detail::get_iterator(*this, this->values, std::forward<Ns>(ns)...);
}
template <class T, bool is_scal>
template <typename... Ns>
inline decltype(auto) Array<T, is_scal>::end_reinterpret(Ns &&... ns) {
return detail::get_iterator(
*this, this->values + detail::product_all(std::forward<Ns>(ns)...),
std::forward<Ns>(ns)...);
}
template <class T, bool is_scal>
template <typename... Ns>
inline decltype(auto) Array<T, is_scal>::begin_reinterpret(Ns &&... ns) const {
return detail::get_iterator(*this, this->values, std::forward<Ns>(ns)...);
}
template <class T, bool is_scal>
template <typename... Ns>
inline decltype(auto) Array<T, is_scal>::end_reinterpret(Ns &&... ns) const {
return detail::get_iterator(
*this, this->values + detail::product_all(std::forward<Ns>(ns)...),
std::forward<Ns>(ns)...);
}
/* -------------------------------------------------------------------------- */
/* Views */
/* -------------------------------------------------------------------------- */
namespace detail {
template <typename Array, typename... Ns> class ArrayView {
using tuple = std::tuple<Ns...>;
public:
~ArrayView() = default;
ArrayView(Array && array, Ns... ns) noexcept
: array(array), sizes(std::move(ns)...) {}
ArrayView(const ArrayView & array_view) = default;
ArrayView & operator=(const ArrayView & array_view) = default;
ArrayView(ArrayView && array_view) noexcept = default;
ArrayView & operator=(ArrayView && array_view) noexcept = default;
decltype(auto) begin() {
return aka::apply(
[&](auto &&... ns) { return array.get().begin_reinterpret(ns...); },
sizes);
}
decltype(auto) begin() const {
return aka::apply(
[&](auto &&... ns) { return array.get().begin_reinterpret(ns...); },
sizes);
}
decltype(auto) end() {
return aka::apply(
[&](auto &&... ns) { return array.get().end_reinterpret(ns...); },
sizes);
}
decltype(auto) end() const {
return aka::apply(
[&](auto &&... ns) { return array.get().end_reinterpret(ns...); },
sizes);
}
decltype(auto) size() const {
return std::get<std::tuple_size<tuple>::value - 1>(sizes);
}
decltype(auto) dims() const { return std::tuple_size<tuple>::value - 1; }
private:
std::reference_wrapper<std::remove_reference_t<Array>> array;
tuple sizes;
};
} // namespace detail
/* -------------------------------------------------------------------------- */
template <typename Array, typename... Ns>
decltype(auto) make_view(Array && array, const Ns... ns) {
static_assert(aka::conjunction<std::is_integral<std::decay_t<Ns>>...>::value,
"Ns should be integral types");
AKANTU_DEBUG_ASSERT((detail::product_all(ns...) != 0),
"You must specify non zero dimensions");
auto size = std::forward<decltype(array)>(array).size() *
std::forward<decltype(array)>(array).getNbComponent() /
detail::product_all(ns...);
return detail::ArrayView<Array, std::common_type_t<size_t, Ns>...,
std::common_type_t<size_t, decltype(size)>>(
std::forward<Array>(array), std::move(ns)..., size);
}
/* --------------------------------------------------------------------------
*/
template <class T, bool is_scal>
template <typename R>
inline typename Array<T, is_scal>::template iterator<R>
Array<T, is_scal>::erase(const iterator<R> & it) {
T * curr = it.data();
UInt pos = (curr - this->values) / this->nb_component;
erase(pos);
iterator<R> rit = it;
return --rit;
}
} // namespace akantu
#endif /* AKANTU_AKA_ARRAY_TMPL_HH_ */
diff --git a/src/common/aka_common.cc b/src/common/aka_common.cc
index 392fc587e..4687f3fa6 100644
--- a/src/common/aka_common.cc
+++ b/src/common/aka_common.cc
@@ -1,168 +1,153 @@
/**
* @file aka_common.cc
*
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Mon Jun 14 2010
* @date last modification: Mon Feb 05 2018
*
* @brief Initialization of global variables
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_common.hh"
#include "aka_random_generator.hh"
-#include "aka_static_memory.hh"
#include "communicator.hh"
#include "cppargparse.hh"
#include "parser.hh"
#include "communication_tag.hh"
/* -------------------------------------------------------------------------- */
#include <cmath>
#include <ctime>
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
void initialize(int & argc, char **& argv) {
AKANTU_DEBUG_IN();
initialize("", argc, argv);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void initialize(const std::string & input_file, int & argc, char **& argv) {
AKANTU_DEBUG_IN();
- StaticMemory::getStaticMemory();
Communicator & comm = Communicator::getStaticCommunicator(argc, argv);
Tag::setMaxTag(comm.getMaxTag());
debug::debugger.setParallelContext(comm.whoAmI(), comm.getNbProc());
debug::setDebugLevel(dblError);
static_argparser.setParallelContext(comm.whoAmI(), comm.getNbProc());
static_argparser.setExternalExitFunction(debug::exit);
static_argparser.addArgument("--aka_input_file", "Akantu's input file", 1,
cppargparse::_string, std::string());
static_argparser.addArgument(
"--aka_debug_level",
std::string("Akantu's overall debug level") +
std::string(" (0: error, 1: exceptions, 4: warnings, 5: info, ..., "
"100: dump") +
std::string(" more info on levels can be foind in aka_error.hh)"),
1, cppargparse::_integer, (long int)(dblWarning));
static_argparser.addArgument(
"--aka_print_backtrace",
"Should Akantu print a backtrace in case of error", 0,
cppargparse::_boolean, false, true);
static_argparser.addArgument("--aka_seed", "The seed to use on prank 0", 1,
cppargparse::_integer);
static_argparser.parse(argc, argv, cppargparse::_remove_parsed);
std::string infile = static_argparser["aka_input_file"];
if (infile.empty()) {
infile = input_file;
}
debug::debugger.printBacktrace(static_argparser["aka_print_backtrace"]);
if (not infile.empty()) {
readInputFile(infile);
}
long int seed;
if (static_argparser.has("aka_seed")) {
seed = static_argparser["aka_seed"];
} else {
seed =
static_parser.getParameter("seed", time(nullptr), _ppsc_current_scope);
}
seed *= (comm.whoAmI() + 1);
RandomGenerator<UInt>::seed(seed);
long int dbl_level = static_argparser["aka_debug_level"];
debug::setDebugLevel(DebugLevel(dbl_level));
AKANTU_DEBUG_INFO("Random seed set to " << seed);
std::atexit(finalize);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
-void finalize() {
- AKANTU_DEBUG_IN();
-
- // if (StaticCommunicator::isInstantiated()) {
- // StaticCommunicator & comm = StaticCommunicator::getStaticCommunicator();
- // delete &comm;
- // }
-
- if (StaticMemory::isInstantiated()) {
- delete &(StaticMemory::getStaticMemory());
- }
-
- AKANTU_DEBUG_OUT();
-}
+void finalize() { }
/* -------------------------------------------------------------------------- */
void readInputFile(const std::string & input_file) {
static_parser.parse(input_file);
}
/* -------------------------------------------------------------------------- */
cppargparse::ArgumentParser & getStaticArgumentParser() {
return static_argparser;
}
/* -------------------------------------------------------------------------- */
Parser & getStaticParser() { return static_parser; }
/* -------------------------------------------------------------------------- */
const ParserSection & getUserParser() {
return *(static_parser.getSubSections(ParserType::_user).first);
}
std::unique_ptr<Communicator> Communicator::static_communicator;
std::ostream & operator<<(std::ostream & stream, NodeFlag flag) {
using under = std::underlying_type_t<NodeFlag>;
auto digits = static_cast<int>(std::log(std::numeric_limits<under>::max() + 1) / std::log(16));
std::ios_base::fmtflags ff;
ff = stream.flags();
auto value = static_cast<std::common_type_t<under, unsigned int>>(flag);
stream << "0x" << std::hex << std::setw(digits) << std::setfill('0') << value;
stream.flags(ff);
return stream;
}
} // namespace akantu
diff --git a/src/common/aka_common.hh b/src/common/aka_common.hh
index b1c273b8a..d7d19e109 100644
--- a/src/common/aka_common.hh
+++ b/src/common/aka_common.hh
@@ -1,676 +1,675 @@
/**
* @file aka_common.hh
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Mon Jun 14 2010
* @date last modification: Mon Feb 12 2018
*
* @brief common type descriptions for 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_COMMON_HH_
#define AKANTU_COMMON_HH_
#include "aka_compatibilty_with_cpp_standard.hh"
/* -------------------------------------------------------------------------- */
#if defined(WIN32)
#define __attribute__(x)
#endif
/* -------------------------------------------------------------------------- */
#include "aka_config.hh"
#include "aka_error.hh"
#include "aka_safe_enum.hh"
/* -------------------------------------------------------------------------- */
#include <boost/preprocessor.hpp>
#include <limits>
#include <list>
#include <memory>
#include <string>
#include <type_traits>
#include <unordered_map>
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
/* Constants */
/* -------------------------------------------------------------------------- */
namespace {
[[gnu::unused]] constexpr UInt _all_dimensions{
std::numeric_limits<UInt>::max()};
#ifdef AKANTU_NDEBUG
[[gnu::unused]] constexpr Real REAL_INIT_VALUE{0.};
#else
[[gnu::unused]] constexpr Real REAL_INIT_VALUE{
std::numeric_limits<Real>::quiet_NaN()};
#endif
} // namespace
/* -------------------------------------------------------------------------- */
/* Common types */
/* -------------------------------------------------------------------------- */
using ID = std::string;
-using MemoryID = UInt;
} // namespace akantu
/* -------------------------------------------------------------------------- */
#include "aka_enum_macros.hh"
/* -------------------------------------------------------------------------- */
#include "aka_element_classes_info.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
/* Mesh/FEM/Model types */
/* -------------------------------------------------------------------------- */
/// small help to use names for directions
enum SpatialDirection { _x = 0, _y = 1, _z = 2 };
/// enum MeshIOType type of mesh reader/writer
enum MeshIOType {
_miot_auto, ///< Auto guess of the reader to use based on the extension
_miot_gmsh, ///< Gmsh files
_miot_gmsh_struct, ///< Gsmh reader with reintpretation of elements has
/// structures elements
_miot_diana, ///< TNO Diana mesh format
_miot_abaqus ///< Abaqus mesh format
};
/// enum MeshEventHandlerPriority defines relative order of execution of
/// events
enum EventHandlerPriority {
_ehp_highest = 0,
_ehp_mesh = 5,
_ehp_fe_engine = 9,
_ehp_synchronizer = 10,
_ehp_dof_manager = 20,
_ehp_model = 94,
_ehp_non_local_manager = 100,
_ehp_lowest = 100
};
#if !defined(DOXYGEN)
// clang-format off
#define AKANTU_MODEL_TYPES \
(model) \
(solid_mechanics_model) \
(solid_mechanics_model_cohesive) \
(heat_transfer_model) \
(structural_mechanics_model) \
(embedded_model) \
(contact_mechanics_model) \
(coupler_solid_contact) \
(coupler_solid_cohesive_contact)
// clang-format on
/// enum ModelType defines which type of physics is solved
AKANTU_CLASS_ENUM_DECLARE(ModelType, AKANTU_MODEL_TYPES)
AKANTU_CLASS_ENUM_OUTPUT_STREAM(ModelType, AKANTU_MODEL_TYPES)
AKANTU_CLASS_ENUM_INPUT_STREAM(ModelType, AKANTU_MODEL_TYPES)
#else
enum class ModelType {
model,
solid_mechanics_model,
solid_mechanics_model_cohesive,
heat_transfer_model,
structural_mechanics_model,
embedded_model,
};
#endif
/// enum AnalysisMethod type of solving method used to solve the equation of
/// motion
enum AnalysisMethod {
_static = 0,
_implicit_dynamic = 1,
_explicit_lumped_mass = 2,
_explicit_lumped_capacity = 2,
_explicit_consistent_mass = 3,
_explicit_contact = 4,
_implicit_contact = 5,
_explicit_dynamic_contact = 6
};
/// enum DOFSupportType defines which kind of dof that can exists
enum DOFSupportType { _dst_nodal, _dst_generic };
#if !defined(DOXYGEN)
// clang-format off
#define AKANTU_NON_LINEAR_SOLVER_TYPES \
(linear) \
(newton_raphson) \
(newton_raphson_modified) \
(lumped) \
(gmres) \
(bfgs) \
(cg) \
(newton_raphson_contact) \
(auto)
// clang-format on
AKANTU_CLASS_ENUM_DECLARE(NonLinearSolverType, AKANTU_NON_LINEAR_SOLVER_TYPES)
AKANTU_CLASS_ENUM_OUTPUT_STREAM(NonLinearSolverType,
AKANTU_NON_LINEAR_SOLVER_TYPES)
AKANTU_CLASS_ENUM_INPUT_STREAM(NonLinearSolverType,
AKANTU_NON_LINEAR_SOLVER_TYPES)
#else
/// Type of non linear resolution available in akantu
enum class NonLinearSolverType {
_linear, ///< No non linear convergence loop
_newton_raphson, ///< Regular Newton-Raphson
_newton_raphson_modified, ///< Newton-Raphson with initial tangent
_lumped, ///< Case of lumped mass or equivalent matrix
_gmres,
_bfgs,
_cg,
_newton_raphson_contact, ///< Regular Newton-Raphson modified
/// for contact problem
_auto, ///< This will take a default value that make sense in case of
/// model::getNewSolver
};
#endif
#if !defined(DOXYGEN)
// clang-format off
#define AKANTU_TIME_STEP_SOLVER_TYPE \
(static) \
(dynamic) \
(dynamic_lumped) \
(not_defined)
// clang-format on
AKANTU_CLASS_ENUM_DECLARE(TimeStepSolverType, AKANTU_TIME_STEP_SOLVER_TYPE)
AKANTU_CLASS_ENUM_OUTPUT_STREAM(TimeStepSolverType,
AKANTU_TIME_STEP_SOLVER_TYPE)
AKANTU_CLASS_ENUM_INPUT_STREAM(TimeStepSolverType, AKANTU_TIME_STEP_SOLVER_TYPE)
#else
/// Type of time stepping solver
enum class TimeStepSolverType {
_static, ///< Static solution
_dynamic, ///< Dynamic solver
_dynamic_lumped, ///< Dynamic solver with lumped mass
_not_defined, ///< For not defined cases
};
#endif
#if !defined(DOXYGEN)
// clang-format off
#define AKANTU_INTEGRATION_SCHEME_TYPE \
(pseudo_time) \
(forward_euler) \
(trapezoidal_rule_1) \
(backward_euler) \
(central_difference) \
(fox_goodwin) \
(trapezoidal_rule_2) \
(linear_acceleration) \
(newmark_beta) \
(generalized_trapezoidal)
// clang-format on
AKANTU_CLASS_ENUM_DECLARE(IntegrationSchemeType, AKANTU_INTEGRATION_SCHEME_TYPE)
AKANTU_CLASS_ENUM_OUTPUT_STREAM(IntegrationSchemeType,
AKANTU_INTEGRATION_SCHEME_TYPE)
AKANTU_CLASS_ENUM_INPUT_STREAM(IntegrationSchemeType,
AKANTU_INTEGRATION_SCHEME_TYPE)
#else
/// Type of integration scheme
enum class IntegrationSchemeType {
_pseudo_time, ///< Pseudo Time
_forward_euler, ///< GeneralizedTrapezoidal(0)
_trapezoidal_rule_1, ///< GeneralizedTrapezoidal(1/2)
_backward_euler, ///< GeneralizedTrapezoidal(1)
_central_difference, ///< NewmarkBeta(0, 1/2)
_fox_goodwin, ///< NewmarkBeta(1/6, 1/2)
_trapezoidal_rule_2, ///< NewmarkBeta(1/2, 1/2)
_linear_acceleration, ///< NewmarkBeta(1/3, 1/2)
_newmark_beta, ///< generic NewmarkBeta with user defined
/// alpha and beta
_generalized_trapezoidal ///< generic GeneralizedTrapezoidal with user
/// defined alpha
};
#endif
#if !defined(DOXYGEN)
// clang-format off
#define AKANTU_SOLVE_CONVERGENCE_CRITERIA \
(residual) \
(solution) \
(residual_mass_wgh)
// clang-format on
AKANTU_CLASS_ENUM_DECLARE(SolveConvergenceCriteria,
AKANTU_SOLVE_CONVERGENCE_CRITERIA)
AKANTU_CLASS_ENUM_OUTPUT_STREAM(SolveConvergenceCriteria,
AKANTU_SOLVE_CONVERGENCE_CRITERIA)
AKANTU_CLASS_ENUM_INPUT_STREAM(SolveConvergenceCriteria,
AKANTU_SOLVE_CONVERGENCE_CRITERIA)
#else
/// enum SolveConvergenceCriteria different convergence criteria
enum class SolveConvergenceCriteria {
_residual, ///< Use residual to test the convergence
_solution, ///< Use solution to test the convergence
_residual_mass_wgh ///< Use residual weighted by inv. nodal mass to
///< testb
};
#endif
/// enum CohesiveMethod type of insertion of cohesive elements
enum CohesiveMethod { _intrinsic, _extrinsic };
/// @enum MatrixType type of sparse matrix used
enum MatrixType { _unsymmetric, _symmetric, _mt_not_defined };
/// @enum Type of contact detection
enum DetectionType { _explicit, _implicit};
/// @enum no contact or stick or slip state
enum ContactState {
_no_contact = 0,
_stick = 1,
_slip = 2,
};
/* -------------------------------------------------------------------------- */
/* Ghosts handling */
/* -------------------------------------------------------------------------- */
/// @enum CommunicatorType type of communication method to use
enum CommunicatorType { _communicator_mpi, _communicator_dummy };
#if !defined(DOXYGEN)
// clang-format off
#define AKANTU_SYNCHRONIZATION_TAG \
(whatever) \
(update) \
(ask_nodes) \
(size) \
(smm_mass) \
(smm_for_gradu) \
(smm_boundary) \
(smm_uv) \
(smm_res) \
(smm_init_mat) \
(smm_stress) \
(smmc_facets) \
(smmc_facets_conn) \
(smmc_facets_stress) \
(smmc_damage) \
(giu_global_conn) \
(ce_groups) \
(ce_insertion_order) \
(gm_clusters) \
(htm_temperature) \
(htm_gradient_temperature) \
(htm_phi) \
(htm_gradient_phi) \
(mnl_for_average) \
(mnl_weight) \
(nh_criterion) \
(test) \
(user_1) \
(user_2) \
(material_id) \
(for_dump) \
(cf_nodal) \
(cf_incr) \
(solver_solution)
// clang-format on
AKANTU_CLASS_ENUM_DECLARE(SynchronizationTag, AKANTU_SYNCHRONIZATION_TAG)
AKANTU_CLASS_ENUM_OUTPUT_STREAM(SynchronizationTag, AKANTU_SYNCHRONIZATION_TAG)
#else
/// @enum SynchronizationTag type of synchronizations
enum class SynchronizationTag {
//--- Generic tags ---
_whatever,
_update,
_ask_nodes,
_size,
//--- SolidMechanicsModel tags ---
_smm_mass, ///< synchronization of the SolidMechanicsModel.mass
_smm_for_gradu, ///< synchronization of the
/// SolidMechanicsModel.displacement
_smm_boundary, ///< synchronization of the boundary, forces, velocities
/// and displacement
_smm_uv, ///< synchronization of the nodal velocities and displacement
_smm_res, ///< synchronization of the nodal residual
_smm_init_mat, ///< synchronization of the data to initialize materials
_smm_stress, ///< synchronization of the stresses to compute the
///< internal
/// forces
_smmc_facets, ///< synchronization of facet data to setup facet synch
_smmc_facets_conn, ///< synchronization of facet global connectivity
_smmc_facets_stress, ///< synchronization of facets' stress to setup
///< facet
/// synch
_smmc_damage, ///< synchronization of damage
// --- GlobalIdsUpdater tags ---
_giu_global_conn, ///< synchronization of global connectivities
// --- CohesiveElementInserter tags ---
_ce_groups, ///< synchronization of cohesive element insertion depending
/// on facet groups
_ce_insertion_order, ///< synchronization of the order of insertion of
- ///cohesive elements
+ /// cohesive elements
// --- GroupManager tags ---
_gm_clusters, ///< synchronization of clusters
// --- HeatTransfer tags ---
_htm_temperature, ///< synchronization of the nodal temperature
_htm_gradient_temperature, ///< synchronization of the element gradient
/// temperature
// --- LevelSet tags ---
_htm_phi, ///< synchronization of the nodal level set value phi
_htm_gradient_phi, ///< synchronization of the element gradient phi
_pfm_damage,
_pfm_gradient_damage,
//--- Material non local ---
_mnl_for_average, ///< synchronization of data to average in non local
/// material
_mnl_weight, ///< synchronization of data for the weight computations
// --- NeighborhoodSynchronization tags ---
_nh_criterion,
// --- General tags ---
_test, ///< Test tag
_user_1, ///< tag for user simulations
_user_2, ///< tag for user simulations
_material_id, ///< synchronization of the material ids
_for_dump, ///< everything that needs to be synch before dump
// --- Contact & Friction ---
_cf_nodal, ///< synchronization of disp, velo, and current position
_cf_incr, ///< synchronization of increment
// --- Solver tags ---
_solver_solution ///< synchronization of the solution obained with the
/// PETSc solver
};
#endif
/// @enum GhostType type of ghost
enum GhostType {
_not_ghost = 0,
_ghost = 1,
_casper // not used but a real cute ghost
};
/// Define the flag that can be set to a node
enum class NodeFlag : std::uint8_t {
_normal = 0x00,
_distributed = 0x01,
_master = 0x03,
_slave = 0x05,
_pure_ghost = 0x09,
_shared_mask = 0x0F,
_periodic = 0x10,
_periodic_master = 0x30,
_periodic_slave = 0x50,
_periodic_mask = 0xF0,
_local_master_mask = 0xCC, // ~(_master & _periodic_mask)
};
inline NodeFlag operator&(const NodeFlag & a, const NodeFlag & b) {
using under = std::underlying_type_t<NodeFlag>;
return NodeFlag(under(a) & under(b));
}
inline NodeFlag operator|(const NodeFlag & a, const NodeFlag & b) {
using under = std::underlying_type_t<NodeFlag>;
return NodeFlag(under(a) | under(b));
}
inline NodeFlag & operator|=(NodeFlag & a, const NodeFlag & b) {
a = a | b;
return a;
}
inline NodeFlag & operator&=(NodeFlag & a, const NodeFlag & b) {
a = a & b;
return a;
}
inline NodeFlag operator~(const NodeFlag & a) {
using under = std::underlying_type_t<NodeFlag>;
return NodeFlag(~under(a));
}
std::ostream & operator<<(std::ostream & stream, NodeFlag flag);
} // namespace akantu
AKANTU_ENUM_HASH(GhostType)
namespace akantu {
/* -------------------------------------------------------------------------- */
struct GhostType_def {
using type = GhostType;
static const type _begin_ = _not_ghost;
static const type _end_ = _casper;
};
using ghost_type_t = safe_enum<GhostType_def>;
namespace {
constexpr ghost_type_t ghost_types{_casper};
}
/// standard output stream operator for GhostType
// inline std::ostream & operator<<(std::ostream & stream, GhostType type);
/* -------------------------------------------------------------------------- */
/* Global defines */
/* -------------------------------------------------------------------------- */
#define AKANTU_MIN_ALLOCATION 2000
#define AKANTU_INDENT ' '
#define AKANTU_INCLUDE_INLINE_IMPL
/* -------------------------------------------------------------------------- */
#define AKANTU_SET_MACRO(name, variable, type) \
inline void set##name(type variable) { this->variable = variable; }
#define AKANTU_GET_MACRO(name, variable, type) \
inline type get##name() const { return variable; }
#define AKANTU_GET_MACRO_NOT_CONST(name, variable, type) \
inline type get##name() { return variable; }
#define AKANTU_GET_MACRO_DEREF_PTR(name, ptr) \
inline const auto & get##name() const { \
if (not(ptr)) { \
AKANTU_EXCEPTION("The member " << #ptr << " is not initialized"); \
} \
return (*(ptr)); \
}
#define AKANTU_GET_MACRO_DEREF_PTR_NOT_CONST(name, ptr) \
inline auto & get##name() { \
if (not(ptr)) { \
AKANTU_EXCEPTION("The member " << #ptr << " is not initialized"); \
} \
return (*(ptr)); \
}
#define AKANTU_GET_MACRO_BY_SUPPORT_TYPE(name, variable, type, support, con) \
inline con Array<type> & get##name(const support & el_type, \
GhostType ghost_type = _not_ghost) \
con { /* NOLINT */ \
return variable(el_type, ghost_type); \
} // NOLINT
#define AKANTU_GET_MACRO_BY_ELEMENT_TYPE(name, variable, type) \
AKANTU_GET_MACRO_BY_SUPPORT_TYPE(name, variable, type, ElementType, )
#define AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(name, variable, type) \
AKANTU_GET_MACRO_BY_SUPPORT_TYPE(name, variable, type, ElementType, const)
#define AKANTU_GET_MACRO_BY_GEOMETRIE_TYPE(name, variable, type) \
AKANTU_GET_MACRO_BY_SUPPORT_TYPE(name, variable, type, GeometricalType, )
#define AKANTU_GET_MACRO_BY_GEOMETRIE_TYPE_CONST(name, variable, type) \
AKANTU_GET_MACRO_BY_SUPPORT_TYPE(name, variable, type, GeometricalType, const)
/* -------------------------------------------------------------------------- */
/// initialize the static part of akantu
void initialize(int & argc, char **& argv);
/// initialize the static part of akantu and read the global input_file
void initialize(const std::string & input_file, int & argc, char **& argv);
/* -------------------------------------------------------------------------- */
/// finilize correctly akantu and clean the memory
void finalize();
/* -------------------------------------------------------------------------- */
/// Read an new input file
void readInputFile(const std::string & input_file);
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* string manipulation */
/* -------------------------------------------------------------------------- */
inline std::string to_lower(const std::string & str);
/* -------------------------------------------------------------------------- */
inline std::string trim(const std::string & to_trim);
inline std::string trim(const std::string & to_trim, char c);
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/// give a string representation of the a human readable size in bit
template <typename T> std::string printMemorySize(UInt size);
/* -------------------------------------------------------------------------- */
struct TensorTrait {};
struct TensorProxyTrait {};
} // namespace akantu
/* -------------------------------------------------------------------------- */
/* Type traits */
/* -------------------------------------------------------------------------- */
namespace aka {
/* ------------------------------------------------------------------------ */
template <typename T> using is_tensor = std::is_base_of<akantu::TensorTrait, T>;
template <typename T>
using is_tensor_proxy = std::is_base_of<akantu::TensorProxyTrait, T>;
/* ------------------------------------------------------------------------ */
template <typename T> using is_scalar = std::is_arithmetic<T>;
/* ------------------------------------------------------------------------ */
template <typename R, typename T,
std::enable_if_t<std::is_reference<T>::value> * = nullptr>
bool is_of_type(T && t) {
return (
dynamic_cast<std::add_pointer_t<
std::conditional_t<std::is_const<std::remove_reference_t<T>>::value,
std::add_const_t<R>, R>>>(&t) != nullptr);
}
/* -------------------------------------------------------------------------- */
template <typename R, typename T> bool is_of_type(std::unique_ptr<T> & t) {
return (
dynamic_cast<std::add_pointer_t<
std::conditional_t<std::is_const<T>::value, std::add_const_t<R>, R>>>(
t.get()) != nullptr);
}
/* ------------------------------------------------------------------------ */
template <typename R, typename T,
std::enable_if_t<std::is_reference<T>::value> * = nullptr>
decltype(auto) as_type(T && t) {
static_assert(
disjunction<
std::is_base_of<std::decay_t<T>, std::decay_t<R>>, // down-cast
std::is_base_of<std::decay_t<R>, std::decay_t<T>> // up-cast
>::value,
"Type T and R are not valid for a as_type conversion");
return dynamic_cast<std::add_lvalue_reference_t<
std::conditional_t<std::is_const<std::remove_reference_t<T>>::value,
std::add_const_t<R>, R>>>(t);
}
/* -------------------------------------------------------------------------- */
template <typename R, typename T,
std::enable_if_t<std::is_pointer<T>::value> * = nullptr>
decltype(auto) as_type(T && t) {
return &as_type<R>(*t);
}
/* -------------------------------------------------------------------------- */
template <typename R, typename T>
decltype(auto) as_type(const std::shared_ptr<T> & t) {
return std::dynamic_pointer_cast<R>(t);
}
} // namespace aka
#include "aka_common_inline_impl.hh"
#include "aka_fwd.hh"
namespace akantu {
/// get access to the internal argument parser
cppargparse::ArgumentParser & getStaticArgumentParser();
/// get access to the internal input file parser
Parser & getStaticParser();
/// get access to the user part of the internal input file parser
const ParserSection & getUserParser();
#define AKANTU_CURRENT_FUNCTION \
(std::string(__func__) + "():" + std::to_string(__LINE__))
} // namespace akantu
/* -------------------------------------------------------------------------- */
#if AKANTU_INTEGER_SIZE == 4
#define AKANTU_HASH_COMBINE_MAGIC_NUMBER 0x9e3779b9
#elif AKANTU_INTEGER_SIZE == 8
#define AKANTU_HASH_COMBINE_MAGIC_NUMBER 0x9e3779b97f4a7c13LL
#endif
namespace std {
/**
* Hashing function for pairs based on hash_combine from boost The magic
* number is coming from the golden number @f[\phi = \frac{1 + \sqrt5}{2}@f]
* @f[\frac{2^32}{\phi} = 0x9e3779b9@f]
* http://stackoverflow.com/questions/4948780/magic-number-in-boosthash-combine
* http://burtleburtle.net/bob/hash/doobs.html
*/
template <typename a, typename b> struct hash<std::pair<a, b>> {
hash() = default;
size_t operator()(const std::pair<a, b> & p) const {
size_t seed = ah(p.first);
return bh(p.second) + AKANTU_HASH_COMBINE_MAGIC_NUMBER + (seed << 6) +
(seed >> 2);
}
private:
const hash<a> ah{};
const hash<b> bh{};
};
} // namespace std
#endif // AKANTU_COMMON_HH_
diff --git a/src/common/aka_common_inline_impl.hh b/src/common/aka_common_inline_impl.hh
index 64616aa1a..31a34b97f 100644
--- a/src/common/aka_common_inline_impl.hh
+++ b/src/common/aka_common_inline_impl.hh
@@ -1,150 +1,129 @@
/**
* @file aka_common_inline_impl.hh
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Tue Feb 20 2018
*
* @brief inline implementations of common akantu type descriptions
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_common.hh"
/* -------------------------------------------------------------------------- */
#include <algorithm>
#include <cctype>
#include <cmath>
#include <iomanip>
#include <iostream>
+#include <array>
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
/// standard output stream operator for GhostType
inline std::ostream & operator<<(std::ostream & stream, GhostType type) {
switch (type) {
case _not_ghost:
stream << "not_ghost";
break;
case _ghost:
stream << "ghost";
break;
case _casper:
stream << "Casper the friendly ghost";
break;
}
return stream;
}
/* -------------------------------------------------------------------------- */
inline std::string to_lower(const std::string & str) {
std::string lstr = str;
std::transform(lstr.begin(), lstr.end(), lstr.begin(), (int (*)(int))tolower);
return lstr;
}
namespace {
template <typename pred>
inline std::string trim_p(const std::string & to_trim, pred && p) {
std::string trimed = to_trim;
auto && not_ = [&](auto && a) { return not p(a); };
// left trim
trimed.erase(trimed.begin(),
std::find_if(trimed.begin(), trimed.end(), not_));
// right trim
trimed.erase(std::find_if(trimed.rbegin(), trimed.rend(), not_).base(),
trimed.end());
return trimed;
}
} // namespace
/* -------------------------------------------------------------------------- */
inline std::string trim(const std::string & to_trim) {
return trim_p(to_trim, [&](auto && a) { return std::isspace(a); });
}
inline std::string trim(const std::string & to_trim, char c) {
return trim_p(to_trim, [&c](auto && a) { return (a == c); });
}
/* -------------------------------------------------------------------------- */
template <typename T> std::string printMemorySize(UInt size) {
Real real_size = size * sizeof(T);
UInt mult = 0;
if (real_size != 0) {
mult = (std::log(real_size) / std::log(2)) / 10;
}
std::stringstream sstr;
real_size /= Real(1 << (10 * mult));
sstr << std::setprecision(2) << std::fixed << real_size;
std::string size_prefix;
- switch (mult) {
- case 0:
- sstr << "";
- break;
- case 1:
- sstr << "Ki";
- break;
- case 2:
- sstr << "Mi";
- break;
- case 3:
- sstr << "Gi";
- break; // I started on this type of machines
- // (32bit computers) (Nicolas)
- case 4:
- sstr << "Ti";
- break;
- case 5:
- sstr << "Pi";
- break;
- case 6:
- sstr << "Ei";
- break; // theoritical limit of RAM of the current
- // computers in 2014 (64bit computers) (Nicolas)
- case 7:
- sstr << "Zi";
- break;
- case 8:
- sstr << "Yi";
- break;
- default:
+ std::array<std::string, 9> ratio = {
+ "", "Ki", "Mi",
+ "Gi", // I started on this type of machines (32bit computers) (Nicolas)
+ "Ti", "Pi",
+ "Ei", // theoritical limit of RAM of the current computers in 2014 (64bit
+ // computers) (Nicolas)
+ "Zi", "Yi"};
+
+ if (mult >= ratio.size()) {
AKANTU_ERROR(
"The programmer in 2014 didn't thought so far (even wikipedia does not "
"go further)."
<< " You have at least 1024 times more than a yobibit of RAM!!!"
- << " Just add the prefix corresponding in this switch case.");
+ << " Just add the prefix corresponding in the ratio array.");
}
- sstr << "Byte";
+ sstr << ratio[mult] << "Byte";
return sstr.str();
}
} // namespace akantu
diff --git a/src/common/aka_error.hh b/src/common/aka_error.hh
index 269a7400b..e048320f9 100644
--- a/src/common/aka_error.hh
+++ b/src/common/aka_error.hh
@@ -1,418 +1,419 @@
/**
* @file aka_error.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Mon Jun 14 2010
* @date last modification: Tue Feb 20 2018
*
* @brief error management and internal exceptions
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include <set>
#include <sstream>
#include <typeinfo>
#include <utility>
#include <vector>
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
#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 <class T> std::string demangle() {
return demangle(typeid(T).name());
}
template <class T> std::string demangle(const T & t) {
return demangle(typeid(t).name());
}
auto exec(const std::string & cmd) -> std::string;
auto getBacktrace() -> std::vector<std::string>;
void
printBacktrace(const std::vector<std::string> & 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(std::string info = "") : _info(std::move(info)) {}
+ explicit Exception(const std::string & info = "") : _info(info) {}
public:
//! full constructor
- Exception(std::string info, std::string file, unsigned int line)
- : _info(std::move(info)), _file(std::move(file)), _line(line) {}
+ 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<std::string> & 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<std::string> 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 <class Except>
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 <class Except>
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) {
- modules_to_debug.insert(id);
+ this->modules_to_debug.insert(id);
}
void removeModuleToDebug(const std::string & id) {
- auto it = modules_to_debug.find(id);
- if (it != modules_to_debug.end()) {
- modules_to_debug.erase(it);
+ 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<std::string> 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)) \
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 <class Except>
- void
- Debugger::throwCustomException(Except ex, const std::string & info,
- const std::string & file, unsigned int line,
- const std::string & module_) const
+ 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 <class Except>
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_event_handler_manager.hh b/src/common/aka_event_handler_manager.hh
index 258f1f94f..33b5f1544 100644
--- a/src/common/aka_event_handler_manager.hh
+++ b/src/common/aka_event_handler_manager.hh
@@ -1,126 +1,126 @@
/**
* @file aka_event_handler_manager.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Sun Dec 03 2017
*
* @brief Base of Event Handler classes
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_AKA_EVENT_HANDLER_MANAGER_HH_
#define AKANTU_AKA_EVENT_HANDLER_MANAGER_HH_
/* -------------------------------------------------------------------------- */
#include "aka_common.hh"
/* -------------------------------------------------------------------------- */
#include <algorithm>
#include <list>
/* -------------------------------------------------------------------------- */
namespace akantu {
template <class EventHandler> class EventHandlerManager {
private:
using priority_value = std::pair<EventHandlerPriority, EventHandler *>;
using priority_list = std::list<priority_value>;
struct KeyComp {
bool operator()(const priority_value & a, const priority_value & b) const {
return (a.first < b.first);
}
bool operator()(const priority_value & a, UInt b) const {
return (a.first < b);
}
};
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
- virtual ~EventHandlerManager() { event_handlers.clear(); }
+ virtual ~EventHandlerManager() = default;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// register a new EventHandler to the Manager. The register object
/// will then be informed about the events the manager observes.
void registerEventHandler(EventHandler & event_handler,
EventHandlerPriority priority = _ehp_highest) {
auto it = this->searchEventHandler(event_handler);
if (it != this->event_handlers.end()) {
AKANTU_EXCEPTION("This event handler was already registered (priority: "
<< priority << ")");
}
auto pos =
std::lower_bound(this->event_handlers.begin(),
this->event_handlers.end(), priority, KeyComp());
this->event_handlers.insert(pos, std::make_pair(priority, &event_handler));
}
/// unregister a EventHandler object. This object will not be
/// notified anymore about the events this manager observes.
void unregisterEventHandler(EventHandler & event_handler) {
auto it = this->searchEventHandler(event_handler);
if (it == this->event_handlers.end()) {
AKANTU_EXCEPTION("This event handler is not registered");
}
this->event_handlers.erase(it);
}
/// Notify all the registered EventHandlers about the event that just occured.
template <class Event> void sendEvent(const Event & event) {
for (auto & pair : this->event_handlers) {
pair.second->sendEvent(event);
}
}
private:
typename priority_list::iterator searchEventHandler(EventHandler & handler) {
auto it = this->event_handlers.begin();
auto end = this->event_handlers.end();
for (; it != end && it->second != &handler; ++it) {
;
}
return it;
}
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
private:
/// list of the event handlers
priority_list event_handlers;
};
} // namespace akantu
#endif /* AKANTU_AKA_EVENT_HANDLER_MANAGER_HH_ */
diff --git a/src/common/aka_factory.hh b/src/common/aka_factory.hh
index 1e178991f..9c4221826 100644
--- a/src/common/aka_factory.hh
+++ b/src/common/aka_factory.hh
@@ -1,86 +1,94 @@
/**
* @file aka_factory.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Sun Jul 09 2017
* @date last modification: Fri Dec 08 2017
*
* @brief This is a generic factory
*
*
* Copyright (©) 2016-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_common.hh"
/* -------------------------------------------------------------------------- */
#include <functional>
#include <map>
#include <memory>
#include <string>
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_AKA_FACTORY_HH_
#define AKANTU_AKA_FACTORY_HH_
namespace akantu {
template <class Base, class T = ID, class... Args> class Factory {
using allocator_t = std::function<std::unique_ptr<Base>(Args...)>;
private:
Factory() = default;
public:
Factory(const Factory &) = delete;
Factory & operator=(const Factory &) = delete;
static Factory & getInstance() {
static Factory instance;
return instance;
}
/* ------------------------------------------------------------------------ */
bool registerAllocator(const T & id, const allocator_t & allocator) {
if (allocators.find(id) != allocators.end()) {
AKANTU_EXCEPTION("The id "
<< " is already registered in the "
<< debug::demangle(typeid(Base).name()) << " factory");
}
allocators[id] = allocator;
return true;
}
template <typename... AArgs>
std::unique_ptr<Base> allocate(const T & id, AArgs &&... args) const {
if (allocators.find(id) == allocators.end()) {
AKANTU_EXCEPTION("The id "
<< " is not registered in the "
<< debug::demangle(typeid(Base).name()) << " factory.");
}
return std::forward<std::unique_ptr<Base>>(
allocators.at(id)(std::forward<AArgs>(args)...));
}
+ std::vector<T> getPossibleAllocators() {
+ std::vector<T> keys;
+ for (auto & e : allocators) {
+ keys.push_back(e.first);
+ }
+ return keys;
+ }
+
private:
std::map<T, allocator_t> allocators;
};
} // namespace akantu
#endif /* AKANTU_AKA_FACTORY_HH_ */
diff --git a/src/common/aka_grid_dynamic.hh b/src/common/aka_grid_dynamic.hh
index 2d1e9054e..46ce34137 100644
--- a/src/common/aka_grid_dynamic.hh
+++ b/src/common/aka_grid_dynamic.hh
@@ -1,533 +1,530 @@
/**
* @file aka_grid_dynamic.hh
*
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Thu Feb 21 2013
* @date last modification: Wed Nov 08 2017
*
* @brief Grid that is auto balanced
*
*
* Copyright (©) 2014-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_array.hh"
#include "aka_common.hh"
#include "aka_types.hh"
#include "mesh_accessor.hh"
#include <iostream>
/* -------------------------------------------------------------------------- */
#include <map>
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_AKA_GRID_DYNAMIC_HH_
#define AKANTU_AKA_GRID_DYNAMIC_HH_
namespace akantu {
class Mesh;
template <typename T> class SpatialGrid {
public:
explicit SpatialGrid(UInt dimension)
: dimension(dimension), spacing(dimension), center(dimension),
lower(dimension), upper(dimension), empty_cell() {}
SpatialGrid(UInt dimension, const Vector<Real> & spacing,
const Vector<Real> & center)
: dimension(dimension), spacing(spacing), center(center),
lower(dimension), upper(dimension), empty_cell() {
for (UInt i = 0; i < dimension; ++i) {
lower(i) = std::numeric_limits<Real>::max();
upper(i) = -std::numeric_limits<Real>::max();
}
}
virtual ~SpatialGrid() = default;
class neighbor_cells_iterator;
class cells_iterator;
class CellID {
public:
CellID() = default;
explicit CellID(UInt dimention) : ids(dimention) {}
void setID(UInt dir, Int id) { ids(dir) = id; }
Int getID(UInt dir) const { return ids(dir); }
bool operator<(const CellID & id) const {
return std::lexicographical_compare(
ids.storage(), ids.storage() + ids.size(), id.ids.storage(),
id.ids.storage() + id.ids.size());
}
bool operator==(const CellID & id) const {
return std::equal(ids.storage(), ids.storage() + ids.size(),
id.ids.storage());
}
bool operator!=(const CellID & id) const { return !(operator==(id)); }
class neighbor_cells_iterator
: private std::iterator<std::forward_iterator_tag, UInt> {
public:
neighbor_cells_iterator(const CellID & cell_id, bool end)
: cell_id(cell_id), position(cell_id.ids.size(), end ? 1 : -1) {
this->updateIt();
if (end) {
this->it++;
}
}
neighbor_cells_iterator & operator++() {
UInt i = 0;
for (; i < position.size() && position(i) == 1; ++i) {
;
}
if (i == position.size()) {
++it;
return *this;
}
for (UInt j = 0; j < i; ++j) {
position(j) = -1;
}
position(i)++;
updateIt();
return *this;
}
neighbor_cells_iterator operator++(int) {
neighbor_cells_iterator tmp(*this);
operator++();
return tmp;
};
bool operator==(const neighbor_cells_iterator & rhs) const {
return cell_id == rhs.cell_id && it == rhs.it;
};
bool operator!=(const neighbor_cells_iterator & rhs) const {
return !operator==(rhs);
};
CellID operator*() const {
CellID cur_cell_id(cell_id);
cur_cell_id.ids += position;
return cur_cell_id;
};
private:
void updateIt() {
it = 0;
for (UInt i = 0; i < position.size(); ++i) {
it = it * 3 + (position(i) + 1);
}
}
private:
/// central cell id
const CellID & cell_id;
// number representing the current neighbor in base 3;
UInt it;
// current cell shift
Vector<Int> position;
};
class Neighbors {
public:
explicit Neighbors(const CellID & cell_id) : cell_id(cell_id) {}
decltype(auto) begin() { return neighbor_cells_iterator(cell_id, false); }
decltype(auto) end() { return neighbor_cells_iterator(cell_id, true); }
private:
const CellID & cell_id;
};
decltype(auto) neighbors() { return Neighbors(*this); }
private:
friend class cells_iterator;
Vector<Int> ids;
};
/* ------------------------------------------------------------------------ */
class Cell {
public:
using iterator = typename std::vector<T>::iterator;
using const_iterator = typename std::vector<T>::const_iterator;
Cell() : id(), data() {}
explicit Cell(const CellID & cell_id) : id(cell_id), data() {}
bool operator==(const Cell & cell) const { return id == cell.id; }
bool operator!=(const Cell & cell) const { return id != cell.id; }
Cell & add(const T & d) {
data.push_back(d);
return *this;
}
iterator begin() { return data.begin(); }
const_iterator begin() const { return data.begin(); }
iterator end() { return data.end(); }
const_iterator end() const { return data.end(); }
private:
CellID id;
std::vector<T> data;
};
private:
using cells_container = std::map<CellID, Cell>;
public:
const Cell & getCell(const CellID & cell_id) const {
auto it = cells.find(cell_id);
if (it != cells.end()) {
return it->second;
}
return empty_cell;
}
decltype(auto) beginCell(const CellID & cell_id) {
auto it = cells.find(cell_id);
if (it != cells.end()) {
return it->second.begin();
}
return empty_cell.begin();
}
decltype(auto) endCell(const CellID & cell_id) {
auto it = cells.find(cell_id);
if (it != cells.end()) {
return it->second.end();
}
return empty_cell.end();
}
decltype(auto) beginCell(const CellID & cell_id) const {
auto it = cells.find(cell_id);
if (it != cells.end()) {
return it->second.begin();
}
return empty_cell.begin();
}
decltype(auto) endCell(const CellID & cell_id) const {
auto it = cells.find(cell_id);
if (it != cells.end()) {
return it->second.end();
}
return empty_cell.end();
}
/* ------------------------------------------------------------------------ */
class cells_iterator
: private std::iterator<std::forward_iterator_tag, CellID> {
public:
explicit cells_iterator(typename std::map<CellID, Cell>::const_iterator it)
: it(it) {}
cells_iterator & operator++() {
this->it++;
return *this;
}
- cells_iterator operator++(int) {
+ cells_iterator operator++(int /*unused*/) {
cells_iterator tmp(*this);
operator++();
return tmp;
};
bool operator==(const cells_iterator & rhs) const { return it == rhs.it; };
bool operator!=(const cells_iterator & rhs) const {
return !operator==(rhs);
};
CellID operator*() const {
CellID cur_cell_id(this->it->first);
return cur_cell_id;
};
private:
/// map iterator
typename std::map<CellID, Cell>::const_iterator it;
};
public:
template <class vector_type>
Cell & insert(const T & d, const vector_type & position) {
auto && cell_id = getCellID(position);
auto && it = cells.find(cell_id);
if (it == cells.end()) {
Cell cell(cell_id);
auto & tmp = (cells[cell_id] = cell).add(d);
for (UInt i = 0; i < dimension; ++i) {
Real posl = center(i) + cell_id.getID(i) * spacing(i);
Real posu = posl + spacing(i);
if (posl <= lower(i)) {
lower(i) = posl;
}
if (posu > upper(i)) {
upper(i) = posu;
}
}
return tmp;
}
return it->second.add(d);
}
/* ------------------------------------------------------------------------ */
inline decltype(auto) begin() const {
auto begin = this->cells.begin();
return cells_iterator(begin);
}
inline decltype(auto) end() const {
auto end = this->cells.end();
return cells_iterator(end);
}
template <class vector_type>
CellID getCellID(const vector_type & position) const {
CellID cell_id(dimension);
for (UInt i = 0; i < dimension; ++i) {
cell_id.setID(i, getCellID(position(i), i));
}
return cell_id;
}
void printself(std::ostream & stream, int indent = 0) const {
- std::string space;
- for (Int i = 0; i < indent; i++, space += AKANTU_INDENT) {
- ;
- }
+ 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(5);
stream << space << "SpatialGrid<" << debug::demangle(typeid(T).name())
<< "> [" << std::endl;
stream << space << " + dimension : " << this->dimension << std::endl;
stream << space << " + lower bounds : {";
for (UInt i = 0; i < lower.size(); ++i) {
if (i != 0) {
stream << ", ";
}
stream << lower(i);
};
stream << "}" << std::endl;
stream << space << " + upper bounds : {";
for (UInt i = 0; i < upper.size(); ++i) {
if (i != 0) {
stream << ", ";
}
stream << upper(i);
};
stream << "}" << std::endl;
stream << space << " + spacing : {";
for (UInt i = 0; i < spacing.size(); ++i) {
if (i != 0) {
stream << ", ";
}
stream << spacing(i);
};
stream << "}" << std::endl;
stream << space << " + center : {";
for (UInt i = 0; i < center.size(); ++i) {
if (i != 0) {
stream << ", ";
}
stream << center(i);
};
stream << "}" << std::endl;
stream << space << " + nb_cells : " << this->cells.size() << "/";
Vector<Real> dist(this->dimension);
dist = upper;
dist -= lower;
for (UInt i = 0; i < this->dimension; ++i) {
dist(i) /= spacing(i);
}
UInt nb_cells = std::ceil(dist(0));
for (UInt i = 1; i < this->dimension; ++i) {
nb_cells *= std::ceil(dist(i));
}
stream << nb_cells << std::endl;
stream << space << "]" << std::endl;
stream.precision(prec);
stream.flags(ff);
}
void saveAsMesh(Mesh & mesh) const;
private:
/* --------------------------------------------------------------------------
*/
inline UInt getCellID(Real position, UInt direction) const {
AKANTU_DEBUG_ASSERT(direction < center.size(), "The direction asked ("
<< direction
<< ") is out of range "
<< center.size());
Real dist_center = position - center(direction);
Int id = std::floor(dist_center / spacing(direction));
// if(dist_center < 0) id--;
return id;
}
friend class GridSynchronizer;
public:
AKANTU_GET_MACRO(LowerBounds, lower, const Vector<Real> &);
AKANTU_GET_MACRO(UpperBounds, upper, const Vector<Real> &);
AKANTU_GET_MACRO(Spacing, spacing, const Vector<Real> &);
AKANTU_SET_MACRO(Spacing, spacing, Vector<Real> &);
AKANTU_GET_MACRO(Center, center, const Vector<Real> &);
AKANTU_SET_MACRO(Center, center, Vector<Real> &);
protected:
UInt dimension;
cells_container cells;
Vector<Real> spacing;
Vector<Real> center;
Vector<Real> lower;
Vector<Real> upper;
Cell empty_cell;
};
/// standard output stream operator
template <typename T>
inline std::ostream & operator<<(std::ostream & stream,
const SpatialGrid<T> & _this) {
_this.printself(stream);
return stream;
}
} // namespace akantu
#include "mesh.hh"
namespace akantu {
/* -------------------------------------------------------------------------- */
template <typename T> void SpatialGrid<T>::saveAsMesh(Mesh & mesh) const {
ElementType type = _not_defined;
switch (dimension) {
case 1:
type = _segment_2;
break;
case 2:
type = _quadrangle_4;
break;
case 3:
type = _hexahedron_8;
break;
}
MeshAccessor mesh_accessor(mesh);
auto & connectivity = mesh_accessor.getConnectivity(type);
auto & nodes = mesh_accessor.getNodes();
auto & uint_data = mesh.getDataPointer<UInt>("tag_1", type);
Vector<Real> pos(dimension);
UInt global_id = 0;
for (auto & cell_pair : cells) {
UInt cur_node = nodes.size();
UInt cur_elem = connectivity.size();
const CellID & cell_id = cell_pair.first;
for (UInt i = 0; i < dimension; ++i) {
pos(i) = center(i) + cell_id.getID(i) * spacing(i);
}
nodes.push_back(pos);
for (UInt i = 0; i < dimension; ++i) {
pos(i) += spacing(i);
}
nodes.push_back(pos);
connectivity.push_back(cur_node);
switch (dimension) {
case 1:
connectivity(cur_elem, 1) = cur_node + 1;
break;
case 2:
pos(0) -= spacing(0);
nodes.push_back(pos);
pos(0) += spacing(0);
pos(1) -= spacing(1);
nodes.push_back(pos);
connectivity(cur_elem, 1) = cur_node + 3;
connectivity(cur_elem, 2) = cur_node + 1;
connectivity(cur_elem, 3) = cur_node + 2;
break;
case 3:
pos(1) -= spacing(1);
pos(2) -= spacing(2);
nodes.push_back(pos);
pos(1) += spacing(1);
nodes.push_back(pos);
pos(0) -= spacing(0);
nodes.push_back(pos);
pos(1) -= spacing(1);
pos(2) += spacing(2);
nodes.push_back(pos);
pos(0) += spacing(0);
nodes.push_back(pos);
pos(0) -= spacing(0);
pos(1) += spacing(1);
nodes.push_back(pos);
connectivity(cur_elem, 1) = cur_node + 2;
connectivity(cur_elem, 2) = cur_node + 3;
connectivity(cur_elem, 3) = cur_node + 4;
connectivity(cur_elem, 4) = cur_node + 5;
connectivity(cur_elem, 5) = cur_node + 6;
connectivity(cur_elem, 6) = cur_node + 1;
connectivity(cur_elem, 7) = cur_node + 7;
break;
}
uint_data.push_back(global_id);
++global_id;
}
}
} // namespace akantu
#endif /* AKANTU_AKA_GRID_DYNAMIC_HH_ */
diff --git a/src/common/aka_math.hh b/src/common/aka_math.hh
index 513de5618..2423885b6 100644
--- a/src/common/aka_math.hh
+++ b/src/common/aka_math.hh
@@ -1,288 +1,288 @@
/**
* @file aka_math.hh
*
* @author Ramin Aghababaei <ramin.aghababaei@epfl.ch>
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author Marion Estelle Chambart <marion.chambart@epfl.ch>
* @author David Simon Kammer <david.kammer@epfl.ch>
* @author Daniel Pino Muñoz <daniel.pinomunoz@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
* @author Leonardo Snozzi <leonardo.snozzi@epfl.ch>
* @author Peter Spijker <peter.spijker@epfl.ch>
* @author Marco Vocialta <marco.vocialta@epfl.ch>
*
* @date creation: Wed Aug 04 2010
* @date last modification: Mon Sep 11 2017
*
* @brief mathematical operations
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_common.hh"
/* -------------------------------------------------------------------------- */
#include <utility>
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_AKA_MATH_H_
#define AKANTU_AKA_MATH_H_
namespace akantu {
/* -------------------------------------------------------------------------- */
namespace Math {
/// tolerance for functions that need one
extern Real tolerance; // NOLINT
/* ------------------------------------------------------------------------ */
/* Matrix algebra */
/* ------------------------------------------------------------------------ */
/// @f$ y = A*x @f$
void matrix_vector(UInt m, UInt n, const Array<Real> & A,
const Array<Real> & x, Array<Real> & y, Real alpha = 1.);
/// @f$ y = A*x @f$
inline void matrix_vector(UInt m, UInt n, Real * A, Real * x, Real * y,
Real alpha = 1.);
/// @f$ y = A^t*x @f$
inline void matrixt_vector(UInt m, UInt n, Real * A, Real * x, Real * y,
Real alpha = 1.);
/// @f$ C = A*B @f$
void matrix_matrix(UInt m, UInt n, UInt k, const Array<Real> & A,
const Array<Real> & B, Array<Real> & C, Real alpha = 1.);
/// @f$ C = A*B^t @f$
void matrix_matrixt(UInt m, UInt n, UInt k, const Array<Real> & A,
const Array<Real> & B, Array<Real> & C, Real alpha = 1.);
/// @f$ C = A*B @f$
inline void matrix_matrix(UInt m, UInt n, UInt k, Real * A, Real * B,
Real * C, Real alpha = 1.);
/// @f$ C = A^t*B @f$
inline void matrixt_matrix(UInt m, UInt n, UInt k, Real * A, Real * B,
Real * C, Real alpha = 1.);
/// @f$ C = A*B^t @f$
inline void matrix_matrixt(UInt m, UInt n, UInt k, Real * A, Real * B,
Real * C, Real alpha = 1.);
/// @f$ C = A^t*B^t @f$
inline void matrixt_matrixt(UInt m, UInt n, UInt k, Real * A, Real * B,
Real * C, Real alpha = 1.);
template <bool tr_A, bool tr_B>
inline void matMul(UInt m, UInt n, UInt k, Real alpha, Real * A, Real * B,
Real beta, Real * C);
template <bool tr_A>
inline void matVectMul(UInt m, UInt n, Real alpha, Real * A, Real * x,
Real beta, Real * y);
inline void aXplusY(UInt n, Real alpha, Real * x, Real * y);
inline void matrix33_eigenvalues(Real * A, Real * Adiag);
inline void matrix22_eigenvalues(Real * A, Real * Adiag);
template <UInt dim> inline void eigenvalues(Real * A, Real * d);
/// solve @f$ A x = \Lambda x @f$ and return d and V such as @f$ A V[i:] =
/// d[i] V[i:]@f$
template <typename T> void matrixEig(UInt n, T * A, T * d, T * V = nullptr);
/// determinent of a 2x2 matrix
Real det2(const Real * mat);
/// determinent of a 3x3 matrix
Real det3(const Real * mat);
/// determinent of a nxn matrix
template <UInt n> Real det(const Real * mat);
/// determinent of a nxn matrix
template <typename T> T det(UInt n, const T * A);
/// inverse a nxn matrix
template <UInt n> inline void inv(const Real * A, Real * inv);
/// inverse a nxn matrix
template <typename T> inline void inv(UInt n, const T * A, T * inv);
/// inverse a 3x3 matrix
inline void inv3(const Real * mat, Real * inv);
/// inverse a 2x2 matrix
inline void inv2(const Real * mat, Real * inv);
/// solve A x = b using a LU factorization
template <typename T>
inline void solve(UInt n, const T * A, T * x, const T * b);
/// return the double dot product between 2 tensors in 2d
inline Real matrixDoubleDot22(Real * A, Real * B);
/// return the double dot product between 2 tensors in 3d
inline Real matrixDoubleDot33(Real * A, Real * B);
/// extension of the double dot product to two 2nd order tensor in dimension n
inline Real matrixDoubleDot(UInt n, Real * A, Real * B);
/* ------------------------------------------------------------------------ */
/* Array algebra */
/* ------------------------------------------------------------------------ */
/// vector cross product
inline void vectorProduct3(const Real * v1, const Real * v2, Real * res);
/// normalize a vector
inline void normalize2(Real * v);
/// normalize a vector
inline void normalize3(Real * v);
/// return norm of a 2-vector
inline Real norm2(const Real * v);
/// return norm of a 3-vector
inline Real norm3(const Real * v);
/// return norm of a vector
inline Real norm(UInt n, const Real * v);
/// return the dot product between 2 vectors in 2d
inline Real vectorDot2(const Real * v1, const Real * v2);
/// return the dot product between 2 vectors in 3d
inline Real vectorDot3(const Real * v1, const Real * v2);
/// return the dot product between 2 vectors
inline Real vectorDot(Real * v1, Real * v2, UInt n);
/* ------------------------------------------------------------------------ */
/* Geometry */
/* ------------------------------------------------------------------------ */
/// compute normal a normal to a vector
inline void normal2(const Real * vec, Real * normal);
/// compute normal a normal to a vector
inline void normal3(const Real * vec1, const Real * vec2, Real * normal);
/// compute the tangents to an array of normal vectors
void compute_tangents(const Array<Real> & normals, Array<Real> & tangents);
/// distance in 2D between x and y
inline Real distance_2d(const Real * x, const Real * y);
/// distance in 3D between x and y
inline Real distance_3d(const Real * x, const Real * y);
/// radius of the in-circle of a triangle in 2d space
static inline Real triangle_inradius_2d(const Real * coord1, const Real * coord2,
const Real * coord3);
/// radius of the in-circle of a triangle in 3d space
static inline Real triangle_inradius_3d(const Real * coord1, const Real * coord2,
const Real * coord3);
/// radius of the in-circle of a tetrahedron
inline Real tetrahedron_inradius(const Real * coord1, const Real * coord2,
const Real * coord3, const Real * coord4);
/// volume of a tetrahedron
inline Real tetrahedron_volume(const Real * coord1, const Real * coord2,
const Real * coord3, const Real * coord4);
/// compute the barycenter of n points
inline void barycenter(const Real * coord, UInt nb_points,
UInt spatial_dimension, Real * barycenter);
/// vector between x and y
inline void vector_2d(const Real * x, const Real * y, Real * res);
/// vector pointing from x to y in 3 spatial dimension
inline void vector_3d(const Real * x, const Real * y, Real * res);
/// test if two scalar are equal within a given tolerance
inline bool are_float_equal(Real x, Real y);
/// test if two vectors are equal within a given tolerance
inline bool are_vector_equal(UInt n, Real * x, Real * y);
#ifdef isnan
#error \
"You probably included <math.h> which is incompatible with aka_math please use\
<cmath> or add a \"#undef isnan\" before akantu includes"
#endif
/// test if a real is a NaN
inline bool isnan(Real x);
/// test if the line x and y intersects each other
inline bool intersects(Real x_min, Real x_max, Real y_min, Real y_max);
/// test if a is in the range [x_min, x_max]
inline bool is_in_range(Real a, Real x_min, Real x_max);
inline Real getTolerance() { return Math::tolerance; }
inline void setTolerance(Real tol) { Math::tolerance = tol; }
template <UInt p, typename T> inline T pow(T x);
template <class T1, class T2,
std::enable_if_t<std::is_integral<T1>::value and
std::is_integral<T2>::value> * = nullptr>
inline Real kronecker(T1 i, T2 j) {
return static_cast<Real>(i == j);
}
/// reduce all the values of an array, the summation is done in place and the
/// array is modified
Real reduce(Array<Real> & array);
class NewtonRaphson {
public:
NewtonRaphson(Real tolerance, Real max_iteration)
: tolerance(tolerance), max_iteration(max_iteration) {}
template <class Functor> Real solve(const Functor & funct, Real x_0);
private:
Real tolerance;
Real max_iteration;
};
struct NewtonRaphsonFunctor {
- explicit NewtonRaphsonFunctor(std::string name) : name(std::move(name)) {}
+ explicit NewtonRaphsonFunctor(const std::string & name) : name(name) {}
virtual ~NewtonRaphsonFunctor() = default;
NewtonRaphsonFunctor(const NewtonRaphsonFunctor & other) = default;
NewtonRaphsonFunctor(NewtonRaphsonFunctor && other) noexcept = default;
NewtonRaphsonFunctor &
operator=(const NewtonRaphsonFunctor & other) = default;
NewtonRaphsonFunctor &
operator=(NewtonRaphsonFunctor && other) noexcept = default;
virtual Real f(Real x) const = 0;
virtual Real f_prime(Real x) const = 0;
std::string name;
};
} // namespace Math
} // namespace akantu
/* -------------------------------------------------------------------------- */
/* inline functions */
/* -------------------------------------------------------------------------- */
#include "aka_math_tmpl.hh"
#endif /* AKANTU_AKA_MATH_H_ */
diff --git a/src/common/aka_memory.cc b/src/common/aka_memory.cc
deleted file mode 100644
index 8bb7b3474..000000000
--- a/src/common/aka_memory.cc
+++ /dev/null
@@ -1,62 +0,0 @@
-/**
- * @file aka_memory.cc
- *
- * @author Nicolas Richart <nicolas.richart@epfl.ch>
- *
- * @date creation: Fri Jun 18 2010
- * @date last modification: Wed Nov 08 2017
- *
- * @brief static memory wrapper
- *
- *
- * 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 <http://www.gnu.org/licenses/>.
- *
- */
-
-/* -------------------------------------------------------------------------- */
-#include <utility>
-
-#include "aka_memory.hh"
-#include "aka_static_memory.hh"
-
-/* -------------------------------------------------------------------------- */
-
-namespace akantu {
-
-/* -------------------------------------------------------------------------- */
-Memory::Memory(ID id, MemoryID memory_id)
- : static_memory(StaticMemory::getStaticMemory()), id(std::move(id)),
- memory_id(memory_id) {}
-
-/* -------------------------------------------------------------------------- */
-Memory::~Memory() {
- if (StaticMemory::isInstantiated()) {
- std::list<ID>::iterator it;
- for (it = handeld_vectors_id.begin(); it != handeld_vectors_id.end();
- ++it) {
- AKANTU_DEBUG(dblAccessory, "Deleting the vector " << *it);
- static_memory.sfree(memory_id, *it);
- }
- StaticMemory::destroy();
- }
-
- handeld_vectors_id.clear();
-}
-
-/* -------------------------------------------------------------------------- */
-
-} // namespace akantu
diff --git a/src/common/aka_memory.hh b/src/common/aka_memory.hh
deleted file mode 100644
index ae4f599a5..000000000
--- a/src/common/aka_memory.hh
+++ /dev/null
@@ -1,106 +0,0 @@
-/**
- * @file aka_memory.hh
- *
- * @author Nicolas Richart <nicolas.richart@epfl.ch>
- *
- * @date creation: Fri Jun 18 2010
- * @date last modification: Wed Nov 08 2017
- *
- * @brief wrapper for the static_memory, all object which wants
- * to access the static_memory as to inherit from the class memory
- *
- *
- * 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 <http://www.gnu.org/licenses/>.
- *
- */
-/* -------------------------------------------------------------------------- */
-#include "aka_array.hh"
-#include "aka_static_memory.hh"
-/* -------------------------------------------------------------------------- */
-#include <list>
-/* -------------------------------------------------------------------------- */
-
-/* -------------------------------------------------------------------------- */
-#ifndef AKANTU_MEMORY_HH_
-#define AKANTU_MEMORY_HH_
-
-namespace akantu {
-
-class Memory {
- /* ------------------------------------------------------------------------ */
- /* Constructors/Destructors */
- /* ------------------------------------------------------------------------ */
-protected:
- Memory(ID id, MemoryID memory_id = 0);
-
- virtual ~Memory();
-
- /* ------------------------------------------------------------------------ */
- /* Methods */
- /* ------------------------------------------------------------------------ */
-protected:
- /// malloc
- template <class T>
- inline Array<T> & alloc(const ID & name, UInt size, UInt nb_component);
-
- /// malloc
- template <class T>
- inline Array<T> & alloc(const ID & name, UInt size, UInt nb_component,
- const T & init_value);
-
- /* ------------------------------------------------------------------------ */
- /// free an array
- inline void dealloc(const ID & name);
-
- /* ------------------------------------------------------------------------ */
- /* Accessors */
- /* ------------------------------------------------------------------------ */
-protected:
- template <typename T> inline Array<T> & getArray(const ID & name);
-
- template <typename T> inline const Array<T> & getArray(const ID & name) const;
-
-public:
- AKANTU_GET_MACRO(MemoryID, memory_id, const MemoryID &);
-
- AKANTU_GET_MACRO(ID, id, const ID &);
-
- /* ------------------------------------------------------------------------ */
- /* Class Members */
- /* ------------------------------------------------------------------------ */
-private:
- /// the static memory instance
- StaticMemory & static_memory;
-
- /// list of allocated vectors id
- std::list<ID> handeld_vectors_id;
-
-protected:
- ID id;
-
- /// the id registred in the static memory
- MemoryID memory_id;
-};
-
-/* -------------------------------------------------------------------------- */
-/* Inline functions */
-/* -------------------------------------------------------------------------- */
-} // namespace akantu
-
-#include "aka_memory_inline_impl.hh"
-
-#endif /* AKANTU_MEMORY_HH_ */
diff --git a/src/common/aka_memory_inline_impl.hh b/src/common/aka_memory_inline_impl.hh
deleted file mode 100644
index 10858f426..000000000
--- a/src/common/aka_memory_inline_impl.hh
+++ /dev/null
@@ -1,70 +0,0 @@
-/**
- * @file aka_memory_inline_impl.hh
- *
- * @author Nicolas Richart <nicolas.richart@epfl.ch>
- *
- * @date creation: Thu Jul 15 2010
- * @date last modification: Wed Nov 08 2017
- *
- * @brief Implementation of the inline functions of the class Memory
- *
- *
- * 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 <http://www.gnu.org/licenses/>.
- *
- */
-/* -------------------------------------------------------------------------- */
-#include "aka_memory.hh"
-/* -------------------------------------------------------------------------- */
-
-namespace akantu {
-/* -------------------------------------------------------------------------- */
-template <class T>
-inline Array<T> & Memory::alloc(const ID & name, UInt size, UInt nb_component) {
- handeld_vectors_id.push_back(name);
- return static_memory.smalloc<T>(memory_id, name, size, nb_component);
-}
-
-/* -------------------------------------------------------------------------- */
-template <class T>
-inline Array<T> & Memory::alloc(const ID & name, UInt size, UInt nb_component,
- const T & init_value) {
- handeld_vectors_id.push_back(name);
- return static_memory.smalloc<T>(memory_id, name, size, nb_component,
- init_value);
-}
-
-/* -------------------------------------------------------------------------- */
-inline void Memory::dealloc(const ID & name) {
- AKANTU_DEBUG(dblAccessory, "Deleting the vector " << name);
- static_memory.sfree(memory_id, name);
- handeld_vectors_id.remove(name);
-}
-
-/* -------------------------------------------------------------------------- */
-template <class T> inline Array<T> & Memory::getArray(const ID & name) {
- return static_cast<Array<T> &>(
- const_cast<ArrayBase &>(static_memory.getArray(memory_id, name)));
-}
-
-/* -------------------------------------------------------------------------- */
-template <class T>
-inline const Array<T> & Memory::getArray(const ID & name) const {
- return static_cast<Array<T> &>(
- const_cast<ArrayBase &>(static_memory.getArray(memory_id, name)));
-}
-
-} // namespace akantu
diff --git a/src/common/aka_static_memory.cc b/src/common/aka_static_memory.cc
deleted file mode 100644
index eec8a3cbb..000000000
--- a/src/common/aka_static_memory.cc
+++ /dev/null
@@ -1,157 +0,0 @@
-/**
- * @file aka_static_memory.cc
- *
- * @author Nicolas Richart <nicolas.richart@epfl.ch>
- *
- * @date creation: Fri Jun 18 2010
- * @date last modification: Wed Nov 08 2017
- *
- * @brief Memory management
- *
- *
- * 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 <http://www.gnu.org/licenses/>.
- *
- */
-
-/* -------------------------------------------------------------------------- */
-#include <sstream>
-#include <stdexcept>
-
-/* -------------------------------------------------------------------------- */
-#include "aka_static_memory.hh"
-#include "aka_array.hh"
-/* -------------------------------------------------------------------------- */
-namespace akantu {
-
-bool StaticMemory::is_instantiated = false;
-StaticMemory * StaticMemory::single_static_memory = nullptr;
-UInt StaticMemory::nb_reference = 0;
-
-/* -------------------------------------------------------------------------- */
-StaticMemory & StaticMemory::getStaticMemory() {
- if (single_static_memory == nullptr) {
- single_static_memory = new StaticMemory();
- is_instantiated = true;
- }
-
- nb_reference++;
-
- return *single_static_memory;
-}
-
-/* -------------------------------------------------------------------------- */
-void StaticMemory::destroy() {
- nb_reference--;
- if (nb_reference == 0) {
- delete single_static_memory;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-StaticMemory::~StaticMemory() {
- AKANTU_DEBUG_IN();
-
- MemoryMap::iterator memory_it;
- for (memory_it = memories.begin(); memory_it != memories.end(); ++memory_it) {
- ArrayMap::iterator vector_it;
- for (vector_it = (memory_it->second).begin();
- vector_it != (memory_it->second).end(); ++vector_it) {
- delete vector_it->second;
- }
- (memory_it->second).clear();
- }
- memories.clear();
- is_instantiated = false;
- StaticMemory::single_static_memory = nullptr;
- AKANTU_DEBUG_OUT();
-}
-/* -------------------------------------------------------------------------- */
-void StaticMemory::sfree(const MemoryID & memory_id, const ID & name) {
- AKANTU_DEBUG_IN();
-
- try {
- auto & vectors = const_cast<ArrayMap &>(getMemory(memory_id));
- ArrayMap::iterator vector_it;
- vector_it = vectors.find(name);
- if (vector_it != vectors.end()) {
- AKANTU_DEBUG_INFO("Array " << name
- << " removed from the static memory number "
- << memory_id);
- delete vector_it->second;
- vectors.erase(vector_it);
- AKANTU_DEBUG_OUT();
- return;
- }
- } catch (debug::Exception & e) {
- AKANTU_EXCEPTION("The memory "
- << memory_id << " does not exist (perhaps already freed) ["
- << e.what() << "]");
- AKANTU_DEBUG_OUT();
- return;
- }
-
- AKANTU_DEBUG_INFO("The vector " << name
- << " does not exist (perhaps already freed)");
- AKANTU_DEBUG_OUT();
-}
-
-/* -------------------------------------------------------------------------- */
-void StaticMemory::printself(std::ostream & stream, int indent) const {
- std::string space(indent, AKANTU_INDENT);
-
- std::streamsize prec = stream.precision();
- stream.precision(2);
-
- stream << space << "StaticMemory [" << std::endl;
- UInt nb_memories = memories.size();
- stream << space << " + nb memories : " << nb_memories << std::endl;
-
- Real tot_size = 0;
- MemoryMap::const_iterator memory_it;
- for (memory_it = memories.begin(); memory_it != memories.end(); ++memory_it) {
- Real mem_size = 0;
-
- stream << space << AKANTU_INDENT << "Memory [" << std::endl;
- UInt mem_id = memory_it->first;
- stream << space << AKANTU_INDENT << " + memory id : " << mem_id
- << std::endl;
- UInt nb_vectors = (memory_it->second).size();
- stream << space << AKANTU_INDENT << " + nb vectors : " << nb_vectors
- << std::endl;
- stream.precision(prec);
- ArrayMap::const_iterator vector_it;
- for (vector_it = (memory_it->second).begin();
- vector_it != (memory_it->second).end(); ++vector_it) {
- (vector_it->second)->printself(stream, indent + 2);
- mem_size += (vector_it->second)->getMemorySize();
- }
- stream << space << AKANTU_INDENT
- << " + total size : " << printMemorySize<char>(mem_size)
- << std::endl;
- stream << space << AKANTU_INDENT << "]" << std::endl;
- tot_size += mem_size;
- }
- stream << space << " + total size : " << printMemorySize<char>(tot_size)
- << std::endl;
- stream << space << "]" << std::endl;
-
- stream.precision(prec);
-}
-
-/* -------------------------------------------------------------------------- */
-
-} // namespace akantu
diff --git a/src/common/aka_static_memory.hh b/src/common/aka_static_memory.hh
deleted file mode 100644
index 24ba0c5a9..000000000
--- a/src/common/aka_static_memory.hh
+++ /dev/null
@@ -1,156 +0,0 @@
-/**
- * @file aka_static_memory.hh
- *
- * @author Nicolas Richart <nicolas.richart@epfl.ch>
- *
- * @date creation: Fri Jun 18 2010
- * @date last modification: Sun Dec 03 2017
- *
- * @brief Memory management
- *
- *
- * 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 <http://www.gnu.org/licenses/>.
- *
- * @section DESCRIPTION
- *
- * The class handling the memory, allocation/reallocation/desallocation
- * The objects can register their array and ask for allocation or realocation
- *
- */
-
-/* -------------------------------------------------------------------------- */
-
-#ifndef AKANTU_STATIC_MEMORY_HH_
-#define AKANTU_STATIC_MEMORY_HH_
-
-/* -------------------------------------------------------------------------- */
-#include "aka_common.hh"
-/* -------------------------------------------------------------------------- */
-#include <map>
-
-namespace akantu {
-class ArrayBase;
-}
-/* -------------------------------------------------------------------------- */
-namespace akantu {
-
-using ArrayMap = std::map<ID, ArrayBase *>;
-using MemoryMap = std::map<MemoryID, ArrayMap>;
-
-/**
- * @class StaticMemory
- * @brief Class for memory management common to all objects (this class as to
- * be accessed by an interface class memory)
- */
-class StaticMemory {
-
- /* ------------------------------------------------------------------------ */
- /* Constructors/Destructors */
- /* ------------------------------------------------------------------------ */
-private:
- /// Default constructor
- StaticMemory() = default;
-
-public:
- virtual ~StaticMemory();
-
- /* ------------------------------------------------------------------------ */
- /* Accessors */
- /* ------------------------------------------------------------------------ */
-public:
- /// Get the global instance of the StaticMemory
- static StaticMemory & getStaticMemory();
-
- static bool isInstantiated() { return is_instantiated; };
-
- /// remove a reference on the static memory
- static void destroy();
-
- /// access to an Array
- inline const ArrayBase & getArray(const MemoryID & memory_id,
- const ID & name) const;
-
- /// get all vectors of a memory
- inline const ArrayMap & getMemory(const MemoryID & memory_id) const;
-
- /* ------------------------------------------------------------------------ */
- /* Class Methods */
- /* ------------------------------------------------------------------------ */
-public:
- /**
- * Allocation of an array of type
- *
- * @param memory_id the id of the memory accessing to the static memory
- * @param name name of the array (for example connectivity)
- * @param size number of size (for example number of nodes)
- * @param nb_component number of component (for example spatial dimension)
- *
- * @return pointer an array of memory actual size: size * nb_component *
- * sizeof(T)
- */
- template <typename T>
- Array<T> & smalloc(const MemoryID & memory_id, const ID & name, UInt size,
- UInt nb_component);
-
- template <typename T>
- Array<T> & smalloc(const MemoryID & memory_id, const ID & name, UInt size,
- UInt nb_component, const T & init_value);
- /**
- * free the memory associated to the array name
- *
- * @param memory_id the id of the memory accessing to the static memory
- * @param name the name of an existing array
- */
- void sfree(const MemoryID & memory_id, const ID & name);
-
- /// function to print the containt of the class
- virtual void printself(std::ostream & stream, int indent = 0) const;
-
- /* ------------------------------------------------------------------------ */
- /* Class Members */
- /* ------------------------------------------------------------------------ */
-private:
- /// is the static memory instantiated
- static bool is_instantiated;
-
- /// unique instance of the StaticMemory
- static StaticMemory * single_static_memory;
-
- /// map of all allocated arrays, indexed by their names
- MemoryMap memories;
-
- /// number of references on the static memory
- static UInt nb_reference;
-};
-
-/* -------------------------------------------------------------------------- */
-/* inline functions */
-/* -------------------------------------------------------------------------- */
-
-/// standard output stream operator
-inline std::ostream & operator<<(std::ostream & stream,
- const StaticMemory & _this) {
- _this.printself(stream);
- return stream;
-}
-
-} // namespace akantu
-
-#include "aka_static_memory_inline_impl.hh"
-#include "aka_static_memory_tmpl.hh"
-
-#endif /* AKANTU_STATIC_MEMORY_HH_ */
diff --git a/src/common/aka_static_memory_inline_impl.hh b/src/common/aka_static_memory_inline_impl.hh
deleted file mode 100644
index 9942790fb..000000000
--- a/src/common/aka_static_memory_inline_impl.hh
+++ /dev/null
@@ -1,65 +0,0 @@
-/**
- * @file aka_static_memory_inline_impl.hh
- *
- * @author Nicolas Richart <nicolas.richart@epfl.ch>
- *
- * @date creation: Thu Jul 15 2010
- * @date last modification: Wed Feb 03 2016
- *
- * @brief Implementation of inline functions of the class StaticMemory
- *
- *
- * 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 <http://www.gnu.org/licenses/>.
- *
- */
-/* -------------------------------------------------------------------------- */
-#include "aka_static_memory.hh"
-
-namespace akantu {
-/* -------------------------------------------------------------------------- */
-inline const ArrayMap &
-StaticMemory::getMemory(const MemoryID & memory_id) const {
- AKANTU_DEBUG_IN();
- auto memory_it = memories.find(memory_id);
-
- if (memory_it == memories.end()) {
- AKANTU_SILENT_EXCEPTION("StaticMemory as no memory with ID " << memory_id);
- }
-
- AKANTU_DEBUG_OUT();
- return memory_it->second;
-}
-
-/* -------------------------------------------------------------------------- */
-inline const ArrayBase & StaticMemory::getArray(const MemoryID & memory_id,
- const ID & name) const {
- AKANTU_DEBUG_IN();
-
- const ArrayMap & vectors = this->getMemory(memory_id);
-
- auto vectors_it = vectors.find(name);
- if (vectors_it == vectors.end()) {
- AKANTU_SILENT_EXCEPTION("StaticMemory as no array named "
- << name << " for the Memory " << memory_id);
- }
-
- AKANTU_DEBUG_OUT();
- return *(vectors_it->second);
-}
-
-/* -------------------------------------------------------------------------- */
-} // namespace akantu
diff --git a/src/common/aka_static_memory_tmpl.hh b/src/common/aka_static_memory_tmpl.hh
deleted file mode 100644
index a80ab8495..000000000
--- a/src/common/aka_static_memory_tmpl.hh
+++ /dev/null
@@ -1,90 +0,0 @@
-/**
- * @file aka_static_memory_tmpl.hh
- *
- * @author Nicolas Richart <nicolas.richart@epfl.ch>
- *
- * @date creation: Thu Jul 15 2010
- * @date last modification: Tue Feb 20 2018
- *
- * @brief template part of the StaticMemory
- *
- *
- * 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 <http://www.gnu.org/licenses/>.
- *
- */
-/* -------------------------------------------------------------------------- */
-#include "aka_static_memory.hh"
-/* -------------------------------------------------------------------------- */
-
-namespace akantu {
-/* -------------------------------------------------------------------------- */
-template <typename T>
-Array<T> & StaticMemory::smalloc(const MemoryID & memory_id, const ID & name,
- UInt size, UInt nb_component) {
- AKANTU_DEBUG_IN();
-
- MemoryMap::iterator memory_it;
- memory_it = memories.find(memory_id);
-
- if (memory_it == memories.end()) {
- memories[memory_id] = ArrayMap();
- memory_it = memories.find(memory_id);
- }
-
- if ((memory_it->second).find(name) != (memory_it->second).end()) {
- AKANTU_ERROR("The vector \"" << name
- << "\" is already registred in the memory "
- << memory_id);
- }
-
- auto * tmp_vect = new Array<T>(size, nb_component, name);
- (memory_it->second)[name] = tmp_vect;
-
- AKANTU_DEBUG_OUT();
- return *tmp_vect;
-}
-
-/* -------------------------------------------------------------------------- */
-template <typename T>
-Array<T> & StaticMemory::smalloc(const MemoryID & memory_id, const ID & name,
- UInt size, UInt nb_component,
- const T & init_value) {
- AKANTU_DEBUG_IN();
-
- MemoryMap::iterator memory_it;
- memory_it = memories.find(memory_id);
-
- if (memory_it == memories.end()) {
- memories[memory_id] = ArrayMap();
- memory_it = memories.find(memory_id);
- }
-
- if ((memory_it->second).find(name) != (memory_it->second).end()) {
- AKANTU_ERROR("The vector \"" << name
- << "\" is already registred in the memory "
- << memory_id);
- }
-
- auto * tmp_vect = new Array<T>(size, nb_component, init_value, name);
- (memory_it->second)[name] = tmp_vect;
-
- AKANTU_DEBUG_OUT();
- return *tmp_vect;
-}
-
-/* -------------------------------------------------------------------------- */
-} // namespace akantu
diff --git a/src/fe_engine/element_class_structural.hh b/src/fe_engine/element_class_structural.hh
index 2d9ae92a9..76732da6a 100644
--- a/src/fe_engine/element_class_structural.hh
+++ b/src/fe_engine/element_class_structural.hh
@@ -1,253 +1,274 @@
/**
* @file element_class_structural.hh
*
* @author Fabian Barras <fabian.barras@epfl.ch>
* @author Lucas Frerot <lucas.frerot@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
* @author Damien Spielmann <damien.spielmann@epfl.ch>
*
* @date creation: Thu Feb 21 2013
* @date last modification: Tue Feb 20 2018
*
* @brief Specialization of the element classes for structural elements
*
*
* Copyright (©) 2014-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "element_class.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_ELEMENT_CLASS_STRUCTURAL_HH_
#define AKANTU_ELEMENT_CLASS_STRUCTURAL_HH_
namespace akantu {
/// Macro to generate the InterpolationProperty structures for different
/// interpolation types
#define AKANTU_DEFINE_STRUCTURAL_INTERPOLATION_TYPE_PROPERTY( \
itp_type, itp_geom_type, ndof, nb_stress, nb_dnds_cols) \
template <> struct InterpolationProperty<itp_type> { \
static const InterpolationKind kind{_itk_structural}; \
static const UInt nb_nodes_per_element{ \
InterpolationProperty<itp_geom_type>::nb_nodes_per_element}; \
static const InterpolationType itp_geometry_type{itp_geom_type}; \
static const UInt natural_space_dimension{ \
InterpolationProperty<itp_geom_type>::natural_space_dimension}; \
static const UInt nb_degree_of_freedom{ndof}; \
static const UInt nb_stress_components{nb_stress}; \
static const UInt dnds_columns{nb_dnds_cols}; \
}
/* -------------------------------------------------------------------------- */
template <InterpolationType interpolation_type>
class InterpolationElement<interpolation_type, _itk_structural> {
public:
using interpolation_property = InterpolationProperty<interpolation_type>;
/// compute the shape values for a given set of points in natural coordinates
static inline void computeShapes(const Matrix<Real> & natural_coord,
const Matrix<Real> & real_coord,
- Tensor3<Real> & N) {
+ const Matrix<Real> & T, Tensor3<Real> & Ns) {
for (UInt i = 0; i < natural_coord.cols(); ++i) {
- Matrix<Real> n_t = N(i);
- computeShapes(natural_coord(i), real_coord, n_t);
+ Matrix<Real> N_T = Ns(i);
+ Matrix<Real> N(N_T.rows(), N_T.cols());
+ computeShapes(natural_coord(i), real_coord, N);
+ N_T.mul<false, false>(N, T);
}
}
/// compute the shape values for a given point in natural coordinates
static inline void computeShapes(const Vector<Real> & natural_coord,
const Matrix<Real> & real_coord,
Matrix<Real> & N);
+ static inline void computeShapesMass(const Matrix<Real> & natural_coords,
+ const Matrix<Real> & xs,
+ const Matrix<Real> & T,
+ Tensor3<Real> & Ns) {
+ for (UInt i = 0; i < natural_coords.cols(); ++i) {
+ Matrix<Real> N_T = Ns(i);
+ Vector<Real> X = natural_coords(i);
+ Matrix<Real> N(interpolation_property::nb_degree_of_freedom, N_T.cols());
+
+ computeShapes(X, xs, N);
+ N_T.mul<false, false>(N.block(0, 0, N_T.rows(), N_T.cols()), T);
+ }
+ }
+
/// compute shape derivatives (input is dxds) for a set of points
static inline void computeShapeDerivatives(const Tensor3<Real> & Js,
const Tensor3<Real> & DNDSs,
const Matrix<Real> & R,
Tensor3<Real> & Bs) {
for (UInt i = 0; i < Js.size(2); ++i) {
Matrix<Real> J = Js(i);
Matrix<Real> DNDS = DNDSs(i);
Matrix<Real> DNDX(DNDS.rows(), DNDS.cols());
auto inv_J = J.inverse();
DNDX.mul<false, false>(inv_J, DNDS);
Matrix<Real> B_R = Bs(i);
Matrix<Real> B(B_R.rows(), B_R.cols());
arrangeInVoigt(DNDX, B);
B_R.mul<false, false>(B, R);
}
}
/**
* compute @f$ B_{ij} = \frac{\partial N_j}{\partial S_i} @f$ the variation of
* shape functions along with variation of natural coordinates on a given set
* of points in natural coordinates
*/
static inline void computeDNDS(const Matrix<Real> & natural_coord,
const Matrix<Real> & real_coord,
Tensor3<Real> & dnds) {
for (UInt i = 0; i < natural_coord.cols(); ++i) {
Matrix<Real> dnds_t = dnds(i);
computeDNDS(natural_coord(i), real_coord, dnds_t);
}
}
/**
* compute @f$ B_{ij} = \frac{\partial N_j}{\partial S_i} @f$ the variation of
* shape functions along with
* variation of natural coordinates on a given point in natural
* coordinates
*/
static inline void computeDNDS(const Vector<Real> & natural_coord,
const Matrix<Real> & real_coord,
Matrix<Real> & dnds);
/**
* arrange B in Voigt notation from DNDS
*/
static inline void arrangeInVoigt(const Matrix<Real> & dnds,
Matrix<Real> & B) {
// Default implementation assumes dnds is already in Voigt notation
B.deepCopy(dnds);
}
public:
static inline constexpr auto getNbNodesPerInterpolationElement() {
return interpolation_property::nb_nodes_per_element;
}
static inline constexpr auto getShapeSize() {
return interpolation_property::nb_nodes_per_element *
interpolation_property::nb_degree_of_freedom *
interpolation_property::nb_degree_of_freedom;
}
+ static inline constexpr auto getShapeIndependantSize() {
+ return interpolation_property::nb_nodes_per_element *
+ interpolation_property::nb_degree_of_freedom *
+ interpolation_property::nb_stress_components;
+ }
static inline constexpr auto getShapeDerivativesSize() {
return interpolation_property::nb_nodes_per_element *
interpolation_property::nb_degree_of_freedom *
interpolation_property::nb_stress_components;
}
static inline constexpr auto getNaturalSpaceDimension() {
return interpolation_property::natural_space_dimension;
}
static inline constexpr auto getNbDegreeOfFreedom() {
return interpolation_property::nb_degree_of_freedom;
}
static inline constexpr auto getNbStressComponents() {
return interpolation_property::nb_stress_components;
}
};
/// Macro to generate the element class structures for different structural
/// element types
/* -------------------------------------------------------------------------- */
#define AKANTU_DEFINE_STRUCTURAL_ELEMENT_CLASS_PROPERTY( \
elem_type, geom_type, interp_type, parent_el_type, elem_kind, sp, \
gauss_int_type, min_int_order) \
template <> struct ElementClassProperty<elem_type> { \
static const GeometricalType geometrical_type{geom_type}; \
static const InterpolationType interpolation_type{interp_type}; \
static const ElementType parent_element_type{parent_el_type}; \
static const ElementKind element_kind{elem_kind}; \
static const UInt spatial_dimension{sp}; \
static const GaussIntegrationType gauss_integration_type{gauss_int_type}; \
static const UInt polynomial_degree{min_int_order}; \
}
/* -------------------------------------------------------------------------- */
/* ElementClass for structural elements */
/* -------------------------------------------------------------------------- */
template <ElementType element_type>
class ElementClass<element_type, _ek_structural>
: public GeometricalElement<
ElementClassProperty<element_type>::geometrical_type>,
public InterpolationElement<
ElementClassProperty<element_type>::interpolation_type> {
protected:
using geometrical_element =
GeometricalElement<ElementClassProperty<element_type>::geometrical_type>;
using interpolation_element = InterpolationElement<
ElementClassProperty<element_type>::interpolation_type>;
using parent_element =
ElementClass<ElementClassProperty<element_type>::parent_element_type>;
public:
static inline void
computeRotationMatrix(Matrix<Real> & /*R*/, const Matrix<Real> & /*X*/,
const Vector<Real> & /*extra_normal*/) {
AKANTU_TO_IMPLEMENT();
}
/// compute jacobian (or integration variable change factor) for a given point
static inline void computeJMat(const Vector<Real> & natural_coords,
const Matrix<Real> & Xs, Matrix<Real> & J) {
Matrix<Real> dnds(Xs.rows(), Xs.cols());
parent_element::computeDNDS(natural_coords, dnds);
J.mul<false, true>(dnds, Xs);
}
static inline void computeJMat(const Matrix<Real> & natural_coords,
const Matrix<Real> & Xs, Tensor3<Real> & Js) {
for (UInt i = 0; i < natural_coords.cols(); ++i) {
// because non-const l-value reference does not bind to r-value
Matrix<Real> J = Js(i);
computeJMat(Vector<Real>(natural_coords(i)), Xs, J);
}
}
static inline void computeJacobian(const Matrix<Real> & natural_coords,
const Matrix<Real> & node_coords,
Vector<Real> & jacobians) {
using itp = typename interpolation_element::interpolation_property;
Tensor3<Real> Js(itp::natural_space_dimension, itp::natural_space_dimension,
natural_coords.cols());
computeJMat(natural_coords, node_coords, Js);
for (UInt i = 0; i < natural_coords.cols(); ++i) {
Matrix<Real> J = Js(i);
jacobians(i) = J.det();
}
}
static inline void computeRotation(const Matrix<Real> & node_coords,
Matrix<Real> & rotation);
public:
static AKANTU_GET_MACRO_NOT_CONST(Kind, _ek_structural, ElementKind);
static AKANTU_GET_MACRO_NOT_CONST(P1ElementType, _not_defined, ElementType);
static AKANTU_GET_MACRO_NOT_CONST(FacetType, _not_defined, ElementType);
static constexpr auto getFacetType(__attribute__((unused)) UInt t = 0) {
return _not_defined;
}
static constexpr AKANTU_GET_MACRO_NOT_CONST(
SpatialDimension, ElementClassProperty<element_type>::spatial_dimension,
UInt);
static constexpr auto getFacetTypes() {
return ElementClass<_not_defined>::getFacetTypes();
}
};
} // namespace akantu
/* -------------------------------------------------------------------------- */
#include "element_class_hermite_inline_impl.hh"
/* keep order */
#include "element_class_bernoulli_beam_inline_impl.hh"
#include "element_class_kirchhoff_shell_inline_impl.hh"
/* -------------------------------------------------------------------------- */
#endif /* AKANTU_ELEMENT_CLASS_STRUCTURAL_HH_ */
diff --git a/src/fe_engine/element_classes/element_class_bernoulli_beam_inline_impl.hh b/src/fe_engine/element_classes/element_class_bernoulli_beam_inline_impl.hh
index 8145600a2..98b59a223 100644
--- a/src/fe_engine/element_classes/element_class_bernoulli_beam_inline_impl.hh
+++ b/src/fe_engine/element_classes/element_class_bernoulli_beam_inline_impl.hh
@@ -1,223 +1,233 @@
/**
* @file element_class_bernoulli_beam_inline_impl.hh
*
* @author Fabian Barras <fabian.barras@epfl.ch>
* @author Lucas Frerot <lucas.frerot@epfl.ch>
*
* @date creation: Fri Jul 15 2011
* @date last modification: Mon Feb 19 2018
*
* @brief Specialization of the element_class class for the type
* _bernoulli_beam_2
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*
* @verbatim
--x-----q1----|----q2-----x---> x
-1 0 1
@endverbatim
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_static_if.hh"
#include "element_class_structural.hh"
//#include "aka_element_classes_info.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_ELEMENT_CLASS_BERNOULLI_BEAM_INLINE_IMPL_HH_
#define AKANTU_ELEMENT_CLASS_BERNOULLI_BEAM_INLINE_IMPL_HH_
namespace akantu {
/* -------------------------------------------------------------------------- */
AKANTU_DEFINE_STRUCTURAL_INTERPOLATION_TYPE_PROPERTY(_itp_bernoulli_beam_2,
_itp_lagrange_segment_2, 3,
2, 6);
AKANTU_DEFINE_STRUCTURAL_INTERPOLATION_TYPE_PROPERTY(_itp_bernoulli_beam_3,
_itp_lagrange_segment_2, 6,
4, 6);
AKANTU_DEFINE_STRUCTURAL_ELEMENT_CLASS_PROPERTY(_bernoulli_beam_2,
_gt_segment_2,
_itp_bernoulli_beam_2,
_segment_2, _ek_structural, 2,
_git_segment, 3);
AKANTU_DEFINE_STRUCTURAL_ELEMENT_CLASS_PROPERTY(_bernoulli_beam_3,
_gt_segment_2,
_itp_bernoulli_beam_3,
_segment_2, _ek_structural, 3,
_git_segment, 3);
/* -------------------------------------------------------------------------- */
template <>
inline void
InterpolationElement<_itp_bernoulli_beam_2, _itk_structural>::computeShapes(
const Vector<Real> & natural_coords, const Matrix<Real> & real_coord,
Matrix<Real> & N) {
Vector<Real> L(2);
InterpolationElement<_itp_lagrange_segment_2, _itk_lagrangian>::computeShapes(
natural_coords, L);
Matrix<Real> H(2, 4);
InterpolationElement<_itp_hermite_2, _itk_structural>::computeShapes(
natural_coords, real_coord, H);
// clang-format off
// u1 v1 t1 u2 v2 t2
N = {{L(0), 0 , 0 , L(1), 0 , 0 }, // u
{0 , H(0, 0), H(0, 1), 0 , H(0, 2), H(0, 3)}, // v
{0 , H(1, 0), H(1, 1), 0 , H(1, 2), H(1, 3)}}; // theta
// clang-format on
}
template <>
inline void
InterpolationElement<_itp_bernoulli_beam_3, _itk_structural>::computeShapes(
const Vector<Real> & natural_coords, const Matrix<Real> & real_coord,
Matrix<Real> & N) {
Vector<Real> L(2);
InterpolationElement<_itp_lagrange_segment_2, _itk_lagrangian>::computeShapes(
natural_coords, L);
Matrix<Real> H(2, 4);
InterpolationElement<_itp_hermite_2, _itk_structural>::computeShapes(
natural_coords, real_coord, H);
// clang-format off
- // u1 v1 w1 x1 y1 z1 u2 v2 w2 x2 y2 z2
- N = {{L(0), 0 , 0 , 0 , 0 , 0 , L(1), 0 , 0 , 0 , 0 , 0 }, // u
- {0 , H(0, 0), 0 , 0 , H(0, 1), 0 , 0 , H(0, 2), 0 , 0 , H(0, 3), 0 }, // v
- {0 , 0 , H(0, 0), 0 , 0 , H(0, 1), 0 , 0 , H(0, 2), 0 , 0 , H(0, 3)}, // w
- {0 , 0 , 0 , L(0), 0 , 0 , 0 , 0 , 0 , L(1), 0 , 0 }, // thetax
- {0 , H(1, 0), 0 , 0 , H(1, 1), 0 , 0 , H(1, 2), 0 , 0 , H(1, 3), 0 }, // thetay
- {0 , 0 , H(1, 0), 0 , 0 , H(1, 1), 0 , 0 , H(1, 2), 0 , 0 , H(1, 3)}}; // thetaz
+ // u1 v1 w1 tx1 ty1 tz1 u2 v2 w2 tx2 ty2 tz2
+ N = {{L(0), 0 , 0 , 0 , 0 , 0 , L(1), 0 , 0 , 0 , 0 , 0 }, // u
+ {0 , H(0, 0), 0 , 0 , 0 , H(0, 1), 0 , H(0, 2), 0 , 0 , 0 , H(0, 3)}, // v
+ {0 , 0 , H(0, 0), 0 , -H(0, 1), 0 , 0 , 0 , H(0, 2), 0 , -H(0, 3), 0 }, // w
+ {0 , 0 , 0 , L(0), 0 , 0 , 0 , 0 , 0 , L(1), 0 , 0 }, // thetax
+ {0 , 0 , H(1, 0), 0 , -H(1, 1), 0 , 0 , 0 , H(1, 2), 0 , -H(1, 3), 0 }, // thetay
+ {0 , H(1, 0), 0 , 0 , 0 , H(1, 1), 0 , H(1, 2), 0 , 0 , 0 , H(1, 3)}}; // thetaz
// clang-format on
}
+/* -------------------------------------------------------------------------- */
+#if 0
+template <>
+inline void
+InterpolationElement<_itp_bernoulli_beam_3, _itk_structural>::computeShapesDisplacements(
+ const Vector<Real> & natural_coords, const Matrix<Real> & real_coord,
+ Matrix<Real> & N) {
+}
+#endif
+
/* -------------------------------------------------------------------------- */
template <>
inline void
InterpolationElement<_itp_bernoulli_beam_2, _itk_structural>::computeDNDS(
const Vector<Real> & natural_coords, const Matrix<Real> & real_coord,
Matrix<Real> & dnds) {
Matrix<Real> L(1, 2);
InterpolationElement<_itp_lagrange_segment_2, _itk_lagrangian>::computeDNDS(
natural_coords, L);
Matrix<Real> H(1, 4);
InterpolationElement<_itp_hermite_2, _itk_structural>::computeDNDS(
natural_coords, real_coord, H);
// Storing the derivatives in dnds
dnds.block(L, 0, 0);
dnds.block(H, 0, 2);
}
/* -------------------------------------------------------------------------- */
template <>
inline void
InterpolationElement<_itp_bernoulli_beam_2, _itk_structural>::arrangeInVoigt(
const Matrix<Real> & dnds, Matrix<Real> & B) {
auto L = dnds.block(0, 0, 1, 2); // Lagrange shape derivatives
auto H = dnds.block(0, 2, 1, 4); // Hermite shape derivatives
// clang-format off
- // u1 v1 t1 u2 v2 t2
- B = {{L(0, 0), 0, 0, L(0, 1), 0, 0 },
- {0, H(0, 0), H(0, 1), 0, H(0, 2), H(0, 3)}};
+ // u1 v1 t1 u2 v2 t2
+ B = {{L(0, 0), 0, 0, L(0, 1), 0, 0 },
+ {0, -H(0, 0), -H(0, 1), 0, -H(0, 2), -H(0, 3)}};
// clang-format on
}
/* -------------------------------------------------------------------------- */
template <>
inline void
InterpolationElement<_itp_bernoulli_beam_3, _itk_structural>::computeDNDS(
const Vector<Real> & natural_coords, const Matrix<Real> & real_coord,
Matrix<Real> & dnds) {
InterpolationElement<_itp_bernoulli_beam_2, _itk_structural>::computeDNDS(
natural_coords, real_coord, dnds);
}
/* -------------------------------------------------------------------------- */
template <>
inline void
InterpolationElement<_itp_bernoulli_beam_3, _itk_structural>::arrangeInVoigt(
const Matrix<Real> & dnds, Matrix<Real> & B) {
auto L = dnds.block(0, 0, 1, 2); // Lagrange shape derivatives
auto H = dnds.block(0, 2, 1, 4); // Hermite shape derivatives
// clang-format off
- // u1 v1 w1 x1 y1 z1 u2 v2 w2 x2 y2 z2
- B = {{L(0, 0), 0 , 0 , 0 , 0 , 0 , L(0, 1), 0 , 0 , 0 , 0 , 0 }, // eps
- {0 , H(0, 0), 0 , 0 , 0 , H(0, 1), 0 , H(0, 2), 0 , 0 , 0 , H(0, 3)}, // chi strong axis
- {0 , 0 ,-H(0, 0), 0 , H(0, 1), 0 , 0 , 0 ,-H(0, 2), 0 , H(0, 3), 0 }, // chi weak axis
- {0 , 0 , 0 , L(0, 0), 0 , 0 , 0 , 0 , 0 , L(0, 1), 0 , 0 }}; // chi torsion
+ // u1 v1 w1 x1 y1 z1 u2 v2 w2 x2 y2 z2
+ B = {{L(0, 0), 0 , 0 , 0 , 0 , 0 , L(0, 1), 0 , 0 , 0 , 0 , 0 }, // eps
+ {0 , -H(0, 0), 0 , 0 , 0 , -H(0, 1), 0 , -H(0, 2), 0 , 0 , 0 ,-H(0, 3)}, // chi strong axis
+ {0 , 0 , -H(0, 0), 0 , H(0, 1) , 0 , 0 , 0 , -H(0, 2) , 0 , H(0, 3) , 0 }, // chi weak axis
+ {0 , 0 , 0 , L(0, 0), 0 , 0 , 0 , 0 , 0 , L(0, 1), 0 , 0 }}; // chi torsion
// clang-format on
}
/* -------------------------------------------------------------------------- */
template <>
inline void ElementClass<_bernoulli_beam_2>::computeRotationMatrix(
Matrix<Real> & R, const Matrix<Real> & X, const Vector<Real> & /*n*/) {
Vector<Real> x2 = X(1); // X2
Vector<Real> x1 = X(0); // X1
auto cs = (x2 - x1);
cs.normalize();
auto c = cs(0);
auto s = cs(1);
// clang-format off
/// Definition of the rotation matrix
R = {{ c, s, 0.},
{-s, c, 0.},
{ 0., 0., 1.}};
// clang-format on
}
/* -------------------------------------------------------------------------- */
template <>
inline void ElementClass<_bernoulli_beam_3>::computeRotationMatrix(
Matrix<Real> & R, const Matrix<Real> & X, const Vector<Real> & n) {
Vector<Real> x2 = X(1); // X2
Vector<Real> x1 = X(0); // X1
auto dim = X.rows();
auto x = (x2 - x1);
x.normalize();
auto x_n = x.crossProduct(n);
Matrix<Real> Pe = {{1., 0., 0.}, {0., -1., 0.}, {0., 0., 1.}};
Matrix<Real> Pg(dim, dim);
Pg(0) = x;
Pg(1) = x_n;
Pg(2) = n;
Pe *= Pg.inverse();
R.zero();
/// Definition of the rotation matrix
for (UInt i = 0; i < dim; ++i) {
for (UInt j = 0; j < dim; ++j) {
R(i + dim, j + dim) = R(i, j) = Pe(i, j);
}
}
}
} // namespace akantu
#endif /* AKANTU_ELEMENT_CLASS_BERNOULLI_BEAM_INLINE_IMPL_HH_ */
diff --git a/src/fe_engine/element_classes/element_class_hermite_inline_impl.hh b/src/fe_engine/element_classes/element_class_hermite_inline_impl.hh
index cbb1c5949..77ad5d0ff 100644
--- a/src/fe_engine/element_classes/element_class_hermite_inline_impl.hh
+++ b/src/fe_engine/element_classes/element_class_hermite_inline_impl.hh
@@ -1,175 +1,176 @@
/**
* @file element_class_hermite_inline_impl.hh
*
* @author Fabian Barras <fabian.barras@epfl.ch>
* @author Lucas Frerot <lucas.frerot@epfl.ch>
*
* @date creation: Fri Nov 10 2017
* @date last modification: Mon Feb 19 2018
*
* @brief Specialization of the element_class class for the type
* _hermite
*
*
* Copyright (©) 2016-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 <http://www.gnu.org/licenses/>.
*
*
* @verbatim
--x-----q1----|----q2-----x---> x
-1 0 1
@endverbatim
*
* @f[
* \begin{array}{ll}
* M_1(\xi) &= 1/4(\xi^{3}/-3\xi+2)\\
* M_2(\xi) &= -1/4(\xi^{3}-3\xi-2)
* \end{array}
*
* \begin{array}{ll}
* L_1(\xi) &= 1/4(\xi^{3}-\xi^{2}-\xi+1)\\
* L_2(\xi) &= 1/4(\xi^{3}+\xi^{2}-\xi-1)
* \end{array}
*
* \begin{array}{ll}
* M'_1(\xi) &= 3/4(\xi^{2}-1)\\
* M'_2(\xi) &= -3/4(\xi^{2}-1)
* \end{array}
*
* \begin{array}{ll}
* L'_1(\xi) &= 1/4(3\xi^{2}-2\xi-1)\\
* L'_2(\xi) &= 1/4(3\xi^{2}+2\xi-1)
* \end{array}
*@f]
*
*
*@f[
* \begin{array}{ll}
* N'_1(\xi) &= -1/2\\
* N'_2(\xi) &= 1/2
* \end{array}]
*
* \begin{array}{ll}
* -M''_1(\xi) &= -3\xi/2\\
* -M''_2(\xi) &= 3\xi/2\\
* \end{array}
*
* \begin{array}{ll}
* -L''_1(\xi) &= -1/2a(3\xi/a-1)\\
* -L''_2(\xi) &= -1/2a(3\xi/a+1)
* \end{array}
*@f]
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_static_if.hh"
#include "element_class_structural.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_ELEMENT_CLASS_HERMITE_INLINE_IMPL_HH_
#define AKANTU_ELEMENT_CLASS_HERMITE_INLINE_IMPL_HH_
namespace akantu {
/* -------------------------------------------------------------------------- */
AKANTU_DEFINE_STRUCTURAL_INTERPOLATION_TYPE_PROPERTY(_itp_hermite_2,
_itp_lagrange_segment_2, 2,
1, 4);
/* -------------------------------------------------------------------------- */
namespace {
namespace details {
inline Real computeLength(const Matrix<Real> & real_coord) {
Vector<Real> x1 = real_coord(0);
Vector<Real> x2 = real_coord(1);
return x1.distance(x2);
}
inline void computeShapes(const Vector<Real> & natural_coords, Real a,
Matrix<Real> & N) {
/// natural coordinate
Real xi = natural_coords(0);
-
+ auto xi2 = xi * xi;
+ auto xi3 = xi * xi * xi;
// Cubic Hermite splines interpolating displacement
- auto M1 = 1. / 4. * Math::pow<2>(xi - 1) * (xi + 2);
- auto M2 = 1. / 4. * Math::pow<2>(xi + 1) * (2 - xi);
- auto L1 = a / 4. * Math::pow<2>(xi - 1) * (xi + 1);
- auto L2 = a / 4. * Math::pow<2>(xi + 1) * (xi - 1);
+ auto M1 = 1. / 4. * (2. - 3. * xi + xi3);
+ auto M2 = 1. / 4. * (2. + 3. * xi - xi3);
+ auto L1 = a / 4. * (1 - xi - xi2 + xi3);
+ auto L2 = a / 4. * (-1 - xi + xi2 + xi3);;
#if 1 // Version where we also interpolate the rotations
// Derivatives (with respect to x) of previous functions interpolating
// rotations
- auto M1_ = 3. / (4. * a) * (xi * xi - 1);
- auto M2_ = 3. / (4. * a) * (1 - xi * xi);
- auto L1_ = 1 / 4. * (3 * xi * xi - 2 * xi - 1);
- auto L2_ = 1 / 4. * (3 * xi * xi + 2 * xi - 1);
+ auto M1_ = 3. / (4. * a) * (xi2 - 1);
+ auto M2_ = 3. / (4. * a) * (1 - xi2);
+ auto L1_ = 1 / 4. * (3 * xi2 - 2 * xi - 1);
+ auto L2_ = 1 / 4. * (3 * xi2 + 2 * xi - 1);
// clang-format off
// v1 t1 v2 t2
N = {{M1 , L1 , M2 , L2}, // displacement interpolation
{M1_, L1_, M2_, L2_}}; // rotation interpolation
// clang-format on
#else // Version where we only interpolate displacements
// clang-format off
// v1 t1 v2 t2
N = {{M1, L1, M2, L2}};
// clang-format on
#endif
}
/* ---------------------------------------------------------------------- */
inline void computeDNDS(const Vector<Real> & natural_coords, Real a,
Matrix<Real> & B) {
// natural coordinate
Real xi = natural_coords(0);
// Derivatives with respect to xi for rotations
auto M1 = 3. / 2. * xi;
auto M2 = 3. / 2. * (-xi);
- auto L1 = a / 2. * (3 * xi - 1);
- auto L2 = a / 2. * (3 * xi + 1);
+ auto L1 = 1. * a / 2. * (3 * xi - 1);
+ auto L2 = 1. * a / 2. * (3 * xi + 1);
// v1 t1 v2 t2
B = {{M1, L1, M2, L2}}; // computing curvature : {chi} = [B]{d}
B /= a; // to account for first order deriv w/r to x
}
} // namespace details
} // namespace
/* -------------------------------------------------------------------------- */
template <>
inline void
InterpolationElement<_itp_hermite_2, _itk_structural>::computeShapes(
const Vector<Real> & natural_coords, const Matrix<Real> & real_coord,
Matrix<Real> & N) {
auto L = details::computeLength(real_coord);
details::computeShapes(natural_coords, L / 2, N);
}
/* -------------------------------------------------------------------------- */
template <>
inline void InterpolationElement<_itp_hermite_2, _itk_structural>::computeDNDS(
const Vector<Real> & natural_coords, const Matrix<Real> & real_coord,
Matrix<Real> & B) {
auto L = details::computeLength(real_coord);
details::computeDNDS(natural_coords, L / 2, B);
}
} // namespace akantu
#endif /* AKANTU_ELEMENT_CLASS_HERMITE_INLINE_IMPL_HH_ */
diff --git a/src/fe_engine/fe_engine.cc b/src/fe_engine/fe_engine.cc
index dffe416c8..cb50656b5 100644
--- a/src/fe_engine/fe_engine.cc
+++ b/src/fe_engine/fe_engine.cc
@@ -1,91 +1,89 @@
/**
* @file fe_engine.cc
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Tue Jul 20 2010
* @date last modification: Thu Feb 01 2018
*
* @brief Implementation of the FEEngine class
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "fe_engine.hh"
-#include "aka_memory.hh"
#include "mesh.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
-FEEngine::FEEngine(Mesh & mesh, UInt element_dimension, const ID & id,
- MemoryID memory_id)
- : Memory(id, memory_id), mesh(mesh),
- normals_on_integration_points("normals_on_quad_points", id, memory_id) {
+FEEngine::FEEngine(Mesh & mesh, UInt element_dimension, const ID & id)
+ : mesh(mesh),
+ normals_on_integration_points("normals_on_quad_points", id) {
AKANTU_DEBUG_IN();
this->element_dimension = (element_dimension != _all_dimensions)
? element_dimension
: mesh.getSpatialDimension();
this->mesh.registerEventHandler(*this, _ehp_fe_engine);
init();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void FEEngine::init() {}
/* -------------------------------------------------------------------------- */
FEEngine::~FEEngine() {
AKANTU_DEBUG_IN();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
typename FEEngine::ElementTypesIteratorHelper
FEEngine::elementTypes(UInt dim, GhostType ghost_type, ElementKind kind) const {
return this->getIntegratorInterface().getJacobians().elementTypes(
dim, ghost_type, kind);
}
/* -------------------------------------------------------------------------- */
void FEEngine::printself(std::ostream & stream, int indent) const {
std::string space(indent, AKANTU_INDENT);
stream << space << "FEEngine [" << std::endl;
stream << space << " + id : " << id << std::endl;
stream << space << " + element dimension : " << element_dimension
<< std::endl;
stream << space << " + mesh [" << std::endl;
mesh.printself(stream, indent + 2);
stream << space << AKANTU_INDENT << "]" << std::endl;
stream << space << "]" << std::endl;
}
/* -------------------------------------------------------------------------- */
} // namespace akantu
diff --git a/src/fe_engine/fe_engine.hh b/src/fe_engine/fe_engine.hh
index 8be4ae903..6e9f629fb 100644
--- a/src/fe_engine/fe_engine.hh
+++ b/src/fe_engine/fe_engine.hh
@@ -1,368 +1,371 @@
/**
* @file fe_engine.hh
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Tue Feb 20 2018
*
* @brief FEM class
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
-#include "aka_memory.hh"
#include "element_type_map.hh"
#include "mesh_events.hh"
/* -------------------------------------------------------------------------- */
#include <functional>
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_FE_ENGINE_HH_
#define AKANTU_FE_ENGINE_HH_
namespace akantu {
class Mesh;
class Integrator;
class ShapeFunctions;
class DOFManager;
class Element;
} // namespace akantu
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
/**
* The generic FEEngine class derived in a FEEngineTemplate class
* containing the
* shape functions and the integration method
*/
-class FEEngine : protected Memory, public MeshEventHandler {
+class FEEngine : public MeshEventHandler {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
FEEngine(Mesh & mesh, UInt element_dimension = _all_dimensions,
- const ID & id = "fem", MemoryID memory_id = 0);
+ const ID & id = "fem");
~FEEngine() override;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// pre-compute all the shape functions, their derivatives and the jacobians
virtual void
initShapeFunctions(GhostType ghost_type = _not_ghost) = 0;
/// extract the nodal values and store them per element
template <typename T>
static void extractNodalToElementField(
const Mesh & mesh, const Array<T> & nodal_f, Array<T> & elemental_f,
ElementType type, GhostType ghost_type = _not_ghost,
const Array<UInt> & filter_elements = empty_filter);
/// filter a field
template <typename T>
static void
filterElementalData(const Mesh & mesh, const Array<T> & elem_f,
Array<T> & filtered_f, ElementType type,
GhostType ghost_type = _not_ghost,
const Array<UInt> & filter_elements = empty_filter);
/* ------------------------------------------------------------------------ */
/* Integration method bridges */
/* ------------------------------------------------------------------------ */
/// integrate f for all elements of type "type"
virtual void
integrate(const Array<Real> & f, Array<Real> & intf,
UInt nb_degree_of_freedom, ElementType type,
GhostType ghost_type = _not_ghost,
const Array<UInt> & filter_elements = empty_filter) const = 0;
/// integrate a scalar value f on all elements of type "type"
virtual Real
integrate(const Array<Real> & f, ElementType type,
GhostType ghost_type = _not_ghost,
const Array<UInt> & filter_elements = empty_filter) const = 0;
/// integrate f for all integration points of type "type" but don't sum over
/// all integration points
virtual void integrateOnIntegrationPoints(
const Array<Real> & f, Array<Real> & intf, UInt nb_degree_of_freedom,
ElementType type, GhostType ghost_type = _not_ghost,
const Array<UInt> & filter_elements = empty_filter) const = 0;
/// integrate one element scalar value on all elements of type "type"
virtual Real integrate(const Vector<Real> & f, ElementType type,
UInt index,
GhostType ghost_type = _not_ghost) const = 0;
/* ------------------------------------------------------------------------ */
/* compatibility with old FEEngine fashion */
/* ------------------------------------------------------------------------ */
/// get the number of integration points
virtual UInt
getNbIntegrationPoints(ElementType type,
GhostType ghost_type = _not_ghost) const = 0;
/// get the precomputed shapes
const virtual Array<Real> &
getShapes(ElementType type, GhostType ghost_type = _not_ghost,
UInt id = 0) const = 0;
/// get the derivatives of shapes
const virtual Array<Real> &
getShapesDerivatives(ElementType type,
GhostType ghost_type = _not_ghost,
UInt id = 0) const = 0;
/// get integration points
const virtual Matrix<Real> &
getIntegrationPoints(ElementType type,
GhostType ghost_type = _not_ghost) const = 0;
/* ------------------------------------------------------------------------ */
/* Shape method bridges */
/* ------------------------------------------------------------------------ */
/// Compute the gradient nablauq on the integration points of an element type
/// from nodal values u
virtual void gradientOnIntegrationPoints(
const Array<Real> & u, Array<Real> & nablauq,
UInt nb_degree_of_freedom, ElementType type,
GhostType ghost_type = _not_ghost,
const Array<UInt> & filter_elements = empty_filter) const = 0;
/// Interpolate a nodal field u at the integration points of an element type
/// -> uq
virtual void interpolateOnIntegrationPoints(
const Array<Real> & u, Array<Real> & uq, UInt nb_degree_of_freedom,
ElementType type, GhostType ghost_type = _not_ghost,
const Array<UInt> & filter_elements = empty_filter) const = 0;
/// Interpolate a nodal field u at the integration points of many element
/// types -> uq
virtual void interpolateOnIntegrationPoints(
const Array<Real> & u, ElementTypeMapArray<Real> & uq,
const ElementTypeMapArray<UInt> * filter_elements = nullptr) const = 0;
/// pre multiplies a tensor by the shapes derivaties
virtual void
computeBtD(const Array<Real> & Ds, Array<Real> & BtDs,
ElementType type,
GhostType ghost_type = _not_ghost,
const Array<UInt> & filter_elements = empty_filter) const = 0;
/// left and right multiplies a tensor by the shapes derivaties
virtual void
computeBtDB(const Array<Real> & Ds, Array<Real> & BtDBs, UInt order_d,
ElementType type,
GhostType ghost_type = _not_ghost,
const Array<UInt> & filter_elements = empty_filter) const = 0;
/// left multiples a vector by the shape functions
virtual void
computeNtb(const Array<Real> & bs, Array<Real> & Ntbs,
ElementType type,
GhostType ghost_type = _not_ghost,
const Array<UInt> & filter_elements = empty_filter) const = 0;
/// Compute the interpolation point position in the global coordinates for
/// many element types
virtual void computeIntegrationPointsCoordinates(
ElementTypeMapArray<Real> & integration_points_coordinates,
const ElementTypeMapArray<UInt> * filter_elements = nullptr) const = 0;
/// Compute the interpolation point position in the global coordinates for an
/// element type
virtual void computeIntegrationPointsCoordinates(
Array<Real> & integration_points_coordinates, ElementType type,
GhostType ghost_type = _not_ghost,
const Array<UInt> & filter_elements = empty_filter) const = 0;
/// Build pre-computed matrices for interpolation of field form integration
/// points at other given positions (interpolation_points)
virtual void initElementalFieldInterpolationFromIntegrationPoints(
const ElementTypeMapArray<Real> & interpolation_points_coordinates,
ElementTypeMapArray<Real> & interpolation_points_coordinates_matrices,
ElementTypeMapArray<Real> & integration_points_coordinates_inv_matrices,
const ElementTypeMapArray<UInt> * element_filter) const = 0;
/// interpolate field at given position (interpolation_points) from given
/// values of this field at integration points (field)
virtual void interpolateElementalFieldFromIntegrationPoints(
const ElementTypeMapArray<Real> & field,
const ElementTypeMapArray<Real> & interpolation_points_coordinates,
ElementTypeMapArray<Real> & result, GhostType ghost_type,
const ElementTypeMapArray<UInt> * element_filter) const = 0;
/// Interpolate field at given position from given values of this field at
/// integration points (field)
/// using matrices precomputed with
/// initElementalFieldInterplationFromIntegrationPoints
virtual void interpolateElementalFieldFromIntegrationPoints(
const ElementTypeMapArray<Real> & field,
const ElementTypeMapArray<Real> &
interpolation_points_coordinates_matrices,
const ElementTypeMapArray<Real> &
integration_points_coordinates_inv_matrices,
ElementTypeMapArray<Real> & result, GhostType ghost_type,
const ElementTypeMapArray<UInt> * element_filter) const = 0;
/// interpolate on a phyiscal point inside an element
virtual void interpolate(const Vector<Real> & real_coords,
const Matrix<Real> & nodal_values,
Vector<Real> & interpolated,
const Element & element) const = 0;
/// compute the shape on a provided point
virtual void
computeShapes(const Vector<Real> & real_coords, UInt elem,
ElementType type, Vector<Real> & shapes,
GhostType ghost_type = _not_ghost) const = 0;
/// compute the shape derivatives on a provided point
virtual void
computeShapeDerivatives(const Vector<Real> & real_coords, UInt element,
ElementType type,
Matrix<Real> & shape_derivatives,
GhostType ghost_type = _not_ghost) const = 0;
/// assembles the lumped version of @f[ \int N^t rho N @f]
virtual void assembleFieldLumped(
const std::function<void(Matrix<Real> &, const Element &)> & field_funct,
const ID & matrix_id, const ID & dof_id, DOFManager & dof_manager,
ElementType type, GhostType ghost_type = _not_ghost) const = 0;
/// assembles the matrix @f[ \int N^t rho N @f]
virtual void assembleFieldMatrix(
const std::function<void(Matrix<Real> &, const Element &)> & field_funct,
const ID & matrix_id, const ID & dof_id, DOFManager & dof_manager,
ElementType type, GhostType ghost_type = _not_ghost) const = 0;
/* ------------------------------------------------------------------------ */
/* Other methods */
/* ------------------------------------------------------------------------ */
/// pre-compute normals on integration points
virtual void computeNormalsOnIntegrationPoints(
GhostType ghost_type = _not_ghost) = 0;
/// pre-compute normals on integration points
virtual void computeNormalsOnIntegrationPoints(
const Array<Real> & /*field*/,
GhostType /*ghost_type*/ = _not_ghost) {
AKANTU_TO_IMPLEMENT();
}
/// pre-compute normals on integration points
virtual void computeNormalsOnIntegrationPoints(
const Array<Real> & /*field*/, Array<Real> & /*normal*/,
ElementType /*type*/,
GhostType /*ghost_type*/ = _not_ghost) const {
AKANTU_TO_IMPLEMENT();
}
/// function to print the containt of the class
virtual void printself(std::ostream & stream, int indent = 0) const;
private:
/// initialise the class
void init();
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
using ElementTypesIteratorHelper =
ElementTypeMapArray<Real, ElementType>::ElementTypesIteratorHelper;
ElementTypesIteratorHelper elementTypes(UInt dim = _all_dimensions,
GhostType ghost_type = _not_ghost,
ElementKind kind = _ek_regular) const;
/// get the dimension of the element handeled by this fe_engine object
AKANTU_GET_MACRO(ElementDimension, element_dimension, UInt);
/// get the mesh contained in the fem object
AKANTU_GET_MACRO(Mesh, mesh, const Mesh &);
/// get the mesh contained in the fem object
AKANTU_GET_MACRO_NOT_CONST(Mesh, mesh, Mesh &);
/// get the in-radius of an element
static inline Real getElementInradius(const Matrix<Real> & coord,
ElementType type);
/// get the normals on integration points
AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(NormalsOnIntegrationPoints,
normals_on_integration_points, Real);
/// get cohesive element type for a given facet type
static inline ElementType
getCohesiveElementType(ElementType type_facet);
/// get igfem element type for a given regular type
static inline Vector<ElementType>
getIGFEMElementTypes(ElementType type);
/// get the interpolation element associated to an element type
static inline InterpolationType
getInterpolationType(ElementType el_type);
/// get the shape function class (probably useless: see getShapeFunction in
/// fe_engine_template.hh)
virtual const ShapeFunctions & getShapeFunctionsInterface() const = 0;
/// get the integrator class (probably useless: see getIntegrator in
/// fe_engine_template.hh)
virtual const Integrator & getIntegratorInterface() const = 0;
+ AKANTU_GET_MACRO(ID, id, ID);
+
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
+ ID id;
+
/// spatial dimension of the problem
UInt element_dimension;
/// the mesh on which all computation are made
Mesh & mesh;
/// normals at integration points
ElementTypeMapArray<Real> normals_on_integration_points;
};
/* -------------------------------------------------------------------------- */
/* inline functions */
/* -------------------------------------------------------------------------- */
/// standard output stream operator
inline std::ostream & operator<<(std::ostream & stream,
const FEEngine & _this) {
_this.printself(stream);
return stream;
}
} // namespace akantu
#include "fe_engine_inline_impl.hh"
#include "fe_engine_template.hh"
#endif /* AKANTU_FE_ENGINE_HH_ */
diff --git a/src/fe_engine/fe_engine_template.hh b/src/fe_engine/fe_engine_template.hh
index d53bc08dc..ba9c1ca52 100644
--- a/src/fe_engine/fe_engine_template.hh
+++ b/src/fe_engine/fe_engine_template.hh
@@ -1,426 +1,426 @@
/**
* @file fe_engine_template.hh
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Mon Jan 29 2018
*
* @brief templated class that calls integration and shape objects
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "fe_engine.hh"
#include "integrator.hh"
#include "shape_functions.hh"
/* -------------------------------------------------------------------------- */
#include <type_traits>
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_FE_ENGINE_TEMPLATE_HH_
#define AKANTU_FE_ENGINE_TEMPLATE_HH_
namespace akantu {
class DOFManager;
namespace fe_engine {
namespace details {
template <ElementKind> struct AssembleLumpedTemplateHelper;
template <ElementKind> struct AssembleFieldMatrixHelper;
} // namespace details
} // namespace fe_engine
template <ElementKind, typename> struct AssembleFieldMatrixStructHelper;
struct DefaultIntegrationOrderFunctor {
template <ElementType type> static inline constexpr int getOrder() {
return ElementClassProperty<type>::polynomial_degree;
}
};
/* -------------------------------------------------------------------------- */
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind = _ek_regular,
class IntegrationOrderFunctor = DefaultIntegrationOrderFunctor>
class FEEngineTemplate : public FEEngine {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
using Integ = I<kind, IntegrationOrderFunctor>;
using Shape = S<kind>;
FEEngineTemplate(Mesh & mesh, UInt spatial_dimension = _all_dimensions,
- const ID & id = "fem", MemoryID memory_id = 0);
+ const ID & id = "fem");
~FEEngineTemplate() override;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// pre-compute all the shape functions, their derivatives and the jacobians
void initShapeFunctions(GhostType ghost_type = _not_ghost) override;
void initShapeFunctions(const Array<Real> & nodes,
GhostType ghost_type = _not_ghost);
/* ------------------------------------------------------------------------ */
/* Integration method bridges */
/* ------------------------------------------------------------------------ */
/// integrate f for all elements of type "type"
void
integrate(const Array<Real> & f, Array<Real> & intf,
UInt nb_degree_of_freedom, ElementType type,
GhostType ghost_type = _not_ghost,
const Array<UInt> & filter_elements = empty_filter) const override;
/// integrate a scalar value on all elements of type "type"
Real
integrate(const Array<Real> & f, ElementType type,
GhostType ghost_type = _not_ghost,
const Array<UInt> & filter_elements = empty_filter) const override;
/// integrate one element scalar value on all elements of type "type"
Real integrate(const Vector<Real> & f, ElementType type, UInt index,
GhostType ghost_type = _not_ghost) const override;
/// integrate partially around an integration point (@f$ intf_q = f_q * J_q *
/// w_q @f$)
void integrateOnIntegrationPoints(
const Array<Real> & f, Array<Real> & intf, UInt nb_degree_of_freedom,
ElementType type, GhostType ghost_type = _not_ghost,
const Array<UInt> & filter_elements = empty_filter) const override;
/// interpolate on a phyiscal point inside an element
void interpolate(const Vector<Real> & real_coords,
const Matrix<Real> & nodal_values,
Vector<Real> & interpolated,
const Element & element) const override;
/// get the number of integration points
UInt getNbIntegrationPoints(
ElementType type,
GhostType ghost_type = _not_ghost) const override;
/// get shapes precomputed
const Array<Real> & getShapes(ElementType type,
GhostType ghost_type = _not_ghost,
UInt id = 0) const override;
/// get the derivatives of shapes
const Array<Real> &
getShapesDerivatives(ElementType type,
GhostType ghost_type = _not_ghost,
UInt id = 0) const override;
/// get integration points
const inline Matrix<Real> & getIntegrationPoints(
ElementType type,
GhostType ghost_type = _not_ghost) const override;
/* ------------------------------------------------------------------------ */
/* Shape method bridges */
/* ------------------------------------------------------------------------ */
/// compute the gradient of a nodal field on the integration points
void gradientOnIntegrationPoints(
const Array<Real> & u, Array<Real> & nablauq,
UInt nb_degree_of_freedom, ElementType type,
GhostType ghost_type = _not_ghost,
const Array<UInt> & filter_elements = empty_filter) const override;
/// interpolate a nodal field on the integration points
void interpolateOnIntegrationPoints(
const Array<Real> & u, Array<Real> & uq, UInt nb_degree_of_freedom,
ElementType type, GhostType ghost_type = _not_ghost,
const Array<UInt> & filter_elements = empty_filter) const override;
/// interpolate a nodal field on the integration points given a
/// by_element_type
void interpolateOnIntegrationPoints(
const Array<Real> & u, ElementTypeMapArray<Real> & uq,
const ElementTypeMapArray<UInt> * filter_elements =
nullptr) const override;
/// pre multiplies a tensor by the shapes derivaties
void
computeBtD(const Array<Real> & Ds, Array<Real> & BtDs,
ElementType type, GhostType ghost_type,
const Array<UInt> & filter_elements = empty_filter) const override;
/// left and right multiplies a tensor by the shapes derivaties
void computeBtDB(
const Array<Real> & Ds, Array<Real> & BtDBs, UInt order_d,
ElementType type, GhostType ghost_type,
const Array<UInt> & filter_elements = empty_filter) const override;
/// left multiples a vector by the shape functions
void computeNtb(const Array<Real> & bs, Array<Real> & Ntbs,
ElementType type, GhostType ghost_type,
const Array<UInt> & filter_elements) const override;
/// compute the position of integration points given by an element_type_map
/// from nodes position
inline void computeIntegrationPointsCoordinates(
ElementTypeMapArray<Real> & quadrature_points_coordinates,
const ElementTypeMapArray<UInt> * filter_elements =
nullptr) const override;
/// compute the position of integration points from nodes position
inline void computeIntegrationPointsCoordinates(
Array<Real> & quadrature_points_coordinates, ElementType type,
GhostType ghost_type = _not_ghost,
const Array<UInt> & filter_elements = empty_filter) const override;
/// interpolate field at given position (interpolation_points) from given
/// values of this field at integration points (field)
inline void interpolateElementalFieldFromIntegrationPoints(
const ElementTypeMapArray<Real> & field,
const ElementTypeMapArray<Real> & interpolation_points_coordinates,
ElementTypeMapArray<Real> & result, GhostType ghost_type,
const ElementTypeMapArray<UInt> * element_filter) const override;
/// Interpolate field at given position from given values of this field at
/// integration points (field)
/// using matrices precomputed with
/// initElementalFieldInterplationFromIntegrationPoints
inline void interpolateElementalFieldFromIntegrationPoints(
const ElementTypeMapArray<Real> & field,
const ElementTypeMapArray<Real> &
interpolation_points_coordinates_matrices,
const ElementTypeMapArray<Real> & quad_points_coordinates_inv_matrices,
ElementTypeMapArray<Real> & result, GhostType ghost_type,
const ElementTypeMapArray<UInt> * element_filter) const override;
/// Build pre-computed matrices for interpolation of field form integration
/// points at other given positions (interpolation_points)
inline void initElementalFieldInterpolationFromIntegrationPoints(
const ElementTypeMapArray<Real> & interpolation_points_coordinates,
ElementTypeMapArray<Real> & interpolation_points_coordinates_matrices,
ElementTypeMapArray<Real> & quad_points_coordinates_inv_matrices,
const ElementTypeMapArray<UInt> * element_filter =
nullptr) const override;
/// find natural coords from real coords provided an element
void inverseMap(const Vector<Real> & real_coords, UInt element,
ElementType type, Vector<Real> & natural_coords,
GhostType ghost_type = _not_ghost) const;
/// return true if the coordinates provided are inside the element, false
/// otherwise
inline bool contains(const Vector<Real> & real_coords, UInt element,
ElementType type,
GhostType ghost_type = _not_ghost) const;
/// compute the shape on a provided point
inline void
computeShapes(const Vector<Real> & real_coords, UInt element,
ElementType type, Vector<Real> & shapes,
GhostType ghost_type = _not_ghost) const override;
/// compute the shape derivatives on a provided point
inline void computeShapeDerivatives(
const Vector<Real> & real_coords, UInt element, ElementType type,
Matrix<Real> & shape_derivatives,
GhostType ghost_type = _not_ghost) const override;
/* ------------------------------------------------------------------------ */
/* Other methods */
/* ------------------------------------------------------------------------ */
/// pre-compute normals on integration points
void computeNormalsOnIntegrationPoints(
GhostType ghost_type = _not_ghost) override;
void computeNormalsOnIntegrationPoints(
const Array<Real> & field,
GhostType ghost_type = _not_ghost) override;
void computeNormalsOnIntegrationPoints(
const Array<Real> & field, Array<Real> & normal, ElementType type,
GhostType ghost_type = _not_ghost) const override;
template <ElementType type>
void computeNormalsOnIntegrationPoints(const Array<Real> & field,
Array<Real> & normal,
GhostType ghost_type) const;
private:
// To avoid a weird full specialization of a method in a non specalized class
void
computeNormalsOnIntegrationPointsPoint1(const Array<Real> & /*unused*/,
Array<Real> & normal,
GhostType ghost_type) const;
public:
/// function to print the contain of the class
void printself(std::ostream & stream, int indent = 0) const override;
void assembleFieldLumped(
const std::function<void(Matrix<Real> &, const Element &)> & field_funct,
const ID & matrix_id, const ID & dof_id, DOFManager & dof_manager,
ElementType type, GhostType ghost_type) const override;
/// assemble a field as a matrix (ex. rho to mass matrix)
void assembleFieldMatrix(
const std::function<void(Matrix<Real> &, const Element &)> & field_funct,
const ID & matrix_id, const ID & dof_id, DOFManager & dof_manager,
ElementType type, GhostType ghost_type) const override;
/// assemble a field as a lumped matrix (ex. rho in lumped mass)
// template <class Functor>
// void assembleFieldLumped(const Functor & field_funct, const ID & matrix_id,
// const ID & dof_id, DOFManager & dof_manager,
// ElementType type,
// GhostType ghost_type) const;
// /// assemble a field as a matrix (ex. rho to mass matrix)
// template <class Functor>
// void assembleFieldMatrix(const Functor & field_funct, const ID & matrix_id,
// const ID & dof_id, DOFManager & dof_manager,
// ElementType type,
// GhostType ghost_type) const;
// #ifdef AKANTU_STRUCTURAL_MECHANICS
// /// assemble a field as a matrix (ex. rho to mass matrix)
// void assembleFieldMatrix(const Array<Real> & field_1,
// UInt nb_degree_of_freedom, SparseMatrix & M,
// Array<Real> * n,
// ElementTypeMapArray<Real> & rotation_mat,
// ElementType type,
// GhostType ghost_type = _not_ghost)
// const;
// /// compute shapes function in a matrix for structural elements
// void
// computeShapesMatrix(ElementType type, UInt nb_degree_of_freedom,
// UInt nb_nodes_per_element, Array<Real> * n, UInt id,
// UInt degree_to_interpolate, UInt degree_interpolated,
// const bool sign,
// GhostType ghost_type = _not_ghost) const
// override;
// #endif
private:
friend struct fe_engine::details::AssembleLumpedTemplateHelper<kind>;
friend struct fe_engine::details::AssembleFieldMatrixHelper<kind>;
friend struct AssembleFieldMatrixStructHelper<kind, void>;
/// templated function to compute the scaling to assemble a lumped matrix
template <ElementType type>
void assembleFieldLumped(
const std::function<void(Matrix<Real> &, const Element &)> & field_funct,
const ID & matrix_id, const ID & dof_id, DOFManager & dof_manager,
GhostType ghost_type) const;
/// @f$ \tilde{M}_{i} = \sum_j M_{ij} = \sum_j \int \rho \varphi_i \varphi_j
/// dV = \int \rho \varphi_i dV @f$
template <ElementType type>
void assembleLumpedRowSum(const Array<Real> & field, const ID & matrix_id,
const ID & dof_id, DOFManager & dof_manager,
GhostType ghost_type) const;
/// @f$ \tilde{M}_{i} = c * M_{ii} = \int_{V_e} \rho dV @f$
template <ElementType type>
void assembleLumpedDiagonalScaling(const Array<Real> & field,
const ID & matrix_id, const ID & dof_id,
DOFManager & dof_manager,
GhostType ghost_type) const;
/// assemble a field as a matrix (ex. rho to mass matrix)
template <ElementType type>
void assembleFieldMatrix(
const std::function<void(Matrix<Real> &, const Element &)> & field_funct,
const ID & matrix_id, const ID & dof_id, DOFManager & dof_manager,
GhostType ghost_type) const;
#ifdef AKANTU_STRUCTURAL_MECHANICS
/// assemble a field as a matrix for structural elements (ex. rho to mass
/// matrix)
template <ElementType type>
void assembleFieldMatrix(const Array<Real> & field_1,
UInt nb_degree_of_freedom, SparseMatrix & M,
Array<Real> * n,
ElementTypeMapArray<Real> & rotation_mat,
__attribute__((unused))
GhostType ghost_type) const;
#endif
/* ------------------------------------------------------------------------ */
/* Mesh Event Handler interface */
/* ------------------------------------------------------------------------ */
public:
void onElementsAdded(const Array<Element> & /*new_elements*/,
const NewElementsEvent & /*unused*/) override;
void onElementsRemoved(const Array<Element> & /*unused*/,
const ElementTypeMapArray<UInt> & /*unused*/,
const RemovedElementsEvent & /*unused*/) override;
void onElementsChanged(const Array<Element> & /*unused*/,
const Array<Element> & /*unused*/,
const ElementTypeMapArray<UInt> & /*unused*/,
const ChangedElementsEvent & /*unused*/) override;
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
/// get the shape class (probably useless: see getShapeFunction)
const ShapeFunctions & getShapeFunctionsInterface() const override {
return shape_functions;
};
/// get the shape class
const Shape & getShapeFunctions() const { return shape_functions; };
/// get the integrator class (probably useless: see getIntegrator)
const Integrator & getIntegratorInterface() const override {
return integrator;
};
/// get the integrator class
const Integ & getIntegrator() const { return integrator; };
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
private:
Integ integrator;
Shape shape_functions;
};
} // namespace akantu
/* -------------------------------------------------------------------------- */
/* inline functions */
/* -------------------------------------------------------------------------- */
#include "fe_engine_template_tmpl.hh"
#include "fe_engine_template_tmpl_field.hh"
/* -------------------------------------------------------------------------- */
/* Shape Linked specialization */
/* -------------------------------------------------------------------------- */
#if defined(AKANTU_STRUCTURAL_MECHANICS)
#include "fe_engine_template_tmpl_struct.hh"
#endif
/* -------------------------------------------------------------------------- */
/* Shape IGFEM specialization */
/* -------------------------------------------------------------------------- */
#if defined(AKANTU_IGFEM)
#include "fe_engine_template_tmpl_igfem.hh"
#endif
#endif /* AKANTU_FE_ENGINE_TEMPLATE_HH_ */
diff --git a/src/fe_engine/fe_engine_template_tmpl.hh b/src/fe_engine/fe_engine_template_tmpl.hh
index e6d3f59ec..0a9e46ec8 100644
--- a/src/fe_engine/fe_engine_template_tmpl.hh
+++ b/src/fe_engine/fe_engine_template_tmpl.hh
@@ -1,1421 +1,1420 @@
/**
* @file fe_engine_template_tmpl.hh
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author Mauro Corrado <mauro.corrado@epfl.ch>
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
* @author Marco Vocialta <marco.vocialta@epfl.ch>
*
* @date creation: Tue Feb 15 2011
* @date last modification: Tue Feb 20 2018
*
* @brief Template implementation of FEEngineTemplate
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_common.hh"
#include "dof_manager.hh"
#include "fe_engine_template.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::FEEngineTemplate(
- Mesh & mesh, UInt spatial_dimension, const ID & id, MemoryID memory_id)
- : FEEngine(mesh, spatial_dimension, id, memory_id),
- integrator(mesh, spatial_dimension, id, memory_id),
- shape_functions(mesh, spatial_dimension, id, memory_id) {}
+ Mesh & mesh, UInt spatial_dimension, const ID & id)
+ : FEEngine(mesh, spatial_dimension, id),
+ integrator(mesh, spatial_dimension, id),
+ shape_functions(mesh, spatial_dimension, id) {}
/* -------------------------------------------------------------------------- */
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::~FEEngineTemplate() =
default;
/* -------------------------------------------------------------------------- */
/**
* Helper class to be able to write a partial specialization on the element kind
*/
namespace fe_engine {
namespace details {
template <ElementKind kind> struct GradientOnIntegrationPointsHelper {
template <class S>
static void call(const S & /*unused*/, Mesh & /*unused*/,
const Array<Real> & /*unused*/, Array<Real> & /*unused*/,
const UInt /*unused*/, ElementType /*unused*/,
GhostType /*unused*/,
const Array<UInt> & /*unused*/) {
AKANTU_TO_IMPLEMENT();
}
};
#define COMPUTE_GRADIENT(type) \
if (element_dimension == ElementClass<type>::getSpatialDimension()) \
shape_functions.template gradientOnIntegrationPoints<type>( \
u, nablauq, nb_degree_of_freedom, ghost_type, filter_elements);
#define AKANTU_SPECIALIZE_GRADIENT_ON_INTEGRATION_POINTS_HELPER(kind) \
template <> struct GradientOnIntegrationPointsHelper<kind> { \
template <class S> \
static void call(const S & shape_functions, Mesh & mesh, \
const Array<Real> & u, Array<Real> & nablauq, \
const UInt nb_degree_of_freedom, \
ElementType type, GhostType ghost_type, \
const Array<UInt> & filter_elements) { \
UInt element_dimension = mesh.getSpatialDimension(type); \
AKANTU_BOOST_KIND_ELEMENT_SWITCH(COMPUTE_GRADIENT, kind); \
} \
};
AKANTU_BOOST_ALL_KIND_LIST(
AKANTU_SPECIALIZE_GRADIENT_ON_INTEGRATION_POINTS_HELPER,
AKANTU_FE_ENGINE_LIST_GRADIENT_ON_INTEGRATION_POINTS)
#undef AKANTU_SPECIALIZE_GRADIENT_ON_INTEGRATION_POINTS_HELPER
#undef COMPUTE_GRADIENT
} // namespace details
} // namespace fe_engine
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
void FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::
gradientOnIntegrationPoints(const Array<Real> & u, Array<Real> & nablauq,
const UInt nb_degree_of_freedom,
ElementType type,
GhostType ghost_type,
const Array<UInt> & filter_elements) const {
AKANTU_DEBUG_IN();
UInt nb_element = mesh.getNbElement(type, ghost_type);
if (filter_elements != empty_filter) {
nb_element = filter_elements.size();
}
UInt nb_points =
shape_functions.getIntegrationPoints(type, ghost_type).cols();
#ifndef AKANTU_NDEBUG
UInt element_dimension = mesh.getSpatialDimension(type);
AKANTU_DEBUG_ASSERT(u.size() == mesh.getNbNodes(),
"The vector u(" << u.getID()
<< ") has not the good size.");
AKANTU_DEBUG_ASSERT(u.getNbComponent() == nb_degree_of_freedom,
"The vector u("
<< u.getID()
<< ") has not the good number of component.");
AKANTU_DEBUG_ASSERT(
nablauq.getNbComponent() == nb_degree_of_freedom * element_dimension,
"The vector nablauq(" << nablauq.getID()
<< ") has not the good number of component.");
// AKANTU_DEBUG_ASSERT(nablauq.size() == nb_element * nb_points,
// "The vector nablauq(" << nablauq.getID()
// << ") has not the good size.");
#endif
nablauq.resize(nb_element * nb_points);
fe_engine::details::GradientOnIntegrationPointsHelper<kind>::call(
shape_functions, mesh, u, nablauq, nb_degree_of_freedom, type, ghost_type,
filter_elements);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
void FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::initShapeFunctions(
GhostType ghost_type) {
initShapeFunctions(mesh.getNodes(), ghost_type);
}
/* -------------------------------------------------------------------------- */
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
void FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::initShapeFunctions(
const Array<Real> & nodes, GhostType ghost_type) {
AKANTU_DEBUG_IN();
for (auto & type : mesh.elementTypes(element_dimension, ghost_type, kind)) {
integrator.initIntegrator(nodes, type, ghost_type);
const auto & control_points = getIntegrationPoints(type, ghost_type);
shape_functions.initShapeFunctions(nodes, control_points, type, ghost_type);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
/**
* Helper class to be able to write a partial specialization on the element kind
*/
namespace fe_engine {
namespace details {
template <ElementKind kind> struct IntegrateHelper {};
#define INTEGRATE(type) \
integrator.template integrate<type>(f, intf, nb_degree_of_freedom, \
ghost_type, filter_elements);
#define AKANTU_SPECIALIZE_INTEGRATE_HELPER(kind) \
template <> struct IntegrateHelper<kind> { \
template <class I> \
static void call(const I & integrator, const Array<Real> & f, \
Array<Real> & intf, UInt nb_degree_of_freedom, \
ElementType type, GhostType ghost_type, \
const Array<UInt> & filter_elements) { \
AKANTU_BOOST_KIND_ELEMENT_SWITCH(INTEGRATE, kind); \
} \
};
AKANTU_BOOST_ALL_KIND(AKANTU_SPECIALIZE_INTEGRATE_HELPER)
#undef AKANTU_SPECIALIZE_INTEGRATE_HELPER
#undef INTEGRATE
} // namespace details
} // namespace fe_engine
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
void FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::integrate(
const Array<Real> & f, Array<Real> & intf, UInt nb_degree_of_freedom,
ElementType type, GhostType ghost_type,
const Array<UInt> & filter_elements) const {
UInt nb_element = mesh.getNbElement(type, ghost_type);
if (filter_elements != empty_filter) {
nb_element = filter_elements.size();
}
#ifndef AKANTU_NDEBUG
UInt nb_quadrature_points = getNbIntegrationPoints(type);
AKANTU_DEBUG_ASSERT(f.size() == nb_element * nb_quadrature_points,
"The vector f(" << f.getID() << " size " << f.size()
<< ") has not the good size ("
<< nb_element << ").");
AKANTU_DEBUG_ASSERT(f.getNbComponent() == nb_degree_of_freedom,
"The vector f("
<< f.getID()
<< ") has not the good number of component.");
AKANTU_DEBUG_ASSERT(intf.getNbComponent() == nb_degree_of_freedom,
"The vector intf("
<< intf.getID()
<< ") has not the good number of component.");
#endif
intf.resize(nb_element);
fe_engine::details::IntegrateHelper<kind>::call(integrator, f, intf,
nb_degree_of_freedom, type,
ghost_type, filter_elements);
}
/* -------------------------------------------------------------------------- */
/**
* Helper class to be able to write a partial specialization on the element kind
*/
namespace fe_engine {
namespace details {
template <ElementKind kind> struct IntegrateScalarHelper {};
#define INTEGRATE(type) \
integral = \
integrator.template integrate<type>(f, ghost_type, filter_elements);
#define AKANTU_SPECIALIZE_INTEGRATE_SCALAR_HELPER(kind) \
template <> struct IntegrateScalarHelper<kind> { \
template <class I> \
static Real call(const I & integrator, const Array<Real> & f, \
ElementType type, GhostType ghost_type, \
const Array<UInt> & filter_elements) { \
Real integral = 0.; \
AKANTU_BOOST_KIND_ELEMENT_SWITCH(INTEGRATE, kind); \
return integral; \
} \
};
AKANTU_BOOST_ALL_KIND(AKANTU_SPECIALIZE_INTEGRATE_SCALAR_HELPER)
#undef AKANTU_SPECIALIZE_INTEGRATE_SCALAR_HELPER
#undef INTEGRATE
} // namespace details
} // namespace fe_engine
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
Real FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::integrate(
const Array<Real> & f, ElementType type,
GhostType ghost_type, const Array<UInt> & filter_elements) const {
AKANTU_DEBUG_IN();
#ifndef AKANTU_NDEBUG
// std::stringstream sstr; sstr << ghost_type;
// AKANTU_DEBUG_ASSERT(sstr.str() == nablauq.getTag(),
// "The vector " << nablauq.getID() << " is not taged " <<
// ghost_type);
UInt nb_element = mesh.getNbElement(type, ghost_type);
if (filter_elements != empty_filter) {
nb_element = filter_elements.size();
}
UInt nb_quadrature_points = getNbIntegrationPoints(type, ghost_type);
AKANTU_DEBUG_ASSERT(
f.size() == nb_element * nb_quadrature_points,
"The vector f(" << f.getID() << ") has not the good size. (" << f.size()
<< "!=" << nb_quadrature_points * nb_element << ")");
AKANTU_DEBUG_ASSERT(f.getNbComponent() == 1,
"The vector f("
<< f.getID()
<< ") has not the good number of component.");
#endif
Real integral = fe_engine::details::IntegrateScalarHelper<kind>::call(
integrator, f, type, ghost_type, filter_elements);
AKANTU_DEBUG_OUT();
return integral;
}
/* -------------------------------------------------------------------------- */
/**
* Helper class to be able to write a partial specialization on the element kind
*/
namespace fe_engine {
namespace details {
template <ElementKind kind> struct IntegrateScalarOnOneElementHelper {};
#define INTEGRATE(type) \
res = integrator.template integrate<type>(f, index, ghost_type);
#define AKANTU_SPECIALIZE_INTEGRATE_SCALAR_ON_ONE_ELEMENT_HELPER(kind) \
template <> struct IntegrateScalarOnOneElementHelper<kind> { \
template <class I> \
static Real call(const I & integrator, const Vector<Real> & f, \
ElementType type, UInt index, \
GhostType ghost_type) { \
Real res = 0.; \
AKANTU_BOOST_KIND_ELEMENT_SWITCH(INTEGRATE, kind); \
return res; \
} \
};
AKANTU_BOOST_ALL_KIND(
AKANTU_SPECIALIZE_INTEGRATE_SCALAR_ON_ONE_ELEMENT_HELPER)
#undef AKANTU_SPECIALIZE_INTEGRATE_SCALAR_ON_ONE_ELEMENT_HELPER
#undef INTEGRATE
} // namespace details
} // namespace fe_engine
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
Real FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::integrate(
const Vector<Real> & f, ElementType type, UInt index,
GhostType ghost_type) const {
Real res = fe_engine::details::IntegrateScalarOnOneElementHelper<kind>::call(
integrator, f, type, index, ghost_type);
return res;
}
/* -------------------------------------------------------------------------- */
/**
* Helper class to be able to write a partial specialization on the element kind
*/
namespace fe_engine {
namespace details {
template <ElementKind kind> struct IntegrateOnIntegrationPointsHelper {};
#define INTEGRATE(type) \
integrator.template integrateOnIntegrationPoints<type>( \
f, intf, nb_degree_of_freedom, ghost_type, filter_elements);
#define AKANTU_SPECIALIZE_INTEGRATE_ON_INTEGRATION_POINTS_HELPER(kind) \
template <> struct IntegrateOnIntegrationPointsHelper<kind> { \
template <class I> \
static void call(const I & integrator, const Array<Real> & f, \
Array<Real> & intf, UInt nb_degree_of_freedom, \
ElementType type, GhostType ghost_type, \
const Array<UInt> & filter_elements) { \
AKANTU_BOOST_KIND_ELEMENT_SWITCH(INTEGRATE, kind); \
} \
};
AKANTU_BOOST_ALL_KIND(
AKANTU_SPECIALIZE_INTEGRATE_ON_INTEGRATION_POINTS_HELPER)
#undef AKANTU_SPECIALIZE_INTEGRATE_ON_INTEGRATION_POINTS_HELPER
#undef INTEGRATE
} // namespace details
} // namespace fe_engine
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
void FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::
integrateOnIntegrationPoints(const Array<Real> & f, Array<Real> & intf,
UInt nb_degree_of_freedom,
ElementType type,
GhostType ghost_type,
const Array<UInt> & filter_elements) const {
UInt nb_element = mesh.getNbElement(type, ghost_type);
if (filter_elements != empty_filter) {
nb_element = filter_elements.size();
}
UInt nb_quadrature_points = getNbIntegrationPoints(type);
#ifndef AKANTU_NDEBUG
// std::stringstream sstr; sstr << ghost_type;
// AKANTU_DEBUG_ASSERT(sstr.str() == nablauq.getTag(),
// "The vector " << nablauq.getID() << " is not taged " <<
// ghost_type);
AKANTU_DEBUG_ASSERT(f.size() == nb_element * nb_quadrature_points,
"The vector f(" << f.getID() << " size " << f.size()
<< ") has not the good size ("
<< nb_element << ").");
AKANTU_DEBUG_ASSERT(f.getNbComponent() == nb_degree_of_freedom,
"The vector f("
<< f.getID()
<< ") has not the good number of component.");
AKANTU_DEBUG_ASSERT(intf.getNbComponent() == nb_degree_of_freedom,
"The vector intf("
<< intf.getID()
<< ") has not the good number of component.");
#endif
intf.resize(nb_element * nb_quadrature_points);
fe_engine::details::IntegrateOnIntegrationPointsHelper<kind>::call(
integrator, f, intf, nb_degree_of_freedom, type, ghost_type,
filter_elements);
}
/* -------------------------------------------------------------------------- */
/**
* Helper class to be able to write a partial specialization on the element kind
*/
namespace fe_engine {
namespace details {
template <ElementKind kind> struct InterpolateOnIntegrationPointsHelper {
template <class S>
static void call(const S & /*unused*/, const Array<Real> & /*unused*/,
Array<Real> & /*unused*/, const UInt /*unused*/,
ElementType /*unused*/,
GhostType /*unused*/,
const Array<UInt> & /*unused*/) {
AKANTU_TO_IMPLEMENT();
}
};
#define INTERPOLATE(type) \
shape_functions.template interpolateOnIntegrationPoints<type>( \
u, uq, nb_degree_of_freedom, ghost_type, filter_elements);
#define AKANTU_SPECIALIZE_INTERPOLATE_ON_INTEGRATION_POINTS_HELPER(kind) \
template <> struct InterpolateOnIntegrationPointsHelper<kind> { \
template <class S> \
static void call(const S & shape_functions, const Array<Real> & u, \
Array<Real> & uq, const UInt nb_degree_of_freedom, \
ElementType type, GhostType ghost_type, \
const Array<UInt> & filter_elements) { \
AKANTU_BOOST_KIND_ELEMENT_SWITCH(INTERPOLATE, kind); \
} \
};
AKANTU_BOOST_ALL_KIND_LIST(
AKANTU_SPECIALIZE_INTERPOLATE_ON_INTEGRATION_POINTS_HELPER,
AKANTU_FE_ENGINE_LIST_INTERPOLATE_ON_INTEGRATION_POINTS)
#undef AKANTU_SPECIALIZE_INTERPOLATE_ON_INTEGRATION_POINTS_HELPER
#undef INTERPOLATE
} // namespace details
} // namespace fe_engine
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
void FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::
interpolateOnIntegrationPoints(const Array<Real> & u, Array<Real> & uq,
const UInt nb_degree_of_freedom,
ElementType type,
GhostType ghost_type,
const Array<UInt> & filter_elements) const {
AKANTU_DEBUG_IN();
UInt nb_points =
shape_functions.getIntegrationPoints(type, ghost_type).cols();
UInt nb_element = mesh.getNbElement(type, ghost_type);
if (filter_elements != empty_filter) {
nb_element = filter_elements.size();
}
#ifndef AKANTU_NDEBUG
AKANTU_DEBUG_ASSERT(u.size() == mesh.getNbNodes(),
"The vector u(" << u.getID()
<< ") has not the good size.");
AKANTU_DEBUG_ASSERT(u.getNbComponent() == nb_degree_of_freedom,
"The vector u("
<< u.getID()
<< ") has not the good number of component.");
AKANTU_DEBUG_ASSERT(uq.getNbComponent() == nb_degree_of_freedom,
"The vector uq("
<< uq.getID()
<< ") has not the good number of component.");
#endif
uq.resize(nb_element * nb_points);
fe_engine::details::InterpolateOnIntegrationPointsHelper<kind>::call(
shape_functions, u, uq, nb_degree_of_freedom, type, ghost_type,
filter_elements);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
void FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::
interpolateOnIntegrationPoints(
const Array<Real> & u, ElementTypeMapArray<Real> & uq,
const ElementTypeMapArray<UInt> * filter_elements) const {
AKANTU_DEBUG_IN();
const Array<UInt> * filter = nullptr;
for (auto ghost_type : ghost_types) {
for (auto && type : uq.elementTypes(_all_dimensions, ghost_type, kind)) {
UInt nb_quad_per_element = getNbIntegrationPoints(type, ghost_type);
UInt nb_element = 0;
if (filter_elements != nullptr) {
filter = &((*filter_elements)(type, ghost_type));
nb_element = filter->size();
} else {
filter = &empty_filter;
nb_element = mesh.getNbElement(type, ghost_type);
}
UInt nb_tot_quad = nb_quad_per_element * nb_element;
Array<Real> & quad = uq(type, ghost_type);
quad.resize(nb_tot_quad);
interpolateOnIntegrationPoints(u, quad, quad.getNbComponent(), type,
ghost_type, *filter);
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
namespace fe_engine {
namespace details {
template <ElementKind kind> struct ComputeBtDHelper {};
#define COMPUTE_BTD(type) \
shape_functions.template computeBtD<type>(Ds, BtDs, ghost_type, \
filter_elements);
#define AKANTU_SPECIALIZE_COMPUTE_BtD_HELPER(kind) \
template <> struct ComputeBtDHelper<kind> { \
template <class S> \
static void call(const S & shape_functions, const Array<Real> & Ds, \
Array<Real> & BtDs, ElementType type, \
GhostType ghost_type, \
const Array<UInt> & filter_elements) { \
AKANTU_BOOST_KIND_ELEMENT_SWITCH(COMPUTE_BTD, kind); \
} \
};
AKANTU_BOOST_ALL_KIND(AKANTU_SPECIALIZE_COMPUTE_BtD_HELPER)
#undef AKANTU_SPECIALIZE_COMPUTE_BtD_HELPER
#undef COMPUTE_BTD
} // namespace details
} // namespace fe_engine
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
inline void FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::computeBtD(
const Array<Real> & Ds, Array<Real> & BtDs, ElementType type,
GhostType ghost_type, const Array<UInt> & filter_elements) const {
fe_engine::details::ComputeBtDHelper<kind>::call(
shape_functions, Ds, BtDs, type, ghost_type, filter_elements);
}
/* -------------------------------------------------------------------------- */
namespace fe_engine {
namespace details {
template <ElementKind kind> struct ComputeBtDBHelper {};
#define COMPUTE_BTDB(type) \
shape_functions.template computeBtDB<type>(Ds, BtDBs, order_d, ghost_type, \
filter_elements);
#define AKANTU_SPECIALIZE_COMPUTE_BtDB_HELPER(kind) \
template <> struct ComputeBtDBHelper<kind> { \
template <class S> \
static void call(const S & shape_functions, const Array<Real> & Ds, \
Array<Real> & BtDBs, UInt order_d, \
ElementType type, GhostType ghost_type, \
const Array<UInt> & filter_elements) { \
AKANTU_BOOST_KIND_ELEMENT_SWITCH(COMPUTE_BTDB, kind); \
} \
};
AKANTU_BOOST_ALL_KIND(AKANTU_SPECIALIZE_COMPUTE_BtDB_HELPER)
#undef AKANTU_SPECIALIZE_COMPUTE_BtDB_HELPER
#undef COMPUTE_BTDB
} // namespace details
} // namespace fe_engine
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
inline void FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::computeBtDB(
const Array<Real> & Ds, Array<Real> & BtDBs, UInt order_d,
ElementType type, GhostType ghost_type,
const Array<UInt> & filter_elements) const {
fe_engine::details::ComputeBtDBHelper<kind>::call(
shape_functions, Ds, BtDBs, order_d, type, ghost_type, filter_elements);
}
/* -------------------------------------------------------------------------- */
namespace fe_engine {
namespace details {
template <ElementKind kind> struct ComputeNtbHelper {};
#define COMPUTE_Ntb(type) \
shape_functions.template computeNtb<type>(bs, Ntbs, ghost_type, \
filter_elements);
#define AKANTU_SPECIALIZE_COMPUTE_Ntb_HELPER(kind) \
template <> struct ComputeNtbHelper<kind> { \
template <class S> \
static void call(const S & shape_functions, const Array<Real> & bs, \
Array<Real> & Ntbs, ElementType type, \
GhostType ghost_type, \
const Array<UInt> & filter_elements) { \
AKANTU_BOOST_KIND_ELEMENT_SWITCH(COMPUTE_Ntb, kind); \
} \
};
AKANTU_BOOST_ALL_KIND(AKANTU_SPECIALIZE_COMPUTE_Ntb_HELPER)
#undef AKANTU_SPECIALIZE_COMPUTE_Ntb_HELPER
#undef COMPUTE_Ntb
} // namespace details
} // namespace fe_engine
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
inline void FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::computeNtb(
const Array<Real> & bs, Array<Real> & Ntbs, ElementType type,
GhostType ghost_type, const Array<UInt> & filter_elements) const {
fe_engine::details::ComputeNtbHelper<kind>::call(
shape_functions, bs, Ntbs, type, ghost_type, filter_elements);
}
/* -------------------------------------------------------------------------- */
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
inline void FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::
computeIntegrationPointsCoordinates(
ElementTypeMapArray<Real> & quadrature_points_coordinates,
const ElementTypeMapArray<UInt> * filter_elements) const {
const Array<Real> & nodes_coordinates = mesh.getNodes();
interpolateOnIntegrationPoints(
nodes_coordinates, quadrature_points_coordinates, filter_elements);
}
/* -------------------------------------------------------------------------- */
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
inline void FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::
computeIntegrationPointsCoordinates(
Array<Real> & quadrature_points_coordinates, ElementType type,
GhostType ghost_type,
const Array<UInt> & filter_elements) const {
const Array<Real> & nodes_coordinates = mesh.getNodes();
UInt spatial_dimension = mesh.getSpatialDimension();
interpolateOnIntegrationPoints(
nodes_coordinates, quadrature_points_coordinates, spatial_dimension, type,
ghost_type, filter_elements);
}
/* -------------------------------------------------------------------------- */
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
inline void FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::
initElementalFieldInterpolationFromIntegrationPoints(
const ElementTypeMapArray<Real> & interpolation_points_coordinates,
ElementTypeMapArray<Real> & interpolation_points_coordinates_matrices,
ElementTypeMapArray<Real> & quad_points_coordinates_inv_matrices,
const ElementTypeMapArray<UInt> * element_filter) const {
AKANTU_DEBUG_IN();
UInt spatial_dimension = this->mesh.getSpatialDimension();
ElementTypeMapArray<Real> quadrature_points_coordinates(
- "quadrature_points_coordinates_for_interpolation", getID(),
- getMemoryID());
+ "quadrature_points_coordinates_for_interpolation", getID());
quadrature_points_coordinates.initialize(*this,
_nb_component = spatial_dimension);
computeIntegrationPointsCoordinates(quadrature_points_coordinates,
element_filter);
shape_functions.initElementalFieldInterpolationFromIntegrationPoints(
interpolation_points_coordinates,
interpolation_points_coordinates_matrices,
quad_points_coordinates_inv_matrices, quadrature_points_coordinates,
element_filter);
}
/* -------------------------------------------------------------------------- */
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
inline void FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::
interpolateElementalFieldFromIntegrationPoints(
const ElementTypeMapArray<Real> & field,
const ElementTypeMapArray<Real> & interpolation_points_coordinates,
ElementTypeMapArray<Real> & result, GhostType ghost_type,
const ElementTypeMapArray<UInt> * element_filter) const {
ElementTypeMapArray<Real> interpolation_points_coordinates_matrices(
- "interpolation_points_coordinates_matrices", id, memory_id);
+ "interpolation_points_coordinates_matrices", id);
ElementTypeMapArray<Real> quad_points_coordinates_inv_matrices(
- "quad_points_coordinates_inv_matrices", id, memory_id);
+ "quad_points_coordinates_inv_matrices", id);
initElementalFieldInterpolationFromIntegrationPoints(
interpolation_points_coordinates,
interpolation_points_coordinates_matrices,
quad_points_coordinates_inv_matrices, element_filter);
interpolateElementalFieldFromIntegrationPoints(
field, interpolation_points_coordinates_matrices,
quad_points_coordinates_inv_matrices, result, ghost_type, element_filter);
}
/* -------------------------------------------------------------------------- */
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
inline void FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::
interpolateElementalFieldFromIntegrationPoints(
const ElementTypeMapArray<Real> & field,
const ElementTypeMapArray<Real> &
interpolation_points_coordinates_matrices,
const ElementTypeMapArray<Real> & quad_points_coordinates_inv_matrices,
ElementTypeMapArray<Real> & result, GhostType ghost_type,
const ElementTypeMapArray<UInt> * element_filter) const {
shape_functions.interpolateElementalFieldFromIntegrationPoints(
field, interpolation_points_coordinates_matrices,
quad_points_coordinates_inv_matrices, result, ghost_type, element_filter);
}
/* -------------------------------------------------------------------------- */
/**
* Helper class to be able to write a partial specialization on the element kind
*/
namespace fe_engine {
namespace details {
template <ElementKind kind> struct InterpolateHelper {
template <class S>
static void call(const S & /*unused*/, const Vector<Real> & /*unused*/,
UInt /*unused*/, const Matrix<Real> & /*unused*/,
Vector<Real> & /*unused*/,
ElementType /*unused*/,
GhostType /*unused*/) {
AKANTU_TO_IMPLEMENT();
}
};
#define INTERPOLATE(type) \
shape_functions.template interpolate<type>( \
real_coords, element, nodal_values, interpolated, ghost_type);
#define AKANTU_SPECIALIZE_INTERPOLATE_HELPER(kind) \
template <> struct InterpolateHelper<kind> { \
template <class S> \
static void call(const S & shape_functions, \
const Vector<Real> & real_coords, UInt element, \
const Matrix<Real> & nodal_values, \
Vector<Real> & interpolated, ElementType type, \
GhostType ghost_type) { \
AKANTU_BOOST_KIND_ELEMENT_SWITCH(INTERPOLATE, kind); \
} \
};
AKANTU_BOOST_ALL_KIND_LIST(AKANTU_SPECIALIZE_INTERPOLATE_HELPER,
AKANTU_FE_ENGINE_LIST_INTERPOLATE)
#undef AKANTU_SPECIALIZE_INTERPOLATE_HELPER
#undef INTERPOLATE
} // namespace details
} // namespace fe_engine
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
inline void FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::interpolate(
const Vector<Real> & real_coords, const Matrix<Real> & nodal_values,
Vector<Real> & interpolated, const Element & element) const {
AKANTU_DEBUG_IN();
fe_engine::details::InterpolateHelper<kind>::call(
shape_functions, real_coords, element.element, nodal_values, interpolated,
element.type, element.ghost_type);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
void FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::
computeNormalsOnIntegrationPoints(GhostType ghost_type) {
AKANTU_DEBUG_IN();
computeNormalsOnIntegrationPoints(mesh.getNodes(), ghost_type);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
void FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::
computeNormalsOnIntegrationPoints(const Array<Real> & field,
GhostType ghost_type) {
AKANTU_DEBUG_IN();
// Real * coord = mesh.getNodes().storage();
UInt spatial_dimension = mesh.getSpatialDimension();
// allocate the normal arrays
normals_on_integration_points.initialize(
*this, _nb_component = spatial_dimension,
_spatial_dimension = element_dimension, _ghost_type = ghost_type,
_element_kind = kind);
// loop over the type to build the normals
for (auto & type : mesh.elementTypes(element_dimension, ghost_type, kind)) {
auto & normals_on_quad = normals_on_integration_points(type, ghost_type);
computeNormalsOnIntegrationPoints(field, normals_on_quad, type, ghost_type);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
/**
* Helper class to be able to write a partial specialization on the element kind
*/
namespace fe_engine {
namespace details {
template <ElementKind kind> struct ComputeNormalsOnIntegrationPoints {
template <template <ElementKind, class> class I,
template <ElementKind> class S, ElementKind k, class IOF>
static void call(const FEEngineTemplate<I, S, k, IOF> & /*unused*/,
const Array<Real> & /*unused*/, Array<Real> & /*unused*/,
ElementType /*unused*/,
GhostType /*unused*/) {
AKANTU_TO_IMPLEMENT();
}
};
#define COMPUTE_NORMALS_ON_INTEGRATION_POINTS(type) \
fem.template computeNormalsOnIntegrationPoints<type>(field, normal, \
ghost_type);
#define AKANTU_SPECIALIZE_COMPUTE_NORMALS_ON_INTEGRATION_POINTS(kind) \
template <> struct ComputeNormalsOnIntegrationPoints<kind> { \
template <template <ElementKind, class> class I, \
template <ElementKind> class S, ElementKind k, class IOF> \
static void call(const FEEngineTemplate<I, S, k, IOF> & fem, \
const Array<Real> & field, Array<Real> & normal, \
ElementType type, GhostType ghost_type) { \
AKANTU_BOOST_KIND_ELEMENT_SWITCH(COMPUTE_NORMALS_ON_INTEGRATION_POINTS, \
kind); \
} \
};
AKANTU_BOOST_ALL_KIND_LIST(
AKANTU_SPECIALIZE_COMPUTE_NORMALS_ON_INTEGRATION_POINTS,
AKANTU_FE_ENGINE_LIST_COMPUTE_NORMALS_ON_INTEGRATION_POINTS)
#undef AKANTU_SPECIALIZE_COMPUTE_NORMALS_ON_INTEGRATION_POINTS
#undef COMPUTE_NORMALS_ON_INTEGRATION_POINTS
} // namespace details
} // namespace fe_engine
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
void FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::
computeNormalsOnIntegrationPoints(const Array<Real> & field,
Array<Real> & normal,
ElementType type,
GhostType ghost_type) const {
fe_engine::details::ComputeNormalsOnIntegrationPoints<kind>::call(
*this, field, normal, type, ghost_type);
}
/* -------------------------------------------------------------------------- */
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
template <ElementType type>
void FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::
computeNormalsOnIntegrationPoints(const Array<Real> & field,
Array<Real> & normal,
GhostType ghost_type) const {
AKANTU_DEBUG_IN();
if (type == _point_1) {
computeNormalsOnIntegrationPointsPoint1(field, normal, ghost_type);
return;
}
UInt spatial_dimension = mesh.getSpatialDimension();
UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
UInt nb_points = getNbIntegrationPoints(type, ghost_type);
UInt nb_element = mesh.getConnectivity(type, ghost_type).size();
normal.resize(nb_element * nb_points);
Array<Real>::matrix_iterator normals_on_quad =
normal.begin_reinterpret(spatial_dimension, nb_points, nb_element);
Array<Real> f_el(0, spatial_dimension * nb_nodes_per_element);
FEEngine::extractNodalToElementField(mesh, field, f_el, type, ghost_type);
const Matrix<Real> & quads =
integrator.template getIntegrationPoints<type>(ghost_type);
Array<Real>::matrix_iterator f_it =
f_el.begin(spatial_dimension, nb_nodes_per_element);
for (UInt elem = 0; elem < nb_element; ++elem) {
ElementClass<type>::computeNormalsOnNaturalCoordinates(quads, *f_it,
*normals_on_quad);
++normals_on_quad;
++f_it;
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
/**
* Helper class to be able to write a partial specialization on the element kind
*/
template <ElementKind kind> struct InverseMapHelper {
template <class S>
static void
call(const S & /*shape_functions*/, const Vector<Real> & /*real_coords*/,
UInt /*element*/, ElementType /*type*/,
Vector<Real> & /*natural_coords*/, GhostType /*ghost_type*/) {
AKANTU_TO_IMPLEMENT();
}
};
#define INVERSE_MAP(type) \
shape_functions.template inverseMap<type>(real_coords, element, \
natural_coords, ghost_type);
#define AKANTU_SPECIALIZE_INVERSE_MAP_HELPER(kind) \
template <> struct InverseMapHelper<kind> { \
template <class S> \
static void call(const S & shape_functions, \
const Vector<Real> & real_coords, UInt element, \
ElementType type, Vector<Real> & natural_coords, \
GhostType ghost_type) { \
AKANTU_BOOST_KIND_ELEMENT_SWITCH(INVERSE_MAP, kind); \
} \
};
AKANTU_BOOST_ALL_KIND_LIST(AKANTU_SPECIALIZE_INVERSE_MAP_HELPER,
AKANTU_FE_ENGINE_LIST_INVERSE_MAP)
#undef AKANTU_SPECIALIZE_INVERSE_MAP_HELPER
#undef INVERSE_MAP
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
inline void FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::inverseMap(
const Vector<Real> & real_coords, UInt element, ElementType type,
Vector<Real> & natural_coords, GhostType ghost_type) const {
AKANTU_DEBUG_IN();
InverseMapHelper<kind>::call(shape_functions, real_coords, element, type,
natural_coords, ghost_type);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
/**
* Helper class to be able to write a partial specialization on the element kind
*/
namespace fe_engine {
namespace details {
template <ElementKind kind> struct ContainsHelper {
template <class S>
static void call(const S & /*unused*/, const Vector<Real> & /*unused*/,
UInt /*unused*/, ElementType /*unused*/,
GhostType /*unused*/) {
AKANTU_TO_IMPLEMENT();
}
};
#define CONTAINS(type) \
contain = shape_functions.template contains<type>(real_coords, element, \
ghost_type);
#define AKANTU_SPECIALIZE_CONTAINS_HELPER(kind) \
template <> struct ContainsHelper<kind> { \
template <template <ElementKind> class S, ElementKind k> \
static bool call(const S<k> & shape_functions, \
const Vector<Real> & real_coords, UInt element, \
ElementType type, GhostType ghost_type) { \
bool contain = false; \
AKANTU_BOOST_KIND_ELEMENT_SWITCH(CONTAINS, kind); \
return contain; \
} \
};
AKANTU_BOOST_ALL_KIND_LIST(AKANTU_SPECIALIZE_CONTAINS_HELPER,
AKANTU_FE_ENGINE_LIST_CONTAINS)
#undef AKANTU_SPECIALIZE_CONTAINS_HELPER
#undef CONTAINS
} // namespace details
} // namespace fe_engine
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
inline bool FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::contains(
const Vector<Real> & real_coords, UInt element, ElementType type,
GhostType ghost_type) const {
return fe_engine::details::ContainsHelper<kind>::call(
shape_functions, real_coords, element, type, ghost_type);
}
/* -------------------------------------------------------------------------- */
/**
* Helper class to be able to write a partial specialization on the element kind
*/
namespace fe_engine {
namespace details {
template <ElementKind kind> struct ComputeShapesHelper {
template <class S>
static void call(const S & /*unused*/, const Vector<Real> & /*unused*/,
UInt /*unused*/, const ElementType /*unused*/,
Vector<Real> & /*unused*/,
GhostType /*unused*/) {
AKANTU_TO_IMPLEMENT();
}
};
#define COMPUTE_SHAPES(type) \
shape_functions.template computeShapes<type>(real_coords, element, shapes, \
ghost_type);
#define AKANTU_SPECIALIZE_COMPUTE_SHAPES_HELPER(kind) \
template <> struct ComputeShapesHelper<kind> { \
template <class S> \
static void call(const S & shape_functions, \
const Vector<Real> & real_coords, UInt element, \
const ElementType type, Vector<Real> & shapes, \
GhostType ghost_type) { \
AKANTU_BOOST_KIND_ELEMENT_SWITCH(COMPUTE_SHAPES, kind); \
} \
};
AKANTU_BOOST_ALL_KIND_LIST(AKANTU_SPECIALIZE_COMPUTE_SHAPES_HELPER,
AKANTU_FE_ENGINE_LIST_COMPUTE_SHAPES)
#undef AKANTU_SPECIALIZE_COMPUTE_SHAPES_HELPER
#undef COMPUTE_SHAPES
} // namespace details
} // namespace fe_engine
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
inline void
FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::computeShapes(
const Vector<Real> & real_coords, UInt element, ElementType type,
Vector<Real> & shapes, GhostType ghost_type) const {
AKANTU_DEBUG_IN();
fe_engine::details::ComputeShapesHelper<kind>::call(
shape_functions, real_coords, element, type, shapes, ghost_type);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
/**
* Helper class to be able to write a partial specialization on the element kind
*/
namespace fe_engine {
namespace details {
template <ElementKind kind> struct ComputeShapeDerivativesHelper {
template <class S>
static void call(__attribute__((unused)) const S & shape_functions,
__attribute__((unused)) const Vector<Real> & real_coords,
__attribute__((unused)) UInt element,
__attribute__((unused)) const ElementType type,
__attribute__((unused)) Matrix<Real> & shape_derivatives,
__attribute__((unused)) GhostType ghost_type) {
AKANTU_TO_IMPLEMENT();
}
};
#define COMPUTE_SHAPE_DERIVATIVES(type) \
Matrix<Real> coords_mat(real_coords.storage(), shape_derivatives.rows(), 1); \
Tensor3<Real> shapesd_tensor(shape_derivatives.storage(), \
shape_derivatives.rows(), \
shape_derivatives.cols(), 1); \
shape_functions.template computeShapeDerivatives<type>( \
coords_mat, element, shapesd_tensor, ghost_type);
#define AKANTU_SPECIALIZE_COMPUTE_SHAPE_DERIVATIVES_HELPER(kind) \
template <> struct ComputeShapeDerivativesHelper<kind> { \
template <class S> \
static void call(const S & shape_functions, \
const Vector<Real> & real_coords, UInt element, \
const ElementType type, Matrix<Real> & shape_derivatives, \
GhostType ghost_type) { \
AKANTU_BOOST_KIND_ELEMENT_SWITCH(COMPUTE_SHAPE_DERIVATIVES, kind); \
} \
};
AKANTU_BOOST_ALL_KIND_LIST(
AKANTU_SPECIALIZE_COMPUTE_SHAPE_DERIVATIVES_HELPER,
AKANTU_FE_ENGINE_LIST_COMPUTE_SHAPES_DERIVATIVES)
#undef AKANTU_SPECIALIZE_COMPUTE_SHAPE_DERIVATIVES_HELPER
#undef COMPUTE_SHAPE_DERIVATIVES
} // namespace details
} // namespace fe_engine
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
inline void
FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::computeShapeDerivatives(
const Vector<Real> & real_coords, UInt element, ElementType type,
Matrix<Real> & shape_derivatives, GhostType ghost_type) const {
AKANTU_DEBUG_IN();
fe_engine::details::ComputeShapeDerivativesHelper<kind>::call(
shape_functions, real_coords, element, type, shape_derivatives,
ghost_type);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
/**
* Helper class to be able to write a partial specialization on the element kind
*/
namespace fe_engine {
namespace details {
template <ElementKind kind> struct GetNbIntegrationPointsHelper {};
#define GET_NB_INTEGRATION_POINTS(type) \
nb_quad_points = integrator.template getNbIntegrationPoints<type>(ghost_type);
#define AKANTU_SPECIALIZE_GET_NB_INTEGRATION_POINTS_HELPER(kind) \
template <> struct GetNbIntegrationPointsHelper<kind> { \
template <template <ElementKind, class> class I, ElementKind k, class IOF> \
static UInt call(const I<k, IOF> & integrator, const ElementType type, \
GhostType ghost_type) { \
UInt nb_quad_points = 0; \
AKANTU_BOOST_KIND_ELEMENT_SWITCH(GET_NB_INTEGRATION_POINTS, kind); \
return nb_quad_points; \
} \
};
AKANTU_BOOST_ALL_KIND(AKANTU_SPECIALIZE_GET_NB_INTEGRATION_POINTS_HELPER)
#undef AKANTU_SPECIALIZE_GET_NB_INTEGRATION_POINTS_HELPER
#undef GET_NB_INTEGRATION
} // namespace details
} // namespace fe_engine
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
inline UInt
FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::getNbIntegrationPoints(
ElementType type, GhostType ghost_type) const {
return fe_engine::details::GetNbIntegrationPointsHelper<kind>::call(
integrator, type, ghost_type);
}
/* -------------------------------------------------------------------------- */
/**
* Helper class to be able to write a partial specialization on the element kind
*/
namespace fe_engine {
namespace details {
template <ElementKind kind> struct GetShapesHelper {};
#define GET_SHAPES(type) ret = &(shape_functions.getShapes(type, ghost_type));
#define AKANTU_SPECIALIZE_GET_SHAPES_HELPER(kind) \
template <> struct GetShapesHelper<kind> { \
template <class S> \
static const Array<Real> & call(const S & shape_functions, \
const ElementType type, \
GhostType ghost_type) { \
const Array<Real> * ret = NULL; \
AKANTU_BOOST_KIND_ELEMENT_SWITCH(GET_SHAPES, kind); \
return *ret; \
} \
};
AKANTU_BOOST_ALL_KIND(AKANTU_SPECIALIZE_GET_SHAPES_HELPER)
#undef AKANTU_SPECIALIZE_GET_SHAPES_HELPER
#undef GET_SHAPES
} // namespace details
} // namespace fe_engine
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
inline const Array<Real> &
FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::getShapes(
ElementType type, GhostType ghost_type,
__attribute__((unused)) UInt id) const {
return fe_engine::details::GetShapesHelper<kind>::call(shape_functions, type,
ghost_type);
}
/* -------------------------------------------------------------------------- */
/**
* Helper class to be able to write a partial specialization on the element kind
*/
namespace fe_engine {
namespace details {
template <ElementKind kind> struct GetShapesDerivativesHelper {
template <template <ElementKind> class S, ElementKind k>
static const Array<Real> &
call(const S<k> & /*unused*/, ElementType /*unused*/,
GhostType /*unused*/, UInt /*unused*/) {
AKANTU_TO_IMPLEMENT();
}
};
#define GET_SHAPES_DERIVATIVES(type) \
ret = &(shape_functions.getShapesDerivatives(type, ghost_type));
#define AKANTU_SPECIALIZE_GET_SHAPES_DERIVATIVES_HELPER(kind) \
template <> struct GetShapesDerivativesHelper<kind> { \
template <template <ElementKind> class S, ElementKind k> \
static const Array<Real> & \
call(const S<k> & shape_functions, const ElementType type, \
GhostType ghost_type, __attribute__((unused)) UInt id) { \
const Array<Real> * ret = NULL; \
AKANTU_BOOST_KIND_ELEMENT_SWITCH(GET_SHAPES_DERIVATIVES, kind); \
return *ret; \
} \
};
AKANTU_BOOST_ALL_KIND_LIST(AKANTU_SPECIALIZE_GET_SHAPES_DERIVATIVES_HELPER,
AKANTU_FE_ENGINE_LIST_GET_SHAPES_DERIVATIVES)
#undef AKANTU_SPECIALIZE_GET_SHAPE_DERIVATIVES_HELPER
#undef GET_SHAPES_DERIVATIVES
} // namespace details
} // namespace fe_engine
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
inline const Array<Real> &
FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::getShapesDerivatives(
ElementType type, GhostType ghost_type,
__attribute__((unused)) UInt id) const {
return fe_engine::details::GetShapesDerivativesHelper<kind>::call(
shape_functions, type, ghost_type, id);
}
/* -------------------------------------------------------------------------- */
/**
* Helper class to be able to write a partial specialization on the element kind
*/
namespace fe_engine {
namespace details {
template <ElementKind kind> struct GetIntegrationPointsHelper {};
#define GET_INTEGRATION_POINTS(type) \
ret = &(integrator.template getIntegrationPoints<type>(ghost_type));
#define AKANTU_SPECIALIZE_GET_INTEGRATION_POINTS_HELPER(kind) \
template <> struct GetIntegrationPointsHelper<kind> { \
template <template <ElementKind, class> class I, ElementKind k, class IOF> \
static const Matrix<Real> & call(const I<k, IOF> & integrator, \
const ElementType type, \
GhostType ghost_type) { \
const Matrix<Real> * ret = NULL; \
AKANTU_BOOST_KIND_ELEMENT_SWITCH(GET_INTEGRATION_POINTS, kind); \
return *ret; \
} \
};
AKANTU_BOOST_ALL_KIND(AKANTU_SPECIALIZE_GET_INTEGRATION_POINTS_HELPER)
#undef AKANTU_SPECIALIZE_GET_INTEGRATION_POINTS_HELPER
#undef GET_INTEGRATION_POINTS
} // namespace details
} // namespace fe_engine
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
inline const Matrix<Real> &
FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::getIntegrationPoints(
ElementType type, GhostType ghost_type) const {
return fe_engine::details::GetIntegrationPointsHelper<kind>::call(
integrator, type, ghost_type);
}
/* -------------------------------------------------------------------------- */
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
void FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::printself(
std::ostream & stream, int indent) const {
std::string space(indent, AKANTU_INDENT);
stream << space << "FEEngineTemplate [" << std::endl;
stream << space << " + parent [" << std::endl;
FEEngine::printself(stream, indent + 3);
stream << space << " ]" << std::endl;
stream << space << " + shape functions [" << std::endl;
shape_functions.printself(stream, indent + 3);
stream << space << " ]" << std::endl;
stream << space << " + integrator [" << std::endl;
integrator.printself(stream, indent + 3);
stream << space << " ]" << std::endl;
stream << space << "]" << std::endl;
}
/* -------------------------------------------------------------------------- */
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
void FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::onElementsAdded(
const Array<Element> & new_elements, const NewElementsEvent & /*unused*/) {
integrator.onElementsAdded(new_elements);
shape_functions.onElementsAdded(new_elements);
}
/* -------------------------------------------------------------------------- */
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
void FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::onElementsRemoved(
const Array<Element> & /*unused*/,
const ElementTypeMapArray<UInt> & /*unused*/,
const RemovedElementsEvent & /*unused*/) {}
/* -------------------------------------------------------------------------- */
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
void FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::onElementsChanged(
const Array<Element> & /*unused*/, const Array<Element> & /*unused*/,
const ElementTypeMapArray<UInt> & /*unused*/,
const ChangedElementsEvent & /*unused*/) {}
/* -------------------------------------------------------------------------- */
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
inline void FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::
computeNormalsOnIntegrationPointsPoint1(
const Array<Real> & /*unused*/, Array<Real> & normal,
GhostType ghost_type) const {
AKANTU_DEBUG_IN();
AKANTU_DEBUG_ASSERT(mesh.getSpatialDimension() == 1,
"Mesh dimension must be 1 to compute normals on points!");
const auto type = _point_1;
auto spatial_dimension = mesh.getSpatialDimension();
// UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
auto nb_points = getNbIntegrationPoints(type, ghost_type);
const auto & connectivity = mesh.getConnectivity(type, ghost_type);
auto nb_element = connectivity.size();
normal.resize(nb_element * nb_points);
auto normals_on_quad =
normal.begin_reinterpret(spatial_dimension, nb_points, nb_element);
const auto & segments = mesh.getElementToSubelement(type, ghost_type);
const auto & coords = mesh.getNodes();
const Mesh * mesh_segment;
if (mesh.isMeshFacets()) {
mesh_segment = &(mesh.getMeshParent());
} else {
mesh_segment = &mesh;
}
for (UInt elem = 0; elem < nb_element; ++elem) {
UInt nb_segment = segments(elem).size();
AKANTU_DEBUG_ASSERT(
nb_segment > 0,
"Impossible to compute a normal on a point connected to 0 segments");
Real normal_value = 1;
if (nb_segment == 1) {
auto point = connectivity(elem);
const auto segment = segments(elem)[0];
const auto & segment_connectivity =
mesh_segment->getConnectivity(segment.type, segment.ghost_type);
Vector<UInt> segment_points = segment_connectivity.begin(
Mesh::getNbNodesPerElement(segment.type))[segment.element];
Real difference;
if (segment_points(0) == point) {
difference = coords(elem) - coords(segment_points(1));
} else {
difference = coords(elem) - coords(segment_points(0));
}
normal_value = difference / std::abs(difference);
}
for (UInt n(0); n < nb_points; ++n) {
(*normals_on_quad)(0, n) = normal_value;
}
++normals_on_quad;
}
AKANTU_DEBUG_OUT();
}
} // namespace akantu
diff --git a/src/fe_engine/fe_engine_template_tmpl_field.hh b/src/fe_engine/fe_engine_template_tmpl_field.hh
index c217c5892..59bca0a79 100644
--- a/src/fe_engine/fe_engine_template_tmpl_field.hh
+++ b/src/fe_engine/fe_engine_template_tmpl_field.hh
@@ -1,455 +1,505 @@
/**
* @file fe_engine_template_tmpl_field.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Wed Aug 09 2017
* @date last modification: Thu Dec 07 2017
*
* @brief implementation of the assemble field s functions
*
*
* Copyright (©) 2016-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "fe_engine_template.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_FE_ENGINE_TEMPLATE_TMPL_FIELD_HH_
#define AKANTU_FE_ENGINE_TEMPLATE_TMPL_FIELD_HH_
namespace akantu {
/* -------------------------------------------------------------------------- */
/* Matrix lumping functions */
/* -------------------------------------------------------------------------- */
namespace fe_engine {
namespace details {
namespace {
template <class Functor>
void fillField(const Functor & field_funct, Array<Real> & field,
UInt nb_element, UInt nb_integration_points,
ElementType type, GhostType ghost_type) {
UInt nb_degree_of_freedom = field.getNbComponent();
field.resize(nb_integration_points * nb_element);
auto field_it = field.begin_reinterpret(
nb_degree_of_freedom, nb_integration_points, nb_element);
Element el{type, 0, ghost_type};
for (; el.element < nb_element; ++el.element, ++field_it) {
field_funct(*field_it, el);
}
}
} // namespace
} // namespace details
} // namespace fe_engine
/**
* Helper class to be able to write a partial specialization on the element kind
*/
namespace fe_engine {
namespace details {
template <ElementKind kind> struct AssembleLumpedTemplateHelper {
template <template <ElementKind, class> class I,
template <ElementKind> class S, ElementKind k, class IOF>
static void call(const FEEngineTemplate<I, S, k, IOF> & /*unused*/,
const std::function<void(Matrix<Real> &,
const Element &)> & /*unused*/,
const ID & /*unused*/, const ID & /*unused*/,
DOFManager & /*unused*/, ElementType /*unused*/,
GhostType /*unused*/) {
AKANTU_TO_IMPLEMENT();
}
};
#define ASSEMBLE_LUMPED(type) \
fem.template assembleFieldLumped<type>(field_funct, lumped, dof_id, \
dof_manager, ghost_type)
#define AKANTU_SPECIALIZE_ASSEMBLE_HELPER(kind) \
template <> struct AssembleLumpedTemplateHelper<kind> { \
template <template <ElementKind, class> class I, \
template <ElementKind> class S, ElementKind k, class IOF> \
static void \
call(const FEEngineTemplate<I, S, k, IOF> & fem, \
const std::function<void(Matrix<Real> &, const Element &)> & \
field_funct, \
const ID & lumped, const ID & dof_id, DOFManager & dof_manager, \
- ElementType type, GhostType ghost_type) { \
+ ElementType type, GhostType ghost_type) { \
AKANTU_BOOST_KIND_ELEMENT_SWITCH(ASSEMBLE_LUMPED, kind); \
} \
};
AKANTU_BOOST_ALL_KIND_LIST(AKANTU_SPECIALIZE_ASSEMBLE_HELPER,
AKANTU_FE_ENGINE_LIST_ASSEMBLE_FIELDS)
#undef AKANTU_SPECIALIZE_ASSEMBLE_HELPER
#undef AKANTU_SPECIALIZE_ASSEMBLE_HELPER_LIST_KIND
#undef ASSEMBLE_LUMPED
} // namespace details
} // namespace fe_engine
/* -------------------------------------------------------------------------- */
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IOF>
void FEEngineTemplate<I, S, kind, IOF>::assembleFieldLumped(
const std::function<void(Matrix<Real> &, const Element &)> & field_funct,
const ID & matrix_id, const ID & dof_id, DOFManager & dof_manager,
ElementType type, GhostType ghost_type) const {
AKANTU_DEBUG_IN();
fe_engine::details::AssembleLumpedTemplateHelper<kind>::call(
*this, field_funct, matrix_id, dof_id, dof_manager, type, ghost_type);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
template <ElementType type>
void FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::assembleFieldLumped(
const std::function<void(Matrix<Real> &, const Element &)> & field_funct,
const ID & matrix_id, const ID & dof_id, DOFManager & dof_manager,
GhostType ghost_type) const {
AKANTU_DEBUG_IN();
UInt nb_degree_of_freedom = dof_manager.getDOFs(dof_id).getNbComponent();
UInt nb_element = mesh.getNbElement(type, ghost_type);
UInt nb_integration_points = this->getNbIntegrationPoints(type);
Array<Real> field(0, nb_degree_of_freedom);
fe_engine::details::fillField(field_funct, field, nb_element,
nb_integration_points, type, ghost_type);
switch (type) {
case _triangle_6:
case _quadrangle_8:
case _tetrahedron_10:
case _hexahedron_20:
case _pentahedron_15:
this->template assembleLumpedDiagonalScaling<type>(field, matrix_id, dof_id,
dof_manager, ghost_type);
break;
default:
this->template assembleLumpedRowSum<type>(field, matrix_id, dof_id,
dof_manager, ghost_type);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
/**
* @f$ \tilde{M}_{i} = \sum_j M_{ij} = \sum_j \int \rho \varphi_i \varphi_j dV =
* \int \rho \varphi_i dV @f$
*/
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
template <ElementType type>
void FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::
assembleLumpedRowSum(const Array<Real> & field, const ID & matrix_id,
const ID & dof_id, DOFManager & dof_manager,
GhostType ghost_type) const {
AKANTU_DEBUG_IN();
UInt shapes_size = ElementClass<type>::getShapeSize();
UInt nb_degree_of_freedom = field.getNbComponent();
auto * field_times_shapes =
new Array<Real>(0, shapes_size * nb_degree_of_freedom);
shape_functions.template computeNtb<type>(field, *field_times_shapes,
ghost_type);
UInt nb_element = mesh.getNbElement(type, ghost_type);
auto * int_field_times_shapes = new Array<Real>(
nb_element, shapes_size * nb_degree_of_freedom, "inte_rho_x_shapes");
integrator.template integrate<type>(
*field_times_shapes, *int_field_times_shapes,
nb_degree_of_freedom * shapes_size, ghost_type, empty_filter);
delete field_times_shapes;
dof_manager.assembleElementalArrayToLumpedMatrix(
dof_id, *int_field_times_shapes, matrix_id, type, ghost_type);
delete int_field_times_shapes;
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
/**
* @f$ \tilde{M}_{i} = c * M_{ii} = \int_{V_e} \rho dV @f$
*/
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
template <ElementType type>
void FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::
assembleLumpedDiagonalScaling(const Array<Real> & field,
const ID & matrix_id, const ID & dof_id,
DOFManager & dof_manager,
GhostType ghost_type) const {
AKANTU_DEBUG_IN();
ElementType type_p1 = ElementClass<type>::getP1ElementType();
UInt nb_nodes_per_element_p1 = Mesh::getNbNodesPerElement(type_p1);
UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
UInt nb_degree_of_freedom = field.getNbComponent();
UInt nb_element = mesh.getNbElement(type, ghost_type);
Vector<Real> nodal_factor(nb_nodes_per_element);
#define ASSIGN_WEIGHT_TO_NODES(corner, mid) \
{ \
for (UInt n = 0; n < nb_nodes_per_element_p1; n++) \
nodal_factor(n) = corner; \
for (UInt n = nb_nodes_per_element_p1; n < nb_nodes_per_element; n++) \
nodal_factor(n) = mid; \
}
if (type == _triangle_6)
ASSIGN_WEIGHT_TO_NODES(1. / 12., 1. / 4.);
if (type == _tetrahedron_10)
ASSIGN_WEIGHT_TO_NODES(1. / 32., 7. / 48.);
if (type == _quadrangle_8)
ASSIGN_WEIGHT_TO_NODES(
3. / 76.,
16. / 76.); /** coeff. derived by scaling
* the diagonal terms of the corresponding
* consistent mass computed with 3x3 gauss points;
* coeff. are (1./36., 8./36.) for 2x2 gauss points */
if (type == _hexahedron_20)
ASSIGN_WEIGHT_TO_NODES(
7. / 248., 16. / 248.); /** coeff. derived by scaling
* the diagonal terms of the corresponding
* consistent mass computed with 3x3x3 gauss
* points; coeff. are (1./40.,
* 1./15.) for 2x2x2 gauss points */
if (type == _pentahedron_15) {
// coefficients derived by scaling the diagonal terms of the corresponding
// consistent mass computed with 8 gauss points;
for (UInt n = 0; n < nb_nodes_per_element_p1; n++) {
nodal_factor(n) = 51. / 2358.;
}
Real mid_triangle = 192. / 2358.;
Real mid_quadrangle = 300. / 2358.;
nodal_factor(6) = mid_triangle;
nodal_factor(7) = mid_triangle;
nodal_factor(8) = mid_triangle;
nodal_factor(9) = mid_quadrangle;
nodal_factor(10) = mid_quadrangle;
nodal_factor(11) = mid_quadrangle;
nodal_factor(12) = mid_triangle;
nodal_factor(13) = mid_triangle;
nodal_factor(14) = mid_triangle;
}
if (nb_element == 0) {
AKANTU_DEBUG_OUT();
return;
}
#undef ASSIGN_WEIGHT_TO_NODES
/// compute @f$ \int \rho dV = \rho V @f$ for each element
auto int_field = std::make_unique<Array<Real>>(
field.size(), nb_degree_of_freedom, "inte_rho_x");
integrator.template integrate<type>(field, *int_field, nb_degree_of_freedom,
ghost_type, empty_filter);
/// distribute the mass of the element to the nodes
auto lumped_per_node = std::make_unique<Array<Real>>(
nb_element, nb_degree_of_freedom * nb_nodes_per_element, "mass_per_node");
auto int_field_it = int_field->begin(nb_degree_of_freedom);
auto lumped_per_node_it =
lumped_per_node->begin(nb_degree_of_freedom, nb_nodes_per_element);
for (UInt e = 0; e < nb_element; ++e) {
for (UInt n = 0; n < nb_nodes_per_element; ++n) {
Vector<Real> l = (*lumped_per_node_it)(n);
l = *int_field_it;
l *= nodal_factor(n);
}
++int_field_it;
++lumped_per_node_it;
}
dof_manager.assembleElementalArrayToLumpedMatrix(dof_id, *lumped_per_node,
matrix_id, type, ghost_type);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
/**
* Helper class to be able to write a partial specialization on the element kind
*/
namespace fe_engine {
namespace details {
template <ElementKind kind> struct AssembleFieldMatrixHelper {
template <template <ElementKind, class> class I,
template <ElementKind> class S, ElementKind k, class IOF>
static void call(const FEEngineTemplate<I, S, k, IOF> & /*unused*/,
const std::function<void(Matrix<Real> &,
const Element &)> & /*unused*/,
const ID & /*unused*/, const ID & /*unused*/,
DOFManager & /*unused*/, ElementType /*unused*/,
GhostType /*unused*/) {
AKANTU_TO_IMPLEMENT();
}
};
#define ASSEMBLE_MATRIX(type) \
fem.template assembleFieldMatrix<type>(field_funct, matrix_id, dof_id, \
dof_manager, ghost_type)
#define AKANTU_SPECIALIZE_ASSEMBLE_FIELD_MATRIX_HELPER(kind) \
template <> struct AssembleFieldMatrixHelper<kind> { \
template <template <ElementKind, class> class I, \
template <ElementKind> class S, ElementKind k, class IOF> \
static void \
call(const FEEngineTemplate<I, S, k, IOF> & fem, \
const std::function<void(Matrix<Real> &, const Element &)> & \
field_funct, \
const ID & matrix_id, const ID & dof_id, DOFManager & dof_manager, \
- ElementType type, GhostType ghost_type) { \
+ ElementType type, GhostType ghost_type) { \
AKANTU_BOOST_KIND_ELEMENT_SWITCH(ASSEMBLE_MATRIX, kind); \
} \
};
AKANTU_BOOST_ALL_KIND_LIST(AKANTU_SPECIALIZE_ASSEMBLE_FIELD_MATRIX_HELPER,
AKANTU_FE_ENGINE_LIST_ASSEMBLE_FIELDS)
#undef AKANTU_SPECIALIZE_ASSEMBLE_FIELD_MATRIX_HELPER
#undef ASSEMBLE_MATRIX
} // namespace details
} // namespace fe_engine
/* -------------------------------------------------------------------------- */
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IOF>
void FEEngineTemplate<I, S, kind, IOF>::assembleFieldMatrix(
const std::function<void(Matrix<Real> &, const Element &)> & field_funct,
const ID & matrix_id, const ID & dof_id, DOFManager & dof_manager,
ElementType type, GhostType ghost_type) const {
AKANTU_DEBUG_IN();
fe_engine::details::AssembleFieldMatrixHelper<kind>::template call(
*this, field_funct, matrix_id, dof_id, dof_manager, type, ghost_type);
AKANTU_DEBUG_OUT();
}
+namespace fe_engine {
+ namespace details {
+ template <ElementKind kind> struct ShapesForMassHelper {
+ template <ElementType type, class ShapeFunctions>
+ static auto getShapes(ShapeFunctions & shape_functions,
+ const Matrix<Real> & integration_points,
+ const Array<Real> & nodes,
+ UInt & nb_degree_of_freedom, UInt nb_element,
+ GhostType ghost_type) {
+
+ UInt shapes_size = ElementClass<type>::getShapeSize();
+ Array<Real> shapes(0, shapes_size);
+
+ shape_functions.template computeShapesOnIntegrationPoints<type>(
+ nodes, integration_points, shapes, ghost_type);
+
+ UInt nb_integration_points = integration_points.cols();
+ UInt vect_size = nb_integration_points * nb_element;
+ UInt lmat_size = nb_degree_of_freedom * shapes_size;
+
+ // Extending the shape functions
+ /// \todo move this in the shape functions as Voigt format shapes to
+ /// have the code in common with the structural elements
+ auto shapes_voigt = std::make_unique<Array<Real>>(
+ vect_size, lmat_size * nb_degree_of_freedom, 0.);
+ auto mshapes_it = shapes_voigt->begin(nb_degree_of_freedom, lmat_size);
+ auto shapes_it = shapes.begin(shapes_size);
+
+ for (UInt q = 0; q < vect_size; ++q, ++mshapes_it, ++shapes_it) {
+ for (UInt d = 0; d < nb_degree_of_freedom; ++d) {
+ for (UInt s = 0; s < shapes_size; ++s) {
+ (*mshapes_it)(d, s * nb_degree_of_freedom + d) = (*shapes_it)(s);
+ }
+ }
+ }
+
+ return shapes_voigt;
+ }
+ };
+
+#if defined(AKANTU_STRUCTURAL_MECHANICS)
+ template <> struct ShapesForMassHelper<_ek_structural> {
+ template <ElementType type, class ShapeFunctions>
+ static auto getShapes(ShapeFunctions & shape_functions,
+ const Matrix<Real> & integration_points,
+ const Array<Real> & nodes,
+ UInt & nb_degree_of_freedom, UInt /*nb_element*/,
+ GhostType ghost_type) {
+
+ auto nb_unknown = ElementClass<type>::getNbStressComponents();
+ auto nb_degree_of_freedom_ = ElementClass<type>::getNbDegreeOfFreedom();
+ auto nb_nodes_per_element = ElementClass<type>::getNbNodesPerElement();
+ auto shapes = std::make_unique<Array<Real>>(
+ 0, nb_unknown * nb_nodes_per_element * nb_degree_of_freedom_);
+ nb_degree_of_freedom = nb_unknown;
+ shape_functions.template computeShapesMassOnIntegrationPoints<type>(
+ nodes, integration_points, *shapes, ghost_type);
+
+ return shapes;
+ }
+ };
+#endif
+ } // namespace details
+} // namespace fe_engine
+ //
/* -------------------------------------------------------------------------- */
/**
* @f$ \tilde{M}_{i} = \sum_j M_{ij} = \sum_j \int \rho \varphi_i \varphi_j dV =
* \int \rho \varphi_i dV @f$
*/
template <template <ElementKind, class> class I, template <ElementKind> class S,
ElementKind kind, class IntegrationOrderFunctor>
template <ElementType type>
void FEEngineTemplate<I, S, kind, IntegrationOrderFunctor>::assembleFieldMatrix(
const std::function<void(Matrix<Real> &, const Element &)> & field_funct,
const ID & matrix_id, const ID & dof_id, DOFManager & dof_manager,
GhostType ghost_type) const {
AKANTU_DEBUG_IN();
- UInt shapes_size = ElementClass<type>::getShapeSize();
- UInt nb_degree_of_freedom = dof_manager.getDOFs(dof_id).getNbComponent();
- UInt lmat_size = nb_degree_of_freedom * shapes_size;
- UInt nb_element = mesh.getNbElement(type, ghost_type);
-
// \int N * N so degree 2 * degree of N
const UInt polynomial_degree =
2 * ElementClassProperty<type>::polynomial_degree;
// getting the integration points
Matrix<Real> integration_points =
integrator.template getIntegrationPoints<type, polynomial_degree>();
- UInt nb_integration_points = integration_points.cols();
- UInt vect_size = nb_integration_points * nb_element;
+ UInt nb_degree_of_freedom = dof_manager.getDOFs(dof_id).getNbComponent();
+ UInt nb_element = mesh.getNbElement(type, ghost_type);
// getting the shapes on the integration points
- Array<Real> shapes(0, shapes_size);
- shape_functions.template computeShapesOnIntegrationPoints<type>(
- mesh.getNodes(), integration_points, shapes, ghost_type);
-
- // Extending the shape functions
- /// \todo move this in the shape functions as Voigt format shapes to have the
- /// code in common with the structural elements
- Array<Real> modified_shapes(vect_size, lmat_size * nb_degree_of_freedom, 0.);
- Array<Real> local_mat(vect_size, lmat_size * lmat_size);
- auto mshapes_it = modified_shapes.begin(nb_degree_of_freedom, lmat_size);
- auto shapes_it = shapes.begin(shapes_size);
+ auto shapes_voigt =
+ fe_engine::details::ShapesForMassHelper<kind>::template getShapes<type>(
+ shape_functions, integration_points, mesh.getNodes(),
+ nb_degree_of_freedom, nb_element, ghost_type);
- for (UInt q = 0; q < vect_size; ++q, ++mshapes_it, ++shapes_it) {
- for (UInt d = 0; d < nb_degree_of_freedom; ++d) {
- for (UInt s = 0; s < shapes_size; ++s) {
- (*mshapes_it)(d, s * nb_degree_of_freedom + d) = (*shapes_it)(s);
- }
- }
- }
+ auto vect_size = shapes_voigt->size();
// getting the value to assemble on the integration points
Array<Real> field(vect_size, nb_degree_of_freedom);
fe_engine::details::fillField(field_funct, field, nb_element,
- nb_integration_points, type, ghost_type);
+ integration_points.cols(), type, ghost_type);
+
+ auto lmat_size = shapes_voigt->getNbComponent() / nb_degree_of_freedom;
// computing \rho * N
- mshapes_it = modified_shapes.begin(nb_degree_of_freedom, lmat_size);
- auto lmat = local_mat.begin(lmat_size, lmat_size);
+ Array<Real> local_mat(vect_size, lmat_size * lmat_size);
+ auto N_it = shapes_voigt->begin(nb_degree_of_freedom, lmat_size);
+ auto lmat_it = local_mat.begin(lmat_size, lmat_size);
auto field_it = field.begin_reinterpret(nb_degree_of_freedom, field.size());
- for (UInt q = 0; q < vect_size; ++q, ++lmat, ++mshapes_it, ++field_it) {
+ for (UInt q = 0; q < vect_size; ++q, ++lmat_it, ++N_it, ++field_it) {
const auto & rho = *field_it;
- const auto & N = *mshapes_it;
- auto & mat = *lmat;
+ const auto & N = *N_it;
+ auto & mat = *lmat_it;
Matrix<Real> Nt = N.transpose();
for (UInt d = 0; d < Nt.cols(); ++d) {
Nt(d) *= rho(d);
}
mat.template mul<false, false>(Nt, N);
}
// integrate the elemental values
Array<Real> int_field_times_shapes(nb_element, lmat_size * lmat_size,
"inte_rho_x_shapes");
this->integrator.template integrate<type, polynomial_degree>(
local_mat, int_field_times_shapes, lmat_size * lmat_size, ghost_type);
// assemble the elemental values to the matrix
dof_manager.assembleElementalMatricesToMatrix(
matrix_id, dof_id, int_field_times_shapes, type, ghost_type);
AKANTU_DEBUG_OUT();
}
} // namespace akantu
#endif /* AKANTU_FE_ENGINE_TEMPLATE_TMPL_FIELD_HH_ */
diff --git a/src/fe_engine/integrator.hh b/src/fe_engine/integrator.hh
index ac6f4bc0e..6acc7aaf1 100644
--- a/src/fe_engine/integrator.hh
+++ b/src/fe_engine/integrator.hh
@@ -1,139 +1,136 @@
/**
* @file integrator.hh
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Sun Dec 03 2017
*
* @brief interface for integrator classes
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef AKANTU_INTEGRATOR_HH_
#define AKANTU_INTEGRATOR_HH_
/* -------------------------------------------------------------------------- */
-#include "aka_memory.hh"
#include "mesh.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
-class Integrator : protected Memory {
+class Integrator {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
Integrator(const Mesh & mesh, UInt spatial_dimension,
- const ID & id = "integrator", const MemoryID & memory_id = 0)
- : Memory(id, memory_id), mesh(mesh),
- _spatial_dimension(spatial_dimension),
- jacobians("jacobians", id, memory_id) {
+ const ID & id = "integrator")
+ : mesh(mesh), _spatial_dimension(spatial_dimension),
+ jacobians("jacobians", id) {
AKANTU_DEBUG_IN();
AKANTU_DEBUG_OUT();
};
- ~Integrator() override = default;
+ virtual ~Integrator() = default;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// empty method
template <ElementType type>
inline void precomputeJacobiansOnQuadraturePoints(__attribute__((unused))
GhostType ghost_type) {}
/// empty method
void integrateOnElement(const Array<Real> & /*f*/, Real * /*intf*/,
UInt /*nb_degree_of_freedom*/,
const Element & /*elem*/,
GhostType /*ghost_type*/) const {};
/// function to print the contain of the class
virtual void printself(std::ostream & stream, int indent = 0) const {
std::string space;
for (Int i = 0; i < indent; i++, space += AKANTU_INDENT) {
;
}
stream << space << "Integrator [" << std::endl;
jacobians.printself(stream, indent + 1);
stream << space << "]" << std::endl;
};
/* ------------------------------------------------------------------------ */
public:
virtual void onElementsAdded(const Array<Element> & /*unused*/) {}
virtual void
onElementsRemoved(const Array<Element> & /*unused*/,
const ElementTypeMapArray<UInt> & new_numbering) {
jacobians.onElementsRemoved(new_numbering);
}
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
/// access to the jacobians
Array<Real> & getJacobians(ElementType type,
GhostType ghost_type = _not_ghost) {
return jacobians(type, ghost_type);
};
/// access to the jacobians const
- const Array<Real> &
- getJacobians(ElementType type,
- GhostType ghost_type = _not_ghost) const {
+ const Array<Real> & getJacobians(ElementType type,
+ GhostType ghost_type = _not_ghost) const {
return jacobians(type, ghost_type);
};
AKANTU_GET_MACRO(Jacobians, jacobians, const ElementTypeMapArray<Real> &);
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
/// mesh associated to the integrator
const Mesh & mesh;
// spatial dimension of the elements to consider
UInt _spatial_dimension;
/// jacobians for all elements
ElementTypeMapArray<Real> jacobians;
};
/* -------------------------------------------------------------------------- */
/* inline functions */
/* -------------------------------------------------------------------------- */
//#include "integrator_inline_impl.hh"
/// standard output stream operator
inline std::ostream & operator<<(std::ostream & stream,
const Integrator & _this) {
_this.printself(stream);
return stream;
}
} // namespace akantu
#endif /* AKANTU_INTEGRATOR_HH_ */
diff --git a/src/fe_engine/integrator_gauss.hh b/src/fe_engine/integrator_gauss.hh
index f822a5b41..8897fe963 100644
--- a/src/fe_engine/integrator_gauss.hh
+++ b/src/fe_engine/integrator_gauss.hh
@@ -1,204 +1,203 @@
/**
* @file integrator_gauss.hh
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Wed Nov 08 2017
*
* @brief Gauss integration facilities
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "integrator.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_INTEGRATOR_GAUSS_HH_
#define AKANTU_INTEGRATOR_GAUSS_HH_
namespace akantu {
namespace integrator {
namespace details {
template <ElementKind> struct GaussIntegratorComputeJacobiansHelper;
} // namespace details
} // namespace integrator
/* -------------------------------------------------------------------------- */
template <ElementKind kind, class IntegrationOrderFunctor>
class IntegratorGauss : public Integrator {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
IntegratorGauss(const Mesh & mesh, UInt spatial_dimension,
- const ID & id = "integrator_gauss",
- const MemoryID & memory_id = 0);
+ const ID & id = "integrator_gauss");
~IntegratorGauss() override = default;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
void initIntegrator(const Array<Real> & nodes, ElementType type,
GhostType ghost_type);
template <ElementType type>
inline void initIntegrator(const Array<Real> & nodes,
GhostType ghost_type);
/// integrate f on the element "elem" of type "type"
template <ElementType type>
inline void integrateOnElement(const Array<Real> & f, Real * intf,
UInt nb_degree_of_freedom, UInt elem,
GhostType ghost_type) const;
/// integrate f for all elements of type "type"
template <ElementType type>
void integrate(const Array<Real> & in_f, Array<Real> & intf,
UInt nb_degree_of_freedom, GhostType ghost_type,
const Array<UInt> & filter_elements) const;
/// integrate scalar field in_f
template <ElementType type, UInt polynomial_degree>
Real integrate(const Array<Real> & in_f,
GhostType ghost_type = _not_ghost) const;
/// integrate partially around a quadrature point (@f$ intf_q = f_q * J_q *
/// w_q @f$)
template <ElementType type>
Real integrate(const Vector<Real> & in_f, UInt index,
GhostType ghost_type) const;
/// integrate scalar field in_f
template <ElementType type>
Real integrate(const Array<Real> & in_f, GhostType ghost_type,
const Array<UInt> & filter_elements) const;
/// integrate a field without using the pre-computed values
template <ElementType type, UInt polynomial_degree>
void integrate(const Array<Real> & in_f, Array<Real> & intf,
UInt nb_degree_of_freedom, GhostType ghost_type) const;
/// integrate partially around a quadrature point (@f$ intf_q = f_q * J_q *
/// w_q @f$)
template <ElementType type>
void integrateOnIntegrationPoints(const Array<Real> & in_f,
Array<Real> & intf,
UInt nb_degree_of_freedom,
GhostType ghost_type,
const Array<UInt> & filter_elements) const;
/// return a matrix with quadrature points natural coordinates
template <ElementType type>
const Matrix<Real> & getIntegrationPoints(GhostType ghost_type) const;
/// return number of quadrature points
template <ElementType type>
UInt getNbIntegrationPoints(GhostType ghost_type) const;
template <ElementType type, UInt n> Matrix<Real> getIntegrationPoints() const;
template <ElementType type, UInt n>
Vector<Real> getIntegrationWeights() const;
protected:
friend struct integrator::details::GaussIntegratorComputeJacobiansHelper<
kind>;
template <ElementType type>
void computeJacobiansOnIntegrationPoints(
const Array<Real> & nodes, const Matrix<Real> & quad_points,
Array<Real> & jacobians, GhostType ghost_type,
const Array<UInt> & filter_elements = empty_filter) const;
void computeJacobiansOnIntegrationPoints(
const Array<Real> & nodes, const Matrix<Real> & quad_points,
Array<Real> & jacobians, ElementType type,
GhostType ghost_type,
const Array<UInt> & filter_elements = empty_filter) const;
/// precompute jacobians on elements of type "type"
template <ElementType type>
void precomputeJacobiansOnQuadraturePoints(const Array<Real> & nodes,
GhostType ghost_type);
// multiply the jacobians by the integration weights and stores the results in
// jacobians
template <ElementType type, UInt polynomial_degree>
void multiplyJacobiansByWeights(
Array<Real> & jacobians,
const Array<UInt> & filter_elements = empty_filter) const;
/// compute the vector of quadrature points natural coordinates
template <ElementType type>
void computeQuadraturePoints(GhostType ghost_type);
/// check that the jacobians are not negative
template <ElementType type>
void checkJacobians(GhostType ghost_type) const;
/// internal integrate partially around a quadrature point (@f$ intf_q = f_q *
/// J_q *
/// w_q @f$)
void integrateOnIntegrationPoints(const Array<Real> & in_f,
Array<Real> & intf,
UInt nb_degree_of_freedom,
const Array<Real> & jacobians,
UInt nb_element) const;
void integrate(const Array<Real> & in_f, Array<Real> & intf,
UInt nb_degree_of_freedom, const Array<Real> & jacobians,
UInt nb_element) const;
public:
/// compute the jacobians on quad points for a given element
template <ElementType type>
void computeJacobianOnQuadPointsByElement(const Matrix<Real> & node_coords,
const Matrix<Real> & quad,
Vector<Real> & jacobians) const;
public:
void onElementsAdded(const Array<Element> & elements) override;
template <ElementType type>
void onElementsAddedByType(const Array<UInt> & new_elements,
GhostType ghost_type);
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
/// integrate the field f with the jacobian jac -> inte
inline void integrate(Real * f, Real * jac, Real * inte,
UInt nb_degree_of_freedom,
UInt nb_quadrature_points) const;
private:
/// ElementTypeMap of the quadrature points
ElementTypeMap<Matrix<Real>> quadrature_points;
};
} // namespace akantu
#include "integrator_gauss_inline_impl.hh"
#endif /* AKANTU_INTEGRATOR_GAUSS_HH_ */
diff --git a/src/fe_engine/integrator_gauss_inline_impl.hh b/src/fe_engine/integrator_gauss_inline_impl.hh
index 04334d50b..cd4c13a56 100644
--- a/src/fe_engine/integrator_gauss_inline_impl.hh
+++ b/src/fe_engine/integrator_gauss_inline_impl.hh
@@ -1,764 +1,763 @@
/**
* @file integrator_gauss_inline_impl.hh
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Tue Feb 15 2011
* @date last modification: Tue Feb 20 2018
*
* @brief inline function of gauss integrator
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "fe_engine.hh"
#include "mesh_iterators.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
namespace debug {
struct IntegratorGaussException : public Exception {};
} // namespace debug
/* -------------------------------------------------------------------------- */
template <ElementKind kind, class IntegrationOrderFunctor>
template <ElementType type>
inline void IntegratorGauss<kind, IntegrationOrderFunctor>::integrateOnElement(
const Array<Real> & f, Real * intf, UInt nb_degree_of_freedom,
const UInt elem, GhostType ghost_type) const {
Array<Real> & jac_loc = jacobians(type, ghost_type);
UInt nb_quadrature_points = ElementClass<type>::getNbQuadraturePoints();
AKANTU_DEBUG_ASSERT(f.getNbComponent() == nb_degree_of_freedom,
"The vector f do not have the good number of component.");
Real * f_val = f.storage() + elem * f.getNbComponent();
Real * jac_val = jac_loc.storage() + elem * nb_quadrature_points;
integrate(f_val, jac_val, intf, nb_degree_of_freedom, nb_quadrature_points);
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind, class IntegrationOrderFunctor>
template <ElementType type>
inline Real IntegratorGauss<kind, IntegrationOrderFunctor>::integrate(
const Vector<Real> & in_f, UInt index, GhostType ghost_type) const {
const Array<Real> & jac_loc = jacobians(type, ghost_type);
UInt nb_quadrature_points =
GaussIntegrationElement<type>::getNbQuadraturePoints();
AKANTU_DEBUG_ASSERT(in_f.size() == nb_quadrature_points,
"The vector f do not have nb_quadrature_points entries.");
Real * jac_val = jac_loc.storage() + index * nb_quadrature_points;
Real intf;
integrate(in_f.storage(), jac_val, &intf, 1, nb_quadrature_points);
return intf;
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind, class IntegrationOrderFunctor>
inline void IntegratorGauss<kind, IntegrationOrderFunctor>::integrate(
Real * f, Real * jac, Real * inte, UInt nb_degree_of_freedom,
UInt nb_quadrature_points) const {
std::fill_n(inte, nb_degree_of_freedom, 0.);
Real * cjac = jac;
for (UInt q = 0; q < nb_quadrature_points; ++q) {
for (UInt dof = 0; dof < nb_degree_of_freedom; ++dof) {
inte[dof] += *f * *cjac;
++f;
}
++cjac;
}
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind, class IntegrationOrderFunctor>
template <ElementType type>
inline const Matrix<Real> &
IntegratorGauss<kind, IntegrationOrderFunctor>::getIntegrationPoints(
GhostType ghost_type) const {
AKANTU_DEBUG_ASSERT(
quadrature_points.exists(type, ghost_type),
"Quadrature points for type "
<< quadrature_points.printType(type, ghost_type)
<< " have not been initialized."
<< " Did you use 'computeQuadraturePoints' function ?");
return quadrature_points(type, ghost_type);
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind, class IntegrationOrderFunctor>
template <ElementType type>
inline UInt
IntegratorGauss<kind, IntegrationOrderFunctor>::getNbIntegrationPoints(
GhostType ghost_type) const {
AKANTU_DEBUG_ASSERT(
quadrature_points.exists(type, ghost_type),
"Quadrature points for type "
<< quadrature_points.printType(type, ghost_type)
<< " have not been initialized."
<< " Did you use 'computeQuadraturePoints' function ?");
return quadrature_points(type, ghost_type).cols();
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind, class IntegrationOrderFunctor>
template <ElementType type, UInt polynomial_degree>
inline Matrix<Real>
IntegratorGauss<kind, IntegrationOrderFunctor>::getIntegrationPoints() const {
return GaussIntegrationElement<type,
polynomial_degree>::getQuadraturePoints();
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind, class IntegrationOrderFunctor>
template <ElementType type, UInt polynomial_degree>
inline Vector<Real>
IntegratorGauss<kind, IntegrationOrderFunctor>::getIntegrationWeights() const {
return GaussIntegrationElement<type, polynomial_degree>::getWeights();
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind, class IntegrationOrderFunctor>
template <ElementType type>
inline void
IntegratorGauss<kind, IntegrationOrderFunctor>::computeQuadraturePoints(
GhostType ghost_type) {
Matrix<Real> & quads = quadrature_points(type, ghost_type);
const UInt polynomial_degree =
IntegrationOrderFunctor::template getOrder<type>();
quads =
GaussIntegrationElement<type, polynomial_degree>::getQuadraturePoints();
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind, class IntegrationOrderFunctor>
template <ElementType type>
inline void IntegratorGauss<kind, IntegrationOrderFunctor>::
computeJacobianOnQuadPointsByElement(const Matrix<Real> & node_coords,
const Matrix<Real> & quad,
Vector<Real> & jacobians) const {
// jacobian
ElementClass<type>::computeJacobian(quad, node_coords, jacobians);
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind, class IntegrationOrderFunctor>
IntegratorGauss<kind, IntegrationOrderFunctor>::IntegratorGauss(
- const Mesh & mesh, UInt spatial_dimension, const ID & id,
- const MemoryID & memory_id)
- : Integrator(mesh, spatial_dimension, id, memory_id) {
+ const Mesh & mesh, UInt spatial_dimension, const ID & id)
+ : Integrator(mesh, spatial_dimension, id) {
AKANTU_DEBUG_IN();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind, class IntegrationOrderFunctor>
template <ElementType type>
void IntegratorGauss<kind, IntegrationOrderFunctor>::checkJacobians(
GhostType ghost_type) const {
AKANTU_DEBUG_IN();
UInt nb_quadrature_points = this->quadrature_points(type, ghost_type).cols();
UInt nb_element = mesh.getConnectivity(type, ghost_type).size();
Real * jacobians_val = jacobians(type, ghost_type).storage();
for (UInt i = 0; i < nb_element * nb_quadrature_points;
++i, ++jacobians_val) {
if (*jacobians_val < 0) {
AKANTU_CUSTOM_EXCEPTION_INFO(debug::IntegratorGaussException{},
"Negative jacobian computed,"
<< " possible problem in the element "
"node ordering (Quadrature Point "
<< i % nb_quadrature_points << ":"
<< i / nb_quadrature_points << ":"
<< type << ":" << ghost_type << ")");
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind, class IntegrationOrderFunctor>
template <ElementType type>
void IntegratorGauss<kind, IntegrationOrderFunctor>::
computeJacobiansOnIntegrationPoints(
const Array<Real> & nodes, const Matrix<Real> & quad_points,
Array<Real> & jacobians, GhostType ghost_type,
const Array<UInt> & filter_elements) const {
AKANTU_DEBUG_IN();
UInt spatial_dimension = mesh.getSpatialDimension();
UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
UInt nb_quadrature_points = quad_points.cols();
UInt nb_element = mesh.getNbElement(type, ghost_type);
jacobians.resize(nb_element * nb_quadrature_points);
auto jacobians_it =
jacobians.begin_reinterpret(nb_quadrature_points, nb_element);
auto jacobians_begin = jacobians_it;
Array<Real> x_el(0, spatial_dimension * nb_nodes_per_element);
FEEngine::extractNodalToElementField(mesh, nodes, x_el, type, ghost_type,
filter_elements);
auto x_it = x_el.begin(spatial_dimension, nb_nodes_per_element);
nb_element = x_el.size();
// Matrix<Real> local_coord(spatial_dimension, nb_nodes_per_element);
for (UInt elem = 0; elem < nb_element; ++elem, ++x_it) {
const Matrix<Real> & x = *x_it;
if (filter_elements != empty_filter) {
jacobians_it = jacobians_begin + filter_elements(elem);
}
Vector<Real> & J = *jacobians_it;
computeJacobianOnQuadPointsByElement<type>(x, quad_points, J);
if (filter_elements == empty_filter) {
++jacobians_it;
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
#if defined(AKANTU_STRUCTURAL_MECHANICS)
template <>
template <ElementType type>
void IntegratorGauss<_ek_structural, DefaultIntegrationOrderFunctor>::
computeJacobiansOnIntegrationPoints(
const Array<Real> & nodes, const Matrix<Real> & quad_points,
Array<Real> & jacobians, GhostType ghost_type,
const Array<UInt> & filter_elements) const {
AKANTU_DEBUG_IN();
const UInt spatial_dimension = mesh.getSpatialDimension();
const UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
const UInt nb_quadrature_points = quad_points.cols();
const UInt nb_dofs = ElementClass<type>::getNbDegreeOfFreedom();
UInt nb_element = mesh.getNbElement(type, ghost_type);
jacobians.resize(nb_element * nb_quadrature_points);
auto jacobians_it =
jacobians.begin_reinterpret(nb_quadrature_points, nb_element);
auto jacobians_begin = jacobians_it;
Array<Real> x_el(0, spatial_dimension * nb_nodes_per_element);
FEEngine::extractNodalToElementField(mesh, nodes, x_el, type, ghost_type,
filter_elements);
auto x_it = x_el.begin(spatial_dimension, nb_nodes_per_element);
nb_element = x_el.size();
const bool has_extra_normal =
mesh.hasData<Real>("extra_normal", type, ghost_type);
Array<Real>::const_vector_iterator extra_normal;
Array<Real>::const_vector_iterator extra_normal_begin;
if (has_extra_normal) {
extra_normal = mesh.getData<Real>("extra_normal", type, ghost_type)
.begin(spatial_dimension);
extra_normal_begin = extra_normal;
}
// Matrix<Real> local_coord(spatial_dimension, nb_nodes_per_element);
for (UInt elem = 0; elem < nb_element; ++elem, ++x_it) {
if (filter_elements != empty_filter) {
jacobians_it = jacobians_begin + filter_elements(elem);
extra_normal = extra_normal_begin + filter_elements(elem);
}
const Matrix<Real> & X = *x_it;
Vector<Real> & J = *jacobians_it;
Matrix<Real> R(nb_dofs, nb_dofs);
if (has_extra_normal) {
ElementClass<type>::computeRotationMatrix(R, X, *extra_normal);
} else {
ElementClass<type>::computeRotationMatrix(R, X, Vector<Real>(X.rows()));
}
// Extracting relevant lines
auto x = (R.block(0, 0, spatial_dimension, spatial_dimension) * X)
.block(0, 0, ElementClass<type>::getNaturalSpaceDimension(),
ElementClass<type>::getNbNodesPerElement());
computeJacobianOnQuadPointsByElement<type>(x, quad_points, J);
if (filter_elements == empty_filter) {
++jacobians_it;
++extra_normal;
}
}
AKANTU_DEBUG_OUT();
}
#endif
/* -------------------------------------------------------------------------- */
#if defined(AKANTU_COHESIVE_ELEMENT)
template <>
template <ElementType type>
void IntegratorGauss<_ek_cohesive, DefaultIntegrationOrderFunctor>::
computeJacobiansOnIntegrationPoints(
const Array<Real> & nodes, const Matrix<Real> & quad_points,
Array<Real> & jacobians, GhostType ghost_type,
const Array<UInt> & filter_elements) const {
AKANTU_DEBUG_IN();
UInt spatial_dimension = mesh.getSpatialDimension();
UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
UInt nb_quadrature_points = quad_points.cols();
UInt nb_element = mesh.getNbElement(type, ghost_type);
jacobians.resize(nb_element * nb_quadrature_points);
auto jacobians_begin =
jacobians.begin_reinterpret(nb_quadrature_points, nb_element);
Array<Real> x_el(0, spatial_dimension * nb_nodes_per_element);
FEEngine::extractNodalToElementField(mesh, nodes, x_el, type, ghost_type,
filter_elements);
auto x_it = x_el.begin(spatial_dimension, nb_nodes_per_element);
UInt nb_nodes_per_subelement = nb_nodes_per_element / 2;
Matrix<Real> x(spatial_dimension, nb_nodes_per_subelement);
nb_element = x_el.size();
UInt l_el = 0;
auto compute = [&](const auto & el) {
Vector<Real> J(jacobians_begin[el]);
Matrix<Real> X(x_it[l_el]);
++l_el;
for (UInt n = 0; n < nb_nodes_per_subelement; ++n) {
Vector<Real>(x(n)) =
(Vector<Real>(X(n)) + Vector<Real>(X(n + nb_nodes_per_subelement))) /
2.;
}
if (type == _cohesive_1d_2) {
J(0) = 1;
} else {
this->computeJacobianOnQuadPointsByElement<type>(x, quad_points, J);
}
};
for_each_element(nb_element, filter_elements, compute);
AKANTU_DEBUG_OUT();
}
#endif
/* -------------------------------------------------------------------------- */
template <ElementKind kind, class IntegrationOrderFunctor>
template <ElementType type>
void IntegratorGauss<kind, IntegrationOrderFunctor>::
precomputeJacobiansOnQuadraturePoints(const Array<Real> & nodes,
GhostType ghost_type) {
AKANTU_DEBUG_IN();
Array<Real> & jacobians_tmp = jacobians.alloc(0, 1, type, ghost_type);
this->computeJacobiansOnIntegrationPoints<type>(
nodes, quadrature_points(type, ghost_type), jacobians_tmp, ghost_type);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind, class IntegrationOrderFunctor>
template <ElementType type, UInt polynomial_degree>
void IntegratorGauss<kind, IntegrationOrderFunctor>::multiplyJacobiansByWeights(
Array<Real> & jacobians, const Array<UInt> & filter_elements) const {
AKANTU_DEBUG_IN();
UInt nb_quadrature_points =
GaussIntegrationElement<type, polynomial_degree>::getNbQuadraturePoints();
Vector<Real> weights =
GaussIntegrationElement<type, polynomial_degree>::getWeights();
auto && view = make_view(jacobians, nb_quadrature_points);
if (filter_elements != empty_filter) {
auto J_it = view.begin();
for (auto el : filter_elements) {
Vector<Real> J(J_it[el]);
J *= weights;
}
} else {
for (auto & J : make_view(jacobians, nb_quadrature_points)) {
J *= weights;
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind, class IntegrationOrderFunctor>
void IntegratorGauss<kind, IntegrationOrderFunctor>::integrate(
const Array<Real> & in_f, Array<Real> & intf, UInt nb_degree_of_freedom,
const Array<Real> & jacobians, UInt nb_element) const {
AKANTU_DEBUG_IN();
intf.resize(nb_element);
if (nb_element == 0) {
return;
}
UInt nb_points = jacobians.size() / nb_element;
Array<Real>::const_matrix_iterator J_it;
Array<Real>::matrix_iterator inte_it;
Array<Real>::const_matrix_iterator f_it;
f_it = in_f.begin_reinterpret(nb_degree_of_freedom, nb_points, nb_element);
inte_it = intf.begin_reinterpret(nb_degree_of_freedom, 1, nb_element);
J_it = jacobians.begin_reinterpret(nb_points, 1, nb_element);
for (UInt el = 0; el < nb_element; ++el, ++J_it, ++f_it, ++inte_it) {
const Matrix<Real> & f = *f_it;
const Matrix<Real> & J = *J_it;
Matrix<Real> & inte_f = *inte_it;
inte_f.mul<false, false>(f, J);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind, class IntegrationOrderFunctor>
template <ElementType type>
void IntegratorGauss<kind, IntegrationOrderFunctor>::integrate(
const Array<Real> & in_f, Array<Real> & intf, UInt nb_degree_of_freedom,
GhostType ghost_type, const Array<UInt> & filter_elements) const {
AKANTU_DEBUG_IN();
AKANTU_DEBUG_ASSERT(jacobians.exists(type, ghost_type),
"No jacobians for the type "
<< jacobians.printType(type, ghost_type));
const Array<Real> & jac_loc = jacobians(type, ghost_type);
if (filter_elements != empty_filter) {
UInt nb_element = filter_elements.size();
auto * filtered_J = new Array<Real>(0, jac_loc.getNbComponent());
FEEngine::filterElementalData(mesh, jac_loc, *filtered_J, type, ghost_type,
filter_elements);
this->integrate(in_f, intf, nb_degree_of_freedom, *filtered_J, nb_element);
delete filtered_J;
} else {
UInt nb_element = mesh.getNbElement(type, ghost_type);
this->integrate(in_f, intf, nb_degree_of_freedom, jac_loc, nb_element);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind, class IntegrationOrderFunctor>
template <ElementType type, UInt polynomial_degree>
void IntegratorGauss<kind, IntegrationOrderFunctor>::integrate(
const Array<Real> & in_f, Array<Real> & intf, UInt nb_degree_of_freedom,
GhostType ghost_type) const {
AKANTU_DEBUG_IN();
Matrix<Real> quads = this->getIntegrationPoints<type, polynomial_degree>();
Array<Real> jacobians;
this->computeJacobiansOnIntegrationPoints<type>(mesh.getNodes(), quads,
jacobians, ghost_type);
this->multiplyJacobiansByWeights<type, polynomial_degree>(jacobians);
this->integrate(in_f, intf, nb_degree_of_freedom, jacobians,
mesh.getNbElement(type, ghost_type));
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind, class IntegrationOrderFunctor>
template <ElementType type, UInt polynomial_degree>
Real IntegratorGauss<kind, IntegrationOrderFunctor>::integrate(
const Array<Real> & in_f, GhostType ghost_type) const {
AKANTU_DEBUG_IN();
Array<Real> intfv(0, 1);
integrate<type, polynomial_degree>(in_f, intfv, 1, ghost_type);
Real res = Math::reduce(intfv);
AKANTU_DEBUG_OUT();
return res;
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind, class IntegrationOrderFunctor>
template <ElementType type>
Real IntegratorGauss<kind, IntegrationOrderFunctor>::integrate(
const Array<Real> & in_f, GhostType ghost_type,
const Array<UInt> & filter_elements) const {
AKANTU_DEBUG_IN();
AKANTU_DEBUG_ASSERT(jacobians.exists(type, ghost_type),
"No jacobians for the type "
<< jacobians.printType(type, ghost_type));
Array<Real> intfv(0, 1);
integrate<type>(in_f, intfv, 1, ghost_type, filter_elements);
Real res = Math::reduce(intfv);
AKANTU_DEBUG_OUT();
return res;
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind, class IntegrationOrderFunctor>
void IntegratorGauss<kind, IntegrationOrderFunctor>::
integrateOnIntegrationPoints(const Array<Real> & in_f, Array<Real> & intf,
UInt nb_degree_of_freedom,
const Array<Real> & jacobians,
UInt nb_element) const {
AKANTU_DEBUG_IN();
UInt nb_points = jacobians.size() / nb_element;
intf.resize(nb_element * nb_points);
auto J_it = jacobians.begin();
auto f_it = in_f.begin(nb_degree_of_freedom);
auto inte_it = intf.begin(nb_degree_of_freedom);
for (UInt el = 0; el < nb_element; ++el, ++J_it, ++f_it, ++inte_it) {
const Real & J = *J_it;
const Vector<Real> & f = *f_it;
Vector<Real> & inte_f = *inte_it;
inte_f = f;
inte_f *= J;
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind, class IntegrationOrderFunctor>
template <ElementType type>
void IntegratorGauss<kind, IntegrationOrderFunctor>::
integrateOnIntegrationPoints(const Array<Real> & in_f, Array<Real> & intf,
UInt nb_degree_of_freedom,
GhostType ghost_type,
const Array<UInt> & filter_elements) const {
AKANTU_DEBUG_IN();
AKANTU_DEBUG_ASSERT(jacobians.exists(type, ghost_type),
"No jacobians for the type "
<< jacobians.printType(type, ghost_type));
const Array<Real> & jac_loc = this->jacobians(type, ghost_type);
if (filter_elements != empty_filter) {
UInt nb_element = filter_elements.size();
auto * filtered_J = new Array<Real>(0, jac_loc.getNbComponent());
FEEngine::filterElementalData(mesh, jac_loc, *filtered_J, type, ghost_type,
filter_elements);
this->integrateOnIntegrationPoints(in_f, intf, nb_degree_of_freedom,
*filtered_J, nb_element);
} else {
UInt nb_element = mesh.getNbElement(type, ghost_type);
this->integrateOnIntegrationPoints(in_f, intf, nb_degree_of_freedom,
jac_loc, nb_element);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind, class IntegrationOrderFunctor>
template <ElementType type>
inline void
IntegratorGauss<kind, IntegrationOrderFunctor>::onElementsAddedByType(
const Array<UInt> & elements, GhostType ghost_type) {
const auto & nodes = mesh.getNodes();
if (not quadrature_points.exists(type, ghost_type)) {
computeQuadraturePoints<type>(ghost_type);
}
if (not jacobians.exists(type, ghost_type)) {
jacobians.alloc(0, 1, type, ghost_type);
}
this->computeJacobiansOnIntegrationPoints(
nodes, quadrature_points(type, ghost_type), jacobians(type, ghost_type),
type, ghost_type, elements);
constexpr UInt polynomial_degree =
IntegrationOrderFunctor::template getOrder<type>();
multiplyJacobiansByWeights<type, polynomial_degree>(
this->jacobians(type, ghost_type), elements);
}
/* -------------------------------------------------------------------------- */
namespace integrator {
namespace details {
template <ElementKind kind> struct IntegratorOnElementsAddedHelper {};
#define ON_ELEMENT_ADDED(type) \
integrator.template onElementsAddedByType<type>(elements, ghost_type);
#define AKANTU_SPECIALIZE_ON_ELEMENT_ADDED_HELPER(kind) \
template <> struct IntegratorOnElementsAddedHelper<kind> { \
template <class I> \
static void call(I & integrator, const Array<UInt> & elements, \
ElementType type, GhostType ghost_type) { \
AKANTU_BOOST_KIND_ELEMENT_SWITCH(ON_ELEMENT_ADDED, kind); \
} \
};
AKANTU_BOOST_ALL_KIND(AKANTU_SPECIALIZE_ON_ELEMENT_ADDED_HELPER)
#undef AKANTU_SPECIALIZE_ON_ELEMENT_ADDED_HELPER
#undef ON_ELEMENT_ADDED
} // namespace details
} // namespace integrator
/* -------------------------------------------------------------------------- */
template <ElementKind kind, class IntegrationOrderFunctor>
void IntegratorGauss<kind, IntegrationOrderFunctor>::onElementsAdded(
const Array<Element> & new_elements) {
for (auto elements_range : MeshElementsByTypes(new_elements)) {
auto type = elements_range.getType();
auto ghost_type = elements_range.getGhostType();
if (mesh.getSpatialDimension(type) != _spatial_dimension) {
continue;
}
if (mesh.getKind(type) != kind) {
continue;
}
integrator::details::IntegratorOnElementsAddedHelper<kind>::call(
*this, elements_range.getElements(), type, ghost_type);
}
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind, class IntegrationOrderFunctor>
template <ElementType type>
inline void IntegratorGauss<kind, IntegrationOrderFunctor>::initIntegrator(
const Array<Real> & nodes, GhostType ghost_type) {
computeQuadraturePoints<type>(ghost_type);
precomputeJacobiansOnQuadraturePoints<type>(nodes, ghost_type);
checkJacobians<type>(ghost_type);
constexpr UInt polynomial_degree =
IntegrationOrderFunctor::template getOrder<type>();
multiplyJacobiansByWeights<type, polynomial_degree>(
this->jacobians(type, ghost_type));
}
namespace integrator {
namespace details {
template <ElementKind kind> struct GaussIntegratorInitHelper {};
#define INIT_INTEGRATOR(type) \
_int.template initIntegrator<type>(nodes, ghost_type)
#define AKANTU_GAUSS_INTERGRATOR_INIT_HELPER(kind) \
template <> struct GaussIntegratorInitHelper<kind> { \
template <ElementKind k, class IOF> \
static void call(IntegratorGauss<k, IOF> & _int, \
const Array<Real> & nodes, ElementType type, \
GhostType ghost_type) { \
AKANTU_BOOST_KIND_ELEMENT_SWITCH(INIT_INTEGRATOR, kind); \
} \
};
AKANTU_BOOST_ALL_KIND(AKANTU_GAUSS_INTERGRATOR_INIT_HELPER)
#undef AKANTU_GAUSS_INTERGRATOR_INIT_HELPER
#undef INIT_INTEGRATOR
} // namespace details
} // namespace integrator
template <ElementKind kind, class IntegrationOrderFunctor>
inline void IntegratorGauss<kind, IntegrationOrderFunctor>::initIntegrator(
const Array<Real> & nodes, ElementType type, GhostType ghost_type) {
integrator::details::GaussIntegratorInitHelper<kind>::call(*this, nodes, type,
ghost_type);
}
namespace integrator {
namespace details {
template <ElementKind kind> struct GaussIntegratorComputeJacobiansHelper {};
#define AKANTU_COMPUTE_JACOBIANS(type) \
_int.template computeJacobiansOnIntegrationPoints<type>( \
nodes, quad_points, jacobians, ghost_type, filter_elements);
#define AKANTU_GAUSS_INTERGRATOR_COMPUTE_JACOBIANS(kind) \
template <> struct GaussIntegratorComputeJacobiansHelper<kind> { \
template <ElementKind k, class IOF> \
static void \
call(const IntegratorGauss<k, IOF> & _int, const Array<Real> & nodes, \
const Matrix<Real> & quad_points, Array<Real> & jacobians, \
ElementType type, GhostType ghost_type, \
const Array<UInt> & filter_elements) { \
AKANTU_BOOST_KIND_ELEMENT_SWITCH(AKANTU_COMPUTE_JACOBIANS, kind); \
} \
};
AKANTU_BOOST_ALL_KIND(AKANTU_GAUSS_INTERGRATOR_COMPUTE_JACOBIANS)
#undef AKANTU_GAUSS_INTERGRATOR_COMPUTE_JACOBIANS
#undef AKANTU_COMPUTE_JACOBIANS
} // namespace details
} // namespace integrator
template <ElementKind kind, class IntegrationOrderFunctor>
void IntegratorGauss<kind, IntegrationOrderFunctor>::
computeJacobiansOnIntegrationPoints(
const Array<Real> & nodes, const Matrix<Real> & quad_points,
Array<Real> & jacobians, ElementType type, GhostType ghost_type,
const Array<UInt> & filter_elements) const {
integrator::details::GaussIntegratorComputeJacobiansHelper<kind>::call(
*this, nodes, quad_points, jacobians, type, ghost_type, filter_elements);
}
} // namespace akantu
diff --git a/src/fe_engine/shape_cohesive.hh b/src/fe_engine/shape_cohesive.hh
index 3b282c839..6f5a7bbd1 100644
--- a/src/fe_engine/shape_cohesive.hh
+++ b/src/fe_engine/shape_cohesive.hh
@@ -1,184 +1,183 @@
/**
* @file shape_cohesive.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
* @author Marco Vocialta <marco.vocialta@epfl.ch>
*
* @date creation: Tue Feb 15 2011
* @date last modification: Tue Feb 20 2018
*
* @brief shape functions for cohesive elements description
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_array.hh"
#include "shape_lagrange.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_SHAPE_COHESIVE_HH_
#define AKANTU_SHAPE_COHESIVE_HH_
namespace akantu {
struct CohesiveReduceFunctionMean {
inline Real operator()(Real u_plus, Real u_minus) {
return .5 * (u_plus + u_minus);
}
};
struct CohesiveReduceFunctionOpening {
inline Real operator()(Real u_plus, Real u_minus) {
return (u_plus - u_minus);
}
};
template <> class ShapeLagrange<_ek_cohesive> : public ShapeLagrangeBase {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
ShapeLagrange(const Mesh & mesh, UInt spatial_dimension,
- const ID & id = "shape_cohesive",
- const MemoryID & memory_id = 0);
+ const ID & id = "shape_cohesive");
~ShapeLagrange() override = default;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
inline void initShapeFunctions(const Array<Real> & nodes,
const Matrix<Real> & integration_points,
ElementType type,
GhostType ghost_type);
/// extract the nodal values and store them per element
template <ElementType type, class ReduceFunction>
void extractNodalToElementField(
const Array<Real> & nodal_f, Array<Real> & elemental_f,
GhostType ghost_type = _not_ghost,
const Array<UInt> & filter_elements = empty_filter) const;
/// computes the shape functions derivatives for given interpolation points
template <ElementType type>
void computeShapeDerivativesOnIntegrationPoints(
const Array<Real> & nodes, const Matrix<Real> & integration_points,
Array<Real> & shape_derivatives, GhostType ghost_type,
const Array<UInt> & filter_elements = empty_filter) const;
void computeShapeDerivativesOnIntegrationPoints(
const Array<Real> & nodes, const Matrix<Real> & integration_points,
Array<Real> & shape_derivatives, ElementType type,
GhostType ghost_type,
const Array<UInt> & filter_elements) const override;
/// pre compute all shapes on the element integration points from natural
/// coordinates
template <ElementType type>
void precomputeShapesOnIntegrationPoints(const Array<Real> & nodes,
GhostType ghost_type);
/// pre compute all shape derivatives on the element integration points from
/// natural coordinates
template <ElementType type>
void precomputeShapeDerivativesOnIntegrationPoints(const Array<Real> & nodes,
GhostType ghost_type);
/// interpolate nodal values on the integration points
template <ElementType type, class ReduceFunction>
void interpolateOnIntegrationPoints(
const Array<Real> & u, Array<Real> & uq, UInt nb_degree_of_freedom,
GhostType ghost_type = _not_ghost,
const Array<UInt> & filter_elements = empty_filter) const;
template <ElementType type>
void interpolateOnIntegrationPoints(
const Array<Real> & u, Array<Real> & uq, UInt nb_degree_of_freedom,
GhostType ghost_type = _not_ghost,
const Array<UInt> & filter_elements = empty_filter) const {
interpolateOnIntegrationPoints<type, CohesiveReduceFunctionMean>(
u, uq, nb_degree_of_freedom, ghost_type, filter_elements);
}
/// compute the gradient of u on the integration points in the natural
/// coordinates
template <ElementType type>
void gradientOnIntegrationPoints(
const Array<Real> & u, Array<Real> & nablauq, UInt nb_degree_of_freedom,
GhostType ghost_type = _not_ghost,
const Array<UInt> & filter_elements = empty_filter) const {
variationOnIntegrationPoints<type, CohesiveReduceFunctionMean>(
u, nablauq, nb_degree_of_freedom, ghost_type, filter_elements);
}
/* ------------------------------------------------------------------------ */
template <ElementType type>
void computeBtD(const Array<Real> & /*Ds*/, Array<Real> & /*BtDs*/,
GhostType /*ghost_type*/,
const Array<UInt> & /*filter_elements*/) const {
AKANTU_TO_IMPLEMENT();
}
template <ElementType type>
void computeBtDB(const Array<Real> & /*Ds*/, Array<Real> & /*BtDBs*/,
UInt /*order_d*/, GhostType/*ghost_type*/,
const Array<UInt> & /*filter_elements*/) const {
AKANTU_TO_IMPLEMENT();
}
/// multiply a field by shape functions
template <ElementType type>
void
computeNtb(const Array<Real> & /*bs*/, Array<Real> & /*Ntbs*/,
GhostType /*ghost_type*/,
const Array<UInt> & /*filter_elements*/ = empty_filter) const {
AKANTU_TO_IMPLEMENT();
}
/* ------------------------------------------------------------------------ */
/// compute the gradient of u on the integration points
template <ElementType type, class ReduceFunction>
void variationOnIntegrationPoints(
const Array<Real> & u, Array<Real> & nablauq, UInt nb_degree_of_freedom,
GhostType ghost_type = _not_ghost,
const Array<UInt> & filter_elements = empty_filter) const;
/// compute the normals to the field u on integration points
template <ElementType type, class ReduceFunction>
void computeNormalsOnIntegrationPoints(
const Array<Real> & u, Array<Real> & normals_u,
GhostType ghost_type = _not_ghost,
const Array<UInt> & filter_elements = empty_filter) const;
};
/// standard output stream operator
template <class ShapeFunction>
inline std::ostream & operator<<(std::ostream & stream,
const ShapeCohesive<ShapeFunction> & _this) {
_this.printself(stream);
return stream;
}
} // namespace akantu
#include "shape_cohesive_inline_impl.hh"
#endif /* AKANTU_SHAPE_COHESIVE_HH_ */
diff --git a/src/fe_engine/shape_cohesive_inline_impl.hh b/src/fe_engine/shape_cohesive_inline_impl.hh
index fc7134bac..ce49829b1 100644
--- a/src/fe_engine/shape_cohesive_inline_impl.hh
+++ b/src/fe_engine/shape_cohesive_inline_impl.hh
@@ -1,332 +1,331 @@
/**
* @file shape_cohesive_inline_impl.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
* @author Marco Vocialta <marco.vocialta@epfl.ch>
*
* @date creation: Fri Feb 03 2012
* @date last modification: Mon Feb 19 2018
*
* @brief ShapeCohesive inline implementation
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "mesh_iterators.hh"
#include "shape_cohesive.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_SHAPE_COHESIVE_INLINE_IMPL_HH_
#define AKANTU_SHAPE_COHESIVE_INLINE_IMPL_HH_
namespace akantu {
/* -------------------------------------------------------------------------- */
inline ShapeLagrange<_ek_cohesive>::ShapeLagrange(const Mesh & mesh,
UInt spatial_dimension,
- const ID & id,
- const MemoryID & memory_id)
- : ShapeLagrangeBase(mesh, spatial_dimension, _ek_cohesive, id, memory_id) {}
+ const ID & id)
+ : ShapeLagrangeBase(mesh, spatial_dimension, _ek_cohesive, id) {}
#define INIT_SHAPE_FUNCTIONS(type) \
setIntegrationPointsByType<type>(integration_points, ghost_type); \
precomputeShapesOnIntegrationPoints<type>(nodes, ghost_type); \
precomputeShapeDerivativesOnIntegrationPoints<type>(nodes, ghost_type);
/* -------------------------------------------------------------------------- */
inline void ShapeLagrange<_ek_cohesive>::initShapeFunctions(
const Array<Real> & nodes, const Matrix<Real> & integration_points,
ElementType type, GhostType ghost_type) {
AKANTU_BOOST_COHESIVE_ELEMENT_SWITCH(INIT_SHAPE_FUNCTIONS);
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
template <ElementType type>
void ShapeLagrange<_ek_cohesive>::computeShapeDerivativesOnIntegrationPoints(
const Array<Real> & /*unused*/, const Matrix<Real> & integration_points,
Array<Real> & shape_derivatives, GhostType ghost_type,
const Array<UInt> & filter_elements) const {
AKANTU_DEBUG_IN();
UInt size_of_shapesd = ElementClass<type>::getShapeDerivativesSize();
UInt spatial_dimension = ElementClass<type>::getNaturalSpaceDimension();
UInt nb_nodes_per_element =
ElementClass<type>::getNbNodesPerInterpolationElement();
UInt nb_points = integration_points.cols();
UInt nb_element = mesh.getConnectivity(type, ghost_type).size();
AKANTU_DEBUG_ASSERT(shape_derivatives.getNbComponent() == size_of_shapesd,
"The shapes_derivatives array does not have the correct "
<< "number of component");
shape_derivatives.resize(nb_element * nb_points);
Real * shapesd_val = shape_derivatives.storage();
auto compute = [&](const auto & el) {
auto ptr = shapesd_val + el * nb_points * size_of_shapesd;
Tensor3<Real> B(ptr, spatial_dimension, nb_nodes_per_element, nb_points);
ElementClass<type>::computeDNDS(integration_points, B);
};
for_each_element(nb_element, filter_elements, compute);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
inline void
ShapeLagrange<_ek_cohesive>::computeShapeDerivativesOnIntegrationPoints(
const Array<Real> & nodes, const Matrix<Real> & integration_points,
Array<Real> & shape_derivatives, ElementType type,
GhostType ghost_type, const Array<UInt> & filter_elements) const {
#define AKANTU_COMPUTE_SHAPES(type) \
computeShapeDerivativesOnIntegrationPoints<type>( \
nodes, integration_points, shape_derivatives, ghost_type, \
filter_elements);
AKANTU_BOOST_COHESIVE_ELEMENT_SWITCH(AKANTU_COMPUTE_SHAPES);
#undef AKANTU_COMPUTE_SHAPES
}
/* -------------------------------------------------------------------------- */
template <ElementType type>
void ShapeLagrange<_ek_cohesive>::precomputeShapesOnIntegrationPoints(
const Array<Real> & nodes, GhostType ghost_type) {
AKANTU_DEBUG_IN();
InterpolationType itp_type = ElementClassProperty<type>::interpolation_type;
Matrix<Real> & natural_coords = integration_points(type, ghost_type);
UInt size_of_shapes = ElementClass<type>::getShapeSize();
Array<Real> & shapes_tmp =
shapes.alloc(0, size_of_shapes, itp_type, ghost_type);
this->computeShapesOnIntegrationPoints<type>(nodes, natural_coords,
shapes_tmp, ghost_type);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <ElementType type>
void ShapeLagrange<_ek_cohesive>::precomputeShapeDerivativesOnIntegrationPoints(
const Array<Real> & nodes, GhostType ghost_type) {
AKANTU_DEBUG_IN();
InterpolationType itp_type = ElementClassProperty<type>::interpolation_type;
Matrix<Real> & natural_coords = integration_points(type, ghost_type);
UInt size_of_shapesd = ElementClass<type>::getShapeDerivativesSize();
Array<Real> & shapes_derivatives_tmp =
shapes_derivatives.alloc(0, size_of_shapesd, itp_type, ghost_type);
this->computeShapeDerivativesOnIntegrationPoints<type>(
nodes, natural_coords, shapes_derivatives_tmp, ghost_type);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <ElementType type, class ReduceFunction>
void ShapeLagrange<_ek_cohesive>::extractNodalToElementField(
const Array<Real> & nodal_f, Array<Real> & elemental_f,
GhostType ghost_type, const Array<UInt> & filter_elements) const {
AKANTU_DEBUG_IN();
UInt nb_nodes_per_itp_element =
ElementClass<type>::getNbNodesPerInterpolationElement();
UInt nb_degree_of_freedom = nodal_f.getNbComponent();
UInt nb_element = this->mesh.getNbElement(type, ghost_type);
const auto & conn_array = this->mesh.getConnectivity(type, ghost_type);
auto conn = conn_array.begin(conn_array.getNbComponent() / 2, 2);
if (filter_elements != empty_filter) {
nb_element = filter_elements.size();
}
elemental_f.resize(nb_element);
Array<Real>::matrix_iterator u_it =
elemental_f.begin(nb_degree_of_freedom, nb_nodes_per_itp_element);
ReduceFunction reduce_function;
auto compute = [&](const auto & el) {
Matrix<Real> & u = *u_it;
Matrix<UInt> el_conn(conn[el]);
// compute the average/difference of the nodal field loaded from cohesive
// element
for (UInt n = 0; n < el_conn.rows(); ++n) {
UInt node_plus = el_conn(n, 0);
UInt node_minus = el_conn(n, 1);
for (UInt d = 0; d < nb_degree_of_freedom; ++d) {
Real u_plus = nodal_f(node_plus, d);
Real u_minus = nodal_f(node_minus, d);
u(d, n) = reduce_function(u_plus, u_minus);
}
}
++u_it;
};
for_each_element(nb_element, filter_elements, compute);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <ElementType type, class ReduceFunction>
void ShapeLagrange<_ek_cohesive>::interpolateOnIntegrationPoints(
const Array<Real> & in_u, Array<Real> & out_uq, UInt nb_degree_of_freedom,
GhostType ghost_type, const Array<UInt> & filter_elements) const {
AKANTU_DEBUG_IN();
InterpolationType itp_type = ElementClassProperty<type>::interpolation_type;
AKANTU_DEBUG_ASSERT(this->shapes.exists(itp_type, ghost_type),
"No shapes for the type "
<< this->shapes.printType(itp_type, ghost_type));
UInt nb_nodes_per_element =
ElementClass<type>::getNbNodesPerInterpolationElement();
Array<Real> u_el(0, nb_degree_of_freedom * nb_nodes_per_element);
this->extractNodalToElementField<type, ReduceFunction>(in_u, u_el, ghost_type,
filter_elements);
this->template interpolateElementalFieldOnIntegrationPoints<type>(
u_el, out_uq, ghost_type, shapes(itp_type, ghost_type), filter_elements);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <ElementType type, class ReduceFunction>
void ShapeLagrange<_ek_cohesive>::variationOnIntegrationPoints(
const Array<Real> & in_u, Array<Real> & nablauq, UInt nb_degree_of_freedom,
GhostType ghost_type, const Array<UInt> & filter_elements) const {
AKANTU_DEBUG_IN();
InterpolationType itp_type = ElementClassProperty<type>::interpolation_type;
AKANTU_DEBUG_ASSERT(
this->shapes_derivatives.exists(itp_type, ghost_type),
"No shapes for the type "
<< this->shapes_derivatives.printType(itp_type, ghost_type));
UInt nb_nodes_per_element =
ElementClass<type>::getNbNodesPerInterpolationElement();
Array<Real> u_el(0, nb_degree_of_freedom * nb_nodes_per_element);
this->extractNodalToElementField<type, ReduceFunction>(in_u, u_el, ghost_type,
filter_elements);
this->template gradientElementalFieldOnIntegrationPoints<type>(
u_el, nablauq, ghost_type, shapes_derivatives(itp_type, ghost_type),
filter_elements);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <ElementType type, class ReduceFunction>
void ShapeLagrange<_ek_cohesive>::computeNormalsOnIntegrationPoints(
const Array<Real> & u, Array<Real> & normals_u,
GhostType ghost_type, const Array<UInt> & filter_elements) const {
AKANTU_DEBUG_IN();
UInt nb_element = this->mesh.getNbElement(type, ghost_type);
UInt nb_points = this->integration_points(type, ghost_type).cols();
UInt spatial_dimension = this->mesh.getSpatialDimension();
if (filter_elements != empty_filter) {
nb_element = filter_elements.size();
}
normals_u.resize(nb_points * nb_element);
Array<Real> tangents_u(0, (spatial_dimension * (spatial_dimension - 1)));
if (spatial_dimension > 1) {
tangents_u.resize(nb_element * nb_points);
this->template variationOnIntegrationPoints<type, ReduceFunction>(
u, tangents_u, spatial_dimension, ghost_type, filter_elements);
}
Real * tangent = tangents_u.storage();
if (spatial_dimension == 3) {
for (auto & normal : make_view(normals_u, spatial_dimension)) {
Math::vectorProduct3(tangent, tangent + spatial_dimension,
normal.storage());
normal /= normal.norm();
tangent += spatial_dimension * 2;
}
} else if (spatial_dimension == 2) {
for (auto & normal : make_view(normals_u, spatial_dimension)) {
Vector<Real> a1(tangent, spatial_dimension);
normal(0) = -a1(1);
normal(1) = a1(0);
normal.normalize();
tangent += spatial_dimension;
}
} else if (spatial_dimension == 1) {
const auto facet_type = Mesh::getFacetType(type);
const auto & mesh_facets = mesh.getMeshFacets();
const auto & facets = mesh_facets.getSubelementToElement(type, ghost_type);
const auto & segments =
mesh_facets.getElementToSubelement(facet_type, ghost_type);
Real values[2];
for (auto el : arange(nb_element)) {
if (filter_elements != empty_filter) {
el = filter_elements(el);
}
for (UInt p = 0; p < 2; ++p) {
Element facet = facets(el, p);
Element segment = segments(facet.element)[0];
Vector<Real> barycenter(values + p, 1);
mesh.getBarycenter(segment, barycenter);
}
Real difference = values[0] - values[1];
AKANTU_DEBUG_ASSERT(difference != 0.,
"Error in normal computation for cohesive elements");
normals_u(el) = difference / std::abs(difference);
}
}
AKANTU_DEBUG_OUT();
}
} // namespace akantu
#endif /* AKANTU_SHAPE_COHESIVE_INLINE_IMPL_HH_ */
diff --git a/src/fe_engine/shape_functions.cc b/src/fe_engine/shape_functions.cc
index a35173c9a..80f0d33f2 100644
--- a/src/fe_engine/shape_functions.cc
+++ b/src/fe_engine/shape_functions.cc
@@ -1,241 +1,241 @@
/**
* @file shape_functions.cc
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Wed Aug 09 2017
* @date last modification: Wed Oct 11 2017
*
* @brief implementation of th shape functions interface
*
*
* Copyright (©) 2016-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "shape_functions.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
ShapeFunctions::ShapeFunctions(const Mesh & mesh, UInt spatial_dimension,
- const ID & id, const MemoryID & memory_id)
- : Memory(id, memory_id), shapes("shapes_generic", id, memory_id),
- shapes_derivatives("shapes_derivatives_generic", id, memory_id),
+ const ID & id)
+ : shapes("shapes_generic", id),
+ shapes_derivatives("shapes_derivatives_generic", id),
mesh(mesh), _spatial_dimension(spatial_dimension) {}
/* -------------------------------------------------------------------------- */
template <ElementType type>
inline void
ShapeFunctions::initElementalFieldInterpolationFromIntegrationPoints(
const Array<Real> & interpolation_points_coordinates,
ElementTypeMapArray<Real> & interpolation_points_coordinates_matrices,
ElementTypeMapArray<Real> & quad_points_coordinates_inv_matrices,
const Array<Real> & quadrature_points_coordinates,
GhostType ghost_type, const Array<UInt> & element_filter) const {
AKANTU_DEBUG_IN();
UInt spatial_dimension = this->mesh.getSpatialDimension();
UInt nb_element = this->mesh.getNbElement(type, ghost_type);
UInt nb_element_filter;
if (element_filter == empty_filter) {
nb_element_filter = nb_element;
} else {
nb_element_filter = element_filter.size();
}
- UInt nb_quad_per_element =
+ auto nb_quad_per_element =
GaussIntegrationElement<type>::getNbQuadraturePoints();
- UInt nb_interpolation_points_per_elem =
+ auto nb_interpolation_points_per_elem =
interpolation_points_coordinates.size() / nb_element;
AKANTU_DEBUG_ASSERT(interpolation_points_coordinates.size() % nb_element == 0,
"Number of interpolation points should be a multiple of "
"total number of elements");
- if (!quad_points_coordinates_inv_matrices.exists(type, ghost_type)) {
+ if (not quad_points_coordinates_inv_matrices.exists(type, ghost_type)) {
quad_points_coordinates_inv_matrices.alloc(
nb_element_filter, nb_quad_per_element * nb_quad_per_element, type,
ghost_type);
} else {
quad_points_coordinates_inv_matrices(type, ghost_type)
.resize(nb_element_filter);
}
if (!interpolation_points_coordinates_matrices.exists(type, ghost_type)) {
interpolation_points_coordinates_matrices.alloc(
nb_element_filter,
nb_interpolation_points_per_elem * nb_quad_per_element, type,
ghost_type);
} else {
interpolation_points_coordinates_matrices(type, ghost_type)
.resize(nb_element_filter);
}
Array<Real> & quad_inv_mat =
quad_points_coordinates_inv_matrices(type, ghost_type);
Array<Real> & interp_points_mat =
interpolation_points_coordinates_matrices(type, ghost_type);
Matrix<Real> quad_coord_matrix(nb_quad_per_element, nb_quad_per_element);
Array<Real>::const_matrix_iterator quad_coords_it =
quadrature_points_coordinates.begin_reinterpret(
spatial_dimension, nb_quad_per_element, nb_element_filter);
Array<Real>::const_matrix_iterator points_coords_begin =
interpolation_points_coordinates.begin_reinterpret(
spatial_dimension, nb_interpolation_points_per_elem, nb_element);
Array<Real>::matrix_iterator inv_quad_coord_it =
quad_inv_mat.begin(nb_quad_per_element, nb_quad_per_element);
Array<Real>::matrix_iterator int_points_mat_it = interp_points_mat.begin(
nb_interpolation_points_per_elem, nb_quad_per_element);
/// loop over the elements of the current material and element type
for (UInt el = 0; el < nb_element_filter;
++el, ++inv_quad_coord_it, ++int_points_mat_it, ++quad_coords_it) {
/// matrix containing the quadrature points coordinates
const Matrix<Real> & quad_coords = *quad_coords_it;
/// matrix to store the matrix inversion result
Matrix<Real> & inv_quad_coord_matrix = *inv_quad_coord_it;
/// insert the quad coordinates in a matrix compatible with the
/// interpolation
buildElementalFieldInterpolationMatrix<type>(quad_coords,
quad_coord_matrix);
/// invert the interpolation matrix
inv_quad_coord_matrix.inverse(quad_coord_matrix);
/// matrix containing the interpolation points coordinates
const Matrix<Real> & points_coords =
points_coords_begin[element_filter(el)];
/// matrix to store the interpolation points coordinates
/// compatible with these functions
Matrix<Real> & inv_points_coord_matrix = *int_points_mat_it;
/// insert the quad coordinates in a matrix compatible with the
/// interpolation
buildElementalFieldInterpolationMatrix<type>(points_coords,
inv_points_coord_matrix);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void ShapeFunctions::initElementalFieldInterpolationFromIntegrationPoints(
const ElementTypeMapArray<Real> & interpolation_points_coordinates,
ElementTypeMapArray<Real> & interpolation_points_coordinates_matrices,
ElementTypeMapArray<Real> & quad_points_coordinates_inv_matrices,
const ElementTypeMapArray<Real> & quadrature_points_coordinates,
const ElementTypeMapArray<UInt> * element_filter) const {
AKANTU_DEBUG_IN();
UInt spatial_dimension = this->mesh.getSpatialDimension();
for (auto ghost_type : ghost_types) {
auto types_iterable = mesh.elementTypes(spatial_dimension, ghost_type);
if (element_filter != nullptr) {
types_iterable =
element_filter->elementTypes(spatial_dimension, ghost_type);
}
for (auto type : types_iterable) {
UInt nb_element = mesh.getNbElement(type, ghost_type);
if (nb_element == 0) {
continue;
}
const Array<UInt> * elem_filter;
if (element_filter != nullptr) {
elem_filter = &((*element_filter)(type, ghost_type));
} else {
elem_filter = &(empty_filter);
}
#define AKANTU_INIT_ELEMENTAL_FIELD_INTERPOLATION_FROM_C_POINTS(type) \
this->initElementalFieldInterpolationFromIntegrationPoints<type>( \
interpolation_points_coordinates(type, ghost_type), \
interpolation_points_coordinates_matrices, \
quad_points_coordinates_inv_matrices, \
quadrature_points_coordinates(type, ghost_type), ghost_type, \
*elem_filter)
AKANTU_BOOST_REGULAR_ELEMENT_SWITCH(
AKANTU_INIT_ELEMENTAL_FIELD_INTERPOLATION_FROM_C_POINTS);
#undef AKANTU_INIT_ELEMENTAL_FIELD_INTERPOLATION_FROM_C_POINTS
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void ShapeFunctions::interpolateElementalFieldFromIntegrationPoints(
const ElementTypeMapArray<Real> & field,
const ElementTypeMapArray<Real> & interpolation_points_coordinates_matrices,
const ElementTypeMapArray<Real> & quad_points_coordinates_inv_matrices,
ElementTypeMapArray<Real> & result, GhostType ghost_type,
const ElementTypeMapArray<UInt> * element_filter) const {
AKANTU_DEBUG_IN();
UInt spatial_dimension = this->mesh.getSpatialDimension();
auto types_iterable = mesh.elementTypes(spatial_dimension, ghost_type);
if (element_filter != nullptr) {
types_iterable =
element_filter->elementTypes(spatial_dimension, ghost_type);
}
for (auto type : types_iterable) {
UInt nb_element = mesh.getNbElement(type, ghost_type);
if (nb_element == 0) {
continue;
}
const Array<UInt> * elem_filter;
if (element_filter != nullptr) {
elem_filter = &((*element_filter)(type, ghost_type));
} else {
elem_filter = &(empty_filter);
}
#define AKANTU_INTERPOLATE_ELEMENTAL_FIELD_FROM_C_POINTS(type) \
interpolateElementalFieldFromIntegrationPoints<type>( \
field(type, ghost_type), \
interpolation_points_coordinates_matrices(type, ghost_type), \
quad_points_coordinates_inv_matrices(type, ghost_type), result, \
ghost_type, *elem_filter)
AKANTU_BOOST_REGULAR_ELEMENT_SWITCH(
AKANTU_INTERPOLATE_ELEMENTAL_FIELD_FROM_C_POINTS);
#undef AKANTU_INTERPOLATE_ELEMENTAL_FIELD_FROM_C_POINTS
}
AKANTU_DEBUG_OUT();
}
} // namespace akantu
diff --git a/src/fe_engine/shape_functions.hh b/src/fe_engine/shape_functions.hh
index 2c17873ce..96b5f4d07 100644
--- a/src/fe_engine/shape_functions.hh
+++ b/src/fe_engine/shape_functions.hh
@@ -1,215 +1,214 @@
/**
* @file shape_functions.hh
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Tue Feb 20 2018
*
* @brief shape function class
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
-#include "aka_memory.hh"
#include "mesh.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_SHAPE_FUNCTIONS_HH_
#define AKANTU_SHAPE_FUNCTIONS_HH_
namespace akantu {
/* -------------------------------------------------------------------------- */
-class ShapeFunctions : protected Memory {
+class ShapeFunctions {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
ShapeFunctions(const Mesh & mesh, UInt spatial_dimension,
- const ID & id = "shape", const MemoryID & memory_id = 0);
- ~ShapeFunctions() override = default;
+ const ID & id = "shape");
+ virtual ~ShapeFunctions() = default;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// function to print the contain of the class
virtual void printself(std::ostream & stream, int indent = 0) const {
std::string space;
for (Int i = 0; i < indent; i++, space += AKANTU_INDENT) {
;
}
stream << space << "Shapes [" << std::endl;
integration_points.printself(stream, indent + 1);
// shapes.printself(stream, indent + 1);
// shapes_derivatives.printself(stream, indent + 1);
stream << space << "]" << std::endl;
}
/// set the integration points for a given element
template <ElementType type>
void setIntegrationPointsByType(const Matrix<Real> & integration_points,
GhostType ghost_type);
/// Build pre-computed matrices for interpolation of field form integration
/// points at other given positions (interpolation_points)
void initElementalFieldInterpolationFromIntegrationPoints(
const ElementTypeMapArray<Real> & interpolation_points_coordinates,
ElementTypeMapArray<Real> & interpolation_points_coordinates_matrices,
ElementTypeMapArray<Real> & quad_points_coordinates_inv_matrices,
const ElementTypeMapArray<Real> & quadrature_points_coordinates,
const ElementTypeMapArray<UInt> * element_filter) const;
/// Interpolate field at given position from given values of this field at
/// integration points (field)
/// using matrices precomputed with
/// initElementalFieldInterplationFromIntegrationPoints
void interpolateElementalFieldFromIntegrationPoints(
const ElementTypeMapArray<Real> & field,
const ElementTypeMapArray<Real> &
interpolation_points_coordinates_matrices,
const ElementTypeMapArray<Real> & quad_points_coordinates_inv_matrices,
ElementTypeMapArray<Real> & result, GhostType ghost_type,
const ElementTypeMapArray<UInt> * element_filter) const;
protected:
/// interpolate nodal values stored by element on the integration points
template <ElementType type>
void interpolateElementalFieldOnIntegrationPoints(
const Array<Real> & u_el, Array<Real> & uq, GhostType ghost_type,
const Array<Real> & shapes,
const Array<UInt> & filter_elements = empty_filter) const;
/// gradient of nodal values stored by element on the control points
template <ElementType type>
void gradientElementalFieldOnIntegrationPoints(
const Array<Real> & u_el, Array<Real> & out_nablauq,
GhostType ghost_type, const Array<Real> & shapes_derivatives,
const Array<UInt> & filter_elements) const;
protected:
/// By element versions of non-templated eponym methods
template <ElementType type>
inline void interpolateElementalFieldFromIntegrationPoints(
const Array<Real> & field,
const Array<Real> & interpolation_points_coordinates_matrices,
const Array<Real> & quad_points_coordinates_inv_matrices,
ElementTypeMapArray<Real> & result, GhostType ghost_type,
const Array<UInt> & element_filter) const;
/// Interpolate field at given position from given values of this field at
/// integration points (field)
/// using matrices precomputed with
/// initElementalFieldInterplationFromIntegrationPoints
template <ElementType type>
inline void initElementalFieldInterpolationFromIntegrationPoints(
const Array<Real> & interpolation_points_coordinates,
ElementTypeMapArray<Real> & interpolation_points_coordinates_matrices,
ElementTypeMapArray<Real> & quad_points_coordinates_inv_matrices,
const Array<Real> & quadrature_points_coordinates,
GhostType ghost_type, const Array<UInt> & element_filter) const;
/// build matrix for the interpolation of field form integration points
template <ElementType type>
inline void buildElementalFieldInterpolationMatrix(
const Matrix<Real> & coordinates, Matrix<Real> & coordMatrix,
UInt integration_order =
ElementClassProperty<type>::polynomial_degree) const;
/// build the so called interpolation matrix (first collumn is 1, then the
/// other collumns are the traansposed coordinates)
static inline void buildInterpolationMatrix(const Matrix<Real> & coordinates,
Matrix<Real> & coordMatrix,
UInt integration_order);
public:
virtual void onElementsAdded(const Array<Element> & /*unused*/) {
AKANTU_TO_IMPLEMENT();
}
virtual void onElementsRemoved(const Array<Element> & /*unused*/,
const ElementTypeMapArray<UInt> & /*unused*/) {
AKANTU_TO_IMPLEMENT();
}
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
/// get the size of the shapes returned by the element class
static inline UInt getShapeSize(ElementType type);
/// get the size of the shapes derivatives returned by the element class
static inline UInt getShapeDerivativesSize(ElementType type);
inline const Matrix<Real> &
getIntegrationPoints(ElementType type,
GhostType ghost_type) const {
return integration_points(type, ghost_type);
}
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
/// get a the shapes vector
inline const Array<Real> &
getShapes(ElementType el_type,
GhostType ghost_type = _not_ghost) const;
/// get a the shapes derivatives vector
inline const Array<Real> &
getShapesDerivatives(ElementType el_type,
GhostType ghost_type = _not_ghost) const;
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
/// shape functions for all elements
ElementTypeMapArray<Real, InterpolationType> shapes;
/// shape functions derivatives for all elements
ElementTypeMapArray<Real, InterpolationType> shapes_derivatives;
/// associated mesh
const Mesh & mesh;
// spatial dimension of the elements to consider
UInt _spatial_dimension;
/// shape functions for all elements
ElementTypeMap<Matrix<Real>> integration_points;
};
/* -------------------------------------------------------------------------- */
/* inline functions */
/* -------------------------------------------------------------------------- */
/// standard output stream operator
inline std::ostream & operator<<(std::ostream & stream,
const ShapeFunctions & _this) {
_this.printself(stream);
return stream;
}
} // namespace akantu
#include "shape_functions_inline_impl.hh"
#endif /* AKANTU_SHAPE_FUNCTIONS_HH_ */
diff --git a/src/fe_engine/shape_lagrange.hh b/src/fe_engine/shape_lagrange.hh
index ad931716e..d808082b1 100644
--- a/src/fe_engine/shape_lagrange.hh
+++ b/src/fe_engine/shape_lagrange.hh
@@ -1,172 +1,171 @@
/**
* @file shape_lagrange.hh
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Tue Feb 15 2011
* @date last modification: Mon Jan 29 2018
*
* @brief lagrangian shape functions class
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "shape_lagrange_base.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_SHAPE_LAGRANGE_HH_
#define AKANTU_SHAPE_LAGRANGE_HH_
namespace akantu {
/* -------------------------------------------------------------------------- */
template <class Shape> class ShapeCohesive;
class ShapeIGFEM;
template <ElementKind kind> class ShapeLagrange : public ShapeLagrangeBase {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
ShapeLagrange(const Mesh & mesh, UInt spatial_dimension,
- const ID & id = "shape_lagrange",
- const MemoryID & memory_id = 0);
+ const ID & id = "shape_lagrange");
~ShapeLagrange() override = default;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// initialization function for structural elements not yet implemented
inline void initShapeFunctions(const Array<Real> & nodes,
const Matrix<Real> & integration_points,
ElementType type,
GhostType ghost_type);
/// computes the shape functions derivatives for given interpolation points
template <ElementType type>
void computeShapeDerivativesOnIntegrationPoints(
const Array<Real> & nodes, const Matrix<Real> & integration_points,
Array<Real> & shape_derivatives, GhostType ghost_type,
const Array<UInt> & filter_elements = empty_filter) const;
void computeShapeDerivativesOnIntegrationPoints(
const Array<Real> & nodes, const Matrix<Real> & integration_points,
Array<Real> & shape_derivatives, ElementType type,
GhostType ghost_type,
const Array<UInt> & filter_elements) const override;
/// pre compute all shapes on the element integration points from natural
/// coordinates
template <ElementType type>
void precomputeShapesOnIntegrationPoints(const Array<Real> & nodes,
GhostType ghost_type);
/// pre compute all shape derivatives on the element integration points from
/// natural coordinates
template <ElementType type>
void
precomputeShapeDerivativesOnIntegrationPoints(const Array<Real> & nodes,
GhostType ghost_type);
/// interpolate nodal values on the integration points
template <ElementType type>
void interpolateOnIntegrationPoints(
const Array<Real> & u, Array<Real> & uq, UInt nb_degree_of_freedom,
GhostType ghost_type = _not_ghost,
const Array<UInt> & filter_elements = empty_filter) const;
template <ElementType type>
void interpolateOnIntegrationPoints(
const Array<Real> & in_u, Array<Real> & out_uq, UInt nb_degree_of_freedom,
const Array<Real> & shapes, GhostType ghost_type = _not_ghost,
const Array<UInt> & filter_elements = empty_filter) const;
/// interpolate on physical point
template <ElementType type>
void interpolate(const Vector<Real> & real_coords, UInt elem,
const Matrix<Real> & nodal_values,
Vector<Real> & interpolated,
GhostType ghost_type) const;
/// compute the gradient of u on the integration points
template <ElementType type>
void gradientOnIntegrationPoints(
const Array<Real> & u, Array<Real> & nablauq, UInt nb_degree_of_freedom,
GhostType ghost_type = _not_ghost,
const Array<UInt> & filter_elements = empty_filter) const;
template <ElementType type>
void computeBtD(const Array<Real> & Ds, Array<Real> & BtDs,
GhostType ghost_type,
const Array<UInt> & filter_elements) const;
template <ElementType type>
void computeBtDB(const Array<Real> & Ds, Array<Real> & BtDBs, UInt order_d,
GhostType ghost_type,
const Array<UInt> & filter_elements) const;
/// multiply a field by shape functions @f$ fts_{ij} = f_i * \varphi_j @f$
template <ElementType type>
void computeNtb(const Array<Real> & bs, Array<Real> & Ntbs,
GhostType ghost_type,
const Array<UInt> & filter_elements = empty_filter) const;
/// find natural coords from real coords provided an element
template <ElementType type>
void inverseMap(const Vector<Real> & real_coords, UInt element,
Vector<Real> & natural_coords,
GhostType ghost_type = _not_ghost) const;
/// return true if the coordinates provided are inside the element, false
/// otherwise
template <ElementType type>
bool contains(const Vector<Real> & real_coords, UInt elem,
GhostType ghost_type) const;
/// compute the shape on a provided point
template <ElementType type>
void computeShapes(const Vector<Real> & real_coords, UInt elem,
Vector<Real> & shapes, GhostType ghost_type) const;
/// compute the shape derivatives on a provided point
template <ElementType type>
void computeShapeDerivatives(const Matrix<Real> & real_coords, UInt elem,
Tensor3<Real> & shapes,
GhostType ghost_type) const;
protected:
/// compute the shape derivatives on integration points for a given element
template <ElementType type>
inline void
computeShapeDerivativesOnCPointsByElement(const Matrix<Real> & node_coords,
const Matrix<Real> & natural_coords,
Tensor3<Real> & shapesd) const;
};
} // namespace akantu
/* -------------------------------------------------------------------------- */
/* inline functions */
/* -------------------------------------------------------------------------- */
#include "shape_lagrange_inline_impl.hh"
#endif /* AKANTU_SHAPE_LAGRANGE_HH_ */
diff --git a/src/fe_engine/shape_lagrange_base.cc b/src/fe_engine/shape_lagrange_base.cc
index f0faa46ef..9ba9ec3e7 100644
--- a/src/fe_engine/shape_lagrange_base.cc
+++ b/src/fe_engine/shape_lagrange_base.cc
@@ -1,172 +1,171 @@
/**
* @file shape_lagrange_base.cc
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Wed Aug 09 2017
* @date last modification: Tue Feb 20 2018
*
* @brief common par for the shape lagrange
*
*
* Copyright (©) 2016-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "shape_lagrange_base.hh"
#include "mesh_iterators.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
ShapeLagrangeBase::ShapeLagrangeBase(const Mesh & mesh, UInt spatial_dimension,
- ElementKind kind, const ID & id,
- const MemoryID & memory_id)
- : ShapeFunctions(mesh, spatial_dimension, id, memory_id), _kind(kind) {}
+ ElementKind kind, const ID & id)
+ : ShapeFunctions(mesh, spatial_dimension, id), _kind(kind) {}
/* -------------------------------------------------------------------------- */
ShapeLagrangeBase::~ShapeLagrangeBase() = default;
/* -------------------------------------------------------------------------- */
#define AKANTU_COMPUTE_SHAPES(type) \
_this.template computeShapesOnIntegrationPoints<type>( \
nodes, integration_points, shapes, ghost_type, filter_elements)
namespace shape_lagrange {
namespace details {
template <ElementKind kind> struct Helper {
template <class S>
static void call(const S & /*unused*/, const Array<Real> & /*unused*/,
const Matrix<Real> & /*unused*/,
Array<Real> & /*unused*/, ElementType /*unused*/,
GhostType /*unused*/,
const Array<UInt> & /*unused*/) {
AKANTU_TO_IMPLEMENT();
}
};
#if !defined(DOXYGEN)
#define AKANTU_COMPUTE_SHAPES_KIND(kind) \
template <> struct Helper<kind> { \
template <class S> \
static void call(const S & _this, const Array<Real> & nodes, \
const Matrix<Real> & integration_points, \
Array<Real> & shapes, ElementType type, \
GhostType ghost_type, \
const Array<UInt> & filter_elements) { \
AKANTU_BOOST_KIND_ELEMENT_SWITCH(AKANTU_COMPUTE_SHAPES, kind); \
} \
};
AKANTU_BOOST_ALL_KIND_LIST(AKANTU_COMPUTE_SHAPES_KIND,
AKANTU_FE_ENGINE_LIST_LAGRANGE_BASE)
} // namespace details
} // namespace shape_lagrange
#endif
/* -------------------------------------------------------------------------- */
void ShapeLagrangeBase::computeShapesOnIntegrationPoints(
const Array<Real> & nodes, const Matrix<Real> & integration_points,
Array<Real> & shapes, ElementType type,
GhostType ghost_type, const Array<UInt> & filter_elements) const {
auto kind = Mesh::getKind(type);
#define AKANTU_COMPUTE_SHAPES_KIND_SWITCH(kind) \
shape_lagrange::details::Helper<kind>::call( \
*this, nodes, integration_points, shapes, type, ghost_type, \
filter_elements);
AKANTU_BOOST_LIST_SWITCH(
AKANTU_COMPUTE_SHAPES_KIND_SWITCH,
BOOST_PP_LIST_TO_SEQ(AKANTU_FE_ENGINE_LIST_LAGRANGE_BASE), kind);
#undef AKANTU_COMPUTE_SHAPES
#undef AKANTU_COMPUTE_SHAPES_KIND
#undef AKANTU_COMPUTE_SHAPES_KIND_SWITCH
}
/* -------------------------------------------------------------------------- */
void ShapeLagrangeBase::onElementsAdded(const Array<Element> & new_elements) {
AKANTU_DEBUG_IN();
const auto & nodes = mesh.getNodes();
for (auto elements_range : MeshElementsByTypes(new_elements)) {
auto type = elements_range.getType();
auto ghost_type = elements_range.getGhostType();
if (mesh.getSpatialDimension(type) != _spatial_dimension) {
continue;
}
if (mesh.getKind(type) != _kind) {
continue;
}
const auto & elements = elements_range.getElements();
auto itp_type = FEEngine::getInterpolationType(type);
if (not shapes.exists(itp_type, ghost_type)) {
auto size_of_shapes = this->getShapeSize(type);
this->shapes.alloc(0, size_of_shapes, itp_type, ghost_type);
}
const auto & natural_coords = integration_points(type, ghost_type);
computeShapesOnIntegrationPoints(nodes, natural_coords,
shapes(itp_type, ghost_type), type,
ghost_type, elements);
if (_spatial_dimension != mesh.getSpatialDimension()) {
continue;
}
if (not this->shapes_derivatives.exists(itp_type, ghost_type)) {
auto size_of_shapesd = this->getShapeDerivativesSize(type);
this->shapes_derivatives.alloc(0, size_of_shapesd, itp_type, ghost_type);
}
computeShapeDerivativesOnIntegrationPoints(
nodes, natural_coords, shapes_derivatives(itp_type, ghost_type), type,
ghost_type, elements);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void ShapeLagrangeBase::onElementsRemoved(
const Array<Element> & /*unused*/,
const ElementTypeMapArray<UInt> & new_numbering) {
this->shapes.onElementsRemoved(new_numbering);
this->shapes_derivatives.onElementsRemoved(new_numbering);
}
/* -------------------------------------------------------------------------- */
void ShapeLagrangeBase::printself(std::ostream & stream, int indent) const {
std::string space(indent, AKANTU_INDENT);
stream << space << "Shapes Lagrange [" << std::endl;
ShapeFunctions::printself(stream, indent + 1);
shapes.printself(stream, indent + 1);
shapes_derivatives.printself(stream, indent + 1);
stream << space << "]" << std::endl;
}
} // namespace akantu
diff --git a/src/fe_engine/shape_lagrange_base.hh b/src/fe_engine/shape_lagrange_base.hh
index 095822a12..a033026c4 100644
--- a/src/fe_engine/shape_lagrange_base.hh
+++ b/src/fe_engine/shape_lagrange_base.hh
@@ -1,91 +1,88 @@
/**
* @file shape_lagrange_base.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Wed Aug 09 2017
* @date last modification: Wed Nov 08 2017
*
* @brief Base class for the shape lagrange
*
*
* Copyright (©) 2016-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "shape_functions.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_SHAPE_LAGRANGE_BASE_HH_
#define AKANTU_SHAPE_LAGRANGE_BASE_HH_
namespace akantu {
class ShapeLagrangeBase : public ShapeFunctions {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
- ShapeLagrangeBase(const Mesh & mesh, UInt spatial_dimension,
- ElementKind kind, const ID & id = "shape_lagrange",
- const MemoryID & memory_id = 0);
+ ShapeLagrangeBase(const Mesh & mesh, UInt spatial_dimension, ElementKind kind,
+ const ID & id = "shape_lagrange");
~ShapeLagrangeBase() override;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// computes the shape functions for given interpolation points
virtual void computeShapesOnIntegrationPoints(
const Array<Real> & nodes, const Matrix<Real> & integration_points,
- Array<Real> & shapes, ElementType type,
- GhostType ghost_type,
+ Array<Real> & shapes, ElementType type, GhostType ghost_type,
const Array<UInt> & filter_elements = empty_filter) const;
/// computes the shape functions derivatives for given interpolation points
virtual void computeShapeDerivativesOnIntegrationPoints(
const Array<Real> & nodes, const Matrix<Real> & integration_points,
- Array<Real> & shape_derivatives, ElementType type,
- GhostType ghost_type,
+ Array<Real> & shape_derivatives, ElementType type, GhostType ghost_type,
const Array<UInt> & filter_elements = empty_filter) const = 0;
/// function to print the containt of the class
void printself(std::ostream & stream, int indent = 0) const override;
template <ElementType type>
void computeShapesOnIntegrationPoints(
const Array<Real> & nodes, const Matrix<Real> & integration_points,
Array<Real> & shapes, GhostType ghost_type,
const Array<UInt> & filter_elements = empty_filter) const;
public:
void onElementsAdded(const Array<Element> & elements) override;
void
onElementsRemoved(const Array<Element> & elements,
const ElementTypeMapArray<UInt> & new_numbering) override;
protected:
/// The kind to consider
ElementKind _kind;
};
} // namespace akantu
#include "shape_lagrange_base_inline_impl.hh"
#endif /* AKANTU_SHAPE_LAGRANGE_BASE_HH_ */
diff --git a/src/fe_engine/shape_lagrange_inline_impl.hh b/src/fe_engine/shape_lagrange_inline_impl.hh
index c536c317c..457029a1f 100644
--- a/src/fe_engine/shape_lagrange_inline_impl.hh
+++ b/src/fe_engine/shape_lagrange_inline_impl.hh
@@ -1,540 +1,540 @@
/**
* @file shape_lagrange_inline_impl.hh
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Wed Oct 27 2010
* @date last modification: Tue Feb 20 2018
*
* @brief ShapeLagrange inline implementation
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_iterators.hh"
#include "aka_voigthelper.hh"
#include "fe_engine.hh"
#include "shape_lagrange.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_SHAPE_LAGRANGE_INLINE_IMPL_HH_
#define AKANTU_SHAPE_LAGRANGE_INLINE_IMPL_HH_
namespace akantu {
/* -------------------------------------------------------------------------- */
#define INIT_SHAPE_FUNCTIONS(type) \
setIntegrationPointsByType<type>(integration_points, ghost_type); \
precomputeShapesOnIntegrationPoints<type>(nodes, ghost_type); \
if (ElementClass<type>::getNaturalSpaceDimension() == \
mesh.getSpatialDimension() || \
kind != _ek_regular) \
precomputeShapeDerivativesOnIntegrationPoints<type>(nodes, ghost_type);
template <ElementKind kind>
inline void ShapeLagrange<kind>::initShapeFunctions(
const Array<Real> & nodes, const Matrix<Real> & integration_points,
ElementType type, GhostType ghost_type) {
AKANTU_BOOST_REGULAR_ELEMENT_SWITCH(INIT_SHAPE_FUNCTIONS);
}
#undef INIT_SHAPE_FUNCTIONS
/* -------------------------------------------------------------------------- */
template <ElementKind kind>
template <ElementType type>
inline void ShapeLagrange<kind>::computeShapeDerivativesOnCPointsByElement(
const Matrix<Real> & node_coords, const Matrix<Real> & natural_coords,
Tensor3<Real> & shapesd) const {
AKANTU_DEBUG_IN();
// compute dnds
Tensor3<Real> dnds(node_coords.rows(), node_coords.cols(),
natural_coords.cols());
ElementClass<type>::computeDNDS(natural_coords, dnds);
// compute jacobian
Tensor3<Real> J(node_coords.rows(), natural_coords.rows(),
natural_coords.cols());
ElementClass<type>::computeJMat(dnds, node_coords, J);
// compute dndx
ElementClass<type>::computeShapeDerivatives(J, dnds, shapesd);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind>
template <ElementType type>
void ShapeLagrange<kind>::inverseMap(const Vector<Real> & real_coords,
UInt elem, Vector<Real> & natural_coords,
GhostType ghost_type) const {
AKANTU_DEBUG_IN();
UInt spatial_dimension = mesh.getSpatialDimension();
UInt nb_nodes_per_element =
ElementClass<type>::getNbNodesPerInterpolationElement();
UInt * elem_val = mesh.getConnectivity(type, ghost_type).storage();
Matrix<Real> nodes_coord(spatial_dimension, nb_nodes_per_element);
mesh.extractNodalValuesFromElement(mesh.getNodes(), nodes_coord.storage(),
elem_val + elem * nb_nodes_per_element,
nb_nodes_per_element, spatial_dimension);
ElementClass<type>::inverseMap(real_coords, nodes_coord, natural_coords);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind>
template <ElementType type>
bool ShapeLagrange<kind>::contains(const Vector<Real> & real_coords, UInt elem,
GhostType ghost_type) const {
UInt spatial_dimension = mesh.getSpatialDimension();
Vector<Real> natural_coords(spatial_dimension);
inverseMap<type>(real_coords, elem, natural_coords, ghost_type);
return ElementClass<type>::contains(natural_coords);
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind>
template <ElementType type>
void ShapeLagrange<kind>::interpolate(const Vector<Real> & real_coords,
UInt elem,
const Matrix<Real> & nodal_values,
Vector<Real> & interpolated,
GhostType ghost_type) const {
UInt nb_shapes = ElementClass<type>::getShapeSize();
Vector<Real> shapes(nb_shapes);
computeShapes<type>(real_coords, elem, shapes, ghost_type);
ElementClass<type>::interpolate(nodal_values, shapes, interpolated);
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind>
template <ElementType type>
void ShapeLagrange<kind>::computeShapes(const Vector<Real> & real_coords,
UInt elem, Vector<Real> & shapes,
GhostType ghost_type) const {
AKANTU_DEBUG_IN();
UInt spatial_dimension = mesh.getSpatialDimension();
Vector<Real> natural_coords(spatial_dimension);
inverseMap<type>(real_coords, elem, natural_coords, ghost_type);
ElementClass<type>::computeShapes(natural_coords, shapes);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind>
template <ElementType type>
void ShapeLagrange<kind>::computeShapeDerivatives(
const Matrix<Real> & real_coords, UInt elem, Tensor3<Real> & shapesd,
GhostType ghost_type) const {
AKANTU_DEBUG_IN();
UInt spatial_dimension = mesh.getSpatialDimension();
UInt nb_points = real_coords.cols();
UInt nb_nodes_per_element =
ElementClass<type>::getNbNodesPerInterpolationElement();
AKANTU_DEBUG_ASSERT(mesh.getSpatialDimension() == shapesd.size(0) &&
nb_nodes_per_element == shapesd.size(1),
"Shape size doesn't match");
AKANTU_DEBUG_ASSERT(nb_points == shapesd.size(2),
"Number of points doesn't match shapes size");
Matrix<Real> natural_coords(spatial_dimension, nb_points);
// Creates the matrix of natural coordinates
for (UInt i = 0; i < nb_points; i++) {
Vector<Real> real_point = real_coords(i);
Vector<Real> natural_point = natural_coords(i);
inverseMap<type>(real_point, elem, natural_point, ghost_type);
}
UInt * elem_val = mesh.getConnectivity(type, ghost_type).storage();
Matrix<Real> nodes_coord(spatial_dimension, nb_nodes_per_element);
mesh.extractNodalValuesFromElement(mesh.getNodes(), nodes_coord.storage(),
elem_val + elem * nb_nodes_per_element,
nb_nodes_per_element, spatial_dimension);
computeShapeDerivativesOnCPointsByElement<type>(nodes_coord, natural_coords,
shapesd);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind>
ShapeLagrange<kind>::ShapeLagrange(const Mesh & mesh, UInt spatial_dimension,
- const ID & id, const MemoryID & memory_id)
- : ShapeLagrangeBase(mesh, spatial_dimension, kind, id, memory_id) {}
+ const ID & id)
+ : ShapeLagrangeBase(mesh, spatial_dimension, kind, id) {}
/* -------------------------------------------------------------------------- */
template <ElementKind kind>
template <ElementType type>
void ShapeLagrange<kind>::computeShapeDerivativesOnIntegrationPoints(
const Array<Real> & nodes, const Matrix<Real> & integration_points,
Array<Real> & shape_derivatives, GhostType ghost_type,
const Array<UInt> & filter_elements) const {
AKANTU_DEBUG_IN();
UInt spatial_dimension = mesh.getSpatialDimension();
UInt nb_nodes_per_element =
ElementClass<type>::getNbNodesPerInterpolationElement();
UInt nb_points = integration_points.cols();
UInt nb_element = mesh.getConnectivity(type, ghost_type).size();
UInt size_of_shapesd = ElementClass<type>::getShapeDerivativesSize();
AKANTU_DEBUG_ASSERT(shape_derivatives.getNbComponent() == size_of_shapesd,
"The shapes_derivatives array does not have the correct "
<< "number of component");
shape_derivatives.resize(nb_element * nb_points);
Array<Real> x_el(0, spatial_dimension * nb_nodes_per_element);
FEEngine::extractNodalToElementField(mesh, nodes, x_el, type, ghost_type,
filter_elements);
Real * shapesd_val = shape_derivatives.storage();
Array<Real>::matrix_iterator x_it =
x_el.begin(spatial_dimension, nb_nodes_per_element);
if (filter_elements != empty_filter) {
nb_element = filter_elements.size();
}
for (UInt elem = 0; elem < nb_element; ++elem, ++x_it) {
if (filter_elements != empty_filter) {
shapesd_val = shape_derivatives.storage() +
filter_elements(elem) * size_of_shapesd * nb_points;
}
Matrix<Real> & X = *x_it;
Tensor3<Real> B(shapesd_val, spatial_dimension, nb_nodes_per_element,
nb_points);
computeShapeDerivativesOnCPointsByElement<type>(X, integration_points, B);
if (filter_elements == empty_filter) {
shapesd_val += size_of_shapesd * nb_points;
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind>
void ShapeLagrange<kind>::computeShapeDerivativesOnIntegrationPoints(
const Array<Real> & nodes, const Matrix<Real> & integration_points,
Array<Real> & shape_derivatives, ElementType type,
GhostType ghost_type, const Array<UInt> & filter_elements) const {
#define AKANTU_COMPUTE_SHAPES(type) \
computeShapeDerivativesOnIntegrationPoints<type>( \
nodes, integration_points, shape_derivatives, ghost_type, \
filter_elements);
AKANTU_BOOST_REGULAR_ELEMENT_SWITCH(AKANTU_COMPUTE_SHAPES);
#undef AKANTU_COMPUTE_SHAPES
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind>
template <ElementType type>
void ShapeLagrange<kind>::precomputeShapesOnIntegrationPoints(
const Array<Real> & nodes, GhostType ghost_type) {
AKANTU_DEBUG_IN();
InterpolationType itp_type = ElementClassProperty<type>::interpolation_type;
Matrix<Real> & natural_coords = integration_points(type, ghost_type);
UInt size_of_shapes = ElementClass<type>::getShapeSize();
Array<Real> & shapes_tmp =
shapes.alloc(0, size_of_shapes, itp_type, ghost_type);
this->computeShapesOnIntegrationPoints<type>(nodes, natural_coords,
shapes_tmp, ghost_type);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind>
template <ElementType type>
void ShapeLagrange<kind>::precomputeShapeDerivativesOnIntegrationPoints(
const Array<Real> & nodes, GhostType ghost_type) {
AKANTU_DEBUG_IN();
InterpolationType itp_type = ElementClassProperty<type>::interpolation_type;
Matrix<Real> & natural_coords = integration_points(type, ghost_type);
UInt size_of_shapesd = ElementClass<type>::getShapeDerivativesSize();
Array<Real> & shapes_derivatives_tmp =
shapes_derivatives.alloc(0, size_of_shapesd, itp_type, ghost_type);
this->computeShapeDerivativesOnIntegrationPoints<type>(
nodes, natural_coords, shapes_derivatives_tmp, ghost_type);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
template <ElementKind kind>
template <ElementType type>
void ShapeLagrange<kind>::interpolateOnIntegrationPoints(
const Array<Real> & in_u, Array<Real> & out_uq, UInt nb_degree_of_freedom,
const Array<Real> & shapes, GhostType ghost_type,
const Array<UInt> & filter_elements) const {
AKANTU_DEBUG_IN();
UInt nb_nodes_per_element =
ElementClass<type>::getNbNodesPerInterpolationElement();
Array<Real> u_el(0, nb_degree_of_freedom * nb_nodes_per_element);
FEEngine::extractNodalToElementField(mesh, in_u, u_el, type, ghost_type,
filter_elements);
this->interpolateElementalFieldOnIntegrationPoints<type>(
u_el, out_uq, ghost_type, shapes, filter_elements);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind>
template <ElementType type>
void ShapeLagrange<kind>::interpolateOnIntegrationPoints(
const Array<Real> & in_u, Array<Real> & out_uq, UInt nb_degree_of_freedom,
GhostType ghost_type, const Array<UInt> & filter_elements) const {
AKANTU_DEBUG_IN();
InterpolationType itp_type = ElementClassProperty<type>::interpolation_type;
AKANTU_DEBUG_ASSERT(shapes.exists(itp_type, ghost_type),
"No shapes for the type "
<< shapes.printType(itp_type, ghost_type));
this->interpolateOnIntegrationPoints<type>(in_u, out_uq, nb_degree_of_freedom,
shapes(itp_type, ghost_type),
ghost_type, filter_elements);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind>
template <ElementType type>
void ShapeLagrange<kind>::gradientOnIntegrationPoints(
const Array<Real> & in_u, Array<Real> & out_nablauq,
UInt nb_degree_of_freedom, GhostType ghost_type,
const Array<UInt> & filter_elements) const {
AKANTU_DEBUG_IN();
InterpolationType itp_type = ElementClassProperty<type>::interpolation_type;
AKANTU_DEBUG_ASSERT(
shapes_derivatives.exists(itp_type, ghost_type),
"No shapes derivatives for the type "
<< shapes_derivatives.printType(itp_type, ghost_type));
UInt nb_nodes_per_element =
ElementClass<type>::getNbNodesPerInterpolationElement();
Array<Real> u_el(0, nb_degree_of_freedom * nb_nodes_per_element);
FEEngine::extractNodalToElementField(mesh, in_u, u_el, type, ghost_type,
filter_elements);
this->gradientElementalFieldOnIntegrationPoints<type>(
u_el, out_nablauq, ghost_type, shapes_derivatives(itp_type, ghost_type),
filter_elements);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind>
template <ElementType type>
void ShapeLagrange<kind>::computeBtD(
const Array<Real> & Ds, Array<Real> & BtDs, GhostType ghost_type,
const Array<UInt> & filter_elements) const {
auto itp_type = ElementClassProperty<type>::interpolation_type;
const auto & shapes_derivatives =
this->shapes_derivatives(itp_type, ghost_type);
auto spatial_dimension = mesh.getSpatialDimension();
auto nb_nodes_per_element = mesh.getNbNodesPerElement(type);
Array<Real> shapes_derivatives_filtered(0,
shapes_derivatives.getNbComponent());
auto && view =
make_view(shapes_derivatives, spatial_dimension, nb_nodes_per_element);
auto B_it = view.begin();
auto B_end = view.end();
if (filter_elements != empty_filter) {
FEEngine::filterElementalData(this->mesh, shapes_derivatives,
shapes_derivatives_filtered, type, ghost_type,
filter_elements);
auto && view = make_view(shapes_derivatives_filtered, spatial_dimension,
nb_nodes_per_element);
B_it = view.begin();
B_end = view.end();
}
for (auto && values :
zip(range(B_it, B_end),
make_view(Ds, Ds.getNbComponent() / spatial_dimension,
spatial_dimension),
make_view(BtDs, BtDs.getNbComponent() / nb_nodes_per_element,
nb_nodes_per_element))) {
const auto & B = std::get<0>(values);
const auto & D = std::get<1>(values);
auto & Bt_D = std::get<2>(values);
// transposed due to the storage layout of B
Bt_D.template mul<false, false>(D, B);
}
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind>
template <ElementType type>
void ShapeLagrange<kind>::computeBtDB(
const Array<Real> & Ds, Array<Real> & BtDBs, UInt order_d,
GhostType ghost_type, const Array<UInt> & filter_elements) const {
auto itp_type = ElementClassProperty<type>::interpolation_type;
const auto & shapes_derivatives =
this->shapes_derivatives(itp_type, ghost_type);
constexpr auto dim = ElementClass<type>::getSpatialDimension();
auto nb_nodes_per_element = mesh.getNbNodesPerElement(type);
Array<Real> shapes_derivatives_filtered(0,
shapes_derivatives.getNbComponent());
auto && view = make_view(shapes_derivatives, dim, nb_nodes_per_element);
auto B_it = view.begin();
auto B_end = view.end();
if (filter_elements != empty_filter) {
FEEngine::filterElementalData(this->mesh, shapes_derivatives,
shapes_derivatives_filtered, type, ghost_type,
filter_elements);
auto && view =
make_view(shapes_derivatives_filtered, dim, nb_nodes_per_element);
B_it = view.begin();
B_end = view.end();
}
if (order_d == 4) {
UInt tangent_size = VoigtHelper<dim>::size;
Matrix<Real> B(tangent_size, dim * nb_nodes_per_element);
Matrix<Real> Bt_D(dim * nb_nodes_per_element, tangent_size);
for (auto && values :
zip(range(B_it, B_end), make_view(Ds, tangent_size, tangent_size),
make_view(BtDBs, dim * nb_nodes_per_element,
dim * nb_nodes_per_element))) {
const auto & Bfull = std::get<0>(values);
const auto & D = std::get<1>(values);
auto & Bt_D_B = std::get<2>(values);
VoigtHelper<dim>::transferBMatrixToSymVoigtBMatrix(Bfull, B,
nb_nodes_per_element);
Bt_D.template mul<true, false>(B, D);
Bt_D_B.template mul<false, false>(Bt_D, B);
}
} else if (order_d == 2) {
Matrix<Real> Bt_D(nb_nodes_per_element, dim);
for (auto && values :
zip(range(B_it, B_end), make_view(Ds, dim, dim),
make_view(BtDBs, nb_nodes_per_element, nb_nodes_per_element))) {
const auto & B = std::get<0>(values);
const auto & D = std::get<1>(values);
auto & Bt_D_B = std::get<2>(values);
Bt_D.template mul<true, false>(B, D);
Bt_D_B.template mul<false, false>(Bt_D, B);
}
}
}
template <>
template <>
inline void ShapeLagrange<_ek_regular>::computeBtDB<_point_1>(
const Array<Real> & /*Ds*/, Array<Real> & /*BtDBs*/, UInt /*order_d*/,
GhostType /*ghost_type*/, const Array<UInt> & /*filter_elements*/) const {
AKANTU_TO_IMPLEMENT();
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind>
template <ElementType type>
void ShapeLagrange<kind>::computeNtb(
const Array<Real> & bs, Array<Real> & Ntbs, GhostType ghost_type,
const Array<UInt> & filter_elements) const {
AKANTU_DEBUG_IN();
Ntbs.resize(bs.size());
UInt size_of_shapes = ElementClass<type>::getShapeSize();
InterpolationType itp_type = ElementClassProperty<type>::interpolation_type;
UInt nb_degree_of_freedom = bs.getNbComponent();
Array<Real> shapes_filtered(0, size_of_shapes);
auto && view = make_view(shapes(itp_type, ghost_type), 1, size_of_shapes);
auto N_it = view.begin();
auto N_end = view.end();
if (filter_elements != empty_filter) {
FEEngine::filterElementalData(this->mesh, shapes(itp_type, ghost_type),
shapes_filtered, type, ghost_type,
filter_elements);
auto && view = make_view(shapes_filtered, 1, size_of_shapes);
N_it = view.begin();
N_end = view.end();
}
for (auto && values :
zip(make_view(bs, nb_degree_of_freedom, 1), range(N_it, N_end),
make_view(Ntbs, nb_degree_of_freedom, size_of_shapes))) {
const auto & b = std::get<0>(values);
const auto & N = std::get<1>(values);
auto & Ntb = std::get<2>(values);
Ntb.template mul<false, false>(b, N);
}
AKANTU_DEBUG_OUT();
}
} // namespace akantu
#endif /* AKANTU_SHAPE_LAGRANGE_INLINE_IMPL_HH_ */
diff --git a/src/fe_engine/shape_structural.cc b/src/fe_engine/shape_structural.cc
index e7bfef4af..16d6d9ca0 100644
--- a/src/fe_engine/shape_structural.cc
+++ b/src/fe_engine/shape_structural.cc
@@ -1,54 +1,52 @@
/**
* @file shape_structural.cc
*
* @author Fabian Barras <fabian.barras@epfl.ch>
* @author Lucas Frerot <lucas.frerot@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Thu Feb 21 2013
* @date last modification: Thu Dec 21 2017
*
* @brief ShapeStructural implementation
*
*
* Copyright (©) 2014-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "shape_structural.hh"
-#include "aka_memory.hh"
#include "mesh.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
template <>
ShapeStructural<_ek_structural>::ShapeStructural(Mesh & mesh,
UInt spatial_dimension,
- const ID & id,
- const MemoryID & memory_id)
- : ShapeFunctions(mesh, spatial_dimension, id, memory_id),
- rotation_matrices("rotation_matrices", id, memory_id) {}
+ const ID & id)
+ : ShapeFunctions(mesh, spatial_dimension, id),
+ rotation_matrices("rotation_matrices", id) {}
/* -------------------------------------------------------------------------- */
template <> ShapeStructural<_ek_structural>::~ShapeStructural() = default;
/* -------------------------------------------------------------------------- */
} // namespace akantu
diff --git a/src/fe_engine/shape_structural.hh b/src/fe_engine/shape_structural.hh
index c6b606939..d637b320b 100644
--- a/src/fe_engine/shape_structural.hh
+++ b/src/fe_engine/shape_structural.hh
@@ -1,172 +1,192 @@
/**
* @file shape_structural.hh
*
* @author Fabian Barras <fabian.barras@epfl.ch>
* @author Lucas Frerot <lucas.frerot@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Tue Feb 15 2011
* @date last modification: Tue Feb 20 2018
*
* @brief shape class for element with different set of shapes functions
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "shape_functions.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_SHAPE_STRUCTURAL_HH_
#define AKANTU_SHAPE_STRUCTURAL_HH_
namespace akantu {
template <ElementKind kind> class ShapeStructural : public ShapeFunctions {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
// Ctors/Dtors should be explicitely implemented for _ek_structural
public:
ShapeStructural(Mesh & mesh, UInt spatial_dimension,
- const ID & id = "shape_structural",
- const MemoryID & memory_id = 0);
+ const ID & id = "shape_structural");
~ShapeStructural() override;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// function to print the contain of the class
void printself(std::ostream & stream, int indent = 0) const override {
std::string space(indent, AKANTU_INDENT);
stream << space << "ShapesStructural [" << std::endl;
rotation_matrices.printself(stream, indent + 1);
ShapeFunctions::printself(stream, indent + 1);
stream << space << "]" << std::endl;
}
+private:
+ template <ElementType type>
+ void computeShapesOnIntegrationPointsInternal(
+ const Array<Real> & nodes, const Matrix<Real> & integration_points,
+ Array<Real> & shapes, GhostType ghost_type,
+ const Array<UInt> & filter_elements = empty_filter,
+ bool mass = false) const;
+
+public:
/// compute shape functions on given integration points
template <ElementType type>
void computeShapesOnIntegrationPoints(
- const Array<Real> &, const Matrix<Real> & integration_points,
+ const Array<Real> & nodes, const Matrix<Real> & integration_points,
Array<Real> & shapes, GhostType ghost_type,
- const Array<UInt> & filter_elements = empty_filter) const;
+ const Array<UInt> & filter_elements = empty_filter) const {
+ this->template computeShapesOnIntegrationPointsInternal<type>(
+ nodes, integration_points, shapes, ghost_type, filter_elements, false);
+ }
+
+ template <ElementType type>
+ void computeShapesMassOnIntegrationPoints(
+ const Array<Real> & nodes, const Matrix<Real> & integration_points,
+ Array<Real> & shapes, GhostType ghost_type,
+ const Array<UInt> & filter_elements = empty_filter) const {
+ this->template computeShapesOnIntegrationPointsInternal<type>(
+ nodes, integration_points, shapes, ghost_type, filter_elements, true);
+ }
/// initialization function for structural elements
inline void initShapeFunctions(const Array<Real> & nodes,
const Matrix<Real> & integration_points,
ElementType type, GhostType ghost_type);
/// precompute the rotation matrices for the elements dofs
template <ElementType type>
void precomputeRotationMatrices(const Array<Real> & nodes,
GhostType ghost_type);
/// pre compute all shapes on the element integration points from natural
/// coordinates
template <ElementType type>
void precomputeShapesOnIntegrationPoints(const Array<Real> & nodes,
GhostType ghost_type);
/// pre compute all shapes on the element integration points from natural
/// coordinates
template <ElementType type>
void precomputeShapeDerivativesOnIntegrationPoints(const Array<Real> & nodes,
GhostType ghost_type);
/// interpolate nodal values on the integration points
template <ElementType type>
void interpolateOnIntegrationPoints(
const Array<Real> & u, Array<Real> & uq, UInt nb_dof,
GhostType ghost_type = _not_ghost,
const Array<UInt> & filter_elements = empty_filter) const;
/// compute the gradient of u on the integration points
template <ElementType type>
void gradientOnIntegrationPoints(
const Array<Real> & u, Array<Real> & nablauq, UInt nb_dof,
GhostType ghost_type = _not_ghost,
const Array<UInt> & filter_elements = empty_filter) const;
/// interpolate on physical point
template <ElementType type>
void interpolate(const Vector<Real> & /*real_coords*/, UInt /*elem*/,
const Matrix<Real> & /*nodal_values*/,
Vector<Real> & /*interpolated*/,
GhostType /*ghost_type*/) const {
AKANTU_TO_IMPLEMENT();
}
/// compute the shapes on a provided point
template <ElementType type>
void computeShapes(const Vector<Real> & /*real_coords*/, UInt /*elem*/,
Vector<Real> & /*shapes*/,
GhostType /*ghost_type*/) const {
AKANTU_TO_IMPLEMENT();
}
/// compute the shape derivatives on a provided point
template <ElementType type>
void computeShapeDerivatives(const Matrix<Real> & /*real_coords*/,
UInt /*elem*/, Tensor3<Real> & /*shapes*/,
GhostType /*ghost_type*/) const {
AKANTU_TO_IMPLEMENT();
}
/// get the rotations vector
inline const Array<Real> &
getRotations(ElementType el_type, __attribute__((unused))
GhostType ghost_type = _not_ghost) const {
return rotation_matrices(el_type);
}
/* ------------------------------------------------------------------------ */
template <ElementType type>
void computeBtD(const Array<Real> & /*Ds*/, Array<Real> & /*BtDs*/,
GhostType /*ghost_type*/,
const Array<UInt> & /*filter_elements*/) const {
AKANTU_TO_IMPLEMENT();
}
template <ElementType type>
void computeBtDB(const Array<Real> & /*Ds*/, Array<Real> & /*BtDBs*/,
UInt /*order_d*/, GhostType /*ghost_type*/,
const Array<UInt> & /*filter_elements*/) const {
AKANTU_TO_IMPLEMENT();
}
/// multiply a field by shape functions
template <ElementType type>
void
computeNtb(const Array<Real> & /*bs*/, Array<Real> & /*Ntbs*/,
GhostType /*ghost_type*/,
const Array<UInt> & /*filter_elements*/ = empty_filter) const {
AKANTU_TO_IMPLEMENT();
}
protected:
ElementTypeMapArray<Real> rotation_matrices;
};
} // namespace akantu
#include "shape_structural_inline_impl.hh"
#endif /* AKANTU_SHAPE_STRUCTURAL_HH_ */
diff --git a/src/fe_engine/shape_structural_inline_impl.hh b/src/fe_engine/shape_structural_inline_impl.hh
index bb29be0c5..820898908 100644
--- a/src/fe_engine/shape_structural_inline_impl.hh
+++ b/src/fe_engine/shape_structural_inline_impl.hh
@@ -1,433 +1,509 @@
/**
* @file shape_structural_inline_impl.hh
*
* @author Fabian Barras <fabian.barras@epfl.ch>
* @author Lucas Frerot <lucas.frerot@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Wed Oct 11 2017
* @date last modification: Wed Feb 21 2018
*
* @brief ShapeStructural inline implementation
*
*
* Copyright (©) 2016-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "mesh_iterators.hh"
#include "shape_structural.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_SHAPE_STRUCTURAL_INLINE_IMPL_HH_
#define AKANTU_SHAPE_STRUCTURAL_INLINE_IMPL_HH_
namespace akantu {
namespace {
/// Extract nodal coordinates per elements
template <ElementType type>
std::unique_ptr<Array<Real>> getNodesPerElement(const Mesh & mesh,
const Array<Real> & nodes,
GhostType ghost_type) {
const auto dim = ElementClass<type>::getSpatialDimension();
const auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
auto nodes_per_element =
std::make_unique<Array<Real>>(0, dim * nb_nodes_per_element);
FEEngine::extractNodalToElementField(mesh, nodes, *nodes_per_element, type,
ghost_type);
return nodes_per_element;
}
} // namespace
template <ElementKind kind>
inline void ShapeStructural<kind>::initShapeFunctions(
const Array<Real> & /* unused */, const Matrix<Real> & /* unused */,
ElementType /* unused */, GhostType /* unused */) {
AKANTU_TO_IMPLEMENT();
}
/* -------------------------------------------------------------------------- */
#define INIT_SHAPE_FUNCTIONS(type) \
setIntegrationPointsByType<type>(integration_points, ghost_type); \
precomputeRotationMatrices<type>(nodes, ghost_type); \
precomputeShapesOnIntegrationPoints<type>(nodes, ghost_type); \
precomputeShapeDerivativesOnIntegrationPoints<type>(nodes, ghost_type);
template <>
inline void ShapeStructural<_ek_structural>::initShapeFunctions(
const Array<Real> & nodes, const Matrix<Real> & integration_points,
ElementType type, GhostType ghost_type) {
AKANTU_BOOST_STRUCTURAL_ELEMENT_SWITCH(INIT_SHAPE_FUNCTIONS);
}
#undef INIT_SHAPE_FUNCTIONS
/* -------------------------------------------------------------------------- */
-template <>
+template <ElementKind kind>
template <ElementType type>
-void ShapeStructural<_ek_structural>::computeShapesOnIntegrationPoints(
+void ShapeStructural<kind>::computeShapesOnIntegrationPointsInternal(
const Array<Real> & nodes, const Matrix<Real> & integration_points,
Array<Real> & shapes, GhostType ghost_type,
- const Array<UInt> & filter_elements) const {
+ const Array<UInt> & filter_elements, bool mass) const {
- UInt nb_points = integration_points.cols();
- UInt nb_element = mesh.getConnectivity(type, ghost_type).size();
+ auto nb_points = integration_points.cols();
+ auto nb_element = mesh.getConnectivity(type, ghost_type).size();
+ auto nb_nodes_per_element = ElementClass<type>::getNbNodesPerElement();
shapes.resize(nb_element * nb_points);
- UInt ndof = ElementClass<type>::getNbDegreeOfFreedom();
+ auto nb_dofs = ElementClass<type>::getNbDegreeOfFreedom();
+ auto nb_rows = nb_dofs;
+ if (mass) {
+ nb_rows = ElementClass<type>::getNbStressComponents();
+ }
#if !defined(AKANTU_NDEBUG)
- UInt size_of_shapes = ElementClass<type>::getShapeSize();
+ UInt size_of_shapes = nb_rows * nb_dofs * nb_nodes_per_element;
AKANTU_DEBUG_ASSERT(shapes.getNbComponent() == size_of_shapes,
"The shapes array does not have the correct "
<< "number of component");
#endif
- auto shapes_it = shapes.begin_reinterpret(
- ElementClass<type>::getNbNodesPerInterpolationElement(), ndof, nb_points,
- nb_element);
+ auto shapes_it = shapes.begin_reinterpret(
+ nb_rows, ElementClass<type>::getNbNodesPerInterpolationElement() * nb_dofs,
+ nb_points, nb_element);
auto shapes_begin = shapes_it;
if (filter_elements != empty_filter) {
nb_element = filter_elements.size();
}
auto nodes_per_element = getNodesPerElement<type>(mesh, nodes, ghost_type);
auto nodes_it = nodes_per_element->begin(mesh.getSpatialDimension(),
Mesh::getNbNodesPerElement(type));
auto nodes_begin = nodes_it;
+ auto rot_matrix_it =
+ make_view(rotation_matrices(type, ghost_type), nb_dofs, nb_dofs).begin();
+ auto rot_matrix_begin = rot_matrix_it;
for (UInt elem = 0; elem < nb_element; ++elem) {
if (filter_elements != empty_filter) {
shapes_it = shapes_begin + filter_elements(elem);
nodes_it = nodes_begin + filter_elements(elem);
+ rot_matrix_it = rot_matrix_begin + filter_elements(elem);
}
Tensor3<Real> & N = *shapes_it;
auto & real_coord = *nodes_it;
- ElementClass<type>::computeShapes(integration_points, real_coord, N);
+ auto & RDOFs = *rot_matrix_it;
+
+ Matrix<Real> T(N.size(1), N.size(1), 0);
+
+ for (UInt i = 0; i < nb_nodes_per_element; ++i) {
+ T.block(RDOFs, i * RDOFs.rows(), i * RDOFs.rows());
+ }
+
+ if (not mass) {
+ ElementClass<type>::computeShapes(integration_points, real_coord, T, N);
+ } else {
+ ElementClass<type>::computeShapesMass(integration_points, real_coord, T,
+ N);
+ }
if (filter_elements == empty_filter) {
++shapes_it;
++nodes_it;
}
}
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind>
template <ElementType type>
void ShapeStructural<kind>::precomputeRotationMatrices(
const Array<Real> & nodes, GhostType ghost_type) {
AKANTU_DEBUG_IN();
const auto spatial_dimension = mesh.getSpatialDimension();
const auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
const auto nb_element = mesh.getNbElement(type, ghost_type);
const auto nb_dof = ElementClass<type>::getNbDegreeOfFreedom();
if (not this->rotation_matrices.exists(type, ghost_type)) {
this->rotation_matrices.alloc(0, nb_dof * nb_dof, type, ghost_type);
}
auto & rot_matrices = this->rotation_matrices(type, ghost_type);
rot_matrices.resize(nb_element);
Array<Real> x_el(0, spatial_dimension * nb_nodes_per_element);
FEEngine::extractNodalToElementField(mesh, nodes, x_el, type, ghost_type);
bool has_extra_normal = mesh.hasData<Real>("extra_normal", type, ghost_type);
Array<Real>::const_vector_iterator extra_normal;
if (has_extra_normal) {
extra_normal = mesh.getData<Real>("extra_normal", type, ghost_type)
.begin(spatial_dimension);
}
for (auto && tuple :
zip(make_view(x_el, spatial_dimension, nb_nodes_per_element),
make_view(rot_matrices, nb_dof, nb_dof))) {
// compute shape derivatives
auto & X = std::get<0>(tuple);
auto & R = std::get<1>(tuple);
if (has_extra_normal) {
ElementClass<type>::computeRotationMatrix(R, X, *extra_normal);
++extra_normal;
} else {
ElementClass<type>::computeRotationMatrix(
R, X, Vector<Real>(spatial_dimension));
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind>
template <ElementType type>
void ShapeStructural<kind>::precomputeShapesOnIntegrationPoints(
const Array<Real> & nodes, GhostType ghost_type) {
AKANTU_DEBUG_IN();
const auto & natural_coords = integration_points(type, ghost_type);
auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
auto nb_points = integration_points(type, ghost_type).cols();
auto nb_element = mesh.getNbElement(type, ghost_type);
auto nb_dof = ElementClass<type>::getNbDegreeOfFreedom();
const auto dim = ElementClass<type>::getSpatialDimension();
+ const auto spatial_dimension = mesh.getSpatialDimension();
+ const auto natural_spatial_dimension =
+ ElementClass<type>::getNaturalSpaceDimension();
auto itp_type = FEEngine::getInterpolationType(type);
if (not shapes.exists(itp_type, ghost_type)) {
auto size_of_shapes = this->getShapeSize(type);
this->shapes.alloc(0, size_of_shapes, itp_type, ghost_type);
}
+ auto & rot_matrices = this->rotation_matrices(type, ghost_type);
auto & shapes_ = this->shapes(itp_type, ghost_type);
shapes_.resize(nb_element * nb_points);
auto nodes_per_element = getNodesPerElement<type>(mesh, nodes, ghost_type);
for (auto && tuple :
zip(make_view(shapes_, nb_dof, nb_dof * nb_nodes_per_element, nb_points),
- make_view(*nodes_per_element, dim, nb_nodes_per_element))) {
+ make_view(*nodes_per_element, dim, nb_nodes_per_element),
+ make_view(rot_matrices, nb_dof, nb_dof))) {
auto & N = std::get<0>(tuple);
- auto & real_coord = std::get<1>(tuple);
- ElementClass<type>::computeShapes(natural_coords, real_coord, N);
+ auto & X = std::get<1>(tuple);
+ auto & RDOFs = std::get<2>(tuple);
+
+ Matrix<Real> T(N.size(1), N.size(1), 0);
+
+ for (UInt i = 0; i < nb_nodes_per_element; ++i) {
+ T.block(RDOFs, i * RDOFs.rows(), i * RDOFs.rows());
+ }
+
+ auto R = RDOFs.block(0, 0, spatial_dimension, spatial_dimension);
+ // Rotate to local basis
+ auto x =
+ (R * X).block(0, 0, natural_spatial_dimension, nb_nodes_per_element);
+
+ ElementClass<type>::computeShapes(natural_coords, x, T, N);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind>
template <ElementType type>
void ShapeStructural<kind>::precomputeShapeDerivativesOnIntegrationPoints(
const Array<Real> & nodes, GhostType ghost_type) {
AKANTU_DEBUG_IN();
const auto & natural_coords = integration_points(type, ghost_type);
const auto spatial_dimension = mesh.getSpatialDimension();
const auto natural_spatial_dimension =
ElementClass<type>::getNaturalSpaceDimension();
const auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
const auto nb_points = natural_coords.cols();
const auto nb_dof = ElementClass<type>::getNbDegreeOfFreedom();
const auto nb_element = mesh.getNbElement(type, ghost_type);
const auto nb_stress_components = ElementClass<type>::getNbStressComponents();
auto itp_type = FEEngine::getInterpolationType(type);
if (not this->shapes_derivatives.exists(itp_type, ghost_type)) {
auto size_of_shapesd = this->getShapeDerivativesSize(type);
this->shapes_derivatives.alloc(0, size_of_shapesd, itp_type, ghost_type);
}
auto & rot_matrices = this->rotation_matrices(type, ghost_type);
Array<Real> x_el(0, spatial_dimension * nb_nodes_per_element);
FEEngine::extractNodalToElementField(mesh, nodes, x_el, type, ghost_type);
auto & shapesd = this->shapes_derivatives(itp_type, ghost_type);
shapesd.resize(nb_element * nb_points);
for (auto && tuple :
zip(make_view(x_el, spatial_dimension, nb_nodes_per_element),
make_view(shapesd, nb_stress_components,
nb_nodes_per_element * nb_dof, nb_points),
make_view(rot_matrices, nb_dof, nb_dof))) {
// compute shape derivatives
auto & X = std::get<0>(tuple);
auto & B = std::get<1>(tuple);
auto & RDOFs = std::get<2>(tuple);
Tensor3<Real> dnds(natural_spatial_dimension,
ElementClass<type>::interpolation_property::dnds_columns,
B.size(2));
ElementClass<type>::computeDNDS(natural_coords, X, dnds);
Tensor3<Real> J(natural_spatial_dimension, natural_spatial_dimension,
natural_coords.cols());
// Computing the coordinates of the element in the natural space
auto R = RDOFs.block(0, 0, spatial_dimension, spatial_dimension);
Matrix<Real> T(B.size(1), B.size(1), 0);
for (UInt i = 0; i < nb_nodes_per_element; ++i) {
T.block(RDOFs, i * RDOFs.rows(), i * RDOFs.rows());
}
// Rotate to local basis
auto x =
(R * X).block(0, 0, natural_spatial_dimension, nb_nodes_per_element);
ElementClass<type>::computeJMat(natural_coords, x, J);
ElementClass<type>::computeShapeDerivatives(J, dnds, T, B);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind>
template <ElementType type>
void ShapeStructural<kind>::interpolateOnIntegrationPoints(
const Array<Real> & in_u, Array<Real> & out_uq, UInt nb_dof,
GhostType ghost_type, const Array<UInt> & filter_elements) const {
AKANTU_DEBUG_IN();
AKANTU_DEBUG_ASSERT(out_uq.getNbComponent() == nb_dof,
"The output array shape is not correct");
auto itp_type = FEEngine::getInterpolationType(type);
const auto & shapes_ = shapes(itp_type, ghost_type);
auto nb_element = mesh.getNbElement(type, ghost_type);
auto nb_nodes_per_element = ElementClass<type>::getNbNodesPerElement();
auto nb_quad_points_per_element = integration_points(type, ghost_type).cols();
Array<Real> u_el(0, nb_nodes_per_element * nb_dof);
FEEngine::extractNodalToElementField(mesh, in_u, u_el, type, ghost_type,
filter_elements);
auto nb_quad_points = nb_quad_points_per_element * u_el.size();
out_uq.resize(nb_quad_points);
auto out_it = out_uq.begin_reinterpret(nb_dof, 1, nb_quad_points_per_element,
u_el.size());
auto shapes_it =
shapes_.begin_reinterpret(nb_dof, nb_dof * nb_nodes_per_element,
nb_quad_points_per_element, nb_element);
auto u_it = u_el.begin_reinterpret(nb_dof * nb_nodes_per_element, 1,
nb_quad_points_per_element, u_el.size());
for_each_element(nb_element, filter_elements, [&](auto && el) {
auto & uq = *out_it;
const auto & u = *u_it;
auto N = Tensor3<Real>(shapes_it[el]);
for (auto && q : arange(uq.size(2))) {
auto uq_q = Matrix<Real>(uq(q));
auto u_q = Matrix<Real>(u(q));
auto N_q = Matrix<Real>(N(q));
uq_q.mul<false, false>(N_q, u_q);
}
++out_it;
++u_it;
});
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <ElementKind kind>
template <ElementType type>
void ShapeStructural<kind>::gradientOnIntegrationPoints(
const Array<Real> & in_u, Array<Real> & out_nablauq, UInt nb_dof,
GhostType ghost_type, const Array<UInt> & filter_elements) const {
AKANTU_DEBUG_IN();
auto itp_type = FEEngine::getInterpolationType(type);
const auto & shapesd = shapes_derivatives(itp_type, ghost_type);
auto nb_element = mesh.getNbElement(type, ghost_type);
auto element_dimension = ElementClass<type>::getSpatialDimension();
auto nb_quad_points_per_element = integration_points(type, ghost_type).cols();
auto nb_nodes_per_element = ElementClass<type>::getNbNodesPerElement();
Array<Real> u_el(0, nb_nodes_per_element * nb_dof);
FEEngine::extractNodalToElementField(mesh, in_u, u_el, type, ghost_type,
filter_elements);
auto nb_quad_points = nb_quad_points_per_element * u_el.size();
out_nablauq.resize(nb_quad_points);
auto out_it = out_nablauq.begin_reinterpret(
element_dimension, 1, nb_quad_points_per_element, u_el.size());
auto shapesd_it = shapesd.begin_reinterpret(
element_dimension, nb_dof * nb_nodes_per_element,
nb_quad_points_per_element, nb_element);
auto u_it = u_el.begin_reinterpret(nb_dof * nb_nodes_per_element, 1,
nb_quad_points_per_element, u_el.size());
for_each_element(nb_element, filter_elements, [&](auto && el) {
auto & nablau = *out_it;
const auto & u = *u_it;
auto B = Tensor3<Real>(shapesd_it[el]);
for (auto && q : arange(nablau.size(2))) {
auto nablau_q = Matrix<Real>(nablau(q));
auto u_q = Matrix<Real>(u(q));
auto B_q = Matrix<Real>(B(q));
nablau_q.mul<false, false>(B_q, u_q);
}
++out_it;
++u_it;
});
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <>
template <ElementType type>
void ShapeStructural<_ek_structural>::computeBtD(
const Array<Real> & Ds, Array<Real> & BtDs, GhostType ghost_type,
const Array<UInt> & filter_elements) const {
auto itp_type = ElementClassProperty<type>::interpolation_type;
auto nb_stress = ElementClass<type>::getNbStressComponents();
auto nb_dof_per_element = ElementClass<type>::getNbDegreeOfFreedom() *
mesh.getNbNodesPerElement(type);
const auto & shapes_derivatives =
this->shapes_derivatives(itp_type, ghost_type);
Array<Real> shapes_derivatives_filtered(0,
shapes_derivatives.getNbComponent());
auto && view = make_view(shapes_derivatives, nb_stress, nb_dof_per_element);
auto B_it = view.begin();
auto B_end = view.end();
if (filter_elements != empty_filter) {
FEEngine::filterElementalData(this->mesh, shapes_derivatives,
shapes_derivatives_filtered, type, ghost_type,
filter_elements);
auto && view =
make_view(shapes_derivatives_filtered, nb_stress, nb_dof_per_element);
B_it = view.begin();
B_end = view.end();
}
for (auto && values : zip(range(B_it, B_end), make_view(Ds, nb_stress),
make_view(BtDs, BtDs.getNbComponent()))) {
const auto & B = std::get<0>(values);
const auto & D = std::get<1>(values);
auto & Bt_D = std::get<2>(values);
Bt_D.template mul<true>(B, D);
}
}
+/* -------------------------------------------------------------------------- */
+template <>
+template <ElementType type>
+void ShapeStructural<_ek_structural>::computeNtb(
+ const Array<Real> & bs, Array<Real> & Ntbs, GhostType ghost_type,
+ const Array<UInt> & filter_elements) const {
+ auto itp_type = ElementClassProperty<type>::interpolation_type;
+
+ auto nb_dof = ElementClass<type>::getNbDegreeOfFreedom();
+ auto nb_nodes_per_element = mesh.getNbNodesPerElement(type);
+
+ const auto & shapes = this->shapes(itp_type, ghost_type);
+
+ Array<Real> shapes_filtered(0, shapes.getNbComponent());
+ auto && view = make_view(shapes, nb_dof, nb_dof * nb_nodes_per_element);
+ auto N_it = view.begin();
+ auto N_end = view.end();
+
+ if (filter_elements != empty_filter) {
+ FEEngine::filterElementalData(this->mesh, shapes, shapes_filtered, type,
+ ghost_type, filter_elements);
+ auto && view =
+ make_view(shapes_filtered, nb_dof, nb_dof * nb_nodes_per_element);
+ N_it = view.begin();
+ N_end = view.end();
+ }
+
+ for (auto && values : zip(range(N_it, N_end), make_view(bs, nb_dof),
+ make_view(Ntbs, nb_dof * nb_nodes_per_element))) {
+ const auto & N = std::get<0>(values);
+ const auto & b = std::get<1>(values);
+ auto & Nt_b = std::get<2>(values);
+ Nt_b.template mul<true>(N, b);
+ }
+}
+
} // namespace akantu
#endif /* AKANTU_SHAPE_STRUCTURAL_INLINE_IMPL_HH_ */
diff --git a/src/io/dumper/dumper_material_padders.hh b/src/io/dumper/dumper_material_padders.hh
index b40d2df1b..7ee640f0d 100644
--- a/src/io/dumper/dumper_material_padders.hh
+++ b/src/io/dumper/dumper_material_padders.hh
@@ -1,320 +1,319 @@
/**
* @file dumper_material_padders.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
* @author Marco Vocialta <marco.vocialta@epfl.ch>
*
* @date creation: Tue Sep 02 2014
* @date last modification: Wed Nov 29 2017
*
* @brief Material padders for plane stress/ plane strain
*
*
* Copyright (©) 2014-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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef AKANTU_DUMPER_MATERIAL_PADDERS_HH_
#define AKANTU_DUMPER_MATERIAL_PADDERS_HH_
/* -------------------------------------------------------------------------- */
#include "dumper_padding_helper.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
namespace dumpers {
/* --------------------------------------------------------------------------
*/
class MaterialFunctor {
/* ------------------------------------------------------------------------
*/
/* Constructors/Destructors */
/* ------------------------------------------------------------------------
*/
public:
MaterialFunctor(const SolidMechanicsModel & model)
: model(model), material_index(model.getMaterialByElement()),
- nb_data_per_element("nb_data_per_element", model.getID(),
- model.getMemoryID()),
+ nb_data_per_element("nb_data_per_element", model.getID()),
spatial_dimension(model.getSpatialDimension()) {}
/* ------------------------------------------------------------------------
*/
/* Methods */
/* ------------------------------------------------------------------------
*/
/// return the material from the global element index
const Material & getMaterialFromGlobalIndex(Element global_index) {
UInt index = global_index.element;
UInt material_id = material_index(global_index.type)(index);
const Material & material = model.getMaterial(material_id);
return material;
}
/// return the type of the element from global index
ElementType
getElementTypeFromGlobalIndex( // NOLINT(readability-convert-member-functions-to-static)
Element global_index) {
return global_index.type;
}
protected:
/* ------------------------------------------------------------------------
*/
/* Class Members */
/* ------------------------------------------------------------------------
*/
/// all material padders probably need access to solid mechanics model
const SolidMechanicsModel & model;
/// they also need an access to the map from global ids to material id and
/// local ids
const ElementTypeMapArray<UInt> & material_index;
/// the number of data per element
const ElementTypeMapArray<UInt> nb_data_per_element;
UInt spatial_dimension;
};
/* --------------------------------------------------------------------------
*/
template <class T, class R>
class MaterialPadder : public MaterialFunctor,
public PadderGeneric<Vector<T>, R> {
public:
MaterialPadder(const SolidMechanicsModel & model)
: MaterialFunctor(model) {}
};
/* --------------------------------------------------------------------------
*/
template <UInt spatial_dimension>
class StressPadder : public MaterialPadder<Real, Matrix<Real>> {
public:
StressPadder(const SolidMechanicsModel & model)
: MaterialPadder<Real, Matrix<Real>>(model) {
this->setPadding(3, 3);
}
inline Matrix<Real> func(const Vector<Real> & in,
Element global_element_id) override {
UInt nrows = spatial_dimension;
UInt ncols = in.size() / nrows;
UInt nb_data = in.size() / (nrows * nrows);
Matrix<Real> stress = this->pad(in, nrows, ncols, nb_data);
const Material & material =
this->getMaterialFromGlobalIndex(global_element_id);
bool plane_strain = true;
if (spatial_dimension == 2) {
plane_strain = !((bool)material.getParam("Plane_Stress"));
}
if (plane_strain) {
Real nu = material.getParam("nu");
for (UInt d = 0; d < nb_data; ++d) {
stress(2, 2 + 3 * d) =
nu * (stress(0, 0 + 3 * d) + stress(1, 1 + 3 * d));
}
}
return stress;
}
UInt getDim() override { return 9; };
UInt getNbComponent(UInt /*old_nb_comp*/) override {
return this->getDim();
};
};
/* --------------------------------------------------------------------------
*/
template <UInt spatial_dimension>
class StrainPadder : public MaterialFunctor,
public PadderGeneric<Matrix<Real>, Matrix<Real>> {
public:
StrainPadder(const SolidMechanicsModel & model) : MaterialFunctor(model) {
this->setPadding(3, 3);
}
inline Matrix<Real> func(const Matrix<Real> & in,
Element global_element_id) override {
UInt nrows = spatial_dimension;
UInt nb_data = in.size() / (nrows * nrows);
Matrix<Real> strain = this->pad(in, nb_data);
const Material & material =
this->getMaterialFromGlobalIndex(global_element_id);
bool plane_stress = material.getParam("Plane_Stress");
if (plane_stress) {
Real nu = material.getParam("nu");
for (UInt d = 0; d < nb_data; ++d) {
strain(2, 2 + 3 * d) =
nu / (nu - 1) * (strain(0, 0 + 3 * d) + strain(1, 1 + 3 * d));
}
}
return strain;
}
UInt getDim() override { return 9; };
UInt getNbComponent(UInt /*old_nb_comp*/) override {
return this->getDim();
};
};
/* --------------------------------------------------------------------------
*/
template <bool green_strain>
class ComputeStrain : public MaterialFunctor,
public ComputeFunctor<Vector<Real>, Matrix<Real>> {
public:
ComputeStrain(const SolidMechanicsModel & model) : MaterialFunctor(model) {}
inline Matrix<Real> func(const Vector<Real> & in,
Element /*global_element_id*/) override {
UInt nrows = spatial_dimension;
UInt ncols = in.size() / nrows;
UInt nb_data = in.size() / (nrows * nrows);
Matrix<Real> ret_all_strain(nrows, ncols);
Tensor3<Real> all_grad_u(in.storage(), nrows, nrows, nb_data);
Tensor3<Real> all_strain(ret_all_strain.storage(), nrows, nrows, nb_data);
for (UInt d = 0; d < nb_data; ++d) {
Matrix<Real> grad_u = all_grad_u(d);
Matrix<Real> strain = all_strain(d);
if (spatial_dimension == 2) {
if (green_strain) {
Material::gradUToE<2>(grad_u, strain);
} else {
Material::gradUToEpsilon<2>(grad_u, strain);
}
} else if (spatial_dimension == 3) {
if (green_strain) {
Material::gradUToE<3>(grad_u, strain);
} else {
Material::gradUToEpsilon<3>(grad_u, strain);
}
}
}
return ret_all_strain;
}
UInt getDim() override { return spatial_dimension * spatial_dimension; };
UInt getNbComponent(UInt /*old_nb_comp*/) override {
return this->getDim();
};
};
/* --------------------------------------------------------------------------
*/
template <bool green_strain>
class ComputePrincipalStrain
: public MaterialFunctor,
public ComputeFunctor<Vector<Real>, Matrix<Real>> {
public:
ComputePrincipalStrain(const SolidMechanicsModel & model)
: MaterialFunctor(model) {}
inline Matrix<Real> func(const Vector<Real> & in,
Element /*global_element_id*/) override {
UInt nrows = spatial_dimension;
UInt nb_data = in.size() / (nrows * nrows);
Matrix<Real> ret_all_strain(nrows, nb_data);
Tensor3<Real> all_grad_u(in.storage(), nrows, nrows, nb_data);
Matrix<Real> strain(nrows, nrows);
for (UInt d = 0; d < nb_data; ++d) {
Matrix<Real> grad_u = all_grad_u(d);
if (spatial_dimension == 2) {
if (green_strain) {
Material::gradUToE<2>(grad_u, strain);
} else {
Material::gradUToEpsilon<2>(grad_u, strain);
}
} else if (spatial_dimension == 3) {
if (green_strain) {
Material::gradUToE<3>(grad_u, strain);
} else {
Material::gradUToEpsilon<3>(grad_u, strain);
}
}
Vector<Real> principal_strain(ret_all_strain(d));
strain.eig(principal_strain);
}
return ret_all_strain;
}
UInt getDim() override { return spatial_dimension; };
UInt getNbComponent(UInt /*old_nb_comp*/) override {
return this->getDim();
};
};
/* --------------------------------------------------------------------------
*/
class ComputeVonMisesStress
: public MaterialFunctor,
public ComputeFunctor<Vector<Real>, Vector<Real>> {
public:
ComputeVonMisesStress(const SolidMechanicsModel & model)
: MaterialFunctor(model) {}
inline Vector<Real> func(const Vector<Real> & in,
Element /*global_element_id*/) override {
UInt nrows = spatial_dimension;
UInt nb_data = in.size() / (nrows * nrows);
Vector<Real> von_mises_stress(nb_data);
Matrix<Real> deviatoric_stress(3, 3);
for (UInt d = 0; d < nb_data; ++d) {
Matrix<Real> cauchy_stress(in.storage() + d * nrows * nrows, nrows,
nrows);
von_mises_stress(d) = Material::stressToVonMises(cauchy_stress);
}
return von_mises_stress;
}
UInt getDim() override { return 1; };
UInt getNbComponent(UInt /*old_nb_comp*/) override {
return this->getDim();
};
};
/* --------------------------------------------------------------------------
*/
} // namespace dumpers
} // namespace akantu
#endif /* AKANTU_DUMPER_MATERIAL_PADDERS_HH_ */
diff --git a/src/mesh/element_group.cc b/src/mesh/element_group.cc
index b9dc383a0..4de478fbb 100644
--- a/src/mesh/element_group.cc
+++ b/src/mesh/element_group.cc
@@ -1,213 +1,213 @@
/**
* @file element_group.cc
*
* @author Dana Christen <dana.christen@gmail.com>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
* @author Marco Vocialta <marco.vocialta@epfl.ch>
*
* @date creation: Wed Nov 13 2013
* @date last modification: Mon Jan 22 2018
*
* @brief Stores information relevent to the notion of domain boundary and
* surfaces.
*
*
* Copyright (©) 2014-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#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 <algorithm>
#include <iterator>
#include <sstream>
#include "element_group.hh"
#if defined(AKANTU_USE_IOHELPER)
#include "dumper_iohelper_paraview.hh"
#endif
namespace akantu {
/* -------------------------------------------------------------------------- */
ElementGroup::ElementGroup(const std::string & group_name, const Mesh & mesh,
NodeGroup & node_group, UInt dimension,
- const std::string & id, const MemoryID & mem_id)
- : Memory(id, mem_id), mesh(mesh), name(group_name),
- elements("elements", id, mem_id), node_group(node_group),
+ const std::string & id)
+ : mesh(mesh), name(group_name),
+ elements("elements", id), node_group(node_group),
dimension(dimension) {
AKANTU_DEBUG_IN();
#if defined(AKANTU_USE_IOHELPER)
this->registerDumper<DumperParaview>("paraview_" + group_name, group_name,
true);
this->addDumpFilteredMesh(mesh, elements, node_group.getNodes(),
_all_dimensions);
#endif
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 Array<UInt> & other_elem_list =
other_group.elements(type, ghost_type);
UInt nb_other_elem = other_elem_list.size();
Array<UInt> * elem_list;
UInt nb_elem = 0;
/// create current type if doesn't exists, otherwise get information
if (elements.exists(type, ghost_type)) {
elem_list = &elements(type, ghost_type);
nb_elem = elem_list->size();
} else {
elem_list = &(elements.alloc(0, 1, type, ghost_type));
}
/// 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());
Array<UInt>::iterator<> end =
std::unique(elem_list->begin(), elem_list->end());
elem_list->resize(end - elem_list->begin());
}
}
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)) {
Array<UInt> & els = elements(type, ghost_type);
std::sort(els.begin(), els.end());
Array<UInt>::iterator<> end = std::unique(els.begin(), els.end());
els.resize(end - els.begin());
}
}
node_group.optimize();
}
/* -------------------------------------------------------------------------- */
void ElementGroup::fillFromNodeGroup() {
CSR<Element> node_to_elem;
MeshUtils::buildNode2Elements(this->mesh, node_to_elem, this->dimension);
std::set<Element> seen;
Array<UInt>::const_iterator<> itn = this->node_group.begin();
Array<UInt>::const_iterator<> endn = this->node_group.end();
for (; itn != endn; ++itn) {
CSR<Element>::iterator ite = node_to_elem.begin(*itn);
CSR<Element>::iterator ende = node_to_elem.end(*itn);
for (; ite != ende; ++ite) {
const Element & elem = *ite;
if (this->dimension != _all_dimensions &&
this->dimension != Mesh::getSpatialDimension(elem.type)) {
continue;
}
if (seen.find(elem) != seen.end()) {
continue;
}
UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(elem.type);
Array<UInt>::const_iterator<Vector<UInt>> conn_it =
this->mesh.getConnectivity(elem.type, elem.ghost_type)
.begin(nb_nodes_per_element);
const Vector<UInt> & conn = conn_it[elem.element];
UInt count = 0;
for (UInt n = 0; n < conn.size(); ++n) {
count +=
(this->node_group.getNodes().find(conn(n)) != UInt(-1) ? 1 : 0);
}
if (count == nb_nodes_per_element) {
this->add(elem);
}
seen.insert(elem);
}
}
this->optimize();
}
/* -------------------------------------------------------------------------- */
void ElementGroup::addDimension(UInt dimension) {
this->dimension = std::max(dimension, this->dimension);
}
/* -------------------------------------------------------------------------- */
} // namespace akantu
diff --git a/src/mesh/element_group.hh b/src/mesh/element_group.hh
index b1a8eaa48..b8dab597a 100644
--- a/src/mesh/element_group.hh
+++ b/src/mesh/element_group.hh
@@ -1,202 +1,203 @@
/**
* @file element_group.hh
*
* @author Dana Christen <dana.christen@gmail.com>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri May 03 2013
* @date last modification: Wed Nov 08 2017
*
* @brief Stores information relevent to the notion of domain boundary and
* surfaces.
*
*
* Copyright (©) 2014-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_common.hh"
-#include "aka_memory.hh"
#include "dumpable.hh"
#include "element_type_map.hh"
#include "node_group.hh"
/* -------------------------------------------------------------------------- */
#include <set>
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_ELEMENT_GROUP_HH_
#define AKANTU_ELEMENT_GROUP_HH_
namespace akantu {
class Mesh;
class Element;
} // namespace akantu
namespace akantu {
/* -------------------------------------------------------------------------- */
-class ElementGroup : private Memory, public Dumpable {
+class ElementGroup : public Dumpable {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
ElementGroup(const std::string & name, const Mesh & mesh,
NodeGroup & node_group, UInt dimension = _all_dimensions,
- const std::string & id = "element_group",
- const MemoryID & mem_id = 0);
+ const std::string & id = "element_group");
ElementGroup(const ElementGroup & /*unused*/);
/* ------------------------------------------------------------------------ */
/* Type definitions */
/* ------------------------------------------------------------------------ */
public:
using ElementList = ElementTypeMapArray<UInt>;
using NodeList = Array<UInt>;
/* ------------------------------------------------------------------------ */
/* Element iterator */
/* ------------------------------------------------------------------------ */
using type_iterator = ElementList::type_iterator;
[[deprecated("Use elementTypes instead")]] inline type_iterator
firstType(UInt dim = _all_dimensions, GhostType ghost_type = _not_ghost,
ElementKind kind = _ek_regular) const;
[[deprecated("Use elementTypes instead")]] inline type_iterator
lastType(UInt dim = _all_dimensions, GhostType ghost_type = _not_ghost,
ElementKind kind = _ek_regular) const;
template <typename... pack>
inline decltype(auto) elementTypes(pack &&... _pack) const {
return elements.elementTypes(_pack...);
}
using const_element_iterator = Array<UInt>::const_iterator<UInt>;
inline const_element_iterator begin(ElementType type,
GhostType ghost_type = _not_ghost) const;
inline const_element_iterator end(ElementType type,
GhostType ghost_type = _not_ghost) const;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// empty the element group
void clear();
void clear(ElementType type, GhostType ghost_type = _not_ghost);
bool empty() const __attribute__((warn_unused_result));
/// append another group to this group
/// BE CAREFUL: it doesn't conserve the element order
void append(const ElementGroup & other_group);
/// add an element to the group. By default the it does not add the nodes to
/// the group
inline void add(const Element & el, bool add_nodes = false,
bool check_for_duplicate = true);
/// \todo fix the default for add_nodes : make it coherent with the other
/// method
inline void add(ElementType type, UInt element,
GhostType ghost_type = _not_ghost, bool add_nodes = true,
bool check_for_duplicate = true);
inline void addNode(UInt node_id, bool check_for_duplicate = true);
inline void removeNode(UInt node_id);
/// function to print the contain of the class
virtual void printself(std::ostream & stream, int indent = 0) const;
/// fill the elements based on the underlying node group.
virtual void fillFromNodeGroup();
// sort and remove duplicated values
void optimize();
/// change the dimension if needed
void addDimension(UInt dimension);
private:
inline void addElement(ElementType elem_type, UInt elem_id,
GhostType ghost_type);
friend class GroupManager;
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
const Array<UInt> & getElements(ElementType type,
GhostType ghost_type = _not_ghost) const;
AKANTU_GET_MACRO(Elements, elements, const ElementTypeMapArray<UInt> &);
AKANTU_GET_MACRO_NOT_CONST(Elements, elements, ElementTypeMapArray<UInt> &);
template <class... Args> auto size(Args &&... pack) const {
return elements.size(std::forward<Args>(pack)...);
}
+ decltype(auto) getElementsIterable(ElementType type,
+ GhostType ghost_type = _not_ghost) const;
+
// AKANTU_GET_MACRO(Nodes, node_group.getNodes(), const Array<UInt> &);
AKANTU_GET_MACRO(NodeGroup, node_group, const NodeGroup &);
AKANTU_GET_MACRO_NOT_CONST(NodeGroup, node_group, NodeGroup &);
AKANTU_GET_MACRO(Dimension, dimension, UInt);
AKANTU_GET_MACRO(Name, name, std::string);
inline UInt getNbNodes() const;
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
private:
/// Mesh to which this group belongs
const Mesh & mesh;
/// name of the group
std::string name;
/// list of elements composing the group
ElementList elements;
/// sub list of nodes which are composing the elements
NodeGroup & node_group;
/// group dimension
UInt dimension{_all_dimensions};
/// empty arry for the iterator to work when an element type not present
Array<UInt> empty_elements;
};
/// standard output stream operator
inline std::ostream & operator<<(std::ostream & stream,
const ElementGroup & _this) {
_this.printself(stream);
return stream;
}
} // namespace akantu
#include "element.hh"
#include "element_group_inline_impl.hh"
#endif /* AKANTU_ELEMENT_GROUP_HH_ */
diff --git a/src/mesh/element_group_inline_impl.hh b/src/mesh/element_group_inline_impl.hh
index d45a3ffb9..9ade23bc3 100644
--- a/src/mesh/element_group_inline_impl.hh
+++ b/src/mesh/element_group_inline_impl.hh
@@ -1,143 +1,146 @@
/**
* @file element_group_inline_impl.hh
*
* @author Dana Christen <dana.christen@gmail.com>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Wed Nov 13 2013
* @date last modification: Sun Aug 13 2017
*
* @brief Stores information relevent to the notion of domain boundary and
* surfaces.
*
*
* Copyright (©) 2014-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "element_group.hh"
#include "mesh.hh"
-
+#include "aka_iterators.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_ELEMENT_GROUP_INLINE_IMPL_HH_
#define AKANTU_ELEMENT_GROUP_INLINE_IMPL_HH_
namespace akantu {
/* -------------------------------------------------------------------------- */
inline void ElementGroup::add(const Element & el, bool add_nodes,
bool check_for_duplicate) {
this->add(el.type, el.element, el.ghost_type, add_nodes, check_for_duplicate);
}
/* -------------------------------------------------------------------------- */
inline void ElementGroup::add(ElementType type, UInt element,
GhostType ghost_type, bool add_nodes,
bool check_for_duplicate) {
addElement(type, element, ghost_type);
if (add_nodes) {
Array<UInt>::const_vector_iterator it =
mesh.getConnectivity(type, ghost_type)
.begin(mesh.getNbNodesPerElement(type)) +
element;
const Vector<UInt> & conn = *it;
for (UInt i = 0; i < conn.size(); ++i) {
addNode(conn[i], check_for_duplicate);
}
}
}
/* -------------------------------------------------------------------------- */
inline void ElementGroup::addNode(UInt node_id, bool check_for_duplicate) {
node_group.add(node_id, check_for_duplicate);
}
/* -------------------------------------------------------------------------- */
inline void ElementGroup::removeNode(UInt node_id) {
node_group.remove(node_id);
}
/* -------------------------------------------------------------------------- */
-inline void ElementGroup::addElement(ElementType elem_type,
- UInt elem_id,
+inline void ElementGroup::addElement(ElementType elem_type, UInt elem_id,
GhostType ghost_type) {
if (!(elements.exists(elem_type, ghost_type))) {
elements.alloc(0, 1, elem_type, ghost_type);
}
elements(elem_type, ghost_type).push_back(elem_id);
this->dimension = UInt(
std::max(Int(this->dimension), Int(mesh.getSpatialDimension(elem_type))));
}
/* -------------------------------------------------------------------------- */
inline UInt ElementGroup::getNbNodes() const { return node_group.size(); }
/* -------------------------------------------------------------------------- */
inline ElementGroup::type_iterator
ElementGroup::firstType(UInt dim, GhostType ghost_type,
ElementKind kind) const {
return elements.elementTypes(dim, ghost_type, kind).begin();
}
/* -------------------------------------------------------------------------- */
inline ElementGroup::type_iterator
-ElementGroup::lastType(UInt dim, GhostType ghost_type,
- ElementKind kind) const {
+ElementGroup::lastType(UInt dim, GhostType ghost_type, ElementKind kind) const {
return elements.elementTypes(dim, ghost_type, kind).end();
}
/* -------------------------------------------------------------------------- */
inline ElementGroup::const_element_iterator
-ElementGroup::begin(ElementType type,
- GhostType ghost_type) const {
+ElementGroup::begin(ElementType type, GhostType ghost_type) const {
if (elements.exists(type, ghost_type)) {
return elements(type, ghost_type).begin();
}
return empty_elements.begin();
}
/* -------------------------------------------------------------------------- */
inline ElementGroup::const_element_iterator
-ElementGroup::end(ElementType type,
- GhostType ghost_type) const {
+ElementGroup::end(ElementType type, GhostType ghost_type) const {
if (elements.exists(type, ghost_type)) {
return elements(type, ghost_type).end();
}
return empty_elements.end();
}
/* -------------------------------------------------------------------------- */
inline const Array<UInt> &
-ElementGroup::getElements(ElementType type,
- GhostType ghost_type) const {
+ElementGroup::getElements(ElementType type, GhostType ghost_type) const {
if (elements.exists(type, ghost_type)) {
return elements(type, ghost_type);
}
return empty_elements;
}
/* -------------------------------------------------------------------------- */
+inline decltype(auto)
+ElementGroup::getElementsIterable(ElementType type,
+ GhostType ghost_type) const {
+ return make_transform_adaptor(this->elements(type, ghost_type),
+ [type, ghost_type](auto && el) {
+ return Element{type, el, ghost_type};
+ });
+}
} // namespace akantu
#endif /* AKANTU_ELEMENT_GROUP_INLINE_IMPL_HH_ */
diff --git a/src/mesh/element_type_map.hh b/src/mesh/element_type_map.hh
index 85f4f1de8..af04ab03d 100644
--- a/src/mesh/element_type_map.hh
+++ b/src/mesh/element_type_map.hh
@@ -1,491 +1,490 @@
/**
* @file element_type_map.hh
*
* @author Lucas Frerot <lucas.frerot@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Wed Aug 31 2011
* @date last modification: Tue Feb 20 2018
*
* @brief storage class by element type
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_array.hh"
-#include "aka_memory.hh"
#include "aka_named_argument.hh"
#include "element.hh"
/* -------------------------------------------------------------------------- */
+#include <map>
+/* -------------------------------------------------------------------------- */
#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 Stored, typename SupportType = ElementType>
class ElementTypeMap;
/* -------------------------------------------------------------------------- */
/* ElementTypeMapBase */
/* -------------------------------------------------------------------------- */
/// Common non templated base class for the ElementTypeMap class
class ElementTypeMapBase {
public:
virtual ~ElementTypeMapBase() = default;
};
/* -------------------------------------------------------------------------- */
/* ElementTypeMap */
/* -------------------------------------------------------------------------- */
template <class Stored, typename SupportType>
class ElementTypeMap : public ElementTypeMapBase {
public:
ElementTypeMap();
~ElementTypeMap() override;
inline static std::string printType(const SupportType & type,
GhostType ghost_type);
/*! 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 bool exists(const SupportType & type,
GhostType ghost_type = _not_ghost) const;
/*! 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 const Stored &
- operator()(const SupportType & type,
- GhostType ghost_type = _not_ghost) const;
+ inline const Stored & operator()(const SupportType & type,
+ GhostType ghost_type = _not_ghost) const;
/*! 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 Stored & operator()(const SupportType & type,
GhostType ghost_type = _not_ghost);
/*! 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 <typename U>
inline Stored & operator()(U && insertee, const SupportType & type,
GhostType ghost_type = _not_ghost);
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<SupportType, Stored>;
/// helper class to use in range for constructions
class type_iterator
: private std::iterator<std::forward_iterator_tag, const SupportType> {
public:
using value_type = const SupportType;
using pointer = const SupportType *;
using reference = const SupportType &;
protected:
using DataMapIterator =
typename ElementTypeMap<Stored>::DataMap::const_iterator;
public:
type_iterator(DataMapIterator & list_begin, DataMapIterator & list_end,
UInt dim, ElementKind ek);
type_iterator(const type_iterator & it);
type_iterator() = default;
inline reference operator*();
inline reference operator*() const;
inline type_iterator & operator++();
type_iterator operator++(int);
inline bool operator==(const type_iterator & other) const;
inline bool operator!=(const type_iterator & other) const;
type_iterator & operator=(const type_iterator & it);
private:
DataMapIterator list_begin;
DataMapIterator list_end;
UInt dim;
ElementKind kind;
};
/// helper class to use in range for constructions
class ElementTypesIteratorHelper {
public:
using Container = ElementTypeMap<Stored, SupportType>;
using iterator = typename Container::type_iterator;
ElementTypesIteratorHelper(const Container & container, UInt dim,
GhostType ghost_type, ElementKind kind)
: container(std::cref(container)), dim(dim), ghost_type(ghost_type),
kind(kind) {}
template <typename... pack>
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;
ElementTypesIteratorHelper &
operator=(const ElementTypesIteratorHelper &) = default;
ElementTypesIteratorHelper &
operator=(ElementTypesIteratorHelper &&) noexcept = default;
iterator begin();
iterator end();
private:
std::reference_wrapper<const Container> container;
UInt dim;
GhostType ghost_type;
ElementKind kind;
};
private:
ElementTypesIteratorHelper
elementTypesImpl(UInt dim = _all_dimensions,
GhostType ghost_type = _not_ghost,
ElementKind kind = _ek_not_defined) const;
template <typename... pack>
ElementTypesIteratorHelper
elementTypesImpl(const use_named_args_t & /*unused*/, pack &&... _pack) const;
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 <typename... pack>
std::enable_if_t<are_named_argument<pack...>::value,
ElementTypesIteratorHelper>
elementTypes(pack &&... _pack) const {
return elementTypesImpl(use_named_args,
std::forward<decltype(_pack)>(_pack)...);
}
template <typename... pack>
std::enable_if_t<not are_named_argument<pack...>::value,
ElementTypesIteratorHelper>
elementTypes(pack &&... _pack) const {
return elementTypesImpl(std::forward<decltype(_pack)>(_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 type_iterator
firstType(UInt dim = _all_dimensions, GhostType ghost_type = _not_ghost,
ElementKind kind = _ek_not_defined) const;
/*! 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 type_iterator
lastType(UInt dim = _all_dimensions, GhostType ghost_type = _not_ghost,
ElementKind kind = _ek_not_defined) const;
/*! 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 DataMap & getData(GhostType ghost_type);
/*! 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 const DataMap & getData(GhostType ghost_type) const;
/* ------------------------------------------------------------------------ */
protected:
DataMap data;
DataMap ghost_data;
};
/* -------------------------------------------------------------------------- */
/* Some typedefs */
/* -------------------------------------------------------------------------- */
template <typename T, typename SupportType>
-class ElementTypeMapArray : public ElementTypeMap<Array<T> *, SupportType>,
- public Memory {
+class ElementTypeMapArray
+ : public ElementTypeMap<std::unique_ptr<Array<T>>, SupportType> {
public:
using type = T;
using array_type = Array<T>;
protected:
- using parent = ElementTypeMap<Array<T> *, SupportType>;
+ using parent = ElementTypeMap<std::unique_ptr<Array<T>>, 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*/);
/// explicit copy
void copy(const ElementTypeMapArray & other);
/*! Constructor
* @param id optional: identifier (string)
* @param parent_id optional: parent identifier. for organizational purposes
* only
- * @param memory_id optional: choose a specific memory, defaults to memory 0
*/
ElementTypeMapArray(const ID & id = "by_element_type_array",
- const ID & parent_id = "no_parent",
- const MemoryID & memory_id = 0)
- : parent(), Memory(parent_id + ":" + id, memory_id), name(id){};
+ 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 Array<T> & alloc(UInt size, UInt nb_component,
- const SupportType & type,
- GhostType ghost_type,
+ const SupportType & type, GhostType ghost_type,
const T & default_value = T());
/*! 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
* @param default_value the default value to use to fill the array
*/
inline void alloc(UInt size, UInt nb_component, const 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 const Array<T> &
- operator()(const SupportType & type,
- GhostType ghost_type = _not_ghost) const;
+ inline const Array<T> & operator()(const SupportType & type,
+ GhostType ghost_type = _not_ghost) const;
/// access the data of an element, this combine the map and array accessor
inline const T & operator()(const Element & element,
UInt component = 0) const;
/// access the data of an element, this combine the map and array accessor
inline T & operator()(const Element & element, UInt component = 0);
/// 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;
/* 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 Array<T> & operator()(const SupportType & type,
GhostType ghost_type = _not_ghost);
/*! 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(const SupportType & type, GhostType ghost_type,
const Array<T> & 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<typename ST>
- inline void set(const ST & value);
-
+ template <typename ST> 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<UInt> & 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; }
+
ElementTypeMap<UInt>
- getNbComponents(UInt dim = _all_dimensions, GhostType requested_ghost_type = _not_ghost,
+ getNbComponents(UInt dim = _all_dimensions,
+ GhostType requested_ghost_type = _not_ghost,
ElementKind kind = _ek_not_defined) const {
ElementTypeMap<UInt> nb_components;
bool 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))
+ if ((not(ghost_type == requested_ghost_type)) and (not all_ghost_types)) {
continue;
+ }
for (auto & type : this->elementTypes(dim, ghost_type, kind)) {
UInt 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 <class Func>
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 <typename... pack>
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 <typename... pack>
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<T>
* @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 <typename... pack> UInt size(pack &&... _pack) const;
bool isNodal() const { return is_nodal; }
void isNodal(bool is_nodal) { this->is_nodal = is_nodal; }
private:
UInt sizeImpl(UInt spatial_dimension, GhostType ghost_type,
ElementKind kind) const;
+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<Real> by element type
using ElementTypeMapReal = ElementTypeMapArray<Real>;
/// to store data Array<Int> by element type
using ElementTypeMapInt = ElementTypeMapArray<Int>;
/// to store data Array<UInt> by element type
using ElementTypeMapUInt = ElementTypeMapArray<UInt, ElementType>;
-/// Map of data of type UInt stored in a mesh
-using UIntDataMap = std::map<std::string, Array<UInt> *>;
-using ElementTypeMapUIntDataMap = ElementTypeMap<UIntDataMap, ElementType>;
-
} // 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 a8c4a476d..2ed347b00 100644
--- a/src/mesh/element_type_map_tmpl.hh
+++ b/src/mesh/element_type_map_tmpl.hh
@@ -1,846 +1,849 @@
/**
* @file element_type_map_tmpl.hh
*
* @author Lucas Frerot <lucas.frerot@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Wed Aug 31 2011
* @date last modification: Tue Feb 20 2018
*
* @brief implementation of template functions of the ElementTypeMap and
* ElementTypeMapArray classes
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_static_if.hh"
#include "element_type_map.hh"
#include "mesh.hh"
/* -------------------------------------------------------------------------- */
#include "element_type_conversion.hh"
/* -------------------------------------------------------------------------- */
#include <functional>
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_ELEMENT_TYPE_MAP_TMPL_HH_
#define AKANTU_ELEMENT_TYPE_MAP_TMPL_HH_
namespace akantu {
/* -------------------------------------------------------------------------- */
/* ElementTypeMap */
/* -------------------------------------------------------------------------- */
template <class Stored, typename SupportType>
inline std::string
ElementTypeMap<Stored, SupportType>::printType(const SupportType & type,
GhostType ghost_type) {
std::stringstream sstr;
sstr << "(" << ghost_type << ":" << type << ")";
return sstr.str();
}
/* -------------------------------------------------------------------------- */
template <class Stored, typename SupportType>
inline bool
ElementTypeMap<Stored, SupportType>::exists(const SupportType & type,
GhostType ghost_type) const {
return this->getData(ghost_type).find(type) !=
this->getData(ghost_type).end();
}
/* -------------------------------------------------------------------------- */
template <class Stored, typename SupportType>
inline const Stored &
ElementTypeMap<Stored, SupportType>::operator()(const 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 <class Stored, typename SupportType>
inline Stored &
ElementTypeMap<Stored, SupportType>::operator()(const SupportType & type,
GhostType ghost_type) {
return this->getData(ghost_type)[type];
}
/* -------------------------------------------------------------------------- */
template <class Stored, typename SupportType>
template <typename U>
inline Stored & ElementTypeMap<Stored, SupportType>::operator()(
U && insertee, const 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<U>(insertee)));
it = res.first;
}
return it->second;
}
/* -------------------------------------------------------------------------- */
template <class Stored, typename SupportType>
inline typename ElementTypeMap<Stored, SupportType>::DataMap &
ElementTypeMap<Stored, SupportType>::getData(GhostType ghost_type) {
if (ghost_type == _not_ghost) {
return data;
}
return ghost_data;
}
/* -------------------------------------------------------------------------- */
template <class Stored, typename SupportType>
inline const typename ElementTypeMap<Stored, SupportType>::DataMap &
ElementTypeMap<Stored, SupportType>::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 <class Stored, typename SupportType>
void ElementTypeMap<Stored, SupportType>::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 <class Stored, typename SupportType>
ElementTypeMap<Stored, SupportType>::ElementTypeMap() = default;
/* -------------------------------------------------------------------------- */
template <class Stored, typename SupportType>
ElementTypeMap<Stored, SupportType>::~ElementTypeMap() = default;
/* -------------------------------------------------------------------------- */
/* ElementTypeMapArray */
/* -------------------------------------------------------------------------- */
template <typename T, typename SupportType>
void ElementTypeMapArray<T, SupportType>::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 <typename T, typename SupportType>
ElementTypeMapArray<T, SupportType>::ElementTypeMapArray(
const ElementTypeMapArray & other)
- : parent(), Memory(other.id + "_copy", other.memory_id),
- name(other.name + "_copy") {
+ : parent(), id(other.id + "_copy"), name(other.name + "_copy") {
this->copy(other);
}
/* -------------------------------------------------------------------------- */
template <typename T, typename SupportType>
inline Array<T> & ElementTypeMapArray<T, SupportType>::alloc(
UInt size, UInt nb_component, const SupportType & type,
GhostType ghost_type, const T & default_value) {
std::string ghost_id;
if (ghost_type == _ghost) {
ghost_id = ":ghost";
}
- Array<T> * tmp;
-
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;
- tmp = &(Memory::alloc<T>(id, size, nb_component, default_value));
- this->getData(ghost_type)[type] = tmp;
- } else {
- AKANTU_DEBUG_INFO(
- "The vector "
- << this->id << this->printType(type, ghost_type)
- << " already exists, it is resized instead of allocated.");
- tmp = it->second;
- it->second->resize(size);
+ this->getData(ghost_type)[type] =
+ std::make_unique<Array<T>>(size, nb_component, default_value, id);
+ return *(this->getData(ghost_type)[type]);
}
- return *tmp;
+ 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 <typename T, typename SupportType>
inline void
ElementTypeMapArray<T, SupportType>::alloc(UInt size, UInt nb_component,
const 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 <typename T, typename SupportType>
inline void ElementTypeMapArray<T, SupportType>::free() {
AKANTU_DEBUG_IN();
for (auto gt : ghost_types) {
auto & data = this->getData(gt);
- for (auto & pair : data) {
- dealloc(pair.second->getID());
- }
data.clear();
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <typename T, typename SupportType>
inline void ElementTypeMapArray<T, SupportType>::clear() {
for (auto gt : ghost_types) {
auto & data = this->getData(gt);
for (auto & vect : data) {
vect.second->clear();
}
}
}
/* -------------------------------------------------------------------------- */
template <typename T, typename SupportType>
inline bool ElementTypeMapArray<T, SupportType>::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 <typename T, typename SupportType>
template <typename ST>
inline void ElementTypeMapArray<T, SupportType>::set(const ST & value) {
for (auto gt : ghost_types) {
auto & data = this->getData(gt);
for (auto & vect : data) {
vect.second->set(value);
}
}
}
/* -------------------------------------------------------------------------- */
template <typename T, typename SupportType>
inline const Array<T> &
ElementTypeMapArray<T, SupportType>::operator()(const 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 <typename T, typename SupportType>
inline Array<T> &
ElementTypeMapArray<T, SupportType>::operator()(const 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 <typename T, typename SupportType>
inline void ElementTypeMapArray<T, SupportType>::setArray(
const SupportType & type, GhostType ghost_type, const Array<T> & 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<Array<T> &>(vect));
}
/* -------------------------------------------------------------------------- */
template <typename T, typename SupportType>
inline void ElementTypeMapArray<T, SupportType>::onElementsRemoved(
const ElementTypeMapArray<UInt> & new_numbering) {
for (auto gt : ghost_types) {
for (auto && type :
new_numbering.elementTypes(_all_dimensions, gt, _ek_not_defined)) {
auto support_type = convertType<ElementType, SupportType>(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_component = vect.getNbComponent();
Array<T> tmp(renumbering.size(), nb_component);
UInt new_size = 0;
for (UInt i = 0; i < vect.size(); ++i) {
UInt new_i = renumbering(i);
if (new_i != UInt(-1)) {
std::copy_n(vect.storage() + i * nb_component, nb_component,
tmp.storage() + new_i * nb_component);
++new_size;
}
}
tmp.resize(new_size);
vect.copy(tmp);
}
}
}
}
/* -------------------------------------------------------------------------- */
template <typename T, typename SupportType>
void ElementTypeMapArray<T, SupportType>::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 <class Stored, typename SupportType>
ElementTypeMap<Stored, SupportType>::type_iterator::type_iterator(
DataMapIterator & list_begin, DataMapIterator & list_end, UInt dim,
ElementKind ek)
: list_begin(list_begin), list_end(list_end), dim(dim), kind(ek) {}
/* -------------------------------------------------------------------------- */
template <class Stored, typename SupportType>
ElementTypeMap<Stored, SupportType>::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 <class Stored, typename SupportType>
typename ElementTypeMap<Stored, SupportType>::type_iterator &
ElementTypeMap<Stored, SupportType>::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 <class Stored, typename SupportType>
inline typename ElementTypeMap<Stored, SupportType>::type_iterator::reference
ElementTypeMap<Stored, SupportType>::type_iterator::operator*() {
return list_begin->first;
}
/* -------------------------------------------------------------------------- */
template <class Stored, typename SupportType>
inline typename ElementTypeMap<Stored, SupportType>::type_iterator::reference
ElementTypeMap<Stored, SupportType>::type_iterator::operator*() const {
return list_begin->first;
}
/* -------------------------------------------------------------------------- */
template <class Stored, typename SupportType>
inline typename ElementTypeMap<Stored, SupportType>::type_iterator &
ElementTypeMap<Stored, SupportType>::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 <class Stored, typename SupportType>
typename ElementTypeMap<Stored, SupportType>::type_iterator
ElementTypeMap<Stored, SupportType>::type_iterator::operator++(int) {
type_iterator tmp(*this);
operator++();
return tmp;
}
/* -------------------------------------------------------------------------- */
template <class Stored, typename SupportType>
inline bool ElementTypeMap<Stored, SupportType>::type_iterator::operator==(
const type_iterator & other) const {
return this->list_begin == other.list_begin;
}
/* -------------------------------------------------------------------------- */
template <class Stored, typename SupportType>
inline bool ElementTypeMap<Stored, SupportType>::type_iterator::operator!=(
const type_iterator & other) const {
return this->list_begin != other.list_begin;
}
/* -------------------------------------------------------------------------- */
template <class Stored, typename SupportType>
auto ElementTypeMap<Stored, SupportType>::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 <class Stored, typename SupportType>
auto ElementTypeMap<Stored, SupportType>::ElementTypesIteratorHelper::end()
-> iterator {
auto e = container.get().getData(ghost_type).end();
return iterator(e, e, dim, kind);
}
/* -------------------------------------------------------------------------- */
template <class Stored, typename SupportType>
auto ElementTypeMap<Stored, SupportType>::elementTypesImpl(
UInt dim, GhostType ghost_type, ElementKind kind) const
-> ElementTypesIteratorHelper {
return ElementTypesIteratorHelper(*this, dim, ghost_type, kind);
}
/* -------------------------------------------------------------------------- */
template <class Stored, typename SupportType>
template <typename... pack>
auto ElementTypeMap<Stored, SupportType>::elementTypesImpl(
const use_named_args_t & unused, pack &&... _pack) const
-> ElementTypesIteratorHelper {
return ElementTypesIteratorHelper(*this, unused, _pack...);
}
/* -------------------------------------------------------------------------- */
template <class Stored, typename SupportType>
inline auto ElementTypeMap<Stored, SupportType>::firstType(
UInt dim, GhostType ghost_type, ElementKind kind) const -> type_iterator {
return elementTypes(dim, ghost_type, kind).begin();
}
/* -------------------------------------------------------------------------- */
template <class Stored, typename SupportType>
inline auto ElementTypeMap<Stored, SupportType>::lastType(
UInt dim, GhostType ghost_type, ElementKind kind) const -> type_iterator {
typename DataMap::const_iterator e;
e = getData(ghost_type).end();
return typename ElementTypeMap<Stored, SupportType>::type_iterator(e, e, dim,
kind);
}
/* -------------------------------------------------------------------------- */
/// standard output stream operator
template <class Stored, typename SupportType>
inline std::ostream &
operator<<(std::ostream & stream,
const ElementTypeMap<Stored, SupportType> & _this) {
_this.printself(stream);
return stream;
}
/* -------------------------------------------------------------------------- */
class ElementTypeMapArrayInitializer {
protected:
using CompFunc = std::function<UInt(ElementType, GhostType)>;
public:
ElementTypeMapArrayInitializer(const CompFunc & comp_func,
UInt 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 UInt nbComponent(ElementType type) const {
return comp_func(type, ghostType());
}
virtual bool isNodal() const { return false; }
protected:
CompFunc comp_func;
UInt spatial_dimension;
GhostType ghost_type;
ElementKind element_kind;
};
/* -------------------------------------------------------------------------- */
class MeshElementTypeMapArrayInitializer
: public ElementTypeMapArrayInitializer {
using CompFunc = ElementTypeMapArrayInitializer::CompFunc;
public:
- MeshElementTypeMapArrayInitializer(const Mesh & mesh, UInt nb_component = 1,
- UInt 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)
+ MeshElementTypeMapArrayInitializer(
+ const Mesh & mesh, UInt nb_component = 1,
+ UInt 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<UInt> * filter = nullptr)
: MeshElementTypeMapArrayInitializer(
mesh,
[nb_component](ElementType /*unused*/, GhostType /*unused*/)
-> UInt { return nb_component; },
spatial_dimension, ghost_type, element_kind, with_nb_element,
- with_nb_nodes_per_element) {}
-
- MeshElementTypeMapArrayInitializer(const Mesh & mesh,
- const CompFunc & comp_func,
- UInt 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)
+ with_nb_nodes_per_element, filter) {}
+
+ MeshElementTypeMapArrayInitializer(
+ const Mesh & mesh, const CompFunc & comp_func,
+ UInt 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<UInt> * 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) {}
+ with_nb_nodes_per_element(with_nb_nodes_per_element), filter(filter) {}
decltype(auto) elementTypes() const {
+ if (filter) {
+ 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 UInt size(ElementType type) const {
if (with_nb_element) {
+ if (filter) {
+ return (*filter)(type, this->ghost_type).size();
+ }
return mesh.getNbElement(type, this->ghost_type);
}
return 0;
}
UInt nbComponent(ElementType type) const override {
UInt 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;
- bool with_nb_nodes_per_element;
+ bool with_nb_element{false};
+ bool with_nb_nodes_per_element{false};
+ const ElementTypeMapArray<UInt> * filter{nullptr};
};
/* -------------------------------------------------------------------------- */
class FEEngineElementTypeMapArrayInitializer
: public MeshElementTypeMapArrayInitializer {
public:
FEEngineElementTypeMapArrayInitializer(
const FEEngine & fe_engine, UInt nb_component = 1,
UInt 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,
UInt spatial_dimension = _all_dimensions,
GhostType ghost_type = _not_ghost,
ElementKind element_kind = _ek_not_defined);
UInt size(ElementType type) const override;
using ElementTypesIteratorHelper =
ElementTypeMapArray<Real, ElementType>::ElementTypesIteratorHelper;
ElementTypesIteratorHelper elementTypes() const;
protected:
const FEEngine & fe_engine;
};
/* -------------------------------------------------------------------------- */
template <typename T, typename SupportType>
template <class Func>
void ElementTypeMapArray<T, SupportType>::initialize(const Func & f,
const T & default_value,
bool do_not_default) {
this->is_nodal = f.isNodal();
auto ghost_type = f.ghostType();
for (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 <typename T, typename SupportType>
template <typename... pack>
void ElementTypeMapArray<T, SupportType>::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 (auto ghost_type : ghost_types) {
+ 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(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 <typename T, typename SupportType>
template <typename... pack>
void ElementTypeMapArray<T, SupportType>::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, UInt(-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 <class T, typename SupportType>
inline T &
ElementTypeMapArray<T, SupportType>::operator()(const Element & element,
UInt component) {
return this->operator()(element.type, element.ghost_type)(element.element,
component);
}
/* -------------------------------------------------------------------------- */
template <class T, typename SupportType>
inline const T &
ElementTypeMapArray<T, SupportType>::operator()(const Element & element,
UInt component) const {
return this->operator()(element.type, element.ghost_type)(element.element,
component);
}
/* -------------------------------------------------------------------------- */
template <class T, typename SupportType>
inline decltype(auto)
ElementTypeMapArray<T, SupportType>::get(const Element & element) {
auto & array = operator()(element.type, element.ghost_type);
auto it = array.begin(array.getNbComponent());
return it[element.element];
}
/* -------------------------------------------------------------------------- */
template <class T, typename SupportType>
inline decltype(auto)
ElementTypeMapArray<T, SupportType>::get(const Element & element) const {
auto & array = operator()(element.type, element.ghost_type);
auto it = array.begin(array.getNbComponent());
return it[element.element];
}
/* -------------------------------------------------------------------------- */
template <class T, typename SupportType>
UInt ElementTypeMapArray<T, SupportType>::sizeImpl(UInt spatial_dimension,
GhostType ghost_type,
ElementKind kind) const {
UInt size = 0;
for (auto && type : this->elementTypes(spatial_dimension, ghost_type, kind)) {
size += this->operator()(type, ghost_type).size();
}
return size;
}
/* -------------------------------------------------------------------------- */
template <class T, typename SupportType>
template <typename... pack>
UInt ElementTypeMapArray<T, SupportType>::size(pack &&... _pack) const {
UInt 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/group_manager.cc b/src/mesh/group_manager.cc
index 70dba4f1b..c10def800 100644
--- a/src/mesh/group_manager.cc
+++ b/src/mesh/group_manager.cc
@@ -1,1033 +1,1033 @@
/**
* @file group_manager.cc
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author Dana Christen <dana.christen@gmail.com>
* @author David Simon Kammer <david.kammer@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
* @author Marco Vocialta <marco.vocialta@epfl.ch>
*
* @date creation: Wed Nov 13 2013
* @date last modification: Tue Feb 20 2018
*
* @brief Stores information about ElementGroup and NodeGroup
*
*
* Copyright (©) 2014-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "group_manager.hh"
#include "aka_csr.hh"
#include "data_accessor.hh"
#include "element_group.hh"
#include "element_synchronizer.hh"
#include "mesh.hh"
#include "mesh_accessor.hh"
#include "mesh_utils.hh"
#include "node_group.hh"
/* -------------------------------------------------------------------------- */
#include <algorithm>
#include <iterator>
#include <list>
#include <numeric>
#include <queue>
#include <sstream>
#include <utility>
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
-GroupManager::GroupManager(Mesh & mesh, const ID & id, const MemoryID & mem_id)
- : id(id), memory_id(mem_id), mesh(mesh) {
+GroupManager::GroupManager(Mesh & mesh, const ID & id)
+ : id(id), mesh(mesh) {
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
GroupManager::~GroupManager() = default;
/* -------------------------------------------------------------------------- */
NodeGroup & GroupManager::createNodeGroup(const std::string & group_name,
bool replace_group) {
AKANTU_DEBUG_IN();
auto it = node_groups.find(group_name);
if (it != node_groups.end()) {
if (replace_group) {
it->second.reset();
} else {
AKANTU_EXCEPTION(
"Trying to create a node group that already exists:" << group_name);
}
}
std::stringstream sstr;
sstr << this->id << ":" << group_name << "_node_group";
auto && ptr =
- std::make_unique<NodeGroup>(group_name, mesh, sstr.str(), memory_id);
+ std::make_unique<NodeGroup>(group_name, mesh, sstr.str());
auto & node_group = *ptr;
// \todo insert_or_assign in c++17
if (it != node_groups.end()) {
it->second = std::move(ptr);
} else {
node_groups[group_name] = std::move(ptr);
}
AKANTU_DEBUG_OUT();
return node_group;
}
/* -------------------------------------------------------------------------- */
template <typename T>
NodeGroup &
GroupManager::createFilteredNodeGroup(const std::string & group_name,
const NodeGroup & source_node_group,
T & filter) {
AKANTU_DEBUG_IN();
NodeGroup & node_group = this->createNodeGroup(group_name);
node_group.append(source_node_group);
if (T::type == FilterFunctor::_node_filter_functor) {
node_group.applyNodeFilter(filter);
} else {
AKANTU_ERROR("ElementFilter cannot be applied to NodeGroup yet."
<< " Needs to be implemented.");
}
AKANTU_DEBUG_OUT();
return node_group;
}
/* -------------------------------------------------------------------------- */
ElementGroup & GroupManager::createElementGroup(const std::string & group_name,
UInt dimension,
bool replace_group) {
AKANTU_DEBUG_IN();
auto it = element_groups.find(group_name);
if (it != element_groups.end()) {
if (replace_group) {
it->second.reset();
} else {
AKANTU_EXCEPTION("Trying to create a element group that already exists:"
<< group_name);
}
}
NodeGroup & new_node_group =
createNodeGroup(group_name + "_nodes", replace_group);
auto && ptr = std::make_unique<ElementGroup>(
group_name, mesh, new_node_group, dimension,
- this->id + ":" + group_name + "_element_group", memory_id);
+ this->id + ":" + group_name + "_element_group");
auto & element_group = *ptr;
if (it != element_groups.end()) {
it->second = std::move(ptr);
} else {
element_groups[group_name] = std::move(ptr);
}
AKANTU_DEBUG_OUT();
return element_group;
}
/* -------------------------------------------------------------------------- */
void GroupManager::destroyElementGroup(const std::string & group_name,
bool destroy_node_group) {
AKANTU_DEBUG_IN();
auto eit = element_groups.find(group_name);
if (eit != element_groups.end()) {
if (destroy_node_group) {
destroyNodeGroup(eit->second->getNodeGroup().getName());
}
element_groups.erase(eit);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void GroupManager::destroyNodeGroup(const std::string & group_name) {
AKANTU_DEBUG_IN();
auto nit = node_groups.find(group_name);
if (nit != node_groups.end()) {
node_groups.erase(nit);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
ElementGroup & GroupManager::createElementGroup(const std::string & group_name,
UInt dimension,
NodeGroup & node_group) {
AKANTU_DEBUG_IN();
if (element_groups.find(group_name) != element_groups.end()) {
AKANTU_EXCEPTION(
"Trying to create a element group that already exists:" << group_name);
}
auto && ptr = std::make_unique<ElementGroup>(
group_name, mesh, node_group, dimension,
- id + ":" + group_name + "_element_group", memory_id);
+ id + ":" + group_name + "_element_group");
auto & element_group = *ptr;
element_groups[group_name] = std::move(ptr);
AKANTU_DEBUG_OUT();
return element_group;
}
/* -------------------------------------------------------------------------- */
template <typename T>
ElementGroup & GroupManager::createFilteredElementGroup(
const std::string & group_name, UInt dimension,
const NodeGroup & node_group, T & filter) {
AKANTU_DEBUG_IN();
if (T::type == FilterFunctor::_node_filter_functor) {
auto & filtered_node_group = this->createFilteredNodeGroup(
group_name + "_nodes", node_group, filter);
AKANTU_DEBUG_OUT();
return this->createElementGroup(group_name, dimension, filtered_node_group);
}
if (T::type == FilterFunctor::_element_filter_functor) {
AKANTU_ERROR(
"Cannot handle an ElementFilter yet. Needs to be implemented.");
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
class ClusterSynchronizer : public DataAccessor<Element> {
using DistantIDs = std::set<std::pair<UInt, UInt>>;
public:
ClusterSynchronizer(GroupManager & group_manager, UInt element_dimension,
std::string cluster_name_prefix,
ElementTypeMapArray<UInt> & element_to_fragment,
const ElementSynchronizer & element_synchronizer,
UInt nb_cluster)
: group_manager(group_manager), element_dimension(element_dimension),
cluster_name_prefix(std::move(cluster_name_prefix)),
element_to_fragment(element_to_fragment),
element_synchronizer(element_synchronizer), nb_cluster(nb_cluster) {}
UInt synchronize() {
Communicator & comm = Communicator::getStaticCommunicator();
UInt rank = comm.whoAmI();
UInt nb_proc = comm.getNbProc();
/// find starting index to renumber local clusters
Array<UInt> nb_cluster_per_proc(nb_proc);
nb_cluster_per_proc(rank) = nb_cluster;
comm.allGather(nb_cluster_per_proc);
starting_index = std::accumulate(nb_cluster_per_proc.begin(),
nb_cluster_per_proc.begin() + rank, 0U);
UInt global_nb_fragment =
std::accumulate(nb_cluster_per_proc.begin() + rank,
nb_cluster_per_proc.end(), starting_index);
/// create the local to distant cluster pairs with neighbors
element_synchronizer.synchronizeOnce(*this,
SynchronizationTag::_gm_clusters);
/// count total number of pairs
Array<int> nb_pairs(nb_proc); // This is potentially a bug for more than
// 2**31 pairs, but due to a all gatherv after
// it must be int to match MPI interfaces
nb_pairs(rank) = distant_ids.size();
comm.allGather(nb_pairs);
UInt total_nb_pairs = std::accumulate(nb_pairs.begin(), nb_pairs.end(), 0);
/// generate pairs global array
UInt local_pair_index =
std::accumulate(nb_pairs.storage(), nb_pairs.storage() + rank, 0);
Array<UInt> total_pairs(total_nb_pairs, 2);
for (const auto & ids : distant_ids) {
total_pairs(local_pair_index, 0) = ids.first;
total_pairs(local_pair_index, 1) = ids.second;
++local_pair_index;
}
/// communicate pairs to all processors
nb_pairs *= 2;
comm.allGatherV(total_pairs, nb_pairs);
/// renumber clusters
/// generate fragment list
std::vector<std::set<UInt>> global_clusters;
UInt total_nb_cluster = 0;
Array<bool> is_fragment_in_cluster(global_nb_fragment, 1, false);
std::queue<UInt> fragment_check_list;
while (not total_pairs.empty()) {
/// create a new cluster
++total_nb_cluster;
global_clusters.resize(total_nb_cluster);
std::set<UInt> & current_cluster = global_clusters[total_nb_cluster - 1];
UInt first_fragment = total_pairs(0, 0);
UInt second_fragment = total_pairs(0, 1);
total_pairs.erase(0);
fragment_check_list.push(first_fragment);
fragment_check_list.push(second_fragment);
while (!fragment_check_list.empty()) {
UInt current_fragment = fragment_check_list.front();
UInt * total_pairs_end = total_pairs.storage() + total_pairs.size() * 2;
UInt * fragment_found =
std::find(total_pairs.storage(), total_pairs_end, current_fragment);
if (fragment_found != total_pairs_end) {
UInt position = fragment_found - total_pairs.storage();
UInt pair = position / 2;
UInt other_index = (position + 1) % 2;
fragment_check_list.push(total_pairs(pair, other_index));
total_pairs.erase(pair);
} else {
fragment_check_list.pop();
current_cluster.insert(current_fragment);
is_fragment_in_cluster(current_fragment) = true;
}
}
}
/// add to FragmentToCluster all local fragments
for (UInt c = 0; c < global_nb_fragment; ++c) {
if (!is_fragment_in_cluster(c)) {
++total_nb_cluster;
global_clusters.resize(total_nb_cluster);
std::set<UInt> & current_cluster =
global_clusters[total_nb_cluster - 1];
current_cluster.insert(c);
}
}
/// reorganize element groups to match global clusters
for (UInt c = 0; c < global_clusters.size(); ++c) {
/// create new element group corresponding to current cluster
std::stringstream sstr;
sstr << cluster_name_prefix << "_" << c;
ElementGroup & cluster =
group_manager.createElementGroup(sstr.str(), element_dimension, true);
auto it = global_clusters[c].begin();
auto end = global_clusters[c].end();
/// append to current element group all fragments that belong to
/// the same cluster if they exist
for (; it != end; ++it) {
Int local_index = *it - starting_index;
if (local_index < 0 || local_index >= Int(nb_cluster)) {
continue;
}
std::stringstream tmp_sstr;
tmp_sstr << "tmp_" << cluster_name_prefix << "_" << local_index;
AKANTU_DEBUG_ASSERT(group_manager.elementGroupExists(tmp_sstr.str()),
"Temporary fragment \"" << tmp_sstr.str()
<< "\" not found");
cluster.append(group_manager.getElementGroup(tmp_sstr.str()));
group_manager.destroyElementGroup(tmp_sstr.str(), true);
}
}
return total_nb_cluster;
}
private:
/// functions for parallel communications
inline UInt getNbData(const Array<Element> & elements,
const SynchronizationTag & tag) const override {
if (tag == SynchronizationTag::_gm_clusters) {
return elements.size() * sizeof(UInt);
}
return 0;
}
inline void packData(CommunicationBuffer & buffer,
const Array<Element> & elements,
const SynchronizationTag & tag) const override {
if (tag != SynchronizationTag::_gm_clusters) {
return;
}
Array<Element>::const_iterator<> el_it = elements.begin();
Array<Element>::const_iterator<> el_end = elements.end();
for (; el_it != el_end; ++el_it) {
const Element & el = *el_it;
/// for each element pack its global cluster index
buffer << element_to_fragment(el.type, el.ghost_type)(el.element) +
starting_index;
}
}
inline void unpackData(CommunicationBuffer & buffer,
const Array<Element> & elements,
const SynchronizationTag & tag) override {
if (tag != SynchronizationTag::_gm_clusters) {
return;
}
Array<Element>::const_iterator<> el_it = elements.begin();
Array<Element>::const_iterator<> el_end = elements.end();
for (; el_it != el_end; ++el_it) {
UInt distant_cluster;
buffer >> distant_cluster;
const Element & el = *el_it;
UInt local_cluster =
element_to_fragment(el.type, el.ghost_type)(el.element) +
starting_index;
distant_ids.insert(std::make_pair(local_cluster, distant_cluster));
}
}
private:
GroupManager & group_manager;
UInt element_dimension;
std::string cluster_name_prefix;
ElementTypeMapArray<UInt> & element_to_fragment;
const ElementSynchronizer & element_synchronizer;
UInt nb_cluster;
DistantIDs distant_ids;
UInt starting_index;
};
/* -------------------------------------------------------------------------- */
/// \todo this function doesn't work in 1D
UInt GroupManager::createBoundaryGroupFromGeometry() {
UInt spatial_dimension = mesh.getSpatialDimension();
return createClusters(spatial_dimension - 1, "boundary");
}
/* -------------------------------------------------------------------------- */
UInt GroupManager::createClusters(
UInt element_dimension, Mesh & mesh_facets,
const std::string & cluster_name_prefix,
const GroupManager::ClusteringFilter & filter) {
return createClusters(element_dimension, cluster_name_prefix, filter,
mesh_facets);
}
/* -------------------------------------------------------------------------- */
UInt GroupManager::createClusters(
UInt element_dimension, const std::string & cluster_name_prefix,
const GroupManager::ClusteringFilter & filter) {
MeshAccessor mesh_accessor(mesh);
auto mesh_facets = std::make_unique<Mesh>(mesh.getSpatialDimension(),
mesh_accessor.getNodesSharedPtr(),
"mesh_facets_for_clusters");
mesh_facets->defineMeshParent(mesh);
MeshUtils::buildAllFacets(mesh, *mesh_facets, element_dimension,
element_dimension - 1);
return createClusters(element_dimension, cluster_name_prefix, filter,
*mesh_facets);
}
/* -------------------------------------------------------------------------- */
//// \todo if needed element list construction can be optimized by
//// templating the filter class
UInt GroupManager::createClusters(UInt element_dimension,
const std::string & cluster_name_prefix,
const GroupManager::ClusteringFilter & filter,
Mesh & mesh_facets) {
AKANTU_DEBUG_IN();
UInt nb_proc = mesh.getCommunicator().getNbProc();
std::string tmp_cluster_name_prefix = cluster_name_prefix;
ElementTypeMapArray<UInt> * element_to_fragment = nullptr;
if (nb_proc > 1 && mesh.isDistributed()) {
element_to_fragment =
- new ElementTypeMapArray<UInt>("element_to_fragment", id, memory_id);
+ new ElementTypeMapArray<UInt>("element_to_fragment", id);
element_to_fragment->initialize(
mesh, _nb_component = 1, _spatial_dimension = element_dimension,
_element_kind = _ek_not_defined, _with_nb_element = true);
// mesh.initElementTypeMapArray(*element_to_fragment, 1, element_dimension,
// false, _ek_not_defined, true);
tmp_cluster_name_prefix = "tmp_" + tmp_cluster_name_prefix;
}
- ElementTypeMapArray<bool> seen_elements("seen_elements", id, memory_id);
+ ElementTypeMapArray<bool> seen_elements("seen_elements", id);
seen_elements.initialize(mesh, _spatial_dimension = element_dimension,
_element_kind = _ek_not_defined,
_with_nb_element = true);
// mesh.initElementTypeMapArray(seen_elements, 1, element_dimension, false,
// _ek_not_defined, true);
for (auto ghost_type : ghost_types) {
Element el;
el.ghost_type = ghost_type;
for (auto type :
mesh.elementTypes(_spatial_dimension = element_dimension,
_ghost_type = ghost_type, _element_kind = _ek_not_defined)) {
el.type = type;
UInt nb_element = mesh.getNbElement(type, ghost_type);
Array<bool> & seen_elements_array = seen_elements(type, ghost_type);
for (UInt e = 0; e < nb_element; ++e) {
el.element = e;
if (!filter(el)) {
seen_elements_array(e) = true;
}
}
}
}
Array<bool> checked_node(mesh.getNbNodes(), 1, false);
UInt nb_cluster = 0;
for (auto ghost_type : ghost_types) {
Element uns_el;
uns_el.ghost_type = ghost_type;
for (auto type :
mesh.elementTypes(_spatial_dimension = element_dimension,
_ghost_type = ghost_type, _element_kind = _ek_not_defined)) {
uns_el.type = type;
Array<bool> & seen_elements_vec =
seen_elements(uns_el.type, uns_el.ghost_type);
for (UInt e = 0; e < seen_elements_vec.size(); ++e) {
// skip elements that have been already seen
if (seen_elements_vec(e)) {
continue;
}
// set current element
uns_el.element = e;
seen_elements_vec(e) = true;
/// create a new cluster
std::stringstream sstr;
sstr << tmp_cluster_name_prefix << "_" << nb_cluster;
ElementGroup & cluster =
createElementGroup(sstr.str(), element_dimension, true);
++nb_cluster;
// point element are cluster by themself
if (element_dimension == 0) {
cluster.add(uns_el);
UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(uns_el.type);
Vector<UInt> connect =
mesh.getConnectivity(uns_el.type, uns_el.ghost_type)
.begin(nb_nodes_per_element)[uns_el.element];
for (UInt n = 0; n < nb_nodes_per_element; ++n) {
/// add element's nodes to the cluster
UInt node = connect[n];
if (!checked_node(node)) {
cluster.addNode(node);
checked_node(node) = true;
}
}
continue;
}
std::queue<Element> element_to_add;
element_to_add.push(uns_el);
/// keep looping until current cluster is complete (no more
/// connected elements)
while (!element_to_add.empty()) {
/// take first element and erase it in the queue
Element el = element_to_add.front();
element_to_add.pop();
/// if parallel, store cluster index per element
if (nb_proc > 1 && mesh.isDistributed()) {
(*element_to_fragment)(el.type, el.ghost_type)(el.element) =
nb_cluster - 1;
}
/// add current element to the cluster
cluster.add(el);
const Array<Element> & element_to_facet =
mesh_facets.getSubelementToElement(el.type, el.ghost_type);
UInt nb_facet_per_element = element_to_facet.getNbComponent();
for (UInt f = 0; f < nb_facet_per_element; ++f) {
const Element & facet = element_to_facet(el.element, f);
if (facet == ElementNull) {
continue;
}
const std::vector<Element> & connected_elements =
mesh_facets.getElementToSubelement(
facet.type, facet.ghost_type)(facet.element);
for (UInt elem = 0; elem < connected_elements.size(); ++elem) {
const Element & check_el = connected_elements[elem];
// check if this element has to be skipped
if (check_el == ElementNull || check_el == el) {
continue;
}
Array<bool> & seen_elements_vec_current =
seen_elements(check_el.type, check_el.ghost_type);
if (not seen_elements_vec_current(check_el.element)) {
seen_elements_vec_current(check_el.element) = true;
element_to_add.push(check_el);
}
}
}
UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(el.type);
Vector<UInt> connect = mesh.getConnectivity(el.type, el.ghost_type)
.begin(nb_nodes_per_element)[el.element];
for (UInt n = 0; n < nb_nodes_per_element; ++n) {
/// add element's nodes to the cluster
UInt node = connect[n];
if (!checked_node(node)) {
cluster.addNode(node, false);
checked_node(node) = true;
}
}
}
}
}
}
if (nb_proc > 1 && mesh.isDistributed()) {
ClusterSynchronizer cluster_synchronizer(
*this, element_dimension, cluster_name_prefix, *element_to_fragment,
this->mesh.getElementSynchronizer(), nb_cluster);
nb_cluster = cluster_synchronizer.synchronize();
delete element_to_fragment;
}
if (mesh.isDistributed()) {
this->synchronizeGroupNames();
}
AKANTU_DEBUG_OUT();
return nb_cluster;
}
/* -------------------------------------------------------------------------- */
template <typename T>
void GroupManager::createGroupsFromMeshData(const std::string & dataset_name) {
std::set<std::string> group_names;
const auto & datas = mesh.getData<T>(dataset_name);
std::map<std::string, UInt> group_dim;
for (auto ghost_type : ghost_types) {
for (auto type : datas.elementTypes(_ghost_type = ghost_type)) {
const Array<T> & dataset = datas(type, ghost_type);
UInt nb_element = mesh.getNbElement(type, ghost_type);
AKANTU_DEBUG_ASSERT(dataset.size() == nb_element,
"Not the same number of elements ("
<< type << ":" << ghost_type
<< ") in the map from MeshData ("
<< dataset.size() << ") " << dataset_name
<< " and in the mesh (" << nb_element << ")!");
for (UInt e(0); e < nb_element; ++e) {
std::stringstream sstr;
sstr << dataset(e);
std::string gname = sstr.str();
group_names.insert(gname);
auto it = group_dim.find(gname);
if (it == group_dim.end()) {
group_dim[gname] = mesh.getSpatialDimension(type);
} else {
it->second = std::max(it->second, mesh.getSpatialDimension(type));
}
}
}
}
for (auto && name : group_names) {
createElementGroup(name, group_dim[name]);
}
if (mesh.isDistributed()) {
this->synchronizeGroupNames();
}
Element el;
for (auto ghost_type : ghost_types) {
el.ghost_type = ghost_type;
for (auto type : datas.elementTypes(_ghost_type = ghost_type)) {
el.type = type;
const Array<T> & dataset = datas(type, ghost_type);
UInt nb_element = mesh.getNbElement(type, ghost_type);
AKANTU_DEBUG_ASSERT(dataset.size() == nb_element,
"Not the same number of elements in the map from "
"MeshData and in the mesh!");
UInt nb_nodes_per_element = mesh.getNbNodesPerElement(el.type);
Array<UInt>::const_iterator<Vector<UInt>> cit =
mesh.getConnectivity(type, ghost_type).begin(nb_nodes_per_element);
for (UInt e(0); e < nb_element; ++e, ++cit) {
el.element = e;
std::stringstream sstr;
sstr << dataset(e);
ElementGroup & group = getElementGroup(sstr.str());
group.add(el, false, false);
const Vector<UInt> & connect = *cit;
for (UInt n = 0; n < nb_nodes_per_element; ++n) {
UInt node = connect[n];
group.addNode(node, false);
}
}
}
}
for (auto && name : group_names) {
getElementGroup(name).optimize();
}
}
template void GroupManager::createGroupsFromMeshData<std::string>(
const std::string & dataset_name);
template void
GroupManager::createGroupsFromMeshData<UInt>(const std::string & dataset_name);
/* -------------------------------------------------------------------------- */
void GroupManager::createElementGroupFromNodeGroup(
const std::string & name, const std::string & node_group_name,
UInt dimension) {
NodeGroup & node_group = getNodeGroup(node_group_name);
ElementGroup & group = createElementGroup(name, dimension, node_group);
group.fillFromNodeGroup();
}
/* -------------------------------------------------------------------------- */
void GroupManager::printself(std::ostream & stream, int indent) const {
std::string space(indent, AKANTU_INDENT);
stream << space << "GroupManager [" << std::endl;
std::set<std::string> node_group_seen;
for (auto & group : iterateElementGroups()) {
group.printself(stream, indent + 1);
node_group_seen.insert(group.getNodeGroup().getName());
}
for (auto & group : iterateNodeGroups()) {
if (node_group_seen.find(group.getName()) == node_group_seen.end()) {
group.printself(stream, indent + 1);
}
}
stream << space << "]" << std::endl;
}
/* -------------------------------------------------------------------------- */
UInt GroupManager::getNbElementGroups(UInt dimension) const {
if (dimension == _all_dimensions) {
return element_groups.size();
}
return std::count_if(
element_groups.begin(), element_groups.end(),
[dimension](auto && eg) { return eg.second->getDimension() == dimension; });
}
/* -------------------------------------------------------------------------- */
void GroupManager::checkAndAddGroups(DynamicCommunicationBuffer & buffer) {
AKANTU_DEBUG_IN();
UInt nb_node_group;
buffer >> nb_node_group;
AKANTU_DEBUG_INFO("Received " << nb_node_group << " node group names");
for (UInt ng = 0; ng < nb_node_group; ++ng) {
std::string node_group_name;
buffer >> node_group_name;
if (node_groups.find(node_group_name) == node_groups.end()) {
this->createNodeGroup(node_group_name);
}
AKANTU_DEBUG_INFO("Received node goup name: " << node_group_name);
}
UInt nb_element_group;
buffer >> nb_element_group;
AKANTU_DEBUG_INFO("Received " << nb_element_group << " element group names");
for (UInt eg = 0; eg < nb_element_group; ++eg) {
std::string element_group_name;
buffer >> element_group_name;
std::string node_group_name;
buffer >> node_group_name;
UInt dim;
buffer >> dim;
AKANTU_DEBUG_INFO("Received element group name: "
<< element_group_name << " corresponding to a "
<< Int(dim) << "D group with node group "
<< node_group_name);
NodeGroup & node_group = *node_groups[node_group_name];
if (element_groups.find(element_group_name) == element_groups.end()) {
this->createElementGroup(element_group_name, dim, node_group);
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void GroupManager::fillBufferWithGroupNames(
DynamicCommunicationBuffer & comm_buffer) const {
AKANTU_DEBUG_IN();
// packing node group names;
UInt nb_groups = this->node_groups.size();
comm_buffer << nb_groups;
AKANTU_DEBUG_INFO("Sending " << nb_groups << " node group names");
auto nnames_it = node_groups.begin();
auto nnames_end = node_groups.end();
for (; nnames_it != nnames_end; ++nnames_it) {
std::string node_group_name = nnames_it->first;
comm_buffer << node_group_name;
AKANTU_DEBUG_INFO("Sending node goupe name: " << node_group_name);
}
// packing element group names with there associated node group name
nb_groups = this->element_groups.size();
comm_buffer << nb_groups;
AKANTU_DEBUG_INFO("Sending " << nb_groups << " element group names");
auto gnames_it = this->element_groups.begin();
auto gnames_end = this->element_groups.end();
for (; gnames_it != gnames_end; ++gnames_it) {
ElementGroup & element_group = *(gnames_it->second);
std::string element_group_name = gnames_it->first;
std::string node_group_name = element_group.getNodeGroup().getName();
UInt dim = element_group.getDimension();
comm_buffer << element_group_name;
comm_buffer << node_group_name;
comm_buffer << dim;
AKANTU_DEBUG_INFO("Sending element group name: "
<< element_group_name << " corresponding to a "
<< Int(dim) << "D group with the node group "
<< node_group_name);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void GroupManager::synchronizeGroupNames() {
AKANTU_DEBUG_IN();
const Communicator & comm = mesh.getCommunicator();
Int nb_proc = comm.getNbProc();
Int my_rank = comm.whoAmI();
if (nb_proc == 1) {
return;
}
if (my_rank == 0) {
for (Int p = 1; p < nb_proc; ++p) {
DynamicCommunicationBuffer recv_buffer;
auto tag = Tag::genTag(p, 0, Tag::_element_group);
comm.receive(recv_buffer, p, tag);
AKANTU_DEBUG_INFO("Got " << printMemorySize<char>(recv_buffer.size())
<< " from proc " << p << " " << tag);
this->checkAndAddGroups(recv_buffer);
}
DynamicCommunicationBuffer comm_buffer;
this->fillBufferWithGroupNames(comm_buffer);
AKANTU_DEBUG_INFO("Initiating broadcast with "
<< printMemorySize<char>(comm_buffer.size()));
comm.broadcast(comm_buffer);
} else {
DynamicCommunicationBuffer comm_buffer;
this->fillBufferWithGroupNames(comm_buffer);
auto tag = Tag::genTag(my_rank, 0, Tag::_element_group);
AKANTU_DEBUG_INFO("Sending " << printMemorySize<char>(comm_buffer.size())
<< " to proc " << 0 << " " << tag);
comm.send(comm_buffer, 0, tag);
DynamicCommunicationBuffer recv_buffer;
comm.broadcast(recv_buffer);
AKANTU_DEBUG_INFO("Receiving broadcast with "
<< printMemorySize<char>(recv_buffer.size()));
this->checkAndAddGroups(recv_buffer);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
const ElementGroup &
GroupManager::getElementGroup(const std::string & name) const {
auto it = element_groups.find(name);
if (it == element_groups.end()) {
AKANTU_EXCEPTION("There are no element groups named "
<< name << " associated to the group manager: " << id);
}
return *(it->second);
}
/* -------------------------------------------------------------------------- */
ElementGroup & GroupManager::getElementGroup(const std::string & name) {
auto it = element_groups.find(name);
if (it == element_groups.end()) {
AKANTU_EXCEPTION("There are no element groups named "
<< name << " associated to the group manager: " << id);
}
return *(it->second);
}
/* -------------------------------------------------------------------------- */
const NodeGroup & GroupManager::getNodeGroup(const std::string & name) const {
auto it = node_groups.find(name);
if (it == node_groups.end()) {
AKANTU_EXCEPTION("There are no node groups named "
<< name << " associated to the group manager: " << id);
}
return *(it->second);
}
/* -------------------------------------------------------------------------- */
NodeGroup & GroupManager::getNodeGroup(const std::string & name) {
auto it = node_groups.find(name);
if (it == node_groups.end()) {
AKANTU_EXCEPTION("There are no node groups named "
<< name << " associated to the group manager: " << id);
}
return *(it->second);
}
/* -------------------------------------------------------------------------- */
template <typename GroupsType>
void GroupManager::renameGroup(GroupsType & groups, const std::string & name,
const std::string & new_name) {
auto it = groups.find(name);
if (it == groups.end()) {
AKANTU_EXCEPTION("There are no group named "
<< name << " associated to the group manager: " << id);
}
auto && group_ptr = std::move(it->second);
group_ptr->name = new_name;
groups.erase(it);
groups[new_name] = std::move(group_ptr);
}
/* -------------------------------------------------------------------------- */
void GroupManager::renameElementGroup(const std::string & name,
const std::string & new_name) {
renameGroup(element_groups, name, new_name);
}
/* -------------------------------------------------------------------------- */
void GroupManager::renameNodeGroup(const std::string & name,
const std::string & new_name) {
renameGroup(node_groups, name, new_name);
}
/* -------------------------------------------------------------------------- */
void GroupManager::copyElementGroup(const std::string & name,
const std::string & new_name) {
const auto & grp = getElementGroup(name);
auto & new_grp = createElementGroup(new_name, grp.getDimension());
new_grp.getElements().copy(grp.getElements());
}
/* -------------------------------------------------------------------------- */
void GroupManager::copyNodeGroup(const std::string & name,
const std::string & new_name) {
const auto & grp = getNodeGroup(name);
auto & new_grp = createNodeGroup(new_name);
new_grp.getNodes().copy(grp.getNodes());
}
} // namespace akantu
diff --git a/src/mesh/group_manager.hh b/src/mesh/group_manager.hh
index 43a5becc6..43da4e554 100644
--- a/src/mesh/group_manager.hh
+++ b/src/mesh/group_manager.hh
@@ -1,351 +1,348 @@
/**
* @file group_manager.hh
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author Dana Christen <dana.christen@gmail.com>
* @author David Simon Kammer <david.kammer@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
* @author Marco Vocialta <marco.vocialta@epfl.ch>
*
* @date creation: Wed Nov 13 2013
* @date last modification: Wed Feb 07 2018
*
* @brief Stores information relevent to the notion of element and nodes
* groups.
*
*
* Copyright (©) 2014-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_GROUP_MANAGER_HH_
#define AKANTU_GROUP_MANAGER_HH_
/* -------------------------------------------------------------------------- */
#include "aka_common.hh"
#include "aka_iterators.hh"
#include "element_type_map.hh"
/* -------------------------------------------------------------------------- */
#include <set>
/* -------------------------------------------------------------------------- */
namespace akantu {
class ElementGroup;
class NodeGroup;
class Mesh;
class Element;
class ElementSynchronizer;
template <bool> class CommunicationBufferTemplated;
namespace dumpers {
class Field;
}
} // namespace akantu
namespace akantu {
/* -------------------------------------------------------------------------- */
class GroupManager {
/* ------------------------------------------------------------------------ */
/* Typedefs */
/* ------------------------------------------------------------------------ */
private:
using ElementGroups = std::map<std::string, std::unique_ptr<ElementGroup>>;
using NodeGroups = std::map<std::string, std::unique_ptr<NodeGroup>>;
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
- GroupManager(Mesh & mesh, const ID & id = "group_manager",
- const MemoryID & mem_id = 0);
+ GroupManager(Mesh & mesh, const ID & id = "group_manager");
virtual ~GroupManager();
/* ------------------------------------------------------------------------ */
/* Groups iterators */
/* ------------------------------------------------------------------------ */
public:
using node_group_iterator = NodeGroups::iterator;
using element_group_iterator = ElementGroups::iterator;
using const_node_group_iterator = NodeGroups::const_iterator;
using const_element_group_iterator = ElementGroups::const_iterator;
#define AKANTU_GROUP_MANAGER_DEFINE_ITERATOR_FUNCTION(group_type, function, \
param_in, param_out) \
[[deprecated( \
"use iterate(Element|Node)Groups or " \
"(element|node)GroupExists")]] inline BOOST_PP_CAT(BOOST_PP_CAT(const_, \
group_type), \
_iterator) \
BOOST_PP_CAT(BOOST_PP_CAT(group_type, _), function)(param_in) const { \
return BOOST_PP_CAT(group_type, s).function(param_out); \
}; \
\
[[deprecated("use iterate(Element|Node)Groups or " \
"(element|node)GroupExists")]] inline BOOST_PP_CAT(group_type, \
_iterator) \
BOOST_PP_CAT(BOOST_PP_CAT(group_type, _), function)(param_in) { \
return BOOST_PP_CAT(group_type, s).function(param_out); \
}
#define AKANTU_GROUP_MANAGER_DEFINE_ITERATOR_FUNCTION_NP(group_type, function) \
AKANTU_GROUP_MANAGER_DEFINE_ITERATOR_FUNCTION( \
group_type, function, BOOST_PP_EMPTY(), BOOST_PP_EMPTY())
AKANTU_GROUP_MANAGER_DEFINE_ITERATOR_FUNCTION_NP(node_group, begin);
AKANTU_GROUP_MANAGER_DEFINE_ITERATOR_FUNCTION_NP(node_group, end);
AKANTU_GROUP_MANAGER_DEFINE_ITERATOR_FUNCTION_NP(element_group, begin);
AKANTU_GROUP_MANAGER_DEFINE_ITERATOR_FUNCTION_NP(element_group, end);
AKANTU_GROUP_MANAGER_DEFINE_ITERATOR_FUNCTION(element_group, find,
const std::string & name, name);
AKANTU_GROUP_MANAGER_DEFINE_ITERATOR_FUNCTION(node_group, find,
const std::string & name, name);
public:
decltype(auto) iterateNodeGroups() {
return make_dereference_adaptor(make_values_adaptor(node_groups));
}
decltype(auto) iterateNodeGroups() const {
return make_dereference_adaptor(make_values_adaptor(node_groups));
}
decltype(auto) iterateElementGroups() {
return make_dereference_adaptor(make_values_adaptor(element_groups));
}
decltype(auto) iterateElementGroups() const {
return make_dereference_adaptor(make_values_adaptor(element_groups));
}
/* ------------------------------------------------------------------------ */
/* Clustering filter */
/* ------------------------------------------------------------------------ */
public:
class ClusteringFilter {
public:
virtual bool operator()(const Element & /*unused*/) const { return true; }
};
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// create an empty node group
NodeGroup & createNodeGroup(const std::string & group_name,
bool replace_group = false);
/// create an element group and the associated node group
ElementGroup & createElementGroup(const std::string & group_name,
UInt dimension = _all_dimensions,
bool replace_group = false);
/* ------------------------------------------------------------------------ */
/// renames an element group
void renameElementGroup(const std::string & name,
const std::string & new_name);
/// renames a node group
void renameNodeGroup(const std::string & name, const std::string & new_name);
/// copy an existing element group
void copyElementGroup(const std::string & name, const std::string & new_name);
/// copy an existing node group
void copyNodeGroup(const std::string & name, const std::string & new_name);
/* ------------------------------------------------------------------------ */
/// create a node group from another node group but filtered
template <typename T>
NodeGroup & createFilteredNodeGroup(const std::string & group_name,
const NodeGroup & node_group, T & filter);
/// create an element group from another element group but filtered
template <typename T>
ElementGroup &
createFilteredElementGroup(const std::string & group_name, UInt dimension,
const NodeGroup & node_group, T & filter);
/// destroy a node group
void destroyNodeGroup(const std::string & group_name);
/// destroy an element group and the associated node group
void destroyElementGroup(const std::string & group_name,
bool destroy_node_group = false);
// /// destroy all element groups and the associated node groups
// void destroyAllElementGroups(bool destroy_node_groups = false);
/// create a element group using an existing node group
ElementGroup & createElementGroup(const std::string & group_name,
UInt dimension, NodeGroup & node_group);
/// create groups based on values stored in a given mesh data
template <typename T>
void createGroupsFromMeshData(const std::string & dataset_name);
/// create boundaries group by a clustering algorithm \todo extend to parallel
UInt createBoundaryGroupFromGeometry();
/// create element clusters for a given dimension
UInt createClusters(UInt element_dimension, Mesh & mesh_facets,
const std::string & cluster_name_prefix = "cluster",
const ClusteringFilter & filter = ClusteringFilter());
/// create element clusters for a given dimension
UInt createClusters(UInt element_dimension,
const std::string & cluster_name_prefix = "cluster",
const ClusteringFilter & filter = ClusteringFilter());
private:
/// create element clusters for a given dimension
UInt createClusters(UInt element_dimension,
const std::string & cluster_name_prefix,
const ClusteringFilter & filter, Mesh & mesh_facets);
public:
/// Create an ElementGroup based on a NodeGroup
void createElementGroupFromNodeGroup(const std::string & name,
const std::string & node_group,
UInt dimension = _all_dimensions);
virtual void printself(std::ostream & stream, int indent = 0) const;
/// this function insure that the group names are present on all processors
/// /!\ it is a SMP call
void synchronizeGroupNames();
/// register an elemental field to the given group name (overloading for
/// ElementalPartionField)
template <typename T, template <bool> class dump_type>
std::shared_ptr<dumpers::Field> createElementalField(
const ElementTypeMapArray<T> & field, const std::string & group_name,
UInt spatial_dimension, ElementKind kind,
ElementTypeMap<UInt> nb_data_per_elem = ElementTypeMap<UInt>());
/// register an elemental field to the given group name (overloading for
/// ElementalField)
template <typename T, template <class> class ret_type,
template <class, template <class> class, bool> class dump_type>
std::shared_ptr<dumpers::Field> createElementalField(
const ElementTypeMapArray<T> & field, const std::string & group_name,
UInt spatial_dimension, ElementKind kind,
ElementTypeMap<UInt> nb_data_per_elem = ElementTypeMap<UInt>());
/// register an elemental field to the given group name (overloading for
/// MaterialInternalField)
template <typename T,
/// type of InternalMaterialField
template <typename, bool filtered> class dump_type>
std::shared_ptr<dumpers::Field>
createElementalField(const ElementTypeMapArray<T> & field,
const std::string & group_name, UInt spatial_dimension,
ElementKind kind,
ElementTypeMap<UInt> nb_data_per_elem);
template <typename type, bool flag, template <class, bool> class ftype>
std::shared_ptr<dumpers::Field>
createNodalField(const ftype<type, flag> * field,
const std::string & group_name, UInt padding_size = 0);
template <typename type, bool flag, template <class, bool> class ftype>
std::shared_ptr<dumpers::Field>
createStridedNodalField(const ftype<type, flag> * field,
const std::string & group_name, UInt size,
UInt stride, UInt padding_size);
protected:
/// fill a buffer with all the group names
void fillBufferWithGroupNames(
CommunicationBufferTemplated<false> & comm_buffer) const;
/// take a buffer and create the missing groups localy
void checkAndAddGroups(CommunicationBufferTemplated<false> & buffer);
/// register an elemental field to the given group name
template <class dump_type, typename field_type>
inline std::shared_ptr<dumpers::Field>
createElementalField(const field_type & field, const std::string & group_name,
UInt spatial_dimension, ElementKind kind,
const ElementTypeMap<UInt> & nb_data_per_elem);
/// register an elemental field to the given group name
template <class dump_type, typename field_type>
inline std::shared_ptr<dumpers::Field>
createElementalFilteredField(const field_type & field,
const std::string & group_name,
UInt spatial_dimension, ElementKind kind,
ElementTypeMap<UInt> nb_data_per_elem);
/* ------------------------------------------------------------------------ */
/* Accessor */
/* ------------------------------------------------------------------------ */
public:
// AKANTU_GET_MACRO(ElementGroups, element_groups, const ElementGroups &);
const ElementGroup & getElementGroup(const std::string & name) const;
const NodeGroup & getNodeGroup(const std::string & name) const;
ElementGroup & getElementGroup(const std::string & name);
NodeGroup & getNodeGroup(const std::string & name);
UInt getNbElementGroups(UInt dimension = _all_dimensions) const;
UInt getNbNodeGroups() { return node_groups.size(); };
bool elementGroupExists(const std::string & name) {
return element_groups.find(name) != element_groups.end();
}
bool nodeGroupExists(const std::string & name) {
return node_groups.find(name) != node_groups.end();
}
private:
template <typename GroupsType>
void renameGroup(GroupsType & groups, const std::string & name,
const std::string & new_name);
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
/// id to create element and node groups
ID id;
- /// memory_id to create element and node groups
- MemoryID memory_id;
/// list of the node groups managed
NodeGroups node_groups;
/// list of the element groups managed
ElementGroups element_groups;
/// Mesh to which the element belongs
Mesh & mesh;
};
/// standard output stream operator
inline std::ostream & operator<<(std::ostream & stream,
const GroupManager & _this) {
_this.printself(stream);
return stream;
}
} // namespace akantu
#endif /* AKANTU_GROUP_MANAGER_HH_ */
diff --git a/src/mesh/mesh.cc b/src/mesh/mesh.cc
index ed22a9000..624f79638 100644
--- a/src/mesh/mesh.cc
+++ b/src/mesh/mesh.cc
@@ -1,672 +1,666 @@
/**
* @file mesh.cc
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author David Simon Kammer <david.kammer@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
* @author Marco Vocialta <marco.vocialta@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Tue Feb 20 2018
*
* @brief class handling meshes
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#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 <algorithm>
/* -------------------------------------------------------------------------- */
#ifdef AKANTU_USE_IOHELPER
#include "dumper_field.hh"
#include "dumper_internal_material_field.hh"
#endif
/* -------------------------------------------------------------------------- */
#include <limits>
#include <sstream>
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
-Mesh::Mesh(UInt spatial_dimension, const ID & id, const MemoryID & memory_id,
- Communicator & communicator)
- : Memory(id, memory_id),
- GroupManager(*this, id + ":group_manager", memory_id),
- MeshData("mesh_data", id, memory_id),
- connectivities("connectivities", id, memory_id),
- ghosts_counters("ghosts_counters", id, memory_id),
- normals("normals", id, memory_id), spatial_dimension(spatial_dimension),
- size(spatial_dimension, 0.), bbox(spatial_dimension),
- bbox_local(spatial_dimension), communicator(&communicator) {
+Mesh::Mesh(UInt 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), normals("normals", id),
+ spatial_dimension(spatial_dimension), size(spatial_dimension, 0.),
+ bbox(spatial_dimension), bbox_local(spatial_dimension),
+ communicator(&communicator) {
AKANTU_DEBUG_IN();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
-Mesh::Mesh(UInt spatial_dimension, Communicator & communicator, const ID & id,
- const MemoryID & memory_id)
- : Mesh(spatial_dimension, id, memory_id, communicator) {
+Mesh::Mesh(UInt spatial_dimension, Communicator & communicator, const ID & id)
+ : Mesh(spatial_dimension, id, communicator) {
AKANTU_DEBUG_IN();
this->nodes =
std::make_shared<Array<Real>>(0, spatial_dimension, id + ":coordinates");
this->nodes_flags = std::make_shared<Array<NodeFlag>>(0, 1, NodeFlag::_normal,
id + ":nodes_flags");
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
-Mesh::Mesh(UInt spatial_dimension, const ID & id, const MemoryID & memory_id)
- : Mesh(spatial_dimension, Communicator::getStaticCommunicator(), id,
- memory_id) {}
+Mesh::Mesh(UInt spatial_dimension, const ID & id)
+ : Mesh(spatial_dimension, Communicator::getStaticCommunicator(), id) {}
/* -------------------------------------------------------------------------- */
Mesh::Mesh(UInt spatial_dimension, const std::shared_ptr<Array<Real>> & nodes,
- const ID & id, const MemoryID & memory_id)
- : Mesh(spatial_dimension, id, memory_id,
- Communicator::getStaticCommunicator()) {
+ 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<std::set<Element>>();
}
this->computeBoundingBox();
}
/* -------------------------------------------------------------------------- */
void Mesh::getBarycenters(Array<Real> & 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, UInt(std::get<0>(data)), ghost_type},
std::get<1>(data));
}
}
class FacetGlobalConnectivityAccessor : public DataAccessor<Element> {
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);
}
UInt getNbData(const Array<Element> & elements,
const SynchronizationTag & tag) const override {
UInt size = 0;
if (tag == SynchronizationTag::_smmc_facets_conn) {
UInt nb_nodes = Mesh::getNbNodesPerElementList(elements);
size += nb_nodes * sizeof(UInt);
}
return size;
}
void packData(CommunicationBuffer & buffer, const Array<Element> & 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);
+ 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<Element> & 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<UInt> global_connectivity;
};
/* -------------------------------------------------------------------------- */
Mesh & Mesh::initMeshFacets(const ID & id) {
AKANTU_DEBUG_IN();
if (mesh_facets) {
AKANTU_DEBUG_OUT();
return *mesh_facets;
}
mesh_facets = std::make_unique<Mesh>(spatial_dimension, this->nodes,
- getID() + ":" + id, getMemoryID());
+ 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<FacetSynchronizer>(
*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<std::string>("physical_names");
auto & phys_data = mesh_facets->getData<std::string>("physical_names");
phys_data.initialize(*mesh_facets, _spatial_dimension = spatial_dimension - 1,
_with_nb_element = true);
ElementTypeMapArray<Real> 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<Real> barycenter(spatial_dimension);
mesh.getBarycenter(element, barycenter);
auto norm_barycenter = barycenter.norm();
auto tolerance = Math::getTolerance();
if (norm_barycenter > tolerance) {
tolerance *= norm_barycenter;
}
// const auto & element_to_facet = mesh_facets->getElementToSubelement(
// element.type, element.ghost_type);
Vector<Real> 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<Real>::max();
#endif
// this is a spacial search coded the most inefficient way.
auto facet =
std::find_if(range.begin(), range.end(), [&](auto && data) {
// auto facet = std::get<0>(data);
// if (element_to_facet(facet)[1] == ElementNull)
// return false;
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, UInt(std::get<0>(*facet)),
- element.ghost_type};
+ element.ghost_type};
phys_data(facet_element) = mesh_phys_data(element);
},
_spatial_dimension = spatial_dimension - 1);
mesh_facets->createGroupsFromMeshData<std::string>("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<std::set<Element>>();
}
}
/* -------------------------------------------------------------------------- */
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::initNormals() {
normals.initialize(*this, _nb_component = spatial_dimension,
_spatial_dimension = spatial_dimension,
_element_kind = _ek_not_defined);
}
/* -------------------------------------------------------------------------- */
void Mesh::getGlobalConnectivity(
ElementTypeMapArray<UInt> & 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 = connectivities(type, ghost_type);
auto & g_connectivity = global_connectivity(type, ghost_type);
UInt nb_nodes = local_conn.size() * local_conn.getNbComponent();
std::transform(local_conn.begin_reinterpret(nb_nodes),
local_conn.end_reinterpret(nb_nodes),
g_connectivity.begin_reinterpret(nb_nodes),
[&](UInt l) -> UInt { 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 <typename T>
ElementTypeMap<UInt> Mesh::getNbDataPerElem(ElementTypeMapArray<T> & arrays) {
ElementTypeMap<UInt> nb_data_per_elem;
- for (auto type : arrays.elementTypes()) {
+ for (auto type : arrays.elementTypes(_element_kind = _ek_not_defined)) {
UInt 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<UInt>
Mesh::getNbDataPerElem(ElementTypeMapArray<Real> & array);
template ElementTypeMap<UInt>
Mesh::getNbDataPerElem(ElementTypeMapArray<UInt> & array);
/* -------------------------------------------------------------------------- */
#ifdef AKANTU_USE_IOHELPER
template <typename T>
std::shared_ptr<dumpers::Field>
Mesh::createFieldFromAttachedData(const std::string & field_id,
const std::string & group_name,
ElementKind element_kind) {
std::shared_ptr<dumpers::Field> field;
ElementTypeMapArray<T> * internal = nullptr;
try {
internal = &(this->getData<T>(field_id));
} catch (...) {
return nullptr;
}
ElementTypeMap<UInt> nb_data_per_elem = this->getNbDataPerElem(*internal);
field = this->createElementalField<T, dumpers::InternalMaterialField>(
*internal, group_name, this->spatial_dimension, element_kind,
nb_data_per_elem);
return field;
}
template std::shared_ptr<dumpers::Field>
Mesh::createFieldFromAttachedData<Real>(const std::string & field_id,
const std::string & group_name,
ElementKind element_kind);
template std::shared_ptr<dumpers::Field>
Mesh::createFieldFromAttachedData<UInt>(const std::string & field_id,
const std::string & group_name,
ElementKind element_kind);
#endif
/* -------------------------------------------------------------------------- */
void Mesh::distributeImpl(
Communicator & communicator,
const std::function<Int(const Element &, const Element &)> &
edge_weight_function [[gnu::unused]],
const std::function<Int(const Element &)> & 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<ElementSynchronizer>(
- *this, this->getID() + ":element_synchronizer", this->getMemoryID(),
- true);
+ *this, this->getID() + ":element_synchronizer", true);
this->node_synchronizer = std::make_unique<NodeSynchronizer>(
- *this, this->getID() + ":node_synchronizer", this->getMemoryID(), true);
+ *this, this->getID() + ":node_synchronizer", true);
Int psize = this->communicator->getNbProc();
if (psize > 1) {
#ifdef AKANTU_USE_SCOTCH
Int 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");
}
#endif
}
// if (psize > 1)
this->is_distributed = true;
this->computeBoundingBox();
}
/* -------------------------------------------------------------------------- */
void Mesh::getAssociatedElements(const Array<UInt> & node_list,
Array<Element> & elements) {
for (const auto & node : node_list) {
for (const auto & element : *nodes_to_elements[node]) {
elements.push_back(element);
}
}
}
/* -------------------------------------------------------------------------- */
void Mesh::getAssociatedElements(const UInt & node,
Array<Element> & elements) {
for (const auto & element : *nodes_to_elements[node])
elements.push_back(element);
}
/* -------------------------------------------------------------------------- */
void Mesh::fillNodesToElements(UInt dimension) {
Element e;
UInt nb_nodes = nodes->size();
this->nodes_to_elements.resize(nodes->size());
for (UInt 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<std::set<Element>>();
}
}
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;
UInt nb_element = this->getNbElement(type, ghost_type);
Array<UInt>::const_iterator<Vector<UInt>> conn_it =
connectivities(type, ghost_type)
.begin(Mesh::getNbNodesPerElement(type));
for (UInt el = 0; el < nb_element; ++el, ++conn_it) {
e.element = el;
const Vector<UInt> & conn = *conn_it;
for (UInt n = 0; n < conn.size(); ++n) {
nodes_to_elements[conn(n)]->insert(e);
}
}
}
}
}
/* -------------------------------------------------------------------------- */
std::tuple<UInt, UInt>
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<MeshGlobalDataUpdater> && global_data_updater) {
this->global_data_updater = std::move(global_data_updater);
}
/* -------------------------------------------------------------------------- */
void Mesh::eraseElements(const Array<Element> & elements) {
ElementTypeMap<UInt> 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;
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) = UInt(-1);
}
auto find_last_not_deleted = [](auto && array, Int start) -> Int {
do {
--start;
} while (start >= 0 and array[start] == UInt(-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 == UInt(-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->sendEvent(event);
}
} // namespace akantu
diff --git a/src/mesh/mesh.hh b/src/mesh/mesh.hh
index ce0d4a262..35279d603 100644
--- a/src/mesh/mesh.hh
+++ b/src/mesh/mesh.hh
@@ -1,704 +1,700 @@
/**
* @file mesh.hh
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author Dana Christen <dana.christen@epfl.ch>
* @author David Simon Kammer <david.kammer@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
* @author Marco Vocialta <marco.vocialta@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Mon Feb 19 2018
*
* @brief the class representing the meshes
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_MESH_HH_
#define AKANTU_MESH_HH_
/* -------------------------------------------------------------------------- */
#include "aka_array.hh"
#include "aka_bbox.hh"
#include "aka_event_handler_manager.hh"
-#include "aka_memory.hh"
#include "communicator.hh"
#include "dumpable.hh"
#include "element.hh"
#include "element_class.hh"
#include "element_type_map.hh"
#include "group_manager.hh"
#include "mesh_data.hh"
#include "mesh_events.hh"
/* -------------------------------------------------------------------------- */
#include <functional>
#include <set>
#include <unordered_map>
/* -------------------------------------------------------------------------- */
namespace akantu {
class ElementSynchronizer;
class NodeSynchronizer;
class PeriodicNodeSynchronizer;
class MeshGlobalDataUpdater;
} // namespace akantu
namespace akantu {
namespace {
DECLARE_NAMED_ARGUMENT(communicator);
DECLARE_NAMED_ARGUMENT(edge_weight_function);
DECLARE_NAMED_ARGUMENT(vertex_weight_function);
} // namespace
/* -------------------------------------------------------------------------- */
/* Mesh */
/* -------------------------------------------------------------------------- */
/**
* @class Mesh mesh.hh
*
* This class contaisn the coordinates of the nodes in the Mesh.nodes
* akant::Array, and the connectivity. The connectivity are stored in by element
* types.
*
* In order to loop on all element you have to loop on all types like this :
* @code{.cpp}
for(auto & type : mesh.elementTypes()) {
UInt nb_element = mesh.getNbElement(type);
const Array<UInt> & conn = mesh.getConnectivity(type);
for(UInt e = 0; e < nb_element; ++e) {
...
}
}
or
for_each_element(mesh, [](Element & element) {
std::cout << element << std::endl
});
@endcode
*/
-class Mesh : protected Memory,
- public EventHandlerManager<MeshEventHandler>,
+class Mesh : public EventHandlerManager<MeshEventHandler>,
public GroupManager,
public MeshData,
public Dumpable {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
private:
/// default constructor used for chaining, the last parameter is just to
/// differentiate constructors
- Mesh(UInt spatial_dimension, const ID & id, const MemoryID & memory_id,
+ Mesh(UInt spatial_dimension, const ID & id,
Communicator & communicator);
public:
/// constructor that create nodes coordinates array
- Mesh(UInt spatial_dimension, const ID & id = "mesh",
- const MemoryID & memory_id = 0);
+ Mesh(UInt spatial_dimension, const ID & id = "mesh");
/// mesh not distributed and not using the default communicator
Mesh(UInt spatial_dimension, Communicator & communicator,
- const ID & id = "mesh", const MemoryID & memory_id = 0);
+ const ID & id = "mesh");
/**
* constructor that use an existing nodes coordinates
* array, by getting the vector of coordinates
*/
Mesh(UInt spatial_dimension, const std::shared_ptr<Array<Real>> & nodes,
- const ID & id = "mesh", const MemoryID & memory_id = 0);
+ const ID & id = "mesh");
~Mesh() override;
/// read the mesh from a file
void read(const std::string & filename,
const MeshIOType & mesh_io_type = _miot_auto);
/// write the mesh to a file
void write(const std::string & filename,
const MeshIOType & mesh_io_type = _miot_auto);
protected:
void makeReady();
private:
/// initialize the connectivity to NULL and other stuff
void init();
/// function that computes the bounding box (fills xmin, xmax)
void computeBoundingBox();
/* ------------------------------------------------------------------------ */
/* Distributed memory methods and accessors */
/* ------------------------------------------------------------------------ */
public:
protected:
/// patitionate the mesh among the processors involved in their computation
virtual void distributeImpl(
Communicator & communicator,
const std::function<Int(const Element &, const Element &)> &
edge_weight_function,
const std::function<Int(const Element &)> & vertex_weight_function);
public:
/// with the arguments to pass to the partitionner
template <typename... pack>
std::enable_if_t<are_named_argument<pack...>::value>
distribute(pack &&... _pack) {
distributeImpl(
OPTIONAL_NAMED_ARG(communicator, Communicator::getStaticCommunicator()),
OPTIONAL_NAMED_ARG(edge_weight_function,
[](auto &&, auto &&) { return 1; }),
OPTIONAL_NAMED_ARG(vertex_weight_function, [](auto &&) { return 1; }));
}
/// defines is the mesh is distributed or not
inline bool isDistributed() const { return this->is_distributed; }
/* ------------------------------------------------------------------------ */
/* Periodicity methods and accessors */
/* ------------------------------------------------------------------------ */
public:
/// set the periodicity in a given direction
void makePeriodic(const SpatialDirection & direction);
void makePeriodic(const SpatialDirection & direction, const ID & list_1,
const ID & list_2);
protected:
void makePeriodic(const SpatialDirection & direction,
const Array<UInt> & list_left,
const Array<UInt> & list_right);
/// Removes the face that the mesh is periodic
void wipePeriodicInfo();
inline void addPeriodicSlave(UInt slave, UInt master);
template <typename T>
void synchronizePeriodicSlaveDataWithMaster(Array<T> & data);
// update the periodic synchronizer (creates it if it does not exists)
void updatePeriodicSynchronizer();
public:
/// defines if the mesh is periodic or not
inline bool isPeriodic() const { return this->is_periodic; }
inline bool isPeriodic(const SpatialDirection & /*direction*/) const {
return this->is_periodic;
}
class PeriodicSlaves;
/// get the master node for a given slave nodes, except if node not a slave
inline UInt getPeriodicMaster(UInt slave) const;
/// get an iterable list of slaves for a given master node
inline decltype(auto) getPeriodicSlaves(UInt master) const;
/* ------------------------------------------------------------------------ */
/* General Methods */
/* ------------------------------------------------------------------------ */
public:
/// function to print the containt of the class
void printself(std::ostream & stream, int indent = 0) const override;
/// extract coordinates of nodes from an element
template <typename T>
inline void
extractNodalValuesFromElement(const Array<T> & nodal_values, T * local_coord,
const UInt * connectivity, UInt n_nodes,
UInt nb_degree_of_freedom) const;
// /// extract coordinates of nodes from a reversed element
// inline void extractNodalCoordinatesFromPBCElement(Real * local_coords,
// UInt * connectivity,
// UInt n_nodes);
/// add a Array of connectivity for the given ElementType and GhostType .
inline void addConnectivityType(ElementType type,
GhostType ghost_type = _not_ghost);
/* ------------------------------------------------------------------------ */
template <class Event> inline void sendEvent(Event & event) {
// if(event.getList().size() != 0)
EventHandlerManager<MeshEventHandler>::sendEvent<Event>(event);
}
/// prepare the event to remove the elements listed
void eraseElements(const Array<Element> & elements);
/* ------------------------------------------------------------------------ */
template <typename T>
inline void removeNodesFromArray(Array<T> & vect,
const Array<UInt> & new_numbering);
/// initialize normals
void initNormals();
/// init facets' mesh
Mesh & initMeshFacets(const ID & id = "mesh_facets");
/// define parent mesh
void defineMeshParent(const Mesh & mesh);
/// get global connectivity array
void getGlobalConnectivity(ElementTypeMapArray<UInt> & global_connectivity);
public:
void getAssociatedElements(const Array<UInt> & node_list,
Array<Element> & elements);
void getAssociatedElements(const UInt & node,
Array<Element> & elements);
public:
/// fills the nodes_to_elements for given dimension elements
void fillNodesToElements(UInt dimension = _all_dimensions);
private:
/// update the global ids, nodes type, ...
std::tuple<UInt, UInt> updateGlobalData(NewNodesEvent & nodes_event,
NewElementsEvent & elements_event);
void registerGlobalDataUpdater(
std::unique_ptr<MeshGlobalDataUpdater> && global_data_updater);
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
/// get the id of the mesh
- AKANTU_GET_MACRO(ID, Memory::id, const ID &);
-
- /// get the id of the mesh
- AKANTU_GET_MACRO(MemoryID, Memory::memory_id, const MemoryID &);
+ AKANTU_GET_MACRO(ID, id, const ID &);
/// get the spatial dimension of the mesh = number of component of the
/// coordinates
AKANTU_GET_MACRO(SpatialDimension, spatial_dimension, UInt);
/// get the nodes Array aka coordinates
AKANTU_GET_MACRO(Nodes, *nodes, const Array<Real> &);
AKANTU_GET_MACRO_NOT_CONST(Nodes, *nodes, Array<Real> &);
/// get the normals for the elements
AKANTU_GET_MACRO_BY_ELEMENT_TYPE(Normals, normals, Real);
/// get the number of nodes
AKANTU_GET_MACRO(NbNodes, nodes->size(), UInt);
/// get the Array of global ids of the nodes (only used in parallel)
AKANTU_GET_MACRO(GlobalNodesIds, *nodes_global_ids, const Array<UInt> &);
// AKANTU_GET_MACRO_NOT_CONST(GlobalNodesIds, *nodes_global_ids, Array<UInt>
// &);
/// get the global id of a node
inline UInt getNodeGlobalId(UInt local_id) const;
/// get the global id of a node
inline UInt getNodeLocalId(UInt global_id) const;
/// get the global number of nodes
inline UInt getNbGlobalNodes() const;
/// get the nodes type Array
AKANTU_GET_MACRO(NodesFlags, *nodes_flags, const Array<NodeFlag> &);
protected:
AKANTU_GET_MACRO_NOT_CONST(NodesFlags, *nodes_flags, Array<NodeFlag> &);
public:
inline NodeFlag getNodeFlag(UInt local_id) const;
inline Int getNodePrank(UInt local_id) const;
/// say if a node is a pure ghost node
inline bool isPureGhostNode(UInt n) const;
/// say if a node is pur local or master node
inline bool isLocalOrMasterNode(UInt n) const;
inline bool isLocalNode(UInt n) const;
inline bool isMasterNode(UInt n) const;
inline bool isSlaveNode(UInt n) const;
inline bool isPeriodicSlave(UInt n) const;
inline bool isPeriodicMaster(UInt n) const;
const Vector<Real> & getLowerBounds() const { return bbox.getLowerBounds(); }
const Vector<Real> & getUpperBounds() const { return bbox.getUpperBounds(); }
AKANTU_GET_MACRO(BBox, bbox, const BBox &);
const Vector<Real> & getLocalLowerBounds() const {
return bbox_local.getLowerBounds();
}
const Vector<Real> & getLocalUpperBounds() const {
return bbox_local.getUpperBounds();
}
AKANTU_GET_MACRO(LocalBBox, bbox_local, const BBox &);
/// get the connectivity Array for a given type
AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(Connectivity, connectivities, UInt);
AKANTU_GET_MACRO_BY_ELEMENT_TYPE(Connectivity, connectivities, UInt);
AKANTU_GET_MACRO(Connectivities, connectivities,
const ElementTypeMapArray<UInt> &);
/// get the number of element of a type in the mesh
inline UInt getNbElement(ElementType type,
GhostType ghost_type = _not_ghost) const;
/// get the number of element for a given ghost_type and a given dimension
inline UInt getNbElement(UInt spatial_dimension = _all_dimensions,
GhostType ghost_type = _not_ghost,
ElementKind kind = _ek_not_defined) const;
/// compute the barycenter of a given element
inline void getBarycenter(const Element & element,
Vector<Real> & barycenter) const;
void getBarycenters(Array<Real> & barycenter, ElementType type,
GhostType ghost_type) const;
/// get the element connected to a subelement (element of lower dimension)
const auto & getElementToSubelement() const;
/// get the element connected to a subelement
const auto & getElementToSubelement(ElementType el_type,
GhostType ghost_type = _not_ghost) const;
/// get the elements connected to a subelement
const auto & getElementToSubelement(const Element & element) const;
/// get the subelement (element of lower dimension) connected to a element
const auto & getSubelementToElement() const;
/// get the subelement connected to an element
const auto &
getSubelementToElement(ElementType el_type,
GhostType ghost_type = _not_ghost) const;
/// get the subelement (element of lower dimension) connected to a element
VectorProxy<Element> getSubelementToElement(const Element & element) const;
/// get connectivity of a given element
inline VectorProxy<UInt> getConnectivity(const Element & element) const;
inline Vector<UInt>
getConnectivityWithPeriodicity(const Element & element) const;
protected:
/// get the element connected to a subelement (element of lower dimension)
auto & getElementToSubelementNC();
auto & getSubelementToElementNC();
inline auto & getElementToSubelementNC(const Element & element);
inline VectorProxy<Element> getSubelementToElementNC(const Element & element);
/// get the element connected to a subelement
auto & getElementToSubelementNC(ElementType el_type,
GhostType ghost_type = _not_ghost);
/// get the subelement connected to an element
auto & getSubelementToElementNC(ElementType el_type,
GhostType ghost_type = _not_ghost);
inline VectorProxy<UInt> getConnectivityNC(const Element & element);
public:
/// get a name field associated to the mesh
template <typename T>
inline const Array<T> & getData(const ID & data_name, ElementType el_type,
GhostType ghost_type = _not_ghost) const;
/// get a name field associated to the mesh
template <typename T>
inline Array<T> & getData(const ID & data_name, ElementType el_type,
GhostType ghost_type = _not_ghost);
/// get a name field associated to the mesh
template <typename T>
inline const ElementTypeMapArray<T> & getData(const ID & data_name) const;
/// get a name field associated to the mesh
template <typename T>
inline ElementTypeMapArray<T> & getData(const ID & data_name);
template <typename T>
ElementTypeMap<UInt> getNbDataPerElem(ElementTypeMapArray<T> & array);
template <typename T>
std::shared_ptr<dumpers::Field>
createFieldFromAttachedData(const std::string & field_id,
const std::string & group_name,
ElementKind element_kind);
/// templated getter returning the pointer to data in MeshData (modifiable)
template <typename T>
inline Array<T> &
getDataPointer(const std::string & data_name, ElementType el_type,
GhostType ghost_type = _not_ghost, UInt nb_component = 1,
bool size_to_nb_element = true,
bool resize_with_parent = false);
template <typename T>
inline Array<T> & getDataPointer(const ID & data_name, ElementType el_type,
GhostType ghost_type, UInt nb_component,
bool size_to_nb_element,
bool resize_with_parent, const T & defaul_);
/// Facets mesh accessor
inline const Mesh & getMeshFacets() const;
inline Mesh & getMeshFacets();
inline auto hasMeshFacets() const { return mesh_facets != nullptr; }
/// Parent mesh accessor
inline const Mesh & getMeshParent() const;
inline bool isMeshFacets() const { return this->is_mesh_facets; }
/// return the dumper from a group and and a dumper name
DumperIOHelper & getGroupDumper(const std::string & dumper_name,
const std::string & group_name);
/* ------------------------------------------------------------------------ */
/* Wrappers on ElementClass functions */
/* ------------------------------------------------------------------------ */
public:
/// get the number of nodes per element for a given element type
static inline UInt getNbNodesPerElement(ElementType type);
/// get the number of nodes per element for a given element type considered as
/// a first order element
static inline ElementType getP1ElementType(ElementType type);
/// get the kind of the element type
static inline ElementKind getKind(ElementType type);
/// get spatial dimension of a type of element
static inline UInt getSpatialDimension(ElementType type);
/// get the natural space dimension of a type of element
static inline UInt getNaturalSpaceDimension(const ElementType & type);
/// get number of facets of a given element type
static inline UInt getNbFacetsPerElement(ElementType type);
/// get number of facets of a given element type
static inline UInt getNbFacetsPerElement(ElementType type, UInt t);
/// get local connectivity of a facet for a given facet type
static inline auto getFacetLocalConnectivity(ElementType type, UInt t = 0);
/// get connectivity of facets for a given element
inline auto getFacetConnectivity(const Element & element, UInt t = 0) const;
/// get the number of type of the surface element associated to a given
/// element type
static inline UInt getNbFacetTypes(ElementType type, UInt t = 0);
/// get the type of the surface element associated to a given element
static inline constexpr auto getFacetType(ElementType type, UInt t = 0);
/// get all the type of the surface element associated to a given element
static inline constexpr auto getAllFacetTypes(ElementType type);
/// get the number of nodes in the given element list
static inline UInt getNbNodesPerElementList(const Array<Element> & elements);
/* ------------------------------------------------------------------------ */
/* Element type Iterator */
/* ------------------------------------------------------------------------ */
using type_iterator [[deprecated]] =
ElementTypeMapArray<UInt, ElementType>::type_iterator;
using ElementTypesIteratorHelper =
ElementTypeMapArray<UInt, ElementType>::ElementTypesIteratorHelper;
template <typename... pack>
ElementTypesIteratorHelper elementTypes(pack &&... _pack) const;
[[deprecated("Use elementTypes instead")]] inline decltype(auto)
firstType(UInt dim = _all_dimensions, GhostType ghost_type = _not_ghost,
ElementKind kind = _ek_regular) const {
return connectivities.elementTypes(dim, ghost_type, kind).begin();
}
[[deprecated("Use elementTypes instead")]] inline decltype(auto)
lastType(UInt dim = _all_dimensions, GhostType ghost_type = _not_ghost,
ElementKind kind = _ek_regular) const {
return connectivities.elementTypes(dim, ghost_type, kind).end();
}
AKANTU_GET_MACRO(ElementSynchronizer, *element_synchronizer,
const ElementSynchronizer &);
AKANTU_GET_MACRO_NOT_CONST(ElementSynchronizer, *element_synchronizer,
ElementSynchronizer &);
AKANTU_GET_MACRO(NodeSynchronizer, *node_synchronizer,
const NodeSynchronizer &);
AKANTU_GET_MACRO_NOT_CONST(NodeSynchronizer, *node_synchronizer,
NodeSynchronizer &);
AKANTU_GET_MACRO(PeriodicNodeSynchronizer, *periodic_node_synchronizer,
const PeriodicNodeSynchronizer &);
AKANTU_GET_MACRO_NOT_CONST(PeriodicNodeSynchronizer,
*periodic_node_synchronizer,
PeriodicNodeSynchronizer &);
// AKANTU_GET_MACRO_NOT_CONST(Communicator, *communicator, StaticCommunicator
// &);
AKANTU_GET_MACRO(Communicator, *communicator, const auto &);
AKANTU_GET_MACRO_NOT_CONST(Communicator, *communicator, auto &);
AKANTU_GET_MACRO(PeriodicMasterSlaves, periodic_master_slave, const auto &);
/* ------------------------------------------------------------------------ */
/* Private methods for friends */
/* ------------------------------------------------------------------------ */
private:
friend class MeshAccessor;
friend class MeshUtils;
AKANTU_GET_MACRO(NodesPointer, *nodes, Array<Real> &);
/// get a pointer to the nodes_global_ids Array<UInt> and create it if
/// necessary
inline Array<UInt> & getNodesGlobalIdsPointer();
/// get a pointer to the nodes_type Array<Int> and create it if necessary
inline Array<NodeFlag> & getNodesFlagsPointer();
/// get a pointer to the connectivity Array for the given type and create it
/// if necessary
inline Array<UInt> &
getConnectivityPointer(ElementType type, GhostType ghost_type = _not_ghost);
/// get the ghost element counter
inline Array<UInt> & getGhostsCounters(ElementType type,
GhostType ghost_type = _ghost) {
AKANTU_DEBUG_ASSERT(ghost_type != _not_ghost,
"No ghost counter for _not_ghost elements");
return ghosts_counters(type, ghost_type);
}
/// get a pointer to the element_to_subelement Array for the given type and
/// create it if necessary
inline Array<std::vector<Element>> &
getElementToSubelementPointer(ElementType type,
GhostType ghost_type = _not_ghost);
/// get a pointer to the subelement_to_element Array for the given type and
/// create it if necessary
inline Array<Element> &
getSubelementToElementPointer(ElementType type,
GhostType ghost_type = _not_ghost);
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
private:
+ ID id;
+
/// array of the nodes coordinates
std::shared_ptr<Array<Real>> nodes;
/// global node ids
std::shared_ptr<Array<UInt>> nodes_global_ids;
/// node flags (shared/periodic/...)
std::shared_ptr<Array<NodeFlag>> nodes_flags;
/// processor handling the node when not local or master
std::unordered_map<UInt, Int> nodes_prank;
/// global number of nodes;
UInt nb_global_nodes{0};
/// all class of elements present in this mesh (for heterogenous meshes)
ElementTypeMapArray<UInt> connectivities;
/// count the references on ghost elements
ElementTypeMapArray<UInt> ghosts_counters;
/// map to normals for all class of elements present in this mesh
ElementTypeMapArray<Real> normals;
/// the spatial dimension of this mesh
UInt spatial_dimension{0};
/// size covered by the mesh on each direction
Vector<Real> size;
/// global bounding box
BBox bbox;
/// local bounding box
BBox bbox_local;
/// Extra data loaded from the mesh file
// MeshData mesh_data;
/// facets' mesh
std::unique_ptr<Mesh> mesh_facets;
/// parent mesh (this is set for mesh_facets meshes)
const Mesh * mesh_parent{nullptr};
/// defines if current mesh is mesh_facets or not
bool is_mesh_facets{false};
/// defines if the mesh is centralized or distributed
bool is_distributed{false};
/// defines if the mesh is periodic
bool is_periodic{false};
/// Communicator on which mesh is distributed
Communicator * communicator;
/// Element synchronizer
std::unique_ptr<ElementSynchronizer> element_synchronizer;
/// Node synchronizer
std::unique_ptr<NodeSynchronizer> node_synchronizer;
/// Node synchronizer for periodic nodes
std::unique_ptr<PeriodicNodeSynchronizer> periodic_node_synchronizer;
using NodesToElements = std::vector<std::unique_ptr<std::set<Element>>>;
/// class to update global data using external knowledge
std::unique_ptr<MeshGlobalDataUpdater> global_data_updater;
/// This info is stored to simplify the dynamic changes
NodesToElements nodes_to_elements;
/// periodicity local info
std::unordered_map<UInt, UInt> periodic_slave_master;
std::unordered_multimap<UInt, UInt> periodic_master_slave;
};
/// standard output stream operator
inline std::ostream & operator<<(std::ostream & stream, const Mesh & _this) {
_this.printself(stream);
return stream;
}
} // namespace akantu
/* -------------------------------------------------------------------------- */
/* Inline functions */
/* -------------------------------------------------------------------------- */
#include "element_type_map_tmpl.hh"
#include "mesh_inline_impl.hh"
#endif /* AKANTU_MESH_HH_ */
diff --git a/src/mesh/mesh_accessor.hh b/src/mesh/mesh_accessor.hh
index 8e8ecd599..524c77fb9 100644
--- a/src/mesh/mesh_accessor.hh
+++ b/src/mesh/mesh_accessor.hh
@@ -1,210 +1,219 @@
/**
* @file mesh_accessor.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Tue Jun 30 2015
* @date last modification: Tue Sep 19 2017
*
* @brief this class allow to access some private member of mesh it is used for
* IO for examples
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "mesh.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_MESH_ACCESSOR_HH_
#define AKANTU_MESH_ACCESSOR_HH_
namespace akantu {
class NodeSynchronizer;
class ElementSynchronizer;
class MeshGlobalDataUpdater;
} // namespace akantu
namespace akantu {
class MeshAccessor {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
explicit MeshAccessor(Mesh & mesh) : _mesh(mesh) {}
virtual ~MeshAccessor() = default;
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
/// get the global number of nodes
inline UInt getNbGlobalNodes() const { return this->_mesh.nb_global_nodes; }
/// set the global number of nodes
inline void setNbGlobalNodes(UInt nb_global_nodes) {
this->_mesh.nb_global_nodes = nb_global_nodes;
}
/// set the mesh as being distributed
inline void setDistributed() { this->_mesh.is_distributed = true; }
/// get a pointer to the nodes_global_ids Array<UInt> and create it if
/// necessary
inline auto & getNodesGlobalIds() {
return this->_mesh.getNodesGlobalIdsPointer();
}
/// get a pointer to the nodes_type Array<Int> and create it if necessary
inline auto & getNodesFlags() { return this->_mesh.getNodesFlags(); }
/// get a pointer to the nodes_type Array<Int> and create it if necessary
inline void setNodePrank(UInt node, Int prank) {
this->_mesh.nodes_prank[node] = prank;
}
/// get a pointer to the coordinates Array
inline auto & getNodes() { return this->_mesh.getNodesPointer(); }
/// get a pointer to the coordinates Array
inline auto getNodesSharedPtr() { return this->_mesh.nodes; }
/// get the connectivities
inline auto & getConnectivities() { return this->_mesh.connectivities; }
/// get the connectivity Array for the given type and create it
/// if necessary
inline auto & getConnectivity(ElementType type,
GhostType ghost_type = _not_ghost) {
return this->_mesh.getConnectivityPointer(type, ghost_type);
}
+ /// resize the connectivity (use carefully)
+ inline void resizeConnectivity(UInt new_size, ElementType type,
+ GhostType ghost_type = _not_ghost) {
+ this->getConnectivity(type, ghost_type).resize(new_size, UInt(-1));
+ }
+
+ /// resize the nodes (use carefully)
+ inline void resizeNodes(UInt new_size) {
+ this->getNodes().resize(new_size, UInt(-1));
+ }
+
/// get the connectivity for the given element
inline decltype(auto) getConnectivity(const Element & element) {
return this->_mesh.getConnectivityNC(element);
}
/// get the ghost element counter
inline auto & getGhostsCounters(ElementType type,
GhostType ghost_type = _ghost) {
return this->_mesh.getGhostsCounters(type, ghost_type);
}
/// get the element_to_subelement Array for the given type and
/// create it if necessary
- inline auto &
- getElementToSubelement(ElementType type,
- GhostType ghost_type = _not_ghost) {
+ inline auto & getElementToSubelement(ElementType type,
+ GhostType ghost_type = _not_ghost) {
return this->_mesh.getElementToSubelementPointer(type, ghost_type);
}
inline decltype(auto)
getElementToSubelementNC(const ElementType & type,
- const GhostType & ghost_type = _not_ghost) {
+ const GhostType & ghost_type = _not_ghost) {
return this->_mesh.getElementToSubelementNC(type, ghost_type);
}
/// get the subelement_to_element Array for the given type and
/// create it if necessary
- inline auto &
- getSubelementToElement(ElementType type,
- GhostType ghost_type = _not_ghost) {
+ inline auto & getSubelementToElement(ElementType type,
+ GhostType ghost_type = _not_ghost) {
return this->_mesh.getSubelementToElementPointer(type, ghost_type);
}
inline decltype(auto)
getSubelementToElementNC(const ElementType & type,
const GhostType & ghost_type = _not_ghost) {
return this->_mesh.getSubelementToElementNC(type, ghost_type);
}
/// get the element_to_subelement, creates it if necessary
inline decltype(auto) getElementToSubelement() {
return this->_mesh.getElementToSubelementNC();
}
/// get subelement_to_element, creates it if necessary
inline decltype(auto) getSubelementToElement() {
return this->_mesh.getSubelementToElementNC();
}
/// get a pointer to the element_to_subelement Array for element and
/// create it if necessary
inline decltype(auto) getElementToSubelement(const Element & element) {
return this->_mesh.getElementToSubelementNC(element);
}
/// get a pointer to the subelement_to_element Array for the given element and
/// create it if necessary
inline decltype(auto) getSubelementToElement(const Element & element) {
return this->_mesh.getSubelementToElementNC(element);
}
template <typename T>
- inline auto &
- getData(const std::string & data_name, ElementType el_type,
- GhostType ghost_type = _not_ghost, UInt nb_component = 1,
- bool size_to_nb_element = true, bool resize_with_parent = false) {
+ inline auto & getData(const std::string & data_name, ElementType el_type,
+ GhostType ghost_type = _not_ghost,
+ UInt nb_component = 1, bool size_to_nb_element = true,
+ bool resize_with_parent = false) {
return this->_mesh.getDataPointer<T>(data_name, el_type, ghost_type,
nb_component, size_to_nb_element,
resize_with_parent);
}
/// get the node synchonizer
auto & getNodeSynchronizer() { return *this->_mesh.node_synchronizer; }
/// get the element synchonizer
auto & getElementSynchronizer() { return *this->_mesh.element_synchronizer; }
decltype(auto) updateGlobalData(NewNodesEvent & nodes_event,
NewElementsEvent & elements_event) {
return this->_mesh.updateGlobalData(nodes_event, elements_event);
}
void registerGlobalDataUpdater(
std::unique_ptr<MeshGlobalDataUpdater> && global_data_updater) {
this->_mesh.registerGlobalDataUpdater(
std::forward<std::unique_ptr<MeshGlobalDataUpdater>>(
global_data_updater));
}
/* ------------------------------------------------------------------------ */
void makeReady() { this->_mesh.makeReady(); }
/* ------------------------------------------------------------------------ */
void addPeriodicSlave(UInt slave, UInt master) {
this->_mesh.addPeriodicSlave(slave, master);
}
void markMeshPeriodic() {
for (UInt s : arange(this->_mesh.spatial_dimension)) {
this->_mesh.is_periodic |= 1 << s;
}
}
void wipePeriodicInfo() { this->_mesh.wipePeriodicInfo(); }
private:
Mesh & _mesh;
};
} // namespace akantu
#endif /* AKANTU_MESH_ACCESSOR_HH_ */
diff --git a/src/mesh/mesh_data.cc b/src/mesh/mesh_data.cc
index 8ebbe2750..ff1b628cc 100644
--- a/src/mesh/mesh_data.cc
+++ b/src/mesh/mesh_data.cc
@@ -1,39 +1,38 @@
/**
* @file mesh_data.cc
*
* @author Dana Christen <dana.christen@gmail.com>
*
* @date creation: Fri Apr 13 2012
* @date last modification: Mon Jun 19 2017
*
* @brief Stores generic data loaded from the mesh file
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "mesh_data.hh"
#include "mesh.hh"
namespace akantu {
-MeshData::MeshData(const ID & _id, const ID & parent_id,
- const MemoryID & mem_id)
- : _id(parent_id + ":" + _id), _memory_id(mem_id) {}
+MeshData::MeshData(const ID & _id, const ID & parent_id)
+ : _id(parent_id + ":" + _id) {}
} // namespace akantu
diff --git a/src/mesh/mesh_data.hh b/src/mesh/mesh_data.hh
index 2815aa9eb..ddab17361 100644
--- a/src/mesh/mesh_data.hh
+++ b/src/mesh/mesh_data.hh
@@ -1,191 +1,188 @@
/**
* @file mesh_data.hh
*
* @author Dana Christen <dana.christen@gmail.com>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri May 03 2013
* @date last modification: Mon Dec 18 2017
*
* @brief Stores generic data loaded from the mesh file
*
*
* Copyright (©) 2014-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_MESH_DATA_HH_
#define AKANTU_MESH_DATA_HH_
/* -------------------------------------------------------------------------- */
-#include "aka_memory.hh"
#include "element_type_map.hh"
#include <map>
#include <string>
/* -------------------------------------------------------------------------- */
namespace akantu {
#define AKANTU_MESH_DATA_TYPES \
((_int, Int))((_uint, UInt))((_real, Real))((_bool, bool))( \
(_element, Element))((_std_string, std::string))( \
(_std_vector_element, std::vector<Element>))
#define AKANTU_MESH_DATA_TUPLE_FIRST_ELEM(s, data, elem) \
BOOST_PP_TUPLE_ELEM(2, 0, elem)
enum class MeshDataTypeCode : int {
BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(AKANTU_MESH_DATA_TUPLE_FIRST_ELEM, ,
AKANTU_MESH_DATA_TYPES)),
_unknown
};
enum class MeshDataType {
_nodal,
_elemental,
};
class MeshData {
/* ------------------------------------------------------------------------ */
/* Typedefs */
/* ------------------------------------------------------------------------ */
private:
using TypeCode = MeshDataTypeCode;
using ElementalDataMap =
std::map<std::string, std::unique_ptr<ElementTypeMapBase>>;
using NodalDataMap = std::map<std::string, std::unique_ptr<ArrayBase>>;
using TypeCodeMap = std::map<std::string, TypeCode>;
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
- MeshData(const ID & id = "mesh_data", const ID & parent_id = "",
- const MemoryID & mem_id = 0);
+ MeshData(const ID & id = "mesh_data", const ID & parent_id = "");
/* ------------------------------------------------------------------------ */
/* Methods and accessors */
/* ------------------------------------------------------------------------ */
public:
/// tells if the given array exists
template <typename T>
bool hasData(const ID & data_name, ElementType elem_type,
GhostType ghost_type = _not_ghost) const;
/// tells if the given data exists
bool hasData(const ID & data_name,
MeshDataType type = MeshDataType::_elemental) const;
bool hasData(MeshDataType type = MeshDataType::_elemental) const;
/// get the names of the data stored in elemental_data
inline auto getTagNames(ElementType type,
GhostType ghost_type = _not_ghost) const;
/// get the names of the data stored in elemental_data
inline auto getTagNames() const;
/// get the type of the data stored in elemental_data
template <typename T> TypeCode getTypeCode() const;
inline TypeCode
getTypeCode(const ID & name,
MeshDataType type = MeshDataType::_elemental) const;
/// Get an existing elemental data array
template <typename T>
const Array<T> &
getElementalDataArray(const ID & data_name, ElementType elem_type,
GhostType ghost_type = _not_ghost) const;
template <typename T>
Array<T> & getElementalDataArray(const ID & data_name,
ElementType elem_type,
GhostType ghost_type = _not_ghost);
/// Get an elemental data array, if it does not exist: allocate it
template <typename T>
Array<T> & getElementalDataArrayAlloc(
const ID & data_name, ElementType elem_type,
GhostType ghost_type = _not_ghost, UInt nb_component = 1);
template <typename T>
inline UInt getNbComponentTemplated(const ID & name,
ElementType el_type,
GhostType ghost_type) const;
inline UInt getNbComponent(const ID & name, ElementType el_type,
GhostType ghost_type = _not_ghost) const;
inline UInt getNbComponent(const ID & name) const;
/// Get an existing elemental data
template <typename T>
const ElementTypeMapArray<T> & getElementalData(const ID & name) const;
template <typename T>
ElementTypeMapArray<T> & getElementalData(const ID & name);
template <typename T>
Array<T> & getNodalData(const ID & name, UInt nb_components = 1);
template <typename T> const Array<T> & getNodalData(const ID & name) const;
private:
/// Register new elemental data (and alloc data) with check if the name is
/// new
template <typename T>
ElementTypeMapArray<T> & registerElementalData(const ID & name);
inline void registerElementalData(const ID & name, TypeCode type);
/// Register new nodal data (and alloc data) with check if the name is
/// new
template <typename T>
Array<T> & registerNodalData(const ID & name, UInt nb_components = 1);
inline void registerNodalData(const ID & name, UInt nb_components,
TypeCode type);
/// Register new elemental data (add alloc data)
template <typename T>
ElementTypeMapArray<T> & allocElementalData(const ID & name);
/// Register new nodal data (add alloc data)
template <typename T>
Array<T> & allocNodalData(const ID & name, UInt nb_components);
friend class SlaveNodeInfoPerProc;
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
private:
ID _id;
- UInt _memory_id{0};
/// Map when elemental data is stored as ElementTypeMap
ElementalDataMap elemental_data;
/// Map when elemental data is stored as ElementTypeMap
NodalDataMap nodal_data;
/// Map when elementalType of the data stored in elemental_data
std::map<MeshDataType, TypeCodeMap> typecode_map{
{MeshDataType::_elemental, {}}, {MeshDataType::_nodal, {}}};
};
} // namespace akantu
#include "mesh_data_tmpl.hh"
#undef AKANTU_MESH_DATA_TUPLE_FIRST_ELEM
#endif /* AKANTU_MESH_DATA_HH_ */
diff --git a/src/mesh/mesh_data_tmpl.hh b/src/mesh/mesh_data_tmpl.hh
index b7a054373..0d7294441 100644
--- a/src/mesh/mesh_data_tmpl.hh
+++ b/src/mesh/mesh_data_tmpl.hh
@@ -1,412 +1,412 @@
/**
* @file mesh_data_tmpl.hh
*
* @author Dana Christen <dana.christen@gmail.com>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri May 03 2013
* @date last modification: Tue Feb 20 2018
*
* @brief Stores generic data loaded from the mesh file
*
*
* Copyright (©) 2014-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "mesh_data.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_MESH_DATA_TMPL_HH_
#define AKANTU_MESH_DATA_TMPL_HH_
namespace akantu {
#define AKANTU_MESH_DATA_OSTREAM(r, name, elem) \
case MeshDataTypeCode::BOOST_PP_TUPLE_ELEM(2, 0, elem): { \
stream << BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(2, 1, elem)); \
break; \
}
inline std::ostream & operator<<(std::ostream & stream,
const MeshDataTypeCode & type_code) {
switch (type_code) {
BOOST_PP_SEQ_FOR_EACH(AKANTU_MESH_DATA_OSTREAM, name,
AKANTU_MESH_DATA_TYPES)
default:
stream << "(unknown type)";
}
return stream;
}
#undef AKANTU_MESH_DATA_OSTREAM
#define MESH_DATA_GET_TYPE(r, data, type) \
template <> \
inline MeshDataTypeCode \
MeshData::getTypeCode<BOOST_PP_TUPLE_ELEM(2, 1, type)>() const { \
return MeshDataTypeCode::BOOST_PP_TUPLE_ELEM(2, 0, type); \
}
/* -------------------------------------------------------------------------- */
// get the type of the data stored in elemental_data
template <typename T> inline MeshDataTypeCode MeshData::getTypeCode() const {
AKANTU_ERROR("Type " << debug::demangle(typeid(T).name())
<< " not implemented by MeshData.");
}
/* -------------------------------------------------------------------------- */
BOOST_PP_SEQ_FOR_EACH(MESH_DATA_GET_TYPE, void, AKANTU_MESH_DATA_TYPES)
#undef MESH_DATA_GET_TYPE
inline MeshDataTypeCode MeshData::getTypeCode(const ID & name,
MeshDataType type) const {
auto it = typecode_map.at(type).find(name);
if (it == typecode_map.at(type).end()) {
AKANTU_EXCEPTION("No dataset named " << name << " found.");
}
return it->second;
}
/* -------------------------------------------------------------------------- */
// Register new elemental data templated (and alloc data) with check if the
// name is new
template <typename T>
ElementTypeMapArray<T> & MeshData::registerElementalData(const ID & name) {
auto it = elemental_data.find(name);
if (it == elemental_data.end()) {
return allocElementalData<T>(name);
}
AKANTU_DEBUG_INFO("Data named " << name << " already registered.");
return getElementalData<T>(name);
}
/* -------------------------------------------------------------------------- */
// Register new elemental data of a given MeshDataTypeCode with check if the
// name is new
#define AKANTU_MESH_DATA_CASE_MACRO(r, name, elem) \
case MeshDataTypeCode::BOOST_PP_TUPLE_ELEM(2, 0, elem): { \
registerElementalData<BOOST_PP_TUPLE_ELEM(2, 1, elem)>(name); \
break; \
}
inline void MeshData::registerElementalData(const ID & name,
MeshDataTypeCode type) {
switch (type) {
BOOST_PP_SEQ_FOR_EACH(AKANTU_MESH_DATA_CASE_MACRO, name,
AKANTU_MESH_DATA_TYPES)
default:
AKANTU_ERROR("Type " << type << "not implemented by MeshData.");
}
}
#undef AKANTU_MESH_DATA_CASE_MACRO
/* -------------------------------------------------------------------------- */
/// Register new elemental data (and alloc data)
template <typename T>
ElementTypeMapArray<T> & MeshData::allocElementalData(const ID & name) {
auto dataset =
- std::make_unique<ElementTypeMapArray<T>>(name, _id, _memory_id);
+ std::make_unique<ElementTypeMapArray<T>>(name, _id);
auto * dataset_typed = dataset.get();
elemental_data[name] = std::move(dataset);
typecode_map[MeshDataType::_elemental][name] = getTypeCode<T>();
return *dataset_typed;
}
/* -------------------------------------------------------------------------- */
// Register new nodal data templated (and alloc data) with check if the
// name is new
template <typename T>
Array<T> & MeshData::registerNodalData(const ID & name, UInt nb_components) {
auto it = nodal_data.find(name);
if (it == nodal_data.end()) {
return allocNodalData<T>(name, nb_components);
}
AKANTU_DEBUG_INFO("Data named " << name << " already registered.");
return getNodalData<T>(name);
}
/* -------------------------------------------------------------------------- */
// Register new elemental data of a given MeshDataTypeCode with check if the
// name is new
#define AKANTU_MESH_NODAL_DATA_CASE_MACRO(r, name, elem) \
case MeshDataTypeCode::BOOST_PP_TUPLE_ELEM(2, 0, elem): { \
registerNodalData<BOOST_PP_TUPLE_ELEM(2, 1, elem)>(name, nb_components); \
break; \
}
inline void MeshData::registerNodalData(const ID & name, UInt nb_components,
MeshDataTypeCode type) {
switch (type) {
BOOST_PP_SEQ_FOR_EACH(AKANTU_MESH_NODAL_DATA_CASE_MACRO, name,
AKANTU_MESH_DATA_TYPES)
default:
AKANTU_ERROR("Type " << type << "not implemented by MeshData.");
}
}
#undef AKANTU_MESH_NODAL_DATA_CASE_MACRO
/* -------------------------------------------------------------------------- */
/// Register new elemental data (and alloc data)
template <typename T>
Array<T> & MeshData::allocNodalData(const ID & name, UInt nb_components) {
auto dataset =
std::make_unique<Array<T>>(0, nb_components, T(), _id + ":" + name);
auto * dataset_typed = dataset.get();
nodal_data[name] = std::move(dataset);
typecode_map[MeshDataType::_nodal][name] = getTypeCode<T>();
return *dataset_typed;
}
/* -------------------------------------------------------------------------- */
template <typename T>
const Array<T> & MeshData::getNodalData(const ID & name) const {
auto it = nodal_data.find(name);
if (it == nodal_data.end()) {
AKANTU_EXCEPTION("No nodal dataset named " << name << " found.");
}
return aka::as_type<Array<T>>(*(it->second.get()));
}
/* -------------------------------------------------------------------------- */
// Get an existing elemental data
template <typename T>
Array<T> & MeshData::getNodalData(const ID & name, UInt nb_components) {
auto it = nodal_data.find(name);
if (it == nodal_data.end()) {
return allocNodalData<T>(name, nb_components);
}
return aka::as_type<Array<T>>(*(it->second.get()));
}
/* -------------------------------------------------------------------------- */
template <typename T>
const ElementTypeMapArray<T> &
MeshData::getElementalData(const ID & name) const {
auto it = elemental_data.find(name);
if (it == elemental_data.end()) {
AKANTU_EXCEPTION("No dataset named " << name << " found.");
}
return aka::as_type<ElementTypeMapArray<T>>(*(it->second.get()));
}
/* -------------------------------------------------------------------------- */
// Get an existing elemental data
template <typename T>
ElementTypeMapArray<T> & MeshData::getElementalData(const ID & name) {
auto it = elemental_data.find(name);
if (it == elemental_data.end()) {
return allocElementalData<T>(name);
}
return aka::as_type<ElementTypeMapArray<T>>(*(it->second.get()));
}
/* -------------------------------------------------------------------------- */
template <typename T>
bool MeshData::hasData(const ID & name, ElementType elem_type,
GhostType ghost_type) const {
auto it = elemental_data.find(name);
if (it == elemental_data.end()) {
return false;
}
auto & elem_map = aka::as_type<ElementTypeMapArray<T>>(*(it->second));
return elem_map.exists(elem_type, ghost_type);
}
/* -------------------------------------------------------------------------- */
inline bool MeshData::hasData(const ID & name, MeshDataType type) const {
if (type == MeshDataType::_elemental) {
auto it = elemental_data.find(name);
return (it != elemental_data.end());
}
if (type == MeshDataType::_nodal) {
auto it = nodal_data.find(name);
return (it != nodal_data.end());
}
return false;
}
/* -------------------------------------------------------------------------- */
inline bool MeshData::hasData(MeshDataType type) const {
switch (type) {
case MeshDataType::_elemental:
return (not elemental_data.empty());
case MeshDataType::_nodal:
return (not nodal_data.empty());
}
return false;
}
/* -------------------------------------------------------------------------- */
template <typename T>
const Array<T> &
MeshData::getElementalDataArray(const ID & name, ElementType elem_type,
GhostType ghost_type) const {
auto it = elemental_data.find(name);
if (it == elemental_data.end()) {
AKANTU_EXCEPTION("Data named " << name
<< " not registered for type: " << elem_type
<< " - ghost_type:" << ghost_type << "!");
}
return aka::as_type<ElementTypeMapArray<T>>(*(it->second))(elem_type,
ghost_type);
}
template <typename T>
Array<T> & MeshData::getElementalDataArray(const ID & name,
ElementType elem_type,
GhostType ghost_type) {
auto it = elemental_data.find(name);
if (it == elemental_data.end()) {
AKANTU_EXCEPTION("Data named " << name
<< " not registered for type: " << elem_type
<< " - ghost_type:" << ghost_type << "!");
}
return aka::as_type<ElementTypeMapArray<T>>(*(it->second.get()))(elem_type,
ghost_type);
}
/* -------------------------------------------------------------------------- */
// Get an elemental data array, if it does not exist: allocate it
template <typename T>
Array<T> & MeshData::getElementalDataArrayAlloc(const ID & name,
ElementType elem_type,
GhostType ghost_type,
UInt nb_component) {
auto it = elemental_data.find(name);
ElementTypeMapArray<T> * dataset;
if (it == elemental_data.end()) {
dataset = &allocElementalData<T>(name);
} else {
dataset = dynamic_cast<ElementTypeMapArray<T> *>(it->second.get());
}
AKANTU_DEBUG_ASSERT(
getTypeCode<T>() ==
typecode_map.at(MeshDataType::_elemental).find(name)->second,
"Function getElementalDataArrayAlloc called with the wrong type!");
if (!(dataset->exists(elem_type, ghost_type))) {
dataset->alloc(0, nb_component, elem_type, ghost_type);
}
return (*dataset)(elem_type, ghost_type);
}
/* -------------------------------------------------------------------------- */
#define AKANTU_MESH_DATA_CASE_MACRO(r, name, elem) \
case MeshDataTypeCode::BOOST_PP_TUPLE_ELEM(2, 0, elem): { \
nb_comp = getNbComponentTemplated<BOOST_PP_TUPLE_ELEM(2, 1, elem)>( \
name, el_type, ghost_type); \
break; \
}
inline UInt MeshData::getNbComponent(const ID & name,
ElementType el_type,
GhostType ghost_type) const {
auto it = typecode_map.at(MeshDataType::_elemental).find(name);
UInt nb_comp(0);
if (it == typecode_map.at(MeshDataType::_elemental).end()) {
AKANTU_EXCEPTION("Could not determine the type held in dataset "
<< name << " for type: " << el_type
<< " - ghost_type:" << ghost_type << ".");
}
MeshDataTypeCode type = it->second;
switch (type) {
BOOST_PP_SEQ_FOR_EACH(AKANTU_MESH_DATA_CASE_MACRO, name,
AKANTU_MESH_DATA_TYPES)
default:
AKANTU_ERROR(
"Could not call the correct instance of getNbComponentTemplated.");
break;
}
return nb_comp;
}
#undef AKANTU_MESH_DATA_CASE_MACRO
/* -------------------------------------------------------------------------- */
template <typename T>
inline UInt
MeshData::getNbComponentTemplated(const ID & name, ElementType el_type,
GhostType ghost_type) const {
return getElementalDataArray<T>(name, el_type, ghost_type).getNbComponent();
}
/* -------------------------------------------------------------------------- */
inline UInt MeshData::getNbComponent(const ID & name) const {
auto it = nodal_data.find(name);
if (it == nodal_data.end()) {
AKANTU_EXCEPTION("No nodal dataset registered with the name" << name
<< ".");
}
return it->second->getNbComponent();
}
/* -------------------------------------------------------------------------- */
// get the names of the data stored in elemental_data
#define AKANTU_MESH_DATA_CASE_MACRO(r, name, elem) \
case MeshDataTypeCode::BOOST_PP_TUPLE_ELEM(2, 0, elem): { \
ElementTypeMapArray<BOOST_PP_TUPLE_ELEM(2, 1, elem)> * dataset; \
dataset = \
dynamic_cast<ElementTypeMapArray<BOOST_PP_TUPLE_ELEM(2, 1, elem)> *>( \
it->second.get()); \
exists = dataset->exists(el_type, ghost_type); \
break; \
}
inline auto MeshData::getTagNames(ElementType el_type,
GhostType ghost_type) const {
std::vector<std::string> tags;
bool exists(false);
auto it = elemental_data.begin();
auto it_end = elemental_data.end();
for (; it != it_end; ++it) {
MeshDataTypeCode type = getTypeCode(it->first);
switch (type) {
BOOST_PP_SEQ_FOR_EACH(AKANTU_MESH_DATA_CASE_MACRO, ,
AKANTU_MESH_DATA_TYPES)
default:
AKANTU_ERROR("Could not determine the proper type to (dynamic-)cast.");
break;
}
if (exists) {
tags.push_back(it->first);
}
}
return tags;
}
#undef AKANTU_MESH_DATA_CASE_MACRO
/* -------------------------------------------------------------------------- */
inline auto MeshData::getTagNames() const {
std::vector<std::string> tags;
for (auto && data : nodal_data) {
tags.push_back(std::get<0>(data));
}
return tags;
}
/* -------------------------------------------------------------------------- */
} // namespace akantu
#endif /* AKANTU_MESH_DATA_TMPL_HH_ */
diff --git a/src/mesh/mesh_inline_impl.hh b/src/mesh/mesh_inline_impl.hh
index 627a3ee3e..651315b8c 100644
--- a/src/mesh/mesh_inline_impl.hh
+++ b/src/mesh/mesh_inline_impl.hh
@@ -1,776 +1,776 @@
/**
* @file mesh_inline_impl.hh
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author Dana Christen <dana.christen@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
* @author Marco Vocialta <marco.vocialta@epfl.ch>
*
* @date creation: Thu Jul 15 2010
* @date last modification: Mon Dec 18 2017
*
* @brief Implementation of the inline functions of the mesh class
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_iterators.hh"
#include "element_class.hh"
#include "mesh.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_MESH_INLINE_IMPL_HH_
#define AKANTU_MESH_INLINE_IMPL_HH_
namespace akantu {
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
inline ElementKind Element::kind() const { return Mesh::getKind(type); }
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
template <typename... pack>
Mesh::ElementTypesIteratorHelper Mesh::elementTypes(pack &&... _pack) const {
return connectivities.elementTypes(_pack...);
}
/* -------------------------------------------------------------------------- */
inline RemovedNodesEvent::RemovedNodesEvent(const Mesh & mesh,
const std::string & origin)
: MeshEvent<UInt>(origin),
new_numbering(mesh.getNbNodes(), 1, "new_numbering") {}
/* -------------------------------------------------------------------------- */
inline RemovedElementsEvent::RemovedElementsEvent(const Mesh & mesh,
const ID & new_numbering_id,
const std::string & origin)
: MeshEvent<Element>(origin),
- new_numbering(new_numbering_id, mesh.getID(), mesh.getMemoryID()) {}
+ new_numbering(new_numbering_id, mesh.getID()) {}
/* -------------------------------------------------------------------------- */
template <>
inline void Mesh::sendEvent<NewElementsEvent>(NewElementsEvent & event) {
this->fillNodesToElements();
/*this->nodes_to_elements.resize(nodes->size());
for (const auto & elem : event.getList()) {
const Array<UInt> & conn = connectivities(elem.type, elem.ghost_type);
UInt nb_nodes_per_elem = Mesh::getNbNodesPerElement(elem.type);
for (UInt n = 0; n < nb_nodes_per_elem; ++n) {
UInt node = conn(elem.element, n);
if (not nodes_to_elements[node]) {
nodes_to_elements[node] = std::make_unique<std::set<Element>>();
}
nodes_to_elements[node]->insert(elem);
}
}*/
EventHandlerManager<MeshEventHandler>::sendEvent(event);
}
/* -------------------------------------------------------------------------- */
template <> inline void Mesh::sendEvent<NewNodesEvent>(NewNodesEvent & event) {
this->computeBoundingBox();
this->nodes_flags->resize(this->nodes->size(), NodeFlag::_normal);
EventHandlerManager<MeshEventHandler>::sendEvent(event);
}
/* -------------------------------------------------------------------------- */
template <>
inline void
Mesh::sendEvent<RemovedElementsEvent>(RemovedElementsEvent & event) {
this->connectivities.onElementsRemoved(event.getNewNumbering());
this->fillNodesToElements();
this->computeBoundingBox();
EventHandlerManager<MeshEventHandler>::sendEvent(event);
}
/* -------------------------------------------------------------------------- */
template <>
inline void Mesh::sendEvent<RemovedNodesEvent>(RemovedNodesEvent & event) {
const auto & new_numbering = event.getNewNumbering();
this->removeNodesFromArray(*nodes, new_numbering);
if (nodes_global_ids and not is_mesh_facets) {
this->removeNodesFromArray(*nodes_global_ids, new_numbering);
}
if (not is_mesh_facets) {
this->removeNodesFromArray(*nodes_flags, new_numbering);
}
if (not nodes_to_elements.empty()) {
std::vector<std::unique_ptr<std::set<Element>>> tmp(
nodes_to_elements.size());
auto it = nodes_to_elements.begin();
UInt new_nb_nodes = 0;
for (auto new_i : new_numbering) {
if (new_i != UInt(-1)) {
tmp[new_i] = std::move(*it);
++new_nb_nodes;
}
++it;
}
tmp.resize(new_nb_nodes);
std::move(tmp.begin(), tmp.end(), nodes_to_elements.begin());
}
computeBoundingBox();
EventHandlerManager<MeshEventHandler>::sendEvent(event);
}
/* -------------------------------------------------------------------------- */
template <typename T>
inline void Mesh::removeNodesFromArray(Array<T> & vect,
const Array<UInt> & new_numbering) {
Array<T> tmp(vect.size(), vect.getNbComponent());
UInt nb_component = vect.getNbComponent();
UInt new_nb_nodes = 0;
for (UInt i = 0; i < new_numbering.size(); ++i) {
UInt new_i = new_numbering(i);
if (new_i != UInt(-1)) {
T * to_copy = vect.storage() + i * nb_component;
std::uninitialized_copy(to_copy, to_copy + nb_component,
tmp.storage() + new_i * nb_component);
++new_nb_nodes;
}
}
tmp.resize(new_nb_nodes);
vect.copy(tmp);
}
/* -------------------------------------------------------------------------- */
inline Array<UInt> & Mesh::getNodesGlobalIdsPointer() {
AKANTU_DEBUG_IN();
if (not nodes_global_ids) {
nodes_global_ids = std::make_shared<Array<UInt>>(
nodes->size(), 1, getID() + ":nodes_global_ids");
for (auto && global_ids : enumerate(*nodes_global_ids)) {
std::get<1>(global_ids) = std::get<0>(global_ids);
}
}
AKANTU_DEBUG_OUT();
return *nodes_global_ids;
}
/* -------------------------------------------------------------------------- */
inline Array<UInt> & Mesh::getConnectivityPointer(ElementType type,
GhostType ghost_type) {
if (connectivities.exists(type, ghost_type)) {
return connectivities(type, ghost_type);
}
if (ghost_type != _not_ghost) {
ghosts_counters.alloc(0, 1, type, ghost_type, 1);
}
AKANTU_DEBUG_INFO("The connectivity vector for the type " << type
<< " created");
UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
return connectivities.alloc(0, nb_nodes_per_element, type, ghost_type);
}
/* -------------------------------------------------------------------------- */
inline Array<std::vector<Element>> &
Mesh::getElementToSubelementPointer(ElementType type, GhostType ghost_type) {
return getDataPointer<std::vector<Element>>("element_to_subelement", type,
ghost_type, 1, true);
}
/* -------------------------------------------------------------------------- */
inline Array<Element> &
Mesh::getSubelementToElementPointer(ElementType type, GhostType ghost_type) {
auto & array = getDataPointer<Element>(
"subelement_to_element", type, ghost_type, getNbFacetsPerElement(type),
false, is_mesh_facets, ElementNull);
return array;
}
/* -------------------------------------------------------------------------- */
inline const auto & Mesh::getElementToSubelement() const {
return getData<std::vector<Element>>("element_to_subelement");
}
/* -------------------------------------------------------------------------- */
inline auto & Mesh::getElementToSubelementNC() {
return getData<std::vector<Element>>("element_to_subelement");
}
/* -------------------------------------------------------------------------- */
inline const auto & Mesh::getElementToSubelement(ElementType type,
GhostType ghost_type) const {
return getData<std::vector<Element>>("element_to_subelement", type,
ghost_type);
}
/* -------------------------------------------------------------------------- */
inline auto & Mesh::getElementToSubelementNC(ElementType type,
GhostType ghost_type) {
return getData<std::vector<Element>>("element_to_subelement", type,
ghost_type);
}
/* -------------------------------------------------------------------------- */
inline const auto &
Mesh::getElementToSubelement(const Element & element) const {
return getData<std::vector<Element>>("element_to_subelement")(element, 0);
}
/* -------------------------------------------------------------------------- */
inline auto & Mesh::getElementToSubelementNC(const Element & element) {
return getData<std::vector<Element>>("element_to_subelement")(element, 0);
}
/* -------------------------------------------------------------------------- */
inline const auto & Mesh::getSubelementToElement() const {
return getData<Element>("subelement_to_element");
}
/* -------------------------------------------------------------------------- */
inline auto & Mesh::getSubelementToElementNC() {
return getData<Element>("subelement_to_element");
}
/* -------------------------------------------------------------------------- */
inline const auto & Mesh::getSubelementToElement(ElementType type,
GhostType ghost_type) const {
return getData<Element>("subelement_to_element", type, ghost_type);
}
/* -------------------------------------------------------------------------- */
inline auto & Mesh::getSubelementToElementNC(ElementType type,
GhostType ghost_type) {
return getData<Element>("subelement_to_element", type, ghost_type);
}
/* -------------------------------------------------------------------------- */
inline VectorProxy<Element>
Mesh::getSubelementToElement(const Element & element) const {
return this->getSubelementToElement().get(element);
}
/* -------------------------------------------------------------------------- */
inline VectorProxy<Element>
Mesh::getSubelementToElementNC(const Element & element) {
return this->getSubelementToElement().get(element);
}
/* -------------------------------------------------------------------------- */
template <typename T>
inline Array<T> &
Mesh::getDataPointer(const ID & data_name, ElementType el_type,
GhostType ghost_type, UInt nb_component,
bool size_to_nb_element, bool resize_with_parent) {
Array<T> & tmp = this->getElementalDataArrayAlloc<T>(
data_name, el_type, ghost_type, nb_component);
if (size_to_nb_element) {
if (resize_with_parent) {
tmp.resize(mesh_parent->getNbElement(el_type, ghost_type));
} else {
tmp.resize(this->getNbElement(el_type, ghost_type));
}
}
return tmp;
}
/* -------------------------------------------------------------------------- */
template <typename T>
inline Array<T> &
Mesh::getDataPointer(const ID & data_name, ElementType el_type,
GhostType ghost_type, UInt nb_component,
bool size_to_nb_element, bool resize_with_parent,
const T & defaul_) {
Array<T> & tmp = this->getElementalDataArrayAlloc<T>(
data_name, el_type, ghost_type, nb_component);
if (size_to_nb_element) {
if (resize_with_parent) {
tmp.resize(mesh_parent->getNbElement(el_type, ghost_type), defaul_);
} else {
tmp.resize(this->getNbElement(el_type, ghost_type), defaul_);
}
}
return tmp;
}
/* -------------------------------------------------------------------------- */
template <typename T>
inline const Array<T> & Mesh::getData(const ID & data_name, ElementType el_type,
GhostType ghost_type) const {
return this->getElementalDataArray<T>(data_name, el_type, ghost_type);
}
/* -------------------------------------------------------------------------- */
template <typename T>
inline Array<T> & Mesh::getData(const ID & data_name, ElementType el_type,
GhostType ghost_type) {
return this->getElementalDataArray<T>(data_name, el_type, ghost_type);
}
/* -------------------------------------------------------------------------- */
template <typename T>
inline const ElementTypeMapArray<T> &
Mesh::getData(const ID & data_name) const {
return this->getElementalData<T>(data_name);
}
/* -------------------------------------------------------------------------- */
template <typename T>
inline ElementTypeMapArray<T> & Mesh::getData(const ID & data_name) {
return this->getElementalData<T>(data_name);
}
/* -------------------------------------------------------------------------- */
inline UInt Mesh::getNbElement(ElementType type, GhostType ghost_type) const {
try {
const Array<UInt> & conn = connectivities(type, ghost_type);
return conn.size();
} catch (...) {
return 0;
}
}
/* -------------------------------------------------------------------------- */
inline UInt Mesh::getNbElement(const UInt spatial_dimension,
GhostType ghost_type, ElementKind kind) const {
AKANTU_DEBUG_ASSERT(spatial_dimension <= 3 || spatial_dimension == UInt(-1),
"spatial_dimension is " << spatial_dimension
<< " and is greater than 3 !");
UInt nb_element = 0;
for (auto type : elementTypes(spatial_dimension, ghost_type, kind)) {
nb_element += getNbElement(type, ghost_type);
}
return nb_element;
}
/* -------------------------------------------------------------------------- */
inline void Mesh::getBarycenter(const Element & element,
Vector<Real> & barycenter) const {
Vector<UInt> conn = getConnectivity(element);
Matrix<Real> local_coord(spatial_dimension, conn.size());
auto node_begin = make_view(*nodes, spatial_dimension).begin();
for (auto && node : enumerate(conn)) {
local_coord(std::get<0>(node)) =
Vector<Real>(node_begin[std::get<1>(node)]);
}
Math::barycenter(local_coord.storage(), conn.size(), spatial_dimension,
barycenter.storage());
}
/* -------------------------------------------------------------------------- */
inline UInt Mesh::getNbNodesPerElement(ElementType type) {
UInt nb_nodes_per_element = 0;
#define GET_NB_NODES_PER_ELEMENT(type) \
nb_nodes_per_element = ElementClass<type>::getNbNodesPerElement()
AKANTU_BOOST_ALL_ELEMENT_SWITCH(GET_NB_NODES_PER_ELEMENT);
#undef GET_NB_NODES_PER_ELEMENT
return nb_nodes_per_element;
}
/* -------------------------------------------------------------------------- */
inline ElementType Mesh::getP1ElementType(ElementType type) {
ElementType p1_type = _not_defined;
#define GET_P1_TYPE(type) p1_type = ElementClass<type>::getP1ElementType()
AKANTU_BOOST_ALL_ELEMENT_SWITCH(GET_P1_TYPE);
#undef GET_P1_TYPE
return p1_type;
}
/* -------------------------------------------------------------------------- */
inline ElementKind Mesh::getKind(ElementType type) {
ElementKind kind = _ek_not_defined;
#define GET_KIND(type) kind = ElementClass<type>::getKind()
AKANTU_BOOST_ALL_ELEMENT_SWITCH(GET_KIND);
#undef GET_KIND
return kind;
}
/* -------------------------------------------------------------------------- */
inline UInt Mesh::getSpatialDimension(ElementType type) {
UInt spatial_dimension = 0;
#define GET_SPATIAL_DIMENSION(type) \
spatial_dimension = ElementClass<type>::getSpatialDimension()
AKANTU_BOOST_ALL_ELEMENT_SWITCH(GET_SPATIAL_DIMENSION);
#undef GET_SPATIAL_DIMENSION
return spatial_dimension;
}
/* -------------------------------------------------------------------------- */
inline UInt Mesh::getNaturalSpaceDimension(const ElementType & type) {
UInt natural_dimension = 0;
#define GET_NATURAL_DIMENSION(type) \
natural_dimension = ElementClass<type>::getNaturalSpaceDimension()
AKANTU_BOOST_ALL_ELEMENT_SWITCH(GET_NATURAL_DIMENSION);
#undef GET_NATURAL_DIMENSION
return natural_dimension;
}
/* -------------------------------------------------------------------------- */
inline UInt Mesh::getNbFacetTypes(ElementType type, UInt /*t*/) {
UInt nb = 0;
#define GET_NB_FACET_TYPE(type) nb = ElementClass<type>::getNbFacetTypes()
AKANTU_BOOST_ALL_ELEMENT_SWITCH(GET_NB_FACET_TYPE);
#undef GET_NB_FACET_TYPE
return nb;
}
/* -------------------------------------------------------------------------- */
inline constexpr auto Mesh::getFacetType(ElementType type, UInt t) {
#define GET_FACET_TYPE(type) return ElementClass<type>::getFacetType(t);
AKANTU_BOOST_ALL_ELEMENT_SWITCH_NO_DEFAULT(GET_FACET_TYPE);
#undef GET_FACET_TYPE
return _not_defined;
}
/* -------------------------------------------------------------------------- */
inline constexpr auto Mesh::getAllFacetTypes(ElementType type) {
#define GET_FACET_TYPE(type) return ElementClass<type>::getFacetTypes();
AKANTU_BOOST_ALL_ELEMENT_SWITCH_NO_DEFAULT(GET_FACET_TYPE);
#undef GET_FACET_TYPE
return ElementClass<_not_defined>::getFacetTypes();
}
/* -------------------------------------------------------------------------- */
inline UInt Mesh::getNbFacetsPerElement(ElementType type) {
AKANTU_DEBUG_IN();
UInt n_facet = 0;
#define GET_NB_FACET(type) n_facet = ElementClass<type>::getNbFacetsPerElement()
AKANTU_BOOST_ALL_ELEMENT_SWITCH(GET_NB_FACET);
#undef GET_NB_FACET
AKANTU_DEBUG_OUT();
return n_facet;
}
/* -------------------------------------------------------------------------- */
inline UInt Mesh::getNbFacetsPerElement(ElementType type, UInt t) {
AKANTU_DEBUG_IN();
UInt n_facet = 0;
#define GET_NB_FACET(type) \
n_facet = ElementClass<type>::getNbFacetsPerElement(t)
AKANTU_BOOST_ALL_ELEMENT_SWITCH(GET_NB_FACET);
#undef GET_NB_FACET
AKANTU_DEBUG_OUT();
return n_facet;
}
/* -------------------------------------------------------------------------- */
inline auto Mesh::getFacetLocalConnectivity(ElementType type, UInt t) {
AKANTU_DEBUG_IN();
#define GET_FACET_CON(type) \
AKANTU_DEBUG_OUT(); \
return ElementClass<type>::getFacetLocalConnectivityPerElement(t)
AKANTU_BOOST_ALL_ELEMENT_SWITCH(GET_FACET_CON);
#undef GET_FACET_CON
AKANTU_DEBUG_OUT();
return ElementClass<_not_defined>::getFacetLocalConnectivityPerElement(0);
// This avoid a compilation warning but will certainly
// also cause a segfault if reached
}
/* -------------------------------------------------------------------------- */
inline auto Mesh::getFacetConnectivity(const Element & element, UInt t) const {
AKANTU_DEBUG_IN();
Matrix<const UInt> local_facets(getFacetLocalConnectivity(element.type, t));
Matrix<UInt> facets(local_facets.rows(), local_facets.cols());
const Array<UInt> & conn = connectivities(element.type, element.ghost_type);
for (UInt f = 0; f < facets.rows(); ++f) {
for (UInt n = 0; n < facets.cols(); ++n) {
facets(f, n) = conn(element.element, local_facets(f, n));
}
}
AKANTU_DEBUG_OUT();
return facets;
}
/* -------------------------------------------------------------------------- */
inline VectorProxy<UInt> Mesh::getConnectivity(const Element & element) const {
return connectivities.get(element);
}
/* -------------------------------------------------------------------------- */
inline VectorProxy<UInt> Mesh::getConnectivityNC(const Element & element) {
return connectivities.get(element);
}
/* -------------------------------------------------------------------------- */
template <typename T>
inline void Mesh::extractNodalValuesFromElement(
const Array<T> & nodal_values, T * local_coord, const UInt * connectivity,
UInt n_nodes, UInt nb_degree_of_freedom) const {
for (UInt n = 0; n < n_nodes; ++n) {
memcpy(local_coord + n * nb_degree_of_freedom,
nodal_values.storage() + connectivity[n] * nb_degree_of_freedom,
nb_degree_of_freedom * sizeof(T));
}
}
/* -------------------------------------------------------------------------- */
inline void Mesh::addConnectivityType(ElementType type, GhostType ghost_type) {
getConnectivityPointer(type, ghost_type);
}
/* -------------------------------------------------------------------------- */
inline bool Mesh::isPureGhostNode(UInt n) const {
return ((*nodes_flags)(n)&NodeFlag::_shared_mask) == NodeFlag::_pure_ghost;
}
/* -------------------------------------------------------------------------- */
inline bool Mesh::isLocalOrMasterNode(UInt n) const {
return ((*nodes_flags)(n)&NodeFlag::_local_master_mask) == NodeFlag::_normal;
}
/* -------------------------------------------------------------------------- */
inline bool Mesh::isLocalNode(UInt n) const {
return ((*nodes_flags)(n)&NodeFlag::_shared_mask) == NodeFlag::_normal;
}
/* -------------------------------------------------------------------------- */
inline bool Mesh::isMasterNode(UInt n) const {
return ((*nodes_flags)(n)&NodeFlag::_shared_mask) == NodeFlag::_master;
}
/* -------------------------------------------------------------------------- */
inline bool Mesh::isSlaveNode(UInt n) const {
return ((*nodes_flags)(n)&NodeFlag::_shared_mask) == NodeFlag::_slave;
}
/* -------------------------------------------------------------------------- */
inline bool Mesh::isPeriodicSlave(UInt n) const {
return ((*nodes_flags)(n)&NodeFlag::_periodic_mask) ==
NodeFlag::_periodic_slave;
}
/* -------------------------------------------------------------------------- */
inline bool Mesh::isPeriodicMaster(UInt n) const {
return ((*nodes_flags)(n)&NodeFlag::_periodic_mask) ==
NodeFlag::_periodic_master;
}
/* -------------------------------------------------------------------------- */
inline NodeFlag Mesh::getNodeFlag(UInt local_id) const {
return (*nodes_flags)(local_id);
}
/* -------------------------------------------------------------------------- */
inline Int Mesh::getNodePrank(UInt local_id) const {
auto it = nodes_prank.find(local_id);
return it == nodes_prank.end() ? -1 : it->second;
}
/* -------------------------------------------------------------------------- */
inline UInt Mesh::getNodeGlobalId(UInt local_id) const {
return nodes_global_ids ? (*nodes_global_ids)(local_id) : local_id;
}
/* -------------------------------------------------------------------------- */
inline UInt Mesh::getNodeLocalId(UInt global_id) const {
if (nodes_global_ids == nullptr) {
return global_id;
}
return nodes_global_ids->find(global_id);
}
/* -------------------------------------------------------------------------- */
inline UInt Mesh::getNbGlobalNodes() const {
return nodes_global_ids ? nb_global_nodes : nodes->size();
}
/* -------------------------------------------------------------------------- */
inline UInt Mesh::getNbNodesPerElementList(const Array<Element> & elements) {
UInt nb_nodes_per_element = 0;
UInt nb_nodes = 0;
ElementType current_element_type = _not_defined;
for (const auto & el : elements) {
if (el.type != current_element_type) {
current_element_type = el.type;
nb_nodes_per_element = Mesh::getNbNodesPerElement(current_element_type);
}
nb_nodes += nb_nodes_per_element;
}
return nb_nodes;
}
/* -------------------------------------------------------------------------- */
inline Mesh & Mesh::getMeshFacets() {
if (this->mesh_facets == nullptr) {
AKANTU_SILENT_EXCEPTION(
"No facet mesh is defined yet! check the buildFacets functions");
}
return *this->mesh_facets;
}
/* -------------------------------------------------------------------------- */
inline const Mesh & Mesh::getMeshFacets() const {
if (this->mesh_facets == nullptr) {
AKANTU_SILENT_EXCEPTION(
"No facet mesh is defined yet! check the buildFacets functions");
}
return *this->mesh_facets;
}
/* -------------------------------------------------------------------------- */
inline const Mesh & Mesh::getMeshParent() const {
if (this->mesh_parent == nullptr) {
AKANTU_SILENT_EXCEPTION(
"No parent mesh is defined! This is only valid in a mesh_facets");
}
return *this->mesh_parent;
}
/* -------------------------------------------------------------------------- */
void Mesh::addPeriodicSlave(UInt slave, UInt master) {
if (master == slave) {
return;
}
// if pair already registered
auto master_slaves = periodic_master_slave.equal_range(master);
auto slave_it =
std::find_if(master_slaves.first, master_slaves.second,
[&](auto & pair) { return pair.second == slave; });
if (slave_it == master_slaves.second) {
// no duplicates
periodic_master_slave.insert(std::make_pair(master, slave));
AKANTU_DEBUG_INFO("adding periodic slave, slave gid:"
<< getNodeGlobalId(slave) << " [lid: " << slave << "]"
<< ", master gid:" << getNodeGlobalId(master)
<< " [lid: " << master << "]");
// std::cout << "adding periodic slave, slave gid:" <<
// getNodeGlobalId(slave)
// << " [lid: " << slave << "]"
// << ", master gid:" << getNodeGlobalId(master)
// << " [lid: " << master << "]" << std::endl;
}
periodic_slave_master[slave] = master;
auto set_flag = [&](auto node, auto flag) {
(*nodes_flags)[node] &= ~NodeFlag::_periodic_mask; // clean periodic flags
(*nodes_flags)[node] |= flag;
};
set_flag(slave, NodeFlag::_periodic_slave);
set_flag(master, NodeFlag::_periodic_master);
}
/* -------------------------------------------------------------------------- */
UInt Mesh::getPeriodicMaster(UInt slave) const {
return periodic_slave_master.at(slave);
}
/* -------------------------------------------------------------------------- */
class Mesh::PeriodicSlaves {
using internal_iterator = std::unordered_multimap<UInt, UInt>::const_iterator;
std::pair<internal_iterator, internal_iterator> pair;
public:
PeriodicSlaves(const Mesh & mesh, UInt master)
: pair(mesh.getPeriodicMasterSlaves().equal_range(master)) {}
PeriodicSlaves(const PeriodicSlaves & other) = default;
PeriodicSlaves(PeriodicSlaves && other) = default;
PeriodicSlaves & operator=(const PeriodicSlaves & other) = default;
class const_iterator {
internal_iterator it;
public:
const_iterator(internal_iterator it) : it(it) {}
const_iterator operator++() {
++it;
return *this;
}
bool operator!=(const const_iterator & other) { return other.it != it; }
auto operator*() { return it->second; }
};
auto begin() const { return const_iterator(pair.first); }
auto end() const { return const_iterator(pair.second); }
};
/* -------------------------------------------------------------------------- */
inline decltype(auto) Mesh::getPeriodicSlaves(UInt master) const {
return PeriodicSlaves(*this, master);
}
/* -------------------------------------------------------------------------- */
inline Vector<UInt>
Mesh::getConnectivityWithPeriodicity(const Element & element) const {
Vector<UInt> conn = getConnectivity(element);
if (not isPeriodic()) {
return conn;
}
for (auto && node : conn) {
if (isPeriodicSlave(node)) {
node = getPeriodicMaster(node);
}
}
return conn;
}
} // namespace akantu
#endif /* AKANTU_MESH_INLINE_IMPL_HH_ */
diff --git a/src/mesh/mesh_iterators.hh b/src/mesh/mesh_iterators.hh
index a10877ba8..4d14f8f69 100644
--- a/src/mesh/mesh_iterators.hh
+++ b/src/mesh/mesh_iterators.hh
@@ -1,228 +1,224 @@
/**
* @file mesh_iterators.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Thu Jul 16 2015
* @date last modification: Wed Jan 31 2018
*
* @brief Set of helper classes to have fun with range based for
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_named_argument.hh"
#include "aka_static_if.hh"
#include "mesh.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_MESH_ITERATORS_HH_
#define AKANTU_MESH_ITERATORS_HH_
namespace akantu {
class MeshElementsByTypes {
using elements_iterator = Array<Element>::scalar_iterator;
public:
explicit MeshElementsByTypes(const Array<Element> & elements) {
this->elements.copy(elements);
std::sort(this->elements.begin(), this->elements.end());
}
/* ------------------------------------------------------------------------ */
class MeshElementsRange {
public:
MeshElementsRange() = default;
MeshElementsRange(const elements_iterator & begin,
const elements_iterator & end)
: type((*begin).type), ghost_type((*begin).ghost_type), begin(begin),
end(end) {}
AKANTU_GET_MACRO(Type, type, ElementType);
AKANTU_GET_MACRO(GhostType, ghost_type, GhostType);
const Array<UInt> & getElements() {
elements.resize(end - begin);
auto el_it = elements.begin();
for (auto it = begin; it != end; ++it, ++el_it) {
*el_it = it->element;
}
return elements;
}
private:
ElementType type{_not_defined};
GhostType ghost_type{_casper};
elements_iterator begin;
elements_iterator end;
Array<UInt> elements;
};
/* ------------------------------------------------------------------------ */
class iterator {
struct element_comparator {
bool operator()(const Element & lhs, const Element & rhs) const {
return ((rhs == ElementNull) || std::tie(lhs.ghost_type, lhs.type) <
std::tie(rhs.ghost_type, rhs.type));
}
};
public:
iterator(const iterator &) = default;
iterator(const elements_iterator & first, const elements_iterator & last)
: range(std::equal_range(first, last, *first, element_comparator())),
first(first), last(last) {}
decltype(auto) operator*() const {
return MeshElementsRange(range.first, range.second);
}
iterator operator++() {
first = range.second;
range = std::equal_range(first, last, *first, element_comparator());
return *this;
}
bool operator==(const iterator & other) const {
return (first == other.first and last == other.last);
}
bool operator!=(const iterator & other) const {
return (not operator==(other));
}
private:
std::pair<elements_iterator, elements_iterator> range;
elements_iterator first;
elements_iterator last;
};
iterator begin() { return iterator(elements.begin(), elements.end()); }
iterator end() { return iterator(elements.end(), elements.end()); }
private:
Array<Element> elements;
};
/* -------------------------------------------------------------------------- */
namespace mesh_iterators {
namespace details {
template <class internal_iterator> class delegated_iterator {
public:
using value_type = std::remove_pointer_t<
typename internal_iterator::value_type::second_type>;
using difference_type = std::ptrdiff_t;
using pointer = value_type *;
using reference = value_type &;
using iterator_category = std::input_iterator_tag;
explicit delegated_iterator(internal_iterator it) : it(std::move(it)) {}
decltype(auto) operator*() {
return std::forward<decltype(*(it->second))>(*(it->second));
}
delegated_iterator operator++() {
++it;
return *this;
}
bool operator==(const delegated_iterator & other) const {
return other.it == it;
}
bool operator!=(const delegated_iterator & other) const {
return other.it != it;
}
private:
internal_iterator it;
};
} // namespace details
} // namespace mesh_iterators
/* -------------------------------------------------------------------------- */
template <class Func>
void for_each_element(UInt nb_elements, const Array<UInt> & filter_elements,
Func && function) {
if (filter_elements != empty_filter) {
std::for_each(filter_elements.begin(), filter_elements.end(),
std::forward<Func>(function));
} else {
auto && range = arange(nb_elements);
std::for_each(range.begin(), range.end(), std::forward<Func>(function));
}
}
-namespace {
- DECLARE_NAMED_ARGUMENT(element_filter);
-}
-
/* -------------------------------------------------------------------------- */
template <class Func, typename... pack>
void for_each_element(const Mesh & mesh, Func && function, pack &&... _pack) {
auto requested_ghost_type = OPTIONAL_NAMED_ARG(ghost_type, _casper);
const ElementTypeMapArray<UInt> * filter =
OPTIONAL_NAMED_ARG(element_filter, nullptr);
bool all_ghost_types = requested_ghost_type == _casper;
auto spatial_dimension =
OPTIONAL_NAMED_ARG(spatial_dimension, mesh.getSpatialDimension());
auto element_kind = OPTIONAL_NAMED_ARG(element_kind, _ek_not_defined);
for (auto ghost_type : ghost_types) {
if ((not(ghost_type == requested_ghost_type)) and (not all_ghost_types)) {
continue;
}
auto element_types =
mesh.elementTypes(spatial_dimension, ghost_type, element_kind);
if (filter) {
element_types =
filter->elementTypes(spatial_dimension, ghost_type, element_kind);
}
for (auto type : element_types) {
const Array<UInt> * filter_array;
if (filter) {
filter_array = &((*filter)(type, ghost_type));
} else {
filter_array = &empty_filter;
}
auto nb_elements = mesh.getNbElement(type, ghost_type);
for_each_element(nb_elements, *filter_array, [&](auto && el) {
auto element = Element{type, el, ghost_type};
std::forward<Func>(function)(element);
});
}
}
}
} // namespace akantu
#endif /* AKANTU_MESH_ITERATORS_HH_ */
diff --git a/src/mesh/mesh_periodic.cc b/src/mesh/mesh_periodic.cc
index 01d226553..e374b4033 100644
--- a/src/mesh/mesh_periodic.cc
+++ b/src/mesh/mesh_periodic.cc
@@ -1,463 +1,462 @@
/**
* @file mesh_periodic.cc
*
* @author Nicolas Richart
*
* @date creation Sat Feb 10 2018
*
* @brief Implementation of the perdiodicity capabilities in the mesh
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "communication_tag.hh"
#include "communicator.hh"
#include "element_group.hh"
#include "mesh.hh"
#include "periodic_node_synchronizer.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
void Mesh::makePeriodic(const SpatialDirection & direction) {
Array<UInt> list_1;
Array<UInt> list_2;
Real tolerance = 1e-10;
auto lower_bound = this->getLowerBounds();
auto upper_bound = this->getUpperBounds();
auto length = upper_bound(direction) - lower_bound(direction);
const auto & positions = *nodes;
for (auto && data : enumerate(make_view(positions, spatial_dimension))) {
UInt node = std::get<0>(data);
const auto & pos = std::get<1>(data);
if (std::abs((pos(direction) - lower_bound(direction)) / length) <
tolerance) {
list_1.push_back(node);
}
if (std::abs((pos(direction) - upper_bound(direction)) / length) <
tolerance) {
list_2.push_back(node);
}
}
this->makePeriodic(direction, list_1, list_2);
}
/* -------------------------------------------------------------------------- */
void Mesh::makePeriodic(const SpatialDirection & direction, const ID & list_1,
const ID & list_2) {
const auto & list_nodes_1 =
mesh.getElementGroup(list_1).getNodeGroup().getNodes();
const auto & list_nodes_2 =
mesh.getElementGroup(list_2).getNodeGroup().getNodes();
this->makePeriodic(direction, list_nodes_1, list_nodes_2);
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
namespace {
struct NodeInfo {
NodeInfo() = default;
NodeInfo(UInt spatial_dimension) : position(spatial_dimension) {}
NodeInfo(UInt node, const Vector<Real> & position,
const SpatialDirection & direction)
: node(node), position(position) {
this->direction_position = position(direction);
this->position(direction) = 0.;
}
NodeInfo(const NodeInfo & other) = default;
NodeInfo(NodeInfo && other) noexcept = default;
NodeInfo & operator=(const NodeInfo & other) = default;
NodeInfo & operator=(NodeInfo && other) = default;
UInt node{0};
Vector<Real> position;
Real direction_position{0.};
};
} // namespace
/* -------------------------------------------------------------------------- */
// left is for lower values on direction and right for highest values
void Mesh::makePeriodic(const SpatialDirection & direction,
const Array<UInt> & list_left,
const Array<UInt> & list_right) {
Real tolerance = 1e-10;
const auto & positions = *nodes;
auto lower_bound = this->getLowerBounds();
auto upper_bound = this->getUpperBounds();
auto length = upper_bound(direction) - lower_bound(direction);
lower_bound(direction) = 0;
upper_bound(direction) = 0;
auto prank = communicator->whoAmI();
std::vector<NodeInfo> nodes_left(list_left.size());
std::vector<NodeInfo> nodes_right(list_right.size());
BBox bbox(spatial_dimension);
auto to_position = [&](UInt node) {
Vector<Real> pos(spatial_dimension);
for (UInt s : arange(spatial_dimension)) {
pos(s) = positions(node, s);
}
auto && info = NodeInfo(node, pos, direction);
bbox += info.position;
return std::move(info);
};
std::transform(list_left.begin(), list_left.end(), nodes_left.begin(),
to_position);
BBox bbox_left = bbox;
bbox.reset();
std::transform(list_right.begin(), list_right.end(), nodes_right.begin(),
to_position);
BBox bbox_right = bbox;
std::vector<UInt> new_nodes;
if (is_distributed) {
NewNodesEvent event(AKANTU_CURRENT_FUNCTION);
/* ---------------------------------------------------------------------- */
// function to send nodes in bboxes intersections
auto extract_and_send_nodes = [&](const auto & bbox, const auto & node_list,
auto & buffers, auto proc, auto cnt) {
// buffers.resize(buffers.size() + 1);
buffers.push_back(std::make_unique<DynamicCommunicationBuffer>());
auto & buffer = *buffers.back();
// std::cout << "Sending to " << proc << std::endl;
for (auto & info : node_list) {
if (bbox.contains(info.position) and isLocalOrMasterNode(info.node)) {
Vector<Real> pos = info.position;
pos(direction) = info.direction_position;
NodeFlag flag = (*nodes_flags)(info.node) & NodeFlag::_periodic_mask;
UInt gnode = getNodeGlobalId(info.node);
buffer << gnode;
buffer << pos;
buffer << flag;
// std::cout << " - node " << getNodeGlobalId(info.node);
// if is slave sends master info
if (flag == NodeFlag::_periodic_slave) {
UInt master = getNodeGlobalId(periodic_slave_master[info.node]);
// std::cout << " slave of " << master << std::endl;
buffer << master;
}
// if is master sends list of slaves
if (flag == NodeFlag::_periodic_master) {
UInt nb_slaves = periodic_master_slave.count(info.node);
buffer << nb_slaves;
// std::cout << " master of " << nb_slaves << " nodes : [";
auto slaves = periodic_master_slave.equal_range(info.node);
for (auto it = slaves.first; it != slaves.second; ++it) {
UInt gslave = getNodeGlobalId(it->second);
// std::cout << (it == slaves.first ? "" : ", ") << gslave;
buffer << gslave;
}
// std::cout << "]";
}
// std::cout << std::endl;
}
}
auto tag = Tag::genTag(prank, 10 * direction + cnt, Tag::_periodic_nodes);
// std::cout << "SBuffer size " << buffer.size() << " " << tag <<
// std::endl;
return communicator->asyncSend(buffer, proc, tag);
};
/* ---------------------------------------------------------------------- */
// function to receive nodes in bboxes intersections
auto recv_and_extract_nodes = [&](auto & node_list, const auto proc,
auto cnt) {
DynamicCommunicationBuffer buffer;
auto tag = Tag::genTag(proc, 10 * direction + cnt, Tag::_periodic_nodes);
communicator->receive(buffer, proc, tag);
// std::cout << "RBuffer size " << buffer.size() << " " << tag <<
// std::endl; std::cout << "Receiving from " << proc << std::endl;
while (not buffer.empty()) {
Vector<Real> pos(spatial_dimension);
UInt global_node;
NodeFlag flag;
buffer >> global_node;
buffer >> pos;
buffer >> flag;
// std::cout << " - node " << global_node;
auto local_node = getNodeLocalId(global_node);
// get the master info of is slave
if (flag == NodeFlag::_periodic_slave) {
UInt master_node;
buffer >> master_node;
// std::cout << " slave of " << master_node << std::endl;
// auto local_master_node = getNodeLocalId(master_node);
// AKANTU_DEBUG_ASSERT(local_master_node != UInt(-1),
//"Should I know the master node " << master_node);
}
// get the list of slaves if is master
if ((flag & NodeFlag::_periodic_mask) == NodeFlag::_periodic_master) {
UInt nb_slaves;
buffer >> nb_slaves;
// std::cout << " master of " << nb_slaves << " nodes : [";
for (auto ns [[gnu::unused]] : arange(nb_slaves)) {
UInt gslave_node;
buffer >> gslave_node;
// std::cout << (ns == 0 ? "" : ", ") << gslave_node;
// auto lslave_node = getNodeLocalId(gslave_node);
// AKANTU_DEBUG_ASSERT(lslave_node != UInt(-1),
// "Should I know the slave node " <<
// gslave_node);
}
// std::cout << "]";
}
// std::cout << std::endl;
if (local_node != UInt(-1)) {
continue;
}
local_node = nodes->size();
NodeInfo info(local_node, pos, direction);
nodes->push_back(pos);
nodes_global_ids->push_back(global_node);
nodes_flags->push_back(flag | NodeFlag::_pure_ghost);
new_nodes.push_back(info.node);
node_list.push_back(info);
nodes_prank[info.node] = proc;
event.getList().push_back(local_node);
}
};
/* ---------------------------------------------------------------------- */
auto && intersections_with_right =
bbox_left.intersection(bbox_right, *communicator);
auto && intersections_with_left =
bbox_right.intersection(bbox_left, *communicator);
std::vector<CommunicationRequest> send_requests;
std::vector<std::unique_ptr<DynamicCommunicationBuffer>> send_buffers;
// sending nodes in the common zones
auto send_intersections = [&](auto & intersections, auto send_count) {
for (auto && data : intersections) {
auto proc = std::get<0>(data);
// Send local nodes if intersects with remote
const auto & intersection_with_proc = std::get<1>(data);
if (intersection_with_proc) {
send_requests.push_back(
extract_and_send_nodes(intersection_with_proc, nodes_right,
send_buffers, proc, send_count));
}
send_count += 2;
}
};
auto recv_intersections = [&](auto & intersections, auto recv_count) {
for (auto && data : intersections) {
auto proc = std::get<0>(data);
// receive remote nodes if intersects with local
const auto & intersection_with_proc = std::get<1>(data);
if (intersection_with_proc) {
recv_and_extract_nodes(nodes_right, proc, recv_count);
}
recv_count += 2;
}
};
send_intersections(intersections_with_left, 0);
send_intersections(intersections_with_right, 1);
recv_intersections(intersections_with_right, 0);
recv_intersections(intersections_with_right, 1);
Communicator::waitAll(send_requests);
Communicator::freeCommunicationRequest(send_requests);
this->sendEvent(event);
} // end distributed work
auto to_sort = [&](auto && info1, auto && info2) -> bool {
return info1.position < info2.position;
};
// sort nodes based on their distance to lower corner
std::sort(nodes_left.begin(), nodes_left.end(), to_sort);
std::sort(nodes_right.begin(), nodes_right.end(), to_sort);
// function to change the master of nodes
auto updating_master = [&](auto & old_master, auto & new_master) {
if (old_master == new_master) {
return;
}
auto slaves = periodic_master_slave.equal_range(old_master);
AKANTU_DEBUG_ASSERT(
isPeriodicMaster(
old_master), // slaves.first != periodic_master_slave.end(),
"Cannot update master " << old_master << ", its not a master node!");
decltype(periodic_master_slave) tmp_master_slave;
for (auto it = slaves.first; it != slaves.second; ++it) {
auto slave = it->second;
tmp_master_slave.insert(std::make_pair(new_master, slave));
periodic_slave_master[slave] = new_master;
}
periodic_master_slave.erase(old_master);
(*nodes_flags)[old_master] &= ~NodeFlag::_periodic_master;
addPeriodicSlave(old_master, new_master);
for (auto && data : tmp_master_slave) {
addPeriodicSlave(data.second, data.first);
}
};
// handling 2 nodes that are periodic
auto match_found = [&](auto & info1, auto & info2) {
const auto & node1 = info1.node;
const auto & node2 = info2.node;
auto master = node1;
bool node1_side_master = false;
if (isPeriodicMaster(node1)) {
node1_side_master = true;
} else if (isPeriodicSlave(node1)) {
node1_side_master = true;
master = periodic_slave_master[node1];
}
auto node2_master = node2;
if (isPeriodicSlave(node2)) {
node2_master = periodic_slave_master[node2];
}
if (node1_side_master) {
if (isPeriodicSlave(node2)) {
updating_master(node2_master, master);
return;
}
if (isPeriodicMaster(node2)) {
updating_master(node2, master);
return;
}
addPeriodicSlave(node2, master);
} else {
if (isPeriodicSlave(node2)) {
addPeriodicSlave(node1, node2_master);
return;
}
if (isPeriodicMaster(node2)) {
addPeriodicSlave(node1, node2);
return;
}
addPeriodicSlave(node2, node1);
}
};
// matching the nodes from 2 lists
auto match_pairs = [&](auto & nodes_1, auto & nodes_2) {
// Guillaume to Nico: It seems that the list of nodes is not sorted
// as it was: therefore the loop cannot be truncated anymore.
// Otherwise many pairs are missing.
// I replaced (temporarily?) for the N^2 loop so as not to miss
// any pbc pair.
//
// auto it = nodes_2.begin();
// for every nodes in 1st list
for (auto && info1 : nodes_1) {
auto & pos1 = info1.position;
// auto it_cur = it;
// try to find a match in 2nd list
for (auto && info2 : nodes_2) {
// auto & info2 = *it_cur;
auto & pos2 = info2.position;
auto dist = pos1.distance(pos2) / length;
if (dist < tolerance) {
// handles the found matches
match_found(info1, info2);
// it = it_cur;
break;
}
}
}
};
match_pairs(nodes_left, nodes_right);
// match_pairs(nodes_right, nodes_left);
this->updatePeriodicSynchronizer();
this->is_periodic = true;
}
/* -------------------------------------------------------------------------- */
void Mesh::wipePeriodicInfo() {
this->is_periodic = false;
this->periodic_slave_master.clear();
this->periodic_master_slave.clear();
for (auto && flags : *nodes_flags) {
flags &= ~NodeFlag::_periodic_mask;
}
}
/* -------------------------------------------------------------------------- */
void Mesh::updatePeriodicSynchronizer() {
if (not this->periodic_node_synchronizer) {
this->periodic_node_synchronizer =
std::make_unique<PeriodicNodeSynchronizer>(
- *this, this->getID() + ":periodic_synchronizer",
- this->getMemoryID(), false);
+ *this, this->getID() + ":periodic_synchronizer", false);
}
this->periodic_node_synchronizer->update();
}
} // namespace akantu
diff --git a/src/mesh/node_group.cc b/src/mesh/node_group.cc
index 5b081ba47..cb788d6bb 100644
--- a/src/mesh/node_group.cc
+++ b/src/mesh/node_group.cc
@@ -1,96 +1,96 @@
/**
* @file node_group.cc
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Thu Feb 01 2018
*
* @brief Implementation of the node group
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "node_group.hh"
#include "dumpable.hh"
#include "dumpable_inline_impl.hh"
#include "mesh.hh"
#if defined(AKANTU_USE_IOHELPER)
#include "dumper_iohelper_paraview.hh"
#endif
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
NodeGroup::NodeGroup(const std::string & name, const Mesh & mesh,
- const std::string & id, const MemoryID & memory_id)
- : Memory(id, memory_id), name(name),
- node_group(alloc<UInt>(std::string(this->id + ":nodes"), 0, 1)) {
+ const std::string & id)
+ : name(name),
+ node_group(0, 1, std::string(id + ":nodes")) {
#if defined(AKANTU_USE_IOHELPER)
this->registerDumper<DumperParaview>("paraview_" + name, name, true);
auto field = std::make_shared<dumpers::NodalField<Real, true>>(
mesh.getNodes(), 0, 0, &this->getNodes());
this->getDumper().registerField("positions", field);
#endif
}
/* -------------------------------------------------------------------------- */
NodeGroup::~NodeGroup() = default;
/* -------------------------------------------------------------------------- */
void NodeGroup::clear() { node_group.resize(0); }
/* -------------------------------------------------------------------------- */
-//bool NodeGroup::empty() { return node_group.empty(); }
+// bool NodeGroup::empty() { return node_group.empty(); }
/* -------------------------------------------------------------------------- */
void NodeGroup::optimize() {
std::sort(node_group.begin(), node_group.end());
Array<UInt>::iterator<> end =
std::unique(node_group.begin(), node_group.end());
node_group.resize(end - node_group.begin());
}
/* -------------------------------------------------------------------------- */
void NodeGroup::append(const NodeGroup & other_group) {
AKANTU_DEBUG_IN();
UInt nb_nodes = node_group.size();
/// append new nodes to current list
node_group.resize(nb_nodes + other_group.node_group.size());
std::copy(other_group.node_group.begin(), other_group.node_group.end(),
node_group.begin() + nb_nodes);
optimize();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NodeGroup::printself(std::ostream & stream, int indent) const {
std::string space(indent, AKANTU_INDENT);
stream << space << "NodeGroup [" << std::endl;
stream << space << " + name: " << name << std::endl;
node_group.printself(stream, indent + 1);
stream << space << "]" << std::endl;
}
} // namespace akantu
diff --git a/src/mesh/node_group.hh b/src/mesh/node_group.hh
index 1c5c320ee..804310797 100644
--- a/src/mesh/node_group.hh
+++ b/src/mesh/node_group.hh
@@ -1,134 +1,132 @@
/**
* @file node_group.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Wed Nov 08 2017
*
* @brief Node group definition
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_array.hh"
#include "aka_common.hh"
-#include "aka_memory.hh"
#include "dumpable.hh"
#include "mesh_filter.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_NODE_GROUP_HH_
#define AKANTU_NODE_GROUP_HH_
namespace akantu {
-class NodeGroup : public Memory, public Dumpable {
+class NodeGroup : public Dumpable {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
NodeGroup(const std::string & name, const Mesh & mesh,
- const std::string & id = "node_group",
- const MemoryID & memory_id = 0);
+ const std::string & id = "node_group");
~NodeGroup() override;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
using const_node_iterator = Array<UInt>::const_iterator<UInt>;
/// empty the node group
void clear();
/// returns treu if the group is empty \warning this changed beahavior if you
/// want to empty the group use clear
bool empty() const __attribute__((warn_unused_result));
/// iterator to the beginning of the node group
inline const_node_iterator begin() const;
/// iterator to the end of the node group
inline const_node_iterator end() const;
/// add a node and give the local position through an iterator
inline const_node_iterator add(UInt node, bool check_for_duplicate = true);
/// remove a node
inline void remove(UInt node);
inline decltype(auto) find(UInt node) const { return node_group.find(node); }
/// remove duplicated nodes
void optimize();
/// append a group to current one
void append(const NodeGroup & other_group);
/// apply a filter on current node group
template <typename T> void applyNodeFilter(T & filter);
/// function to print the contain of the class
virtual void printself(std::ostream & stream, int indent = 0) const;
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
AKANTU_GET_MACRO_NOT_CONST(Nodes, node_group, Array<UInt> &);
AKANTU_GET_MACRO(Nodes, node_group, const Array<UInt> &);
AKANTU_GET_MACRO(Name, name, const std::string &);
/// give the number of nodes in the current group
inline UInt size() const;
// UInt * storage() { return node_group.storage(); };
friend class GroupManager;
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
private:
/// name of the group
std::string name;
/// list of nodes in the group
- Array<UInt> & node_group;
+ Array<UInt> node_group;
/// reference to the mesh in question
// const Mesh & mesh;
};
/// standard output stream operator
inline std::ostream & operator<<(std::ostream & stream,
const NodeGroup & _this) {
_this.printself(stream);
return stream;
}
} // namespace akantu
/* -------------------------------------------------------------------------- */
/* inline functions */
/* -------------------------------------------------------------------------- */
#include "node_group_inline_impl.hh"
#endif /* AKANTU_NODE_GROUP_HH_ */
diff --git a/src/mesh_utils/mesh_partition.cc b/src/mesh_utils/mesh_partition.cc
index 4f6c1f75a..38a560665 100644
--- a/src/mesh_utils/mesh_partition.cc
+++ b/src/mesh_utils/mesh_partition.cc
@@ -1,405 +1,403 @@
/**
* @file mesh_partition.cc
*
* @author David Simon Kammer <david.kammer@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Tue Aug 17 2010
* @date last modification: Wed Jan 24 2018
*
* @brief implementation of common part of all partitioner
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "mesh_partition.hh"
#include "aka_iterators.hh"
#include "aka_types.hh"
#include "mesh_accessor.hh"
#include "mesh_iterators.hh"
#include "mesh_utils.hh"
/* -------------------------------------------------------------------------- */
#include <algorithm>
#include <numeric>
#include <unordered_map>
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
-MeshPartition::MeshPartition(Mesh & mesh, UInt spatial_dimension,
- const ID & id, const MemoryID & memory_id)
- : Memory(id, memory_id), mesh(mesh), spatial_dimension(spatial_dimension),
- partitions("partition", id, memory_id),
- ghost_partitions("ghost_partition", id, memory_id),
- ghost_partitions_offset("ghost_partition_offset", id, memory_id),
- saved_connectivity("saved_connectivity", id, memory_id) {
+MeshPartition::MeshPartition(Mesh & mesh, UInt spatial_dimension, const ID & id)
+ : mesh(mesh), spatial_dimension(spatial_dimension),
+ partitions("partition", id), ghost_partitions("ghost_partition", id),
+ ghost_partitions_offset("ghost_partition_offset", id),
+ saved_connectivity("saved_connectivity", id) {
AKANTU_DEBUG_IN();
UInt nb_total_element = 0;
for (auto && type :
mesh.elementTypes(spatial_dimension, _not_ghost, _ek_not_defined)) {
linearized_offsets.emplace_back(type, nb_total_element);
nb_total_element += mesh.getConnectivity(type).size();
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
MeshPartition::~MeshPartition() = default;
/* -------------------------------------------------------------------------- */
UInt MeshPartition::linearized(const Element & element) {
auto it =
std::find_if(linearized_offsets.begin(), linearized_offsets.end(),
[&element](auto & a) { return a.first == element.type; });
AKANTU_DEBUG_ASSERT(it != linearized_offsets.end(),
"A bug might be crawling around this corner...");
return (it->second + element.element);
}
/* -------------------------------------------------------------------------- */
Element MeshPartition::unlinearized(UInt lin_element) {
ElementType type{_not_defined};
UInt offset{0};
for (auto & pair : linearized_offsets) {
if (lin_element < pair.second) {
continue;
}
std::tie(type, offset) = pair;
}
return Element{type, lin_element - offset, _not_ghost};
}
/* -------------------------------------------------------------------------- */
/**
* conversion in c++ of the METIS_MeshToDual (mesh.c) function wrote by George
* in Metis (University of Minnesota)
*/
void MeshPartition::buildDualGraph(
Array<Int> & dxadj, Array<Int> & dadjncy, Array<Int> & edge_loads,
- const std::function<Int(const Element &, const Element &)> &edge_load_func,
+ const std::function<Int(const Element &, const Element &)> & edge_load_func,
Array<Int> & vertex_loads,
- const std::function<Int(const Element &)> &vertex_load_func) {
+ const std::function<Int(const Element &)> & vertex_load_func) {
CSR<Element> nodes_to_elements;
MeshUtils::buildNode2Elements(mesh, nodes_to_elements);
std::unordered_map<UInt, std::vector<UInt>> adjacent_elements;
// for each elements look for its connected elements
for_each_element(
mesh,
[&](auto && element) {
- const auto & conn = const_cast<const Mesh &>(mesh).getConnectivity(element);
+ const auto & conn =
+ const_cast<const Mesh &>(mesh).getConnectivity(element);
std::map<Element, UInt> hits;
// count the number of nodes shared with a given element
for (auto && node : conn) {
for (auto && connected_element : nodes_to_elements.getRow(node)) {
++hits[connected_element];
}
}
// define a minumum number of nodes to share to be considered as a
// ajacent element
UInt magic_number{conn.size()};
for (auto n : arange(mesh.getNbFacetTypes(element.type))) {
magic_number = std::min(
mesh.getNbNodesPerElement(mesh.getFacetType(element.type, n)),
magic_number);
}
// check all neighbors to see which ones are "adjacent"
for (auto && data : hits) {
const auto & adjacent_element = data.first;
// not adjacent to miself
if (adjacent_element == element) {
continue;
}
// not enough shared nodes
if (data.second < magic_number) {
continue;
}
- /// Patch in order to prevent neighboring cohesive elements
- /// from detecting each other
+ /// Patch in order to prevent neighboring cohesive elements
+ /// from detecting each other
#if defined(AKANTU_COHESIVE_ELEMENT)
auto element_kind = element.kind();
auto adjacent_element_kind = adjacent_element.kind();
if (element_kind == adjacent_element_kind &&
element_kind == _ek_cohesive) {
continue;
}
#endif
adjacent_elements[this->linearized(element)].push_back(
this->linearized(adjacent_element));
}
},
_spatial_dimension = mesh.getSpatialDimension(),
_element_kind = _ek_not_defined);
// prepare the arrays
auto nb_elements{adjacent_elements.size()};
dxadj.resize(nb_elements + 1);
vertex_loads.resize(nb_elements);
for (auto && data : adjacent_elements) {
const auto & element{data.first};
const auto & neighbors{data.second};
dxadj[element] = neighbors.size();
}
/// convert the dxadj array of sizes in a csr one of offsets
for (UInt i = 1; i < nb_elements; ++i) {
dxadj(i) += dxadj(i - 1);
}
for (UInt i = nb_elements; i > 0; --i) {
dxadj(i) = dxadj(i - 1);
}
dxadj(0) = 0;
dadjncy.resize(dxadj(nb_elements));
edge_loads.resize(dadjncy.size());
// fill the different arrays
for (auto && data : adjacent_elements) {
const auto & element{data.first};
const auto & neighbors{data.second};
auto unlinearized_element = unlinearized(element);
vertex_loads(element) = vertex_load_func(unlinearized_element);
auto pos = dxadj(element);
for (auto && neighbor : neighbors) {
dadjncy(pos) = neighbor;
edge_loads(pos) =
edge_load_func(unlinearized_element, unlinearized(neighbor));
++pos;
}
}
}
-
/* -------------------------------------------------------------------------- */
void MeshPartition::fillPartitionInformation(
const Mesh & mesh, const Int * linearized_partitions) {
AKANTU_DEBUG_IN();
CSR<Element> node_to_elem;
MeshUtils::buildNode2Elements(mesh, node_to_elem);
UInt linearized_el = 0;
for (const auto & type :
mesh.elementTypes(spatial_dimension, _not_ghost, _ek_not_defined)) {
UInt nb_element = mesh.getNbElement(type);
UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
auto & partition = partitions.alloc(nb_element, 1, type, _not_ghost);
auto & ghost_part_csr = ghost_partitions_csr(type, _not_ghost);
ghost_part_csr.resizeRows(nb_element);
auto & ghost_partition_offset =
ghost_partitions_offset.alloc(nb_element + 1, 1, type, _ghost);
auto & ghost_partition = ghost_partitions.alloc(0, 1, type, _ghost);
const auto & connectivity = mesh.getConnectivity(type, _not_ghost);
auto conn_it = connectivity.begin(connectivity.getNbComponent());
for (UInt el = 0; el < nb_element; ++el, ++linearized_el) {
UInt part = linearized_partitions[linearized_el];
partition(el) = part;
std::list<UInt> list_adj_part;
for (UInt n = 0; n < nb_nodes_per_element; ++n) {
auto conn = Vector<UInt>(*(conn_it + el));
UInt node = conn(n);
for (const auto & adj_element : node_to_elem.getRow(node)) {
UInt adj_el = linearized(adj_element);
UInt adj_part = linearized_partitions[adj_el];
if (part != adj_part) {
list_adj_part.push_back(adj_part);
}
}
}
list_adj_part.sort();
list_adj_part.unique();
for (auto & adj_part : list_adj_part) {
ghost_part_csr.getRows().push_back(adj_part);
ghost_part_csr.rowOffset(el)++;
ghost_partition.push_back(adj_part);
ghost_partition_offset(el)++;
}
}
ghost_part_csr.countToCSR();
/// convert the ghost_partitions_offset array in an offset array
auto & ghost_partitions_offset_ptr = ghost_partitions_offset(type, _ghost);
for (UInt i = 1; i < nb_element; ++i) {
ghost_partitions_offset_ptr(i) += ghost_partitions_offset_ptr(i - 1);
}
for (UInt i = nb_element; i > 0; --i) {
ghost_partitions_offset_ptr(i) = ghost_partitions_offset_ptr(i - 1);
}
ghost_partitions_offset_ptr(0) = 0;
}
// All Facets
for (Int sp = spatial_dimension - 1; sp >= 0; --sp) {
- for (const auto & type : mesh.elementTypes(sp, _not_ghost, _ek_not_defined)) {
+ for (const auto & type :
+ mesh.elementTypes(sp, _not_ghost, _ek_not_defined)) {
UInt nb_element = mesh.getNbElement(type);
auto & partition = partitions.alloc(nb_element, 1, type, _not_ghost);
AKANTU_DEBUG_INFO("Allocating partitions for " << type);
auto & ghost_part_csr = ghost_partitions_csr(type, _not_ghost);
ghost_part_csr.resizeRows(nb_element);
auto & ghost_partition_offset =
ghost_partitions_offset.alloc(nb_element + 1, 1, type, _ghost);
auto & ghost_partition = ghost_partitions.alloc(0, 1, type, _ghost);
AKANTU_DEBUG_INFO("Allocating ghost_partitions for " << type);
const Array<std::vector<Element>> & elem_to_subelem =
mesh.getElementToSubelement(type, _not_ghost);
// Facet loop
for (UInt i(0); i < mesh.getNbElement(type, _not_ghost); ++i) {
const auto & adjacent_elems = elem_to_subelem(i);
if (adjacent_elems.empty()) {
partition(i) = 0;
continue;
}
Element min_elem{_max_element_type, std::numeric_limits<UInt>::max(),
- *(ghost_type_t{}.end())};
+ *(ghost_type_t{}.end())};
UInt min_part(std::numeric_limits<UInt>::max());
std::set<UInt> adjacent_parts;
for (auto adj_elem : adjacent_elems) {
if (adj_elem == ElementNull) { // case of boundary elements
continue;
}
auto adjacent_elem_part = partitions(adj_elem);
if (adjacent_elem_part < min_part) {
min_part = adjacent_elem_part;
min_elem = adj_elem;
}
adjacent_parts.insert(adjacent_elem_part);
}
partition(i) = min_part;
auto git = ghost_partitions_csr(min_elem.type, _not_ghost)
.begin(min_elem.element);
auto gend = ghost_partitions_csr(min_elem.type, _not_ghost)
.end(min_elem.element);
for (; git != gend; ++git) {
adjacent_parts.insert(*git);
}
adjacent_parts.erase(min_part);
for (const auto & part : adjacent_parts) {
ghost_part_csr.getRows().push_back(part);
ghost_part_csr.rowOffset(i)++;
ghost_partition.push_back(part);
}
ghost_partition_offset(i + 1) =
ghost_partition_offset(i + 1) + adjacent_elems.size();
}
ghost_part_csr.countToCSR();
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void MeshPartition::tweakConnectivity() {
AKANTU_DEBUG_IN();
MeshAccessor mesh_accessor(const_cast<Mesh &>(mesh));
for (auto && type :
mesh.elementTypes(spatial_dimension, _not_ghost, _ek_not_defined)) {
auto & connectivity = mesh_accessor.getConnectivity(type, _not_ghost);
auto & saved_conn = saved_connectivity.alloc(
connectivity.size(), connectivity.getNbComponent(), type, _not_ghost);
saved_conn.copy(connectivity);
for (auto && conn :
make_view(connectivity, connectivity.getNbComponent())) {
for (auto && node : conn) {
if (mesh.isPeriodicSlave(node)) {
node = mesh.getPeriodicMaster(node);
}
}
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void MeshPartition::restoreConnectivity() {
AKANTU_DEBUG_IN();
MeshAccessor mesh_accessor(const_cast<Mesh &>(mesh));
for (auto && type : saved_connectivity.elementTypes(
spatial_dimension, _not_ghost, _ek_not_defined)) {
auto & conn = mesh_accessor.getConnectivity(type, _not_ghost);
auto & saved_conn = saved_connectivity(type, _not_ghost);
conn.copy(saved_conn);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
-bool MeshPartition::hasPartitions(ElementType type,
- GhostType ghost_type) {
+bool MeshPartition::hasPartitions(ElementType type, GhostType ghost_type) {
return partitions.exists(type, ghost_type);
}
/* -------------------------------------------------------------------------- */
void MeshPartition::printself(std::ostream & stream, int indent) const {
std::string space(indent, AKANTU_INDENT);
stream << space << "MeshPartition ["
<< "\n";
stream << space << " + id : " << id << "\n";
stream << space << " + nb partitions: " << nb_partitions << "\n";
stream << space << " + partitions [ "
<< "\n";
partitions.printself(stream, indent + 2);
stream << space << " ]"
<< "\n";
stream << space << "]"
<< "\n";
}
/* -------------------------------------------------------------------------- */
} // namespace akantu
diff --git a/src/mesh_utils/mesh_partition.hh b/src/mesh_utils/mesh_partition.hh
index 623854832..ab85a9c20 100644
--- a/src/mesh_utils/mesh_partition.hh
+++ b/src/mesh_utils/mesh_partition.hh
@@ -1,151 +1,150 @@
/**
* @file mesh_partition.hh
*
* @author David Simon Kammer <david.kammer@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Tue Jan 23 2018
*
* @brief tools to partitionate a mesh
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_csr.hh"
#include "mesh.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_MESH_PARTITION_HH_
#define AKANTU_MESH_PARTITION_HH_
namespace akantu {
-class MeshPartition : protected Memory {
+class MeshPartition {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
MeshPartition(Mesh & mesh, UInt spatial_dimension,
- const ID & id = "MeshPartitioner",
- const MemoryID & memory_id = 0);
+ const ID & id = "MeshPartitioner");
- ~MeshPartition() override;
+ virtual ~MeshPartition();
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// define a partition of the mesh
virtual void partitionate(
UInt nb_part,
const std::function<Int(const Element &, const Element &)> &
edge_load_func =
[](auto && /*unused*/, auto && /*unused*/) { return 1; },
const std::function<Int(const Element &)> & vertex_load_func =
[](auto && /*unused*/) { return 1; }) = 0;
/// reorder the nodes to reduce the filling during the factorization of a
/// matrix that has a profil based on the connectivity of the mesh
virtual void reorder() = 0;
/// fill the partitions array with a given linearized partition information
void fillPartitionInformation(const Mesh & mesh,
const Int * linearized_partitions);
virtual void printself(std::ostream & stream, int indent = 0) const;
protected:
/// build the dual graph of the mesh, for all element of spatial_dimension
void
buildDualGraph(Array<Int> & dxadj, Array<Int> & dadjncy,
Array<Int> & edge_loads,
const std::function<Int(const Element &, const Element &)> &
edge_load_func,
Array<Int> & vertex_loads,
const std::function<Int(const Element &)> & vertex_load_func);
/// tweak the mesh to handle the PBC pairs
void tweakConnectivity();
/// restore the mesh that has been tweaked
void restoreConnectivity();
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
bool hasPartitions(ElementType type, GhostType ghost_type);
AKANTU_GET_MACRO(Partitions, partitions, const ElementTypeMapArray<UInt> &);
AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(Partition, partitions, UInt);
AKANTU_GET_MACRO(GhostPartitionCSR, ghost_partitions_csr,
const ElementTypeMap<CSR<UInt>> &);
AKANTU_GET_MACRO(NbPartition, nb_partitions, UInt);
AKANTU_SET_MACRO(NbPartition, nb_partitions, UInt);
protected:
UInt linearized(const Element & element);
Element unlinearized(UInt lin_element);
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
/// id
- std::string id;
+ ID id;
/// the mesh to partition
Mesh & mesh;
/// dimension of the elements to consider in the mesh
UInt spatial_dimension;
/// number of partitions
UInt nb_partitions;
/// partition numbers
ElementTypeMapArray<UInt> partitions;
ElementTypeMap<CSR<UInt>> ghost_partitions_csr;
ElementTypeMapArray<UInt> ghost_partitions;
ElementTypeMapArray<UInt> ghost_partitions_offset;
Array<UInt> * permutation;
ElementTypeMapArray<UInt> saved_connectivity;
// vector of pair to ensure the iteration order
std::vector<std::pair<ElementType, UInt>> linearized_offsets;
};
/// standard output stream operator
inline std::ostream & operator<<(std::ostream & stream,
const MeshPartition & _this) {
_this.printself(stream);
return stream;
}
} // namespace akantu
#ifdef AKANTU_USE_SCOTCH
#include "mesh_partition_scotch.hh"
#endif
#endif /* AKANTU_MESH_PARTITION_HH_ */
diff --git a/src/mesh_utils/mesh_partition/mesh_partition_mesh_data.cc b/src/mesh_utils/mesh_partition/mesh_partition_mesh_data.cc
index a2c871d2a..5572fc5e8 100644
--- a/src/mesh_utils/mesh_partition/mesh_partition_mesh_data.cc
+++ b/src/mesh_utils/mesh_partition/mesh_partition_mesh_data.cc
@@ -1,140 +1,139 @@
/**
* @file mesh_partition_mesh_data.cc
*
* @author Dana Christen <dana.christen@epfl.ch>
* @author David Simon Kammer <david.kammer@epfl.ch>
*
* @date creation: Fri May 03 2013
* @date last modification: Tue Feb 20 2018
*
* @brief implementation of the MeshPartitionMeshData class
*
*
* Copyright (©) 2014-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
#include "mesh_partition_mesh_data.hh"
#if !defined(AKANTU_NDEBUG)
#include <set>
#endif
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
MeshPartitionMeshData::MeshPartitionMeshData(Mesh & mesh,
UInt spatial_dimension,
- const ID & id,
- const MemoryID & memory_id)
- : MeshPartition(mesh, spatial_dimension, id, memory_id) {
+ const ID & id)
+ : MeshPartition(mesh, spatial_dimension, id) {
AKANTU_DEBUG_IN();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
MeshPartitionMeshData::MeshPartitionMeshData(
Mesh & mesh, const ElementTypeMapArray<UInt> & mapping,
- UInt spatial_dimension, const ID & id, const MemoryID & memory_id)
- : MeshPartition(mesh, spatial_dimension, id, memory_id),
+ UInt spatial_dimension, const ID & id)
+ : MeshPartition(mesh, spatial_dimension, id),
partition_mapping(&mapping) {
AKANTU_DEBUG_IN();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void MeshPartitionMeshData::partitionate(
UInt nb_part,
const std::function<Int(const Element &, const Element &)> &/*edge_load_func*/,
const std::function<Int(const Element &)> &/*vertex_load_func*/) {
AKANTU_DEBUG_IN();
if (mesh.isPeriodic()) {
tweakConnectivity();
}
nb_partitions = nb_part;
auto ghost_type = _not_ghost;
auto spatial_dimension = mesh.getSpatialDimension();
UInt linearized_el = 0;
auto nb_elements = mesh.getNbElement(mesh.getSpatialDimension(), ghost_type);
auto *partition_list = new Int[nb_elements];
#if !defined(AKANTU_NDEBUG)
std::set<UInt> partitions;
#endif
for (auto type :
mesh.elementTypes(spatial_dimension, ghost_type, _ek_not_defined)) {
const auto & partition_array = (*partition_mapping)(type, ghost_type);
AKANTU_DEBUG_ASSERT(partition_array.size() ==
mesh.getNbElement(type, ghost_type),
"The partition mapping does not have the right number "
<< "of entries for type " << type
<< " and ghost type " << ghost_type << "."
<< " Tags=" << partition_array.size()
<< " Mesh=" << mesh.getNbElement(type, ghost_type));
for (auto && part : partition_array) {
partition_list[linearized_el] = part;
#if !defined(AKANTU_NDEBUG)
partitions.insert(part);
#endif
++linearized_el;
}
}
#if !defined(AKANTU_NDEBUG)
AKANTU_DEBUG_ASSERT(partitions.size() == nb_part,
"The number of real partitions does not match with the "
"number of asked partitions");
#endif
fillPartitionInformation(mesh, partition_list);
delete[] partition_list;
if (mesh.isPeriodic()) {
restoreConnectivity();
}
AKANTU_DEBUG_OUT();
} // namespace akantu
/* -------------------------------------------------------------------------- */
void MeshPartitionMeshData::reorder() { AKANTU_TO_IMPLEMENT(); }
/* -------------------------------------------------------------------------- */
void MeshPartitionMeshData::setPartitionMapping(
const ElementTypeMapArray<UInt> & mapping) {
partition_mapping = &mapping;
}
/* -------------------------------------------------------------------------- */
void MeshPartitionMeshData::setPartitionMappingFromMeshData(
const std::string & data_name) {
partition_mapping = &(mesh.getData<UInt>(data_name));
}
} // namespace akantu
diff --git a/src/mesh_utils/mesh_partition/mesh_partition_mesh_data.hh b/src/mesh_utils/mesh_partition/mesh_partition_mesh_data.hh
index 0497f0de4..ea386a223 100644
--- a/src/mesh_utils/mesh_partition/mesh_partition_mesh_data.hh
+++ b/src/mesh_utils/mesh_partition/mesh_partition_mesh_data.hh
@@ -1,94 +1,93 @@
/**
* @file mesh_partition_mesh_data.hh
*
* @author Dana Christen <dana.christen@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Wed Nov 08 2017
*
* @brief mesh partitioning based on data provided in the mesh
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_MESH_PARTITION_MESH_DATA_HH_
#define AKANTU_MESH_PARTITION_MESH_DATA_HH_
/* -------------------------------------------------------------------------- */
#include "aka_common.hh"
#include "mesh_partition.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
class MeshPartitionMeshData : public MeshPartition {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
MeshPartitionMeshData(Mesh & mesh, UInt spatial_dimension,
- const ID & id = "MeshPartitionerMeshData",
- const MemoryID & memory_id = 0);
+ const ID & id = "MeshPartitionerMeshData"
+ );
MeshPartitionMeshData(Mesh & mesh,
const ElementTypeMapArray<UInt> & mapping,
UInt spatial_dimension,
- const ID & id = "MeshPartitionerMeshData",
- const MemoryID & memory_id = 0);
+ const ID & id = "MeshPartitionerMeshData");
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
void partitionate(
UInt nb_part,
const std::function<Int(const Element &, const Element &)> &edge_load_func =
[](auto && /*unused*/, auto && /*unused*/) { return 1; },
const std::function<Int(const Element &)> &vertex_load_func =
[](auto && /*unused*/) { return 1; }) override;
void reorder() override;
void setPartitionMapping(const ElementTypeMapArray<UInt> & mapping);
void setPartitionMappingFromMeshData(const std::string & data_name);
private:
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
private:
const ElementTypeMapArray<UInt> * partition_mapping;
};
/* -------------------------------------------------------------------------- */
/* inline functions */
/* -------------------------------------------------------------------------- */
} // namespace akantu
#endif /* AKANTU_MESH_PARTITION_MESH_DATA_HH_ */
diff --git a/src/mesh_utils/mesh_partition/mesh_partition_scotch.cc b/src/mesh_utils/mesh_partition/mesh_partition_scotch.cc
index 7e2848937..d1c33da68 100644
--- a/src/mesh_utils/mesh_partition/mesh_partition_scotch.cc
+++ b/src/mesh_utils/mesh_partition/mesh_partition_scotch.cc
@@ -1,472 +1,471 @@
/**
* @file mesh_partition_scotch.cc
*
* @author David Simon Kammer <david.kammer@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Tue Feb 20 2018
*
* @brief implementation of the MeshPartitionScotch class
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "mesh_partition_scotch.hh"
#include "aka_common.hh"
#include "aka_random_generator.hh"
#include "aka_static_if.hh"
#include "mesh_accessor.hh"
#include "mesh_utils.hh"
/* -------------------------------------------------------------------------- */
#include <cstdio>
#include <fstream>
/* -------------------------------------------------------------------------- */
#if !defined(AKANTU_USE_PTSCOTCH)
#ifndef AKANTU_SCOTCH_NO_EXTERN
extern "C" {
#endif // AKANTU_SCOTCH_NO_EXTERN
#include <scotch.h>
#ifndef AKANTU_SCOTCH_NO_EXTERN
}
#endif // AKANTU_SCOTCH_NO_EXTERN
#else // AKANTU_USE_PTSCOTCH
#include <ptscotch.h>
#endif // AKANTU_USE_PTSCOTCH
namespace akantu {
namespace {
constexpr int scotch_version = int(SCOTCH_VERSION);
}
/* -------------------------------------------------------------------------- */
MeshPartitionScotch::MeshPartitionScotch(Mesh & mesh, UInt spatial_dimension,
- const ID & id,
- const MemoryID & memory_id)
- : MeshPartition(mesh, spatial_dimension, id, memory_id) {
+ const ID & id)
+ : MeshPartition(mesh, spatial_dimension, id) {
AKANTU_DEBUG_IN();
// check if the akantu types and Scotch one are consistent
static_assert(
sizeof(Int) == sizeof(SCOTCH_Num),
"The integer type of Akantu does not match the one from Scotch");
static_if(aka::bool_constant<scotch_version >= 6>{})
.then([](auto && y) { SCOTCH_randomSeed(y); })
.else_([](auto && y) { srandom(y); })(
std::forward<UInt>(RandomGenerator<UInt>::seed()));
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
static SCOTCH_Mesh * createMesh(const Mesh & mesh) {
AKANTU_DEBUG_IN();
UInt spatial_dimension = mesh.getSpatialDimension();
UInt nb_nodes = mesh.getNbNodes();
UInt total_nb_element = 0;
UInt nb_edge = 0;
for (const auto & type : mesh.elementTypes(spatial_dimension)) {
UInt nb_element = mesh.getNbElement(type);
UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
total_nb_element += nb_element;
nb_edge += nb_element * nb_nodes_per_element;
}
SCOTCH_Num vnodbas = 0;
SCOTCH_Num vnodnbr = nb_nodes;
SCOTCH_Num velmbas = vnodnbr;
SCOTCH_Num velmnbr = total_nb_element;
auto * verttab = new SCOTCH_Num[vnodnbr + velmnbr + 1];
SCOTCH_Num * vendtab = verttab + 1;
SCOTCH_Num * velotab = nullptr;
SCOTCH_Num * vnlotab = nullptr;
SCOTCH_Num * vlbltab = nullptr;
memset(verttab, 0, (vnodnbr + velmnbr + 1) * sizeof(SCOTCH_Num));
for (const auto & type : mesh.elementTypes(spatial_dimension)) {
if (Mesh::getSpatialDimension(type) != spatial_dimension) {
continue;
}
UInt nb_element = mesh.getNbElement(type);
UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
const Array<UInt> & connectivity = mesh.getConnectivity(type, _not_ghost);
/// count number of occurrence of each node
for (UInt el = 0; el < nb_element; ++el) {
UInt * conn_val = connectivity.storage() + el * nb_nodes_per_element;
for (UInt n = 0; n < nb_nodes_per_element; ++n) {
verttab[*(conn_val++)]++;
}
}
}
/// convert the occurrence array in a csr one
for (UInt i = 1; i < nb_nodes; ++i) {
verttab[i] += verttab[i - 1];
}
for (UInt i = nb_nodes; i > 0; --i) {
verttab[i] = verttab[i - 1];
}
verttab[0] = 0;
/// rearrange element to get the node-element list
SCOTCH_Num edgenbr = verttab[vnodnbr] + nb_edge;
auto * edgetab = new SCOTCH_Num[edgenbr];
UInt linearized_el = 0;
for (const auto & type : mesh.elementTypes(spatial_dimension)) {
auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
const auto & connectivity = mesh.getConnectivity(type, _not_ghost);
for (auto && conn : make_view(connectivity, nb_nodes_per_element)) {
for (auto c : conn) {
edgetab[verttab[c]++] = linearized_el + velmbas;
}
++linearized_el;
}
}
for (UInt i = nb_nodes; i > 0; --i) {
verttab[i] = verttab[i - 1];
}
verttab[0] = 0;
SCOTCH_Num * verttab_tmp = verttab + vnodnbr + 1;
SCOTCH_Num * edgetab_tmp = edgetab + verttab[vnodnbr];
for (const auto & type : mesh.elementTypes(spatial_dimension)) {
auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
const auto & connectivity = mesh.getConnectivity(type, _not_ghost);
for (auto && conn : make_view(connectivity, nb_nodes_per_element)) {
*verttab_tmp = *(verttab_tmp - 1) + nb_nodes_per_element;
verttab_tmp++;
for (auto c : conn) {
*(edgetab_tmp++) = c + vnodbas;
}
}
}
auto * meshptr = new SCOTCH_Mesh;
SCOTCH_meshInit(meshptr);
SCOTCH_meshBuild(meshptr, velmbas, vnodbas, velmnbr, vnodnbr, verttab,
vendtab, velotab, vnlotab, vlbltab, edgenbr, edgetab);
/// Check the mesh
AKANTU_DEBUG_ASSERT(SCOTCH_meshCheck(meshptr) == 0,
"Scotch mesh is not consistent");
#ifndef AKANTU_NDEBUG
if (AKANTU_DEBUG_TEST(dblDump)) {
/// save initial graph
FILE * fmesh = fopen("ScotchMesh.msh", "w");
SCOTCH_meshSave(meshptr, fmesh);
fclose(fmesh);
/// write geometry file
std::ofstream fgeominit;
fgeominit.open("ScotchMesh.xyz");
fgeominit << spatial_dimension << std::endl << nb_nodes << std::endl;
const Array<Real> & nodes = mesh.getNodes();
Real * nodes_val = nodes.storage();
for (UInt i = 0; i < nb_nodes; ++i) {
fgeominit << i << " ";
for (UInt s = 0; s < spatial_dimension; ++s) {
fgeominit << *(nodes_val++) << " ";
}
fgeominit << std::endl;
;
}
fgeominit.close();
}
#endif
AKANTU_DEBUG_OUT();
return meshptr;
}
/* -------------------------------------------------------------------------- */
static void destroyMesh(SCOTCH_Mesh * meshptr) {
AKANTU_DEBUG_IN();
SCOTCH_Num velmbas;
SCOTCH_Num vnodbas;
SCOTCH_Num vnodnbr;
SCOTCH_Num velmnbr;
SCOTCH_Num * verttab;
SCOTCH_Num * vendtab;
SCOTCH_Num * velotab;
SCOTCH_Num * vnlotab;
SCOTCH_Num * vlbltab;
SCOTCH_Num edgenbr;
SCOTCH_Num * edgetab;
SCOTCH_Num degrptr;
SCOTCH_meshData(meshptr, &velmbas, &vnodbas, &velmnbr, &vnodnbr, &verttab,
&vendtab, &velotab, &vnlotab, &vlbltab, &edgenbr, &edgetab,
&degrptr);
delete[] verttab;
delete[] edgetab;
SCOTCH_meshExit(meshptr);
delete meshptr;
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void MeshPartitionScotch::partitionate(
UInt nb_part,
const std::function<Int(const Element &, const Element &)> & edge_load_func,
const std::function<Int(const Element &)> & vertex_load_func) {
AKANTU_DEBUG_IN();
nb_partitions = nb_part;
if (mesh.isPeriodic()) {
tweakConnectivity();
}
AKANTU_DEBUG_INFO("Partitioning the mesh " << mesh.getID() << " in "
<< nb_part << " parts.");
Array<Int> dxadj;
Array<Int> dadjncy;
Array<Int> edge_loads;
Array<Int> vertex_loads;
buildDualGraph(dxadj, dadjncy, edge_loads, edge_load_func, vertex_loads,
vertex_load_func);
/// variables that will hold our structures in scotch format
SCOTCH_Graph scotch_graph;
SCOTCH_Strat scotch_strat;
/// description number and arrays for struct mesh for scotch
SCOTCH_Num baseval = 0; // base numbering for element and
// nodes (0 -> C , 1 -> fortran)
SCOTCH_Num vertnbr = dxadj.size() - 1; // number of vertexes
SCOTCH_Num * parttab; // array of partitions
SCOTCH_Num edgenbr = dxadj(vertnbr); // twice the number of "edges"
//(an "edge" bounds two nodes)
SCOTCH_Num * verttab = dxadj.storage(); // array of start indices in edgetab
SCOTCH_Num * vendtab = nullptr; // array of after-last indices in edgetab
SCOTCH_Num * velotab =
vertex_loads.storage(); // integer load associated with
// every vertex ( optional )
SCOTCH_Num * edlotab = edge_loads.storage(); // integer load associated with
// every edge ( optional )
SCOTCH_Num * edgetab = dadjncy.storage(); // adjacency array of every vertex
SCOTCH_Num * vlbltab = nullptr; // vertex label array (optional)
/// Allocate space for Scotch arrays
parttab = new SCOTCH_Num[vertnbr];
/// Initialize the strategy structure
SCOTCH_stratInit(&scotch_strat);
/// Initialize the graph structure
SCOTCH_graphInit(&scotch_graph);
/// Build the graph from the adjacency arrays
SCOTCH_graphBuild(&scotch_graph, baseval, vertnbr, verttab, vendtab, velotab,
vlbltab, edgenbr, edgetab, edlotab);
#ifndef AKANTU_NDEBUG
if (AKANTU_DEBUG_TEST(dblDump)) {
/// save initial graph
FILE * fgraphinit = fopen("GraphIniFile.grf", "w");
SCOTCH_graphSave(&scotch_graph, fgraphinit);
fclose(fgraphinit);
/// write geometry file
std::ofstream fgeominit;
fgeominit.open("GeomIniFile.xyz");
fgeominit << spatial_dimension << std::endl << vertnbr << std::endl;
const Array<Real> & nodes = mesh.getNodes();
auto nodes_it = nodes.begin(spatial_dimension);
UInt out_linerized_el = 0;
for (const auto & type :
mesh.elementTypes(spatial_dimension, _not_ghost, _ek_not_defined)) {
UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
const Array<UInt> & connectivity = mesh.getConnectivity(type);
Vector<Real> mid(spatial_dimension);
for (auto && conn : make_view(connectivity, nb_nodes_per_element)) {
mid.set(0.);
for (auto node : conn) {
mid += Vector<Real>(nodes_it[node]);
}
mid /= nb_nodes_per_element;
fgeominit << out_linerized_el++ << " ";
for (UInt s = 0; s < spatial_dimension; ++s) {
fgeominit << mid[s] << " ";
}
fgeominit << std::endl;
;
}
}
fgeominit.close();
}
#endif
/// Check the graph
AKANTU_DEBUG_ASSERT(SCOTCH_graphCheck(&scotch_graph) == 0,
"Graph to partition is not consistent");
/// Partition the mesh
SCOTCH_graphPart(&scotch_graph, nb_part, &scotch_strat, parttab);
/// Check the graph
AKANTU_DEBUG_ASSERT(SCOTCH_graphCheck(&scotch_graph) == 0,
"Partitioned graph is not consistent");
#ifndef AKANTU_NDEBUG
if (AKANTU_DEBUG_TEST(dblDump)) {
/// save the partitioned graph
FILE * fgraph = fopen("GraphFile.grf", "w");
SCOTCH_graphSave(&scotch_graph, fgraph);
fclose(fgraph);
/// save the partition map
std::ofstream fmap;
fmap.open("MapFile.map");
fmap << vertnbr << std::endl;
for (Int i = 0; i < vertnbr; i++) {
fmap << i << " " << parttab[i] << std::endl;
}
fmap.close();
}
#endif
/// free the scotch data structures
SCOTCH_stratExit(&scotch_strat);
SCOTCH_graphFree(&scotch_graph);
SCOTCH_graphExit(&scotch_graph);
fillPartitionInformation(mesh, parttab);
delete[] parttab;
if (mesh.isPeriodic()) {
restoreConnectivity();
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void MeshPartitionScotch::reorder() {
AKANTU_DEBUG_IN();
AKANTU_DEBUG_INFO("Reordering the mesh " << mesh.getID());
SCOTCH_Mesh * scotch_mesh = createMesh(mesh);
UInt nb_nodes = mesh.getNbNodes();
SCOTCH_Strat scotch_strat;
// SCOTCH_Ordering scotch_order;
auto * permtab = new SCOTCH_Num[nb_nodes];
SCOTCH_Num * peritab = nullptr;
SCOTCH_Num cblknbr = 0;
SCOTCH_Num * rangtab = nullptr;
SCOTCH_Num * treetab = nullptr;
/// Initialize the strategy structure
SCOTCH_stratInit(&scotch_strat);
SCOTCH_Graph scotch_graph;
SCOTCH_graphInit(&scotch_graph);
SCOTCH_meshGraph(scotch_mesh, &scotch_graph);
#ifndef AKANTU_NDEBUG
if (AKANTU_DEBUG_TEST(dblDump)) {
FILE * fgraphinit = fopen("ScotchMesh.grf", "w");
SCOTCH_graphSave(&scotch_graph, fgraphinit);
fclose(fgraphinit);
}
#endif
/// Check the graph
// AKANTU_DEBUG_ASSERT(SCOTCH_graphCheck(&scotch_graph) == 0,
// "Mesh to Graph is not consistent");
SCOTCH_graphOrder(&scotch_graph, &scotch_strat, permtab, peritab, &cblknbr,
rangtab, treetab);
SCOTCH_graphExit(&scotch_graph);
SCOTCH_stratExit(&scotch_strat);
destroyMesh(scotch_mesh);
/// Renumbering
UInt spatial_dimension = mesh.getSpatialDimension();
MeshAccessor mesh_accessor(mesh);
for (auto gt : ghost_types) {
for (const auto & type : mesh.elementTypes(_ghost_type = gt)) {
auto & connectivity = mesh_accessor.getConnectivity(type, gt);
for (auto && c : make_view(connectivity)) {
c = permtab[c];
}
}
}
/// \todo think of a in-place way to do it
auto * new_coordinates = new Real[spatial_dimension * nb_nodes];
Real * old_coordinates = mesh.getNodes().storage();
for (UInt i = 0; i < nb_nodes; ++i) {
memcpy(new_coordinates + permtab[i] * spatial_dimension,
old_coordinates + i * spatial_dimension,
spatial_dimension * sizeof(Real));
}
memcpy(old_coordinates, new_coordinates,
nb_nodes * spatial_dimension * sizeof(Real));
delete[] new_coordinates;
delete[] permtab;
AKANTU_DEBUG_OUT();
}
} // namespace akantu
diff --git a/src/mesh_utils/mesh_partition/mesh_partition_scotch.hh b/src/mesh_utils/mesh_partition/mesh_partition_scotch.hh
index 06eb2c9f2..4e51d4fe6 100644
--- a/src/mesh_utils/mesh_partition/mesh_partition_scotch.hh
+++ b/src/mesh_utils/mesh_partition/mesh_partition_scotch.hh
@@ -1,76 +1,75 @@
/**
* @file mesh_partition_scotch.hh
*
* @author David Simon Kammer <david.kammer@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Wed Nov 08 2017
*
* @brief mesh partitioning based on libScotch
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "mesh_partition.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_MESH_PARTITION_SCOTCH_HH_
#define AKANTU_MESH_PARTITION_SCOTCH_HH_
/* -------------------------------------------------------------------------- */
namespace akantu {
class MeshPartitionScotch : public MeshPartition {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
MeshPartitionScotch(Mesh & mesh, UInt spatial_dimension,
- const ID & id = "mesh_partition_scotch",
- const MemoryID & memory_id = 0);
+ const ID & id = "mesh_partition_scotch");
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
void partitionate(
UInt nb_part,
const std::function<Int(const Element &, const Element &)> &
edge_load_func =
[](auto && /*unused*/, auto && /*unused*/) { return 1; },
const std::function<Int(const Element &)> & vertex_load_func =
[](auto && /*unused*/) { return 1; }) override;
void reorder() override;
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
private:
};
} // namespace akantu
#endif /* AKANTU_MESH_PARTITION_SCOTCH_HH_ */
diff --git a/src/mesh_utils/mesh_utils.cc b/src/mesh_utils/mesh_utils.cc
index 0491e90d8..b1a88f4dd 100644
--- a/src/mesh_utils/mesh_utils.cc
+++ b/src/mesh_utils/mesh_utils.cc
@@ -1,810 +1,808 @@
/**
* @file mesh_utils.cc
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author Dana Christen <dana.christen@epfl.ch>
* @author David Simon Kammer <david.kammer@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
* @author Leonardo Snozzi <leonardo.snozzi@epfl.ch>
* @author Marco Vocialta <marco.vocialta@epfl.ch>
*
* @date creation: Fri Aug 20 2010
* @date last modification: Wed Feb 21 2018
*
* @brief All mesh utils necessary for various tasks
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "element_synchronizer.hh"
#include "fe_engine.hh"
#include "mesh_accessor.hh"
#include "mesh_iterators.hh"
#include "mesh_utils.hh"
/* -------------------------------------------------------------------------- */
#include <limits>
#include <numeric>
#include <queue>
#include <set>
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
void MeshUtils::buildNode2Elements(const Mesh & mesh,
CSR<Element> & node_to_elem,
UInt spatial_dimension) {
AKANTU_DEBUG_IN();
if (spatial_dimension == _all_dimensions) {
spatial_dimension = mesh.getSpatialDimension();
}
/// count number of occurrence of each node
UInt nb_nodes = mesh.getNbNodes();
/// array for the node-element list
node_to_elem.resizeRows(nb_nodes);
node_to_elem.clearRows();
for_each_element(
mesh,
[&](auto && element) {
Vector<UInt> conn = mesh.getConnectivity(element);
for (auto && node : conn) {
++node_to_elem.rowOffset(node);
}
},
_spatial_dimension = spatial_dimension, _element_kind = _ek_not_defined);
node_to_elem.countToCSR();
node_to_elem.resizeCols();
/// rearrange element to get the node-element list
// Element e;
node_to_elem.beginInsertions();
for_each_element(
mesh,
[&](auto && element) {
Vector<UInt> conn = mesh.getConnectivity(element);
for (auto && node : conn) {
node_to_elem.insertInRow(node, element);
}
},
_spatial_dimension = spatial_dimension, _element_kind = _ek_not_defined);
node_to_elem.endInsertions();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void MeshUtils::buildNode2ElementsElementTypeMap(const Mesh & mesh,
CSR<UInt> & node_to_elem,
ElementType type,
GhostType ghost_type) {
AKANTU_DEBUG_IN();
UInt nb_nodes = mesh.getNbNodes();
UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
UInt nb_elements = mesh.getConnectivity(type, ghost_type).size();
UInt * conn_val = mesh.getConnectivity(type, ghost_type).storage();
/// array for the node-element list
node_to_elem.resizeRows(nb_nodes);
node_to_elem.clearRows();
/// count number of occurrence of each node
for (UInt el = 0; el < nb_elements; ++el) {
UInt el_offset = el * nb_nodes_per_element;
for (UInt n = 0; n < nb_nodes_per_element; ++n) {
++node_to_elem.rowOffset(conn_val[el_offset + n]);
}
}
/// convert the occurrence array in a csr one
node_to_elem.countToCSR();
node_to_elem.resizeCols();
node_to_elem.beginInsertions();
/// save the element index in the node-element list
for (UInt el = 0; el < nb_elements; ++el) {
UInt el_offset = el * nb_nodes_per_element;
for (UInt n = 0; n < nb_nodes_per_element; ++n) {
node_to_elem.insertInRow(conn_val[el_offset + n], el);
}
}
node_to_elem.endInsertions();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void MeshUtils::buildFacets(Mesh & mesh) {
AKANTU_DEBUG_IN();
UInt spatial_dimension = mesh.getSpatialDimension();
for (auto ghost_type : ghost_types) {
for (const auto & type :
mesh.elementTypes(spatial_dimension - 1, ghost_type)) {
mesh.getConnectivity(type, ghost_type).resize(0);
// \todo inform the mesh event handler
}
}
buildFacetsDimension(mesh, mesh, true, spatial_dimension);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void MeshUtils::buildAllFacets(const Mesh & mesh, Mesh & mesh_facets,
UInt to_dimension) {
AKANTU_DEBUG_IN();
UInt spatial_dimension = mesh.getSpatialDimension();
buildAllFacets(mesh, mesh_facets, spatial_dimension, to_dimension);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void MeshUtils::buildAllFacets(const Mesh & mesh, Mesh & mesh_facets,
UInt from_dimension, UInt to_dimension) {
AKANTU_DEBUG_IN();
to_dimension = std::max(to_dimension, UInt(0));
AKANTU_DEBUG_ASSERT(
mesh_facets.isMeshFacets(),
"The mesh_facets should be initialized with initMeshFacets");
/// generate facets
buildFacetsDimension(mesh, mesh_facets, false, from_dimension);
/// sort facets and generate sub-facets
for (UInt i = from_dimension - 1; i > to_dimension; --i) {
buildFacetsDimension(mesh_facets, mesh_facets, false, i);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void MeshUtils::buildFacetsDimension(const Mesh & mesh, Mesh & mesh_facets,
bool boundary_only, UInt dimension) {
AKANTU_DEBUG_IN();
// save the current parent of mesh_facets and set it temporarly to mesh since
// mesh is the one containing the elements for which mesh_facets has the
// sub-elements
// example: if the function is called with mesh = mesh_facets
const Mesh * mesh_facets_parent = nullptr;
try {
mesh_facets_parent = &mesh_facets.getMeshParent();
} catch (...) {
}
mesh_facets.defineMeshParent(mesh);
MeshAccessor mesh_accessor(mesh_facets);
UInt spatial_dimension = mesh.getSpatialDimension();
const Array<Real> & mesh_facets_nodes = mesh_facets.getNodes();
const auto mesh_facets_nodes_it = mesh_facets_nodes.begin(spatial_dimension);
CSR<Element> node_to_elem;
buildNode2Elements(mesh, node_to_elem, dimension);
Array<UInt> counter;
std::vector<Element> connected_elements;
NewElementsEvent event(AKANTU_CURRENT_FUNCTION);
// init the SubelementToElement data to improve performance
for (auto && ghost_type : ghost_types) {
for (auto && type : mesh.elementTypes(dimension, ghost_type)) {
auto & subelement_to_element =
mesh_accessor.getSubelementToElement(type, ghost_type);
subelement_to_element.resize(mesh.getNbElement(type, ghost_type),
ElementNull);
auto facet_types = mesh.getAllFacetTypes(type);
for (auto && ft : arange(facet_types.size())) {
auto facet_type = facet_types(ft);
mesh_accessor.getElementToSubelement(facet_type, ghost_type);
mesh_accessor.getConnectivity(facet_type, ghost_type);
}
}
}
const ElementSynchronizer * synchronizer = nullptr;
if (mesh.isDistributed()) {
synchronizer = &(mesh.getElementSynchronizer());
}
Element current_element;
for (auto && ghost_type : ghost_types) {
GhostType facet_ghost_type = ghost_type;
current_element.ghost_type = ghost_type;
for (auto && type : mesh.elementTypes(dimension, ghost_type)) {
auto facet_types = mesh.getAllFacetTypes(type);
current_element.type = type;
for (auto && ft : arange(facet_types.size())) {
auto facet_type = facet_types(ft);
auto nb_element = mesh.getNbElement(type, ghost_type);
auto && element_to_subelement =
&mesh_accessor.getElementToSubelementNC(facet_type, ghost_type);
auto && connectivity_facets =
&mesh_accessor.getConnectivity(facet_type, ghost_type);
auto nb_nodes_per_facet = connectivity_facets->getNbComponent();
// Vector<UInt> facet(nb_nodes_per_facet);
for (UInt el = 0; el < nb_element; ++el) {
current_element.element = el;
auto && facets =
mesh.getFacetConnectivity(current_element, ft).transpose();
for (auto facet : facets) {
// facet = facets(f);
UInt first_node_nb_elements = node_to_elem.getNbCols(facet(0));
counter.resize(first_node_nb_elements);
counter.zero();
// loop over the other nodes to search intersecting elements,
// which are the elements that share another node with the
// starting element after first_node
for (auto && data : enumerate(node_to_elem.getRow(facet(0)))) {
auto && local_el = std::get<0>(data);
auto && first_node = std::get<1>(data);
for (auto n : arange(1, nb_nodes_per_facet)) {
auto && node_elements = node_to_elem.getRow(facet(n));
counter(local_el) += std::count(
node_elements.begin(), node_elements.end(), first_node);
}
}
// counting the number of elements connected to the facets and
// taking the minimum element number, because the facet should
// be inserted just once
UInt nb_element_connected_to_facet = 0;
Element minimum_el = ElementNull;
connected_elements.clear();
for (auto && data : enumerate(node_to_elem.getRow(facet(0)))) {
if (not(counter(std::get<0>(data)) == nb_nodes_per_facet - 1)) {
continue;
}
auto && real_el = std::get<1>(data);
++nb_element_connected_to_facet;
minimum_el = std::min(minimum_el, real_el);
connected_elements.push_back(real_el);
}
if (minimum_el != current_element) {
continue;
}
bool full_ghost_facet = false;
UInt n = 0;
while (n < nb_nodes_per_facet and mesh.isPureGhostNode(facet(n))) {
++n;
}
if (n == nb_nodes_per_facet) {
full_ghost_facet = true;
}
if (full_ghost_facet) {
continue;
}
if (boundary_only and nb_element_connected_to_facet != 1) {
continue;
}
std::vector<Element> elements;
// build elements_on_facets: linearized_el must come first
// in order to store the facet in the correct direction
// and avoid to invert the sign in the normal computation
elements.reserve(elements.size() + connected_elements.size());
for (auto && connected_element : connected_elements) {
elements.push_back(connected_element);
}
if (nb_element_connected_to_facet == 1) { /// boundary facet
elements.push_back(ElementNull);
} else if (nb_element_connected_to_facet == 2) { /// internal facet
/// check if facet is in between ghost and normal
/// elements: if it's the case, the facet is either
/// ghost or not ghost. The criterion to decide this
/// is arbitrary. It was chosen to check the processor
/// id (prank) of the two neighboring elements. If
/// prank of the ghost element is lower than prank of
/// the normal one, the facet is not ghost, otherwise
/// it's ghost
GhostType gt[2] = {_not_ghost, _not_ghost};
for (UInt el = 0; el < connected_elements.size(); ++el) {
gt[el] = connected_elements[el].ghost_type;
}
if ((gt[0] == _not_ghost) xor (gt[1] == _not_ghost)) {
UInt prank[2];
for (UInt el = 0; el < 2; ++el) {
prank[el] = synchronizer->getRank(connected_elements[el]);
}
// ugly trick from Marco detected :P
bool ghost_one = (gt[0] != _ghost);
if (prank[ghost_one] > prank[!ghost_one]) {
facet_ghost_type = _not_ghost;
} else {
facet_ghost_type = _ghost;
}
connectivity_facets = &mesh_accessor.getConnectivity(
facet_type, facet_ghost_type);
element_to_subelement = &mesh_accessor.getElementToSubelementNC(
facet_type, facet_ghost_type);
}
}
element_to_subelement->push_back(elements);
connectivity_facets->push_back(facet);
/// current facet index
UInt current_facet = connectivity_facets->size() - 1;
Element facet_element{facet_type, current_facet, facet_ghost_type};
event.getList().push_back(facet_element);
/// loop on every element connected to current facet and
/// insert current facet in the first free spot of the
/// subelement_to_element vector
for (auto & loc_el : elements) {
if (loc_el == ElementNull) {
continue;
}
auto && subelements =
mesh_accessor.getSubelementToElement(loc_el);
for (auto & el : subelements) {
if (el != ElementNull) {
continue;
}
el = facet_element;
break;
}
}
/// reset connectivity in case a facet was found in
/// between ghost and normal elements
if (facet_ghost_type != ghost_type) {
facet_ghost_type = ghost_type;
connectivity_facets =
&mesh_accessor.getConnectivity(facet_type, facet_ghost_type);
element_to_subelement = &mesh_accessor.getElementToSubelement(
facet_type, facet_ghost_type);
}
}
}
}
}
}
mesh_facets.sendEvent(event);
// restore the parent of mesh_facet
if (mesh_facets_parent != nullptr) {
mesh_facets.defineMeshParent(*mesh_facets_parent);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void MeshUtils::renumberMeshNodes(Mesh & mesh,
Array<UInt> & local_connectivities,
UInt nb_local_element, UInt nb_ghost_element,
ElementType type,
Array<UInt> & old_nodes_numbers) {
AKANTU_DEBUG_IN();
UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
std::map<UInt, UInt> renumbering_map;
for (UInt i = 0; i < old_nodes_numbers.size(); ++i) {
renumbering_map[old_nodes_numbers(i)] = i;
}
/// renumber the nodes
renumberNodesInConnectivity(local_connectivities,
(nb_local_element + nb_ghost_element) *
nb_nodes_per_element,
renumbering_map);
old_nodes_numbers.resize(renumbering_map.size());
for (auto & renumber_pair : renumbering_map) {
old_nodes_numbers(renumber_pair.second) = renumber_pair.first;
}
renumbering_map.clear();
MeshAccessor mesh_accessor(mesh);
/// copy the renumbered connectivity to the right place
auto & local_conn = mesh_accessor.getConnectivity(type);
local_conn.resize(nb_local_element);
if (nb_local_element > 0) {
memcpy(local_conn.storage(), local_connectivities.storage(),
nb_local_element * nb_nodes_per_element * sizeof(UInt));
}
auto & ghost_conn = mesh_accessor.getConnectivity(type, _ghost);
ghost_conn.resize(nb_ghost_element);
if (nb_ghost_element > 0) {
std::memcpy(ghost_conn.storage(),
local_connectivities.storage() +
nb_local_element * nb_nodes_per_element,
nb_ghost_element * nb_nodes_per_element * sizeof(UInt));
}
auto & ghost_counter = mesh_accessor.getGhostsCounters(type, _ghost);
ghost_counter.resize(nb_ghost_element, 1);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void MeshUtils::renumberNodesInConnectivity(
Array<UInt> & list_nodes, UInt nb_nodes,
std::map<UInt, UInt> & renumbering_map) {
AKANTU_DEBUG_IN();
UInt * connectivity = list_nodes.storage();
UInt new_node_num = renumbering_map.size();
for (UInt n = 0; n < nb_nodes; ++n, ++connectivity) {
UInt & node = *connectivity;
auto it = renumbering_map.find(node);
if (it == renumbering_map.end()) {
UInt old_node = node;
renumbering_map[old_node] = new_node_num;
node = new_node_num;
++new_node_num;
} else {
node = it->second;
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void MeshUtils::purifyMesh(Mesh & mesh) {
AKANTU_DEBUG_IN();
std::map<UInt, UInt> renumbering_map;
RemovedNodesEvent remove_nodes(mesh, AKANTU_CURRENT_FUNCTION);
Array<UInt> & nodes_removed = remove_nodes.getList();
for (auto ghost_type : ghost_types) {
for (auto type :
mesh.elementTypes(_all_dimensions, ghost_type, _ek_not_defined)) {
UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
Array<UInt> & connectivity = mesh.getConnectivity(type, ghost_type);
UInt nb_element(connectivity.size());
renumberNodesInConnectivity(
connectivity, nb_element * nb_nodes_per_element, renumbering_map);
}
}
Array<UInt> & new_numbering = remove_nodes.getNewNumbering();
std::fill(new_numbering.begin(), new_numbering.end(), UInt(-1));
for (auto && pair : renumbering_map) {
new_numbering(std::get<0>(pair)) = std::get<1>(pair);
}
for (UInt i = 0; i < new_numbering.size(); ++i) {
if (new_numbering(i) == UInt(-1)) {
nodes_removed.push_back(i);
}
}
mesh.sendEvent(remove_nodes);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void MeshUtils::flipFacets(
Mesh & mesh_facets,
const ElementTypeMapArray<UInt> & remote_global_connectivities,
GhostType gt_facet) {
AKANTU_DEBUG_IN();
UInt spatial_dimension = mesh_facets.getSpatialDimension();
/// get global connectivity for local mesh
ElementTypeMapArray<UInt> local_global_connectivities(
- "local_global_connectivity", mesh_facets.getID(),
- mesh_facets.getMemoryID());
+ "local_global_connectivity", mesh_facets.getID());
local_global_connectivities.initialize(
mesh_facets, _spatial_dimension = spatial_dimension - 1,
_ghost_type = gt_facet, _with_nb_nodes_per_element = true,
_with_nb_element = true);
mesh_facets.getGlobalConnectivity(local_global_connectivities);
MeshAccessor mesh_accessor(mesh_facets);
/// loop on every facet
for (auto type_facet :
mesh_facets.elementTypes(spatial_dimension - 1, gt_facet)) {
auto & connectivity = mesh_accessor.getConnectivity(type_facet, gt_facet);
auto & local_global_connectivity =
local_global_connectivities(type_facet, gt_facet);
const auto & remote_global_connectivity =
remote_global_connectivities(type_facet, gt_facet);
auto & element_per_facet =
mesh_accessor.getElementToSubelementNC(type_facet, gt_facet);
auto & subfacet_to_facet =
mesh_accessor.getSubelementToElementNC(type_facet, gt_facet);
auto nb_nodes_per_facet = connectivity.getNbComponent();
auto nb_nodes_per_P1_facet =
Mesh::getNbNodesPerElement(Mesh::getP1ElementType(type_facet));
for (auto && data :
zip(make_view(connectivity, nb_nodes_per_facet),
make_view(local_global_connectivity, nb_nodes_per_facet),
make_view(remote_global_connectivity, nb_nodes_per_facet),
make_view(subfacet_to_facet, subfacet_to_facet.getNbComponent()),
make_view(element_per_facet))) {
auto & conn = std::get<0>(data);
auto & local_gconn = std::get<1>(data);
const auto & remote_gconn = std::get<2>(data);
/// skip facet if connectivities are the same
if (local_gconn == remote_gconn) {
continue;
}
/// re-arrange connectivity
auto conn_tmp = conn;
auto begin = local_gconn.begin();
auto end = local_gconn.end();
AKANTU_DEBUG_ASSERT(std::is_permutation(begin, end, remote_gconn.begin()),
"This facets are not just permutation of each other, "
<< local_gconn << " and " << remote_gconn);
for (auto && data : enumerate(remote_gconn)) {
auto it = std::find(begin, end, std::get<1>(data));
AKANTU_DEBUG_ASSERT(it != end, "Node not found");
UInt new_position = it - begin;
conn(new_position) = conn_tmp(std::get<0>(data));
;
}
// std::transform(remote_gconn.begin(), remote_gconn.end(), conn.begin(),
// [&](auto && gnode) {
// auto it = std::find(begin, end, gnode);
// AKANTU_DEBUG_ASSERT(it != end, "Node not found");
// return conn_tmp(it - begin);
// });
/// if 3D, check if facets are just rotated
if (spatial_dimension == 3) {
auto begin = remote_gconn.begin();
/// find first node
auto it = std::find(begin, remote_gconn.end(), local_gconn(0));
UInt n;
UInt start = it - begin;
/// count how many nodes in the received connectivity follow
/// the same order of those in the local connectivity
for (n = 1; n < nb_nodes_per_P1_facet &&
local_gconn(n) ==
remote_gconn((start + n) % nb_nodes_per_P1_facet);
++n) {
;
}
/// skip the facet inversion if facet is just rotated
if (n == nb_nodes_per_P1_facet) {
continue;
}
}
/// update data to invert facet
auto & element_per_facet = std::get<4>(data);
if (element_per_facet[1] !=
ElementNull) { // by convention the first facet
// cannot be a ElementNull
std::swap(element_per_facet[0], element_per_facet[1]);
}
auto & subfacets_of_facet = std::get<3>(data);
std::swap(subfacets_of_facet(0), subfacets_of_facet(1));
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void MeshUtils::fillElementToSubElementsData(Mesh & mesh) {
AKANTU_DEBUG_IN();
if (mesh.getNbElement(mesh.getSpatialDimension() - 1) == 0) {
AKANTU_DEBUG_INFO("There are not facets, add them in the mesh file or call "
"the buildFacet method.");
return;
}
UInt spatial_dimension = mesh.getSpatialDimension();
- ElementTypeMapArray<Real> barycenters("barycenter_tmp", mesh.getID(),
- mesh.getMemoryID());
+ ElementTypeMapArray<Real> barycenters("barycenter_tmp", mesh.getID());
barycenters.initialize(mesh, _nb_component = spatial_dimension,
_spatial_dimension = _all_dimensions);
Element element;
for (auto ghost_type : ghost_types) {
element.ghost_type = ghost_type;
for (const auto & type : mesh.elementTypes(_all_dimensions, ghost_type)) {
element.type = type;
UInt nb_element = mesh.getNbElement(type, ghost_type);
Array<Real> & barycenters_arr = barycenters(type, ghost_type);
barycenters_arr.resize(nb_element);
auto bary = barycenters_arr.begin(spatial_dimension);
auto bary_end = barycenters_arr.end(spatial_dimension);
for (UInt el = 0; bary != bary_end; ++bary, ++el) {
element.element = el;
mesh.getBarycenter(element, *bary);
}
}
}
MeshAccessor mesh_accessor(mesh);
for (Int sp(spatial_dimension); sp >= 1; --sp) {
if (mesh.getNbElement(sp) == 0) {
continue;
}
for (auto ghost_type : ghost_types) {
for (auto & type : mesh.elementTypes(sp, ghost_type)) {
auto & subelement_to_element =
mesh_accessor.getSubelementToElement(type, ghost_type);
subelement_to_element.resize(mesh.getNbElement(type, ghost_type));
subelement_to_element.set(ElementNull);
}
for (auto & type : mesh.elementTypes(sp - 1, ghost_type)) {
auto & element_to_subelement =
mesh_accessor.getElementToSubelement(type, ghost_type);
element_to_subelement.resize(mesh.getNbElement(type, ghost_type));
element_to_subelement.clear();
}
}
CSR<Element> nodes_to_elements;
buildNode2Elements(mesh, nodes_to_elements, sp);
Element facet_element;
for (auto ghost_type : ghost_types) {
facet_element.ghost_type = ghost_type;
for (const auto & type : mesh.elementTypes(sp - 1, ghost_type)) {
facet_element.type = type;
auto & element_to_subelement =
mesh_accessor.getElementToSubelement(type, ghost_type);
const auto & connectivity = mesh.getConnectivity(type, ghost_type);
//element_to_subelement.resize(connectivity.size());
for (auto && data : enumerate(
make_view(connectivity, mesh.getNbNodesPerElement(type)))) {
const auto & facet = std::get<1>(data);
facet_element.element = std::get<0>(data);
std::map<Element, UInt> element_seen_counter;
auto nb_nodes_per_facet =
mesh.getNbNodesPerElement(Mesh::getP1ElementType(type));
// count the number of node in common between the facet and the
// other element connected to the nodes of the facet
for (auto node : arange(nb_nodes_per_facet)) {
for (auto & elem : nodes_to_elements.getRow(facet(node))) {
auto cit = element_seen_counter.find(elem);
if (cit != element_seen_counter.end()) {
cit->second++;
} else {
element_seen_counter[elem] = 1;
}
}
}
// check which are the connected elements
std::vector<Element> connected_elements;
for (auto && cit : element_seen_counter) {
if (cit.second == nb_nodes_per_facet) {
connected_elements.push_back(cit.first);
}
}
// add the connected elements as sub-elements
for (auto & connected_element : connected_elements) {
element_to_subelement(facet_element.element)
.push_back(connected_element);
}
// add the element as sub-element to the connected elements
for (auto & connected_element : connected_elements) {
Vector<Element> subelements_to_element =
mesh.getSubelementToElement(connected_element);
// find the position where to insert the element
auto it = std::find(subelements_to_element.begin(),
subelements_to_element.end(), ElementNull);
AKANTU_DEBUG_ASSERT(
it != subelements_to_element.end(),
"The element "
<< connected_element << " seems to have too many facets!! ("
<< (it - subelements_to_element.begin()) << " < "
<< mesh.getNbFacetsPerElement(connected_element.type)
<< ")");
*it = facet_element;
}
}
}
}
}
AKANTU_DEBUG_OUT();
}
} // namespace akantu
diff --git a/src/model/common/dof_manager/dof_manager.cc b/src/model/common/dof_manager/dof_manager.cc
index f5b0847bf..783f2a658 100644
--- a/src/model/common/dof_manager/dof_manager.cc
+++ b/src/model/common/dof_manager/dof_manager.cc
@@ -1,1017 +1,1018 @@
/**
* @file dof_manager.cc
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Tue Aug 18 2015
* @date last modification: Wed Feb 21 2018
*
* @brief Implementation of the common parts of the DOFManagers
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#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 <memory>
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
-DOFManager::DOFManager(const ID & id, const MemoryID & memory_id)
- : Memory(id, memory_id), dofs_flag(0, 1, std::string(id + ":dofs_type")),
+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, const MemoryID & memory_id)
- : Memory(id, memory_id), mesh(&mesh),
+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<ID> DOFManager::getDOFIDs() const {
std::vector<ID> keys;
for (const auto & dof_data : this->dofs) {
keys.push_back(dof_data.first);
}
return keys;
}
/* -------------------------------------------------------------------------- */
void DOFManager::assembleElementalArrayLocalArray(
const Array<Real> & elementary_vect, Array<Real> & array_assembeled,
ElementType type, GhostType ghost_type, Real scale_factor,
const Array<UInt> & filter_elements) {
AKANTU_DEBUG_IN();
UInt nb_element;
UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
UInt nb_degree_of_freedom =
elementary_vect.getNbComponent() / nb_nodes_per_element;
UInt * filter_it = nullptr;
if (filter_elements != empty_filter) {
nb_element = filter_elements.size();
filter_it = filter_elements.storage();
} 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 Array<UInt> & connectivity =
this->mesh->getConnectivity(type, ghost_type);
Array<Real>::const_matrix_iterator elem_it =
elementary_vect.begin(nb_degree_of_freedom, nb_nodes_per_element);
for (UInt el = 0; el < nb_element; ++el, ++elem_it) {
UInt element = el;
if (filter_it != nullptr) {
// conn_it = conn_begin + *filter_it;
element = *filter_it;
}
// const Vector<UInt> & conn = *conn_it;
const Matrix<Real> & elemental_val = *elem_it;
for (UInt n = 0; n < nb_nodes_per_element; ++n) {
UInt offset_node = connectivity(element, n) * nb_degree_of_freedom;
Vector<Real> assemble(array_assembeled.storage() + offset_node,
nb_degree_of_freedom);
Vector<Real> elem_val = elemental_val(n);
assemble.aXplusY(elem_val, scale_factor);
}
if (filter_it != nullptr) {
++filter_it;
}
// else
// ++conn_it;
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void DOFManager::assembleElementalArrayToResidual(
const ID & dof_id, const Array<Real> & elementary_vect,
ElementType type, GhostType ghost_type, Real scale_factor,
const Array<UInt> & filter_elements) {
AKANTU_DEBUG_IN();
UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
UInt nb_degree_of_freedom =
elementary_vect.getNbComponent() / nb_nodes_per_element;
Array<Real> 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<Real> & elementary_vect,
const ID & lumped_mtx, ElementType type,
GhostType ghost_type, Real scale_factor,
const Array<UInt> & filter_elements) {
AKANTU_DEBUG_IN();
UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
UInt nb_degree_of_freedom =
elementary_vect.getNbComponent() / nb_nodes_per_element;
Array<Real> 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<Real> & 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<Real> & lumped) {
AKANTU_DEBUG_IN();
this->getArrayPerDOFs(dof_id, this->getLumpedMatrix(lumped_mtx), lumped);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void DOFManager::assembleToResidual(const ID & dof_id,
Array<Real> & 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<Real> & 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 <typename Func>
auto DOFManager::countDOFsForNodes(const DOFData & dof_data, UInt 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<DOFData> 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<Real> & dofs_array,
const 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<Real> & 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<UInt, UInt, UInt>
DOFManager::registerDOFsInternal(const ID & dof_id, Array<Real> & dofs_array) {
DOFData & dof_data = this->getDOFData(dof_id);
dof_data.dof = &dofs_array;
UInt nb_local_dofs = 0;
UInt 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<UInt(UInt)> 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](UInt node) -> UInt { return support_nodes[node]; });
} else {
this->updateDOFsData(dof_data, nb_local_dofs, nb_pure_local,
mesh->getNbNodes(),
[](UInt node) -> UInt { 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<Real> & 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<Real> & 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, UInt order,
Array<Real> & dofs_derivative) {
DOFData & dof = this->getDOFData(dof_id);
std::vector<Array<Real> *> & derivatives = dof.dof_derivatives;
if (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<bool> & blocked_dofs) {
DOFData & 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<SparseMatrix> & 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<SolverVector> & 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<NonLinearSolver> & 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<TimeStepSolver> & 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<UInt, UInt>
DOFManager::updateNodalDOFs(const ID & dof_id, const Array<UInt> & nodes_list) {
auto & dof_data = this->getDOFData(dof_id);
UInt nb_new_local_dofs;
UInt 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;
UInt 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](UInt 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<UInt> & nodes_list,
const NewNodesEvent & /*unused*/) {
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<UInt> new_nodes_list;
for (const auto & node : nodes_list) {
if (node_group.find(node) != UInt(-1)) {
new_nodes_list.push_back(node);
}
}
this->updateNodalDOFs(dof_id, new_nodes_list);
}
}
this->resizeGlobalArrays();
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
class GlobalDOFInfoDataAccessor : public DataAccessor<UInt> {
public:
using size_type =
typename std::unordered_map<UInt, std::vector<UInt>>::size_type;
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)) {
UInt node;
Int dof;
std::tie(dof, node) = pair;
dofs_per_node[node].push_back(dof);
}
}
UInt getNbData(const Array<UInt> & 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(Int);
}
return 0;
}
void packData(CommunicationBuffer & buffer, const Array<UInt> & 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<UInt> & 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) {
Int 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<UInt, std::vector<Int>> dofs_per_node;
DOFManager::DOFData & dof_data;
DOFManager & dof_manager;
};
/* -------------------------------------------------------------------------- */
auto DOFManager::computeFirstDOFIDs(UInt nb_new_local_dofs,
UInt nb_new_pure_local) {
// determine the first local/global dof id to use
UInt 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, UInt nb_new_local_dofs,
UInt nb_new_pure_local, UInt nb_node,
const std::function<UInt(UInt)> & 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<std::pair<UInt, UInt>, UInt> masters_dofs;
// update per dof info
UInt local_eq_num;
UInt 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, UInt nb_new_local_dofs,
UInt nb_new_pure_local) {
dof_data.local_equation_number.reserve(dof_data.local_equation_number.size() +
nb_new_local_dofs);
UInt first_local_dof_id;
UInt 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<UInt> & /*unused*/,
const Array<UInt> & /*unused*/,
const RemovedNodesEvent & /*unused*/) {}
/* -------------------------------------------------------------------------- */
void DOFManager::onElementsAdded(const Array<Element> & /*unused*/,
const NewElementsEvent & /*unused*/) {}
/* -------------------------------------------------------------------------- */
void DOFManager::onElementsRemoved(const Array<Element> & /*unused*/,
const ElementTypeMapArray<UInt> & /*unused*/,
const RemovedElementsEvent & /*unused*/) {}
/* -------------------------------------------------------------------------- */
void DOFManager::onElementsChanged(const Array<Element> & /*unused*/,
const Array<Element> & /*unused*/,
const ElementTypeMapArray<UInt> & /*unused*/,
const ChangedElementsEvent & /*unused*/) {}
/* -------------------------------------------------------------------------- */
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 (!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()) {
auto are_equal = this->global_blocked_dofs_release ==
this->previous_global_blocked_dofs_release;
// std::equal(global_blocked_dofs.begin(), global_blocked_dofs.end(),
// previous_global_blocked_dofs.begin());
if (not are_equal) {
J.applyBoundary();
}
previous_global_blocked_dofs.copy(global_blocked_dofs);
} else {
J.applyBoundary();
}
this->jacobian_release = J.getRelease();
}
/* -------------------------------------------------------------------------- */
void DOFManager::assembleMatMulVectToGlobalArray(const ID & dof_id,
const ID & A_id,
const Array<Real> & 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<Real> & 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 1dfd7a573..916370287 100644
--- a/src/model/common/dof_manager/dof_manager.hh
+++ b/src/model/common/dof_manager/dof_manager.hh
@@ -1,715 +1,715 @@
/**
* @file dof_manager.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Tue Aug 18 2015
* @date last modification: Wed Feb 21 2018
*
* @brief Class handling the different types of dofs
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_factory.hh"
-#include "aka_memory.hh"
#include "mesh.hh"
/* -------------------------------------------------------------------------- */
#include <map>
#include <set>
/* -------------------------------------------------------------------------- */
#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 Memory, protected MeshEventHandler {
+class DOFManager : protected MeshEventHandler {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
protected:
struct DOFData;
public:
- DOFManager(const ID & id = "dof_manager", const MemoryID & memory_id = 0);
- DOFManager(Mesh & mesh, const ID & id = "dof_manager",
- const MemoryID & memory_id = 0);
+ 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<Real> & dofs_array,
const 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<Real> & 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<Real> & dofs_array);
/// register an array of increment of degree of freedom
virtual void registerDOFsIncrement(const ID & dof_id,
Array<Real> & dofs_array);
/// register an array of derivatives for a particular dof array
virtual void registerDOFsDerivative(const ID & dof_id, UInt order,
Array<Real> & dofs_derivative);
/// register array representing the blocked degree of freedoms
virtual void registerBlockedDOFs(const ID & dof_id,
Array<bool> & blocked_dofs);
/// Assemble an array to the global residual array
virtual void assembleToResidual(const ID & dof_id,
Array<Real> & array_to_assemble,
Real scale_factor = 1.);
/// Assemble an array to the global lumped matrix array
virtual void assembleToLumpedMatrix(const ID & dof_id,
Array<Real> & 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<Real> & elementary_vect, Array<Real> & array_assembeled,
ElementType type, GhostType ghost_type,
Real scale_factor = 1.,
const Array<UInt> & 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<Real> & elementary_vect,
ElementType type, GhostType ghost_type,
Real scale_factor = 1.,
const Array<UInt> & 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<Real> & elementary_vect,
const ID & lumped_mtx, ElementType type,
GhostType ghost_type, Real scale_factor = 1.,
const Array<UInt> & 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<Real> & elementary_mat, ElementType type,
GhostType ghost_type = _not_ghost,
const MatrixType & elemental_matrix_type = _symmetric,
const Array<UInt> & 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<Real> & x,
Array<Real> & 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<Real> & x,
Real scale_factor = 1) = 0;
/// assemble coupling terms between to dofs
virtual void assemblePreassembledMatrix(const ID & dof_id_m,
const ID & dof_id_n,
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<Real> & 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,
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);
/// sets the lumped matrix to 0
virtual void zeroLumpedMatrix(const ID & mtx);
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<Real> & 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);
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<UInt, UInt, UInt>
registerDOFsInternal(const ID & dof_id, Array<Real> & 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<Real> & solution_array);
/// fill a Vector with the equation numbers corresponding to the given
/// connectivity
static inline void extractElementEquationNumber(
const Array<Int> & equation_numbers, const Vector<UInt> & connectivity,
UInt nb_degree_of_freedom, Vector<Int> & element_equation_number);
/// Assemble a array to a global one
void assembleMatMulVectToGlobalArray(const ID & dof_id, const ID & A_id,
const Array<Real> & x,
SolverVector & array,
Real scale_factor = 1.);
/// common function that can be called by derived class with proper matrice
/// types
template <typename Mat>
void assemblePreassembledMatrix_(Mat & A, const ID & dof_id_m,
const ID & dof_id_n,
const TermsToAssemble & terms);
template <typename Mat>
void assembleElementalMatricesToMatrix_(
Mat & A, const ID & dof_id, const Array<Real> & elementary_mat,
ElementType type, GhostType ghost_type,
const MatrixType & elemental_matrix_type,
const Array<UInt> & filter_elements);
template <typename Vec>
void assembleMatMulVectToArray_(const ID & dof_id, const ID & A_id,
const Array<Real> & x, Array<Real> & array,
Real scale_factor);
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
/// Get the location type of a given dof
inline bool isLocalOrMasterDOF(UInt local_dof_num);
/// Answer to the question is a dof a slave dof ?
inline bool isSlaveDOF(UInt local_dof_num);
/// Answer to the question is a dof a slave dof ?
inline bool isPureGhostDOF(UInt local_dof_num);
/// tells if the dof manager knows about a global dof
bool hasGlobalEquationNumber(Int global) const;
/// return the local index of the global equation number
inline Int globalToLocalEquationNumber(Int global) const;
/// converts local equation numbers to global equation numbers;
inline Int localToGlobalEquationNumber(Int local) const;
/// get the array of dof types (use only if you know what you do...)
inline NodeFlag getDOFFlag(Int local_id) const;
/// Global number of dofs
AKANTU_GET_MACRO(SystemSize, this->system_size, UInt);
/// Local number of dofs
AKANTU_GET_MACRO(LocalSystemSize, this->local_system_size, UInt);
/// Pure local number of dofs
AKANTU_GET_MACRO(PureLocalSystemSize, this->pure_local_system_size, UInt);
/// Retrieve all the registered DOFs
std::vector<ID> getDOFIDs() const;
/* ------------------------------------------------------------------------ */
/* DOFs and derivatives accessors */
/* ------------------------------------------------------------------------ */
/// Get a reference to the registered dof array for a given id
inline Array<Real> & getDOFs(const ID & dofs_id);
/// Get the support type of a given dof
inline DOFSupportType getSupportType(const ID & dofs_id) const;
/// are the dofs registered
inline bool hasDOFs(const ID & dof_id) const;
/// Get a reference to the registered dof derivatives array for a given id
inline Array<Real> & getDOFsDerivatives(const ID & dofs_id, UInt order);
/// Does the dof has derivatives
inline bool hasDOFsDerivatives(const ID & dofs_id, UInt order) const;
/// Get a reference to the blocked dofs array registered for the given id
inline const Array<bool> & getBlockedDOFs(const ID & dofs_id) const;
/// Does the dof has a blocked array
inline bool hasBlockedDOFs(const ID & dofs_id) const;
/// Get a reference to the registered dof increment array for a given id
inline Array<Real> & getDOFsIncrement(const ID & dofs_id);
/// Does the dof has a increment array
inline bool hasDOFsIncrement(const ID & dofs_id) const;
/// Does the dof has a previous array
inline Array<Real> & 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;
/// saves the values from dofs to previous dofs
virtual void savePreviousDOFs(const ID & dofs_id);
/// Get a reference to the solution array registered for the given id
inline const Array<Real> & getSolution(const ID & dofs_id) const;
/// Get a reference to the solution array registered for the given id
inline Array<Real> & getSolution(const ID & dofs_id);
/// Get the blocked dofs array
AKANTU_GET_MACRO(GlobalBlockedDOFs, global_blocked_dofs, const Array<Int> &);
/// Get the blocked dofs array
AKANTU_GET_MACRO(PreviousGlobalBlockedDOFs, previous_global_blocked_dofs,
const Array<Int> &);
/* ------------------------------------------------------------------------ */
/* Matrices accessors */
/* ------------------------------------------------------------------------ */
/// Get an instance of a new SparseMatrix
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;
/// Get the equation numbers corresponding to a dof_id. This might be used to
/// access the matrix.
inline const Array<Int> & getLocalEquationsNumbers(const ID & dof_id) const;
protected:
/// get the array of dof types (use only if you know what you do...)
inline const Array<UInt> & getDOFsAssociatedNodes(const ID & dof_id) const;
protected:
/* ------------------------------------------------------------------------ */
/// register a matrix
SparseMatrix & registerSparseMatrix(const ID & matrix_id,
std::unique_ptr<SparseMatrix> & matrix);
/// register a lumped matrix (aka a Vector)
SolverVector & registerLumpedMatrix(const ID & matrix_id,
std::unique_ptr<SolverVector> & matrix);
/// register a non linear solver instantiated by a derived class
NonLinearSolver &
registerNonLinearSolver(const ID & non_linear_solver_id,
std::unique_ptr<NonLinearSolver> & non_linear_solver);
/// register a time step solver instantiated by a derived class
TimeStepSolver &
registerTimeStepSolver(const ID & time_step_solver_id,
std::unique_ptr<TimeStepSolver> & time_step_solver);
template <class NLSType, class DMType>
NonLinearSolver & registerNonLinearSolver(DMType & dm, const ID & id,
const NonLinearSolverType & type) {
ID non_linear_solver_id = this->id + ":nls:" + id;
std::unique_ptr<NonLinearSolver> nls = std::make_unique<NLSType>(
- dm, type, non_linear_solver_id, this->memory_id);
+ dm, type, non_linear_solver_id);
return this->registerNonLinearSolver(non_linear_solver_id, nls);
}
template <class TSSType, class DMType>
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<TimeStepSolver> tss =
std::make_unique<TSSType>(dm, type, non_linear_solver, solver_callback,
- time_step_solver_id, this->memory_id);
+ time_step_solver_id);
return this->registerTimeStepSolver(time_step_solver_id, tss);
}
template <class MatType, class DMType>
SparseMatrix & registerSparseMatrix(DMType & dm, const ID & id,
const MatrixType & matrix_type) {
ID matrix_id = this->id + ":mtx:" + id;
std::unique_ptr<SparseMatrix> sm =
std::make_unique<MatType>(dm, matrix_type, matrix_id);
return this->registerSparseMatrix(matrix_id, sm);
}
template <class MatType>
SparseMatrix & registerSparseMatrix(const ID & id,
const ID & matrix_to_copy_id) {
ID matrix_id = this->id + ":mtx:" + id;
auto & sm_to_copy =
aka::as_type<MatType>(this->getMatrix(matrix_to_copy_id));
std::unique_ptr<SparseMatrix> sm =
std::make_unique<MatType>(sm_to_copy, matrix_id);
return this->registerSparseMatrix(matrix_id, sm);
}
template <class MatType, class DMType>
SolverVector & registerLumpedMatrix(DMType & dm, const ID & id) {
ID matrix_id = this->id + ":lumped_mtx:" + id;
std::unique_ptr<SolverVector> sm = std::make_unique<MatType>(dm, matrix_id);
return this->registerLumpedMatrix(matrix_id, sm);
}
protected:
virtual void makeConsistentForPeriodicity(const ID & dof_id,
SolverVector & array) = 0;
virtual void assembleToGlobalArray(const ID & dof_id,
const Array<Real> & 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<Real> & local) = 0;
/// Get the reference of an existing matrix
SparseMatrix & getMatrix(const ID & matrix_id);
/// check if the given matrix exists
bool hasMatrix(const ID & matrix_id) const;
/// Get an instance of a new lumped matrix
virtual SolverVector & getNewLumpedMatrix(const ID & matrix_id) = 0;
/// Get the lumped version of a given matrix
const SolverVector & getLumpedMatrix(const ID & matrix_id) const;
/// Get the lumped version of a given matrix
SolverVector & getLumpedMatrix(const ID & matrix_id);
/// check if the given matrix exists
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;
/// get instance of a non linear solver
virtual NonLinearSolver & getNonLinearSolver(const ID & nls_solver_id);
/// check if the given solver exists
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;
/// get instance of a time step solver
virtual TimeStepSolver & getTimeStepSolver(const ID & time_step_solver_id);
/// check if the given solver exists
bool hasTimeStepSolver(const ID & solver_id) const;
/* ------------------------------------------------------------------------ */
const Mesh & getMesh() {
if (mesh != nullptr) {
return *mesh;
}
AKANTU_EXCEPTION("No mesh registered in this dof manager");
}
/* ------------------------------------------------------------------------ */
AKANTU_GET_MACRO(Communicator, communicator, const auto &);
AKANTU_GET_MACRO_NOT_CONST(Communicator, communicator, auto &);
/* ------------------------------------------------------------------------ */
AKANTU_GET_MACRO(Solution, *(solution.get()), const auto &);
AKANTU_GET_MACRO_NOT_CONST(Solution, *(solution.get()), auto &);
AKANTU_GET_MACRO(Residual, *(residual.get()), const auto &);
AKANTU_GET_MACRO_NOT_CONST(Residual, *(residual.get()), auto &);
/* ------------------------------------------------------------------------ */
/* MeshEventHandler interface */
/* ------------------------------------------------------------------------ */
protected:
friend class GlobalDOFInfoDataAccessor;
/// helper function for the DOFManager::onNodesAdded method
virtual std::pair<UInt, UInt> updateNodalDOFs(const ID & dof_id,
const Array<UInt> & nodes_list);
template <typename Func>
auto countDOFsForNodes(const DOFData & dof_data, UInt nb_nodes,
Func && getNode);
void updateDOFsData(DOFData & dof_data, UInt nb_new_local_dofs,
UInt nb_new_pure_local, UInt nb_nodes,
const std::function<UInt(UInt)> & getNode);
void updateDOFsData(DOFData & dof_data, UInt nb_new_local_dofs,
UInt nb_new_pure_local);
auto computeFirstDOFIDs(UInt nb_new_local_dofs, UInt 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<UInt> & nodes_list,
const NewNodesEvent & event) override;
/// function to implement to react on akantu::RemovedNodesEvent
void onNodesRemoved(const Array<UInt> & nodes_list,
const Array<UInt> & new_numbering,
const RemovedNodesEvent & event) override;
/// function to implement to react on akantu::NewElementsEvent
void onElementsAdded(const Array<Element> & elements_list,
const NewElementsEvent & event) override;
/// function to implement to react on akantu::RemovedElementsEvent
void onElementsRemoved(const Array<Element> & elements_list,
const ElementTypeMapArray<UInt> & new_numbering,
const RemovedElementsEvent & event) override;
/// function to implement to react on akantu::ChangedElementsEvent
void onElementsChanged(const Array<Element> & old_elements_list,
const Array<Element> & new_elements_list,
const ElementTypeMapArray<UInt> & new_numbering,
const ChangedElementsEvent & event) override;
protected:
inline DOFData & getDOFData(const ID & dof_id);
inline const DOFData & getDOFData(const ID & dof_id) const;
template <class DOFData_>
inline DOFData_ & getDOFDataTyped(const ID & dof_id);
template <class DOFData_>
inline const DOFData_ & getDOFDataTyped(const ID & dof_id) const;
virtual std::unique_ptr<DOFData> 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);
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<Real> * dof{nullptr};
/// Blocked degree of freedoms array
Array<bool> * blocked_dofs{nullptr};
/// Degree of freedoms increment
Array<Real> * increment{nullptr};
/// Degree of freedoms at previous step
Array<Real> * previous{nullptr};
/// Solution associated to the dof
Array<Real> solution;
/* ---------------------------------------------------------------------- */
/* data for dynamic simulations */
/* ---------------------------------------------------------------------- */
/// Degree of freedom derivatives arrays
std::vector<Array<Real> *> dof_derivatives;
/* ---------------------------------------------------------------------- */
/// number of dofs to consider locally for this dof id
UInt local_nb_dofs{0};
/// Number of purely local dofs
UInt pure_local_nb_dofs{0};
/// number of ghost dofs
UInt ghosts_nb_dofs{0};
/// local numbering equation numbers
Array<Int> local_equation_number;
/// associated node for _dst_nodal dofs only
Array<UInt> associated_nodes;
virtual Array<Int> & getLocalEquationsNumbers() {
return local_equation_number;
}
};
/// type to store dofs information
using DOFStorage = std::map<ID, std::unique_ptr<DOFData>>;
/// type to store all the matrices
using SparseMatricesMap = std::map<ID, std::unique_ptr<SparseMatrix>>;
/// type to store all the lumped matrices
using LumpedMatricesMap = std::map<ID, std::unique_ptr<SolverVector>>;
/// type to store all the non linear solver
using NonLinearSolversMap = std::map<ID, std::unique_ptr<NonLinearSolver>>;
/// type to store all the time step solver
using TimeStepSolversMap = std::map<ID, std::unique_ptr<TimeStepSolver>>;
+ 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};
/// Total number of degrees of freedom (size with the ghosts)
UInt local_system_size{0};
/// Number of purely local dofs (size without the ghosts)
UInt pure_local_system_size{0};
/// Total number of degrees of freedom
UInt system_size{0};
/// rhs to the system of equation corresponding to the residual linked to the
/// different dofs
std::unique_ptr<SolverVector> residual;
/// solution of the system of equation corresponding to the different dofs
std::unique_ptr<SolverVector> solution;
/// a vector that helps internally to perform some tasks
std::unique_ptr<SolverVector> data_cache;
/// define the dofs type, local, shared, ghost
Array<NodeFlag> dofs_flag;
/// equation number in global numbering
Array<Int> global_equation_number;
using equation_numbers_map = std::unordered_map<Int, Int>;
/// 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;
/// accumulator to know what would be the next global id to use
UInt first_global_dof_id{0};
/// Release at last apply boundary on jacobian
UInt jacobian_release{0};
/// blocked degree of freedom in the system equation corresponding to the
/// different dofs
Array<Int> global_blocked_dofs;
UInt global_blocked_dofs_release{0};
/// blocked degree of freedom in the system equation corresponding to the
/// different dofs
Array<Int> previous_global_blocked_dofs;
UInt previous_global_blocked_dofs_release{0};
private:
/// This is for unit testing
friend class DOFManagerTester;
};
using DefaultDOFManagerFactory =
- Factory<DOFManager, ID, const ID &, const MemoryID &>;
+ Factory<DOFManager, ID, const ID &>;
using DOFManagerFactory =
- Factory<DOFManager, ID, Mesh &, const ID &, const MemoryID &>;
+ Factory<DOFManager, ID, Mesh &, const ID &>;
} // namespace akantu
#include "dof_manager_inline_impl.hh"
#endif /* AKANTU_DOF_MANAGER_HH_ */
diff --git a/src/model/common/dof_manager/dof_manager_default.cc b/src/model/common/dof_manager/dof_manager_default.cc
index f931833f5..202de3108 100644
--- a/src/model/common/dof_manager/dof_manager_default.cc
+++ b/src/model/common/dof_manager/dof_manager_default.cc
@@ -1,500 +1,488 @@
/**
* @file dof_manager_default.cc
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Tue Aug 18 2015
* @date last modification: Thu Feb 08 2018
*
* @brief Implementation of the default DOFManager
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "dof_manager_default.hh"
#include "communicator.hh"
#include "dof_synchronizer.hh"
#include "element_group.hh"
#include "non_linear_solver_default.hh"
#include "periodic_node_synchronizer.hh"
#include "solver_vector_default.hh"
#include "solver_vector_distributed.hh"
#include "sparse_matrix_aij.hh"
#include "time_step_solver_default.hh"
/* -------------------------------------------------------------------------- */
#include <algorithm>
#include <memory>
#include <numeric>
#include <unordered_map>
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
-DOFManagerDefault::DOFManagerDefault(const ID & id, const MemoryID & memory_id)
- : DOFManager(id, memory_id), synchronizer(nullptr) {
+DOFManagerDefault::DOFManagerDefault(const ID & id)
+ : DOFManager(id), synchronizer(nullptr) {
residual = std::make_unique<SolverVectorDefault>(
*this, std::string(id + ":residual"));
solution = std::make_unique<SolverVectorDefault>(
*this, std::string(id + ":solution"));
data_cache = std::make_unique<SolverVectorDefault>(
*this, std::string(id + ":data_cache"));
}
/* -------------------------------------------------------------------------- */
-DOFManagerDefault::DOFManagerDefault(Mesh & mesh, const ID & id,
- const MemoryID & memory_id)
- : DOFManager(mesh, id, memory_id), synchronizer(nullptr) {
+DOFManagerDefault::DOFManagerDefault(Mesh & mesh, const ID & id)
+ : DOFManager(mesh, id), synchronizer(nullptr) {
if (this->mesh->isDistributed()) {
this->synchronizer = std::make_unique<DOFSynchronizer>(
- *this, this->id + ":dof_synchronizer", this->memory_id);
+ *this, this->id + ":dof_synchronizer");
residual = std::make_unique<SolverVectorDistributed>(
*this, std::string(id + ":residual"));
solution = std::make_unique<SolverVectorDistributed>(
*this, std::string(id + ":solution"));
data_cache = std::make_unique<SolverVectorDistributed>(
*this, std::string(id + ":data_cache"));
} else {
residual = std::make_unique<SolverVectorDefault>(
*this, std::string(id + ":residual"));
solution = std::make_unique<SolverVectorDefault>(
*this, std::string(id + ":solution"));
data_cache = std::make_unique<SolverVectorDefault>(
*this, std::string(id + ":data_cache"));
}
}
/* -------------------------------------------------------------------------- */
DOFManagerDefault::~DOFManagerDefault() = default;
/* -------------------------------------------------------------------------- */
void DOFManagerDefault::makeConsistentForPeriodicity(const ID & dof_id,
SolverVector & array) {
auto & dof_data = this->getDOFDataTyped<DOFDataDefault>(dof_id);
if (dof_data.support_type != _dst_nodal) {
return;
}
if (not mesh->isPeriodic()) {
return;
}
this->mesh->getPeriodicNodeSynchronizer()
.reduceSynchronizeWithPBCSlaves<AddOperation>(
aka::as_type<SolverVectorDefault>(array).getVector());
}
/* -------------------------------------------------------------------------- */
template <typename T>
void DOFManagerDefault::assembleToGlobalArray(
const ID & dof_id, const Array<T> & array_to_assemble,
Array<T> & global_array, T scale_factor) {
AKANTU_DEBUG_IN();
auto & dof_data = this->getDOFDataTyped<DOFDataDefault>(dof_id);
AKANTU_DEBUG_ASSERT(dof_data.local_equation_number.size() ==
array_to_assemble.size() *
array_to_assemble.getNbComponent(),
"The array to assemble does not have a correct size."
<< " (" << array_to_assemble.getID() << ")");
if (dof_data.support_type == _dst_nodal and mesh->isPeriodic()) {
for (auto && data :
zip(dof_data.local_equation_number, dof_data.associated_nodes,
make_view(array_to_assemble))) {
auto && equ_num = std::get<0>(data);
// auto && node = std::get<1>(data);
auto && arr = std::get<2>(data);
// Guillaume to Nico:
// This filter of periodic slave should not be.
// Indeed you want to get the contribution even
// from periodic slaves and cumulate to the right
// equation number.
global_array(equ_num) += scale_factor * (arr);
// scale_factor * (arr) * (not this->mesh->isPeriodicSlave(node));
}
} else {
for (auto && data :
zip(dof_data.local_equation_number, make_view(array_to_assemble))) {
auto && equ_num = std::get<0>(data);
auto && arr = std::get<1>(data);
global_array(equ_num) += scale_factor * (arr);
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void DOFManagerDefault::assembleToGlobalArray(
const ID & dof_id, const Array<Real> & array_to_assemble,
SolverVector & global_array_v, Real scale_factor) {
assembleToGlobalArray(
dof_id, array_to_assemble,
aka::as_type<SolverVectorDefault>(global_array_v).getVector(),
scale_factor);
}
/* -------------------------------------------------------------------------- */
DOFManagerDefault::DOFDataDefault::DOFDataDefault(const ID & dof_id)
: DOFData(dof_id) {}
/* -------------------------------------------------------------------------- */
auto DOFManagerDefault::getNewDOFData(const ID & dof_id)
-> std::unique_ptr<DOFData> {
return std::make_unique<DOFDataDefault>(dof_id);
}
/* -------------------------------------------------------------------------- */
std::tuple<UInt, UInt, UInt>
DOFManagerDefault::registerDOFsInternal(const ID & dof_id,
Array<Real> & dofs_array) {
auto ret = DOFManager::registerDOFsInternal(dof_id, dofs_array);
// update the synchronizer if needed
if (this->synchronizer) {
this->synchronizer->registerDOFs(dof_id);
}
return ret;
}
/* -------------------------------------------------------------------------- */
SparseMatrix & DOFManagerDefault::getNewMatrix(const ID & id,
const MatrixType & matrix_type) {
return this->registerSparseMatrix<SparseMatrixAIJ>(*this, id, matrix_type);
}
/* -------------------------------------------------------------------------- */
SparseMatrix & DOFManagerDefault::getNewMatrix(const ID & id,
const ID & matrix_to_copy_id) {
return this->registerSparseMatrix<SparseMatrixAIJ>(id, matrix_to_copy_id);
}
/* -------------------------------------------------------------------------- */
SolverVector & DOFManagerDefault::getNewLumpedMatrix(const ID & id) {
return this->registerLumpedMatrix<SolverVectorDefault>(*this, id);
}
/* -------------------------------------------------------------------------- */
SparseMatrixAIJ & DOFManagerDefault::getMatrix(const ID & id) {
auto & matrix = DOFManager::getMatrix(id);
return aka::as_type<SparseMatrixAIJ>(matrix);
}
/* -------------------------------------------------------------------------- */
NonLinearSolver &
DOFManagerDefault::getNewNonLinearSolver(const ID & id,
const NonLinearSolverType & type) {
switch (type) {
#if defined(AKANTU_USE_MUMPS)
case NonLinearSolverType::_newton_raphson:
/* FALLTHRU */
/* [[fallthrough]]; un-comment when compiler will get it */
case NonLinearSolverType::_newton_raphson_contact:
case NonLinearSolverType::_newton_raphson_modified: {
return this->registerNonLinearSolver<NonLinearSolverNewtonRaphson>(
*this, id, type);
}
case NonLinearSolverType::_linear: {
return this->registerNonLinearSolver<NonLinearSolverLinear>(*this, id,
type);
}
#endif
case NonLinearSolverType::_lumped: {
return this->registerNonLinearSolver<NonLinearSolverLumped>(*this, id,
type);
}
default:
AKANTU_EXCEPTION("The asked type of non linear solver is not supported by "
"this dof manager");
}
}
/* -------------------------------------------------------------------------- */
TimeStepSolver & DOFManagerDefault::getNewTimeStepSolver(
const ID & id, const TimeStepSolverType & type,
NonLinearSolver & non_linear_solver, SolverCallback & solver_callback) {
return this->registerTimeStepSolver<TimeStepSolverDefault>(
*this, id, type, non_linear_solver, solver_callback);
}
/* -------------------------------------------------------------------------- */
template <typename T>
void DOFManagerDefault::getArrayPerDOFs(const ID & dof_id,
const Array<T> & global_array,
Array<T> & local_array) const {
AKANTU_DEBUG_IN();
const Array<Int> & equation_number = this->getLocalEquationsNumbers(dof_id);
UInt nb_degree_of_freedoms = equation_number.size();
local_array.resize(nb_degree_of_freedoms / local_array.getNbComponent());
auto loc_it = local_array.begin_reinterpret(nb_degree_of_freedoms);
auto equ_it = equation_number.begin();
for (UInt d = 0; d < nb_degree_of_freedoms; ++d, ++loc_it, ++equ_it) {
(*loc_it) = global_array(*equ_it);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void DOFManagerDefault::getArrayPerDOFs(const ID & dof_id,
const SolverVector & global_array,
Array<Real> & local_array) {
getArrayPerDOFs(dof_id,
aka::as_type<SolverVectorDefault>(global_array).getVector(),
local_array);
}
/* -------------------------------------------------------------------------- */
void DOFManagerDefault::assembleLumpedMatMulVectToResidual(
const ID & dof_id, const ID & A_id, const Array<Real> & x,
Real scale_factor) {
const Array<Real> & A = this->getLumpedMatrix(A_id);
auto & cache = aka::as_type<SolverVectorArray>(*this->data_cache);
cache.zero();
this->assembleToGlobalArray(dof_id, x, cache.getVector(), scale_factor);
for (auto && data : zip(make_view(A), make_view(cache.getVector()),
make_view(this->getResidualArray()))) {
const auto & A = std::get<0>(data);
const auto & x = std::get<1>(data);
auto & r = std::get<2>(data);
r += A * x;
}
}
/* -------------------------------------------------------------------------- */
void DOFManagerDefault::assembleElementalMatricesToMatrix(
const ID & matrix_id, const ID & dof_id, const Array<Real> & elementary_mat,
ElementType type, GhostType ghost_type,
const MatrixType & elemental_matrix_type,
const Array<UInt> & filter_elements) {
this->addToProfile(matrix_id, dof_id, type, ghost_type);
auto & A = getMatrix(matrix_id);
DOFManager::assembleElementalMatricesToMatrix_(
A, dof_id, elementary_mat, type, ghost_type, elemental_matrix_type,
filter_elements);
}
/* -------------------------------------------------------------------------- */
void DOFManagerDefault::assemblePreassembledMatrix(
const ID & dof_id_m, const ID & dof_id_n, const ID & matrix_id,
const TermsToAssemble & terms) {
auto & A = getMatrix(matrix_id);
DOFManager::assemblePreassembledMatrix_(A, dof_id_m, dof_id_n, terms);
}
/* -------------------------------------------------------------------------- */
void DOFManagerDefault::assembleMatMulVectToArray(const ID & dof_id,
const ID & A_id,
const Array<Real> & x,
Array<Real> & array,
Real scale_factor) {
if (mesh->isDistributed()) {
DOFManager::assembleMatMulVectToArray_<SolverVectorDistributed>(
dof_id, A_id, x, array, scale_factor);
} else {
DOFManager::assembleMatMulVectToArray_<SolverVectorDefault>(
dof_id, A_id, x, array, scale_factor);
}
}
/* -------------------------------------------------------------------------- */
void DOFManagerDefault::addToProfile(const ID & matrix_id, const ID & dof_id,
ElementType type, GhostType ghost_type) {
AKANTU_DEBUG_IN();
const auto & dof_data = this->getDOFData(dof_id);
if (dof_data.support_type != _dst_nodal) {
return;
}
auto mat_dof = std::make_pair(matrix_id, dof_id);
auto type_pair = std::make_pair(type, ghost_type);
auto prof_it = this->matrix_profiled_dofs.find(mat_dof);
if (prof_it != this->matrix_profiled_dofs.end() &&
std::find(prof_it->second.begin(), prof_it->second.end(), type_pair) !=
prof_it->second.end()) {
return;
}
auto nb_degree_of_freedom_per_node = dof_data.dof->getNbComponent();
const auto & equation_number = this->getLocalEquationsNumbers(dof_id);
auto & A = this->getMatrix(matrix_id);
A.resize(system_size);
auto size = A.size();
auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
const auto & connectivity = this->mesh->getConnectivity(type, ghost_type);
auto cbegin = connectivity.begin(nb_nodes_per_element);
auto cit = cbegin;
auto nb_elements = connectivity.size();
UInt * ge_it = nullptr;
if (dof_data.group_support != "__mesh__") {
const auto & group_elements =
this->mesh->getElementGroup(dof_data.group_support)
.getElements(type, ghost_type);
ge_it = group_elements.storage();
nb_elements = group_elements.size();
}
UInt size_mat = nb_nodes_per_element * nb_degree_of_freedom_per_node;
Vector<Int> element_eq_nb(size_mat);
for (UInt e = 0; e < nb_elements; ++e) {
if (ge_it != nullptr) {
cit = cbegin + *ge_it;
}
this->extractElementEquationNumber(
equation_number, *cit, nb_degree_of_freedom_per_node, element_eq_nb);
std::transform(
element_eq_nb.storage(), element_eq_nb.storage() + element_eq_nb.size(),
element_eq_nb.storage(),
[&](auto & local) { return this->localToGlobalEquationNumber(local); });
if (ge_it != nullptr) {
++ge_it;
} else {
++cit;
}
for (UInt i = 0; i < size_mat; ++i) {
UInt c_irn = element_eq_nb(i);
if (c_irn < size) {
for (UInt j = 0; j < size_mat; ++j) {
UInt c_jcn = element_eq_nb(j);
if (c_jcn < size) {
A.add(c_irn, c_jcn);
}
}
}
}
}
this->matrix_profiled_dofs[mat_dof].push_back(type_pair);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
Array<Real> & DOFManagerDefault::getSolutionArray() {
return dynamic_cast<SolverVectorDefault *>(this->solution.get())->getVector();
}
/* -------------------------------------------------------------------------- */
const Array<Real> & DOFManagerDefault::getResidualArray() const {
return dynamic_cast<SolverVectorDefault *>(this->residual.get())->getVector();
}
/* -------------------------------------------------------------------------- */
Array<Real> & DOFManagerDefault::getResidualArray() {
return dynamic_cast<SolverVectorDefault *>(this->residual.get())->getVector();
}
/* -------------------------------------------------------------------------- */
void DOFManagerDefault::onNodesAdded(const Array<UInt> & nodes_list,
const NewNodesEvent & event) {
DOFManager::onNodesAdded(nodes_list, event);
if (this->synchronizer) {
this->synchronizer->onNodesAdded(nodes_list);
}
}
/* -------------------------------------------------------------------------- */
void DOFManagerDefault::resizeGlobalArrays() {
DOFManager::resizeGlobalArrays();
this->global_blocked_dofs.resize(this->local_system_size, 1);
this->previous_global_blocked_dofs.resize(this->local_system_size, 1);
matrix_profiled_dofs.clear();
}
/* -------------------------------------------------------------------------- */
void DOFManagerDefault::updateGlobalBlockedDofs() {
DOFManager::updateGlobalBlockedDofs();
if (this->global_blocked_dofs_release ==
this->previous_global_blocked_dofs_release) {
return;
}
global_blocked_dofs_uint.resize(local_system_size);
global_blocked_dofs_uint.set(false);
for (const auto & dof : global_blocked_dofs) {
global_blocked_dofs_uint[dof] = true;
}
}
/* -------------------------------------------------------------------------- */
Array<bool> & DOFManagerDefault::getBlockedDOFs() {
return global_blocked_dofs_uint;
}
/* -------------------------------------------------------------------------- */
const Array<bool> & DOFManagerDefault::getBlockedDOFs() const {
return global_blocked_dofs_uint;
}
/* -------------------------------------------------------------------------- */
-// register in factory
-// static bool default_dof_manager_is_registered [[gnu::unused]] =
-// DefaultDOFManagerFactory::getInstance().registerAllocator(
-// "default",
-// [](const ID & id,
-// const MemoryID & mem_id) -> std::unique_ptr<DOFManager> {
-// return std::make_unique<DOFManagerDefault>(id, mem_id);
-// });
-
static bool dof_manager_is_registered [[gnu::unused]] =
DOFManagerFactory::getInstance().registerAllocator(
"default",
- [](Mesh & mesh, const ID & id,
- const MemoryID & mem_id) -> std::unique_ptr<DOFManager> {
- return std::make_unique<DOFManagerDefault>(mesh, id, mem_id);
+ [](Mesh & mesh, const ID & id) -> std::unique_ptr<DOFManager> {
+ return std::make_unique<DOFManagerDefault>(mesh, id);
});
static bool dof_manager_is_registered_mumps [[gnu::unused]] =
DOFManagerFactory::getInstance().registerAllocator(
"mumps",
- [](Mesh & mesh, const ID & id,
- const MemoryID & mem_id) -> std::unique_ptr<DOFManager> {
- return std::make_unique<DOFManagerDefault>(mesh, id, mem_id);
+ [](Mesh & mesh, const ID & id) -> std::unique_ptr<DOFManager> {
+ return std::make_unique<DOFManagerDefault>(mesh, id);
});
} // namespace akantu
diff --git a/src/model/common/dof_manager/dof_manager_default.hh b/src/model/common/dof_manager/dof_manager_default.hh
index 3e962e315..4cebb4b6c 100644
--- a/src/model/common/dof_manager/dof_manager_default.hh
+++ b/src/model/common/dof_manager/dof_manager_default.hh
@@ -1,255 +1,253 @@
/**
* @file dof_manager_default.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Tue Aug 18 2015
* @date last modification: Wed Jan 31 2018
*
* @brief Default implementation of the dof manager
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "dof_manager.hh"
/* -------------------------------------------------------------------------- */
#include <functional>
#include <unordered_map>
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_DOF_MANAGER_DEFAULT_HH_
#define AKANTU_DOF_MANAGER_DEFAULT_HH_
namespace akantu {
class SparseMatrixAIJ;
class NonLinearSolverDefault;
class TimeStepSolverDefault;
class DOFSynchronizer;
} // namespace akantu
namespace akantu {
class DOFManagerDefault : public DOFManager {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
- DOFManagerDefault(const ID & id = "dof_manager_default",
- const MemoryID & memory_id = 0);
- DOFManagerDefault(Mesh & mesh, const ID & id = "dof_manager_default",
- const MemoryID & memory_id = 0);
+ DOFManagerDefault(const ID & id = "dof_manager_default");
+ DOFManagerDefault(Mesh & mesh, const ID & id = "dof_manager_default");
~DOFManagerDefault() override;
protected:
struct DOFDataDefault : public DOFData {
explicit DOFDataDefault(const ID & dof_id);
};
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
// /// register an array of degree of freedom
// void registerDOFs(const ID & dof_id, Array<Real> & dofs_array,
// const DOFSupportType & support_type) override;
// /// the dof as an implied type of _dst_nodal and is defined only on a
// subset
// /// of nodes
// void registerDOFs(const ID & dof_id, Array<Real> & dofs_array,
// const ID & group_support) override;
/**
* Assemble elementary values to the global matrix. 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
**/
void assembleElementalMatricesToMatrix(
const ID & matrix_id, const ID & dof_id,
const Array<Real> & elementary_mat, ElementType type,
GhostType ghost_type, const MatrixType & elemental_matrix_type,
const Array<UInt> & filter_elements) override;
void assembleMatMulVectToArray(const ID & dof_id, const ID & A_id,
const Array<Real> & x, Array<Real> & array,
Real scale_factor = 1.) override;
/// multiply a vector by a lumped matrix and assemble the result to the
/// residual
void assembleLumpedMatMulVectToResidual(const ID & dof_id, const ID & A_id,
const Array<Real> & x,
Real scale_factor = 1) override;
/// assemble coupling terms between to dofs
void assemblePreassembledMatrix(const ID & dof_id_m, const ID & dof_id_n,
const ID & matrix_id,
const TermsToAssemble & terms) override;
protected:
void assembleToGlobalArray(const ID & dof_id,
const Array<Real> & array_to_assemble,
SolverVector & global_array,
Real scale_factor) override;
template <typename T>
void assembleToGlobalArray(const ID & dof_id,
const Array<T> & array_to_assemble,
Array<T> & global_array, T scale_factor);
void getArrayPerDOFs(const ID & dof_id, const SolverVector & global,
Array<Real> & local) override;
template <typename T>
void getArrayPerDOFs(const ID & dof_id, const Array<T> & global_array,
Array<T> & local_array) const;
void makeConsistentForPeriodicity(const ID & dof_id,
SolverVector & array) override;
public:
/// update the global dofs vector
void updateGlobalBlockedDofs() override;
// /// apply boundary conditions to jacobian matrix
// void applyBoundary(const ID & matrix_id = "J") override;
private:
/// Add a symmetric matrices to a symmetric sparse matrix
void addSymmetricElementalMatrixToSymmetric(
SparseMatrixAIJ & matrix, const Matrix<Real> & element_mat,
const Vector<Int> & equation_numbers, UInt max_size);
/// Add a unsymmetric matrices to a symmetric sparse matrix (i.e. cohesive
/// elements)
void addUnsymmetricElementalMatrixToSymmetric(
SparseMatrixAIJ & matrix, const Matrix<Real> & element_mat,
const Vector<Int> & equation_numbers, UInt max_size);
/// Add a matrices to a unsymmetric sparse matrix
void addElementalMatrixToUnsymmetric(SparseMatrixAIJ & matrix,
const Matrix<Real> & element_mat,
const Vector<Int> & equation_numbers,
UInt max_size);
void addToProfile(const ID & matrix_id, const ID & dof_id,
ElementType type, GhostType ghost_type);
/* ------------------------------------------------------------------------ */
/* MeshEventHandler interface */
/* ------------------------------------------------------------------------ */
protected:
std::tuple<UInt, UInt, UInt>
registerDOFsInternal(const ID & dof_id, Array<Real> & dofs_array) override;
// std::pair<UInt, UInt>
// updateNodalDOFs(const ID & dof_id, const Array<UInt> & nodes_list)
// override;
void resizeGlobalArrays() override;
public:
/// function to implement to react on akantu::NewNodesEvent
void onNodesAdded(const Array<UInt> & nodes_list,
const NewNodesEvent & event) override;
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
/// Get an instance of a new SparseMatrix
SparseMatrix & getNewMatrix(const ID & matrix_id,
const MatrixType & matrix_type) override;
/// Get an instance of a new SparseMatrix as a copy of the SparseMatrix
/// matrix_to_copy_id
SparseMatrix & getNewMatrix(const ID & matrix_id,
const ID & matrix_to_copy_id) override;
/// Get the reference of an existing matrix
SparseMatrixAIJ & getMatrix(const ID & matrix_id);
/// Get an instance of a new lumped matrix
SolverVector & getNewLumpedMatrix(const ID & matrix_id) override;
/* ------------------------------------------------------------------------ */
/* Non Linear Solver */
/* ------------------------------------------------------------------------ */
/// Get instance of a non linear solver
NonLinearSolver & getNewNonLinearSolver(
const ID & nls_solver_id,
const NonLinearSolverType & _non_linear_solver_type) override;
/* ------------------------------------------------------------------------ */
/* Time-Step Solver */
/* ------------------------------------------------------------------------ */
/// Get instance of a time step solver
TimeStepSolver &
getNewTimeStepSolver(const ID & id, const TimeStepSolverType & type,
NonLinearSolver & non_linear_solver,
SolverCallback & solver_callback) override;
/* ------------------------------------------------------------------------ */
private:
/// Get the solution array
Array<Real> & getSolutionArray();
/// Get the residual array
const Array<Real> & getResidualArray() const;
/// Get the residual array
Array<Real> & getResidualArray();
public:
/// access the internal dof_synchronizer
AKANTU_GET_MACRO_NOT_CONST(Synchronizer, *synchronizer, DOFSynchronizer &);
/// access the internal dof_synchronizer
bool hasSynchronizer() const { return synchronizer != nullptr; }
Array<bool> & getBlockedDOFs();
const Array<bool> & getBlockedDOFs() const;
protected:
std::unique_ptr<DOFData> getNewDOFData(const ID & dof_id) override;
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
using DOFToMatrixProfile =
std::map<std::pair<ID, ID>,
std::vector<std::pair<ElementType, GhostType>>>;
/// contains the the dofs that where added to the profile of a given matrix.
DOFToMatrixProfile matrix_profiled_dofs;
/// synchronizer to maintain coherency in dof fields
std::unique_ptr<DOFSynchronizer> synchronizer;
friend class DOFSynchronizer;
/// Array containing the true or false if the node is in global_blocked_dofs
Array<bool> global_blocked_dofs_uint;
};
} // namespace akantu
#include "dof_manager_default_inline_impl.hh"
#endif /* AKANTU_DOF_MANAGER_DEFAULT_HH_ */
diff --git a/src/model/common/dof_manager/dof_manager_petsc.cc b/src/model/common/dof_manager/dof_manager_petsc.cc
index 4fe29e244..9980ed3d6 100644
--- a/src/model/common/dof_manager/dof_manager_petsc.cc
+++ b/src/model/common/dof_manager/dof_manager_petsc.cc
@@ -1,305 +1,303 @@
/**
* @file dof_manager_petsc.cc
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Wed Oct 07 2015
* @date last modification: Tue Feb 20 2018
*
* @brief DOFManaterPETSc is the PETSc implementation of the DOFManager
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "dof_manager_petsc.hh"
#include "aka_iterators.hh"
#include "communicator.hh"
#include "cppargparse.hh"
#include "non_linear_solver_petsc.hh"
#include "solver_vector_petsc.hh"
#include "sparse_matrix_petsc.hh"
#include "time_step_solver_default.hh"
#if defined(AKANTU_USE_MPI)
#include "mpi_communicator_data.hh"
#endif
/* -------------------------------------------------------------------------- */
#include <petscis.h>
#include <petscsys.h>
/* -------------------------------------------------------------------------- */
namespace akantu {
class PETScSingleton {
private:
PETScSingleton() {
PETSc_call(PetscInitialized, &is_initialized);
if (is_initialized == 0U) {
cppargparse::ArgumentParser & argparser = getStaticArgumentParser();
int & argc = argparser.getArgC();
char **& argv = argparser.getArgV();
PETSc_call(PetscInitialize, &argc, &argv, nullptr, nullptr);
PETSc_call(
PetscPopErrorHandler); // remove the default PETSc signal handler
PETSc_call(PetscPushErrorHandler, PetscIgnoreErrorHandler, nullptr);
}
}
public:
PETScSingleton(const PETScSingleton &) = delete;
PETScSingleton & operator=(const PETScSingleton &) = delete;
~PETScSingleton() {
if (is_initialized == 0U) {
PetscFinalize();
}
}
static PETScSingleton & getInstance() {
static PETScSingleton instance;
return instance;
}
private:
PetscBool is_initialized;
};
/* -------------------------------------------------------------------------- */
DOFManagerPETSc::DOFDataPETSc::DOFDataPETSc(const ID & dof_id)
: DOFData(dof_id) {}
/* -------------------------------------------------------------------------- */
-DOFManagerPETSc::DOFManagerPETSc(const ID & id, const MemoryID & memory_id)
- : DOFManager(id, memory_id) {
+DOFManagerPETSc::DOFManagerPETSc(const ID & id)
+ : DOFManager(id) {
init();
}
/* -------------------------------------------------------------------------- */
-DOFManagerPETSc::DOFManagerPETSc(Mesh & mesh, const ID & id,
- const MemoryID & memory_id)
- : DOFManager(mesh, id, memory_id) {
+DOFManagerPETSc::DOFManagerPETSc(Mesh & mesh, const ID & id)
+ : DOFManager(mesh, id) {
init();
}
/* -------------------------------------------------------------------------- */
void DOFManagerPETSc::init() {
// check if the akantu types and PETSc one are consistant
static_assert(sizeof(Int) == sizeof(PetscInt),
"The integer type of Akantu does not match the one from PETSc");
static_assert(sizeof(Real) == sizeof(PetscReal),
"The integer type of Akantu does not match the one from PETSc");
#if defined(AKANTU_USE_MPI)
const auto & mpi_data =
aka::as_type<MPICommunicatorData>(communicator.getCommunicatorData());
MPI_Comm mpi_comm = mpi_data.getMPICommunicator();
this->mpi_communicator = mpi_comm;
#else
this->mpi_communicator = PETSC_COMM_SELF;
#endif
PETScSingleton & instance [[gnu::unused]] = PETScSingleton::getInstance();
}
/* -------------------------------------------------------------------------- */
auto DOFManagerPETSc::getNewDOFData(const ID & dof_id)
-> std::unique_ptr<DOFData> {
return std::make_unique<DOFDataPETSc>(dof_id);
}
/* -------------------------------------------------------------------------- */
std::tuple<UInt, UInt, UInt>
DOFManagerPETSc::registerDOFsInternal(const ID & dof_id,
Array<Real> & dofs_array) {
dofs_ids.push_back(dof_id);
auto ret = DOFManager::registerDOFsInternal(dof_id, dofs_array);
UInt nb_dofs;
UInt nb_pure_local_dofs;
std::tie(nb_dofs, nb_pure_local_dofs, std::ignore) = ret;
auto && vector = std::make_unique<SolverVectorPETSc>(*this, id + ":solution");
auto *x = vector->getVec();
PETSc_call(VecGetLocalToGlobalMapping, x, &is_ltog_map);
// redoing the indexes based on the petsc numbering
for (auto & dof_id : dofs_ids) {
auto & dof_data = this->getDOFDataTyped<DOFDataPETSc>(dof_id);
Array<PetscInt> gidx(dof_data.local_equation_number.size());
for (auto && data : zip(dof_data.local_equation_number, gidx)) {
std::get<1>(data) = localToGlobalEquationNumber(std::get<0>(data));
}
auto & lidx = dof_data.local_equation_number_petsc;
if (is_ltog_map != nullptr) {
lidx.resize(gidx.size());
PetscInt n;
PETSc_call(ISGlobalToLocalMappingApply, is_ltog_map, IS_GTOLM_MASK,
gidx.size(), gidx.storage(), &n, lidx.storage());
}
}
residual = std::make_unique<SolverVectorPETSc>(*vector, id + ":residual");
data_cache = std::make_unique<SolverVectorPETSc>(*vector, id + ":data_cache");
solution = std::move(vector);
for (auto & mat : matrices) {
auto & A = this->getMatrix(mat.first);
A.resize();
}
return ret;
}
/* -------------------------------------------------------------------------- */
void DOFManagerPETSc::assembleToGlobalArray(
const ID & dof_id, const Array<Real> & array_to_assemble,
SolverVector & global_array, Real scale_factor) {
const auto & dof_data = getDOFDataTyped<DOFDataPETSc>(dof_id);
auto & g = aka::as_type<SolverVectorPETSc>(global_array);
AKANTU_DEBUG_ASSERT(array_to_assemble.size() *
array_to_assemble.getNbComponent() ==
dof_data.local_nb_dofs,
"The array to assemble does not have the proper size");
g.addValuesLocal(dof_data.local_equation_number_petsc, array_to_assemble,
scale_factor);
}
/* -------------------------------------------------------------------------- */
void DOFManagerPETSc::getArrayPerDOFs(const ID & dof_id,
const SolverVector & global_array,
Array<Real> & local) {
const auto & dof_data = getDOFDataTyped<DOFDataPETSc>(dof_id);
const auto & petsc_vector = aka::as_type<SolverVectorPETSc>(global_array);
AKANTU_DEBUG_ASSERT(
local.size() * local.getNbComponent() == dof_data.local_nb_dofs,
"The array to get the values does not have the proper size");
petsc_vector.getValuesLocal(dof_data.local_equation_number_petsc, local);
}
/* -------------------------------------------------------------------------- */
void DOFManagerPETSc::assembleElementalMatricesToMatrix(
const ID & matrix_id, const ID & dof_id, const Array<Real> & elementary_mat,
ElementType type, GhostType ghost_type,
const MatrixType & elemental_matrix_type,
const Array<UInt> & filter_elements) {
auto & A = getMatrix(matrix_id);
DOFManager::assembleElementalMatricesToMatrix_(
A, dof_id, elementary_mat, type, ghost_type, elemental_matrix_type,
filter_elements);
A.applyModifications();
}
/* -------------------------------------------------------------------------- */
void DOFManagerPETSc::assemblePreassembledMatrix(
const ID & dof_id_m, const ID & dof_id_n, const ID & matrix_id,
const TermsToAssemble & terms) {
auto & A = getMatrix(matrix_id);
DOFManager::assemblePreassembledMatrix_(A, dof_id_m, dof_id_n, terms);
A.applyModifications();
}
/* -------------------------------------------------------------------------- */
void DOFManagerPETSc::assembleMatMulVectToArray(const ID & dof_id,
const ID & A_id,
const Array<Real> & x,
Array<Real> & array,
Real scale_factor) {
DOFManager::assembleMatMulVectToArray_<SolverVectorPETSc>(
dof_id, A_id, x, array, scale_factor);
}
/* -------------------------------------------------------------------------- */
void DOFManagerPETSc::makeConsistentForPeriodicity(const ID & /*dof_id*/,
SolverVector & /*array*/) {}
/* -------------------------------------------------------------------------- */
NonLinearSolver &
DOFManagerPETSc::getNewNonLinearSolver(const ID & id,
const NonLinearSolverType & type) {
return this->registerNonLinearSolver<NonLinearSolverPETSc>(*this, id, type);
}
/* -------------------------------------------------------------------------- */
TimeStepSolver & DOFManagerPETSc::getNewTimeStepSolver(
const ID & id, const TimeStepSolverType & type,
NonLinearSolver & non_linear_solver, SolverCallback & callback) {
return this->registerTimeStepSolver<TimeStepSolverDefault>(
*this, id, type, non_linear_solver, callback);
}
/* -------------------------------------------------------------------------- */
SparseMatrix & DOFManagerPETSc::getNewMatrix(const ID & id,
const MatrixType & matrix_type) {
return this->registerSparseMatrix<SparseMatrixPETSc>(*this, id, matrix_type);
}
/* -------------------------------------------------------------------------- */
SparseMatrix & DOFManagerPETSc::getNewMatrix(const ID & id,
const ID & matrix_to_copy_id) {
return this->registerSparseMatrix<SparseMatrixPETSc>(id, matrix_to_copy_id);
}
/* -------------------------------------------------------------------------- */
SparseMatrixPETSc & DOFManagerPETSc::getMatrix(const ID & id) {
auto & matrix = DOFManager::getMatrix(id);
return aka::as_type<SparseMatrixPETSc>(matrix);
}
/* -------------------------------------------------------------------------- */
SolverVector & DOFManagerPETSc::getNewLumpedMatrix(const ID & id) {
return this->registerLumpedMatrix<SolverVectorPETSc>(*this, id);
}
/* -------------------------------------------------------------------------- */
SolverVectorPETSc & DOFManagerPETSc::getSolution() {
return aka::as_type<SolverVectorPETSc>(*this->solution);
}
const SolverVectorPETSc & DOFManagerPETSc::getSolution() const {
return aka::as_type<SolverVectorPETSc>(*this->solution);
}
SolverVectorPETSc & DOFManagerPETSc::getResidual() {
return aka::as_type<SolverVectorPETSc>(*this->residual);
}
const SolverVectorPETSc & DOFManagerPETSc::getResidual() const {
return aka::as_type<SolverVectorPETSc>(*this->residual);
}
/* -------------------------------------------------------------------------- */
static bool dof_manager_is_registered [[gnu::unused]] =
DOFManagerFactory::getInstance().registerAllocator(
"petsc",
- [](Mesh & mesh, const ID & id,
- const MemoryID & mem_id) -> std::unique_ptr<DOFManager> {
- return std::make_unique<DOFManagerPETSc>(mesh, id, mem_id);
+ [](Mesh & mesh, const ID & id) -> std::unique_ptr<DOFManager> {
+ return std::make_unique<DOFManagerPETSc>(mesh, id);
});
} // namespace akantu
diff --git a/src/model/common/dof_manager/dof_manager_petsc.hh b/src/model/common/dof_manager/dof_manager_petsc.hh
index 5f5010162..d0f441c87 100644
--- a/src/model/common/dof_manager/dof_manager_petsc.hh
+++ b/src/model/common/dof_manager/dof_manager_petsc.hh
@@ -1,218 +1,216 @@
/**
* @file dof_manager_petsc.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Tue Aug 18 2015
* @date last modification: Wed Jan 31 2018
*
* @brief PETSc implementation of the dof manager
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "dof_manager.hh"
/* -------------------------------------------------------------------------- */
#include <petscis.h>
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_DOF_MANAGER_PETSC_HH_
#define AKANTU_DOF_MANAGER_PETSC_HH_
#define PETSc_call(func, ...) \
do { \
auto ierr = func(__VA_ARGS__); \
if (PetscUnlikely(ierr != 0)) { \
const char * desc; \
PetscErrorMessage(ierr, &desc, nullptr); \
AKANTU_EXCEPTION("Error in PETSc call to \'" << #func \
<< "\': " << desc); \
} \
} while (false)
namespace akantu {
namespace detail {
template <typename T> void PETScSetName(T t, const ID & id) {
PETSc_call(PetscObjectSetName, reinterpret_cast<PetscObject>(t),
id.c_str());
}
} // namespace detail
} // namespace akantu
namespace akantu {
class SparseMatrixPETSc;
class SolverVectorPETSc;
} // namespace akantu
namespace akantu {
class DOFManagerPETSc : public DOFManager {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
- DOFManagerPETSc(const ID & id = "dof_manager_petsc",
- const MemoryID & memory_id = 0);
- DOFManagerPETSc(Mesh & mesh, const ID & id = "dof_manager_petsc",
- const MemoryID & memory_id = 0);
+ DOFManagerPETSc(const ID & id = "dof_manager_petsc");
+ DOFManagerPETSc(Mesh & mesh, const ID & id = "dof_manager_petsc");
~DOFManagerPETSc() override = default;
protected:
void init();
struct DOFDataPETSc : public DOFData {
explicit DOFDataPETSc(const ID & dof_id);
/// petsc compressed version of local_equation_number
Array<PetscInt> local_equation_number_petsc;
Array<Int> & getLocalEquationsNumbers() override {
return local_equation_number_petsc;
}
};
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
void assembleToLumpedMatrix(const ID & /*dof_id*/,
Array<Real> & /*array_to_assemble*/,
const ID & /*lumped_mtx*/,
Real /*scale_factor*/ = 1.) override {
AKANTU_TO_IMPLEMENT();
}
void assembleElementalMatricesToMatrix(
const ID & /*matrix_id*/, const ID & /*dof_id*/,
const Array<Real> & /*elementary_mat*/, ElementType /*type*/,
GhostType /*ghost_type*/,
const MatrixType & /*elemental_matrix_type*/,
const Array<UInt> & /*filter_elements*/) override;
void assembleMatMulVectToArray(const ID & /*dof_id*/, const ID & /*A_id*/,
const Array<Real> & /*x*/,
Array<Real> & /*array*/,
Real /*scale_factor*/ = 1.) override;
void assembleLumpedMatMulVectToResidual(const ID & /*dof_id*/,
const ID & /*A_id*/,
const Array<Real> & /*x*/,
Real /*scale_factor*/ = 1) override {
AKANTU_TO_IMPLEMENT();
}
void assemblePreassembledMatrix(const ID & /* dof_id_m*/,
const ID & /*dof_id_n*/,
const ID & /*matrix_id*/,
const TermsToAssemble & /*terms*/) override;
protected:
void assembleToGlobalArray(const ID & dof_id,
const Array<Real> & array_to_assemble,
SolverVector & global_array,
Real scale_factor) override;
void getArrayPerDOFs(const ID & dof_id, const SolverVector & global,
Array<Real> & local) override;
void makeConsistentForPeriodicity(const ID & dof_id,
SolverVector & array) override;
std::unique_ptr<DOFData> getNewDOFData(const ID & dof_id) override;
std::tuple<UInt, UInt, UInt>
registerDOFsInternal(const ID & dof_id, Array<Real> & dofs_array) override;
void updateDOFsData(DOFDataPETSc & dof_data, UInt nb_new_local_dofs,
UInt nb_new_pure_local, UInt nb_node,
const std::function<UInt(UInt)> & getNode);
protected:
void getLumpedMatrixPerDOFs(const ID & /*dof_id*/, const ID & /*lumped_mtx*/,
Array<Real> & /*lumped*/) override {}
NonLinearSolver & getNewNonLinearSolver(
const ID & nls_solver_id,
const NonLinearSolverType & non_linear_solver_type) override;
TimeStepSolver &
getNewTimeStepSolver(const ID & id, const TimeStepSolverType & type,
NonLinearSolver & non_linear_solver,
SolverCallback & solver_callback) override;
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
/// Get an instance of a new SparseMatrix
SparseMatrix & getNewMatrix(const ID & matrix_id,
const MatrixType & matrix_type) override;
/// Get an instance of a new SparseMatrix as a copy of the SparseMatrix
/// matrix_to_copy_id
SparseMatrix & getNewMatrix(const ID & matrix_id,
const ID & matrix_to_copy_id) override;
/// Get the reference of an existing matrix
SparseMatrixPETSc & getMatrix(const ID & matrix_id);
/// Get an instance of a new lumped matrix
SolverVector & getNewLumpedMatrix(const ID & matrix_id) override;
/// Get the blocked dofs array
// AKANTU_GET_MACRO(BlockedDOFs, blocked_dofs, const Array<bool> &);
AKANTU_GET_MACRO(MPIComm, mpi_communicator, MPI_Comm);
AKANTU_GET_MACRO_NOT_CONST(ISLocalToGlobalMapping, is_ltog_map,
ISLocalToGlobalMapping &);
SolverVectorPETSc & getSolution();
const SolverVectorPETSc & getSolution() const;
SolverVectorPETSc & getResidual();
const SolverVectorPETSc & getResidual() const;
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
private:
using PETScMatrixMap = std::map<ID, SparseMatrixPETSc *>;
using PETScLumpedMatrixMap = std::map<ID, SolverVectorPETSc *>;
/// list of matrices registered to the dof manager
PETScMatrixMap petsc_matrices;
/// list of lumped matrices registered
PETScLumpedMatrixMap petsc_lumped_matrices;
/// PETSc local to global mapping of dofs
ISLocalToGlobalMapping is_ltog_map{nullptr};
/// Communicator associated to PETSc
MPI_Comm mpi_communicator;
/// list of the dof ids to be able to always iterate in the same order
std::vector<ID> dofs_ids;
};
/* -------------------------------------------------------------------------- */
} // namespace akantu
#endif /* AKANTU_DOF_MANAGER_PETSC_HH_ */
diff --git a/src/model/common/model_solver.cc b/src/model/common/model_solver.cc
index 12531d0f8..3b680522e 100644
--- a/src/model/common/model_solver.cc
+++ b/src/model/common/model_solver.cc
@@ -1,406 +1,403 @@
/**
* @file model_solver.cc
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Tue Aug 18 2015
* @date last modification: Wed Feb 21 2018
*
* @brief Implementation of ModelSolver
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "model_solver.hh"
#include "dof_manager.hh"
#include "dof_manager_default.hh"
#include "mesh.hh"
#include "non_linear_solver.hh"
#include "time_step_solver.hh"
#if defined(AKANTU_USE_PETSC)
#include "dof_manager_petsc.hh"
#endif
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
template <typename T> static T getOptionToType(const std::string & opt_str) {
std::stringstream sstr(opt_str);
T opt;
sstr >> opt;
return opt;
}
/* -------------------------------------------------------------------------- */
-ModelSolver::ModelSolver(Mesh & mesh, const ModelType & type, const ID & id,
- UInt memory_id)
- : Parsable(ParserType::_model, id), SolverCallback(), model_type(type),
- parent_id(id), parent_memory_id(memory_id), mesh(mesh) {
+ModelSolver::ModelSolver(Mesh & mesh, const ModelType & type, const ID & id)
+ : Parsable(ParserType::_model, id), model_type(type),
+ parent_id(id), mesh(mesh) {
}
/* -------------------------------------------------------------------------- */
ModelSolver::ModelSolver(Mesh & mesh, const ModelType & type, const ID & id,
- UInt memory_id,
std::shared_ptr<DOFManager> dof_manager)
- : ModelSolver(mesh, type, id, memory_id) {
+ : ModelSolver(mesh, type, id) {
if (not dof_manager) {
this->initDOFManager();
} else {
this->dof_manager = dof_manager;
this->setDOFManager(*this->dof_manager);
}
}
/* -------------------------------------------------------------------------- */
ModelSolver::~ModelSolver() = default;
/* -------------------------------------------------------------------------- */
std::tuple<ParserSection, bool> ModelSolver::getParserSection() {
auto sub_sections = getStaticParser().getSubSections(ParserType::_model);
auto it = std::find_if(
sub_sections.begin(), sub_sections.end(), [&](auto && section) {
auto type = getOptionToType<ModelType>(section.getName());
// default id should be the model type if not defined
std::string name = section.getParameter("name", this->parent_id);
return type == model_type and name == this->parent_id;
});
if (it == sub_sections.end()) {
return std::make_tuple(ParserSection(), true);
}
return std::make_tuple(*it, false);
}
/* -------------------------------------------------------------------------- */
std::shared_ptr<DOFManager> ModelSolver::initDOFManager() {
// default without external solver activated at compilation same as mumps that
// is the historical solver but with only the lumped solver
ID solver_type = "default";
#if defined(AKANTU_USE_MUMPS)
solver_type = "default";
#elif defined(AKANTU_USE_PETSC)
solver_type = "petsc";
#endif
ParserSection section;
bool is_empty;
std::tie(section, is_empty) = this->getParserSection();
if (not is_empty) {
solver_type = section.getOption(solver_type);
return this->initDOFManager(section, solver_type);
} else {
return this->initDOFManager(solver_type);
}
}
/* -------------------------------------------------------------------------- */
std::shared_ptr<DOFManager>
ModelSolver::initDOFManager(const ID & solver_type) {
if (dof_manager) {
AKANTU_EXCEPTION("The DOF manager for this model is already initialized !");
}
try {
this->dof_manager = DOFManagerFactory::getInstance().allocate(
- solver_type, mesh, this->parent_id + ":dof_manager_" + solver_type,
- this->parent_memory_id);
+ solver_type, mesh, this->parent_id + ":dof_manager_" + solver_type);
} catch (...) {
AKANTU_EXCEPTION(
"To use the solver "
<< solver_type
<< " you will have to code it. This is an unknown solver type.");
}
this->setDOFManager(*this->dof_manager);
return this->dof_manager;
}
/* -------------------------------------------------------------------------- */
std::shared_ptr<DOFManager>
ModelSolver::initDOFManager(const ParserSection & section,
const ID & solver_type) {
this->initDOFManager(solver_type);
auto sub_sections = section.getSubSections(ParserType::_time_step_solver);
// parsing the time step solvers
for (auto && section : sub_sections) {
ID type = section.getName();
ID solver_id = section.getParameter("name", type);
auto tss_type = getOptionToType<TimeStepSolverType>(type);
auto tss_options = this->getDefaultSolverOptions(tss_type);
auto sub_solvers_sect =
section.getSubSections(ParserType::_non_linear_solver);
auto nb_non_linear_solver_section =
section.getNbSubSections(ParserType::_non_linear_solver);
auto nls_type = tss_options.non_linear_solver_type;
if (nb_non_linear_solver_section == 1) {
auto && nls_section = *(sub_solvers_sect.first);
nls_type = getOptionToType<NonLinearSolverType>(nls_section.getName());
} else if (nb_non_linear_solver_section > 0) {
AKANTU_EXCEPTION("More than one non linear solver are provided for the "
"time step solver "
<< solver_id);
}
this->getNewSolver(solver_id, tss_type, nls_type);
if (nb_non_linear_solver_section == 1) {
const auto & nls_section = *(sub_solvers_sect.first);
this->dof_manager->getNonLinearSolver(solver_id).parseSection(
nls_section);
}
auto sub_integrator_sections =
section.getSubSections(ParserType::_integration_scheme);
for (auto && is_section : sub_integrator_sections) {
const auto & dof_type_str = is_section.getName();
ID dof_id;
try {
ID tmp = is_section.getParameter("name");
dof_id = tmp;
} catch (...) {
AKANTU_EXCEPTION("No degree of freedom name specified for the "
"integration scheme of type "
<< dof_type_str);
}
auto it_type = getOptionToType<IntegrationSchemeType>(dof_type_str);
IntegrationScheme::SolutionType s_type = is_section.getParameter(
"solution_type", tss_options.solution_type[dof_id]);
this->setIntegrationScheme(solver_id, dof_id, it_type, s_type);
}
for (auto & is_type : tss_options.integration_scheme_type) {
if (!this->hasIntegrationScheme(solver_id, is_type.first)) {
this->setIntegrationScheme(solver_id, is_type.first, is_type.second,
tss_options.solution_type[is_type.first]);
}
}
}
if (section.hasParameter("default_solver")) {
ID default_solver = section.getParameter("default_solver");
if (this->hasSolver(default_solver)) {
this->setDefaultSolver(default_solver);
} else {
AKANTU_EXCEPTION(
"The solver \""
<< default_solver
<< "\" was not created, it cannot be set as default solver");
}
}
return this->dof_manager;
}
/* -------------------------------------------------------------------------- */
TimeStepSolver & ModelSolver::getSolver(const ID & solver_id) {
ID tmp_solver_id = solver_id;
if (tmp_solver_id.empty()) {
tmp_solver_id = this->default_solver_id;
}
TimeStepSolver & tss = this->dof_manager->getTimeStepSolver(tmp_solver_id);
return tss;
}
/* -------------------------------------------------------------------------- */
const TimeStepSolver & ModelSolver::getSolver(const ID & solver_id) const {
ID tmp_solver_id = solver_id;
if (solver_id.empty()) {
tmp_solver_id = this->default_solver_id;
}
const TimeStepSolver & tss =
this->dof_manager->getTimeStepSolver(tmp_solver_id);
return tss;
}
/* -------------------------------------------------------------------------- */
TimeStepSolver & ModelSolver::getTimeStepSolver(const ID & solver_id) {
return this->getSolver(solver_id);
}
/* -------------------------------------------------------------------------- */
const TimeStepSolver &
ModelSolver::getTimeStepSolver(const ID & solver_id) const {
return this->getSolver(solver_id);
}
/* -------------------------------------------------------------------------- */
NonLinearSolver & ModelSolver::getNonLinearSolver(const ID & solver_id) {
return this->getSolver(solver_id).getNonLinearSolver();
}
/* -------------------------------------------------------------------------- */
const NonLinearSolver &
ModelSolver::getNonLinearSolver(const ID & solver_id) const {
return this->getSolver(solver_id).getNonLinearSolver();
}
/* -------------------------------------------------------------------------- */
bool ModelSolver::hasSolver(const ID & solver_id) const {
ID tmp_solver_id = solver_id;
if (solver_id.empty()) {
tmp_solver_id = this->default_solver_id;
}
if (not this->dof_manager) {
AKANTU_EXCEPTION("No DOF manager was initialized");
}
return this->dof_manager->hasTimeStepSolver(tmp_solver_id);
}
/* -------------------------------------------------------------------------- */
void ModelSolver::setDefaultSolver(const ID & solver_id) {
AKANTU_DEBUG_ASSERT(
this->hasSolver(solver_id),
"Cannot set the default solver to a solver that does not exists");
this->default_solver_id = solver_id;
}
/* -------------------------------------------------------------------------- */
void ModelSolver::solveStep(SolverCallback & callback, const ID & solver_id) {
AKANTU_DEBUG_IN();
TimeStepSolver & tss = this->getSolver(solver_id);
// make one non linear solve
tss.solveStep(callback);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void ModelSolver::solveStep(const ID & solver_id) {
solveStep(*this, solver_id);
}
/* -------------------------------------------------------------------------- */
void ModelSolver::getNewSolver(const ID & solver_id,
TimeStepSolverType time_step_solver_type,
NonLinearSolverType non_linear_solver_type) {
if (this->default_solver_id.empty()) {
this->default_solver_id = solver_id;
}
if (non_linear_solver_type == NonLinearSolverType::_auto) {
switch (time_step_solver_type) {
case TimeStepSolverType::_dynamic:
case TimeStepSolverType::_static:
non_linear_solver_type = NonLinearSolverType::_newton_raphson;
break;
case TimeStepSolverType::_dynamic_lumped:
non_linear_solver_type = NonLinearSolverType::_lumped;
break;
case TimeStepSolverType::_not_defined:
AKANTU_EXCEPTION(time_step_solver_type
<< " is not a valid time step solver type");
break;
}
}
this->initSolver(time_step_solver_type, non_linear_solver_type);
NonLinearSolver & nls = this->dof_manager->getNewNonLinearSolver(
solver_id, non_linear_solver_type);
this->dof_manager->getNewTimeStepSolver(solver_id, time_step_solver_type, nls,
*this);
}
/* -------------------------------------------------------------------------- */
Real ModelSolver::getTimeStep(const ID & solver_id) const {
const TimeStepSolver & tss = this->getSolver(solver_id);
return tss.getTimeStep();
}
/* -------------------------------------------------------------------------- */
void ModelSolver::setTimeStep(Real time_step, const ID & solver_id) {
TimeStepSolver & tss = this->getSolver(solver_id);
return tss.setTimeStep(time_step);
}
/* -------------------------------------------------------------------------- */
void ModelSolver::setIntegrationScheme(
const ID & solver_id, const ID & dof_id,
const IntegrationSchemeType & integration_scheme_type,
IntegrationScheme::SolutionType solution_type) {
TimeStepSolver & tss = this->dof_manager->getTimeStepSolver(solver_id);
tss.setIntegrationScheme(dof_id, integration_scheme_type, solution_type);
}
/* -------------------------------------------------------------------------- */
void ModelSolver::setIntegrationScheme(
const ID & solver_id, const ID & dof_id,
std::unique_ptr<IntegrationScheme> & integration_scheme,
IntegrationScheme::SolutionType solution_type) {
TimeStepSolver & tss = this->dof_manager->getTimeStepSolver(solver_id);
tss.setIntegrationScheme(dof_id, integration_scheme, solution_type);
}
/* -------------------------------------------------------------------------- */
bool ModelSolver::hasDefaultSolver() const {
return (not this->default_solver_id.empty());
}
/* -------------------------------------------------------------------------- */
bool ModelSolver::hasIntegrationScheme(const ID & solver_id,
const ID & dof_id) const {
TimeStepSolver & tss = this->dof_manager->getTimeStepSolver(solver_id);
return tss.hasIntegrationScheme(dof_id);
}
/* -------------------------------------------------------------------------- */
void ModelSolver::predictor() {}
/* -------------------------------------------------------------------------- */
void ModelSolver::corrector() {}
/* -------------------------------------------------------------------------- */
TimeStepSolverType ModelSolver::getDefaultSolverType() const {
return TimeStepSolverType::_dynamic_lumped;
}
/* -------------------------------------------------------------------------- */
ModelSolverOptions
ModelSolver::getDefaultSolverOptions(__attribute__((unused))
const TimeStepSolverType & type) const {
ModelSolverOptions options;
options.non_linear_solver_type = NonLinearSolverType::_auto;
return options;
}
} // namespace akantu
diff --git a/src/model/common/model_solver.hh b/src/model/common/model_solver.hh
index 4f8e79856..e25d74603 100644
--- a/src/model/common/model_solver.hh
+++ b/src/model/common/model_solver.hh
@@ -1,204 +1,203 @@
/**
* @file model_solver.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Wed Jan 31 2018
*
* @brief Class regrouping the common solve interface to the different models
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_common.hh"
#include "integration_scheme.hh"
#include "parsable.hh"
#include "solver_callback.hh"
#include "synchronizer_registry.hh"
/* -------------------------------------------------------------------------- */
#include <set>
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_MODEL_SOLVER_HH_
#define AKANTU_MODEL_SOLVER_HH_
namespace akantu {
class Mesh;
class DOFManager;
class TimeStepSolver;
class NonLinearSolver;
struct ModelSolverOptions;
} // namespace akantu
namespace akantu {
class ModelSolver : public Parsable,
public SolverCallback,
public SynchronizerRegistry {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
+
+ ModelSolver(Mesh & mesh, const ModelType & type, const ID & id);
ModelSolver(Mesh & mesh, const ModelType & type, const ID & id,
- UInt memory_id);
- ModelSolver(Mesh & mesh, const ModelType & type, const ID & id,
- UInt memory_id,
std::shared_ptr<DOFManager> dof_manager);
+
~ModelSolver() override;
/// initialize the dof manager based on solver type passed in the input file
std::shared_ptr<DOFManager> initDOFManager();
/// initialize the dof manager based on the used chosen solver type
std::shared_ptr<DOFManager> initDOFManager(const ID & solver_type);
protected:
/// initialize the dof manager based on the used chosen solver type
std::shared_ptr<DOFManager> initDOFManager(const ParserSection & section,
const ID & solver_type);
public:
/// Callback for the model to instantiate the matricees when needed
virtual void initSolver(TimeStepSolverType /*time_step_solver_type*/,
NonLinearSolverType /*non_linear_solver_type*/) {}
/// get the section in the input file (if it exsits) corresponding to this
/// model
std::tuple<ParserSection, bool> getParserSection();
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// solve a step using a given pre instantiated time step solver and
/// non linear solver
virtual void solveStep(const ID & solver_id = "");
/// solve a step using a given pre instantiated time step solver and
/// non linear solver with a user defined callback instead of the
/// model itself /!\ This can mess up everything
virtual void solveStep(SolverCallback & callback, const ID & solver_id = "");
/// Initialize a time solver that can be used afterwards with its id
void getNewSolver(
const ID & solver_id, TimeStepSolverType time_step_solver_type,
NonLinearSolverType non_linear_solver_type = NonLinearSolverType::_auto);
/// set an integration scheme for a given dof and a given solver
void
setIntegrationScheme(const ID & solver_id, const ID & dof_id,
const IntegrationSchemeType & integration_scheme_type,
IntegrationScheme::SolutionType solution_type =
IntegrationScheme::_not_defined);
/// set an externally instantiated integration scheme
void
setIntegrationScheme(const ID & solver_id, const ID & dof_id,
std::unique_ptr<IntegrationScheme> & integration_scheme,
IntegrationScheme::SolutionType solution_type =
IntegrationScheme::_not_defined);
/* ------------------------------------------------------------------------ */
/* SolverCallback interface */
/* ------------------------------------------------------------------------ */
public:
/// Predictor interface for the callback
void predictor() override;
/// Corrector interface for the callback
void corrector() override;
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
/// Default time step solver to instantiate for this model
virtual TimeStepSolverType getDefaultSolverType() const;
/// Default configurations for a given time step solver
virtual ModelSolverOptions
getDefaultSolverOptions(const TimeStepSolverType & type) const;
/// get access to the internal dof manager
DOFManager & getDOFManager() { return *this->dof_manager; }
/// get the time step of a given solver
Real getTimeStep(const ID & solver_id = "") const;
/// set the time step of a given solver
virtual void setTimeStep(Real time_step, const ID & solver_id = "");
/// set the parameter 'param' of the solver 'solver_id'
// template <typename T>
// void set(const ID & param, const T & value, const ID & solver_id = "");
/// get the parameter 'param' of the solver 'solver_id'
// const Parameter & get(const ID & param, const ID & solver_id = "") const;
/// answer to the question "does the solver exists ?"
bool hasSolver(const ID & solver_id) const;
/// changes the current default solver
void setDefaultSolver(const ID & solver_id);
/// is a default solver defined
bool hasDefaultSolver() const;
/// is an integration scheme set for a given solver and a given dof
bool hasIntegrationScheme(const ID & solver_id, const ID & dof_id) const;
TimeStepSolver & getTimeStepSolver(const ID & solver_id = "");
NonLinearSolver & getNonLinearSolver(const ID & solver_id = "");
const TimeStepSolver & getTimeStepSolver(const ID & solver_id = "") const;
const NonLinearSolver & getNonLinearSolver(const ID & solver_id = "") const;
private:
TimeStepSolver & getSolver(const ID & solver_id);
const TimeStepSolver & getSolver(const ID & solver_id) const;
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
ModelType model_type;
/// Underlying dof_manager (the brain...)
std::shared_ptr<DOFManager> dof_manager;
private:
ID parent_id;
- UInt parent_memory_id;
/// Underlying mesh
Mesh & mesh;
/// Default time step solver to use
ID default_solver_id{""};
};
struct ModelSolverOptions {
NonLinearSolverType non_linear_solver_type;
std::map<ID, IntegrationSchemeType> integration_scheme_type;
std::map<ID, IntegrationScheme::SolutionType> solution_type;
};
} // namespace akantu
#endif /* AKANTU_MODEL_SOLVER_HH_ */
diff --git a/src/model/common/non_linear_solver/non_linear_solver.cc b/src/model/common/non_linear_solver/non_linear_solver.cc
index 9891039de..ed9814301 100644
--- a/src/model/common/non_linear_solver/non_linear_solver.cc
+++ b/src/model/common/non_linear_solver/non_linear_solver.cc
@@ -1,78 +1,77 @@
/**
* @file non_linear_solver.cc
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Tue Jul 20 2010
* @date last modification: Wed Feb 21 2018
*
* @brief Implementation of the base class NonLinearSolver
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "non_linear_solver.hh"
#include "dof_manager.hh"
#include "solver_callback.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
NonLinearSolver::NonLinearSolver(
DOFManager & dof_manager,
- const NonLinearSolverType & non_linear_solver_type, const ID & id,
- UInt memory_id)
- : Memory(id, memory_id), Parsable(ParserType::_non_linear_solver, id),
+ const NonLinearSolverType & non_linear_solver_type, const ID & id)
+ : Parsable(ParserType::_non_linear_solver, id), id(id),
_dof_manager(dof_manager),
non_linear_solver_type(non_linear_solver_type) {
this->registerParam("type", this->non_linear_solver_type, _pat_parsable,
"Non linear solver type");
}
/* -------------------------------------------------------------------------- */
NonLinearSolver::~NonLinearSolver() = default;
/* -------------------------------------------------------------------------- */
void NonLinearSolver::checkIfTypeIsSupported() {
if (this->supported_type.find(this->non_linear_solver_type) ==
this->supported_type.end() and
this->non_linear_solver_type != NonLinearSolverType::_auto) {
AKANTU_EXCEPTION("The resolution method "
<< this->non_linear_solver_type
<< " is not implemented in the non linear solver "
<< this->id << "!");
}
}
/* -------------------------------------------------------------------------- */
void NonLinearSolver::assembleResidual(SolverCallback & solver_callback) {
if (solver_callback.canSplitResidual() and
non_linear_solver_type == NonLinearSolverType::_linear) {
this->_dof_manager.zeroResidual();
solver_callback.assembleResidual("external");
this->_dof_manager.assembleMatMulDOFsToResidual("K", -1.);
solver_callback.assembleResidual("inertial");
} else {
solver_callback.assembleResidual();
}
}
} // namespace akantu
diff --git a/src/model/common/non_linear_solver/non_linear_solver.hh b/src/model/common/non_linear_solver/non_linear_solver.hh
index c6011d772..626e59068 100644
--- a/src/model/common/non_linear_solver/non_linear_solver.hh
+++ b/src/model/common/non_linear_solver/non_linear_solver.hh
@@ -1,112 +1,113 @@
/**
* @file non_linear_solver.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Wed Feb 21 2018
*
* @brief Non linear solver interface
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_common.hh"
-#include "aka_memory.hh"
#include "parsable.hh"
/* -------------------------------------------------------------------------- */
#include <set>
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_NON_LINEAR_SOLVER_HH_
#define AKANTU_NON_LINEAR_SOLVER_HH_
namespace akantu {
class DOFManager;
class SolverCallback;
} // namespace akantu
namespace akantu {
-class NonLinearSolver : private Memory, public Parsable {
+class NonLinearSolver : public Parsable {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
NonLinearSolver(DOFManager & dof_manager,
const NonLinearSolverType & non_linear_solver_type,
- const ID & id = "non_linear_solver", UInt memory_id = 0);
+ const ID & id = "non_linear_solver");
~NonLinearSolver() override;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// solve the system described by the jacobian matrix, and rhs contained in
/// the dof manager
virtual void solve(SolverCallback & callback) = 0;
/// intercept the call to set for options
template <typename T> void set(const ID & param, T && t) {
if (has_internal_set_param) {
set_param(param, std::to_string(t));
} else {
ParameterRegistry::set(param, t);
}
}
protected:
void checkIfTypeIsSupported();
void assembleResidual(SolverCallback & callback);
/// internal set param for solvers that should intercept the parameters
virtual void set_param(const ID & /*param*/, const std::string & /*value*/) {}
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
+ ID id;
+
DOFManager & _dof_manager;
/// type of non linear solver
NonLinearSolverType non_linear_solver_type;
/// list of supported non linear solver types
std::set<NonLinearSolverType> supported_type;
/// specifies if the set param should be redirected
bool has_internal_set_param{false};
};
namespace debug {
class NLSNotConvergedException : public Exception {
public:
NLSNotConvergedException(Real threshold, UInt niter, Real error)
: Exception("The non linear solver did not converge."),
threshold(threshold), niter(niter), error(error) {}
Real threshold;
UInt niter;
Real error;
};
} // namespace debug
} // namespace akantu
#endif /* AKANTU_NON_LINEAR_SOLVER_HH_ */
diff --git a/src/model/common/non_linear_solver/non_linear_solver_linear.cc b/src/model/common/non_linear_solver/non_linear_solver_linear.cc
index 510723a3d..2b1c16fa7 100644
--- a/src/model/common/non_linear_solver/non_linear_solver_linear.cc
+++ b/src/model/common/non_linear_solver/non_linear_solver_linear.cc
@@ -1,75 +1,79 @@
/**
* @file non_linear_solver_linear.cc
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Tue Jul 20 2010
* @date last modification: Wed Feb 21 2018
*
* @brief Implementation of the default NonLinearSolver
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "non_linear_solver_linear.hh"
#include "dof_manager_default.hh"
#include "solver_callback.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
NonLinearSolverLinear::NonLinearSolverLinear(
DOFManagerDefault & dof_manager,
- const NonLinearSolverType & non_linear_solver_type, const ID & id,
- UInt memory_id)
- : NonLinearSolver(dof_manager, non_linear_solver_type, id, memory_id),
+ const NonLinearSolverType & non_linear_solver_type, const ID & id)
+ : NonLinearSolver(dof_manager, non_linear_solver_type, id),
dof_manager(dof_manager),
- solver(dof_manager, "J", id + ":sparse_solver", memory_id) {
+ solver(dof_manager, "J", id + ":sparse_solver") {
this->supported_type.insert(NonLinearSolverType::_linear);
this->checkIfTypeIsSupported();
}
/* -------------------------------------------------------------------------- */
NonLinearSolverLinear::~NonLinearSolverLinear() = default;
/* ------------------------------------------------------------------------ */
void NonLinearSolverLinear::solve(SolverCallback & solver_callback) {
solver_callback.beforeSolveStep();
this->dof_manager.updateGlobalBlockedDofs();
solver_callback.predictor();
solver_callback.assembleMatrix("J");
// Residual computed after J to allow the model to use K to compute the
// residual
this->assembleResidual(solver_callback);
this->solver.solve();
solver_callback.corrector();
+
+ if (solver_callback.canSplitResidual()) {
+ solver_callback.assembleResidual("internal");
+ }
+
solver_callback.afterSolveStep(true);
}
/* -------------------------------------------------------------------------- */
} // namespace akantu
diff --git a/src/model/common/non_linear_solver/non_linear_solver_linear.hh b/src/model/common/non_linear_solver/non_linear_solver_linear.hh
index 5fb338291..a7c2a58fe 100644
--- a/src/model/common/non_linear_solver/non_linear_solver_linear.hh
+++ b/src/model/common/non_linear_solver/non_linear_solver_linear.hh
@@ -1,79 +1,78 @@
/**
* @file non_linear_solver_linear.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Wed Jan 31 2018
*
* @brief Default implementation of NonLinearSolver, in case no external
* library
* is there to do the job
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "non_linear_solver.hh"
#include "sparse_solver_mumps.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_NON_LINEAR_SOLVER_LINEAR_HH_
#define AKANTU_NON_LINEAR_SOLVER_LINEAR_HH_
namespace akantu {
class DOFManagerDefault;
}
namespace akantu {
class NonLinearSolverLinear : public NonLinearSolver {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
NonLinearSolverLinear(DOFManagerDefault & dof_manager,
const NonLinearSolverType & non_linear_solver_type,
- const ID & id = "non_linear_solver_linear",
- UInt memory_id = 0);
+ const ID & id = "non_linear_solver_linear");
~NonLinearSolverLinear() override;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// Function that solve the non linear system described by the dof manager and
/// the solver callback functions
void solve(SolverCallback & solver_callback) override;
AKANTU_GET_MACRO_NOT_CONST(Solver, solver, SparseSolverMumps &);
AKANTU_GET_MACRO(Solver, solver, const SparseSolverMumps &);
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
DOFManagerDefault & dof_manager;
/// Sparse solver used for the linear solves
SparseSolverMumps solver;
};
} // namespace akantu
#endif /* AKANTU_NON_LINEAR_SOLVER_LINEAR_HH_ */
diff --git a/src/model/common/non_linear_solver/non_linear_solver_lumped.cc b/src/model/common/non_linear_solver/non_linear_solver_lumped.cc
index cc200f34a..c6ef42b74 100644
--- a/src/model/common/non_linear_solver/non_linear_solver_lumped.cc
+++ b/src/model/common/non_linear_solver/non_linear_solver_lumped.cc
@@ -1,102 +1,101 @@
/**
* @file non_linear_solver_lumped.cc
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Tue Feb 16 2016
* @date last modification: Wed Jan 31 2018
*
* @brief Implementation of the default NonLinearSolver
*
*
* Copyright (©) 2016-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "non_linear_solver_lumped.hh"
#include "communicator.hh"
#include "dof_manager_default.hh"
#include "solver_callback.hh"
#include "solver_vector_default.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
NonLinearSolverLumped::NonLinearSolverLumped(
DOFManagerDefault & dof_manager,
- const NonLinearSolverType & non_linear_solver_type, const ID & id,
- UInt memory_id)
- : NonLinearSolver(dof_manager, non_linear_solver_type, id, memory_id),
+ const NonLinearSolverType & non_linear_solver_type, const ID & id)
+ : NonLinearSolver(dof_manager, non_linear_solver_type, id),
dof_manager(dof_manager) {
this->supported_type.insert(NonLinearSolverType::_lumped);
this->checkIfTypeIsSupported();
this->registerParam("b_a2x", this->alpha, 1., _pat_parsmod,
"Conversion coefficient between x and A^{-1} b");
}
/* -------------------------------------------------------------------------- */
NonLinearSolverLumped::~NonLinearSolverLumped() = default;
/* ------------------------------------------------------------------------ */
void NonLinearSolverLumped::solve(SolverCallback & solver_callback) {
solver_callback.beforeSolveStep();
this->dof_manager.updateGlobalBlockedDofs();
solver_callback.predictor();
solver_callback.assembleResidual();
auto & x = aka::as_type<SolverVectorDefault>(this->dof_manager.getSolution());
const auto & b = this->dof_manager.getResidual();
x.resize();
const auto & blocked_dofs = this->dof_manager.getBlockedDOFs();
const auto & A = this->dof_manager.getLumpedMatrix("M");
// alpha is the conversion factor from from force/mass to acceleration needed
// in model coupled with atomistic \todo find a way to define alpha per dof
// type
NonLinearSolverLumped::solveLumped(A, x, b, alpha, blocked_dofs);
this->dof_manager.splitSolutionPerDOFs();
solver_callback.corrector();
solver_callback.afterSolveStep(true);
}
/* -------------------------------------------------------------------------- */
void NonLinearSolverLumped::solveLumped(const Array<Real> & A, Array<Real> & x,
const Array<Real> & b, Real alpha,
const Array<bool> & blocked_dofs) {
for (auto && data :
zip(make_view(A), make_view(x), make_view(b), make_view(blocked_dofs))) {
const auto & A = std::get<0>(data);
auto & x = std::get<1>(data);
const auto & b = std::get<2>(data);
const auto & blocked = std::get<3>(data);
if (not blocked) {
x = alpha * (b / A);
}
}
}
/* -------------------------------------------------------------------------- */
} // namespace akantu
diff --git a/src/model/common/non_linear_solver/non_linear_solver_lumped.hh b/src/model/common/non_linear_solver/non_linear_solver_lumped.hh
index 448d744d2..9c9127549 100644
--- a/src/model/common/non_linear_solver/non_linear_solver_lumped.hh
+++ b/src/model/common/non_linear_solver/non_linear_solver_lumped.hh
@@ -1,80 +1,79 @@
/**
* @file non_linear_solver_lumped.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Wed Jan 31 2018
*
* @brief Default implementation of NonLinearSolver, in case no external
* library
* is there to do the job
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "non_linear_solver.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_NON_LINEAR_SOLVER_LUMPED_HH_
#define AKANTU_NON_LINEAR_SOLVER_LUMPED_HH_
namespace akantu {
class DOFManagerDefault;
}
namespace akantu {
class NonLinearSolverLumped : public NonLinearSolver {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
NonLinearSolverLumped(DOFManagerDefault & dof_manager,
const NonLinearSolverType & non_linear_solver_type,
- const ID & id = "non_linear_solver_lumped",
- UInt memory_id = 0);
+ const ID & id = "non_linear_solver_lumped");
~NonLinearSolverLumped() override;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// Function that solve the non linear system described by the dof manager and
/// the solver callback functions
void solve(SolverCallback & solver_callback) override;
static void solveLumped(const Array<Real> & A, Array<Real> & x,
const Array<Real> & b, Real alpha,
const Array<bool> & blocked_dofs);
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
private:
DOFManagerDefault & dof_manager;
/// Coefficient to apply between x and A^{-1} b
Real alpha;
};
} // namespace akantu
#endif /* AKANTU_NON_LINEAR_SOLVER_LUMPED_HH_ */
diff --git a/src/model/common/non_linear_solver/non_linear_solver_newton_raphson.cc b/src/model/common/non_linear_solver/non_linear_solver_newton_raphson.cc
index 77ef110ab..e5f8e3aa9 100644
--- a/src/model/common/non_linear_solver/non_linear_solver_newton_raphson.cc
+++ b/src/model/common/non_linear_solver/non_linear_solver_newton_raphson.cc
@@ -1,210 +1,209 @@
/**
* @file non_linear_solver_newton_raphson.cc
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Tue Sep 15 2015
* @date last modification: Wed Feb 21 2018
*
* @brief Implementation of the default NonLinearSolver
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "non_linear_solver_newton_raphson.hh"
#include "communicator.hh"
#include "dof_manager_default.hh"
#include "solver_callback.hh"
#include "solver_vector.hh"
#include "sparse_solver_mumps.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
NonLinearSolverNewtonRaphson::NonLinearSolverNewtonRaphson(
DOFManagerDefault & dof_manager,
- const NonLinearSolverType & non_linear_solver_type, const ID & id,
- UInt memory_id)
- : NonLinearSolver(dof_manager, non_linear_solver_type, id, memory_id),
+ const NonLinearSolverType & non_linear_solver_type, const ID & id)
+ : NonLinearSolver(dof_manager, non_linear_solver_type, id),
dof_manager(dof_manager),
solver(std::make_unique<SparseSolverMumps>(
- dof_manager, "J", id + ":sparse_solver", memory_id)) {
+ dof_manager, "J", id + ":sparse_solver")) {
this->supported_type.insert(NonLinearSolverType::_newton_raphson_modified);
this->supported_type.insert(NonLinearSolverType::_newton_raphson_contact);
this->supported_type.insert(NonLinearSolverType::_newton_raphson);
this->supported_type.insert(NonLinearSolverType::_linear);
this->checkIfTypeIsSupported();
this->registerParam("threshold", convergence_criteria, 1e-10, _pat_parsmod,
"Threshold to consider results as converged");
this->registerParam("convergence_type", convergence_criteria_type,
SolveConvergenceCriteria::_solution, _pat_parsmod,
"Type of convergence criteria");
this->registerParam("max_iterations", max_iterations, 10, _pat_parsmod,
"Max number of iterations");
this->registerParam("error", error, _pat_readable, "Last reached error");
this->registerParam("nb_iterations", n_iter, _pat_readable,
"Last reached number of iterations");
this->registerParam("converged", converged, _pat_readable,
"Did last solve converged");
this->registerParam("force_linear_recompute", force_linear_recompute, true,
_pat_modifiable,
"Force reassembly of the jacobian matrix");
}
/* -------------------------------------------------------------------------- */
NonLinearSolverNewtonRaphson::~NonLinearSolverNewtonRaphson() = default;
/* ------------------------------------------------------------------------ */
void NonLinearSolverNewtonRaphson::solve(SolverCallback & solver_callback) {
solver_callback.beforeSolveStep();
this->dof_manager.updateGlobalBlockedDofs();
solver_callback.predictor();
if (non_linear_solver_type == NonLinearSolverType::_linear and
solver_callback.canSplitResidual()) {
solver_callback.assembleMatrix("K");
}
this->assembleResidual(solver_callback);
if (this->non_linear_solver_type ==
NonLinearSolverType::_newton_raphson_modified ||
(this->non_linear_solver_type == NonLinearSolverType::_linear &&
this->force_linear_recompute)) {
solver_callback.assembleMatrix("J");
this->force_linear_recompute = false;
}
this->n_iter = 0;
this->converged = false;
this->convergence_criteria_normalized = this->convergence_criteria;
if (this->convergence_criteria_type == SolveConvergenceCriteria::_residual) {
this->converged = this->testConvergence(this->dof_manager.getResidual());
if (this->converged) {
return;
}
this->convergence_criteria_normalized =
this->error * this->convergence_criteria;
}
do {
if (this->non_linear_solver_type == NonLinearSolverType::_newton_raphson or
this->non_linear_solver_type ==
NonLinearSolverType::_newton_raphson_contact) {
solver_callback.assembleMatrix("J");
}
this->solver->solve();
solver_callback.corrector();
// EventManager::sendEvent(NonLinearSolver::AfterSparseSolve(method));
if (this->convergence_criteria_type ==
SolveConvergenceCriteria::_residual) {
this->assembleResidual(solver_callback);
this->converged = this->testConvergence(this->dof_manager.getResidual());
} else {
this->converged = this->testConvergence(this->dof_manager.getSolution());
}
if (this->convergence_criteria_type ==
SolveConvergenceCriteria::_solution and
not this->converged) {
this->assembleResidual(solver_callback);
}
this->n_iter++;
AKANTU_DEBUG_INFO(
"[" << this->convergence_criteria_type << "] Convergence iteration "
<< std::setw(std::log10(this->max_iterations)) << this->n_iter
<< ": error " << this->error << (this->converged ? " < " : " > ")
<< this->convergence_criteria);
} while (not this->converged and this->n_iter <= this->max_iterations);
// this makes sure that you have correct strains and stresses after the
// solveStep function (e.g., for dumping)
if (this->convergence_criteria_type == SolveConvergenceCriteria::_solution) {
this->assembleResidual(solver_callback);
}
this->converged =
this->converged and not(this->n_iter > this->max_iterations);
solver_callback.afterSolveStep(this->converged);
if (not this->converged) {
AKANTU_CUSTOM_EXCEPTION(debug::NLSNotConvergedException(
this->convergence_criteria, this->n_iter, this->error));
AKANTU_DEBUG_WARNING("[" << this->convergence_criteria_type
<< "] Convergence not reached after "
<< std::setw(std::log10(this->max_iterations))
<< this->n_iter << " iteration"
<< (this->n_iter == 1 ? "" : "s") << "!");
}
}
/* -------------------------------------------------------------------------- */
bool NonLinearSolverNewtonRaphson::testConvergence(
const SolverVector & solver_vector) {
AKANTU_DEBUG_IN();
const auto & blocked_dofs = this->dof_manager.getBlockedDOFs();
const Array<Real> & array(solver_vector);
UInt nb_degree_of_freedoms = array.size();
auto arr_it = array.begin();
auto bld_it = blocked_dofs.begin();
Real norm = 0.;
for (UInt n = 0; n < nb_degree_of_freedoms; ++n, ++arr_it, ++bld_it) {
bool is_local_node = this->dof_manager.isLocalOrMasterDOF(n);
if ((!*bld_it) && is_local_node) {
norm += *arr_it * *arr_it;
}
}
dof_manager.getCommunicator().allReduce(norm, SynchronizerOperation::_sum);
norm = std::sqrt(norm);
AKANTU_DEBUG_ASSERT(!Math::isnan(norm),
"Something went wrong in the solve phase");
this->error = norm;
return (error < this->convergence_criteria_normalized);
}
/* -------------------------------------------------------------------------- */
} // namespace akantu
diff --git a/src/model/common/non_linear_solver/non_linear_solver_newton_raphson.hh b/src/model/common/non_linear_solver/non_linear_solver_newton_raphson.hh
index cd6675881..37fa51ac5 100644
--- a/src/model/common/non_linear_solver/non_linear_solver_newton_raphson.hh
+++ b/src/model/common/non_linear_solver/non_linear_solver_newton_raphson.hh
@@ -1,109 +1,109 @@
/**
* @file non_linear_solver_newton_raphson.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Wed Jan 31 2018
*
* @brief Default implementation of NonLinearSolver, in case no external
* library
* is there to do the job
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "non_linear_solver.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_NON_LINEAR_SOLVER_NEWTON_RAPHSON_HH_
#define AKANTU_NON_LINEAR_SOLVER_NEWTON_RAPHSON_HH_
namespace akantu {
class DOFManagerDefault;
class SparseSolverMumps;
class SolverVector;
} // namespace akantu
namespace akantu {
class NonLinearSolverNewtonRaphson : public NonLinearSolver {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
NonLinearSolverNewtonRaphson(
DOFManagerDefault & dof_manager,
const NonLinearSolverType & non_linear_solver_type,
- const ID & id = "non_linear_solver_newton_raphson", UInt memory_id = 0);
+ const ID & id = "non_linear_solver_newton_raphson");
~NonLinearSolverNewtonRaphson() override;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// Function that solve the non linear system described by the dof manager and
/// the solver callback functions
void solve(SolverCallback & solver_callback) override;
AKANTU_GET_MACRO_NOT_CONST(Solver, *solver, SparseSolverMumps &);
AKANTU_GET_MACRO(Solver, *solver, const SparseSolverMumps &);
protected:
/// test the convergence compare norm of array to convergence_criteria
bool testConvergence(const SolverVector & solver_vector);
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
private:
DOFManagerDefault & dof_manager;
/// Sparse solver used for the linear solves
std::unique_ptr<SparseSolverMumps> solver;
/// Type of convergence criteria
SolveConvergenceCriteria convergence_criteria_type;
/// convergence threshold
Real convergence_criteria;
/// convergence threshold
Real convergence_criteria_normalized;
/// Max number of iterations
int max_iterations;
/// Number of iterations at last solve call
int n_iter{0};
/// Convergence error at last solve call
Real error{0.};
/// Did the last call to solve reached convergence
bool converged{false};
/// Force a re-computation of the jacobian matrix
bool force_linear_recompute{true};
};
} // namespace akantu
#endif /* AKANTU_NON_LINEAR_SOLVER_NEWTON_RAPHSON_HH_ */
diff --git a/src/model/common/non_linear_solver/non_linear_solver_petsc.cc b/src/model/common/non_linear_solver/non_linear_solver_petsc.cc
index 629fa4a23..e936282f4 100644
--- a/src/model/common/non_linear_solver/non_linear_solver_petsc.cc
+++ b/src/model/common/non_linear_solver/non_linear_solver_petsc.cc
@@ -1,224 +1,223 @@
/**
* @file non_linear_solver_petsc.cc
*
* @author Nicolas Richart
*
* @date creation Mon Dec 31 2018
*
* @brief A Documented file.
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "non_linear_solver_petsc.hh"
#include "dof_manager_petsc.hh"
#include "mpi_communicator_data.hh"
#include "solver_callback.hh"
#include "solver_vector_petsc.hh"
#include "sparse_matrix_petsc.hh"
/* -------------------------------------------------------------------------- */
#include <petscoptions.h>
/* -------------------------------------------------------------------------- */
namespace akantu {
NonLinearSolverPETSc::NonLinearSolverPETSc(
DOFManagerPETSc & dof_manager,
- const NonLinearSolverType & non_linear_solver_type, const ID & id,
- UInt memory_id)
- : NonLinearSolver(dof_manager, non_linear_solver_type, id, memory_id),
+ const NonLinearSolverType & non_linear_solver_type, const ID & id)
+ : NonLinearSolver(dof_manager, non_linear_solver_type, id),
dof_manager(dof_manager) {
std::unordered_map<NonLinearSolverType, SNESType>
petsc_non_linear_solver_types{
{NonLinearSolverType::_newton_raphson, SNESNEWTONLS},
{NonLinearSolverType::_linear, SNESKSPONLY},
{NonLinearSolverType::_gmres, SNESNGMRES},
{NonLinearSolverType::_bfgs, SNESQN},
{NonLinearSolverType::_cg, SNESNCG}};
this->has_internal_set_param = true;
for (const auto & pair : petsc_non_linear_solver_types) {
supported_type.insert(pair.first);
}
this->checkIfTypeIsSupported();
auto && mpi_comm = dof_manager.getMPIComm();
PETSc_call(SNESCreate, mpi_comm, &snes);
auto it = petsc_non_linear_solver_types.find(non_linear_solver_type);
if (it != petsc_non_linear_solver_types.end()) {
PETSc_call(SNESSetType, snes, it->second);
}
SNESSetFromOptions(snes);
}
/* -------------------------------------------------------------------------- */
NonLinearSolverPETSc::~NonLinearSolverPETSc() {
PETSc_call(SNESDestroy, &snes);
}
/* -------------------------------------------------------------------------- */
class NonLinearSolverPETScCallback {
public:
NonLinearSolverPETScCallback(DOFManagerPETSc & dof_manager,
SolverVectorPETSc & x)
: dof_manager(dof_manager), x(x), x_prev(x, "previous_solution") {}
void corrector() {
auto & dx = dof_manager.getSolution();
PETSc_call(VecWAXPY, dx, -1., x_prev, x);
dof_manager.splitSolutionPerDOFs();
callback->corrector();
PETSc_call(VecCopy, x, x_prev);
}
void assembleResidual() {
corrector();
callback->assembleResidual();
}
void assembleJacobian() {
// corrector();
callback->assembleMatrix("J");
}
void setInitialSolution(SolverVectorPETSc & x) {
PETSc_call(VecCopy, x, x_prev);
}
void setCallback(SolverCallback & callback) { this->callback = &callback; }
private:
// SNES & snes;
SolverCallback * callback;
DOFManagerPETSc & dof_manager;
SolverVectorPETSc & x;
SolverVectorPETSc x_prev;
}; // namespace akantu
/* -------------------------------------------------------------------------- */
PetscErrorCode NonLinearSolverPETSc::FormFunction(SNES /*snes*/, Vec /*dx*/,
Vec /*f*/, void * ctx) {
auto * _this = reinterpret_cast<NonLinearSolverPETScCallback *>(ctx);
_this->assembleResidual();
return 0;
}
/* -------------------------------------------------------------------------- */
PetscErrorCode NonLinearSolverPETSc::FormJacobian(SNES /*snes*/, Vec /*dx*/,
Mat /*J*/, Mat /*P*/,
void * ctx) {
auto * _this = reinterpret_cast<NonLinearSolverPETScCallback *>(ctx);
_this->assembleJacobian();
return 0;
}
/* -------------------------------------------------------------------------- */
void NonLinearSolverPETSc::solve(SolverCallback & callback) {
callback.beforeSolveStep();
this->dof_manager.updateGlobalBlockedDofs();
callback.assembleMatrix("J");
auto & global_x = dof_manager.getSolution();
global_x.zero();
if (not x) {
x = std::make_unique<SolverVectorPETSc>(global_x, "temporary_solution");
}
*x = global_x;
if (not ctx) {
ctx = std::make_unique<NonLinearSolverPETScCallback>(dof_manager, *x);
}
ctx->setCallback(callback);
ctx->setInitialSolution(global_x);
auto & rhs = dof_manager.getResidual();
auto & J = dof_manager.getMatrix("J");
PETSc_call(SNESSetFunction, snes, rhs, NonLinearSolverPETSc::FormFunction,
ctx.get());
PETSc_call(SNESSetJacobian, snes, J, J, NonLinearSolverPETSc::FormJacobian,
ctx.get());
rhs.zero();
callback.predictor();
callback.assembleResidual();
PETSc_call(SNESSolve, snes, nullptr, *x);
PETSc_call(SNESGetConvergedReason, snes, &reason);
PETSc_call(SNESGetIterationNumber, snes, &n_iter);
PETSc_call(VecAXPY, global_x, -1.0, *x);
dof_manager.splitSolutionPerDOFs();
callback.corrector();
bool converged = reason >= 0;
callback.afterSolveStep(converged);
if (not converged) {
PetscReal atol;
PetscReal rtol;
PetscReal stol;
PetscInt maxit;
PetscInt maxf;
PETSc_call(SNESGetTolerances, snes, &atol, &rtol, &stol, &maxit, &maxf);
AKANTU_CUSTOM_EXCEPTION(debug::SNESNotConvergedException(
this->reason, this->n_iter, stol, atol, rtol, maxit));
}
}
/* -------------------------------------------------------------------------- */
void NonLinearSolverPETSc::set_param(const ID & param,
const std::string & value) {
std::map<ID, ID> akantu_to_petsc_option = {{"max_iterations", "snes_max_it"},
{"threshold", "snes_stol"}};
auto it = akantu_to_petsc_option.find(param);
auto p = it == akantu_to_petsc_option.end() ? param : it->second;
PetscOptionsSetValue(nullptr, p.c_str(), value.c_str());
SNESSetFromOptions(snes);
PetscOptionsClear(nullptr);
}
/* -------------------------------------------------------------------------- */
void NonLinearSolverPETSc::parseSection(const ParserSection & section) {
auto parameters = section.getParameters();
for (auto && param : range(parameters.first, parameters.second)) {
PetscOptionsSetValue(nullptr, param.getName().c_str(),
param.getValue().c_str());
}
SNESSetFromOptions(snes);
PetscOptionsClear(nullptr);
}
} // namespace akantu
diff --git a/src/model/common/non_linear_solver/non_linear_solver_petsc.hh b/src/model/common/non_linear_solver/non_linear_solver_petsc.hh
index 15118d841..5e90c3b7e 100644
--- a/src/model/common/non_linear_solver/non_linear_solver_petsc.hh
+++ b/src/model/common/non_linear_solver/non_linear_solver_petsc.hh
@@ -1,108 +1,107 @@
/**
* @file non_linear_solver_petsc.hh
*
* @author Nicolas Richart
*
* @date creation Tue Jan 01 2019
*
* @brief A Documented file.
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "non_linear_solver.hh"
/* -------------------------------------------------------------------------- */
#include <petscsnes.h>
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_NON_LINEAR_SOLVER_PETSC_HH_
#define AKANTU_NON_LINEAR_SOLVER_PETSC_HH_
namespace akantu {
class DOFManagerPETSc;
class NonLinearSolverPETScCallback;
class SolverVectorPETSc;
} // namespace akantu
namespace akantu {
class NonLinearSolverPETSc : public NonLinearSolver {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
NonLinearSolverPETSc(DOFManagerPETSc & dof_manager,
const NonLinearSolverType & non_linear_solver_type,
- const ID & id = "non_linear_solver_petsc",
- UInt memory_id = 0);
+ const ID & id = "non_linear_solver_petsc");
~NonLinearSolverPETSc() override;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// solve the system described by the jacobian matrix, and rhs contained in
/// the dof manager
void solve(SolverCallback & callback) override;
/// parse the arguments from the input file
void parseSection(const ParserSection & section) override;
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
static PetscErrorCode FormFunction(SNES snes, Vec dx, Vec f, void * ctx);
static PetscErrorCode FormJacobian(SNES snes, Vec dx, Mat J, Mat P,
void * ctx);
void set_param(const ID & param, const std::string & value) override;
DOFManagerPETSc & dof_manager;
/// PETSc non linear solver
SNES snes;
SNESConvergedReason reason;
SolverCallback * callback{nullptr};
std::unique_ptr<SolverVectorPETSc> x;
std::unique_ptr<NonLinearSolverPETScCallback> ctx;
Int n_iter{0};
};
namespace debug {
class SNESNotConvergedException : public NLSNotConvergedException {
public:
SNESNotConvergedException(SNESConvergedReason reason, UInt niter,
Real error, Real absolute_tolerance,
Real relative_tolerance, UInt max_iterations)
: NLSNotConvergedException(relative_tolerance, niter, error),
reason(reason), absolute_tolerance(absolute_tolerance),
max_iterations(max_iterations) {}
SNESConvergedReason reason;
Real absolute_tolerance;
UInt max_iterations;
};
} // namespace debug
} // namespace akantu
#endif /* AKANTU_NON_LINEAR_SOLVER_PETSC_HH_ */
diff --git a/src/model/common/non_local_toolbox/neighborhood_base.cc b/src/model/common/non_local_toolbox/neighborhood_base.cc
index f69b520fc..990dd6e96 100644
--- a/src/model/common/non_local_toolbox/neighborhood_base.cc
+++ b/src/model/common/non_local_toolbox/neighborhood_base.cc
@@ -1,304 +1,303 @@
/**
* @file neighborhood_base.cc
*
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Sat Sep 26 2015
* @date last modification: Wed Jan 31 2018
*
* @brief Implementation of generic neighborhood base
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "neighborhood_base.hh"
#include "grid_synchronizer.hh"
#include "mesh_accessor.hh"
#include "model.hh"
/* -------------------------------------------------------------------------- */
#include <fstream>
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
NeighborhoodBase::NeighborhoodBase(Model & model,
const ElementTypeMapReal & quad_coordinates,
- const ID & id, const MemoryID & memory_id)
- : Memory(id, memory_id), model(model), neighborhood_radius(0.),
- spatial_grid(nullptr), is_creating_grid(false),
- grid_synchronizer(nullptr), quad_coordinates(quad_coordinates),
+ const ID & id)
+ : id(id), model(model), quad_coordinates(quad_coordinates),
spatial_dimension(this->model.getMesh().getSpatialDimension()) {
AKANTU_DEBUG_IN();
this->registerDataAccessor(*this);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
NeighborhoodBase::~NeighborhoodBase() = default;
/* -------------------------------------------------------------------------- */
// void NeighborhoodBase::createSynchronizerRegistry(
// DataAccessor<Element> * data_accessor) {
// this->synch_registry = new SynchronizerRegistry(*data_accessor);
// }
/* -------------------------------------------------------------------------- */
void NeighborhoodBase::initNeighborhood() {
AKANTU_DEBUG_IN();
AKANTU_DEBUG_INFO("Creating the grid");
this->createGrid();
AKANTU_DEBUG_OUT();
}
/* ------------------------------------------------------------------------- */
void NeighborhoodBase::createGrid() {
AKANTU_DEBUG_IN();
const Real safety_factor = 1.2; // for the cell grid spacing
Mesh & mesh = this->model.getMesh();
const auto & lower_bounds = mesh.getLocalLowerBounds();
const auto & upper_bounds = mesh.getLocalUpperBounds();
Vector<Real> center = 0.5 * (upper_bounds + lower_bounds);
Vector<Real> spacing(spatial_dimension,
this->neighborhood_radius * safety_factor);
spatial_grid = std::make_unique<SpatialGrid<IntegrationPoint>>(
spatial_dimension, spacing, center);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NeighborhoodBase::updatePairList() {
AKANTU_DEBUG_IN();
//// loop over all quads -> all cells
for (auto && cell_id : *spatial_grid) {
AKANTU_DEBUG_INFO("Looping on next cell");
for (auto && q1 : spatial_grid->getCell(cell_id)) {
if (q1.ghost_type == _ghost) {
break;
}
auto coords_type_1_it = this->quad_coordinates(q1.type, q1.ghost_type)
.begin(spatial_dimension);
auto q1_coords = Vector<Real>(coords_type_1_it[q1.global_num]);
AKANTU_DEBUG_INFO("Current quadrature point in this cell: " << q1);
auto cell_id = spatial_grid->getCellID(q1_coords);
/// loop over all the neighboring cells of the current quad
for (auto && neighbor_cell : cell_id.neighbors()) {
// loop over the quadrature point in the current neighboring cell
for (auto && q2 : spatial_grid->getCell(neighbor_cell)) {
auto coords_type_2_it = this->quad_coordinates(q2.type, q2.ghost_type)
.begin(spatial_dimension);
auto q2_coords = Vector<Real>(coords_type_2_it[q2.global_num]);
Real distance = q1_coords.distance(q2_coords);
if (distance <= this->neighborhood_radius + Math::getTolerance() &&
(q2.ghost_type == _ghost ||
(q2.ghost_type == _not_ghost &&
q1.global_num <= q2.global_num))) { // storing only half lists
pair_list[q2.ghost_type].push_back(std::make_pair(q1, q2));
}
}
}
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NeighborhoodBase::savePairs(const std::string & filename) const {
std::stringstream sstr;
const Communicator & comm = model.getMesh().getCommunicator();
Int prank = comm.whoAmI();
sstr << filename << "." << prank;
std::ofstream pout;
pout.open(sstr.str().c_str());
for (auto && ghost_type : ghost_types) {
for (const auto & pair : pair_list[ghost_type]) {
const auto & q1 = pair.first;
const auto & q2 = pair.second;
pout << q1 << " " << q2 << " " << std::endl;
}
}
pout.close();
if (comm.getNbProc() != 1) {
return;
}
Mesh mesh_out(spatial_dimension);
MeshAccessor mesh_accessor(mesh_out);
auto & connectivity = mesh_accessor.getConnectivity(_segment_2);
auto & tag = mesh_accessor.getData<UInt>("tag_1", _segment_2);
auto & nodes = mesh_accessor.getNodes();
std::map<IntegrationPoint, UInt> quad_to_nodes;
UInt node = 0;
IntegrationPoint q1;
IntegrationPoint q2;
bool inserted;
for (auto && ghost_type : ghost_types) {
for (const auto & pair : pair_list[ghost_type]) {
std::tie(q1, q2) = pair;
auto add_node = [&](auto && q) {
std::tie(std::ignore, inserted) =
quad_to_nodes.insert(std::make_pair(q, node));
if (not inserted) {
return;
}
auto coords_it = this->quad_coordinates(q.type, q.ghost_type)
.begin(spatial_dimension);
auto && coords = Vector<Real>(coords_it[q.global_num]);
nodes.push_back(coords);
++node;
};
add_node(q1);
add_node(q2);
}
}
for (auto && ghost_type : ghost_types) {
for (const auto & pair : pair_list[ghost_type]) {
std::tie(q1, q2) = pair;
UInt node1 = quad_to_nodes[q1];
UInt node2 = quad_to_nodes[q2];
connectivity.push_back(Vector<UInt>{node1, node2});
tag.push_back(node1 + 1);
if (node1 != node2) {
connectivity.push_back(Vector<UInt>{node2, node1});
tag.push_back(node2 + 1);
}
}
}
mesh_out.write(filename + ".msh");
}
/* -------------------------------------------------------------------------- */
void NeighborhoodBase::saveNeighborCoords(const std::string & filename) const {
// this function is not optimized and only used for tests on small meshes
// @todo maybe optimize this function for better performance?
IntegrationPoint q2;
std::stringstream sstr;
const Communicator & comm = model.getMesh().getCommunicator();
Int prank = comm.whoAmI();
sstr << filename << "." << prank;
std::ofstream pout;
pout.open(sstr.str().c_str());
/// loop over all the quads and write the position of their neighbors
for (auto && cell_id : *spatial_grid) {
for (auto && q1 : spatial_grid->getCell(cell_id)) {
auto coords_type_1_it = this->quad_coordinates(q1.type, q1.ghost_type)
.begin(spatial_dimension);
auto && q1_coords = Vector<Real>(coords_type_1_it[q1.global_num]);
pout << "#neighbors for quad " << q1.global_num << std::endl;
pout << q1_coords << std::endl;
for (auto && ghost_type2 : ghost_types) {
for (auto && pair : pair_list[ghost_type2]) {
if (q1 == pair.first && pair.second != q1) {
q2 = pair.second;
} else if (q1 == pair.second && pair.first != q1) {
q2 = pair.first;
} else {
continue;
}
auto coords_type_2_it = this->quad_coordinates(q2.type, q2.ghost_type)
.begin(spatial_dimension);
auto && q2_coords = Vector<Real>(coords_type_2_it[q2.global_num]);
pout << q2_coords << std::endl;
}
}
}
}
}
/* -------------------------------------------------------------------------- */
void NeighborhoodBase::onElementsRemoved(
const Array<Element> & element_list,
const ElementTypeMapArray<UInt> & new_numbering,
const RemovedElementsEvent & event) {
AKANTU_DEBUG_IN();
FEEngine & fem = this->model.getFEEngine();
UInt nb_quad = 0;
auto cleanPoint = [&](auto && q) {
if (new_numbering.exists(q.type, q.ghost_type)) {
UInt q_new_el = new_numbering(q.type, q.ghost_type)(q.element);
AKANTU_DEBUG_ASSERT(q_new_el != UInt(-1),
"A local quadrature_point "
<< q
<< " as been removed instead of "
- "just being renumbered: " << id);
+ "just being renumbered: "
+ << id);
q.element = q_new_el;
nb_quad = fem.getNbIntegrationPoints(q.type, q.ghost_type);
q.global_num = nb_quad * q.element + q.num_point;
}
};
// Change the pairs in new global numbering
for (auto ghost_type : ghost_types) {
auto & pair_list = this->pair_list.at(ghost_type);
for (auto && pair : pair_list) {
if (pair.first.ghost_type == _ghost) {
cleanPoint(pair.first);
}
if (pair.second.ghost_type == _ghost) {
cleanPoint(pair.second);
}
}
}
this->grid_synchronizer->onElementsRemoved(element_list, new_numbering,
event);
AKANTU_DEBUG_OUT();
}
} // namespace akantu
diff --git a/src/model/common/non_local_toolbox/neighborhood_base.hh b/src/model/common/non_local_toolbox/neighborhood_base.hh
index acbf88b23..8b6966e22 100644
--- a/src/model/common/non_local_toolbox/neighborhood_base.hh
+++ b/src/model/common/non_local_toolbox/neighborhood_base.hh
@@ -1,152 +1,151 @@
/**
* @file neighborhood_base.hh
*
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Sat Sep 26 2015
* @date last modification: Wed Jan 31 2018
*
* @brief Generic neighborhood of quadrature points
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_NEIGHBORHOOD_BASE_HH_
#define AKANTU_NEIGHBORHOOD_BASE_HH_
/* -------------------------------------------------------------------------- */
#include "aka_common.hh"
-#include "aka_memory.hh"
#include "data_accessor.hh"
#include "integration_point.hh"
#include "synchronizer_registry.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
class Model;
template <class T> class SpatialGrid;
class GridSynchronizer;
class RemovedElementsEvent;
} // namespace akantu
namespace akantu {
-class NeighborhoodBase : protected Memory,
- public DataAccessor<Element>,
+class NeighborhoodBase : public DataAccessor<Element>,
public SynchronizerRegistry {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
NeighborhoodBase(Model & model,
const ElementTypeMapArray<Real> & quad_coordinates,
- const ID & id = "neighborhood",
- const MemoryID & memory_id = 0);
+ const ID & id = "neighborhood");
~NeighborhoodBase() override;
using PairList = std::vector<std::pair<IntegrationPoint, IntegrationPoint>>;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// intialize the neighborhood
virtual void initNeighborhood();
// /// create a synchronizer registry
// void createSynchronizerRegistry(DataAccessor * data_accessor);
/// initialize the material computed parameter
inline void insertIntegrationPoint(const IntegrationPoint & quad,
const Vector<Real> & coords);
/// create the pairs of quadrature points
void updatePairList();
/// save the pairs of quadrature points in a file
void savePairs(const std::string & filename) const;
/// save the coordinates of all neighbors of a quad
void saveNeighborCoords(const std::string & filename) const;
/// create grid synchronizer and exchange ghost cells
virtual void createGridSynchronizer() = 0;
virtual void synchronize(DataAccessor<Element> & data_accessor,
const SynchronizationTag & tag) = 0;
/// inherited function from MeshEventHandler
virtual void
onElementsRemoved(const Array<Element> & element_list,
const ElementTypeMapArray<UInt> & new_numbering,
const RemovedElementsEvent & event);
protected:
/// create the grid
void createGrid();
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
AKANTU_GET_MACRO(SpatialDimension, spatial_dimension, UInt);
AKANTU_GET_MACRO(Model, model, const Model &);
/// return the object handling synchronizers
const PairList & getPairLists(GhostType type) {
return pair_list[type == _not_ghost ? 0 : 1];
}
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
+ ID id;
+
/// the model to which the neighborhood belongs
Model & model;
/// Radius of impact: to determine if two quadrature points influence each
/// other
- Real neighborhood_radius;
+ Real neighborhood_radius{0.};
/**
* the pairs of quadrature points
* 0: not ghost to not ghost
* 1: not ghost to ghost
*/
std::array<PairList, 2> pair_list;
/// the regular grid to construct/update the pair lists
std::unique_ptr<SpatialGrid<IntegrationPoint>> spatial_grid;
- bool is_creating_grid;
+ bool is_creating_grid{false};
/// the grid synchronizer for parallel computations
std::unique_ptr<GridSynchronizer> grid_synchronizer;
/// the quadrature point positions
const ElementTypeMapArray<Real> & quad_coordinates;
/// the spatial dimension of the problem
const UInt spatial_dimension;
};
} // namespace akantu
#include "neighborhood_base_inline_impl.hh"
#endif /* AKANTU_NEIGHBORHOOD_BASE_HH_ */
diff --git a/src/model/common/non_local_toolbox/neighborhoods_criterion_evaluation/neighborhood_max_criterion.cc b/src/model/common/non_local_toolbox/neighborhoods_criterion_evaluation/neighborhood_max_criterion.cc
index 9628dc97e..80b7dc24e 100644
--- a/src/model/common/non_local_toolbox/neighborhoods_criterion_evaluation/neighborhood_max_criterion.cc
+++ b/src/model/common/non_local_toolbox/neighborhoods_criterion_evaluation/neighborhood_max_criterion.cc
@@ -1,290 +1,290 @@
/**
* @file neighborhood_max_criterion.cc
*
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
*
* @date creation: Thu Oct 15 2015
* @date last modification: Tue Feb 20 2018
*
* @brief Implementation of class NeighborhoodMaxCriterion
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "neighborhood_max_criterion.hh"
#include "grid_synchronizer.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
NeighborhoodMaxCriterion::NeighborhoodMaxCriterion(
Model & model, const ElementTypeMapReal & quad_coordinates,
- const ID & criterion_id, const ID & id, const MemoryID & memory_id)
- : NeighborhoodBase(model, quad_coordinates, id, memory_id),
+ const ID & criterion_id, const ID & id)
+ : NeighborhoodBase(model, quad_coordinates, id),
Parsable(ParserType::_non_local, id),
- is_highest("is_highest", id, memory_id),
- criterion(criterion_id, id, memory_id) {
+ is_highest("is_highest", id),
+ criterion(criterion_id, id) {
AKANTU_DEBUG_IN();
this->registerParam("radius", neighborhood_radius, 100.,
_pat_parsable | _pat_readable, "Non local radius");
Mesh & mesh = this->model.getMesh();
/// allocate the element type map arrays for _not_ghosts: One entry per quad
GhostType ghost_type = _not_ghost;
for (auto type : mesh.elementTypes(spatial_dimension, ghost_type)) {
UInt new_size = this->quad_coordinates(type, ghost_type).size();
this->is_highest.alloc(new_size, 1, type, ghost_type, true);
this->criterion.alloc(new_size, 1, type, ghost_type, 1.);
}
/// criterion needs allocation also for ghost
ghost_type = _ghost;
for (auto type : mesh.elementTypes(spatial_dimension, ghost_type)) {
UInt new_size = this->quad_coordinates(type, ghost_type).size();
this->criterion.alloc(new_size, 1, type, ghost_type, 1.);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
NeighborhoodMaxCriterion::~NeighborhoodMaxCriterion() {
AKANTU_DEBUG_IN();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NeighborhoodMaxCriterion::initNeighborhood() {
AKANTU_DEBUG_IN();
/// parse the input parameter
const Parser & parser = getStaticParser();
const ParserSection & section_neighborhood =
*(parser.getSubSections(ParserType::_neighborhood).first);
this->parseSection(section_neighborhood);
AKANTU_DEBUG_INFO("Creating the grid");
this->createGrid();
/// insert the non-ghost quads into the grid
this->insertAllQuads(_not_ghost);
/// store the number of current ghost elements for each type in the mesh
ElementTypeMap<UInt> nb_ghost_protected;
Mesh & mesh = this->model.getMesh();
for (auto type : mesh.elementTypes(spatial_dimension, _ghost)) {
nb_ghost_protected(mesh.getNbElement(type, _ghost), type, _ghost);
}
/// create the grid synchronizer
this->createGridSynchronizer();
/// insert the ghost quads into the grid
this->insertAllQuads(_ghost);
/// create the pair lists
this->updatePairList();
/// remove the unneccessary ghosts
this->cleanupExtraGhostElements(nb_ghost_protected);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NeighborhoodMaxCriterion::createGridSynchronizer() {
this->is_creating_grid = true;
std::set<SynchronizationTag> tags;
tags.insert(SynchronizationTag::_nh_criterion);
std::stringstream sstr;
- sstr << getID() << ":grid_synchronizer";
+ sstr << id << ":grid_synchronizer";
this->grid_synchronizer = std::make_unique<GridSynchronizer>(
- this->model.getMesh(), *spatial_grid, *this, tags, sstr.str(), 0, false);
+ this->model.getMesh(), *spatial_grid, *this, tags, sstr.str(), false);
this->is_creating_grid = false;
}
/* -------------------------------------------------------------------------- */
void NeighborhoodMaxCriterion::insertAllQuads(GhostType ghost_type) {
IntegrationPoint q;
q.ghost_type = ghost_type;
Mesh & mesh = this->model.getMesh();
for (auto type : mesh.elementTypes(spatial_dimension, ghost_type)) {
UInt nb_element = mesh.getNbElement(type, ghost_type);
UInt nb_quad =
this->model.getFEEngine().getNbIntegrationPoints(type, ghost_type);
const Array<Real> & quads = this->quad_coordinates(type, ghost_type);
q.type = type;
auto quad = quads.begin(spatial_dimension);
for (UInt e = 0; e < nb_element; ++e) {
q.element = e;
for (UInt nq = 0; nq < nb_quad; ++nq) {
q.num_point = nq;
q.global_num = q.element * nb_quad + nq;
spatial_grid->insert(q, *quad);
++quad;
}
}
}
}
/* -------------------------------------------------------------------------- */
void NeighborhoodMaxCriterion::findMaxQuads(
std::vector<IntegrationPoint> & max_quads) {
AKANTU_DEBUG_IN();
/// clear the element type maps
this->is_highest.zero();
this->criterion.zero();
/// update the values of the criterion
this->model.updateDataForNonLocalCriterion(criterion);
/// start the exchange the value of the criterion on the ghost elements
this->model.asynchronousSynchronize(SynchronizationTag::_nh_criterion);
/// compare to not-ghost neighbors
checkNeighbors(_not_ghost);
/// finish the exchange
this->model.waitEndSynchronize(SynchronizationTag::_nh_criterion);
/// compare to ghost neighbors
checkNeighbors(_ghost);
/// extract the quads with highest criterion in their neighborhood
IntegrationPoint quad;
quad.ghost_type = _not_ghost;
Mesh & mesh = this->model.getMesh();
for (auto type : mesh.elementTypes(spatial_dimension, _not_ghost)) {
quad.type = type;
UInt nb_quadrature_points =
this->model.getFEEngine().getNbIntegrationPoints(type, _not_ghost);
/// loop over is_highest for the current element type
for (auto data : enumerate(is_highest(type, _not_ghost))) {
const auto & is_highest = std::get<1>(data);
if (is_highest) {
auto q = std::get<0>(data);
/// gauss point has the highest stress in his neighbourhood
quad.element = q / nb_quadrature_points;
quad.global_num = q;
quad.num_point = q % nb_quadrature_points;
max_quads.push_back(quad);
}
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NeighborhoodMaxCriterion::checkNeighbors(GhostType ghost_type2) {
AKANTU_DEBUG_IN();
// Compute the weights
for (auto & pair : pair_list[ghost_type2]) {
const auto & lq1 = pair.first;
const auto & lq2 = pair.second;
Array<bool> & has_highest_eq_stress_1 =
is_highest(lq1.type, lq1.ghost_type);
const Array<Real> & criterion_1 = this->criterion(lq1.type, lq1.ghost_type);
const Array<Real> & criterion_2 = this->criterion(lq2.type, lq2.ghost_type);
if (criterion_1(lq1.global_num) < criterion_2(lq2.global_num)) {
has_highest_eq_stress_1(lq1.global_num) = false;
} else if (ghost_type2 != _ghost) {
Array<bool> & has_highest_eq_stress_2 =
is_highest(lq2.type, lq2.ghost_type);
has_highest_eq_stress_2(lq2.global_num) = false;
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NeighborhoodMaxCriterion::cleanupExtraGhostElements(
const ElementTypeMap<UInt> & nb_ghost_protected) {
Mesh & mesh = this->model.getMesh();
/// create remove elements event
RemovedElementsEvent remove_elem(mesh);
/// create set of ghosts to keep
std::set<Element> relevant_ghost_elements;
for (auto & pair : pair_list[_ghost]) {
const auto & q2 = pair.second;
relevant_ghost_elements.insert(q2);
}
Array<Element> ghosts_to_erase(0);
Element element;
element.ghost_type = _ghost;
auto end = relevant_ghost_elements.end();
for (const auto & type : mesh.elementTypes(spatial_dimension, _ghost)) {
element.type = type;
UInt nb_ghost_elem = mesh.getNbElement(type, _ghost);
UInt nb_ghost_elem_protected = 0;
try {
nb_ghost_elem_protected = nb_ghost_protected(type, _ghost);
} catch (...) {
}
if (!remove_elem.getNewNumbering().exists(type, _ghost)) {
remove_elem.getNewNumbering().alloc(nb_ghost_elem, 1, type, _ghost);
} else {
remove_elem.getNewNumbering(type, _ghost).resize(nb_ghost_elem);
}
Array<UInt> & new_numbering = remove_elem.getNewNumbering(type, _ghost);
for (UInt g = 0; g < nb_ghost_elem; ++g) {
element.element = g;
if (element.element >= nb_ghost_elem_protected &&
relevant_ghost_elements.find(element) == end) {
ghosts_to_erase.push_back(element);
new_numbering(element.element) = UInt(-1);
}
}
/// renumber remaining ghosts
UInt ng = 0;
for (UInt g = 0; g < nb_ghost_elem; ++g) {
if (new_numbering(g) != UInt(-1)) {
new_numbering(g) = ng;
++ng;
}
}
}
mesh.sendEvent(remove_elem);
this->onElementsRemoved(ghosts_to_erase, remove_elem.getNewNumbering(),
remove_elem);
}
} // namespace akantu
diff --git a/src/model/common/non_local_toolbox/neighborhoods_criterion_evaluation/neighborhood_max_criterion.hh b/src/model/common/non_local_toolbox/neighborhoods_criterion_evaluation/neighborhood_max_criterion.hh
index 377e89cfb..4e2f23860 100644
--- a/src/model/common/non_local_toolbox/neighborhoods_criterion_evaluation/neighborhood_max_criterion.hh
+++ b/src/model/common/non_local_toolbox/neighborhoods_criterion_evaluation/neighborhood_max_criterion.hh
@@ -1,116 +1,113 @@
/**
* @file neighborhood_max_criterion.hh
*
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
*
* @date creation: Sat Sep 26 2015
* @date last modification: Wed Jan 31 2018
*
* @brief Neighborhood to find a maximum value in a neighborhood
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_NEIGHBORHOOD_MAX_CRITERION_BASE_HH_
#define AKANTU_NEIGHBORHOOD_MAX_CRITERION_BASE_HH_
/* -------------------------------------------------------------------------- */
#include "neighborhood_base.hh"
#include "parsable.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
class NeighborhoodMaxCriterion : public NeighborhoodBase, public Parsable {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
NeighborhoodMaxCriterion(Model & model,
const ElementTypeMapReal & quad_coordinates,
const ID & criterion_id,
- const ID & id = "neighborhood_max_criterion",
- const MemoryID & memory_id = 0);
+ const ID & id = "neighborhood_max_criterion");
~NeighborhoodMaxCriterion() override;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// initialize the neighborhood
void initNeighborhood() override;
/// create grid synchronizer and exchange ghost cells
void createGridSynchronizer() override;
/// find the quads which have the maximum criterion in their neighborhood
void findMaxQuads(std::vector<IntegrationPoint> & max_quads);
protected:
/// remove unneccessary ghost elements
void
cleanupExtraGhostElements(const ElementTypeMap<UInt> & nb_ghost_protected);
/// insert the quadrature points in the grid
void insertAllQuads(GhostType ghost_type);
/// compare criterion with neighbors
void checkNeighbors(GhostType ghost_type);
/* --------------------------------------------------------------------------
*/
/* DataAccessor inherited members */
/* --------------------------------------------------------------------------
*/
public:
virtual inline UInt getNbDataForElements(const Array<Element> & elements,
SynchronizationTag tag) const;
virtual inline void packElementData(CommunicationBuffer & buffer,
const Array<Element> & elements,
SynchronizationTag tag) const;
virtual inline void unpackElementData(CommunicationBuffer & buffer,
const Array<Element> & elements,
SynchronizationTag tag);
- /* --------------------------------------------------------------------------
- */
- /* Accessors */
- /* --------------------------------------------------------------------------
- */
+ /* -------------------------------------------------------------------------*/
+ /* Accessors */
+ /* -------------------------------------------------------------------------*/
public:
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
/// a boolean to store the information if a quad has the max
/// criterion in the neighborhood
ElementTypeMapArray<bool> is_highest;
/// an element type map to store the flattened internal of the criterion
ElementTypeMapReal criterion;
};
} // namespace akantu
#include "neighborhood_max_criterion_inline_impl.hh"
#endif /* AKANTU_NEIGHBORHOOD_MAX_CRITERION_BASE_HH_ */
diff --git a/src/model/common/non_local_toolbox/non_local_manager.cc b/src/model/common/non_local_toolbox/non_local_manager.cc
index 925234458..fa2f9a088 100644
--- a/src/model/common/non_local_toolbox/non_local_manager.cc
+++ b/src/model/common/non_local_toolbox/non_local_manager.cc
@@ -1,654 +1,652 @@
/**
* @file non_local_manager.cc
*
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Apr 13 2012
* @date last modification: Tue Jan 16 2018
*
* @brief Implementation of non-local manager
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "non_local_manager.hh"
#include "grid_synchronizer.hh"
#include "model.hh"
#include "non_local_neighborhood.hh"
/* -------------------------------------------------------------------------- */
#include <numeric>
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
NonLocalManager::NonLocalManager(Model & model,
NonLocalManagerCallback & callback,
- const ID & id, const MemoryID & memory_id)
- : Memory(id, memory_id), Parsable(ParserType::_neighborhoods, id),
- spatial_dimension(model.getMesh().getSpatialDimension()), model(model),
- integration_points_positions("integration_points_positions", id,
- memory_id),
- volumes("volumes", id, memory_id), compute_stress_calls(0),
+ const ID & id)
+ : Parsable(ParserType::_neighborhoods, id),
+ spatial_dimension(model.getMesh().getSpatialDimension()), id(id), model(model),
+ integration_points_positions("integration_points_positions", id),
+ volumes("volumes", id), compute_stress_calls(0),
dummy_registry(nullptr), dummy_grid(nullptr) {
/// parse the neighborhood information from the input file
const Parser & parser = getStaticParser();
/// iterate over all the non-local sections and store them in a map
std::pair<Parser::const_section_iterator, Parser::const_section_iterator>
weight_sect = parser.getSubSections(ParserType::_non_local);
Parser::const_section_iterator it = weight_sect.first;
for (; it != weight_sect.second; ++it) {
const ParserSection & section = *it;
ID name = section.getName();
this->weight_function_types[name] = section;
}
this->callback = &callback;
}
/* -------------------------------------------------------------------------- */
NonLocalManager::~NonLocalManager() = default;
/* -------------------------------------------------------------------------- */
void NonLocalManager::initialize() {
volumes.initialize(this->model.getFEEngine(),
_spatial_dimension = spatial_dimension);
AKANTU_DEBUG_ASSERT(this->callback,
"A callback should be registered prior to this call");
this->callback->insertIntegrationPointsInNeighborhoods(_not_ghost);
auto & mesh = this->model.getMesh();
mesh.registerEventHandler(*this, _ehp_non_local_manager);
/// store the number of current ghost elements for each type in the mesh
// ElementTypeMap<UInt> nb_ghost_protected;
// for (auto type : mesh.elementTypes(spatial_dimension, _ghost))
// nb_ghost_protected(mesh.getNbElement(type, _ghost), type, _ghost);
/// exchange the missing ghosts for the non-local neighborhoods
this->createNeighborhoodSynchronizers();
/// insert the ghost quadrature points of the non-local materials into the
/// non-local neighborhoods
this->callback->insertIntegrationPointsInNeighborhoods(_ghost);
FEEngine & fee = this->model.getFEEngine();
this->updatePairLists();
/// cleanup the unneccessary ghost elements
this->cleanupExtraGhostElements(); // nb_ghost_protected);
this->callback->initializeNonLocal();
this->setJacobians(fee, _ek_regular);
this->initNonLocalVariables();
this->computeWeights();
}
/* -------------------------------------------------------------------------- */
void NonLocalManager::setJacobians(const FEEngine & fe_engine,
ElementKind kind) {
Mesh & mesh = this->model.getMesh();
for (auto ghost_type : ghost_types) {
for (auto type : mesh.elementTypes(spatial_dimension, ghost_type, kind)) {
jacobians(type, ghost_type) =
&fe_engine.getIntegratorInterface().getJacobians(type, ghost_type);
}
}
}
/* -------------------------------------------------------------------------- */
void NonLocalManager::createNeighborhood(const ID & weight_func,
const ID & neighborhood_id) {
AKANTU_DEBUG_IN();
auto weight_func_it = this->weight_function_types.find(weight_func);
AKANTU_DEBUG_ASSERT(weight_func_it != weight_function_types.end(),
"No info found in the input file for the weight_function "
<< weight_func << " in the neighborhood "
<< neighborhood_id);
const ParserSection & section = weight_func_it->second;
const ID weight_func_type = section.getOption();
/// create new neighborhood for given ID
std::stringstream sstr;
sstr << id << ":neighborhood:" << neighborhood_id;
if (weight_func_type == "base_wf") {
neighborhoods[neighborhood_id] =
std::make_unique<NonLocalNeighborhood<BaseWeightFunction>>(
*this, this->integration_points_positions, sstr.str());
#if defined(AKANTU_DAMAGE_NON_LOCAL)
} else if (weight_func_type == "remove_wf") {
neighborhoods[neighborhood_id] =
std::make_unique<NonLocalNeighborhood<RemoveDamagedWeightFunction>>(
*this, this->integration_points_positions, sstr.str());
} else if (weight_func_type == "stress_wf") {
neighborhoods[neighborhood_id] =
std::make_unique<NonLocalNeighborhood<StressBasedWeightFunction>>(
*this, this->integration_points_positions, sstr.str());
} else if (weight_func_type == "damage_wf") {
neighborhoods[neighborhood_id] =
std::make_unique<NonLocalNeighborhood<DamagedWeightFunction>>(
*this, this->integration_points_positions, sstr.str());
#endif
} else {
AKANTU_EXCEPTION("error in weight function type provided in material file");
}
neighborhoods[neighborhood_id]->parseSection(section);
neighborhoods[neighborhood_id]->initNeighborhood();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NonLocalManager::createNeighborhoodSynchronizers() {
/// exchange all the neighborhood IDs, so that every proc knows how many
/// neighborhoods exist globally
/// First: Compute locally the maximum ID size
UInt max_id_size = 0;
UInt current_size = 0;
NeighborhoodMap::const_iterator it;
for (it = neighborhoods.begin(); it != neighborhoods.end(); ++it) {
current_size = it->first.size();
if (current_size > max_id_size) {
max_id_size = current_size;
}
}
/// get the global maximum ID size on each proc
const Communicator & static_communicator = model.getMesh().getCommunicator();
static_communicator.allReduce(max_id_size, SynchronizerOperation::_max);
/// get the rank for this proc and the total nb proc
UInt prank = static_communicator.whoAmI();
UInt psize = static_communicator.getNbProc();
/// exchange the number of neighborhoods on each proc
Array<Int> nb_neighborhoods_per_proc(psize);
nb_neighborhoods_per_proc(prank) = neighborhoods.size();
static_communicator.allGather(nb_neighborhoods_per_proc);
/// compute the total number of neighborhoods
UInt nb_neighborhoods_global = std::accumulate(
nb_neighborhoods_per_proc.begin(), nb_neighborhoods_per_proc.end(), 0);
/// allocate an array of chars to store the names of all neighborhoods
Array<char> buffer(nb_neighborhoods_global, max_id_size);
/// starting index on this proc
UInt starting_index =
std::accumulate(nb_neighborhoods_per_proc.begin(),
nb_neighborhoods_per_proc.begin() + prank, 0);
it = neighborhoods.begin();
/// store the names of local neighborhoods in the buffer
for (UInt i = 0; i < neighborhoods.size(); ++i, ++it) {
UInt c = 0;
for (; c < it->first.size(); ++c) {
buffer(i + starting_index, c) = it->first[c];
}
for (; c < max_id_size; ++c) {
buffer(i + starting_index, c) = char(0);
}
}
/// store the nb of data to send in the all gather
Array<Int> buffer_size(nb_neighborhoods_per_proc);
buffer_size *= max_id_size;
/// exchange the names of all the neighborhoods with all procs
static_communicator.allGatherV(buffer, buffer_size);
for (UInt i = 0; i < nb_neighborhoods_global; ++i) {
std::stringstream neighborhood_id;
for (UInt c = 0; c < max_id_size; ++c) {
if (buffer(i, c) == char(0)) {
break;
}
neighborhood_id << buffer(i, c);
}
global_neighborhoods.insert(neighborhood_id.str());
}
/// this proc does not know all the neighborhoods -> create dummy
/// grid so that this proc can participate in the all gather for
/// detecting the overlap of neighborhoods this proc doesn't know
Vector<Real> grid_center(this->spatial_dimension,
std::numeric_limits<Real>::max());
Vector<Real> spacing(this->spatial_dimension, 0.);
dummy_grid = std::make_unique<SpatialGrid<IntegrationPoint>>(
this->spatial_dimension, spacing, grid_center);
for (const auto & neighborhood_id : global_neighborhoods) {
it = neighborhoods.find(neighborhood_id);
if (it != neighborhoods.end()) {
it->second->createGridSynchronizer();
} else {
dummy_synchronizers[neighborhood_id] = std::make_unique<GridSynchronizer>(
this->model.getMesh(), *dummy_grid,
std::string(this->id + ":" + neighborhood_id + ":grid_synchronizer"),
- this->memory_id, false);
+ false);
}
}
}
/* -------------------------------------------------------------------------- */
void NonLocalManager::synchronize(DataAccessor<Element> & data_accessor,
const SynchronizationTag & tag) {
for (const auto & neighborhood_id : global_neighborhoods) {
auto it = neighborhoods.find(neighborhood_id);
if (it != neighborhoods.end()) {
it->second->synchronize(data_accessor, tag);
} else {
auto synchronizer_it = dummy_synchronizers.find(neighborhood_id);
if (synchronizer_it == dummy_synchronizers.end()) {
continue;
}
synchronizer_it->second->synchronizeOnce(data_accessor, tag);
}
}
}
/* -------------------------------------------------------------------------- */
void NonLocalManager::averageInternals(GhostType ghost_type) {
/// update the weights of the weight function
if (ghost_type == _not_ghost) {
this->computeWeights();
}
/// loop over all neighborhoods and compute the non-local variables
for (auto & neighborhood : neighborhoods) {
/// loop over all the non-local variables of the given neighborhood
for (auto & non_local_variable : non_local_variables) {
NonLocalVariable & non_local_var = *non_local_variable.second;
neighborhood.second->weightedAverageOnNeighbours(
non_local_var.local, non_local_var.non_local,
non_local_var.nb_component, ghost_type);
}
}
}
/* -------------------------------------------------------------------------- */
void NonLocalManager::computeWeights() {
AKANTU_DEBUG_IN();
this->updateWeightFunctionInternals();
this->volumes.zero();
for (const auto & global_neighborhood : global_neighborhoods) {
auto it = neighborhoods.find(global_neighborhood);
if (it != neighborhoods.end()) {
it->second->updateWeights();
} else {
dummy_synchronizers[global_neighborhood]->synchronize(
dummy_accessor, SynchronizationTag::_mnl_weight);
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NonLocalManager::updatePairLists() {
AKANTU_DEBUG_IN();
integration_points_positions.initialize(
this->model.getFEEngine(), _nb_component = spatial_dimension,
_spatial_dimension = spatial_dimension);
/// compute the position of the quadrature points
this->model.getFEEngine().computeIntegrationPointsCoordinates(
integration_points_positions);
for (auto & pair : neighborhoods) {
pair.second->updatePairList();
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void NonLocalManager::registerNonLocalVariable(const ID & variable_name,
const ID & nl_variable_name,
UInt nb_component) {
AKANTU_DEBUG_IN();
auto non_local_variable_it = non_local_variables.find(variable_name);
if (non_local_variable_it == non_local_variables.end()) {
non_local_variables[nl_variable_name] = std::make_unique<NonLocalVariable>(
variable_name, nl_variable_name, this->id, nb_component);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
ElementTypeMapReal &
NonLocalManager::registerWeightFunctionInternal(const ID & field_name) {
AKANTU_DEBUG_IN();
auto it = this->weight_function_internals.find(field_name);
if (it == weight_function_internals.end()) {
weight_function_internals[field_name] =
- std::make_unique<ElementTypeMapReal>(field_name, this->id,
- this->memory_id);
+ std::make_unique<ElementTypeMapReal>(field_name, this->id);
}
AKANTU_DEBUG_OUT();
return *(weight_function_internals[field_name]);
}
/* -------------------------------------------------------------------------- */
void NonLocalManager::updateWeightFunctionInternals() {
for (auto & pair : this->weight_function_internals) {
auto & internals = *pair.second;
internals.zero();
for (auto ghost_type : ghost_types) {
this->callback->updateLocalInternal(internals, ghost_type, _ek_regular);
}
}
}
/* -------------------------------------------------------------------------- */
void NonLocalManager::initNonLocalVariables() {
/// loop over all the non-local variables
for (auto & pair : non_local_variables) {
auto & variable = *pair.second;
variable.non_local.initialize(this->model.getFEEngine(),
_nb_component = variable.nb_component,
_spatial_dimension = spatial_dimension);
}
}
/* -------------------------------------------------------------------------- */
void NonLocalManager::computeAllNonLocalStresses() {
/// update the flattened version of the internals
for (auto & pair : non_local_variables) {
auto & variable = *pair.second;
variable.local.zero();
variable.non_local.zero();
for (auto ghost_type : ghost_types) {
this->callback->updateLocalInternal(variable.local, ghost_type,
_ek_regular);
}
}
this->volumes.zero();
for (auto & pair : neighborhoods) {
auto & neighborhood = *pair.second;
neighborhood.asynchronousSynchronize(SynchronizationTag::_mnl_for_average);
}
this->averageInternals(_not_ghost);
AKANTU_DEBUG_INFO("Wait distant non local stresses");
for (auto & pair : neighborhoods) {
auto & neighborhood = *pair.second;
neighborhood.waitEndSynchronize(SynchronizationTag::_mnl_for_average);
}
this->averageInternals(_ghost);
/// copy the results in the materials
for (auto & pair : non_local_variables) {
auto & variable = *pair.second;
for (auto ghost_type : ghost_types) {
this->callback->updateNonLocalInternal(variable.non_local, ghost_type,
_ek_regular);
}
}
this->callback->computeNonLocalStresses(_not_ghost);
++this->compute_stress_calls;
}
/* -------------------------------------------------------------------------- */
void NonLocalManager::cleanupExtraGhostElements() {
// ElementTypeMap<UInt> & nb_ghost_protected) {
using ElementSet = std::set<Element>;
ElementSet relevant_ghost_elements;
/// loop over all the neighborhoods and get their protected ghosts
for (auto & pair : neighborhoods) {
auto & neighborhood = *pair.second;
ElementSet to_keep_per_neighborhood;
neighborhood.getRelevantGhostElements(to_keep_per_neighborhood);
relevant_ghost_elements.insert(to_keep_per_neighborhood.begin(),
to_keep_per_neighborhood.end());
}
for (auto & pair : neighborhoods) {
auto & neighborhood = *pair.second;
neighborhood.cleanupExtraGhostElements(relevant_ghost_elements);
}
// /// remove all unneccessary ghosts from the mesh
// /// Create list of element to remove and new numbering for element to keep
// Mesh & mesh = this->model.getMesh();
// ElementSet ghost_to_erase;
// RemovedElementsEvent remove_elem(mesh);
// auto & new_numberings = remove_elem.getNewNumbering();
// Element element;
// element.ghost_type = _ghost;
// for (auto & type : mesh.elementTypes(spatial_dimension, _ghost)) {
// element.type = type;
// UInt nb_ghost_elem = mesh.getNbElement(type, _ghost);
// // UInt nb_ghost_elem_protected = 0;
// // try {
// // nb_ghost_elem_protected = nb_ghost_protected(type, _ghost);
// // } catch (...) {
// // }
// if (!new_numberings.exists(type, _ghost))
// new_numberings.alloc(nb_ghost_elem, 1, type, _ghost);
// else
// new_numberings(type, _ghost).resize(nb_ghost_elem);
// Array<UInt> & new_numbering = new_numberings(type, _ghost);
// for (UInt g = 0; g < nb_ghost_elem; ++g) {
// element.element = g;
// if (element.element >= nb_ghost_elem_protected &&
// relevant_ghost_elements.find(element) ==
// relevant_ghost_elements.end()) {
// remove_elem.getList().push_back(element);
// new_numbering(element.element) = UInt(-1);
// }
// }
// /// renumber remaining ghosts
// UInt ng = 0;
// for (UInt g = 0; g < nb_ghost_elem; ++g) {
// if (new_numbering(g) != UInt(-1)) {
// new_numbering(g) = ng;
// ++ng;
// }
// }
// }
// for (auto & type : mesh.elementTypes(spatial_dimension, _not_ghost)) {
// UInt nb_elem = mesh.getNbElement(type, _not_ghost);
// if (!new_numberings.exists(type, _not_ghost))
// new_numberings.alloc(nb_elem, 1, type, _not_ghost);
// Array<UInt> & new_numbering = new_numberings(type, _not_ghost);
// for (UInt e = 0; e < nb_elem; ++e) {
// new_numbering(e) = e;
// }
// }
// mesh.sendEvent(remove_elem);
}
/* -------------------------------------------------------------------------- */
void NonLocalManager::onElementsRemoved(
const Array<Element> & element_list,
const ElementTypeMapArray<UInt> & new_numbering,
__attribute__((unused)) const RemovedElementsEvent & event) {
FEEngine & fee = this->model.getFEEngine();
NonLocalManager::removeIntegrationPointsFromMap(
event.getNewNumbering(), spatial_dimension, integration_points_positions,
fee, _ek_regular);
NonLocalManager::removeIntegrationPointsFromMap(event.getNewNumbering(), 1,
volumes, fee, _ek_regular);
/// loop over all the neighborhoods and call onElementsRemoved
auto global_neighborhood_it = global_neighborhoods.begin();
NeighborhoodMap::iterator it;
for (; global_neighborhood_it != global_neighborhoods.end();
++global_neighborhood_it) {
it = neighborhoods.find(*global_neighborhood_it);
if (it != neighborhoods.end()) {
it->second->onElementsRemoved(element_list, new_numbering, event);
} else {
dummy_synchronizers[*global_neighborhood_it]->onElementsRemoved(
element_list, new_numbering, event);
}
}
}
/* -------------------------------------------------------------------------- */
void NonLocalManager::onElementsAdded(const Array<Element> & /*unused*/,
const NewElementsEvent & /*unused*/) {
this->resizeElementTypeMap(1, volumes, model.getFEEngine());
this->resizeElementTypeMap(spatial_dimension, integration_points_positions,
model.getFEEngine());
}
/* -------------------------------------------------------------------------- */
void NonLocalManager::resizeElementTypeMap(UInt nb_component,
ElementTypeMapReal & element_map,
const FEEngine & fee,
const ElementKind el_kind) {
Mesh & mesh = this->model.getMesh();
for (auto gt : ghost_types) {
for (auto type : mesh.elementTypes(spatial_dimension, gt, el_kind)) {
UInt nb_element = mesh.getNbElement(type, gt);
UInt nb_quads = fee.getNbIntegrationPoints(type, gt);
if (!element_map.exists(type, gt)) {
element_map.alloc(nb_element * nb_quads, nb_component, type, gt);
} else {
element_map(type, gt).resize(nb_element * nb_quads);
}
}
}
}
/* -------------------------------------------------------------------------- */
void NonLocalManager::removeIntegrationPointsFromMap(
const ElementTypeMapArray<UInt> & new_numbering, UInt nb_component,
ElementTypeMapReal & element_map, const FEEngine & fee,
const ElementKind el_kind) {
for (auto gt : ghost_types) {
for (auto type : new_numbering.elementTypes(_all_dimensions, gt, el_kind)) {
if (element_map.exists(type, gt)) {
const Array<UInt> & renumbering = new_numbering(type, gt);
Array<Real> & vect = element_map(type, gt);
UInt nb_quad_per_elem = fee.getNbIntegrationPoints(type, gt);
Array<Real> tmp(renumbering.size() * nb_quad_per_elem, nb_component);
AKANTU_DEBUG_ASSERT(
tmp.size() == vect.size(),
"Something strange append some mater was created or disappeared in "
<< vect.getID() << "(" << vect.size() << "!=" << tmp.size()
<< ") "
"!!");
UInt new_size = 0;
for (UInt i = 0; i < renumbering.size(); ++i) {
UInt new_i = renumbering(i);
if (new_i != UInt(-1)) {
memcpy(tmp.storage() + new_i * nb_component * nb_quad_per_elem,
vect.storage() + i * nb_component * nb_quad_per_elem,
nb_component * nb_quad_per_elem * sizeof(Real));
++new_size;
}
}
tmp.resize(new_size * nb_quad_per_elem);
vect.copy(tmp);
}
}
}
}
/* -------------------------------------------------------------------------- */
UInt NonLocalManager::getNbData(const Array<Element> & elements,
const ID & id) const {
UInt size = 0;
UInt nb_quadrature_points = this->model.getNbIntegrationPoints(elements);
auto it = non_local_variables.find(id);
AKANTU_DEBUG_ASSERT(it != non_local_variables.end(),
"The non-local variable " << id << " is not registered");
size += it->second->nb_component * sizeof(Real) * nb_quadrature_points;
return size;
}
/* -------------------------------------------------------------------------- */
void NonLocalManager::packData(CommunicationBuffer & buffer,
const Array<Element> & elements,
const ID & id) const {
auto it = non_local_variables.find(id);
AKANTU_DEBUG_ASSERT(it != non_local_variables.end(),
"The non-local variable " << id << " is not registered");
DataAccessor<Element>::packElementalDataHelper<Real>(
it->second->local, buffer, elements, true, this->model.getFEEngine());
}
/* -------------------------------------------------------------------------- */
void NonLocalManager::unpackData(CommunicationBuffer & buffer,
const Array<Element> & elements,
const ID & id) const {
auto it = non_local_variables.find(id);
AKANTU_DEBUG_ASSERT(it != non_local_variables.end(),
"The non-local variable " << id << " is not registered");
DataAccessor<Element>::unpackElementalDataHelper<Real>(
it->second->local, buffer, elements, true, this->model.getFEEngine());
}
} // namespace akantu
diff --git a/src/model/common/non_local_toolbox/non_local_manager.hh b/src/model/common/non_local_toolbox/non_local_manager.hh
index d9fbf3a7c..ff214c325 100644
--- a/src/model/common/non_local_toolbox/non_local_manager.hh
+++ b/src/model/common/non_local_toolbox/non_local_manager.hh
@@ -1,286 +1,284 @@
/**
* @file non_local_manager.hh
*
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Wed Nov 08 2017
*
* @brief Classes that manages all the non-local neighborhoods
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "communication_buffer.hh"
#include "data_accessor.hh"
#include "mesh_events.hh"
#include "non_local_manager_callback.hh"
#include "parsable.hh"
/* -------------------------------------------------------------------------- */
#include <map>
#include <set>
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_NON_LOCAL_MANAGER_HH_
#define AKANTU_NON_LOCAL_MANAGER_HH_
namespace akantu {
class Model;
class NonLocalNeighborhoodBase;
class GridSynchronizer;
class SynchronizerRegistry;
class IntegrationPoint;
template <typename T> class SpatialGrid;
class FEEngine;
} // namespace akantu
namespace akantu {
-class NonLocalManager : protected Memory,
- public MeshEventHandler,
- public Parsable {
+class NonLocalManager : public MeshEventHandler, public Parsable {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
NonLocalManager(Model & model, NonLocalManagerCallback & callback,
- const ID & id = "non_local_manager",
- const MemoryID & memory_id = 0);
+ const ID & id = "non_local_manager");
~NonLocalManager() override;
using NeighborhoodMap =
std::map<ID, std::unique_ptr<NonLocalNeighborhoodBase>>;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ----------------------------------------------------------------------- */
public:
/// register a new internal needed for the weight computations
ElementTypeMapReal & registerWeightFunctionInternal(const ID & field_name);
/// register a non-local variable
void registerNonLocalVariable(const ID & variable_name,
const ID & nl_variable_name, UInt nb_component);
/// register non-local neighborhood
inline void registerNeighborhood(const ID & neighborhood,
const ID & weight_func_id);
// void registerNonLocalManagerCallback(NonLocalManagerCallback & callback);
/// average the internals and compute the non-local stresses
virtual void computeAllNonLocalStresses();
/// initialize the non-local manager: compute pair lists and weights for all
/// neighborhoods
virtual void initialize();
/// synchronize once on a given tag using the neighborhoods synchronizer
void synchronize(DataAccessor<Element> & data_accessor,
const SynchronizationTag & /*tag*/);
protected:
/// create the grid synchronizers for each neighborhood
void createNeighborhoodSynchronizers();
/// compute the weights in each neighborhood for non-local averaging
void computeWeights();
/// compute the weights in each neighborhood for non-local averaging
void updatePairLists();
/// average the non-local variables
void averageInternals(GhostType ghost_type = _not_ghost);
/// update the flattened version of the weight function internals
void updateWeightFunctionInternals();
protected:
/// create a new neighborhood for a given domain ID
void createNeighborhood(const ID & weight_func, const ID & neighborhood);
/// set the values of the jacobians
void setJacobians(const FEEngine & fe_engine, ElementKind kind);
/// allocation of eelment type maps
// void initElementTypeMap(UInt nb_component,
// ElementTypeMapReal & element_map,
// const FEEngine & fe_engine,
// const ElementKind el_kind = _ek_regular);
/// resizing of element type maps
void resizeElementTypeMap(UInt nb_component, ElementTypeMapReal & element_map,
const FEEngine & fee,
- ElementKind el_kind = _ek_regular);
+ ElementKind el_kind = _ek_regular);
/// remove integration points from element type maps
static void removeIntegrationPointsFromMap(
const ElementTypeMapArray<UInt> & new_numbering, UInt nb_component,
ElementTypeMapReal & element_map, const FEEngine & fee,
ElementKind el_kind = _ek_regular);
/// allocate the non-local variables
void initNonLocalVariables();
/// cleanup unneccessary ghosts
void
cleanupExtraGhostElements(); // ElementTypeMap<UInt> & nb_ghost_protected);
/* ------------------------------------------------------------------------ */
/* DataAccessor kind of interface */
/* ------------------------------------------------------------------------ */
public:
/// get Nb data for synchronization in parallel
UInt getNbData(const Array<Element> & elements, const ID & id) const;
/// pack data for synchronization in parallel
void packData(CommunicationBuffer & buffer, const Array<Element> & elements,
const ID & id) const;
/// unpack data for synchronization in parallel
void unpackData(CommunicationBuffer & buffer, const Array<Element> & elements,
const ID & id) const;
/* ------------------------------------------------------------------------ */
/* MeshEventHandler inherited members */
/* ------------------------------------------------------------------------ */
public:
void onElementsRemoved(const Array<Element> & element_list,
const ElementTypeMapArray<UInt> & new_numbering,
const RemovedElementsEvent & event) override;
void onElementsAdded(const Array<Element> & element_list,
const NewElementsEvent & event) override;
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
AKANTU_GET_MACRO(SpatialDimension, spatial_dimension, UInt);
AKANTU_GET_MACRO(Model, model, const Model &);
AKANTU_GET_MACRO_NOT_CONST(Model, model, Model &);
AKANTU_GET_MACRO_NOT_CONST(Volumes, volumes, ElementTypeMapReal &)
AKANTU_GET_MACRO(NbStressCalls, compute_stress_calls, UInt);
/// return the fem object associated with a provided name
inline NonLocalNeighborhoodBase & getNeighborhood(const ID & name) const;
inline const Array<Real> & getJacobians(ElementType type,
GhostType ghost_type) {
return *jacobians(type, ghost_type);
}
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
private:
/// the spatial dimension
const UInt spatial_dimension;
+ ID id;
protected:
/// the non-local neighborhoods present
NeighborhoodMap neighborhoods;
/// list of all the non-local materials in the model
// std::vector<ID> non_local_materials;
struct NonLocalVariable {
NonLocalVariable(const ID & variable_name, const ID & nl_variable_name,
const ID & id, UInt nb_component)
- : local(variable_name, id, 0), non_local(nl_variable_name, id, 0),
+ : local(variable_name, id), non_local(nl_variable_name, id),
nb_component(nb_component) {}
ElementTypeMapReal local;
ElementTypeMapReal non_local;
UInt nb_component;
};
/// the non-local variables associated to a certain neighborhood
std::map<ID, std::unique_ptr<NonLocalVariable>> non_local_variables;
/// reference to the model
Model & model;
/// jacobians for all the elements in the mesh
ElementTypeMap<const Array<Real> *> jacobians;
/// store the position of the quadrature points
ElementTypeMapReal integration_points_positions;
/// store the volume of each quadrature point for the non-local weight
/// normalization
ElementTypeMapReal volumes;
/// counter for computeStress calls
UInt compute_stress_calls;
/// map to store weight function types from input file
std::map<ID, ParserSection> weight_function_types;
/// map to store the internals needed by the weight functions
std::map<ID, std::unique_ptr<ElementTypeMapReal>> weight_function_internals;
/* --------------------------------------------------------------------------
*/
/// the following are members needed to make this processor participate in the
/// grid creation of neighborhoods he doesn't own as a member. For details see
/// createGridSynchronizers function
/// synchronizer registry for dummy grid synchronizers
std::unique_ptr<SynchronizerRegistry> dummy_registry;
/// map of dummy synchronizers
std::map<ID, std::unique_ptr<GridSynchronizer>> dummy_synchronizers;
/// dummy spatial grid
std::unique_ptr<SpatialGrid<IntegrationPoint>> dummy_grid;
/// create a set of all neighborhoods present in the simulation
std::set<ID> global_neighborhoods;
class DummyDataAccessor : public DataAccessor<Element> {
public:
inline UInt getNbData(const Array<Element> & /*elements*/,
const SynchronizationTag & /*tag*/) const override {
return 0;
};
inline void packData(CommunicationBuffer & /*buffer*/,
const Array<Element> & /*element*/,
const SynchronizationTag & /*tag*/) const override{};
inline void unpackData(CommunicationBuffer & /*buffer*/,
const Array<Element> & /*element*/,
const SynchronizationTag & /*tag*/) override{};
};
DummyDataAccessor dummy_accessor;
/* ------------------------------------------------------------------------ */
NonLocalManagerCallback * callback;
};
} // namespace akantu
/* -------------------------------------------------------------------------- */
/* inline functions */
/* -------------------------------------------------------------------------- */
#include "non_local_manager_inline_impl.hh"
#endif /* AKANTU_NON_LOCAL_MANAGER_HH_ */
diff --git a/src/model/common/non_local_toolbox/non_local_neighborhood.hh b/src/model/common/non_local_toolbox/non_local_neighborhood.hh
index 48ceebd66..c265f5db5 100644
--- a/src/model/common/non_local_toolbox/non_local_neighborhood.hh
+++ b/src/model/common/non_local_toolbox/non_local_neighborhood.hh
@@ -1,134 +1,133 @@
/**
* @file non_local_neighborhood.hh
*
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Wed Nov 08 2017
*
* @brief Non-local neighborhood for non-local averaging based on
* weight function
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_NON_LOCAL_NEIGHBORHOOD_HH_
#define AKANTU_NON_LOCAL_NEIGHBORHOOD_HH_
/* -------------------------------------------------------------------------- */
#include "base_weight_function.hh"
#include "non_local_neighborhood_base.hh"
#include "parsable.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
class NonLocalManager;
class BaseWeightFunction;
} // namespace akantu
namespace akantu {
template <class WeightFunction = BaseWeightFunction>
class NonLocalNeighborhood : public NonLocalNeighborhoodBase {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
NonLocalNeighborhood(NonLocalManager & manager,
const ElementTypeMapReal & quad_coordinates,
- const ID & id = "neighborhood",
- const MemoryID & memory_id = 0);
+ const ID & id = "neighborhood");
~NonLocalNeighborhood() override;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// compute the weights for non-local averaging
void computeWeights() override;
/// save the pair of weights in a file
void saveWeights(const std::string & filename) const override;
/// compute the non-local counter part for a given element type map
// compute the non-local counter part for a given element type map
void
weightedAverageOnNeighbours(const ElementTypeMapReal & to_accumulate,
ElementTypeMapReal & accumulated,
UInt nb_degree_of_freedom,
GhostType ghost_type2) const override;
/// update the weights based on the weight function
void updateWeights() override;
/// register a new non-local variable in the neighborhood
// void registerNonLocalVariable(const ID & id);
protected:
template <class Func>
inline void foreach_weight(GhostType ghost_type, Func && func);
template <class Func>
inline void foreach_weight(GhostType ghost_type, Func && func) const;
inline UInt getNbData(const Array<Element> & elements,
const SynchronizationTag & tag) const override;
inline void packData(CommunicationBuffer & buffer,
const Array<Element> & elements,
const SynchronizationTag & tag) const override;
inline void unpackData(CommunicationBuffer & buffer,
const Array<Element> & elements,
const SynchronizationTag & tag) override;
/* ------------------------------------------------------------------------ */
/* Accessor */
/* ------------------------------------------------------------------------ */
public:
AKANTU_GET_MACRO(NonLocalManager, non_local_manager, const NonLocalManager &);
AKANTU_GET_MACRO_NOT_CONST(NonLocalManager, non_local_manager,
NonLocalManager &);
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
private:
/// Pointer to non-local manager class
NonLocalManager & non_local_manager;
/// the weights associated to the pairs
std::array<std::unique_ptr<Array<Real>>, 2> pair_weight;
/// weight function
std::unique_ptr<WeightFunction> weight_function;
};
} // namespace akantu
/* -------------------------------------------------------------------------- */
/* Implementation of template functions */
/* -------------------------------------------------------------------------- */
#include "non_local_neighborhood_tmpl.hh"
/* -------------------------------------------------------------------------- */
/* inline functions */
/* -------------------------------------------------------------------------- */
#include "non_local_neighborhood_inline_impl.hh"
#endif /* AKANTU_NON_LOCAL_NEIGHBORHOOD_HH_ */
diff --git a/src/model/common/non_local_toolbox/non_local_neighborhood_base.cc b/src/model/common/non_local_toolbox/non_local_neighborhood_base.cc
index a696d72db..317625b30 100644
--- a/src/model/common/non_local_toolbox/non_local_neighborhood_base.cc
+++ b/src/model/common/non_local_toolbox/non_local_neighborhood_base.cc
@@ -1,126 +1,124 @@
/**
* @file non_local_neighborhood_base.cc
*
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Sat Sep 26 2015
* @date last modification: Fri Dec 08 2017
*
* @brief Implementation of non-local neighborhood base
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "non_local_neighborhood_base.hh"
#include "grid_synchronizer.hh"
#include "model.hh"
/* -------------------------------------------------------------------------- */
#include <memory>
namespace akantu {
/* -------------------------------------------------------------------------- */
NonLocalNeighborhoodBase::NonLocalNeighborhoodBase(
- Model & model, const ElementTypeMapReal & quad_coordinates, const ID & id,
- const MemoryID & memory_id)
- : NeighborhoodBase(model, quad_coordinates, id, memory_id),
+ Model & model, const ElementTypeMapReal & quad_coordinates, const ID & id)
+ : NeighborhoodBase(model, quad_coordinates, id),
Parsable(ParserType::_non_local, id) {
-
AKANTU_DEBUG_IN();
this->registerParam("radius", neighborhood_radius, 100.,
_pat_parsable | _pat_readable, "Non local radius");
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
NonLocalNeighborhoodBase::~NonLocalNeighborhoodBase() = default;
/* -------------------------------------------------------------------------- */
void NonLocalNeighborhoodBase::createGridSynchronizer() {
this->is_creating_grid = true;
this->grid_synchronizer = std::make_unique<GridSynchronizer>(
this->model.getMesh(), *spatial_grid, *this,
std::set<SynchronizationTag>{SynchronizationTag::_mnl_weight,
SynchronizationTag::_mnl_for_average},
- std::string(getID() + ":grid_synchronizer"), this->memory_id, false);
+ std::string(id + ":grid_synchronizer"), false);
this->is_creating_grid = false;
}
/* -------------------------------------------------------------------------- */
void NonLocalNeighborhoodBase::synchronize(
DataAccessor<Element> & data_accessor, const SynchronizationTag & tag) {
if (not grid_synchronizer) {
return;
}
grid_synchronizer->synchronizeOnce(data_accessor, tag);
}
/* -------------------------------------------------------------------------- */
void NonLocalNeighborhoodBase::getRelevantGhostElements(
std::set<Element> & relevant_ghost_elements) {
for (auto && ghost_type : ghost_type_t{}) {
auto & pair_list = this->pair_list.at(ghost_type);
for (auto && pair : pair_list) {
if (pair.first.ghost_type == _ghost) {
relevant_ghost_elements.insert(pair.first);
}
if (pair.second.ghost_type == _ghost) {
relevant_ghost_elements.insert(pair.second);
}
}
}
}
/* -------------------------------------------------------------------------- */
void NonLocalNeighborhoodBase::cleanupExtraGhostElements(
std::set<Element> & relevant_ghost_elements) {
Array<Element> ghosts_to_erase;
auto & mesh = this->model.getMesh();
auto end = relevant_ghost_elements.end();
for (const auto & type : mesh.elementTypes(
_spatial_dimension = spatial_dimension, _ghost_type = _ghost)) {
auto nb_ghost_elem = mesh.getNbElement(type, _ghost);
for (UInt g = 0; g < nb_ghost_elem; ++g) {
Element element{type, g, _ghost};
if (relevant_ghost_elements.find(element) == end) {
ghosts_to_erase.push_back(element);
}
}
}
/// remove the unneccessary ghosts from the synchronizer
mesh.eraseElements(ghosts_to_erase);
}
/* -------------------------------------------------------------------------- */
void NonLocalNeighborhoodBase::registerNonLocalVariable(const ID & id) {
this->non_local_variables.insert(id);
}
} // namespace akantu
diff --git a/src/model/common/non_local_toolbox/non_local_neighborhood_base.hh b/src/model/common/non_local_toolbox/non_local_neighborhood_base.hh
index 5bf1dc2c7..28214e2fe 100644
--- a/src/model/common/non_local_toolbox/non_local_neighborhood_base.hh
+++ b/src/model/common/non_local_toolbox/non_local_neighborhood_base.hh
@@ -1,135 +1,134 @@
/**
* @file non_local_neighborhood_base.hh
*
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Sat Sep 26 2015
* @date last modification: Tue Feb 20 2018
*
* @brief Non-local neighborhood base class
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "neighborhood_base.hh"
#include "parsable.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_NON_LOCAL_NEIGHBORHOOD_BASE_HH_
#define AKANTU_NON_LOCAL_NEIGHBORHOOD_BASE_HH_
namespace akantu {
class Model;
}
/* -------------------------------------------------------------------------- */
namespace akantu {
class NonLocalNeighborhoodBase : public NeighborhoodBase, public Parsable {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
NonLocalNeighborhoodBase(Model & model,
const ElementTypeMapReal & quad_coordinates,
- const ID & id = "non_local_neighborhood",
- const MemoryID & memory_id = 0);
+ const ID & id = "non_local_neighborhood");
~NonLocalNeighborhoodBase() override;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// create grid synchronizer and exchange ghost cells
void createGridSynchronizer() override;
void synchronize(DataAccessor<Element> & data_accessor,
const SynchronizationTag & tag) override;
/// compute weights, for instance needed for non-local damage computation
virtual void computeWeights(){};
// compute the non-local counter part for a given element type map
virtual void
weightedAverageOnNeighbours(const ElementTypeMapReal & to_accumulate,
ElementTypeMapReal & accumulated,
UInt nb_degree_of_freedom,
GhostType ghost_type2) const = 0;
/// update the weights for the non-local averaging
virtual void updateWeights() = 0;
/// update the weights for the non-local averaging
virtual void saveWeights(const std::string & /*unused*/) const {
AKANTU_TO_IMPLEMENT();
}
/// register a new non-local variable in the neighborhood
virtual void registerNonLocalVariable(const ID & id);
/// clean up the unneccessary ghosts
void cleanupExtraGhostElements(std::set<Element> & relevant_ghost_elements);
/// list releveant ghosts
void getRelevantGhostElements(std::set<Element> & relevant_ghost_elements);
protected:
/// create the grid
void createGrid();
/* --------------------------------------------------------------------------
*/
/* DataAccessor inherited members */
/* --------------------------------------------------------------------------
*/
public:
inline UInt getNbData(const Array<Element> & /*elements*/,
const SynchronizationTag & /*tag*/) const override {
return 0;
}
inline void packData(CommunicationBuffer & /*buffer*/,
const Array<Element> & /*element*/,
const SynchronizationTag & /*tag*/) const override {}
inline void unpackData(CommunicationBuffer & /*buffer*/,
const Array<Element> & /*element*/,
const SynchronizationTag & /*tag*/) override {}
/* --------------------------------------------------------------------------
*/
/* Accessors */
/* --------------------------------------------------------------------------
*/
public:
AKANTU_GET_MACRO(NonLocalVariables, non_local_variables,
const std::set<ID> &);
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
/// list of non-local variables associated to the neighborhood
std::set<ID> non_local_variables;
};
} // namespace akantu
#endif /* AKANTU_NON_LOCAL_NEIGHBORHOOD_BASE_HH_ */
diff --git a/src/model/common/non_local_toolbox/non_local_neighborhood_tmpl.hh b/src/model/common/non_local_toolbox/non_local_neighborhood_tmpl.hh
index 68a76fca0..e4282845b 100644
--- a/src/model/common/non_local_toolbox/non_local_neighborhood_tmpl.hh
+++ b/src/model/common/non_local_toolbox/non_local_neighborhood_tmpl.hh
@@ -1,278 +1,277 @@
/**
* @file non_local_neighborhood_tmpl.hh
*
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Mon Sep 28 2015
* @date last modification: Tue Feb 20 2018
*
* @brief Implementation of class non-local neighborhood
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "communicator.hh"
#include "non_local_manager.hh"
#include "non_local_neighborhood.hh"
/* -------------------------------------------------------------------------- */
#include <fstream>
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_NON_LOCAL_NEIGHBORHOOD_TMPL_HH_
#define AKANTU_NON_LOCAL_NEIGHBORHOOD_TMPL_HH_
namespace akantu {
/* -------------------------------------------------------------------------- */
template <class WeightFunction>
template <class Func>
inline void NonLocalNeighborhood<WeightFunction>::foreach_weight(
GhostType ghost_type, Func && func) {
auto weight_it =
pair_weight[ghost_type]->begin(pair_weight[ghost_type]->getNbComponent());
for (auto & pair : pair_list[ghost_type]) {
std::forward<decltype(func)>(func)(pair.first, pair.second, *weight_it);
++weight_it;
}
}
/* -------------------------------------------------------------------------- */
template <class WeightFunction>
template <class Func>
inline void NonLocalNeighborhood<WeightFunction>::foreach_weight(
GhostType ghost_type, Func && func) const {
auto weight_it =
pair_weight[ghost_type]->begin(pair_weight[ghost_type]->getNbComponent());
for (auto & pair : pair_list[ghost_type]) {
std::forward<decltype(func)>(func)(pair.first, pair.second, *weight_it);
++weight_it;
}
}
/* -------------------------------------------------------------------------- */
template <class WeightFunction>
NonLocalNeighborhood<WeightFunction>::NonLocalNeighborhood(
NonLocalManager & manager, const ElementTypeMapReal & quad_coordinates,
- const ID & id, const MemoryID & memory_id)
- : NonLocalNeighborhoodBase(manager.getModel(), quad_coordinates, id,
- memory_id),
+ const ID & id)
+ : NonLocalNeighborhoodBase(manager.getModel(), quad_coordinates, id),
non_local_manager(manager) {
AKANTU_DEBUG_IN();
this->weight_function = std::make_unique<WeightFunction>(manager);
this->registerSubSection(ParserType::_weight_function, "weight_parameter",
*weight_function);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class WeightFunction>
NonLocalNeighborhood<WeightFunction>::~NonLocalNeighborhood() = default;
/* -------------------------------------------------------------------------- */
template <class WeightFunction>
void NonLocalNeighborhood<WeightFunction>::computeWeights() {
AKANTU_DEBUG_IN();
this->weight_function->setRadius(this->neighborhood_radius);
Vector<Real> q1_coord(this->spatial_dimension);
Vector<Real> q2_coord(this->spatial_dimension);
UInt nb_weights_per_pair = 2; /// w1: q1->q2, w2: q2->q1
/// get the elementtypemap for the neighborhood volume for each quadrature
/// point
ElementTypeMapReal & quadrature_points_volumes =
this->non_local_manager.getVolumes();
/// update the internals of the weight function if applicable (not
/// all the weight functions have internals and do noting in that
/// case)
weight_function->updateInternals();
for (auto ghost_type : ghost_types) {
/// allocate the array to store the weight, if it doesn't exist already
if (!(pair_weight[ghost_type])) {
pair_weight[ghost_type] =
std::make_unique<Array<Real>>(0, nb_weights_per_pair);
}
/// resize the array to the correct size
pair_weight[ghost_type]->resize(pair_list[ghost_type].size());
/// set entries to zero
pair_weight[ghost_type]->zero();
/// loop over all pairs in the current pair list array and their
/// corresponding weights
auto first_pair = pair_list[ghost_type].begin();
auto last_pair = pair_list[ghost_type].end();
auto weight_it = pair_weight[ghost_type]->begin(nb_weights_per_pair);
// Compute the weights
for (; first_pair != last_pair; ++first_pair, ++weight_it) {
Vector<Real> & weight = *weight_it;
const IntegrationPoint & q1 = first_pair->first;
const IntegrationPoint & q2 = first_pair->second;
/// get the coordinates for the given pair of quads
auto coords_type_1_it = this->quad_coordinates(q1.type, q1.ghost_type)
.begin(this->spatial_dimension);
q1_coord = coords_type_1_it[q1.global_num];
auto coords_type_2_it = this->quad_coordinates(q2.type, q2.ghost_type)
.begin(this->spatial_dimension);
q2_coord = coords_type_2_it[q2.global_num];
Array<Real> & quad_volumes_1 =
quadrature_points_volumes(q1.type, q1.ghost_type);
const Array<Real> & jacobians_2 =
this->non_local_manager.getJacobians(q2.type, q2.ghost_type);
const Real & q2_wJ = jacobians_2(q2.global_num);
/// compute distance between the two quadrature points
Real r = q1_coord.distance(q2_coord);
/// compute the weight for averaging on q1 based on the distance
Real w1 = this->weight_function->operator()(r, q1, q2);
weight(0) = q2_wJ * w1;
quad_volumes_1(q1.global_num) += weight(0);
if (q2.ghost_type != _ghost && q1.global_num != q2.global_num) {
const Array<Real> & jacobians_1 =
this->non_local_manager.getJacobians(q1.type, q1.ghost_type);
Array<Real> & quad_volumes_2 =
quadrature_points_volumes(q2.type, q2.ghost_type);
/// compute the weight for averaging on q2
const Real & q1_wJ = jacobians_1(q1.global_num);
Real w2 = this->weight_function->operator()(r, q2, q1);
weight(1) = q1_wJ * w2;
quad_volumes_2(q2.global_num) += weight(1);
} else {
weight(1) = 0.;
}
}
}
/// normalize the weights
for (auto ghost_type : ghost_types) {
foreach_weight(ghost_type, [&](const auto & q1, const auto & q2,
auto & weight) {
auto & quad_volumes_1 = quadrature_points_volumes(q1.type, q1.ghost_type);
auto & quad_volumes_2 = quadrature_points_volumes(q2.type, q2.ghost_type);
Real q1_volume = quad_volumes_1(q1.global_num);
auto ghost_type2 = q2.ghost_type;
weight(0) *= 1. / q1_volume;
if (ghost_type2 != _ghost) {
Real q2_volume = quad_volumes_2(q2.global_num);
weight(1) *= 1. / q2_volume;
}
});
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class WeightFunction>
void NonLocalNeighborhood<WeightFunction>::saveWeights(
const std::string & filename) const {
std::ofstream pout;
std::stringstream sstr;
const Communicator & comm = model.getMesh().getCommunicator();
Int prank = comm.whoAmI();
sstr << filename << "." << prank;
pout.open(sstr.str().c_str());
for (UInt gt = _not_ghost; gt <= _ghost; ++gt) {
auto ghost_type = (GhostType)gt;
AKANTU_DEBUG_ASSERT((pair_weight[ghost_type]),
"the weights have not been computed yet");
Array<Real> & weights = *(pair_weight[ghost_type]);
auto weights_it = weights.begin(2);
for (UInt i = 0; i < weights.size(); ++i, ++weights_it) {
pout << "w1: " << (*weights_it)(0) << " w2: " << (*weights_it)(1)
<< std::endl;
}
}
}
/* -------------------------------------------------------------------------- */
template <class WeightFunction>
void NonLocalNeighborhood<WeightFunction>::weightedAverageOnNeighbours(
const ElementTypeMapReal & to_accumulate, ElementTypeMapReal & accumulated,
UInt nb_degree_of_freedom, GhostType ghost_type2) const {
auto it = non_local_variables.find(accumulated.getName());
// do averaging only for variables registered in the neighborhood
if (it == non_local_variables.end()) {
return;
}
foreach_weight(
ghost_type2,
[ghost_type2, nb_degree_of_freedom, &to_accumulate,
&accumulated](const auto & q1, const auto & q2, auto & weight) {
const Vector<Real> to_acc_1 =
to_accumulate(q1.type, q1.ghost_type)
.begin(nb_degree_of_freedom)[q1.global_num];
const Vector<Real> to_acc_2 =
to_accumulate(q2.type, q2.ghost_type)
.begin(nb_degree_of_freedom)[q2.global_num];
Vector<Real> acc_1 = accumulated(q1.type, q1.ghost_type)
.begin(nb_degree_of_freedom)[q1.global_num];
Vector<Real> acc_2 = accumulated(q2.type, q2.ghost_type)
.begin(nb_degree_of_freedom)[q2.global_num];
acc_1 += weight(0) * to_acc_2;
if (ghost_type2 != _ghost) {
acc_2 += weight(1) * to_acc_1;
}
});
}
/* -------------------------------------------------------------------------- */
template <class WeightFunction>
void NonLocalNeighborhood<WeightFunction>::updateWeights() {
// Update the weights for the non local variable averaging
if (this->weight_function->getUpdateRate() &&
(this->non_local_manager.getNbStressCalls() %
this->weight_function->getUpdateRate() ==
0)) {
SynchronizerRegistry::synchronize(SynchronizationTag::_mnl_weight);
this->computeWeights();
}
}
} // namespace akantu
#endif /* __AKANTU_NON_LOCAL_NEIGHBORHOOD_TMPL__ */
diff --git a/src/model/common/time_step_solvers/time_step_solver.cc b/src/model/common/time_step_solvers/time_step_solver.cc
index 3d282b19d..bce3372e4 100644
--- a/src/model/common/time_step_solvers/time_step_solver.cc
+++ b/src/model/common/time_step_solvers/time_step_solver.cc
@@ -1,206 +1,205 @@
/**
* @file time_step_solver.cc
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Tue Aug 18 2015
* @date last modification: Wed Feb 21 2018
*
* @brief Implementation of common part of TimeStepSolvers
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "time_step_solver.hh"
#include "dof_manager.hh"
#include "non_linear_solver.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
TimeStepSolver::TimeStepSolver(DOFManager & dof_manager,
const TimeStepSolverType & type,
NonLinearSolver & non_linear_solver,
- SolverCallback & solver_callback, const ID & id,
- UInt memory_id)
- : Memory(id, memory_id), SolverCallback(dof_manager),
+ SolverCallback & solver_callback, const ID & id)
+ : SolverCallback(dof_manager), id(id),
_dof_manager(dof_manager), type(type), time_step(0.),
solver_callback(&solver_callback), non_linear_solver(non_linear_solver) {
this->registerSubRegistry("non_linear_solver", non_linear_solver);
}
/* -------------------------------------------------------------------------- */
TimeStepSolver::~TimeStepSolver() = default;
/* -------------------------------------------------------------------------- */
void TimeStepSolver::setIntegrationScheme(
const ID & dof_id, const IntegrationSchemeType & type,
IntegrationScheme::SolutionType solution_type) {
auto scheme = this->getIntegrationSchemeInternal(dof_id, type, solution_type);
this->setIntegrationScheme(dof_id, scheme, solution_type);
}
/* -------------------------------------------------------------------------- */
void TimeStepSolver::setIntegrationScheme(
const ID & dof_id, std::unique_ptr<IntegrationScheme> & scheme,
IntegrationScheme::SolutionType solution_type) {
this->setIntegrationSchemeInternal(dof_id, scheme, solution_type);
for (auto & pair : needed_matrices) {
auto & mat_type = pair.second;
const auto & name = pair.first;
if (mat_type == _mt_not_defined) {
mat_type = this->solver_callback->getMatrixType(name);
}
if (mat_type == _mt_not_defined) {
continue;
}
if (not _dof_manager.hasMatrix(name)) {
_dof_manager.getNewMatrix(name, mat_type);
}
}
}
/* -------------------------------------------------------------------------- */
MatrixType TimeStepSolver::getCommonMatrixType() {
MatrixType common_type = _mt_not_defined;
for (auto & pair : needed_matrices) {
auto & type = pair.second;
common_type = std::min(common_type, type);
}
AKANTU_DEBUG_ASSERT(common_type != _mt_not_defined,
"No type defined for the matrices");
return common_type;
}
/* -------------------------------------------------------------------------- */
void TimeStepSolver::predictor() {
AKANTU_DEBUG_ASSERT(
this->solver_callback != nullptr,
"This function cannot be called if the solver_callback is not set");
this->solver_callback->predictor();
}
/* -------------------------------------------------------------------------- */
void TimeStepSolver::corrector() {
AKANTU_DEBUG_ASSERT(
this->solver_callback != nullptr,
"This function cannot be called if the solver_callback is not set");
this->solver_callback->corrector();
}
/* -------------------------------------------------------------------------- */
void TimeStepSolver::beforeSolveStep() {
AKANTU_DEBUG_ASSERT(
this->solver_callback != nullptr,
"This function cannot be called if the solver_callback is not set");
this->solver_callback->beforeSolveStep();
}
/* -------------------------------------------------------------------------- */
void TimeStepSolver::afterSolveStep(bool converged) {
AKANTU_DEBUG_ASSERT(
this->solver_callback != nullptr,
"This function cannot be called if the solver_callback is not set");
this->solver_callback->afterSolveStep(converged);
}
/* -------------------------------------------------------------------------- */
void TimeStepSolver::assembleLumpedMatrix(const ID & matrix_id) {
AKANTU_DEBUG_ASSERT(
this->solver_callback != nullptr,
"This function cannot be called if the solver_callback is not set");
if (not _dof_manager.hasLumpedMatrix(matrix_id)) {
_dof_manager.getNewLumpedMatrix(matrix_id);
}
this->solver_callback->assembleLumpedMatrix(matrix_id);
}
/* -------------------------------------------------------------------------- */
void TimeStepSolver::assembleMatrix(const ID & matrix_id) {
AKANTU_DEBUG_ASSERT(
this->solver_callback != nullptr,
"This function cannot be called if the solver_callback is not set");
auto common_type = this->getCommonMatrixType();
if (matrix_id != "J") {
auto type = needed_matrices[matrix_id];
if (type == _mt_not_defined) {
return;
}
if (not _dof_manager.hasMatrix(matrix_id)) {
_dof_manager.getNewMatrix(matrix_id, type);
}
this->solver_callback->assembleMatrix(matrix_id);
return;
}
if (not _dof_manager.hasMatrix("J")) {
_dof_manager.getNewMatrix("J", common_type);
}
MatrixType type;
ID name;
for (auto & pair : needed_matrices) {
std::tie(name, type) = pair;
if (type == _mt_not_defined) {
continue;
}
this->solver_callback->assembleMatrix(name);
}
}
/* -------------------------------------------------------------------------- */
void TimeStepSolver::assembleResidual() {
AKANTU_DEBUG_ASSERT(
this->solver_callback != nullptr,
"This function cannot be called if the solver_callback is not set");
this->_dof_manager.zeroResidual();
this->solver_callback->assembleResidual();
}
/* -------------------------------------------------------------------------- */
void TimeStepSolver::assembleResidual(const ID & residual_part) {
AKANTU_DEBUG_ASSERT(
this->solver_callback != nullptr,
"This function cannot be called if the solver_callback is not set");
this->solver_callback->assembleResidual(residual_part);
}
/* -------------------------------------------------------------------------- */
} // namespace akantu
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 a525cd235..c9b1a4953 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,164 @@
/**
* @file time_step_solver.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Wed Feb 21 2018
*
* @brief This corresponding to the time step evolution solver
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
-
/* -------------------------------------------------------------------------- */
#include "aka_array.hh"
-#include "aka_memory.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 Memory,
- public ParameterRegistry,
- public SolverCallback {
+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,
- UInt memory_id);
+ 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<IntegrationScheme> & scheme,
IntegrationScheme::SolutionType solution_type =
IntegrationScheme::_not_defined);
protected:
/// register an integration scheme for a given dof
virtual std::unique_ptr<IntegrationScheme>
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<IntegrationScheme> & 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*/) 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() 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<std::string, MatrixType> 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.cc b/src/model/common/time_step_solvers/time_step_solver_default.cc
index 34eb6a64d..55c37f267 100644
--- a/src/model/common/time_step_solvers/time_step_solver_default.cc
+++ b/src/model/common/time_step_solvers/time_step_solver_default.cc
@@ -1,341 +1,340 @@
/**
* @file time_step_solver_default.cc
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Tue Sep 15 2015
* @date last modification: Wed Feb 21 2018
*
* @brief Default implementation of the time step solver
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "time_step_solver_default.hh"
#include "dof_manager_default.hh"
#include "integration_scheme_1st_order.hh"
#include "integration_scheme_2nd_order.hh"
#include "mesh.hh"
#include "non_linear_solver.hh"
#include "pseudo_time.hh"
#include "sparse_matrix_aij.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
TimeStepSolverDefault::TimeStepSolverDefault(
DOFManager & dof_manager, const TimeStepSolverType & type,
NonLinearSolver & non_linear_solver, SolverCallback & solver_callback,
- const ID & id, UInt memory_id)
- : TimeStepSolver(dof_manager, type, non_linear_solver, solver_callback, id,
- memory_id) {
+ const ID & id)
+ : TimeStepSolver(dof_manager, type, non_linear_solver, solver_callback, id) {
switch (type) {
case TimeStepSolverType::_dynamic:
break;
case TimeStepSolverType::_dynamic_lumped:
this->is_mass_lumped = true;
break;
case TimeStepSolverType::_static:
/// initialize a static time solver for callback dofs
break;
default:
AKANTU_TO_IMPLEMENT();
}
}
/* -------------------------------------------------------------------------- */
std::unique_ptr<IntegrationScheme>
TimeStepSolverDefault::getIntegrationSchemeInternal(
const ID & dof_id, const IntegrationSchemeType & type,
IntegrationScheme::SolutionType /*solution_type*/) {
std::unique_ptr<IntegrationScheme> integration_scheme;
if (this->is_mass_lumped) {
switch (type) {
case IntegrationSchemeType::_forward_euler: {
integration_scheme = std::make_unique<ForwardEuler>(_dof_manager, dof_id);
break;
}
case IntegrationSchemeType::_central_difference: {
integration_scheme =
std::make_unique<CentralDifference>(_dof_manager, dof_id);
break;
}
default:
AKANTU_EXCEPTION(
"This integration scheme cannot be used in lumped dynamic");
}
} else {
switch (type) {
case IntegrationSchemeType::_pseudo_time: {
integration_scheme = std::make_unique<PseudoTime>(_dof_manager, dof_id);
break;
}
case IntegrationSchemeType::_forward_euler: {
integration_scheme = std::make_unique<ForwardEuler>(_dof_manager, dof_id);
break;
}
case IntegrationSchemeType::_trapezoidal_rule_1: {
integration_scheme =
std::make_unique<TrapezoidalRule1>(_dof_manager, dof_id);
break;
}
case IntegrationSchemeType::_backward_euler: {
integration_scheme =
std::make_unique<BackwardEuler>(_dof_manager, dof_id);
break;
}
case IntegrationSchemeType::_central_difference: {
integration_scheme =
std::make_unique<CentralDifference>(_dof_manager, dof_id);
break;
}
case IntegrationSchemeType::_fox_goodwin: {
integration_scheme = std::make_unique<FoxGoodwin>(_dof_manager, dof_id);
break;
}
case IntegrationSchemeType::_trapezoidal_rule_2: {
integration_scheme =
std::make_unique<TrapezoidalRule2>(_dof_manager, dof_id);
break;
}
case IntegrationSchemeType::_linear_acceleration: {
integration_scheme =
std::make_unique<LinearAceleration>(_dof_manager, dof_id);
break;
}
case IntegrationSchemeType::_generalized_trapezoidal: {
integration_scheme =
std::make_unique<GeneralizedTrapezoidal>(_dof_manager, dof_id);
break;
}
case IntegrationSchemeType::_newmark_beta:
integration_scheme = std::make_unique<NewmarkBeta>(_dof_manager, dof_id);
break;
}
}
AKANTU_DEBUG_ASSERT(integration_scheme,
"No integration scheme was found for the provided types");
return integration_scheme;
}
/* -------------------------------------------------------------------------- */
void TimeStepSolverDefault::setIntegrationSchemeInternal(
const ID & dof_id, std::unique_ptr<IntegrationScheme> & integration_scheme,
IntegrationScheme::SolutionType solution_type) {
if (this->integration_schemes.find(dof_id) !=
this->integration_schemes.end()) {
AKANTU_EXCEPTION("Their DOFs "
<< dof_id
<< " have already an integration scheme associated");
}
auto && matrices_names = integration_scheme->getNeededMatrixList();
for (auto && name : matrices_names) {
needed_matrices.insert({name, _mt_not_defined});
}
this->integration_schemes[dof_id] = std::move(integration_scheme);
this->solution_types[dof_id] = solution_type;
this->integration_schemes_owner.insert(dof_id);
}
/* -------------------------------------------------------------------------- */
bool TimeStepSolverDefault::hasIntegrationScheme(const ID & dof_id) const {
return this->integration_schemes.find(dof_id) !=
this->integration_schemes.end();
}
/* -------------------------------------------------------------------------- */
TimeStepSolverDefault::~TimeStepSolverDefault() = default;
/* -------------------------------------------------------------------------- */
void TimeStepSolverDefault::solveStep(SolverCallback & solver_callback) {
this->solver_callback = &solver_callback;
this->non_linear_solver.solve(*this);
this->solver_callback = nullptr;
}
/* -------------------------------------------------------------------------- */
void TimeStepSolverDefault::predictor() {
TimeStepSolver::predictor();
for (auto && pair : this->integration_schemes) {
const auto & dof_id = pair.first;
auto & integration_scheme = pair.second;
if (this->_dof_manager.hasPreviousDOFs(dof_id)) {
this->_dof_manager.savePreviousDOFs(dof_id);
}
/// integrator predictor
integration_scheme->predictor(this->time_step);
}
}
/* -------------------------------------------------------------------------- */
void TimeStepSolverDefault::corrector() {
AKANTU_DEBUG_IN();
for (auto & pair : this->integration_schemes) {
const auto & dof_id = pair.first;
auto & integration_scheme = pair.second;
const auto & solution_type = this->solution_types[dof_id];
integration_scheme->corrector(solution_type, this->time_step);
/// computing the increment of dof if needed
if (this->_dof_manager.hasDOFsIncrement(dof_id)) {
if (not this->_dof_manager.hasPreviousDOFs(dof_id)) {
AKANTU_DEBUG_WARNING("In order to compute the increment of "
<< dof_id << " a 'previous' has to be registered");
continue;
}
auto & increment = this->_dof_manager.getDOFsIncrement(dof_id);
auto & previous = this->_dof_manager.getPreviousDOFs(dof_id);
auto dof_array_comp = this->_dof_manager.getDOFs(dof_id).getNbComponent();
increment.copy(this->_dof_manager.getDOFs(dof_id));
for (auto && data : zip(make_view(increment, dof_array_comp),
make_view(previous, dof_array_comp))) {
std::get<0>(data) -= std::get<1>(data);
}
}
}
TimeStepSolver::corrector();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void TimeStepSolverDefault::assembleMatrix(const ID & matrix_id) {
AKANTU_DEBUG_IN();
TimeStepSolver::assembleMatrix(matrix_id);
if (matrix_id != "J") {
return;
}
for_each_integrator([&](auto && dof_id, auto && integration_scheme) {
const auto & solution_type = this->solution_types[dof_id];
integration_scheme.assembleJacobian(solution_type, this->time_step);
});
this->_dof_manager.applyBoundary("J");
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
// void TimeStepSolverDefault::assembleLumpedMatrix(const ID & matrix_id) {
// AKANTU_DEBUG_IN();
// TimeStepSolver::assembleLumpedMatrix(matrix_id);
// if (matrix_id != "J")
// return;
// for (auto & pair : this->integration_schemes) {
// auto & dof_id = pair.first;
// auto & integration_scheme = pair.second;
// const auto & solution_type = this->solution_types[dof_id];
// integration_scheme->assembleJacobianLumped(solution_type,
// this->time_step);
// }
// this->_dof_manager.applyBoundaryLumped("J");
// AKANTU_DEBUG_OUT();
// }
/* -------------------------------------------------------------------------- */
void TimeStepSolverDefault::assembleResidual() {
if (this->needed_matrices.find("M") != needed_matrices.end()) {
if (this->is_mass_lumped) {
this->assembleLumpedMatrix("M");
} else {
this->assembleMatrix("M");
}
}
TimeStepSolver::assembleResidual();
for_each_integrator([&](auto && /*unused*/, auto && integration_scheme) {
integration_scheme.assembleResidual(this->is_mass_lumped);
});
}
/* -------------------------------------------------------------------------- */
void TimeStepSolverDefault::assembleResidual(const ID & residual_part) {
AKANTU_DEBUG_IN();
if (this->needed_matrices.find("M") != needed_matrices.end()) {
if (this->is_mass_lumped) {
this->assembleLumpedMatrix("M");
} else {
this->assembleMatrix("M");
}
}
if (residual_part != "inertial") {
TimeStepSolver::assembleResidual(residual_part);
}
if (residual_part == "inertial") {
for_each_integrator([&](auto && /*unused*/, auto && integration_scheme) {
integration_scheme.assembleResidual(this->is_mass_lumped);
});
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void TimeStepSolverDefault::beforeSolveStep() {
TimeStepSolver::beforeSolveStep();
for_each_integrator([&](auto && /*unused*/, auto && integration_scheme) {
integration_scheme.store();
});
}
/* -------------------------------------------------------------------------- */
void TimeStepSolverDefault::afterSolveStep(bool converged) {
if (not converged) {
for_each_integrator([&](auto && /*unused*/, auto && integration_scheme) {
integration_scheme.restore();
});
}
TimeStepSolver::afterSolveStep(converged);
}
/* -------------------------------------------------------------------------- */
} // namespace akantu
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 3d2b5faac..08faf6c14 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,133 +1,132 @@
/**
* @file time_step_solver_default.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Wed Feb 21 2018
*
* @brief Default implementation for the time stepper
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "integration_scheme.hh"
#include "time_step_solver.hh"
/* -------------------------------------------------------------------------- */
#include <map>
#include <set>
/* -------------------------------------------------------------------------- */
#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,
- UInt memory_id);
+ SolverCallback & solver_callback, const ID & id);
~TimeStepSolverDefault() override;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
protected:
/// registers an integration scheme for a given dof
std::unique_ptr<IntegrationScheme>
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<IntegrationScheme> & 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;
private:
template<class Func>
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<ID, std::unique_ptr<IntegrationScheme>>;
using DOFsIntegrationSchemesSolutionTypes =
std::map<ID, IntegrationScheme::SolutionType>;
using DOFsIntegrationSchemesOwner = std::set<ID>;
/// 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/heat_transfer/heat_transfer_model.cc b/src/model/heat_transfer/heat_transfer_model.cc
index 5b98d3f32..8e3e0c920 100644
--- a/src/model/heat_transfer/heat_transfer_model.cc
+++ b/src/model/heat_transfer/heat_transfer_model.cc
@@ -1,957 +1,917 @@
/**
* @file heat_transfer_model.cc
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author Lucas Frerot <lucas.frerot@epfl.ch>
* @author Emil Gallyamov <emil.gallyamov@epfl.ch>
* @author David Simon Kammer <david.kammer@epfl.ch>
* @author Srinivasa Babu Ramisetti <srinivasa.ramisetti@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
* @author Rui Wang <rui.wang@epfl.ch>
*
* @date creation: Sun May 01 2011
* @date last modification: Tue Feb 20 2018
*
* @brief Implementation of HeatTransferModel class
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "heat_transfer_model.hh"
#include "dumpable_inline_impl.hh"
#include "element_synchronizer.hh"
#include "fe_engine_template.hh"
#include "generalized_trapezoidal.hh"
#include "group_manager_inline_impl.hh"
#include "integrator_gauss.hh"
#include "mesh.hh"
#include "parser.hh"
#include "shape_lagrange.hh"
#ifdef AKANTU_USE_IOHELPER
#include "dumper_element_partition.hh"
#include "dumper_elemental_field.hh"
#include "dumper_internal_material_field.hh"
#include "dumper_iohelper_paraview.hh"
#endif
/* -------------------------------------------------------------------------- */
namespace akantu {
namespace heat_transfer {
namespace details {
class ComputeRhoFunctor {
public:
ComputeRhoFunctor(const HeatTransferModel & model) : model(model){};
void operator()(Matrix<Real> & rho, const Element & /*unused*/) {
rho.set(model.getCapacity() * model.getDensity());
}
private:
const HeatTransferModel & model;
};
} // namespace details
} // namespace heat_transfer
/* -------------------------------------------------------------------------- */
HeatTransferModel::HeatTransferModel(Mesh & mesh, UInt dim, const ID & id,
- const MemoryID & memory_id,
std::shared_ptr<DOFManager> dof_manager)
- : Model(mesh, ModelType::_heat_transfer_model, dof_manager, dim, id, memory_id),
+ : Model(mesh, ModelType::_heat_transfer_model, dof_manager, dim, id),
temperature_gradient("temperature_gradient", id),
temperature_on_qpoints("temperature_on_qpoints", id),
conductivity_on_qpoints("conductivity_on_qpoints", id),
k_gradt_on_qpoints("k_gradt_on_qpoints", id) {
AKANTU_DEBUG_IN();
conductivity = Matrix<Real>(this->spatial_dimension, this->spatial_dimension);
this->registerDataAccessor(*this);
if (this->mesh.isDistributed()) {
auto & synchronizer = this->mesh.getElementSynchronizer();
this->registerSynchronizer(synchronizer,
SynchronizationTag::_htm_temperature);
this->registerSynchronizer(synchronizer,
SynchronizationTag::_htm_gradient_temperature);
}
registerFEEngineObject<FEEngineType>(id + ":fem", mesh, spatial_dimension);
#ifdef AKANTU_USE_IOHELPER
this->mesh.registerDumper<DumperParaview>("heat_transfer", id, true);
this->mesh.addDumpMesh(mesh, spatial_dimension, _not_ghost, _ek_regular);
#endif
this->registerParam("conductivity", conductivity, _pat_parsmod);
this->registerParam("conductivity_variation", conductivity_variation, 0.,
_pat_parsmod);
this->registerParam("temperature_reference", T_ref, 0., _pat_parsmod);
this->registerParam("capacity", capacity, _pat_parsmod);
this->registerParam("density", density, _pat_parsmod);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void HeatTransferModel::initModel() {
auto & fem = this->getFEEngine();
fem.initShapeFunctions(_not_ghost);
fem.initShapeFunctions(_ghost);
temperature_on_qpoints.initialize(fem, _nb_component = 1);
temperature_gradient.initialize(fem, _nb_component = spatial_dimension);
conductivity_on_qpoints.initialize(fem, _nb_component = spatial_dimension *
spatial_dimension);
k_gradt_on_qpoints.initialize(fem, _nb_component = spatial_dimension);
}
/* -------------------------------------------------------------------------- */
FEEngine & HeatTransferModel::getFEEngineBoundary(const ID & name) {
return aka::as_type<FEEngine>(getFEEngineClassBoundary<FEEngineType>(name));
}
-/* -------------------------------------------------------------------------- */
-template <typename T>
-void HeatTransferModel::allocNodalField(Array<T> *& array, const ID & name) {
- if (array == nullptr) {
- UInt nb_nodes = mesh.getNbNodes();
- std::stringstream sstr_disp;
- sstr_disp << id << ":" << name;
-
- array = &(alloc<T>(sstr_disp.str(), nb_nodes, 1, T()));
- }
-}
-
/* -------------------------------------------------------------------------- */
HeatTransferModel::~HeatTransferModel() = default;
/* -------------------------------------------------------------------------- */
void HeatTransferModel::assembleCapacityLumped(GhostType ghost_type) {
AKANTU_DEBUG_IN();
auto & fem = getFEEngineClass<FEEngineType>();
heat_transfer::details::ComputeRhoFunctor compute_rho(*this);
for (auto && type :
mesh.elementTypes(spatial_dimension, ghost_type, _ek_regular)) {
fem.assembleFieldLumped(compute_rho, "M", "temperature",
this->getDOFManager(), type, ghost_type);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
MatrixType HeatTransferModel::getMatrixType(const ID & matrix_id) {
if (matrix_id == "K" or matrix_id == "M") {
return _symmetric;
}
return _mt_not_defined;
}
/* -------------------------------------------------------------------------- */
void HeatTransferModel::assembleMatrix(const ID & matrix_id) {
if (matrix_id == "K") {
this->assembleConductivityMatrix();
} else if (matrix_id == "M" and need_to_reassemble_capacity) {
this->assembleCapacity();
}
}
/* -------------------------------------------------------------------------- */
void HeatTransferModel::assembleLumpedMatrix(const ID & matrix_id) {
if (matrix_id == "M" and need_to_reassemble_capacity) {
this->assembleCapacityLumped();
}
}
/* -------------------------------------------------------------------------- */
void HeatTransferModel::assembleResidual() {
AKANTU_DEBUG_IN();
this->assembleInternalHeatRate();
this->getDOFManager().assembleToResidual("temperature",
*this->external_heat_rate, 1);
this->getDOFManager().assembleToResidual("temperature",
*this->internal_heat_rate, 1);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void HeatTransferModel::predictor() { ++temperature_release; }
/* -------------------------------------------------------------------------- */
void HeatTransferModel::assembleCapacityLumped() {
AKANTU_DEBUG_IN();
if (!this->getDOFManager().hasLumpedMatrix("M")) {
this->getDOFManager().getNewLumpedMatrix("M");
}
this->getDOFManager().zeroLumpedMatrix("M");
assembleCapacityLumped(_not_ghost);
assembleCapacityLumped(_ghost);
need_to_reassemble_capacity_lumped = false;
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void HeatTransferModel::initSolver(TimeStepSolverType time_step_solver_type,
NonLinearSolverType /*unused*/) {
DOFManager & dof_manager = this->getDOFManager();
- this->allocNodalField(this->temperature, "temperature");
- this->allocNodalField(this->external_heat_rate, "external_heat_rate");
- this->allocNodalField(this->internal_heat_rate, "internal_heat_rate");
- this->allocNodalField(this->blocked_dofs, "blocked_dofs");
+ this->allocNodalField(this->temperature, 1, "temperature");
+ this->allocNodalField(this->external_heat_rate, 1, "external_heat_rate");
+ this->allocNodalField(this->internal_heat_rate, 1, "internal_heat_rate");
+ this->allocNodalField(this->blocked_dofs, 1, "blocked_dofs");
if (!dof_manager.hasDOFs("temperature")) {
dof_manager.registerDOFs("temperature", *this->temperature, _dst_nodal);
dof_manager.registerBlockedDOFs("temperature", *this->blocked_dofs);
}
if (time_step_solver_type == TimeStepSolverType::_dynamic ||
time_step_solver_type == TimeStepSolverType::_dynamic_lumped) {
- this->allocNodalField(this->temperature_rate, "temperature_rate");
+ this->allocNodalField(this->temperature_rate, 1, "temperature_rate");
if (!dof_manager.hasDOFsDerivatives("temperature", 1)) {
dof_manager.registerDOFsDerivative("temperature", 1,
*this->temperature_rate);
}
}
}
/* -------------------------------------------------------------------------- */
std::tuple<ID, TimeStepSolverType>
HeatTransferModel::getDefaultSolverID(const AnalysisMethod & method) {
switch (method) {
case _explicit_lumped_mass: {
return std::make_tuple("explicit_lumped",
TimeStepSolverType::_dynamic_lumped);
}
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);
}
}
/* -------------------------------------------------------------------------- */
ModelSolverOptions HeatTransferModel::getDefaultSolverOptions(
const TimeStepSolverType & type) const {
ModelSolverOptions options;
switch (type) {
case TimeStepSolverType::_dynamic_lumped: {
options.non_linear_solver_type = NonLinearSolverType::_lumped;
options.integration_scheme_type["temperature"] =
IntegrationSchemeType::_forward_euler;
options.solution_type["temperature"] = IntegrationScheme::_temperature_rate;
break;
}
case TimeStepSolverType::_static: {
options.non_linear_solver_type = NonLinearSolverType::_newton_raphson;
options.integration_scheme_type["temperature"] =
IntegrationSchemeType::_pseudo_time;
options.solution_type["temperature"] = 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["temperature"] =
IntegrationSchemeType::_forward_euler;
options.solution_type["temperature"] =
IntegrationScheme::_temperature_rate;
} else {
options.non_linear_solver_type = NonLinearSolverType::_newton_raphson;
options.integration_scheme_type["temperature"] =
IntegrationSchemeType::_backward_euler;
options.solution_type["temperature"] = IntegrationScheme::_temperature;
}
break;
}
default:
AKANTU_EXCEPTION(type << " is not a valid time step solver type");
}
return options;
}
/* -------------------------------------------------------------------------- */
void HeatTransferModel::assembleConductivityMatrix() {
AKANTU_DEBUG_IN();
this->computeConductivityOnQuadPoints(_not_ghost);
if (conductivity_release[_not_ghost] == conductivity_matrix_release) {
return;
}
AKANTU_DEBUG_ASSERT(this->getDOFManager().hasMatrix("K"),
"The K matrix has not been initialized yet.");
this->getDOFManager().zeroMatrix("K");
auto & fem = this->getFEEngine();
for (auto && type : mesh.elementTypes(spatial_dimension)) {
auto nb_element = mesh.getNbElement(type);
auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
auto nb_quadrature_points = fem.getNbIntegrationPoints(type);
auto bt_d_b = std::make_unique<Array<Real>>(
nb_element * nb_quadrature_points,
nb_nodes_per_element * nb_nodes_per_element, "B^t*D*B");
fem.computeBtDB(conductivity_on_qpoints(type), *bt_d_b, 2, type);
/// compute @f$ k_e = \int_e \mathbf{B}^t * \mathbf{D} * \mathbf{B}@f$
auto K_e = std::make_unique<Array<Real>>(
nb_element, nb_nodes_per_element * nb_nodes_per_element, "K_e");
fem.integrate(*bt_d_b, *K_e, nb_nodes_per_element * nb_nodes_per_element,
type);
this->getDOFManager().assembleElementalMatricesToMatrix(
"K", "temperature", *K_e, type, _not_ghost, _symmetric);
}
conductivity_matrix_release = conductivity_release[_not_ghost];
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void HeatTransferModel::computeConductivityOnQuadPoints(GhostType ghost_type) {
// if already computed once check if need to compute
if (not initial_conductivity[ghost_type]) {
// if temperature did not change, conductivity will not vary
if (temperature_release == conductivity_release[ghost_type]) {
return;
}
// if conductivity_variation is 0 no need to recompute
if (conductivity_variation == 0.) {
return;
}
}
for (auto && type :
mesh.elementTypes(spatial_dimension, ghost_type, _ek_regular)) {
auto & temperature_interpolated = temperature_on_qpoints(type, ghost_type);
// compute the temperature on quadrature points
this->getFEEngine().interpolateOnIntegrationPoints(
*temperature, temperature_interpolated, 1, type, ghost_type);
auto & cond = conductivity_on_qpoints(type, ghost_type);
for (auto && tuple :
zip(make_view(cond, spatial_dimension, spatial_dimension),
temperature_interpolated)) {
auto & C = std::get<0>(tuple);
auto & T = std::get<1>(tuple);
C = conductivity;
Matrix<Real> variation(spatial_dimension, spatial_dimension,
conductivity_variation * (T - T_ref));
// @TODO: Guillaume are you sure ? why due you compute variation then ?
C += conductivity_variation;
}
}
conductivity_release[ghost_type] = temperature_release;
initial_conductivity[ghost_type] = false;
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void HeatTransferModel::computeKgradT(GhostType ghost_type) {
computeConductivityOnQuadPoints(ghost_type);
for (auto && type :
mesh.elementTypes(spatial_dimension, ghost_type, _ek_regular)) {
auto & gradient = temperature_gradient(type, ghost_type);
this->getFEEngine().gradientOnIntegrationPoints(*temperature, gradient, 1,
type, ghost_type);
for (auto && values :
zip(make_view(conductivity_on_qpoints(type, ghost_type),
spatial_dimension, spatial_dimension),
make_view(gradient, spatial_dimension),
make_view(k_gradt_on_qpoints(type, ghost_type),
spatial_dimension))) {
const auto & C = std::get<0>(values);
const auto & BT = std::get<1>(values);
auto & k_BT = std::get<2>(values);
k_BT.mul<false>(C, BT);
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void HeatTransferModel::assembleInternalHeatRate() {
AKANTU_DEBUG_IN();
this->internal_heat_rate->zero();
this->synchronize(SynchronizationTag::_htm_temperature);
auto & fem = this->getFEEngine();
for (auto ghost_type : ghost_types) {
// compute k \grad T
computeKgradT(ghost_type);
for (auto type :
mesh.elementTypes(spatial_dimension, ghost_type, _ek_regular)) {
UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
auto & k_gradt_on_qpoints_vect = k_gradt_on_qpoints(type, ghost_type);
UInt nb_quad_points = k_gradt_on_qpoints_vect.size();
Array<Real> bt_k_gT(nb_quad_points, nb_nodes_per_element);
fem.computeBtD(k_gradt_on_qpoints_vect, bt_k_gT, type, ghost_type);
UInt nb_elements = mesh.getNbElement(type, ghost_type);
Array<Real> int_bt_k_gT(nb_elements, nb_nodes_per_element);
fem.integrate(bt_k_gT, int_bt_k_gT, nb_nodes_per_element, type,
ghost_type);
this->getDOFManager().assembleElementalArrayLocalArray(
int_bt_k_gT, *this->internal_heat_rate, type, ghost_type, -1);
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
Real HeatTransferModel::getStableTimeStep() {
AKANTU_DEBUG_IN();
Real el_size;
Real min_el_size = std::numeric_limits<Real>::max();
Real conductivitymax = conductivity(0, 0);
// get the biggest parameter from k11 until k33//
for (UInt i = 0; i < spatial_dimension; i++) {
for (UInt j = 0; j < spatial_dimension; j++) {
conductivitymax = std::max(conductivity(i, j), conductivitymax);
}
}
for (auto && type :
mesh.elementTypes(spatial_dimension, _not_ghost, _ek_regular)) {
UInt nb_nodes_per_element = mesh.getNbNodesPerElement(type);
Array<Real> coord(0, nb_nodes_per_element * spatial_dimension);
FEEngine::extractNodalToElementField(mesh, mesh.getNodes(), coord, type,
_not_ghost);
auto el_coord = coord.begin(spatial_dimension, nb_nodes_per_element);
UInt nb_element = mesh.getNbElement(type);
for (UInt el = 0; el < nb_element; ++el, ++el_coord) {
el_size = getFEEngine().getElementInradius(*el_coord, type);
min_el_size = std::min(min_el_size, el_size);
}
AKANTU_DEBUG_INFO("The minimum element size : "
<< min_el_size
<< " and the max conductivity is : " << conductivitymax);
}
Real min_dt = 2. * min_el_size * min_el_size / 4. * density * capacity /
conductivitymax;
mesh.getCommunicator().allReduce(min_dt, SynchronizerOperation::_min);
AKANTU_DEBUG_OUT();
return min_dt;
}
/* -------------------------------------------------------------------------- */
void HeatTransferModel::setTimeStep(Real time_step, const ID & solver_id) {
Model::setTimeStep(time_step, solver_id);
#if defined(AKANTU_USE_IOHELPER)
this->mesh.getDumper("heat_transfer").setTimeStep(time_step);
#endif
}
/* -------------------------------------------------------------------------- */
void HeatTransferModel::readMaterials() {
auto sect = this->getParserSection();
if (not std::get<1>(sect)) {
const auto & section = std::get<0>(sect);
this->parseSection(section);
}
conductivity_on_qpoints.set(conductivity);
}
/* -------------------------------------------------------------------------- */
void HeatTransferModel::initFullImpl(const ModelOptions & options) {
Model::initFullImpl(options);
readMaterials();
}
/* -------------------------------------------------------------------------- */
void HeatTransferModel::assembleCapacity() {
AKANTU_DEBUG_IN();
auto ghost_type = _not_ghost;
this->getDOFManager().zeroMatrix("M");
auto & fem = getFEEngineClass<FEEngineType>();
heat_transfer::details::ComputeRhoFunctor rho_functor(*this);
for (auto && type :
mesh.elementTypes(spatial_dimension, ghost_type, _ek_regular)) {
fem.assembleFieldMatrix(rho_functor, "M", "temperature",
this->getDOFManager(), type, ghost_type);
}
need_to_reassemble_capacity = false;
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void HeatTransferModel::computeRho(Array<Real> & rho, ElementType type,
GhostType ghost_type) {
AKANTU_DEBUG_IN();
FEEngine & fem = this->getFEEngine();
UInt nb_element = mesh.getNbElement(type, ghost_type);
UInt nb_quadrature_points = fem.getNbIntegrationPoints(type, ghost_type);
rho.resize(nb_element * nb_quadrature_points);
rho.set(this->capacity);
// Real * rho_1_val = rho.storage();
// /// compute @f$ rho @f$ for each nodes of each element
// for (UInt el = 0; el < nb_element; ++el) {
// for (UInt n = 0; n < nb_quadrature_points; ++n) {
// *rho_1_val++ = this->capacity;
// }
// }
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
Real HeatTransferModel::computeThermalEnergyByNode() {
AKANTU_DEBUG_IN();
Real ethermal = 0.;
for (auto && pair : enumerate(make_view(
*internal_heat_rate, internal_heat_rate->getNbComponent()))) {
auto n = std::get<0>(pair);
auto & heat_rate = std::get<1>(pair);
Real heat = 0.;
bool is_local_node = mesh.isLocalOrMasterNode(n);
bool count_node = is_local_node;
for (UInt i = 0; i < heat_rate.size(); ++i) {
if (count_node) {
heat += heat_rate[i] * time_step;
}
}
ethermal += heat;
}
mesh.getCommunicator().allReduce(ethermal, SynchronizerOperation::_sum);
AKANTU_DEBUG_OUT();
return ethermal;
}
/* -------------------------------------------------------------------------- */
template <class iterator>
void HeatTransferModel::getThermalEnergy(
iterator Eth, Array<Real>::const_iterator<Real> T_it,
const Array<Real>::const_iterator<Real> & T_end) const {
for (; T_it != T_end; ++T_it, ++Eth) {
*Eth = capacity * density * *T_it;
}
}
/* -------------------------------------------------------------------------- */
Real HeatTransferModel::getThermalEnergy(ElementType type, UInt index) {
AKANTU_DEBUG_IN();
UInt nb_quadrature_points = getFEEngine().getNbIntegrationPoints(type);
Vector<Real> Eth_on_quarature_points(nb_quadrature_points);
auto T_it = this->temperature_on_qpoints(type).begin();
T_it += index * nb_quadrature_points;
auto T_end = T_it + nb_quadrature_points;
getThermalEnergy(Eth_on_quarature_points.storage(), T_it, T_end);
return getFEEngine().integrate(Eth_on_quarature_points, type, index);
}
/* -------------------------------------------------------------------------- */
Real HeatTransferModel::getThermalEnergy() {
Real Eth = 0;
auto & fem = getFEEngine();
for (auto && type :
mesh.elementTypes(spatial_dimension, _not_ghost, _ek_regular)) {
auto nb_element = mesh.getNbElement(type, _not_ghost);
auto nb_quadrature_points = fem.getNbIntegrationPoints(type, _not_ghost);
Array<Real> Eth_per_quad(nb_element * nb_quadrature_points, 1);
auto & temperature_interpolated = temperature_on_qpoints(type);
// compute the temperature on quadrature points
this->getFEEngine().interpolateOnIntegrationPoints(
*temperature, temperature_interpolated, 1, type);
auto T_it = temperature_interpolated.begin();
auto T_end = temperature_interpolated.end();
getThermalEnergy(Eth_per_quad.begin(), T_it, T_end);
Eth += fem.integrate(Eth_per_quad, type);
}
return Eth;
}
/* -------------------------------------------------------------------------- */
Real HeatTransferModel::getEnergy(const std::string & id) {
AKANTU_DEBUG_IN();
Real energy = 0;
if (id == "thermal") {
energy = getThermalEnergy();
}
// reduction sum over all processors
mesh.getCommunicator().allReduce(energy, SynchronizerOperation::_sum);
AKANTU_DEBUG_OUT();
return energy;
}
/* -------------------------------------------------------------------------- */
Real HeatTransferModel::getEnergy(const std::string & id, ElementType type,
UInt index) {
AKANTU_DEBUG_IN();
Real energy = 0.;
if (id == "thermal") {
energy = getThermalEnergy(type, index);
}
AKANTU_DEBUG_OUT();
return energy;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
#ifdef AKANTU_USE_IOHELPER
std::shared_ptr<dumpers::Field> HeatTransferModel::createNodalFieldBool(
const std::string & field_name, const std::string & group_name,
__attribute__((unused)) bool padding_flag) {
std::map<std::string, Array<bool> *> uint_nodal_fields;
- uint_nodal_fields["blocked_dofs"] = blocked_dofs;
+ uint_nodal_fields["blocked_dofs"] = blocked_dofs.get();
auto field = mesh.createNodalField(uint_nodal_fields[field_name], group_name);
return field;
}
/* -------------------------------------------------------------------------- */
std::shared_ptr<dumpers::Field> HeatTransferModel::createNodalFieldReal(
const std::string & field_name, const std::string & group_name,
__attribute__((unused)) bool padding_flag) {
if (field_name == "capacity_lumped") {
AKANTU_EXCEPTION(
"Capacity lumped is a nodal field now stored in the DOF manager."
"Therefore it cannot be used by a dumper anymore");
}
std::map<std::string, Array<Real> *> real_nodal_fields;
- real_nodal_fields["temperature"] = temperature;
- real_nodal_fields["temperature_rate"] = temperature_rate;
- real_nodal_fields["external_heat_rate"] = external_heat_rate;
- real_nodal_fields["internal_heat_rate"] = internal_heat_rate;
- real_nodal_fields["increment"] = increment;
+ real_nodal_fields["temperature"] = temperature.get();
+ real_nodal_fields["temperature_rate"] = temperature_rate.get();
+ real_nodal_fields["external_heat_rate"] = external_heat_rate.get();
+ real_nodal_fields["internal_heat_rate"] = internal_heat_rate.get();
+ real_nodal_fields["increment"] = increment.get();
std::shared_ptr<dumpers::Field> field =
mesh.createNodalField(real_nodal_fields[field_name], group_name);
return field;
}
/* -------------------------------------------------------------------------- */
std::shared_ptr<dumpers::Field> HeatTransferModel::createElementalField(
const std::string & field_name, const std::string & group_name,
bool /*padding_flag*/, UInt /*spatial_dimension*/,
ElementKind element_kind) {
std::shared_ptr<dumpers::Field> field;
if (field_name == "partitions") {
field = mesh.createElementalField<UInt, dumpers::ElementPartitionField>(
mesh.getConnectivities(), group_name, this->spatial_dimension,
element_kind);
} else if (field_name == "temperature_gradient") {
ElementTypeMap<UInt> nb_data_per_elem =
this->mesh.getNbDataPerElem(temperature_gradient);
field = mesh.createElementalField<Real, dumpers::InternalMaterialField>(
temperature_gradient, group_name, this->spatial_dimension, element_kind,
nb_data_per_elem);
} else if (field_name == "conductivity") {
ElementTypeMap<UInt> nb_data_per_elem =
this->mesh.getNbDataPerElem(conductivity_on_qpoints);
field = mesh.createElementalField<Real, dumpers::InternalMaterialField>(
conductivity_on_qpoints, group_name, this->spatial_dimension,
element_kind, nb_data_per_elem);
}
return field;
}
/* -------------------------------------------------------------------------- */
#else
/* -------------------------------------------------------------------------- */
std::shared_ptr<dumpers::Field> HeatTransferModel::createElementalField(
- __attribute__((unused)) const std::string & field_name,
- __attribute__((unused)) const std::string & group_name,
- __attribute__((unused)) bool padding_flag,
- __attribute__((unused)) ElementKind element_kind) {
+ const std::string & /* field_name*/, const std::string & /*group_name*/,
+ bool /*padding_flag*/, ElementKind /*element_kind*/) {
return nullptr;
}
/* -------------------------------------------------------------------------- */
-std::shared_ptr<dumpers::Field> HeatTransferModel::createNodalFieldBool(
- __attribute__((unused)) const std::string & field_name,
- __attribute__((unused)) const std::string & group_name,
- __attribute__((unused)) bool padding_flag) {
+std::shared_ptr<dumpers::Field>
+HeatTransferModel::createNodalFieldBool(const std::string & /*field_name*/,
+ const std::string & /*group_name*/,
+ bool /*padding_flag*/) {
return nullptr;
}
/* -------------------------------------------------------------------------- */
-std::shared_ptr<dumpers::Field> HeatTransferModel::createNodalFieldReal(
- __attribute__((unused)) const std::string & field_name,
- __attribute__((unused)) const std::string & group_name,
- __attribute__((unused)) bool padding_flag) {
+std::shared_ptr<dumpers::Field>
+HeatTransferModel::createNodalFieldReal(const std::string & /*field_name*/,
+ const std::string & /*group_name*/,
+ bool /*padding_flag*/) {
return nullptr;
}
#endif
-/* -------------------------------------------------------------------------- */
-void HeatTransferModel::dump(const std::string & dumper_name) {
- mesh.dump(dumper_name);
-}
-
-/* -------------------------------------------------------------------------- */
-void HeatTransferModel::dump(const std::string & dumper_name, UInt step) {
- mesh.dump(dumper_name, step);
-}
-
-/* ------------------------------------------------------------------------- */
-void HeatTransferModel::dump(const std::string & dumper_name, Real time,
- UInt step) {
- mesh.dump(dumper_name, time, step);
-}
-
-/* -------------------------------------------------------------------------- */
-void HeatTransferModel::dump() { mesh.dump(); }
-
-/* -------------------------------------------------------------------------- */
-void HeatTransferModel::dump(UInt step) { mesh.dump(step); }
-
-/* -------------------------------------------------------------------------- */
-void HeatTransferModel::dump(Real time, UInt step) { mesh.dump(time, step); }
-
/* -------------------------------------------------------------------------- */
inline UInt HeatTransferModel::getNbData(const Array<UInt> & indexes,
const SynchronizationTag & tag) const {
AKANTU_DEBUG_IN();
UInt size = 0;
UInt nb_nodes = indexes.size();
switch (tag) {
case SynchronizationTag::_htm_temperature: {
size += nb_nodes * sizeof(Real);
break;
}
default: {
AKANTU_ERROR("Unknown ghost synchronization tag : " << tag);
}
}
AKANTU_DEBUG_OUT();
return size;
}
/* -------------------------------------------------------------------------- */
inline void HeatTransferModel::packData(CommunicationBuffer & buffer,
const Array<UInt> & indexes,
const SynchronizationTag & tag) const {
AKANTU_DEBUG_IN();
for (auto index : indexes) {
switch (tag) {
case SynchronizationTag::_htm_temperature: {
buffer << (*temperature)(index);
break;
}
default: {
AKANTU_ERROR("Unknown ghost synchronization tag : " << tag);
}
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
inline void HeatTransferModel::unpackData(CommunicationBuffer & buffer,
const Array<UInt> & indexes,
const SynchronizationTag & tag) {
AKANTU_DEBUG_IN();
for (auto index : indexes) {
switch (tag) {
case SynchronizationTag::_htm_temperature: {
buffer >> (*temperature)(index);
break;
}
default: {
AKANTU_ERROR("Unknown ghost synchronization tag : " << tag);
}
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
inline UInt HeatTransferModel::getNbData(const Array<Element> & elements,
const SynchronizationTag & tag) const {
AKANTU_DEBUG_IN();
UInt size = 0;
UInt nb_nodes_per_element = 0;
Array<Element>::const_iterator<Element> it = elements.begin();
Array<Element>::const_iterator<Element> end = elements.end();
for (; it != end; ++it) {
const Element & el = *it;
nb_nodes_per_element += Mesh::getNbNodesPerElement(el.type);
}
switch (tag) {
case SynchronizationTag::_htm_temperature: {
size += nb_nodes_per_element * sizeof(Real); // temperature
break;
}
case SynchronizationTag::_htm_gradient_temperature: {
// temperature gradient
size += getNbIntegrationPoints(elements) * spatial_dimension * sizeof(Real);
size += nb_nodes_per_element * sizeof(Real); // nodal temperatures
break;
}
default: {
AKANTU_ERROR("Unknown ghost synchronization tag : " << tag);
}
}
AKANTU_DEBUG_OUT();
return size;
}
/* -------------------------------------------------------------------------- */
inline void HeatTransferModel::packData(CommunicationBuffer & buffer,
const Array<Element> & elements,
const SynchronizationTag & tag) const {
switch (tag) {
case SynchronizationTag::_htm_temperature: {
packNodalDataHelper(*temperature, buffer, elements, mesh);
break;
}
case SynchronizationTag::_htm_gradient_temperature: {
packElementalDataHelper(temperature_gradient, buffer, elements, true,
getFEEngine());
packNodalDataHelper(*temperature, buffer, elements, mesh);
break;
}
default: {
AKANTU_ERROR("Unknown ghost synchronization tag : " << tag);
}
}
}
/* -------------------------------------------------------------------------- */
inline void HeatTransferModel::unpackData(CommunicationBuffer & buffer,
const Array<Element> & elements,
const SynchronizationTag & tag) {
switch (tag) {
case SynchronizationTag::_htm_temperature: {
unpackNodalDataHelper(*temperature, buffer, elements, mesh);
break;
}
case SynchronizationTag::_htm_gradient_temperature: {
unpackElementalDataHelper(temperature_gradient, buffer, elements, true,
getFEEngine());
unpackNodalDataHelper(*temperature, buffer, elements, mesh);
break;
}
default: {
AKANTU_ERROR("Unknown ghost synchronization tag : " << tag);
}
}
}
/* -------------------------------------------------------------------------- */
} // namespace akantu
diff --git a/src/model/heat_transfer/heat_transfer_model.hh b/src/model/heat_transfer/heat_transfer_model.hh
index ae9c47c0e..2c43de151 100644
--- a/src/model/heat_transfer/heat_transfer_model.hh
+++ b/src/model/heat_transfer/heat_transfer_model.hh
@@ -1,335 +1,316 @@
/**
* @file heat_transfer_model.hh
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author Lucas Frerot <lucas.frerot@epfl.ch>
* @author Srinivasa Babu Ramisetti <srinivasa.ramisetti@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
* @author Rui Wang <rui.wang@epfl.ch>
*
* @date creation: Sun May 01 2011
* @date last modification: Mon Feb 05 2018
*
* @brief Model of Heat Transfer
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "data_accessor.hh"
#include "fe_engine.hh"
#include "model.hh"
/* -------------------------------------------------------------------------- */
#include <array>
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_HEAT_TRANSFER_MODEL_HH_
#define AKANTU_HEAT_TRANSFER_MODEL_HH_
namespace akantu {
template <ElementKind kind, class IntegrationOrderFunctor>
class IntegratorGauss;
template <ElementKind kind> class ShapeLagrange;
} // namespace akantu
namespace akantu {
class HeatTransferModel : public Model,
public DataAccessor<Element>,
public DataAccessor<UInt> {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
using FEEngineType = FEEngineTemplate<IntegratorGauss, ShapeLagrange>;
HeatTransferModel(Mesh & mesh, UInt dim = _all_dimensions,
const ID & id = "heat_transfer_model",
- const MemoryID & memory_id = 0,
std::shared_ptr<DOFManager> dof_manager = nullptr);
~HeatTransferModel() override;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
protected:
/// generic function to initialize everything ready for explicit dynamics
void initFullImpl(const ModelOptions & options) override;
/// read one material file to instantiate all the materials
void readMaterials();
/// allocate all vectors
void initSolver(TimeStepSolverType time_step_solver_type,
NonLinearSolverType non_linear_solver_type) override;
/// initialize the model
void initModel() override;
void predictor() override;
/// compute the heat flux
void assembleResidual() override;
/// get the type of matrix needed
MatrixType getMatrixType(const ID & matrix_id) 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;
std::tuple<ID, TimeStepSolverType>
getDefaultSolverID(const AnalysisMethod & method) override;
ModelSolverOptions
getDefaultSolverOptions(const TimeStepSolverType & type) const override;
/* ------------------------------------------------------------------------ */
/* Methods for explicit */
/* ------------------------------------------------------------------------ */
public:
/// compute and get the stable time step
Real getStableTimeStep();
/// set the stable timestep
void setTimeStep(Real time_step, const ID & solver_id = "") override;
// temporary protection to prevent bad usage: should check for bug
protected:
/// compute the internal heat flux \todo Need code review: currently not
/// public method
void assembleInternalHeatRate();
public:
/// calculate the lumped capacity vector for heat transfer problem
void assembleCapacityLumped();
public:
/// assemble the conductivity matrix
void assembleConductivityMatrix();
/// assemble the conductivity matrix
void assembleCapacity();
/// compute the capacity on quadrature points
void computeRho(Array<Real> & rho, ElementType type, GhostType ghost_type);
private:
/// calculate the lumped capacity vector for heat transfer problem (w
/// ghost type)
void assembleCapacityLumped(GhostType ghost_type);
/// compute the conductivity tensor for each quadrature point in an array
void computeConductivityOnQuadPoints(GhostType ghost_type);
/// compute vector \f[k \grad T\f] for each quadrature point
void computeKgradT(GhostType ghost_type);
/// compute the thermal energy
Real computeThermalEnergyByNode();
/* ------------------------------------------------------------------------ */
/* Data Accessor inherited members */
/* ------------------------------------------------------------------------ */
public:
inline UInt getNbData(const Array<Element> & elements,
const SynchronizationTag & tag) const override;
inline void packData(CommunicationBuffer & buffer,
const Array<Element> & elements,
const SynchronizationTag & tag) const override;
inline void unpackData(CommunicationBuffer & buffer,
const Array<Element> & elements,
const SynchronizationTag & tag) override;
inline UInt getNbData(const Array<UInt> & indexes,
const SynchronizationTag & tag) const override;
inline void packData(CommunicationBuffer & buffer,
const Array<UInt> & indexes,
const SynchronizationTag & tag) const override;
inline void unpackData(CommunicationBuffer & buffer,
const Array<UInt> & indexes,
const SynchronizationTag & tag) override;
/* ------------------------------------------------------------------------ */
/* Dumpable interface */
/* ------------------------------------------------------------------------ */
public:
std::shared_ptr<dumpers::Field>
createNodalFieldReal(const std::string & field_name,
const std::string & group_name,
bool padding_flag) override;
std::shared_ptr<dumpers::Field>
createNodalFieldBool(const std::string & field_name,
const std::string & group_name,
bool padding_flag) override;
std::shared_ptr<dumpers::Field>
createElementalField(const std::string & field_name,
const std::string & group_name, bool padding_flag,
UInt spatial_dimension, ElementKind kind) override;
- virtual void dump(const std::string & dumper_name);
-
- virtual void dump(const std::string & dumper_name, UInt step);
-
- virtual void dump(const std::string & dumper_name, Real time, UInt step);
-
- void dump() override;
-
- virtual void dump(UInt step);
-
- virtual void dump(Real time, UInt step);
-
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
AKANTU_GET_MACRO(Density, density, Real);
AKANTU_GET_MACRO(Capacity, capacity, Real);
/// get the dimension of the system space
AKANTU_GET_MACRO(SpatialDimension, spatial_dimension, UInt);
/// get the current value of the time step
AKANTU_GET_MACRO(TimeStep, time_step, Real);
/// get the assembled heat flux
AKANTU_GET_MACRO(InternalHeatRate, *internal_heat_rate, Array<Real> &);
/// get the boundary vector
AKANTU_GET_MACRO(BlockedDOFs, *blocked_dofs, Array<bool> &);
/// get the external heat rate vector
AKANTU_GET_MACRO(ExternalHeatRate, *external_heat_rate, Array<Real> &);
/// get the temperature gradient
AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(TemperatureGradient,
temperature_gradient, Real);
/// get the conductivity on q points
AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(ConductivityOnQpoints,
conductivity_on_qpoints, Real);
/// get the conductivity on q points
AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(TemperatureOnQpoints,
temperature_on_qpoints, Real);
/// internal variables
AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(KgradT, k_gradt_on_qpoints, Real);
/// get the temperature
AKANTU_GET_MACRO(Temperature, *temperature, Array<Real> &);
/// get the temperature derivative
AKANTU_GET_MACRO(TemperatureRate, *temperature_rate, Array<Real> &);
/// get the energy denominated by thermal
Real getEnergy(const std::string & energy_id, ElementType type, UInt index);
/// get the energy denominated by thermal
Real getEnergy(const std::string & energy_id);
/// get the thermal energy for a given element
Real getThermalEnergy(ElementType type, UInt index);
/// get the thermal energy for a given element
Real getThermalEnergy();
protected:
/* ------------------------------------------------------------------------ */
FEEngine & getFEEngineBoundary(const ID & name = "") override;
/* ----------------------------------------------------------------------- */
template <class iterator>
void getThermalEnergy(iterator Eth, Array<Real>::const_iterator<Real> T_it,
const Array<Real>::const_iterator<Real> & T_end) const;
- template <typename T>
- void allocNodalField(Array<T> *& array, const ID & name);
-
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
private:
- /// number of iterations
- // UInt n_iter;
-
/// time step
Real time_step;
/// temperatures array
- Array<Real> * temperature{nullptr};
+ std::unique_ptr<Array<Real>> temperature;
/// temperatures derivatives array
- Array<Real> * temperature_rate{nullptr};
+ std::unique_ptr<Array<Real>> temperature_rate;
/// increment array (@f$\delta \dot T@f$ or @f$\delta T@f$)
- Array<Real> * increment{nullptr};
+ std::unique_ptr<Array<Real>> increment;
/// the density
Real density;
/// the speed of the changing temperature
ElementTypeMapArray<Real> temperature_gradient;
/// temperature field on quadrature points
ElementTypeMapArray<Real> temperature_on_qpoints;
/// conductivity tensor on quadrature points
ElementTypeMapArray<Real> conductivity_on_qpoints;
/// vector \f[k \grad T\f] on quad points
ElementTypeMapArray<Real> k_gradt_on_qpoints;
/// external flux vector
- Array<Real> * external_heat_rate{nullptr};
+ std::unique_ptr<Array<Real>> external_heat_rate;
/// residuals array
- Array<Real> * internal_heat_rate{nullptr};
+ std::unique_ptr<Array<Real>> internal_heat_rate;
/// boundary vector
- Array<bool> * blocked_dofs{nullptr};
+ std::unique_ptr<Array<bool>> blocked_dofs;
// realtime
// Real time;
/// capacity
Real capacity;
// conductivity matrix
Matrix<Real> conductivity;
// linear variation of the conductivity (for temperature dependent
// conductivity)
Real conductivity_variation;
// reference temperature for the interpretation of temperature variation
Real T_ref;
// the biggest parameter of conductivity matrix
// Real conductivitymax;
bool need_to_reassemble_capacity{true};
bool need_to_reassemble_capacity_lumped{true};
UInt temperature_release{0};
UInt conductivity_matrix_release{UInt(-1)};
std::unordered_map<GhostType, bool> initial_conductivity{{_not_ghost, true},
{_ghost, true}};
std::unordered_map<GhostType, UInt> conductivity_release{{_not_ghost, 0},
{_ghost, 0}};
};
} // namespace akantu
/* -------------------------------------------------------------------------- */
/* inline functions */
/* -------------------------------------------------------------------------- */
#include "heat_transfer_model_inline_impl.hh"
#endif /* AKANTU_HEAT_TRANSFER_MODEL_HH_ */
diff --git a/src/model/model.cc b/src/model/model.cc
index 02c111a8e..0615c45bb 100644
--- a/src/model/model.cc
+++ b/src/model/model.cc
@@ -1,349 +1,372 @@
/**
* @file model.cc
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author David Simon Kammer <david.kammer@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Mon Oct 03 2011
* @date last modification: Tue Feb 20 2018
*
* @brief implementation of model common parts
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "model.hh"
#include "communicator.hh"
#include "data_accessor.hh"
#include "element_group.hh"
#include "element_synchronizer.hh"
#include "synchronizer_registry.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
Model::Model(Mesh & mesh, const ModelType & type,
- std::shared_ptr<DOFManager> dof_manager, UInt dim, const ID & id,
- const MemoryID & memory_id)
- : Memory(id, memory_id),
- ModelSolver(mesh, type, id, memory_id, std::move(dof_manager)),
+ std::shared_ptr<DOFManager> dof_manager, UInt dim, const ID & id)
+ : ModelSolver(mesh, type, id, std::move(dof_manager)),
mesh(mesh),
spatial_dimension(dim == _all_dimensions ? mesh.getSpatialDimension()
: dim),
parser(getStaticParser()) {
this->mesh.registerEventHandler(*this, _ehp_model);
}
/* -------------------------------------------------------------------------- */
-Model::Model(Mesh & mesh, const ModelType & type, UInt dim, const ID & id,
- const MemoryID & memory_id)
- : Memory(id, memory_id), ModelSolver(mesh, type, id, memory_id), mesh(mesh),
+Model::Model(Mesh & mesh, const ModelType & type, UInt dim, const ID & id)
+ : ModelSolver(mesh, type, id), mesh(mesh),
spatial_dimension(dim == _all_dimensions ? mesh.getSpatialDimension()
: dim),
parser(getStaticParser()) {
this->mesh.registerEventHandler(*this, _ehp_model);
}
/* -------------------------------------------------------------------------- */
Model::~Model() = default;
/* -------------------------------------------------------------------------- */
void Model::initFullImpl(const ModelOptions & options) {
AKANTU_DEBUG_IN();
method = options.analysis_method;
if (!this->hasDefaultSolver()) {
this->initNewSolver(this->method);
}
initModel();
initFEEngineBoundary();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void Model::initNewSolver(const AnalysisMethod & method) {
ID solver_name;
TimeStepSolverType tss_type;
std::tie(solver_name, tss_type) = this->getDefaultSolverID(method);
if (not this->hasSolver(solver_name)) {
ModelSolverOptions options = this->getDefaultSolverOptions(tss_type);
this->getNewSolver(solver_name, tss_type, options.non_linear_solver_type);
for (auto && is_type : options.integration_scheme_type) {
if (!this->hasIntegrationScheme(solver_name, is_type.first)) {
this->setIntegrationScheme(solver_name, is_type.first, is_type.second,
options.solution_type[is_type.first]);
}
}
}
this->method = method;
this->setDefaultSolver(solver_name);
}
/* -------------------------------------------------------------------------- */
void Model::initFEEngineBoundary() {
FEEngine & fem_boundary = getFEEngineBoundary();
fem_boundary.initShapeFunctions(_not_ghost);
fem_boundary.initShapeFunctions(_ghost);
fem_boundary.computeNormalsOnIntegrationPoints(_not_ghost);
fem_boundary.computeNormalsOnIntegrationPoints(_ghost);
}
/* -------------------------------------------------------------------------- */
void Model::dumpGroup(const std::string & group_name) {
ElementGroup & group = mesh.getElementGroup(group_name);
group.dump();
}
/* -------------------------------------------------------------------------- */
void Model::dumpGroup(const std::string & group_name,
const std::string & dumper_name) {
ElementGroup & group = mesh.getElementGroup(group_name);
group.dump(dumper_name);
}
/* -------------------------------------------------------------------------- */
void Model::dumpGroup() {
for (auto & group : mesh.iterateElementGroups()) {
group.dump();
}
}
/* -------------------------------------------------------------------------- */
void Model::setGroupDirectory(const std::string & directory) {
for (auto & group : mesh.iterateElementGroups()) {
group.setDirectory(directory);
}
}
/* -------------------------------------------------------------------------- */
void Model::setGroupDirectory(const std::string & directory,
const std::string & group_name) {
ElementGroup & group = mesh.getElementGroup(group_name);
group.setDirectory(directory);
}
/* -------------------------------------------------------------------------- */
void Model::setGroupBaseName(const std::string & basename,
const std::string & group_name) {
ElementGroup & group = mesh.getElementGroup(group_name);
group.setBaseName(basename);
}
/* -------------------------------------------------------------------------- */
DumperIOHelper & Model::getGroupDumper(const std::string & group_name) {
ElementGroup & group = mesh.getElementGroup(group_name);
return group.getDumper();
}
/* -------------------------------------------------------------------------- */
// DUMPER stuff
/* -------------------------------------------------------------------------- */
void Model::addDumpGroupFieldToDumper(const std::string & field_id,
std::shared_ptr<dumpers::Field> field,
DumperIOHelper & dumper) {
#ifdef AKANTU_USE_IOHELPER
dumper.registerField(field_id, std::move(field));
#endif
}
/* -------------------------------------------------------------------------- */
void Model::addDumpField(const std::string & field_id) {
this->addDumpFieldToDumper(mesh.getDefaultDumperName(), field_id);
}
/* -------------------------------------------------------------------------- */
void Model::addDumpFieldVector(const std::string & field_id) {
this->addDumpFieldVectorToDumper(mesh.getDefaultDumperName(), field_id);
}
/* -------------------------------------------------------------------------- */
void Model::addDumpFieldTensor(const std::string & field_id) {
this->addDumpFieldTensorToDumper(mesh.getDefaultDumperName(), field_id);
}
/* -------------------------------------------------------------------------- */
void Model::setBaseName(const std::string & field_id) {
mesh.setBaseName(field_id);
}
/* -------------------------------------------------------------------------- */
void Model::setBaseNameToDumper(const std::string & dumper_name,
const std::string & basename) {
mesh.setBaseNameToDumper(dumper_name, basename);
}
/* -------------------------------------------------------------------------- */
void Model::addDumpFieldToDumper(const std::string & dumper_name,
const std::string & field_id) {
-
- this->addDumpGroupFieldToDumper(dumper_name, field_id, "all", _ek_regular,
- false);
+ this->addDumpGroupFieldToDumper(dumper_name, field_id, "all",
+ dumper_default_element_kind, false);
}
/* -------------------------------------------------------------------------- */
void Model::addDumpGroupField(const std::string & field_id,
const std::string & group_name) {
-
ElementGroup & group = mesh.getElementGroup(group_name);
this->addDumpGroupFieldToDumper(group.getDefaultDumperName(), field_id,
- group_name, _ek_regular, false);
+ group_name, dumper_default_element_kind,
+ false);
}
/* -------------------------------------------------------------------------- */
void Model::removeDumpGroupField(const std::string & field_id,
const std::string & group_name) {
ElementGroup & group = mesh.getElementGroup(group_name);
this->removeDumpGroupFieldFromDumper(group.getDefaultDumperName(), field_id,
group_name);
}
/* -------------------------------------------------------------------------- */
void Model::removeDumpGroupFieldFromDumper(const std::string & dumper_name,
const std::string & field_id,
const std::string & group_name) {
ElementGroup & group = mesh.getElementGroup(group_name);
group.removeDumpFieldFromDumper(dumper_name, field_id);
}
/* -------------------------------------------------------------------------- */
void Model::addDumpFieldVectorToDumper(const std::string & dumper_name,
const std::string & field_id) {
- this->addDumpGroupFieldToDumper(dumper_name, field_id, "all", _ek_regular,
- true);
+ this->addDumpGroupFieldToDumper(dumper_name, field_id, "all",
+ dumper_default_element_kind, true);
}
/* -------------------------------------------------------------------------- */
void Model::addDumpGroupFieldVector(const std::string & field_id,
const std::string & group_name) {
ElementGroup & group = mesh.getElementGroup(group_name);
this->addDumpGroupFieldVectorToDumper(group.getDefaultDumperName(), field_id,
group_name);
}
/* -------------------------------------------------------------------------- */
void Model::addDumpGroupFieldVectorToDumper(const std::string & dumper_name,
const std::string & field_id,
const std::string & group_name) {
this->addDumpGroupFieldToDumper(dumper_name, field_id, group_name,
- _ek_regular, true);
+ dumper_default_element_kind, true);
}
/* -------------------------------------------------------------------------- */
void Model::addDumpFieldTensorToDumper(const std::string & dumper_name,
const std::string & field_id) {
- this->addDumpGroupFieldToDumper(dumper_name, field_id, "all", _ek_regular,
- true);
+ this->addDumpGroupFieldToDumper(dumper_name, field_id, "all",
+ dumper_default_element_kind, true);
}
/* -------------------------------------------------------------------------- */
void Model::addDumpGroupFieldToDumper(const std::string & dumper_name,
const std::string & field_id,
const std::string & group_name,
ElementKind element_kind,
bool padding_flag) {
this->addDumpGroupFieldToDumper(dumper_name, field_id, group_name,
this->spatial_dimension, element_kind,
padding_flag);
}
/* -------------------------------------------------------------------------- */
void Model::addDumpGroupFieldToDumper(const std::string & dumper_name,
const std::string & field_id,
const std::string & group_name,
UInt spatial_dimension,
ElementKind element_kind,
bool padding_flag) {
#ifdef AKANTU_USE_IOHELPER
std::shared_ptr<dumpers::Field> field;
if (!field) {
field = this->createNodalFieldReal(field_id, group_name, padding_flag);
}
if (!field) {
field = this->createNodalFieldUInt(field_id, group_name, padding_flag);
}
if (!field) {
field = this->createNodalFieldBool(field_id, group_name, padding_flag);
}
if (!field) {
field = this->createElementalField(field_id, group_name, padding_flag,
spatial_dimension, element_kind);
}
if (!field) {
field = this->mesh.createFieldFromAttachedData<UInt>(field_id, group_name,
element_kind);
}
if (!field) {
field = this->mesh.createFieldFromAttachedData<Real>(field_id, group_name,
element_kind);
}
#ifndef AKANTU_NDEBUG
if (!field) {
AKANTU_DEBUG_WARNING("No field could be found based on name: " << field_id);
}
#endif
if (field) {
DumperIOHelper & dumper = mesh.getGroupDumper(dumper_name, group_name);
this->addDumpGroupFieldToDumper(field_id, field, dumper);
}
#endif
}
/* -------------------------------------------------------------------------- */
+void Model::dump(const std::string & dumper_name) {
+ mesh.dump(dumper_name);
+}
+
+/* -------------------------------------------------------------------------- */
+void Model::dump(const std::string & dumper_name, UInt step) {
+ mesh.dump(dumper_name, step);
+}
-void Model::dump() { mesh.dump(); }
+/* ------------------------------------------------------------------------- */
+void Model::dump(const std::string & dumper_name, Real time,
+ UInt step) {
+ mesh.dump(dumper_name, time, step);
+}
/* -------------------------------------------------------------------------- */
+void Model::dump() {
+ auto default_dumper = mesh.getDefaultDumperName();
+ this->dump(default_dumper);
+}
+/* -------------------------------------------------------------------------- */
+void Model::dump(UInt step) {
+ auto default_dumper = mesh.getDefaultDumperName();
+ this->dump(default_dumper, step);
+}
+
+/* -------------------------------------------------------------------------- */
+void Model::dump(Real time, UInt step) {
+ auto default_dumper = mesh.getDefaultDumperName();
+ this->dump(default_dumper, time, step);
+}
+
+/* -------------------------------------------------------------------------- */
void Model::setDirectory(const std::string & directory) {
mesh.setDirectory(directory);
}
/* -------------------------------------------------------------------------- */
-
void Model::setDirectoryToDumper(const std::string & dumper_name,
const std::string & directory) {
mesh.setDirectoryToDumper(dumper_name, directory);
}
/* -------------------------------------------------------------------------- */
-
void Model::setTextModeToDumper() { mesh.setTextModeToDumper(); }
/* -------------------------------------------------------------------------- */
} // namespace akantu
diff --git a/src/model/model.hh b/src/model/model.hh
index cdb0513e3..f6468cb27 100644
--- a/src/model/model.hh
+++ b/src/model/model.hh
@@ -1,370 +1,378 @@
/**
* @file model.hh
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author David Simon Kammer <david.kammer@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Tue Feb 20 2018
*
* @brief Interface of a model
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_common.hh"
-#include "aka_memory.hh"
#include "aka_named_argument.hh"
#include "fe_engine.hh"
#include "mesh.hh"
#include "model_options.hh"
#include "model_solver.hh"
/* -------------------------------------------------------------------------- */
#include <typeindex>
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_MODEL_HH_
#define AKANTU_MODEL_HH_
namespace akantu {
class SynchronizerRegistry;
class Parser;
class DumperIOHelper;
class DOFManager;
} // namespace akantu
/* -------------------------------------------------------------------------- */
namespace akantu {
-class Model : public Memory, public ModelSolver, public MeshEventHandler {
+class Model : public ModelSolver, public MeshEventHandler {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
/// Normal constructor where the DOFManager is created internally
Model(Mesh & mesh, const ModelType & type, UInt dim = _all_dimensions,
- const ID & id = "model", const MemoryID & memory_id = 0);
+ const ID & id = "model");
/// Model constructor the the dof manager is created externally, for example
/// in a ModelCoupler
Model(Mesh & mesh, const ModelType & type,
std::shared_ptr<DOFManager> dof_manager, UInt dim = _all_dimensions,
const ID & id = "model", const MemoryID & memory_id = 0);
~Model() override;
using FEEngineMap = std::map<std::string, std::unique_ptr<FEEngine>>;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
protected:
virtual void initFullImpl(const ModelOptions & options);
public:
template <typename... pack>
std::enable_if_t<are_named_argument<pack...>::value>
initFull(pack &&... _pack) {
switch (this->model_type) {
#ifdef AKANTU_SOLID_MECHANICS
case ModelType::_solid_mechanics_model:
this->initFullImpl(SolidMechanicsModelOptions{
use_named_args, std::forward<decltype(_pack)>(_pack)...});
break;
#endif
#ifdef AKANTU_COHESIVE_ELEMENT
case ModelType::_solid_mechanics_model_cohesive:
this->initFullImpl(SolidMechanicsModelCohesiveOptions{
use_named_args, std::forward<decltype(_pack)>(_pack)...});
break;
#endif
#ifdef AKANTU_HEAT_TRANSFER
case ModelType::_heat_transfer_model:
this->initFullImpl(HeatTransferModelOptions{
use_named_args, std::forward<decltype(_pack)>(_pack)...});
break;
#endif
#ifdef AKANTU_EMBEDDED
case ModelType::_embedded_model:
this->initFullImpl(EmbeddedInterfaceModelOptions{
use_named_args, std::forward<decltype(_pack)>(_pack)...});
break;
#endif
#ifdef AKANTU_CONTACT_MECHANICS
case ModelType::_contact_mechanics_model:
this->initFullImpl(ContactMechanicsModelOptions{
use_named_args, std::forward<decltype(_pack)>(_pack)...});
break;
#endif
#ifdef AKANTU_MODEL_COUPLERS
case ModelType::_coupler_solid_contact:
this->initFullImpl(CouplerSolidContactOptions{
use_named_args, std::forward<decltype(_pack)>(_pack)...});
break;
case ModelType::_coupler_solid_cohesive_contact:
this->initFullImpl(CouplerSolidCohesiveContactOptions{
use_named_args, std::forward<decltype(_pack)>(_pack)...});
break;
#endif
default:
this->initFullImpl(ModelOptions{use_named_args,
std::forward<decltype(_pack)>(_pack)...});
}
}
template <typename... pack>
std::enable_if_t<not are_named_argument<pack...>::value>
initFull(pack &&... _pack) {
this->initFullImpl(std::forward<decltype(_pack)>(_pack)...);
}
/// initialize a new solver if needed
void initNewSolver(const AnalysisMethod & method);
protected:
/// get some default values for derived classes
virtual std::tuple<ID, TimeStepSolverType>
getDefaultSolverID(const AnalysisMethod & method) = 0;
virtual void initModel() = 0;
virtual void initFEEngineBoundary();
/// function to print the containt of the class
void printself(std::ostream & /*stream*/,
int /*indent*/ = 0) const override{};
public:
/* ------------------------------------------------------------------------ */
/* Access to the dumpable interface of the boundaries */
/* ------------------------------------------------------------------------ */
/// Dump the data for a given group
void dumpGroup(const std::string & group_name);
void dumpGroup(const std::string & group_name,
const std::string & dumper_name);
/// Dump the data for all boundaries
void dumpGroup();
/// Set the directory for a given group
void setGroupDirectory(const std::string & directory,
const std::string & group_name);
/// Set the directory for all boundaries
void setGroupDirectory(const std::string & directory);
/// Set the base name for a given group
void setGroupBaseName(const std::string & basename,
const std::string & group_name);
/// Get the internal dumper of a given group
DumperIOHelper & getGroupDumper(const std::string & group_name);
/* ------------------------------------------------------------------------ */
/* Function for non local capabilities */
/* ------------------------------------------------------------------------ */
virtual void updateDataForNonLocalCriterion(__attribute__((unused))
ElementTypeMapReal & criterion) {
AKANTU_TO_IMPLEMENT();
}
protected:
template <typename T>
- void allocNodalField(Array<T> *& array, UInt nb_component, const ID & name,
- T t_default = T());
-
- template <typename T>
- void allocNodalField(std::unique_ptr<Array<T>> & array, UInt nb_component,
+void allocNodalField(std::unique_ptr<Array<T>> & array, UInt nb_component,
const ID & name) const;
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
/// get id of model
AKANTU_GET_MACRO(ID, id, const ID &)
/// get the number of surfaces
AKANTU_GET_MACRO(Mesh, mesh, Mesh &)
/// synchronize the boundary in case of parallel run
virtual void synchronizeBoundaries(){};
/// return the fem object associated with a provided name
inline FEEngine & getFEEngine(const ID & name = "") const;
/// return the fem boundary object associated with a provided name
virtual FEEngine & getFEEngineBoundary(const ID & name = "");
/// register a fem object associated with name
template <typename FEEngineClass>
inline void registerFEEngineObject(const std::string & name, Mesh & mesh,
UInt spatial_dimension);
/// unregister a fem object associated with name
inline void unRegisterFEEngineObject(const std::string & name);
/// return the synchronizer registry
SynchronizerRegistry & getSynchronizerRegistry();
/// return the fem object associated with a provided name
template <typename FEEngineClass>
inline FEEngineClass & getFEEngineClass(std::string name = "") const;
/// return the fem boundary object associated with a provided name
template <typename FEEngineClass>
inline FEEngineClass & getFEEngineClassBoundary(std::string name = "");
/// Get the type of analysis method used
AKANTU_GET_MACRO(AnalysisMethod, method, AnalysisMethod);
/* ------------------------------------------------------------------------ */
- /* Pack and unpack helper functions */
+ /* Pack and unpack hexlper functions */
/* ------------------------------------------------------------------------ */
public:
inline UInt getNbIntegrationPoints(const Array<Element> & elements,
const ID & fem_id = ID()) const;
/* ------------------------------------------------------------------------ */
/* Dumpable interface (kept for convenience) and dumper relative functions */
/* ------------------------------------------------------------------------ */
void setTextModeToDumper();
virtual void addDumpGroupFieldToDumper(const std::string & field_id,
std::shared_ptr<dumpers::Field> field,
DumperIOHelper & dumper);
virtual void addDumpField(const std::string & field_id);
virtual void addDumpFieldVector(const std::string & field_id);
virtual void addDumpFieldToDumper(const std::string & dumper_name,
const std::string & field_id);
virtual void addDumpFieldVectorToDumper(const std::string & dumper_name,
const std::string & field_id);
virtual void addDumpFieldTensorToDumper(const std::string & dumper_name,
const std::string & field_id);
virtual void addDumpFieldTensor(const std::string & field_id);
virtual void setBaseName(const std::string & field_id);
virtual void setBaseNameToDumper(const std::string & dumper_name,
const std::string & basename);
virtual void addDumpGroupField(const std::string & field_id,
const std::string & group_name);
virtual void addDumpGroupFieldToDumper(const std::string & dumper_name,
const std::string & field_id,
const std::string & group_name,
ElementKind element_kind,
bool padding_flag);
virtual void addDumpGroupFieldToDumper(const std::string & dumper_name,
const std::string & field_id,
const std::string & group_name,
UInt spatial_dimension,
ElementKind element_kind,
bool padding_flag);
virtual void removeDumpGroupField(const std::string & field_id,
const std::string & group_name);
virtual void removeDumpGroupFieldFromDumper(const std::string & dumper_name,
const std::string & field_id,
const std::string & group_name);
virtual void addDumpGroupFieldVector(const std::string & field_id,
const std::string & group_name);
virtual void addDumpGroupFieldVectorToDumper(const std::string & dumper_name,
const std::string & field_id,
const std::string & group_name);
virtual std::shared_ptr<dumpers::Field>
createNodalFieldReal(const std::string & /*field_name*/,
const std::string & /*group_name*/,
bool /*padding_flag*/) {
return nullptr;
}
virtual std::shared_ptr<dumpers::Field>
createNodalFieldUInt(const std::string & /*field_name*/,
const std::string & /*group_name*/,
bool /*padding_flag*/) {
return nullptr;
}
virtual std::shared_ptr<dumpers::Field>
createNodalFieldBool(const std::string & /*field_name*/,
const std::string & /*group_name*/,
bool /*padding_flag*/) {
return nullptr;
}
virtual std::shared_ptr<dumpers::Field> createElementalField(
const std::string & /*field_name*/, const std::string & /*group_name*/,
bool /*padding_flag*/, UInt /*spatial_dimension*/, ElementKind /*kind*/) {
return nullptr;
}
void setDirectory(const std::string & directory);
void setDirectoryToDumper(const std::string & dumper_name,
const std::string & directory);
+
+ /* ------------------------------------------------------------------------ */
+ virtual void dump(const std::string & dumper_name);
+ virtual void dump(const std::string & dumper_name, UInt step);
+ virtual void dump(const std::string & dumper_name, Real time, UInt step);
+ /* ------------------------------------------------------------------------ */
virtual void dump();
+ virtual void dump(UInt step);
+ virtual void dump(Real time, UInt step);
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
friend std::ostream & operator<<(std::ostream & /*stream*/,
const Model & /*_this*/);
+ ID id;
+
/// analysis method check the list in akantu::AnalysisMethod
AnalysisMethod method;
/// Mesh
Mesh & mesh;
/// Spatial dimension of the problem
UInt spatial_dimension;
/// the main fem object present in all models
FEEngineMap fems;
/// the fem object present in all models for boundaries
FEEngineMap fems_boundary;
/// default fem object
std::string default_fem;
/// parser to the pointer to use
Parser & parser;
+
+ /// default ElementKind for dumper
+ ElementKind dumper_default_element_kind{_ek_regular};
};
/// standard output stream operator
inline std::ostream & operator<<(std::ostream & stream, const Model & _this) {
_this.printself(stream);
return stream;
}
} // namespace akantu
#include "model_inline_impl.hh"
#endif /* AKANTU_MODEL_HH_ */
diff --git a/src/model/model_inline_impl.hh b/src/model/model_inline_impl.hh
index 1c7397f3c..05a916d0a 100644
--- a/src/model/model_inline_impl.hh
+++ b/src/model/model_inline_impl.hh
@@ -1,181 +1,168 @@
/**
* @file model_inline_impl.hh
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author David Simon Kammer <david.kammer@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Wed Aug 25 2010
* @date last modification: Wed Nov 08 2017
*
* @brief inline implementation of the model class
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "model.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_MODEL_INLINE_IMPL_HH_
#define AKANTU_MODEL_INLINE_IMPL_HH_
namespace akantu {
/* -------------------------------------------------------------------------- */
template <typename FEEngineClass>
inline FEEngineClass & Model::getFEEngineClassBoundary(std::string name) {
if (name.empty()) {
name = default_fem;
}
auto it_boun = fems_boundary.find(name);
if (it_boun == fems_boundary.end()) {
AKANTU_DEBUG_INFO("Creating FEEngine boundary " << name);
auto it = fems.find(name);
if (it == fems.end()) {
AKANTU_EXCEPTION("The FEEngine " << name << " is not registered");
}
auto spatial_dimension = it->second->getElementDimension();
fems_boundary[name] = std::make_unique<FEEngineClass>(
it->second->getMesh(), spatial_dimension - 1,
- id + ":fem_boundary:" + name, memory_id);
+ id + ":fem_boundary:" + name);
}
return aka::as_type<FEEngineClass>(*fems_boundary[name]);
}
/* -------------------------------------------------------------------------- */
template <typename FEEngineClass>
inline FEEngineClass & Model::getFEEngineClass(std::string name) const {
if (name.empty()) {
name = default_fem;
}
auto it = fems.find(name);
if (it == fems.end()) {
AKANTU_EXCEPTION("The FEEngine " << name << " is not registered");
}
return aka::as_type<FEEngineClass>(*(it->second));
}
/* -------------------------------------------------------------------------- */
inline void Model::unRegisterFEEngineObject(const std::string & name) {
auto it = fems.find(name);
if (it == fems.end()) {
AKANTU_EXCEPTION("FEEngine object with name " << name << " was not found");
}
fems.erase(it);
if (not fems.empty() and default_fem == name) {
default_fem = (*fems.begin()).first;
}
}
/* -------------------------------------------------------------------------- */
template <typename FEEngineClass>
inline void Model::registerFEEngineObject(const std::string & name, Mesh & mesh,
UInt spatial_dimension) {
if (fems.empty()) {
default_fem = name;
}
auto it = fems.find(name);
if (it != fems.end()) {
AKANTU_EXCEPTION("FEEngine object with name " << name
<< " was already created");
}
fems[name] = std::make_unique<FEEngineClass>(
- mesh, spatial_dimension, id + ":fem:" + name + std::to_string(memory_id),
- memory_id);
+ mesh, spatial_dimension, id + ":fem:" + name);
}
/* -------------------------------------------------------------------------- */
inline FEEngine & Model::getFEEngine(const ID & name) const {
ID tmp_name = (name.empty()) ? default_fem : name;
auto it = fems.find(tmp_name);
if (it == fems.end()) {
AKANTU_EXCEPTION("The FEEngine " << tmp_name << " is not registered");
}
return *(it->second);
}
/* -------------------------------------------------------------------------- */
inline FEEngine & Model::getFEEngineBoundary(const ID & name) {
ID tmp_name = (name.empty()) ? default_fem : name;
auto it = fems_boundary.find(tmp_name);
if (it == fems_boundary.end()) {
AKANTU_EXCEPTION("The FEEngine boundary " << tmp_name
<< " is not registered");
}
AKANTU_DEBUG_ASSERT(it->second != nullptr, "The FEEngine boundary "
<< tmp_name
<< " was not created");
return *(it->second);
}
-/* -------------------------------------------------------------------------- */
-template <typename T>
-void Model::allocNodalField(Array<T> *& array, UInt nb_component,
- const ID & name, T t_default) {
- if (array) {
- return;
- }
-
- UInt nb_nodes = mesh.getNbNodes();
- array = &(alloc<T>(id + ":" + name, nb_nodes, nb_component, t_default));
-}
-
/* -------------------------------------------------------------------------- */
template <typename T>
void Model::allocNodalField(std::unique_ptr<Array<T>> & array,
UInt nb_component, const ID & name) const {
if (array) {
return;
}
UInt nb_nodes = mesh.getNbNodes();
array =
std::make_unique<Array<T>>(nb_nodes, nb_component, T(), id + ":" + name);
}
/* -------------------------------------------------------------------------- */
inline UInt Model::getNbIntegrationPoints(const Array<Element> & elements,
const ID & fem_id) const {
UInt nb_quad = 0;
for (auto && el : elements) {
nb_quad +=
getFEEngine(fem_id).getNbIntegrationPoints(el.type, el.ghost_type);
}
return nb_quad;
}
/* -------------------------------------------------------------------------- */
} // namespace akantu
#endif /* AKANTU_MODEL_INLINE_IMPL_HH_ */
diff --git a/src/model/solid_mechanics/material.cc b/src/model/solid_mechanics/material.cc
index dfd9f484e..c21d92e4d 100644
--- a/src/model/solid_mechanics/material.cc
+++ b/src/model/solid_mechanics/material.cc
@@ -1,1385 +1,1314 @@
/**
* @file material.cc
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
* @author Daniel Pino Muñoz <daniel.pinomunoz@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
* @author Marco Vocialta <marco.vocialta@epfl.ch>
*
* @date creation: Tue Jul 27 2010
* @date last modification: Wed Feb 21 2018
*
* @brief Implementation of the common part of the material class
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "material.hh"
+#include "mesh_iterators.hh"
#include "solid_mechanics_model.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
Material::Material(SolidMechanicsModel & model, const ID & id)
- : Memory(id, model.getMemoryID()), Parsable(ParserType::_material, id),
- is_init(false), fem(model.getFEEngine()), finite_deformation(false),
+ : Parsable(ParserType::_material, id), id(id), fem(model.getFEEngine()),
model(model), spatial_dimension(this->model.getSpatialDimension()),
- element_filter("element_filter", id, this->memory_id),
- stress("stress", *this), eigengradu("eigen_grad_u", *this),
- gradu("grad_u", *this), green_strain("green_strain", *this),
+ element_filter("element_filter", id), stress("stress", *this),
+ eigengradu("eigen_grad_u", *this), gradu("grad_u", *this),
+ green_strain("green_strain", *this),
piola_kirchhoff_2("piola_kirchhoff_2", *this),
- potential_energy("potential_energy", *this), is_non_local(false),
- use_previous_stress(false), use_previous_gradu(false),
+ potential_energy("potential_energy", *this),
interpolation_inverse_coordinates("interpolation inverse coordinates",
*this),
interpolation_points_matrices("interpolation points matrices", *this),
eigen_grad_u(model.getSpatialDimension(), model.getSpatialDimension(),
0.) {
AKANTU_DEBUG_IN();
this->registerParam("eigen_grad_u", eigen_grad_u, _pat_parsable,
"EigenGradU");
/// for each connectivity types allocate the element filer array of the
/// material
element_filter.initialize(model.getMesh(),
_spatial_dimension = spatial_dimension,
_element_kind = _ek_regular);
this->initialize();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
Material::Material(SolidMechanicsModel & model, UInt dim, const Mesh & mesh,
FEEngine & fe_engine, const ID & id)
- : Memory(id, model.getMemoryID()), Parsable(ParserType::_material, id),
- is_init(false), fem(fe_engine), finite_deformation(false), model(model),
- spatial_dimension(dim),
- element_filter("element_filter", id, this->memory_id),
+ : Parsable(ParserType::_material, id), id(id), fem(fe_engine), model(model),
+ spatial_dimension(dim), element_filter("element_filter", id),
stress("stress", *this, dim, fe_engine, this->element_filter),
eigengradu("eigen_grad_u", *this, dim, fe_engine, this->element_filter),
gradu("gradu", *this, dim, fe_engine, this->element_filter),
green_strain("green_strain", *this, dim, fe_engine, this->element_filter),
piola_kirchhoff_2("piola_kirchhoff_2", *this, dim, fe_engine,
this->element_filter),
potential_energy("potential_energy", *this, dim, fe_engine,
this->element_filter),
- is_non_local(false), use_previous_stress(false),
- use_previous_gradu(false),
interpolation_inverse_coordinates("interpolation inverse_coordinates",
*this, dim, fe_engine,
this->element_filter),
interpolation_points_matrices("interpolation points matrices", *this, dim,
fe_engine, this->element_filter) {
AKANTU_DEBUG_IN();
element_filter.initialize(mesh, _spatial_dimension = spatial_dimension,
_element_kind = _ek_regular);
this->initialize();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
Material::~Material() = default;
/* -------------------------------------------------------------------------- */
void Material::initialize() {
registerParam("rho", rho, Real(0.), _pat_parsable | _pat_modifiable,
"Density");
registerParam("name", name, std::string(), _pat_parsable | _pat_readable);
registerParam("finite_deformation", finite_deformation, false,
_pat_parsable | _pat_readable, "Is finite deformation");
registerParam("inelastic_deformation", inelastic_deformation, false,
_pat_internal, "Is inelastic deformation");
/// allocate gradu stress for local elements
eigengradu.initialize(spatial_dimension * spatial_dimension);
gradu.initialize(spatial_dimension * spatial_dimension);
stress.initialize(spatial_dimension * spatial_dimension);
potential_energy.initialize(1);
this->model.registerEventHandler(*this);
}
/* -------------------------------------------------------------------------- */
void Material::initMaterial() {
AKANTU_DEBUG_IN();
if (finite_deformation) {
this->piola_kirchhoff_2.initialize(spatial_dimension * spatial_dimension);
if (use_previous_stress) {
this->piola_kirchhoff_2.initializeHistory();
}
this->green_strain.initialize(spatial_dimension * spatial_dimension);
}
if (use_previous_stress) {
this->stress.initializeHistory();
}
if (use_previous_gradu) {
this->gradu.initializeHistory();
}
this->resizeInternals();
auto dim = model.getSpatialDimension();
for (const auto & type :
element_filter.elementTypes(_element_kind = _ek_regular)) {
- for (auto eigen_gradu : make_view(eigengradu(type), dim, dim)) {
+ for (auto & eigen_gradu : make_view(eigengradu(type), dim, dim)) {
eigen_gradu = eigen_grad_u;
}
}
is_init = true;
updateInternalParameters();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void Material::savePreviousState() {
AKANTU_DEBUG_IN();
for (auto pair : internal_vectors_real) {
if (pair.second->hasHistory()) {
pair.second->saveCurrentValues();
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void Material::restorePreviousState() {
AKANTU_DEBUG_IN();
for (auto pair : internal_vectors_real) {
if (pair.second->hasHistory()) {
pair.second->restorePreviousValues();
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
/**
* Compute the internal forces by assembling @f$\int_{e} \sigma_e \frac{\partial
* \varphi}{\partial X} dX @f$
*
* @param[in] ghost_type compute the internal forces for _ghost or _not_ghost
* element
*/
void Material::assembleInternalForces(GhostType ghost_type) {
AKANTU_DEBUG_IN();
UInt spatial_dimension = model.getSpatialDimension();
if (!finite_deformation) {
auto & internal_force = const_cast<Array<Real> &>(model.getInternalForce());
// Mesh & mesh = fem.getMesh();
for (auto && type :
element_filter.elementTypes(spatial_dimension, ghost_type)) {
Array<UInt> & elem_filter = element_filter(type, ghost_type);
UInt nb_element = elem_filter.size();
if (nb_element == 0) {
continue;
}
const Array<Real> & shapes_derivatives =
fem.getShapesDerivatives(type, ghost_type);
UInt size_of_shapes_derivatives = shapes_derivatives.getNbComponent();
UInt nb_quadrature_points = fem.getNbIntegrationPoints(type, ghost_type);
UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
/// compute @f$\sigma \frac{\partial \varphi}{\partial X}@f$ by
/// @f$\mathbf{B}^t \mathbf{\sigma}_q@f$
auto * sigma_dphi_dx =
new Array<Real>(nb_element * nb_quadrature_points,
size_of_shapes_derivatives, "sigma_x_dphi_/_dX");
fem.computeBtD(stress(type, ghost_type), *sigma_dphi_dx, type, ghost_type,
elem_filter);
/**
* compute @f$\int \sigma * \frac{\partial \varphi}{\partial X}dX@f$ by
* @f$ \sum_q \mathbf{B}^t
* \mathbf{\sigma}_q \overline w_q J_q@f$
*/
auto * int_sigma_dphi_dx =
new Array<Real>(nb_element, nb_nodes_per_element * spatial_dimension,
"int_sigma_x_dphi_/_dX");
fem.integrate(*sigma_dphi_dx, *int_sigma_dphi_dx,
size_of_shapes_derivatives, type, ghost_type, elem_filter);
delete sigma_dphi_dx;
/// assemble
model.getDOFManager().assembleElementalArrayLocalArray(
*int_sigma_dphi_dx, internal_force, type, ghost_type, -1,
elem_filter);
delete int_sigma_dphi_dx;
}
} else {
switch (spatial_dimension) {
case 1:
this->assembleInternalForces<1>(ghost_type);
break;
case 2:
this->assembleInternalForces<2>(ghost_type);
break;
case 3:
this->assembleInternalForces<3>(ghost_type);
break;
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
/**
* Compute the stress from the gradu
*
* @param[in] ghost_type compute the residual for _ghost or _not_ghost element
*/
void Material::computeAllStresses(GhostType ghost_type) {
AKANTU_DEBUG_IN();
UInt spatial_dimension = model.getSpatialDimension();
for (const auto & type :
element_filter.elementTypes(spatial_dimension, ghost_type)) {
Array<UInt> & elem_filter = element_filter(type, ghost_type);
if (elem_filter.empty()) {
continue;
}
Array<Real> & gradu_vect = gradu(type, ghost_type);
/// compute @f$\nabla u@f$
fem.gradientOnIntegrationPoints(model.getDisplacement(), gradu_vect,
spatial_dimension, type, ghost_type,
elem_filter);
gradu_vect -= eigengradu(type, ghost_type);
/// compute @f$\mathbf{\sigma}_q@f$ from @f$\nabla u@f$
computeStress(type, ghost_type);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void Material::computeAllCauchyStresses(GhostType ghost_type) {
AKANTU_DEBUG_IN();
AKANTU_DEBUG_ASSERT(finite_deformation, "The Cauchy stress can only be "
"computed if you are working in "
"finite deformation.");
for (auto type : element_filter.elementTypes(spatial_dimension, ghost_type)) {
switch (spatial_dimension) {
case 1:
this->StoCauchy<1>(type, ghost_type);
break;
case 2:
this->StoCauchy<2>(type, ghost_type);
break;
case 3:
this->StoCauchy<3>(type, ghost_type);
break;
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <UInt dim>
void Material::StoCauchy(ElementType el_type, GhostType ghost_type) {
AKANTU_DEBUG_IN();
auto gradu_it = this->gradu(el_type, ghost_type).begin(dim, dim);
-
auto gradu_end = this->gradu(el_type, ghost_type).end(dim, dim);
auto piola_it = this->piola_kirchhoff_2(el_type, ghost_type).begin(dim, dim);
-
auto stress_it = this->stress(el_type, ghost_type).begin(dim, dim);
for (; gradu_it != gradu_end; ++gradu_it, ++piola_it, ++stress_it) {
Matrix<Real> & grad_u = *gradu_it;
Matrix<Real> & piola = *piola_it;
Matrix<Real> & sigma = *stress_it;
auto F_tensor = gradUToF<dim>(grad_u);
this->StoCauchy<dim>(F_tensor, piola, sigma);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void Material::setToSteadyState(GhostType ghost_type) {
AKANTU_DEBUG_IN();
const Array<Real> & displacement = model.getDisplacement();
// resizeInternalArray(gradu);
UInt spatial_dimension = model.getSpatialDimension();
for (auto type : element_filter.elementTypes(spatial_dimension, ghost_type)) {
Array<UInt> & elem_filter = element_filter(type, ghost_type);
Array<Real> & gradu_vect = gradu(type, ghost_type);
/// compute @f$\nabla u@f$
fem.gradientOnIntegrationPoints(displacement, gradu_vect, spatial_dimension,
type, ghost_type, elem_filter);
setToSteadyState(type, ghost_type);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
/**
* Compute the stiffness matrix by assembling @f$\int_{\omega} B^t \times D
* \times B d\omega @f$
*
* @param[in] ghost_type compute the residual for _ghost or _not_ghost element
*/
void Material::assembleStiffnessMatrix(GhostType ghost_type) {
AKANTU_DEBUG_IN();
UInt spatial_dimension = model.getSpatialDimension();
for (auto type : element_filter.elementTypes(spatial_dimension, ghost_type)) {
if (finite_deformation) {
switch (spatial_dimension) {
case 1: {
assembleStiffnessMatrixNL<1>(type, ghost_type);
assembleStiffnessMatrixL2<1>(type, ghost_type);
break;
}
case 2: {
assembleStiffnessMatrixNL<2>(type, ghost_type);
assembleStiffnessMatrixL2<2>(type, ghost_type);
break;
}
case 3: {
assembleStiffnessMatrixNL<3>(type, ghost_type);
assembleStiffnessMatrixL2<3>(type, ghost_type);
break;
}
}
} else {
switch (spatial_dimension) {
case 1: {
assembleStiffnessMatrix<1>(type, ghost_type);
break;
}
case 2: {
assembleStiffnessMatrix<2>(type, ghost_type);
break;
}
case 3: {
assembleStiffnessMatrix<3>(type, ghost_type);
break;
}
}
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <UInt dim>
void Material::assembleStiffnessMatrix(ElementType type, GhostType ghost_type) {
AKANTU_DEBUG_IN();
Array<UInt> & elem_filter = element_filter(type, ghost_type);
if (elem_filter.empty()) {
AKANTU_DEBUG_OUT();
return;
}
// const Array<Real> & shapes_derivatives =
// fem.getShapesDerivatives(type, ghost_type);
Array<Real> & gradu_vect = gradu(type, ghost_type);
UInt nb_element = elem_filter.size();
UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
UInt nb_quadrature_points = fem.getNbIntegrationPoints(type, ghost_type);
gradu_vect.resize(nb_quadrature_points * nb_element);
fem.gradientOnIntegrationPoints(model.getDisplacement(), gradu_vect, dim,
type, ghost_type, elem_filter);
UInt tangent_size = getTangentStiffnessVoigtSize(dim);
auto * tangent_stiffness_matrix =
new Array<Real>(nb_element * nb_quadrature_points,
tangent_size * tangent_size, "tangent_stiffness_matrix");
tangent_stiffness_matrix->zero();
computeTangentModuli(type, *tangent_stiffness_matrix, ghost_type);
/// compute @f$\mathbf{B}^t * \mathbf{D} * \mathbf{B}@f$
UInt bt_d_b_size = dim * nb_nodes_per_element;
auto * bt_d_b = new Array<Real>(nb_element * nb_quadrature_points,
bt_d_b_size * bt_d_b_size, "B^t*D*B");
fem.computeBtDB(*tangent_stiffness_matrix, *bt_d_b, 4, type, ghost_type,
elem_filter);
delete tangent_stiffness_matrix;
/// compute @f$ k_e = \int_e \mathbf{B}^t * \mathbf{D} * \mathbf{B}@f$
auto * K_e = new Array<Real>(nb_element, bt_d_b_size * bt_d_b_size, "K_e");
fem.integrate(*bt_d_b, *K_e, bt_d_b_size * bt_d_b_size, type, ghost_type,
elem_filter);
delete bt_d_b;
model.getDOFManager().assembleElementalMatricesToMatrix(
"K", "displacement", *K_e, type, ghost_type, _symmetric, elem_filter);
delete K_e;
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <UInt dim>
void Material::assembleStiffnessMatrixNL(ElementType type,
GhostType ghost_type) {
AKANTU_DEBUG_IN();
const Array<Real> & shapes_derivatives =
fem.getShapesDerivatives(type, ghost_type);
Array<UInt> & elem_filter = element_filter(type, ghost_type);
// Array<Real> & gradu_vect = delta_gradu(type, ghost_type);
UInt nb_element = elem_filter.size();
UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
UInt nb_quadrature_points = fem.getNbIntegrationPoints(type, ghost_type);
auto * shapes_derivatives_filtered = new Array<Real>(
nb_element * nb_quadrature_points, dim * nb_nodes_per_element,
"shapes derivatives filtered");
FEEngine::filterElementalData(fem.getMesh(), shapes_derivatives,
*shapes_derivatives_filtered, type, ghost_type,
elem_filter);
/// compute @f$\mathbf{B}^t * \mathbf{D} * \mathbf{B}@f$
UInt bt_s_b_size = dim * nb_nodes_per_element;
auto * bt_s_b = new Array<Real>(nb_element * nb_quadrature_points,
bt_s_b_size * bt_s_b_size, "B^t*D*B");
UInt piola_matrix_size = getCauchyStressMatrixSize(dim);
Matrix<Real> B(piola_matrix_size, bt_s_b_size);
Matrix<Real> Bt_S(bt_s_b_size, piola_matrix_size);
Matrix<Real> S(piola_matrix_size, piola_matrix_size);
auto shapes_derivatives_filtered_it = shapes_derivatives_filtered->begin(
spatial_dimension, nb_nodes_per_element);
auto Bt_S_B_it = bt_s_b->begin(bt_s_b_size, bt_s_b_size);
auto Bt_S_B_end = bt_s_b->end(bt_s_b_size, bt_s_b_size);
auto piola_it = piola_kirchhoff_2(type, ghost_type).begin(dim, dim);
for (; Bt_S_B_it != Bt_S_B_end;
++Bt_S_B_it, ++shapes_derivatives_filtered_it, ++piola_it) {
auto & Bt_S_B = *Bt_S_B_it;
const auto & Piola_kirchhoff_matrix = *piola_it;
setCauchyStressMatrix<dim>(Piola_kirchhoff_matrix, S);
VoigtHelper<dim>::transferBMatrixToBNL(*shapes_derivatives_filtered_it, B,
nb_nodes_per_element);
Bt_S.template mul<true, false>(B, S);
Bt_S_B.template mul<false, false>(Bt_S, B);
}
delete shapes_derivatives_filtered;
/// compute @f$ k_e = \int_e \mathbf{B}^t * \mathbf{D} * \mathbf{B}@f$
auto * K_e = new Array<Real>(nb_element, bt_s_b_size * bt_s_b_size, "K_e");
fem.integrate(*bt_s_b, *K_e, bt_s_b_size * bt_s_b_size, type, ghost_type,
elem_filter);
delete bt_s_b;
model.getDOFManager().assembleElementalMatricesToMatrix(
"K", "displacement", *K_e, type, ghost_type, _symmetric, elem_filter);
delete K_e;
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <UInt dim>
void Material::assembleStiffnessMatrixL2(ElementType type,
GhostType ghost_type) {
AKANTU_DEBUG_IN();
const Array<Real> & shapes_derivatives =
fem.getShapesDerivatives(type, ghost_type);
Array<UInt> & elem_filter = element_filter(type, ghost_type);
Array<Real> & gradu_vect = gradu(type, ghost_type);
UInt nb_element = elem_filter.size();
UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
UInt nb_quadrature_points = fem.getNbIntegrationPoints(type, ghost_type);
gradu_vect.resize(nb_quadrature_points * nb_element);
fem.gradientOnIntegrationPoints(model.getDisplacement(), gradu_vect, dim,
type, ghost_type, elem_filter);
UInt tangent_size = getTangentStiffnessVoigtSize(dim);
auto * tangent_stiffness_matrix =
new Array<Real>(nb_element * nb_quadrature_points,
tangent_size * tangent_size, "tangent_stiffness_matrix");
tangent_stiffness_matrix->zero();
computeTangentModuli(type, *tangent_stiffness_matrix, ghost_type);
auto * shapes_derivatives_filtered = new Array<Real>(
nb_element * nb_quadrature_points, dim * nb_nodes_per_element,
"shapes derivatives filtered");
FEEngine::filterElementalData(fem.getMesh(), shapes_derivatives,
*shapes_derivatives_filtered, type, ghost_type,
elem_filter);
/// compute @f$\mathbf{B}^t * \mathbf{D} * \mathbf{B}@f$
UInt bt_d_b_size = dim * nb_nodes_per_element;
auto * bt_d_b = new Array<Real>(nb_element * nb_quadrature_points,
bt_d_b_size * bt_d_b_size, "B^t*D*B");
Matrix<Real> B(tangent_size, dim * nb_nodes_per_element);
Matrix<Real> B2(tangent_size, dim * nb_nodes_per_element);
Matrix<Real> Bt_D(dim * nb_nodes_per_element, tangent_size);
auto shapes_derivatives_filtered_it = shapes_derivatives_filtered->begin(
spatial_dimension, nb_nodes_per_element);
auto Bt_D_B_it = bt_d_b->begin(bt_d_b_size, bt_d_b_size);
auto grad_u_it = gradu_vect.begin(dim, dim);
auto D_it = tangent_stiffness_matrix->begin(tangent_size, tangent_size);
auto D_end = tangent_stiffness_matrix->end(tangent_size, tangent_size);
for (; D_it != D_end;
++D_it, ++Bt_D_B_it, ++shapes_derivatives_filtered_it, ++grad_u_it) {
const auto & grad_u = *grad_u_it;
const auto & D = *D_it;
auto & Bt_D_B = *Bt_D_B_it;
// transferBMatrixToBL1<dim > (*shapes_derivatives_filtered_it, B,
// nb_nodes_per_element);
VoigtHelper<dim>::transferBMatrixToSymVoigtBMatrix(
*shapes_derivatives_filtered_it, B, nb_nodes_per_element);
VoigtHelper<dim>::transferBMatrixToBL2(*shapes_derivatives_filtered_it,
grad_u, B2, nb_nodes_per_element);
B += B2;
Bt_D.template mul<true, false>(B, D);
Bt_D_B.template mul<false, false>(Bt_D, B);
}
delete tangent_stiffness_matrix;
delete shapes_derivatives_filtered;
/// compute @f$ k_e = \int_e \mathbf{B}^t * \mathbf{D} * \mathbf{B}@f$
auto * K_e = new Array<Real>(nb_element, bt_d_b_size * bt_d_b_size, "K_e");
fem.integrate(*bt_d_b, *K_e, bt_d_b_size * bt_d_b_size, type, ghost_type,
elem_filter);
delete bt_d_b;
model.getDOFManager().assembleElementalMatricesToMatrix(
"K", "displacement", *K_e, type, ghost_type, _symmetric, elem_filter);
delete K_e;
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <UInt dim>
void Material::assembleInternalForces(GhostType ghost_type) {
AKANTU_DEBUG_IN();
Array<Real> & internal_force = model.getInternalForce();
Mesh & mesh = fem.getMesh();
for (auto type : element_filter.elementTypes(_ghost_type = ghost_type)) {
const Array<Real> & shapes_derivatives =
fem.getShapesDerivatives(type, ghost_type);
Array<UInt> & elem_filter = element_filter(type, ghost_type);
if (elem_filter.empty()) {
continue;
}
UInt size_of_shapes_derivatives = shapes_derivatives.getNbComponent();
UInt nb_element = elem_filter.size();
UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
UInt nb_quadrature_points = fem.getNbIntegrationPoints(type, ghost_type);
auto * shapesd_filtered = new Array<Real>(
nb_element, size_of_shapes_derivatives, "filtered shapesd");
FEEngine::filterElementalData(mesh, shapes_derivatives, *shapesd_filtered,
type, ghost_type, elem_filter);
Array<Real>::matrix_iterator shapes_derivatives_filtered_it =
shapesd_filtered->begin(dim, nb_nodes_per_element);
// Set stress vectors
UInt stress_size = getTangentStiffnessVoigtSize(dim);
// Set matrices B and BNL*
UInt bt_s_size = dim * nb_nodes_per_element;
auto * bt_s =
new Array<Real>(nb_element * nb_quadrature_points, bt_s_size, "B^t*S");
auto grad_u_it = this->gradu(type, ghost_type).begin(dim, dim);
auto grad_u_end = this->gradu(type, ghost_type).end(dim, dim);
auto stress_it = this->piola_kirchhoff_2(type, ghost_type).begin(dim, dim);
shapes_derivatives_filtered_it =
shapesd_filtered->begin(dim, nb_nodes_per_element);
Array<Real>::matrix_iterator bt_s_it = bt_s->begin(bt_s_size, 1);
Matrix<Real> B_tensor(stress_size, bt_s_size);
Matrix<Real> B2_tensor(stress_size, bt_s_size);
for (; grad_u_it != grad_u_end; ++grad_u_it, ++stress_it,
++shapes_derivatives_filtered_it,
++bt_s_it) {
auto & grad_u = *grad_u_it;
auto & r = *bt_s_it;
auto & S = *stress_it;
VoigtHelper<dim>::transferBMatrixToSymVoigtBMatrix(
*shapes_derivatives_filtered_it, B_tensor, nb_nodes_per_element);
VoigtHelper<dim>::transferBMatrixToBL2(*shapes_derivatives_filtered_it,
grad_u, B2_tensor,
nb_nodes_per_element);
B_tensor += B2_tensor;
auto S_vect = Material::stressToVoigt<dim>(S);
Matrix<Real> S_voigt(S_vect.storage(), stress_size, 1);
r.template mul<true, false>(B_tensor, S_voigt);
}
delete shapesd_filtered;
/// compute @f$ k_e = \int_e \mathbf{B}^t * \mathbf{D} * \mathbf{B}@f$
auto * r_e = new Array<Real>(nb_element, bt_s_size, "r_e");
fem.integrate(*bt_s, *r_e, bt_s_size, type, ghost_type, elem_filter);
delete bt_s;
model.getDOFManager().assembleElementalArrayLocalArray(
*r_e, internal_force, type, ghost_type, -1., elem_filter);
delete r_e;
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void Material::computePotentialEnergyByElements() {
AKANTU_DEBUG_IN();
for (auto type : element_filter.elementTypes(spatial_dimension, _not_ghost)) {
computePotentialEnergy(type);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void Material::computePotentialEnergy(ElementType /*unused*/) {
AKANTU_DEBUG_IN();
AKANTU_TO_IMPLEMENT();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
Real Material::getPotentialEnergy() {
AKANTU_DEBUG_IN();
Real epot = 0.;
computePotentialEnergyByElements();
/// integrate the potential energy for each type of elements
for (auto type : element_filter.elementTypes(spatial_dimension, _not_ghost)) {
epot += fem.integrate(potential_energy(type, _not_ghost), type, _not_ghost,
element_filter(type, _not_ghost));
}
AKANTU_DEBUG_OUT();
return epot;
}
/* -------------------------------------------------------------------------- */
Real Material::getPotentialEnergy(ElementType & type, UInt index) {
AKANTU_DEBUG_IN();
Real epot = 0.;
Vector<Real> epot_on_quad_points(fem.getNbIntegrationPoints(type));
computePotentialEnergyByElement(type, index, epot_on_quad_points);
epot = fem.integrate(epot_on_quad_points, type, element_filter(type)(index));
AKANTU_DEBUG_OUT();
return epot;
}
/* -------------------------------------------------------------------------- */
Real Material::getEnergy(const std::string & type) {
AKANTU_DEBUG_IN();
if (type == "potential") {
return getPotentialEnergy();
}
AKANTU_DEBUG_OUT();
return 0.;
}
/* -------------------------------------------------------------------------- */
Real Material::getEnergy(const std::string & energy_id, ElementType type,
UInt index) {
AKANTU_DEBUG_IN();
if (energy_id == "potential") {
return getPotentialEnergy(type, index);
}
AKANTU_DEBUG_OUT();
return 0.;
}
/* -------------------------------------------------------------------------- */
void Material::initElementalFieldInterpolation(
const ElementTypeMapArray<Real> & interpolation_points_coordinates) {
AKANTU_DEBUG_IN();
this->fem.initElementalFieldInterpolationFromIntegrationPoints(
interpolation_points_coordinates, this->interpolation_points_matrices,
this->interpolation_inverse_coordinates, &(this->element_filter));
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void Material::interpolateStress(ElementTypeMapArray<Real> & result,
const GhostType ghost_type) {
this->fem.interpolateElementalFieldFromIntegrationPoints(
this->stress, this->interpolation_points_matrices,
this->interpolation_inverse_coordinates, result, ghost_type,
&(this->element_filter));
}
/* -------------------------------------------------------------------------- */
void Material::interpolateStressOnFacets(
ElementTypeMapArray<Real> & result,
ElementTypeMapArray<Real> & by_elem_result, const GhostType ghost_type) {
interpolateStress(by_elem_result, ghost_type);
UInt stress_size = this->stress.getNbComponent();
const Mesh & mesh = this->model.getMesh();
const Mesh & mesh_facets = mesh.getMeshFacets();
for (auto type : element_filter.elementTypes(spatial_dimension, ghost_type)) {
Array<UInt> & elem_fil = element_filter(type, ghost_type);
Array<Real> & by_elem_res = by_elem_result(type, ghost_type);
UInt nb_element = elem_fil.size();
UInt nb_element_full = this->model.getMesh().getNbElement(type, ghost_type);
UInt nb_interpolation_points_per_elem =
by_elem_res.size() / nb_element_full;
const Array<Element> & facet_to_element =
mesh_facets.getSubelementToElement(type, ghost_type);
ElementType type_facet = Mesh::getFacetType(type);
UInt nb_facet_per_elem = facet_to_element.getNbComponent();
UInt nb_quad_per_facet =
nb_interpolation_points_per_elem / nb_facet_per_elem;
Element element_for_comparison{type, 0, ghost_type};
const Array<std::vector<Element>> * element_to_facet = nullptr;
GhostType current_ghost_type = _casper;
Array<Real> * result_vec = nullptr;
Array<Real>::const_matrix_iterator result_it =
by_elem_res.begin_reinterpret(
stress_size, nb_interpolation_points_per_elem, nb_element_full);
for (UInt el = 0; el < nb_element; ++el) {
UInt global_el = elem_fil(el);
element_for_comparison.element = global_el;
for (UInt f = 0; f < nb_facet_per_elem; ++f) {
Element facet_elem = facet_to_element(global_el, f);
UInt global_facet = facet_elem.element;
if (facet_elem.ghost_type != current_ghost_type) {
current_ghost_type = facet_elem.ghost_type;
element_to_facet = &mesh_facets.getElementToSubelement(
type_facet, current_ghost_type);
result_vec = &result(type_facet, current_ghost_type);
}
bool is_second_element =
(*element_to_facet)(global_facet)[0] != element_for_comparison;
for (UInt q = 0; q < nb_quad_per_facet; ++q) {
Vector<Real> result_local(result_vec->storage() +
(global_facet * nb_quad_per_facet + q) *
result_vec->getNbComponent() +
static_cast<UInt>(is_second_element) *
stress_size,
stress_size);
const Matrix<Real> & result_tmp(result_it[global_el]);
result_local = result_tmp(f * nb_quad_per_facet + q);
}
}
}
}
}
-/* -------------------------------------------------------------------------- */
-template <typename T>
-const Array<T> & Material::getArray(const ID & /*vect_id*/,
- ElementType /*type*/,
- GhostType /*ghost_type*/) const {
- AKANTU_TO_IMPLEMENT();
- return NULL;
-}
-
-/* -------------------------------------------------------------------------- */
-template <typename T>
-Array<T> & Material::getArray(const ID & /*vect_id*/, ElementType /*type*/,
- GhostType /*ghost_type*/) {
- AKANTU_TO_IMPLEMENT();
-}
-
-/* -------------------------------------------------------------------------- */
-template <>
-const Array<Real> & Material::getArray(const ID & vect_id, ElementType type,
- GhostType ghost_type) const {
- std::stringstream sstr;
- std::string ghost_id;
- if (ghost_type == _ghost) {
- ghost_id = ":ghost";
- }
- sstr << getID() << ":" << vect_id << ":" << type << ghost_id;
-
- ID fvect_id = sstr.str();
- try {
- return Memory::getArray<Real>(fvect_id);
- } catch (debug::Exception & e) {
- AKANTU_SILENT_EXCEPTION("The material " << name << "(" << getID()
- << ") does not contain a vector "
- << vect_id << " (" << fvect_id
- << ") [" << e << "]");
- }
-}
-
-/* -------------------------------------------------------------------------- */
-template <>
-Array<Real> & Material::getArray(const ID & vect_id, ElementType type,
- GhostType ghost_type) {
- std::stringstream sstr;
- std::string ghost_id;
- if (ghost_type == _ghost) {
- ghost_id = ":ghost";
- }
- sstr << getID() << ":" << vect_id << ":" << type << ghost_id;
-
- ID fvect_id = sstr.str();
- try {
- return Memory::getArray<Real>(fvect_id);
- } catch (debug::Exception & e) {
- AKANTU_SILENT_EXCEPTION("The material " << name << "(" << getID()
- << ") does not contain a vector "
- << vect_id << " (" << fvect_id
- << ") [" << e << "]");
- }
-}
-
-/* -------------------------------------------------------------------------- */
-template <>
-const Array<UInt> & Material::getArray(const ID & vect_id, ElementType type,
- GhostType ghost_type) const {
- std::stringstream sstr;
- std::string ghost_id;
- if (ghost_type == _ghost) {
- ghost_id = ":ghost";
- }
- sstr << getID() << ":" << vect_id << ":" << type << ghost_id;
-
- ID fvect_id = sstr.str();
- try {
- return Memory::getArray<UInt>(fvect_id);
- } catch (debug::Exception & e) {
- AKANTU_SILENT_EXCEPTION("The material " << name << "(" << getID()
- << ") does not contain a vector "
- << vect_id << " (" << fvect_id
- << ") [" << e << "]");
- }
-}
-
-/* -------------------------------------------------------------------------- */
-template <>
-Array<UInt> & Material::getArray(const ID & vect_id, ElementType type,
- GhostType ghost_type) {
- std::stringstream sstr;
- std::string ghost_id;
- if (ghost_type == _ghost) {
- ghost_id = ":ghost";
- }
- sstr << getID() << ":" << vect_id << ":" << type << ghost_id;
-
- ID fvect_id = sstr.str();
- try {
- return Memory::getArray<UInt>(fvect_id);
- } catch (debug::Exception & e) {
- AKANTU_SILENT_EXCEPTION("The material " << name << "(" << getID()
- << ") does not contain a vector "
- << vect_id << "(" << fvect_id
- << ") [" << e << "]");
- }
-}
-
/* -------------------------------------------------------------------------- */
template <typename T>
const InternalField<T> &
Material::getInternal([[gnu::unused]] const ID & int_id) const {
AKANTU_TO_IMPLEMENT();
return NULL;
}
/* -------------------------------------------------------------------------- */
template <typename T>
InternalField<T> & Material::getInternal([[gnu::unused]] const ID & int_id) {
AKANTU_TO_IMPLEMENT();
return NULL;
}
/* -------------------------------------------------------------------------- */
template <>
const InternalField<Real> & 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 <> InternalField<Real> & 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 <>
const InternalField<UInt> & Material::getInternal(const ID & int_id) const {
auto it = internal_vectors_uint.find(getID() + ":" + int_id);
if (it == internal_vectors_uint.end()) {
AKANTU_SILENT_EXCEPTION("The material " << name << "(" << getID()
<< ") does not contain an internal "
<< int_id << " ("
<< (getID() + ":" + int_id) << ")");
}
return *it->second;
}
/* -------------------------------------------------------------------------- */
template <> InternalField<UInt> & Material::getInternal(const ID & int_id) {
auto it = internal_vectors_uint.find(getID() + ":" + int_id);
if (it == internal_vectors_uint.end()) {
AKANTU_SILENT_EXCEPTION("The material " << name << "(" << getID()
<< ") does not contain an internal "
<< int_id << " ("
<< (getID() + ":" + int_id) << ")");
}
return *it->second;
}
+/* -------------------------------------------------------------------------- */
+template <typename T>
+const Array<T> & Material::getArray(const ID & vect_id, ElementType type,
+ GhostType ghost_type) const {
+ try {
+ return this->template getInternal<T>(vect_id)(type, ghost_type);
+ } catch (debug::Exception & e) {
+ AKANTU_SILENT_EXCEPTION("The material " << name << "(" << getID()
+ << ") does not contain a vector "
+ << vect_id << " [" << e << "]");
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+template <typename T>
+Array<T> & Material::getArray(const ID & vect_id, ElementType type,
+ GhostType ghost_type) {
+ try {
+ return this->template getInternal<T>(vect_id)(type, ghost_type);
+ } catch (debug::Exception & e) {
+ AKANTU_SILENT_EXCEPTION("The material " << name << "(" << getID()
+ << ") does not contain a vector "
+ << vect_id << " [" << e << "]");
+ }
+}
+
+template const Array<Real> & Material::getArray(const ID & vect_id,
+ ElementType type,
+ GhostType ghost_type) const;
+/* -------------------------------------------------------------------------- */
+template Array<Real> & Material::getArray(const ID & vect_id, ElementType type,
+ GhostType ghost_type);
+/* -------------------------------------------------------------------------- */
+template const Array<UInt> & Material::getArray(const ID & vect_id,
+ ElementType type,
+ GhostType ghost_type) const;
+/* -------------------------------------------------------------------------- */
+template Array<UInt> & Material::getArray(const ID & vect_id, ElementType type,
+ GhostType ghost_type);
+
/* -------------------------------------------------------------------------- */
void Material::addElements(const Array<Element> & elements_to_add) {
AKANTU_DEBUG_IN();
- UInt mat_id = model.getInternalIndexFromID(getID());
- Array<Element>::const_iterator<Element> el_begin = elements_to_add.begin();
- Array<Element>::const_iterator<Element> el_end = elements_to_add.end();
- for (; el_begin != el_end; ++el_begin) {
- const Element & element = *el_begin;
- Array<UInt> & mat_indexes =
- model.getMaterialByElement(element.type, element.ghost_type);
- Array<UInt> & mat_loc_num =
- model.getMaterialLocalNumbering(element.type, element.ghost_type);
-
- UInt index =
- this->addElement(element.type, element.element, element.ghost_type);
- mat_indexes(element.element) = mat_id;
- mat_loc_num(element.element) = index;
+
+ UInt mat_id = model.getMaterialIndex(name);
+ for (const auto & element : elements_to_add) {
+ auto index = this->addElement(element);
+ model.material_index(element) = mat_id;
+ model.material_local_numbering(element) = index;
}
this->resizeInternals();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void Material::removeElements(const Array<Element> & elements_to_remove) {
AKANTU_DEBUG_IN();
- Array<Element>::const_iterator<Element> el_begin = elements_to_remove.begin();
- Array<Element>::const_iterator<Element> el_end = elements_to_remove.end();
+ auto el_begin = elements_to_remove.begin();
+ auto el_end = elements_to_remove.end();
- if (el_begin == el_end) {
+ if (elements_to_remove.empty()) {
return;
}
- ElementTypeMapArray<UInt> material_local_new_numbering(
- "remove mat filter elem", getID(), getMemoryID());
+ auto & mesh = this->model.getMesh();
- Element element;
- for (auto ghost_type : ghost_types) {
- element.ghost_type = ghost_type;
+ ElementTypeMapArray<UInt> material_local_new_numbering(
+ "remove mat filter elem", id);
- for (const auto & type : element_filter.elementTypes(
- _ghost_type = ghost_type, _element_kind = _ek_not_defined)) {
- element.type = type;
+ material_local_new_numbering.initialize(
+ mesh, _element_filter = &element_filter, _element_kind = _ek_not_defined,
+ _with_nb_element = true);
- Array<UInt> & elem_filter = this->element_filter(type, ghost_type);
- Array<UInt> & mat_loc_num =
- this->model.getMaterialLocalNumbering(type, ghost_type);
+ ElementTypeMapArray<UInt> element_filter_tmp("element_filter_tmp", id);
- if (!material_local_new_numbering.exists(type, ghost_type)) {
- material_local_new_numbering.alloc(elem_filter.size(), 1, type,
- ghost_type);
- }
- Array<UInt> & mat_renumbering =
- material_local_new_numbering(type, ghost_type);
+ element_filter_tmp.initialize(mesh, _element_filter = &element_filter,
+ _element_kind = _ek_not_defined);
- UInt nb_element = elem_filter.size();
- Array<UInt> elem_filter_tmp;
+ ElementTypeMap<UInt> new_ids, element_ids;
- UInt new_id = 0;
- for (UInt el = 0; el < nb_element; ++el) {
- element.element = elem_filter(el);
+ for_each_element(
+ mesh,
+ [&](auto && el) {
+ if (not new_ids(el.type, el.ghost_type)) {
+ element_ids(el.type, el.ghost_type) = 0;
+ }
- if (std::find(el_begin, el_end, element) == el_end) {
- elem_filter_tmp.push_back(element.element);
+ auto & element_id = element_ids(el.type, el.ghost_type);
+ auto l_el = Element{el.type, element_id, el.ghost_type};
+ if (std::find(el_begin, el_end, el) != el_end) {
+ material_local_new_numbering(l_el) = UInt(-1);
+ return;
+ }
- mat_renumbering(el) = new_id;
- mat_loc_num(element.element) = new_id;
- ++new_id;
- } else {
- mat_renumbering(el) = UInt(-1);
+ element_filter_tmp(el.type, el.ghost_type).push_back(el.element);
+ if (not new_ids(el.type, el.ghost_type)) {
+ new_ids(el.type, el.ghost_type) = 0;
}
- }
- elem_filter.resize(elem_filter_tmp.size());
- elem_filter.copy(elem_filter_tmp);
+ auto & new_id = new_ids(el.type, el.ghost_type);
+
+ material_local_new_numbering(l_el) = new_id;
+ model.material_local_numbering(el) = new_id;
+
+ ++new_id;
+ ++element_id;
+ },
+ _element_filter = &element_filter, _element_kind = _ek_not_defined);
+
+ for (auto ghost_type : ghost_types) {
+ for (const auto & type : element_filter.elementTypes(
+ _ghost_type = ghost_type, _element_kind = _ek_not_defined)) {
+ element_filter(type, ghost_type)
+ .copy(element_filter_tmp(type, ghost_type));
}
}
for (auto it = internal_vectors_real.begin();
it != internal_vectors_real.end(); ++it) {
it->second->removeIntegrationPoints(material_local_new_numbering);
}
for (auto it = internal_vectors_uint.begin();
it != internal_vectors_uint.end(); ++it) {
it->second->removeIntegrationPoints(material_local_new_numbering);
}
for (auto it = internal_vectors_bool.begin();
it != internal_vectors_bool.end(); ++it) {
it->second->removeIntegrationPoints(material_local_new_numbering);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void Material::resizeInternals() {
AKANTU_DEBUG_IN();
for (auto it = internal_vectors_real.begin();
it != internal_vectors_real.end(); ++it) {
it->second->resize();
}
for (auto it = internal_vectors_uint.begin();
it != internal_vectors_uint.end(); ++it) {
it->second->resize();
}
for (auto it = internal_vectors_bool.begin();
it != internal_vectors_bool.end(); ++it) {
it->second->resize();
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void Material::onElementsAdded(const Array<Element> & /*unused*/,
const NewElementsEvent & /*unused*/) {
this->resizeInternals();
}
/* -------------------------------------------------------------------------- */
void Material::onElementsRemoved(
const Array<Element> & element_list,
const ElementTypeMapArray<UInt> & new_numbering,
[[gnu::unused]] const RemovedElementsEvent & event) {
UInt my_num = model.getInternalIndexFromID(getID());
ElementTypeMapArray<UInt> material_local_new_numbering(
- "remove mat filter elem", getID(), getMemoryID());
+ "remove mat filter elem", getID());
auto el_begin = element_list.begin();
auto el_end = element_list.end();
for (auto && gt : ghost_types) {
for (auto && type :
new_numbering.elementTypes(_all_dimensions, gt, _ek_not_defined)) {
if (not element_filter.exists(type, gt) ||
element_filter(type, gt).empty()) {
continue;
}
auto & elem_filter = element_filter(type, gt);
- auto & mat_indexes = this->model.getMaterialByElement(type, gt);
- auto & mat_loc_num = this->model.getMaterialLocalNumbering(type, gt);
+ auto & mat_indexes = this->model.material_index(type, gt);
+ auto & mat_loc_num = this->model.material_local_numbering(type, gt);
auto nb_element = this->model.getMesh().getNbElement(type, gt);
// all materials will resize of the same size...
mat_indexes.resize(nb_element);
mat_loc_num.resize(nb_element);
if (!material_local_new_numbering.exists(type, gt)) {
material_local_new_numbering.alloc(elem_filter.size(), 1, type, gt);
}
auto & mat_renumbering = material_local_new_numbering(type, gt);
const auto & renumbering = new_numbering(type, gt);
Array<UInt> elem_filter_tmp;
UInt ni = 0;
Element el{type, 0, gt};
for (UInt i = 0; i < elem_filter.size(); ++i) {
el.element = elem_filter(i);
if (std::find(el_begin, el_end, el) == el_end) {
UInt new_el = renumbering(el.element);
AKANTU_DEBUG_ASSERT(new_el != UInt(-1),
"A not removed element as been badly renumbered");
elem_filter_tmp.push_back(new_el);
mat_renumbering(i) = ni;
mat_indexes(new_el) = my_num;
mat_loc_num(new_el) = ni;
++ni;
} else {
mat_renumbering(i) = UInt(-1);
}
}
elem_filter.resize(elem_filter_tmp.size());
elem_filter.copy(elem_filter_tmp);
}
}
for (auto it = internal_vectors_real.begin();
it != internal_vectors_real.end(); ++it) {
it->second->removeIntegrationPoints(material_local_new_numbering);
}
for (auto it = internal_vectors_uint.begin();
it != internal_vectors_uint.end(); ++it) {
it->second->removeIntegrationPoints(material_local_new_numbering);
}
for (auto it = internal_vectors_bool.begin();
it != internal_vectors_bool.end(); ++it) {
it->second->removeIntegrationPoints(material_local_new_numbering);
}
}
/* -------------------------------------------------------------------------- */
void Material::beforeSolveStep() { this->savePreviousState(); }
/* -------------------------------------------------------------------------- */
void Material::afterSolveStep(bool converged) {
if (not converged) {
this->restorePreviousState();
return;
}
for (const auto & type : element_filter.elementTypes(
_all_dimensions, _not_ghost, _ek_not_defined)) {
this->updateEnergies(type);
}
}
/* -------------------------------------------------------------------------- */
void Material::onDamageIteration() { this->savePreviousState(); }
/* -------------------------------------------------------------------------- */
void Material::onDamageUpdate() {
for (const auto & type : element_filter.elementTypes(
_all_dimensions, _not_ghost, _ek_not_defined)) {
this->updateEnergiesAfterDamage(type);
}
}
/* -------------------------------------------------------------------------- */
void Material::onDump() {
if (this->isFiniteDeformation()) {
this->computeAllCauchyStresses(_not_ghost);
}
}
/* -------------------------------------------------------------------------- */
void Material::printself(std::ostream & stream, int indent) const {
std::string space(indent, AKANTU_INDENT);
std::string type = getID().substr(getID().find_last_of(':') + 1);
stream << space << "Material " << type << " [" << std::endl;
Parsable::printself(stream, indent);
stream << space << "]" << std::endl;
}
/* -------------------------------------------------------------------------- */
/// extrapolate internal values
void Material::extrapolateInternal(const ID & id, const Element & element,
[[gnu::unused]] const Matrix<Real> & point,
Matrix<Real> & extrapolated) {
if (this->isInternal<Real>(id, element.kind())) {
UInt nb_element =
this->element_filter(element.type, element.ghost_type).size();
const ID name = this->getID() + ":" + id;
UInt nb_quads =
this->internal_vectors_real[name]->getFEEngine().getNbIntegrationPoints(
element.type, element.ghost_type);
const Array<Real> & internal =
this->getArray<Real>(id, element.type, element.ghost_type);
UInt nb_component = internal.getNbComponent();
Array<Real>::const_matrix_iterator internal_it =
internal.begin_reinterpret(nb_component, nb_quads, nb_element);
Element local_element = this->convertToLocalElement(element);
/// instead of really extrapolating, here the value of the first GP
/// is copied into the result vector. This works only for linear
/// elements
/// @todo extrapolate!!!!
AKANTU_DEBUG_WARNING("This is a fix, values are not truly extrapolated");
const Matrix<Real> & values = internal_it[local_element.element];
UInt index = 0;
Vector<Real> tmp(nb_component);
for (UInt j = 0; j < values.cols(); ++j) {
tmp = values(j);
if (tmp.norm() > 0) {
index = j;
break;
}
}
for (UInt i = 0; i < extrapolated.size(); ++i) {
extrapolated(i) = values(index);
}
} else {
Matrix<Real> default_values(extrapolated.rows(), extrapolated.cols(), 0.);
extrapolated = default_values;
}
}
/* -------------------------------------------------------------------------- */
void Material::applyEigenGradU(const Matrix<Real> & prescribed_eigen_grad_u,
const GhostType ghost_type) {
for (auto && type : element_filter.elementTypes(_all_dimensions, _not_ghost,
_ek_not_defined)) {
if (element_filter(type, ghost_type).empty()) {
continue;
}
for (auto & eigengradu : make_view(this->eigengradu(type, ghost_type),
spatial_dimension, spatial_dimension)) {
eigengradu = prescribed_eigen_grad_u;
}
}
}
/* -------------------------------------------------------------------------- */
MaterialFactory & Material::getFactory() {
return MaterialFactory::getInstance();
}
} // namespace akantu
diff --git a/src/model/solid_mechanics/material.hh b/src/model/solid_mechanics/material.hh
index cbb2c63da..4ae6f4b1c 100644
--- a/src/model/solid_mechanics/material.hh
+++ b/src/model/solid_mechanics/material.hh
@@ -1,705 +1,706 @@
/**
* @file material.hh
*
* @author Daniel Pino Muñoz <daniel.pinomunoz@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
* @author Marco Vocialta <marco.vocialta@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Wed Feb 21 2018
*
* @brief Mother class for all materials
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#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<Material, ID, UInt, const ID &, SolidMechanicsModel &, const ID &>;
/**
* 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<Real> & tangent_matrix,
* GhostType ghost_type = _not_ghost);
* \endcode
*
*/
-class Material : public Memory,
- public DataAccessor<Element>,
+class Material : public DataAccessor<Element>,
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, UInt 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<Real> & /*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
virtual void
computePotentialEnergyByElement(ElementType /*type*/, UInt /*index*/,
Vector<Real> & /*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<Real> & points,
Matrix<Real> & 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 <typename T> void registerInternal(InternalField<T> & /*vect*/) {
AKANTU_TO_IMPLEMENT();
}
template <typename T> void unregisterInternal(InternalField<T> & /*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 stress in the previous_stress if needed
virtual void savePreviousState();
/// restore the stress 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 UInt addElement(ElementType type, UInt element, GhostType ghost_type);
inline UInt addElement(const Element & element);
/// add many elements at once
void addElements(const Array<Element> & elements_to_add);
/// remove many element at once
void removeElements(const Array<Element> & 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<Real> & 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<Real> & result,
ElementTypeMapArray<Real> & 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<Real> & interpolation_points_coordinates);
/* ------------------------------------------------------------------------ */
/* Common part */
/* ------------------------------------------------------------------------ */
protected:
/* ------------------------------------------------------------------------ */
static inline UInt getTangentStiffnessVoigtSize(UInt dim);
/// compute the potential energy by element
void computePotentialEnergyByElements();
/// resize the intenals arrays
virtual void resizeInternals();
/* ------------------------------------------------------------------------ */
/* 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 <UInt dim> void assembleInternalForces(GhostType ghost_type);
template <UInt dim>
void computeAllStressesFromTangentModuli(ElementType type,
GhostType ghost_type);
template <UInt dim>
void assembleStiffnessMatrix(ElementType type, GhostType ghost_type);
/// assembling in finite deformation
template <UInt dim>
void assembleStiffnessMatrixNL(ElementType type, GhostType ghost_type);
template <UInt dim>
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 inline UInt getCauchyStressMatrixSize(UInt dim);
/// Sets the stress matrix according to Bathe et al, IJNME, Vol 9, 353-386,
/// 1975
template <UInt dim>
static inline void setCauchyStressMatrix(const Matrix<Real> & S_t,
Matrix<Real> & sigma);
/// write the stress tensor in the Voigt notation.
template <UInt dim>
static inline decltype(auto) stressToVoigt(const Matrix<Real> & stress) {
return VoigtHelper<dim>::matrixToVoigt(stress);
}
/// write the strain tensor in the Voigt notation.
template <UInt dim>
static inline decltype(auto) strainToVoigt(const Matrix<Real> & strain) {
return VoigtHelper<dim>::matrixToVoigtWithFactors(strain);
}
/// write a voigt vector to stress
template <UInt dim>
static inline void voigtToStress(const Vector<Real> & voigt,
Matrix<Real> & stress) {
VoigtHelper<dim>::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 <UInt dim>
void StoCauchy(ElementType el_type, GhostType ghost_type = _not_ghost);
/// Computation the Cauchy stress the 2nd Piola-Kirchhoff and the deformation
/// gradient
template <UInt dim>
inline void StoCauchy(const Matrix<Real> & F, const Matrix<Real> & S,
Matrix<Real> & sigma, const Real & C33 = 1.0) const;
template <UInt dim>
static inline void gradUToF(const Matrix<Real> & grad_u, Matrix<Real> & F);
template <UInt dim>
static inline decltype(auto) gradUToF(const Matrix<Real> & grad_u);
static inline void rightCauchy(const Matrix<Real> & F, Matrix<Real> & C);
static inline void leftCauchy(const Matrix<Real> & F, Matrix<Real> & B);
template <UInt dim>
static inline void gradUToEpsilon(const Matrix<Real> & grad_u,
Matrix<Real> & epsilon);
template <UInt dim>
static inline decltype(auto) gradUToEpsilon(const Matrix<Real> & grad_u);
template <UInt dim>
static inline void gradUToE(const Matrix<Real> & grad_u,
Matrix<Real> & epsilon);
template <UInt dim>
static inline decltype(auto) gradUToE(const Matrix<Real> & grad_u);
static inline Real stressToVonMises(const Matrix<Real> & 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 UInt getNbData(const Array<Element> & elements,
const SynchronizationTag & tag) const override;
inline void packData(CommunicationBuffer & buffer,
const Array<Element> & elements,
const SynchronizationTag & tag) const override;
inline void unpackData(CommunicationBuffer & buffer,
const Array<Element> & elements,
const SynchronizationTag & tag) override;
template <typename T>
inline void packElementDataHelper(const ElementTypeMapArray<T> & data_to_pack,
CommunicationBuffer & buffer,
const Array<Element> & elements,
const ID & fem_id = ID()) const;
template <typename T>
inline void unpackElementDataHelper(ElementTypeMapArray<T> & data_to_unpack,
CommunicationBuffer & buffer,
const Array<Element> & elements,
const ID & fem_id = ID());
/* ------------------------------------------------------------------------ */
/* MeshEventHandler inherited members */
/* ------------------------------------------------------------------------ */
public:
/* ------------------------------------------------------------------------ */
void onNodesAdded(const Array<UInt> & /*unused*/,
const NewNodesEvent & /*unused*/) override{};
void onNodesRemoved(const Array<UInt> & /*unused*/,
const Array<UInt> & /*unused*/,
const RemovedNodesEvent & /*unused*/) override{};
void onElementsAdded(const Array<Element> & element_list,
const NewElementsEvent & event) override;
void onElementsRemoved(const Array<Element> & element_list,
const ElementTypeMapArray<UInt> & new_numbering,
const RemovedElementsEvent & event) override;
void onElementsChanged(const Array<Element> & /*unused*/,
const Array<Element> & /*unused*/,
const ElementTypeMapArray<UInt> & /*unused*/,
const ChangedElementsEvent & /*unused*/) 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_GET_MACRO(Model, model, const SolidMechanicsModel &)
- AKANTU_GET_MACRO(ID, Memory::getID(), const ID &);
+ 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, UInt);
/// 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(ElementType & type, UInt 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, ElementType type,
UInt index);
AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(ElementFilter, element_filter, UInt);
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<Real> &);
AKANTU_GET_MACRO(Stress, stress, const ElementTypeMapArray<Real> &);
AKANTU_GET_MACRO(ElementFilter, element_filter,
const ElementTypeMapArray<UInt> &);
AKANTU_GET_MACRO(FEEngine, fem, FEEngine &);
bool isNonLocal() const { return is_non_local; }
template <typename T>
const Array<T> & getArray(const ID & id, ElementType type,
GhostType ghost_type = _not_ghost) const;
template <typename T>
Array<T> & getArray(const ID & id, ElementType type,
GhostType ghost_type = _not_ghost);
template <typename T>
const InternalField<T> & getInternal(const ID & id) const;
template <typename T> InternalField<T> & getInternal(const ID & id);
template <typename T>
inline bool isInternal(const ID & id, ElementKind element_kind) const;
template <typename T>
ElementTypeMap<UInt> getInternalDataPerElem(const ID & id,
ElementKind element_kind) const;
bool isFiniteDeformation() const { return finite_deformation; }
bool isInelasticDeformation() const { return inelastic_deformation; }
template <typename T> inline void setParam(const ID & param, T value);
inline const Parameter & getParam(const ID & param) const;
template <typename T>
void flattenInternal(const std::string & field_id,
ElementTypeMapArray<T> & internal_flat,
GhostType ghost_type = _not_ghost,
ElementKind element_kind = _ek_not_defined) const;
/// apply a constant eigengrad_u everywhere in the material
virtual void applyEigenGradU(const Matrix<Real> & 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;
+ bool is_init{false};
std::map<ID, InternalField<Real> *> internal_vectors_real;
std::map<ID, InternalField<UInt> *> internal_vectors_uint;
std::map<ID, InternalField<bool> *> internal_vectors_bool;
protected:
+ ID id;
+
/// Link to the fem object in the model
FEEngine & fem;
/// Finite deformation
- bool finite_deformation;
+ bool finite_deformation{false};
/// Finite deformation
- bool inelastic_deformation;
+ bool inelastic_deformation{false};
/// material name
std::string name;
/// The model to witch the material belong
SolidMechanicsModel & model;
/// density : rho
- Real rho;
+ Real rho{0.};
/// spatial dimension
UInt spatial_dimension;
/// list of element handled by the material
ElementTypeMapArray<UInt> element_filter;
/// stresses arrays ordered by element types
InternalField<Real> stress;
/// eigengrad_u arrays ordered by element types
InternalField<Real> eigengradu;
/// grad_u arrays ordered by element types
InternalField<Real> gradu;
/// Green Lagrange strain (Finite deformation)
InternalField<Real> green_strain;
/// Second Piola-Kirchhoff stress tensor arrays ordered by element types
/// (Finite deformation)
InternalField<Real> piola_kirchhoff_2;
/// potential energy by element
InternalField<Real> potential_energy;
/// tell if using in non local mode or not
- bool is_non_local;
+ bool is_non_local{false};
/// tell if the material need the previous stress state
- bool use_previous_stress;
+ bool use_previous_stress{false};
/// tell if the material need the previous strain state
- bool use_previous_gradu;
+ bool use_previous_gradu{false};
/// elemental field interpolation coordinates
InternalField<Real> interpolation_inverse_coordinates;
/// elemental field interpolation points
InternalField<Real> interpolation_points_matrices;
/// vector that contains the names of all the internals that need to
/// be transferred when material interfaces move
std::vector<ID> internals_to_transfer;
private:
/// eigen_grad_u for the parser
Matrix<Real> 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]] Matrix<Real> & grad_u = std::get<0>(data); \
[[gnu::unused]] Matrix<Real> & 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 = \
this->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]] Matrix<Real> & grad_u = std::get<0>(data); \
[[gnu::unused]] Matrix<Real> & sigma = std::get<1>(data); \
Matrix<Real> & 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) \
[](UInt dim, const ID &, SolidMechanicsModel & model, \
const ID & id) /* NOLINT */ \
-> std::unique_ptr< \
Material> { /* NOLINT */ \
switch (dim) { \
case 1: \
return std::make_unique<mat_name<1>>(/* NOLINT */ \
model, id); \
case 2: \
return std::make_unique<mat_name<2>>(/* NOLINT */ \
model, id); \
case 3: \
return std::make_unique<mat_name<3>>(/* 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 [[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/materials/internal_field_tmpl.hh b/src/model/solid_mechanics/materials/internal_field_tmpl.hh
index 623e78b15..c61dffe55 100644
--- a/src/model/solid_mechanics/materials/internal_field_tmpl.hh
+++ b/src/model/solid_mechanics/materials/internal_field_tmpl.hh
@@ -1,322 +1,321 @@
/**
* @file internal_field_tmpl.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Wed Nov 13 2013
* @date last modification: Wed Feb 21 2018
*
* @brief Material internal properties
*
*
* Copyright (©) 2014-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "material.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_INTERNAL_FIELD_TMPL_HH_
#define AKANTU_INTERNAL_FIELD_TMPL_HH_
namespace akantu {
/* -------------------------------------------------------------------------- */
template <typename T>
InternalField<T>::InternalField(const ID & id, Material & material)
- : ElementTypeMapArray<T>(id, material.getID(), material.getMemoryID()),
+ : ElementTypeMapArray<T>(id, material.getID()),
material(material), fem(&(material.getModel().getFEEngine())),
element_filter(material.getElementFilter()),
spatial_dimension(material.getModel().getSpatialDimension()) {}
/* -------------------------------------------------------------------------- */
template <typename T>
InternalField<T>::InternalField(
const ID & id, Material & material, FEEngine & fem,
const ElementTypeMapArray<UInt> & element_filter)
- : ElementTypeMapArray<T>(id, material.getID(), material.getMemoryID()),
+ : ElementTypeMapArray<T>(id, material.getID()),
material(material), fem(&fem), element_filter(element_filter),
spatial_dimension(material.getSpatialDimension()) {}
/* -------------------------------------------------------------------------- */
template <typename T>
InternalField<T>::InternalField(
const ID & id, Material & material, UInt dim, FEEngine & fem,
const ElementTypeMapArray<UInt> & element_filter)
- : ElementTypeMapArray<T>(id, material.getID(), material.getMemoryID()),
+ : ElementTypeMapArray<T>(id, material.getID()),
material(material), fem(&fem), element_filter(element_filter),
spatial_dimension(dim) {}
/* -------------------------------------------------------------------------- */
template <typename T>
InternalField<T>::InternalField(const ID & id, const InternalField<T> & other)
- : ElementTypeMapArray<T>(id, other.material.getID(),
- other.material.getMemoryID()),
+ : ElementTypeMapArray<T>(id, other.material.getID()),
material(other.material), fem(other.fem),
element_filter(other.element_filter), default_value(other.default_value),
spatial_dimension(other.spatial_dimension),
element_kind(other.element_kind), nb_component(other.nb_component) {
AKANTU_DEBUG_ASSERT(other.is_init,
"Cannot create a copy of a non initialized field");
this->internalInitialize(this->nb_component);
}
/* -------------------------------------------------------------------------- */
template <typename T> InternalField<T>::~InternalField() {
if (this->is_init) {
this->material.unregisterInternal(*this);
}
}
/* -------------------------------------------------------------------------- */
template <typename T> void InternalField<T>::setFEEngine(FEEngine & fe_engine) {
this->fem = &fe_engine;
}
/* -------------------------------------------------------------------------- */
template <typename T>
void InternalField<T>::setElementKind(ElementKind element_kind) {
this->element_kind = element_kind;
}
/* -------------------------------------------------------------------------- */
template <typename T> void InternalField<T>::initialize(UInt nb_component) {
internalInitialize(nb_component);
}
/* -------------------------------------------------------------------------- */
template <typename T> void InternalField<T>::initializeHistory() {
if (!previous_values) {
previous_values =
std::make_unique<InternalField<T>>("previous_" + this->getID(), *this);
}
}
/* -------------------------------------------------------------------------- */
template <typename T> void InternalField<T>::resize() {
if (!this->is_init) {
return;
}
for (auto ghost : ghost_types) {
for (const auto & type : this->filterTypes(ghost)) {
UInt nb_element = this->element_filter(type, ghost).size();
UInt nb_quadrature_points =
this->fem->getNbIntegrationPoints(type, ghost);
UInt new_size = nb_element * nb_quadrature_points;
UInt old_size = 0;
Array<T> * vect = nullptr;
if (this->exists(type, ghost)) {
vect = &(this->operator()(type, ghost));
old_size = vect->size();
vect->resize(new_size);
} else {
vect = &(this->alloc(nb_element * nb_quadrature_points, nb_component,
type, ghost));
}
this->setArrayValues(vect->storage() + old_size * vect->getNbComponent(),
vect->storage() + new_size * vect->getNbComponent());
}
}
}
/* -------------------------------------------------------------------------- */
template <typename T> void InternalField<T>::setDefaultValue(const T & value) {
this->default_value = value;
this->reset();
}
/* -------------------------------------------------------------------------- */
template <typename T> void InternalField<T>::reset() {
for (auto ghost_type : ghost_types) {
for (const auto & type : this->elementTypes(ghost_type)) {
Array<T> & vect = (*this)(type, ghost_type);
//vect.zero();
this->setArrayValues(
vect.storage(), vect.storage() + vect.size() * vect.getNbComponent());
}
}
}
/* -------------------------------------------------------------------------- */
template <typename T>
void InternalField<T>::internalInitialize(UInt nb_component) {
if (!this->is_init) {
this->nb_component = nb_component;
for (auto ghost : ghost_types) {
for (const auto & type : this->filterTypes(ghost)) {
UInt nb_element = this->element_filter(type, ghost).size();
UInt nb_quadrature_points =
this->fem->getNbIntegrationPoints(type, ghost);
if (this->exists(type, ghost)) {
this->operator()(type, ghost)
.resize(nb_element * nb_quadrature_points);
} else {
this->alloc(nb_element * nb_quadrature_points, nb_component, type,
ghost);
}
}
}
this->material.registerInternal(*this);
this->is_init = true;
}
this->reset();
if (this->previous_values) {
this->previous_values->internalInitialize(nb_component);
}
}
/* -------------------------------------------------------------------------- */
template <typename T>
void InternalField<T>::setArrayValues(T * begin, T * end) {
for (; begin < end; ++begin) {
*begin = this->default_value;
}
}
/* -------------------------------------------------------------------------- */
template <typename T> void InternalField<T>::saveCurrentValues() {
AKANTU_DEBUG_ASSERT(this->previous_values != nullptr,
"The history of the internal "
<< this->getID() << " has not been activated");
if (not this->is_init) {
return;
}
for (auto ghost_type : ghost_types) {
for (const auto & type : this->elementTypes(ghost_type)) {
(*this->previous_values)(type, ghost_type)
.copy((*this)(type, ghost_type));
}
}
}
/* -------------------------------------------------------------------------- */
template <typename T> void InternalField<T>::restorePreviousValues() {
AKANTU_DEBUG_ASSERT(this->previous_values != nullptr,
"The history of the internal "
<< this->getID() << " has not been activated");
if (not this->is_init) {
return;
}
for (auto ghost_type : ghost_types) {
for (const auto & type : this->elementTypes(ghost_type)) {
(*this)(type, ghost_type)
.copy((*this->previous_values)(type, ghost_type));
}
}
}
/* -------------------------------------------------------------------------- */
template <typename T>
void InternalField<T>::removeIntegrationPoints(
const ElementTypeMapArray<UInt> & new_numbering) {
for (auto ghost_type : ghost_types) {
for (auto type : new_numbering.elementTypes(_all_dimensions, ghost_type,
_ek_not_defined)) {
if (not this->exists(type, ghost_type)) {
continue;
}
Array<T> & vect = (*this)(type, ghost_type);
if (vect.empty()) {
continue;
}
const Array<UInt> & renumbering = new_numbering(type, ghost_type);
UInt nb_quad_per_elem = fem->getNbIntegrationPoints(type, ghost_type);
UInt nb_component = vect.getNbComponent();
Array<T> tmp(renumbering.size() * nb_quad_per_elem, nb_component);
AKANTU_DEBUG_ASSERT(
tmp.size() == vect.size(),
"Something strange append some mater was created from nowhere!!");
AKANTU_DEBUG_ASSERT(
tmp.size() == vect.size(),
"Something strange append some mater was created or disappeared in "
<< vect.getID() << "(" << vect.size() << "!=" << tmp.size()
<< ") "
"!!");
UInt new_size = 0;
for (UInt i = 0; i < renumbering.size(); ++i) {
UInt new_i = renumbering(i);
if (new_i != UInt(-1)) {
memcpy(tmp.storage() + new_i * nb_component * nb_quad_per_elem,
vect.storage() + i * nb_component * nb_quad_per_elem,
nb_component * nb_quad_per_elem * sizeof(T));
++new_size;
}
}
tmp.resize(new_size * nb_quad_per_elem);
vect.copy(tmp);
}
}
}
/* -------------------------------------------------------------------------- */
template <typename T>
void InternalField<T>::printself(std::ostream & stream,
int indent [[gnu::unused]]) const {
stream << "InternalField [ " << this->getID();
#if !defined(AKANTU_NDEBUG)
if (AKANTU_DEBUG_TEST(dblDump)) {
stream << std::endl;
ElementTypeMapArray<T>::printself(stream, indent + 3);
} else {
#endif
stream << " {" << this->getData(_not_ghost).size() << " types - "
<< this->getData(_ghost).size() << " ghost types"
<< "}";
#if !defined(AKANTU_NDEBUG)
}
#endif
stream << " ]";
}
/* -------------------------------------------------------------------------- */
template <>
inline void
ParameterTyped<InternalField<Real>>::setAuto(const ParserParameter & in_param) {
Parameter::setAuto(in_param);
Real r = in_param;
param.setDefaultValue(r);
}
/* -------------------------------------------------------------------------- */
template <typename T> inline InternalField<T>::operator T() const {
return default_value;
}
} // namespace akantu
#endif /* AKANTU_INTERNAL_FIELD_TMPL_HH_ */
diff --git a/src/model/solid_mechanics/solid_mechanics_model.cc b/src/model/solid_mechanics/solid_mechanics_model.cc
index f35e1755b..ca487e418 100644
--- a/src/model/solid_mechanics/solid_mechanics_model.cc
+++ b/src/model/solid_mechanics/solid_mechanics_model.cc
@@ -1,1242 +1,1258 @@
/**
* @file solid_mechanics_model.cc
*
* @author Ramin Aghababaei <ramin.aghababaei@epfl.ch>
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
* @author David Simon Kammer <david.kammer@epfl.ch>
* @author Daniel Pino Muñoz <daniel.pinomunoz@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
* @author Clement Roux <clement.roux@epfl.ch>
* @author Marco Vocialta <marco.vocialta@epfl.ch>
*
* @date creation: Tue Jul 27 2010
* @date last modification: Wed Feb 21 2018
*
* @brief Implementation of the SolidMechanicsModel class
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#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"
#ifdef AKANTU_USE_IOHELPER
#include "dumper_iohelper_paraview.hh"
#endif
#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 memory_id the id of the memory
* @param model_type this is an internal parameter for inheritance purposes
*/
SolidMechanicsModel::SolidMechanicsModel(
- Mesh & mesh, UInt dim, const ID & id, const MemoryID & memory_id,
- std::shared_ptr<DOFManager> dof_manager, const ModelType model_type)
- : Model(mesh, model_type, std::move(dof_manager), dim, id, memory_id),
- BoundaryCondition<SolidMechanicsModel>(),
- material_index("material index", id, memory_id),
- material_local_numbering("material local numbering", id, memory_id) {
+ Mesh & mesh, UInt dim, const ID & id, std::shared_ptr<DOFManager> 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<MyFEEngineType>("SolidMechanicsFEEngine", mesh,
Model::spatial_dimension);
#if defined(AKANTU_USE_IOHELPER)
this->mesh.registerDumper<DumperParaview>("solid_mechanics_model", id, true);
this->mesh.addDumpMesh(mesh, Model::spatial_dimension, _not_ghost,
_ek_regular);
#endif
material_selector = std::make_shared<DefaultMaterialSelector>(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);
#if defined(AKANTU_USE_IOHELPER)
this->mesh.getDumper().setTimeStep(time_step);
#endif
}
/* -------------------------------------------------------------------------- */
/* 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 = UInt(-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<ID, TimeStepSolverType>
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) {
// \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.");
// Check if materials need to recompute the matrix
/*bool need_to_reassemble = true;*/
//bool need_to_reassemble = false;
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());
auto cpos_it = this->current_position->begin(Model::spatial_dimension);
auto cpos_end = this->current_position->end(Model::spatial_dimension);
auto disp_it = this->displacement->begin(Model::spatial_dimension);
for (; cpos_it != cpos_end; ++cpos_it, ++disp_it) {
*cpos_it += *disp_it;
}
this->current_position_release = this->displacement_release;
}
/* -------------------------------------------------------------------------- */
const Array<Real> & 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<Real>(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<Real>::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;
UInt nb_nodes_per_element = mesh.getNbNodesPerElement(type);
UInt nb_element = mesh.getNbElement(type);
auto mat_indexes = material_index(type, ghost_type).begin();
auto mat_loc_num = material_local_numbering(type, ghost_type).begin();
Array<Real> X(0, nb_nodes_per_element * Model::spatial_dimension);
FEEngine::extractNodalToElementField(mesh, *current_position, X, type,
_not_ghost);
auto X_el = X.begin(Model::spatial_dimension, nb_nodes_per_element);
for (UInt el = 0; el < nb_element;
++el, ++X_el, ++mat_indexes, ++mat_loc_num) {
elem.element = *mat_loc_num;
Real el_h = getFEEngine().getElementInradius(*X_el, type);
Real el_c = this->materials[*mat_indexes]->getCelerity(elem);
Real 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.;
UInt nb_nodes = mesh.getNbNodes();
if (this->getDOFManager().hasLumpedMatrix("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 (UInt 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 (UInt i = 0; i < Model::spatial_dimension; ++i) {
if (m(i) > std::numeric_limits<Real>::epsilon()) {
mv2 += v(i) * v(i) * m(i);
}
}
}
ekin += mv2;
}
} else if (this->getDOFManager().hasMatrix("M")) {
Array<Real> 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<Real>(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(ElementType type, UInt index) {
AKANTU_DEBUG_IN();
UInt nb_quadrature_points = getFEEngine().getNbIntegrationPoints(type);
Array<Real> vel_on_quad(nb_quadrature_points, Model::spatial_dimension);
Array<UInt> filter_element(1, 1, index);
getFEEngine().interpolateOnIntegrationPoints(*velocity, vel_on_quad,
Model::spatial_dimension, type,
_not_ghost, filter_element);
auto vit = vel_on_quad.begin(Model::spatial_dimension);
auto vend = vel_on_quad.end(Model::spatial_dimension);
Vector<Real> rho_v2(nb_quadrature_points);
Real rho = materials[material_index(type)(index)]->getRho();
for (UInt q = 0; vit != vend; ++vit, ++q) {
rho_v2(q) = rho * vit->dot(*vit);
}
AKANTU_DEBUG_OUT();
return .5 * getFEEngine().integrate(rho_v2, type, index);
}
/* -------------------------------------------------------------------------- */
Real SolidMechanicsModel::getExternalWork() {
AKANTU_DEBUG_IN();
auto ext_force_it = external_force->begin(Model::spatial_dimension);
auto int_force_it = internal_force->begin(Model::spatial_dimension);
auto boun_it = blocked_dofs->begin(Model::spatial_dimension);
decltype(ext_force_it) incr_or_velo_it;
if (this->method == _static) {
incr_or_velo_it =
this->displacement_increment->begin(Model::spatial_dimension);
} else {
incr_or_velo_it = this->velocity->begin(Model::spatial_dimension);
}
Real work = 0.;
UInt nb_nodes = this->mesh.getNbNodes();
for (UInt n = 0; n < nb_nodes;
++n, ++ext_force_it, ++int_force_it, ++boun_it, ++incr_or_velo_it) {
const auto & int_force = *int_force_it;
const auto & ext_force = *ext_force_it;
const auto & boun = *boun_it;
const auto & incr_or_velo = *incr_or_velo_it;
bool is_local_node = this->mesh.isLocalOrMasterNode(n);
// bool is_not_pbc_slave_node = !this->isPBCSlaveNode(n);
bool count_node = is_local_node; // && is_not_pbc_slave_node;
if (count_node) {
for (UInt i = 0; i < Model::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,
ElementType type, UInt index) {
AKANTU_DEBUG_IN();
if (energy_id == "kinetic") {
return getKineticEnergy(type, index);
}
UInt mat_index = this->material_index(type, _not_ghost)(index);
UInt mat_loc_num = this->material_local_numbering(type, _not_ghost)(index);
Real energy =
this->materials[mat_index]->getEnergy(energy_id, type, mat_loc_num);
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> & element_list,
const NewElementsEvent & event) {
AKANTU_DEBUG_IN();
this->material_index.initialize(mesh, _element_kind = _ek_not_defined,
_with_nb_element = true,
_default_value = UInt(-1));
this->material_local_numbering.initialize(
mesh, _element_kind = _ek_not_defined, _with_nb_element = true,
_default_value = UInt(-1));
- ElementTypeMapArray<UInt> filter("new_element_filter", this->getID(),
- this->getMemoryID());
+ ElementTypeMapArray<UInt> 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> & element_list,
const ElementTypeMapArray<UInt> & new_numbering,
const RemovedElementsEvent & event) {
for (auto & material : materials) {
material->onElementsRemoved(element_list, new_numbering, event);
}
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModel::onNodesAdded(const Array<UInt> & nodes_list,
const NewNodesEvent & event) {
AKANTU_DEBUG_IN();
UInt 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<UInt> & /*element_list*/,
const Array<UInt> & 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) {
MaterialNonLocalInterface * mat_non_local;
if ((mat_non_local =
dynamic_cast<MaterialNonLocalInterface *>(mat.get())) == nullptr) {
continue;
}
ElementTypeMapArray<Real> quadrature_points_coordinates(
- "quadrature_points_coordinates_tmp_nl", this->id, this->memory_id);
+ "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<MaterialNonLocalInterface>(*mat)) {
continue;
}
auto & mat_non_local = dynamic_cast<MaterialNonLocalInterface &>(*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<Real>(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<MaterialNonLocalInterface>(*mat)) {
continue;
}
auto & mat_non_local = dynamic_cast<MaterialNonLocalInterface &>(*mat);
mat_non_local.updateNonLocalInternals(internal_flat, field_name, ghost_type,
kind);
}
}
/* -------------------------------------------------------------------------- */
FEEngine & SolidMechanicsModel::getFEEngineBoundary(const ID & name) {
return getFEEngineClassBoundary<MyFEEngineType>(name);
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModel::splitElementByMaterial(
const Array<Element> & elements,
std::vector<Array<Element>> & elements_per_mat) const {
for (const auto & el : elements) {
Element mat_el = el;
mat_el.element = this->material_local_numbering(el);
elements_per_mat[this->material_index(el)].push_back(mat_el);
}
}
/* -------------------------------------------------------------------------- */
UInt SolidMechanicsModel::getNbData(const Array<Element> & elements,
const SynchronizationTag & tag) const {
AKANTU_DEBUG_IN();
UInt size = 0;
UInt 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(UInt);
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<Element> & 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<Element> & elements,
const SynchronizationTag & tag) {
AKANTU_DEBUG_IN();
switch (tag) {
case SynchronizationTag::_material_id: {
for (auto && element : elements) {
UInt recv_mat_index;
buffer >> recv_mat_index;
UInt & mat_index = material_index(element);
if (mat_index != UInt(-1)) {
continue;
}
// add ghosts element to the correct material
mat_index = recv_mat_index;
UInt 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();
}
/* -------------------------------------------------------------------------- */
UInt SolidMechanicsModel::getNbData(const Array<UInt> & dofs,
const SynchronizationTag & tag) const {
AKANTU_DEBUG_IN();
UInt size = 0;
// UInt nb_nodes = mesh.getNbNodes();
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<UInt> & 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<UInt> & 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 ef1e97219..be928bd13 100644
--- a/src/model/solid_mechanics/solid_mechanics_model.hh
+++ b/src/model/solid_mechanics/solid_mechanics_model.hh
@@ -1,577 +1,587 @@
/**
* @file solid_mechanics_model.hh
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author Daniel Pino Muñoz <daniel.pinomunoz@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Tue Jul 27 2010
* @date last modification: Wed Feb 21 2018
*
* @brief Model of Solid Mechanics
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#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 <ElementKind kind, class IntegrationOrderFunctor>
class IntegratorGauss;
template <ElementKind kind> class ShapeLagrange;
} // namespace akantu
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
class SolidMechanicsModel
: public Model,
public DataAccessor<Element>,
public DataAccessor<UInt>,
public BoundaryCondition<SolidMechanicsModel>,
public NonLocalManagerCallback,
public EventHandlerManager<SolidMechanicsModelEventHandler> {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
class NewMaterialElementsEvent : public NewElementsEvent {
public:
AKANTU_GET_MACRO_NOT_CONST(MaterialList, material, Array<UInt> &);
AKANTU_GET_MACRO(MaterialList, material, const Array<UInt> &);
protected:
Array<UInt> material;
};
using MyFEEngineType = FEEngineTemplate<IntegratorGauss, ShapeLagrange>;
protected:
using EventManager = EventHandlerManager<SolidMechanicsModelEventHandler>;
public:
SolidMechanicsModel(Mesh & mesh, UInt dim = _all_dimensions,
const ID & id = "solid_mechanics_model",
- const MemoryID & memory_id = 0,
- std::shared_ptr<DOFManager> dof_manager = nullptr,
+ std::shared_ptr<DOFManager> 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<ID, TimeStepSolverType>
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() override { return true; }
/// get the type of matrix needed
MatrixType getMatrixType(const ID & matrix_id) 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 & registerNewMaterial(const ID & mat_name, const ID & mat_type,
const ID & opt_param);
/// reassigns materials depending on the material selector
virtual void reassignMaterial();
/// applya constant eigen_grad_u on all quadrature points of a given material
virtual void applyEigenGradU(const Matrix<Real> & prescribed_eigen_grad_u,
const ID & material_name,
GhostType ghost_type = _not_ghost);
protected:
/// register a material in the dynamic database
Material & registerNewMaterial(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<UInt> * 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<Real> & rho, ElementType type, GhostType ghost_type);
/// compute the kinetic energy
Real getKineticEnergy();
Real getKineticEnergy(ElementType type, UInt index);
/// 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:
UInt getNbData(const Array<Element> & elements,
const SynchronizationTag & tag) const override;
void packData(CommunicationBuffer & buffer, const Array<Element> & elements,
const SynchronizationTag & tag) const override;
void unpackData(CommunicationBuffer & buffer, const Array<Element> & elements,
const SynchronizationTag & tag) override;
UInt getNbData(const Array<UInt> & dofs,
const SynchronizationTag & tag) const override;
void packData(CommunicationBuffer & buffer, const Array<UInt> & dofs,
const SynchronizationTag & tag) const override;
void unpackData(CommunicationBuffer & buffer, const Array<UInt> & dofs,
const SynchronizationTag & tag) override;
protected:
void
splitElementByMaterial(const Array<Element> & elements,
std::vector<Array<Element>> & elements_per_mat) const;
template <typename Operation>
void splitByMaterial(const Array<Element> & elements, Operation && op) const;
/* ------------------------------------------------------------------------ */
/* Mesh Event Handler inherited members */
/* ------------------------------------------------------------------------ */
protected:
void onNodesAdded(const Array<UInt> & nodes_list,
const NewNodesEvent & event) override;
void onNodesRemoved(const Array<UInt> & element_list,
const Array<UInt> & new_numbering,
const RemovedNodesEvent & event) override;
void onElementsAdded(const Array<Element> & element_list,
const NewElementsEvent & event) override;
void onElementsRemoved(const Array<Element> & element_list,
const ElementTypeMapArray<UInt> & new_numbering,
const RemovedElementsEvent & event) override;
void onElementsChanged(const Array<Element> & /*unused*/,
const Array<Element> & /*unused*/,
const ElementTypeMapArray<UInt> & /*unused*/,
const ChangedElementsEvent & /*unused*/) 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<UInt>
getInternalDataPerElem(const std::string & field_name, ElementKind kind);
//! flatten a given material internal field
ElementTypeMapArray<Real> &
flattenInternal(const std::string & field_name, ElementKind kind,
GhostType ghost_type = _not_ghost);
//! flatten all the registered material internals
void flattenAllRegisteredInternals(ElementKind kind);
std::shared_ptr<dumpers::Field>
createNodalFieldReal(const std::string & field_name,
const std::string & group_name,
bool padding_flag) override;
std::shared_ptr<dumpers::Field>
createNodalFieldBool(const std::string & field_name,
const std::string & group_name,
bool padding_flag) override;
std::shared_ptr<dumpers::Field>
createElementalField(const std::string & field_name,
const std::string & group_name, bool padding_flag,
UInt spatial_dimension, ElementKind kind) override;
virtual void dump(const std::string & dumper_name);
virtual void dump(const std::string & dumper_name, UInt step);
- virtual void dump(const std::string & dumper_name, Real time, UInt step);
+
+ void dump(const std::string & dumper_name) override;
+ void dump(const std::string & dumper_name, UInt step) override;
+ void dump(const std::string & dumper_name, Real time, UInt step) override;
void dump() override;
-
- virtual void dump(UInt step);
-
- virtual void dump(Real time, UInt step);
+ void dump(UInt step) override;
+ void dump(Real time, UInt step) override;
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
/// return the dimension of the system space
AKANTU_GET_MACRO(SpatialDimension, Model::spatial_dimension, UInt);
/// 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<Real> & 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<Real> &
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 id from is name
inline UInt getMaterialIndex(const std::string & name) const;
/// give the number of materials
inline UInt 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();
/// get the energies
Real getEnergy(const std::string & energy_id);
- /// compute the energy for energy
+ /// compute the energy for an element
Real getEnergy(const std::string & energy_id, ElementType type, UInt index);
+ /// compute the energy for an element
+ Real getEnergy(const std::string & energy_id, const Element & element) {
+ return getEnergy(energy_id, element.type, element.element);
+ }
+
+ /// compute the energy for an element group
+ Real getEnergy(const ID & energy_id, const ID & group_id);
+
AKANTU_GET_MACRO(MaterialByElement, material_index,
const ElementTypeMapArray<UInt> &);
AKANTU_GET_MACRO(MaterialLocalNumbering, material_local_numbering,
const ElementTypeMapArray<UInt> &);
/// vectors containing local material element index for each global element
/// index
AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(MaterialByElement, material_index,
UInt);
- AKANTU_GET_MACRO_BY_ELEMENT_TYPE(MaterialByElement, material_index, UInt);
+ // AKANTU_GET_MACRO_BY_ELEMENT_TYPE(MaterialByElement, material_index, UInt);
AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(MaterialLocalNumbering,
material_local_numbering, UInt);
- AKANTU_GET_MACRO_BY_ELEMENT_TYPE(MaterialLocalNumbering,
- material_local_numbering, UInt);
+ // AKANTU_GET_MACRO_BY_ELEMENT_TYPE(MaterialLocalNumbering,
+ // material_local_numbering, UInt);
AKANTU_GET_MACRO_NOT_CONST(MaterialSelector, *material_selector,
MaterialSelector &);
void
setMaterialSelector(std::shared_ptr<MaterialSelector> 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
UInt displacement_release{0};
/// release version of the current_position array
UInt 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<std::string, UInt> materials_names_to_id;
protected:
/// conversion coefficient form force/mass to acceleration
Real f_m2a{1.0};
/// displacements array
std::unique_ptr<Array<Real>> displacement;
/// displacements array at the previous time step (used in finite deformation)
std::unique_ptr<Array<Real>> previous_displacement;
/// increment of displacement
std::unique_ptr<Array<Real>> displacement_increment;
/// lumped mass array
std::unique_ptr<Array<Real>> mass;
/// velocities array
std::unique_ptr<Array<Real>> velocity;
/// accelerations array
std::unique_ptr<Array<Real>> acceleration;
/// external forces array
std::unique_ptr<Array<Real>> external_force;
/// internal forces array
std::unique_ptr<Array<Real>> internal_force;
/// array specifing if a degree of freedom is blocked or not
std::unique_ptr<Array<bool>> blocked_dofs;
/// array of current position used during update residual
std::unique_ptr<Array<Real>> current_position;
/// Arrays containing the material index for each element
ElementTypeMapArray<UInt> material_index;
/// Arrays containing the position in the element filter of the material
/// (material's local numbering)
ElementTypeMapArray<UInt> material_local_numbering;
/// list of used materials
std::vector<std::unique_ptr<Material>> materials;
/// class defining of to choose a material
std::shared_ptr<MaterialSelector> material_selector;
using flatten_internal_map =
std::map<std::pair<std::string, ElementKind>,
std::unique_ptr<ElementTypeMapArray<Real>>>;
/// map a registered internals to be flattened for dump purposes
flatten_internal_map registered_internals;
/// non local manager
std::unique_ptr<NonLocalManager> non_local_manager;
/// tells if the material are instantiated
bool are_materials_instantiated{false};
+
+ friend class Material;
};
/* -------------------------------------------------------------------------- */
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/fragment_manager.cc b/src/model/solid_mechanics/solid_mechanics_model_cohesive/fragment_manager.cc
index 468b0240f..ebc197151 100644
--- a/src/model/solid_mechanics/solid_mechanics_model_cohesive/fragment_manager.cc
+++ b/src/model/solid_mechanics/solid_mechanics_model_cohesive/fragment_manager.cc
@@ -1,545 +1,545 @@
/**
* @file fragment_manager.cc
*
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
* @author Marco Vocialta <marco.vocialta@epfl.ch>
*
* @date creation: Thu Jan 23 2014
* @date last modification: Tue Feb 20 2018
*
* @brief Group manager to handle fragments
*
*
* Copyright (©) 2014-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "fragment_manager.hh"
#include "aka_iterators.hh"
#include "communicator.hh"
#include "element_synchronizer.hh"
#include "material_cohesive.hh"
#include "mesh_iterators.hh"
#include "solid_mechanics_model_cohesive.hh"
/* -------------------------------------------------------------------------- */
#include <algorithm>
#include <functional>
#include <numeric>
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
FragmentManager::FragmentManager(SolidMechanicsModelCohesive & model,
- bool dump_data, const ID & id,
- const MemoryID & memory_id)
- : GroupManager(model.getMesh(), id, memory_id), model(model),
+ bool dump_data, const ID & id)
+ : GroupManager(model.getMesh(), id), model(model),
mass_center(0, model.getSpatialDimension(), "mass_center"),
mass(0, model.getSpatialDimension(), "mass"),
velocity(0, model.getSpatialDimension(), "velocity"),
inertia_moments(0, model.getSpatialDimension(), "inertia_moments"),
principal_directions(
0, model.getSpatialDimension() * model.getSpatialDimension(),
"principal_directions"),
quad_coordinates("quad_coordinates", id),
mass_density("mass_density", id),
nb_elements_per_fragment(0, 1, "nb_elements_per_fragment"),
dump_data(dump_data) {
AKANTU_DEBUG_IN();
UInt spatial_dimension = mesh.getSpatialDimension();
/// compute quadrature points' coordinates
quad_coordinates.initialize(mesh, _nb_component = spatial_dimension,
_spatial_dimension = spatial_dimension,
_ghost_type = _not_ghost);
// mesh.initElementTypeMapArray(quad_coordinates, spatial_dimension,
// spatial_dimension, _not_ghost);
model.getFEEngine().interpolateOnIntegrationPoints(model.getMesh().getNodes(),
quad_coordinates);
/// store mass density per quadrature point
mass_density.initialize(mesh, _spatial_dimension = spatial_dimension,
_ghost_type = _not_ghost);
// mesh.initElementTypeMapArray(mass_density, 1, spatial_dimension,
// _not_ghost);
storeMassDensityPerIntegrationPoint();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
class CohesiveElementFilter : public GroupManager::ClusteringFilter {
public:
CohesiveElementFilter(const SolidMechanicsModelCohesive & model,
const Real max_damage = 1.)
: model(model), is_unbroken(max_damage) {}
bool operator()(const Element & el) const override {
if (Mesh::getKind(el.type) == _ek_regular) {
return true;
}
const Array<UInt> & mat_indexes =
model.getMaterialByElement(el.type, el.ghost_type);
const Array<UInt> & mat_loc_num =
model.getMaterialLocalNumbering(el.type, el.ghost_type);
const auto & mat = static_cast<const MaterialCohesive &>(
model.getMaterial(mat_indexes(el.element)));
UInt el_index = mat_loc_num(el.element);
UInt nb_quad_per_element =
model.getFEEngine("CohesiveFEEngine")
.getNbIntegrationPoints(el.type, el.ghost_type);
const Array<Real> & damage_array = mat.getDamage(el.type, el.ghost_type);
AKANTU_DEBUG_ASSERT(nb_quad_per_element * el_index < damage_array.size(),
"This quadrature point is out of range");
const Real * element_damage =
damage_array.storage() + nb_quad_per_element * el_index;
UInt unbroken_quads = std::count_if(
element_damage, element_damage + nb_quad_per_element, is_unbroken);
return (unbroken_quads > 0);
}
private:
struct IsUnbrokenFunctor {
IsUnbrokenFunctor(const Real & max_damage) : max_damage(max_damage) {}
- bool operator()(const Real & x) const { return x < max_damage; }
+ bool operator()(const Real & x) const { return x < max_damage; }
const Real max_damage;
};
const SolidMechanicsModelCohesive & model;
const IsUnbrokenFunctor is_unbroken;
};
/* -------------------------------------------------------------------------- */
void FragmentManager::buildFragments(Real damage_limit) {
AKANTU_DEBUG_IN();
if (mesh.isDistributed()) {
auto & cohesive_synchronizer = model.getCohesiveSynchronizer();
cohesive_synchronizer.synchronize(model, SynchronizationTag::_smmc_damage);
}
auto & mesh_facets = mesh.getMeshFacets();
UInt spatial_dimension = model.getSpatialDimension();
std::string fragment_prefix("fragment");
/// generate fragments
global_nb_fragment =
createClusters(spatial_dimension, mesh_facets, fragment_prefix,
CohesiveElementFilter(model, damage_limit));
nb_fragment = getNbElementGroups(spatial_dimension);
fragment_index.resize(nb_fragment);
/// loop over fragments
for (auto && data : zip(iterateElementGroups(), fragment_index)) {
auto name = std::get<0>(data).getName();
/// get fragment index
std::string fragment_index_string = name.substr(fragment_prefix.size() + 1);
std::get<1>(data) = std::stoul(fragment_index_string);
}
/// compute fragments' mass
computeMass();
if (dump_data) {
createDumpDataArray(fragment_index, "fragments", true);
createDumpDataArray(mass, "fragments mass");
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void FragmentManager::computeMass() {
AKANTU_DEBUG_IN();
UInt spatial_dimension = model.getSpatialDimension();
/// create a unit field per quadrature point, since to compute mass
/// it's neccessary to integrate only density
ElementTypeMapArray<Real> unit_field("unit_field", id);
unit_field.initialize(model.getFEEngine(), _nb_component = spatial_dimension,
_spatial_dimension = spatial_dimension,
_ghost_type = _not_ghost, _default_value = 1.);
integrateFieldOnFragments(unit_field, mass);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void FragmentManager::computeCenterOfMass() {
AKANTU_DEBUG_IN();
/// integrate position multiplied by density
integrateFieldOnFragments(quad_coordinates, mass_center);
/// divide it by the fragments' mass
Real * mass_storage = mass.storage();
Real * mass_center_storage = mass_center.storage();
UInt total_components = mass_center.size() * mass_center.getNbComponent();
for (UInt i = 0; i < total_components; ++i) {
mass_center_storage[i] /= mass_storage[i];
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void FragmentManager::computeVelocity() {
AKANTU_DEBUG_IN();
UInt spatial_dimension = model.getSpatialDimension();
/// compute velocity per quadrature point
ElementTypeMapArray<Real> velocity_field("velocity_field", id);
velocity_field.initialize(
model.getFEEngine(), _nb_component = spatial_dimension,
_spatial_dimension = spatial_dimension, _ghost_type = _not_ghost);
model.getFEEngine().interpolateOnIntegrationPoints(model.getVelocity(),
velocity_field);
/// integrate on fragments
integrateFieldOnFragments(velocity_field, velocity);
/// divide it by the fragments' mass
Real * mass_storage = mass.storage();
Real * velocity_storage = velocity.storage();
UInt total_components = velocity.size() * velocity.getNbComponent();
for (UInt i = 0; i < total_components; ++i) {
velocity_storage[i] /= mass_storage[i];
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
/**
* Given the distance @f$ \mathbf{r} @f$ between a quadrature point
* and its center of mass, the moment of inertia is computed as \f[
* I_\mathrm{CM} = \mathrm{tr}(\mathbf{r}\mathbf{r}^\mathrm{T})
* \mathbf{I} - \mathbf{r}\mathbf{r}^\mathrm{T} \f] for more
* information check Wikipedia
* (http://en.wikipedia.org/wiki/Moment_of_inertia#Identities_for_a_skew-symmetric_matrix)
*
*/
void FragmentManager::computeInertiaMoments() {
AKANTU_DEBUG_IN();
UInt spatial_dimension = model.getSpatialDimension();
computeCenterOfMass();
/// compute local coordinates products with respect to the center of match
ElementTypeMapArray<Real> moments_coords("moments_coords", id);
moments_coords.initialize(model.getFEEngine(),
_nb_component =
spatial_dimension * spatial_dimension,
_spatial_dimension = spatial_dimension,
_ghost_type = _not_ghost, _default_value = 1.);
/// loop over fragments
for (auto && data :
zip(iterateElementGroups(), make_view(mass_center, spatial_dimension))) {
const auto & el_list = std::get<0>(data).getElements();
auto & mass_center = std::get<1>(data);
/// loop over elements of the fragment
for (auto type :
el_list.elementTypes(spatial_dimension, _not_ghost, _ek_regular)) {
auto nb_quad_per_element =
model.getFEEngine().getNbIntegrationPoints(type);
auto & moments_coords_array = moments_coords(type);
const auto & quad_coordinates_array = quad_coordinates(type);
const auto & el_list_array = el_list(type);
auto moments_begin =
moments_coords_array.begin(spatial_dimension, spatial_dimension);
auto quad_coordinates_begin =
quad_coordinates_array.begin(spatial_dimension);
Vector<Real> relative_coords(spatial_dimension);
for (UInt el = 0; el < el_list_array.size(); ++el) {
UInt global_el = el_list_array(el);
/// loop over quadrature points
for (UInt q = 0; q < nb_quad_per_element; ++q) {
UInt global_q = global_el * nb_quad_per_element + q;
Matrix<Real> moments_matrix = moments_begin[global_q];
const Vector<Real> & quad_coord_vector =
quad_coordinates_begin[global_q];
/// to understand this read the documentation written just
/// before this function
relative_coords = quad_coord_vector;
relative_coords -= mass_center;
moments_matrix.outerProduct(relative_coords, relative_coords);
Real trace = moments_matrix.trace();
moments_matrix *= -1.;
moments_matrix += Matrix<Real>::eye(spatial_dimension, trace);
}
}
}
}
/// integrate moments
Array<Real> integrated_moments(global_nb_fragment,
spatial_dimension * spatial_dimension);
integrateFieldOnFragments(moments_coords, integrated_moments);
/// compute and store principal moments
inertia_moments.resize(global_nb_fragment);
principal_directions.resize(global_nb_fragment);
auto integrated_moments_it =
integrated_moments.begin(spatial_dimension, spatial_dimension);
auto inertia_moments_it = inertia_moments.begin(spatial_dimension);
auto principal_directions_it =
principal_directions.begin(spatial_dimension, spatial_dimension);
for (UInt frag = 0; frag < global_nb_fragment; ++frag,
++integrated_moments_it, ++inertia_moments_it,
++principal_directions_it) {
integrated_moments_it->eig(*inertia_moments_it, *principal_directions_it);
}
if (dump_data) {
createDumpDataArray(inertia_moments, "moments of inertia");
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
-void FragmentManager::computeAllData() {
+void FragmentManager::computeAllData(Real damage_limit) {
AKANTU_DEBUG_IN();
- buildFragments();
+ buildFragments(damage_limit);
computeVelocity();
computeInertiaMoments();
+ computeNbElementsPerFragment();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void FragmentManager::storeMassDensityPerIntegrationPoint() {
AKANTU_DEBUG_IN();
UInt spatial_dimension = model.getSpatialDimension();
for (auto type : mesh.elementTypes(_spatial_dimension = spatial_dimension,
_element_kind = _ek_regular)) {
Array<Real> & mass_density_array = mass_density(type);
UInt nb_element = mesh.getNbElement(type);
UInt nb_quad_per_element = model.getFEEngine().getNbIntegrationPoints(type);
mass_density_array.resize(nb_element * nb_quad_per_element);
const Array<UInt> & mat_indexes = model.getMaterialByElement(type);
Real * mass_density_it = mass_density_array.storage();
/// store mass_density for each element and quadrature point
for (UInt el = 0; el < nb_element; ++el) {
Material & mat = model.getMaterial(mat_indexes(el));
for (UInt q = 0; q < nb_quad_per_element; ++q, ++mass_density_it) {
*mass_density_it = mat.getRho();
}
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void FragmentManager::integrateFieldOnFragments(
ElementTypeMapArray<Real> & field, Array<Real> & output) {
AKANTU_DEBUG_IN();
UInt spatial_dimension = model.getSpatialDimension();
UInt nb_component = output.getNbComponent();
/// integration part
output.resize(global_nb_fragment);
output.zero();
auto output_begin = output.begin(nb_component);
/// loop over fragments
for (auto && data : zip(iterateElementGroups(), fragment_index)) {
const auto & el_list = std::get<0>(data).getElements();
auto fragment_index = std::get<1>(data);
/// loop over elements of the fragment
for (auto type :
el_list.elementTypes(spatial_dimension, _not_ghost, _ek_regular)) {
UInt nb_quad_per_element =
model.getFEEngine().getNbIntegrationPoints(type);
const Array<Real> & density_array = mass_density(type);
Array<Real> & field_array = field(type);
const Array<UInt> & elements = el_list(type);
/// generate array to be integrated by filtering fragment's elements
Array<Real> integration_array(elements.size() * nb_quad_per_element,
nb_component);
auto field_array_begin = field_array.begin_reinterpret(
nb_quad_per_element, nb_component,
field_array.size() / nb_quad_per_element);
auto density_array_begin = density_array.begin_reinterpret(
nb_quad_per_element, density_array.size() / nb_quad_per_element);
for (auto && data : enumerate(make_view(
integration_array, nb_quad_per_element, nb_component))) {
UInt global_el = elements(std::get<0>(data));
auto & int_array = std::get<1>(data);
int_array = field_array_begin[global_el];
/// multiply field by density
const Vector<Real> & density_vector = density_array_begin[global_el];
for (UInt i = 0; i < nb_quad_per_element; ++i) {
for (UInt j = 0; j < nb_component; ++j) {
int_array(i, j) *= density_vector(i);
}
}
}
/// integrate the field over the fragment
Array<Real> integrated_array(elements.size(), nb_component);
model.getFEEngine().integrate(integration_array, integrated_array,
nb_component, type, _not_ghost, elements);
/// sum over all elements and store the result
Vector<Real> output_tmp(output_begin[fragment_index]);
output_tmp += std::accumulate(integrated_array.begin(nb_component),
integrated_array.end(nb_component),
Vector<Real>(nb_component));
}
}
/// sum output over all processors
const Communicator & comm = mesh.getCommunicator();
comm.allReduce(output, SynchronizerOperation::_sum);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void FragmentManager::computeNbElementsPerFragment() {
AKANTU_DEBUG_IN();
UInt spatial_dimension = model.getSpatialDimension();
nb_elements_per_fragment.resize(global_nb_fragment);
nb_elements_per_fragment.zero();
/// loop over fragments
for (auto && data : zip(iterateElementGroups(), fragment_index)) {
const auto & el_list = std::get<0>(data).getElements();
auto fragment_index = std::get<1>(data);
/// loop over elements of the fragment
for (auto type :
el_list.elementTypes(spatial_dimension, _not_ghost, _ek_regular)) {
UInt nb_element = el_list(type).size();
nb_elements_per_fragment(fragment_index) += nb_element;
}
}
/// sum values over all processors
const auto & comm = mesh.getCommunicator();
comm.allReduce(nb_elements_per_fragment, SynchronizerOperation::_sum);
if (dump_data) {
createDumpDataArray(nb_elements_per_fragment, "elements per fragment");
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <typename T>
void FragmentManager::createDumpDataArray(Array<T> & data, std::string name,
bool fragment_index_output) {
AKANTU_DEBUG_IN();
if (data.empty()) {
return;
}
auto & mesh_not_const = const_cast<Mesh &>(mesh);
auto && spatial_dimension = mesh.getSpatialDimension();
auto && nb_component = data.getNbComponent();
auto && data_begin = data.begin(nb_component);
/// loop over fragments
for (auto && data : zip(iterateElementGroups(), fragment_index)) {
const auto & fragment = std::get<0>(data);
auto fragment_idx = std::get<1>(data);
/// loop over cluster types
for (auto && type : fragment.elementTypes(spatial_dimension)) {
/// init mesh data
auto & mesh_data = mesh_not_const.getDataPointer<T>(
name, type, _not_ghost, nb_component);
auto mesh_data_begin = mesh_data.begin(nb_component);
/// fill mesh data
for (const auto & elem : fragment.getElements(type)) {
Vector<T> md_tmp = mesh_data_begin[elem];
if (fragment_index_output) {
md_tmp(0) = fragment_idx;
} else {
md_tmp = data_begin[fragment_idx];
}
}
}
}
AKANTU_DEBUG_OUT();
}
} // namespace akantu
diff --git a/src/model/solid_mechanics/solid_mechanics_model_cohesive/fragment_manager.hh b/src/model/solid_mechanics/solid_mechanics_model_cohesive/fragment_manager.hh
index 707153d0f..3ef723a11 100644
--- a/src/model/solid_mechanics/solid_mechanics_model_cohesive/fragment_manager.hh
+++ b/src/model/solid_mechanics/solid_mechanics_model_cohesive/fragment_manager.hh
@@ -1,168 +1,167 @@
/**
* @file fragment_manager.hh
*
* @author Marco Vocialta <marco.vocialta@epfl.ch>
*
* @date creation: Thu Jan 23 2014
* @date last modification: Thu Jul 06 2017
*
* @brief Group manager to handle fragments
*
*
* Copyright (©) 2014-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "group_manager.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_FRAGMENT_MANAGER_HH_
#define AKANTU_FRAGMENT_MANAGER_HH_
namespace akantu {
class SolidMechanicsModelCohesive;
}
namespace akantu {
/* -------------------------------------------------------------------------- */
class FragmentManager : public GroupManager {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
FragmentManager(SolidMechanicsModelCohesive & model, bool dump_data = true,
- const ID & id = "fragment_manager",
- const MemoryID & memory_id = 0);
+ const ID & id = "fragment_manager");
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
private:
/// store mass density per integration point
void storeMassDensityPerIntegrationPoint();
/// integrate an elemental field multiplied by density on global
/// fragments
void integrateFieldOnFragments(ElementTypeMapArray<Real> & field,
Array<Real> & output);
/// compute fragments' mass
void computeMass();
/// create dump data for a single array
template <typename T>
void createDumpDataArray(Array<T> & data, std::string name,
bool fragment_index_output = false);
public:
/// build fragment list (cohesive elements are considered broken if
/// damage >= damage_limit)
void buildFragments(Real damage_limit = 1.);
/// compute fragments' center of mass
void computeCenterOfMass();
/// compute fragments' velocity
void computeVelocity();
/// computes principal moments of inertia with respect to the center
/// of mass of each fragment
void computeInertiaMoments();
/// compute all fragments' data
- void computeAllData();
+ void computeAllData(Real damage_limit = 1.);
/// compute number of elements per fragment
void computeNbElementsPerFragment();
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
/// get number of fragments
AKANTU_GET_MACRO(NbFragment, global_nb_fragment, UInt);
/// get fragments' mass
AKANTU_GET_MACRO(Mass, mass, const Array<Real> &);
/// get fragments' center of mass
AKANTU_GET_MACRO(CenterOfMass, mass_center, const Array<Real> &);
/// get fragments' velocity
AKANTU_GET_MACRO(Velocity, velocity, const Array<Real> &);
/// get fragments' principal moments of inertia
AKANTU_GET_MACRO(MomentsOfInertia, inertia_moments, const Array<Real> &);
/// get fragments' principal directions
AKANTU_GET_MACRO(PrincipalDirections, principal_directions,
const Array<Real> &);
/// get number of elements per fragment
AKANTU_GET_MACRO(NbElementsPerFragment, nb_elements_per_fragment,
const Array<UInt> &);
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
private:
/// local_fragment index
Array<UInt> fragment_index;
/// global number of fragments (parallel simulations)
UInt global_nb_fragment;
/// number of fragments
UInt nb_fragment;
/// cohesive solid mechanics model associated
SolidMechanicsModelCohesive & model;
/// fragments' center of mass
Array<Real> mass_center;
/// fragments' mass
Array<Real> mass;
/// fragments' velocity
Array<Real> velocity;
/// fragments' principal moments of inertia with respect to the
/// center of mass
Array<Real> inertia_moments;
/// fragments' principal directions
Array<Real> principal_directions;
/// quadrature points' coordinates
ElementTypeMapArray<Real> quad_coordinates;
/// mass density per quadrature point
ElementTypeMapArray<Real> mass_density;
/// fragment filter
Array<UInt> nb_elements_per_fragment;
/// dump data
bool dump_data;
};
} // namespace akantu
#endif /* AKANTU_FRAGMENT_MANAGER_HH_ */
diff --git a/src/model/solid_mechanics/solid_mechanics_model_cohesive/materials/material_cohesive.cc b/src/model/solid_mechanics/solid_mechanics_model_cohesive/materials/material_cohesive.cc
index ca05da5d8..3ceebd44d 100644
--- a/src/model/solid_mechanics/solid_mechanics_model_cohesive/materials/material_cohesive.cc
+++ b/src/model/solid_mechanics/solid_mechanics_model_cohesive/materials/material_cohesive.cc
@@ -1,568 +1,568 @@
/**
* @file material_cohesive.cc
*
* @author Mauro Corrado <mauro.corrado@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
* @author Seyedeh Mohadeseh Taheri Mousavi <mohadeseh.taherimousavi@epfl.ch>
* @author Marco Vocialta <marco.vocialta@epfl.ch>
*
* @date creation: Wed Feb 22 2012
* @date last modification: Mon Feb 19 2018
*
* @brief Specialization of the material class for cohesive elements
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "material_cohesive.hh"
#include "aka_random_generator.hh"
#include "dof_synchronizer.hh"
#include "fe_engine_template.hh"
#include "integrator_gauss.hh"
#include "shape_cohesive.hh"
#include "solid_mechanics_model_cohesive.hh"
#include "sparse_matrix.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
MaterialCohesive::MaterialCohesive(SolidMechanicsModel & model, const ID & id)
: Material(model, id),
- facet_filter("facet_filter", id, this->getMemoryID()),
+ facet_filter("facet_filter", id),
fem_cohesive(
model.getFEEngineClass<MyFEEngineCohesiveType>("CohesiveFEEngine")),
reversible_energy("reversible_energy", *this),
total_energy("total_energy", *this), opening("opening", *this),
tractions("tractions", *this),
contact_tractions("contact_tractions", *this),
contact_opening("contact_opening", *this), delta_max("delta max", *this),
use_previous_delta_max(false), use_previous_opening(false),
damage("damage", *this), sigma_c("sigma_c", *this),
normal(0, spatial_dimension, "normal") {
AKANTU_DEBUG_IN();
this->model = dynamic_cast<SolidMechanicsModelCohesive *>(&model);
this->registerParam("sigma_c", sigma_c, _pat_parsable | _pat_readable,
"Critical stress");
this->registerParam("delta_c", delta_c, Real(0.),
_pat_parsable | _pat_readable, "Critical displacement");
this->element_filter.initialize(this->model->getMesh(),
_spatial_dimension = spatial_dimension,
_element_kind = _ek_cohesive);
// this->model->getMesh().initElementTypeMapArray(
// this->element_filter, 1, spatial_dimension, false, _ek_cohesive);
if (this->model->getIsExtrinsic()) {
this->facet_filter.initialize(this->model->getMeshFacets(),
_spatial_dimension = spatial_dimension - 1,
_element_kind = _ek_regular);
}
// this->model->getMeshFacets().initElementTypeMapArray(facet_filter, 1,
// spatial_dimension -
// 1);
this->reversible_energy.initialize(1);
this->total_energy.initialize(1);
this->tractions.initialize(spatial_dimension);
this->tractions.initializeHistory();
this->contact_tractions.initialize(spatial_dimension);
this->contact_opening.initialize(spatial_dimension);
this->opening.initialize(spatial_dimension);
this->opening.initializeHistory();
this->delta_max.initialize(1);
this->damage.initialize(1);
if (this->model->getIsExtrinsic()) {
this->sigma_c.initialize(1);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
MaterialCohesive::~MaterialCohesive() = default;
/* -------------------------------------------------------------------------- */
void MaterialCohesive::initMaterial() {
AKANTU_DEBUG_IN();
Material::initMaterial();
if (this->use_previous_delta_max) {
this->delta_max.initializeHistory();
}
if (this->use_previous_opening) {
this->opening.initializeHistory();
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void MaterialCohesive::assembleInternalForces(GhostType ghost_type) {
AKANTU_DEBUG_IN();
#if defined(AKANTU_DEBUG_TOOLS)
debug::element_manager.printData(debug::_dm_material_cohesive,
"Cohesive Tractions", tractions);
#endif
auto & internal_force = const_cast<Array<Real> &>(model->getInternalForce());
for (auto type : element_filter.elementTypes(spatial_dimension, ghost_type,
_ek_cohesive)) {
auto & elem_filter = element_filter(type, ghost_type);
UInt nb_element = elem_filter.size();
if (nb_element == 0) {
continue;
}
const auto & shapes = fem_cohesive.getShapes(type, ghost_type);
auto & traction = tractions(type, ghost_type);
auto & contact_traction = contact_tractions(type, ghost_type);
UInt size_of_shapes = shapes.getNbComponent();
UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
UInt nb_quadrature_points =
fem_cohesive.getNbIntegrationPoints(type, ghost_type);
/// compute @f$t_i N_a@f$
auto * traction_cpy = new Array<Real>(nb_element * nb_quadrature_points,
spatial_dimension * size_of_shapes);
auto traction_it = traction.begin(spatial_dimension, 1);
auto contact_traction_it = contact_traction.begin(spatial_dimension, 1);
auto shapes_filtered_begin = shapes.begin(1, size_of_shapes);
auto traction_cpy_it =
traction_cpy->begin(spatial_dimension, size_of_shapes);
Matrix<Real> traction_tmp(spatial_dimension, 1);
for (UInt el = 0; el < nb_element; ++el) {
UInt current_quad = elem_filter(el) * nb_quadrature_points;
for (UInt q = 0; q < nb_quadrature_points; ++q, ++traction_it,
++contact_traction_it, ++current_quad, ++traction_cpy_it) {
const Matrix<Real> & shapes_filtered =
shapes_filtered_begin[current_quad];
traction_tmp.copy(*traction_it);
traction_tmp += *contact_traction_it;
traction_cpy_it->mul<false, false>(traction_tmp, shapes_filtered);
}
}
/**
* compute @f$\int t \cdot N\, dS@f$ by @f$ \sum_q \mathbf{N}^t
* \mathbf{t}_q \overline w_q J_q@f$
*/
auto * partial_int_t_N = new Array<Real>(
nb_element, spatial_dimension * size_of_shapes, "int_t_N");
fem_cohesive.integrate(*traction_cpy, *partial_int_t_N,
spatial_dimension * size_of_shapes, type, ghost_type,
elem_filter);
delete traction_cpy;
auto * int_t_N = new Array<Real>(
nb_element, 2 * spatial_dimension * size_of_shapes, "int_t_N");
Real * int_t_N_val = int_t_N->storage();
Real * partial_int_t_N_val = partial_int_t_N->storage();
for (UInt el = 0; el < nb_element; ++el) {
std::copy_n(partial_int_t_N_val, size_of_shapes * spatial_dimension,
int_t_N_val);
std::copy_n(partial_int_t_N_val, size_of_shapes * spatial_dimension,
int_t_N_val + size_of_shapes * spatial_dimension);
for (UInt n = 0; n < size_of_shapes * spatial_dimension; ++n) {
int_t_N_val[n] *= -1.;
}
int_t_N_val += nb_nodes_per_element * spatial_dimension;
partial_int_t_N_val += size_of_shapes * spatial_dimension;
}
delete partial_int_t_N;
/// assemble
model->getDOFManager().assembleElementalArrayLocalArray(
*int_t_N, internal_force, type, ghost_type, 1, elem_filter);
delete int_t_N;
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void MaterialCohesive::assembleStiffnessMatrix(GhostType ghost_type) {
AKANTU_DEBUG_IN();
for (auto type : element_filter.elementTypes(spatial_dimension, ghost_type,
_ek_cohesive)) {
UInt nb_quadrature_points =
fem_cohesive.getNbIntegrationPoints(type, ghost_type);
UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
const Array<Real> & shapes = fem_cohesive.getShapes(type, ghost_type);
Array<UInt> & elem_filter = element_filter(type, ghost_type);
UInt nb_element = elem_filter.size();
if (nb_element == 0U) {
continue;
}
UInt size_of_shapes = shapes.getNbComponent();
auto * shapes_filtered = new Array<Real>(nb_element * nb_quadrature_points,
size_of_shapes, "filtered shapes");
Real * shapes_filtered_val = shapes_filtered->storage();
UInt * elem_filter_val = elem_filter.storage();
for (UInt el = 0; el < nb_element; ++el) {
auto * shapes_val = shapes.storage() + elem_filter_val[el] *
size_of_shapes *
nb_quadrature_points;
memcpy(shapes_filtered_val, shapes_val,
size_of_shapes * nb_quadrature_points * sizeof(Real));
shapes_filtered_val += size_of_shapes * nb_quadrature_points;
}
Matrix<Real> A(spatial_dimension * size_of_shapes,
spatial_dimension * nb_nodes_per_element);
for (UInt i = 0; i < spatial_dimension * size_of_shapes; ++i) {
A(i, i) = 1;
A(i, i + spatial_dimension * size_of_shapes) = -1;
}
/// get the tangent matrix @f$\frac{\partial{(t/\delta)}}{\partial{\delta}}
/// @f$
auto * tangent_stiffness_matrix = new Array<Real>(
nb_element * nb_quadrature_points,
spatial_dimension * spatial_dimension, "tangent_stiffness_matrix");
// Array<Real> * normal = new Array<Real>(nb_element *
// nb_quadrature_points, spatial_dimension, "normal");
normal.resize(nb_quadrature_points);
computeNormal(model->getCurrentPosition(), normal, type, ghost_type);
/// compute openings @f$\mathbf{\delta}@f$
// computeOpening(model->getDisplacement(), opening(type, ghost_type), type,
// ghost_type);
tangent_stiffness_matrix->zero();
computeTangentTraction(type, *tangent_stiffness_matrix, normal, ghost_type);
// delete normal;
UInt size_at_nt_d_n_a = spatial_dimension * nb_nodes_per_element *
spatial_dimension * nb_nodes_per_element;
auto * at_nt_d_n_a = new Array<Real>(nb_element * nb_quadrature_points,
size_at_nt_d_n_a, "A^t*N^t*D*N*A");
Array<Real>::iterator<Vector<Real>> shapes_filt_it =
shapes_filtered->begin(size_of_shapes);
Array<Real>::matrix_iterator D_it =
tangent_stiffness_matrix->begin(spatial_dimension, spatial_dimension);
Array<Real>::matrix_iterator At_Nt_D_N_A_it =
at_nt_d_n_a->begin(spatial_dimension * nb_nodes_per_element,
spatial_dimension * nb_nodes_per_element);
Array<Real>::matrix_iterator At_Nt_D_N_A_end =
at_nt_d_n_a->end(spatial_dimension * nb_nodes_per_element,
spatial_dimension * nb_nodes_per_element);
Matrix<Real> N(spatial_dimension, spatial_dimension * size_of_shapes);
Matrix<Real> N_A(spatial_dimension,
spatial_dimension * nb_nodes_per_element);
Matrix<Real> D_N_A(spatial_dimension,
spatial_dimension * nb_nodes_per_element);
for (; At_Nt_D_N_A_it != At_Nt_D_N_A_end;
++At_Nt_D_N_A_it, ++D_it, ++shapes_filt_it) {
N.zero();
/**
* store the shapes in voigt notations matrix @f$\mathbf{N} =
* \begin{array}{cccccc} N_0(\xi) & 0 & N_1(\xi) &0 & N_2(\xi) & 0 \\
* 0 & * N_0(\xi)& 0 &N_1(\xi)& 0 & N_2(\xi) \end{array} @f$
**/
for (UInt i = 0; i < spatial_dimension; ++i) {
for (UInt n = 0; n < size_of_shapes; ++n) {
N(i, i + spatial_dimension * n) = (*shapes_filt_it)(n);
}
}
/**
* compute stiffness matrix @f$ \mathbf{K} = \delta \mathbf{U}^T
* \int_{\Gamma_c} {\mathbf{P}^t \frac{\partial{\mathbf{t}}}
*{\partial{\delta}}
* \mathbf{P} d\Gamma \Delta \mathbf{U}} @f$
**/
N_A.mul<false, false>(N, A);
D_N_A.mul<false, false>(*D_it, N_A);
(*At_Nt_D_N_A_it).mul<true, false>(D_N_A, N_A);
}
delete tangent_stiffness_matrix;
delete shapes_filtered;
auto * K_e = new Array<Real>(nb_element, size_at_nt_d_n_a, "K_e");
fem_cohesive.integrate(*at_nt_d_n_a, *K_e, size_at_nt_d_n_a, type,
ghost_type, elem_filter);
delete at_nt_d_n_a;
model->getDOFManager().assembleElementalMatricesToMatrix(
"K", "displacement", *K_e, type, ghost_type, _unsymmetric, elem_filter);
delete K_e;
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- *
* Compute traction from displacements
*
* @param[in] ghost_type compute the residual for _ghost or _not_ghost element
*/
void MaterialCohesive::computeTraction(GhostType ghost_type) {
AKANTU_DEBUG_IN();
#if defined(AKANTU_DEBUG_TOOLS)
debug::element_manager.printData(debug::_dm_material_cohesive,
"Cohesive Openings", opening);
#endif
for (const auto & type : element_filter.elementTypes(
spatial_dimension, ghost_type, _ek_cohesive)) {
Array<UInt> & elem_filter = element_filter(type, ghost_type);
UInt nb_element = elem_filter.size();
if (nb_element == 0) {
continue;
}
UInt nb_quadrature_points =
nb_element * fem_cohesive.getNbIntegrationPoints(type, ghost_type);
normal.resize(nb_quadrature_points);
/// compute normals @f$\mathbf{n}@f$
computeNormal(model->getCurrentPosition(), normal, type, ghost_type);
/// compute openings @f$\mathbf{\delta}@f$
computeOpening(model->getDisplacement(), opening(type, ghost_type), type,
ghost_type);
/// compute traction @f$\mathbf{t}@f$
computeTraction(normal, type, ghost_type);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void MaterialCohesive::computeNormal(const Array<Real> & position,
Array<Real> & normal, ElementType type,
GhostType ghost_type) {
AKANTU_DEBUG_IN();
auto & fem_cohesive =
this->model->getFEEngineClass<MyFEEngineCohesiveType>("CohesiveFEEngine");
normal.zero();
#define COMPUTE_NORMAL(type) \
fem_cohesive.getShapeFunctions() \
.computeNormalsOnIntegrationPoints<type, CohesiveReduceFunctionMean>( \
position, normal, ghost_type, element_filter(type, ghost_type));
AKANTU_BOOST_COHESIVE_ELEMENT_SWITCH(COMPUTE_NORMAL);
#undef COMPUTE_NORMAL
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void MaterialCohesive::computeOpening(const Array<Real> & displacement,
Array<Real> & opening, ElementType type,
GhostType ghost_type) {
AKANTU_DEBUG_IN();
auto & fem_cohesive =
this->model->getFEEngineClass<MyFEEngineCohesiveType>("CohesiveFEEngine");
#define COMPUTE_OPENING(type) \
fem_cohesive.getShapeFunctions() \
.interpolateOnIntegrationPoints<type, CohesiveReduceFunctionOpening>( \
displacement, opening, spatial_dimension, ghost_type, \
element_filter(type, ghost_type));
AKANTU_BOOST_COHESIVE_ELEMENT_SWITCH(COMPUTE_OPENING);
#undef COMPUTE_OPENING
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void MaterialCohesive::updateEnergies(ElementType type) {
AKANTU_DEBUG_IN();
if (Mesh::getKind(type) != _ek_cohesive) {
return;
}
Vector<Real> b(spatial_dimension);
Vector<Real> h(spatial_dimension);
auto erev = reversible_energy(type).begin();
auto etot = total_energy(type).begin();
auto traction_it = tractions(type).begin(spatial_dimension);
auto traction_old_it = tractions.previous(type).begin(spatial_dimension);
auto opening_it = opening(type).begin(spatial_dimension);
auto opening_old_it = opening.previous(type).begin(spatial_dimension);
auto traction_end = tractions(type).end(spatial_dimension);
/// loop on each quadrature point
for (; traction_it != traction_end; ++traction_it, ++traction_old_it,
++opening_it, ++opening_old_it, ++erev,
++etot) {
/// trapezoidal integration
b = *opening_it;
b -= *opening_old_it;
h = *traction_old_it;
h += *traction_it;
*etot += .5 * b.dot(h);
*erev = .5 * traction_it->dot(*opening_it);
}
/// update old values
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
Real MaterialCohesive::getReversibleEnergy() {
AKANTU_DEBUG_IN();
Real erev = 0.;
/// integrate reversible energy for each type of elements
for (const auto & type : element_filter.elementTypes(
spatial_dimension, _not_ghost, _ek_cohesive)) {
erev +=
fem_cohesive.integrate(reversible_energy(type, _not_ghost), type,
_not_ghost, element_filter(type, _not_ghost));
}
AKANTU_DEBUG_OUT();
return erev;
}
/* -------------------------------------------------------------------------- */
Real MaterialCohesive::getDissipatedEnergy() {
AKANTU_DEBUG_IN();
Real edis = 0.;
/// integrate dissipated energy for each type of elements
for (const auto & type : element_filter.elementTypes(
spatial_dimension, _not_ghost, _ek_cohesive)) {
Array<Real> dissipated_energy(total_energy(type, _not_ghost));
dissipated_energy -= reversible_energy(type, _not_ghost);
edis += fem_cohesive.integrate(dissipated_energy, type, _not_ghost,
element_filter(type, _not_ghost));
}
AKANTU_DEBUG_OUT();
return edis;
}
/* -------------------------------------------------------------------------- */
Real MaterialCohesive::getContactEnergy() {
AKANTU_DEBUG_IN();
Real econ = 0.;
/// integrate contact energy for each type of elements
for (const auto & type : element_filter.elementTypes(
spatial_dimension, _not_ghost, _ek_cohesive)) {
auto & el_filter = element_filter(type, _not_ghost);
UInt nb_quad_per_el = fem_cohesive.getNbIntegrationPoints(type, _not_ghost);
UInt nb_quad_points = el_filter.size() * nb_quad_per_el;
Array<Real> contact_energy(nb_quad_points);
auto contact_traction_it =
contact_tractions(type, _not_ghost).begin(spatial_dimension);
auto contact_opening_it =
contact_opening(type, _not_ghost).begin(spatial_dimension);
/// loop on each quadrature point
for (UInt q = 0; q < nb_quad_points;
++contact_traction_it, ++contact_opening_it, ++q) {
contact_energy(q) = .5 * contact_traction_it->dot(*contact_opening_it);
}
econ += fem_cohesive.integrate(contact_energy, type, _not_ghost, el_filter);
}
AKANTU_DEBUG_OUT();
return econ;
}
/* -------------------------------------------------------------------------- */
Real MaterialCohesive::getEnergy(const std::string & type) {
if (type == "reversible") {
return getReversibleEnergy();
}
if (type == "dissipated") {
return getDissipatedEnergy();
}
if (type == "cohesive contact") {
return getContactEnergy();
}
return 0.;
}
/* -------------------------------------------------------------------------- */
} // namespace akantu
diff --git a/src/model/solid_mechanics/solid_mechanics_model_cohesive/solid_mechanics_model_cohesive.cc b/src/model/solid_mechanics/solid_mechanics_model_cohesive/solid_mechanics_model_cohesive.cc
index 12517a22f..656247b01 100644
--- a/src/model/solid_mechanics/solid_mechanics_model_cohesive/solid_mechanics_model_cohesive.cc
+++ b/src/model/solid_mechanics/solid_mechanics_model_cohesive/solid_mechanics_model_cohesive.cc
@@ -1,726 +1,726 @@
/**
* @file solid_mechanics_model_cohesive.cc
*
* @author Fabian Barras <fabian.barras@epfl.ch>
* @author Mauro Corrado <mauro.corrado@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
* @author Marco Vocialta <marco.vocialta@epfl.ch>
*
* @date creation: Tue May 08 2012
* @date last modification: Wed Feb 21 2018
*
* @brief Solid mechanics model for cohesive elements
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "solid_mechanics_model_cohesive.hh"
#include "aka_iterators.hh"
#include "cohesive_element_inserter.hh"
#include "element_synchronizer.hh"
#include "facet_synchronizer.hh"
#include "fe_engine_template.hh"
#include "global_ids_updater.hh"
#include "integrator_gauss.hh"
#include "material_cohesive.hh"
#include "mesh_accessor.hh"
#include "mesh_global_data_updater.hh"
#include "parser.hh"
#include "shape_cohesive.hh"
/* -------------------------------------------------------------------------- */
#include "dumpable_inline_impl.hh"
#ifdef AKANTU_USE_IOHELPER
#include "dumper_iohelper_paraview.hh"
#endif
/* -------------------------------------------------------------------------- */
#include <algorithm>
/* -------------------------------------------------------------------------- */
namespace akantu {
class CohesiveMeshGlobalDataUpdater : public MeshGlobalDataUpdater {
public:
CohesiveMeshGlobalDataUpdater(SolidMechanicsModelCohesive & model)
: model(model), mesh(model.getMesh()),
global_ids_updater(model.getMesh(), *model.cohesive_synchronizer) {}
/* ------------------------------------------------------------------------ */
std::tuple<UInt, UInt>
updateData(NewNodesEvent & nodes_event,
NewElementsEvent & elements_event) override {
auto *cohesive_nodes_event =
dynamic_cast<CohesiveNewNodesEvent *>(&nodes_event);
if (cohesive_nodes_event == nullptr) {
return std::make_tuple(nodes_event.getList().size(),
elements_event.getList().size());
}
/// update nodes type
auto & new_nodes = cohesive_nodes_event->getList();
auto & old_nodes = cohesive_nodes_event->getOldNodesList();
auto local_nb_new_nodes = new_nodes.size();
auto nb_new_nodes = local_nb_new_nodes;
if (mesh.isDistributed()) {
MeshAccessor mesh_accessor(mesh);
auto & nodes_flags = mesh_accessor.getNodesFlags();
auto nb_old_nodes = nodes_flags.size();
nodes_flags.resize(nb_old_nodes + local_nb_new_nodes);
for (auto && data : zip(old_nodes, new_nodes)) {
UInt old_node;
UInt new_node;
std::tie(old_node, new_node) = data;
nodes_flags(new_node) = nodes_flags(old_node);
}
model.updateCohesiveSynchronizers(elements_event);
nb_new_nodes = global_ids_updater.updateGlobalIDs(new_nodes.size());
}
Vector<UInt> nb_new_stuff = {nb_new_nodes, elements_event.getList().size()};
const auto & comm = mesh.getCommunicator();
comm.allReduce(nb_new_stuff, SynchronizerOperation::_sum);
if (nb_new_stuff(1) > 0) {
mesh.sendEvent(elements_event);
}
if (nb_new_stuff(0) > 0) {
mesh.sendEvent(nodes_event);
// mesh.sendEvent(global_ids_updater.getChangedNodeEvent());
}
return std::make_tuple(nb_new_stuff(0), nb_new_stuff(1));
}
private:
SolidMechanicsModelCohesive & model;
Mesh & mesh;
GlobalIdsUpdater global_ids_updater;
};
/* -------------------------------------------------------------------------- */
SolidMechanicsModelCohesive::SolidMechanicsModelCohesive(
- Mesh & mesh, UInt dim, const ID & id, const MemoryID & memory_id,
- std::shared_ptr<DOFManager> dof_manager)
- : SolidMechanicsModel(mesh, dim, id, memory_id, dof_manager,
+ Mesh & mesh, UInt dim, const ID & id, std::shared_ptr<DOFManager> dof_manager)
+ : SolidMechanicsModel(mesh, dim, id, dof_manager,
+
ModelType::_solid_mechanics_model_cohesive),
tangents("tangents", id), facet_stress("facet_stress", id),
facet_material("facet_material", id) {
AKANTU_DEBUG_IN();
registerFEEngineObject<MyFEEngineCohesiveType>("CohesiveFEEngine", mesh,
Model::spatial_dimension);
auto && tmp_material_selector =
std::make_shared<DefaultMaterialCohesiveSelector>(*this);
tmp_material_selector->setFallback(this->material_selector);
this->material_selector = tmp_material_selector;
#if defined(AKANTU_USE_IOHELPER)
this->mesh.registerDumper<DumperParaview>("cohesive elements", id);
this->mesh.addDumpMeshToDumper("cohesive elements", mesh,
Model::spatial_dimension, _not_ghost,
_ek_cohesive);
#endif
if (this->mesh.isDistributed()) {
/// create the distributed synchronizer for cohesive elements
this->cohesive_synchronizer = std::make_unique<ElementSynchronizer>(
mesh, "cohesive_distributed_synchronizer");
auto & synchronizer = mesh.getElementSynchronizer();
this->cohesive_synchronizer->split(synchronizer, [](auto && el) {
return Mesh::getKind(el.type) == _ek_cohesive;
});
this->registerSynchronizer(*cohesive_synchronizer,
SynchronizationTag::_material_id);
this->registerSynchronizer(*cohesive_synchronizer,
SynchronizationTag::_smm_stress);
this->registerSynchronizer(*cohesive_synchronizer,
SynchronizationTag::_smm_boundary);
}
this->inserter = std::make_unique<CohesiveElementInserter>(
this->mesh, id + ":cohesive_element_inserter");
registerFEEngineObject<MyFEEngineFacetType>(
"FacetsFEEngine", mesh.getMeshFacets(), Model::spatial_dimension - 1);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
SolidMechanicsModelCohesive::~SolidMechanicsModelCohesive() = default;
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelCohesive::setTimeStep(Real time_step,
const ID & solver_id) {
SolidMechanicsModel::setTimeStep(time_step, solver_id);
#if defined(AKANTU_USE_IOHELPER)
this->mesh.getDumper("cohesive elements").setTimeStep(time_step);
#endif
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelCohesive::initFullImpl(const ModelOptions & options) {
AKANTU_DEBUG_IN();
const auto & smmc_options =
aka::as_type<SolidMechanicsModelCohesiveOptions>(options);
this->is_extrinsic = smmc_options.is_extrinsic;
inserter->setIsExtrinsic(is_extrinsic);
if (mesh.isDistributed()) {
auto & mesh_facets = inserter->getMeshFacets();
auto & synchronizer =
aka::as_type<FacetSynchronizer>(mesh_facets.getElementSynchronizer());
// synchronizeGhostFacetsConnectivity();
/// create the facet synchronizer for extrinsic simulations
if (is_extrinsic) {
facet_stress_synchronizer = std::make_unique<ElementSynchronizer>(
synchronizer, id + ":facet_stress_synchronizer");
facet_stress_synchronizer->swapSendRecv();
this->registerSynchronizer(*facet_stress_synchronizer,
SynchronizationTag::_smmc_facets_stress);
}
}
MeshAccessor mesh_accessor(mesh);
mesh_accessor.registerGlobalDataUpdater(
std::make_unique<CohesiveMeshGlobalDataUpdater>(*this));
ParserSection section;
bool is_empty;
std::tie(section, is_empty) = this->getParserSection();
if (not is_empty) {
auto inserter_section =
section.getSubSections(ParserType::_cohesive_inserter);
if (inserter_section.begin() != inserter_section.end()) {
inserter->parseSection(*inserter_section.begin());
}
}
SolidMechanicsModel::initFullImpl(options);
AKANTU_DEBUG_OUT();
} // namespace akantu
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelCohesive::initMaterials() {
AKANTU_DEBUG_IN();
// make sure the material are instantiated
if (not are_materials_instantiated) {
instantiateMaterials();
}
/// find the first cohesive material
UInt cohesive_index = UInt(-1);
for (auto && material : enumerate(materials)) {
if (dynamic_cast<MaterialCohesive *>(std::get<1>(material).get()) !=
nullptr) {
cohesive_index = std::get<0>(material);
break;
}
}
if (cohesive_index == UInt(-1)) {
AKANTU_EXCEPTION("No cohesive materials in the material input file");
}
material_selector->setFallback(cohesive_index);
// set the facet information in the material in case of dynamic insertion
// to know what material to call for stress checks
const Mesh & mesh_facets = inserter->getMeshFacets();
facet_material.initialize(
mesh_facets, _spatial_dimension = spatial_dimension - 1,
_with_nb_element = true,
_default_value = material_selector->getFallbackValue());
for_each_element(
mesh_facets,
[&](auto && element) {
auto mat_index = (*material_selector)(element);
auto & mat = aka::as_type<MaterialCohesive>(*materials[mat_index]);
facet_material(element) = mat_index;
if (is_extrinsic) {
mat.addFacet(element);
}
},
_spatial_dimension = spatial_dimension - 1, _ghost_type = _not_ghost);
SolidMechanicsModel::initMaterials();
if (is_extrinsic) {
this->initAutomaticInsertion();
} else {
this->insertIntrinsicElements();
}
AKANTU_DEBUG_OUT();
} // namespace akantu
/* -------------------------------------------------------------------------- */
/**
* Initialize the model,basically it pre-compute the shapes, shapes derivatives
* and jacobian
*/
void SolidMechanicsModelCohesive::initModel() {
AKANTU_DEBUG_IN();
SolidMechanicsModel::initModel();
/// add cohesive type connectivity
ElementType type = _not_defined;
for (auto && type_ghost : ghost_types) {
for (const auto & tmp_type :
mesh.elementTypes(spatial_dimension, type_ghost)) {
const auto & connectivity = mesh.getConnectivity(tmp_type, type_ghost);
if (connectivity.empty()) {
continue;
}
type = tmp_type;
auto type_facet = Mesh::getFacetType(type);
auto type_cohesive = FEEngine::getCohesiveElementType(type_facet);
mesh.addConnectivityType(type_cohesive, type_ghost);
}
}
AKANTU_DEBUG_ASSERT(type != _not_defined, "No elements in the mesh");
getFEEngine("CohesiveFEEngine").initShapeFunctions(_not_ghost);
getFEEngine("CohesiveFEEngine").initShapeFunctions(_ghost);
if (is_extrinsic) {
getFEEngine("FacetsFEEngine").initShapeFunctions(_not_ghost);
getFEEngine("FacetsFEEngine").initShapeFunctions(_ghost);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelCohesive::insertIntrinsicElements() {
AKANTU_DEBUG_IN();
inserter->insertIntrinsicElements();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelCohesive::initAutomaticInsertion() {
AKANTU_DEBUG_IN();
this->inserter->limitCheckFacets();
this->updateFacetStressSynchronizer();
this->resizeFacetStress();
/// compute normals on facets
this->computeNormals();
this->initStressInterpolation();
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelCohesive::updateAutomaticInsertion() {
AKANTU_DEBUG_IN();
this->inserter->limitCheckFacets();
this->updateFacetStressSynchronizer();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelCohesive::initStressInterpolation() {
Mesh & mesh_facets = inserter->getMeshFacets();
/// compute quadrature points coordinates on facets
Array<Real> & position = mesh.getNodes();
ElementTypeMapArray<Real> quad_facets("quad_facets", id);
quad_facets.initialize(mesh_facets, _nb_component = Model::spatial_dimension,
_spatial_dimension = Model::spatial_dimension - 1);
// mesh_facets.initElementTypeMapArray(quad_facets, Model::spatial_dimension,
// Model::spatial_dimension - 1);
getFEEngine("FacetsFEEngine")
.interpolateOnIntegrationPoints(position, quad_facets);
/// compute elements quadrature point positions and build
/// element-facet quadrature points data structure
ElementTypeMapArray<Real> elements_quad_facets("elements_quad_facets", id);
elements_quad_facets.initialize(
mesh, _nb_component = Model::spatial_dimension,
_spatial_dimension = Model::spatial_dimension);
// mesh.initElementTypeMapArray(elements_quad_facets,
// Model::spatial_dimension,
// Model::spatial_dimension);
for (auto elem_gt : ghost_types) {
for (const auto & type : mesh.elementTypes(Model::spatial_dimension, elem_gt)) {
UInt nb_element = mesh.getNbElement(type, elem_gt);
if (nb_element == 0) {
continue;
}
/// compute elements' quadrature points and list of facet
/// quadrature points positions by element
const auto & facet_to_element =
mesh_facets.getSubelementToElement(type, elem_gt);
auto & el_q_facet = elements_quad_facets(type, elem_gt);
auto facet_type = Mesh::getFacetType(type);
auto nb_quad_per_facet =
getFEEngine("FacetsFEEngine").getNbIntegrationPoints(facet_type);
auto nb_facet_per_elem = facet_to_element.getNbComponent();
// small hack in the loop to skip boundary elements, they are silently
// initialized to NaN to see if this causes problems
el_q_facet.resize(nb_element * nb_facet_per_elem * nb_quad_per_facet,
std::numeric_limits<Real>::quiet_NaN());
for (auto && data :
zip(make_view(facet_to_element),
make_view(el_q_facet, spatial_dimension, nb_quad_per_facet))) {
const auto & global_facet = std::get<0>(data);
auto & el_q = std::get<1>(data);
if (global_facet == ElementNull) {
continue;
}
Matrix<Real> quad_f =
make_view(quad_facets(global_facet.type, global_facet.ghost_type),
spatial_dimension, nb_quad_per_facet)
.begin()[global_facet.element];
el_q = quad_f;
// for (UInt q = 0; q < nb_quad_per_facet; ++q) {
// for (UInt s = 0; s < Model::spatial_dimension; ++s) {
// el_q_facet(el * nb_facet_per_elem * nb_quad_per_facet +
// f * nb_quad_per_facet + q,
// s) = quad_f(global_facet * nb_quad_per_facet + q,
// s);
// }
// }
//}
}
}
}
/// loop over non cohesive materials
for (auto && material : materials) {
if (aka::is_of_type<MaterialCohesive>(material)) {
continue;
}
/// initialize the interpolation function
material->initElementalFieldInterpolation(elements_quad_facets);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelCohesive::assembleInternalForces() {
AKANTU_DEBUG_IN();
// f_int += f_int_cohe
for (auto & material : this->materials) {
try {
auto & mat = aka::as_type<MaterialCohesive>(*material);
mat.computeTraction(_not_ghost);
} catch (std::bad_cast & bce) {
}
}
SolidMechanicsModel::assembleInternalForces();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelCohesive::computeNormals() {
AKANTU_DEBUG_IN();
Mesh & mesh_facets = this->inserter->getMeshFacets();
this->getFEEngine("FacetsFEEngine")
.computeNormalsOnIntegrationPoints(_not_ghost);
/**
* @todo store tangents while computing normals instead of
* recomputing them as follows:
*/
/* ------------------------------------------------------------------------ */
UInt tangent_components =
Model::spatial_dimension * (Model::spatial_dimension - 1);
tangents.initialize(mesh_facets, _nb_component = tangent_components,
_spatial_dimension = Model::spatial_dimension - 1);
// mesh_facets.initElementTypeMapArray(tangents, tangent_components,
// Model::spatial_dimension - 1);
for (auto facet_type :
mesh_facets.elementTypes(Model::spatial_dimension - 1)) {
const Array<Real> & normals =
this->getFEEngine("FacetsFEEngine")
.getNormalsOnIntegrationPoints(facet_type);
Array<Real> & tangents = this->tangents(facet_type);
Math::compute_tangents(normals, tangents);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelCohesive::interpolateStress() {
ElementTypeMapArray<Real> by_elem_result("temporary_stress_by_facets", id);
for (auto & material : materials) {
if (not aka::is_of_type<MaterialCohesive>(material)) {
/// interpolate stress on facet quadrature points positions
material->interpolateStressOnFacets(facet_stress, by_elem_result);
}
}
this->synchronize(SynchronizationTag::_smmc_facets_stress);
}
/* -------------------------------------------------------------------------- */
UInt SolidMechanicsModelCohesive::checkCohesiveStress() {
AKANTU_DEBUG_IN();
if (not is_extrinsic) {
AKANTU_EXCEPTION(
"This function can only be used for extrinsic cohesive elements");
}
interpolateStress();
for (auto & mat : materials) {
if (aka::is_of_type<MaterialCohesive>(mat)) {
/// check which not ghost cohesive elements are to be created
auto * mat_cohesive = aka::as_type<MaterialCohesive>(mat.get());
mat_cohesive->checkInsertion();
}
}
/// communicate data among processors
// this->synchronize(SynchronizationTag::_smmc_facets);
/// insert cohesive elements
UInt nb_new_elements = inserter->insertElements();
// if (nb_new_elements > 0) {
// this->reinitializeSolver();
// }
AKANTU_DEBUG_OUT();
return nb_new_elements;
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelCohesive::onElementsAdded(
const Array<Element> & element_list, const NewElementsEvent & event) {
AKANTU_DEBUG_IN();
SolidMechanicsModel::onElementsAdded(element_list, event);
if (is_extrinsic) {
resizeFacetStress();
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelCohesive::onNodesAdded(const Array<UInt> & new_nodes,
const NewNodesEvent & event) {
AKANTU_DEBUG_IN();
SolidMechanicsModel::onNodesAdded(new_nodes, event);
const CohesiveNewNodesEvent * cohesive_event;
if ((cohesive_event = dynamic_cast<const CohesiveNewNodesEvent *>(&event)) ==
nullptr) {
return;
}
const auto & old_nodes = cohesive_event->getOldNodesList();
auto copy = [this, &new_nodes, &old_nodes](auto & arr) {
UInt new_node;
UInt old_node;
auto view = make_view(arr, spatial_dimension);
auto begin = view.begin();
for (auto && pair : zip(new_nodes, old_nodes)) {
std::tie(new_node, old_node) = pair;
auto old_ = begin + old_node;
auto new_ = begin + new_node;
*new_ = *old_;
}
};
copy(*displacement);
copy(*blocked_dofs);
if (velocity) {
copy(*velocity);
}
if (acceleration) {
copy(*acceleration);
}
if (current_position) {
copy(*current_position);
}
if (previous_displacement) {
copy(*previous_displacement);
}
// if (external_force)
// copy(*external_force);
// if (internal_force)
// copy(*internal_force);
if (displacement_increment) {
copy(*displacement_increment);
}
copy(getDOFManager().getSolution("displacement"));
// this->assembleMassLumped();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelCohesive::afterSolveStep(bool converged) {
AKANTU_DEBUG_IN();
/*
* This is required because the Cauchy stress is the stress measure that
* is used to check the insertion of cohesive elements
*/
if (converged) {
for (auto & mat : materials) {
if (mat->isFiniteDeformation()) {
mat->computeAllCauchyStresses(_not_ghost);
}
}
}
SolidMechanicsModel::afterSolveStep(converged);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelCohesive::printself(std::ostream & stream,
int indent) const {
std::string space(indent, AKANTU_INDENT);
stream << space << "SolidMechanicsModelCohesive ["
<< "\n";
SolidMechanicsModel::printself(stream, indent + 2);
stream << space << "]" << std::endl;
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelCohesive::resizeFacetStress() {
AKANTU_DEBUG_IN();
this->facet_stress.initialize(getFEEngine("FacetsFEEngine"),
_nb_component =
2 * spatial_dimension * spatial_dimension,
_spatial_dimension = spatial_dimension - 1);
// for (auto && ghost_type : ghost_types) {
// for (const const auto & type :
// mesh_facets.elementTypes(spatial_dimension - 1, ghost_type)) {
// UInt nb_facet = mesh_facets.getNbElement(type, ghost_type);
// UInt nb_quadrature_points = getFEEngine("FacetsFEEngine")
// .getNbIntegrationPoints(type,
// ghost_type);
// UInt new_size = nb_facet * nb_quadrature_points;
// facet_stress(type, ghost_type).resize(new_size);
// }
// }
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelCohesive::addDumpGroupFieldToDumper(
const std::string & dumper_name, const std::string & field_id,
const std::string & group_name, ElementKind element_kind,
bool padding_flag) {
AKANTU_DEBUG_IN();
UInt spatial_dimension = Model::spatial_dimension;
ElementKind _element_kind = element_kind;
if (dumper_name == "cohesive elements") {
_element_kind = _ek_cohesive;
} else if (dumper_name == "facets") {
spatial_dimension = Model::spatial_dimension - 1;
}
SolidMechanicsModel::addDumpGroupFieldToDumper(dumper_name, field_id,
group_name, spatial_dimension,
_element_kind, padding_flag);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModelCohesive::onDump() {
this->flattenAllRegisteredInternals(_ek_cohesive);
SolidMechanicsModel::onDump();
}
/* -------------------------------------------------------------------------- */
} // namespace akantu
diff --git a/src/model/solid_mechanics/solid_mechanics_model_cohesive/solid_mechanics_model_cohesive.hh b/src/model/solid_mechanics/solid_mechanics_model_cohesive/solid_mechanics_model_cohesive.hh
index 41131c3b6..e99d82cc3 100644
--- a/src/model/solid_mechanics/solid_mechanics_model_cohesive/solid_mechanics_model_cohesive.hh
+++ b/src/model/solid_mechanics/solid_mechanics_model_cohesive/solid_mechanics_model_cohesive.hh
@@ -1,309 +1,308 @@
/**
* @file solid_mechanics_model_cohesive.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
* @author Marco Vocialta <marco.vocialta@epfl.ch>
*
* @date creation: Tue May 08 2012
* @date last modification: Mon Feb 05 2018
*
* @brief Solid mechanics model for cohesive elements
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "cohesive_element_inserter.hh"
#include "material_selector_cohesive.hh"
#include "random_internal_field.hh" // included to have the specialization of
// ParameterTyped::operator Real()
#include "solid_mechanics_model.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_SOLID_MECHANICS_MODEL_COHESIVE_HH_
#define AKANTU_SOLID_MECHANICS_MODEL_COHESIVE_HH_
/* -------------------------------------------------------------------------- */
namespace akantu {
class FacetSynchronizer;
class FacetStressSynchronizer;
class ElementSynchronizer;
} // namespace akantu
namespace akantu {
/* -------------------------------------------------------------------------- */
struct FacetsCohesiveIntegrationOrderFunctor {
template <ElementType type, ElementType cohesive_type =
CohesiveFacetProperty<type>::cohesive_type>
struct _helper {
static constexpr int get() {
return ElementClassProperty<cohesive_type>::polynomial_degree;
}
};
template <ElementType type> struct _helper<type, _not_defined> {
static constexpr int get() {
return ElementClassProperty<type>::polynomial_degree;
}
};
template <ElementType type> static inline constexpr int getOrder() {
return _helper<type>::get();
}
};
/* -------------------------------------------------------------------------- */
/* Solid Mechanics Model for Cohesive elements */
/* -------------------------------------------------------------------------- */
class SolidMechanicsModelCohesive : public SolidMechanicsModel,
public SolidMechanicsModelEventHandler {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
class NewCohesiveNodesEvent : public NewNodesEvent {
public:
AKANTU_GET_MACRO_NOT_CONST(OldNodesList, old_nodes, Array<UInt> &);
AKANTU_GET_MACRO(OldNodesList, old_nodes, const Array<UInt> &);
protected:
Array<UInt> old_nodes;
};
using MyFEEngineCohesiveType =
FEEngineTemplate<IntegratorGauss, ShapeLagrange, _ek_cohesive>;
using MyFEEngineFacetType =
FEEngineTemplate<IntegratorGauss, ShapeLagrange, _ek_regular,
FacetsCohesiveIntegrationOrderFunctor>;
SolidMechanicsModelCohesive(Mesh & mesh, UInt dim = _all_dimensions,
- const ID & id = "solid_mechanics_model_cohesive",
- const MemoryID & memory_id = 0,
+ const ID & id = "solid_mechanics_model_cohesive",
std::shared_ptr<DOFManager> dof_manager = nullptr);
~SolidMechanicsModelCohesive() override;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
protected:
/// initialize the cohesive model
void initFullImpl(const ModelOptions & options) override;
public:
/// set the value of the time step
void setTimeStep(Real time_step, const ID & solver_id = "") override;
/// assemble the residual for the explicit scheme
void assembleInternalForces() override;
/// function to perform a stress check on each facet and insert
/// cohesive elements if needed (returns the number of new cohesive
/// elements)
UInt checkCohesiveStress();
/// interpolate stress on facets
void interpolateStress();
/// update automatic insertion after a change in the element inserter
void updateAutomaticInsertion();
/// insert intrinsic cohesive elements
void insertIntrinsicElements();
// template <SolveConvergenceMethod cmethod, SolveConvergenceCriteria
// criteria> bool solveStepCohesive(Real tolerance, Real & error, UInt
// max_iteration = 100,
// bool load_reduction = false,
// Real tol_increase_factor = 1.0,
// bool do_not_factorize = false);
protected:
/// initialize stress interpolation
void initStressInterpolation();
/// initialize the model
void initModel() override;
/// initialize cohesive material
void initMaterials() override;
/// init facet filters for cohesive materials
void initFacetFilter();
/// function to print the contain of the class
void printself(std::ostream & stream, int indent = 0) const override;
private:
/// insert cohesive elements along a given physical surface of the mesh
void insertElementsFromMeshData(const std::string & physical_name);
/// initialize completely the model for extrinsic elements
void initAutomaticInsertion();
/// compute facets' normals
void computeNormals();
/// resize facet stress
void resizeFacetStress();
/// init facets_check array
void initFacetsCheck();
/* ------------------------------------------------------------------------ */
/* Mesh Event Handler inherited members */
/* ------------------------------------------------------------------------ */
protected:
void onNodesAdded(const Array<UInt> & new_nodes,
const NewNodesEvent & event) override;
void onElementsAdded(const Array<Element> & element_list,
const NewElementsEvent & event) override;
/* ------------------------------------------------------------------------ */
/* SolidMechanicsModelEventHandler inherited members */
/* ------------------------------------------------------------------------ */
public:
void afterSolveStep(bool converged = true) override;
/* ------------------------------------------------------------------------ */
/* Dumpable interface */
/* ------------------------------------------------------------------------ */
public:
void onDump() override;
void addDumpGroupFieldToDumper(const std::string & dumper_name,
const std::string & field_id,
const std::string & group_name,
ElementKind element_kind,
bool padding_flag) override;
public:
/// register the tags associated with the parallel synchronizer for
/// cohesive elements
// void initParallel(MeshPartition * partition,
// DataAccessor * data_accessor = NULL,
// bool extrinsic = false);
protected:
//void synchronizeGhostFacetsConnectivity();
void updateCohesiveSynchronizers(NewElementsEvent & elements_event);
void updateFacetStressSynchronizer();
friend class CohesiveElementInserter;
/* ------------------------------------------------------------------------ */
/* Data Accessor inherited members */
/* ------------------------------------------------------------------------ */
public:
UInt getNbData(const Array<Element> & elements,
const SynchronizationTag & tag) const override;
void packData(CommunicationBuffer & buffer, const Array<Element> & elements,
const SynchronizationTag & tag) const override;
void unpackData(CommunicationBuffer & buffer, const Array<Element> & elements,
const SynchronizationTag & tag) override;
protected:
UInt getNbQuadsForFacetCheck(const Array<Element> & elements) const;
template <typename T>
void packFacetStressDataHelper(const ElementTypeMapArray<T> & data_to_pack,
CommunicationBuffer & buffer,
const Array<Element> & elements) const;
template <typename T>
void unpackFacetStressDataHelper(ElementTypeMapArray<T> & data_to_unpack,
CommunicationBuffer & buffer,
const Array<Element> & elements) const;
template <typename T, bool pack_helper>
void packUnpackFacetStressDataHelper(ElementTypeMapArray<T> & data_to_pack,
CommunicationBuffer & buffer,
const Array<Element> & element) const;
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
/// get facet mesh
AKANTU_GET_MACRO(MeshFacets, mesh.getMeshFacets(), const Mesh &);
/// get stress on facets vector
AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(StressOnFacets, facet_stress, Real);
/// get facet material
AKANTU_GET_MACRO_BY_ELEMENT_TYPE(FacetMaterial, facet_material, UInt);
/// get facet material
AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(FacetMaterial, facet_material, UInt);
/// get facet material
AKANTU_GET_MACRO(FacetMaterial, facet_material,
const ElementTypeMapArray<UInt> &);
/// @todo THIS HAS TO BE CHANGED
AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(Tangents, tangents, Real);
/// get element inserter
AKANTU_GET_MACRO_NOT_CONST(ElementInserter, *inserter,
CohesiveElementInserter &);
/// get is_extrinsic boolean
AKANTU_GET_MACRO(IsExtrinsic, is_extrinsic, bool);
/// get cohesive elements synchronizer
AKANTU_GET_MACRO_NOT_CONST(CohesiveSynchronizer, *cohesive_synchronizer,
ElementSynchronizer &);
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
private:
friend class CohesiveMeshGlobalDataUpdater;
/// @todo store tangents when normals are computed:
ElementTypeMapArray<Real> tangents;
/// stress on facets on the two sides by quadrature point
ElementTypeMapArray<Real> facet_stress;
/// material to use if a cohesive element is created on a facet
ElementTypeMapArray<UInt> facet_material;
bool is_extrinsic{false};
/// cohesive element inserter
std::unique_ptr<CohesiveElementInserter> inserter;
/// facet stress synchronizer
std::unique_ptr<ElementSynchronizer> facet_stress_synchronizer;
/// cohesive elements synchronizer
std::unique_ptr<ElementSynchronizer> cohesive_synchronizer;
};
} // namespace akantu
#include "solid_mechanics_model_cohesive_inline_impl.hh"
#endif /* AKANTU_SOLID_MECHANICS_MODEL_COHESIVE_HH_ */
diff --git a/src/model/solid_mechanics/solid_mechanics_model_embedded_interface/embedded_interface_model.cc b/src/model/solid_mechanics/solid_mechanics_model_embedded_interface/embedded_interface_model.cc
index b71f9e3cc..0b5a8b4d3 100644
--- a/src/model/solid_mechanics/solid_mechanics_model_embedded_interface/embedded_interface_model.cc
+++ b/src/model/solid_mechanics/solid_mechanics_model_embedded_interface/embedded_interface_model.cc
@@ -1,173 +1,172 @@
/**
* @file embedded_interface_model.cc
*
* @author Lucas Frerot <lucas.frerot@epfl.ch>
*
* @date creation: Fri Mar 13 2015
* @date last modification: Wed Feb 14 2018
*
* @brief Model of Solid Mechanics with embedded interfaces
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "embedded_interface_model.hh"
#include "integrator_gauss.hh"
#include "material_elastic.hh"
#include "material_reinforcement.hh"
#include "mesh_iterators.hh"
#include "shape_lagrange.hh"
#ifdef AKANTU_USE_IOHELPER
#include "dumpable_inline_impl.hh"
#include "dumper_iohelper_paraview.hh"
#endif
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
EmbeddedInterfaceModel::EmbeddedInterfaceModel(Mesh & mesh,
Mesh & primitive_mesh,
UInt spatial_dimension,
- const ID & id,
- const MemoryID & memory_id)
- : SolidMechanicsModel(mesh, spatial_dimension, id, memory_id),
+ const ID & id)
+ : SolidMechanicsModel(mesh, spatial_dimension, id),
intersector(mesh, primitive_mesh), interface_mesh(nullptr),
primitive_mesh(primitive_mesh), interface_material_selector(nullptr) {
this->model_type = ModelType::_embedded_model;
// This pointer should be deleted by ~SolidMechanicsModel()
auto mat_sel_pointer =
std::make_shared<MeshDataMaterialSelector<std::string>>("physical_names",
*this);
this->setMaterialSelector(mat_sel_pointer);
interface_mesh = &(intersector.getInterfaceMesh());
// Create 1D FEEngine on the interface mesh
registerFEEngineObject<MyFEEngineType>("EmbeddedInterfaceFEEngine",
*interface_mesh, 1);
// Registering allocator for material reinforcement
MaterialFactory::getInstance().registerAllocator(
"reinforcement",
[&](UInt dim, const ID & constitutive, SolidMechanicsModel & /*unused*/,
const ID & id) -> std::unique_ptr<Material> {
if (constitutive == "elastic") {
using mat = MaterialElastic<1>;
switch (dim) {
case 2:
return std::make_unique<MaterialReinforcement<mat, 2>>(*this, id);
case 3:
return std::make_unique<MaterialReinforcement<mat, 3>>(*this, id);
default:
AKANTU_EXCEPTION("Dimension 1 is invalid for reinforcements");
}
} else {
AKANTU_EXCEPTION("Reinforcement type" << constitutive
<< " is not recognized");
}
});
}
/* -------------------------------------------------------------------------- */
EmbeddedInterfaceModel::~EmbeddedInterfaceModel() {
delete interface_material_selector;
}
/* -------------------------------------------------------------------------- */
void EmbeddedInterfaceModel::initFullImpl(const ModelOptions & options) {
const auto & eim_options =
aka::as_type<EmbeddedInterfaceModelOptions>(options);
// Do no initialize interface_mesh if told so
if (eim_options.has_intersections) {
intersector.constructData();
}
SolidMechanicsModel::initFullImpl(options);
#if defined(AKANTU_USE_IOHELPER)
this->mesh.registerDumper<DumperParaview>("reinforcement", id);
this->mesh.addDumpMeshToDumper("reinforcement", *interface_mesh, 1,
_not_ghost, _ek_regular);
#endif
}
void EmbeddedInterfaceModel::initModel() {
// Initialize interface FEEngine
SolidMechanicsModel::initModel();
FEEngine & engine = getFEEngine("EmbeddedInterfaceFEEngine");
engine.initShapeFunctions(_not_ghost);
engine.initShapeFunctions(_ghost);
}
/* -------------------------------------------------------------------------- */
void EmbeddedInterfaceModel::assignMaterialToElements(
const ElementTypeMapArray<UInt> * filter) {
delete interface_material_selector;
interface_material_selector =
new InterfaceMeshDataMaterialSelector<std::string>("physical_names",
*this);
for_each_element(getInterfaceMesh(),
[&](auto && element) {
auto mat_index = (*interface_material_selector)(element);
// material_index(element) = mat_index;
materials[mat_index]->addElement(element);
// this->material_local_numbering(element) = index;
},
_element_filter = filter, _spatial_dimension = 1);
SolidMechanicsModel::assignMaterialToElements(filter);
}
/* -------------------------------------------------------------------------- */
void EmbeddedInterfaceModel::addDumpGroupFieldToDumper(
const std::string & dumper_name, const std::string & field_id,
const std::string & group_name, ElementKind element_kind,
bool padding_flag) {
#ifdef AKANTU_USE_IOHELPER
std::shared_ptr<dumpers::Field> field;
// If dumper is reinforcement, create a 1D elemental field
if (dumper_name == "reinforcement") {
field = this->createElementalField(field_id, group_name, padding_flag, 1,
element_kind);
} else {
try {
SolidMechanicsModel::addDumpGroupFieldToDumper(
dumper_name, field_id, group_name, element_kind, padding_flag);
} catch (...) {
}
}
if (field) {
DumperIOHelper & dumper = mesh.getGroupDumper(dumper_name, group_name);
Model::addDumpGroupFieldToDumper(field_id, field, dumper);
}
#endif
}
} // namespace akantu
diff --git a/src/model/solid_mechanics/solid_mechanics_model_embedded_interface/embedded_interface_model.hh b/src/model/solid_mechanics/solid_mechanics_model_embedded_interface/embedded_interface_model.hh
index 452ca95c5..eb9922ce2 100644
--- a/src/model/solid_mechanics/solid_mechanics_model_embedded_interface/embedded_interface_model.hh
+++ b/src/model/solid_mechanics/solid_mechanics_model_embedded_interface/embedded_interface_model.hh
@@ -1,155 +1,153 @@
/**
* @file embedded_interface_model.hh
*
* @author Lucas Frerot <lucas.frerot@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Wed Jan 31 2018
*
* @brief Model of Solid Mechanics with embedded interfaces
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_EMBEDDED_INTERFACE_MODEL_HH_
#define AKANTU_EMBEDDED_INTERFACE_MODEL_HH_
#include "aka_common.hh"
#include "mesh.hh"
#include "solid_mechanics_model.hh"
#include "embedded_interface_intersector.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
/**
* @brief Solid mechanics model using the embedded model.
*
* This SolidMechanicsModel subclass implements the embedded model,
* a method used to represent 1D elements in a finite elements model
* (eg. reinforcements in concrete).
*
* In addition to the SolidMechanicsModel properties, this model has
* a mesh of the 1D elements embedded in the model, and an instance of the
* EmbeddedInterfaceIntersector class for the computation of the intersections
* of the
* 1D elements with the background (bulk) mesh.
*
* @see MaterialReinforcement
*/
class EmbeddedInterfaceModel : public SolidMechanicsModel {
using MyFEEngineType = SolidMechanicsModel::MyFEEngineType;
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
/**
* @brief Constructor
*
* @param mesh main mesh (concrete)
* @param primitive_mesh mesh of the embedded reinforcement
* @param spatial_dimension the spatial dimension to be considered by this model
* @param id the id of the model
- * @param memory_id the id of the memory manager to use
*/
EmbeddedInterfaceModel(Mesh & mesh, Mesh & primitive_mesh,
UInt spatial_dimension = _all_dimensions,
- const ID & id = "embedded_interface_model",
- const MemoryID & memory_id = 0);
+ const ID & id = "embedded_interface_model");
/// Destructor
~EmbeddedInterfaceModel() override;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// Initialise the model
void initFullImpl(
const ModelOptions & options = EmbeddedInterfaceModelOptions()) override;
/// Initialise the materials
void
assignMaterialToElements(const ElementTypeMapArray<UInt> * filter) override;
/// Initialize the embedded shape functions
void initModel() override;
/// Allows filtering of dump fields which need to be dumpes on interface mesh
void addDumpGroupFieldToDumper(const std::string & dumper_name,
const std::string & field_id,
const std::string & group_name,
ElementKind element_kind,
bool padding_flag) override;
// virtual ElementTypeMap<UInt> getInternalDataPerElem(const std::string &
// field_name,
// ElementKind
// kind);
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
/// Get interface mesh
AKANTU_GET_MACRO(InterfaceMesh, *interface_mesh, Mesh &);
/// Get associated elements
AKANTU_GET_MACRO_BY_ELEMENT_TYPE(
InterfaceAssociatedElements,
interface_mesh->getData<Element>("associated_element"), Element);
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
/// Intersector object to build the interface mesh
EmbeddedInterfaceIntersector intersector;
/// Interface mesh (weak reference)
Mesh * interface_mesh;
/// Mesh used to create the CGAL primitives for intersections
Mesh & primitive_mesh;
/// Material selector for interface
MaterialSelector * interface_material_selector;
};
/// Material selector based on mesh data for interface elements
template <typename T>
class InterfaceMeshDataMaterialSelector
: public ElementDataMaterialSelector<T> {
public:
InterfaceMeshDataMaterialSelector(const std::string & name,
const EmbeddedInterfaceModel & model,
UInt first_index = 1)
: ElementDataMaterialSelector<T>(
model.getInterfaceMesh().getData<T>(name), model, first_index) {}
};
} // namespace akantu
#endif // AKANTU_EMBEDDED_INTERFACE_MODEL_HH_
diff --git a/src/model/solid_mechanics/solid_mechanics_model_io.cc b/src/model/solid_mechanics/solid_mechanics_model_io.cc
index b7603d9cb..b6fc9c8da 100644
--- a/src/model/solid_mechanics/solid_mechanics_model_io.cc
+++ b/src/model/solid_mechanics/solid_mechanics_model_io.cc
@@ -1,341 +1,338 @@
/**
* @file solid_mechanics_model_io.cc
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author David Simon Kammer <david.kammer@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Sun Jul 09 2017
* @date last modification: Sun Dec 03 2017
*
* @brief Dumpable part of the SolidMechnicsModel
*
*
* Copyright (©) 2016-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
#include "solid_mechanics_model.hh"
#include "group_manager_inline_impl.hh"
#include "dumpable_inline_impl.hh"
#ifdef AKANTU_USE_IOHELPER
#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"
#endif
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<Real>(field_name, element_kind);
if (is_internal) {
return true;
}
}
return false;
}
/* -------------------------------------------------------------------------- */
ElementTypeMap<UInt>
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<Real>(field_name, element_kind)) {
return material->getInternalDataPerElem<Real>(field_name, element_kind);
}
}
return ElementTypeMap<UInt>();
}
/* -------------------------------------------------------------------------- */
ElementTypeMapArray<Real> &
SolidMechanicsModel::flattenInternal(const std::string & field_name,
ElementKind kind,
const GhostType ghost_type) {
auto key = std::make_pair(field_name, kind);
ElementTypeMapArray<Real> * internal_flat;
auto it = this->registered_internals.find(key);
if (it == this->registered_internals.end()) {
auto internal = std::make_unique<ElementTypeMapArray<Real>>(
- field_name, this->id, this->memory_id);
+ 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<Real>(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::onDump() {
this->flattenAllRegisteredInternals(_ek_regular);
}
/* -------------------------------------------------------------------------- */
#ifdef AKANTU_USE_IOHELPER
std::shared_ptr<dumpers::Field> SolidMechanicsModel::createElementalField(
const std::string & field_name, const std::string & group_name,
bool padding_flag, UInt spatial_dimension,
ElementKind kind) {
std::shared_ptr<dumpers::Field> field;
if (field_name == "partitions") {
field = mesh.createElementalField<UInt, dumpers::ElementPartitionField>(
mesh.getConnectivities(), group_name, spatial_dimension, kind);
} else if (field_name == "material_index") {
field = mesh.createElementalField<UInt, Vector, 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<Real, dumpers::InternalMaterialField>(
internal_flat, group_name, spatial_dimension, kind, nb_data_per_elem);
std::unique_ptr<dumpers::ComputeFunctorInterface> func;
if (field_name == "strain") {
func = std::make_unique<dumpers::ComputeStrain<false>>(*this);
} else if (field_name == "Von Mises stress") {
func = std::make_unique<dumpers::ComputeVonMisesStress>(*this);
} else if (field_name == "Green strain") {
func = std::make_unique<dumpers::ComputeStrain<true>>(*this);
} else if (field_name == "principal strain") {
func = std::make_unique<dumpers::ComputePrincipalStrain<false>>(*this);
} else if (field_name == "principal Green strain") {
func = std::make_unique<dumpers::ComputePrincipalStrain<true>>(*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<dumpers::StressPadder<2>>(*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<dumpers::StrainPadder<2>>(*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<dumpers::Field>
SolidMechanicsModel::createNodalFieldReal(const std::string & field_name,
const std::string & group_name,
bool padding_flag) {
std::map<std::string, Array<Real> *> 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<dumpers::Field> 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<dumpers::Field> SolidMechanicsModel::createNodalFieldBool(
const std::string & field_name, const std::string & group_name,
__attribute__((unused)) bool padding_flag) {
std::map<std::string, Array<bool> *> uint_nodal_fields;
uint_nodal_fields["blocked_dofs"] = blocked_dofs.get();
std::shared_ptr<dumpers::Field> field;
field = mesh.createNodalField(uint_nodal_fields[field_name], group_name);
return field;
}
/* -------------------------------------------------------------------------- */
#else
/* -------------------------------------------------------------------------- */
std::shared_ptr<dumpers::Field>
SolidMechanicsModel::createElementalField(const std::string &,
const std::string &, bool,
const UInt &, ElementKind) {
return nullptr;
}
/* --------------------------------------------------------------------------
*/
std::shaed_ptr<dumpers::Field>
SolidMechanicsModel::createNodalFieldReal(const std::string &,
const std::string &, bool) {
return nullptr;
}
/* --------------------------------------------------------------------------
*/
std::shared_ptr<dumpers::Field>
SolidMechanicsModel::createNodalFieldBool(const std::string &,
const std::string &, bool) {
return nullptr;
}
#endif
-/* --------------------------------------------------------------------------
- */
+/* -------------------------------------------------------------------------- */
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, UInt step) {
this->onDump();
EventManager::sendEvent(SolidMechanicsModelEvent::BeforeDumpEvent());
mesh.dump(dumper_name, step);
}
-/* -------------------------------------------------------------------------
- */
+/* -------------------------------------------------------------------------- */
void SolidMechanicsModel::dump(const std::string & dumper_name, Real time,
UInt 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(UInt step) {
this->onDump();
EventManager::sendEvent(SolidMechanicsModelEvent::BeforeDumpEvent());
mesh.dump(step);
}
/* -------------------------------------------------------------------------- */
void SolidMechanicsModel::dump(Real time, UInt step) {
this->onDump();
EventManager::sendEvent(SolidMechanicsModelEvent::BeforeDumpEvent());
mesh.dump(time, step);
}
} // 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 7580b96a6..6992dd52e 100644
--- a/src/model/solid_mechanics/solid_mechanics_model_material.cc
+++ b/src/model/solid_mechanics/solid_mechanics_model_material.cc
@@ -1,259 +1,243 @@
/**
* @file solid_mechanics_model_material.cc
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Nov 26 2010
* @date last modification: Tue Feb 20 2018
*
* @brief instatiation of materials
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#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");
UInt 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();
std::unique_ptr<Material> material = MaterialFactory::getInstance().allocate(
mat_type, spatial_dimension, opt_param, *this, mat_id);
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<MaterialNonLocalInterface *>(material.get()) == nullptr) {
continue;
}
this->non_local_manager = std::make_unique<NonLocalManager>(
- *this, *this, id + ":non_local_manager", memory_id);
+ *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<UInt> * filter) {
for_each_element(
mesh,
[&](auto && element) {
UInt mat_index = (*material_selector)(element);
AKANTU_DEBUG_ASSERT(
mat_index < 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<Array<Element>> element_to_add(materials.size());
std::vector<Array<Element>> element_to_remove(materials.size());
- Element element;
- for (auto ghost_type : ghost_types) {
- element.ghost_type = ghost_type;
-
- for (auto type :
- mesh.elementTypes(spatial_dimension, ghost_type, _ek_not_defined)) {
- element.type = type;
-
- UInt nb_element = mesh.getNbElement(type, ghost_type);
- Array<UInt> & mat_indexes = material_index(type, ghost_type);
-
- for (UInt el = 0; el < nb_element; ++el) {
- element.element = el;
-
- UInt old_material = mat_indexes(el);
- UInt 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);
}
- }
+ });
- UInt mat_index = 0;
- for (auto mat_it = materials.begin(); mat_it != materials.end();
- ++mat_it, ++mat_index) {
- (*mat_it)->removeElements(element_to_remove[mat_index]);
- (*mat_it)->addElements(element_to_add[mat_index]);
+ 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<Real> & 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_elements/structural_element_bernoulli_beam_2.hh b/src/model/structural_mechanics/structural_elements/structural_element_bernoulli_beam_2.hh
index 990560be4..84ca974d4 100644
--- a/src/model/structural_mechanics/structural_elements/structural_element_bernoulli_beam_2.hh
+++ b/src/model/structural_mechanics/structural_elements/structural_element_bernoulli_beam_2.hh
@@ -1,135 +1,70 @@
/**
* @file structural_element_bernoulli_beam_2.hh
*
* @author Fabian Barras <fabian.barras@epfl.ch>
* @author Lucas Frerot <lucas.frerot@epfl.ch>
* @author Sébastien Hartmann <sebastien.hartmann@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Wed Oct 11 2017
* @date last modification: Wed Jan 10 2018
*
* @brief Specific functions for bernoulli beam 2d
*
*
* Copyright (©) 2016-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_common.hh"
#include "structural_mechanics_model.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_STRUCTURAL_ELEMENT_BERNOULLI_BEAM_2_HH_
#define AKANTU_STRUCTURAL_ELEMENT_BERNOULLI_BEAM_2_HH_
namespace akantu {
-/* -------------------------------------------------------------------------- */
-template <>
-inline void StructuralMechanicsModel::assembleMass<_bernoulli_beam_2>() {
- AKANTU_DEBUG_IN();
- constexpr ElementType type = _bernoulli_beam_2;
-
- auto & fem = getFEEngineClass<MyFEEngineType>();
- auto nb_element = mesh.getNbElement(type);
- auto nb_nodes_per_element = mesh.getNbNodesPerElement(type);
- auto nb_quadrature_points = fem.getNbIntegrationPoints(type);
- auto nb_fields_to_interpolate = ElementClass<type>::getNbStressComponents();
- auto nt_n_field_size = nb_degree_of_freedom * nb_nodes_per_element;
-
- Array<Real> n(nb_element * nb_quadrature_points,
- nb_fields_to_interpolate * nt_n_field_size, "N");
-
- auto * rho_field =
- new Array<Real>(nb_element * nb_quadrature_points, 1, 0., "Rho");
- computeRho(*rho_field, type, _not_ghost);
-
-#if 0
- bool sign = true;
-
- for (auto && ghost_type : ghost_types) {
- fem.computeShapesMatrix(type, nb_degree_of_freedom, nb_nodes_per_element, n,
- 0, 0, 0, sign, ghost_type); // Ni ui -> u
- fem.computeShapesMatrix(type, nb_degree_of_freedom, nb_nodes_per_element, n,
- 1, 1, 1, sign, ghost_type); // Mi vi -> v
- fem.computeShapesMatrix(type, nb_degree_of_freedom, nb_nodes_per_element, n,
- 2, 2, 1, sign, ghost_type); // Li Theta_i -> v
- fem.assembleFieldMatrix(*rho_field, nb_degree_of_freedom, *mass_matrix, n,
- rotation_matrix, type, ghost_type);
- }
-#endif
-
- delete rho_field;
- AKANTU_DEBUG_OUT();
-}
-
-/* -------------------------------------------------------------------------- */
-template <>
-void StructuralMechanicsModel::computeRotationMatrix<_bernoulli_beam_2>(
- Array<Real> & rotations) {
- auto type = _bernoulli_beam_2;
- auto nodes_it = mesh.getNodes().begin(this->spatial_dimension);
-
- for (auto && tuple :
- zip(make_view(mesh.getConnectivity(type), 2),
- make_view(rotations, nb_degree_of_freedom, nb_degree_of_freedom))) {
-
- auto & connec = std::get<0>(tuple);
- auto & R = std::get<1>(tuple);
-
- Vector<Real> x2 = nodes_it[connec(1)]; // X2
- Vector<Real> x1 = nodes_it[connec(0)]; // X1
-
- auto le = x1.distance(x2);
- auto c = (x2(0) - x1(0)) / le;
- auto s = (x2(1) - x1(1)) / le;
-
- /// Definition of the rotation matrix
- R = {{c, s, 0.}, {-s, c, 0.}, {0., 0., 1.}};
- }
-}
-
/* -------------------------------------------------------------------------- */
template <>
void StructuralMechanicsModel::computeTangentModuli<_bernoulli_beam_2>(
Array<Real> & tangent_moduli) {
// auto nb_element = getFEEngine().getMesh().getNbElement(_bernoulli_beam_2);
auto nb_quadrature_points =
getFEEngine().getNbIntegrationPoints(_bernoulli_beam_2);
auto tangent_size = 2;
tangent_moduli.zero();
auto D_it = tangent_moduli.begin(tangent_size, tangent_size);
auto el_mat = element_material(_bernoulli_beam_2, _not_ghost).begin();
for (auto & mat : element_material(_bernoulli_beam_2, _not_ghost)) {
auto E = materials[mat].E;
auto A = materials[mat].A;
auto I = materials[mat].I;
for (UInt q = 0; q < nb_quadrature_points; ++q, ++D_it) {
auto & D = *D_it;
D(0, 0) = E * A;
D(1, 1) = E * I;
}
}
}
} // namespace akantu
#endif /* AKANTU_STRUCTURAL_ELEMENT_BERNOULLI_BEAM_2_HH_ */
diff --git a/src/model/structural_mechanics/structural_elements/structural_element_bernoulli_beam_3.hh b/src/model/structural_mechanics/structural_elements/structural_element_bernoulli_beam_3.hh
index 26e01970f..7a5a0f668 100644
--- a/src/model/structural_mechanics/structural_elements/structural_element_bernoulli_beam_3.hh
+++ b/src/model/structural_mechanics/structural_elements/structural_element_bernoulli_beam_3.hh
@@ -1,189 +1,74 @@
/**
* @file structural_element_bernoulli_beam_3.hh
*
* @author Fabian Barras <fabian.barras@epfl.ch>
* @author Lucas Frerot <lucas.frerot@epfl.ch>
* @author Sébastien Hartmann <sebastien.hartmann@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
* @author Damien Spielmann <damien.spielmann@epfl.ch>
*
* @date creation: Wed Oct 11 2017
* @date last modification: Tue Feb 20 2018
*
* @brief Specific functions for bernoulli beam 3d
*
*
* Copyright (©) 2016-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_STRUCTURAL_ELEMENT_BERNOULLI_BEAM_3_HH_
#define AKANTU_STRUCTURAL_ELEMENT_BERNOULLI_BEAM_3_HH_
#include "structural_mechanics_model.hh"
namespace akantu {
-/* -------------------------------------------------------------------------- */
-template <>
-inline void StructuralMechanicsModel::assembleMass<_bernoulli_beam_3>() {
- AKANTU_DEBUG_IN();
-#if 0
- GhostType ghost_type = _not_ghost;
- ElementType type = _bernoulli_beam_3;
- MyFEEngineType & fem = getFEEngineClass<MyFEEngineType>();
- UInt nb_element = getFEEngine().getMesh().getNbElement(type);
- UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
- UInt nb_quadrature_points = getFEEngine().getNbIntegrationPoints(type);
- UInt nb_fields_to_interpolate =
- getTangentStiffnessVoigtSize<_bernoulli_beam_3>();
-
- UInt nt_n_field_size = nb_degree_of_freedom * nb_nodes_per_element;
-
- Array<Real> * n =
- new Array<Real>(nb_element * nb_quadrature_points,
- nb_fields_to_interpolate * nt_n_field_size, "N");
- n->clear();
- Array<Real> * rho_field =
- new Array<Real>(nb_element * nb_quadrature_points, "Rho");
- rho_field->clear();
- computeRho(*rho_field, type, _not_ghost);
-
- /* --------------------------------------------------------------------------
- */
- fem.computeShapesMatrix(type, nb_degree_of_freedom, nb_nodes_per_element, n,
- 0, 0, 0, true, ghost_type); // Ni ui -> u
-
- fem.computeShapesMatrix(type, nb_degree_of_freedom, nb_nodes_per_element, n,
- 1, 1, 1, true, ghost_type); // Mi vi -> v
- fem.computeShapesMatrix(type, nb_degree_of_freedom, nb_nodes_per_element, n,
- 2, 5, 1, true, ghost_type); // Li Theta_z_i -> v
-
- fem.computeShapesMatrix(type, nb_degree_of_freedom, nb_nodes_per_element, n,
- 1, 2, 2, true, ghost_type); // Mi wi -> w
- fem.computeShapesMatrix(type, nb_degree_of_freedom, nb_nodes_per_element, n,
- 2, 4, 2, false, ghost_type); // -Li Theta_y_i -> w
-
- fem.computeShapesMatrix(type, nb_degree_of_freedom, nb_nodes_per_element, n,
- 0, 3, 3, true, ghost_type); // Ni Theta_x_i->Theta_x
- /* --------------------------------------------------------------------------
- */
-
- fem.assembleFieldMatrix(*rho_field, nb_degree_of_freedom, *mass_matrix, n,
- rotation_matrix, type, ghost_type);
-
- delete n;
- delete rho_field;
-#endif
- AKANTU_DEBUG_OUT();
-}
-
-/* -------------------------------------------------------------------------- */
-template <>
-void StructuralMechanicsModel::computeRotationMatrix<_bernoulli_beam_3>(
- Array<Real> & rotations) {
- ElementType type = _bernoulli_beam_3;
- Mesh & mesh = getFEEngine().getMesh();
- UInt nb_element = mesh.getNbElement(type);
-
- auto n_it = mesh.getNormals(type).begin(spatial_dimension);
- Array<UInt>::iterator<Vector<UInt>> connec_it =
- mesh.getConnectivity(type).begin(2);
- auto nodes_it = mesh.getNodes().begin(spatial_dimension);
-
- Matrix<Real> Pe(spatial_dimension, spatial_dimension);
- Matrix<Real> Pg(spatial_dimension, spatial_dimension);
- Matrix<Real> inv_Pg(spatial_dimension, spatial_dimension);
- Vector<Real> x_n(spatial_dimension); // x vect n
-
- Array<Real>::matrix_iterator R_it =
- rotations.begin(nb_degree_of_freedom, nb_degree_of_freedom);
-
- for (UInt e = 0; e < nb_element; ++e, ++n_it, ++connec_it, ++R_it) {
- Vector<Real> & n = *n_it;
- Matrix<Real> & R = *R_it;
- Vector<UInt> & connec = *connec_it;
-
- Vector<Real> x;
- x = nodes_it[connec(1)]; // X2
- Vector<Real> y;
- y = nodes_it[connec(0)]; // X1
-
- Real l = x.distance(y);
- x -= y; // X2 - X1
- x_n.crossProduct(x, n);
-
- Pe.eye();
- Pe(0, 0) *= l;
- Pe(1, 1) *= -l;
-
- Pg(0, 0) = x(0);
- Pg(0, 1) = x_n(0);
- Pg(0, 2) = n(0);
- Pg(1, 0) = x(1);
- Pg(1, 1) = x_n(1);
- Pg(1, 2) = n(1);
- Pg(2, 0) = x(2);
- Pg(2, 1) = x_n(2);
- Pg(2, 2) = n(2);
-
- inv_Pg.inverse(Pg);
-
- Pe *= inv_Pg;
- for (UInt i = 0; i < spatial_dimension; ++i) {
- for (UInt j = 0; j < spatial_dimension; ++j) {
- R(i, j) = Pe(i, j);
- R(i + spatial_dimension, j + spatial_dimension) = Pe(i, j);
- }
- }
- }
-}
-
/* -------------------------------------------------------------------------- */
template <>
void StructuralMechanicsModel::computeTangentModuli<_bernoulli_beam_3>(
Array<Real> & tangent_moduli) {
UInt nb_element = getFEEngine().getMesh().getNbElement(_bernoulli_beam_3);
UInt nb_quadrature_points =
getFEEngine().getNbIntegrationPoints(_bernoulli_beam_3);
UInt tangent_size = 4;
tangent_moduli.zero();
Array<Real>::matrix_iterator D_it =
tangent_moduli.begin(tangent_size, tangent_size);
for (UInt e = 0; e < nb_element; ++e) {
UInt mat = element_material(_bernoulli_beam_3, _not_ghost)(e);
Real E = materials[mat].E;
Real A = materials[mat].A;
Real Iz = materials[mat].Iz;
Real Iy = materials[mat].Iy;
Real GJ = materials[mat].GJ;
for (UInt q = 0; q < nb_quadrature_points; ++q, ++D_it) {
Matrix<Real> & D = *D_it;
D(0, 0) = E * A;
D(1, 1) = E * Iz;
D(2, 2) = E * Iy;
D(3, 3) = GJ;
}
}
}
} // namespace akantu
#endif /* AKANTU_STRUCTURAL_ELEMENT_BERNOULLI_BEAM_3_HH_ */
diff --git a/src/model/structural_mechanics/structural_elements/structural_element_kirchhoff_shell.hh b/src/model/structural_mechanics/structural_elements/structural_element_kirchhoff_shell.hh
index 240715e4b..a574fe15f 100644
--- a/src/model/structural_mechanics/structural_elements/structural_element_kirchhoff_shell.hh
+++ b/src/model/structural_mechanics/structural_elements/structural_element_kirchhoff_shell.hh
@@ -1,79 +1,72 @@
/**
* @file structural_element_kirchhoff_shell.hh
*
* @author Fabian Barras <fabian.barras@epfl.ch>
* @author Lucas Frerot <lucas.frerot@epfl.ch>
* @author Sébastien Hartmann <sebastien.hartmann@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
* @author Damien Spielmann <damien.spielmann@epfl.ch>
*
* @date creation: Wed Oct 11 2017
* @date last modification: Wed Feb 21 2018
*
* @brief Specific functions for bernoulli kirchhoff shell
*
*
* Copyright (©) 2016-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_STRUCTURAL_ELEMENT_BERNOULLI_KIRCHHOFF_SHELL_HH_
#define AKANTU_STRUCTURAL_ELEMENT_BERNOULLI_KIRCHHOFF_SHELL_HH_
#include "structural_mechanics_model.hh"
namespace akantu {
-/* -------------------------------------------------------------------------- */
-template <>
-inline void
-StructuralMechanicsModel::assembleMass<_discrete_kirchhoff_triangle_18>() {
- AKANTU_TO_IMPLEMENT();
-}
-
/* -------------------------------------------------------------------------- */
template <>
void StructuralMechanicsModel::computeTangentModuli<
_discrete_kirchhoff_triangle_18>(Array<Real> & tangent_moduli) {
auto tangent_size =
ElementClass<_discrete_kirchhoff_triangle_18>::getNbStressComponents();
auto nb_quad =
getFEEngine().getNbIntegrationPoints(_discrete_kirchhoff_triangle_18);
auto H_it = tangent_moduli.begin(tangent_size, tangent_size);
for (UInt mat :
element_material(_discrete_kirchhoff_triangle_18, _not_ghost)) {
auto & m = materials[mat];
for (UInt q = 0; q < nb_quad; ++q, ++H_it) {
auto & H = *H_it;
H.zero();
Matrix<Real> D = {{1, m.nu, 0}, {m.nu, 1, 0}, {0, 0, (1 - m.nu) / 2}};
D *= m.E * m.t / (1 - m.nu * m.nu);
H.block(D, 0, 0); // in plane membrane behavior
H.block(D * Math::pow<3>(m.t) / 12., 3, 3); // bending behavior
}
}
}
} // namespace akantu
#endif /* AKANTU_STRUCTURAL_ELEMENT_BERNOULLI_DISCRETE_KIRCHHOFF_TRIANGLE_18_HH_ \
*/
diff --git a/src/model/structural_mechanics/structural_mechanics_model.cc b/src/model/structural_mechanics/structural_mechanics_model.cc
index 40732fef3..4ffa28fcf 100644
--- a/src/model/structural_mechanics/structural_mechanics_model.cc
+++ b/src/model/structural_mechanics/structural_mechanics_model.cc
@@ -1,465 +1,620 @@
/**
* @file structural_mechanics_model.cc
*
* @author Fabian Barras <fabian.barras@epfl.ch>
* @author Lucas Frerot <lucas.frerot@epfl.ch>
* @author Sébastien Hartmann <sebastien.hartmann@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
* @author Damien Spielmann <damien.spielmann@epfl.ch>
*
* @date creation: Fri Jul 15 2011
* @date last modification: Wed Feb 21 2018
*
* @brief Model implementation for Structural Mechanics elements
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#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"
/* -------------------------------------------------------------------------- */
#ifdef AKANTU_USE_IOHELPER
#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"
#endif
/* -------------------------------------------------------------------------- */
-#include "structural_mechanics_model_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) {
+ UInt ndof = 0;
+#define GET_(type) ndof = ElementClass<type>::getNbDegreeOfFreedom()
+ AKANTU_BOOST_KIND_ELEMENT_SWITCH(GET_, _ek_structural);
+#undef GET_
+
+ return ndof;
+}
+
/* -------------------------------------------------------------------------- */
StructuralMechanicsModel::StructuralMechanicsModel(Mesh & mesh, UInt dim,
- const ID & id,
- const MemoryID & memory_id)
- : Model(mesh, ModelType::_structural_mechanics_model, dim, id,
- memory_id),
- time_step(NAN), f_m2a(1.0), stress("stress", id, memory_id),
- element_material("element_material", id, memory_id),
- set_ID("beam sets", id, memory_id),
- rotation_matrix("rotation_matices", id, memory_id) {
+ 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<MyFEEngineType>("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();
}
#ifdef AKANTU_USE_IOHELPER
this->mesh.registerDumper<DumperParaview>("structural_mechanics_model", id,
true);
#endif
this->mesh.addDumpMesh(mesh, spatial_dimension, _not_ghost, _ek_structural);
this->initDOFManager();
+ this->dumper_default_element_kind = _ek_structural;
+
+ mesh.getElementalData<Real>("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) {
- // <<<< This is the SolidMechanicsModel implementation for future ref >>>>
- // material_index.initialize(mesh, _element_kind = _ek_not_defined,
- // _default_value = UInt(-1), _with_nb_element =
- // true);
- // material_local_numbering.initialize(mesh, _element_kind = _ek_not_defined,
- // _with_nb_element = true);
-
- // Model::initFullImpl(options);
-
- // // initialize pbc
- // if (this->pbc_pair.size() != 0)
- // this->initPBC();
-
- // // initialize the materials
- // if (this->parser.getLastParsedFile() != "") {
- // this->instantiateMaterials();
- // }
-
- // this->initMaterials();
- // this->initBC(*this, *displacement, *displacement_increment,
- // *external_force);
-
- // <<<< END >>>>
-
Model::initFullImpl(options);
// Initializing stresses
ElementTypeMap<UInt> 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)) {
UInt nb_components = 0;
// Getting number of components for each element type
#define GET_(type) nb_components = ElementClass<type>::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) {
-// this->time_step = time_step;
-
-// #if defined(AKANTU_USE_IOHELPER)
-// this->mesh.getDumper().setTimeStep(time_step);
-// #endif
-// }
+void StructuralMechanicsModel::setTimeStep(Real time_step,
+ const ID & solver_id) {
+ Model::setTimeStep(time_step, solver_id);
+#if defined(AKANTU_USE_IOHELPER)
+ this->mesh.getDumper().setTimeStep(time_step);
+#endif
+}
/* -------------------------------------------------------------------------- */
/* 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();
- if (!dof_manager.hasDOFs("displacement")) {
+ 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, spatial_dimension, "velocity");
- this->allocNodalField(acceleration, spatial_dimension, "acceleration");
+ 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);
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void StructuralMechanicsModel::initModel() {
- for (auto && type : mesh.elementTypes(_element_kind = _ek_structural)) {
- // computeRotationMatrix(type);
- element_material.alloc(mesh.getNbElement(type), 1, type);
- }
+ 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();
- getDOFManager().getMatrix("K").zero();
+ 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 :
mesh.elementTypes(spatial_dimension, _not_ghost, _ek_structural)) {
#define ASSEMBLE_STIFFNESS_MATRIX(type) assembleStiffnessMatrix<type>();
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 :
mesh.elementTypes(spatial_dimension, _not_ghost, _ek_structural)) {
#define COMPUTE_STRESS_ON_QUAD(type) computeStressOnQuad<type>();
AKANTU_BOOST_STRUCTURAL_ELEMENT_SWITCH(COMPUTE_STRESS_ON_QUAD);
#undef COMPUTE_STRESS_ON_QUAD
}
AKANTU_DEBUG_OUT();
}
-/* -------------------------------------------------------------------------- */
-void StructuralMechanicsModel::computeRotationMatrix(ElementType type) {
- Mesh & mesh = getFEEngine().getMesh();
-
- UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
- UInt nb_element = mesh.getNbElement(type);
-
- if (!rotation_matrix.exists(type)) {
- rotation_matrix.alloc(nb_element,
- nb_degree_of_freedom * nb_nodes_per_element *
- nb_degree_of_freedom * nb_nodes_per_element,
- type);
- } else {
- rotation_matrix(type).resize(nb_element);
- }
- rotation_matrix(type).zero();
-
- Array<Real> rotations(nb_element,
- nb_degree_of_freedom * nb_degree_of_freedom);
- rotations.zero();
-
-#define COMPUTE_ROTATION_MATRIX(type) computeRotationMatrix<type>(rotations);
-
- AKANTU_BOOST_STRUCTURAL_ELEMENT_SWITCH(COMPUTE_ROTATION_MATRIX);
-#undef COMPUTE_ROTATION_MATRIX
-
- auto R_it = rotations.begin(nb_degree_of_freedom, nb_degree_of_freedom);
- auto T_it =
- rotation_matrix(type).begin(nb_degree_of_freedom * nb_nodes_per_element,
- nb_degree_of_freedom * nb_nodes_per_element);
-
- for (UInt el = 0; el < nb_element; ++el, ++R_it, ++T_it) {
- auto & T = *T_it;
- auto & R = *R_it;
- for (UInt k = 0; k < nb_nodes_per_element; ++k) {
- for (UInt i = 0; i < nb_degree_of_freedom; ++i) {
- for (UInt j = 0; j < nb_degree_of_freedom; ++j) {
- T(k * nb_degree_of_freedom + i, k * nb_degree_of_freedom + j) =
- R(i, j);
- }
- }
- }
- }
-}
-
/* -------------------------------------------------------------------------- */
std::shared_ptr<dumpers::Field> StructuralMechanicsModel::createNodalFieldBool(
const std::string & field_name, const std::string & group_name,
__attribute__((unused)) bool padding_flag) {
std::map<std::string, Array<bool> *> uint_nodal_fields;
- uint_nodal_fields["blocked_dofs"] = blocked_dofs;
+ uint_nodal_fields["blocked_dofs"] = blocked_dofs.get();
return mesh.createNodalField(uint_nodal_fields[field_name], group_name);
}
/* -------------------------------------------------------------------------- */
std::shared_ptr<dumpers::Field>
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, group_name, n, 0,
+ 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, group_name,
+ 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, group_name, n, 0,
+ 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, group_name, nb_degree_of_freedom - n, n, padding_size);
+ 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, group_name, n, 0,
+ return mesh.createStridedNodalField(internal_force.get(), group_name, n, 0,
padding_size);
}
if (field_name == "internal_momentum") {
- return mesh.createStridedNodalField(
- internal_force, group_name, nb_degree_of_freedom - n, n, padding_size);
+ return mesh.createStridedNodalField(internal_force.get(), group_name,
+ nb_degree_of_freedom - n, n,
+ padding_size);
}
return nullptr;
}
/* -------------------------------------------------------------------------- */
std::shared_ptr<dumpers::Field> StructuralMechanicsModel::createElementalField(
const std::string & field_name, const std::string & group_name,
bool /*unused*/, UInt spatial_dimension, ElementKind kind) {
std::shared_ptr<dumpers::Field> field;
if (field_name == "element_index_by_material") {
field = mesh.createElementalField<UInt, Vector, dumpers::ElementalField>(
field_name, group_name, spatial_dimension, kind);
}
+ if (field_name == "stress") {
+ ElementTypeMap<UInt> nb_data_per_elem = this->mesh.getNbDataPerElem(stress);
+
+ field = mesh.createElementalField<Real, dumpers::InternalMaterialField>(
+ 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*/) {
return _symmetric;
}
/// callback to assemble a Matrix
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*/) {}
/// callback to assemble the residual StructuralMechanicsModel::(rhs)
void StructuralMechanicsModel::assembleResidual() {
AKANTU_DEBUG_IN();
auto & dof_manager = getDOFManager();
- internal_force->zero();
- computeStresses();
assembleInternalForce();
- dof_manager.assembleToResidual("displacement", *internal_force, -1);
+
dof_manager.assembleToResidual("displacement", *external_force, 1);
+ dof_manager.assembleToResidual("displacement", *internal_force, 1);
+
+ AKANTU_DEBUG_OUT();
+}
+
+/* -------------------------------------------------------------------------- */
+void StructuralMechanicsModel::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->assembleInternalForce();
+ this->getDOFManager().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<ID, TimeStepSolverType>
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);
}
default:
return std::make_tuple("unknown", TimeStepSolverType::_not_defined);
}
}
/* ------------------------------------------------------------------------ */
ModelSolverOptions StructuralMechanicsModel::getDefaultSolverOptions(
const TimeStepSolverType & type) const {
ModelSolverOptions options;
switch (type) {
case TimeStepSolverType::_static: {
options.non_linear_solver_type = NonLinearSolverType::_linear;
options.integration_scheme_type["displacement"] =
IntegrationSchemeType::_pseudo_time;
options.solution_type["displacement"] = IntegrationScheme::_not_defined;
break;
}
+ case TimeStepSolverType::_dynamic: {
+ 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;
}
/* -------------------------------------------------------------------------- */
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 ndof = getNbDegreeOfFreedom(type);
auto nb_nodes = mesh.getNbNodesPerElement(type);
auto ndof_per_elem = ndof * nb_nodes;
Array<Real> BtSigma(fem.getNbIntegrationPoints(type) *
mesh.getNbElement(type),
ndof_per_elem, "BtSigma");
fem.computeBtD(sigma, BtSigma, type, gt);
Array<Real> intBtSigma(0, ndof_per_elem, "intBtSigma");
fem.integrate(BtSigma, intBtSigma, ndof_per_elem, type, gt);
- BtSigma.resize(0);
getDOFManager().assembleElementalArrayLocalArray(intBtSigma, *internal_force,
- type, gt, 1);
+ type, gt, -1.);
}
+
+/* -------------------------------------------------------------------------- */
+Real StructuralMechanicsModel::getKineticEnergy() {
+
+ if (not this->getDOFManager().hasMatrix("M")) {
+ return 0.;
+ }
+
+ Real ekin = 0.;
+ UInt nb_nodes = mesh.getNbNodes();
+
+ Array<Real> 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))) {
+ ekin += std::get<2>(data).dot(std::get<1>(data)) *
+ static_cast<Real>(mesh.isLocalOrMasterNode(std::get<0>(data)));
+ }
+
+ mesh.getCommunicator().allReduce(ekin, SynchronizerOperation::_sum);
+
+ return ekin / 2.;
+}
+
+/* -------------------------------------------------------------------------- */
+Real StructuralMechanicsModel::getPotentialEnergy() {
+ Real epot = 0.;
+ UInt nb_nodes = mesh.getNbNodes();
+
+ Array<Real> Ku(nb_nodes, nb_degree_of_freedom);
+ this->getDOFManager().assembleMatMulVectToArray(
+ "displacement", "K", *this->displacement_rotation, Ku);
+
+ 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<Real>(mesh.isLocalOrMasterNode(std::get<0>(data)));
+ }
+
+ mesh.getCommunicator().allReduce(epot, SynchronizerOperation::_sum);
+
+ return epot / 2.;
+}
+
+/* -------------------------------------------------------------------------- */
+Real StructuralMechanicsModel::getEnergy(const ID & energy) {
+ if (energy == "kinetic") {
+ return getKineticEnergy();
+ }
+
+ if (energy == "potential") {
+ return getPotentialEnergy();
+ }
+
+ return 0;
+}
+
/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+void StructuralMechanicsModel::computeForcesByLocalTractionArray(
+ const Array<Real> & 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<Real> Ntbs(nb_element * nb_quad,
+ nb_degree_of_freedom * nb_nodes_per_element);
+
+ 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<Real> 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<Real> & traction_global, ElementType type) {
+ AKANTU_DEBUG_IN();
+
+ UInt nb_element = mesh.getNbElement(type);
+ UInt nb_quad = getFEEngine().getNbIntegrationPoints(type);
+
+ Array<Real> traction_local(nb_element * nb_quad, nb_degree_of_freedom,
+ id + ":structuralmechanics:imposed_linear_load");
+
+ auto R_it = getFEEngineClass<MyFEEngineType>()
+ .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 (UInt e = 0; e < nb_element; ++e, ++R_it) {
+ for (UInt q = 0; q < nb_quad; ++q, ++Te_it, ++te_it) {
+ // turn the traction in the local referential
+ te_it->template mul<false>(*R_it, *Te_it);
+ }
+ }
+
+ computeForcesByLocalTractionArray(traction_local, type);
+
+ AKANTU_DEBUG_OUT();
+}
} // namespace akantu
diff --git a/src/model/structural_mechanics/structural_mechanics_model.hh b/src/model/structural_mechanics/structural_mechanics_model.hh
index b5e3b833b..b313d951c 100644
--- a/src/model/structural_mechanics/structural_mechanics_model.hh
+++ b/src/model/structural_mechanics/structural_mechanics_model.hh
@@ -1,309 +1,338 @@
/**
* @file structural_mechanics_model.hh
*
* @author Fabian Barras <fabian.barras@epfl.ch>
* @author Lucas Frerot <lucas.frerot@epfl.ch>
* @author Sébastien Hartmann <sebastien.hartmann@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
* @author Damien Spielmann <damien.spielmann@epfl.ch>
*
* @date creation: Fri Jul 15 2011
* @date last modification: Tue Feb 20 2018
*
* @brief Particular implementation of the structural elements in the
* StructuralMechanicsModel
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_named_argument.hh"
#include "boundary_condition.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 <ElementKind kind, class IntegrationOrderFunctor>
class IntegratorGauss;
template <ElementKind kind> 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<IntegratorGauss, ShapeStructural, _ek_structural>;
StructuralMechanicsModel(Mesh & mesh, UInt dim = _all_dimensions,
- const ID & id = "structural_mechanics_model",
- const MemoryID & memory_id = 0);
+ 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) 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() override { return false; }
+
+ /// compute kinetic energy
+ Real getKineticEnergy();
+
+ /// compute potential energy
+ Real getPotentialEnergy();
+
+ /// compute the specified energy
+ Real getEnergy(const ID & energy);
+
/* ------------------------------------------------------------------------ */
/* Virtual methods from Model */
/* ------------------------------------------------------------------------ */
protected:
/// get some default values for derived classes
std::tuple<ID, TimeStepSolverType>
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 assembleMass();
-
- /// TODO remove
- void computeRotationMatrix(ElementType type);
+ void assembleMassMatrix();
protected:
- /// compute Rotation Matrices
- template <const ElementType type>
- void computeRotationMatrix(__attribute__((unused)) Array<Real> & rotations) {}
-
- /* ------------------------------------------------------------------------ */
- /* Mass (structural_mechanics_model_mass.cc) */
- /* ------------------------------------------------------------------------ */
-
/// assemble the mass matrix for either _ghost or _not_ghost elements
- void assembleMass(GhostType ghost_type);
+ void assembleMassMatrix(GhostType ghost_type);
/// computes rho
void computeRho(Array<Real> & rho, ElementType type, GhostType ghost_type);
/// finish the computation of residual to solve in increment
void updateResidualInternal();
/* ------------------------------------------------------------------------ */
private:
template <ElementType type> void assembleStiffnessMatrix();
- template <ElementType type> void assembleMass();
template <ElementType type> void computeStressOnQuad();
template <ElementType type>
void computeTangentModuli(Array<Real> & tangent_moduli);
/* ------------------------------------------------------------------------ */
/* Dumpable interface */
/* ------------------------------------------------------------------------ */
public:
std::shared_ptr<dumpers::Field>
createNodalFieldReal(const std::string & field_name,
const std::string & group_name,
bool padding_flag) override;
std::shared_ptr<dumpers::Field>
createNodalFieldBool(const std::string & field_name,
const std::string & group_name,
bool padding_flag) override;
std::shared_ptr<dumpers::Field>
createElementalField(const std::string & field_name,
const std::string & group_name, bool padding_flag,
UInt spatial_dimension, ElementKind kind) override;
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
/// set the value of the time step
- // void setTimeStep(Real time_step, const ID & solver_id = "") override;
+ void setTimeStep(Real time_step, const ID & solver_id = "") override;
/// return the dimension of the system space
AKANTU_GET_MACRO(SpatialDimension, spatial_dimension, UInt);
/// get the StructuralMechanicsModel::displacement vector
AKANTU_GET_MACRO(Displacement, *displacement_rotation, Array<Real> &);
/// get the StructuralMechanicsModel::velocity vector
AKANTU_GET_MACRO(Velocity, *velocity, Array<Real> &);
/// get the StructuralMechanicsModel::acceleration vector, updated
/// by
/// StructuralMechanicsModel::updateAcceleration
AKANTU_GET_MACRO(Acceleration, *acceleration, Array<Real> &);
/// get the StructuralMechanicsModel::external_force vector
AKANTU_GET_MACRO(ExternalForce, *external_force, Array<Real> &);
/// get the StructuralMechanicsModel::internal_force vector (boundary forces)
AKANTU_GET_MACRO(InternalForce, *internal_force, Array<Real> &);
/// get the StructuralMechanicsModel::boundary vector
AKANTU_GET_MACRO(BlockedDOFs, *blocked_dofs, Array<bool> &);
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);
- void addMaterial(StructuralMaterial & material) {
- materials.push_back(material);
- }
-
- const StructuralMaterial & getMaterial(const Element & element) const {
- return materials[element_material(element)];
- }
+ /**
+ * \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
- template <ElementType type>
- void computeForcesByGlobalTractionArray(const Array<Real> & tractions);
+ void computeForcesByGlobalTractionArray(const Array<Real> & traction_global,
+ ElementType type);
/// Compute Linear load function set in local axis
- template <ElementType type>
- void computeForcesByLocalTractionArray(const Array<Real> & tractions);
+ void computeForcesByLocalTractionArray(const Array<Real> & tractions,
+ ElementType type);
/// compute force vector from a function(x,y,momentum) that describe stresses
// template <ElementType type>
// void computeForcesFromFunction(BoundaryFunction in_function,
// BoundaryFunctionType function_type);
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
private:
/// time step
Real time_step;
/// conversion coefficient form force/mass to acceleration
Real f_m2a;
/// displacements array
- Array<Real> * displacement_rotation{nullptr};
+ std::unique_ptr<Array<Real>> displacement_rotation;
/// velocities array
- Array<Real> * velocity{nullptr};
+ std::unique_ptr<Array<Real>> velocity;
/// accelerations array
- Array<Real> * acceleration{nullptr};
+ std::unique_ptr<Array<Real>> acceleration;
/// forces array
- Array<Real> * internal_force{nullptr};
+ std::unique_ptr<Array<Real>> internal_force;
/// forces array
- Array<Real> * external_force{nullptr};
+ std::unique_ptr<Array<Real>> external_force;
/// lumped mass array
- Array<Real> * mass{nullptr};
+ std::unique_ptr<Array<Real>> mass;
/// boundaries array
- Array<bool> * blocked_dofs{nullptr};
+ std::unique_ptr<Array<bool>> blocked_dofs;
/// stress array
ElementTypeMapArray<Real> stress;
ElementTypeMapArray<UInt> element_material;
// Define sets of beams
ElementTypeMapArray<UInt> set_ID;
/// number of degre of freedom
UInt nb_degree_of_freedom;
// Rotation matrix
ElementTypeMapArray<Real> 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};
+
/* ------------------------------------------------------------------------ */
std::vector<StructuralMaterial> materials;
+ std::map<std::string, UInt> 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_inline_impl.hh b/src/model/structural_mechanics/structural_mechanics_model_inline_impl.hh
index bbe416c12..3428080f6 100644
--- a/src/model/structural_mechanics/structural_mechanics_model_inline_impl.hh
+++ b/src/model/structural_mechanics/structural_mechanics_model_inline_impl.hh
@@ -1,370 +1,280 @@
/**
* @file structural_mechanics_model_inline_impl.hh
*
* @author Fabian Barras <fabian.barras@epfl.ch>
* @author Lucas Frerot <lucas.frerot@epfl.ch>
* @author Sébastien Hartmann <sebastien.hartmann@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
* @author Damien Spielmann <damien.spielmann@epfl.ch>
*
* @date creation: Fri Jul 15 2011
* @date last modification: Tue Feb 20 2018
*
* @brief Implementation of inline functions of StructuralMechanicsModel
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "structural_mechanics_model.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_STRUCTURAL_MECHANICS_MODEL_INLINE_IMPL_HH_
#define AKANTU_STRUCTURAL_MECHANICS_MODEL_INLINE_IMPL_HH_
namespace akantu {
+
+/* -------------------------------------------------------------------------- */
+inline UInt StructuralMechanicsModel::addMaterial(StructuralMaterial & material,
+ const ID & name) {
+
+ const auto material_index = materials.size();
+
+ auto material_name = name;
+ if (name.empty()) {
+ material_name = "material_" + std::to_string(material_index);
+ }
+
+ if (materials_names_to_id.find(material_name) !=
+ materials_names_to_id.end()) {
+ AKANTU_EXCEPTION("The material " << material_name
+ << " already exists in the model " << id);
+ }
+
+ AKANTU_DEBUG_ASSERT(material_index <=
+ (::std::size_t)::std::numeric_limits<UInt>::max(),
+ "Can not represent the material ID");
+
+ materials_names_to_id[material_name] = material_index;
+ materials.push_back(material); // add the material, might cause
+ // reallocation.
+
+ return UInt(material_index);
+}
+
+/* -------------------------------------------------------------------------- */
+inline const StructuralMaterial &
+StructuralMechanicsModel::getMaterialByElement(const Element & element) const {
+ return materials[element_material(element)];
+}
+
/* -------------------------------------------------------------------------- */
-inline UInt
-StructuralMechanicsModel::getNbDegreeOfFreedom(ElementType type) {
- UInt ndof = 0;
-#define GET_(type) ndof = ElementClass<type>::getNbDegreeOfFreedom()
- AKANTU_BOOST_KIND_ELEMENT_SWITCH(GET_, _ek_structural);
-#undef GET_
-
- return ndof;
+inline const StructuralMaterial &
+StructuralMechanicsModel::getMaterial(UInt material_index) const {
+ return materials.at(material_index);
+}
+
+/* -------------------------------------------------------------------------- */
+inline const StructuralMaterial &
+StructuralMechanicsModel::getMaterial(const ID & name) const {
+ auto it = materials_names_to_id.find(name);
+ if (it == materials_names_to_id.end()) {
+ AKANTU_EXCEPTION("The material " << name << " was not found in the model "
+ << id);
+ }
+
+ return materials.at(it->second);
}
/* -------------------------------------------------------------------------- */
template <ElementType type>
void StructuralMechanicsModel::computeTangentModuli(
Array<Real> & /*tangent_moduli*/) {
AKANTU_TO_IMPLEMENT();
}
-} // namespace akantu
-
-#include "structural_element_bernoulli_beam_2.hh"
-#include "structural_element_bernoulli_beam_3.hh"
-#include "structural_element_kirchhoff_shell.hh"
-
-namespace akantu {
/* -------------------------------------------------------------------------- */
template <ElementType type>
void StructuralMechanicsModel::assembleStiffnessMatrix() {
AKANTU_DEBUG_IN();
auto nb_element = getFEEngine().getMesh().getNbElement(type);
auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
auto nb_quadrature_points = getFEEngine().getNbIntegrationPoints(type);
auto tangent_size = ElementClass<type>::getNbStressComponents();
auto tangent_moduli = std::make_unique<Array<Real>>(
nb_element * nb_quadrature_points, tangent_size * tangent_size,
"tangent_stiffness_matrix");
computeTangentModuli<type>(*tangent_moduli);
/// compute @f$\mathbf{B}^t * \mathbf{D} * \mathbf{B}@f$
UInt bt_d_b_size = nb_degree_of_freedom * nb_nodes_per_element;
auto bt_d_b = std::make_unique<Array<Real>>(
nb_element * nb_quadrature_points, bt_d_b_size * bt_d_b_size, "B^t*D*B");
const auto & b = getFEEngine().getShapesDerivatives(type);
Matrix<Real> BtD(bt_d_b_size, tangent_size);
for (auto && tuple :
zip(make_view(b, tangent_size, bt_d_b_size),
make_view(*tangent_moduli, tangent_size, tangent_size),
make_view(*bt_d_b, bt_d_b_size, bt_d_b_size))) {
auto & B = std::get<0>(tuple);
auto & D = std::get<1>(tuple);
auto & BtDB = std::get<2>(tuple);
BtD.mul<true, false>(B, D);
BtDB.template mul<false, false>(BtD, B);
}
/// compute @f$ k_e = \int_e \mathbf{B}^t * \mathbf{D} * \mathbf{B}@f$
auto int_bt_d_b = std::make_unique<Array<Real>>(
nb_element, bt_d_b_size * bt_d_b_size, "int_B^t*D*B");
getFEEngine().integrate(*bt_d_b, *int_bt_d_b, bt_d_b_size * bt_d_b_size,
type);
getDOFManager().assembleElementalMatricesToMatrix(
"K", "displacement", *int_bt_d_b, type, _not_ghost, _symmetric);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <ElementType type>
void StructuralMechanicsModel::computeStressOnQuad() {
AKANTU_DEBUG_IN();
- Array<Real> & sigma = stress(type, _not_ghost);
+ auto & sigma = stress(type, _not_ghost);
auto nb_element = mesh.getNbElement(type);
auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type);
auto nb_quadrature_points = getFEEngine().getNbIntegrationPoints(type);
auto tangent_size = ElementClass<type>::getNbStressComponents();
auto tangent_moduli = std::make_unique<Array<Real>>(
nb_element * nb_quadrature_points, tangent_size * tangent_size,
"tangent_stiffness_matrix");
computeTangentModuli<type>(*tangent_moduli);
/// compute DB
auto d_b_size = nb_degree_of_freedom * nb_nodes_per_element;
auto d_b = std::make_unique<Array<Real>>(nb_element * nb_quadrature_points,
d_b_size * tangent_size, "D*B");
const auto & b = getFEEngine().getShapesDerivatives(type);
auto B = b.begin(tangent_size, d_b_size);
auto D = tangent_moduli->begin(tangent_size, tangent_size);
auto D_B = d_b->begin(tangent_size, d_b_size);
for (UInt e = 0; e < nb_element; ++e) {
for (UInt q = 0; q < nb_quadrature_points; ++q, ++B, ++D, ++D_B) {
D_B->template mul<false, false>(*D, *B);
}
}
/// compute DBu
D_B = d_b->begin(tangent_size, d_b_size);
auto DBu = sigma.begin(tangent_size);
Array<Real> u_el(0, d_b_size);
FEEngine::extractNodalToElementField(mesh, *displacement_rotation, u_el,
type);
auto ug = u_el.begin(d_b_size);
// No need to rotate because B is post-multiplied
for (UInt e = 0; e < nb_element; ++e, ++ug) {
for (UInt q = 0; q < nb_quadrature_points; ++q, ++D_B, ++DBu) {
DBu->template mul<false>(*D_B, *ug);
}
}
AKANTU_DEBUG_OUT();
}
-/* -------------------------------------------------------------------------- */
-template <ElementType type>
-void StructuralMechanicsModel::computeForcesByLocalTractionArray(
- const Array<Real> & /*tractions*/) {
- AKANTU_DEBUG_IN();
-
-#if 0
- UInt nb_element = getFEEngine().getMesh().getNbElement(type);
- UInt nb_nodes_per_element =
- getFEEngine().getMesh().getNbNodesPerElement(type);
- UInt 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<Real> Nvoigt(nb_element * nb_quad, nb_degree_of_freedom *
- nb_degree_of_freedom *
- nb_nodes_per_element);
- transferNMatrixToSymVoigtNMatrix<type>(Nvoigt);
-
- Array<Real>::const_matrix_iterator N_it = Nvoigt.begin(
- nb_degree_of_freedom, nb_degree_of_freedom * nb_nodes_per_element);
- Array<Real>::const_matrix_iterator T_it =
- rotation_matrix(type).begin(nb_degree_of_freedom * nb_nodes_per_element,
- nb_degree_of_freedom * nb_nodes_per_element);
- auto te_it =
- tractions.begin(nb_degree_of_freedom);
-
- Array<Real> funct(nb_element * nb_quad,
- nb_degree_of_freedom * nb_nodes_per_element, 0.);
- Array<Real>::iterator<Vector<Real>> Fe_it =
- funct.begin(nb_degree_of_freedom * nb_nodes_per_element);
-
- Vector<Real> fe(nb_degree_of_freedom * nb_nodes_per_element);
- for (UInt e = 0; e < nb_element; ++e, ++T_it) {
- const Matrix<Real> & T = *T_it;
- for (UInt q = 0; q < nb_quad; ++q, ++N_it, ++te_it, ++Fe_it) {
- const Matrix<Real> & N = *N_it;
- const Vector<Real> & te = *te_it;
- Vector<Real> & Fe = *Fe_it;
-
- // compute N^t tl
- fe.mul<true>(N, te);
- // turn N^t tl back in the global referential
- Fe.mul<true>(T, fe);
- }
- }
-
- // allocate the vector that will contain the integrated values
- std::stringstream name;
- name << id << type << ":integral_boundary";
- Array<Real> int_funct(nb_element, nb_degree_of_freedom * nb_nodes_per_element,
- name.str());
-
- // do the integration
- getFEEngine().integrate(funct, int_funct,
- nb_degree_of_freedom * nb_nodes_per_element, type);
-
- // assemble the result into force vector
- getFEEngine().assembleArray(int_funct, *force_momentum,
- dof_synchronizer->getLocalDOFEquationNumbers(),
- nb_degree_of_freedom, type);
-#endif
- AKANTU_DEBUG_OUT();
-}
-
-/* -------------------------------------------------------------------------- */
-template <ElementType type>
-void StructuralMechanicsModel::computeForcesByGlobalTractionArray(
- const Array<Real> & /*traction_global*/) {
- AKANTU_DEBUG_IN();
-#if 0
- UInt nb_element = mesh.getNbElement(type);
- UInt nb_quad = getFEEngine().getNbIntegrationPoints(type);
- UInt nb_nodes_per_element =
- getFEEngine().getMesh().getNbNodesPerElement(type);
-
- std::stringstream name;
- name << id << ":structuralmechanics:imposed_linear_load";
- Array<Real> traction_local(nb_element * nb_quad, nb_degree_of_freedom,
- name.str());
-
- Array<Real>::const_matrix_iterator T_it =
- rotation_matrix(type).begin(nb_degree_of_freedom * nb_nodes_per_element,
- nb_degree_of_freedom * nb_nodes_per_element);
-
- Array<Real>::const_iterator<Vector<Real>> Te_it =
- traction_global.begin(nb_degree_of_freedom);
- Array<Real>::iterator<Vector<Real>> te_it =
- traction_local.begin(nb_degree_of_freedom);
-
- Matrix<Real> R(nb_degree_of_freedom, nb_degree_of_freedom);
- for (UInt e = 0; e < nb_element; ++e, ++T_it) {
- const Matrix<Real> & T = *T_it;
- for (UInt i = 0; i < nb_degree_of_freedom; ++i)
- for (UInt j = 0; j < nb_degree_of_freedom; ++j)
- R(i, j) = T(i, j);
-
- for (UInt q = 0; q < nb_quad; ++q, ++Te_it, ++te_it) {
- const Vector<Real> & Te = *Te_it;
- Vector<Real> & te = *te_it;
- // turn the traction in the local referential
- te.mul<false>(R, Te);
- }
- }
-
- computeForcesByLocalTractionArray<type>(traction_local);
-#endif
- AKANTU_DEBUG_OUT();
-}
-
/* -------------------------------------------------------------------------- */
/**
* @param myf pointer to a function that fills a vector/tensor with respect to
* passed coordinates
*/
#if 0
template <ElementType type>
inline void StructuralMechanicsModel::computeForcesFromFunction(
BoundaryFunction myf, BoundaryFunctionType function_type) {
/** function type is
** _bft_forces : linear load is given
** _bft_stress : stress function is given -> Not already done for this kind
*of model
*/
std::stringstream name;
name << id << ":structuralmechanics:imposed_linear_load";
Array<Real> lin_load(0, nb_degree_of_freedom, name.str());
name.zero();
UInt offset = nb_degree_of_freedom;
// prepare the loop over element types
UInt nb_quad = getFEEngine().getNbIntegrationPoints(type);
UInt nb_element = getFEEngine().getMesh().getNbElement(type);
name.zero();
name << id << ":structuralmechanics:quad_coords";
Array<Real> quad_coords(nb_element * nb_quad, spatial_dimension,
"quad_coords");
getFEEngineClass<MyFEEngineType>()
.getShapeFunctions()
.interpolateOnIntegrationPoints<type>(getFEEngine().getMesh().getNodes(),
quad_coords, spatial_dimension);
getFEEngineClass<MyFEEngineType>()
.getShapeFunctions()
.interpolateOnIntegrationPoints<type>(
getFEEngine().getMesh().getNodes(), quad_coords, spatial_dimension,
_not_ghost, empty_filter, true, 0, 1, 1);
if (spatial_dimension == 3)
getFEEngineClass<MyFEEngineType>()
.getShapeFunctions()
.interpolateOnIntegrationPoints<type>(
getFEEngine().getMesh().getNodes(), quad_coords, spatial_dimension,
_not_ghost, empty_filter, true, 0, 2, 2);
lin_load.resize(nb_element * nb_quad);
Real * imposed_val = lin_load.storage();
/// sigma/load on each quadrature points
Real * qcoord = quad_coords.storage();
for (UInt el = 0; el < nb_element; ++el) {
for (UInt q = 0; q < nb_quad; ++q) {
myf(qcoord, imposed_val, NULL, 0);
imposed_val += offset;
qcoord += spatial_dimension;
}
}
switch (function_type) {
case _bft_traction_local:
computeForcesByLocalTractionArray<type>(lin_load);
break;
case _bft_traction:
computeForcesByGlobalTractionArray<type>(lin_load);
break;
default:
break;
}
}
#endif
} // namespace akantu
#endif /* AKANTU_STRUCTURAL_MECHANICS_MODEL_INLINE_IMPL_HH_ */
diff --git a/src/model/structural_mechanics/structural_mechanics_model_mass.cc b/src/model/structural_mechanics/structural_mechanics_model_mass.cc
index 52bea77c2..c168e2877 100644
--- a/src/model/structural_mechanics/structural_mechanics_model_mass.cc
+++ b/src/model/structural_mechanics/structural_mechanics_model_mass.cc
@@ -1,78 +1,89 @@
/**
* @file structural_mechanics_model_mass.cc
*
* @author Lucas Frerot <lucas.frerot@epfl.ch>
* @author Sébastien Hartmann <sebastien.hartmann@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Mon Jul 07 2014
* @date last modification: Fri Dec 15 2017
*
* @brief function handling mass computation
*
*
* Copyright (©) 2014-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#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<Real> & rho, const Element & element) const {
- Real mat_rho = model.getMaterial(element).rho;
+ Real mat_rho = model.getMaterialByElement(element).rho;
rho.set(mat_rho);
}
private:
const StructuralMechanicsModel & model;
};
/* -------------------------------------------------------------------------- */
-void StructuralMechanicsModel::assembleMass() {
+void StructuralMechanicsModel::assembleMassMatrix() {
AKANTU_DEBUG_IN();
- assembleMass(_not_ghost);
+ 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::assembleMass(GhostType ghost_type) {
+void StructuralMechanicsModel::assembleMassMatrix(GhostType ghost_type) {
AKANTU_DEBUG_IN();
auto & fem = getFEEngineClass<MyFEEngineType>();
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();
}
} // namespace akantu
diff --git a/src/solver/solver_petsc.cc b/src/solver/solver_petsc.cc
index 3ab4cef60..28629e923 100644
--- a/src/solver/solver_petsc.cc
+++ b/src/solver/solver_petsc.cc
@@ -1,91 +1,91 @@
/**
* @file solver_petsc.cc
*
* @author Alejandro M. Aragón <alejandro.aragon@epfl.ch>
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Tue May 13 2014
* @date last modification: Sun Aug 13 2017
*
* @brief Solver class implementation for the petsc solver
*
*
* Copyright (©) 2014-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#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 <petscksp.h>
//#include <petscsys.h>
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
SolverPETSc::SolverPETSc(DOFManagerPETSc & dof_manager, const ID & matrix_id,
- const ID & id, const MemoryID & memory_id)
- : SparseSolver(dof_manager, matrix_id, id, memory_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);
}
} // namespace akantu
diff --git a/src/solver/solver_petsc.hh b/src/solver/solver_petsc.hh
index 046966621..d61abe941 100644
--- a/src/solver/solver_petsc.hh
+++ b/src/solver/solver_petsc.hh
@@ -1,81 +1,81 @@
/**
* @file solver_petsc.hh
*
* @author Alejandro M. Aragón <alejandro.aragon@epfl.ch>
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Tue May 13 2014
* @date last modification: Mon Jun 19 2017
*
* @brief Solver class interface for the petsc solver
*
*
* Copyright (©) 2014-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "sparse_solver.hh"
/* -------------------------------------------------------------------------- */
#include <petscksp.h>
/* -------------------------------------------------------------------------- */
#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", const MemoryID & memory_id = 0);
+ const ID & id = "solver_petsc");
~SolverPETSc() override;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// create the solver context and set the matrices
virtual void setOperators();
void solve() 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/sparse_solver.cc b/src/solver/sparse_solver.cc
index 0a6020bf3..930f0384d 100644
--- a/src/solver/sparse_solver.cc
+++ b/src/solver/sparse_solver.cc
@@ -1,83 +1,83 @@
/**
* @file sparse_solver.cc
*
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Mon Dec 13 2010
* @date last modification: Fri Dec 08 2017
*
* @brief Solver interface class
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "sparse_solver.hh"
#include "communicator.hh"
#include "dof_manager.hh"
#include "mesh.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
SparseSolver::SparseSolver(DOFManager & dof_manager, const ID & matrix_id,
- const ID & id, const MemoryID & memory_id)
- : Memory(id, memory_id), Parsable(ParserType::_solver, id),
+ const ID & id)
+ : Parsable(ParserType::_solver, id),
_dof_manager(dof_manager), matrix_id(matrix_id),
communicator(dof_manager.getCommunicator()) {
AKANTU_DEBUG_IN();
// OK this is fishy...
this->communicator.registerEventHandler(*this);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
SparseSolver::~SparseSolver() {
AKANTU_DEBUG_IN();
// this->destroyInternalData();
this->communicator.unregisterEventHandler(*this);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SparseSolver::beforeStaticSolverDestroy() {
AKANTU_DEBUG_IN();
try {
this->destroyInternalData();
} catch (...) {
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SparseSolver::createSynchronizerRegistry() {
// this->synch_registry = new SynchronizerRegistry(this);
}
void SparseSolver::onCommunicatorFinalize() { this->destroyInternalData(); }
} // namespace akantu
diff --git a/src/solver/sparse_solver.hh b/src/solver/sparse_solver.hh
index 99f99006e..dd8936d53 100644
--- a/src/solver/sparse_solver.hh
+++ b/src/solver/sparse_solver.hh
@@ -1,129 +1,126 @@
/**
* @file sparse_solver.hh
*
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
* @author Lucas Frerot <lucas.frerot@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Wed Jan 24 2018
*
* @brief interface for solvers
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
-#include "aka_memory.hh"
#include "communicator_event_handler.hh"
#include "parsable.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_SOLVER_HH_
#define AKANTU_SOLVER_HH_
namespace akantu {
enum SolverParallelMethod {
_not_parallel,
_fully_distributed,
_master_slave_distributed
};
class DOFManager;
} // namespace akantu
namespace akantu {
-class SparseSolver : protected Memory,
- public Parsable,
- public CommunicatorEventHandler {
+class SparseSolver : public Parsable, public CommunicatorEventHandler {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
SparseSolver(DOFManager & dof_manager, const ID & matrix_id,
- const ID & id = "solver", const MemoryID & memory_id = 0);
+ const ID & id = "solver");
~SparseSolver() override;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// initialize the solver
virtual void initialize() = 0;
virtual void analysis(){};
virtual void factorize(){};
virtual void solve(){};
protected:
virtual void destroyInternalData(){};
public:
virtual void beforeStaticSolverDestroy();
void createSynchronizerRegistry();
/* ------------------------------------------------------------------------ */
/* Data Accessor inherited members */
/* ------------------------------------------------------------------------ */
public:
void onCommunicatorFinalize() override;
// inline virtual UInt getNbDataForDOFs(const Array<UInt> & dofs,
// SynchronizationTag tag) const;
// inline virtual void packDOFData(CommunicationBuffer & buffer,
// const Array<UInt> & dofs,
// SynchronizationTag tag) const;
// inline virtual void unpackDOFData(CommunicationBuffer & buffer,
// const Array<UInt> & dofs,
// SynchronizationTag tag);
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
/// manager handling the dofs for this SparseMatrix solver
DOFManager & _dof_manager;
/// The id of the associated matrix
ID matrix_id;
/// How to parallelize the solve
SolverParallelMethod parallel_method;
/// Communicator used by the solver
Communicator & communicator;
};
namespace debug {
class SingularMatrixException : public Exception {
public:
SingularMatrixException(const SparseMatrix & matrix)
: Exception("Solver encountered singular matrix"), matrix(matrix) {}
const SparseMatrix & matrix;
};
} // namespace debug
} // namespace akantu
#endif /* AKANTU_SOLVER_HH_ */
diff --git a/src/solver/sparse_solver_mumps.cc b/src/solver/sparse_solver_mumps.cc
index 7a5d5a168..b1500f1fd 100644
--- a/src/solver/sparse_solver_mumps.cc
+++ b/src/solver/sparse_solver_mumps.cc
@@ -1,455 +1,454 @@
/**
* @file sparse_solver_mumps.cc
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Mon Dec 13 2010
* @date last modification: Tue Feb 20 2018
*
* @brief implem of SparseSolverMumps class
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*
* @subsection Ctrl_param Control parameters
*
* ICNTL(1),
* ICNTL(2),
* ICNTL(3) : output streams for error, diagnostics, and global messages
*
* ICNTL(4) : verbose level : 0 no message - 4 all messages
*
* ICNTL(5) : type of matrix, 0 assembled, 1 elementary
*
* ICNTL(6) : control the permutation and scaling(default 7) see mumps doc for
* more information
*
* ICNTL(7) : determine the pivot order (default 7) see mumps doc for more
* information
*
* ICNTL(8) : describe the scaling method used
*
* ICNTL(9) : 1 solve A x = b, 0 solve At x = b
*
* ICNTL(10) : number of iterative refinement when NRHS = 1
*
* ICNTL(11) : > 0 return statistics
*
* ICNTL(12) : only used for SYM = 2, ordering strategy
*
* ICNTL(13) :
*
* ICNTL(14) : percentage of increase of the estimated working space
*
* ICNTL(15-17) : not used
*
* ICNTL(18) : only used if ICNTL(5) = 0, 0 matrix centralized, 1 structure on
* host and mumps give the mapping, 2 structure on host and distributed matrix
* for facto, 3 distributed matrix
*
* ICNTL(19) : > 0, Shur complement returned
*
* ICNTL(20) : 0 rhs dense, 1 rhs sparse
*
* ICNTL(21) : 0 solution in rhs, 1 solution distributed in ISOL_loc and SOL_loc
* allocated by user
*
* ICNTL(22) : 0 in-core, 1 out-of-core
*
* ICNTL(23) : maximum memory allocatable by mumps pre proc
*
* ICNTL(24) : controls the detection of "null pivot rows"
*
* ICNTL(25) :
*
* ICNTL(26) :
*
* ICNTL(27) :
*
* ICNTL(28) : 0 automatic choice, 1 sequential analysis, 2 parallel analysis
*
* ICNTL(29) : 0 automatic choice, 1 PT-Scotch, 2 ParMetis
*/
/* -------------------------------------------------------------------------- */
#include "aka_common.hh"
#include "dof_manager_default.hh"
#include "dof_synchronizer.hh"
#include "solver_vector_default.hh"
#include "sparse_matrix_aij.hh"
#if defined(AKANTU_USE_MPI)
#include "mpi_communicator_data.hh"
#endif
#include "sparse_solver_mumps.hh"
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
// static std::ostream & operator <<(std::ostream & stream, const DMUMPS_STRUC_C
// & _this) {
// stream << "DMUMPS Data [" << std::endl;
// stream << " + job : " << _this.job << std::endl;
// stream << " + par : " << _this.par << std::endl;
// stream << " + sym : " << _this.sym << std::endl;
// stream << " + comm_fortran : " << _this.comm_fortran << std::endl;
// stream << " + nz : " << _this.nz << std::endl;
// stream << " + irn : " << _this.irn << std::endl;
// stream << " + jcn : " << _this.jcn << std::endl;
// stream << " + nz_loc : " << _this.nz_loc << std::endl;
// stream << " + irn_loc : " << _this.irn_loc << std::endl;
// stream << " + jcn_loc : " << _this.jcn_loc << std::endl;
// stream << "]";
// return stream;
// }
namespace akantu {
/* -------------------------------------------------------------------------- */
SparseSolverMumps::SparseSolverMumps(DOFManagerDefault & dof_manager,
- const ID & matrix_id, const ID & id,
- const MemoryID & memory_id)
- : SparseSolver(dof_manager, matrix_id, id, memory_id),
+ const ID & matrix_id, const ID & id)
+ : SparseSolver(dof_manager, matrix_id, id),
dof_manager(dof_manager), master_rhs_solution(0, 1) {
AKANTU_DEBUG_IN();
this->prank = communicator.whoAmI();
#ifdef AKANTU_USE_MPI
this->parallel_method = _fully_distributed;
#else // AKANTU_USE_MPI
this->parallel_method = _not_parallel;
#endif // AKANTU_USE_MPI
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
SparseSolverMumps::~SparseSolverMumps() {
AKANTU_DEBUG_IN();
mumpsDataDestroy();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SparseSolverMumps::mumpsDataDestroy() {
#ifdef AKANTU_USE_MPI
int finalized = 0;
MPI_Finalized(&finalized);
if (finalized != 0) { // Da fuck !?
return;
}
#endif
if (this->is_initialized) {
this->mumps_data.job = _smj_destroy; // destroy
dmumps_c(&this->mumps_data);
this->is_initialized = false;
}
}
/* -------------------------------------------------------------------------- */
void SparseSolverMumps::destroyInternalData() { mumpsDataDestroy(); }
/* -------------------------------------------------------------------------- */
void SparseSolverMumps::checkInitialized() {
if (this->is_initialized) {
return;
}
this->initialize();
}
/* -------------------------------------------------------------------------- */
void SparseSolverMumps::setOutputLevel() {
// Output setup
icntl(1) = 0; // error output
icntl(2) = 0; // diagnostics output
icntl(3) = 0; // information
icntl(4) = 0;
#if !defined(AKANTU_NDEBUG)
DebugLevel dbg_lvl = debug::debugger.getDebugLevel();
if (AKANTU_DEBUG_TEST(dblDump)) {
strcpy(this->mumps_data.write_problem, "mumps_matrix.mtx");
}
// clang-format off
icntl(1) = (dbg_lvl >= dblWarning) ? 6 : 0;
icntl(3) = (dbg_lvl >= dblInfo) ? 6 : 0;
icntl(2) = (dbg_lvl >= dblTrace) ? 6 : 0;
icntl(4) =
dbg_lvl >= dblDump ? 4 :
dbg_lvl >= dblTrace ? 3 :
dbg_lvl >= dblInfo ? 2 :
dbg_lvl >= dblWarning ? 1 :
0;
// clang-format on
#endif
}
/* -------------------------------------------------------------------------- */
void SparseSolverMumps::initMumpsData() {
auto & A = dof_manager.getMatrix(matrix_id);
// Default Scaling
icntl(8) = 77;
// Assembled matrix
icntl(5) = 0;
/// Default centralized dense second member
icntl(20) = 0;
icntl(21) = 0;
// automatic choice for analysis
icntl(28) = 0;
UInt size = A.size();
if (prank == 0) {
this->master_rhs_solution.resize(size);
}
this->mumps_data.nz_alloc = 0;
this->mumps_data.n = size;
switch (this->parallel_method) {
case _fully_distributed:
icntl(18) = 3; // fully distributed
this->mumps_data.nz_loc = A.getNbNonZero();
this->mumps_data.irn_loc = A.getIRN().storage();
this->mumps_data.jcn_loc = A.getJCN().storage();
break;
case _not_parallel:
case _master_slave_distributed:
icntl(18) = 0; // centralized
if (prank == 0) {
this->mumps_data.nz = A.getNbNonZero();
this->mumps_data.irn = A.getIRN().storage();
this->mumps_data.jcn = A.getJCN().storage();
} else {
this->mumps_data.nz = 0;
this->mumps_data.irn = nullptr;
this->mumps_data.jcn = nullptr;
}
break;
default:
AKANTU_ERROR("This case should not happen!!");
}
}
/* -------------------------------------------------------------------------- */
void SparseSolverMumps::initialize() {
AKANTU_DEBUG_IN();
this->mumps_data.par = 1; // The host is part of computations
switch (this->parallel_method) {
case _not_parallel:
break;
case _master_slave_distributed:
this->mumps_data.par = 0; // The host is not part of the computations
/* FALLTHRU */
/* [[fallthrough]]; un-comment when compiler will get it */
case _fully_distributed:
#ifdef AKANTU_USE_MPI
const auto & mpi_data =
aka::as_type<MPICommunicatorData>(communicator.getCommunicatorData());
MPI_Comm mpi_comm = mpi_data.getMPICommunicator();
this->mumps_data.comm_fortran = MPI_Comm_c2f(mpi_comm);
#else
AKANTU_ERROR(
"You cannot use parallel method to solve without activating MPI");
#endif
break;
}
const auto & A = dof_manager.getMatrix(matrix_id);
this->mumps_data.sym = 2 * static_cast<int>(A.getMatrixType() == _symmetric);
this->prank = communicator.whoAmI();
this->setOutputLevel();
this->mumps_data.job = _smj_initialize; // initialize
dmumps_c(&this->mumps_data);
this->setOutputLevel();
this->is_initialized = true;
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SparseSolverMumps::analysis() {
AKANTU_DEBUG_IN();
initMumpsData();
this->mumps_data.job = _smj_analyze; // analyze
dmumps_c(&this->mumps_data);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SparseSolverMumps::factorize() {
AKANTU_DEBUG_IN();
auto & A = dof_manager.getMatrix(matrix_id);
if (parallel_method == _fully_distributed) {
this->mumps_data.a_loc = A.getA().storage();
} else {
if (prank == 0) {
this->mumps_data.a = A.getA().storage();
}
}
this->mumps_data.job = _smj_factorize; // factorize
dmumps_c(&this->mumps_data);
this->printError();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SparseSolverMumps::solve(Array<Real> & x, const Array<Real> & b) {
auto & synch = this->dof_manager.getSynchronizer();
if (this->prank == 0) {
this->master_rhs_solution.resize(this->dof_manager.getSystemSize());
synch.gather(b, this->master_rhs_solution);
} else {
synch.gather(b);
}
this->solveInternal();
if (this->prank == 0) {
synch.scatter(x, this->master_rhs_solution);
} else {
synch.scatter(x);
}
}
/* -------------------------------------------------------------------------- */
void SparseSolverMumps::solve() {
this->master_rhs_solution.copy(
aka::as_type<SolverVectorDefault>(this->dof_manager.getResidual())
.getGlobalVector());
this->solveInternal();
aka::as_type<SolverVectorDefault>(this->dof_manager.getSolution())
.setGlobalVector(this->master_rhs_solution);
this->dof_manager.splitSolutionPerDOFs();
}
/* -------------------------------------------------------------------------- */
void SparseSolverMumps::solveInternal() {
AKANTU_DEBUG_IN();
this->checkInitialized();
const auto & A = dof_manager.getMatrix(matrix_id);
this->setOutputLevel();
if (this->last_profile_release != A.getProfileRelease()) {
this->analysis();
this->last_profile_release = A.getProfileRelease();
}
if (AKANTU_DEBUG_TEST(dblDump)) {
A.saveMatrix("solver_mumps" + std::to_string(prank) + ".mtx");
}
if (this->last_value_release != A.getValueRelease()) {
this->factorize();
this->last_value_release = A.getValueRelease();
}
if (prank == 0) {
this->mumps_data.rhs = this->master_rhs_solution.storage();
}
this->mumps_data.job = _smj_solve; // solve
dmumps_c(&this->mumps_data);
this->printError();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void SparseSolverMumps::printError() {
Vector<Int> _info_v(2);
_info_v[0] = info(1); // to get errors
_info_v[1] = -info(1); // to get warnings
dof_manager.getCommunicator().allReduce(_info_v, SynchronizerOperation::_min);
_info_v[1] = -_info_v[1];
if (_info_v[0] < 0) { // < 0 is an error
switch (_info_v[0]) {
case -10: {
AKANTU_CUSTOM_EXCEPTION(
debug::SingularMatrixException(dof_manager.getMatrix(matrix_id)));
break;
}
case -9: {
icntl(14) += 10;
if (icntl(14) != 90) {
// std::cout << "Dynamic memory increase of 10%" << std::endl;
AKANTU_DEBUG_WARNING("MUMPS dynamic memory is insufficient it will be "
"increased allowed to use 10% more");
// change releases to force a recompute
this->last_value_release--;
this->last_profile_release--;
this->solve();
} else {
AKANTU_ERROR("The MUMPS workarray is too small INFO(2)="
<< info(2) << "No further increase possible");
}
break;
}
default:
AKANTU_ERROR("Error in mumps during solve process, check mumps "
"user guide INFO(1) = "
<< _info_v[1]);
}
} else if (_info_v[1] > 0) {
AKANTU_DEBUG_WARNING("Warning in mumps during solve process, check mumps "
"user guide INFO(1) = "
<< _info_v[1]);
}
}
} // namespace akantu
diff --git a/src/solver/sparse_solver_mumps.hh b/src/solver/sparse_solver_mumps.hh
index 8adfe8845..430ac3eed 100644
--- a/src/solver/sparse_solver_mumps.hh
+++ b/src/solver/sparse_solver_mumps.hh
@@ -1,156 +1,155 @@
/**
* @file sparse_solver_mumps.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Sun Dec 03 2017
*
* @brief Solver class implementation for the mumps solver
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "sparse_solver.hh"
/* -------------------------------------------------------------------------- */
#include <dmumps_c.h>
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_SOLVER_MUMPS_HH_
#define AKANTU_SOLVER_MUMPS_HH_
namespace akantu {
class DOFManagerDefault;
class SparseMatrixAIJ;
} // namespace akantu
namespace akantu {
class SparseSolverMumps : public SparseSolver {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
SparseSolverMumps(DOFManagerDefault & dof_manager, const ID & matrix_id,
- const ID & id = "sparse_solver_mumps",
- const MemoryID & memory_id = 0);
+ const ID & id = "sparse_solver_mumps");
~SparseSolverMumps() override;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// build the profile and do the analysis part
void initialize() override;
/// analysis (symbolic facto + permutations)
void analysis() override;
/// factorize the matrix
void factorize() override;
/// solve the system
virtual void solve(Array<Real> & x, const Array<Real> & b);
/// solve using residual and solution from the dof_manager
void solve() override;
private:
/// print the error if any happened in mumps
void printError();
/// solve the system with master_rhs_solution as b and x
void solveInternal();
/// set internal values;
void initMumpsData();
/// set the level of verbosity of mumps based on the debug level of akantu
void setOutputLevel();
protected:
/// de-initialize the internal data
void destroyInternalData() override;
/// check if initialized and except if it is not the case
void checkInitialized();
private:
void mumpsDataDestroy();
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
private:
/// access the control variable
inline Int & icntl(UInt i) { return mumps_data.icntl[i - 1]; }
/// access the results info
inline Int & info(UInt i) { return mumps_data.info[i - 1]; }
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
private:
/// DOFManager used by the Mumps implementation of the SparseSolver
DOFManagerDefault & dof_manager;
/// Full right hand side on the master processors and solution after solve
Array<Real> master_rhs_solution;
/// mumps data
DMUMPS_STRUC_C mumps_data;
/// Rank of the current process
UInt prank;
/// matrix release at last solve
UInt last_profile_release{UInt(-1)};
/// matrix release at last solve
UInt last_value_release{UInt(-1)};
/// check if the solver data are initialized
bool is_initialized{false};
/* ------------------------------------------------------------------------ */
/* Local types */
/* ------------------------------------------------------------------------ */
private:
SolverParallelMethod parallel_method;
// bool rhs_is_local;
enum SolverMumpsJob {
_smj_initialize = -1,
_smj_analyze = 1,
_smj_factorize = 2,
_smj_solve = 3,
_smj_analyze_factorize = 4,
_smj_factorize_solve = 5,
_smj_complete = 6, // analyze, factorize, solve
_smj_destroy = -2
};
};
} // namespace akantu
#endif /* AKANTU_SOLVER_MUMPS_HH_ */
diff --git a/src/synchronizer/dof_synchronizer.cc b/src/synchronizer/dof_synchronizer.cc
index 9221bd6f6..cd5f28ae1 100644
--- a/src/synchronizer/dof_synchronizer.cc
+++ b/src/synchronizer/dof_synchronizer.cc
@@ -1,230 +1,229 @@
/**
* @file dof_synchronizer.cc
*
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 17 2011
* @date last modification: Tue Feb 06 2018
*
* @brief DOF synchronizing object implementation
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "dof_synchronizer.hh"
#include "aka_iterators.hh"
#include "dof_manager_default.hh"
#include "mesh.hh"
#include "node_synchronizer.hh"
/* -------------------------------------------------------------------------- */
#include <algorithm>
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
/**
* A DOFSynchronizer needs a mesh and the number of degrees of freedom
* per node to be created. In the constructor computes the local and global dof
* number for each dof. The member
* proc_informations (std vector) is resized with the number of mpi
* processes. Each entry in the vector is a PerProcInformations object
* that contains the interactions of the current mpi process (prank) with the
* mpi process corresponding to the position of that entry. Every
* ProcInformations object contains one array with the dofs that have
* to be sent to prank and a second one with dofs that willl be received form
* prank.
* This information is needed for the asychronous communications. The
* constructor sets up this information.
*/
-DOFSynchronizer::DOFSynchronizer(DOFManagerDefault & dof_manager, const ID & id,
- MemoryID memory_id)
- : SynchronizerImpl<UInt>(dof_manager.getCommunicator(), id, memory_id),
+DOFSynchronizer::DOFSynchronizer(DOFManagerDefault & dof_manager, const ID & id)
+ : SynchronizerImpl<UInt>(dof_manager.getCommunicator(), id),
dof_manager(dof_manager) {
std::vector<ID> dof_ids = dof_manager.getDOFIDs();
// Transfers nodes to global equation numbers in new schemes
for (const ID & dof_id : dof_ids) {
registerDOFs(dof_id);
}
}
/* -------------------------------------------------------------------------- */
DOFSynchronizer::~DOFSynchronizer() = default;
/* -------------------------------------------------------------------------- */
void DOFSynchronizer::registerDOFs(const ID & dof_id) {
if (this->nb_proc == 1) {
return;
}
if (dof_manager.getSupportType(dof_id) != _dst_nodal) {
return;
}
const auto & equation_numbers = dof_manager.getLocalEquationsNumbers(dof_id);
const auto & associated_nodes = dof_manager.getDOFsAssociatedNodes(dof_id);
const auto & node_synchronizer = dof_manager.getMesh().getNodeSynchronizer();
const auto & node_communications = node_synchronizer.getCommunications();
auto transcode_node_to_global_dof_scheme =
[this, &associated_nodes, &equation_numbers](
auto && it, auto && end, const CommunicationSendRecv & sr) -> void {
for (; it != end; ++it) {
auto & scheme = communications.createScheme(it->first, sr);
const auto & node_scheme = it->second;
for (auto & node : node_scheme) {
auto an_begin = associated_nodes.begin();
auto an_it = an_begin;
auto an_end = associated_nodes.end();
std::vector<UInt> global_dofs_per_node;
while ((an_it = std::find(an_it, an_end, node)) != an_end) {
UInt pos = an_it - an_begin;
UInt local_eq_num = equation_numbers(pos);
UInt global_eq_num =
dof_manager.localToGlobalEquationNumber(local_eq_num);
global_dofs_per_node.push_back(global_eq_num);
++an_it;
}
std::sort(global_dofs_per_node.begin(), global_dofs_per_node.end());
std::transform(global_dofs_per_node.begin(), global_dofs_per_node.end(),
global_dofs_per_node.begin(), [this](UInt g) -> UInt {
UInt l = dof_manager.globalToLocalEquationNumber(g);
return l;
});
for (auto & leqnum : global_dofs_per_node) {
scheme.push_back(leqnum);
}
}
}
};
for (auto sr : send_recv_t{}) {
auto ncs_it = node_communications.begin_scheme(sr);
auto ncs_end = node_communications.end_scheme(sr);
transcode_node_to_global_dof_scheme(ncs_it, ncs_end, sr);
}
entities_changed = true;
}
/* -------------------------------------------------------------------------- */
void DOFSynchronizer::fillEntityToSend(Array<UInt> & dofs_to_send) {
UInt nb_dofs = dof_manager.getLocalSystemSize();
this->entities_from_root.zero();
dofs_to_send.resize(0);
for (UInt d : arange(nb_dofs)) {
if (not dof_manager.isLocalOrMasterDOF(d)) {
continue;
}
entities_from_root.push_back(d);
}
for (auto d : entities_from_root) {
UInt global_dof = dof_manager.localToGlobalEquationNumber(d);
dofs_to_send.push_back(global_dof);
}
}
/* -------------------------------------------------------------------------- */
void DOFSynchronizer::onNodesAdded(const Array<UInt> & /*nodes_list*/) {
auto dof_ids = dof_manager.getDOFIDs();
for (auto sr : iterate_send_recv) {
for (auto && data : communications.iterateSchemes(sr)) {
auto & scheme = data.second;
scheme.resize(0);
}
}
for (auto & dof_id : dof_ids) {
registerDOFs(dof_id);
}
// const auto & node_synchronizer =
// dof_manager.getMesh().getNodeSynchronizer(); const auto &
// node_communications = node_synchronizer.getCommunications();
// std::map<UInt, std::vector<UInt>> nodes_per_proc[2];
// for (auto sr : iterate_send_recv) {
// for (auto && data : node_communications.iterateSchemes(sr)) {
// auto proc = data.first;
// const auto & scheme = data.second;
// for (auto node : scheme) {
// nodes_per_proc[sr][proc].push_back(node);
// }
// }
// }
// std::map<UInt, std::vector<UInt>> dofs_per_proc[2];
// for (auto & dof_id : dof_ids) {
// const auto & associated_nodes =
// dof_manager.getDOFsAssociatedNodes(dof_id); const auto &
// local_equation_numbers =
// dof_manager.getEquationsNumbers(dof_id);
// for (auto tuple : zip(associated_nodes, local_equation_numbers)) {
// UInt assoc_node;
// UInt local_eq_num;
// std::tie(assoc_node, local_eq_num) = tuple;
// for (auto sr_it = send_recv_t::begin(); sr_it != send_recv_t::end();
// ++sr_it) {
// for (auto & pair : nodes_per_proc[*sr_it]) {
// if (std::find(pair.second.end(), pair.second.end(), assoc_node) !=
// pair.second.end()) {
// dofs_per_proc[*sr_it][pair.first].push_back(local_eq_num);
// }
// }
// }
// }
// }
// for (auto sr_it = send_recv_t::begin(); sr_it != send_recv_t::end();
// ++sr_it) {
// for (auto & pair : dofs_per_proc[*sr_it]) {
// std::sort(pair.second.begin(), pair.second.end(),
// [this](UInt la, UInt lb) -> bool {
// UInt ga = dof_manager.localToGlobalEquationNumber(la);
// UInt gb = dof_manager.localToGlobalEquationNumber(lb);
// return ga < gb;
// });
// auto & scheme = communications.getScheme(pair.first, *sr_it);
// scheme.resize(0);
// for (auto leq : pair.second) {
// scheme.push_back(leq);
// }
// }
// }
this->entities_changed = true;
}
} // namespace akantu
diff --git a/src/synchronizer/dof_synchronizer.hh b/src/synchronizer/dof_synchronizer.hh
index d053f4e3c..b458785e6 100644
--- a/src/synchronizer/dof_synchronizer.hh
+++ b/src/synchronizer/dof_synchronizer.hh
@@ -1,83 +1,83 @@
/**
* @file dof_synchronizer.hh
*
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 17 2011
* @date last modification: Tue Feb 20 2018
*
* @brief Synchronize Array of DOFs
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_array.hh"
#include "aka_common.hh"
#include "synchronizer_impl.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
class Mesh;
class DOFManagerDefault;
} // namespace akantu
#ifndef AKANTU_DOF_SYNCHRONIZER_HH_
#define AKANTU_DOF_SYNCHRONIZER_HH_
namespace akantu {
class DOFSynchronizer : public SynchronizerImpl<UInt> {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
DOFSynchronizer(DOFManagerDefault & dof_manager,
- const ID & id = "dof_synchronizer", MemoryID memory_id = 0);
+ const ID & id = "dof_synchronizer");
~DOFSynchronizer() override;
virtual void registerDOFs(const ID & dof_id);
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
void onNodesAdded(const Array<UInt> & nodes);
protected:
Int getRank(const UInt & /*node*/) const final { AKANTU_TO_IMPLEMENT(); }
/// list the entities to send to root process
void fillEntityToSend(Array<UInt> & dofs_to_send) override;
inline UInt canScatterSize() override;
inline UInt gatheredSize() override;
inline UInt localToGlobalEntity(const UInt & local) override;
private:
/// information on the dofs
DOFManagerDefault & dof_manager;
};
} // namespace akantu
#include "dof_synchronizer_inline_impl.hh"
#endif /* AKANTU_DOF_SYNCHRONIZER_HH_ */
diff --git a/src/synchronizer/element_synchronizer.cc b/src/synchronizer/element_synchronizer.cc
index 80db73a6a..772e32982 100644
--- a/src/synchronizer/element_synchronizer.cc
+++ b/src/synchronizer/element_synchronizer.cc
@@ -1,294 +1,293 @@
/**
* @file element_synchronizer.cc
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author Dana Christen <dana.christen@epfl.ch>
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
* @author Marco Vocialta <marco.vocialta@epfl.ch>
*
* @date creation: Wed Sep 01 2010
* @date last modification: Tue Feb 20 2018
*
* @brief implementation of a communicator using a static_communicator for
* real
* send/receive
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "element_synchronizer.hh"
#include "aka_common.hh"
#include "mesh.hh"
#include "mesh_utils.hh"
/* -------------------------------------------------------------------------- */
#include <algorithm>
#include <iostream>
#include <map>
/* -------------------------------------------------------------------------- */
namespace akantu {
#if defined(AKANTU_MODULE)
#define AKANTU_MODULE_SAVE_ AKANTU_MODULE
#undef AKANTU_MODULE
#endif
#define AKANTU_MODULE element_synchronizer
/* -------------------------------------------------------------------------- */
ElementSynchronizer::ElementSynchronizer(Mesh & mesh, const ID & id,
- MemoryID memory_id,
bool register_to_event_manager,
EventHandlerPriority event_priority)
- : SynchronizerImpl<Element>(mesh.getCommunicator(), id, memory_id),
- mesh(mesh), element_to_prank("element_to_prank", id, memory_id) {
+ : SynchronizerImpl<Element>(mesh.getCommunicator(), id),
+ mesh(mesh), element_to_prank("element_to_prank", id) {
AKANTU_DEBUG_IN();
if (register_to_event_manager) {
this->mesh.registerEventHandler(*this, event_priority);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
ElementSynchronizer::ElementSynchronizer(const ElementSynchronizer & other,
const ID & id,
bool register_to_event_manager,
EventHandlerPriority event_priority)
: SynchronizerImpl<Element>(other, id), mesh(other.mesh),
- element_to_prank("element_to_prank", id, other.memory_id) {
+ element_to_prank("element_to_prank", id) {
AKANTU_DEBUG_IN();
element_to_prank.copy(other.element_to_prank);
if (register_to_event_manager) {
this->mesh.registerEventHandler(*this, event_priority);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
ElementSynchronizer::~ElementSynchronizer() = default;
/* -------------------------------------------------------------------------- */
void ElementSynchronizer::substituteElements(
const std::map<Element, Element> & old_to_new_elements) {
auto found_element_end = old_to_new_elements.end();
// substitute old elements with new ones
for (auto && sr : iterate_send_recv) {
for (auto && scheme_pair : communications.iterateSchemes(sr)) {
auto & list = scheme_pair.second;
for (auto & el : list) {
auto found_element_it = old_to_new_elements.find(el);
if (found_element_it != found_element_end) {
el = found_element_it->second;
}
}
}
}
}
/* -------------------------------------------------------------------------- */
void ElementSynchronizer::onElementsChanged(
const Array<Element> & old_elements_list,
const Array<Element> & new_elements_list,
const ElementTypeMapArray<UInt> & /*unused*/,
const ChangedElementsEvent & /*unused*/) {
// create a map to link old elements to new ones
std::map<Element, Element> old_to_new_elements;
for (UInt el = 0; el < old_elements_list.size(); ++el) {
AKANTU_DEBUG_ASSERT(old_to_new_elements.find(old_elements_list(el)) ==
old_to_new_elements.end(),
"The same element cannot appear twice in the list");
old_to_new_elements[old_elements_list(el)] = new_elements_list(el);
}
substituteElements(old_to_new_elements);
communications.invalidateSizes();
}
/* -------------------------------------------------------------------------- */
void ElementSynchronizer::onElementsRemoved(
const Array<Element> & element_to_remove,
const ElementTypeMapArray<UInt> & new_numbering,
const RemovedElementsEvent & /*unused*/) {
AKANTU_DEBUG_IN();
this->filterScheme([&](auto && element) {
return std::find(element_to_remove.begin(), element_to_remove.end(),
element) == element_to_remove.end();
});
this->renumberElements(new_numbering);
communications.invalidateSizes();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
void ElementSynchronizer::buildElementToPrank() {
AKANTU_DEBUG_IN();
UInt spatial_dimension = mesh.getSpatialDimension();
element_to_prank.initialize(mesh, _spatial_dimension = spatial_dimension,
_element_kind = _ek_not_defined,
_with_nb_element = true, _default_value = rank);
/// assign prank to all ghost elements
for (auto && scheme : communications.iterateSchemes(_recv)) {
auto & recv = scheme.second;
auto proc = scheme.first;
for (auto & element : recv) {
element_to_prank(element) = proc;
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
Int ElementSynchronizer::getRank(const Element & element) const {
if (not element_to_prank.exists(element.type, element.ghost_type)) {
// Nicolas: Ok This is nasty I know....
const_cast<ElementSynchronizer *>(this)->buildElementToPrank();
}
return element_to_prank(element);
}
/* -------------------------------------------------------------------------- */
void ElementSynchronizer::renumberElements(
const ElementTypeMapArray<UInt> & new_numbering) {
for (auto && sr : iterate_send_recv) {
for (auto && scheme_pair : communications.iterateSchemes(sr)) {
auto & list = scheme_pair.second;
for (auto && el : list) {
if (new_numbering.exists(el.type, el.ghost_type)) {
el.element = new_numbering(el);
}
}
}
}
}
/* -------------------------------------------------------------------------- */
UInt ElementSynchronizer::sanityCheckDataSize(const Array<Element> & elements,
const SynchronizationTag & tag,
bool from_comm_desc) const {
UInt size = SynchronizerImpl<Element>::sanityCheckDataSize(elements, tag,
from_comm_desc);
// global connectivities;
size += mesh.getNbNodesPerElementList(elements) * sizeof(UInt);
// barycenters
size += (elements.size() * mesh.getSpatialDimension() * sizeof(Real));
return size;
}
/* -------------------------------------------------------------------------- */
void ElementSynchronizer::packSanityCheckData(
CommunicationBuffer & buffer, const Array<Element> & elements,
const SynchronizationTag & /*tag*/) const {
for (auto && element : elements) {
Vector<Real> barycenter(mesh.getSpatialDimension());
mesh.getBarycenter(element, barycenter);
buffer << barycenter;
const auto & conns = mesh.getConnectivity(element.type, element.ghost_type);
for (auto n : arange(conns.getNbComponent())) {
buffer << mesh.getNodeGlobalId(conns(element.element, n));
}
}
}
/* -------------------------------------------------------------------------- */
void ElementSynchronizer::unpackSanityCheckData(CommunicationBuffer & buffer,
const Array<Element> & elements,
const SynchronizationTag & tag,
UInt proc, UInt rank) const {
auto spatial_dimension = mesh.getSpatialDimension();
std::set<SynchronizationTag> skip_conn_tags{
SynchronizationTag::_smmc_facets_conn,
SynchronizationTag::_giu_global_conn};
bool is_skip_tag_conn = skip_conn_tags.find(tag) != skip_conn_tags.end();
for (auto && element : elements) {
Vector<Real> barycenter_loc(spatial_dimension);
mesh.getBarycenter(element, barycenter_loc);
Vector<Real> barycenter(spatial_dimension);
buffer >> barycenter;
auto dist = barycenter_loc.distance(barycenter);
if (not Math::are_float_equal(dist, 0.)) {
AKANTU_EXCEPTION("Unpacking an unknown value for the element "
<< element << "(barycenter " << barycenter_loc
<< " != buffer " << barycenter << ") [" << dist
<< "] - tag: " << tag << " comm from " << proc << " to "
<< rank);
}
const auto & conns = mesh.getConnectivity(element.type, element.ghost_type);
Vector<UInt> global_conn(conns.getNbComponent());
Vector<UInt> local_global_conn(conns.getNbComponent());
auto is_same = true;
for (auto n : arange(global_conn.size())) {
buffer >> global_conn(n);
auto node = conns(element.element, n);
local_global_conn(n) = mesh.getNodeGlobalId(node);
is_same &= is_skip_tag_conn or mesh.isPureGhostNode(node) or
(local_global_conn(n) == global_conn(n));
}
if (not is_same) {
AKANTU_DEBUG_WARNING(
"The connectivity of the element "
<< element << " " << local_global_conn
<< " does not match the connectivity of the equivalent "
"element on proc "
<< proc << " " << global_conn << " in communication with tag "
<< tag);
}
}
}
/* -------------------------------------------------------------------------- */
} // namespace akantu
#if defined(AKANTU_MODULE_SAVE_)
#undef AKANTU_MODULE
#define AKANTU_MODULE AKANTU_MODULE_SAVE_
#undef AKANTU_MODULE_SAVE_
#endif
diff --git a/src/synchronizer/element_synchronizer.hh b/src/synchronizer/element_synchronizer.hh
index c50325b89..e772094dd 100644
--- a/src/synchronizer/element_synchronizer.hh
+++ b/src/synchronizer/element_synchronizer.hh
@@ -1,201 +1,200 @@
/**
* @file element_synchronizer.hh
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author Dana Christen <dana.christen@epfl.ch>
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
* @author Marco Vocialta <marco.vocialta@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Tue Feb 20 2018
*
* @brief Main element synchronizer
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_ELEMENT_SYNCHRONIZER_HH_
#define AKANTU_ELEMENT_SYNCHRONIZER_HH_
/* -------------------------------------------------------------------------- */
#include "aka_array.hh"
#include "aka_common.hh"
#include "mesh_partition.hh"
#include "synchronizer_impl.hh"
namespace akantu {
class Mesh;
}
/* -------------------------------------------------------------------------- */
namespace akantu {
class ElementSynchronizer : public SynchronizerImpl<Element>,
public MeshEventHandler {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
ElementSynchronizer(Mesh & mesh, const ID & id = "element_synchronizer",
- MemoryID memory_id = 0,
bool register_to_event_manager = true,
EventHandlerPriority event_priority = _ehp_synchronizer);
ElementSynchronizer(const ElementSynchronizer & other,
const ID & id = "element_synchronizer_copy",
bool register_to_event_manager = true,
EventHandlerPriority event_priority = _ehp_synchronizer);
public:
~ElementSynchronizer() override;
friend class ElementInfoPerProc;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/* ------------------------------------------------------------------------ */
/// mesh event handler onElementsChanged
void onElementsChanged(const Array<Element> & old_elements_list,
const Array<Element> & new_elements_list,
const ElementTypeMapArray<UInt> & new_numbering,
const ChangedElementsEvent & event) override;
/// mesh event handler onRemovedElement
void onElementsRemoved(const Array<Element> & element_to_remove,
const ElementTypeMapArray<UInt> & new_numbering,
const RemovedElementsEvent & event) override;
protected:
/// remove elements from the synchronizer without renumbering them
void removeElements(const Array<Element> & element_to_remove);
/// renumber the elements in the synchronizer
void renumberElements(const ElementTypeMapArray<UInt> & new_numbering);
/// build processor to element correspondence
void buildElementToPrank();
protected:
/// fill the nodes type vector
void fillNodesType(const MeshData & mesh_data,
DynamicCommunicationBuffer * buffers,
const std::string & tag_name, ElementType el_type,
const Array<UInt> & partition_num);
template <typename T>
void fillTagBufferTemplated(const MeshData & mesh_data,
DynamicCommunicationBuffer * buffers,
const std::string & tag_name,
ElementType el_type,
const Array<UInt> & partition_num,
const CSR<UInt> & ghost_partition);
void fillTagBuffer(const MeshData & mesh_data,
DynamicCommunicationBuffer * buffers,
const std::string & tag_name, ElementType el_type,
const Array<UInt> & partition_num,
const CSR<UInt> & ghost_partition);
/// function that handels the MeshData to be split (root side)
static void synchronizeTagsSend(ElementSynchronizer & communicator, UInt root,
Mesh & mesh, UInt nb_tags,
ElementType type,
const Array<UInt> & partition_num,
const CSR<UInt> & ghost_partition,
UInt nb_local_element, UInt nb_ghost_element);
/// function that handles the MeshData to be split (other nodes)
static void synchronizeTagsRecv(ElementSynchronizer & communicator, UInt root,
Mesh & mesh, UInt nb_tags,
ElementType type,
UInt nb_local_element, UInt nb_ghost_element);
/// function that handles the preexisting groups in the mesh
static void synchronizeElementGroups(ElementSynchronizer & communicator,
UInt root, Mesh & mesh,
ElementType type,
const Array<UInt> & partition_num,
const CSR<UInt> & ghost_partition,
UInt nb_element);
/// function that handles the preexisting groups in the mesh
static void synchronizeElementGroups(ElementSynchronizer & communicator,
UInt root, Mesh & mesh,
ElementType type);
/// function that handles the preexisting groups in the mesh
static void synchronizeNodeGroupsMaster(ElementSynchronizer & communicator,
UInt root, Mesh & mesh);
/// function that handles the preexisting groups in the mesh
static void synchronizeNodeGroupsSlaves(ElementSynchronizer & communicator,
UInt root, Mesh & mesh);
template <class CommunicationBuffer>
static void fillNodeGroupsFromBuffer(ElementSynchronizer & communicator,
Mesh & mesh,
CommunicationBuffer & buffer);
/// substitute elements in the send and recv arrays
void
substituteElements(const std::map<Element, Element> & old_to_new_elements);
/* ------------------------------------------------------------------------ */
/* Sanity checks */
/* ------------------------------------------------------------------------ */
UInt sanityCheckDataSize(const Array<Element> & elements,
const SynchronizationTag & tag,
bool from_comm_desc = true) const override;
void packSanityCheckData(CommunicationBuffer & /*buffer*/,
const Array<Element> & /*elements*/,
const SynchronizationTag & /*tag*/) const override;
void unpackSanityCheckData(CommunicationBuffer & /*buffer*/,
const Array<Element> & /*elements*/,
const SynchronizationTag & /*tag*/, UInt /*proc*/,
UInt /*rank*/) const override;
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
AKANTU_GET_MACRO(Mesh, mesh, Mesh &);
AKANTU_GET_MACRO(ElementToRank, element_to_prank,
const ElementTypeMapArray<Int> &);
Int getRank(const Element & element) const final;
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
/// reference to the underlying mesh
Mesh & mesh;
friend class FacetSynchronizer;
ElementTypeMapArray<Int> element_to_prank;
};
/* -------------------------------------------------------------------------- */
} // namespace akantu
#endif /* AKANTU_ELEMENT_SYNCHRONIZER_HH_ */
diff --git a/src/synchronizer/facet_synchronizer.cc b/src/synchronizer/facet_synchronizer.cc
index d5fc90ddc..9359a7110 100644
--- a/src/synchronizer/facet_synchronizer.cc
+++ b/src/synchronizer/facet_synchronizer.cc
@@ -1,226 +1,226 @@
/**
* @file facet_synchronizer.cc
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
* @author Marco Vocialta <marco.vocialta@epfl.ch>
*
* @date creation: Wed Nov 05 2014
* @date last modification: Fri Jan 26 2018
*
* @brief Facet synchronizer for parallel simulations with cohesive elments
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "facet_synchronizer.hh"
/* -------------------------------------------------------------------------- */
#if defined(AKANTU_MODULE)
#define AKANTU_MODULE_SAVE_ AKANTU_MODULE
#undef AKANTU_MODULE
#endif
#define AKANTU_MODULE facet_synchronizer
namespace akantu {
/* -------------------------------------------------------------------------- */
FacetSynchronizer::FacetSynchronizer(
Mesh & mesh, const ElementSynchronizer & element_synchronizer,
- const ID & id, MemoryID memory_id)
- : ElementSynchronizer(mesh, id, memory_id) {
+ const ID & id)
+ : ElementSynchronizer(mesh, id) {
auto spatial_dimension = mesh.getSpatialDimension();
element_to_prank.initialize(mesh, _spatial_dimension = spatial_dimension - 1,
_ghost_type = _ghost, _with_nb_element = true,
_default_value = rank);
// Build element to prank
for (auto && scheme_pair :
element_synchronizer.communications.iterateSchemes(_recv)) {
auto proc = std::get<0>(scheme_pair);
const auto & scheme = std::get<1>(scheme_pair);
for (auto && elem : scheme) {
const auto & facet_to_element =
mesh.getSubelementToElement(elem.type, elem.ghost_type);
Vector<Element> facets = facet_to_element.begin(
facet_to_element.getNbComponent())[elem.element];
for (UInt f = 0; f < facets.size(); ++f) {
const auto & facet = facets(f);
if (facet == ElementNull) {
continue;
}
if (facet.ghost_type == _not_ghost) {
continue;
}
auto & facet_rank = element_to_prank(facet);
if ((proc < UInt(facet_rank)) || (UInt(facet_rank) == rank)) {
facet_rank = proc;
}
}
}
}
ElementTypeMapArray<UInt> facet_global_connectivities(
- "facet_global_connectivities", id, memory_id);
+ "facet_global_connectivities", id);
facet_global_connectivities.initialize(
mesh, _spatial_dimension = spatial_dimension - 1, _with_nb_element = true,
_with_nb_nodes_per_element = true);
mesh.getGlobalConnectivity(facet_global_connectivities);
// \TODO perhaps a global element numbering might be useful here...
for (auto type : facet_global_connectivities.elementTypes(_spatial_dimension =
_all_dimensions,
_element_kind = _ek_not_defined, _ghost_type = _not_ghost)) {
auto & conn = facet_global_connectivities(type, _not_ghost);
auto conn_view = make_view(conn, conn.getNbComponent());
std::for_each(conn_view.begin(), conn_view.end(), [&](auto & conn) {
std::sort(conn.storage(), conn.storage() + conn.size());
});
}
/// init facet check tracking
- ElementTypeMapArray<bool> facet_checked("facet_checked", id, memory_id);
+ ElementTypeMapArray<bool> facet_checked("facet_checked", id);
std::map<UInt, ElementTypeMapArray<UInt>> recv_connectivities;
/// Generate the recv scheme and connnectivities to send to the other
/// processors
for (auto && scheme_pair :
element_synchronizer.communications.iterateSchemes(_recv)) {
facet_checked.initialize(mesh, _spatial_dimension = spatial_dimension - 1,
_ghost_type = _ghost, _with_nb_element = true,
_default_value = false);
auto proc = scheme_pair.first;
const auto & elements = scheme_pair.second;
auto & facet_scheme = communications.createScheme(proc, _recv);
// this creates empty arrays...
auto & connectivities_for_proc = recv_connectivities[proc];
connectivities_for_proc.setID(
id + ":connectivities_for_proc:" + std::to_string(proc));
connectivities_for_proc.initialize(
mesh, _spatial_dimension = spatial_dimension - 1,
_with_nb_nodes_per_element = true, _ghost_type = _ghost);
// for every element in the element synchronizer communication scheme,
// check the facets to see if they should be communicated and create a
// connectivity array to match with the one other processors might send
for (auto && element : elements) {
const auto & facet_to_element =
mesh.getSubelementToElement(element.type, element.ghost_type);
Vector<Element> facets = facet_to_element.begin(
facet_to_element.getNbComponent())[element.element];
for (UInt f = 0; f < facets.size(); ++f) {
auto & facet = facets(f);
// exclude no valid facets
if (facet == ElementNull) {
continue;
}
// exclude _ghost facet from send scheme and _not_ghost from receive
if (facet.ghost_type != _ghost) {
continue;
}
// exclude facet from other processors then the one of current
// interest in case of receive scheme
if (UInt(element_to_prank(facet)) != proc) {
continue;
}
auto & checked = facet_checked(facet);
// skip already checked facets
if (checked) {
continue;
}
checked = true;
facet_scheme.push_back(facet);
auto & global_conn =
facet_global_connectivities(facet.type, facet.ghost_type);
Vector<UInt> conn =
global_conn.begin(global_conn.getNbComponent())[facet.element];
std::sort(conn.storage(), conn.storage() + conn.size());
connectivities_for_proc(facet.type, facet.ghost_type).push_back(conn);
}
}
}
std::vector<CommunicationRequest> send_requests;
/// do every communication by element type
for (auto && type : mesh.elementTypes(spatial_dimension - 1)) {
for (auto && pair : recv_connectivities) {
auto proc = std::get<0>(pair);
const auto & connectivities_for_proc = std::get<1>(pair);
auto && tag = Tag::genTag(proc, type, 1337);
send_requests.push_back(
communicator.asyncSend(connectivities_for_proc(type, _ghost), proc,
tag, CommunicationMode::_synchronous));
}
auto nb_nodes_per_facet = Mesh::getNbNodesPerElement(type);
communicator.receiveAnyNumber<UInt>(
send_requests,
[&](auto && proc, auto && message) {
auto & local_connectivities =
facet_global_connectivities(type, _not_ghost);
auto & send_scheme = communications.createScheme(proc, _send);
auto conn_view = make_view(local_connectivities, nb_nodes_per_facet);
auto conn_begin = conn_view.begin();
auto conn_end = conn_view.end();
for (const auto & c_to_match :
make_view(message, nb_nodes_per_facet)) {
auto it = std::find(conn_begin, conn_end, c_to_match);
if (it != conn_end) {
auto facet = Element{type, UInt(it - conn_begin), _not_ghost};
send_scheme.push_back(facet);
} else {
AKANTU_EXCEPTION("No local facet found to send to proc "
<< proc << " corresponding to " << c_to_match);
}
}
},
Tag::genTag(rank, type, 1337));
}
}
} // namespace akantu
#if defined(AKANTU_MODULE_SAVE_)
#undef AKANTU_MODULE
#define AKANTU_MODULE AKANTU_MODULE_SAVE_
#undef AKANTU_MODULE_SAVE_
#endif
diff --git a/src/synchronizer/facet_synchronizer.hh b/src/synchronizer/facet_synchronizer.hh
index 23de4e1bb..e7beec804 100644
--- a/src/synchronizer/facet_synchronizer.hh
+++ b/src/synchronizer/facet_synchronizer.hh
@@ -1,96 +1,95 @@
/**
* @file facet_synchronizer.hh
*
* @author Marco Vocialta <marco.vocialta@epfl.ch>
*
*
* @brief Facet synchronizer for parallel simulations with cohesive elments
*
*
* Copyright (©) 2010-2012, 2014 EPFL (Ecole Polytechnique Fédérale de Lausanne)
* Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)
*
*/
/* -------------------------------------------------------------------------- */
#include "element_synchronizer.hh"
#include "fe_engine.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_FACET_SYNCHRONIZER_HH_
#define AKANTU_FACET_SYNCHRONIZER_HH_
namespace akantu {
class FacetSynchronizer : public ElementSynchronizer {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
FacetSynchronizer(Mesh & mesh,
const ElementSynchronizer & element_synchronizer,
- const ID & id = "facet_synchronizer",
- MemoryID memory_id = 0);
+ const ID & id = "facet_synchronizer");
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// update distributed synchronizer after elements' insertion
void
updateDistributedSynchronizer(ElementSynchronizer & distributed_synchronizer,
DataAccessor<Element> & data_accessor,
const Mesh & mesh_cohesive);
protected:
/// update elements list based on facets list
void updateElementList(Array<Element> * elements,
const Array<Element> * facets,
const Mesh & mesh_cohesive);
/// setup facet synchronization
void
setupFacetSynchronization(ElementSynchronizer & distributed_synchronizer);
/// build send facet arrays
void buildSendElementList(
const Array<ElementTypeMapArray<UInt> *> & send_connectivity,
const Array<ElementTypeMapArray<UInt> *> & recv_connectivity,
const Array<ElementTypeMapArray<UInt> *> & temp_send_element);
/// build recv facet arrays
void buildRecvElementList(
const Array<ElementTypeMapArray<UInt> *> & temp_recv_element);
/// get facets' global connectivity for a list of elements
template <GhostType ghost_facets>
inline void getFacetGlobalConnectivity(
const ElementSynchronizer & distributed_synchronizer,
const ElementTypeMapArray<UInt> & rank_to_facet,
const Array<Element> * elements,
Array<ElementTypeMapArray<UInt> *> & connectivity,
Array<ElementTypeMapArray<UInt> *> & facets);
/// initialize ElementTypeMap containing correspondance between
/// facets and processors
void initRankToFacet(ElementTypeMapArray<UInt> & rank_to_facet);
/// find which processor a facet is assigned to
void buildRankToFacet(ElementTypeMapArray<UInt> & rank_to_facet,
const Array<Element> * elements);
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
private:
ElementTypeMapArray<UInt> facet_to_rank;
};
} // namespace akantu
#include "facet_synchronizer_inline_impl.hh"
#endif /* AKANTU_FACET_SYNCHRONIZER_HH_ */
diff --git a/src/synchronizer/grid_synchronizer.cc b/src/synchronizer/grid_synchronizer.cc
index 3c3b13c0f..59d4f30d2 100644
--- a/src/synchronizer/grid_synchronizer.cc
+++ b/src/synchronizer/grid_synchronizer.cc
@@ -1,485 +1,485 @@
/**
* @file grid_synchronizer.cc
*
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Mon Oct 03 2011
* @date last modification: Tue Nov 07 2017
*
* @brief implementation of the grid synchronizer
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "grid_synchronizer.hh"
#include "aka_grid_dynamic.hh"
#include "communicator.hh"
#include "fe_engine.hh"
#include "integration_point.hh"
#include "mesh.hh"
#include "mesh_io.hh"
#include <iostream>
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
template <class E>
void GridSynchronizer::createGridSynchronizer(const SpatialGrid<E> & grid) {
AKANTU_DEBUG_IN();
const Communicator & comm = this->mesh.getCommunicator();
UInt nb_proc = comm.getNbProc();
UInt my_rank = comm.whoAmI();
if (nb_proc == 1) {
return;
}
UInt spatial_dimension = this->mesh.getSpatialDimension();
BBox my_bounding_box(spatial_dimension);
const auto & lower = grid.getLowerBounds();
const auto & upper = grid.getUpperBounds();
const auto & spacing = grid.getSpacing();
my_bounding_box.getLowerBounds() = lower - spacing;
my_bounding_box.getUpperBounds() = upper + spacing;
AKANTU_DEBUG_INFO(
"Exchange of bounding box to detect the overlapping regions.");
auto && bboxes = my_bounding_box.allGather(comm);
std::vector<bool> intersects_proc(nb_proc);
std::fill(intersects_proc.begin(), intersects_proc.end(), true);
Matrix<Int> first_cells(spatial_dimension, nb_proc);
Matrix<Int> last_cells(spatial_dimension, nb_proc);
std::map<UInt, ElementTypeMapArray<UInt>> element_per_proc;
// check the overlapping between my box and the one from other processors
for (UInt p = 0; p < nb_proc; ++p) {
if (p == my_rank) {
continue;
}
const auto & proc_bounding_box = bboxes[p];
auto intersection = my_bounding_box.intersection(proc_bounding_box);
Vector<Int> first_cell_p = first_cells(p);
Vector<Int> last_cell_p = last_cells(p);
intersects_proc[p] = intersection;
if (intersects_proc[p]) {
for (UInt s = 0; s < spatial_dimension; ++s) {
first_cell_p(s) = grid.getCellID(intersection.getLowerBounds()(s), s);
last_cell_p(s) = grid.getCellID(intersection.getUpperBounds()(s), s);
}
}
// create the list of cells in the overlapping
using CellID = typename SpatialGrid<E>::CellID;
std::vector<CellID> cell_ids;
if (intersects_proc[p]) {
AKANTU_DEBUG_INFO("I intersects with processor " << p);
CellID cell_id(spatial_dimension);
// for (UInt i = 0; i < spatial_dimension; ++i) {
// if(first_cell_p[i] != 0) --first_cell_p[i];
// if(last_cell_p[i] != 0) ++last_cell_p[i];
// }
for (Int fd = first_cell_p(0); fd <= last_cell_p(0); ++fd) {
cell_id.setID(0, fd);
if (spatial_dimension == 1) {
cell_ids.push_back(cell_id);
} else {
for (Int sd = first_cell_p(1); sd <= last_cell_p(1); ++sd) {
cell_id.setID(1, sd);
if (spatial_dimension == 2) {
cell_ids.push_back(cell_id);
} else {
for (Int ld = first_cell_p(2); ld <= last_cell_p(2); ++ld) {
cell_id.setID(2, ld);
cell_ids.push_back(cell_id);
}
}
}
}
}
// get the list of elements in the cells of the overlapping
std::set<Element> to_send;
for (auto & cur_cell_id : cell_ids) {
auto & cell = grid.getCell(cur_cell_id);
for (auto & element : cell) {
to_send.insert(element);
}
}
AKANTU_DEBUG_INFO("I have prepared " << to_send.size()
<< " elements to send to processor "
<< p);
auto & scheme = this->getCommunications().createSendScheme(p);
std::stringstream sstr;
sstr << "element_per_proc_" << p;
element_per_proc.emplace(
std::piecewise_construct, std::forward_as_tuple(p),
- std::forward_as_tuple(sstr.str(), id, memory_id));
+ std::forward_as_tuple(sstr.str(), id));
ElementTypeMapArray<UInt> & elempproc = element_per_proc[p];
for (auto elem : to_send) {
ElementType type = elem.type;
UInt nb_nodes_per_element = mesh.getNbNodesPerElement(type);
// /!\ this part must be slow due to the access in the
// ElementTypeMapArray<UInt>
if (!elempproc.exists(type, _not_ghost)) {
elempproc.alloc(0, nb_nodes_per_element, type, _not_ghost);
}
Vector<UInt> global_connect(nb_nodes_per_element);
Vector<UInt> local_connect = mesh.getConnectivity(type).begin(
nb_nodes_per_element)[elem.element];
for (UInt i = 0; i < nb_nodes_per_element; ++i) {
global_connect(i) = mesh.getNodeGlobalId(local_connect(i));
AKANTU_DEBUG_ASSERT(
global_connect(i) < mesh.getNbGlobalNodes(),
"This global node send in the connectivity does not seem correct "
<< global_connect(i) << " corresponding to "
<< local_connect(i) << " from element " << elem.element);
}
elempproc(type).push_back(global_connect);
scheme.push_back(elem);
}
}
}
AKANTU_DEBUG_INFO("I have finished to compute intersection,"
<< " no it's time to communicate with my neighbors");
/**
* Sending loop, sends the connectivity asynchronously to all concerned proc
*/
std::vector<CommunicationRequest> isend_requests;
Tensor3<UInt> space(2, _max_element_type, nb_proc);
for (UInt p = 0; p < nb_proc; ++p) {
if (p == my_rank) {
continue;
}
if (not intersects_proc[p]) {
continue;
}
Matrix<UInt> info_proc = space(p);
auto & elempproc = element_per_proc[p];
UInt count = 0;
for (auto type : elempproc.elementTypes(_all_dimensions, _not_ghost)) {
Array<UInt> & conn = elempproc(type, _not_ghost);
Vector<UInt> info = info_proc((UInt)type);
info[0] = (UInt)type;
info[1] = conn.size() * conn.getNbComponent();
AKANTU_DEBUG_INFO(
"I have " << conn.size() << " elements of type " << type
<< " to send to processor " << p << " (communication tag : "
<< Tag::genTag(my_rank, count, DATA_TAG) << ")");
isend_requests.push_back(
comm.asyncSend(info, p, Tag::genTag(my_rank, count, SIZE_TAG)));
if (info[1] != 0) {
isend_requests.push_back(comm.asyncSend<UInt>(
conn, p, Tag::genTag(my_rank, count, DATA_TAG)));
}
++count;
}
Vector<UInt> info = info_proc((UInt)_not_defined);
info[0] = (UInt)_not_defined;
info[1] = 0;
isend_requests.push_back(
comm.asyncSend(info, p, Tag::genTag(my_rank, count, SIZE_TAG)));
}
/**
* Receives the connectivity and store them in the ghosts elements
*/
MeshAccessor mesh_accessor(mesh);
auto & global_nodes_ids = mesh_accessor.getNodesGlobalIds();
auto & nodes_type = mesh_accessor.getNodesFlags();
std::vector<CommunicationRequest> isend_nodes_requests;
Vector<UInt> nb_nodes_to_recv(nb_proc);
UInt nb_total_nodes_to_recv = 0;
UInt nb_current_nodes = global_nodes_ids.size();
NewNodesEvent new_nodes;
NewElementsEvent new_elements;
std::map<UInt, std::vector<UInt>> ask_nodes_per_proc;
for (UInt p = 0; p < nb_proc; ++p) {
nb_nodes_to_recv(p) = 0;
if (p == my_rank) {
continue;
}
if (!intersects_proc[p]) {
continue;
}
auto & scheme = this->getCommunications().createRecvScheme(p);
ask_nodes_per_proc.emplace(std::piecewise_construct,
std::forward_as_tuple(p),
std::forward_as_tuple(0));
auto & ask_nodes = ask_nodes_per_proc[p];
UInt count = 0;
ElementType type = _not_defined;
do {
Vector<UInt> info(2);
comm.receive(info, p, Tag::genTag(p, count, SIZE_TAG));
type = (ElementType)info[0];
if (type == _not_defined) {
break;
}
UInt nb_nodes_per_element = mesh.getNbNodesPerElement(type);
UInt nb_element = info[1] / nb_nodes_per_element;
Array<UInt> tmp_conn(nb_element, nb_nodes_per_element);
tmp_conn.zero();
if (info[1] != 0) {
comm.receive<UInt>(tmp_conn, p, Tag::genTag(p, count, DATA_TAG));
}
AKANTU_DEBUG_INFO("I will receive "
<< nb_element << " elements of type "
<< ElementType(info[0]) << " from processor " << p
<< " (communication tag : "
<< Tag::genTag(p, count, DATA_TAG) << ")");
auto & ghost_connectivity = mesh_accessor.getConnectivity(type, _ghost);
auto & ghost_counter = mesh_accessor.getGhostsCounters(type, _ghost);
UInt nb_ghost_element = ghost_connectivity.size();
Element element{type, 0, _ghost};
Vector<UInt> conn(nb_nodes_per_element);
for (UInt el = 0; el < nb_element; ++el) {
UInt nb_node_to_ask_for_elem = 0;
for (UInt n = 0; n < nb_nodes_per_element; ++n) {
UInt gn = tmp_conn(el, n);
UInt ln = global_nodes_ids.find(gn);
AKANTU_DEBUG_ASSERT(gn < mesh.getNbGlobalNodes(),
"This global node seems not correct "
<< gn << " from element " << el << " node "
<< n);
if (ln == UInt(-1)) {
global_nodes_ids.push_back(gn);
nodes_type.push_back(NodeFlag::_pure_ghost); // pure ghost node
ln = nb_current_nodes;
new_nodes.getList().push_back(ln);
++nb_current_nodes;
ask_nodes.push_back(gn);
++nb_node_to_ask_for_elem;
}
conn[n] = ln;
}
// all the nodes are already known locally, the element should
// already exists
auto c = UInt(-1);
if (nb_node_to_ask_for_elem == 0) {
c = ghost_connectivity.find(conn);
element.element = c;
}
if (c == UInt(-1)) {
element.element = nb_ghost_element;
++nb_ghost_element;
ghost_connectivity.push_back(conn);
ghost_counter.push_back(1);
new_elements.getList().push_back(element);
} else {
++ghost_counter(c);
}
scheme.push_back(element);
}
count++;
} while (type != _not_defined);
AKANTU_DEBUG_INFO("I have "
<< ask_nodes.size()
<< " missing nodes for elements coming from processor "
<< p << " (communication tag : "
<< Tag::genTag(my_rank, 0, ASK_NODES_TAG) << ")");
ask_nodes.push_back(UInt(-1));
isend_nodes_requests.push_back(
comm.asyncSend(ask_nodes, p, Tag::genTag(my_rank, 0, ASK_NODES_TAG)));
nb_nodes_to_recv(p) = ask_nodes.size() - 1;
nb_total_nodes_to_recv += nb_nodes_to_recv(p);
}
Communicator::waitAll(isend_requests);
Communicator::freeCommunicationRequest(isend_requests);
/**
* Sends requested nodes to proc
*/
auto & nodes = const_cast<Array<Real> &>(mesh.getNodes());
UInt nb_nodes = nodes.size();
std::vector<CommunicationRequest> isend_coordinates_requests;
std::map<UInt, Array<Real>> nodes_to_send_per_proc;
for (UInt p = 0; p < nb_proc; ++p) {
if (p == my_rank || !intersects_proc[p]) {
continue;
}
Array<UInt> asked_nodes;
CommunicationStatus status;
AKANTU_DEBUG_INFO("Waiting list of nodes to send to processor "
<< p << "(communication tag : "
<< Tag::genTag(p, 0, ASK_NODES_TAG) << ")");
comm.probe<UInt>(p, Tag::genTag(p, 0, ASK_NODES_TAG), status);
UInt nb_nodes_to_send = status.size();
asked_nodes.resize(nb_nodes_to_send);
AKANTU_DEBUG_INFO("I have " << nb_nodes_to_send - 1
<< " nodes to send to processor " << p
<< " (communication tag : "
<< Tag::genTag(p, 0, ASK_NODES_TAG) << ")");
AKANTU_DEBUG_INFO("Getting list of nodes to send to processor "
<< p << " (communication tag : "
<< Tag::genTag(p, 0, ASK_NODES_TAG) << ")");
comm.receive(asked_nodes, p, Tag::genTag(p, 0, ASK_NODES_TAG));
nb_nodes_to_send--;
asked_nodes.resize(nb_nodes_to_send);
nodes_to_send_per_proc.emplace(std::piecewise_construct,
std::forward_as_tuple(p),
std::forward_as_tuple(0, spatial_dimension));
auto & nodes_to_send = nodes_to_send_per_proc[p];
auto node_it = nodes.begin(spatial_dimension);
for (UInt n = 0; n < nb_nodes_to_send; ++n) {
UInt ln = global_nodes_ids.find(asked_nodes(n));
AKANTU_DEBUG_ASSERT(ln != UInt(-1), "The node ["
<< asked_nodes(n)
<< "] requested by proc " << p
<< " was not found locally!");
nodes_to_send.push_back(node_it + ln);
}
if (nb_nodes_to_send != 0) {
AKANTU_DEBUG_INFO("Sending the "
<< nb_nodes_to_send << " nodes to processor " << p
<< " (communication tag : "
<< Tag::genTag(p, 0, SEND_NODES_TAG) << ")");
isend_coordinates_requests.push_back(comm.asyncSend(
nodes_to_send, p, Tag::genTag(my_rank, 0, SEND_NODES_TAG)));
}
#if not defined(AKANTU_NDEBUG)
else {
AKANTU_DEBUG_INFO("No nodes to send to processor " << p);
}
#endif
}
Communicator::waitAll(isend_nodes_requests);
Communicator::freeCommunicationRequest(isend_nodes_requests);
nodes.resize(nb_total_nodes_to_recv + nb_nodes);
for (UInt p = 0; p < nb_proc; ++p) {
if ((p != my_rank) && (nb_nodes_to_recv(p) > 0)) {
AKANTU_DEBUG_INFO("Receiving the "
<< nb_nodes_to_recv(p) << " nodes from processor " << p
<< " (communication tag : "
<< Tag::genTag(p, 0, SEND_NODES_TAG) << ")");
Vector<Real> nodes_to_recv(nodes.storage() + nb_nodes * spatial_dimension,
nb_nodes_to_recv(p) * spatial_dimension);
comm.receive(nodes_to_recv, p, Tag::genTag(p, 0, SEND_NODES_TAG));
nb_nodes += nb_nodes_to_recv(p);
}
#if not defined(AKANTU_NDEBUG)
else {
if (p != my_rank) {
AKANTU_DEBUG_INFO("No nodes to receive from processor " << p);
}
}
#endif
}
Communicator::waitAll(isend_coordinates_requests);
Communicator::freeCommunicationRequest(isend_coordinates_requests);
mesh.sendEvent(new_nodes);
mesh.sendEvent(new_elements);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template void GridSynchronizer::createGridSynchronizer<IntegrationPoint>(
const SpatialGrid<IntegrationPoint> & grid);
template void GridSynchronizer::createGridSynchronizer<Element>(
const SpatialGrid<Element> & grid);
} // namespace akantu
diff --git a/src/synchronizer/grid_synchronizer.hh b/src/synchronizer/grid_synchronizer.hh
index 841752fa4..7a2483fe8 100644
--- a/src/synchronizer/grid_synchronizer.hh
+++ b/src/synchronizer/grid_synchronizer.hh
@@ -1,101 +1,101 @@
/**
* @file grid_synchronizer.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Wed Nov 08 2017
*
* @brief Synchronizer based on spatial grid
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_common.hh"
#include "element_synchronizer.hh"
#include "synchronizer_registry.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_GRID_SYNCHRONIZER_HH_
#define AKANTU_GRID_SYNCHRONIZER_HH_
namespace akantu {
class Mesh;
template <class T> class SpatialGrid;
class GridSynchronizer : public ElementSynchronizer {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
template <typename E>
GridSynchronizer(Mesh & mesh, const SpatialGrid<E> & grid,
- const ID & id = "grid_synchronizer", MemoryID memory_id = 0,
+ const ID & id = "grid_synchronizer",
bool register_to_event_manager = true,
EventHandlerPriority event_priority = _ehp_synchronizer);
template <typename E>
GridSynchronizer(Mesh & mesh, const SpatialGrid<E> & grid,
SynchronizerRegistry & synchronizer_registry,
const std::set<SynchronizationTag> & tags_to_register,
- const ID & id = "grid_synchronizer", MemoryID memory_id = 0,
+ const ID & id = "grid_synchronizer",
bool register_to_event_manager = true,
EventHandlerPriority event_priority = _ehp_synchronizer);
~GridSynchronizer() override = default;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
private:
/**
*Create the Grid Synchronizer:
*Compute intersection and send info to neighbours that will be stored in
*ghosts elements
*/
template <typename E>
void createGridSynchronizer(const SpatialGrid<E> & grid);
protected:
/// Define the tags that will be used in the send and receive instructions
enum CommTags {
SIZE_TAG = 0,
DATA_TAG = 1,
ASK_NODES_TAG = 2,
SEND_NODES_TAG = 3
};
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
private:
};
} // namespace akantu
#include "grid_synchronizer_tmpl.hh"
#endif /* AKANTU_GRID_SYNCHRONIZER_HH_ */
diff --git a/src/synchronizer/grid_synchronizer_tmpl.hh b/src/synchronizer/grid_synchronizer_tmpl.hh
index 58a665ee3..0eb0b1cb2 100644
--- a/src/synchronizer/grid_synchronizer_tmpl.hh
+++ b/src/synchronizer/grid_synchronizer_tmpl.hh
@@ -1,74 +1,74 @@
/**
* @file grid_synchronizer_tmpl.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Thu Jul 06 2017
* @date last modification: Wed Aug 09 2017
*
* @brief implementation of the templated part of the grid syncrhonizers
*
*
* Copyright (©) 2016-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "grid_synchronizer.hh"
#ifndef AKANTU_GRID_SYNCHRONIZER_TMPL_HH_
#define AKANTU_GRID_SYNCHRONIZER_TMPL_HH_
namespace akantu {
/* -------------------------------------------------------------------------- */
template <typename E>
GridSynchronizer::GridSynchronizer(Mesh & mesh, const SpatialGrid<E> & grid,
- const ID & id, MemoryID memory_id,
+ const ID & id,
const bool register_to_event_manager,
EventHandlerPriority event_priority)
- : ElementSynchronizer(mesh, id, memory_id, register_to_event_manager,
+ : ElementSynchronizer(mesh, id, register_to_event_manager,
event_priority) {
AKANTU_DEBUG_IN();
this->createGridSynchronizer(grid);
AKANTU_DEBUG_OUT();
}
template <typename E>
GridSynchronizer::GridSynchronizer(
Mesh & mesh, const SpatialGrid<E> & grid,
SynchronizerRegistry & synchronizer_registry,
const std::set<SynchronizationTag> & tags_to_register, const ID & id,
- MemoryID memory_id, const bool register_to_event_manager,
+ const bool register_to_event_manager,
EventHandlerPriority event_priority)
- : GridSynchronizer(mesh, grid, id, memory_id, register_to_event_manager,
+ : GridSynchronizer(mesh, grid, id, register_to_event_manager,
event_priority) {
AKANTU_DEBUG_IN();
// Register the tags if any
for (const auto & tag : tags_to_register) {
synchronizer_registry.registerSynchronizer(*this, tag);
}
AKANTU_DEBUG_OUT();
}
} // namespace akantu
#endif /* AKANTU_GRID_SYNCHRONIZER_TMPL_HH_ */
diff --git a/src/synchronizer/node_synchronizer.cc b/src/synchronizer/node_synchronizer.cc
index 1cbfb95d3..4f873197f 100644
--- a/src/synchronizer/node_synchronizer.cc
+++ b/src/synchronizer/node_synchronizer.cc
@@ -1,247 +1,246 @@
/**
* @file node_synchronizer.cc
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Wed Nov 15 2017
*
* @brief Implementation of the node synchronizer
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "node_synchronizer.hh"
#include "mesh.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
NodeSynchronizer::NodeSynchronizer(Mesh & mesh, const ID & id,
- MemoryID memory_id,
const bool register_to_event_manager,
EventHandlerPriority event_priority)
- : SynchronizerImpl<UInt>(mesh.getCommunicator(), id, memory_id),
+ : SynchronizerImpl<UInt>(mesh.getCommunicator(), id),
mesh(mesh) {
AKANTU_DEBUG_IN();
if (register_to_event_manager) {
this->mesh.registerEventHandler(*this, event_priority);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
NodeSynchronizer::~NodeSynchronizer() = default;
/* -------------------------------------------------------------------------- */
Int NodeSynchronizer::getRank(const UInt & node) const {
return this->mesh.getNodePrank(node);
}
/* -------------------------------------------------------------------------- */
void NodeSynchronizer::onNodesAdded(const Array<UInt> & /*nodes_list*/,
const NewNodesEvent & /*unused*/) {
std::map<UInt, std::vector<UInt>> nodes_per_proc;
// recreates fully the schemes due to changes of global ids
// \TODO add an event to handle global id changes
for (auto && data : communications.iterateSchemes(_recv)) {
auto & scheme = data.second;
scheme.resize(0);
}
for (auto && local_id : arange(mesh.getNbNodes())) {
if (not mesh.isSlaveNode(local_id)) {
continue; // local, master or pure ghost
}
auto global_id = mesh.getNodeGlobalId(local_id);
auto proc = mesh.getNodePrank(local_id);
AKANTU_DEBUG_ASSERT(
proc != -1,
"The node " << local_id << " does not have a valid associated prank");
nodes_per_proc[proc].push_back(global_id);
auto & scheme = communications.createScheme(proc, _recv);
scheme.push_back(local_id);
}
std::vector<CommunicationRequest> send_requests;
for (auto && pair : communications.iterateSchemes(_recv)) {
auto proc = pair.first;
AKANTU_DEBUG_ASSERT(proc != UInt(-1),
"For real I should send something to proc -1");
// if proc not in nodes_per_proc this should insert an empty array to send
send_requests.push_back(communicator.asyncSend(
nodes_per_proc[proc], proc, Tag::genTag(rank, proc, 0xcafe)));
}
for (auto && data : communications.iterateSchemes(_send)) {
auto proc = data.first;
auto & scheme = data.second;
CommunicationStatus status;
auto tag = Tag::genTag(proc, rank, 0xcafe);
communicator.probe<UInt>(proc, tag, status);
scheme.resize(status.size());
communicator.receive(scheme, proc, tag);
std::transform(scheme.begin(), scheme.end(), scheme.begin(),
[&](auto & gnode) { return mesh.getNodeLocalId(gnode); });
}
// communicator.receiveAnyNumber<UInt>(
// send_requests,
// [&](auto && proc, auto && nodes) {
// auto & scheme = communications.createScheme(proc, _send);
// scheme.resize(nodes.size());
// for (auto && data : enumerate(nodes)) {
// auto global_id = std::get<1>(data);
// auto local_id = mesh.getNodeLocalId(global_id);
// AKANTU_DEBUG_ASSERT(local_id != UInt(-1),
// "The global node " << global_id
// << "is not known on rank "
// << rank);
// scheme[std::get<0>(data)] = local_id;
// }
// },
// Tag::genTag(rank, count, 0xcafe));
// ++count;
Communicator::waitAll(send_requests);
Communicator::freeCommunicationRequest(send_requests);
this->entities_changed = true;
}
/* -------------------------------------------------------------------------- */
UInt NodeSynchronizer::sanityCheckDataSize(const Array<UInt> & nodes,
const SynchronizationTag & tag,
bool from_comm_desc) const {
UInt size =
SynchronizerImpl<UInt>::sanityCheckDataSize(nodes, tag, from_comm_desc);
// global id
if (tag != SynchronizationTag::_giu_global_conn) {
size += sizeof(UInt) * nodes.size();
}
// flag
size += sizeof(NodeFlag) * nodes.size();
// positions
size += mesh.getSpatialDimension() * sizeof(Real) * nodes.size();
return size;
}
/* -------------------------------------------------------------------------- */
void NodeSynchronizer::packSanityCheckData(
CommunicationBuffer & buffer, const Array<UInt> & nodes,
const SynchronizationTag & tag) const {
auto dim = mesh.getSpatialDimension();
for (auto && node : nodes) {
if (tag != SynchronizationTag::_giu_global_conn) {
buffer << mesh.getNodeGlobalId(node);
}
buffer << mesh.getNodeFlag(node);
buffer << Vector<Real>(mesh.getNodes().begin(dim)[node]);
}
}
/* -------------------------------------------------------------------------- */
void NodeSynchronizer::unpackSanityCheckData(CommunicationBuffer & buffer,
const Array<UInt> & nodes,
const SynchronizationTag & tag,
UInt proc, UInt rank) const {
auto dim = mesh.getSpatialDimension();
#ifndef AKANTU_NDEBUG
auto periodic = [&](auto && flag) { return flag & NodeFlag::_periodic_mask; };
auto distrib = [&](auto && flag) { return flag & NodeFlag::_shared_mask; };
#endif
for (auto && node : nodes) {
if (tag != SynchronizationTag::_giu_global_conn) {
UInt global_id;
buffer >> global_id;
AKANTU_DEBUG_ASSERT(global_id == mesh.getNodeGlobalId(node),
"The nodes global ids do not match: "
<< global_id
<< " != " << mesh.getNodeGlobalId(node));
}
NodeFlag flag;
buffer >> flag;
AKANTU_DEBUG_ASSERT(
(periodic(flag) == periodic(mesh.getNodeFlag(node))) and
(((distrib(flag) == NodeFlag::_master) and
(distrib(mesh.getNodeFlag(node)) ==
NodeFlag::_slave)) or // master to slave
((distrib(flag) == NodeFlag::_slave) and
(distrib(mesh.getNodeFlag(node)) ==
NodeFlag::_master)) or // reverse comm slave to master
(distrib(mesh.getNodeFlag(node)) ==
NodeFlag::_pure_ghost or // pure ghost nodes
distrib(flag) == NodeFlag::_pure_ghost)),
"The node flags: " << flag << " and " << mesh.getNodeFlag(node));
Vector<Real> pos_remote(dim);
buffer >> pos_remote;
Vector<Real> pos(mesh.getNodes().begin(dim)[node]);
auto dist = pos_remote.distance(pos);
if (not Math::are_float_equal(dist, 0.)) {
AKANTU_EXCEPTION("Unpacking an unknown value for the node "
<< node << "(position " << pos << " != buffer "
<< pos_remote << ") [" << dist << "] - tag: " << tag
<< " comm from " << proc << " to " << rank);
}
}
}
/* -------------------------------------------------------------------------- */
void NodeSynchronizer::fillEntityToSend(Array<UInt> & nodes_to_send) {
UInt nb_nodes = mesh.getNbNodes();
this->entities_from_root.clear();
nodes_to_send.resize(0);
for (UInt n : arange(nb_nodes)) {
if (not mesh.isLocalOrMasterNode(n)) {
continue;
}
entities_from_root.push_back(n);
}
for (auto n : entities_from_root) {
UInt global_node = mesh.getNodeGlobalId(n);
nodes_to_send.push_back(global_node);
}
}
/* -------------------------------------------------------------------------- */
} // namespace akantu
diff --git a/src/synchronizer/node_synchronizer.hh b/src/synchronizer/node_synchronizer.hh
index 2cf5a2b65..04efb149e 100644
--- a/src/synchronizer/node_synchronizer.hh
+++ b/src/synchronizer/node_synchronizer.hh
@@ -1,113 +1,112 @@
/**
* @file node_synchronizer.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Tue Nov 08 2016
* @date last modification: Tue Feb 20 2018
*
* @brief Synchronizer for nodal information
*
*
* Copyright (©) 2016-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "mesh_events.hh"
#include "synchronizer_impl.hh"
/* -------------------------------------------------------------------------- */
#include <unordered_map>
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_NODE_SYNCHRONIZER_HH_
#define AKANTU_NODE_SYNCHRONIZER_HH_
namespace akantu {
class NodeSynchronizer : public MeshEventHandler,
public SynchronizerImpl<UInt> {
public:
NodeSynchronizer(Mesh & mesh, const ID & id = "element_synchronizer",
- MemoryID memory_id = 0,
bool register_to_event_manager = true,
EventHandlerPriority event_priority = _ehp_synchronizer);
~NodeSynchronizer() override;
UInt sanityCheckDataSize(const Array<UInt> & nodes,
const SynchronizationTag & tag,
bool from_comm_desc) const override;
void packSanityCheckData(CommunicationBuffer & buffer,
const Array<UInt> & nodes,
const SynchronizationTag & /*tag*/) const override;
void unpackSanityCheckData(CommunicationBuffer & buffer,
const Array<UInt> & nodes,
const SynchronizationTag & tag, UInt proc,
UInt rank) const override;
/// function to implement to react on akantu::NewNodesEvent
void onNodesAdded(const Array<UInt> & /*unused*/,
const NewNodesEvent & /*unused*/) override;
/// function to implement to react on akantu::RemovedNodesEvent
void onNodesRemoved(const Array<UInt> & /*unused*/,
const Array<UInt> & /*unused*/,
const RemovedNodesEvent & /*unused*/) override {}
/// function to implement to react on akantu::NewElementsEvent
void onElementsAdded(const Array<Element> & /*unused*/,
const NewElementsEvent & /*unused*/) override {}
/// function to implement to react on akantu::RemovedElementsEvent
void onElementsRemoved(const Array<Element> & /*unused*/,
const ElementTypeMapArray<UInt> & /*unused*/,
const RemovedElementsEvent & /*unused*/) override {}
/// function to implement to react on akantu::ChangedElementsEvent
void onElementsChanged(const Array<Element> & /*unused*/,
const Array<Element> & /*unused*/,
const ElementTypeMapArray<UInt> & /*unused*/,
const ChangedElementsEvent & /*unused*/) override {}
/* ------------------------------------------------------------------------ */
NodeSynchronizer & operator=(const NodeSynchronizer & other) {
copySchemes(other);
return *this;
}
friend class NodeInfoPerProc;
protected:
void fillEntityToSend(Array<UInt> & nodes_to_send) override;
public:
AKANTU_GET_MACRO(Mesh, mesh, Mesh &);
inline UInt canScatterSize() override;
inline UInt gatheredSize() override;
inline UInt localToGlobalEntity(const UInt & local) override;
protected:
Int getRank(const UInt & node) const final;
protected:
Mesh & mesh;
};
} // namespace akantu
#include "node_synchronizer_inline_impl.hh"
#endif /* AKANTU_NODE_SYNCHRONIZER_HH_ */
diff --git a/src/synchronizer/periodic_node_synchronizer.cc b/src/synchronizer/periodic_node_synchronizer.cc
index cb6286a93..6389c0e08 100644
--- a/src/synchronizer/periodic_node_synchronizer.cc
+++ b/src/synchronizer/periodic_node_synchronizer.cc
@@ -1,131 +1,131 @@
/**
* @file periodic_node_synchronizer.cc
*
* @author Nicolas Richart
*
* @date creation Tue May 29 2018
*
* @brief Implementation of the periodic node synchronizer
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "periodic_node_synchronizer.hh"
#include "mesh.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
PeriodicNodeSynchronizer::PeriodicNodeSynchronizer(
- Mesh & mesh, const ID & id, MemoryID memory_id,
+ Mesh & mesh, const ID & id,
const bool register_to_event_manager, EventHandlerPriority event_priority)
- : NodeSynchronizer(mesh, id + ":masters", memory_id,
+ : NodeSynchronizer(mesh, id + ":masters",
register_to_event_manager, event_priority) {}
/* -------------------------------------------------------------------------- */
void PeriodicNodeSynchronizer::update() {
static int count = 0;
const auto & masters_to_slaves = this->mesh.getPeriodicMasterSlaves();
masters_list.resize(0);
masters_list.reserve(masters_to_slaves.size());
slaves_list.resize(0);
slaves_list.reserve(masters_to_slaves.size());
reset();
std::set<UInt> masters_to_receive;
for (auto && data : masters_to_slaves) {
auto master = std::get<0>(data);
auto slave = std::get<1>(data);
masters_list.push_back(master);
slaves_list.push_back(slave);
if (not(mesh.isMasterNode(master) or mesh.isLocalNode(master))) {
masters_to_receive.insert(master);
}
}
if (not mesh.isDistributed() or nb_proc == 1) {
return;
}
std::map<Int, Array<UInt>> buffers;
for (auto node : masters_to_receive) {
auto && proc = mesh.getNodePrank(node);
auto && scheme = this->communications.createRecvScheme(proc);
scheme.push_back(node);
buffers[proc].push_back(mesh.getNodeGlobalId(node));
}
auto tag = Tag::genTag(0, count, Tag::_modify_scheme);
std::vector<CommunicationRequest> requests;
for (auto && data : buffers) {
auto proc = std::get<0>(data);
auto & buffer = std::get<1>(data);
requests.push_back(communicator.asyncSend(buffer, proc, tag,
CommunicationMode::_synchronous));
std::cout << "Recv from proc : " << proc << " -> "
<< this->communications.getScheme(proc, _recv).size()
<< std::endl;
}
communicator.receiveAnyNumber<UInt>(
requests,
[&](auto && proc, auto && msg) {
auto && scheme = this->communications.createSendScheme(proc);
for (auto node : msg) {
scheme.push_back(mesh.getNodeLocalId(node));
}
std::cout << "Send to proc : " << proc << " -> " << scheme.size()
<< " [" << tag << "]" << std::endl;
},
tag);
++count;
}
/* -------------------------------------------------------------------------- */
void PeriodicNodeSynchronizer::synchronizeOnceImpl(
DataAccessor<UInt> & data_accessor, const SynchronizationTag & tag) const {
NodeSynchronizer::synchronizeOnceImpl(data_accessor, tag);
auto size = data_accessor.getNbData(masters_list, tag);
CommunicationBuffer buffer(size);
data_accessor.packData(buffer, masters_list, tag);
data_accessor.unpackData(buffer, slaves_list, tag);
}
/* -------------------------------------------------------------------------- */
void PeriodicNodeSynchronizer::waitEndSynchronizeImpl(
DataAccessor<UInt> & data_accessor, const SynchronizationTag & tag) {
NodeSynchronizer::waitEndSynchronizeImpl(data_accessor, tag);
auto size = data_accessor.getNbData(masters_list, tag);
CommunicationBuffer buffer(size);
data_accessor.packData(buffer, masters_list, tag);
data_accessor.unpackData(buffer, slaves_list, tag);
}
} // namespace akantu
diff --git a/src/synchronizer/periodic_node_synchronizer.hh b/src/synchronizer/periodic_node_synchronizer.hh
index 7d90ed7f3..42ec94824 100644
--- a/src/synchronizer/periodic_node_synchronizer.hh
+++ b/src/synchronizer/periodic_node_synchronizer.hh
@@ -1,93 +1,92 @@
/**
* @file periodic_node_synchronizer.hh
*
* @author Nicolas Richart
*
* @date creation Tue May 29 2018
*
* @brief PeriodicNodeSynchronizer definition
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "node_synchronizer.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_PERIODIC_NODE_SYNCHRONIZER_HH_
#define AKANTU_PERIODIC_NODE_SYNCHRONIZER_HH_
namespace akantu {
class PeriodicNodeSynchronizer : public NodeSynchronizer {
public:
PeriodicNodeSynchronizer(
- Mesh & mesh, const ID & id = "periodic_node_synchronizer",
- MemoryID memory_id = 0, bool register_to_event_manager = true,
+ Mesh & mesh, const ID & id = "periodic_node_synchronizer", bool register_to_event_manager = true,
EventHandlerPriority event_priority = _ehp_synchronizer);
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
void update();
/// Uses the synchronizer to perform a reduction on the vector
template <template <class> class Op, typename T>
void reduceSynchronizeWithPBCSlaves(Array<T> & array) const;
/// synchronize ghosts without state
void synchronizeOnceImpl(DataAccessor<UInt> & data_accessor,
const SynchronizationTag & tag) const override;
// /// asynchronous synchronization of ghosts
// void asynchronousSynchronizeImpl(const DataAccessor<UInt> & data_accessor,
// const SynchronizationTag & tag) override;
/// wait end of asynchronous synchronization of ghosts
void waitEndSynchronizeImpl(DataAccessor<UInt> & data_accessor,
const SynchronizationTag & tag) override;
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
private:
// NodeSynchronizer master_to_slaves_synchronizer;
Array<UInt> masters_list;
Array<UInt> slaves_list;
};
/* -------------------------------------------------------------------------- */
template <template <class> class Op, typename T>
void PeriodicNodeSynchronizer::reduceSynchronizeWithPBCSlaves(
Array<T> & array) const {
ReduceDataAccessor<UInt, Op, T> data_accessor(array,
SynchronizationTag::_whatever);
auto size =
data_accessor.getNbData(slaves_list, SynchronizationTag::_whatever);
CommunicationBuffer buffer(size);
data_accessor.packData(buffer, slaves_list, SynchronizationTag::_whatever);
data_accessor.unpackData(buffer, masters_list, SynchronizationTag::_whatever);
this->reduceSynchronizeArray<Op>(array);
}
} // namespace akantu
#endif /* AKANTU_PERIODIC_NODE_SYNCHRONIZER_HH_ */
diff --git a/src/synchronizer/synchronizer.cc b/src/synchronizer/synchronizer.cc
index 9ed1aebaf..280bbc47f 100644
--- a/src/synchronizer/synchronizer.cc
+++ b/src/synchronizer/synchronizer.cc
@@ -1,55 +1,54 @@
/**
* @file synchronizer.cc
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Wed Sep 01 2010
* @date last modification: Wed Nov 15 2017
*
* @brief implementation of the common part
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "synchronizer.hh"
#include "communicator.hh"
/* -------------------------------------------------------------------------- */
#include <functional>
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
-Synchronizer::Synchronizer(const Communicator & comm, const ID & id,
- MemoryID memory_id)
- : Memory(id, memory_id), communicator(comm) {
+Synchronizer::Synchronizer(const Communicator & comm, const ID & id)
+ : communicator(comm) {
int max_tag = comm.getMaxTag();
- this->hash_id = std::hash<std::string>()(this->getID());
+ this->hash_id = std::hash<std::string>()(id);
if (max_tag != 0) {
this->hash_id = this->hash_id % max_tag;
}
this->nb_proc = communicator.getNbProc();
this->rank = communicator.whoAmI();
}
} // namespace akantu
diff --git a/src/synchronizer/synchronizer.hh b/src/synchronizer/synchronizer.hh
index 289fdbe0a..66040e359 100644
--- a/src/synchronizer/synchronizer.hh
+++ b/src/synchronizer/synchronizer.hh
@@ -1,129 +1,127 @@
/**
* @file synchronizer.hh
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Tue Feb 20 2018
*
* @brief Common interface for synchronizers
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
-
/* -------------------------------------------------------------------------- */
-#include "aka_memory.hh"
+#include "aka_common.hh"
/* -------------------------------------------------------------------------- */
#include <map>
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_SYNCHRONIZER_HH_
#define AKANTU_SYNCHRONIZER_HH_
namespace akantu {
class Communicator;
}
namespace akantu {
/* -------------------------------------------------------------------------- */
/* Base class for synchronizers */
/* -------------------------------------------------------------------------- */
-class Synchronizer : protected Memory {
+class Synchronizer {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
- Synchronizer(const Communicator & comm, const ID & id = "synchronizer",
- MemoryID memory_id = 0);
+ Synchronizer(const Communicator & comm, const ID & id = "synchronizer");
Synchronizer(const Synchronizer & other) = default;
- ~Synchronizer() override = default;
+ virtual ~Synchronizer() = default;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
public:
/// synchronous communications form slaves to master
template <class DataAccessor>
void slaveReductionOnce(DataAccessor & data_accessor,
const SynchronizationTag & tag) const;
/// synchronize ghosts without state
template <class DataAccessor>
void synchronizeOnce(DataAccessor & data_accessor,
const SynchronizationTag & tag) const;
/// synchronize ghosts
template <class DataAccessor>
void synchronize(DataAccessor & data_accessor,
const SynchronizationTag & tag);
/// asynchronous synchronization of ghosts
template <class DataAccessor>
void asynchronousSynchronize(const DataAccessor & data_accessor,
const SynchronizationTag & tag);
/// wait end of asynchronous synchronization of ghosts
template <class DataAccessor>
void waitEndSynchronize(DataAccessor & data_accessor,
const SynchronizationTag & tag);
/// compute buffer size for a given tag and data accessor
template <class DataAccessor>
void computeBufferSize(const DataAccessor & data_accessor,
const SynchronizationTag & tag);
/* ------------------------------------------------------------------------ */
/* Accessors */
/* ------------------------------------------------------------------------ */
public:
AKANTU_GET_MACRO(Communicator, communicator, const Communicator &);
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
/// id of the synchronizer
ID id;
/// hashed version of the id
int hash_id;
/// message counter per tag
std::map<SynchronizationTag, UInt> tag_counter;
/// the static memory instance
const Communicator & communicator;
/// nb processors in the communicator
UInt nb_proc;
/// rank in the communicator
UInt rank;
};
} // namespace akantu
#include "synchronizer_tmpl.hh"
#endif /* AKANTU_SYNCHRONIZER_HH_ */
diff --git a/src/synchronizer/synchronizer_impl.hh b/src/synchronizer/synchronizer_impl.hh
index 63ee56c53..576544014 100644
--- a/src/synchronizer/synchronizer_impl.hh
+++ b/src/synchronizer/synchronizer_impl.hh
@@ -1,216 +1,216 @@
/**
* @file synchronizer_impl.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jun 18 2010
* @date last modification: Tue Feb 20 2018
*
* @brief Implementation of the generic part of synchronizers
*
*
* Copyright (©) 2016-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "communications.hh"
#include "synchronizer.hh"
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_SYNCHRONIZER_IMPL_HH_
#define AKANTU_SYNCHRONIZER_IMPL_HH_
namespace akantu {
template <class Entity> class SynchronizerImpl : public Synchronizer {
/* ------------------------------------------------------------------------ */
/* Constructors/Destructors */
/* ------------------------------------------------------------------------ */
public:
SynchronizerImpl(const Communicator & communicator,
- const ID & id = "synchronizer", MemoryID memory_id = 0);
+ const ID & id = "synchronizer");
SynchronizerImpl(const SynchronizerImpl & other, const ID & id);
~SynchronizerImpl() override = default;
/* ------------------------------------------------------------------------ */
/* Methods */
/* ------------------------------------------------------------------------ */
protected:
void communicateOnce(
const std::tuple<CommunicationSendRecv, CommunicationSendRecv> &
send_recv_schemes,
const Tag::CommTags & comm_tag, DataAccessor<Entity> & data_accessor,
const SynchronizationTag & tag) const;
public:
/// synchronous synchronization without state
virtual void slaveReductionOnceImpl(DataAccessor<Entity> & data_accessor,
const SynchronizationTag & tag) const;
/// synchronous synchronization without state
virtual void synchronizeOnceImpl(DataAccessor<Entity> & data_accessor,
const SynchronizationTag & tag) const;
/// asynchronous synchronization of ghosts
virtual void
asynchronousSynchronizeImpl(const DataAccessor<Entity> & data_accessor,
const SynchronizationTag & tag);
/// wait end of asynchronous synchronization of ghosts
virtual void waitEndSynchronizeImpl(DataAccessor<Entity> & data_accessor,
const SynchronizationTag & tag);
/// compute all buffer sizes
virtual void
computeAllBufferSizes(const DataAccessor<Entity> & data_accessor);
/// compute buffer size for a given tag and data accessor
virtual void computeBufferSizeImpl(const DataAccessor<Entity> & data_accessor,
const SynchronizationTag & tag);
/* ------------------------------------------------------------------------ */
virtual void synchronizeImpl(DataAccessor<Entity> & data_accessor,
const SynchronizationTag & tag) {
this->asynchronousSynchronizeImpl(data_accessor, tag);
this->waitEndSynchronizeImpl(data_accessor, tag);
}
/* ------------------------------------------------------------------------ */
/// reset send and recv element lists
void reset();
/// extract the elements that have a true predicate from in_synchronizer and
/// store them in the current synchronizer
template <typename Pred>
void split(SynchronizerImpl & in_synchronizer, Pred && pred);
/// update schemes in a synchronizer
template <typename Updater> void updateSchemes(Updater && scheme_updater);
/// filter the send scheme and let the other processor now about iterate
template <typename Pred> void filterScheme(Pred && pred);
/// flip send and receive schemes
void swapSendRecv();
/// copy the schemes of an other communicator.
SynchronizerImpl & operator=(const SynchronizerImpl & other);
/// gather data on the predefined root process (master version)
template <typename T>
void gather(const Array<T> & to_gather, Array<T> & gathered);
/// gather data on the predefined root process (slave version)
template <typename T> void gather(const Array<T> & to_gather);
/// scatter data from the predefined root process (master version)
template <typename T>
void scatter(Array<T> & scattered, const Array<T> & to_scatter);
/// scatter data from the predefined root process (slave version)
template <typename T> void scatter(Array<T> & scattered);
template <typename T>
void synchronizeArray(Array<T> & array) const;
/// Uses the synchronizer to perform a reduction on the vector
template <template <class> class Op, typename T>
void reduceSynchronizeArray(Array<T> & array) const;
protected:
/// copy schemes
void copySchemes(const SynchronizerImpl & other);
/// check if dof changed set on at least one processor
inline bool hasChanged();
/// init the scheme for scatter and gather operation, need extra memory
inline void initScatterGatherCommunicationScheme();
/// list the entities to send to root process
virtual void fillEntityToSend(Array<Entity> & /*entities_to_send*/) {
AKANTU_TO_IMPLEMENT();
}
virtual Entity localToGlobalEntity(const Entity & /*local*/) {
AKANTU_TO_IMPLEMENT();
}
virtual UInt canScatterSize() {
AKANTU_TO_IMPLEMENT();
}
virtual UInt gatheredSize() {
AKANTU_TO_IMPLEMENT();
}
public:
/* ------------------------------------------------------------------------ */
virtual UInt sanityCheckDataSize(const Array<Entity> & elements,
const SynchronizationTag & tag,
bool is_comm_desc = true) const;
virtual void
packSanityCheckData(CommunicationDescriptor<Entity> & comm_desc) const;
virtual void
unpackSanityCheckData(CommunicationDescriptor<Entity> & comm_desc) const;
virtual void packSanityCheckData(CommunicationBuffer & /*buffer*/,
const Array<Entity> & /*elements*/,
const SynchronizationTag & /*tag*/) const {}
virtual void unpackSanityCheckData(CommunicationBuffer & /*buffer*/,
const Array<Entity> & /*elements*/,
const SynchronizationTag & /*tag*/,
UInt /*proc*/, UInt /*rank*/) const {}
public:
AKANTU_GET_MACRO(Communications, communications,
const Communications<Entity> &);
protected:
AKANTU_GET_MACRO_NOT_CONST(Communications, communications,
Communications<Entity> &);
virtual Int getRank(const Entity & entity) const = 0;
/* ------------------------------------------------------------------------ */
/* Class Members */
/* ------------------------------------------------------------------------ */
protected:
/// information on the communications
Communications<Entity> communications;
/// did the scheme change, this is to recreate the scatter/gather data if
/// needed
bool entities_changed{true};
/// Root processor for scatter/gather operations
Int root{0};
/// entities coming/going from/to root
Array<Entity> entities_from_root;
/// entities received from slaves proc (only on master)
std::map<UInt, Array<Entity>> master_receive_entities;
};
} // namespace akantu
#include "synchronizer_impl_tmpl.hh"
#endif /* AKANTU_SYNCHRONIZER_IMPL_HH_ */
diff --git a/src/synchronizer/synchronizer_impl_tmpl.hh b/src/synchronizer/synchronizer_impl_tmpl.hh
index 36e37152f..a6a317fdc 100644
--- a/src/synchronizer/synchronizer_impl_tmpl.hh
+++ b/src/synchronizer/synchronizer_impl_tmpl.hh
@@ -1,867 +1,867 @@
/**
* @file synchronizer_impl_tmpl.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Wed Sep 07 2016
* @date last modification: Tue Feb 20 2018
*
* @brief Implementation of the SynchronizerImpl
*
*
* Copyright (©) 2016-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "synchronizer_impl.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
/* -------------------------------------------------------------------------- */
template <class Entity>
SynchronizerImpl<Entity>::SynchronizerImpl(const Communicator & comm,
- const ID & id, MemoryID memory_id)
+ const ID & id)
- : Synchronizer(comm, id, memory_id), communications(comm) {}
+ : Synchronizer(comm, id), communications(comm) {}
/* -------------------------------------------------------------------------- */
template <class Entity>
SynchronizerImpl<Entity>::SynchronizerImpl(const SynchronizerImpl & other,
const ID & id)
: Synchronizer(other), communications(other.communications) {
this->id = id;
}
/* -------------------------------------------------------------------------- */
template <class Entity>
void SynchronizerImpl<Entity>::communicateOnce(
const std::tuple<CommunicationSendRecv, CommunicationSendRecv> &
send_recv_schemes,
const Tag::CommTags & comm_tag, DataAccessor<Entity> & data_accessor,
const SynchronizationTag & tag) const {
// no need to synchronize
if (this->nb_proc == 1) {
return;
}
CommunicationSendRecv send_dir;
CommunicationSendRecv recv_dir;
std::tie(send_dir, recv_dir) = send_recv_schemes;
using CommunicationRequests = std::vector<CommunicationRequest>;
using CommunicationBuffers = std::map<UInt, CommunicationBuffer>;
CommunicationRequests send_requests;
CommunicationRequests recv_requests;
CommunicationBuffers send_buffers;
CommunicationBuffers recv_buffers;
auto postComm = [&](const auto & sr, auto & buffers,
auto & requests) -> void {
for (auto && pair : communications.iterateSchemes(sr)) {
auto & proc = pair.first;
const auto & scheme = pair.second;
if (scheme.empty()) {
continue;
}
auto & buffer = buffers[proc];
auto buffer_size = data_accessor.getNbData(scheme, tag);
if (buffer_size == 0) {
continue;
}
#ifndef AKANTU_NDEBUG
buffer_size += this->sanityCheckDataSize(scheme, tag, false);
#endif
buffer.resize(buffer_size);
if (sr == recv_dir) {
requests.push_back(communicator.asyncReceive(
buffer, proc,
Tag::genTag(this->rank, UInt(tag), comm_tag, this->hash_id)));
} else {
#ifndef AKANTU_NDEBUG
this->packSanityCheckData(buffer, scheme, tag);
#endif
data_accessor.packData(buffer, scheme, tag);
AKANTU_DEBUG_ASSERT(
buffer.getPackedSize() == buffer.size(),
"The data accessor did not pack all the data it "
"promised in communication with tag "
<< tag << " (Promised: " << buffer.size()
<< "bytes, packed: " << buffer.getPackedSize() << "bytes [avg: "
<< Real(buffer.size() - buffer.getPackedSize()) / scheme.size()
<< "bytes per entity missing])");
send_requests.push_back(communicator.asyncSend(
buffer, proc,
Tag::genTag(proc, UInt(tag), comm_tag, this->hash_id)));
}
}
};
// post the receive requests
postComm(recv_dir, recv_buffers, recv_requests);
// post the send data requests
postComm(send_dir, send_buffers, send_requests);
// treat the receive requests
UInt request_ready;
while ((request_ready = Communicator::waitAny(recv_requests)) != UInt(-1)) {
auto & req = recv_requests[request_ready];
auto proc = req.getSource();
auto & buffer = recv_buffers[proc];
const auto & scheme = this->communications.getScheme(proc, recv_dir);
#ifndef AKANTU_NDEBUG
this->unpackSanityCheckData(buffer, scheme, tag, proc, this->rank);
#endif
data_accessor.unpackData(buffer, scheme, tag);
AKANTU_DEBUG_ASSERT(
buffer.getLeftToUnpack() == 0,
"The data accessor ignored some data in communication with tag "
<< tag);
req.free();
recv_requests.erase(recv_requests.begin() + request_ready);
}
Communicator::waitAll(send_requests);
Communicator::freeCommunicationRequest(send_requests);
}
/* -------------------------------------------------------------------------- */
template <class Entity>
void SynchronizerImpl<Entity>::slaveReductionOnceImpl(
DataAccessor<Entity> & data_accessor,
const SynchronizationTag & tag) const {
communicateOnce(std::make_tuple(_recv, _send), Tag::_reduce, data_accessor,
tag);
}
/* -------------------------------------------------------------------------- */
template <class Entity>
void SynchronizerImpl<Entity>::synchronizeOnceImpl(
DataAccessor<Entity> & data_accessor,
const SynchronizationTag & tag) const {
communicateOnce(std::make_tuple(_send, _recv), Tag::_synchronize,
data_accessor, tag);
}
/* -------------------------------------------------------------------------- */
template <class Entity>
void SynchronizerImpl<Entity>::asynchronousSynchronizeImpl(
const DataAccessor<Entity> & data_accessor,
const SynchronizationTag & tag) {
AKANTU_DEBUG_IN();
if (not this->communications.hasCommunicationSize(tag)) {
this->computeBufferSize(data_accessor, tag);
}
this->communications.incrementCounter(tag);
// Posting the receive -------------------------------------------------------
if (this->communications.hasPendingRecv(tag)) {
AKANTU_CUSTOM_EXCEPTION_INFO(
debug::CommunicationException(),
"There must still be some pending receive communications."
<< " Tag is " << tag << " Cannot start new ones");
}
for (auto && comm_desc : this->communications.iterateRecv(tag)) {
comm_desc.postRecv(this->hash_id);
}
// Posting the sends -------------------------------------------------------
if (communications.hasPendingSend(tag)) {
AKANTU_CUSTOM_EXCEPTION_INFO(
debug::CommunicationException(),
"There must be some pending sending communications."
<< " Tag is " << tag);
}
for (auto && comm_desc : this->communications.iterateSend(tag)) {
comm_desc.resetBuffer();
#ifndef AKANTU_NDEBUG
this->packSanityCheckData(comm_desc);
#endif
comm_desc.packData(data_accessor);
comm_desc.postSend(this->hash_id);
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class Entity>
void SynchronizerImpl<Entity>::waitEndSynchronizeImpl(
DataAccessor<Entity> & data_accessor, const SynchronizationTag & tag) {
AKANTU_DEBUG_IN();
#ifndef AKANTU_NDEBUG
if (this->communications.begin(tag, _recv) !=
this->communications.end(tag, _recv) &&
!this->communications.hasPendingRecv(tag)) {
AKANTU_CUSTOM_EXCEPTION_INFO(debug::CommunicationException(),
"No pending communication with the tag \""
<< tag);
}
#endif
auto recv_end = this->communications.end(tag, _recv);
decltype(recv_end) recv_it;
while ((recv_it = this->communications.waitAnyRecv(tag)) != recv_end) {
auto && comm_desc = *recv_it;
#ifndef AKANTU_NDEBUG
this->unpackSanityCheckData(comm_desc);
#endif
comm_desc.unpackData(data_accessor);
comm_desc.resetBuffer();
comm_desc.freeRequest();
}
this->communications.waitAllSend(tag);
this->communications.freeSendRequests(tag);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class Entity>
void SynchronizerImpl<Entity>::computeAllBufferSizes(
const DataAccessor<Entity> & data_accessor) {
for (auto && tag : this->communications.iterateTags()) {
this->computeBufferSize(data_accessor, tag);
}
}
/* -------------------------------------------------------------------------- */
template <class Entity>
void SynchronizerImpl<Entity>::computeBufferSizeImpl(
const DataAccessor<Entity> & data_accessor,
const SynchronizationTag & tag) {
AKANTU_DEBUG_IN();
if (not this->communications.hasCommunication(tag)) {
this->communications.initializeCommunications(tag);
AKANTU_DEBUG_ASSERT(communications.hasCommunication(tag) == true,
"Communications where not properly initialized");
}
for (auto sr : iterate_send_recv) {
for (auto && pair : this->communications.iterateSchemes(sr)) {
auto proc = pair.first;
const auto & scheme = pair.second;
UInt size = 0;
#ifndef AKANTU_NDEBUG
size += this->sanityCheckDataSize(scheme, tag);
#endif
size += data_accessor.getNbData(scheme, tag);
AKANTU_DEBUG_INFO("I have "
<< size << "(" << printMemorySize<char>(size) << " - "
<< scheme.size() << " element(s)) data to "
<< std::string(sr == _recv ? "receive from" : "send to")
<< proc << " for tag " << tag);
this->communications.setCommunicationSize(tag, proc, size, sr);
}
}
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <typename Entity> void SynchronizerImpl<Entity>::reset() {
AKANTU_DEBUG_IN();
communications.resetSchemes();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <typename Entity>
template <typename Pred>
void SynchronizerImpl<Entity>::split(SynchronizerImpl<Entity> & in_synchronizer,
Pred && pred) {
AKANTU_DEBUG_IN();
auto filter_list = [&](auto & list, auto & new_list) {
auto copy = list;
list.resize(0);
new_list.resize(0);
for (auto && entity : copy) {
if (std::forward<Pred>(pred)(entity)) {
new_list.push_back(entity);
} else {
list.push_back(entity);
}
}
};
for (auto sr : iterate_send_recv) {
for (auto & scheme_pair :
in_synchronizer.communications.iterateSchemes(sr)) {
auto proc = scheme_pair.first;
auto & scheme = scheme_pair.second;
auto & new_scheme = communications.createScheme(proc, sr);
filter_list(scheme, new_scheme);
}
}
in_synchronizer.communications.invalidateSizes();
communications.invalidateSizes();
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <typename Entity>
template <typename Updater>
void SynchronizerImpl<Entity>::updateSchemes(Updater && scheme_updater) {
for (auto sr : iterate_send_recv) {
for (auto & scheme_pair : communications.iterateSchemes(sr)) {
auto proc = scheme_pair.first;
auto & scheme = scheme_pair.second;
std::forward<Updater>(scheme_updater)(scheme, proc, sr);
}
}
communications.invalidateSizes();
}
/* -------------------------------------------------------------------------- */
template <typename Entity>
template <typename Pred>
void SynchronizerImpl<Entity>::filterScheme(Pred && pred) {
std::vector<CommunicationRequest> requests;
std::unordered_map<UInt, Array<UInt>> keep_entities;
auto filter_list = [](const auto & keep, auto & list) {
Array<Entity> new_list;
for (const auto & keep_entity : keep) {
const Entity & entity = list(keep_entity);
new_list.push_back(entity);
}
list.copy(new_list);
};
// loop over send_schemes
for (auto & scheme_pair : communications.iterateSchemes(_recv)) {
auto proc = scheme_pair.first;
auto & scheme = scheme_pair.second;
auto & keep_entity = keep_entities[proc];
for (auto && entity : enumerate(scheme)) {
if (pred(std::get<1>(entity))) {
keep_entity.push_back(std::get<0>(entity));
}
}
auto tag = Tag::genTag(this->rank, 0, Tag::_modify_scheme);
AKANTU_DEBUG_INFO("I have " << keep_entity.size()
<< " elements to still receive from processor "
<< proc << " (communication tag : " << tag
<< ")");
filter_list(keep_entity, scheme);
requests.push_back(communicator.asyncSend(keep_entity, proc, tag));
}
// clean the receive scheme
for (auto & scheme_pair : communications.iterateSchemes(_send)) {
auto proc = scheme_pair.first;
auto & scheme = scheme_pair.second;
auto tag = Tag::genTag(proc, 0, Tag::_modify_scheme);
AKANTU_DEBUG_INFO("Waiting list of elements to keep from processor "
<< proc << " (communication tag : " << tag << ")");
CommunicationStatus status;
communicator.probe<UInt>(proc, tag, status);
Array<UInt> keep_entity(status.size(), 1, "keep_element");
AKANTU_DEBUG_INFO("I have "
<< keep_entity.size()
<< " elements to keep in my send list to processor "
<< proc << " (communication tag : " << tag << ")");
communicator.receive(keep_entity, proc, tag);
filter_list(keep_entity, scheme);
}
Communicator::waitAll(requests);
Communicator::freeCommunicationRequest(requests);
communications.invalidateSizes();
}
/* -------------------------------------------------------------------------- */
template <class Entity> void SynchronizerImpl<Entity>::swapSendRecv() {
communications.swapSendRecv();
}
/* -------------------------------------------------------------------------- */
template <class Entity>
void SynchronizerImpl<Entity>::copySchemes(const SynchronizerImpl & other) {
reset();
for (auto sr : iterate_send_recv) {
for (auto & scheme_pair : other.communications.iterateSchemes(sr)) {
auto proc = scheme_pair.first;
auto & other_scheme = scheme_pair.second;
auto & scheme = communications.createScheme(proc, sr);
scheme.copy(other_scheme);
}
}
}
/* -------------------------------------------------------------------------- */
template <class Entity>
SynchronizerImpl<Entity> &
SynchronizerImpl<Entity>::operator=(const SynchronizerImpl & other) {
copySchemes(other);
return *this;
}
/* -------------------------------------------------------------------------- */
template <class Entity>
UInt SynchronizerImpl<Entity>::sanityCheckDataSize(
const Array<Entity> & /*unused*/, const SynchronizationTag & /*unused*/,
bool is_comm_desc) const {
if (not is_comm_desc) {
return 0;
}
UInt size = 0;
size += sizeof(SynchronizationTag); // tag
size += sizeof(UInt); // comm_desc.getNbData();
size += sizeof(UInt); // comm_desc.getProc();
size += sizeof(this->rank); // mesh.getCommunicator().whoAmI();
return size;
}
/* -------------------------------------------------------------------------- */
template <class Entity>
void SynchronizerImpl<Entity>::packSanityCheckData(
CommunicationDescriptor<Entity> & comm_desc) const {
auto & buffer = comm_desc.getBuffer();
buffer << comm_desc.getTag();
buffer << comm_desc.getNbData();
buffer << comm_desc.getProc();
buffer << this->rank;
const auto & tag = comm_desc.getTag();
const auto & send_element = comm_desc.getScheme();
this->packSanityCheckData(buffer, send_element, tag);
}
/* -------------------------------------------------------------------------- */
template <class Entity>
void SynchronizerImpl<Entity>::unpackSanityCheckData(
CommunicationDescriptor<Entity> & comm_desc) const {
auto & buffer = comm_desc.getBuffer();
const auto & tag = comm_desc.getTag();
auto nb_data = comm_desc.getNbData();
auto proc = comm_desc.getProc();
auto rank = this->rank;
decltype(nb_data) recv_nb_data;
decltype(proc) recv_proc;
decltype(rank) recv_rank;
SynchronizationTag t;
buffer >> t;
buffer >> recv_nb_data;
buffer >> recv_proc;
buffer >> recv_rank;
AKANTU_DEBUG_ASSERT(
t == tag, "The tag received does not correspond to the tag expected");
AKANTU_DEBUG_ASSERT(
nb_data == recv_nb_data,
"The nb_data received does not correspond to the nb_data expected");
AKANTU_DEBUG_ASSERT(UInt(recv_rank) == proc,
"The rank received does not correspond to the proc");
AKANTU_DEBUG_ASSERT(recv_proc == UInt(rank),
"The proc received does not correspond to the rank");
auto & recv_element = comm_desc.getScheme();
this->unpackSanityCheckData(buffer, recv_element, tag, proc, rank);
}
/* -------------------------------------------------------------------------- */
template <class Entity> bool SynchronizerImpl<Entity>::hasChanged() {
communicator.allReduce(entities_changed, SynchronizerOperation::_lor);
return entities_changed;
}
/* -------------------------------------------------------------------------- */
template <class Entity>
void SynchronizerImpl<Entity>::initScatterGatherCommunicationScheme() {
if (this->nb_proc == 1) {
entities_changed = false;
AKANTU_DEBUG_OUT();
return;
}
AKANTU_TO_IMPLEMENT();
}
/* -------------------------------------------------------------------------- */
template <>
inline void SynchronizerImpl<UInt>::initScatterGatherCommunicationScheme() {
AKANTU_DEBUG_IN();
if (this->nb_proc == 1) {
entities_changed = false;
AKANTU_DEBUG_OUT();
return;
}
this->entities_from_root.clear();
this->master_receive_entities.clear();
Array<UInt> entities_to_send;
fillEntityToSend(entities_to_send);
std::vector<CommunicationRequest> requests;
if (this->rank == UInt(this->root)) {
master_receive_entities[this->root].copy(entities_to_send);
Array<UInt> nb_entities_per_proc(this->nb_proc);
communicator.gather(entities_to_send.size(), nb_entities_per_proc);
for (UInt p = 0; p < nb_proc; ++p) {
if (p == UInt(this->root)) {
continue;
}
auto & receive_per_proc = master_receive_entities[p];
receive_per_proc.resize(nb_entities_per_proc(p));
if (nb_entities_per_proc(p) == 0) {
continue;
}
requests.push_back(communicator.asyncReceive(
receive_per_proc, p,
Tag::genTag(p, 0, Tag::_gather_initialization, this->hash_id)));
}
} else {
communicator.gather(entities_to_send.size(), this->root);
AKANTU_DEBUG(dblDebug, "I have " << entities_to_send.size()
<< " entities to send to master proc");
if (not entities_to_send.empty()) {
requests.push_back(communicator.asyncSend(
entities_to_send, this->root,
Tag::genTag(this->rank, 0, Tag::_gather_initialization,
this->hash_id)));
}
}
entities_changed = false;
Communicator::waitAll(requests);
Communicator::freeCommunicationRequest(requests);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class Entity>
template <typename T>
void SynchronizerImpl<Entity>::gather(const Array<T> & to_gather,
Array<T> & gathered) {
if (this->hasChanged()) {
initScatterGatherCommunicationScheme();
}
AKANTU_DEBUG_ASSERT(this->rank == UInt(this->root),
"This function cannot be called on a slave processor");
AKANTU_DEBUG_ASSERT(to_gather.size() == this->canScatterSize(),
"The array to gather does not have the correct size");
AKANTU_DEBUG_ASSERT(gathered.size() == this->gatheredSize(),
"The gathered array does not have the correct size");
if (this->nb_proc == 1) {
gathered.copy(to_gather, true);
AKANTU_DEBUG_OUT();
return;
}
std::map<UInt, CommunicationBuffer> buffers;
std::vector<CommunicationRequest> requests;
for (UInt p = 0; p < this->nb_proc; ++p) {
if (p == UInt(this->root)) {
continue;
}
auto receive_it = this->master_receive_entities.find(p);
AKANTU_DEBUG_ASSERT(receive_it != this->master_receive_entities.end(),
"Could not find the receive list for dofs of proc "
<< p);
const auto & receive_entities = receive_it->second;
if (receive_entities.empty()) {
continue;
}
CommunicationBuffer & buffer = buffers[p];
buffer.resize(receive_entities.size() * to_gather.getNbComponent() *
sizeof(T));
AKANTU_DEBUG_INFO(
"Preparing to receive data for "
<< receive_entities.size() << " entities from processor " << p << " "
<< Tag::genTag(p, this->root, Tag::_gather, this->hash_id));
requests.push_back(communicator.asyncReceive(
buffer, p, Tag::genTag(p, this->root, Tag::_gather, this->hash_id)));
}
auto data_gathered_it = gathered.begin(to_gather.getNbComponent());
{ // copy master data
auto data_to_gather_it = to_gather.begin(to_gather.getNbComponent());
for (auto local_entity : entities_from_root) {
UInt global_entity = localToGlobalEntity(local_entity);
Vector<T> entity_data_gathered = data_gathered_it[global_entity];
Vector<T> entity_data_to_gather = data_to_gather_it[local_entity];
entity_data_gathered = entity_data_to_gather;
}
}
auto rr = UInt(-1);
while ((rr = Communicator::waitAny(requests)) != UInt(-1)) {
auto & request = requests[rr];
auto sender = request.getSource();
AKANTU_DEBUG_ASSERT(this->master_receive_entities.find(sender) !=
this->master_receive_entities.end() &&
buffers.find(sender) != buffers.end(),
"Missing infos concerning proc " << sender);
const auto & receive_entities =
this->master_receive_entities.find(sender)->second;
auto & buffer = buffers[sender];
for (auto global_entity : receive_entities) {
Vector<T> entity_data = data_gathered_it[global_entity];
buffer >> entity_data;
}
requests.erase(requests.begin() + rr);
}
}
/* -------------------------------------------------------------------------- */
template <class Entity>
template <typename T>
void SynchronizerImpl<Entity>::gather(const Array<T> & to_gather) {
AKANTU_DEBUG_IN();
if (this->hasChanged()) {
initScatterGatherCommunicationScheme();
}
AKANTU_DEBUG_ASSERT(this->rank != UInt(this->root),
"This function cannot be called on the root processor");
AKANTU_DEBUG_ASSERT(to_gather.size() == this->canScatterSize(),
"The array to gather does not have the correct size");
if (this->entities_from_root.empty()) {
AKANTU_DEBUG_OUT();
return;
}
CommunicationBuffer buffer(this->entities_from_root.size() *
to_gather.getNbComponent() * sizeof(T));
auto data_it = to_gather.begin(to_gather.getNbComponent());
for (auto entity : this->entities_from_root) {
Vector<T> data = data_it[entity];
buffer << data;
}
AKANTU_DEBUG_INFO("Gathering data for "
<< to_gather.size() << " dofs on processor " << this->root
<< " "
<< Tag::genTag(this->rank, 0, Tag::_gather, this->hash_id));
communicator.send(buffer, this->root,
Tag::genTag(this->rank, 0, Tag::_gather, this->hash_id));
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class Entity>
template <typename T>
void SynchronizerImpl<Entity>::scatter(Array<T> & scattered,
const Array<T> & to_scatter) {
AKANTU_DEBUG_IN();
if (this->hasChanged()) {
initScatterGatherCommunicationScheme();
}
AKANTU_DEBUG_ASSERT(this->rank == UInt(this->root),
"This function cannot be called on a slave processor");
AKANTU_DEBUG_ASSERT(scattered.size() == this->canScatterSize(),
"The scattered array does not have the correct size");
AKANTU_DEBUG_ASSERT(to_scatter.size() == this->gatheredSize(),
"The array to scatter does not have the correct size");
if (this->nb_proc == 1) {
scattered.copy(to_scatter, true);
AKANTU_DEBUG_OUT();
return;
}
std::map<UInt, CommunicationBuffer> buffers;
std::vector<CommunicationRequest> requests;
for (UInt p = 0; p < nb_proc; ++p) {
auto data_to_scatter_it = to_scatter.begin(to_scatter.getNbComponent());
if (p == this->rank) {
auto data_scattered_it = scattered.begin(to_scatter.getNbComponent());
// copy the data for the local processor
for (auto local_entity : entities_from_root) {
auto global_entity = localToGlobalEntity(local_entity);
Vector<T> entity_data_to_scatter = data_to_scatter_it[global_entity];
Vector<T> entity_data_scattered = data_scattered_it[local_entity];
entity_data_scattered = entity_data_to_scatter;
}
continue;
}
const auto & receive_entities =
this->master_receive_entities.find(p)->second;
// prepare the send buffer
CommunicationBuffer & buffer = buffers[p];
buffer.resize(receive_entities.size() * scattered.getNbComponent() *
sizeof(T));
// pack the data
for (auto global_entity : receive_entities) {
Vector<T> entity_data_to_scatter = data_to_scatter_it[global_entity];
buffer << entity_data_to_scatter;
}
// send the data
requests.push_back(communicator.asyncSend(
buffer, p, Tag::genTag(p, 0, Tag::_scatter, this->hash_id)));
}
// wait a clean communications
Communicator::waitAll(requests);
Communicator::freeCommunicationRequest(requests);
// synchronize slave and ghost nodes
synchronizeArray(scattered);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class Entity>
template <typename T>
void SynchronizerImpl<Entity>::scatter(Array<T> & scattered) {
AKANTU_DEBUG_IN();
if (this->hasChanged()) {
this->initScatterGatherCommunicationScheme();
}
AKANTU_DEBUG_ASSERT(this->rank != UInt(this->root),
"This function cannot be called on the root processor");
AKANTU_DEBUG_ASSERT(scattered.size() == this->canScatterSize(),
"The scattered array does not have the correct size");
// prepare the data
auto data_scattered_it = scattered.begin(scattered.getNbComponent());
CommunicationBuffer buffer(this->entities_from_root.size() *
scattered.getNbComponent() * sizeof(T));
// receive the data
communicator.receive(
buffer, this->root,
Tag::genTag(this->rank, 0, Tag::_scatter, this->hash_id));
// unpack the data
for (auto local_entity : entities_from_root) {
Vector<T> data_scattered(data_scattered_it[local_entity]);
buffer >> data_scattered;
}
// synchronize the ghosts
synchronizeArray(scattered);
AKANTU_DEBUG_OUT();
}
/* -------------------------------------------------------------------------- */
template <class Entity>
template <typename T>
void SynchronizerImpl<Entity>::synchronizeArray(Array<T> & array) const {
static_assert(std::is_same<Entity, UInt>::value,
"Not implemented for other type than UInt");
SimpleUIntDataAccessor<T> data_accessor(array, SynchronizationTag::_whatever);
this->synchronizeOnce(data_accessor, SynchronizationTag::_whatever);
}
/* -------------------------------------------------------------------------- */
template <class Entity>
template <template <class> class Op, typename T>
void SynchronizerImpl<Entity>::reduceSynchronizeArray(Array<T> & array) const {
static_assert(std::is_same<Entity, UInt>::value,
"Not implemented for other type than UInt");
ReduceDataAccessor<UInt, Op, T> data_accessor(array,
SynchronizationTag::_whatever);
this->slaveReductionOnceImpl(data_accessor, SynchronizationTag::_whatever);
this->synchronizeArray(array);
}
/* -------------------------------------------------------------------------- */
} // namespace akantu
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index e9be809e4..ac29527d9 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -1,73 +1,72 @@
#===============================================================================
# @file CMakeLists.txt
#
# @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
# @author Alejandro M. Aragón <alejandro.aragon@epfl.ch>
# @author Nicolas Richart <nicolas.richart@epfl.ch>
#
# @date creation: Fri Sep 03 2010
# @date last modification: Mon Feb 12 2018
#
# @brief configuration for tests
#
# @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 <http://www.gnu.org/licenses/>.
#
# @section DESCRIPTION
#
#===============================================================================
include_directories(
${AKANTU_INCLUDE_DIRS}
${AKANTU_EXTERNAL_LIB_INCLUDE_DIR}
)
set(AKANTU_TESTS_FILES CACHE INTERNAL "")
#===============================================================================
# List of tests
#===============================================================================
add_akantu_test(test_common "Test the common part of Akantu")
-add_akantu_test(test_static_memory "Test static memory")
add_akantu_test(test_fe_engine "Test finite element functionalties")
add_akantu_test(test_mesh_utils "Test mesh utils")
add_akantu_test(test_mesh "Test mesh")
add_akantu_test(test_model "Test model objects")
add_akantu_test(test_solver "Test solver function")
add_akantu_test(test_io "Test the IO modules")
add_akantu_test(test_contact "Test the contact part of Akantu")
add_akantu_test(test_geometry "Test the geometry module of Akantu")
add_akantu_test(test_synchronizer "Test synchronizers")
add_akantu_test(test_python_interface "Test python interface")
package_add_files_to_package(
cmake/akantu_test_driver.sh
cmake/AkantuTestsMacros.cmake
)
package_is_activated(parallel _is_parallel)
if (_is_parallel)
option(AKANTU_TESTS_ALWAYS_USE_MPI "Defines if sequential tests should also use MPIEXEC" FALSE)
mark_as_advanced(AKANTU_TESTS_ALWAYS_USE_MPI)
endif()
package_is_activated(gbenchmark _has_gbenchmark)
if (_has_gbenchmark)
add_subdirectory(benchmark)
endif()
diff --git a/test/ci/codeclimate/codeclimate-clang-tidy/Dockerfile b/test/ci/codeclimate/codeclimate-clang-tidy/Dockerfile
new file mode 100644
index 000000000..90200e79c
--- /dev/null
+++ b/test/ci/codeclimate/codeclimate-clang-tidy/Dockerfile
@@ -0,0 +1,25 @@
+FROM alpine:edge
+LABEL maintainer "Nicolas Richart <nicolas.richart@epfl.ch>"
+
+WORKDIR /usr/src/app
+
+RUN apk --update add --no-cache --upgrade \
+ clang\
+ clang-extra-tools \
+ g++ \
+ musl-dev \
+ boost-dev \
+ python3 \
+ py3-termcolor && \
+ rm -rf /usr/share/ri && \
+ adduser -u 9000 -D -s /bin/false app
+
+#COPY engine.json /
+COPY . ./
+RUN chown -R app:app ./
+USER app
+
+VOLUME /code
+WORKDIR /code
+
+CMD ["/usr/src/app/bin/codeclimate-clang-tidy"]
diff --git a/test/ci/codeclimate/codeclimate-clang-tidy/bin/codeclimate-clang-tidy b/test/ci/codeclimate/codeclimate-clang-tidy/bin/codeclimate-clang-tidy
new file mode 100755
index 000000000..598027114
--- /dev/null
+++ b/test/ci/codeclimate/codeclimate-clang-tidy/bin/codeclimate-clang-tidy
@@ -0,0 +1,13 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+
+lib_path = os.path.abspath(os.path.join(__file__, '..', '..', 'lib'))
+sys.path.append(lib_path)
+
+from runner import Runner # noqa
+
+if __name__ == '__main__':
+ print(f"Arguments: {sys.argv}", file=sys.stderr)
+ Runner().run()
diff --git a/test/ci/codeclimate/codeclimate-clang-tidy/bin/run-clang-tidy b/test/ci/codeclimate/codeclimate-clang-tidy/bin/run-clang-tidy
new file mode 100755
index 000000000..0dbac0b25
--- /dev/null
+++ b/test/ci/codeclimate/codeclimate-clang-tidy/bin/run-clang-tidy
@@ -0,0 +1,337 @@
+#!/usr/bin/env python3
+#
+#===- run-clang-tidy.py - Parallel clang-tidy runner --------*- python -*--===#
+#
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#
+#===-----------------------------------------------------------------------===#
+# FIXME: Integrate with clang-tidy-diff.py
+
+
+"""
+Parallel clang-tidy runner
+==========================
+
+Runs clang-tidy over all files in a compilation database. Requires clang-tidy
+and clang-apply-replacements in $PATH.
+
+Example invocations.
+- Run clang-tidy on all files in the current working directory with a default
+ set of checks and show warnings in the cpp files and all project headers.
+ run-clang-tidy.py $PWD
+
+- Fix all header guards.
+ run-clang-tidy.py -fix -checks=-*,llvm-header-guard
+
+- Fix all header guards included from clang-tidy and header guards
+ for clang-tidy headers.
+ run-clang-tidy.py -fix -checks=-*,llvm-header-guard extra/clang-tidy \
+ -header-filter=extra/clang-tidy
+
+Compilation database setup:
+http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
+"""
+
+from __future__ import print_function
+
+import argparse
+import glob
+import json
+import multiprocessing
+import os
+import re
+import shutil
+import subprocess
+import sys
+import tempfile
+import threading
+import traceback
+
+try:
+ import yaml
+except ImportError:
+ yaml = None
+
+is_py2 = sys.version[0] == '2'
+
+if is_py2:
+ import Queue as queue
+else:
+ import queue as queue
+
+
+def find_compilation_database(path):
+ """Adjusts the directory until a compilation database is found."""
+ result = './'
+ while not os.path.isfile(os.path.join(result, path)):
+ if os.path.realpath(result) == '/':
+ print('Error: could not find compilation database.')
+ sys.exit(1)
+ result += '../'
+ return os.path.realpath(result)
+
+
+def make_absolute(f, directory):
+ if os.path.isabs(f):
+ return f
+ return os.path.normpath(os.path.join(directory, f))
+
+
+def get_tidy_invocation(f, clang_tidy_binary, checks, tmpdir, build_path,
+ header_filter, allow_enabling_alpha_checkers,
+ extra_arg, extra_arg_before, quiet, config):
+ """Gets a command line for clang-tidy."""
+ start = [clang_tidy_binary]
+ if allow_enabling_alpha_checkers:
+ start.append('-allow-enabling-analyzer-alpha-checkers')
+ if header_filter is not None:
+ start.append('-header-filter=' + header_filter)
+ if checks:
+ start.append('-checks=' + checks)
+ if tmpdir is not None:
+ start.append('-export-fixes')
+ # Get a temporary file. We immediately close the handle so clang-tidy can
+ # overwrite it.
+ (handle, name) = tempfile.mkstemp(suffix='.yaml', dir=tmpdir)
+ os.close(handle)
+ start.append(name)
+ for arg in extra_arg:
+ start.append('-extra-arg=%s' % arg)
+ for arg in extra_arg_before:
+ start.append('-extra-arg-before=%s' % arg)
+ start.append('-p=' + build_path)
+ if quiet:
+ start.append('-quiet')
+ if config:
+ start.append('-config=' + config)
+ start.append(f)
+ return start
+
+
+def merge_replacement_files(tmpdir, mergefile):
+ """Merge all replacement files in a directory into a single file"""
+ # The fixes suggested by clang-tidy >= 4.0.0 are given under
+ # the top level key 'Diagnostics' in the output yaml files
+ mergekey = "Diagnostics"
+ merged=[]
+ for replacefile in glob.iglob(os.path.join(tmpdir, '*.yaml')):
+ content = yaml.safe_load(open(replacefile, 'r'))
+ if not content:
+ continue # Skip empty files.
+ merged.extend(content.get(mergekey, []))
+
+ if merged:
+ # MainSourceFile: The key is required by the definition inside
+ # include/clang/Tooling/ReplacementsYaml.h, but the value
+ # is actually never used inside clang-apply-replacements,
+ # so we set it to '' here.
+ output = {'MainSourceFile': '', mergekey: merged}
+ with open(mergefile, 'w') as out:
+ yaml.safe_dump(output, out)
+ else:
+ # Empty the file:
+ open(mergefile, 'w').close()
+
+
+def check_clang_apply_replacements_binary(args):
+ """Checks if invoking supplied clang-apply-replacements binary works."""
+ try:
+ subprocess.check_call([args.clang_apply_replacements_binary, '--version'])
+ except:
+ print('Unable to run clang-apply-replacements. Is clang-apply-replacements '
+ 'binary correctly specified?', file=sys.stderr)
+ traceback.print_exc()
+ sys.exit(1)
+
+
+def apply_fixes(args, tmpdir):
+ """Calls clang-apply-fixes on a given directory."""
+ invocation = [args.clang_apply_replacements_binary]
+ if args.format:
+ invocation.append('-format')
+ if args.style:
+ invocation.append('-style=' + args.style)
+ invocation.append(tmpdir)
+ subprocess.call(invocation)
+
+
+def run_tidy(args, tmpdir, build_path, queue, lock, failed_files):
+ """Takes filenames out of queue and runs clang-tidy on them."""
+ while True:
+ name = queue.get()
+ invocation = get_tidy_invocation(name, args.clang_tidy_binary, args.checks,
+ tmpdir, build_path, args.header_filter,
+ args.allow_enabling_alpha_checkers,
+ args.extra_arg, args.extra_arg_before,
+ args.quiet, args.config)
+
+ proc = subprocess.Popen(invocation, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ output, err = proc.communicate()
+ if proc.returncode != 0:
+ failed_files.append(name)
+ with lock:
+ sys.stdout.write(' '.join(invocation) + '\n' + output.decode('utf-8'))
+ if len(err) > 0:
+ sys.stdout.flush()
+ sys.stderr.write(err.decode('utf-8'))
+ queue.task_done()
+
+
+def main():
+ parser = argparse.ArgumentParser(description='Runs clang-tidy over all files '
+ 'in a compilation database. Requires '
+ 'clang-tidy and clang-apply-replacements in '
+ '$PATH.')
+ parser.add_argument('-allow-enabling-alpha-checkers',
+ action='store_true', help='allow alpha checkers from '
+ 'clang-analyzer.')
+ parser.add_argument('-clang-tidy-binary', metavar='PATH',
+ default='clang-tidy-11',
+ help='path to clang-tidy binary')
+ parser.add_argument('-clang-apply-replacements-binary', metavar='PATH',
+ default='clang-apply-replacements-11',
+ help='path to clang-apply-replacements binary')
+ parser.add_argument('-checks', default=None,
+ help='checks filter, when not specified, use clang-tidy '
+ 'default')
+ parser.add_argument('-config', default=None,
+ help='Specifies a configuration in YAML/JSON format: '
+ ' -config="{Checks: \'*\', '
+ ' CheckOptions: [{key: x, '
+ ' value: y}]}" '
+ 'When the value is empty, clang-tidy will '
+ 'attempt to find a file named .clang-tidy for '
+ 'each source file in its parent directories.')
+ parser.add_argument('-header-filter', default=None,
+ help='regular expression matching the names of the '
+ 'headers to output diagnostics from. Diagnostics from '
+ 'the main file of each translation unit are always '
+ 'displayed.')
+ if yaml:
+ parser.add_argument('-export-fixes', metavar='filename', dest='export_fixes',
+ help='Create a yaml file to store suggested fixes in, '
+ 'which can be applied with clang-apply-replacements.')
+ parser.add_argument('-j', type=int, default=0,
+ help='number of tidy instances to be run in parallel.')
+ parser.add_argument('files', nargs='*', default=['.*'],
+ help='files to be processed (regex on path)')
+ parser.add_argument('-fix', action='store_true', help='apply fix-its')
+ parser.add_argument('-format', action='store_true', help='Reformat code '
+ 'after applying fixes')
+ parser.add_argument('-style', default='file', help='The style of reformat '
+ 'code after applying fixes')
+ parser.add_argument('-p', dest='build_path',
+ help='Path used to read a compile command database.')
+ parser.add_argument('-extra-arg', dest='extra_arg',
+ action='append', default=[],
+ help='Additional argument to append to the compiler '
+ 'command line.')
+ parser.add_argument('-extra-arg-before', dest='extra_arg_before',
+ action='append', default=[],
+ help='Additional argument to prepend to the compiler '
+ 'command line.')
+ parser.add_argument('-quiet', action='store_true',
+ help='Run clang-tidy in quiet mode')
+ args = parser.parse_args()
+
+ db_path = 'compile_commands.json'
+
+ if args.build_path is not None:
+ build_path = args.build_path
+ else:
+ # Find our database
+ build_path = find_compilation_database(db_path)
+
+ try:
+ invocation = [args.clang_tidy_binary, '-list-checks']
+ if args.allow_enabling_alpha_checkers:
+ invocation.append('-allow-enabling-analyzer-alpha-checkers')
+ invocation.append('-p=' + build_path)
+ if args.checks:
+ invocation.append('-checks=' + args.checks)
+ invocation.append('-')
+ if args.quiet:
+ # Even with -quiet we still want to check if we can call clang-tidy.
+ with open(os.devnull, 'w') as dev_null:
+ subprocess.check_call(invocation, stdout=dev_null)
+ else:
+ subprocess.check_call(invocation)
+ except:
+ print("Unable to run clang-tidy.", file=sys.stderr)
+ sys.exit(1)
+
+ # Load the database and extract all files.
+ database = json.load(open(os.path.join(build_path, db_path)))
+ files = [make_absolute(entry['file'], entry['directory'])
+ for entry in database]
+
+ max_task = args.j
+ if max_task == 0:
+ max_task = multiprocessing.cpu_count()
+
+ tmpdir = None
+ if args.fix or (yaml and args.export_fixes):
+ check_clang_apply_replacements_binary(args)
+ tmpdir = tempfile.mkdtemp()
+
+ # Build up a big regexy filter from all command line arguments.
+ file_name_re = re.compile('|'.join(args.files))
+
+ return_code = 0
+ try:
+ # Spin up a bunch of tidy-launching threads.
+ task_queue = queue.Queue(max_task)
+ # List of files with a non-zero return code.
+ failed_files = []
+ lock = threading.Lock()
+ for _ in range(max_task):
+ t = threading.Thread(target=run_tidy,
+ args=(args, tmpdir, build_path, task_queue, lock, failed_files))
+ t.daemon = True
+ t.start()
+
+ # Fill the queue with files.
+ for name in files:
+ if file_name_re.search(name):
+ task_queue.put(name)
+
+ # Wait for all threads to be done.
+ task_queue.join()
+ if len(failed_files):
+ return_code = 1
+
+ except KeyboardInterrupt:
+ # This is a sad hack. Unfortunately subprocess goes
+ # bonkers with ctrl-c and we start forking merrily.
+ print('\nCtrl-C detected, goodbye.')
+ if tmpdir:
+ shutil.rmtree(tmpdir)
+ os.kill(0, 9)
+
+ if yaml and args.export_fixes:
+ print('Writing fixes to ' + args.export_fixes + ' ...')
+ try:
+ merge_replacement_files(tmpdir, args.export_fixes)
+ except:
+ print('Error exporting fixes.\n', file=sys.stderr)
+ traceback.print_exc()
+ return_code=1
+
+ if args.fix:
+ print('Applying fixes ...')
+ try:
+ apply_fixes(args, tmpdir)
+ except:
+ print('Error applying fixes.\n', file=sys.stderr)
+ traceback.print_exc()
+ return_code = 1
+
+ if tmpdir:
+ shutil.rmtree(tmpdir)
+ sys.exit(return_code)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/test/ci/codeclimate/codeclimate-clang-tidy/engine.json b/test/ci/codeclimate/codeclimate-clang-tidy/engine.json
new file mode 100644
index 000000000..0e520cc6f
--- /dev/null
+++ b/test/ci/codeclimate/codeclimate-clang-tidy/engine.json
@@ -0,0 +1,11 @@
+{
+ "name": "codeclimate-clang-tidy",
+ "description": "clang-tidy is a static analysis tool for C/C++ code.",
+ "maintainer": {
+ "name": "Nicolas Richart",
+ "email": "nicolas.richart@epfl.ch"
+ },
+ "languages" : ["C", "C++"],
+ "version": "0.0.1",
+ "spec_version": "0.2.0"
+}
diff --git a/test/ci/codeclimate/codeclimate-clang-tidy/lib/command.py b/test/ci/codeclimate/codeclimate-clang-tidy/lib/command.py
new file mode 100644
index 000000000..ad8c6de0e
--- /dev/null
+++ b/test/ci/codeclimate/codeclimate-clang-tidy/lib/command.py
@@ -0,0 +1,74 @@
+import json
+import os
+import re
+
+
+class Command:
+ """Returns command line arguments by parsing codeclimate config file."""
+ def __init__(self, config, workspace):
+ self.config = config
+ self._workspace = workspace
+
+ def build(self):
+ command = ['/usr/src/app/bin/run-clang-tidy',
+ '-clang-tidy-binary',
+ '/usr/bin/clang-tidy']
+
+ if 'checks' in self.config:
+ checks = self.config["checks"]
+ if not isinstance(checks, list):
+ command.extend(['-checks', f'\'{checks}\''])
+ else:
+ command.extend(['-checks', f'\'{",".join(checks)}\''])
+
+ if 'config' in self.config:
+ command.extend(['-config', self.config["config"]])
+
+ if 'header-filter' in self.config:
+ command.extend(['-header-filter', self.config["header-file"]])
+
+ extra_args = []
+ if 'extra-arg' in self.config:
+ tmp_extra_args = self.config['extra-arg']
+ if not isinstance(extra_args, list):
+ tmp_extra_args = [extra_args]
+
+ extra_args = []
+ includes_re = re.compile(r'-I(.*)')
+ for arg in tmp_extra_args:
+ match = includes_re.match(arg)
+ if match:
+ path = os.path.abspath(match.group(1))
+ extra_args.append(f'-I{path}')
+ else:
+ extra_args.append(arg)
+
+ if 'compilation-database-path' in self.config:
+ for arg in extra_args:
+ command.extend(['-extra-arg', arg])
+
+ if 'compilation-database-path' in self.config:
+ command.extend(['-p', self.config['compilation-database-path']])
+ else:
+ include_flags = ' -I'.join(self._workspace.include_paths)
+
+ compile_commands = []
+ for file_ in self._workspace.files:
+ cmd = {
+ 'directory': os.path.dirname(file_),
+ 'file': file_,
+ 'command': f'/usr/bin/clang++ {include_flags} {" ".join(extra_args)} -c {file_} -o dummy.o', # noqa
+ }
+ compile_commands.append(cmd)
+
+ location = '/tmp'
+ compile_database = os.path.join(location, 'compile_commands.json')
+ with open(compile_database, 'w') as db:
+ json.dump(compile_commands, db)
+
+ command.extend(['-p', location])
+
+ command.extend([f'{path}.*' if os.path.isdir(path) else path
+ for path in self._workspace.paths])
+
+ return command
diff --git a/test/ci/codeclimate/codeclimate-clang-tidy/lib/issue_formatter.py b/test/ci/codeclimate/codeclimate-clang-tidy/lib/issue_formatter.py
new file mode 100644
index 000000000..4b0ec3308
--- /dev/null
+++ b/test/ci/codeclimate/codeclimate-clang-tidy/lib/issue_formatter.py
@@ -0,0 +1,92 @@
+import hashlib
+import os
+
+
+class IssueFormatter:
+ CLASSIFICATIONS = {
+ 'bugprone': {
+ 'categories': ['Bug Risk'],
+ 'severity': 'major',
+ },
+ 'modernize': {
+ 'categories': ['Clarity', 'Compatibility', 'Style'],
+ 'severity': 'info'
+ },
+ 'mpi': {
+ 'categories': ['Bug Risk', 'Performance'],
+ 'severity': 'critical',
+ },
+ 'openmp': {
+ 'categories': ['Bug Risk', 'Performance'],
+ 'severity': 'critical',
+ },
+ 'performance': {
+ 'categories': ['Performance'],
+ 'severity': 'minor',
+ },
+ 'readability': {
+ 'categories': ['Clarity', 'Style'],
+ 'severity': 'info'
+ },
+ }
+
+ def _get_classifiaction(self, type_):
+ categories = ['Bug Risk']
+ severity = 'blocker'
+
+ if type_ in self.CLASSIFICATIONS:
+ categories = self.CLASSIFICATIONS[type_]['categories']
+ severity = self.CLASSIFICATIONS[type_]['severity']
+ elif type_[0] == 'clang':
+ if type_[1] == 'diagnostic':
+ categories = ['Bug Risk']
+ severity = 'blocker'
+ elif type_[1] == 'analyzer':
+ categories = ['Bug Risk']
+ severity = 'major'
+
+ return (categories, severity)
+
+ def __init__(self, issue):
+ self.issue_dict = issue
+
+ def format(self):
+ self.issue_dict['file'] = os.path.relpath(self.issue_dict['file'])
+ issue = {
+ 'type': 'issue',
+ 'check_name': self.issue_dict['type'],
+ 'description': self.issue_dict['detail'],
+ 'location': {
+ "path": self.issue_dict['file'],
+ "lines": {
+ "begin": int(self.issue_dict['line']),
+ "end": int(self.issue_dict['line']),
+ },
+ "positions": {
+ "begin": {
+ "line": int(self.issue_dict['line']),
+ "column": int(self.issue_dict['column']),
+ },
+ "end": {
+ "line": int(self.issue_dict['line']),
+ "column": int(self.issue_dict['column']),
+ },
+ },
+ },
+ }
+
+ if 'content' in self.issue_dict:
+ issue['content'] = {
+ 'body': '```\n' +
+ '\n'.join(self.issue_dict['content']) +
+ '\n```'
+ }
+
+ issue['fingerprint'] = hashlib.md5(
+ '{file}:{line}:{column}:{type}'.format(**self.issue_dict).encode()
+ ).hexdigest()
+
+ type_ = self.issue_dict['type'].split('-')[0]
+ issue['categories'], issue['severity'] = self._get_classifiaction(type_)
+
+ return issue
diff --git a/test/ci/codeclimate/codeclimate-clang-tidy/lib/runner.py b/test/ci/codeclimate/codeclimate-clang-tidy/lib/runner.py
new file mode 100644
index 000000000..eed340983
--- /dev/null
+++ b/test/ci/codeclimate/codeclimate-clang-tidy/lib/runner.py
@@ -0,0 +1,102 @@
+import json
+import subprocess
+import sys
+import re
+import os
+try:
+ from termcolor import colored
+except ImportError:
+ def colored(text, color):
+ return text
+
+from command import Command
+from issue_formatter import IssueFormatter
+from workspace import Workspace
+
+
+class Runner:
+ CONFIG_FILE_PATH = '/config.json'
+
+ """Runs clang-tidy, collects and reports results."""
+ def __init__(self):
+ self._config_file_path = self.CONFIG_FILE_PATH
+ self._config = {}
+ self._decode_config()
+ self._ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
+ self._issue_parse = re.compile(r'(?P<file>.*\.(cc|hh)):(?P<line>[0-9]+):(?P<column>[0-9]+): (warning|error): (?P<detail>.*) \[(?P<type>.*)\]') # noqa
+
+ self._issues_fpr = []
+
+ self._workspace = Workspace(self._config.get('include_paths', []))
+ self._files = self._workspace.files
+ self._include_paths = self._workspace.include_paths
+
+ def run(self):
+ if not len(self._files) > 0:
+ return
+
+ self._print_debug(f'[clang-tidy] analyzing {len(self._files)} files')
+ command = Command(self._config, self._workspace).build()
+
+ self._print_debug(f'[clang-tidy] command: {command}')
+
+ self._generate_issues(command)
+
+ def _decode_config(self):
+ self._print_debug(f"Decoding config file {self._config_file_path}")
+
+ contents = ""
+ with open(self._config_file_path, "r") as config:
+ contents = config.read()
+
+ self._config = json.loads(contents)
+ self._print_debug(f'[clang-tidy] config: {self._config}')
+
+ def _print_issue(self, issue):
+ issue_ = IssueFormatter(issue).format()
+
+ path = os.path.dirname(os.path.abspath(issue_["location"]["path"]))
+ if path not in self._include_paths:
+ return
+
+ if issue_['fingerprint'] in self._issues_fpr:
+ return
+
+ self._issues_fpr.append(issue_['fingerprint'])
+ print('{}\0'.format(json.dumps(issue_)))
+
+ def _generate_issues(self, command):
+ issue = None
+ for line in self._run_command(command):
+ clean_line = self._ansi_escape.sub('', line)
+ match = self._issue_parse.match(clean_line)
+ if match:
+ if issue is not None:
+ self._print_issue(issue)
+ issue = match.groupdict()
+ elif issue:
+ if 'content' in issue:
+ issue['content'].append(line)
+ else:
+ issue['content'] = [line]
+ self._print_issue(issue)
+
+ def _run_command(self, command):
+ popen = subprocess.Popen(command,
+ stdout=subprocess.PIPE,
+ universal_newlines=True)
+
+ for stdout_line in iter(popen.stdout.readline, ""):
+ self._print_debug(stdout_line)
+ yield stdout_line
+
+ popen.stdout.close()
+
+ return_code = popen.wait()
+ if return_code:
+ self._print_debug(
+ f"[clang-tidy] {command} ReturnCode {return_code}")
+ # raise subprocess.CalledProcessError(return_code, command)
+
+ def _print_debug(self, message):
+ print(message, file=sys.stderr, flush=True)
diff --git a/test/ci/codeclimate/codeclimate-clang-tidy/lib/workspace.py b/test/ci/codeclimate/codeclimate-clang-tidy/lib/workspace.py
new file mode 100644
index 000000000..29685c3db
--- /dev/null
+++ b/test/ci/codeclimate/codeclimate-clang-tidy/lib/workspace.py
@@ -0,0 +1,40 @@
+import os
+
+
+class Workspace:
+ def __init__(self, include_paths, suffixes=['.c', '.cpp', '.cc', '.cxx']):
+ self._include_paths = include_paths
+ self._suffixes = suffixes
+
+ @property
+ def files(self):
+ paths = self._walk()
+ return [path for path in paths
+ if self._should_include(path)]
+
+ @property
+ def include_paths(self):
+ paths = self._walk()
+ return [path for path in paths if os.path.isdir(path)]
+
+ @property
+ def paths(self):
+ return [path for path in self._include_paths
+ if self._should_include(path) or os.path.isdir(path)]
+
+ def _should_include(self, name):
+ _, ext = os.path.splitext(name)
+ return ext in self._suffixes
+
+ def _walk(self):
+ paths = []
+
+ for path in self._include_paths:
+ if os.path.isdir(path):
+ for root, dirs, files in os.walk(path):
+ paths.extend([os.path.join(root, dir_) for dir_ in dirs])
+ paths.extend([os.path.join(root, file_) for file_ in files])
+ else:
+ paths.append(path)
+
+ return [os.path.abspath(path) for path in paths]
diff --git a/test/ci/debian:testing/Dockerfile b/test/ci/debian:testing/Dockerfile
index ba3923f1a..c0b2da0b0 100644
--- a/test/ci/debian:testing/Dockerfile
+++ b/test/ci/debian:testing/Dockerfile
@@ -1,30 +1,34 @@
FROM debian:testing
MAINTAINER Nicolas Richart <nicolas.richart@epfl.ch>
# library dependencies
RUN apt-get -qq update && apt-get -qq -y install \
g++ gfortran clang cmake \
openmpi-bin libmumps-dev libscotch-dev \
libboost-dev libopenblas-dev \
python3 python3-dev python3-numpy python3-scipy python3-mpi4py \
&& rm -rf /var/lib/apt/lists/*
# for documentation
RUN apt-get -qq update && apt-get -qq -y install \
- python3-sphinx python3-sphinx-rtd-theme python3-breathe doxygen graphviz \
+ python3-sphinx \
+ python3-sphinxcontrib.bibtex \
+ python3-sphinx-rtd-theme \
+ python3-breathe \
+ doxygen graphviz \
&& rm -rf /var/lib/apt/lists/*
# for ci
RUN apt-get -qq update && apt-get -qq -y install \
gmsh python3-pytest \
ccache clang-format python3-flake8 clang-tidy \
curl git xsltproc jq \
- gcovr binutils \
+ gcovr llvm binutils \
&& rm -rf /var/lib/apt/lists/*
COPY .openmpi /root/.openmpi
# for debug
RUN apt-get -qq update && apt-get -qq -y install \
gdb valgrind \
&& rm -rf /var/lib/apt/lists/*
diff --git a/test/ci/includes_for_ci/aka_config.hh b/test/ci/includes_for_ci/aka_config.hh
new file mode 100644
index 000000000..737661d83
--- /dev/null
+++ b/test/ci/includes_for_ci/aka_config.hh
@@ -0,0 +1,90 @@
+/**
+ * @file aka_config.hh.in
+ *
+ * @author Nicolas Richart <nicolas.richart@epfl.ch>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/* -------------------------------------------------------------------------- */
+#ifndef AKANTU_AKA_CONFIG_HH_
+#define AKANTU_AKA_CONFIG_HH_
+
+#define AKANTU_VERSION_MAJOR 4
+#define AKANTU_VERSION_MINOR 0
+#define AKANTU_VERSION_PATCH 0
+#define AKANTU_VERSION (AKANTU_VERSION_MAJOR * 10000 \
+ + AKANTU_VERSION_MINOR * 100 \
+ + AKANTU_VERSION_PATCH)
+
+
+namespace akantu {
+using Real = double;
+using Int = int;
+using UInt = unsigned int;
+} // akantu
+
+#define AKANTU_INTEGER_SIZE 4
+#define AKANTU_FLOAT_SIZE 8
+
+/* #undef AKANTU_HAS_STRDUP */
+
+/* #undef AKANTU_USE_BLAS */
+#define AKANTU_USE_LAPACK
+
+#define AKANTU_PARALLEL
+#define AKANTU_USE_MPI
+
+#define AKANTU_USE_SCOTCH
+/* #undef AKANTU_USE_PTSCOTCH */
+/* #undef AKANTU_SCOTCH_NO_EXTERN */
+
+#define AKANTU_IMPLICIT
+#define AKANTU_USE_MUMPS
+/* #undef AKANTU_USE_PETSC */
+
+#define AKANTU_USE_IOHELPER
+/* #undef AKANTU_USE_QVIEW */
+/* #undef AKANTU_USE_BLACKDYNAMITE */
+
+#define AKANTU_USE_PYBIND11
+
+/* #undef AKANTU_USE_OBSOLETE_GETTIMEOFDAY */
+
+/* #undef AKANTU_EXTRA_MATERIALS */
+/* #undef AKANTU_STUDENTS_EXTRA_PACKAGE */
+#define AKANTU_DAMAGE_NON_LOCAL
+
+#define AKANTU_SOLID_MECHANICS
+#define AKANTU_STRUCTURAL_MECHANICS
+#define AKANTU_HEAT_TRANSFER
+#define AKANTU_PYTHON_INTERFACE
+
+#define AKANTU_COHESIVE_ELEMENT
+/* #undef AKANTU_PARALLEL_COHESIVE_ELEMENT */
+
+/* #undef AKANTU_IGFEM */
+
+/* #undef AKANTU_USE_CGAL */
+/* #undef AKANTU_EMBEDDED */
+
+// Debug tools
+/* #undef AKANTU_NDEBUG */
+/* #undef AKANTU_DEBUG_TOOLS */
+#define READLINK_COMMAND /bin/readlink
+#define ADDR2LINE_COMMAND /usr/bin/addr2line
+
+#endif /* AKANTU_AKA_CONFIG_HH_ */
diff --git a/test/ci/includes_for_ci/aka_element_classes_info.hh b/test/ci/includes_for_ci/aka_element_classes_info.hh
new file mode 100644
index 000000000..b13600d44
--- /dev/null
+++ b/test/ci/includes_for_ci/aka_element_classes_info.hh
@@ -0,0 +1,373 @@
+/**
+ * @file aka_element_classes_info.hh.in
+ *
+ * @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
+ * @author Nicolas Richart <nicolas.richart@epfl.ch>
+ *
+ * @date creation: Sun Jul 19 2015
+ * @date last modification: Tue Feb 20 2018
+ *
+ * @brief Declaration of the enums for the element classes
+ *
+ *
+ * Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
+ *
+ */
+/* -------------------------------------------------------------------------- */
+#include "aka_safe_enum.hh"
+/* -------------------------------------------------------------------------- */
+#include <boost/preprocessor.hpp>
+/* -------------------------------------------------------------------------- */
+
+#ifndef AKANTU_AKA_ELEMENT_CLASSES_INFO_HH_
+#define AKANTU_AKA_ELEMENT_CLASSES_INFO_HH_
+
+namespace akantu {
+
+/* -------------------------------------------------------------------------- */
+/* Element Types */
+/* -------------------------------------------------------------------------- */
+
+/// @enum ElementType type of elements
+enum ElementType {
+ _not_defined,
+ _cohesive_1d_2,
+ _cohesive_2d_4,
+ _cohesive_2d_6,
+ _cohesive_3d_12,
+ _cohesive_3d_16,
+ _cohesive_3d_6,
+ _cohesive_3d_8,
+ _point_1,
+ _segment_2,
+ _segment_3,
+ _triangle_3,
+ _triangle_6,
+ _quadrangle_4,
+ _quadrangle_8,
+ _tetrahedron_4,
+ _tetrahedron_10,
+ _pentahedron_6,
+ _pentahedron_15,
+ _hexahedron_8,
+ _hexahedron_20,
+ _bernoulli_beam_2,
+ _bernoulli_beam_3,
+ _discrete_kirchhoff_triangle_18,
+ _max_element_type
+};
+
+
+#define AKANTU_ek_cohesive_ELEMENT_TYPE \
+ (_cohesive_1d_2) \
+ (_cohesive_2d_4) \
+ (_cohesive_2d_6) \
+ (_cohesive_3d_12) \
+ (_cohesive_3d_16) \
+ (_cohesive_3d_6) \
+ (_cohesive_3d_8)
+
+#define AKANTU_ek_regular_ELEMENT_TYPE \
+ (_point_1) \
+ (_segment_2) \
+ (_segment_3) \
+ (_triangle_3) \
+ (_triangle_6) \
+ (_quadrangle_4) \
+ (_quadrangle_8) \
+ (_tetrahedron_4) \
+ (_tetrahedron_10) \
+ (_pentahedron_6) \
+ (_pentahedron_15) \
+ (_hexahedron_8) \
+ (_hexahedron_20)
+
+#define AKANTU_ek_structural_ELEMENT_TYPE \
+ (_bernoulli_beam_2) \
+ (_bernoulli_beam_3) \
+ (_discrete_kirchhoff_triangle_18)
+
+
+#define AKANTU_ALL_ELEMENT_TYPE \
+ AKANTU_ek_cohesive_ELEMENT_TYPE \
+ AKANTU_ek_regular_ELEMENT_TYPE \
+ AKANTU_ek_structural_ELEMENT_TYPE
+
+/* -------------------------------------------------------------------------- */
+/* Element Kinds */
+/* -------------------------------------------------------------------------- */
+
+#define AKANTU_COHESIVE_KIND (_ek_cohesive)
+#define AKANTU_REGULAR_KIND (_ek_regular)
+#define AKANTU_STRUCTURAL_KIND (_ek_structural)
+
+#define AKANTU_ELEMENT_KIND \
+ AKANTU_COHESIVE_KIND \
+ AKANTU_REGULAR_KIND \
+ AKANTU_STRUCTURAL_KIND
+
+enum ElementKind {
+ BOOST_PP_SEQ_ENUM(AKANTU_ELEMENT_KIND),
+ _ek_not_defined
+};
+
+
+/* -------------------------------------------------------------------------- */
+struct ElementKind_def {
+ using type = ElementKind;
+ static const type _begin_ = BOOST_PP_SEQ_HEAD(AKANTU_ELEMENT_KIND);
+ static const type _end_ = _ek_not_defined;
+};
+
+using element_kind_t = safe_enum<ElementKind_def> ;
+
+/* -------------------------------------------------------------------------- */
+/// @enum GeometricalType type of element potentially contained in a Mesh
+enum GeometricalType {
+ _gt_cohesive_1d_2,
+ _gt_cohesive_2d_4,
+ _gt_cohesive_2d_6,
+ _gt_cohesive_3d_12,
+ _gt_cohesive_3d_16,
+ _gt_cohesive_3d_6,
+ _gt_cohesive_3d_8,
+ _gt_point,
+ _gt_segment_2,
+ _gt_segment_3,
+ _gt_triangle_3,
+ _gt_triangle_6,
+ _gt_quadrangle_4,
+ _gt_quadrangle_8,
+ _gt_tetrahedron_4,
+ _gt_tetrahedron_10,
+ _gt_hexahedron_8,
+ _gt_hexahedron_20,
+ _gt_pentahedron_6,
+ _gt_pentahedron_15,
+ _gt_not_defined
+};
+
+/* -------------------------------------------------------------------------- */
+/* Interpolation Types */
+/* -------------------------------------------------------------------------- */
+#define AKANTU_INTERPOLATION_TYPES \
+ (_itp_lagrange_point_1) \
+ (_itp_lagrange_segment_2) \
+ (_itp_lagrange_segment_3) \
+ (_itp_lagrange_triangle_3) \
+ (_itp_lagrange_triangle_6) \
+ (_itp_lagrange_quadrangle_4) \
+ (_itp_serendip_quadrangle_8) \
+ (_itp_lagrange_tetrahedron_4) \
+ (_itp_lagrange_tetrahedron_10) \
+ (_itp_lagrange_hexahedron_8) \
+ (_itp_serendip_hexahedron_20) \
+ (_itp_lagrange_pentahedron_6) \
+ (_itp_lagrange_pentahedron_15)\
+ (_itp_hermite_2) \
+ (_itp_bernoulli_beam_2) \
+ (_itp_bernoulli_beam_3) \
+ (_itp_discrete_kirchhoff_triangle_6) \
+ (_itp_discrete_kirchhoff_triangle_18)
+
+/// @enum InterpolationType type of elements
+enum InterpolationType {
+ BOOST_PP_SEQ_ENUM(AKANTU_INTERPOLATION_TYPES),
+ _itp_not_defined
+};
+
+/* -------------------------------------------------------------------------- */
+/* Some sub types less probable to change */
+/* -------------------------------------------------------------------------- */
+/// @enum GeometricalShapeType types of shapes to define the contains
+/// function in the element classes
+enum GeometricalShapeType {
+ _gst_point,
+ _gst_triangle,
+ _gst_square,
+ _gst_prism,
+ _gst_not_defined
+};
+
+/* -------------------------------------------------------------------------- */
+/// @enum GaussIntegrationType classes of types using common
+/// description of the gauss point position and weights
+enum GaussIntegrationType {
+ _git_point,
+ _git_segment,
+ _git_triangle,
+ _git_tetrahedron,
+ _git_pentahedron,
+ _git_not_defined
+};
+
+/* -------------------------------------------------------------------------- */
+/// @enum InterpolationKind the family of interpolation types
+enum InterpolationKind {
+ _itk_lagrangian,
+ _itk_structural,
+ _itk_not_defined
+};
+
+/* -------------------------------------------------------------------------- */
+// BOOST PART: TOUCH ONLY IF YOU KNOW WHAT YOU ARE DOING
+#define AKANTU_BOOST_CASE_MACRO(r, macro, _type) \
+ case _type: { \
+ macro(_type); \
+ break; \
+ }
+
+#define AKANTU_BOOST_LIST_SWITCH(macro1, list1, var) \
+ do { \
+ switch (var) { \
+ BOOST_PP_SEQ_FOR_EACH(AKANTU_BOOST_CASE_MACRO, macro1, list1) \
+ default: { \
+ AKANTU_ERROR("Type (" \
+ << var /* NOLINT */ << ") not handled by this function"); \
+ } \
+ } \
+ } while (0)
+
+#define AKANTU_BOOST_LIST_SWITCH_NO_DEFAULT(macro1, list1, var) \
+ do { \
+ switch (var) { \
+ BOOST_PP_SEQ_FOR_EACH(AKANTU_BOOST_CASE_MACRO, macro1, list1) \
+ case _not_defined: /* FALLTHRU */ \
+ case _max_element_type: \
+ break; \
+ } \
+ } while (0)
+
+#define AKANTU_BOOST_ELEMENT_SWITCH(macro1, list1) \
+ AKANTU_BOOST_LIST_SWITCH(macro1, list1, type)
+
+#define AKANTU_BOOST_ELEMENT_SWITCH_NO_DEFAULT(macro1, list1) \
+ AKANTU_BOOST_LIST_SWITCH_NO_DEFAULT(macro1, list1, type)
+
+#define AKANTU_BOOST_ALL_ELEMENT_SWITCH(macro) \
+ AKANTU_BOOST_ELEMENT_SWITCH(macro, AKANTU_ALL_ELEMENT_TYPE)
+
+#define AKANTU_BOOST_ALL_ELEMENT_SWITCH_NO_DEFAULT(macro) \
+ AKANTU_BOOST_ELEMENT_SWITCH_NO_DEFAULT(macro, AKANTU_ALL_ELEMENT_TYPE)
+
+#define AKANTU_BOOST_LIST_MACRO(r, macro, type) macro(type)
+
+#define AKANTU_BOOST_APPLY_ON_LIST(macro, list) \
+ BOOST_PP_SEQ_FOR_EACH(AKANTU_BOOST_LIST_MACRO, macro, list)
+
+#define AKANTU_BOOST_ALL_ELEMENT_LIST(macro) \
+ AKANTU_BOOST_APPLY_ON_LIST(macro, AKANTU_ALL_ELEMENT_TYPE)
+
+#define AKANTU_GET_ELEMENT_LIST(kind) AKANTU##kind##_ELEMENT_TYPE
+
+#define AKANTU_BOOST_KIND_ELEMENT_SWITCH(macro, kind) \
+ AKANTU_BOOST_ELEMENT_SWITCH(macro, AKANTU_GET_ELEMENT_LIST(kind))
+
+// BOOST_PP_SEQ_TO_LIST does not exists in Boost < 1.49
+#define AKANTU_GENERATE_KIND_LIST(seq) \
+ BOOST_PP_TUPLE_TO_LIST(BOOST_PP_SEQ_SIZE(seq), BOOST_PP_SEQ_TO_TUPLE(seq))
+
+#define AKANTU_ELEMENT_KIND_BOOST_LIST \
+ AKANTU_GENERATE_KIND_LIST(AKANTU_ELEMENT_KIND)
+
+#define AKANTU_BOOST_ALL_KIND_LIST(macro, list) \
+ BOOST_PP_LIST_FOR_EACH(AKANTU_BOOST_LIST_MACRO, macro, list)
+
+#define AKANTU_BOOST_ALL_KIND(macro) \
+ AKANTU_BOOST_ALL_KIND_LIST(macro, AKANTU_ELEMENT_KIND_BOOST_LIST)
+
+#define AKANTU_BOOST_ALL_KIND_SWITCH(macro) \
+ AKANTU_BOOST_LIST_SWITCH(macro, AKANTU_ELEMENT_KIND, kind)
+
+
+#define AKANTU_BOOST_COHESIVE_ELEMENT_SWITCH(macro) \
+ AKANTU_BOOST_ELEMENT_SWITCH(macro, \
+ AKANTU_ek_cohesive_ELEMENT_TYPE)
+
+#define AKANTU_BOOST_COHESIVE_ELEMENT_LIST(macro) \
+ AKANTU_BOOST_APPLY_ON_LIST(macro, \
+ AKANTU_ek_cohesive_ELEMENT_TYPE)
+
+#define AKANTU_BOOST_REGULAR_ELEMENT_SWITCH(macro) \
+ AKANTU_BOOST_ELEMENT_SWITCH(macro, \
+ AKANTU_ek_regular_ELEMENT_TYPE)
+
+#define AKANTU_BOOST_REGULAR_ELEMENT_LIST(macro) \
+ AKANTU_BOOST_APPLY_ON_LIST(macro, \
+ AKANTU_ek_regular_ELEMENT_TYPE)
+
+#define AKANTU_BOOST_STRUCTURAL_ELEMENT_SWITCH(macro) \
+ AKANTU_BOOST_ELEMENT_SWITCH(macro, \
+ AKANTU_ek_structural_ELEMENT_TYPE)
+
+#define AKANTU_BOOST_STRUCTURAL_ELEMENT_LIST(macro) \
+ AKANTU_BOOST_APPLY_ON_LIST(macro, \
+ AKANTU_ek_structural_ELEMENT_TYPE)
+
+
+// /// define kept for compatibility reasons (they are most probably not needed
+// /// anymore) \todo check if they can be removed
+// #define AKANTU_REGULAR_ELEMENT_TYPE AKANTU_ek_regular_ELEMENT_TYPE
+// #define AKANTU_COHESIVE_ELEMENT_TYPE AKANTU_ek_cohesive_ELEMENT_TYPE
+// #define AKANTU_STRUCTURAL_ELEMENT_TYPE AKANTU_ek_structural_ELEMENT_TYPE
+// #define AKANTU_IGFEM_ELEMENT_TYPE AKANTU_ek_igfem_ELEMENT_TYPE
+
+/* -------------------------------------------------------------------------- */
+/* Lists of interests for FEEngineTemplate functions */
+/* -------------------------------------------------------------------------- */
+#define AKANTU_FE_ENGINE_LIST_ASSEMBLE_FIELDS \
+ AKANTU_GENERATE_KIND_LIST((_ek_regular) \
+ (_ek_structural))
+#define AKANTU_FE_ENGINE_LIST_COMPUTE_SHAPES_DERIVATIVES \
+ AKANTU_GENERATE_KIND_LIST((_ek_regular) \
+ (_ek_structural))
+#define AKANTU_FE_ENGINE_LIST_COMPUTE_SHAPES \
+ AKANTU_GENERATE_KIND_LIST((_ek_regular) \
+ (_ek_structural))
+#define AKANTU_FE_ENGINE_LIST_INTERPOLATE \
+ AKANTU_GENERATE_KIND_LIST((_ek_regular))
+#define AKANTU_FE_ENGINE_LIST_LAGRANGE_BASE \
+ AKANTU_GENERATE_KIND_LIST((_ek_cohesive) \
+ (_ek_regular))
+#define AKANTU_FE_ENGINE_LIST_INVERSE_MAP \
+ AKANTU_GENERATE_KIND_LIST((_ek_cohesive) \
+ (_ek_regular))
+#define AKANTU_FE_ENGINE_LIST_INTERPOLATE_ON_INTEGRATION_POINTS \
+ AKANTU_GENERATE_KIND_LIST((_ek_cohesive) \
+ (_ek_regular) \
+ (_ek_structural))
+#define AKANTU_FE_ENGINE_LIST_GRADIENT_ON_INTEGRATION_POINTS \
+ AKANTU_GENERATE_KIND_LIST((_ek_cohesive) \
+ (_ek_regular) \
+ (_ek_structural))
+#define AKANTU_FE_ENGINE_LIST_GET_SHAPES_DERIVATIVES \
+ AKANTU_GENERATE_KIND_LIST((_ek_cohesive) \
+ (_ek_regular) \
+ (_ek_structural))
+#define AKANTU_FE_ENGINE_LIST_CONTAINS \
+ AKANTU_GENERATE_KIND_LIST((_ek_cohesive) \
+ (_ek_regular))
+#define AKANTU_FE_ENGINE_LIST_COMPUTE_NORMALS_ON_INTEGRATION_POINTS \
+ AKANTU_GENERATE_KIND_LIST((_ek_cohesive) \
+ (_ek_regular))
+
+
+} // akantu
+
+#endif /* AKANTU_AKA_ELEMENT_CLASSES_INFO_HH_ */
+
+#include "aka_element_classes_info_inline_impl.hh"
diff --git a/test/ci/includes_for_ci/aka_fortran_mangling.hh b/test/ci/includes_for_ci/aka_fortran_mangling.hh
new file mode 100644
index 000000000..d27b675b2
--- /dev/null
+++ b/test/ci/includes_for_ci/aka_fortran_mangling.hh
@@ -0,0 +1,16 @@
+#ifndef AKA_FC_HEADER_INCLUDED
+#define AKA_FC_HEADER_INCLUDED
+
+/* Mangling for Fortran global symbols without underscores. */
+#define AKA_FC_GLOBAL(name,NAME) name##_
+
+/* Mangling for Fortran global symbols with underscores. */
+#define AKA_FC_GLOBAL_(name,NAME) name##_
+
+/* Mangling for Fortran module symbols without underscores. */
+#define AKA_FC_MODULE(mod_name,name, mod_NAME,NAME) __##mod_name##_MOD_##name
+
+/* Mangling for Fortran module symbols with underscores. */
+#define AKA_FC_MODULE_(mod_name,name, mod_NAME,NAME) __##mod_name##_MOD_##name
+
+#endif
diff --git a/test/ci/includes_for_ci/material_list.hh b/test/ci/includes_for_ci/material_list.hh
new file mode 100644
index 000000000..21440bce2
--- /dev/null
+++ b/test/ci/includes_for_ci/material_list.hh
@@ -0,0 +1,46 @@
+/**
+ * @file material_list.hh.in
+ *
+ * @author Nicolas Richart <nicolas.richart@epfl.ch>
+ *
+ * @date creation: Wed Sep 01 2010
+ * @date last modification: Wed Feb 03 2016
+ *
+ * @brief List of materials and all includes
+ *
+ *
+ * Copyright (©) 2014-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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/* -------------------------------------------------------------------------- */
+
+#ifndef __AKANTU_MATERIAL_LIST_HH__
+#define __AKANTU_MATERIAL_LIST_HH__
+
+#include "aka_config.hh"
+
+/* -------------------------------------------------------------------------- */
+/* Material includes */
+/* -------------------------------------------------------------------------- */
+#include "material_cohesive_includes.hh"
+#include "material_non_local_includes.hh"
+#include "material_core_includes.hh"
+
+/* -------------------------------------------------------------------------- */
+/* Material list */
+/* -------------------------------------------------------------------------- */
+#define AKANTU_MATERIAL_LIST \
+ AKANTU_COHESIVE_MATERIAL_LIST \
+ AKANTU_DAMAGE_NON_LOCAL_MATERIAL_LIST \
+ AKANTU_CORE_MATERIAL_LIST \
+
+// leave an empty line after the list
+
+#endif /* __AKANTU_MATERIAL_LIST_HH__ */
diff --git a/test/ci/manylinux/Dockerfile b/test/ci/manylinux/Dockerfile
index 8fdde3d81..ce6616f97 100644
--- a/test/ci/manylinux/Dockerfile
+++ b/test/ci/manylinux/Dockerfile
@@ -1,10 +1,16 @@
FROM quay.io/pypa/manylinux2010_x86_64
MAINTAINER Guillaume Anciaux <guillaume.anciaux@epfl.ch>
RUN git clone https://github.com/spack/spack.git
RUN yum install -y xz
RUN mkdir -p /root/.spack/linux/
COPY compilers.yaml /root/.spack/linux/
RUN spack/bin/spack install perl
-# RUN /spack/bin/spack install akantu@master external_solvers=mumps ~mpi ^mumps~mpi
+RUN /spack/bin/spack install boost
+RUN /spack/bin/spack install cmake
+RUN /spack/bin/spack install openblas
+RUN /spack/bin/spack install mumps@5.2.0 ~mpi
+RUN /spack/bin/spack install akantu@master external_solvers=mumps ~mpi ^mumps@5.2.0~mpi
+RUN yum install -y environment-modules
+RUN git clone https://gitlab.com/akantu/pypi_akantu.git
\ No newline at end of file
diff --git a/test/ci/scripts/clang-tidy2code-quality.py b/test/ci/scripts/clang-tidy2code-quality.py
new file mode 100644
index 000000000..1f5c5e615
--- /dev/null
+++ b/test/ci/scripts/clang-tidy2code-quality.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python3
+import re
+import os
+import hashlib
+import json
+import sys
+
+
+# 7-bit C1 ANSI sequences
+ansi_escape = re.compile(r'''
+ \x1B # ESC
+ (?: # 7-bit C1 Fe (except CSI)
+ [@-Z\\-_]
+ | # or [ for CSI, followed by a control sequence
+ \[
+ [0-?]* # Parameter bytes
+ [ -/]* # Intermediate bytes
+ [@-~] # Final byte
+ )
+''', re.VERBOSE)
+
+re_log_parse = re.compile(
+ r'(?P<file>.*\.(cc|hh)):(?P<line>[0-9]+):(?P<column>[0-9]+): warning: (?P<detail>.*) \[(?P<type>.*)\]' # noqa
+)
+
+
+categories = {
+ "bugprone": "Bug Risk",
+ "modernize": "Clarity",
+ "mpi": "Bug Risk",
+ "openmp": "Bug Risk",
+ "performance": "Performance",
+ "readability": "Clarity",
+}
+
+issues = {}
+
+with open('clang-tidy-all-out.log', 'r') as log:
+ for line in log:
+ clean_line = ansi_escape.sub('', line)
+ match = re_log_parse.match(clean_line)
+ if match:
+ line_dict = match.groupdict()
+ line_dict['file'] = os.path.relpath(line_dict['file'])
+
+ line_dict['fingerprint'] = hashlib.md5(
+ '{file}:{line}:{column}:{type}'.format(**line_dict).encode()
+ ).hexdigest()
+
+ issue = {
+ 'type': 'issue',
+ 'description': line_dict['detail'],
+ 'fingerprint': line_dict['fingerprint'],
+ 'location': {
+ "path": line_dict['file'],
+ "lines": {
+ "begin": int(line_dict['line']),
+ "end": int(line_dict['line']),
+ },
+ "positions": {
+ "begin": {
+ "line": int(line_dict['line']),
+ "column": int(line_dict['column']),
+ },
+ },
+
+ },
+ 'severity': 'minor',
+ }
+
+ category = line_dict['type'].split('-')[0]
+ if category in categories:
+ issue['category'] = categories[category]
+
+ # use a dictionnary to avoid duplicates
+ issues[issue['fingerprint']] = issue
+
+issues = list(issues.values())
+
+json.dump(issues, sys.stdout)
diff --git a/test/test_model/test_common/test_dof_manager.cc b/test/test_model/test_common/test_dof_manager.cc
index 41cc60eba..4e63fb6db 100644
--- a/test/test_model/test_common/test_dof_manager.cc
+++ b/test/test_model/test_common/test_dof_manager.cc
@@ -1,298 +1,298 @@
/**
* @file test_dof_manager.cc
*
* @author Nicolas Richart
*
* @date creation Wed Jan 30 2019
*
* @brief test the dof managers
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "test_gtest_utils.hh"
/* -------------------------------------------------------------------------- */
#include <dof_manager.hh>
#include <mesh_partition_scotch.hh>
#include <mesh_utils.hh>
/* -------------------------------------------------------------------------- */
-
#include <gtest/gtest.h>
#include <numeric>
#include <string>
#include <type_traits>
/* -------------------------------------------------------------------------- */
+
namespace akantu {
enum DOFManagerType { _dmt_default, _dmt_petsc };
}
AKANTU_ENUM_HASH(DOFManagerType)
using namespace akantu;
// defined as struct to get there names in gtest outputs
-struct _dof_manager_default
+struct dof_manager_default_
: public std::integral_constant<DOFManagerType, _dmt_default> {};
-struct _dof_manager_petsc
+struct dof_manager_petsc_
: public std::integral_constant<DOFManagerType, _dmt_petsc> {};
using dof_manager_types = ::testing::Types<
#ifdef AKANTU_USE_PETSC
- _dof_manager_petsc,
+ dof_manager_petsc_,
#endif
- _dof_manager_default>;
+ dof_manager_default_>;
namespace std {
std::string to_string(const DOFManagerType & type) {
std::unordered_map<DOFManagerType, std::string> map{
#ifdef AKANTU_USE_PETSC
{_dmt_petsc, "petsc"},
#endif
{_dmt_default, "default"},
};
return map.at(type);
}
} // namespace std
/* -------------------------------------------------------------------------- */
using namespace akantu;
/* -------------------------------------------------------------------------- */
namespace akantu {
class DOFManagerTester {
public:
DOFManagerTester(std::unique_ptr<DOFManager> dof_manager)
: dof_manager(std::move(dof_manager)) {}
DOFManager & operator*() { return *dof_manager; }
DOFManager * operator->() { return dof_manager.get(); }
void getArrayPerDOFs(const ID & id, SolverVector & vector,
Array<Real> & array) {
dof_manager->getArrayPerDOFs(id, vector, array);
}
SolverVector & residual() { return *dof_manager->residual; }
private:
std::unique_ptr<DOFManager> dof_manager;
};
} // namespace akantu
template <class T> class DOFManagerFixture : public ::testing::Test {
public:
constexpr static DOFManagerType type = T::value;
constexpr static UInt dim = 3;
void SetUp() override {
mesh = std::make_unique<Mesh>(this->dim);
auto & communicator = Communicator::getStaticCommunicator();
if (communicator.whoAmI() == 0) {
mesh->read("mesh.msh");
}
mesh->distribute();
nb_nodes = this->mesh->getNbNodes();
nb_total_nodes = this->mesh->getNbGlobalNodes();
auto && range_nodes = arange(nb_nodes);
nb_pure_local =
std::accumulate(range_nodes.begin(), range_nodes.end(), 0,
[&](auto && init, auto && val) {
return init + mesh->isLocalOrMasterNode(val);
});
}
void TearDown() override {
mesh.reset();
dof1.reset();
dof2.reset();
}
decltype(auto) alloc() {
std::unordered_map<DOFManagerType, std::string> types{
{_dmt_default, "default"}, {_dmt_petsc, "petsc"}};
return DOFManagerTester(DOFManagerFactory::getInstance().allocate(
- types[T::value], *mesh, "dof_manager", 0));
+ types[T::value], *mesh, "dof_manager"));
}
decltype(auto) registerDOFs(DOFSupportType dst1, DOFSupportType dst2) {
auto dof_manager = DOFManagerTester(this->alloc());
auto n1 = dst1 == _dst_nodal ? nb_nodes : nb_pure_local;
this->dof1 = std::make_unique<Array<Real>>(n1, 3);
dof_manager->registerDOFs("dofs1", *this->dof1, dst1);
EXPECT_EQ(dof_manager.residual().size(), nb_total_nodes * 3);
auto n2 = dst2 == _dst_nodal ? nb_nodes : nb_pure_local;
this->dof2 = std::make_unique<Array<Real>>(n2, 5);
dof_manager->registerDOFs("dofs2", *this->dof2, dst2);
EXPECT_EQ(dof_manager.residual().size(), nb_total_nodes * 8);
return dof_manager;
}
protected:
Int nb_nodes{0}, nb_total_nodes{0}, nb_pure_local{0};
std::unique_ptr<Mesh> mesh;
std::unique_ptr<Array<Real>> dof1;
std::unique_ptr<Array<Real>> dof2;
};
template <class T> constexpr DOFManagerType DOFManagerFixture<T>::type;
template <class T> constexpr UInt DOFManagerFixture<T>::dim;
TYPED_TEST_SUITE(DOFManagerFixture, dof_manager_types, );
/* -------------------------------------------------------------------------- */
TYPED_TEST(DOFManagerFixture, Construction) {
auto dof_manager = this->alloc();
}
/* -------------------------------------------------------------------------- */
TYPED_TEST(DOFManagerFixture, DoubleConstruction) {
auto dof_manager = this->alloc();
dof_manager = this->alloc();
}
/* -------------------------------------------------------------------------- */
TYPED_TEST(DOFManagerFixture, RegisterGenericDOF1) {
auto dof_manager = this->alloc();
Array<Real> dofs(this->nb_pure_local, 3);
dof_manager->registerDOFs("dofs1", dofs, _dst_generic);
EXPECT_GE(dof_manager.residual().size(), this->nb_total_nodes * 3);
}
/* -------------------------------------------------------------------------- */
TYPED_TEST(DOFManagerFixture, RegisterNodalDOF1) {
auto dof_manager = this->alloc();
Array<Real> dofs(this->nb_nodes, 3);
dof_manager->registerDOFs("dofs1", dofs, _dst_nodal);
EXPECT_GE(dof_manager.residual().size(), this->nb_total_nodes * 3);
}
/* -------------------------------------------------------------------------- */
TYPED_TEST(DOFManagerFixture, RegisterGenericDOF2) {
this->registerDOFs(_dst_generic, _dst_generic);
}
/* -------------------------------------------------------------------------- */
TYPED_TEST(DOFManagerFixture, RegisterNodalDOF2) {
this->registerDOFs(_dst_nodal, _dst_nodal);
}
/* -------------------------------------------------------------------------- */
TYPED_TEST(DOFManagerFixture, RegisterMixedDOF) {
auto dof_manager = this->registerDOFs(_dst_nodal, _dst_generic);
}
/* -------------------------------------------------------------------------- */
TYPED_TEST(DOFManagerFixture, AssembleVector) {
auto dof_manager = this->registerDOFs(_dst_nodal, _dst_generic);
dof_manager.residual().zero();
for (auto && data :
enumerate(make_view(*this->dof1, this->dof1->getNbComponent()))) {
auto n = std::get<0>(data);
auto & l = std::get<1>(data);
l.set(1. * this->mesh->isLocalOrMasterNode(n));
}
this->dof2->set(2.);
dof_manager->assembleToResidual("dofs1", *this->dof1);
dof_manager->assembleToResidual("dofs2", *this->dof2);
this->dof1->set(0.);
this->dof2->set(0.);
dof_manager.getArrayPerDOFs("dofs1", dof_manager.residual(), *this->dof1);
for (auto && data :
enumerate(make_view(*this->dof1, this->dof1->getNbComponent()))) {
if (this->mesh->isLocalOrMasterNode(std::get<0>(data))) {
const auto & l = std::get<1>(data);
auto e = (l - Vector<Real>{1., 1., 1.}).norm();
ASSERT_EQ(e, 0.);
}
}
dof_manager.getArrayPerDOFs("dofs2", dof_manager.residual(), *this->dof2);
for (auto && l : make_view(*this->dof2, this->dof2->getNbComponent())) {
auto e = (l - Vector<Real>{2., 2., 2., 2., 2.}).norm();
ASSERT_EQ(e, 0.);
}
}
/* -------------------------------------------------------------------------- */
TYPED_TEST(DOFManagerFixture, AssembleMatrixNodal) {
auto dof_manager = this->registerDOFs(_dst_nodal, _dst_nodal);
auto && K = dof_manager->getNewMatrix("K", _symmetric);
K.zero();
auto && elemental_matrix = std::make_unique<Array<Real>>(
this->mesh->getNbElement(this->dim), 8 * 3 * 8 * 3);
for (auto && m : make_view(*elemental_matrix, 8 * 3, 8 * 3)) {
m.set(1.);
}
dof_manager->assembleElementalMatricesToMatrix(
"K", "dofs1", *elemental_matrix, _hexahedron_8);
elemental_matrix = std::make_unique<Array<Real>>(
this->mesh->getNbElement(this->dim), 8 * 5 * 8 * 5);
for (auto && m : make_view(*elemental_matrix, 8 * 5, 8 * 5)) {
m.set(1.);
}
dof_manager->assembleElementalMatricesToMatrix(
"K", "dofs2", *elemental_matrix, _hexahedron_8);
CSR<Element> node_to_elem;
MeshUtils::buildNode2Elements(*this->mesh, node_to_elem, this->dim);
dof_manager.residual().zero();
for (auto && data :
enumerate(zip(make_view(*this->dof1, this->dof1->getNbComponent()),
make_view(*this->dof2, this->dof2->getNbComponent())))) {
auto n = std::get<0>(data);
auto & l1 = std::get<0>(std::get<1>(data));
auto & l2 = std::get<1>(std::get<1>(data));
auto v = 1. * this->mesh->isLocalOrMasterNode(n);
l1.set(v);
l2.set(v);
}
dof_manager->assembleToResidual("dofs1", *this->dof1);
dof_manager->assembleToResidual("dofs2", *this->dof2);
for (auto && n : arange(this->nb_nodes)) {
if (not this->mesh->isLocalOrMasterNode(n)) {
}
}
}
diff --git a/test/test_model/test_common/test_model_solver/test_model_solver_my_model.hh b/test/test_model/test_common/test_model_solver/test_model_solver_my_model.hh
index a8c75a40e..2b885cffc 100644
--- a/test/test_model/test_common/test_model_solver/test_model_solver_my_model.hh
+++ b/test/test_model/test_common/test_model_solver/test_model_solver_my_model.hh
@@ -1,451 +1,451 @@
/**
* @file test_model_solver_my_model.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Wed Apr 13 2016
* @date last modification: Tue Feb 20 2018
*
* @brief Test default dof manager
*
*
* Copyright (©) 2016-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_iterators.hh"
#include "boundary_condition.hh"
#include "communicator.hh"
#include "data_accessor.hh"
#include "dof_manager_default.hh"
#include "element_synchronizer.hh"
#include "mesh.hh"
#include "model_solver.hh"
#include "periodic_node_synchronizer.hh"
#include "solver_vector_default.hh"
#include "sparse_matrix.hh"
/* -------------------------------------------------------------------------- */
namespace akantu {
#ifndef AKANTU_TEST_MODEL_SOLVER_MY_MODEL_HH_
#define AKANTU_TEST_MODEL_SOLVER_MY_MODEL_HH_
/**
* =\o-----o-----o-> F
* | |
* |---- L ----|
*/
class MyModel : public ModelSolver,
public BoundaryCondition<MyModel>,
public DataAccessor<Element> {
public:
MyModel(Real F, Mesh & mesh, bool lumped,
const ID & dof_manager_type = "default")
- : ModelSolver(mesh, ModelType::_model, "model_solver", 0),
+ : ModelSolver(mesh, ModelType::_model, "model_solver"),
nb_dofs(mesh.getNbNodes()), nb_elements(mesh.getNbElement(_segment_2)),
lumped(lumped), E(1.), A(1.), rho(1.), mesh(mesh),
displacement(nb_dofs, 1, "disp"), velocity(nb_dofs, 1, "velo"),
acceleration(nb_dofs, 1, "accel"), blocked(nb_dofs, 1, "blocked"),
forces(nb_dofs, 1, "force_ext"),
internal_forces(nb_dofs, 1, "force_int"),
stresses(nb_elements, 1, "stress"), strains(nb_elements, 1, "strain"),
initial_lengths(nb_elements, 1, "L0") {
this->initDOFManager(dof_manager_type);
this->initBC(*this, displacement, forces);
this->getDOFManager().registerDOFs("disp", displacement, _dst_nodal);
this->getDOFManager().registerDOFsDerivative("disp", 1, velocity);
this->getDOFManager().registerDOFsDerivative("disp", 2, acceleration);
this->getDOFManager().registerBlockedDOFs("disp", blocked);
displacement.set(0.);
velocity.set(0.);
acceleration.set(0.);
forces.set(0.);
blocked.set(false);
UInt global_nb_nodes = mesh.getNbGlobalNodes();
for (auto && n : arange(nb_dofs)) {
auto global_id = mesh.getNodeGlobalId(n);
if (global_id == (global_nb_nodes - 1))
forces(n, _x) = F;
if (global_id == 0)
blocked(n, _x) = true;
}
auto cit = this->mesh.getConnectivity(_segment_2).begin(2);
auto cend = this->mesh.getConnectivity(_segment_2).end(2);
auto L_it = this->initial_lengths.begin();
for (; cit != cend; ++cit, ++L_it) {
const Vector<UInt> & conn = *cit;
UInt n1 = conn(0);
UInt n2 = conn(1);
Real p1 = this->mesh.getNodes()(n1, _x);
Real p2 = this->mesh.getNodes()(n2, _x);
*L_it = std::abs(p2 - p1);
}
this->registerDataAccessor(*this);
this->registerSynchronizer(
const_cast<ElementSynchronizer &>(this->mesh.getElementSynchronizer()),
SynchronizationTag::_user_1);
}
void assembleLumpedMass() {
auto & M = this->getDOFManager().getLumpedMatrix("M");
M.zero();
this->assembleLumpedMass(_not_ghost);
if (this->mesh.getNbElement(_segment_2, _ghost) > 0)
this->assembleLumpedMass(_ghost);
is_lumped_mass_assembled = true;
}
void assembleLumpedMass(GhostType ghost_type) {
Array<Real> M(nb_dofs, 1, 0.);
Array<Real> m_all_el(this->mesh.getNbElement(_segment_2, ghost_type), 2);
for (auto && data :
zip(make_view(this->mesh.getConnectivity(_segment_2), 2),
make_view(m_all_el, 2))) {
const auto & conn = std::get<0>(data);
auto & m_el = std::get<1>(data);
UInt n1 = conn(0);
UInt n2 = conn(1);
Real p1 = this->mesh.getNodes()(n1, _x);
Real p2 = this->mesh.getNodes()(n2, _x);
Real L = std::abs(p2 - p1);
Real M_n = rho * A * L / 2;
m_el(0) = m_el(1) = M_n;
}
this->getDOFManager().assembleElementalArrayLocalArray(
m_all_el, M, _segment_2, ghost_type);
this->getDOFManager().assembleToLumpedMatrix("disp", M, "M");
}
void assembleMass() {
SparseMatrix & M = this->getDOFManager().getMatrix("M");
M.zero();
Array<Real> m_all_el(this->nb_elements, 4);
Matrix<Real> m(2, 2);
m(0, 0) = m(1, 1) = 2;
m(0, 1) = m(1, 0) = 1;
// under integrated
// m(0, 0) = m(1, 1) = 3./2.;
// m(0, 1) = m(1, 0) = 3./2.;
// lumping the mass matrix
// m(0, 0) += m(0, 1);
// m(1, 1) += m(1, 0);
// m(0, 1) = m(1, 0) = 0;
for (auto && data :
zip(make_view(this->mesh.getConnectivity(_segment_2), 2),
make_view(m_all_el, 2, 2))) {
const auto & conn = std::get<0>(data);
auto & m_el = std::get<1>(data);
UInt n1 = conn(0);
UInt n2 = conn(1);
Real p1 = this->mesh.getNodes()(n1, _x);
Real p2 = this->mesh.getNodes()(n2, _x);
Real L = std::abs(p2 - p1);
m_el = m;
m_el *= rho * A * L / 6.;
}
this->getDOFManager().assembleElementalMatricesToMatrix(
"M", "disp", m_all_el, _segment_2);
is_mass_assembled = true;
}
MatrixType getMatrixType(const ID &) override { return _symmetric; }
void assembleMatrix(const ID & matrix_id) override {
if (matrix_id == "K") {
if (not is_stiffness_assembled)
this->assembleStiffness();
} else if (matrix_id == "M") {
if (not is_mass_assembled)
this->assembleMass();
} else if (matrix_id == "C") {
// pass, no damping matrix
} else {
AKANTU_EXCEPTION("This solver does not know what to do with a matrix "
<< matrix_id);
}
}
void assembleLumpedMatrix(const ID & matrix_id) override {
if (matrix_id == "M") {
if (not is_lumped_mass_assembled)
this->assembleLumpedMass();
} else {
AKANTU_EXCEPTION("This solver does not know what to do with a matrix "
<< matrix_id);
}
}
void assembleStiffness() {
SparseMatrix & K = this->getDOFManager().getMatrix("K");
K.zero();
Matrix<Real> k(2, 2);
k(0, 0) = k(1, 1) = 1;
k(0, 1) = k(1, 0) = -1;
Array<Real> k_all_el(this->nb_elements, 4);
auto k_it = k_all_el.begin(2, 2);
auto cit = this->mesh.getConnectivity(_segment_2).begin(2);
auto cend = this->mesh.getConnectivity(_segment_2).end(2);
for (; cit != cend; ++cit, ++k_it) {
const auto & conn = *cit;
UInt n1 = conn(0);
UInt n2 = conn(1);
Real p1 = this->mesh.getNodes()(n1, _x);
Real p2 = this->mesh.getNodes()(n2, _x);
Real L = std::abs(p2 - p1);
auto & k_el = *k_it;
k_el = k;
k_el *= E * A / L;
}
this->getDOFManager().assembleElementalMatricesToMatrix(
"K", "disp", k_all_el, _segment_2);
is_stiffness_assembled = true;
}
void assembleResidual() override {
this->getDOFManager().assembleToResidual("disp", forces);
internal_forces.zero();
this->assembleResidualInternal(_not_ghost);
this->synchronize(SynchronizationTag::_user_1);
this->getDOFManager().assembleToResidual("disp", internal_forces, -1.);
}
void assembleResidualInternal(GhostType ghost_type) {
Array<Real> forces_internal_el(
this->mesh.getNbElement(_segment_2, ghost_type), 2);
auto cit = this->mesh.getConnectivity(_segment_2, ghost_type).begin(2);
auto cend = this->mesh.getConnectivity(_segment_2, ghost_type).end(2);
auto f_it = forces_internal_el.begin(2);
auto strain_it = this->strains.begin();
auto stress_it = this->stresses.begin();
auto L_it = this->initial_lengths.begin();
for (; cit != cend; ++cit, ++f_it, ++strain_it, ++stress_it, ++L_it) {
const auto & conn = *cit;
UInt n1 = conn(0);
UInt n2 = conn(1);
Real u1 = this->displacement(n1, _x);
Real u2 = this->displacement(n2, _x);
*strain_it = (u2 - u1) / *L_it;
*stress_it = E * *strain_it;
Real f_n = A * *stress_it;
Vector<Real> & f = *f_it;
f(0) = -f_n;
f(1) = f_n;
}
this->getDOFManager().assembleElementalArrayLocalArray(
forces_internal_el, internal_forces, _segment_2, ghost_type);
}
Real getPotentialEnergy() {
Real res = 0;
if (not lumped) {
res = this->mulVectMatVect(this->displacement, "K", this->displacement);
} else {
auto strain_it = this->strains.begin();
auto stress_it = this->stresses.begin();
auto strain_end = this->strains.end();
auto L_it = this->initial_lengths.begin();
for (; strain_it != strain_end; ++strain_it, ++stress_it, ++L_it) {
res += *strain_it * *stress_it * A * *L_it;
}
mesh.getCommunicator().allReduce(res, SynchronizerOperation::_sum);
}
return res / 2.;
}
Real getKineticEnergy() {
Real res = 0;
if (not lumped) {
res = this->mulVectMatVect(this->velocity, "M", this->velocity);
} else {
Array<Real> & m = dynamic_cast<SolverVectorDefault &>(
this->getDOFManager().getLumpedMatrix("M"));
auto it = velocity.begin();
auto end = velocity.end();
auto m_it = m.begin();
for (UInt node = 0; it != end; ++it, ++m_it, ++node) {
if (mesh.isLocalOrMasterNode(node))
res += *m_it * *it * *it;
}
mesh.getCommunicator().allReduce(res, SynchronizerOperation::_sum);
}
return res / 2.;
}
Real getExternalWorkIncrement() {
Real res = 0;
auto it = velocity.begin();
auto end = velocity.end();
auto if_it = internal_forces.begin();
auto ef_it = forces.begin();
auto b_it = blocked.begin();
for (UInt node = 0; it != end; ++it, ++if_it, ++ef_it, ++b_it, ++node) {
if (mesh.isLocalOrMasterNode(node))
res += (*b_it ? -*if_it : *ef_it) * *it;
}
mesh.getCommunicator().allReduce(res, SynchronizerOperation::_sum);
return res * this->getTimeStep();
}
Real mulVectMatVect(const Array<Real> & x, const ID & A_id,
const Array<Real> & y) {
Array<Real> Ay(nb_dofs);
this->getDOFManager().assembleMatMulVectToArray("disp", A_id, y, Ay);
Real res = 0.;
for (auto && data : zip(arange(nb_dofs), make_view(Ay), make_view(x))) {
res += std::get<2>(data) * std::get<1>(data) *
mesh.isLocalOrMasterNode(std::get<0>(data));
}
mesh.getCommunicator().allReduce(res, SynchronizerOperation::_sum);
return res;
}
/* ------------------------------------------------------------------------ */
UInt getNbData(const Array<Element> & elements,
const SynchronizationTag &) const override {
return elements.size() * sizeof(Real);
}
void packData(CommunicationBuffer & buffer, const Array<Element> & elements,
const SynchronizationTag & tag) const override {
if (tag == SynchronizationTag::_user_1) {
for (const auto & el : elements) {
buffer << this->stresses(el.element);
}
}
}
void unpackData(CommunicationBuffer & buffer, const Array<Element> & elements,
const SynchronizationTag & tag) override {
if (tag == SynchronizationTag::_user_1) {
auto cit = this->mesh.getConnectivity(_segment_2, _ghost).begin(2);
for (const auto & el : elements) {
Real stress;
buffer >> stress;
Real f = A * stress;
Vector<UInt> conn = cit[el.element];
this->internal_forces(conn(0), _x) += -f;
this->internal_forces(conn(1), _x) += f;
}
}
}
const Mesh & getMesh() const { return mesh; }
UInt getSpatialDimension() const { return 1; }
auto & getBlockedDOFs() { return blocked; }
private:
UInt nb_dofs;
UInt nb_elements;
bool lumped;
bool is_stiffness_assembled{false};
bool is_mass_assembled{false};
bool is_lumped_mass_assembled{false};
public:
Real E, A, rho;
Mesh & mesh;
Array<Real> displacement;
Array<Real> velocity;
Array<Real> acceleration;
Array<bool> blocked;
Array<Real> forces;
Array<Real> internal_forces;
Array<Real> stresses;
Array<Real> strains;
Array<Real> initial_lengths;
};
#endif /* AKANTU_TEST_MODEL_SOLVER_MY_MODEL_HH_ */
} // namespace akantu
diff --git a/test/test_model/test_common/test_non_local_toolbox/my_model.hh b/test/test_model/test_common/test_non_local_toolbox/my_model.hh
index 46908a634..4b4e9fd80 100644
--- a/test/test_model/test_common/test_non_local_toolbox/my_model.hh
+++ b/test/test_model/test_common/test_non_local_toolbox/my_model.hh
@@ -1,123 +1,123 @@
/**
* @file my_model.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Mon Sep 11 2017
* @date last modification: Sat Feb 03 2018
*
* @brief A Documented file.
*
*
* Copyright (©) 2016-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "integrator_gauss.hh"
#include "model.hh"
#include "non_local_manager.hh"
#include "non_local_manager_callback.hh"
#include "non_local_neighborhood_base.hh"
#include "shape_lagrange.hh"
/* -------------------------------------------------------------------------- */
using namespace akantu;
class MyModel : public Model, public NonLocalManagerCallback {
using MyFEEngineType = FEEngineTemplate<IntegratorGauss, ShapeLagrange>;
public:
MyModel(Mesh & mesh, UInt spatial_dimension)
: Model(mesh, ModelType::_model, spatial_dimension),
manager(*this, *this) {
registerFEEngineObject<MyFEEngineType>("FEEngine", mesh, spatial_dimension);
manager.registerNeighborhood("test_region", "test_region");
getFEEngine().initShapeFunctions();
manager.initialize();
}
void initModel() override {}
MatrixType getMatrixType(const ID &) override { return _mt_not_defined; }
std::tuple<ID, TimeStepSolverType>
getDefaultSolverID(const AnalysisMethod & /*method*/) override {
return std::make_tuple("test", TimeStepSolverType::_static);
}
void assembleMatrix(const ID &) override {}
void assembleLumpedMatrix(const ID &) override {}
void assembleResidual() override {}
void onNodesAdded(const Array<UInt> &, const NewNodesEvent &) override {}
void onNodesRemoved(const Array<UInt> &, const Array<UInt> &,
const RemovedNodesEvent &) override {}
void onElementsAdded(const Array<Element> &,
const NewElementsEvent &) override {}
void onElementsRemoved(const Array<Element> &,
const ElementTypeMapArray<UInt> &,
const RemovedElementsEvent &) override {}
void onElementsChanged(const Array<Element> &, const Array<Element> &,
const ElementTypeMapArray<UInt> &,
const ChangedElementsEvent &) override {}
void insertIntegrationPointsInNeighborhoods(
GhostType ghost_type) override {
ElementTypeMapArray<Real> quadrature_points_coordinates(
- "quadrature_points_coordinates_tmp_nl", this->id, this->memory_id);
+ "quadrature_points_coordinates_tmp_nl", this->id);
quadrature_points_coordinates.initialize(this->getFEEngine(),
_nb_component = spatial_dimension,
_ghost_type = ghost_type);
IntegrationPoint q;
q.ghost_type = ghost_type;
q.global_num = 0;
auto & neighborhood = manager.getNeighborhood("test_region");
for (auto & type : quadrature_points_coordinates.elementTypes(
spatial_dimension, ghost_type)) {
q.type = type;
auto & quads = quadrature_points_coordinates(type, ghost_type);
this->getFEEngine().computeIntegrationPointsCoordinates(quads, type,
ghost_type);
auto quad_it = quads.begin(quads.getNbComponent());
auto quad_end = quads.end(quads.getNbComponent());
q.num_point = 0;
for (; quad_it != quad_end; ++quad_it) {
neighborhood.insertIntegrationPoint(q, *quad_it);
++q.num_point;
++q.global_num;
}
}
}
void computeNonLocalStresses(GhostType) override {}
void updateLocalInternal(ElementTypeMapReal &, GhostType,
ElementKind) override {}
void updateNonLocalInternal(ElementTypeMapReal &, GhostType,
ElementKind) override {}
const auto & getNonLocalManager() const { return manager; }
private:
NonLocalManager manager;
};
diff --git a/test/test_model/test_solid_mechanics_model/cube_two_materials.geo b/test/test_model/test_solid_mechanics_model/cube_two_materials.geo
index 2b9c23319..3de8aed5d 100644
--- a/test/test_model/test_solid_mechanics_model/cube_two_materials.geo
+++ b/test/test_model/test_solid_mechanics_model/cube_two_materials.geo
@@ -1,14 +1,14 @@
L = 1;
-s = 0.4;
+s = 0.1;
Point(1) = {-L/2., -L/2., 0, s};
line[] = Extrude{L,0,0}{ Point{1}; };
surface[] = Extrude{0,L,0}{ Line{line[1]}; };
volume[] = Extrude{0,0,L}{ Surface{surface[1]}; };
Physical Volume(1) = {volume[1]};
Transfinite Surface "*";
Recombine Surface "*";
Transfinite Volume "*";
diff --git a/test/test_model/test_solid_mechanics_model/test_materials/test_plastic_materials.cc b/test/test_model/test_solid_mechanics_model/test_materials/test_plastic_materials.cc
index dd1ed42a9..400e57c94 100644
--- a/test/test_model/test_solid_mechanics_model/test_materials/test_plastic_materials.cc
+++ b/test/test_model/test_solid_mechanics_model/test_materials/test_plastic_materials.cc
@@ -1,191 +1,192 @@
/**
* @file test_plastic_materials.cc
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
*
* @date creation: Fri Nov 17 2017
* @date last modification: Wed Feb 21 2018
*
* @brief Tests the plastic material
*
*
* Copyright (©) 2016-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "material_linear_isotropic_hardening.hh"
#include "solid_mechanics_model.hh"
#include "test_material_fixtures.hh"
#include <gtest/gtest.h>
#include <type_traits>
/* -------------------------------------------------------------------------- */
using namespace akantu;
using mat_types = ::testing::Types<
// Traits<MaterialLinearIsotropicHardening, 1>,
// Traits<MaterialLinearIsotropicHardening, 2>,
Traits<MaterialLinearIsotropicHardening, 3>>;
/* -------------------------------------------------------------------------- */
template <>
void FriendMaterial<MaterialLinearIsotropicHardening<3>>::testComputeStress() {
Real E = 1.;
// Real nu = .3;
Real nu = 0.;
Real rho = 1.;
Real sigma_0 = 1.;
Real h = 0.;
Real bulk_modulus_K = E / 3. / (1 - 2. * nu);
Real shear_modulus_mu = 0.5 * E / (1 + nu);
setParam("E", E);
setParam("nu", nu);
setParam("rho", rho);
setParam("sigma_y", sigma_0);
setParam("h", h);
auto rotation_matrix = getRandomRotation();
Real max_strain = 10.;
Real strain_steps = 100;
Real dt = max_strain / strain_steps;
std::vector<double> steps(strain_steps);
std::iota(steps.begin(), steps.end(), 0.);
Matrix<Real> previous_grad_u_rot(3, 3, 0.);
Matrix<Real> previous_sigma(3, 3, 0.);
Matrix<Real> previous_sigma_rot(3, 3, 0.);
Matrix<Real> inelastic_strain_rot(3, 3, 0.);
Matrix<Real> inelastic_strain(3, 3, 0.);
Matrix<Real> previous_inelastic_strain(3, 3, 0.);
Matrix<Real> previous_inelastic_strain_rot(3, 3, 0.);
Matrix<Real> sigma_rot(3, 3, 0.);
Real iso_hardening = 0.;
Real previous_iso_hardening = 0.;
// hydrostatic loading (should not plastify)
for (auto && i : steps) {
auto t = i * dt;
auto grad_u = this->getHydrostaticStrain(t);
auto grad_u_rot = this->applyRotation(grad_u, rotation_matrix);
this->computeStressOnQuad(grad_u_rot, previous_grad_u_rot, sigma_rot,
previous_sigma_rot, inelastic_strain_rot,
previous_inelastic_strain_rot, iso_hardening,
previous_iso_hardening, 0., 0.);
auto sigma = this->reverseRotation(sigma_rot, rotation_matrix);
Matrix<Real> sigma_expected =
t * 3. * bulk_modulus_K * Matrix<Real>::eye(3, 1.);
Real stress_error = (sigma - sigma_expected).norm<L_inf>();
ASSERT_NEAR(stress_error, 0., 1e-13);
ASSERT_NEAR(inelastic_strain_rot.norm<L_inf>(), 0., 1e-13);
previous_grad_u_rot = grad_u_rot;
previous_sigma_rot = sigma_rot;
previous_inelastic_strain_rot = inelastic_strain_rot;
previous_iso_hardening = iso_hardening;
}
// deviatoric loading (should plastify)
// stress at onset of plastication
Real beta = sqrt(42);
Real t_P = sigma_0 / 2. / shear_modulus_mu / beta;
Matrix<Real> sigma_P = sigma_0 / beta * this->getDeviatoricStrain(1.);
for (auto && i : steps) {
auto t = i * dt;
auto grad_u = this->getDeviatoricStrain(t);
auto grad_u_rot = this->applyRotation(grad_u, rotation_matrix);
- Real iso_hardening, previous_iso_hardening;
+ Real iso_hardening{0.};
+ Real previous_iso_hardening{0.};
this->computeStressOnQuad(grad_u_rot, previous_grad_u_rot, sigma_rot,
previous_sigma_rot, inelastic_strain_rot,
previous_inelastic_strain_rot, iso_hardening,
previous_iso_hardening, 0., 0.);
auto sigma = this->reverseRotation(sigma_rot, rotation_matrix);
auto inelastic_strain =
this->reverseRotation(inelastic_strain_rot, rotation_matrix);
if (t < t_P) {
Matrix<Real> sigma_expected =
shear_modulus_mu * (grad_u + grad_u.transpose());
Real stress_error = (sigma - sigma_expected).norm<L_inf>();
ASSERT_NEAR(stress_error, 0., 1e-13);
ASSERT_NEAR(inelastic_strain_rot.norm<L_inf>(), 0., 1e-13);
} else if (t > t_P + dt) {
// skip the transition from non plastic to plastic
auto delta_lambda_expected =
dt / t * previous_sigma.doubleDot(grad_u + grad_u.transpose()) / 2.;
auto delta_inelastic_strain_expected =
delta_lambda_expected * 3. / 2. / sigma_0 * previous_sigma;
auto inelastic_strain_expected =
delta_inelastic_strain_expected + previous_inelastic_strain;
ASSERT_NEAR((inelastic_strain - inelastic_strain_expected).norm<L_inf>(),
0., 1e-13);
auto delta_sigma_expected =
2. * shear_modulus_mu *
(0.5 * dt / t * (grad_u + grad_u.transpose()) -
delta_inelastic_strain_expected);
auto delta_sigma = sigma - previous_sigma;
ASSERT_NEAR((delta_sigma_expected - delta_sigma).norm<L_inf>(), 0.,
1e-13);
}
previous_sigma = sigma;
previous_sigma_rot = sigma_rot;
previous_grad_u_rot = grad_u_rot;
previous_inelastic_strain = inelastic_strain;
previous_inelastic_strain_rot = inelastic_strain_rot;
}
}
namespace {
template <typename T>
class TestPlasticMaterialFixture : public ::TestMaterialFixture<T> {};
TYPED_TEST_SUITE(TestPlasticMaterialFixture, mat_types, );
TYPED_TEST(TestPlasticMaterialFixture, ComputeStress) {
this->material->testComputeStress();
}
TYPED_TEST(TestPlasticMaterialFixture, DISABLED_EnergyDensity) {
this->material->testEnergyDensity();
}
TYPED_TEST(TestPlasticMaterialFixture, DISABLED_ComputeTangentModuli) {
this->material->testComputeTangentModuli();
}
TYPED_TEST(TestPlasticMaterialFixture, DISABLED_ComputeCelerity) {
this->material->testCelerity();
}
} // namespace
/*****************************************************************/
diff --git a/test/test_model/test_solid_mechanics_model/test_solid_mechanics_model_reassign_material.cc b/test/test_model/test_solid_mechanics_model/test_solid_mechanics_model_reassign_material.cc
index d82deb7cd..b000ee82c 100644
--- a/test/test_model/test_solid_mechanics_model/test_solid_mechanics_model_reassign_material.cc
+++ b/test/test_model/test_solid_mechanics_model/test_solid_mechanics_model_reassign_material.cc
@@ -1,203 +1,193 @@
/**
* @file test_solid_mechanics_model_reassign_material.cc
*
* @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
*
* @date creation: Mon Feb 10 2014
* @date last modification: Tue Feb 20 2018
*
* @brief test the function reassign material
*
*
* Copyright (©) 2014-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_grid_dynamic.hh"
#include "communicator.hh"
#include "material.hh"
#include "mesh_utils.hh"
#include "solid_mechanics_model.hh"
/* -------------------------------------------------------------------------- */
using namespace akantu;
class StraightInterfaceMaterialSelector : public MaterialSelector {
public:
StraightInterfaceMaterialSelector(SolidMechanicsModel & model,
+ UInt horizontal, Real & pos_interface,
const std::string & mat_1_material,
- const std::string & mat_2_material,
- bool & horizontal, Real & pos_interface)
- : model(model), mat_1_material(mat_1_material),
- mat_2_material(mat_2_material), horizontal(horizontal),
- pos_interface(pos_interface) {
+ const std::string & mat_2_material)
+ : model(model), horizontal(horizontal), pos_interface(pos_interface),
+ mat_1_material(mat_1_material), mat_2_material(mat_2_material) {
Mesh & mesh = model.getMesh();
UInt spatial_dimension = mesh.getSpatialDimension();
/// store barycenters of all elements
barycenters.initialize(mesh, _spatial_dimension = spatial_dimension,
- _nb_component = spatial_dimension);
-
- for (auto ghost_type : ghost_types) {
- Element e;
- e.ghost_type = ghost_type;
-
- for (auto & type : mesh.elementTypes(spatial_dimension, ghost_type)) {
- UInt nb_element = mesh.getNbElement(type, ghost_type);
- e.type = type;
- Array<Real> & barycenter = barycenters(type, ghost_type);
- barycenter.resize(nb_element);
-
- Array<Real>::iterator<Vector<Real>> bary_it =
- barycenter.begin(spatial_dimension);
- for (UInt elem = 0; elem < nb_element; ++elem) {
- e.element = elem;
- mesh.getBarycenter(e, *bary_it);
- ++bary_it;
- }
- }
- }
+ _nb_component = spatial_dimension,
+ _with_nb_element = true);
+
+ for_each_element(mesh, [&](auto && el) {
+ Vector<Real> bary(barycenters.get(el));
+ mesh.getBarycenter(el, bary);
+ });
}
- UInt operator()(const Element & elem) {
- UInt spatial_dimension = model.getSpatialDimension();
- const Vector<Real> & bary = barycenters(elem.type, elem.ghost_type)
- .begin(spatial_dimension)[elem.element];
+ void setMaterials() {
+ mat_ids[0] = model.getMaterialIndex(mat_1_material);
+ mat_ids[1] = model.getMaterialIndex(mat_2_material);
+ }
+ UInt operator()(const Element & elem) override {
+ if (not materials_set) {
+ setMaterials();
+ }
+ const Vector<Real> bary = barycenters.get(elem);
/// check for a given element on which side of the material interface plane
/// the bary center lies and assign corresponding material
- if (bary(!horizontal) < pos_interface) {
- return model.getMaterialIndex(mat_1_material);
- ;
+ if (bary(horizontal) < pos_interface) {
+ return mat_ids[0];
}
- return model.getMaterialIndex(mat_2_material);
- ;
+ return mat_ids[1];
}
bool isConditonVerified() {
-
/// check if material has been (re)-assigned correctly
- Mesh & mesh = model.getMesh();
- UInt spatial_dimension = mesh.getSpatialDimension();
- GhostType ghost_type = _not_ghost;
-
- for (auto & type : mesh.elementTypes(spatial_dimension, ghost_type)) {
- Array<UInt> & mat_indexes = model.getMaterialByElement(type, ghost_type);
- UInt nb_element = mesh.getNbElement(type, ghost_type);
- Array<Real>::iterator<Vector<Real>> bary =
- barycenters(type, ghost_type).begin(spatial_dimension);
- for (UInt elem = 0; elem < nb_element; ++elem, ++bary) {
+ auto & mesh = model.getMesh();
+ auto spatial_dimension = mesh.getSpatialDimension();
+ for (const auto & type : mesh.elementTypes(spatial_dimension)) {
+ auto & mat_indexes = model.getMaterialByElement(type);
+ for (auto && data :
+ enumerate(make_view(barycenters(type), spatial_dimension))) {
+ auto elem = std::get<0>(data);
+ auto & bary = std::get<1>(data);
/// compare element_index_by material to material index that should be
/// assigned due to the geometry of the interface
UInt mat_index;
- if ((*bary)(!horizontal) < pos_interface)
- mat_index = model.getMaterialIndex(mat_1_material);
- else
- mat_index = model.getMaterialIndex(mat_2_material);
+ if (bary(horizontal) < pos_interface) {
+ mat_index = mat_ids[0];
+ } else {
+ mat_index = mat_ids[1];
+ }
- if (mat_indexes(elem) != mat_index)
+ if (mat_indexes(elem) != mat_index) {
/// wrong material index, make test fail
return false;
+ }
}
}
return true;
}
- void moveInterface(Real & pos_new, bool & horizontal_new) {
+ void moveInterface(Real & pos_new, UInt horizontal_new) {
/// update position and orientation of material interface plane
pos_interface = pos_new;
horizontal = horizontal_new;
model.reassignMaterial();
}
protected:
SolidMechanicsModel & model;
ElementTypeMapArray<Real> barycenters;
+ std::array<UInt, 2> mat_ids;
+ UInt horizontal;
+ Real pos_interface;
+ bool materials_set{false};
std::string mat_1_material;
std::string mat_2_material;
- bool horizontal;
- Real pos_interface;
};
/* -------------------------------------------------------------------------- */
/* Main */
/* -------------------------------------------------------------------------- */
int main(int argc, char * argv[]) {
bool test_passed;
debug::setDebugLevel(dblWarning);
initialize("two_materials.dat", argc, argv);
/// specify position and orientation of material interface plane
- bool horizontal = true;
Real pos_interface = 0.;
UInt spatial_dimension = 3;
const auto & comm = Communicator::getStaticCommunicator();
Int prank = comm.whoAmI();
Mesh mesh(spatial_dimension);
- if (prank == 0)
+ if (prank == 0) {
mesh.read("cube_two_materials.msh");
+ }
mesh.distribute();
/// model creation
SolidMechanicsModel model(mesh);
/// assign the two different materials using the
/// StraightInterfaceMaterialSelector
auto && mat_selector = std::make_shared<StraightInterfaceMaterialSelector>(
- model, "mat_1", "mat_2", horizontal, pos_interface);
+ model, _x, pos_interface, "mat_1", "mat_2");
model.setMaterialSelector(mat_selector);
model.initFull(_analysis_method = _static);
MeshUtils::buildFacets(mesh);
/// check if different materials have been assigned correctly
test_passed = mat_selector->isConditonVerified();
- if (!test_passed) {
+ if (not test_passed) {
AKANTU_ERROR("materials not correctly assigned");
return EXIT_FAILURE;
}
+ model.addDumpField("material_index");
/// change orientation of material interface plane
- horizontal = false;
- mat_selector->moveInterface(pos_interface, horizontal);
- // model.dump();
+ model.dump();
+ mat_selector->moveInterface(pos_interface, _y);
+ model.dump();
/// test if material has been reassigned correctly
test_passed = mat_selector->isConditonVerified();
- if (!test_passed) {
+ if (not test_passed) {
AKANTU_ERROR("materials not correctly reassigned");
return EXIT_FAILURE;
}
finalize();
if (prank == 0)
std::cout << "OK: test passed!" << std::endl;
return EXIT_SUCCESS;
}
/* -------------------------------------------------------------------------- */
diff --git a/test/test_model/test_structural_mechanics_model/CMakeLists.txt b/test/test_model/test_structural_mechanics_model/CMakeLists.txt
index 8891014e0..81dff85c7 100644
--- a/test/test_model/test_structural_mechanics_model/CMakeLists.txt
+++ b/test/test_model/test_structural_mechanics_model/CMakeLists.txt
@@ -1,59 +1,72 @@
#===============================================================================
# @file CMakeLists.txt
#
# @author Fabian Barras <fabian.barras@epfl.ch>
#
# @date creation: Fri Sep 03 2010
# @date last modification: Fri Feb 09 2018
#
# @brief Structural Mechanics Model Tests
#
# @section LICENSE
#
-# Copyright (©) 2010-2018 EPFL (Ecole Polytechnique Fédérale de Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)
+# 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 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.
+# 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 <http://www.gnu.org/licenses/>.
+# You should have received a copy of the GNU Lesser General Public License along
+# with Akantu. If not, see <http://www.gnu.org/licenses/>.
#
#===============================================================================
# Adding sources
register_gtest_sources(
SOURCES test_structural_mechanics_model_bernoulli_beam_2.cc
PACKAGE implicit structural_mechanics
-)
+ )
register_gtest_sources(
SOURCES test_structural_mechanics_model_bernoulli_beam_3.cc
PACKAGE implicit structural_mechanics
-)
+ )
register_gtest_sources(
SOURCES test_structural_mechanics_model_discrete_kirchhoff_triangle_18.cc
PACKAGE implicit structural_mechanics
-)
+ )
+
+register_gtest_sources(
+ SOURCES test_structural_mechanics_model_bernoulli_beam_dynamics.cc
+ PACKAGE implicit structural_mechanics
+ )
#===============================================================================
# Adding meshes for element types
package_get_element_types(structural_mechanics _types)
foreach(_type ${_types})
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${_type}.msh)
list(APPEND _meshes ${_type}.msh)
#register_fem_test(fe_engine_precomputation ${_type })
else()
if(NOT ${_type} STREQUAL _point_1)
message("The mesh ${_type}.msh is missing, the fe_engine test cannot be activated without it")
endif()
endif()
endforeach()
#===============================================================================
# Registering google test
register_gtest_test(test_structural_mechanics
- FILES_TO_COPY ${_meshes})
-
+ FILES_TO_COPY ${_meshes}
+ )
diff --git a/test/test_model/test_structural_mechanics_model/test_structural_mechanics_model_bernoulli_beam_2.cc b/test/test_model/test_structural_mechanics_model/test_structural_mechanics_model_bernoulli_beam_2.cc
index d1e1d7d97..65c9f4dd6 100644
--- a/test/test_model/test_structural_mechanics_model/test_structural_mechanics_model_bernoulli_beam_2.cc
+++ b/test/test_model/test_structural_mechanics_model/test_structural_mechanics_model_bernoulli_beam_2.cc
@@ -1,112 +1,112 @@
/**
* @file test_structural_mechanics_model_bernoulli_beam_2.cc
*
* @author Fabian Barras <fabian.barras@epfl.ch>
* @author Lucas Frerot <lucas.frerot@epfl.ch>
*
* @date creation: Fri Jul 15 2011
* @date last modification: Fri Feb 09 2018
*
* @brief Computation of the analytical exemple 1.1 in the TGC vol 6
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "test_structural_mechanics_model_fixture.hh"
/* -------------------------------------------------------------------------- */
#include <gtest/gtest.h>
using namespace akantu;
/* -------------------------------------------------------------------------- */
class TestStructBernoulli2
: public TestStructuralFixture<element_type_t<_bernoulli_beam_2>> {
using parent = TestStructuralFixture<element_type_t<_bernoulli_beam_2>>;
public:
void addMaterials() override {
mat.E = 3e10;
mat.I = 0.0025;
mat.A = 0.01;
this->model->addMaterial(mat);
mat.E = 3e10;
mat.I = 0.00128;
mat.A = 0.01;
this->model->addMaterial(mat);
}
void assignMaterials() override {
auto & materials = this->model->getElementMaterial(parent::type);
materials(0) = 0;
materials(1) = 1;
}
- void setDirichlets() override {
+ void setDirichletBCs() override {
auto boundary = this->model->getBlockedDOFs().begin(parent::ndof);
// clang-format off
*boundary = {true, true, true}; ++boundary;
*boundary = {false, true, false}; ++boundary;
*boundary = {false, true, false}; ++boundary;
// clang-format on
}
- void setNeumanns() override {
+ void setNeumannBCs() override {
Real M = 3600; // Nm
- Real q = -6000; // kN/m
+ Real q = 6000; // kN/m
Real L = 10; // m
auto & forces = this->model->getExternalForce();
forces(2, 2) = -M; // moment on last node
#if 1 // as long as integration is not available
- forces(0, 1) = q * L / 2;
- forces(0, 2) = q * L * L / 12;
- forces(1, 1) = q * L / 2;
- forces(1, 2) = -q * L * L / 12;
+ forces(0, 1) = -q * L / 2;
+ forces(0, 2) = -q * L * L / 12;
+ forces(1, 1) = -q * L / 2;
+ forces(1, 2) = q * L * L / 12;
#else
auto & group = mesh.createElementGroup("lin_force");
group.add({type, 0, _not_ghost});
Vector<Real> lin_force = {0, q, 0};
// a linear force is not actually a *boundary* condition
// it is equivalent to a volume force
model.applyBC(BC::Neumann::FromSameDim(lin_force), group);
#endif
forces(2, 0) = mat.E * mat.A / 18;
}
protected:
StructuralMaterial mat;
};
/* -------------------------------------------------------------------------- */
TEST_F(TestStructBernoulli2, TestDisplacements) {
model->solveStep();
auto d1 = model->getDisplacement()(1, 2);
auto d2 = model->getDisplacement()(2, 2);
auto d3 = model->getDisplacement()(1, 0);
Real tol = Math::getTolerance();
EXPECT_NEAR(d1, 5.6 / 4800, tol); // first rotation
EXPECT_NEAR(d2, -3.7 / 4800, tol); // second rotation
EXPECT_NEAR(d3, 10. / 18, tol); // axial deformation
}
diff --git a/test/test_model/test_structural_mechanics_model/test_structural_mechanics_model_bernoulli_beam_3.cc b/test/test_model/test_structural_mechanics_model/test_structural_mechanics_model_bernoulli_beam_3.cc
index bdff772d6..4b334e128 100644
--- a/test/test_model/test_structural_mechanics_model/test_structural_mechanics_model_bernoulli_beam_3.cc
+++ b/test/test_model/test_structural_mechanics_model/test_structural_mechanics_model_bernoulli_beam_3.cc
@@ -1,101 +1,99 @@
/**
* @file test_structural_mechanics_model_bernoulli_beam_3.cc
*
* @author Lucas Frerot <lucas.frerot@epfl.ch>
*
* @date creation: Fri Jul 15 2011
* @date last modification: Fri Feb 09 2018
*
* @brief Computation of the analytical exemple 1.1 in the TGC vol 6
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "test_structural_mechanics_model_fixture.hh"
/* -------------------------------------------------------------------------- */
#include <gtest/gtest.h>
/* -------------------------------------------------------------------------- */
using namespace akantu;
-class TestStructBernoulli3
+class TestStructBernoulli3Static
: public TestStructuralFixture<element_type_t<_bernoulli_beam_3>> {
using parent = TestStructuralFixture<element_type_t<_bernoulli_beam_3>>;
public:
void readMesh(std::string filename) override {
parent::readMesh(filename);
- auto & normals =
- this->mesh->getElementalData<Real>("extra_normal")
- .alloc(0, parent::spatial_dimension, parent::type, _not_ghost);
- Vector<Real> normal = {0, 0, 1};
- normals.push_back(normal);
- normal = {0, 0, 1};
- normals.push_back(normal);
+ }
+
+ void setNormals() override {
+ auto &normals = this->mesh->getData<Real>("extra_normal", parent::type);
+ normals(0, _z) = 1;
+ normals(1, _z) = 1;
}
void addMaterials() override {
StructuralMaterial mat;
mat.E = 1;
mat.Iz = 1;
mat.Iy = 1;
mat.A = 1;
mat.GJ = 1;
this->model->addMaterial(mat);
}
- void setDirichlets() override {
+ void setDirichletBCs() override {
// Boundary conditions (blocking all DOFs of nodes 2 & 3)
auto boundary = ++this->model->getBlockedDOFs().begin(parent::ndof);
// clang-format off
*boundary = {true, true, true, true, true, true}; ++boundary;
*boundary = {true, true, true, true, true, true}; ++boundary;
// clang-format on
}
- void setNeumanns() override {
+ void setNeumannBCs() override {
// Forces
Real P = 1; // N
auto & forces = this->model->getExternalForce();
forces.zero();
forces(0, 2) = -P; // vertical force on first node
}
void assignMaterials() override {
model->getElementMaterial(parent::type).set(0);
}
};
/* -------------------------------------------------------------------------- */
-
-TEST_F(TestStructBernoulli3, TestDisplacements) {
+TEST_F(TestStructBernoulli3Static, TestDisplacements) {
model->solveStep();
auto vz = model->getDisplacement()(0, 2);
auto thy = model->getDisplacement()(0, 4);
auto thx = model->getDisplacement()(0, 3);
auto thz = model->getDisplacement()(0, 5);
Real tol = Math::getTolerance();
EXPECT_NEAR(vz, -5. / 48, tol);
EXPECT_NEAR(thy, -std::sqrt(2) / 8, tol);
EXPECT_NEAR(thz, 0, tol);
EXPECT_NEAR(thx, 0, tol);
}
diff --git a/test/test_model/test_structural_mechanics_model/test_structural_mechanics_model_bernoulli_beam_dynamics.cc b/test/test_model/test_structural_mechanics_model/test_structural_mechanics_model_bernoulli_beam_dynamics.cc
index 2a7a43402..13ed7b1f3 100644
--- a/test/test_model/test_structural_mechanics_model/test_structural_mechanics_model_bernoulli_beam_dynamics.cc
+++ b/test/test_model/test_structural_mechanics_model/test_structural_mechanics_model_bernoulli_beam_dynamics.cc
@@ -1,207 +1,367 @@
/**
* @file test_structural_mechanics_model_bernoulli_beam_dynamics.cc
*
* @author Sébastien Hartmann <sebastien.hartmann@epfl.ch>
*
* @date creation: Mon Jul 07 2014
* @date last modification: Wed Feb 03 2016
*
* @brief Test for _bernouilli_beam in dynamic
*
*
* Copyright (©) 2014-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
-#include <fstream>
-#include <limits>
-
+#include "test_structural_mechanics_model_fixture.hh"
/* -------------------------------------------------------------------------- */
-#include "aka_common.hh"
-#include "material.hh"
-#include "mesh.hh"
-#include "mesh_io.hh"
-#include "mesh_io_msh_struct.hh"
+#include "dof_manager.hh"
+#include "mesh_accessor.hh"
+#include "non_linear_solver_newton_raphson.hh"
#include "structural_mechanics_model.hh"
-
+/* -------------------------------------------------------------------------- */
+#include <fstream>
#include <iostream>
-using namespace akantu;
+#include <limits>
/* -------------------------------------------------------------------------- */
-#define TYPE _bernoulli_beam_2
+using namespace akantu;
+/* -------------------------------------------------------------------------- */
static Real analytical_solution(Real time, Real L, Real rho, Real E,
__attribute__((unused)) Real A, Real I,
Real F) {
Real omega = M_PI * M_PI / L / L * sqrt(E * I / rho);
Real sum = 0.;
UInt i = 5;
for (UInt n = 1; n <= i; n += 2) {
sum += (1. - cos(n * n * omega * time)) / pow(n, 4);
}
return 2. * F * pow(L, 3) / pow(M_PI, 4) / E / I * sum;
}
-// load
-const Real F = 0.5e4;
+template <class Type>
+class TestStructBernoulliDynamic : public TestStructuralFixture<Type> {
+ using parent = TestStructuralFixture<Type>;
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
+public:
+ const UInt nb_element{40};
+ const Real L{2};
+ const Real le{L / nb_element};
+
+ const UInt nb_nodes{nb_element + 1};
+ const Real F{1e4};
+
+ StructuralMaterial mat;
+
+ void readMesh(std::string /*filename*/) override {
+ MeshAccessor mesh_accessor(*this->mesh);
+ auto & nodes = mesh_accessor.getNodes();
+ nodes.resize(nb_nodes);
+
+ nodes.set(0.);
+
+ for (auto && data : enumerate(make_view(nodes, this->spatial_dimension))) {
+ auto & node = std::get<1>(data);
+ UInt i = std::get<0>(data);
+ node[_x] = i * le;
+ }
+
+ this->mesh->addConnectivityType(parent::type);
+ auto & connectivities = mesh_accessor.getConnectivity(parent::type);
+ connectivities.resize(nb_element);
+ for (auto && data : enumerate(make_view(connectivities, 2))) {
+ UInt i = std::get<0>(data);
+ auto & connectivity = std::get<1>(data);
+
+ connectivity = {i, i + 1};
+ }
+
+ mesh_accessor.makeReady();
+ }
+
+ void setNormals() override {
+ if (this->spatial_dimension != 3) {
+ return;
+ }
+ auto & normals =
+ this->mesh->template getData<Real>("extra_normal", parent::type);
+ normals.resize(nb_element);
+
+ for (auto && normal : make_view(normals, this->spatial_dimension)) {
+ normal = {0., 0., 1.};
+ }
+ }
+
+ AnalysisMethod getAnalysisMethod() const override {
+ return _implicit_dynamic;
+ }
-int main(int argc, char * argv[]) {
- initialize(argc, argv);
- Mesh beams(2);
- debug::setDebugLevel(dblWarning);
- const ElementType type = _bernoulli_beam_2;
-
- /* --------------------------------------------------------------------------
- */
- // Mesh
- UInt nb_element = 8;
- UInt nb_nodes = nb_element + 1;
- Real total_length = 10.;
- Real length = total_length / nb_element;
- Real heigth = 1.;
-
- Array<Real> & nodes = const_cast<Array<Real> &>(beams.getNodes());
- nodes.resize(nb_nodes);
-
- beams.addConnectivityType(type);
- Array<UInt> & connectivity =
- const_cast<Array<UInt> &>(beams.getConnectivity(type));
- connectivity.resize(nb_element);
-
- beams.initNormals();
- Array<Real> & normals = const_cast<Array<Real> &>(beams.getNormals(type));
- normals.resize(nb_element);
-
- for (UInt i = 0; i < nb_nodes; ++i) {
- nodes(i, 0) = i * length;
- nodes(i, 1) = 0;
+ void addMaterials() override {
+ this->mat.E = 1e9;
+ this->mat.rho = 10;
+ this->mat.I = 1;
+ this->mat.Iz = 1;
+ this->mat.Iy = 1;
+ this->mat.A = 1;
+ this->mat.GJ = 1;
+
+ this->model->addMaterial(mat);
+ }
+
+ void setDirichletBCs() override {
+ auto & boundary = this->model->getBlockedDOFs();
+ boundary.set(false);
+
+ boundary(0, _x) = true;
+ boundary(0, _y) = true;
+
+ boundary(nb_nodes - 1, _y) = true;
+
+ if (this->spatial_dimension == 3) {
+ boundary(0, _z) = true;
+ boundary(nb_nodes - 1, _z) = true;
+ }
}
- for (UInt i = 0; i < nb_element; ++i) {
- connectivity(i, 0) = i;
- connectivity(i, 1) = i + 1;
+ void setNeumannBCs() override {
+ auto node_to_print = nb_nodes / 2;
+ // Forces
+ auto & forces = this->model->getExternalForce();
+ forces.zero();
+ forces(node_to_print, _y) = F;
}
- /* --------------------------------------------------------------------------
- */
- // Materials
-
- StructuralMechanicsModel model(beams);
-
- StructuralMaterial mat1;
- mat1.E = 120e6;
- mat1.rho = 1000;
- mat1.A = heigth;
- mat1.I = heigth * heigth * heigth / 12;
- model.addMaterial(mat1);
-
- /* --------------------------------------------------------------------------
- */
- // Forces
- // model.initFull();
- model.initFull(StructuralMechanicsModelOptions(_implicit_dynamic));
-
- const Array<Real> & position = beams.getNodes();
- Array<Real> & forces = model.getForce();
- Array<Real> & displacement = model.getDisplacement();
- Array<bool> & boundary = model.getBlockedDOFs();
-
- UInt node_to_print = -1;
-
- forces.zero();
- displacement.zero();
- // boundary.zero();
- // model.getElementMaterial(type)(i,0) = 0;
- // model.getElementMaterial(type)(i,0) = 1;
- for (UInt i = 0; i < nb_element; ++i) {
- model.getElementMaterial(type)(i, 0) = 0;
+ void assignMaterials() override {
+ this->model->getElementMaterial(parent::type).set(0);
+ }
+};
+
+using beam_types = gtest_list_t<std::tuple<element_type_t<_bernoulli_beam_2>,
+ element_type_t<_bernoulli_beam_3>>>;
+
+TYPED_TEST_SUITE(TestStructBernoulliDynamic, beam_types, );
+
+template <class Type>
+void getElementMassMatrix(const StructuralMaterial & /*material*/, Real /*l*/,
+ Matrix<Real> & /*M*/) {}
+
+template <class Type>
+void getElementStifnessMatrix(const StructuralMaterial & /*material*/,
+ Real /*l*/, Matrix<Real> & /*M*/) {}
+
+template <>
+void getElementMassMatrix<element_type_t<_bernoulli_beam_2>>(
+ const StructuralMaterial & material, Real l, Matrix<Real> & M) {
+ auto A = material.A;
+ auto rho = material.rho;
+ // clang-format off
+ M = rho * A * l / 420. * Matrix<Real>({
+ {140, 0, 0, 70, 0, 0},
+ { 0, 156, 22*l, 0, 54, -13*l},
+ { 0, 22*l, 4*l*l, 0, 13*l, -3*l*l},
+ { 70, 0, 0, 140, 0, 0},
+ { 0, 54, 13*l, 0, 156, -22*l},
+ { 0,-13*l, -3*l*l, 0, -22*l, 4*l*l}});
+ // clang-format on
+}
+
+template <>
+void getElementStifnessMatrix<element_type_t<_bernoulli_beam_2>>(
+ const StructuralMaterial & material, Real l, Matrix<Real> & K) {
+ auto E = material.E;
+ auto A = material.A;
+ auto I = material.I;
+
+ auto l_2 = l * l;
+ auto l_3 = l * l * l;
+ // clang-format off
+ K = Matrix<Real>({
+ { E*A/l, 0, 0, -E*A/l, 0, 0},
+ { 0, 12*E*I/l_3, 6*E*I/l_2, 0, -12*E*I/l_3, 6*E*I/l_2},
+ { 0, 6*E*I/l_2, 4*E*I/l, 0, -6*E*I/l_2, 2*E*I/l},
+ {-E*A/l, 0, 0, E*A/l, 0, 0},
+ { 0, -12*E*I/l_3, -6*E*I/l_2, 0, 12*E*I/l_3, -6*E*I/l_2},
+ { 0, 6*E*I/l_2, 2*E*I/l, 0, -6*E*I/l_2, 4*E*I/l}});
+ // clang-format on
+}
+
+template <>
+void getElementMassMatrix<element_type_t<_bernoulli_beam_3>>(
+ const StructuralMaterial & material, Real l, Matrix<Real> & M) {
+ auto A = material.A;
+ auto rho = material.rho;
+ // clang-format off
+ M = rho * A * l / 420. * Matrix<Real>({
+ {140, 0, 0, 0, 0, 0, 70, 0, 0, 0, 0, 0},
+ { 0, 156, 0, 0, 0, 22*l, 0, 54, 0, 0, 0, -13*l},
+ { 0, 0, 156, 0, -22*l, 0, 0, 0, 54, 0, 13*l, 0},
+ { 0, 0, 0, 140, 0, 0, 0, 0, 0, 70, 0, 0},
+ { 0, 0, -22*l, 0, 4*l*l, 0, 0, 0, -13*l, 0, -3*l*l, 0},
+ { 0, 22*l, 0, 0, 0, 4*l*l, 0, 13*l, 0, 0, 0, -3*l*l},
+ { 70, 0, 0, 0, 0, 0, 140, 0, 0, 0, 0, 0},
+ { 0, 54, 0, 0, 0, 13*l, 0, 156, 0, 0, 0, -22*l},
+ { 0, 0, 54, 0, -13*l, 0, 0, 0, 156, 0, 22*l, 0},
+ { 0, 0, 0, 70, 0, 0, 0, 0, 0, 140, 0, 0},
+ { 0, 0, 13*l, 0, -3*l*l, 0, 0, 0, 22*l, 0, 4*l*l, 0},
+ { 0, -13*l, 0, 0, 0, -3*l*l, 0, -22*l, 0, 0, 0, 4*l*l}});
+ // clang-format on
+}
+
+template <>
+void getElementStifnessMatrix<element_type_t<_bernoulli_beam_3>>(
+ const StructuralMaterial & material, Real l, Matrix<Real> & K) {
+ auto E = material.E;
+ auto A = material.A;
+ auto Iy = material.Iy;
+ auto Iz = material.Iz;
+ auto GJ = material.GJ;
+
+ auto a1 = E * A / l;
+ auto b1 = 12 * E * Iz / l / l / l;
+ auto b2 = 6 * E * Iz / l / l;
+ auto b3 = 4 * E * Iz / l;
+ auto b4 = 2 * E * Iz / l;
+
+ auto c1 = 12 * E * Iy / l / l / l;
+ auto c2 = 6 * E * Iy / l / l;
+ auto c3 = 4 * E * Iy / l;
+ auto c4 = 2 * E * Iy / l;
+
+ auto d1 = GJ / l;
+
+ // clang-format off
+ K = Matrix<Real>({
+ { a1, 0, 0, 0, 0, 0, -a1, 0, 0, 0, 0, 0},
+ { 0, b1, 0, 0, 0, b2, 0, -b1, 0, 0, 0, b2},
+ { 0, 0, c1, 0, -c2, 0, 0, 0, -c1, 0, -c2, 0},
+ { 0, 0, 0, d1, 0, 0, 0, 0, 0, -d1, 0, 0},
+ { 0, 0, -c2, 0, c3, 0, 0, 0, c2, 0, c4, 0},
+ { 0, b2, 0, 0, 0, b3, 0, -b2, 0, 0, 0, b4},
+ { -a1, 0, 0, 0, 0, 0, a1, 0, 0, 0, 0, 0},
+ { 0, -b1, 0, 0, 0, -b2, 0, b1, 0, 0, 0, -b2},
+ { 0, 0, -c1, 0, c2, 0, 0, 0, c1, 0, c2, 0},
+ { 0, 0, 0, -d1, 0, 0, 0, 0, 0, d1, 0, 0},
+ { 0, 0, -c2, 0, c4, 0, 0, 0, c2, 0, c3, 0},
+ { 0, b2, 0, 0, 0, b4, 0, -b2, 0, 0, 0, b3}});
+ // clang-format on
+}
+
+TYPED_TEST(TestStructBernoulliDynamic, TestBeamMatrices) {
+ this->model->assembleMatrix("M");
+ this->model->assembleMatrix("K");
+
+ const auto & K = this->model->getDOFManager().getMatrix("K");
+ const auto & M = this->model->getDOFManager().getMatrix("M");
+
+ Matrix<Real> Ka(this->nb_nodes * this->ndof, this->nb_nodes * this->ndof, 0.);
+ Matrix<Real> Ma(this->nb_nodes * this->ndof, this->nb_nodes * this->ndof, 0.);
+
+ Matrix<Real> Ke(this->ndof * 2, this->ndof * 2);
+ Matrix<Real> Me(this->ndof * 2, this->ndof * 2);
+
+ getElementMassMatrix<TypeParam>(this->mat, this->le, Me);
+ getElementStifnessMatrix<TypeParam>(this->mat, this->le, Ke);
+
+ auto assemble = [&](auto && nodes, auto && M, auto && Me) {
+ auto n1 = nodes[0];
+ auto n2 = nodes[1];
+ for (auto i : arange(this->ndof)) {
+ for (auto j : arange(this->ndof)) {
+ M(n1 * this->ndof + i, n1 * this->ndof + j) += Me(i, j);
+ M(n2 * this->ndof + i, n2 * this->ndof + j) +=
+ Me(this->ndof + i, this->ndof + j);
+ M(n1 * this->ndof + i, n2 * this->ndof + j) += Me(i, this->ndof + j);
+ M(n2 * this->ndof + i, n1 * this->ndof + j) += Me(this->ndof + i, j);
+ }
+ }
+ };
+
+ auto && connectivities = this->mesh->getConnectivity(this->type);
+
+ for (auto && connectivity : make_view(connectivities, 2)) {
+ assemble(connectivity, Ka, Ke);
+ assemble(connectivity, Ma, Me);
}
- for (UInt n = 0; n < nb_nodes; ++n) {
- Real x = position(n, 0);
- // Real y = position(n, 1);
+ auto tol = 1e-13;
- if (Math::are_float_equal(x, total_length / 2.)) {
- forces(n, 1) = F;
- node_to_print = n;
+ auto Ka_max = Ka.template norm<L_inf>();
+ auto Ma_max = Ma.template norm<L_inf>();
+
+ for (auto i : arange(Ka.rows())) {
+ for (auto j : arange(Ka.cols())) {
+ EXPECT_NEAR(Ka(i, j), K(i, j), tol * Ka_max);
+ EXPECT_NEAR(Ma(i, j), M(i, j), tol * Ma_max);
}
}
+}
+
+TYPED_TEST(TestStructBernoulliDynamic, TestBeamOscilation) {
+ Real time_step = 1e-6;
+ this->model->setTimeStep(time_step);
+
+ auto & solver = this->model->getNonLinearSolver();
+ solver.set("max_iterations", 100);
+ solver.set("threshold", 1e-8);
+ solver.set("convergence_type", SolveConvergenceCriteria::_solution);
+
+ auto node_to_print = this->nb_nodes / 2;
+ auto & d = this->model->getDisplacement()(node_to_print, _y);
std::ofstream pos;
- pos.open("position.csv");
- if (!pos.good()) {
- std::cerr << "Cannot open file" << std::endl;
- exit(EXIT_FAILURE);
+ std::string filename = "position" + std::to_string(this->type) + ".csv";
+ pos.open(filename);
+ if (not pos.good()) {
+ AKANTU_ERROR("Cannot open file \"position.csv\"");
}
pos << "id,time,position,solution" << std::endl;
- // model.computeForcesFromFunction<type>(load, _bft_traction)
- /* --------------------------------------------------------------------------
- */
- // Boundary conditions
- // true ~ displacement is blocked
- boundary(0, 0) = true;
- boundary(0, 1) = true;
- boundary(nb_nodes - 1, 1) = true;
- /* --------------------------------------------------------------------------
- */
- // "Solve"
-
- Real time = 0;
- model.assembleStiffnessMatrix();
- model.assembleMass();
- model.dump();
- model.getStiffnessMatrix().saveMatrix("K.mtx");
- model.getMassMatrix().saveMatrix("M.mt");
- Real time_step = 1e-4;
- model.setTimeStep(time_step);
-
- std::cout << "Time"
- << " | "
- << "Mid-Span Displacement" << std::endl;
-
- /// time loop
- for (UInt s = 1; time < 0.64; ++s) {
-
- model.solveStep<_scm_newton_raphson_tangent,
- SolveConvergenceCriteria::_increment>(1e-12, 1000);
-
- pos << s << "," << time << "," << displacement(node_to_print, 1) << ","
- << analytical_solution(s * time_step, total_length, mat1.rho, mat1.E,
- mat1.A, mat1.I, F)
- << std::endl;
- // pos << s << "," << time << "," << displacement(node_to_print, 1) <<
- // "," << analytical_solution(s*time_step) << std::endl;
-
- time += time_step;
- if (s % 100 == 0)
- std::cout << time << " | " << displacement(node_to_print, 1)
- << std::endl;
- model.dump();
- }
+//#define debug
+#ifdef debug
+ this->model->addDumpFieldVector("displacement");
+ this->model->addDumpField("blocked_dofs");
+ this->model->addDumpFieldVector("external_force");
+ this->model->addDumpFieldVector("internal_force");
+ this->model->addDumpFieldVector("acceleration");
+ this->model->addDumpFieldVector("velocity");
+ this->model->dump();
+#endif
+
+ this->model->getDisplacement().set(0.);
+
+ Real tol = 1e-6;
+ Real time = 0.;
+ for (UInt s = 1; s < 300; ++s) {
+ EXPECT_NO_THROW(this->model->solveStep());
- pos.close();
+ time = s * time_step;
- finalize();
+ auto da = analytical_solution(time, this->L, this->mat.rho, this->mat.E,
+ this->mat.A, this->mat.Iy, this->F);
+
+ pos << s << "," << time << "," << d << "," << da << std::endl;
+
+#ifdef debug
+ this->model->dump();
+#endif
+ EXPECT_NEAR(d, da, tol);
+ }
- return EXIT_SUCCESS;
}
diff --git a/test/test_model/test_structural_mechanics_model/test_structural_mechanics_model_discrete_kirchhoff_triangle_18.cc b/test/test_model/test_structural_mechanics_model/test_structural_mechanics_model_discrete_kirchhoff_triangle_18.cc
index d254434cf..adbf05c80 100644
--- a/test/test_model/test_structural_mechanics_model/test_structural_mechanics_model_discrete_kirchhoff_triangle_18.cc
+++ b/test/test_model/test_structural_mechanics_model/test_structural_mechanics_model_discrete_kirchhoff_triangle_18.cc
@@ -1,111 +1,111 @@
/**
* @file test_structural_mechanics_model_discrete_kirchhoff_triangle_18.cc
*
* @author Fabian Barras <fabian.barras@epfl.ch>
* @author Lucas Frerot <lucas.frerot@epfl.ch>
*
* @date creation: Fri Jul 15 2011
* @date last modification: Wed Feb 21 2018
*
* @brief Computation of the analytical exemple 1.1 in the TGC vol 6
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "sparse_matrix.hh"
#include "test_structural_mechanics_model_fixture.hh"
/* -------------------------------------------------------------------------- */
#include <gtest/gtest.h>
using namespace akantu;
/* -------------------------------------------------------------------------- */
class TestStructDKT18 : public TestStructuralFixture<
element_type_t<_discrete_kirchhoff_triangle_18>> {
using parent =
TestStructuralFixture<element_type_t<_discrete_kirchhoff_triangle_18>>;
public:
void addMaterials() override {
mat.E = 1;
mat.t = 1;
mat.nu = 0.3;
this->model->addMaterial(mat);
}
void assignMaterials() override {
auto & materials = this->model->getElementMaterial(parent::type);
std::fill(materials.begin(), materials.end(), 0);
}
- void setDirichlets() override {
+ void setDirichletBCs() override {
this->model->getBlockedDOFs().set(true);
auto center_node = this->model->getBlockedDOFs().end(parent::ndof) - 1;
*center_node = {false, false, false, false, false, true};
this->model->getDisplacement().zero();
auto disp = ++this->model->getDisplacement().begin(parent::ndof);
// Displacement field from Batoz Vol. 2 p. 392
// with theta to beta correction from discrete Kirchhoff condition
// see also the master thesis of Michael Lozano
// clang-format off
// This displacement field tests membrane and bending modes
*disp = {40, 20, -800 , -20, 40, 0}; ++disp;
*disp = {50, 40, -1400, -40, 50, 0}; ++disp;
*disp = {10, 20, -200 , -20, 10, 0}; ++disp;
// This displacement tests the bending mode
// *disp = {0, 0, -800 , -20, 40, 0}; ++disp;
// *disp = {0, 0, -1400, -40, 50, 0}; ++disp;
// *disp = {0, 0, -200 , -20, 10, 0}; ++disp;
// This displacement tests the membrane mode
// *disp = {40, 20, 0, 0, 0, 0}; ++disp;
// *disp = {50, 40, 0, 0, 0, 0}; ++disp;
// *disp = {10, 20, 0, 0, 0, 0}; ++disp;
// clang-format on
}
- void setNeumanns() override {}
+ void setNeumannBCs() override {}
protected:
StructuralMaterial mat;
};
/* -------------------------------------------------------------------------- */
// Batoz Vol 2. patch test, ISBN 2-86601-259-3
TEST_F(TestStructDKT18, TestDisplacements) {
model->solveStep();
Vector<Real> solution = {22.5, 22.5, -337.5, -22.5, 22.5, 0};
auto nb_nodes = this->model->getDisplacement().size();
Vector<Real> center_node_disp =
model->getDisplacement().begin(solution.size())[nb_nodes - 1];
auto error = solution - center_node_disp;
EXPECT_NEAR(error.norm<L_2>(), 0., 1e-12);
}
diff --git a/test/test_model/test_structural_mechanics_model/test_structural_mechanics_model_fixture.hh b/test/test_model/test_structural_mechanics_model/test_structural_mechanics_model_fixture.hh
index b1bbc29bb..14b48d567 100644
--- a/test/test_model/test_structural_mechanics_model/test_structural_mechanics_model_fixture.hh
+++ b/test/test_model/test_structural_mechanics_model/test_structural_mechanics_model_fixture.hh
@@ -1,104 +1,115 @@
/**
* @file test_structural_mechanics_model_fixture.hh
*
* @author Lucas Frerot <lucas.frerot@epfl.ch>
*
* @date creation: Tue Nov 14 2017
* @date last modification: Fri Feb 09 2018
*
* @brief Main test for structural model
*
*
* Copyright (©) 2016-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "element_class_structural.hh"
#include "structural_mechanics_model.hh"
#include "test_gtest_utils.hh"
/* -------------------------------------------------------------------------- */
#include <gtest/gtest.h>
#include <vector>
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_TEST_STRUCTURAL_MECHANICS_MODEL_FIXTURE_HH_
#define AKANTU_TEST_STRUCTURAL_MECHANICS_MODEL_FIXTURE_HH_
using namespace akantu;
template <typename type_> class TestStructuralFixture : public ::testing::Test {
public:
static constexpr const ElementType type = type_::value;
static constexpr const size_t spatial_dimension =
ElementClass<type>::getSpatialDimension();
static const UInt ndof = ElementClass<type>::getNbDegreeOfFreedom();
void SetUp() override {
const auto spatial_dimension = this->spatial_dimension;
mesh = std::make_unique<Mesh>(spatial_dimension);
readMesh(makeMeshName());
std::stringstream element_type;
element_type << this->type;
model = std::make_unique<StructuralMechanicsModel>(*mesh, _all_dimensions,
element_type.str());
- addMaterials();
- model->initFull();
- assignMaterials();
- setDirichlets();
- setNeumanns();
+ setNormals();
+ initModel();
}
+ virtual void initModel() {
+ this->addMaterials();
+ auto method = getAnalysisMethod();
+
+ this->model->initFull(_analysis_method = method);
+
+ this->assignMaterials();
+
+ this->setDirichletBCs();
+ this->setNeumannBCs();
+ }
+
+ virtual AnalysisMethod getAnalysisMethod() const { return _static; }
+
virtual void readMesh(std::string filename) {
mesh->read(filename, _miot_gmsh_struct);
}
virtual std::string makeMeshName() {
std::stringstream element_type;
element_type << type;
SCOPED_TRACE(element_type.str().c_str());
return element_type.str() + ".msh";
}
void TearDown() override {
model.reset(nullptr);
mesh.reset(nullptr);
}
virtual void addMaterials() = 0;
virtual void assignMaterials() = 0;
- virtual void setDirichlets() = 0;
- virtual void setNeumanns() = 0;
+ virtual void setDirichletBCs() = 0;
+ virtual void setNeumannBCs() = 0;
+ virtual void setNormals() {}
protected:
std::unique_ptr<Mesh> mesh;
std::unique_ptr<StructuralMechanicsModel> model;
};
template <typename type_>
constexpr ElementType TestStructuralFixture<type_>::type;
template <typename type_>
constexpr size_t TestStructuralFixture<type_>::spatial_dimension;
template <typename type_> const UInt TestStructuralFixture<type_>::ndof;
-// using types = gtest_list_t<StructuralTestElementTypes>;
+using structural_types = gtest_list_t<StructuralTestElementTypes>;
-// TYPED_TEST_SUITE(TestStructuralFixture, types);
#endif /* AKANTU_TEST_STRUCTURAL_MECHANICS_MODEL_FIXTURE_HH_ */
diff --git a/test/test_static_memory/CMakeLists.txt b/test/test_static_memory/CMakeLists.txt
deleted file mode 100644
index ae57de11e..000000000
--- a/test/test_static_memory/CMakeLists.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-#===============================================================================
-# @file CMakeLists.txt
-#
-# @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
-# @author Nicolas Richart <nicolas.richart@epfl.ch>
-#
-# @date creation: Fri Sep 03 2010
-# @date last modification: Wed Feb 03 2016
-#
-# @brief configuration for static memory tests
-#
-# @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 <http://www.gnu.org/licenses/>.
-#
-# @section DESCRIPTION
-#
-#===============================================================================
-
-register_test(test_static_memory SOURCES test_static_memory.cc PACKAGE core)
diff --git a/test/test_static_memory/test_static_memory.cc b/test/test_static_memory/test_static_memory.cc
deleted file mode 100644
index 361e1be51..000000000
--- a/test/test_static_memory/test_static_memory.cc
+++ /dev/null
@@ -1,56 +0,0 @@
-/**
- * @file test_static_memory.cc
- *
- * @author Nicolas Richart <nicolas.richart@epfl.ch>
- *
- * @date creation: Mon Jun 14 2010
- * @date last modification: Wed Feb 03 2016
- *
- * @brief unit test for the StaticMemory class
- *
- *
- * 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 <http://www.gnu.org/licenses/>.
- *
- */
-
-/* -------------------------------------------------------------------------- */
-#include <iostream>
-
-/* -------------------------------------------------------------------------- */
-#include "aka_array.hh"
-#include "aka_static_memory.hh"
-
-/* -------------------------------------------------------------------------- */
-int main(int argc, char * argv[]) {
- akantu::initialize(argc, argv);
-
- akantu::StaticMemory & st_mem = akantu::StaticMemory::getStaticMemory();
-
- akantu::Array<int> & test_int = st_mem.smalloc<int>(0, "test_int", 1000, 3);
-
- test_int.resize(1050);
-
- test_int.resize(2000);
-
- std::cout << st_mem << std::endl;
-
- st_mem.sfree(0, "test_int");
-
- akantu::finalize();
-
- exit(EXIT_SUCCESS);
-}
diff --git a/test/test_synchronizer/test_facet_synchronizer.cc b/test/test_synchronizer/test_facet_synchronizer.cc
index aac15d930..e6f716b3b 100644
--- a/test/test_synchronizer/test_facet_synchronizer.cc
+++ b/test/test_synchronizer/test_facet_synchronizer.cc
@@ -1,96 +1,96 @@
/**
* @file test_facet_synchronizer.cc
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
* @author Marco Vocialta <marco.vocialta@epfl.ch>
*
* @date creation: Wed Nov 05 2014
* @date last modification: Fri Jan 26 2018
*
* @brief Facet synchronizer test
*
*
* Copyright (©) 2015-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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "test_data_accessor.hh"
#include "test_synchronizers_fixture.hh"
/* -------------------------------------------------------------------------- */
#include "element_synchronizer.hh"
/* -------------------------------------------------------------------------- */
#include <chrono>
#include <random>
#include <thread>
/* -------------------------------------------------------------------------- */
class TestFacetSynchronizerFixture : public TestSynchronizerFixture {
public:
void SetUp() override {
TestSynchronizerFixture::SetUp();
this->distribute();
this->mesh->initMeshFacets();
/// compute barycenter for each element
barycenters =
- std::make_unique<ElementTypeMapArray<Real>>("barycenters", "", 0);
+ std::make_unique<ElementTypeMapArray<Real>>("barycenters");
this->initBarycenters(*barycenters, this->mesh->getMeshFacets());
test_accessor =
std::make_unique<TestAccessor>(*this->mesh, *this->barycenters);
}
void TearDown() override {
barycenters.reset(nullptr);
test_accessor.reset(nullptr);
}
protected:
std::unique_ptr<ElementTypeMapArray<Real>> barycenters;
std::unique_ptr<TestAccessor> test_accessor;
};
/* -------------------------------------------------------------------------- */
TEST_F(TestFacetSynchronizerFixture, SynchroneOnce) {
auto & synchronizer = this->mesh->getMeshFacets().getElementSynchronizer();
synchronizer.synchronizeOnce(*this->test_accessor, SynchronizationTag::_test);
}
/* -------------------------------------------------------------------------- */
TEST_F(TestFacetSynchronizerFixture, Synchrone) {
auto & synchronizer = this->mesh->getMeshFacets().getElementSynchronizer();
synchronizer.synchronize(*this->test_accessor, SynchronizationTag::_test);
}
/* -------------------------------------------------------------------------- */
TEST_F(TestFacetSynchronizerFixture, Asynchrone) {
auto & synchronizer = this->mesh->getMeshFacets().getElementSynchronizer();
synchronizer.asynchronousSynchronize(*this->test_accessor,
SynchronizationTag::_test);
std::random_device r;
std::default_random_engine engine(r());
std::uniform_int_distribution<int> uniform_dist(10, 100);
std::chrono::microseconds delay(uniform_dist(engine));
std::this_thread::sleep_for(delay);
synchronizer.waitEndSynchronize(*this->test_accessor,
SynchronizationTag::_test);
}
diff --git a/test/test_synchronizer/test_synchronizer_communication.cc b/test/test_synchronizer/test_synchronizer_communication.cc
index debc92353..8f84eff8a 100644
--- a/test/test_synchronizer/test_synchronizer_communication.cc
+++ b/test/test_synchronizer/test_synchronizer_communication.cc
@@ -1,94 +1,94 @@
/**
* @file test_synchronizer_communication.cc
*
* @author Dana Christen <dana.christen@epfl.ch>
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Wed Sep 01 2010
* @date last modification: Fri Jan 26 2018
*
* @brief test to synchronize barycenters
*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "test_data_accessor.hh"
#include "test_synchronizers_fixture.hh"
/* -------------------------------------------------------------------------- */
#include "element_synchronizer.hh"
/* -------------------------------------------------------------------------- */
#include <chrono>
#include <random>
#include <thread>
/* -------------------------------------------------------------------------- */
class TestElementSynchronizerFixture : public TestSynchronizerFixture {
public:
void SetUp() override {
TestSynchronizerFixture::SetUp();
this->distribute();
/// compute barycenter for each element
barycenters =
- std::make_unique<ElementTypeMapArray<Real>>("barycenters", "", 0);
+ std::make_unique<ElementTypeMapArray<Real>>("barycenters");
this->initBarycenters(*barycenters, *mesh);
test_accessor =
std::make_unique<TestAccessor>(*this->mesh, *this->barycenters);
}
void TearDown() override {
barycenters.reset(nullptr);
test_accessor.reset(nullptr);
}
protected:
std::unique_ptr<ElementTypeMapArray<Real>> barycenters;
std::unique_ptr<TestAccessor> test_accessor;
};
/* -------------------------------------------------------------------------- */
TEST_F(TestElementSynchronizerFixture, SynchroneOnce) {
auto & synchronizer = this->mesh->getElementSynchronizer();
synchronizer.synchronizeOnce(*this->test_accessor, SynchronizationTag::_test);
}
/* -------------------------------------------------------------------------- */
TEST_F(TestElementSynchronizerFixture, Synchrone) {
auto & synchronizer = this->mesh->getElementSynchronizer();
synchronizer.synchronize(*this->test_accessor, SynchronizationTag::_test);
}
/* -------------------------------------------------------------------------- */
TEST_F(TestElementSynchronizerFixture, Asynchrone) {
auto & synchronizer = this->mesh->getElementSynchronizer();
synchronizer.asynchronousSynchronize(*this->test_accessor,
SynchronizationTag::_test);
std::random_device r;
std::default_random_engine engine(r());
std::uniform_int_distribution<int> uniform_dist(10, 100);
std::chrono::microseconds delay(uniform_dist(engine));
std::this_thread::sleep_for(delay);
synchronizer.waitEndSynchronize(*this->test_accessor,
SynchronizationTag::_test);
}
diff --git a/third-party/akantu_iterators/include/aka_iterators.hh b/third-party/akantu_iterators/include/aka_iterators.hh
index fd5e3dd9c..91b676892 100644
--- a/third-party/akantu_iterators/include/aka_iterators.hh
+++ b/third-party/akantu_iterators/include/aka_iterators.hh
@@ -1,36 +1,37 @@
/**
* @file aka_iterators.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Aug 11 2017
* @date last modification: Mon Jan 29 2018
*
* @brief iterator interfaces
*
*
* Copyright (©) 2016-2018 EPFL (Ecole Polytechnique Fédérale de Lausanne)
* Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)
*
* akantu-iterators 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-iterators 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-iterators. If not, see <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "iterators/aka_arange_iterator.hh"
+#include "iterators/aka_concatenate_iterator.hh"
#include "iterators/aka_enumerate_iterator.hh"
#include "iterators/aka_filter_iterator.hh"
#include "iterators/aka_transform_iterator.hh"
#include "iterators/aka_zip_iterator.hh"
/* -------------------------------------------------------------------------- */
diff --git a/third-party/akantu_iterators/include/aka_tuple_tools.hh b/third-party/akantu_iterators/include/aka_tuple_tools.hh
index 31e5e88c4..b6773b150 100644
--- a/third-party/akantu_iterators/include/aka_tuple_tools.hh
+++ b/third-party/akantu_iterators/include/aka_tuple_tools.hh
@@ -1,337 +1,409 @@
/**
* @file aka_tuple_tools.hh
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Aug 11 2017
* @date last modification: Mon Jan 29 2018
*
* @brief iterator interfaces
*
*
* Copyright 2019 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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_compatibilty_with_cpp_standard.hh"
#include "aka_str_hash.hh"
/* -------------------------------------------------------------------------- */
#include <tuple>
/* -------------------------------------------------------------------------- */
#ifndef AKANTU_AKA_TUPLE_TOOLS_HH
#define AKANTU_AKA_TUPLE_TOOLS_HH
#ifndef AKANTU_ITERATORS_NAMESPACE
#define AKANTU_ITERATORS_NAMESPACE akantu
#endif
namespace AKANTU_ITERATORS_NAMESPACE {
namespace tuple {
/* ---------------------------------------------------------------------- */
template <typename tag, typename type> struct named_tag {
using _tag = tag; ///< key
using _type = type; ///< value type
template <
typename T,
std::enable_if_t<not std::is_same<named_tag, T>::value> * = nullptr>
explicit named_tag(T && value) // NOLINT
: _value(std::forward<T>(value)) {}
type _value;
};
namespace details {
/* ---------------------------------------------------------------------- */
#if (defined(__GNUC__) || defined(__GNUG__))
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#endif
template <typename tag> struct named_tag_proxy {
using _tag = tag;
template <typename T> decltype(auto) operator=(T && value) {
return named_tag<_tag, T>{std::forward<T>(value)};
}
};
#if (defined(__GNUC__) || defined(__GNUG__))
#pragma GCC diagnostic pop
#endif
} // namespace details
/* ---------------------------------------------------------------------- */
template <typename T> struct is_named_tag : public std::false_type {};
template <typename tag>
struct is_named_tag<details::named_tag_proxy<tag>> : public std::true_type {};
template <typename tag, typename type>
struct is_named_tag<named_tag<tag, type>> : public std::true_type {};
/* ---------------------------------------------------------------------- */
template <class... Params>
struct named_tuple : public std::tuple<typename Params::_type...> {
using Names_t = std::tuple<typename Params::_tag...>;
using parent = std::tuple<typename Params::_type...>;
named_tuple(Params &&... params)
: parent(std::forward<typename Params::_type>(params._value)...) {}
named_tuple(typename Params::_type &&... args)
: parent(std::forward<typename Params::_type>(args)...) {}
private:
template <typename tag, std::size_t Idx,
std::enable_if_t<Idx == sizeof...(Params)> * = nullptr>
static constexpr std::size_t get_element_index() noexcept {
return -1;
}
template <typename tag, std::size_t Idx,
std::enable_if_t<(Idx < sizeof...(Params))> * = nullptr>
static constexpr std::size_t get_element_index() noexcept {
using _tag = std::tuple_element_t<Idx, Names_t>;
return (std::is_same<_tag, tag>::value)
? Idx
: get_element_index<tag, Idx + 1>();
}
public:
template <typename NT,
std::enable_if_t<is_named_tag<NT>::value> * = nullptr>
constexpr decltype(auto) get(NT && /*unused*/) noexcept {
const auto index = get_element_index<typename NT::_tag, 0>();
static_assert((index != -1), "wrong named_tag");
return (std::get<index>(*this));
}
template <typename NT,
std::enable_if_t<is_named_tag<NT>::value> * = nullptr>
constexpr decltype(auto) get(NT && /*unused*/) const noexcept {
const auto index = get_element_index<typename NT::_tag, 0>();
static_assert((index != -1), "wrong named_tag");
return std::get<index>(*this);
}
};
/* ---------------------------------------------------------------------- */
template <typename T> struct is_named_tuple : public std::false_type {};
template <typename... Params>
struct is_named_tuple<named_tuple<Params...>> : public std::true_type {};
/* ---------------------------------------------------------------------- */
template <typename... Params>
constexpr decltype(auto) make_named_tuple(Params &&... params) noexcept {
return named_tuple<Params...>(std::forward<Params>(params)...);
}
template <typename tag> constexpr decltype(auto) make_named_tag() noexcept {
return details::named_tag_proxy<tag>{};
}
template <size_t HashCode> constexpr decltype(auto) get() {
return make_named_tag<std::integral_constant<size_t, HashCode>>();
}
template <size_t HashCode, class Tuple>
constexpr decltype(auto) get(Tuple && tuple) {
return tuple.get(get<HashCode>());
}
template <typename Param, typename Tuple,
std::enable_if_t<is_named_tag<Param>::value> * = nullptr>
constexpr decltype(auto) get(Tuple && tuple) noexcept {
return tuple.template get<typename Param::hash>();
}
#if defined(__INTEL_COMPILER)
// intel warnings here
#elif defined(__clang__)
// clang warnings here
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu-string-literal-operator-template"
#elif (defined(__GNUC__) || defined(__GNUG__))
// gcc warnings here
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
#endif
/// this is a GNU exstension
/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3599.html
template <class CharT, CharT... chars>
constexpr decltype(auto) operator"" _n() {
return make_named_tag<std::integral_constant<
std::size_t, string_literal<CharT, chars...>::hash>>();
}
#if defined(__INTEL_COMPILER)
#elif defined(__clang__)
#pragma clang diagnostic pop
#elif (defined(__GNUC__) || defined(__GNUG__))
#pragma GCC diagnostic pop
#endif
/* ------------------------------------------------------------------------ */
namespace details {
template <std::size_t N> struct Foreach {
template <class Tuple>
- static inline bool not_equal(Tuple && a, Tuple && b) {
+
+ static constexpr inline auto not_equal(Tuple && a, Tuple && b) -> bool {
if (std::get<N - 1>(std::forward<Tuple>(a)) ==
std::get<N - 1>(std::forward<Tuple>(b))) {
return false;
}
return Foreach<N - 1>::not_equal(std::forward<Tuple>(a),
std::forward<Tuple>(b));
}
+
+ template <class Tuple, class V>
+ static constexpr inline auto find(Tuple && tuple, V && value)
+ -> std::size_t {
+ constexpr auto size = std::tuple_size<std::decay_t<Tuple>>::value;
+ if (std::get<size - N>(std::forward<Tuple>(tuple)) == value) {
+ return size - N;
+ }
+
+ return Foreach<N - 1>::find(std::forward<Tuple>(tuple),
+ std::forward<V>(value));
+ }
};
/* ---------------------------------------------------------------------- */
template <> struct Foreach<0> {
template <class Tuple>
- static inline bool not_equal(Tuple && a, Tuple && b) {
+ static constexpr inline auto not_equal(Tuple && a, Tuple && b) -> bool {
return std::get<0>(std::forward<Tuple>(a)) !=
std::get<0>(std::forward<Tuple>(b));
}
+
+ template <class Tuple, class V>
+ static constexpr inline auto find(Tuple && tuple, V && value)
+ -> std::size_t {
+ constexpr auto size = std::tuple_size<std::decay_t<Tuple>>::value;
+ if (std::get<size - 1>(std::forward<Tuple>(tuple)) == value) {
+ return size - 1;
+ }
+
+ return size;
+ }
};
template <typename... Ts>
decltype(auto) make_tuple_no_decay(Ts &&... args) {
return std::tuple<Ts...>(std::forward<Ts>(args)...);
}
template <typename... Names, typename... Ts>
- decltype(auto) make_named_tuple_no_decay(std::tuple<Names...> /*unused*/,
- Ts &&... args) {
+ constexpr decltype(auto)
+ make_named_tuple_no_decay(std::tuple<Names...> /*unused*/, Ts &&... args) {
return named_tuple<named_tag<Names, Ts>...>(std::forward<Ts>(args)...);
}
template <class F, class Tuple, std::size_t... Is>
- void foreach_impl(F && func, Tuple && tuple,
- std::index_sequence<Is...> && /*unused*/) {
+ constexpr void foreach_impl(F && func, Tuple && tuple,
+ std::index_sequence<Is...> && /*unused*/) {
(void)std::initializer_list<int>{
(std::forward<F>(func)(std::get<Is>(std::forward<Tuple>(tuple))),
0)...};
}
template <class F, class Tuple, std::size_t... Is>
- decltype(auto) transform_impl(F && func, Tuple && tuple,
- std::index_sequence<Is...> && /*unused*/) {
+ constexpr decltype(auto)
+ transform_impl(F && func, Tuple && tuple,
+ std::index_sequence<Is...> && /*unused*/) {
return make_tuple_no_decay(
std::forward<F>(func)(std::get<Is>(std::forward<Tuple>(tuple)))...);
}
template <class F, class Tuple, std::size_t... Is>
- decltype(auto)
+ constexpr decltype(auto)
+ transform_impl(F && func, Tuple && tuple1, Tuple && tuple2,
+ std::index_sequence<Is...> && /*unused*/) {
+ return make_tuple_no_decay(
+ std::forward<F>(func)(std::get<Is>(std::forward<Tuple>(tuple1)),
+ std::get<Is>(std::forward<Tuple>(tuple2)))...);
+ }
+
+ template <class F, class Tuple, std::size_t... Is>
+ constexpr decltype(auto)
transform_named_impl(F && func, Tuple && tuple,
std::index_sequence<Is...> && /*unused*/) {
return make_named_tuple_no_decay(
typename std::decay_t<Tuple>::Names_t{},
std::forward<F>(func)(std::get<Is>(std::forward<Tuple>(tuple)))...);
}
} // namespace details
- /* ------------------------------------------------------------------------ */
+ /* ------------------------------------------------------------------------
+ */
template <class Tuple,
std::enable_if_t<not is_named_tuple<std::decay_t<Tuple>>::value> * =
nullptr>
- bool are_not_equal(Tuple && a, Tuple && b) {
+ constexpr auto are_not_equal(Tuple && a, Tuple && b) -> bool {
return details::Foreach<std::tuple_size<std::decay_t<Tuple>>::value>::
not_equal(std::forward<Tuple>(a), std::forward<Tuple>(b));
}
template <
class Tuple,
std::enable_if_t<is_named_tuple<std::decay_t<Tuple>>::value> * = nullptr>
- bool are_not_equal(Tuple && a, Tuple && b) {
+ constexpr auto are_not_equal(Tuple && a, Tuple && b) -> bool {
return details::Foreach<
std::tuple_size<typename std::decay_t<Tuple>::parent>::value>::
not_equal(std::forward<Tuple>(a), std::forward<Tuple>(b));
}
+ template <class Tuple, class V>
+ constexpr decltype(auto) find(Tuple && tuple, V && value) {
+ return details::Foreach<std::tuple_size<std::decay_t<Tuple>>::value>::find(
+ std::forward<Tuple>(tuple), std::forward<V>(value));
+ }
+
template <class F, class Tuple,
std::enable_if_t<not is_named_tuple<std::decay_t<Tuple>>::value> * =
nullptr>
- void foreach (F && func, Tuple && tuple) {
+ constexpr void foreach (F && func, Tuple && tuple) {
return details::foreach_impl(
std::forward<F>(func), std::forward<Tuple>(tuple),
std::make_index_sequence<
std::tuple_size<std::decay_t<Tuple>>::value>{});
}
template <
class F, class Tuple,
std::enable_if_t<is_named_tuple<std::decay_t<Tuple>>::value> * = nullptr>
- void foreach (F && func, Tuple && tuple) {
+ constexpr void foreach (F && func, Tuple && tuple) {
return details::foreach_impl(
std::forward<F>(func), std::forward<Tuple>(tuple),
std::make_index_sequence<
std::tuple_size<typename std::decay_t<Tuple>::parent>::value>{});
}
template <class F, class Tuple,
std::enable_if_t<not is_named_tuple<std::decay_t<Tuple>>::value> * =
nullptr>
- decltype(auto) transform(F && func, Tuple && tuple) {
+ constexpr decltype(auto) transform(F && func, Tuple && tuple) {
return details::transform_impl(
std::forward<F>(func), std::forward<Tuple>(tuple),
std::make_index_sequence<
std::tuple_size<std::decay_t<Tuple>>::value>{});
}
+ template <class F, class Tuple,
+ std::enable_if_t<not is_named_tuple<std::decay_t<Tuple>>::value> * =
+ nullptr>
+ constexpr decltype(auto) transform(F && func, Tuple && tuple1,
+ Tuple && tuple2) {
+ return details::transform_impl(
+ std::forward<F>(func), std::forward<Tuple>(tuple1),
+ std::forward<Tuple>(tuple2),
+ std::make_index_sequence<
+ std::tuple_size<std::decay_t<Tuple>>::value>{});
+ }
+
template <
class F, class Tuple,
std::enable_if_t<is_named_tuple<std::decay_t<Tuple>>::value> * = nullptr>
- decltype(auto) transform(F && func, Tuple && tuple) {
+ constexpr decltype(auto) transform(F && func, Tuple && tuple) {
return details::transform_named_impl(
std::forward<F>(func), std::forward<Tuple>(tuple),
std::make_index_sequence<
std::tuple_size<typename std::decay_t<Tuple>::parent>::value>{});
}
namespace details {
template <class Tuple, std::size_t... Is>
- decltype(auto) flatten(Tuple && tuples,
- std::index_sequence<Is...> /*unused*/) {
+ constexpr decltype(auto) flatten(Tuple && tuples,
+ std::index_sequence<Is...> /*unused*/) {
return std::tuple_cat(std::get<Is>(tuples)...);
}
} // namespace details
- template <class Tuple> decltype(auto) flatten(Tuple && tuples) {
+ template <class Tuple> constexpr decltype(auto) flatten(Tuple && tuples) {
return details::flatten(std::forward<Tuple>(tuples),
std::make_index_sequence<
std::tuple_size<std::decay_t<Tuple>>::value>());
}
+
+ namespace details {
+ template <size_t n, class Tuple>
+ decltype(auto) dynamic_get_impl(size_t i, Tuple && tuple) {
+ constexpr auto size = std::tuple_size<std::decay_t<Tuple>>::value;
+ if (i == n) {
+ return std::get<n>(tuple);
+ } else if (n == size - 1) {
+ throw std::out_of_range("Tuple element out of range.");
+ } else {
+ return dynamic_get_impl<(n < size - 1 ? n + 1 : 0)>(i, tuple);
+ }
+ }
+ } // namespace details
+
+ template <class Tuple>
+ constexpr decltype(auto) dynamic_get(std::size_t i, Tuple && tuple) {
+ return details::dynamic_get_impl<0>(i, std::forward<Tuple>(tuple));
+ }
} // namespace tuple
} // namespace AKANTU_ITERATORS_NAMESPACE
/* -------------------------------------------------------------------------- */
#include <iterator>
/* -------------------------------------------------------------------------- */
namespace std {
template <typename tag, typename type>
struct iterator_traits<
::AKANTU_ITERATORS_NAMESPACE::tuple::named_tag<tag, type>> {
using iterator_category = typename type::iterator_category;
using value_type = typename type::value_type;
using difference_type = typename type::difference_type;
using pointer = typename type::pointer;
using reference = typename type::reference;
};
} // namespace std
#endif /* AKANTU_AKA_TUPLE_TOOLS_HH */
diff --git a/third-party/akantu_iterators/include/iterators/aka_concatenate_iterator.hh b/third-party/akantu_iterators/include/iterators/aka_concatenate_iterator.hh
new file mode 100644
index 000000000..a7948c5a9
--- /dev/null
+++ b/third-party/akantu_iterators/include/iterators/aka_concatenate_iterator.hh
@@ -0,0 +1,205 @@
+/**
+ * @file aka_concatenate_iterator.hh
+ *
+ * @author Nicolas Richart
+ *
+ * @date creation jeu déc 12 2019
+ *
+ * @brief implementation of arange
+ *
+ *
+ * Copyright (©) 2010-2011 EPFL (Ecole Polytechnique Fédérale de Lausanne)
+ * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)
+ *
+ * akantu-iterators 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-iterators 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-iterators. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+/* -------------------------------------------------------------------------- */
+#include "aka_compatibilty_with_cpp_standard.hh"
+#include "aka_iterator_tools.hh"
+#include "aka_tuple_tools.hh"
+/* -------------------------------------------------------------------------- */
+#include <iterator>
+#include <utility>
+/* -------------------------------------------------------------------------- */
+
+#ifndef AKA_CONCATENATE_ITERATOR_H
+#define AKA_CONCATENATE_ITERATOR_H
+
+#ifndef AKANTU_ITERATORS_NAMESPACE
+#define AKANTU_ITERATORS_NAMESPACE akantu
+#endif
+
+namespace AKANTU_ITERATORS_NAMESPACE {
+
+namespace iterators {
+
+ /* ------------------------------------------------------------------------ */
+ template <class... Iterators>
+ class ConcatIterator
+ : public details::CopyAssignmentEnabler<
+ aka::conjunction<std::is_copy_assignable<Iterators>...,
+ std::is_copy_constructible<Iterators>...>::value>,
+ public details::MoveAssignmentEnabler<
+ aka::conjunction<std::is_move_assignable<Iterators>...,
+ std::is_move_constructible<Iterators>...>::value> {
+ private:
+ using tuple_t = std::tuple<Iterators...>;
+
+ public:
+ using value_type =
+ std::tuple<typename std::iterator_traits<Iterators>::value_type...>;
+ using difference_type = std::common_type_t<
+ typename std::iterator_traits<Iterators>::difference_type...>;
+ using pointer =
+ std::tuple<typename std::iterator_traits<Iterators>::pointer...>;
+ using reference =
+ std::tuple<typename std::iterator_traits<Iterators>::reference...>;
+ using iterator_category = std::input_iterator_tag;
+
+ public:
+ explicit ConcatIterator(tuple_t iterators, tuple_t end_iterators)
+ : iterators(std::move(iterators)),
+ end_iterators(std::move(end_iterators)) {}
+
+ /* ---------------------------------------------------------------------- */
+ // input iterator ++it
+ ConcatIterator & operator++() {
+ auto && ends =
+ tuple::transform([](auto && a, auto && b) { return a == b; },
+ iterators, end_iterators);
+ auto && pos = tuple::find(ends, false);
+ ++(tuple::dynamic_get(pos, iterators));
+ return *this;
+ }
+
+ // input iterator it++
+ ConcatIterator operator++(int) {
+ auto cpy = *this;
+ this->operator++();
+ return cpy;
+ }
+
+ // input iterator it != other_it
+ bool operator!=(const ConcatIterator & other) const {
+ return tuple::are_not_equal(iterators, other.iterators);
+ }
+
+ // input iterator dereference *it
+ decltype(auto) operator*() {
+ auto && ends =
+ tuple::transform([](auto && a, auto && b) { return a == b; },
+ iterators, end_iterators);
+ auto && pos = tuple::find(ends, false);
+ return *(tuple::dynamic_get(pos, iterators));
+ }
+
+ template <
+ class iterator_category_ = iterator_category,
+ std::enable_if_t<aka::is_iterator_category_at_least<
+ iterator_category_, std::forward_iterator_tag>::value> * = nullptr>
+ bool operator==(const ConcatIterator & other) const {
+ return not tuple::are_not_equal(iterators, other.iterators);
+ }
+
+ private:
+ tuple_t iterators;
+ tuple_t end_iterators;
+ };
+
+} // namespace iterators
+
+/* -------------------------------------------------------------------------- */
+template <class... Iterators>
+decltype(auto)
+concat_iterator(std::tuple<Iterators...> && iterators_tuple,
+ std::tuple<Iterators...> && end_iterators_tuple) {
+ auto concat = iterators::ConcatIterator<Iterators...>(
+ std::forward<decltype(iterators_tuple)>(iterators_tuple),
+ std::forward<decltype(end_iterators_tuple)>(end_iterators_tuple));
+ return concat;
+}
+
+/* -------------------------------------------------------------------------- */
+namespace containers {
+ template <class... Containers> class ConcatContainer {
+ using containers_t = std::tuple<Containers...>;
+
+ public:
+ explicit ConcatContainer(Containers &&... containers)
+ : containers(std::forward<Containers>(containers)...) {}
+
+ decltype(auto) begin() const {
+ return concat_iterator(
+ tuple::transform([](auto && c) { return c.begin(); },
+ std::forward<containers_t>(containers)),
+ tuple::transform([](auto && c) { return c.end(); },
+ std::forward<containers_t>(containers)));
+ }
+
+ decltype(auto) end() const {
+ return concat_iterator(
+ tuple::transform([](auto && c) { return c.end(); },
+ std::forward<containers_t>(containers)),
+ tuple::transform([](auto && c) { return c.end(); },
+ std::forward<containers_t>(containers)));
+ }
+
+ decltype(auto) begin() {
+ return concat_iterator(
+ tuple::transform([](auto && c) { return c.begin(); },
+ std::forward<containers_t>(containers)),
+ tuple::transform([](auto && c) { return c.end(); },
+ std::forward<containers_t>(containers)));
+ }
+
+ decltype(auto) end() {
+ return concat_iterator(
+ tuple::transform([](auto && c) { return c.end(); },
+ std::forward<containers_t>(containers)),
+ tuple::transform([](auto && c) { return c.end(); },
+ std::forward<containers_t>(containers)));
+ }
+
+ private:
+ containers_t containers;
+ };
+
+ /* ------------------------------------------------------------------------ */
+ template <class... Containers> decltype(auto) concat(Containers &&... conts) {
+ return containers::ConcatContainer<Containers...>(
+ std::forward<Containers>(conts)...);
+ }
+
+} // namespace containers
+} // namespace AKANTU_ITERATORS_NAMESPACE
+
+namespace std {
+template <typename... Its>
+struct iterator_traits<
+ ::AKANTU_ITERATORS_NAMESPACE::iterators::ConcatIterator<Its...>> {
+private:
+ using iterator_type =
+ typename ::AKANTU_ITERATORS_NAMESPACE::iterators::ConcatIterator<Its...>;
+
+public:
+ using iterator_category = typename iterator_type::iterator_category;
+ using value_type = typename iterator_type::value_type;
+ using difference_type = typename iterator_type::difference_type;
+ using pointer = typename iterator_type::pointer;
+ using reference = typename iterator_type::reference;
+};
+} // namespace std
+
+#endif // __AKA_CONCATENATE_ITERATOR_H_
diff --git a/third-party/akantu_iterators/include/iterators/aka_iterator_tools.hh b/third-party/akantu_iterators/include/iterators/aka_iterator_tools.hh
new file mode 100644
index 000000000..aec788231
--- /dev/null
+++ b/third-party/akantu_iterators/include/iterators/aka_iterator_tools.hh
@@ -0,0 +1,69 @@
+/**
+ * @file aka_iterator_tools.hh
+ *
+ * @author Nicolas Richart
+ *
+ * @date creation jeu déc 12 2019
+ *
+ * @brief A Documented file.
+ *
+ *
+ * Copyright (©) 2010-2011 EPFL (Ecole Polytechnique Fédérale de Lausanne)
+ * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)
+ *
+ * akantu-iterators 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-iterators 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-iterators. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+/* -------------------------------------------------------------------------- */
+
+#ifndef AKA_ITERATOR_TOOLS_H
+#define AKA_ITERATOR_TOOLS_H
+
+#ifndef AKANTU_ITERATORS_NAMESPACE
+#define AKANTU_ITERATORS_NAMESPACE akantu
+#endif
+
+namespace AKANTU_ITERATORS_NAMESPACE {
+
+/* -------------------------------------------------------------------------- */
+namespace iterators {
+
+ namespace details {
+ template <bool enable> struct CopyAssignmentEnabler {};
+ template <> struct CopyAssignmentEnabler<false> {
+ CopyAssignmentEnabler() = default;
+ CopyAssignmentEnabler(const CopyAssignmentEnabler &) = default;
+ CopyAssignmentEnabler(CopyAssignmentEnabler &&) = default;
+ auto operator=(const CopyAssignmentEnabler &)
+ -> CopyAssignmentEnabler & = delete;
+ auto operator=(CopyAssignmentEnabler &&)
+ -> CopyAssignmentEnabler & = default;
+ };
+
+ template <bool enable> struct MoveAssignmentEnabler {};
+ template <> struct MoveAssignmentEnabler<false> {
+ MoveAssignmentEnabler() = default;
+ MoveAssignmentEnabler(const MoveAssignmentEnabler &) = default;
+ MoveAssignmentEnabler(MoveAssignmentEnabler &&) = default;
+ auto operator=(const MoveAssignmentEnabler &)
+ -> MoveAssignmentEnabler & = delete;
+ auto operator=(MoveAssignmentEnabler &&)
+ -> MoveAssignmentEnabler & = default;
+ };
+
+ } // namespace details
+} // namespace iterators
+} // namespace AKANTU_ITERATORS_NAMESPACE
+
+#endif // AKA_ITERATOR_TOOLS_H
diff --git a/third-party/akantu_iterators/include/iterators/aka_zip_iterator.hh b/third-party/akantu_iterators/include/iterators/aka_zip_iterator.hh
index 3a82dc04b..30c338996 100644
--- a/third-party/akantu_iterators/include/iterators/aka_zip_iterator.hh
+++ b/third-party/akantu_iterators/include/iterators/aka_zip_iterator.hh
@@ -1,303 +1,282 @@
/**
* @file aka_zip_iterator.hh
*
* @author Nicolas Richart
*
* @date creation jeu déc 12 2019
*
* @brief A Documented file.
*
*
* Copyright (©) 2010-2011 EPFL (Ecole Polytechnique Fédérale de Lausanne)
* Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)
*
* akantu-iterators 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-iterators 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-iterators. If not, see <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_compatibilty_with_cpp_standard.hh"
+#include "aka_iterator_tools.hh"
#include "aka_tuple_tools.hh"
/* -------------------------------------------------------------------------- */
#include <iterator>
-#include <tuple>
#include <utility>
/* -------------------------------------------------------------------------- */
#ifndef AKA_ZIP_ITERATOR_HH
#define AKA_ZIP_ITERATOR_HH
#ifndef AKANTU_ITERATORS_NAMESPACE
#define AKANTU_ITERATORS_NAMESPACE akantu
#endif
namespace AKANTU_ITERATORS_NAMESPACE {
/* -------------------------------------------------------------------------- */
namespace iterators {
- namespace details {
- template <bool enable> struct CopyAssignmentEnabler {};
- template <> struct CopyAssignmentEnabler<false> {
- CopyAssignmentEnabler() = default;
- CopyAssignmentEnabler(const CopyAssignmentEnabler &) = default;
- CopyAssignmentEnabler(CopyAssignmentEnabler &&) = default;
- CopyAssignmentEnabler & operator=(const CopyAssignmentEnabler &) = delete;
- CopyAssignmentEnabler & operator=(CopyAssignmentEnabler &&) = default;
- };
-
- template <bool enable> struct MoveAssignmentEnabler {};
- template <> struct MoveAssignmentEnabler<false> {
- MoveAssignmentEnabler() = default;
- MoveAssignmentEnabler(const MoveAssignmentEnabler &) = default;
- MoveAssignmentEnabler(MoveAssignmentEnabler &&) = default;
- MoveAssignmentEnabler & operator=(const MoveAssignmentEnabler &) = delete;
- MoveAssignmentEnabler & operator=(MoveAssignmentEnabler &&) = default;
- };
-
- } // namespace details
-
/* ------------------------------------------------------------------------ */
template <template <class...> class Tuple, class... Iterators>
class ZipIterator_
: public details::CopyAssignmentEnabler<
aka::conjunction<std::is_copy_assignable<Iterators>...,
std::is_copy_constructible<Iterators>...>::value>,
public details::MoveAssignmentEnabler<
aka::conjunction<std::is_move_assignable<Iterators>...,
std::is_move_constructible<Iterators>...>::value> {
private:
using tuple_t = Tuple<Iterators...>;
public:
using value_type =
Tuple<typename std::iterator_traits<Iterators>::value_type...>;
using difference_type = std::common_type_t<
typename std::iterator_traits<Iterators>::difference_type...>;
using pointer = Tuple<typename std::iterator_traits<Iterators>::pointer...>;
using reference =
Tuple<typename std::iterator_traits<Iterators>::reference...>;
using iterator_category = // std::input_iterator_tag;
std::common_type_t<
typename std::iterator_traits<Iterators>::iterator_category...>;
// using nb_iterators = sizeof...(Iterators);
public:
explicit ZipIterator_(tuple_t iterators)
: iterators(std::move(iterators)) {}
/* ---------------------------------------------------------------------- */
template <class iterator_category_ = iterator_category,
std::enable_if_t<aka::is_iterator_category_at_least<
iterator_category_,
std::bidirectional_iterator_tag>::value> * = nullptr>
ZipIterator_ & operator--() {
tuple::foreach ([](auto && it) { --it; }, iterators);
return *this;
}
template <class iterator_category_ = iterator_category,
std::enable_if_t<aka::is_iterator_category_at_least<
iterator_category_,
std::bidirectional_iterator_tag>::value> * = nullptr>
ZipIterator_ operator--(int) {
auto cpy = *this;
this->operator--();
return cpy;
}
// input iterator ++it
ZipIterator_ & operator++() {
tuple::foreach ([](auto && it) { ++it; }, iterators);
return *this;
}
// input iterator it++
ZipIterator_ operator++(int) {
auto cpy = *this;
this->operator++();
return cpy;
}
// input iterator it != other_it
bool operator!=(const ZipIterator_ & other) const {
// return tuple::are_not_equal(iterators, other.iterators);
return std::get<0>(iterators) !=
std::get<0>(other.iterators); // helps the compiler to optimize
}
// input iterator dereference *it
decltype(auto) operator*() {
return tuple::transform([](auto && it) -> decltype(auto) { return *it; },
iterators);
}
template <class iterator_category_ = iterator_category,
std::enable_if_t<aka::is_iterator_category_at_least<
iterator_category_,
std::random_access_iterator_tag>::value> * = nullptr>
difference_type operator-(const ZipIterator_ & other) {
return std::get<0>(this->iterators) - std::get<0>(other.iterators);
}
// random iterator it[idx]
template <class iterator_category_ = iterator_category,
std::enable_if_t<aka::is_iterator_category_at_least<
iterator_category_,
std::random_access_iterator_tag>::value> * = nullptr>
decltype(auto) operator[](std::size_t idx) {
return tuple::transform(
[idx](auto && it) -> decltype(auto) { return it[idx]; }, iterators);
}
// random iterator it + n
template <class iterator_category_ = iterator_category,
std::enable_if_t<aka::is_iterator_category_at_least<
iterator_category_,
std::random_access_iterator_tag>::value> * = nullptr>
decltype(auto) operator+(std::size_t n) {
return ZipIterator_(std::forward<tuple_t>(tuple::transform(
[n](auto && it) -> decltype(auto) { return it + n; }, iterators)));
}
// random iterator it - n
template <class iterator_category_ = iterator_category,
std::enable_if_t<aka::is_iterator_category_at_least<
iterator_category_,
std::random_access_iterator_tag>::value> * = nullptr>
decltype(auto) operator-(std::size_t n) {
return ZipIterator_(std::forward<tuple_t>(tuple::transform(
[n](auto && it) -> decltype(auto) { return it - n; }, iterators)));
}
template <
class iterator_category_ = iterator_category,
std::enable_if_t<aka::is_iterator_category_at_least<
iterator_category_, std::forward_iterator_tag>::value> * = nullptr>
bool operator==(const ZipIterator_ & other) const {
return not tuple::are_not_equal(iterators, other.iterators);
}
private:
tuple_t iterators;
};
template <class... Iterators>
using ZipIterator = ZipIterator_<std::tuple, Iterators...>;
template <class... Iterators>
using NamedZipIterator = ZipIterator_<tuple::named_tuple, Iterators...>;
} // namespace iterators
/* -------------------------------------------------------------------------- */
template <class... Iterators>
decltype(auto) zip_iterator(std::tuple<Iterators...> && iterators_tuple) {
auto zip = iterators::ZipIterator<Iterators...>(
std::forward<decltype(iterators_tuple)>(iterators_tuple));
return zip;
}
template <class... Iterators>
decltype(auto)
zip_iterator(tuple::named_tuple<Iterators...> && iterators_tuple) {
auto zip = iterators::NamedZipIterator<Iterators...>(
std::forward<decltype(iterators_tuple)>(iterators_tuple));
return zip;
}
/* -------------------------------------------------------------------------- */
namespace containers {
template <template <class...> class Tuple, class... Containers>
class ZipContainer_ {
using containers_t = Tuple<Containers...>;
public:
explicit ZipContainer_(Containers &&... containers)
: containers(std::forward<Containers>(containers)...) {}
decltype(auto) begin() const {
return zip_iterator(
tuple::transform([](auto && c) { return c.begin(); },
std::forward<containers_t>(containers)));
}
decltype(auto) end() const {
return zip_iterator(
tuple::transform([](auto && c) { return c.end(); },
std::forward<containers_t>(containers)));
}
decltype(auto) begin() {
return zip_iterator(
tuple::transform([](auto && c) { return c.begin(); },
std::forward<containers_t>(containers)));
}
decltype(auto) end() {
return zip_iterator(
tuple::transform([](auto && c) { return c.end(); },
std::forward<containers_t>(containers)));
}
private:
containers_t containers;
};
template <class... Containers>
using ZipContainer = ZipContainer_<std::tuple, Containers...>;
template <class... Containers>
using NamedZipContainer = ZipContainer_<tuple::named_tuple, Containers...>;
} // namespace containers
/* -------------------------------------------------------------------------- */
template <class... Containers> decltype(auto) zip(Containers &&... conts) {
return containers::ZipContainer<Containers...>(
std::forward<Containers>(conts)...);
}
template <class... NamedContainers>
decltype(auto) named_zip(NamedContainers &&... conts) {
return containers::NamedZipContainer<NamedContainers...>(
std::forward<NamedContainers>(conts)...);
}
/* -------------------------------------------------------------------------- */
template <class... zip_container_t>
decltype(auto) make_zip_cat(zip_container_t &&... cont) {
return make_transform_adaptor(
zip(std::forward<zip_container_t>(cont)...),
[](auto && value) { return tuple::flatten(value); });
}
} // namespace AKANTU_ITERATORS_NAMESPACE
namespace std {
template <template <class...> class Tuple, typename... Its>
struct iterator_traits<
::AKANTU_ITERATORS_NAMESPACE::iterators::ZipIterator_<Tuple, Its...>> {
private:
using iterator_type =
typename ::AKANTU_ITERATORS_NAMESPACE::iterators::ZipIterator_<Tuple,
Its...>;
public:
using iterator_category = typename iterator_type::iterator_category;
using value_type = typename iterator_type::value_type;
using difference_type = typename iterator_type::difference_type;
using pointer = typename iterator_type::pointer;
using reference = typename iterator_type::reference;
};
} // namespace std
#endif /* AKA_ZIP_ITERATOR_HH */
diff --git a/third-party/akantu_iterators/test/CMakeLists.txt b/third-party/akantu_iterators/test/CMakeLists.txt
index 2bfaba215..f6887166b 100644
--- a/third-party/akantu_iterators/test/CMakeLists.txt
+++ b/third-party/akantu_iterators/test/CMakeLists.txt
@@ -1,25 +1,19 @@
enable_testing()
add_executable(test_akantu_iterators test_gtest_main.cc test_tuples.cc test_str.cc test_iterators.cc)
target_link_libraries(test_akantu_iterators
PRIVATE akantu_iterators GTest::GTest)
target_compile_definitions(test_akantu_iterators PRIVATE -DAKANTU_ITERATORS_NAMESPACE=aka)
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_compile_options(test_akantu_iterators PRIVATE -Wall -Wextra -pedantic -Weffc++)
endif()
if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "5.2") OR
CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_compile_options(test_akantu_iterators PRIVATE -fsanitize=address -fsanitize=leak -fsanitize=undefined -fno-omit-frame-pointer)
target_link_options(test_akantu_iterators PRIVATE -fsanitize=address -fsanitize=leak -fsanitize=undefined -fno-omit-frame-pointer)
endif()
-if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "5.2") OR
- CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
- target_compile_options(test_iterators PRIVATE -fsanitize=address -fsanitize=leak -fsanitize=undefined -fno-omit-frame-pointer)
- target_link_options(test_iterators PRIVATE -fsanitize=address -fsanitize=leak -fsanitize=undefined -fno-omit-frame-pointer)
-endif()
-
include(GoogleTest)
gtest_discover_tests(test_akantu_iterators)
diff --git a/third-party/akantu_iterators/test/test_iterators.cc b/third-party/akantu_iterators/test/test_iterators.cc
index f974ce896..8ac313879 100644
--- a/third-party/akantu_iterators/test/test_iterators.cc
+++ b/third-party/akantu_iterators/test/test_iterators.cc
@@ -1,394 +1,402 @@
/**
* @file test_zip_iterator.cc
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @date creation: Fri Jul 21 2017
* @date last modification: Fri Dec 08 2017
*
* @brief test the zip container and iterator
*
*
* Copyright (©) 2016-2018 EPFL (Ecole Polytechnique Fédérale de Lausanne)
* Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)
*
* akantu-iterators 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-iterators 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-iterators. If not, see <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "aka_iterators.hh"
/* -------------------------------------------------------------------------- */
#include <gtest/gtest.h>
#include <vector>
/* -------------------------------------------------------------------------- */
using namespace aka;
/* -------------------------------------------------------------------------- */
// Non Trivial class that counts moves and copies
template <class T> class A {
public:
A() = default;
A(T a) : a(a){};
A(const A & other)
: a(other.a), copy_counter(other.copy_counter + 1),
move_counter(other.move_counter) {}
A & operator=(const A & other) {
if (this != &other) {
a = other.a;
copy_counter = other.copy_counter + 1;
}
return *this;
}
A(A && other)
: a(std::move(other.a)), copy_counter(std::move(other.copy_counter)),
move_counter(std::move(other.move_counter) + 1) {}
A & operator=(A && other) {
if (this != &other) {
a = std::move(other.a);
copy_counter = std::move(other.copy_counter);
move_counter = std::move(other.move_counter) + 1;
}
return *this;
}
A & operator*=(const T & b) {
a *= b;
return *this;
}
T a{};
size_t copy_counter{0};
size_t move_counter{0};
};
template <typename T> struct C {
struct iterator {
using reference = A<T>;
using difference_type = void;
using iterator_category = std::input_iterator_tag;
using value_type = A<T>;
using pointer = A<T> *;
iterator(T pos) : pos(std::move(pos)) {}
A<T> operator*() { return A<int>(pos); }
bool operator!=(const iterator & other) const { return pos != other.pos; }
bool operator==(const iterator & other) const { return pos == other.pos; }
iterator & operator++() {
++pos;
return *this;
}
T pos;
};
C(T begin_, T end_) : begin_(std::move(begin_)), end_(std::move(end_)) {}
iterator begin() { return iterator(begin_); }
iterator end() { return iterator(end_); }
T begin_, end_;
};
class TestZipFixutre : public ::testing::Test {
protected:
void SetUp() override {
a.reserve(size);
b.reserve(size);
for (size_t i = 0; i < size; ++i) {
a.emplace_back(i);
b.emplace_back(i + size);
}
}
template <typename A, typename B>
void check(A && a, B && b, size_t pos, size_t nb_copy, size_t nb_move) {
EXPECT_EQ(pos, a.a);
EXPECT_EQ(nb_copy, a.copy_counter);
EXPECT_EQ(nb_move, a.move_counter);
EXPECT_FLOAT_EQ(pos + this->size, b.a);
EXPECT_EQ(nb_copy, b.copy_counter);
EXPECT_EQ(nb_move, b.move_counter);
}
protected:
size_t size{20};
std::vector<A<int>> a{};
std::vector<A<float>> b{};
};
TEST_F(TestZipFixutre, SimpleTest) {
size_t i = 0;
for (auto && pair : zip(this->a, this->b)) {
this->check(std::get<0>(pair), std::get<1>(pair), i, 0, 0);
++i;
}
}
TEST_F(TestZipFixutre, ConstTest) {
size_t i = 0;
const auto & ca = this->a;
const auto & cb = this->b;
for (auto && pair : zip(ca, cb)) {
this->check(std::get<0>(pair), std::get<1>(pair), i, 0, 0);
EXPECT_EQ(true,
std::is_const<
std::remove_reference_t<decltype(std::get<0>(pair))>>::value);
EXPECT_EQ(true,
std::is_const<
std::remove_reference_t<decltype(std::get<1>(pair))>>::value);
++i;
}
}
TEST_F(TestZipFixutre, MixteTest) {
size_t i = 0;
const auto & cb = this->b;
for (auto && pair : zip(a, cb)) {
this->check(std::get<0>(pair), std::get<1>(pair), i, 0, 0);
EXPECT_EQ(false,
std::is_const<
std::remove_reference_t<decltype(std::get<0>(pair))>>::value);
EXPECT_EQ(true,
std::is_const<
std::remove_reference_t<decltype(std::get<1>(pair))>>::value);
++i;
}
}
TEST_F(TestZipFixutre, MoveTest) {
size_t i = 0;
for (auto && pair :
zip(C<int>(0, this->size), C<int>(this->size, 2 * this->size))) {
this->check(std::get<0>(pair), std::get<1>(pair), i, 0, 1);
++i;
}
}
TEST_F(TestZipFixutre, Bidirectional) {
auto _zip = zip(a, b);
auto begin = _zip.begin();
auto it = begin;
++it;
EXPECT_EQ(begin, --it);
it = begin;
EXPECT_EQ(begin, it++);
EXPECT_EQ(begin, --it);
auto it2 = it = begin;
++it;
++it2;
EXPECT_EQ(it2, it--);
EXPECT_EQ(begin, it);
}
TEST_F(TestZipFixutre, RandomAccess) {
auto _zip = zip(a, b);
auto begin = _zip.begin();
auto end = _zip.end();
auto && val5 = begin[5];
this->check(std::get<0>(val5), std::get<1>(val5), 5, 0, 0);
auto && val13 = begin[13];
this->check(std::get<0>(val13), std::get<1>(val13), 13, 0, 0);
EXPECT_EQ(end - begin, a.size());
auto it = ++begin;
EXPECT_EQ(begin + 1, ++it);
EXPECT_EQ(begin, it - 1);
}
TEST_F(TestZipFixutre, Cat) {
size_t i = 0;
for (auto && data : make_zip_cat(zip(a, b), zip(a, b))) {
this->check(std::get<0>(data), std::get<1>(data), i, 0, 0);
this->check(std::get<2>(data), std::get<3>(data), i, 0, 0);
++i;
}
}
TEST(TestNamedZipFixutre, Simple) {
std::vector<int> a{0, 10, 20, 30, 40};
std::vector<int> b{0, 1, 2, 3, 4};
using namespace tuple;
for (auto && data : named_zip(get<"a"_h>() = a, get<"b"_h>() = b)) {
auto & a = tuple::get<"a"_h>(data);
auto & b = tuple::get<"b"_h>(data);
b *= 10;
EXPECT_EQ(b, a);
}
- for (auto && data : named_zip(get<"a"_h>() = a, get<"b"_h>() = b)) {
+ for (auto && data : named_zip(get<"a"_h>() = a, get<"b"_h>() = b)) {
auto & a = tuple::get<"a"_h>(data);
auto & b = tuple::get<"b"_h>(data);
EXPECT_EQ(b, a);
}
}
/* -------------------------------------------------------------------------- */
TEST(TestArangeIterator, Stop) {
size_t ref_i = 0;
for (auto i : arange(10)) {
EXPECT_EQ(ref_i, i);
++ref_i;
}
}
TEST(TestArangeIterator, StartStop) {
size_t ref_i = 1;
for (auto i : arange(1, 10)) {
EXPECT_EQ(ref_i, i);
++ref_i;
}
}
TEST(TestArangeIterator, StartStopStep) {
size_t ref_i = 1;
for (auto i : arange(1, 22, 2)) {
EXPECT_EQ(ref_i, i);
ref_i += 2;
}
}
TEST(TestArangeIterator, StartStopStepZipped) {
int ref_i1 = -1, ref_i2 = 1;
for (auto && i : zip(arange(-1, -10, -1), arange(1, 18, 2))) {
EXPECT_EQ(ref_i1, std::get<0>(i));
EXPECT_EQ(ref_i2, std::get<1>(i));
ref_i1 += -1;
ref_i2 += 2;
}
}
/* -------------------------------------------------------------------------- */
TEST(TestEnumerateIterator, SimpleTest) {
std::vector<int> a{0, 10, 20, 30, 40};
std::vector<int> b{0, 2, 4, 6, 8};
for (auto && data : enumerate(a, b)) {
EXPECT_EQ(std::get<0>(data) * 10, std::get<1>(data));
EXPECT_EQ(std::get<0>(data) * 2, std::get<2>(data));
}
}
/* -------------------------------------------------------------------------- */
TEST(TestTransformAdaptor, Keys) {
std::map<std::string, int> map{
{"1", 1}, {"2", 2}, {"3", 3}, {"3", 3}, {"4", 4}};
char counter = '1';
for (auto && key : make_keys_adaptor(map)) {
EXPECT_EQ(counter, key[0]);
++counter;
}
}
TEST(TestTransformAdaptor, Values) {
std::map<std::string, int> map{
{"1", 1}, {"2", 2}, {"3", 3}, {"3", 3}, {"4", 4}};
int counter = 1;
for (auto && value : make_values_adaptor(map)) {
EXPECT_EQ(counter, value);
++counter;
}
}
static int plus1(int value) { return value + 1; }
struct Plus {
Plus(int a) : a(a) {}
int operator()(int b) { return a + b; }
private:
int a{0};
};
TEST(TestTransformAdaptor, Lambda) {
auto && container = arange(10);
for (auto && data :
zip(container, make_transform_adaptor(container, [](auto && value) {
return value + 1;
}))) {
EXPECT_EQ(std::get<0>(data) + 1, std::get<1>(data));
}
}
TEST(TestTransformAdaptor, LambdaLambda) {
std::map<std::string, int> map{
{"1", 1}, {"2", 2}, {"3", 3}, {"3", 3}, {"4", 4}};
int counter = 1;
for (auto && data : make_transform_adaptor(
make_values_adaptor(map), [](auto && value) { return value + 1; })) {
EXPECT_EQ(counter + 1, data);
++counter;
}
auto && container = arange(10);
for (auto && data :
zip(container, make_transform_adaptor(container, [](auto && value) {
return value + 1;
}))) {
EXPECT_EQ(std::get<0>(data) + 1, std::get<1>(data));
}
}
TEST(TestTransformAdaptor, Function) {
auto && container = arange(10);
for (auto && data :
zip(container, make_transform_adaptor(container, plus1))) {
EXPECT_EQ(std::get<0>(data) + 1, std::get<1>(data));
}
}
TEST(TestTransformAdaptor, Functor) {
auto && container = arange(10);
for (auto && data :
zip(container, make_transform_adaptor(container, Plus(1)))) {
EXPECT_EQ(std::get<0>(data) + 1, std::get<1>(data));
}
}
/* -------------------------------------------------------------------------- */
TEST(TestFilteredIterator, Simple) {
std::vector<int> values{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::vector<int> filter_{1, 3, 4, 10, 8, 6};
for (auto && data : zip(filter_, filter(filter_, values))) {
EXPECT_EQ(std::get<0>(data), std::get<1>(data));
}
}
/* -------------------------------------------------------------------------- */
TEST(TestFilteredIterator, Temporary) {
std::vector<int> filter_{1, 3, 4, 10, 8, 6};
for (auto && data :
zip(filter_, filter(filter_, std::vector<int>{0, 1, 2, 3, 4, 5, 6, 7, 8,
9, 10}))) {
EXPECT_EQ(std::get<0>(data), std::get<1>(data));
}
}
+
+/* -------------------------------------------------------------------------- */
+TEST(TestConcatenateIterator, SimpleTest) {
+ for (auto && data : zip(arange(0, 13), concat(arange(0, 5), arange(5, 10),
+ arange(10, 13)))) {
+ EXPECT_EQ(std::get<0>(data), std::get<1>(data));
+ }
+}
diff --git a/third-party/iohelper/CMakeLists.txt b/third-party/iohelper/CMakeLists.txt
index 37acaab8d..da2daf7da 100644
--- a/third-party/iohelper/CMakeLists.txt
+++ b/third-party/iohelper/CMakeLists.txt
@@ -1,81 +1,81 @@
#===============================================================================
# @file CMakeLists.txt
#
# @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
#
# @date creation: Thu Oct 11 2012
# @date last modification: Fri Jun 13 2014
#
# @brief main configuration file
#
# @section LICENSE
#
# Copyright (©) 2010-2012, 2014 EPFL (Ecole Polytechnique Fédérale de Lausanne)
# Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)
#
# IOHelper 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.
#
# IOHelper 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 IOHelper. If not, see <http://www.gnu.org/licenses/>.
#
#===============================================================================
#===============================================================================
# CMake Project
#===============================================================================
cmake_minimum_required(VERSION 2.6)
project(IOHelper)
enable_language(CXX)
#===============================================================================
# Misc.
#===============================================================================
set(BUILD_SHARED_LIBS ON CACHE BOOL "Build shared libraries.")
find_package (ZLIB)
if(ZLIB_FOUND)
set (IOHELPER_EXTERNAL_LIBS ${IOHELPER_EXTERNAL_LIBS} ${ZLIB_LIBRARIES})
- include_directories(${ZLIB_INCLUDE_DIRS})
+ include_directories(${ZLIB_INCLUDE_DIR})
endif()
#===============================================================================
# Version Number
#===============================================================================
# IOHelper version number. An even minor number corresponds to releases.
set(IOHELPER_MAJOR_VERSION 1)
set(IOHELPER_MINOR_VERSION 1)
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/CMakeVersionGenerator.cmake)
define_project_version()
#==============================================================================
# Library
#===============================================================================
add_subdirectory(src)
#==============================================================================
# Packaging
#===============================================================================
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/IOHelperCPack.cmake)
#===============================================================================
# Tests
#===============================================================================
option(IOHELPER_TESTS "Activate tests" OFF)
if(IOHELPER_TESTS)
enable_testing()
include(CTest)
add_subdirectory(test)
-endif()
\ No newline at end of file
+endif()
diff --git a/third-party/iohelper/src/CMakeLists.txt b/third-party/iohelper/src/CMakeLists.txt
index a85da8a54..5f438689a 100644
--- a/third-party/iohelper/src/CMakeLists.txt
+++ b/third-party/iohelper/src/CMakeLists.txt
@@ -1,111 +1,112 @@
#===============================================================================
# @file CMakeLists.txt
#
# @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
#
# @date creation: Thu Nov 24 2011
# @date last modification: Wed Nov 13 2013
#
# @brief main iohelper configuration
#
# @section LICENSE
#
# Copyright (©) 2010-2012, 2014 EPFL (Ecole Polytechnique Fédérale de Lausanne)
# Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)
#
# IOHelper 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.
#
# IOHelper 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 IOHelper. If not, see <http://www.gnu.org/licenses/>.
#
#===============================================================================
#===============================================================================
# List of source files
#===============================================================================
set(IOHELPER_COMMON_SRC
dumper_lammps.cc
dumper.cc
dumper_paraview.cc
dumper_text.cc
paraview_helper.cc
# src/reader_restart.cpp
)
set(IOHELPER_COMMON_HEADERS
field_inline_impl.hh
dumper_restart.hh
field_interface.hh
visitor.hh
field.hh
variable_inline_impl.hh
variable_interface.hh
variable.hh
container_array.hh
dumper_paraview.hh
dumper_text.hh
paraview_helper.tcc
iohelper_common.hh
io_helper.hh
dumper.hh
file_manager.hh
paraview_helper.hh
dumper_C_wrapper.h
base64.hh
dumper_lammps.hh
base64_reader.hh
)
#===============================================================================
# Library creation rule
#===============================================================================
add_library(iohelper ${IOHELPER_COMMON_SRC})
# link library with other libraries
target_link_libraries(iohelper ${IOHELPER_EXTERNAL_LIBS})
target_include_directories(iohelper
PRIVATE $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src>
INTERFACE $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src>
$<INSTALL_INTERFACE:include/iohelper>
+ PUBLIC ${ZLIB_INCLUDE_DIR} ${ZLIB_INCLUDE_DIRS}
)
set_target_properties(iohelper PROPERTIES PUBLIC_HEADER "${IOHELPER_COMMON_HEADERS}")
set_property(TARGET iohelper PROPERTY CXX_STANDARD 11)
export(TARGETS iohelper
FILE "${CMAKE_BINARY_DIR}/IOHelperLibraryDepends.cmake")
export(PACKAGE IOHelper)
#===============================================================================
# Install rules
#===============================================================================
# Tweak for when IOHelper is a subproject
if(NOT IOHELPER_TARGETS_EXPORT)
set(IOHELPER_TARGETS_EXPORT IOHelperLibraryDepends)
endif()
install(TARGETS iohelper
EXPORT ${IOHELPER_TARGETS_EXPORT}
ARCHIVE DESTINATION lib COMPONENT lib
LIBRARY DESTINATION lib COMPONENT lib
RUNTIME DESTINATION lib COMPONENT lib
PUBLIC_HEADER DESTINATION include/iohelper COMPONENT dev
)
# Install the export set for use with the install-tree
if("${IOHELPER_TARGETS_EXPORT}" STREQUAL "IOHelperLibraryDepends")
install(EXPORT IOHelperLibraryDepends DESTINATION lib/iohelper
COMPONENT dev)
endif()
set(IOHELPER_INCLUDE_DIRS ${IOHelper_SOURCE_DIR}/src CACHE INTERNAL "Internal include directorie to link with IOHelper as a subproject")

Event Timeline