diff --git a/cmake/AkantuExtraCompilationProfiles.cmake b/cmake/AkantuExtraCompilationProfiles.cmake index 9a3b7c819..b8e18096e 100644 --- a/cmake/AkantuExtraCompilationProfiles.cmake +++ b/cmake/AkantuExtraCompilationProfiles.cmake @@ -1,114 +1,121 @@ #=============================================================================== # Copyright (©) 2016-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) # Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) # # This file is part of Akantu # # Akantu is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # # Akantu is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License along # with Akantu. If not, see . # #=============================================================================== 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() set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG -DAKANTU_NDEBUG" CACHE STRING "Flags used by the compiler during release builds" FORCE) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") 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) endif() function(declare_compilation_profile name) include(CMakeParseArguments) cmake_parse_arguments(_args "" "COMPILER;LINKER;DOC" "" ${ARGN}) string(TOUPPER "${name}" _u_name) if(NOT _args_DOC) string(TOLOWER "${name}" _args_DOC) endif() if(NOT _args_COMPILER) message(FATAL_ERROR "declare_compilation_profile: you should at least give COMPILER flags") endif() if(NOT _args_LINKER) set(_args_LINKER ${_args_COMPILER}) endif() foreach(_flag CXX C Fortran SHARED_LINKER EXE_LINKER) set(_stage "compiler") set(_flags ${_args_COMPILER}) if(_stage MATCHES ".*LINKER") set(_stage "linker") set(_flags ${_args_LINKER}) endif() set(CMAKE_${_flag}_FLAGS_${_u_name} ${_flags} CACHE STRING "Flags used by the ${_stage} during coverage builds" FORCE) mark_as_advanced(CMAKE_${_flag}_FLAGS_${_u_name}) endforeach() endfunction() # Profiling declare_compilation_profile(PROFILING COMPILER "-g -ggdb3 -pg -DNDEBUG -DAKANTU_NDEBUG -O3") # Valgrind declare_compilation_profile(VALGRIND COMPILER "-g -ggdb3 -DNDEBUG -DAKANTU_NDEBUG -O3") # Coverage declare_compilation_profile(COVERAGE COMPILER "-g -ggdb3 -DNDEBUG -DAKANTU_NDEBUG -O2 --coverage") # 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") if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(_blacklist " -fsanitize-blacklist=${PROJECT_SOURCE_DIR}/cmake/sanitize-blacklist.txt") endif() declare_compilation_profile(SANITIZE COMPILER "-g -ggdb3 -O2 -fsanitize=address -fsanitize=leak -fsanitize=undefined -fno-omit-frame-pointer${_blacklist}") declare_compilation_profile(SANITIZE_DEBUG COMPILER "-g -ggdb3 -DNDEBUG -DAKANTU_NDEBUG -fsanitize=address -fsanitize=leak -fsanitize=undefined -fno-omit-frame-pointer${_blacklist}") endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") declare_compilation_profile(SANITIZEMEMORY COMPILER "-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" DOC "\"sanitize memory\"") endif() string(TOLOWER "${CMAKE_BUILD_TYPE}" _cmake_build_type_lower) if (_cmake_build_type_lower MATCHES "valgrind") find_program(VALGRIND_EXECUTABLE valgrind) endif() + +option(AKANTU_USE_CCACHE "Use ccache if available to build akantu" ON) + +if (AKANTU_USE_CCACHE) + find_program(CCACHE_EXECUTABLE ccache) + set(AKANTU_COMPILER_LAUNCHER "${CCACHE_EXECUTABLE}") +endif() diff --git a/examples/boundary_conditions/user_defined_bc/user_defined_bc.cc b/examples/boundary_conditions/user_defined_bc/user_defined_bc.cc index e805b5f61..8d975d462 100644 --- a/examples/boundary_conditions/user_defined_bc/user_defined_bc.cc +++ b/examples/boundary_conditions/user_defined_bc/user_defined_bc.cc @@ -1,78 +1,78 @@ /** * Copyright (©) 2010-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "solid_mechanics_model.hh" /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ using namespace akantu; class SineBoundary : public BC::Dirichlet::DirichletFunctor { public: SineBoundary(Real amp, Real phase, BC::Axis ax = _x) : DirichletFunctor(ax), amplitude(amp), phase(phase) {} public: - inline void operator()(__attribute__((unused)) UInt node, - Vector & flags, Vector & primal, - const Vector & coord) const { + inline void operator()(Idx /*node*/, VectorProxy & flags, + VectorProxy & primal, + const VectorProxy & coord) { DIRICHLET_SANITY_CHECK; flags(axis) = true; primal(axis) = -amplitude * std::sin(phase * coord(1)); } protected: Real amplitude; Real phase; }; /* -------------------------------------------------------------------------- */ int main(int argc, char * argv[]) { initialize("material.dat", argc, argv); Int spatial_dimension = 2; Mesh mesh(spatial_dimension); mesh.read("fine_mesh.msh"); SolidMechanicsModel model(mesh); /// model initialization model.initFull(); /// boundary conditions Vector traction{.2, .2}; model.applyBC(SineBoundary(.2, 10., _x), "Fixed_x"); model.applyBC(BC::Dirichlet::FixedValue(0., _y), "Fixed_y"); model.applyBC(BC::Neumann::FromTraction(traction), "Traction"); // output a paraview file with the boundary conditions model.setBaseName("plate"); model.addDumpFieldVector("displacement"); model.addDumpFieldVector("external_force"); model.addDumpField("blocked_dofs"); model.dump(); finalize(); return EXIT_SUCCESS; } 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 eb5110fb7..3808c9b4b 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,141 +1,141 @@ /** * Copyright (©) 2014-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "solid_mechanics_model_cohesive.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ 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 & /*flags*/, - Vector & disp, - const Vector & coord) const { + inline void operator()(Idx node, VectorProxy & /*flags*/, + VectorProxy & disp, + const VectorProxy & coord) { 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 Int spatial_dimension = 2; const Int max_steps = 1000; Mesh mesh(spatial_dimension); mesh.read("square.msh"); SolidMechanicsModelCohesive model(mesh); MaterialCohesiveRules rules{{{"btop", "bbottom"}, "tg_cohesive"}, {{"btop", "btop"}, "ig_cohesive"}, {{"bbottom", "bbottom"}, "ig_cohesive"}}; /// model initialization auto cohesive_material_selector = std::make_shared(model, rules); auto bulk_material_selector = std::make_shared>("physical_names", model); auto && current_selector = model.getMaterialSelector(); cohesive_material_selector->setFallback(bulk_material_selector); bulk_material_selector->setFallback(current_selector); model.setMaterialSelector(cohesive_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 (Int 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/phase_field/phase_field_notch.cc b/examples/phase_field/phase_field_notch.cc index 9a9a6a88d..18371775a 100644 --- a/examples/phase_field/phase_field_notch.cc +++ b/examples/phase_field/phase_field_notch.cc @@ -1,128 +1,126 @@ /** * Copyright (©) 2018-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "coupler_solid_phasefield.hh" #include "group_manager.hh" #include "non_linear_solver.hh" #include "phase_field_element_filter.hh" #include "phase_field_model.hh" #include "solid_mechanics_model.hh" /* -------------------------------------------------------------------------- */ #include #include #include /* -------------------------------------------------------------------------- */ using namespace akantu; using clk = std::chrono::high_resolution_clock; using second = std::chrono::duration; using millisecond = std::chrono::duration; const Int spatial_dimension = 2; /* -------------------------------------------------------------------------- */ int main(int argc, char * argv[]) { initialize("material_notch.dat", argc, argv); // create mesh Mesh mesh(spatial_dimension); mesh.read("square_notch.msh"); CouplerSolidPhaseField coupler(mesh); auto & model = coupler.getSolidMechanicsModel(); auto & phase = coupler.getPhaseFieldModel(); model.initFull(_analysis_method = _static); auto && mat_selector = std::make_shared>("physical_names", model); model.setMaterialSelector(mat_selector); auto && selector = std::make_shared>( "physical_names", phase); phase.setPhaseFieldSelector(selector); phase.initFull(_analysis_method = _static); model.applyBC(BC::Dirichlet::FixedValue(0., _y), "bottom"); model.applyBC(BC::Dirichlet::FixedValue(0., _x), "left"); model.setBaseName("phase_notch"); model.addDumpField("stress"); model.addDumpField("grad_u"); model.addDumpFieldVector("displacement"); model.addDumpField("damage"); model.dump(); Int nbSteps = 1000; Real increment = 6e-6; Int nb_staggered_steps = 5; auto start_time = clk::now(); for (Int s = 1; s < nbSteps; ++s) { if (s >= 500) { increment = 2e-6; nb_staggered_steps = 10; } if (s % 200 == 0) { constexpr char wheel[] = "/-\\|"; auto elapsed = clk::now() - start_time; auto time_per_step = elapsed / s; std::cout << "\r[" << wheel[(s / 10) % 4] << "] " << std::setw(5) << s << "/" << nbSteps << " (" << std::setprecision(2) << std::fixed << std::setw(8) << millisecond(time_per_step).count() << "ms/step - elapsed: " << std::setw(8) << second(elapsed).count() << "s - ETA: " << std::setw(8) << second((nbSteps - s) * time_per_step).count() << "s)" << std::string(' ', 20) << std::flush; } model.applyBC(BC::Dirichlet::IncrementValue(increment, _y), "top"); for (Idx i = 0; i < nb_staggered_steps; ++i) { coupler.solve(); } - auto energy = phase.getEnergy(); - if (s % 100 == 0) { model.dump(); } } Real damage_limit = 0.15; auto global_nb_clusters = mesh.createClusters( spatial_dimension, "crack", PhaseFieldElementFilter(phase, damage_limit)); auto nb_fragment = mesh.getNbElementGroups(spatial_dimension); model.dumpGroup("crack_0"); std::cout << std::endl; std::cout << "Nb clusters: " << global_nb_clusters << std::endl; std::cout << "Nb fragments: " << nb_fragment << std::endl; finalize(); return EXIT_SUCCESS; } diff --git a/python/py_boundary_conditions.cc b/python/py_boundary_conditions.cc index fd270deed..3f6acbc06 100644 --- a/python/py_boundary_conditions.cc +++ b/python/py_boundary_conditions.cc @@ -1,118 +1,120 @@ /** * Copyright (©) 2019-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "py_boundary_conditions.hh" -#include "py_aka_array.hh" #include "py_akantu_pybind11_compatibility.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ +#include #include /* -------------------------------------------------------------------------- */ namespace py = pybind11; namespace akantu { namespace { /* ------------------------------------------------------------------------ */ template class PyDirichletFunctor : public daughter { public: /* Inherit the constructors */ using daughter::daughter; /* Trampoline (need one for each virtual function) */ - void operator()(Int node, Vector & flags, Vector & primal, - const Vector & coord) const override { + void operator()(Int node, VectorProxy & flags, + VectorProxy & primal, + const VectorProxy & coord) override { // NOLINTNEXTLINE PYBIND11_OVERRIDE_NAME(void, daughter, "__call__", operator(), node, flags, primal, coord); } }; /* ------------------------------------------------------------------------ */ template class PyNeumannFunctor : public daughter { public: /* Inherit the constructors */ using daughter::daughter; /* Trampoline (need one for each virtual function) */ - void operator()(const IntegrationPoint & quad_point, Vector & dual, - const Vector & coord, - const Vector & normals) const override { + void operator()(const IntegrationPoint & quad_point, + VectorProxy & dual, + const VectorProxy & coord, + const VectorProxy & normals) override { // NOLINTNEXTLINE PYBIND11_OVERRIDE_PURE_NAME(void, daughter, "__call__", operator(), quad_point, dual, coord, normals); } }; /* ------------------------------------------------------------------------ */ template decltype(auto) register_dirichlet_functor(py::module mod, const char * name, Constructor && cons) { py::class_, BC::Dirichlet::DirichletFunctor>(mod, name) .def(cons); } /* ------------------------------------------------------------------------ */ template decltype(auto) register_neumann_functor(py::module mod, const char * name, Constructor && cons) { py::class_, BC::Neumann::NeumannFunctor>( mod, name) .def(cons); } } // namespace /* -------------------------------------------------------------------------- */ void register_boundary_conditions(py::module & mod) { py::class_(mod, "BCFunctor"); py::class_, BC::Functor>(mod, "DirichletFunctor") .def(py::init()) .def(py::init()); py::class_, BC::Functor>( mod, "NeumannFunctor") .def(py::init()); register_dirichlet_functor( mod, "FixedValue", py::init()); register_dirichlet_functor( mod, "IncrementValue", py::init()); register_dirichlet_functor( mod, "Increment", py::init &>()); register_neumann_functor( mod, "FromHigherDim", py::init &>()); register_neumann_functor( mod, "FromSameDim", py::init &>()); register_neumann_functor(mod, "FreeBoundary", py::init()); } } // namespace akantu diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bb43e03c2..5dd68a051 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,241 +1,246 @@ #=============================================================================== # Copyright (©) 2010-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) # Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) # # This file is part of Akantu # # Akantu is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # # Akantu is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License along # with Akantu. If not, see . # #=============================================================================== #=============================================================================== # Package Management #=============================================================================== package_get_all_source_files( AKANTU_LIBRARY_SRCS AKANTU_LIBRARY_PUBLIC_HDRS AKANTU_LIBRARY_PRIVATE_HDRS ) package_get_all_include_directories( AKANTU_LIBRARY_INCLUDE_DIRS ) package_get_all_external_informations( PRIVATE_INCLUDE AKANTU_PRIVATE_EXTERNAL_INCLUDE_DIR INTERFACE_INCLUDE AKANTU_INTERFACE_EXTERNAL_INCLUDE_DIR LIBRARIES AKANTU_EXTERNAL_LIBRARIES ) package_get_all_compilation_flags(CXX _cxx_flags) set(AKANTU_EXTRA_CXX_FLAGS "${_cxx_flags}" CACHE STRING "Extra flags defined by loaded packages" FORCE) mark_as_advanced(AKANTU_EXTRA_CXX_FLAGS) foreach(src_ ${AKANTU_SPIRIT_SOURCES}) set_property(SOURCE ${src_} PROPERTY COMPILE_FLAGS "-g0 -Werror") endforeach() 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) 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_config.hh") configure_file(common/aka_config.cc.in "${CMAKE_CURRENT_BINARY_DIR}/aka_config.cc" @ONLY) list(APPEND AKANTU_LIBRARY_SRCS "${CMAKE_CURRENT_BINARY_DIR}/aka_config.cc") #=============================================================================== # Debug infos #=============================================================================== set(AKANTU_GDB_DIR ${PROJECT_SOURCE_DIR}/cmake) if(UNIX AND NOT APPLE) string(TOUPPER "${CMAKE_BUILD_TYPE}" _u_build_type) if(_u_build_type STREQUAL "DEBUG" OR _u_build_type STREQUAL "RELWITHDEBINFO") configure_file(${PROJECT_SOURCE_DIR}/cmake/libakantu-gdb.py.in "${PROJECT_BINARY_DIR}/libakantu-gdb.py" @ONLY) configure_file(${PROJECT_SOURCE_DIR}/cmake/akantu-debug.cc.in "${PROJECT_BINARY_DIR}/akantu-debug.cc" @ONLY) list(APPEND AKANTU_LIBRARY_SRCS ${PROJECT_BINARY_DIR}/akantu-debug.cc) endif() else() find_program(GDB_EXECUTABLE gdb) if(GDB_EXECUTABLE) execute_process(COMMAND ${GDB_EXECUTABLE} --batch -x "${PROJECT_SOURCE_DIR}/cmake/gdb_python_path" OUTPUT_VARIABLE AKANTU_PYTHON_GDB_DIR ERROR_QUIET RESULT_VARIABLE _res) if(_res EQUAL 0 AND UNIX) set(GDB_USER_CONFIG $ENV{HOME}/.gdb/auto-load) file(MAKE_DIRECTORY ${GDB_USER_CONFIG}) configure_file(${PROJECT_SOURCE_DIR}/cmake/libakantu-gdb.py.in "${GDB_USER_CONFIG}/${CMAKE_SHARED_LIBRARY_PREFIX}akantu${CMAKE_SHARED_LIBRARY_SUFFIX}.${AKANTU_VERSION}-gdb.py" @ONLY) endif() endif() endif() #=============================================================================== # GIT info #=============================================================================== #include(AkantuBuildOptions) #git_version_info(akantu_git_info ALL # OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/akantu_git_info.hh # ) #list(APPEND AKANTU_LIBRARY_SRCS ${CMAKE_CURRENT_BINARY_DIR}/akantu_git_info.hh) #=============================================================================== # Library generation #=============================================================================== add_library(akantu ${AKANTU_LIBRARY_SRCS}) target_include_directories(akantu PRIVATE $ INTERFACE $ ) # small trick for build includes in public set_property(TARGET akantu APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES $) target_include_directories(akantu SYSTEM PUBLIC ${AKANTU_INTERFACE_EXTERNAL_INCLUDE_DIR} ) target_include_directories(akantu SYSTEM PRIVATE ${AKANTU_PRIVATE_EXTERNAL_INCLUDE_DIR} ) target_link_libraries(akantu PUBLIC ${AKANTU_EXTERNAL_LIBRARIES}) separate_arguments(_separated_cxx_flags UNIX_COMMAND ${_cxx_flags}) target_compile_options(akantu PUBLIC ${_separated_cxx_flags}) if(AKANTU_LIBRARY_PROPERTIES) set_target_properties(akantu PROPERTIES ${AKANTU_LIBRARY_PROPERTIES} # this contains the version ) endif() if(NOT AKANTU_SHARED) set_property(TARGET akantu PROPERTY POSITION_INDEPENDENT_CODE ${AKANTU_POSITION_INDEPENDENT}) endif() if(AKANTU_LIBRARY_PUBLIC_HDRS) set_property(TARGET akantu PROPERTY PUBLIC_HEADER ${AKANTU_LIBRARY_PUBLIC_HDRS}) endif() if(AKANTU_LIBRARY_PRIVATE_HDRS) set_property(TARGET akantu PROPERTY PRIVATE_HEADER ${AKANTU_LIBRARY_PRIVATE_HDRS}) endif() target_compile_features(akantu PUBLIC cxx_std_${AKANTU_CXX_STANDARD}) set_target_properties(akantu PROPERTIES CXX_STANDARD ${AKANTU_CXX_STANDARD}) +if (AKANTU_COMPILER_LAUNCHER) + set_target_properties(akantu + PROPERTIES CXX_COMPILER_LAUNCHER ${AKANTU_COMPILER_LAUNCHER}) +endif() + package_get_all_extra_dependencies(_extra_target_dependencies) if(_extra_target_dependencies) # This only adding todo: find a solution for when a dependency was add the is removed... add_dependencies(akantu ${_extra_target_dependencies}) endif() package_get_all_export_list(AKANTU_EXPORT_LIST) list(APPEND AKANTU_EXPORT_LIST akantu) # TODO separate public from private headers install(TARGETS akantu EXPORT ${AKANTU_TARGETS_EXPORT} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Akantu_runtime # NAMELINK_ONLY # LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} # COMPONENT Akantu_development # NAMELINK_SKIP Akantu_development ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Akantu_runtime RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Akantu_runtime PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/akantu/ COMPONENT Akantu_development ) if("${AKANTU_TARGETS_EXPORT}" STREQUAL "AkantuTargets") install(EXPORT AkantuTargets DESTINATION lib/cmake/${PROJECT_NAME} COMPONENT dev) #Export for build tree export(EXPORT AkantuTargets FILE "${CMAKE_BINARY_DIR}/AkantuTargets.cmake") export(PACKAGE Akantu) endif() #=============================================================================== # Adding module names for debug package_get_all_packages(_pkg_list) foreach(_pkg ${_pkg_list}) _package_get_real_name(${_pkg} _pkg_name) _package_get_source_files(${_pkg} _srcs _public_hdrs _private_hdrs) string(TOLOWER "${_pkg_name}" _l_package_name) set_property(SOURCE ${_srcs} ${_public_hdrs} ${_private_hdrs} APPEND PROPERTY COMPILE_DEFINITIONS AKANTU_MODULE=${_l_package_name}) endforeach() # print out the list of materials generate_material_list() register_target_to_tidy(akantu) register_tidy_all(${AKANTU_LIBRARY_SRCS}) register_code_to_format( ${AKANTU_LIBRARY_SRCS} ${AKANTU_LIBRARY_PUBLIC_HDRS} ${AKANTU_LIBRARY_PRIVATE_HDRS} ) cmake_policy(SET CMP0069 NEW) include(CheckIPOSupported) check_ipo_supported(RESULT ipo_supported OUTPUT ipo_error LANGUAGES CXX) if(ipo_supported) message(STATUS "IPO/LTO enabled") set_property(TARGET akantu PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) else() message(STATUS "IPO/LTO not supported: <${ipo_error}>") endif() diff --git a/src/common/aka_array.hh b/src/common/aka_array.hh index 882256acd..30220ca0a 100644 --- a/src/common/aka_array.hh +++ b/src/common/aka_array.hh @@ -1,417 +1,411 @@ /** * Copyright (©) 2010-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "aka_common.hh" #include "aka_types.hh" #include "aka_view_iterators.hh" /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ #ifndef AKANTU_ARRAY_HH_ #define AKANTU_ARRAY_HH_ namespace akantu { /// class that afford to store vectors in static memory // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions) class ArrayBase { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: using size_type = Int; explicit ArrayBase(const ID & id = "") : id(id) {} ArrayBase(const ArrayBase & other, const ID & id = "") { this->id = (id.empty()) ? other.id : id; } ArrayBase(ArrayBase && other) = default; ArrayBase & operator=(const ArrayBase & other) = default; ArrayBase & operator=(ArrayBase && other) noexcept = default; virtual ~ArrayBase() = default; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ public: /// get the amount of space allocated in bytes virtual Int getMemorySize() const = 0; // changed empty to match std::vector empty [[nodiscard]] inline bool empty() const { return size_ == 0; } /// function to print the content of the class virtual void printself(std::ostream & stream, int indent = 0) const = 0; /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: /// Get the Size of the Array [[nodiscard]] decltype(auto) size() const { return size_; } /// Get the number of components [[nodiscard]] decltype(auto) getNbComponent() const { return nb_component; } /// Get the name of the array AKANTU_GET_MACRO_AUTO(ID, id); /// Set the name of th array AKANTU_SET_MACRO(ID, id, const ID &); /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ protected: /// id of the vector ID id; /// the size used Int size_{0}; /// number of components Int nb_component{1}; }; /* -------------------------------------------------------------------------- */ /* Memory handling layer */ /* -------------------------------------------------------------------------- */ enum class ArrayAllocationType { _default, _pod, }; template struct ArrayAllocationTrait : public std::conditional_t< aka::is_scalar::value, std::integral_constant, std::integral_constant> {}; /* -------------------------------------------------------------------------- */ template ::value> class ArrayDataLayer : public ArrayBase { public: using value_type = T; using size_type = typename ArrayBase::size_type; using reference = value_type &; using pointer_type = value_type *; using const_reference = const value_type &; public: ~ArrayDataLayer() override = default; /// Allocation of a new vector explicit ArrayDataLayer(Int size = 0, Int nb_component = 1, const ID & id = ""); /// Allocation of a new vector with a default value ArrayDataLayer(Int size, Int nb_component, const_reference value, const ID & id = ""); /// Copy constructor (deep copy) ArrayDataLayer(const ArrayDataLayer & vect, const ID & id = ""); /// Copy constructor (deep copy) explicit ArrayDataLayer(const std::vector & vect); // copy operator ArrayDataLayer & operator=(const ArrayDataLayer & other); // move constructor ArrayDataLayer(ArrayDataLayer && other) noexcept = default; // move assign ArrayDataLayer & operator=(ArrayDataLayer && other) noexcept = default; protected: // deallocate the memory virtual void deallocate() {} // allocate the memory virtual void allocate(Int size, Int nb_component); // allocate and initialize the memory virtual void allocate(Int size, Int nb_component, const T & value); public: /// append a tuple of size nb_component containing value inline void push_back(const_reference value); /// append a vector // inline void push_back(const value_type new_elem[]); /// append a Vector or a Matrix template inline void push_back(const Eigen::MatrixBase & new_elem); /// changes the allocated size but not the size, if new_size = 0, the size is /// set to min(current_size and reserve size) virtual void reserve(Int size, Int new_size = Int(-1)); /// change the size of the Array virtual void resize(Int size); /// change the size of the Array and initialize the values virtual void resize(Int size, const T & val); /// get the amount of space allocated in bytes inline Int getMemorySize() const override; /// Get the real size allocated in memory inline Int getAllocatedSize() const; /// give the address of the memory allocated for this vector [[deprecated("use data instead to be stl compatible")]] T * storage() const { return values; }; - T * data() const { return values; }; + const T * data() const { return values; }; + T * data() { return values; }; protected: /// allocation type agnostic data access T * values{nullptr}; /// data storage std::vector data_storage; }; /* -------------------------------------------------------------------------- */ /* Actual Array */ /* -------------------------------------------------------------------------- */ template class Array : public ArrayDataLayer { private: using parent = ArrayDataLayer; /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: using value_type = typename parent::value_type; using size_type = typename parent::size_type; using reference = typename parent::reference; using pointer_type = typename parent::pointer_type; using const_reference = typename parent::const_reference; using array_type = Array; ~Array() override; Array() : Array(0){}; /// Allocation of a new vector explicit Array(Int size, Int nb_component = 1, const ID & id = ""); /// Allocation of a new vector with a default value explicit Array(Int size, Int nb_component, const_reference value, const ID & id = ""); /// Copy constructor Array(const Array & vect, const ID & id = ""); /// Copy constructor (deep copy) explicit Array(const std::vector & vect); // copy operator Array & operator=(const Array & other); // move constructor Array(Array && other) noexcept = default; // move assign Array & operator=(Array && other) noexcept = default; /* ------------------------------------------------------------------------ */ /* Iterator */ /* ------------------------------------------------------------------------ */ /// iterator for Array of nb_component = 1 using scalar_iterator = view_iterator; /// const_iterator for Array of nb_component = 1 - using const_scalar_iterator = const_view_iterator; + using const_scalar_iterator = const_view_iterator; /// iterator returning Vectors of size n on entries of Array with /// nb_component = n using vector_iterator = view_iterator>; /// const_iterator returning Vectors of n size on entries of Array with /// nb_component = n using const_vector_iterator = const_view_iterator>; /// iterator returning Matrices of size (m, n) on entries of Array with /// nb_component = m*n using matrix_iterator = view_iterator>; /// const iterator returning Matrices of size (m, n) on entries of Array with /// nb_component = m*n using const_matrix_iterator = const_view_iterator>; - /// iterator returning Tensor3 of size (m, n, k) on entries of Array with - /// nb_component = m*n*k - using tensor3_iterator = view_iterator>; - /// const iterator returning Tensor3 of size (m, n, k) on entries of Array - /// with nb_component = m*n*k - using const_tensor3_iterator = const_view_iterator>; - /* ------------------------------------------------------------------------ */ template inline auto begin(Ns &&... n); template inline auto end(Ns &&... n); template inline auto begin(Ns &&... n) const; template inline auto end(Ns &&... n) const; template inline auto cbegin(Ns &&... n) const; template inline auto cend(Ns &&... n) const; template [[deprecated("use make_view instead")]] inline auto begin_reinterpret(Ns &&... n); template [[deprecated("use make_view instead")]] inline auto end_reinterpret(Ns &&... n); template [[deprecated("use make_view instead")]] inline auto begin_reinterpret(Ns &&... n) const; template [[deprecated("use make_view instead")]] inline auto end_reinterpret(Ns &&... n) const; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ public: /// search elem in the vector, return the position of the first occurrence or /// -1 if not found Idx find(const_reference elem) const; /// @see Array::find(const_reference elem) const // Int find(T elem[]) const; /// append a value to the end of the Array inline void push_back(const_reference value) { parent::push_back(value); } /// append a Vector or a Matrix template inline void push_back(const Eigen::MatrixBase & new_elem) { parent::push_back(new_elem); } template inline void push_back(const const_view_iterator & it) { push_back(*it); } template inline void push_back(const view_iterator & it) { push_back(*it); } /// erase the value at position i inline void erase(Idx i); /// erase the entry corresponding to the iterator template inline auto erase(const view_iterator & it); /// @see Array::find(const_reference elem) const template ::value> * = nullptr> inline Idx find(const C & elem); /// set all entries of the array to the value t /// @param t value to fill the array with inline void set(T t) { std::fill_n(this->values, this->size_ * this->nb_component, t); } /// set all tuples of the array to a given vector or matrix /// @param vm Matrix or Vector to fill the array with template ::value> * = nullptr> inline void set(const C & vm); /// set the array to T{} inline void zero() { this->set({}); } /// resize the array to 0 inline void clear() { this->resize(0); } /// Append the content of the other array to the current one void append(const Array & other); /// copy another Array in the current Array, the no_sanity_check allows you to /// force the copy in cases where you know what you do with two non matching /// Arrays in terms of n void copy(const Array & other, bool no_sanity_check = false); /// function to print the containt of the class void printself(std::ostream & stream, int indent = 0) const override; /// Tests if all elements are finite. template ::value> * = nullptr> bool isFinite() const noexcept; /* ------------------------------------------------------------------------ */ /* Operators */ /* ------------------------------------------------------------------------ */ public: /// substraction entry-wise Array & operator-=(const Array & vect); /// addition entry-wise Array & operator+=(const Array & vect); /// multiply evry entry by alpha Array & operator*=(const T & alpha); /// check if the array are identical entry-wise bool operator==(const Array & other) const; /// @see Array::operator==(const Array & other) const bool operator!=(const Array & other) const; /// return a reference to the j-th entry of the i-th tuple inline reference operator()(Idx i, Idx j = 0); /// return a const reference to the j-th entry of the i-th tuple inline const_reference operator()(Idx i, Idx j = 0) const; /// return a reference to the ith component of the 1D array inline reference operator[](Idx i); /// return a const reference to the ith component of the 1D array inline const_reference operator[](Idx i) const; }; /* -------------------------------------------------------------------------- */ /* Inline Functions Array */ /* -------------------------------------------------------------------------- */ template inline std::ostream & operator<<(std::ostream & stream, const Array & _this) { _this.printself(stream); return stream; } /* -------------------------------------------------------------------------- */ /* Inline Functions ArrayBase */ /* -------------------------------------------------------------------------- */ inline std::ostream & operator<<(std::ostream & stream, const ArrayBase & _this) { _this.printself(stream); return stream; } } // namespace akantu #include "aka_array_tmpl.hh" #endif /* AKANTU_ARRAY_HH_ */ diff --git a/src/common/aka_array_tmpl.hh b/src/common/aka_array_tmpl.hh index 90642d9d8..b0426c796 100644 --- a/src/common/aka_array_tmpl.hh +++ b/src/common/aka_array_tmpl.hh @@ -1,1186 +1,1198 @@ /** * Copyright (©) 2010-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ /* Inline Functions Array */ /* -------------------------------------------------------------------------- */ #include "aka_array.hh" // NOLINT /* -------------------------------------------------------------------------- */ #include #include #include /* -------------------------------------------------------------------------- */ //#ifndef __AKANTU_AKA_ARRAY_TMPL_HH__ //#define __AKANTU_AKA_ARRAY_TMPL_HH__ /* -------------------------------------------------------------------------- */ namespace akantu { namespace debug { struct ArrayException : public Exception {}; } // namespace debug /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ template ArrayDataLayer::ArrayDataLayer(Int size, Int nb_component, const ID & id) : ArrayBase(id) { allocate(size, nb_component); } /* -------------------------------------------------------------------------- */ template ArrayDataLayer::ArrayDataLayer(Int size, Int nb_component, const_reference value, const ID & id) : ArrayBase(id) { allocate(size, nb_component, value); } /* -------------------------------------------------------------------------- */ template ArrayDataLayer::ArrayDataLayer(const ArrayDataLayer & vect, const ID & id) : ArrayBase(vect, id) { this->data_storage = vect.data_storage; this->size_ = vect.size_; this->nb_component = vect.nb_component; this->values = this->data_storage.data(); } /* -------------------------------------------------------------------------- */ template ArrayDataLayer::ArrayDataLayer( const std::vector & vect) { this->data_storage = vect; this->size_ = vect.size(); this->nb_component = 1; this->values = this->data_storage.data(); } /* -------------------------------------------------------------------------- */ template ArrayDataLayer & ArrayDataLayer::operator=(const ArrayDataLayer & other) { if (this != &other) { this->data_storage = other.data_storage; this->nb_component = other.nb_component; this->size_ = other.size_; this->values = this->data_storage.data(); } return *this; } /* -------------------------------------------------------------------------- */ template void ArrayDataLayer::allocate(Int new_size, Int nb_component) { this->nb_component = nb_component; this->resize(new_size); } /* -------------------------------------------------------------------------- */ template void ArrayDataLayer::allocate(Int new_size, Int nb_component, const T & val) { this->nb_component = nb_component; this->resize(new_size, val); } /* -------------------------------------------------------------------------- */ template void ArrayDataLayer::resize(Int new_size) { this->data_storage.resize(new_size * this->nb_component); this->values = this->data_storage.data(); this->size_ = new_size; } /* -------------------------------------------------------------------------- */ template void ArrayDataLayer::resize(Int new_size, const T & value) { this->data_storage.resize(new_size * this->nb_component, value); this->values = this->data_storage.data(); this->size_ = new_size; } /* -------------------------------------------------------------------------- */ template void ArrayDataLayer::reserve(Int size, Int new_size) { if (new_size != -1) { this->data_storage.resize(new_size * this->nb_component); } this->data_storage.reserve(size * this->nb_component); this->values = this->data_storage.data(); } /* -------------------------------------------------------------------------- */ /** * append a tuple to the array with the value value for all components * @param value the new last tuple or the array will contain nb_component copies * of value */ template inline void ArrayDataLayer::push_back(const T & value) { this->data_storage.push_back(value); this->values = this->data_storage.data(); this->size_ += 1; } /* -------------------------------------------------------------------------- */ /** * append a matrix or a vector to the array * @param new_elem a reference to a Matrix or Vector */ template template inline void ArrayDataLayer::push_back( const Eigen::MatrixBase & new_elem) { AKANTU_DEBUG_ASSERT( nb_component == new_elem.size(), "The vector(" << new_elem.size() << ") as not a size compatible with the Array (nb_component=" << nb_component << ")."); for (Idx i = 0; i < new_elem.size(); ++i) { this->data_storage.push_back(new_elem.array()[i]); } this->values = this->data_storage.data(); this->size_ += 1; } /* -------------------------------------------------------------------------- */ template inline Int ArrayDataLayer::getAllocatedSize() const { return this->data_storage.capacity() / this->nb_component; } /* -------------------------------------------------------------------------- */ template inline Int ArrayDataLayer::getMemorySize() const { return this->data_storage.capacity() * sizeof(T); } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ template class ArrayDataLayer : public ArrayBase { public: using value_type = T; using reference = value_type &; using pointer_type = value_type *; using size_type = typename ArrayBase::size_type; using const_reference = const value_type &; public: ~ArrayDataLayer() override { deallocate(); } /// Allocation of a new vector ArrayDataLayer(Int size = 0, Int nb_component = 1, const ID & id = "") : ArrayBase(id) { allocate(size, nb_component); } /// Allocation of a new vector with a default value ArrayDataLayer(Int size, Int nb_component, const_reference value, const ID & id = "") : ArrayBase(id) { allocate(size, nb_component, value); } /// Copy constructor (deep copy) ArrayDataLayer(const ArrayDataLayer & vect, const ID & id = "") : ArrayBase(vect, id) { allocate(vect.size(), vect.getNbComponent()); std::copy_n(vect.data(), this->size_ * this->nb_component, values); } /// Copy constructor (deep copy) explicit ArrayDataLayer(const std::vector & vect) { allocate(vect.size(), 1); std::copy_n(vect.data(), this->size_ * this->nb_component, values); } // copy operator inline ArrayDataLayer & operator=(const ArrayDataLayer & other) { if (this != &other) { allocate(other.size(), other.getNbComponent()); std::copy_n(other.data(), this->size_ * this->nb_component, values); } return *this; } // move constructor inline ArrayDataLayer(ArrayDataLayer && other) noexcept = default; // move assign inline ArrayDataLayer & operator=(ArrayDataLayer && other) noexcept = default; protected: // deallocate the memory virtual void deallocate() { // NOLINTNEXTLINE(cppcoreguidelines-owning-memory, // cppcoreguidelines-no-malloc) free(this->values); } // allocate the memory virtual inline void allocate(Int size, Int nb_component) { if (size != 0) { // malloc can return a non NULL pointer in case size is 0 this->values = static_cast( // NOLINT std::malloc(nb_component * size * sizeof(T))); // NOLINT } if (this->values == nullptr and size != 0) { throw std::bad_alloc(); } this->nb_component = nb_component; this->allocated_size = this->size_ = size; } // allocate and initialize the memory virtual inline void allocate(Int size, Int nb_component, const T & value) { allocate(size, nb_component); std::fill_n(values, size * nb_component, value); } public: /// append a tuple of size nb_component containing value inline void push_back(const_reference value) { resize(this->size_ + 1, value); } /// append a Vector or a Matrix template inline void push_back(const Eigen::MatrixBase & new_elem) { AKANTU_DEBUG_ASSERT( nb_component == new_elem.size(), "The vector(" << new_elem.size() << ") as not a size compatible with the Array (nb_component=" << nb_component << ")."); this->resize(this->size_ + 1); make_view(*this, new_elem.rows(), new_elem.cols()) .begin()[this->size_ - 1] = new_elem; } /// changes the allocated size but not the size virtual void reserve(Int size, Int new_size = Int(-1)) { auto tmp_size = this->size_; if (new_size != Int(-1)) { tmp_size = new_size; } this->resize(size); this->size_ = std::min(this->size_, tmp_size); } /// change the size of the Array virtual void resize(Int size) { if (size * this->nb_component == 0) { free(values); // NOLINT: cppcoreguidelines-no-malloc values = nullptr; this->allocated_size = 0; } else { if (this->values == nullptr) { this->allocate(size, this->nb_component); return; } Int diff = size - allocated_size; Int size_to_allocate = (std::abs(diff) > AKANTU_MIN_ALLOCATION) ? size : (diff > 0) ? allocated_size + AKANTU_MIN_ALLOCATION : allocated_size; if (size_to_allocate == allocated_size) { // otherwhy the reserve + push_back might fail... this->size_ = size; return; } auto * tmp_ptr = reinterpret_cast( // NOLINT realloc(this->values, size_to_allocate * this->nb_component * sizeof(T))); if (tmp_ptr == nullptr) { throw std::bad_alloc(); } this->values = tmp_ptr; this->allocated_size = size_to_allocate; } this->size_ = size; } /// change the size of the Array and initialize the values virtual void resize(Int size, const T & val) { Int tmp_size = this->size_; this->resize(size); if (size > tmp_size) { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) std::fill_n(values + this->nb_component * tmp_size, (size - tmp_size) * this->nb_component, val); } } /// get the amount of space allocated in bytes inline size_type getMemorySize() const final { return this->allocated_size * this->nb_component * sizeof(T); } /// Get the real size allocated in memory inline Int getAllocatedSize() const { return this->allocated_size; } /// give the address of the memory allocated for this vector [[deprecated("use data instead to be stl compatible")]] T * storage() const { return values; }; - T * data() const { return values; }; + const T * data() const { return values; }; + T * data() { return values; }; protected: /// allocation type agnostic data access T * values{nullptr}; Int allocated_size{0}; }; /* -------------------------------------------------------------------------- */ template inline auto Array::operator()(Int i, Int j) -> reference { AKANTU_DEBUG_ASSERT(this->size_ > 0, "The array \"" << this->id << "\" is empty"); AKANTU_DEBUG_ASSERT((i < this->size_) && (j < this->nb_component), "The value at position [" << i << "," << j << "] is out of range in array \"" << this->id << "\""); return this->values[i * this->nb_component + j]; } /* -------------------------------------------------------------------------- */ template inline auto Array::operator()(Int i, Int j) const -> const_reference { AKANTU_DEBUG_ASSERT(this->size_ > 0, "The array \"" << this->id << "\" is empty"); AKANTU_DEBUG_ASSERT((i < this->size_) && (j < this->nb_component), "The value at position [" << i << "," << j << "] is out of range in array \"" << this->id << "\""); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) return this->values[i * this->nb_component + j]; } template inline auto Array::operator[](Int i) -> reference { AKANTU_DEBUG_ASSERT(this->size_ > 0, "The array \"" << this->id << "\" is empty"); AKANTU_DEBUG_ASSERT((i < this->size_ * this->nb_component), "The value at position [" << i << "] is out of range in array \"" << this->id << "\""); return this->values[i]; } /* -------------------------------------------------------------------------- */ template inline auto Array::operator[](Int i) const -> const_reference { AKANTU_DEBUG_ASSERT(this->size_ > 0, "The array \"" << this->id << "\" is empty"); AKANTU_DEBUG_ASSERT((i < this->size_ * this->nb_component), "The value at position [" << i << "] is out of range in array \"" << this->id << "\""); return this->values[i]; } /* -------------------------------------------------------------------------- */ /** * erase an element. If the erased element is not the last of the array, the * last element is moved into the hole in order to maintain contiguity. This * may invalidate existing iterators (For instance an iterator obtained by * Array::end() is no longer correct) and will change the order of the * elements. * @param i index of element to erase */ template inline void Array::erase(Idx i) { - AKANTU_DEBUG_IN(); AKANTU_DEBUG_ASSERT((this->size_ > 0), "The array is empty"); AKANTU_DEBUG_ASSERT((i < this->size_), "The element at position [" << i << "] is out of range (" << i << ">=" << this->size_ << ")"); if (i != (this->size_ - 1)) { for (Idx j = 0; j < this->nb_component; ++j) { this->values[i * this->nb_component + j] = this->values[(this->size_ - 1) * this->nb_component + j]; } } this->resize(this->size_ - 1); - AKANTU_DEBUG_OUT(); } /* ------------------------------------------------------------------------ */ template template inline auto Array::erase(const view_iterator & it) { auto && curr = it.data(); Idx pos = (curr - this->values) / this->nb_component; erase(pos); view_iterator rit = it; return --rit; } /* -------------------------------------------------------------------------- */ /** * Subtract another array entry by entry from this array in place. Both arrays * must * have the same size and nb_component. If the arrays have different shapes, * code compiled in debug mode will throw an expeption and optimised code * will behave in an unpredicted manner * @param other array to subtract from this * @return reference to modified this */ template Array & Array::operator-=(const Array & vect) { AKANTU_DEBUG_ASSERT((this->size_ == vect.size_) && (this->nb_component == vect.nb_component), "The too array don't have the same sizes"); T * a = this->values; - T * b = vect.data(); + const T * b = vect.data(); for (Idx i = 0; i < this->size_ * this->nb_component; ++i) { *a -= *b; ++a; ++b; } return *this; } /* -------------------------------------------------------------------------- */ /** * Add another array entry by entry to this array in * place. Both arrays must have the same size and * nb_component. If the arrays have different shapes, code * compiled in debug mode will throw an expeption and * optimised code will behave in an unpredicted manner * @param other array to add to this * @return reference to modified this */ template Array & Array::operator+=(const Array & vect) { AKANTU_DEBUG_ASSERT((this->size_ == vect.size()) && (this->nb_component == vect.nb_component), "The too array don't have the same sizes"); T * a = this->values; - T * b = vect.data(); + const T * b = vect.data(); for (Idx i = 0; i < this->size_ * this->nb_component; ++i) { *a++ += *b++; } return *this; } /* -------------------------------------------------------------------------- */ /** * Multiply all entries of this array by a scalar in place * @param alpha scalar multiplicant * @return reference to modified this */ template auto Array::operator*=(const T & alpha) -> Array & { T * a = this->values; for (Idx i = 0; i < this->size_ * this->nb_component; ++i) { *a++ *= alpha; } return *this; } /* ------------------------------------------------------------------------- */ /** * Compare this array element by element to another. * @param other array to compare to * @return true it all element are equal and arrays have * the same shape, else false */ template bool Array::operator==(const Array & other) const { bool equal = this->nb_component == other.nb_component && this->size_ == other.size_ && this->id == other.id; if (not equal) { return false; } if (this->values == other.data()) { return true; } return std::equal(this->values, this->values + this->size_ * this->nb_component, other.data()); } /* ------------------------------------------------------------------------ */ template bool Array::operator!=(const Array & other) const { return !operator==(other); } /* ------------------------------------------------------------------------ */ /** * set all tuples of the array to a given vector or matrix * @param vm Matrix or Vector to fill the array with */ template template ::value> *> inline void Array::set(const C & elem) { AKANTU_DEBUG_ASSERT( this->nb_component == elem.array().size(), "The size of the object does not match the number of components"); for (auto && v : make_view(*this, this->nb_component)) { std::copy_n(elem.data(), this->nb_component, v.data()); } } /* ------------------------------------------------------------------------ */ template void Array::append(const Array & other) { AKANTU_DEBUG_ASSERT( this->nb_component == other.nb_component, "Cannot append an array with a different number of component"); Idx old_size = this->size_; this->resize(this->size_ + other.size()); T * tmp = this->values + this->nb_component * old_size; std::copy_n(other.data(), other.size() * this->nb_component, tmp); } /* ------------------------------------------------------------------------ */ /* Functions Array */ /* ------------------------------------------------------------------------ */ template Array::Array(Int size, Int nb_component, const ID & id) : parent(size, nb_component, id) {} template <> inline Array::Array(Int size, Int nb_component, const ID & id) : parent(size, nb_component, "", id) {} /* ------------------------------------------------------------------------ */ template Array::Array(Int size, Int nb_component, const_reference value, const ID & id) : parent(size, nb_component, value, id) {} /* ------------------------------------------------------------------------ */ template Array::Array(const Array & vect, const ID & id) : parent(vect, id) {} /* ------------------------------------------------------------------------ */ template auto Array::operator=(const Array & other) -> Array & { AKANTU_DEBUG_WARNING("You are copying the array " << this->id << " are you sure it is on purpose"); if (&other == this) { return *this; } parent::operator=(other); return *this; } /* ------------------------------------------------------------------------ */ template Array::Array(const std::vector & vect) : parent(vect) {} /* ------------------------------------------------------------------------ */ template Array::~Array() = default; /* ------------------------------------------------------------------------ */ /** * search elem in the array, return the position of the * first occurrence or -1 if not found * @param elem the element to look for * @return index of the first occurrence of elem or -1 if * elem is not present */ template Idx Array::find(const_reference elem) const { - AKANTU_DEBUG_IN(); - auto begin = this->begin(); auto end = this->end(); auto it = std::find(begin, end, elem); - AKANTU_DEBUG_OUT(); return (it != end) ? it - begin : Idx(-1); } /* ------------------------------------------------------------------------ */ template template ::value> *> inline Idx Array::find(const V & elem) { AKANTU_DEBUG_ASSERT(elem.size() == this->nb_component, "Cannot find an element with a wrong size (" << elem.size() << ") != " << this->nb_component); auto && view = make_view(*this, elem.size()); auto begin = view.begin(); auto end = view.end(); auto it = std::find(begin, end, elem); return (it != end) ? it - begin : Idx(-1); } /* ------------------------------------------------------------------------ */ /** * copy the content of another array. This overwrites the * current content. * @param other Array to copy into this array. It has to * have the same nb_component as this. If compiled in * debug mode, an incorrect other will result in an * exception being thrown. Optimised code may result in * unpredicted behaviour. * @param no_sanity_check turns off all checkes */ template void Array::copy(const Array & other, bool no_sanity_check) { - AKANTU_DEBUG_IN(); - if (not no_sanity_check and (other.nb_component != this->nb_component)) { AKANTU_ERROR("The two arrays do not have the same " "number of components"); } this->resize((other.size_ * other.nb_component) / this->nb_component); std::copy_n(other.data(), this->size_ * this->nb_component, this->values); - - AKANTU_DEBUG_OUT(); } /* ------------------------------------------------------------------------ */ template class ArrayPrintHelper { public: template static void print_content(const Array & vect, std::ostream & stream, int indent) { std::string space(indent, AKANTU_INDENT); stream << space << " + values : {"; for (Idx i = 0; i < vect.size(); ++i) { stream << "{"; for (Idx j = 0; j < vect.getNbComponent(); ++j) { stream << vect(i, j); if (j != vect.getNbComponent() - 1) { stream << ", "; } } stream << "}"; if (i != vect.size() - 1) { stream << ", "; } } stream << "}" << std::endl; } }; template <> class ArrayPrintHelper { public: template static void print_content(__attribute__((unused)) const Array & vect, __attribute__((unused)) std::ostream & stream, __attribute__((unused)) int indent) {} }; /* ------------------------------------------------------------------------ */ template void Array::printself(std::ostream & stream, int indent) const { std::string space(indent, AKANTU_INDENT); std::streamsize prec = stream.precision(); std::ios_base::fmtflags ff = stream.flags(); stream.setf(std::ios_base::showbase); stream.precision(2); stream << space << "Array<" << debug::demangle(typeid(T).name()) << "> [" << std::endl; stream << space << " + id : " << this->id << std::endl; stream << space << " + size : " << this->size_ << std::endl; stream << space << " + nb_component : " << this->nb_component << std::endl; stream << space << " + allocated size : " << this->getAllocatedSize() << std::endl; stream << space << " + memory size : " << printMemorySize(this->getMemorySize()) << std::endl; if (not AKANTU_DEBUG_LEVEL_IS_TEST()) { stream << space << " + address : " << std::hex << this->values << std::dec << std::endl; } stream.precision(prec); stream.flags(ff); if (AKANTU_DEBUG_TEST(dblDump) || AKANTU_DEBUG_LEVEL_IS_TEST()) { ArrayPrintHelper::value>::print_content( *this, stream, indent); } stream << space << "]" << std::endl; } /* -------------------------------------------------------------------------- */ template template ::value> *> bool Array::isFinite() const noexcept { return std::all_of(this->values, this->values + this->size_ * this->nb_component, [](auto && a) { return std::isfinite(a); }); } /* ------------------------------------------------------------------------ */ /* ArrayFilter */ /* ------------------------------------------------------------------------ */ template class ArrayFilter { public: /// const iterator template class const_iterator { public: Idx getCurrentIndex() { return (*this->filter_it * this->nb_item_per_elem + this->sub_element_counter); } inline const_iterator() = default; inline const_iterator(original_iterator origin_it, filter_iterator filter_it, Int nb_item_per_elem) : origin_it(std::move(origin_it)), filter_it(std::move(filter_it)), nb_item_per_elem(nb_item_per_elem), sub_element_counter(0){}; inline bool operator!=(const_iterator & other) const { return !((*this) == other); } inline bool operator==(const_iterator & other) const { return (this->origin_it == other.origin_it && this->filter_it == other.filter_it && this->sub_element_counter == other.sub_element_counter); } inline bool operator!=(const const_iterator & other) const { return !((*this) == other); } inline bool operator==(const const_iterator & other) const { return (this->origin_it == other.origin_it && this->filter_it == other.filter_it && this->sub_element_counter == other.sub_element_counter); } inline const_iterator & operator++() { ++sub_element_counter; if (sub_element_counter == nb_item_per_elem) { sub_element_counter = 0; ++filter_it; } return *this; }; inline decltype(auto) operator*() { return origin_it[nb_item_per_elem * (*filter_it) + sub_element_counter]; }; private: original_iterator origin_it; filter_iterator filter_it; /// the number of item per element Int nb_item_per_elem; /// counter for every sub element group Int sub_element_counter; }; using vector_iterator = void; // iterator>; using array_type = Array; using const_vector_iterator = const_iterator::const_scalar_iterator>; using value_type = typename array_type::value_type; private: /* ---------------------------------------------------------------------- */ /* Constructors/Destructors */ /* ---------------------------------------------------------------------- */ public: ArrayFilter(const Array & array, const Array & filter, Int nb_item_per_elem) : array(array), filter(filter), nb_item_per_elem(nb_item_per_elem){}; decltype(auto) begin_reinterpret(Int n, Int new_size) const { Int new_nb_item_per_elem = this->nb_item_per_elem; if (new_size != 0 && n != 0) new_nb_item_per_elem = this->array.getNbComponent() * this->filter.size() * this->nb_item_per_elem / (n * new_size); return const_vector_iterator(make_view(this->array, n).begin(), this->filter.begin(), new_nb_item_per_elem); }; decltype(auto) end_reinterpret(Int n, Int new_size) const { Int new_nb_item_per_elem = this->nb_item_per_elem; if (new_size != 0 && n != 0) new_nb_item_per_elem = this->array.getNbComponent() * this->filter.size() * this->nb_item_per_elem / (n * new_size); return const_vector_iterator(make_view(this->array, n).begin(), this->filter.end(), new_nb_item_per_elem); }; // vector_iterator begin_reinterpret(Int, Int) { throw; }; // vector_iterator end_reinterpret(Int, Int) { throw; }; /// return the size of the filtered array which is the filter size Int size() const { return this->filter.size() * this->nb_item_per_elem; }; /// the number of components of the filtered array Int getNbComponent() const { return this->array.getNbComponent(); }; /// tells if the container is empty [[nodiscard]] bool empty() const { return (size() == 0); } /* ---------------------------------------------------------------------- */ /* Class Members */ /* ---------------------------------------------------------------------- */ private: /// reference to array of data const Array & array; /// reference to the filter used to select elements const Array & filter; /// the number of item per element Int nb_item_per_elem; }; /* ------------------------------------------------------------------------ */ /* Begin/End functions implementation */ /* ------------------------------------------------------------------------ */ namespace detail { + template struct GetNbComponent { + static auto getNbComponent(const C & /*cont*/) { return 1; } + }; + + template struct GetNbComponent> { + static auto getNbComponent(const Array & cont) { + return cont.getNbComponent(); + } + }; + template constexpr auto take_front_impl(Tuple && t, std::index_sequence /*idxs*/) { return std::make_tuple(std::get(std::forward(t))...); } template constexpr auto take_front(Tuple && t) { return take_front_impl(std::forward(t), std::make_index_sequence{}); } template std::string to_string_all(T &&... t) { if (sizeof...(T) == 0) { return ""; } std::stringstream ss; bool noComma = true; ss << "("; (void)std::initializer_list{ (ss << (noComma ? "" : ", ") << t, noComma = false)...}; ss << ")"; return ss.str(); } template struct InstantiationHelper { template static auto instantiate(T && data, Ns... ns) { return std::make_unique(data, ns...); } }; template <> struct InstantiationHelper<0> { template static auto instantiate(T && data) { return data; } }; template decltype(auto) __attribute__((visibility("hidden"))) get_iterator(Arr && array, T * data, Ns &&... ns) { const bool is_const_arr = std::is_const>::value; using type = ViewIteratorHelper_t; using iterator = std::conditional_t, view_iterator>; static_assert(sizeof...(Ns), "You should provide a least one size"); - if (array.getNbComponent() * array.size() != - Int(product_all(std::forward(ns)...))) { + auto product = Idx(product_all(std::forward(ns)...)); + auto nb_component = array.getNbComponent(); + // detail::GetNbComponent>::getNbComponent( + // std::forward(array)); + if (nb_component * array.size() != product) { 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...)); + "The iterator on " << debug::demangle(typeid(Arr).name()) + << to_string_all(array.size(), nb_component) + << "is not compatible with the type " + << debug::demangle(typeid(type).name()) << " " + << to_string_all(ns...)); } return std::apply([&](auto... n) { return iterator(data, n...); }, take_front(std::make_tuple(ns...))); } } // namespace detail /* ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ */ template template inline auto Array::begin(Ns &&... ns) { - return detail::get_iterator(*this, this->values, std::forward(ns)..., + return detail::get_iterator(*this, this->data(), std::forward(ns)..., this->size_); } template template inline auto Array::end(Ns &&... ns) { return detail::get_iterator(*this, - this->values + this->nb_component * this->size_, + this->data() + this->nb_component * this->size_, std::forward(ns)..., this->size_); } template template inline auto Array::begin(Ns &&... ns) const { - return detail::get_iterator(*this, this->values, std::forward(ns)..., + return detail::get_iterator(*this, this->data(), std::forward(ns)..., this->size_); } template template inline auto Array::end(Ns &&... ns) const { return detail::get_iterator(*this, - this->values + this->nb_component * this->size_, + this->data() + this->nb_component * this->size_, std::forward(ns)..., this->size_); } template template inline auto Array::cbegin(Ns &&... ns) const { - return detail::get_iterator(*this, this->values, std::forward(ns)..., + return detail::get_iterator(*this, this->data(), std::forward(ns)..., this->size_); } template template inline auto Array::cend(Ns &&... ns) const { return detail::get_iterator(*this, - this->values + this->nb_component * this->size_, + this->data() + this->nb_component * this->size_, std::forward(ns)..., this->size_); } template template inline auto Array::begin_reinterpret(Ns &&... ns) { - return detail::get_iterator(*this, this->values, std::forward(ns)...); + return detail::get_iterator(*this, this->data(), std::forward(ns)...); } template template inline auto Array::end_reinterpret(Ns &&... ns) { return detail::get_iterator( - *this, this->values + detail::product_all(std::forward(ns)...), + *this, this->data() + detail::product_all(std::forward(ns)...), std::forward(ns)...); } template template inline auto Array::begin_reinterpret(Ns &&... ns) const { - return detail::get_iterator(*this, this->values, std::forward(ns)...); + return detail::get_iterator(*this, this->data(), std::forward(ns)...); } template template inline auto Array::end_reinterpret(Ns &&... ns) const { return detail::get_iterator( - *this, this->values + detail::product_all(std::forward(ns)...), + *this, this->data() + detail::product_all(std::forward(ns)...), std::forward(ns)...); } /* ------------------------------------------------------------------------ */ /* Views */ /* ------------------------------------------------------------------------ */ namespace detail { template class ArrayView { using tuple = std::tuple; public: using size_type = Idx; + using pointer = decltype(std::declval().data()); ~ArrayView() = default; constexpr ArrayView(Array && array, Ns... ns) noexcept : array(array), sizes(std::move(ns)...) {} constexpr ArrayView(const ArrayView & array_view) = default; constexpr ArrayView(ArrayView && array_view) noexcept = default; constexpr ArrayView & operator=(const ArrayView & array_view) = default; constexpr ArrayView & operator=(ArrayView && array_view) noexcept = default; auto begin() { return std::apply( [&](auto &&... ns) { return detail::get_iterator(array.get(), array.get().data(), std::forward(ns)...); }, sizes); } auto begin() const { return std::apply( [&](auto &&... ns) { return detail::get_iterator(array.get(), array.get().data(), std::forward(ns)...); }, sizes); } auto end() { return std::apply( [&](auto &&... ns) { return detail::get_iterator( array.get(), array.get().data() + detail::product_all(std::forward(ns)...), std::forward(ns)...); }, sizes); } auto end() const { return std::apply( [&](auto &&... ns) { return detail::get_iterator( array.get(), array.get().data() + detail::product_all(std::forward(ns)...), std::forward(ns)...); }, sizes); } auto cbegin() const { return this->begin(); } auto cend() const { return this->end(); } constexpr auto size() const { return std::get::value - 1>(sizes); } constexpr auto dims() const { return std::tuple_size::value - 1; } private: std::reference_wrapper> array; tuple sizes; }; /* ---------------------------------------------------------------------- */ template class ArrayView &, Ns...> { using tuple = std::tuple; public: constexpr ArrayView(const ArrayFilter & array, Ns... ns) : array(array), sizes(std::move(ns)...) {} constexpr ArrayView(const ArrayView & array_view) = default; constexpr ArrayView(ArrayView && array_view) = default; constexpr ArrayView & operator=(const ArrayView & array_view) = default; constexpr ArrayView & operator=(ArrayView && array_view) = default; auto begin() const { return std::apply( [&](auto &&... ns) { return array.get().begin_reinterpret( std::forward(ns)...); }, sizes); } auto end() const { return std::apply( [&](auto &&... ns) { return array.get().end_reinterpret( std::forward(ns)...); }, sizes); } auto cbegin() const { return this->begin(); } auto cend() const { return this->end(); } constexpr auto size() const { return std::get::value - 1>(sizes); } constexpr auto dims() const { return std::tuple_size::value - 1; } private: std::reference_wrapper> array; tuple sizes; }; } // namespace detail /* ------------------------------------------------------------------------ */ template >...>::value> * = nullptr> decltype(auto) make_view(Array && array, const Ns... ns) { AKANTU_DEBUG_ASSERT((detail::product_all(ns...) != 0), "You must specify non zero dimensions"); - auto size = std::forward(array).size() * - std::forward(array).getNbComponent() / - detail::product_all(ns...); + // auto size = std::forward(array).size() * + // std::forward(array).getNbComponent() / + // detail::product_all(ns...); + auto array_size = std::forward(array).size(); + auto nb_component = std::forward(array).getNbComponent(); + // detail::GetNbComponent>::getNbComponent( + // std::forward(array)); + auto product_all = detail::product_all(ns...); + auto size = array_size * nb_component / product_all; return detail::ArrayView..., std::common_type_t>( std::forward(array), std::move(ns)..., size); } template decltype(auto) make_const_view(const Array & array, const Ns... ns) { return make_view(array, std::move(ns)...); } } // namespace akantu //#endif /* __AKANTU_AKA_ARRAY_TMPL_HH__ */ diff --git a/src/common/aka_types.hh b/src/common/aka_types.hh index cb1ba09ad..08a0c206d 100644 --- a/src/common/aka_types.hh +++ b/src/common/aka_types.hh @@ -1,538 +1,538 @@ /** * Copyright (©) 2011-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "aka_common.hh" #include "aka_compatibilty_with_cpp_standard.hh" #include "aka_error.hh" /* -------------------------------------------------------------------------- */ #include #include #include #include #include #ifndef AKANTU_AKA_TYPES_HH #define AKANTU_AKA_TYPES_HH /* -------------------------------------------------------------------------- */ #define EIGEN_DEFAULT_DENSE_INDEX_TYPE akantu::Idx #define EIGEN_DEFAULT_MATRIX_STORAGE_ORDER_OPTION Eigen::ColMajor #define EIGEN_DEFAULT_IO_FORMAT \ Eigen::IOFormat(Eigen::StreamPrecision, Eigen::DontAlignCols, ", ", ", ", \ "[", "]", "[", "]") /* -------------------------------------------------------------------------- */ #define EIGEN_MATRIXBASE_PLUGIN "aka_types_eigen_matrix_base_plugin.hh" #define EIGEN_MATRIX_PLUGIN "aka_types_eigen_matrix_plugin.hh" #define EIGEN_PLAINOBJECTBASE_PLUGIN \ "aka_types_eigen_plain_object_base_plugin.hh" #include #include /* -------------------------------------------------------------------------- */ namespace aka { template struct is_eigen_map : public std::false_type {}; template struct is_eigen_map> : public std::true_type {}; /* -------------------------------------------------------------------------- */ template struct is_eigen_matrix : public std::false_type {}; template struct is_eigen_matrix< Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols>> : public std::true_type {}; /* -------------------------------------------------------------------------- */ template struct is_eigen_matrix_base : public std::false_type {}; template struct is_eigen_matrix_base> : public std::true_type {}; } // namespace aka namespace akantu { using Eigen::Ref; template using Vector = Eigen::Matrix; template using Matrix = Eigen::Matrix; template using VectorProxy = Eigen::Map::value, const Vector, n>, Vector, n>>>; template using MatrixProxy = Eigen::Map::value, const Matrix, m, n>, Matrix, m, n>>>; using VectorXr = Vector; using MatrixXr = Matrix; enum NormType : int8_t { L_1 = 1, L_2 = 2, L_inf = -1 }; struct TensorTraitBase {}; template struct TensorTrait : public TensorTraitBase {}; } // namespace akantu namespace aka { template using is_vector = aka::bool_constant< std::remove_reference_t>::IsVectorAtCompileTime>; template inline constexpr bool is_vector_v = is_vector::value; template using is_matrix = aka::negation>; template inline constexpr bool is_matrix_v = is_matrix::value; template using are_vectors = aka::conjunction...>; template inline constexpr bool are_vectors_v = are_vectors::value; template using are_matrices = aka::conjunction...>; template inline constexpr bool are_matrices_v = are_matrices::value; template using enable_if_matrices_t = std::enable_if_t::value>; template using enable_if_vectors_t = std::enable_if_t::value>; /* -------------------------------------------------------------------------- */ template struct is_tensor : public std::is_base_of {}; template struct is_tensor> : public std::true_type {}; template struct is_tensor> : public std::true_type {}; template struct is_tensor> : public std::true_type {}; template using is_tensor_n = std::is_base_of, T>; template inline constexpr bool is_tensor_v = is_tensor::value; template inline constexpr bool is_tensor_n_v = is_tensor_n::value; template using enable_if_tensors_n = std::enable_if< aka::conjunction< is_tensor_n..., std::is_same< std::common_type_t...>, std::decay_t>...>::value, T>; template using enable_if_tensors_n_t = typename enable_if_tensors_n::type; } // namespace aka namespace akantu { // fwd declaration template class TensorBase; template class TensorProxy; template class Tensor; } // namespace akantu /* -------------------------------------------------------------------------- */ #include "aka_view_iterators.hh" /* -------------------------------------------------------------------------- */ #include "aka_tensor.hh" /* -------------------------------------------------------------------------- */ namespace akantu { class ArrayBase; /* -------------------------------------------------------------------------- */ namespace details { template struct MapPlainObjectType { using type = T; }; template struct MapPlainObjectType< Eigen::Map> { using type = PlainObjectType; }; template using MapPlainObjectType_t = typename MapPlainObjectType::type; template struct EigenMatrixViewHelper {}; template struct EigenMatrixViewHelper { using type = Eigen::Matrix; }; template struct EigenMatrixViewHelper { using type = Eigen::Matrix; }; template using EigenMatrixViewHelper_t = typename EigenMatrixViewHelper::type; template class EigenView { static_assert(sizeof...(sizes) == 1 or sizeof...(sizes) == 2, "Eigen only supports Vector and Matrices"); private: template < class A = Array, std::enable_if_t>::value> * = nullptr> auto array_size() const { return array.get().size() * array.get().getNbComponent(); } template < class A = Array, std::enable_if_t>::value> * = nullptr> auto array_size() const { return array.get().size(); } using ArrayRef_t = decltype(std::ref(std::declval())); public: using size_type = typename std::decay_t::size_type; using value_type = typename std::decay_t::value_type; EigenView(Array && array, decltype(sizes)... sizes_) : array(std::ref(array)), sizes_(sizes_...) {} EigenView(Array && array) : array(array), sizes_(sizes...) {} EigenView(const EigenView & other) = default; EigenView(EigenView && other) noexcept = default; auto operator=(const EigenView & other) -> EigenView & = default; auto operator=(EigenView && other) noexcept -> EigenView & = default; - template ::value> * = nullptr> + template >> * = nullptr> decltype(auto) begin() { return aka::make_from_tuple<::akantu::view_iterator< Eigen::Map>>>( std::tuple_cat(std::make_tuple(array.get().data()), sizes_)); } - template ::value> * = nullptr> + template >> * = nullptr> decltype(auto) end() { return aka::make_from_tuple<::akantu::view_iterator< Eigen::Map>>>( std::tuple_cat(std::make_tuple(array.get().data() + array_size()), sizes_)); } decltype(auto) begin() const { return aka::make_from_tuple<::akantu::view_iterator< Eigen::Map>>>( std::tuple_cat(std::make_tuple(array.get().data()), sizes_)); } decltype(auto) end() const { return aka::make_from_tuple<::akantu::view_iterator< Eigen::Map>>>( std::tuple_cat(std::make_tuple(array.get().data() + array_size()), sizes_)); } private: ArrayRef_t array; std::tuple sizes_; }; } // namespace details template decltype(auto) make_view(Array && array, Idx rows = RowsAtCompileTime) { return details::EigenView( std::forward(array), rows); } template decltype(auto) make_view(Array && array, Idx rows = RowsAtCompileTime, Idx cols = ColsAtCompileTime) { return details::EigenView( std::forward(array), rows, cols); } template decltype(auto) make_const_view(const Array & array, Idx rows = RowsAtCompileTime) { return make_view(array, rows); } template decltype(auto) make_const_view(const Array & array, Idx rows = RowsAtCompileTime, Idx cols = ColsAtCompileTime) { return make_view(array, rows, cols); } } // namespace akantu namespace Eigen { template EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void MatrixBase::zero() { return this->fill(0.); } /* -------------------------------------------------------------------------- */ /* Vector */ /* -------------------------------------------------------------------------- */ template template ::value and ED::IsVectorAtCompileTime> *> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE decltype(auto) MatrixBase::begin() { return ::akantu::view_iterator( this->derived().data()); } template template ::value and ED::IsVectorAtCompileTime> *> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE decltype(auto) MatrixBase::end() { return ::akantu::view_iterator( this->derived().data() + this->size()); } template template *> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE decltype(auto) MatrixBase::begin() const { using Scalar = typename Derived::Scalar; return ::akantu::const_view_iterator(this->derived().data()); } template template *> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE decltype(auto) MatrixBase::end() const { using Scalar = typename Derived::Scalar; return ::akantu::const_view_iterator(this->derived().data() + this->size()); } /* -------------------------------------------------------------------------- */ /* Matrix */ /* -------------------------------------------------------------------------- */ template template ::value and not ED::IsVectorAtCompileTime> *> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE decltype(auto) MatrixBase::begin() { return ::akantu::view_iterator< Map>>( this->derived().data(), this->rows()); } template template ::value and not ED::IsVectorAtCompileTime> *> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE decltype(auto) MatrixBase::end() { return ::akantu::view_iterator< Map>>( this->derived().data() + this->size(), this->rows()); } template template *> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE decltype(auto) MatrixBase::begin() const { using Scalar = typename Derived::Scalar; return ::akantu::const_view_iterator< Map>>( const_cast(this->derived().data()), this->rows()); } template template *> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE decltype(auto) MatrixBase::end() const { using Scalar = typename Derived::Scalar; return ::akantu::const_view_iterator< Map>>( const_cast(this->derived().data()) + this->size(), this->rows()); } template template EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void MatrixBase::eig(MatrixBase & values) const { EigenSolver>> solver(*this, false); using OtherScalar = typename OtherDerived::Scalar; // as advised by the Eigen developers even though this is a UB // auto & values = const_cast &>(values_); if constexpr (std::is_floating_point{}) { values = solver.eigenvalues().real(); } else { values = solver.eigenvalues(); } } template template EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void MatrixBase::eig(MatrixBase & values, MatrixBase & vectors, bool sort) const { EigenSolver>> solver(*this, true); // as advised by the Eigen developers even though this is a UB // auto & values = const_cast &>(values_); // auto & vectors = const_cast &>(vectors_); auto norm = this->norm(); using OtherScalar = typename D1::Scalar; if ((solver.eigenvectors().imag().template lpNorm() > 1e-15 * norm) and std::is_floating_point::value) { AKANTU_EXCEPTION("This matrix has complex eigenvectors()"); } if (not sort) { if constexpr (std::is_floating_point{}) { values = solver.eigenvalues().real(); vectors = solver.eigenvectors().real(); } else { values = solver.eigenvalues(); vectors = solver.eigenvectors(); } return; } if (not std::is_floating_point::value) { AKANTU_EXCEPTION("Cannot sort complex eigen values"); } values = solver.eigenvalues().real(); PermutationMatrix P(values.size()); P.setIdentity(); std::sort(P.indices().data(), P.indices().data() + P.indices().size(), [&values](const Index & a, const Index & b) { return (values(a) - values(b)) > 0; }); if constexpr (std::is_floating_point{}) { values = P.transpose() * values; vectors = solver.eigenvectors().real() * P; } return; } template template EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void MatrixBase::eigh(const MatrixBase & values_) const { SelfAdjointEigenSolver< akantu::details::MapPlainObjectType_t>> solver(*this, EigenvaluesOnly); // as advised by the Eigen developers even though this is a UB auto & values = const_cast &>(values_); values = solver.eigenvalues(); } template template EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void MatrixBase::eigh(const MatrixBase & values_, const MatrixBase & vectors_, bool sort) const { SelfAdjointEigenSolver< akantu::details::MapPlainObjectType_t>> solver(*this, ComputeEigenvectors); // as advised by the Eigen developers, even though this is a UB auto & values = const_cast &>(values_); auto & vectors = const_cast &>(vectors_); if (not sort) { values = solver.eigenvalues(); vectors = solver.eigenvectors(); return; } values = solver.eigenvalues(); PermutationMatrix P(values.size()); P.setIdentity(); std::sort(P.indices().data(), P.indices().data() + P.indices().size(), [&values](const Index & a, const Index & b) { return (values(a) - values(b)) > 0; }); values = P.transpose() * values; vectors = solver.eigenvectors() * P; // permutes the columns (eigen vectors) } } // namespace Eigen namespace std { template struct is_convertible, Eigen::Map> : aka::bool_constant::value> {}; } // namespace std #endif /* AKANTU_AKA_TYPES_HH */ diff --git a/src/common/aka_view_iterators.hh b/src/common/aka_view_iterators.hh index 6a6b9f1b3..627854c1e 100644 --- a/src/common/aka_view_iterators.hh +++ b/src/common/aka_view_iterators.hh @@ -1,607 +1,608 @@ /** * Copyright (©) 2018-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "aka_common.hh" //#include "aka_types.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_AKA_VIEW_ITERATORS_HH__ #define __AKANTU_AKA_VIEW_ITERATORS_HH__ namespace akantu { template class TensorBase; } namespace akantu { /* -------------------------------------------------------------------------- */ /* Iterators */ /* -------------------------------------------------------------------------- */ namespace detail { template constexpr auto product_all(V &&... v) { std::common_type_t result = 1; (void)std::initializer_list{(result *= v, 0)...}; return result; } template struct IteratorHelper { static constexpr Int dim = 0; }; template struct IteratorHelper> { private: using T = typename Derived::Scalar; static constexpr Int m = Derived::RowsAtCompileTime; static constexpr Int n = Derived::ColsAtCompileTime; public: static constexpr Int dim = Eigen::MatrixBase::IsVectorAtCompileTime ? 1 : 2; using pointer = T *; using proxy = Eigen::Map>; using const_proxy = Eigen::Map>; }; template struct IteratorHelper> { private: using T = typename Derived::Scalar; static constexpr Int m = Derived::RowsAtCompileTime; static constexpr Int n = Derived::ColsAtCompileTime; public: static constexpr Int dim = Derived::IsVectorAtCompileTime and m != 1 ? 1 : 2; - using pointer = T *; + using pointer = + std::conditional_t, const T *, T *>; using proxy = Eigen::Map; using const_proxy = Eigen::Map; }; template struct IteratorHelper> { static constexpr Int dim = _dim; using pointer = T *; using proxy = TensorProxy; using const_proxy = TensorProxy; }; template struct IteratorHelper> { static constexpr Int dim = _dim; using pointer = T *; using proxy = TensorProxy; using const_proxy = TensorProxy; }; /* ------------------------------------------------------------------------ */ template >::dim> class internal_view_iterator { protected: using helper = IteratorHelper>; using internal_value_type = IR; using internal_pointer = IR *; using scalar_pointer = typename helper::pointer; using proxy_t = typename helper::proxy; static constexpr int dim_ = dim; public: using pointer = proxy_t *; using value_type = proxy_t; using reference = proxy_t &; using difference_type = Int; using iterator_category = std::random_access_iterator_tag; private: template constexpr auto get_new_proxy(scalar_pointer data, std::index_sequence) const { return ProxyType(data, dims[I]...); } constexpr auto get_new_proxy(scalar_pointer data) { return this->template get_new_proxy( data, std::make_index_sequence()); } constexpr auto get_new_proxy(scalar_pointer data) const { return this->template get_new_proxy( data, std::make_index_sequence()); } template constexpr void reset_proxy(std::index_sequence) { new (&proxy) proxy_t(ret_ptr, dims[I]...); } constexpr auto reset_proxy() { return reset_proxy(std::make_index_sequence()); } protected: template ().data()), decltype(std::declval().data())>::value> * = nullptr> explicit internal_view_iterator( internal_view_iterator & it) : dims(it.dims), _offset(it._offset), initial(it.initial), ret_ptr(it.ret_ptr), proxy(get_new_proxy(it.ret_ptr)) {} template friend class internal_view_iterator; template using valid_args_t = std::enable_if_t< aka::conjunction, std::is_enum>...>::value and dim == sizeof...(Args), int>; public: /// Generic constructor dor any tensor dimension template = 0> internal_view_iterator(scalar_pointer data, Ns... ns) : dims({Int(ns)...}), _offset(detail::product_all(std::forward(ns)...)), initial(data), ret_ptr(data), proxy(data, ns...) {} // Specific constructor for Vector of static size 1 template , std::enable_if_t::value and RD::RowsAtCompileTime == 1 and RD::ColsAtCompileTime == 1> * = nullptr> constexpr internal_view_iterator(scalar_pointer data, Idx rows) : dims({rows, 1}), _offset(rows), initial(data), ret_ptr(data), proxy(data, rows, 1) { assert(rows == 1 && "1x1 Matrix"); } /// Specific constructor for Eigen::Map that look like /// Eigen::Map template , std::enable_if_t<(RD::RowsAtCompileTime != 1) and RD::ColsAtCompileTime == 1> * = nullptr> constexpr internal_view_iterator(scalar_pointer data, Idx rows, [[gnu::unused]] Idx cols) : dims({rows}), _offset(rows), initial(data), ret_ptr(data), proxy(data, rows, 1) { assert(cols == 1 && "nx1 Matrix"); } /// Default constructor for Eigen::Map template , std::enable_if_t<_dim == 1> * = nullptr> internal_view_iterator() : proxy(reinterpret_cast(0xdeadbeaf), RD::RowsAtCompileTime == Eigen::Dynamic ? 1 : RD::RowsAtCompileTime) { // initialized to a fake pointer to pass the static_assert in Eigen // this proxy should not be returned } /// Default constructor for Eigen::Map template , std::enable_if_t<_dim == 2> * = nullptr> internal_view_iterator() : proxy(reinterpret_cast(0xdeadbeaf), RD::RowsAtCompileTime == Eigen::Dynamic ? 1 : RD::RowsAtCompileTime, RD::ColsAtCompileTime == Eigen::Dynamic ? 1 : RD::ColsAtCompileTime) { // initialized to a fake pointer to pass the `static_assert` in `Eigen // this proxy should not be returned } template , std::enable_if_t<(_dim > 2)> * = nullptr> internal_view_iterator() {} internal_view_iterator(const internal_view_iterator & it) : dims(it.dims), _offset(it._offset), initial(it.initial), ret_ptr(it.ret_ptr), proxy(get_new_proxy(it.ret_ptr)) {} internal_view_iterator & operator=(internal_view_iterator && it) noexcept = default; internal_view_iterator(internal_view_iterator && it) noexcept = default; virtual ~internal_view_iterator() = default; template ().data()), decltype(std::declval().data())>::value> * = nullptr> inline internal_view_iterator & operator=(const internal_view_iterator & it) { this->dims = it.dims; this->_offset = it._offset; this->initial = it.initial; this->ret_ptr = it.ret_ptr; reset_proxy(); return *this; } inline internal_view_iterator & operator=(const internal_view_iterator & it) { if (this != &it) { this->dims = it.dims; this->_offset = it._offset; this->initial = it.initial; this->ret_ptr = it.ret_ptr; reset_proxy(); } return *this; } public: Idx getCurrentIndex() { return (this->ret_ptr - this->initial) / this->_offset; } inline reference operator*() { this->reset_proxy(); return proxy; } inline pointer operator->() { reset_proxy(); return &proxy; } inline daughter & operator++() { ret_ptr += _offset; return static_cast(*this); } inline daughter & operator--() { ret_ptr -= _offset; return static_cast(*this); } inline daughter & operator+=(Idx n) { ret_ptr += _offset * n; return static_cast(*this); } inline daughter & operator-=(Idx n) { ret_ptr -= _offset * n; return static_cast(*this); } inline auto operator[](Idx n) { return get_new_proxy(ret_ptr + n * _offset); } inline auto operator[](Idx n) const { return get_new_proxy(ret_ptr + n * _offset); } inline bool operator==(const internal_view_iterator & other) const { return this->ret_ptr == other.ret_ptr; } inline bool operator!=(const internal_view_iterator & other) const { return this->ret_ptr != other.ret_ptr; } inline bool operator<(const internal_view_iterator & other) const { return this->ret_ptr < other.ret_ptr; } inline bool operator<=(const internal_view_iterator & other) const { return this->ret_ptr <= other.ret_ptr; } inline bool operator>(const internal_view_iterator & other) const { return this->ret_ptr > other.ret_ptr; } inline bool operator>=(const internal_view_iterator & other) const { return this->ret_ptr >= other.ret_ptr; } inline auto operator+(difference_type n) const { daughter tmp(static_cast(*this)); tmp += n; return tmp; } inline auto operator-(difference_type n) const { daughter tmp(static_cast(*this)); tmp -= n; return tmp; } inline difference_type operator-(const internal_view_iterator & b) const { return (this->ret_ptr - b.ret_ptr) / _offset; } inline scalar_pointer data() const { return ret_ptr; } inline difference_type offset() const { return _offset; } inline decltype(auto) getDims() const { return dims; } protected: std::array dims; difference_type _offset{0}; scalar_pointer initial{nullptr}; scalar_pointer ret_ptr{nullptr}; proxy_t proxy; }; /* ------------------------------------------------------------------------ */ /** * Specialization for scalar types */ template class internal_view_iterator { public: using value_type = R; using pointer = R *; using reference = R &; using const_reference = const R &; using difference_type = Idx; // std::ptrdiff_t; using iterator_category = std::random_access_iterator_tag; static constexpr int dim_ = 0; protected: using internal_value_type = IR; using internal_pointer = IR *; public: internal_view_iterator(pointer data = nullptr) : ret(data), initial(data){}; internal_view_iterator(const internal_view_iterator & it) = default; internal_view_iterator(internal_view_iterator && it) = default; virtual ~internal_view_iterator() = default; inline internal_view_iterator & operator=(const internal_view_iterator & it) = default; Idx getCurrentIndex() { return (this->ret - this->initial); }; inline reference operator*() { return *ret; } inline pointer operator->() { return ret; }; inline daughter & operator++() { ++ret; return static_cast(*this); } inline daughter & operator--() { --ret; return static_cast(*this); } inline daughter & operator+=(const Idx n) { ret += n; return static_cast(*this); } inline daughter & operator-=(const Idx n) { ret -= n; return static_cast(*this); } inline reference operator[](const Idx n) { return ret[n]; } inline bool operator==(const internal_view_iterator & other) const { return ret == other.ret; } inline bool operator!=(const internal_view_iterator & other) const { return ret != other.ret; } inline bool operator<(const internal_view_iterator & other) const { return ret < other.ret; } inline bool operator<=(const internal_view_iterator & other) const { return ret <= other.ret; } inline bool operator>(const internal_view_iterator & other) const { return ret > other.ret; } inline bool operator>=(const internal_view_iterator & other) const { return ret >= other.ret; } inline daughter operator-(difference_type n) const { return daughter(ret - n); } inline daughter operator+(difference_type n) const { return daughter(ret + n); } inline difference_type operator-(const internal_view_iterator & b) const { return ret - b.ret; } inline pointer data() const { return ret; } inline decltype(auto) getDims() const { return dims; } protected: std::array dims; pointer ret{nullptr}; pointer initial{nullptr}; }; } // namespace detail /* -------------------------------------------------------------------------- */ template class view_iterator; template class const_view_iterator : public detail::internal_view_iterator, R> { public: using parent = detail::internal_view_iterator; using value_type = typename parent::value_type; using pointer = typename parent::pointer; using reference = typename parent::reference; using difference_type = typename parent::difference_type; using iterator_category = typename parent::iterator_category; protected: template static inline auto convert_helper(const Iterator & it, std::index_sequence) { return const_view_iterator(it.data(), it.getDims()[I]...); } template static inline auto convert(const Iterator & it) { return convert_helper(it, std::make_index_sequence()); } public: const_view_iterator() : parent(){}; const_view_iterator(const const_view_iterator & it) = default; const_view_iterator(const_view_iterator && it) noexcept = default; template const_view_iterator(P * data, Ns... ns) : parent(data, ns...) {} const_view_iterator & operator=(const const_view_iterator & it) = default; template ::value> * = nullptr> const_view_iterator(const const_view_iterator & it) : parent(convert(it)) {} template ::value> * = nullptr> const_view_iterator(const view_iterator & it) : parent(convert(it)) {} template ::value and std::is_convertible::value> * = nullptr> const_view_iterator & operator=(const const_view_iterator & it) { return dynamic_cast(parent::operator=(it)); } template ::value> * = nullptr> const_view_iterator operator=(const view_iterator & it) { return convert(it); } }; template ::value> struct ConstConverterIteratorHelper { protected: template static inline auto convert_helper(const view_iterator & it, std::index_sequence) { return const_view_iterator(it.data(), it.getDims()[I]...); } public: static inline auto convert(const view_iterator & it) { return convert_helper( it, std::make_index_sequence< std::tuple_size::value>()); } }; template struct ConstConverterIteratorHelper { static inline auto convert(const view_iterator & it) { return const_view_iterator(it.data()); } }; template class view_iterator : public detail::internal_view_iterator> { public: using parent = detail::internal_view_iterator; using value_type = typename parent::value_type; using pointer = typename parent::pointer; using reference = typename parent::reference; using difference_type = typename parent::difference_type; using iterator_category = typename parent::iterator_category; public: view_iterator() : parent(){}; view_iterator(const view_iterator & it) = default; view_iterator(view_iterator && it) = default; template view_iterator(P * data, Ns... ns) : parent(data, ns...) {} view_iterator & operator=(const view_iterator & it) = default; operator const_view_iterator() { return ConstConverterIteratorHelper::convert(*this); } }; namespace { template struct ViewIteratorHelper { using type = TensorProxy; }; template struct ViewIteratorHelper<0, T> { using type = T; }; template struct ViewIteratorHelper<1, T> { using type = Eigen::Map>; }; template struct ViewIteratorHelper<1, const T> { using type = Eigen::Map>; }; template struct ViewIteratorHelper<2, T> { using type = Eigen::Map>; }; template struct ViewIteratorHelper<2, const T> { using type = Eigen::Map>; }; template using ViewIteratorHelper_t = typename ViewIteratorHelper::type; } // namespace } // namespace akantu #include namespace std { template struct iterator_traits<::akantu::const_view_iterator> { protected: using iterator = ::akantu::const_view_iterator; public: using iterator_category = typename iterator::iterator_category; using value_type = typename iterator::value_type; using difference_type = typename iterator::difference_type; using pointer = typename iterator::pointer; using reference = typename iterator::reference; }; template struct iterator_traits<::akantu::view_iterator> { protected: using iterator = ::akantu::view_iterator; public: using iterator_category = typename iterator::iterator_category; using value_type = typename iterator::value_type; using difference_type = typename iterator::difference_type; using pointer = typename iterator::pointer; using reference = typename iterator::reference; }; } // namespace std #endif /* !__AKANTU_AKA_VIEW_ITERATORS_HH__ */ diff --git a/src/fe_engine/fe_engine_inline_impl.hh b/src/fe_engine/fe_engine_inline_impl.hh index 8c1756349..b58520732 100644 --- a/src/fe_engine/fe_engine_inline_impl.hh +++ b/src/fe_engine/fe_engine_inline_impl.hh @@ -1,188 +1,188 @@ /** * Copyright (©) 2010-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "element_class.hh" //#include "fe_engine.hh" #include "mesh.hh" /* -------------------------------------------------------------------------- */ #include "element_type_conversion.hh" /* -------------------------------------------------------------------------- */ // #ifndef __AKANTU_FE_ENGINE_INLINE_IMPL_CC__ // #define __AKANTU_FE_ENGINE_INLINE_IMPL_CC__ namespace akantu { /* -------------------------------------------------------------------------- */ template inline Real FEEngine::getElementInradius(const Eigen::MatrixBase & coord, ElementType type) { return tuple_dispatch( [&coord](auto && enum_type) -> Real { constexpr ElementType type = aka::decay_v; return ElementClass::getInradius(coord); }, type); } /* -------------------------------------------------------------------------- */ inline Real FEEngine::getElementInradius(const Element & element) const { auto spatial_dimension = mesh.getSpatialDimension(); auto positions = make_view(mesh.getNodes(), spatial_dimension).begin(); auto connectivity = mesh.getConnectivities().get(element); Matrix coords(spatial_dimension, connectivity.size()); for (auto && data : zip(connectivity, coords)) { std::get<1>(data) = positions[std::get<0>(data)]; } return getElementInradius(coords, element.type); } /* -------------------------------------------------------------------------- */ inline constexpr auto FEEngine::getInterpolationType(ElementType type) { return convertType(type); } /* -------------------------------------------------------------------------- */ /// @todo rewrite this function in order to get the cohesive element /// type directly from the facet #if defined(AKANTU_COHESIVE_ELEMENT) inline constexpr ElementType FEEngine::getCohesiveElementType(ElementType type) { return tuple_dispatch_with_default>( [&](auto && enum_type) -> ElementType { constexpr ElementType type = aka::decay_v; return CohesiveFacetProperty::cohesive_type; }, type, [](auto && /*enum_type*/) { return _not_defined; }); } #endif /* -------------------------------------------------------------------------- */ #if defined(AKANTU_IGFEM) } // akantu #include "igfem_helper.hh" namespace akantu { inline Vector FEEngine::getIGFEMElementTypes(ElementType type) { tuple_dispatch>( [&](auto && enum_type) { constexpr ElementType type = aka::decay_v; return IGFEMHelper::getIGFEMElementTypes(); }, type); } #endif /* -------------------------------------------------------------------------- */ template void FEEngine::extractNodalToElementField(const Mesh & mesh, const Array & nodal_f, Array & elemental_f, ElementType type, GhostType ghost_type, const Array & filter_elements) { AKANTU_DEBUG_IN(); auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type); auto nb_degree_of_freedom = nodal_f.getNbComponent(); auto nb_element = mesh.getNbElement(type, ghost_type); auto * conn_val = mesh.getConnectivity(type, ghost_type).data(); if (filter_elements != empty_filter) { nb_element = filter_elements.size(); } elemental_f.resize(nb_element); - T * nodal_f_val = nodal_f.data(); + const T * nodal_f_val = nodal_f.data(); T * f_val = elemental_f.data(); - Idx * el_conn; + const Idx * el_conn; for (Int el = 0; el < nb_element; ++el) { if (filter_elements != empty_filter) { el_conn = conn_val + filter_elements(el) * nb_nodes_per_element; } else { el_conn = conn_val + el * nb_nodes_per_element; } for (Int n = 0; n < nb_nodes_per_element; ++n) { auto node = *(el_conn + n); std::copy(nodal_f_val + node * nb_degree_of_freedom, nodal_f_val + (node + 1) * nb_degree_of_freedom, f_val); f_val += nb_degree_of_freedom; } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void FEEngine::filterElementalData(const Mesh & mesh, const Array & elem_f, Array & filtered_f, ElementType type, GhostType ghost_type, const Array & filter_elements) { AKANTU_DEBUG_IN(); auto nb_element = mesh.getNbElement(type, ghost_type); if (nb_element == 0) { filtered_f.resize(0); return; } auto nb_degree_of_freedom = elem_f.getNbComponent(); auto nb_data_per_element = elem_f.size() / nb_element; if (filter_elements != empty_filter) { nb_element = filter_elements.size(); } filtered_f.resize(nb_element * nb_data_per_element); - T * elem_f_val = elem_f.data(); + const T * elem_f_val = elem_f.data(); T * f_val = filtered_f.data(); UInt el_offset; for (Idx el = 0; el < nb_element; ++el) { if (filter_elements != empty_filter) { el_offset = filter_elements(el); } else { el_offset = el; } std::copy(elem_f_val + el_offset * nb_data_per_element * nb_degree_of_freedom, elem_f_val + (el_offset + 1) * nb_data_per_element * nb_degree_of_freedom, f_val); f_val += nb_degree_of_freedom * nb_data_per_element; } AKANTU_DEBUG_OUT(); } } // namespace akantu //#endif /* __AKANTU_FE_ENGINE_INLINE_IMPL_CC__ */ diff --git a/src/fe_engine/shape_functions_inline_impl.hh b/src/fe_engine/shape_functions_inline_impl.hh index 8ac1a43c5..705a5086c 100644 --- a/src/fe_engine/shape_functions_inline_impl.hh +++ b/src/fe_engine/shape_functions_inline_impl.hh @@ -1,336 +1,336 @@ /** * Copyright (©) 2010-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "fe_engine.hh" #include "shape_functions.hh" /* -------------------------------------------------------------------------- */ //#ifndef AKANTU_SHAPE_FUNCTIONS_INLINE_IMPL_HH_ //#define AKANTU_SHAPE_FUNCTIONS_INLINE_IMPL_HH_ namespace akantu { /* -------------------------------------------------------------------------- */ inline const Array & ShapeFunctions::getShapes(ElementType el_type, GhostType ghost_type) const { return shapes(FEEngine::getInterpolationType(el_type), ghost_type); } /* -------------------------------------------------------------------------- */ inline const Array & ShapeFunctions::getShapesDerivatives(ElementType el_type, GhostType ghost_type) const { return shapes_derivatives(FEEngine::getInterpolationType(el_type), ghost_type); } /* -------------------------------------------------------------------------- */ inline Int ShapeFunctions::getShapeSize(ElementType type) { return tuple_dispatch( [&](auto && enum_type) { constexpr ElementType type = aka::decay_v; return ElementClass::getShapeSize(); }, type); } /* -------------------------------------------------------------------------- */ inline Int ShapeFunctions::getShapeDerivativesSize(ElementType type) { return tuple_dispatch( [&](auto && enum_type) { constexpr ElementType type = aka::decay_v; return ElementClass::getShapeDerivativesSize(); }, type); } /* -------------------------------------------------------------------------- */ template void ShapeFunctions::setIntegrationPointsByType( const Eigen::MatrixBase & points, GhostType ghost_type) { this->integration_points(type, ghost_type) = points; } /* -------------------------------------------------------------------------- */ template inline void ShapeFunctions::buildInterpolationMatrix( const Eigen::MatrixBase & coordinates, Eigen::MatrixBase & coordMatrix, Int integration_order) const { switch (integration_order) { case 1: { for (Int i = 0; i < coordinates.cols(); ++i) { coordMatrix(i, 0) = 1; } break; } case 2: { auto nb_quadrature_points = coordMatrix.cols(); for (Int i = 0; i < coordinates.cols(); ++i) { coordMatrix(i, 0) = 1; for (Int j = 1; j < nb_quadrature_points; ++j) { coordMatrix(i, j) = coordinates(j - 1, i); } } break; } default: { AKANTU_TO_IMPLEMENT(); break; } } } /* -------------------------------------------------------------------------- */ template struct BuildElementalFieldInterpolationMatrix { template static inline void call(ShapeFunction && func, const Eigen::MatrixBase & coordinates, Eigen::MatrixBase & coord_matrix, Int integration_order) { func.buildInterpolationMatrix(coordinates, coord_matrix, integration_order); } }; /** * @todo Write a more efficient interpolation for quadrangles by * dropping unnecessary quadrature points * */ /* -------------------------------------------------------------------------- */ template <> struct BuildElementalFieldInterpolationMatrix<_quadrangle_4> { template static inline void call(ShapeFunction && /*func*/, const Eigen::MatrixBase & coordinates, Eigen::MatrixBase & coord_matrix, Int integration_order) { if (integration_order != ElementClassProperty<_quadrangle_4>::polynomial_degree) { AKANTU_TO_IMPLEMENT(); } else { for (Int i = 0; i < coordinates.cols(); ++i) { auto x = coordinates(0, i); auto y = coordinates(1, i); coord_matrix(i, 0) = 1; coord_matrix(i, 1) = x; coord_matrix(i, 2) = y; coord_matrix(i, 3) = x * y; } } } }; /* -------------------------------------------------------------------------- */ template <> struct BuildElementalFieldInterpolationMatrix<_quadrangle_8> { template static inline void call(ShapeFunction && /*func*/, const Eigen::MatrixBase & coordinates, Eigen::MatrixBase & coordMatrix, Int integration_order) { if (integration_order != ElementClassProperty<_quadrangle_8>::polynomial_degree) { AKANTU_TO_IMPLEMENT(); } else { for (Int i = 0; i < coordinates.cols(); ++i) { // Int j = 0; auto x = coordinates(0, i); auto y = coordinates(1, i); coordMatrix(i, 0) = 1; coordMatrix(i, 1) = x; coordMatrix(i, 2) = y; coordMatrix(i, 3) = x * y; } } } }; /* -------------------------------------------------------------------------- */ template inline void ShapeFunctions::buildElementalFieldInterpolationMatrix( const Eigen::MatrixBase & coordinates, Eigen::MatrixBase & coordMatrix, Int integration_order) const { BuildElementalFieldInterpolationMatrix::call( *this, coordinates, coordMatrix, integration_order); } /* -------------------------------------------------------------------------- */ template inline void ShapeFunctions::interpolateElementalFieldFromIntegrationPoints( const Array & field, const Array & interpolation_points_coordinates_matrices, const Array & quad_points_coordinates_inv_matrices, ElementTypeMapArray & result, GhostType ghost_type, const Array & element_filter) const { AKANTU_DEBUG_IN(); constexpr auto nb_quad_per_element = GaussIntegrationElement::getNbQuadraturePoints(); auto nb_interpolation_points_per_elem = interpolation_points_coordinates_matrices.getNbComponent() / nb_quad_per_element; if (not result.exists(type, ghost_type)) { auto nb_element = this->mesh.getNbElement(type, ghost_type); result.alloc(nb_element * nb_interpolation_points_per_elem, field.getNbComponent(), type, ghost_type); } AKANTU_DEBUG_ASSERT(element_filter != empty_filter, "This function does not work without an element_filter"); // auto nb_element = element_filter.size(); Matrix coefficients(nb_quad_per_element, field.getNbComponent()); auto result_begin = make_view(result(type, ghost_type), field.getNbComponent(), nb_interpolation_points_per_elem) .begin(); /// loop over the elements of the current filter and element type for (auto && data : zip(element_filter, make_view(field, field.getNbComponent(), nb_quad_per_element), make_view(interpolation_points_coordinates_matrices, nb_interpolation_points_per_elem, nb_quad_per_element), make_view(quad_points_coordinates_inv_matrices, nb_quad_per_element, nb_quad_per_element))) { /** * matrix containing the inversion of the quadrature points' * coordinates */ auto && inv_quad_coord_matrix = std::get<3>(data); /** * multiply it by the field values over quadrature points to get * the interpolation coefficients */ coefficients = inv_quad_coord_matrix * std::get<1>(data).transpose(); /// matrix containing the points' coordinates auto && coord = std::get<2>(data); auto el = std::get<0>(data); /// multiply the coordinates matrix by the coefficients matrix and store the /// result result_begin[el] = coefficients.transpose() * coord.transpose(); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template inline void ShapeFunctions::interpolateElementalFieldOnIntegrationPoints( const Array & u_el, Array & uq, GhostType ghost_type, const Array & shapes, const Array & filter_elements) const { auto nb_element = mesh.getNbElement(type, ghost_type); if (nb_element == 0) { return; } auto nb_nodes_per_element = ElementClass::getShapeSize(); auto nb_points = shapes.size() / nb_element; auto nb_degree_of_freedom = u_el.getNbComponent() / nb_nodes_per_element; auto N_view = make_view(shapes, nb_nodes_per_element, nb_points); std::unique_ptr> filtered_N; if (filter_elements != empty_filter) { nb_element = filter_elements.size(); filtered_N = std::make_unique>(0, shapes.getNbComponent()); FEEngine::filterElementalData(mesh, shapes, *filtered_N, type, ghost_type, filter_elements); N_view = make_const_view(*filtered_N, nb_nodes_per_element, nb_points); } uq.resize(nb_element * nb_points); for (auto && data : zip(N_view, make_view(uq, nb_degree_of_freedom, nb_points), make_view(u_el, nb_degree_of_freedom, nb_nodes_per_element))) { const auto & u = std::get<2>(data); const auto & N = std::get<0>(data); auto & uq = std::get<1>(data); uq.noalias() = u * N; } } /* -------------------------------------------------------------------------- */ template < ElementType type, std::enable_if_t::getNaturalSpaceDimension() != 0> *> void ShapeFunctions::gradientElementalFieldOnIntegrationPoints( const Array & u_el, Array & out_nablauq, GhostType ghost_type, const Array & shapes_derivatives, const Array & filter_elements) const { AKANTU_DEBUG_IN(); constexpr auto nb_nodes_per_element = ElementClass::getNbNodesPerInterpolationElement(); constexpr auto element_dimension = ElementClass::getNaturalSpaceDimension(); auto nb_points = integration_points(type, ghost_type).cols(); auto nb_element = mesh.getNbElement(type, ghost_type); auto nb_degree_of_freedom = u_el.getNbComponent() / nb_nodes_per_element; - auto B_it = - make_view(shapes_derivatives) - .begin(); + auto B_it = make_const_view( + shapes_derivatives) + .begin(); Array * filtered_B = nullptr; if (filter_elements != empty_filter) { nb_element = filter_elements.size(); filtered_B = new Array(0, shapes_derivatives.getNbComponent()); FEEngine::filterElementalData(mesh, shapes_derivatives, *filtered_B, type, ghost_type, filter_elements); - B_it = - make_view(*filtered_B).begin(); + B_it = make_const_view(*filtered_B) + .begin(); } out_nablauq.resize(nb_element * nb_points); auto u_it = make_view( u_el, nb_degree_of_freedom, nb_nodes_per_element) .begin(); auto nabla_u_it = make_view( out_nablauq, nb_degree_of_freedom, element_dimension) .begin(); for (Int el = 0; el < nb_element; ++el, ++u_it) { const auto & u = *u_it; for (Int q = 0; q < nb_points; ++q, ++B_it, ++nabla_u_it) { const auto & B = *B_it; auto & nabla_u = *nabla_u_it; nabla_u.noalias() = u * B.transpose(); } } delete filtered_B; AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ } // namespace akantu //#endif /* AKANTU_SHAPE_FUNCTIONS_INLINE_IMPL_HH_ */ diff --git a/src/fe_engine/shape_structural_inline_impl.hh b/src/fe_engine/shape_structural_inline_impl.hh index c9f2ec86c..6a08cca7a 100644 --- a/src/fe_engine/shape_structural_inline_impl.hh +++ b/src/fe_engine/shape_structural_inline_impl.hh @@ -1,491 +1,491 @@ /** * Copyright (©) 2017-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "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 std::unique_ptr> getNodesPerElement(const Mesh & mesh, const Array & nodes, GhostType ghost_type) { const auto dim = ElementClass::getSpatialDimension(); const auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type); auto nodes_per_element = std::make_unique>(0, dim * nb_nodes_per_element); FEEngine::extractNodalToElementField(mesh, nodes, *nodes_per_element, type, ghost_type); return nodes_per_element; } } // namespace /* -------------------------------------------------------------------------- */ template template void ShapeStructural::computeShapesOnIntegrationPointsInternal( const Array & nodes, const Matrix & integration_points, Array & shapes, GhostType ghost_type, const Array & filter_elements, bool mass) const { auto nb_points = integration_points.cols(); auto nb_element = mesh.getConnectivity(type, ghost_type).size(); const auto nb_nodes_per_element = ElementClass::getNbNodesPerElement(); shapes.resize(nb_element * nb_points); const auto nb_dofs = ElementClass::getNbDegreeOfFreedom(); auto nb_rows = nb_dofs; if (mass) { nb_rows = ElementClass::getNbStressComponents(); } #if !defined(AKANTU_NDEBUG) Int 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 const auto nb_cols_shapes = ElementClass::getNbNodesPerInterpolationElement() * nb_dofs; auto nodes_per_element = getNodesPerElement(mesh, nodes, ghost_type); auto shapes_view = make_view(shapes, nb_rows, nb_cols_shapes, nb_points); auto nodes_view = make_view( *nodes_per_element, mesh.getSpatialDimension(), nb_nodes_per_element); auto R_view = make_view(rotation_matrices(type, ghost_type)); auto loop_core = [&](auto && data) { auto & N = std::get<0>(data); auto & X = std::get<1>(data); auto & R = std::get<2>(data); Matrix T; T.zero(); for (Int i = 0; i < nb_nodes_per_element; ++i) { T.template block(i * nb_dofs, i * nb_dofs) = R; } if (not mass) { ElementClass::computeShapes(integration_points, X, T, N); } else { ElementClass::computeShapesMass(integration_points, X, T, N); } }; if (filter_elements == empty_filter) { for (auto && data : zip(shapes_view, nodes_view, R_view)) { loop_core(data); } } else { for (auto && data : filter(filter_elements, zip(shapes_view, nodes_view, R_view))) { loop_core(data); } } } /* -------------------------------------------------------------------------- */ template template void ShapeStructural::precomputeRotationMatrices( const Array & 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::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 x_el(0, spatial_dimension * nb_nodes_per_element); FEEngine::extractNodalToElementField(mesh, nodes, x_el, type, ghost_type); bool has_extra_normal = mesh.hasData("extra_normal", type, ghost_type); Array::const_vector_iterator extra_normal; if (has_extra_normal) { extra_normal = mesh.getData("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::computeRotationMatrix(R, X, *extra_normal); ++extra_normal; } else { ElementClass::computeRotationMatrix( R, X, Vector(spatial_dimension)); } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template template void ShapeStructural::precomputeShapesOnIntegrationPoints( const Array & 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::getNbDegreeOfFreedom(); const auto dim = ElementClass::getSpatialDimension(); const auto spatial_dimension = mesh.getSpatialDimension(); const auto natural_spatial_dimension = ElementClass::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(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(rot_matrices, nb_dof, nb_dof))) { auto && N = std::get<0>(tuple); auto && X = std::get<1>(tuple); auto && RDOFs = std::get<2>(tuple); Matrix T(N.size(1), N.size(1)); T.zero(); for (Idx i = 0; i < nb_nodes_per_element; ++i) { T.block(i * RDOFs.rows(), i * RDOFs.cols(), RDOFs.rows(), RDOFs.cols()) = RDOFs; } 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::computeShapes(natural_coords, x, T, N); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template template void ShapeStructural::precomputeShapeDerivativesOnIntegrationPoints( const Array & 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::getNaturalSpaceDimension(); const auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type); const auto nb_points = natural_coords.cols(); const auto nb_dof = ElementClass::getNbDegreeOfFreedom(); const auto nb_element = mesh.getNbElement(type, ghost_type); const auto nb_stress_components = ElementClass::getNbStressComponents(); const auto nb_cols_shaped = nb_dof * nb_nodes_per_element; 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 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_cols_shaped, nb_points), make_view(rot_matrices))) { // compute shape derivatives auto & X = std::get<0>(tuple); auto & B = std::get<1>(tuple); auto & RDOFs = std::get<2>(tuple); Tensor3 dnds(natural_spatial_dimension, ElementClass::interpolation_property::dnds_columns, B.size(2)); ElementClass::computeDNDS(natural_coords, X, dnds); Tensor3 J(natural_spatial_dimension, natural_spatial_dimension, natural_coords.cols()); // Computing the coordinates of the element in the natural space Matrix T; T.zero(); for (Int i = 0; i < nb_nodes_per_element; ++i) { T.template block(i * nb_dof, i * nb_dof) = RDOFs; } auto && R = RDOFs.block(0, 0, spatial_dimension, spatial_dimension); // Rotate to local basis auto x = (R * X).template block( 0, 0); ElementClass::computeJMat(natural_coords, x, J); ElementClass::computeShapeDerivatives(J, dnds, T, B); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template template void ShapeStructural::interpolateOnIntegrationPoints( const Array & in_u, Array & out_uq, Int nb_dof, GhostType ghost_type, const Array & 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::getNbNodesPerElement(); auto nb_quad_points_per_element = integration_points(type, ghost_type).cols(); Array 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 = make_view(out_uq, nb_dof, 1, nb_quad_points_per_element).begin(); auto shapes_it = make_view(shapes_, nb_dof, nb_dof * nb_nodes_per_element, nb_quad_points_per_element) .begin(); auto u_it = make_view(u_el, nb_dof * nb_nodes_per_element, 1, nb_quad_points_per_element) .begin(); for_each_element(nb_element, filter_elements, [&](auto && el) { auto & uq = *out_it; const auto & u = *u_it; auto && N = shapes_it[el]; for (auto && q : arange(uq.size(2))) { uq(q) = N(q) * u(q); } ++out_it; ++u_it; }); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template template void ShapeStructural::gradientOnIntegrationPoints( const Array & in_u, Array & out_nablauq, Int nb_dof, GhostType ghost_type, const Array & 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::getSpatialDimension(); auto nb_quad_points_per_element = integration_points(type, ghost_type).cols(); auto nb_nodes_per_element = ElementClass::getNbNodesPerElement(); Array 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 = make_view(out_nablauq, element_dimension, 1, nb_quad_points_per_element) .begin(); auto shapesd_it = make_view(shapesd, element_dimension, nb_dof * nb_nodes_per_element, nb_quad_points_per_element) .begin(); auto u_it = make_view(u_el, nb_dof * nb_nodes_per_element, 1, nb_quad_points_per_element) .begin(); for_each_element(nb_element, filter_elements, [&](auto && el) { auto & nablau = *out_it; const auto & u = *u_it; auto B = shapesd_it[el]; for (auto && q : arange(nablau.size(2))) { nablau(q) = B(q) * u(q); } ++out_it; ++u_it; }); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template <> template void ShapeStructural<_ek_structural>::computeBtD( const Array & Ds, Array & BtDs, GhostType ghost_type, const Array & filter_elements) const { auto itp_type = ElementClassProperty::interpolation_type; auto nb_stress = ElementClass::getNbStressComponents(); auto nb_dof_per_element = ElementClass::getNbDegreeOfFreedom() * mesh.getNbNodesPerElement(type); const auto & shapes_derivatives = this->shapes_derivatives(itp_type, ghost_type); Array 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 = B.transpose() * D; } } /* -------------------------------------------------------------------------- */ template <> template void ShapeStructural<_ek_structural>::computeNtb( const Array & bs, Array & Ntbs, GhostType ghost_type, const Array & filter_elements) const { constexpr auto itp_type = ElementClassProperty::interpolation_type; constexpr auto nb_dof = ElementClass::getNbDegreeOfFreedom(); constexpr auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type); const auto & shapes = this->shapes(itp_type, ghost_type); Array shapes_filtered(0, shapes.getNbComponent()); auto && view = make_view(shapes); 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); + make_const_view(shapes_filtered); N_it = view.begin(); N_end = view.end(); } for (auto && [N, b, Nt_b] : zip(range(N_it, N_end), make_view(bs), make_view(Ntbs))) { Nt_b = N.transpose() * b; } } /* -------------------------------------------------------------------------- */ template inline void ShapeStructural::initShapeFunctions( const Array & /* unused */, const Matrix & /* unused */, ElementType /* unused */, GhostType /* unused */) { AKANTU_TO_IMPLEMENT(); } template <> inline void ShapeStructural<_ek_structural>::initShapeFunctions( const Array & nodes, const Matrix & integration_points, ElementType type, GhostType ghost_type) { tuple_dispatch>( [&](auto && enum_type) { constexpr ElementType type = aka::decay_v; this->setIntegrationPointsByType(integration_points, ghost_type); this->precomputeRotationMatrices(nodes, ghost_type); this->precomputeShapesOnIntegrationPoints(nodes, ghost_type); this->precomputeShapeDerivativesOnIntegrationPoints(nodes, ghost_type); }, type); } /* -------------------------------------------------------------------------- */ } // namespace akantu //#endif /* AKANTU_SHAPE_STRUCTURAL_INLINE_IMPL_HH_ */ diff --git a/src/io/dumper/dumper_nodal_field.hh b/src/io/dumper/dumper_nodal_field.hh index 39d88d7be..e1282f629 100644 --- a/src/io/dumper/dumper_nodal_field.hh +++ b/src/io/dumper/dumper_nodal_field.hh @@ -1,179 +1,182 @@ /** * Copyright (©) 2012-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #ifndef AKANTU_DUMPER_NODAL_FIELD_HH_ #define AKANTU_DUMPER_NODAL_FIELD_HH_ /* -------------------------------------------------------------------------- */ #include "dumper_compute.hh" #include "dumper_field.hh" #include /* -------------------------------------------------------------------------- */ namespace akantu { namespace dumpers { /* ------------------------------------------------------------------------ */ // This represents a iohelper compatible field template , class Filter = Array> class NodalField : public dumpers::Field { /* ---------------------------------------------------------------------- */ /* Typedefs */ /* ---------------------------------------------------------------------- */ public: using support_type = Idx; using types = TypeTraits, Container>; - class iterator : public iohelper::iterator> { + class iterator + : public iohelper::iterator> { public: - iterator(T * vect, Int _offset, Int _n, Int _stride, const Int * filter) + iterator(const T * vect, Int _offset, Int _n, Int _stride, + const Int * filter) : internal_it(vect), offset(_offset), n(_n), stride(_stride), filter(filter) {} bool operator!=(const iterator & it) const override { if (filter != nullptr) { return filter != it.filter; } return internal_it != it.internal_it; } iterator & operator++() override { if (filter != nullptr) { ++filter; } else { internal_it += offset; } return *this; } - VectorProxy operator*() override { + VectorProxy operator*() override { if (filter != nullptr) { - return VectorProxy(internal_it + *(filter)*offset + stride, n); + return VectorProxy(internal_it + *(filter)*offset + stride, + n); } - return VectorProxy(internal_it + stride, n); + return VectorProxy(internal_it + stride, n); } private: - T * internal_it; + const T * internal_it; Int offset, n, stride; const Int * filter{nullptr}; }; /* ---------------------------------------------------------------------- */ /* Constructors/Destructors */ /* ---------------------------------------------------------------------- */ public: NodalField(const Container & _field, Int _n = 0, Int _stride = 0, const Filter * filter = nullptr) : field(_field), n(_n), stride(_stride), filter(filter), padding(0) { AKANTU_DEBUG_ASSERT(((not filtered) and filter == nullptr) or filtered, "Filter passed to unfiltered NodalField!"); AKANTU_DEBUG_ASSERT((filtered and this->filter != nullptr) or (not filtered), "No filter passed to filtered NodalField!"); AKANTU_DEBUG_ASSERT( (filter != nullptr and this->filter->getNbComponent() == 1) or (filter == nullptr), "Multi-component filter given to NodalField (" << this->filter->getNbComponent() << " components detected, sould be 1"); if (n == 0) { this->n = field.getNbComponent() - stride; } } /* ---------------------------------------------------------------------- */ /* Methods */ /* ---------------------------------------------------------------------- */ public: void registerToDumper(const std::string & id, iohelper::Dumper & dumper) override { dumper.addNodeDataField(id, *this); } inline iterator begin() { return iterator(field.data(), field.getNbComponent(), n, stride, filter == nullptr ? nullptr : filter->data()); } inline iterator end() { return iterator(field.data() + field.getNbComponent() * field.size(), field.getNbComponent(), n, stride, filter == nullptr ? nullptr : filter->data() + filter->size()); } bool isHomogeneous() override { return true; } void checkHomogeneity() override { this->homogeneous = true; } virtual Int getDim() { if (this->padding) { return this->padding; } return n; } void setPadding(Int padding) { this->padding = padding; } Int size() { if (filter != nullptr) { return filter->size(); } return field.size(); } inline std::shared_ptr connect(FieldComputeProxy & proxy) override { return proxy.connectToField(this); } /// for connection to a Homogenizer inline std::unique_ptr connect(HomogenizerProxy & /*proxy*/) override { throw; } template ::value> * = nullptr> iohelper::DataType getDataType() { return iohelper::getDataType(); } template ::value> * = nullptr> iohelper::DataType getDataType() { return iohelper::getDataType(); } /* ---------------------------------------------------------------------- */ /* Class Members */ /* ---------------------------------------------------------------------- */ private: const Container & field; Int n, stride; const Filter * filter{nullptr}; Int padding; }; } // namespace dumpers } // namespace akantu /* -------------------------------------------------------------------------- */ #endif /* AKANTU_DUMPER_NODAL_FIELD_HH_ */ diff --git a/src/io/mesh_io/mesh_io_msh.cc b/src/io/mesh_io/mesh_io_msh.cc index 2febca7d7..25b68d281 100644 --- a/src/io/mesh_io/mesh_io_msh.cc +++ b/src/io/mesh_io/mesh_io_msh.cc @@ -1,1112 +1,1112 @@ /** * Copyright (©) 2010-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* ----------------------------------------------------------------------------- Version (Legacy) 1.0 $NOD number-of-nodes node-number x-coord y-coord z-coord ... $ENDNOD $ELM number-of-elements elm-number elm-type reg-phys reg-elem number-of-nodes node-number-list ... $ENDELM ----------------------------------------------------------------------------- Version 2.1 $MeshFormat version-number file-type data-size $EndMeshFormat $Nodes number-of-nodes node-number x-coord y-coord z-coord ... $EndNodes $Elements number-of-elements elm-number elm-type number-of-tags < tag > ... node-number-list ... $EndElements $PhysicalNames number-of-names physical-dimension physical-number "physical-name" ... $EndPhysicalNames $NodeData number-of-string-tags < "string-tag" > ... number-of-real-tags < real-tag > ... number-of-integer-tags < integer-tag > ... node-number value ... ... $EndNodeData $ElementData number-of-string-tags < "string-tag" > ... number-of-real-tags < real-tag > ... number-of-integer-tags < integer-tag > ... elm-number value ... ... $EndElementData $ElementNodeData number-of-string-tags < "string-tag" > ... number-of-real-tags < real-tag > ... number-of-integer-tags < integer-tag > ... elm-number number-of-nodes-per-element value ... ... $ElementEndNodeData ----------------------------------------------------------------------------- elem-type 1: 2-node line. 2: 3-node triangle. 3: 4-node quadrangle. 4: 4-node tetrahedron. 5: 8-node hexahedron. 6: 6-node prism. 7: 5-node pyramid. 8: 3-node second order line 9: 6-node second order triangle 10: 9-node second order quadrangle 11: 10-node second order tetrahedron 12: 27-node second order hexahedron 13: 18-node second order prism 14: 14-node second order pyramid 15: 1-node point. 16: 8-node second order quadrangle 17: 20-node second order hexahedron 18: 15-node second order prism 19: 13-node second order pyramid 20: 9-node third order incomplete triangle 21: 10-node third order triangle 22: 12-node fourth order incomplete triangle 23: 15-node fourth order triangle 24: 15-node fifth order incomplete triangle 25: 21-node fifth order complete triangle 26: 4-node third order edge 27: 5-node fourth order edge 28: 6-node fifth order edge 29: 20-node third order tetrahedron 30: 35-node fourth order tetrahedron 31: 56-node fifth order tetrahedron -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ #include "element_group.hh" #include "mesh_io.hh" #include "mesh_utils.hh" #include "node_group.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ /* Methods Implentations */ /* -------------------------------------------------------------------------- */ MeshIOMSH::MeshIOMSH() { canReadSurface = true; canReadExtendedData = true; _msh_nodes_per_elem[_msh_not_defined] = 0; _msh_nodes_per_elem[_msh_segment_2] = 2; _msh_nodes_per_elem[_msh_triangle_3] = 3; _msh_nodes_per_elem[_msh_quadrangle_4] = 4; _msh_nodes_per_elem[_msh_tetrahedron_4] = 4; _msh_nodes_per_elem[_msh_hexahedron_8] = 8; _msh_nodes_per_elem[_msh_prism_1] = 6; _msh_nodes_per_elem[_msh_pyramid_1] = 1; _msh_nodes_per_elem[_msh_segment_3] = 3; _msh_nodes_per_elem[_msh_triangle_6] = 6; _msh_nodes_per_elem[_msh_quadrangle_9] = 9; _msh_nodes_per_elem[_msh_tetrahedron_10] = 10; _msh_nodes_per_elem[_msh_hexahedron_27] = 27; _msh_nodes_per_elem[_msh_hexahedron_20] = 20; _msh_nodes_per_elem[_msh_prism_18] = 18; _msh_nodes_per_elem[_msh_prism_15] = 15; _msh_nodes_per_elem[_msh_pyramid_14] = 14; _msh_nodes_per_elem[_msh_point] = 1; _msh_nodes_per_elem[_msh_quadrangle_8] = 8; _msh_to_akantu_element_types[_msh_not_defined] = _not_defined; _msh_to_akantu_element_types[_msh_segment_2] = _segment_2; _msh_to_akantu_element_types[_msh_triangle_3] = _triangle_3; _msh_to_akantu_element_types[_msh_quadrangle_4] = _quadrangle_4; _msh_to_akantu_element_types[_msh_tetrahedron_4] = _tetrahedron_4; _msh_to_akantu_element_types[_msh_hexahedron_8] = _hexahedron_8; _msh_to_akantu_element_types[_msh_prism_1] = _pentahedron_6; _msh_to_akantu_element_types[_msh_pyramid_1] = _not_defined; _msh_to_akantu_element_types[_msh_segment_3] = _segment_3; _msh_to_akantu_element_types[_msh_triangle_6] = _triangle_6; _msh_to_akantu_element_types[_msh_quadrangle_9] = _not_defined; _msh_to_akantu_element_types[_msh_tetrahedron_10] = _tetrahedron_10; _msh_to_akantu_element_types[_msh_hexahedron_27] = _not_defined; _msh_to_akantu_element_types[_msh_hexahedron_20] = _hexahedron_20; _msh_to_akantu_element_types[_msh_prism_18] = _not_defined; _msh_to_akantu_element_types[_msh_prism_15] = _pentahedron_15; _msh_to_akantu_element_types[_msh_pyramid_14] = _not_defined; _msh_to_akantu_element_types[_msh_point] = _point_1; _msh_to_akantu_element_types[_msh_quadrangle_8] = _quadrangle_8; _akantu_to_msh_element_types[_not_defined] = _msh_not_defined; _akantu_to_msh_element_types[_segment_2] = _msh_segment_2; _akantu_to_msh_element_types[_segment_3] = _msh_segment_3; _akantu_to_msh_element_types[_triangle_3] = _msh_triangle_3; _akantu_to_msh_element_types[_triangle_6] = _msh_triangle_6; _akantu_to_msh_element_types[_tetrahedron_4] = _msh_tetrahedron_4; _akantu_to_msh_element_types[_tetrahedron_10] = _msh_tetrahedron_10; _akantu_to_msh_element_types[_quadrangle_4] = _msh_quadrangle_4; _akantu_to_msh_element_types[_quadrangle_8] = _msh_quadrangle_8; _akantu_to_msh_element_types[_hexahedron_8] = _msh_hexahedron_8; _akantu_to_msh_element_types[_hexahedron_20] = _msh_hexahedron_20; _akantu_to_msh_element_types[_pentahedron_6] = _msh_prism_1; _akantu_to_msh_element_types[_pentahedron_15] = _msh_prism_15; _akantu_to_msh_element_types[_point_1] = _msh_point; #if defined(AKANTU_STRUCTURAL_MECHANICS) _akantu_to_msh_element_types[_bernoulli_beam_2] = _msh_segment_2; _akantu_to_msh_element_types[_bernoulli_beam_3] = _msh_segment_2; _akantu_to_msh_element_types[_discrete_kirchhoff_triangle_18] = _msh_triangle_3; #endif std::map::iterator it; for (it = _akantu_to_msh_element_types.begin(); it != _akantu_to_msh_element_types.end(); ++it) { Int nb_nodes = _msh_nodes_per_elem[it->second]; std::vector tmp(nb_nodes); for (Int i = 0; i < nb_nodes; ++i) { tmp[i] = i; } switch (it->first) { case _tetrahedron_10: tmp[8] = 9; tmp[9] = 8; break; case _pentahedron_6: tmp[0] = 2; tmp[1] = 0; tmp[2] = 1; tmp[3] = 5; tmp[4] = 3; tmp[5] = 4; break; case _pentahedron_15: tmp[0] = 2; tmp[1] = 0; tmp[2] = 1; tmp[3] = 5; tmp[4] = 3; tmp[5] = 4; tmp[6] = 8; tmp[8] = 11; tmp[9] = 6; tmp[10] = 9; tmp[11] = 10; tmp[12] = 14; tmp[14] = 12; break; case _hexahedron_20: tmp[9] = 11; tmp[10] = 12; tmp[11] = 9; tmp[12] = 13; tmp[13] = 10; tmp[17] = 19; tmp[18] = 17; tmp[19] = 18; break; default: // nothing to change break; } _read_order[it->first] = tmp; } } /* -------------------------------------------------------------------------- */ MeshIOMSH::~MeshIOMSH() = default; /* -------------------------------------------------------------------------- */ namespace { struct File { std::string filename; std::ifstream infile; std::string line; std::size_t current_line{0}; std::size_t first_node_number{std::numeric_limits::max()}, last_node_number{0}; bool has_physical_names{false}; std::unordered_map node_tags; std::unordered_map element_tags; int version{0}; int size_of_size_t{0}; Mesh & mesh; MeshAccessor mesh_accessor; std::multimap, int> entity_tag_to_physical_tags; File(const std::string & filename, Mesh & mesh) : filename(filename), mesh(mesh), mesh_accessor(mesh) { infile.open(filename.c_str()); if (not infile.good()) { AKANTU_EXCEPTION("Cannot open file " << filename); } } ~File() { infile.close(); } auto good() { return infile.good(); } std::stringstream get_line() { std::string tmp_str; if (infile.eof()) { AKANTU_EXCEPTION("Reached the end of the file " << filename); } std::getline(infile, tmp_str); line = trim(tmp_str); ++current_line; return std::stringstream(line); } template void read_line(Ts &&... ts) { auto && sstr = get_line(); (void)std::initializer_list{ (sstr >> std::forward(ts), 0)...}; } }; } // namespace /* -------------------------------------------------------------------------- */ template void MeshIOMSH::populateReaders2(File & file, Readers & readers) { readers["$NOD"] = readers["$Nodes"] = [&](const std::string & /*unused*/) { UInt nb_nodes; file.read_line(nb_nodes); Array & nodes = file.mesh_accessor.getNodes(); nodes.resize(nb_nodes); file.mesh_accessor.setNbGlobalNodes(nb_nodes); size_t index; Vector coord(3); /// for each node, read the coordinates for (auto && data : enumerate(make_view(nodes, nodes.getNbComponent()))) { file.read_line(index, coord(0), coord(1), coord(2)); if (index > std::numeric_limits::max()) { AKANTU_EXCEPTION( "There are more nodes in this files than the index type of akantu " "can handle, consider recompiling with a bigger index type"); } file.first_node_number = std::min(file.first_node_number, index); file.last_node_number = std::max(file.last_node_number, index); for (auto && coord_data : zip(std::get<1>(data), coord)) { std::get<0>(coord_data) = std::get<1>(coord_data); } file.node_tags[index] = std::get<0>(data); } }; readers["$ELM"] = readers["$Elements"] = [&](const std::string & /*unused*/) { Int nb_elements; file.read_line(nb_elements); Int index; UInt msh_type; ElementType akantu_type; for (Int i = 0; i < nb_elements; ++i) { auto && sstr_elem = file.get_line(); sstr_elem >> index; sstr_elem >> msh_type; /// get the connectivity vector depending on the element type akantu_type = this->_msh_to_akantu_element_types[MSHElementType(msh_type)]; if (akantu_type == _not_defined) { AKANTU_DEBUG_WARNING("Unsuported element kind " << msh_type << " at line " << file.current_line); continue; } Element elem{akantu_type, 0, _not_ghost}; auto & connectivity = file.mesh_accessor.getConnectivity(akantu_type); auto node_per_element = connectivity.getNbComponent(); auto & read_order = this->_read_order[akantu_type]; /// read tags informations if (file.version < 2000) { Int tag0; Int tag1; Int nb_nodes; // reg-phys, reg-elem, number-of-nodes sstr_elem >> tag0 >> tag1 >> nb_nodes; auto & data0 = file.mesh_accessor.template getData("tag_0", akantu_type); data0.push_back(tag0); auto & data1 = file.mesh_accessor.template getData("tag_1", akantu_type); data1.push_back(tag1); } else if (file.version < 4000) { Int nb_tags; sstr_elem >> nb_tags; for (Int j = 0; j < nb_tags; ++j) { Int tag; sstr_elem >> tag; auto & data = file.mesh_accessor.template getData( "tag_" + std::to_string(j), akantu_type); data.push_back(tag); } } Vector local_connect(node_per_element); for (Int j = 0; j < node_per_element; ++j) { Int node_index; sstr_elem >> node_index; AKANTU_DEBUG_ASSERT(node_index <= Int(file.last_node_number), "Node number not in range : line " << file.current_line); local_connect(read_order[j]) = file.node_tags[node_index]; } connectivity.push_back(local_connect); elem.element = connectivity.size() - 1; file.element_tags[index] = elem; } }; readers["$Periodic"] = [&](const std::string &) { Int nb_periodic_entities; file.read_line(nb_periodic_entities); file.mesh_accessor.getNodesFlags().resize(file.mesh.getNbNodes(), NodeFlag::_normal); for (Int p = 0; p < nb_periodic_entities; ++p) { // dimension slave-tag master-tag Int dimension; file.read_line(dimension); // transformation file.get_line(); // nb nodes Int nb_nodes; file.read_line(nb_nodes); for (Int n = 0; n < nb_nodes; ++n) { // slave master auto && sstr = file.get_line(); // The info in the mesh seem inconsistent so they are ignored for now. continue; if (dimension == file.mesh.getSpatialDimension() - 1) { Idx slave, master; sstr >> slave; sstr >> master; file.mesh_accessor.addPeriodicSlave(file.node_tags[slave], file.node_tags[master]); } } } // mesh_accessor.markMeshPeriodic(); }; } /* -------------------------------------------------------------------------- */ template void MeshIOMSH::populateReaders4(File & file, Readers & readers) { static std::map entity_type{ {0, "points"}, {1, "curve"}, {2, "surface"}, {3, "volume"}, }; readers["$Entities"] = [&](const std::string & /*unused*/) { size_t num_entity[4]; file.read_line(num_entity[0], num_entity[1], num_entity[2], num_entity[3]); for (auto entity_dim : arange(4)) { for (auto _ [[gnu::unused]] : arange(num_entity[entity_dim])) { auto && sstr = file.get_line(); int tag; double min_x; double min_y; double min_z; double max_x; double max_y; double max_z; size_t num_physical_tags; sstr >> tag >> min_x >> min_y >> min_z; if (entity_dim > 0 or file.version < 4001) { sstr >> max_x >> max_y >> max_z; } sstr >> num_physical_tags; for (auto _ [[gnu::unused]] : arange(num_physical_tags)) { int phys_tag; sstr >> phys_tag; std::string physical_name; if (this->physical_names.find(phys_tag) == this->physical_names.end()) { physical_name = "msh_block_" + std::to_string(phys_tag); } else { physical_name = this->physical_names[phys_tag]; } if (not file.mesh.elementGroupExists(physical_name)) { file.mesh.createElementGroup(physical_name, entity_dim); } else { file.mesh.getElementGroup(physical_name).addDimension(entity_dim); } file.entity_tag_to_physical_tags.insert( std::make_pair(std::make_pair(tag, entity_dim), phys_tag)); } } } }; readers["$Nodes"] = [&](const std::string & /*unused*/) { size_t num_blocks; size_t num_nodes; if (file.version >= 4001) { file.read_line(num_blocks, num_nodes, file.first_node_number, file.last_node_number); } else { file.read_line(num_blocks, num_nodes); } auto & nodes = file.mesh_accessor.getNodes(); nodes.reserve(num_nodes); file.mesh_accessor.setNbGlobalNodes(num_nodes); if (num_nodes > std::numeric_limits::max()) { AKANTU_EXCEPTION( "There are more nodes in this files than the index type of akantu " "can handle, consider recompiling with a bigger index type"); } size_t node_id{0}; auto dim = nodes.getNbComponent(); for (auto block [[gnu::unused]] : arange(num_blocks)) { int entity_dim; int entity_tag; int parametric; size_t num_nodes_in_block; Vector pos(3); if (file.version >= 4001) { file.read_line(entity_dim, entity_tag, parametric, num_nodes_in_block); if (parametric) { AKANTU_EXCEPTION( "Akantu does not support parametric nodes in msh files"); } for (auto _ [[gnu::unused]] : arange(num_nodes_in_block)) { size_t tag; file.read_line(tag); file.node_tags[tag] = node_id; ++node_id; } for (auto _ [[gnu::unused]] : arange(num_nodes_in_block)) { file.read_line(pos(_x), pos(_y), pos(_z)); nodes.push_back(pos.block(0, 0, dim, 1)); } } else { file.read_line(entity_tag, entity_dim, parametric, num_nodes_in_block); for (auto _ [[gnu::unused]] : arange(num_nodes_in_block)) { size_t tag; file.read_line(tag, pos(_x), pos(_y), pos(_z)); file.first_node_number = std::min(file.first_node_number, tag); file.last_node_number = std::max(file.last_node_number, tag); nodes.push_back(pos.block(0, 0, dim, 1)); file.node_tags[tag] = node_id; ++node_id; } } } }; readers["$Elements"] = [&](const std::string & /*unused*/) { size_t num_blocks; size_t num_elements; file.read_line(num_blocks, num_elements); for (auto block [[gnu::unused]] : arange(num_blocks)) { int entity_dim; int entity_tag; int element_type; size_t num_elements_in_block; if (file.version >= 4001) { file.read_line(entity_dim, entity_tag, element_type, num_elements_in_block); } else { file.read_line(entity_tag, entity_dim, element_type, num_elements_in_block); } /// get the connectivity vector depending on the element type auto && akantu_type = this->_msh_to_akantu_element_types[(MSHElementType)element_type]; if (akantu_type == _not_defined) { AKANTU_DEBUG_WARNING("Unsuported element kind " << element_type << " at line " << file.current_line); continue; } Element elem{akantu_type, 0, _not_ghost}; auto & connectivity = file.mesh_accessor.getConnectivity(akantu_type); Vector local_connect(connectivity.getNbComponent()); auto && read_order = this->_read_order[akantu_type]; auto & data0 = file.mesh_accessor.template getData("tag_0", akantu_type); data0.resize(data0.size() + num_elements_in_block, 0); auto range = file.entity_tag_to_physical_tags.equal_range( std::make_pair(entity_tag, entity_dim)); auto & physical_data = file.mesh_accessor.template getData( "physical_names", akantu_type); physical_data.resize(physical_data.size() + num_elements_in_block, ""); for (auto _ [[gnu::unused]] : arange(num_elements_in_block)) { auto && sstr_elem = file.get_line(); std::size_t elem_tag; sstr_elem >> elem_tag; for (auto && c : arange(connectivity.getNbComponent())) { std::size_t node_tag; sstr_elem >> node_tag; AKANTU_DEBUG_ASSERT(node_tag >= file.first_node_number, "Node number not in range : line " << file.current_line); AKANTU_DEBUG_ASSERT(node_tag <= file.last_node_number, "Node number not in range : line " << file.current_line); node_tag = file.node_tags[node_tag]; local_connect(read_order[c]) = node_tag; } connectivity.push_back(local_connect); elem.element = connectivity.size() - 1; file.element_tags[elem_tag] = elem; bool first = true; for (auto it = range.first; it != range.second; ++it) { auto phys_it = this->physical_names.find(it->second); if (first) { data0(elem.element) = it->second; // for compatibility with version 2 if (phys_it != this->physical_names.end()) { physical_data(elem.element) = phys_it->second; } first = false; } if (phys_it != this->physical_names.end()) { file.mesh.getElementGroup(phys_it->second).add(elem, true, false); } } } } for (auto && element_group : file.mesh.iterateElementGroups()) { element_group.getNodeGroup().optimize(); } }; } /* -------------------------------------------------------------------------- */ void MeshIOMSH::read(const std::string & filename, Mesh & mesh) { File file(filename, mesh); std::map> readers; readers["$MeshFormat"] = [&](const std::string & /*unused*/) { auto && sstr = file.get_line(); double version; int format; sstr >> version >> format; int major = std::trunc(version); int minor = std::round(10 * (version - major)); file.version = major * 1000 + minor; if (format != 0) { AKANTU_ERROR("This reader can only read ASCII files."); } if (file.version > 2000) { sstr >> file.size_of_size_t; if (file.size_of_size_t > int(sizeof(UInt))) { AKANTU_DEBUG_INFO("The size of the indexes in akantu might be to small " "to read this file (akantu " << sizeof(UInt) << " vs. msh file " << file.size_of_size_t << ")"); } } if (file.version < 4000) { this->populateReaders2(file, readers); } else { this->populateReaders4(file, readers); } }; auto && read_data = [&](auto && entity_tags, auto && get_data, auto && read_data) { auto read_data_tags = [&](auto x) { UInt number_of_tags{0}; file.read_line(number_of_tags); std::vector tags(number_of_tags); for (auto && tag : tags) { file.read_line(tag); } return tags; }; auto && string_tags = read_data_tags(std::string{}); auto && real_tags [[gnu::unused]] = read_data_tags(double{}); auto && int_tags = read_data_tags(int{}); for (auto & s : string_tags) { s = trim(s, '"'); } auto id = string_tags[0]; auto size = int_tags[2]; auto nb_component = int_tags[1]; auto & data = get_data(id, size, nb_component); for (auto n [[gnu::unused]] : arange(size)) { auto && sstr = file.get_line(); size_t tag; sstr >> tag; const auto & entity = entity_tags[tag]; read_data(entity, sstr, data, nb_component); } }; readers["$NodeData"] = [&](const std::string & /*unused*/) { /* $NodeData numStringTags(ASCII int) stringTag(string) ... numRealTags(ASCII int) realTag(ASCII double) ... numIntegerTags(ASCII int) integerTag(ASCII int) ... nodeTag(size_t) value(double) ... ... $EndNodeData */ read_data( file.node_tags, [&](auto && id, auto && size [[gnu::unused]], auto && nb_component [[gnu::unused]]) -> Array & { auto & data = file.mesh.template getNodalData(id, nb_component); data.resize(size); return data; }, [&](auto && node, auto && sstr, auto && data, auto && nb_component [[gnu::unused]]) { for (auto c : arange(nb_component)) { sstr >> data(node, c); } }); }; readers["$ElementData"] = [&](const std::string & /*unused*/) { /* $ElementData numStringTags(ASCII int) stringTag(string) ... numRealTags(ASCII int) realTag(ASCII double) ... numIntegerTags(ASCII int) integerTag(ASCII int) ... elementTag(size_t) value(double) ... ... $EndElementData */ read_data( file.element_tags, [&](auto && id, auto && size [[gnu::unused]], auto && nb_component [[gnu::unused]]) -> ElementTypeMapArray & { file.mesh.template getElementalData(id); return file.mesh.template getElementalData(id); }, [&](auto && element, auto && sstr, auto && data, auto && nb_component) { if (not data.exists(element.type)) { data.alloc(mesh.getNbElement(element.type), nb_component, element.type, element.ghost_type); } auto & data_array = data(element.type); for (auto c : arange(nb_component)) { sstr >> data_array(element.element, c); } }); }; readers["$ElementNodeData"] = [&](const std::string & /*unused*/) { /* $ElementNodeData numStringTags(ASCII int) stringTag(string) ... numRealTags(ASCII int) realTag(ASCII double) ... numIntegerTags(ASCII int) integerTag(ASCII int) ... elementTag(size_t) value(double) ... ... $EndElementNodeData */ read_data( file.element_tags, [&](auto && id, auto && size [[gnu::unused]], auto && nb_component [[gnu::unused]]) -> ElementTypeMapArray & { file.mesh.template getElementalData(id); auto & data = file.mesh.template getElementalData(id); data.isNodal(true); return data; }, [&](auto && element, auto && sstr, auto && data, auto && nb_component) { int nb_nodes_per_element; sstr >> nb_nodes_per_element; if (not data.exists(element.type)) { data.alloc(mesh.getNbElement(element.type), nb_component * nb_nodes_per_element, element.type, element.ghost_type); } auto & data_array = data(element.type); for (auto c : arange(nb_component)) { sstr >> data_array(element.element, c); } }); }; readers["$PhysicalNames"] = [&](const std::string & /*unused*/) { file.has_physical_names = true; int num_of_phys_names; file.read_line(num_of_phys_names); /// the format line for (auto k [[gnu::unused]] : arange(num_of_phys_names)) { int phys_name_id; int phys_dim; std::string phys_name; file.read_line(phys_dim, phys_name_id, std::quoted(phys_name)); this->physical_names[phys_name_id] = phys_name; } }; readers["Unsupported"] = [&](const std::string & _block) { std::string block = _block.substr(1); AKANTU_DEBUG_WARNING("Unsupported block_kind " << block << " at line " << file.current_line); auto && end_block = "$End" + block; while (file.line != end_block) { file.get_line(); } }; while (file.good()) { std::string block; file.read_line(block); auto && it = readers.find(block); if (it != readers.end()) { it->second(block); std::string end_block; file.read_line(end_block); block = block.substr(1); if (end_block != "$End" + block) { AKANTU_EXCEPTION("The reader failed to properly read the block " << block << ". Expected a $End" << block << " at line " << file.current_line); } } else if (not block.empty()) { readers["Unsupported"](block); } } // mesh.updateTypesOffsets(_not_ghost); if (file.version < 4000) { this->constructPhysicalNames("tag_0", mesh); if (file.has_physical_names) { mesh.createGroupsFromMeshData("physical_names"); } } MeshUtils::fillElementToSubElementsData(mesh); } /* -------------------------------------------------------------------------- */ void MeshIOMSH::write(const std::string & filename, const Mesh & mesh) { std::ofstream outfile; const Array & nodes = mesh.getNodes(); outfile.open(filename.c_str()); outfile << "$MeshFormat" << "\n"; outfile << "2.2 0 8" << "\n"; outfile << "$EndMeshFormat" << "\n"; outfile << std::setprecision(std::numeric_limits::digits10); outfile << "$Nodes" << "\n"; outfile << nodes.size() << "\n"; outfile << std::uppercase; for (Int i = 0; i < nodes.size(); ++i) { auto offset = i * nodes.getNbComponent(); outfile << i + 1; for (Int j = 0; j < nodes.getNbComponent(); ++j) { outfile << " " << nodes.data()[offset + j]; } for (Int p = nodes.getNbComponent(); p < 3; ++p) { outfile << " " << 0.; } outfile << "\n"; ; } outfile << std::nouppercase; outfile << "$EndNodes" << "\n"; outfile << "$Elements" << "\n"; Int nb_elements = 0; for (auto && type : mesh.elementTypes(_all_dimensions, _not_ghost, _ek_not_defined)) { const auto & connectivity = mesh.getConnectivity(type, _not_ghost); nb_elements += connectivity.size(); } outfile << nb_elements << "\n"; std::map element_to_msh_element; Idx element_idx = 1; auto element = ElementNull; for (auto && type : mesh.elementTypes(_all_dimensions, _not_ghost, _ek_not_defined)) { const auto & connectivity = mesh.getConnectivity(type, _not_ghost); element.type = type; - Int * tag[2] = {nullptr, nullptr}; + const Int * tag[2] = {nullptr, nullptr}; if (mesh.hasData("tag_0", type, _not_ghost)) { const auto & data_tag_0 = mesh.getData("tag_0", type, _not_ghost); tag[0] = data_tag_0.data(); } if (mesh.hasData("tag_1", type, _not_ghost)) { const auto & data_tag_1 = mesh.getData("tag_1", type, _not_ghost); tag[1] = data_tag_1.data(); } for (auto && data : enumerate(make_view(connectivity, connectivity.getNbComponent()))) { element.element = std::get<0>(data); const auto & conn = std::get<1>(data); element_to_msh_element.insert(std::make_pair(element, element_idx)); outfile << element_idx << " " << _akantu_to_msh_element_types[type] << " 2"; /// \todo write the real data in the file for (Int t = 0; t < 2; ++t) { if (tag[t] != nullptr) { outfile << " " << tag[t][element.element]; } else { outfile << " 0"; } } for (auto && c : conn) { outfile << " " << c + 1; } outfile << "\n"; element_idx++; } } outfile << "$EndElements" << "\n"; if (mesh.hasData(MeshDataType::_nodal)) { auto && tags = mesh.getTagNames(); for (auto && tag : tags) { auto type = mesh.getTypeCode(tag, MeshDataType::_nodal); if (type != MeshDataTypeCode::_real) { AKANTU_DEBUG_WARNING( "The field " << tag << " is ignored by the MSH writer, msh files do not support " << type << " data"); continue; } auto && data = mesh.getNodalData(tag); outfile << "$NodeData" << "\n"; outfile << "1" << "\n"; outfile << "\"" << tag << "\"\n"; outfile << "1\n0.0" << "\n"; outfile << "3\n0" << "\n"; outfile << data.getNbComponent() << "\n"; outfile << data.size() << "\n"; for (auto && d : enumerate(make_view(data, data.getNbComponent()))) { outfile << std::get<0>(d) + 1; for (auto && v : std::get<1>(d)) { outfile << " " << v; } outfile << "\n"; } outfile << "$EndNodeData" << "\n"; } } if (mesh.hasData(MeshDataType::_elemental)) { auto && tags = mesh.getTagNames(); for (auto && tag : tags) { auto && data = mesh.getElementalData(tag); auto type = mesh.getTypeCode(tag, MeshDataType::_elemental); if (type != MeshDataTypeCode::_real) { AKANTU_DEBUG_WARNING( "The field " << tag << " is ignored by the MSH writer, msh files do not support " << type << " data"); continue; } if (data.isNodal()) { continue; } auto size = data.size(); if (size == 0) { continue; } auto && nb_components = data.getNbComponents(); auto nb_component = nb_components(*(data.elementTypes().begin())); outfile << "$ElementData" << "\n"; outfile << "1" << "\n"; outfile << "\"" << tag << "\"\n"; outfile << "1\n0.0" << "\n"; outfile << "3\n0" << "\n"; outfile << nb_component << "\n"; outfile << size << "\n"; Element element; for (auto type : data.elementTypes()) { element.type = type; for (auto && _ : enumerate(make_view(data(type), nb_components(type)))) { element.element = std::get<0>(_); outfile << element_to_msh_element[element]; for (auto && v : std::get<1>(_)) { outfile << " " << v; } outfile << "\n"; } } outfile << "$EndElementData" << "\n"; } } outfile.close(); } /* -------------------------------------------------------------------------- */ } // namespace akantu diff --git a/src/mesh/element_group.cc b/src/mesh/element_group.cc index 23b240f18..4a9a309b2 100644 --- a/src/mesh/element_group.cc +++ b/src/mesh/element_group.cc @@ -1,221 +1,205 @@ /** * Copyright (©) 2013-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "element_group.hh" #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_accessor.hh" #include "mesh_utils.hh" #if defined(AKANTU_COHESIVE_ELEMENT) #include "cohesive_element_inserter.hh" #endif #include #include #include /* -------------------------------------------------------------------------- */ #include "dumper_iohelper_paraview.hh" namespace akantu { /* -------------------------------------------------------------------------- */ ElementGroup::ElementGroup(const std::string & group_name, const Mesh & mesh, NodeGroup & node_group, Int dimension, const std::string & id) : mesh(mesh), name(group_name), elements("elements", id), node_group(node_group), dimension(dimension) { AKANTU_DEBUG_IN(); this->registerDumper("paraview_" + group_name, group_name, true); this->addDumpFilteredMesh(mesh, elements, node_group.getNodes(), _all_dimensions); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ ElementGroup::ElementGroup(const ElementGroup & /*other*/) = default; /* -------------------------------------------------------------------------- */ void ElementGroup::clear() { elements.free(); } /* -------------------------------------------------------------------------- */ void ElementGroup::clear(ElementType type, GhostType ghost_type) { if (elements.exists(type, ghost_type)) { elements(type, ghost_type).clear(); } } /* -------------------------------------------------------------------------- */ bool ElementGroup::empty() const { return elements.empty(); } /* -------------------------------------------------------------------------- */ void ElementGroup::append(const ElementGroup & other_group) { AKANTU_DEBUG_IN(); node_group.append(other_group.node_group); /// loop on all element types in all dimensions for (auto ghost_type : ghost_types) { for (auto type : other_group.elementTypes(_ghost_type = ghost_type, _element_kind = _ek_not_defined)) { const auto & other_elem_list = other_group.elements(type, ghost_type); auto nb_other_elem = other_elem_list.size(); auto & elem_list = elements.alloc(0, 1, type, ghost_type); auto nb_elem = elem_list.size(); /// append new elements to current list elem_list.resize(nb_elem + nb_other_elem); std::copy(other_elem_list.begin(), other_elem_list.end(), elem_list.begin() + nb_elem); } } this->optimize(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void ElementGroup::printself(std::ostream & stream, int indent) const { std::string space; for (Int i = 0; i < indent; i++, space += AKANTU_INDENT) { ; } stream << space << "ElementGroup [" << std::endl; stream << space << " + name: " << name << std::endl; stream << space << " + dimension: " << dimension << std::endl; elements.printself(stream, indent + 1); node_group.printself(stream, indent + 1); stream << space << "]" << std::endl; } /* -------------------------------------------------------------------------- */ void ElementGroup::optimize() { // increasing the locality of data when iterating on the element of a group for (auto ghost_type : ghost_types) { for (auto type : elements.elementTypes(_ghost_type = ghost_type)) { auto & els = elements(type, ghost_type); std::sort(els.begin(), els.end()); auto end = std::unique(els.begin(), els.end()); els.resize(end - els.begin()); } } node_group.optimize(); } /* -------------------------------------------------------------------------- */ void ElementGroup::fillFromNodeGroup() { CSR node_to_elem; MeshUtils::buildNode2Elements(this->mesh, node_to_elem, this->dimension); std::set seen; for (const auto & node : this->node_group) { for (const auto & element : node_to_elem.getRow(node)) { if (this->dimension != _all_dimensions && this->dimension != Mesh::getSpatialDimension(element.type)) { continue; } if (seen.find(element) != seen.end()) { continue; } auto nb_nodes_per_element = Mesh::getNbNodesPerElement(element.type); auto conn = mesh.getConnectivity(element); Int count = 0; for (auto n : conn) { count += (this->node_group.getNodes().find(n) != Idx(-1) ? 1 : 0); } if (count == nb_nodes_per_element) { this->add(element); } seen.insert(element); } } this->optimize(); } /* -------------------------------------------------------------------------- */ void ElementGroup::addDimension(Int dimension) { this->dimension = std::max(dimension, this->dimension); } /* -------------------------------------------------------------------------- */ void ElementGroup::onNodesAdded(const Array & /*new_nodes*/, const NewNodesEvent & event [[gnu::unused]]) { #if defined(AKANTU_COHESIVE_ELEMENT) if (aka::is_of_type(event)) { // nodes might have changed in the connectivity node_group.clear(); - const auto & mesh_to_mesh_facet = - mesh.getData("mesh_to_mesh_facet"); - for (auto ghost_type : ghost_types) { for (auto type : elements.elementTypes(_ghost_type = ghost_type)) { auto & els = elements(type, ghost_type); - if (not mesh_to_mesh_facet.exists(type, ghost_type)) { - continue; - } - const auto & mesh_to_mesh_facet_type = - mesh_to_mesh_facet(type, ghost_type); - auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type); auto && conn_it = make_view(mesh.getConnectivity(type, ghost_type), nb_nodes_per_element) .begin(); - auto && mesh_facet_conn_it = - make_view(mesh.getMeshFacets().getConnectivity(type, ghost_type), - nb_nodes_per_element) - .begin(); - for (auto element : els) { - auto && mesh_facet_conn = - mesh_facet_conn_it[mesh_to_mesh_facet_type(element).element]; auto && conn = conn_it[element]; - conn = mesh_facet_conn; for (auto && n : conn) { node_group.add(n, false); } } } } node_group.optimize(); } #endif } /* -------------------------------------------------------------------------- */ } // namespace akantu diff --git a/src/mesh/mesh.cc b/src/mesh/mesh.cc index ee1f67862..cd9cc9390 100644 --- a/src/mesh/mesh.cc +++ b/src/mesh/mesh.cc @@ -1,660 +1,703 @@ /** * Copyright (©) 2010-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "aka_config.hh" /* -------------------------------------------------------------------------- */ #include "element_class.hh" #include "group_manager_inline_impl.hh" #include "mesh.hh" #include "mesh_global_data_updater.hh" #include "mesh_io.hh" #include "mesh_iterators.hh" #include "mesh_utils.hh" /* -------------------------------------------------------------------------- */ #include "communicator.hh" #include "element_synchronizer.hh" #include "facet_synchronizer.hh" #include "mesh_utils_distribution.hh" #include "node_synchronizer.hh" #include "periodic_node_synchronizer.hh" +#if defined(AKANTU_COHESIVE_ELEMENT) +#include "cohesive_element_inserter.hh" +#endif /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ #include "dumper_field.hh" #include "dumper_internal_material_field.hh" /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ Mesh::Mesh(Int spatial_dimension, const ID & id, Communicator & communicator) : GroupManager(*this, id + ":group_manager"), MeshData("mesh_data", id), id(id), connectivities("connectivities", id), ghosts_counters("ghosts_counters", id), spatial_dimension(spatial_dimension), size(Vector::Zero(spatial_dimension)), bbox(spatial_dimension), bbox_local(spatial_dimension), communicator(&communicator) { AKANTU_DEBUG_IN(); size.fill(0.); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ Mesh::Mesh(Int spatial_dimension, Communicator & communicator, const ID & id) : Mesh(spatial_dimension, id, communicator) { AKANTU_DEBUG_IN(); this->nodes = std::make_shared>(0, spatial_dimension, id + ":coordinates"); this->nodes_flags = std::make_shared>(0, 1, NodeFlag::_normal, id + ":nodes_flags"); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ Mesh::Mesh(Int spatial_dimension, const ID & id) : Mesh(spatial_dimension, Communicator::getStaticCommunicator(), id) {} /* -------------------------------------------------------------------------- */ Mesh::Mesh(Int spatial_dimension, const std::shared_ptr> & nodes, const ID & id) : Mesh(spatial_dimension, id, Communicator::getStaticCommunicator()) { // NOLINTBEGIN(cppcoreguidelines-prefer-member-initializer) this->nodes = nodes; this->nb_global_nodes = this->nodes->size(); // NOLINTEND(cppcoreguidelines-prefer-member-initializer) // this->nodes_to_elements.resize(nodes->size()); for (auto & node_set : nodes_to_elements) { node_set = std::make_unique>(); } this->computeBoundingBox(); } /* -------------------------------------------------------------------------- */ Mesh::~Mesh() = default; /* -------------------------------------------------------------------------- */ void Mesh::getBarycenters(Array & barycenter, ElementType type, GhostType ghost_type) const { barycenter.resize(getNbElement(type, ghost_type)); for (auto && data : enumerate(make_view(barycenter, spatial_dimension))) { getBarycenter(Element{type, Idx(std::get<0>(data)), ghost_type}, std::get<1>(data)); } } class FacetGlobalConnectivityAccessor : public DataAccessor { public: FacetGlobalConnectivityAccessor(Mesh & mesh) : global_connectivity("global_connectivity", "facet_connectivity_synchronizer") { global_connectivity.initialize( mesh, _spatial_dimension = _all_dimensions, _with_nb_element = true, _with_nb_nodes_per_element = true, _element_kind = _ek_regular); mesh.getGlobalConnectivity(global_connectivity); } [[nodiscard]] Int getNbData(const Array & elements, const SynchronizationTag & tag) const override { Int size = 0; if (tag == SynchronizationTag::_smmc_facets_conn) { Int nb_nodes = Mesh::getNbNodesPerElementList(elements); size += nb_nodes * sizeof(Idx); } return size; } void packData(CommunicationBuffer & buffer, const Array & elements, const SynchronizationTag & tag) const override { if (tag == SynchronizationTag::_smmc_facets_conn) { for (const auto & element : elements) { const auto & conns = global_connectivity(element.type, element.ghost_type); for (auto n : arange(conns.getNbComponent())) { buffer << conns(element.element, n); } } } } void unpackData(CommunicationBuffer & buffer, const Array & elements, const SynchronizationTag & tag) override { if (tag == SynchronizationTag::_smmc_facets_conn) { for (const auto & element : elements) { auto & conns = global_connectivity(element.type, element.ghost_type); for (auto n : arange(conns.getNbComponent())) { buffer >> conns(element.element, n); } } } } AKANTU_GET_MACRO(GlobalConnectivity, (global_connectivity), decltype(auto)); private: ElementTypeMapArray global_connectivity; }; /* -------------------------------------------------------------------------- */ // const Array & Mesh::getNormals(ElementType element_type, // GhostType ghost_type) { // if (this->hasData("normals", element_type, ghost_type)) { // return this->getData("normals", element_type, ghost_type); // } // auto & normals = getDataPointer("normals", element_type, ghost_type, // spatial_dimension, true); // for (auto && data [[gnu::unused]] : // enumerate(make_view(normals, spatial_dimension))) { // AKANTU_TO_IMPLEMENT(); // } // AKANTU_TO_IMPLEMENT(); // } /* -------------------------------------------------------------------------- */ Mesh & Mesh::initMeshFacets(const ID & id) { AKANTU_DEBUG_IN(); if (mesh_facets) { AKANTU_DEBUG_OUT(); return *mesh_facets; } mesh_facets = std::make_unique(spatial_dimension, this->nodes, getID() + ":" + id); mesh_facets->mesh_parent = this; mesh_facets->is_mesh_facets = true; mesh_facets->nodes_flags = this->nodes_flags; mesh_facets->nodes_global_ids = this->nodes_global_ids; MeshUtils::buildAllFacets(*this, *mesh_facets, 0); if (mesh.isDistributed()) { mesh_facets->is_distributed = true; mesh_facets->element_synchronizer = std::make_unique( *mesh_facets, mesh.getElementSynchronizer()); FacetGlobalConnectivityAccessor data_accessor(*mesh_facets); /// communicate mesh_facets->element_synchronizer->synchronizeOnce( data_accessor, SynchronizationTag::_smmc_facets_conn); /// flip facets MeshUtils::flipFacets(*mesh_facets, data_accessor.getGlobalConnectivity(), _ghost); } /// transfers the the mesh physical names to the mesh facets if (not this->hasData("physical_names")) { AKANTU_DEBUG_OUT(); return *mesh_facets; } auto & mesh_phys_data = this->getData("physical_names"); auto & mesh_to_mesh_facet = this->getData("mesh_to_mesh_facet"); mesh_to_mesh_facet.initialize(*this, _spatial_dimension = spatial_dimension - 1, _with_nb_element = true); auto & phys_data = mesh_facets->getData("physical_names"); phys_data.initialize(*mesh_facets, _spatial_dimension = spatial_dimension - 1, _with_nb_element = true); ElementTypeMapArray barycenters(getID(), "temporary_barycenters"); barycenters.initialize(*mesh_facets, _nb_component = spatial_dimension, _spatial_dimension = spatial_dimension - 1, _with_nb_element = true); for (auto && ghost_type : ghost_types) { for (auto && type : barycenters.elementTypes(spatial_dimension - 1, ghost_type)) { mesh_facets->getBarycenters(barycenters(type, ghost_type), type, ghost_type); } } for_each_element( mesh, [&](auto && element) { Vector barycenter(spatial_dimension); mesh.getBarycenter(element, barycenter); auto norm_barycenter = barycenter.norm(); auto tolerance = Math::getTolerance(); if (norm_barycenter > tolerance) { tolerance *= norm_barycenter; } Vector barycenter_facet(spatial_dimension); auto range = enumerate(make_view( barycenters(element.type, element.ghost_type), spatial_dimension)); #ifndef AKANTU_NDEBUG auto min_dist = std::numeric_limits::max(); #endif // this is a spacial search coded the most inefficient way. auto facet = std::find_if(range.begin(), range.end(), [&](auto && data) { auto norm_distance = barycenter.distance(std::get<1>(data)); #ifndef AKANTU_NDEBUG min_dist = std::min(min_dist, norm_distance); #endif return (norm_distance < tolerance); }); if (facet == range.end()) { AKANTU_DEBUG_INFO("The element " << element << " did not find its associated facet in the " "mesh_facets! Try to decrease math tolerance. " "The closest element was at a distance of " << min_dist); return; } // set physical name auto && facet_element = Element{element.type, std::get<0>(*facet), element.ghost_type}; phys_data(facet_element) = mesh_phys_data(element); mesh_to_mesh_facet(element) = facet_element; }, _spatial_dimension = spatial_dimension - 1); mesh_facets->createGroupsFromMeshData("physical_names"); AKANTU_DEBUG_OUT(); return *mesh_facets; } /* -------------------------------------------------------------------------- */ void Mesh::defineMeshParent(const Mesh & mesh) { AKANTU_DEBUG_IN(); this->mesh_parent = &mesh; this->is_mesh_facets = true; AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void Mesh::read(const std::string & filename, const MeshIOType & mesh_io_type) { AKANTU_DEBUG_ASSERT(not is_distributed, "You cannot read a mesh that is already distributed"); MeshIO::read(filename, *this, mesh_io_type); auto types = this->elementTypes(spatial_dimension, _not_ghost, _ek_not_defined); auto it = types.begin(); auto last = types.end(); if (it == last) { AKANTU_DEBUG_WARNING( "The mesh contained in the file " << filename << " does not seem to be of the good dimension." << " No element of dimension " << spatial_dimension << " were read."); } this->makeReady(); } /* -------------------------------------------------------------------------- */ void Mesh::write(const std::string & filename, const MeshIOType & mesh_io_type) { MeshIO::write(filename, *this, mesh_io_type); } /* -------------------------------------------------------------------------- */ void Mesh::makeReady() { this->nb_global_nodes = this->nodes->size(); this->computeBoundingBox(); this->nodes_flags->resize(nodes->size(), NodeFlag::_normal); this->nodes_to_elements.resize(nodes->size()); for (auto & node_set : nodes_to_elements) { node_set = std::make_unique>(); } } /* -------------------------------------------------------------------------- */ void Mesh::printself(std::ostream & stream, int indent) const { std::string space(indent, AKANTU_INDENT); stream << space << "Mesh [" << std::endl; stream << space << " + id : " << getID() << std::endl; stream << space << " + spatial dimension : " << this->spatial_dimension << std::endl; stream << space << " + nodes [" << std::endl; nodes->printself(stream, indent + 2); stream << space << " + connectivities [" << std::endl; connectivities.printself(stream, indent + 2); stream << space << " ]" << std::endl; GroupManager::printself(stream, indent + 1); stream << space << "]" << std::endl; } /* -------------------------------------------------------------------------- */ void Mesh::computeBoundingBox() { AKANTU_DEBUG_IN(); bbox_local.reset(); for (auto & pos : make_view(*nodes, spatial_dimension)) { // if(!isPureGhostNode(i)) bbox_local += pos; } if (this->is_distributed) { bbox = bbox_local.allSum(*communicator); } else { bbox = bbox_local; } size = bbox.size(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void Mesh::getGlobalConnectivity( ElementTypeMapArray & global_connectivity) { AKANTU_DEBUG_IN(); for (auto && ghost_type : ghost_types) { for (auto type : global_connectivity.elementTypes(_spatial_dimension = _all_dimensions, _element_kind = _ek_not_defined, _ghost_type = ghost_type)) { if (not connectivities.exists(type, ghost_type)) { continue; } auto local_conn_view = make_view(connectivities(type, ghost_type)); auto global_conn_view = make_view(global_connectivity(type, ghost_type)); std::transform(local_conn_view.begin(), local_conn_view.end(), global_conn_view.begin(), [&](Idx l) -> Idx { return this->getNodeGlobalId(l); }); } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ DumperIOHelper & Mesh::getGroupDumper(const std::string & dumper_name, const std::string & group_name) { if (group_name == "all") { return this->getDumper(dumper_name); } return element_groups[group_name]->getDumper(dumper_name); } /* -------------------------------------------------------------------------- */ template ElementTypeMap Mesh::getNbDataPerElem(ElementTypeMapArray & arrays) { ElementTypeMap nb_data_per_elem; for (auto type : arrays.elementTypes(_element_kind = _ek_not_defined)) { auto nb_elements = this->getNbElement(type); auto & array = arrays(type); nb_data_per_elem(type) = array.getNbComponent() * array.size(); nb_data_per_elem(type) /= nb_elements; } return nb_data_per_elem; } /* -------------------------------------------------------------------------- */ template ElementTypeMap Mesh::getNbDataPerElem(ElementTypeMapArray & array); template ElementTypeMap Mesh::getNbDataPerElem(ElementTypeMapArray & array); /* -------------------------------------------------------------------------- */ template std::shared_ptr Mesh::createFieldFromAttachedData(const std::string & field_id, const std::string & group_name, ElementKind element_kind) { std::shared_ptr field; ElementTypeMapArray * internal = nullptr; try { internal = &(this->getData(field_id)); } catch (...) { return nullptr; } auto && nb_data_per_elem = this->getNbDataPerElem(*internal); field = this->createElementalField( *internal, group_name, this->spatial_dimension, element_kind, nb_data_per_elem); return field; } template std::shared_ptr Mesh::createFieldFromAttachedData(const std::string & field_id, const std::string & group_name, ElementKind element_kind); template std::shared_ptr Mesh::createFieldFromAttachedData(const std::string & field_id, const std::string & group_name, ElementKind element_kind); /* -------------------------------------------------------------------------- */ void Mesh::distributeImpl( Communicator & communicator, const std::function & edge_weight_function [[gnu::unused]], const std::function & vertex_weight_function [[gnu::unused]]) { AKANTU_DEBUG_ASSERT(is_distributed == false, "This mesh is already distribute"); this->communicator = &communicator; this->element_synchronizer = std::make_unique( *this, this->getID() + ":element_synchronizer", true); this->node_synchronizer = std::make_unique( *this, this->getID() + ":node_synchronizer", true); auto psize = this->communicator->getNbProc(); if (psize > 1) { #ifdef AKANTU_USE_SCOTCH auto prank = this->communicator->whoAmI(); if (prank == 0) { MeshPartitionScotch partition(*this, spatial_dimension); partition.partitionate(psize, edge_weight_function, vertex_weight_function); MeshUtilsDistribution::distributeMeshCentralized(*this, 0, partition); } else { MeshUtilsDistribution::distributeMeshCentralized(*this, 0); } #else AKANTU_ERROR("Cannot distribute a mesh without a partitioning tool"); #endif } // if (psize > 1) this->is_distributed = true; this->computeBoundingBox(); MeshIsDistributedEvent event(AKANTU_CURRENT_FUNCTION); this->sendEvent(event); } /* -------------------------------------------------------------------------- */ void Mesh::getAssociatedElements(const Array & node_list, Array & elements) { for (const auto & node : node_list) { for (const auto & element : *nodes_to_elements[node]) { elements.push_back(element); } } } /* -------------------------------------------------------------------------- */ void Mesh::getAssociatedElements(const Idx & node, Array & elements) const { for (const auto & element : *nodes_to_elements[node]) { elements.push_back(element); } } /* -------------------------------------------------------------------------- */ void Mesh::fillNodesToElements(Int dimension) { Element element{ElementNull}; auto nb_nodes = nodes->size(); this->nodes_to_elements.resize(nb_nodes); for (Int n = 0; n < nb_nodes; ++n) { if (this->nodes_to_elements[n]) { this->nodes_to_elements[n]->clear(); } else { this->nodes_to_elements[n] = std::make_unique>(); } } for (auto ghost_type : ghost_types) { element.ghost_type = ghost_type; for (const auto & type : elementTypes(dimension, ghost_type, _ek_not_defined)) { element.type = type; auto nb_element = this->getNbElement(type, ghost_type); auto connectivity = connectivities(type, ghost_type); auto conn_it = connectivity.begin(connectivity.getNbComponent()); for (Int el = 0; el < nb_element; ++el, ++conn_it) { element.element = el; const auto & conn = *conn_it; for (auto node : conn) { nodes_to_elements[node]->insert(element); } } } } } /* -------------------------------------------------------------------------- */ std::tuple Mesh::updateGlobalData(NewNodesEvent & nodes_event, NewElementsEvent & elements_event) { if (global_data_updater) { return this->global_data_updater->updateData(nodes_event, elements_event); } return std::make_tuple(nodes_event.getList().size(), elements_event.getList().size()); } /* -------------------------------------------------------------------------- */ void Mesh::registerGlobalDataUpdater( std::unique_ptr && global_data_updater) { this->global_data_updater = std::move(global_data_updater); } /* -------------------------------------------------------------------------- */ void Mesh::eraseElements(const Array & elements) { ElementTypeMap last_element; RemovedElementsEvent event(*this, "new_numbering", AKANTU_CURRENT_FUNCTION); auto & remove_list = event.getList(); auto & new_numbering = event.getNewNumbering(); for (auto && el : elements) { if (el.ghost_type != _not_ghost) { auto & count = ghosts_counters(el); --count; AKANTU_DEBUG_ASSERT(count >= 0, "Something went wrong in the ghost element counter"); if (count > 0) { continue; } } remove_list.push_back(el); if (not new_numbering.exists(el.type, el.ghost_type)) { auto nb_element = mesh.getNbElement(el.type, el.ghost_type); auto & numbering = new_numbering.alloc(nb_element, 1, el.type, el.ghost_type); for (auto && [i, numb] : enumerate(numbering)) { numb = i; } } new_numbering(el) = -1; } auto find_last_not_deleted = [](auto && array, Int start) -> Int { do { --start; } while (start >= 0 and array[start] == -1); return start; }; auto find_first_deleted = [](auto && array, Int start) -> Int { auto begin = array.begin(); auto it = std::find_if(begin + start, array.end(), [](auto & el) { return el == -1; }); return Int(it - begin); }; for (auto ghost_type : ghost_types) { for (auto type : new_numbering.elementTypes(_ghost_type = ghost_type)) { auto & numbering = new_numbering(type, ghost_type); auto last_not_delete = find_last_not_deleted(numbering, numbering.size()); if (last_not_delete < 0) { continue; } auto pos = find_first_deleted(numbering, 0); while (pos < last_not_delete) { std::swap(numbering[pos], numbering[last_not_delete]); last_not_delete = find_last_not_deleted(numbering, last_not_delete); pos = find_first_deleted(numbering, pos + 1); } } } this->ghosts_counters.onElementsRemoved(new_numbering); this->sendEvent(event); } +/* -------------------------------------------------------------------------- */ +template <> inline void Mesh::sendEvent(NewNodesEvent & event) { + this->computeBoundingBox(); + this->nodes_flags->resize(this->nodes->size(), NodeFlag::_normal); + +#if defined(AKANTU_COHESIVE_ELEMENT) + if (aka::is_of_type(event)) { + // nodes might have changed in the connectivity + const auto & mesh_to_mesh_facet = + this->getData("mesh_to_mesh_facet"); + + for (auto ghost_type : ghost_types) { + for (auto type : connectivities.elementTypes(_spatial_dimension = + spatial_dimension - 1, + _ghost_type = ghost_type)) { + auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type); + if (not mesh_to_mesh_facet.exists(type, ghost_type)) { + continue; + } + + const auto & mesh_to_mesh_facet_type = + mesh_to_mesh_facet(type, ghost_type); + auto && mesh_facet_conn_it = + make_view(mesh_facets->connectivities(type, ghost_type), + nb_nodes_per_element) + .begin(); + + for (auto && [el, conn] : enumerate(make_view( + connectivities(type, ghost_type), nb_nodes_per_element))) { + conn = mesh_facet_conn_it[mesh_to_mesh_facet_type(el).element]; + } + } + } + } +#endif + + GroupManager::onNodesAdded(event.getList(), event); + EventHandlerManager::sendEvent(event); +} + } // namespace akantu diff --git a/src/mesh/mesh.hh b/src/mesh/mesh.hh index 7adb1efa1..0acf14980 100644 --- a/src/mesh/mesh.hh +++ b/src/mesh/mesh.hh @@ -1,712 +1,712 @@ /** * Copyright (©) 2010-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #ifndef AKANTU_MESH_HH_ #define AKANTU_MESH_HH_ /* -------------------------------------------------------------------------- */ #include "aka_array.hh" #include "aka_bbox.hh" #include "aka_event_handler_manager.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 #include #include /* -------------------------------------------------------------------------- */ 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()) { Int nb_element = mesh.getNbElement(type); const auto & conn = mesh.getConnectivity(type); for(Int e = 0; e < nb_element; ++e) { ... } } or for_each_element(mesh, [](Element & element) { std::cout << element << std::endl }); @endcode */ class Mesh : public EventHandlerManager, public GroupManager, public MeshData, public Dumpable { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ private: /// default constructor used for chaining, the last parameter is just to /// differentiate constructors Mesh(Int spatial_dimension, const ID & id, Communicator & communicator); public: /// constructor that create nodes coordinates array Mesh(Int spatial_dimension, const ID & id = "mesh"); /// mesh not distributed and not using the default communicator Mesh(Int spatial_dimension, Communicator & communicator, const ID & id = "mesh"); /** * constructor that use an existing nodes coordinates * array, by getting the vector of coordinates */ Mesh(Int spatial_dimension, const std::shared_ptr> & nodes, const ID & id = "mesh"); ~Mesh() override; Mesh(const Mesh &) = delete; Mesh(Mesh &&) = delete; Mesh & operator=(const Mesh &) = delete; Mesh & operator=(Mesh &&) = delete; /// 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 & edge_weight_function, const std::function & vertex_weight_function); public: /// with the arguments to pass to the partitionner template std::enable_if_t::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 & list_1, const Array & list_2); /// Removes the face that the mesh is periodic void wipePeriodicInfo(); inline void addPeriodicSlave(Idx slave, Idx master); template void synchronizePeriodicSlaveDataWithMaster(Array & 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 Idx getPeriodicMaster(Idx slave) const; /// get an iterable list of slaves for a given master node inline decltype(auto) getPeriodicSlaves(Idx 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 > * = nullptr> inline void extractNodalValuesFromElement( const Array & nodal_values, Eigen::MatrixBase & elemental_values, const Eigen::MatrixBase & connectivity) const; /// extract coordinates of nodes from an element template inline decltype(auto) extractNodalValuesFromElement(const Array & nodal_values, const Element & element) const; /// add a Array of connectivity for the given ElementType and GhostType . inline void addConnectivityType(ElementType type, GhostType ghost_type = _not_ghost); /* ------------------------------------------------------------------------ */ template inline void sendEvent(Event & event) { // if(event.getList().size() != 0) EventHandlerManager::sendEvent(event); } /// prepare the event to remove the elements listed void eraseElements(const Array & elements); /* ------------------------------------------------------------------------ */ template inline void removeNodesFromArray(Array & vect, const Array & new_numbering); /// 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 & global_connectivity); public: void getAssociatedElements(const Array & node_list, Array & elements); void getAssociatedElements(const Idx & node, Array & elements) const; inline decltype(auto) getAssociatedElements(const Idx & node) const; public: /// fills the nodes_to_elements for given dimension elements void fillNodesToElements(Int dimension = _all_dimensions); private: /// update the global ids, nodes type, ... std::tuple updateGlobalData(NewNodesEvent & nodes_event, NewElementsEvent & elements_event); void registerGlobalDataUpdater( std::unique_ptr && global_data_updater); /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: /// get the id of the mesh 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, Int); /// get the nodes Array aka coordinates AKANTU_GET_MACRO(Nodes, *nodes, const Array &); AKANTU_GET_MACRO_NOT_CONST(Nodes, *nodes, Array &); /// get the number of nodes auto getNbNodes() const { return nodes->size(); } /// get the Array of global ids of the nodes (only used in parallel) AKANTU_GET_MACRO_AUTO(GlobalNodesIds, *nodes_global_ids); // AKANTU_GET_MACRO_NOT_CONST(GlobalNodesIds, *nodes_global_ids, Array // &); /// get the global id of a node inline auto getNodeGlobalId(Idx local_id) const; /// get the global id of a node inline auto getNodeLocalId(Idx global_id) const; /// get the global number of nodes inline auto getNbGlobalNodes() const; /// get the nodes type Array AKANTU_GET_MACRO(NodesFlags, *nodes_flags, const Array &); protected: AKANTU_GET_MACRO_NOT_CONST(NodesFlags, *nodes_flags, Array &); public: inline NodeFlag getNodeFlag(Idx local_id) const; inline auto getNodePrank(Idx local_id) const; /// say if a node is a pure ghost node inline bool isPureGhostNode(Idx n) const; /// say if a node is pur local or master node inline bool isLocalOrMasterNode(Idx n) const; inline bool isLocalNode(Idx n) const; inline bool isMasterNode(Idx n) const; inline bool isSlaveNode(Idx n) const; inline bool isPeriodicSlave(Idx n) const; inline bool isPeriodicMaster(Idx n) const; const Vector & getLowerBounds() const { return bbox.getLowerBounds(); } const Vector & getUpperBounds() const { return bbox.getUpperBounds(); } AKANTU_GET_MACRO(BBox, bbox, const BBox &); const Vector & getLocalLowerBounds() const { return bbox_local.getLowerBounds(); } const Vector & 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, Idx); AKANTU_GET_MACRO_BY_ELEMENT_TYPE(Connectivity, connectivities, Idx); AKANTU_GET_MACRO(Connectivities, connectivities, const ElementTypeMapArray &); /// get the number of element of a type in the mesh inline auto getNbElement(ElementType type, GhostType ghost_type = _not_ghost) const; /// get the number of element for a given ghost_type and a given dimension inline auto getNbElement(Int spatial_dimension = _all_dimensions, GhostType ghost_type = _not_ghost, ElementKind kind = _ek_not_defined) const; /// compute the barycenter of a given element template > * = nullptr> inline void getBarycenter(const Element & element, const Eigen::MatrixBase & barycenter) const; inline Vector getBarycenter(const Element & element) const; void getBarycenters(Array & barycenter, ElementType type, GhostType ghost_type) const; /// get the element connected to a subelement (element of lower dimension) decltype(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 decltype(auto) getElementToSubelement(const Element & element) const; /// get the subelement (element of lower dimension) connected to a element decltype(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 decltype(auto) getSubelementToElement(const Element & element) const; /// get connectivity of a given element inline decltype(auto) getConnectivity(const Element & element) const; inline decltype(auto) 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 decltype(auto) getSubelementToElementNC(const Element & element) const; + inline decltype(auto) 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 decltype(auto) getConnectivityNC(const Element & element); public: /// get a name field associated to the mesh template inline decltype(auto) getData(const ID & data_name, ElementType el_type, GhostType ghost_type = _not_ghost) const; /// get a name field associated to the mesh template inline decltype(auto) getData(const ID & data_name, ElementType el_type, GhostType ghost_type = _not_ghost); /// get a name field associated to the mesh template inline decltype(auto) getData(const ID & data_name) const; /// get a name field associated to the mesh template inline decltype(auto) getData(const ID & data_name); template auto getNbDataPerElem(ElementTypeMapArray & array) -> ElementTypeMap; template std::shared_ptr 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 inline decltype(auto) getDataPointer(const std::string & data_name, ElementType el_type, GhostType ghost_type = _not_ghost, Int nb_component = 1, bool size_to_nb_element = true, bool resize_with_parent = false); template inline decltype(auto) getDataPointer(const ID & data_name, ElementType el_type, GhostType ghost_type, Int nb_component, bool size_to_nb_element, bool resize_with_parent, const T & defaul_); /// Facets mesh accessor inline auto getMeshFacets() const -> const Mesh &; inline auto getMeshFacets() -> Mesh &; inline auto hasMeshFacets() const { return mesh_facets != nullptr; } /// Parent mesh accessor inline auto getMeshParent() const -> const Mesh &; inline auto isMeshFacets() const { return this->is_mesh_facets; } /// return the dumper from a group and and a dumper name auto getGroupDumper(const std::string & dumper_name, const std::string & group_name) -> DumperIOHelper &; /* ------------------------------------------------------------------------ */ /* Wrappers on ElementClass functions */ /* ------------------------------------------------------------------------ */ public: /// get the number of nodes per element for a given element type static inline constexpr auto getNbNodesPerElement(ElementType type) -> Int; /// get the number of nodes per element for a given element type considered as /// a first order element static inline constexpr auto getP1ElementType(ElementType type) -> ElementType; /// get the kind of the element type static inline constexpr auto getKind(ElementType type) -> ElementKind; /// get spatial dimension of a type of element static inline constexpr auto getSpatialDimension(ElementType type) -> Int; /// get the natural space dimension of a type of element static inline constexpr auto getNaturalSpaceDimension(ElementType type) -> Int; /// get number of facets of a given element type static inline constexpr auto getNbFacetsPerElement(ElementType type) -> Int; /// get number of facets of a given element type static inline constexpr auto getNbFacetsPerElement(ElementType type, Idx t) -> Int; /// get local connectivity of a facet for a given facet type static inline decltype(auto) getFacetLocalConnectivity(ElementType type, Idx t = 0); /// get connectivity of facets for a given element inline auto getFacetConnectivity(const Element & element, Idx t = 0) const -> Matrix; /// get the number of type of the surface element associated to a given /// element type static inline constexpr auto getNbFacetTypes(ElementType type, Idx t = 0) -> Int; /// get the type of the surface element associated to a given element static inline constexpr auto getFacetType(ElementType type, Idx t = 0) -> ElementType; /// get all the type of the surface element associated to a given element static inline decltype(auto) getAllFacetTypes(ElementType type); /// get the number of nodes in the given element list static inline auto getNbNodesPerElementList(const Array & elements) -> Int; /* ------------------------------------------------------------------------ */ /* Element type Iterator */ /* ------------------------------------------------------------------------ */ using ElementTypesIteratorHelper = ElementTypeMapArray::ElementTypesIteratorHelper; template auto elementTypes(pack &&... _pack) const -> ElementTypesIteratorHelper; AKANTU_GET_MACRO_DEREF_PTR(ElementSynchronizer, element_synchronizer); AKANTU_GET_MACRO_DEREF_PTR_NOT_CONST(ElementSynchronizer, element_synchronizer); AKANTU_GET_MACRO_DEREF_PTR(NodeSynchronizer, node_synchronizer); AKANTU_GET_MACRO_DEREF_PTR_NOT_CONST(NodeSynchronizer, node_synchronizer); AKANTU_GET_MACRO_DEREF_PTR(PeriodicNodeSynchronizer, periodic_node_synchronizer); AKANTU_GET_MACRO_DEREF_PTR_NOT_CONST(PeriodicNodeSynchronizer, periodic_node_synchronizer); // AKANTU_GET_MACRO_NOT_CONST(Communicator, *communicator, StaticCommunicator // &); AKANTU_GET_MACRO_DEREF_PTR(Communicator, communicator); AKANTU_GET_MACRO_DEREF_PTR_NOT_CONST(Communicator, communicator); AKANTU_GET_MACRO_AUTO(PeriodicMasterSlaves, periodic_master_slave); /* ------------------------------------------------------------------------ */ /* Private methods for friends */ /* ------------------------------------------------------------------------ */ private: friend class MeshAccessor; friend class MeshUtils; AKANTU_GET_MACRO_DEREF_PTR_NOT_CONST(NodesPointer, nodes); /// get a pointer to the nodes_global_ids Array and create it if /// necessary inline auto getNodesGlobalIdsPointer() -> Array &; /// get a pointer to the nodes_type Array and create it if necessary inline auto getNodesFlagsPointer() -> Array &; /// get a pointer to the connectivity Array for the given type and create it /// if necessary inline auto getConnectivityPointer(ElementType type, GhostType ghost_type = _not_ghost) -> Array &; /// get the ghost element counter inline auto getGhostsCounters(ElementType type, GhostType ghost_type = _ghost) -> Array & { 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 decltype(auto) 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 decltype(auto) getSubelementToElementPointer(ElementType type, GhostType ghost_type = _not_ghost); /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ private: ID id; /// array of the nodes coordinates std::shared_ptr> nodes; /// global node ids std::shared_ptr> nodes_global_ids; /// node flags (shared/periodic/...) std::shared_ptr> nodes_flags; /// processor handling the node when not local or master std::unordered_map nodes_prank; /// global number of nodes; Int nb_global_nodes{0}; /// all class of elements present in this mesh (for heterogenous meshes) ElementTypeMapArray connectivities; /// count the references on ghost elements ElementTypeMapArray ghosts_counters; /// the spatial dimension of this mesh Idx spatial_dimension{0}; /// size covered by the mesh on each direction Vector 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_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 element_synchronizer; /// Node synchronizer std::unique_ptr node_synchronizer; /// Node synchronizer for periodic nodes std::unique_ptr periodic_node_synchronizer; using NodesToElements = std::vector>>; /// class to update global data using external knowledge std::unique_ptr global_data_updater; /// This info is stored to simplify the dynamic changes NodesToElements nodes_to_elements; /// periodicity local info std::unordered_map periodic_slave_master; std::unordered_multimap periodic_master_slave; }; /// standard output stream operator inline std::ostream & operator<<(std::ostream & stream, const Mesh & _this) { _this.printself(stream); return stream; } /* -------------------------------------------------------------------------- */ inline constexpr auto Mesh::getNbNodesPerElement(ElementType type) -> Int { return tuple_dispatch_with_default( [](auto && enum_type) { constexpr ElementType type = aka::decay_v; return ElementClass::getNbNodesPerElement(); }, type, [](auto && /*type*/) { return 0; }); } /* -------------------------------------------------------------------------- */ inline auto Mesh::getNbElement(ElementType type, GhostType ghost_type) const { try { const auto & conn = connectivities(type, ghost_type); return conn.size(); } catch (...) { return 0; } } /* -------------------------------------------------------------------------- */ inline auto Mesh::getNbElement(const Int spatial_dimension, GhostType ghost_type, ElementKind kind) const { AKANTU_DEBUG_ASSERT(spatial_dimension <= 3 || spatial_dimension == Int(-1), "spatial_dimension is " << spatial_dimension << " and is greater than 3 !"); Int nb_element = 0; for (auto type : elementTypes(spatial_dimension, ghost_type, kind)) { nb_element += getNbElement(type, ghost_type); } return nb_element; } /* -------------------------------------------------------------------------- */ } // 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 3f1ca8a2c..a590eb3fa 100644 --- a/src/mesh/mesh_accessor.hh +++ b/src/mesh/mesh_accessor.hh @@ -1,209 +1,207 @@ /** * Copyright (©) 2015-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "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(Int 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 and create it if /// necessary inline auto & getNodesGlobalIds() { return this->_mesh.getNodesGlobalIdsPointer(); } /// get a pointer to the nodes_type Array and create it if necessary inline auto & getNodesFlags() { return this->_mesh.getNodesFlags(); } /// get a pointer to the nodes_type Array 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(Int new_size, ElementType type, GhostType ghost_type = _not_ghost) { this->getConnectivity(type, ghost_type).resize(new_size, Idx(-1)); } /// resize the nodes (use carefully) - inline void resizeNodes(Int new_size) { - this->getNodes().resize(new_size); - } + inline void resizeNodes(Int new_size) { this->getNodes().resize(new_size); } /// 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) { return this->_mesh.getElementToSubelementPointer(type, ghost_type); } inline decltype(auto) getElementToSubelementNC(ElementType type, 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) { return this->_mesh.getSubelementToElementPointer(type, ghost_type); } inline decltype(auto) getSubelementToElementNC(ElementType type, 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 - inline auto & - getData(const std::string & data_name, ElementType el_type, - GhostType ghost_type = _not_ghost, Int 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, Int nb_component = 1, + bool size_to_nb_element = true, + bool resize_with_parent = false) { return this->_mesh.getDataPointer(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 && global_data_updater) { this->_mesh.registerGlobalDataUpdater( std::forward>( global_data_updater)); } /* ------------------------------------------------------------------------ */ void makeReady() { this->_mesh.makeReady(); } /* ------------------------------------------------------------------------ */ void addPeriodicSlave(Idx slave, Idx master) { this->_mesh.addPeriodicSlave(slave, master); } void markMeshPeriodic() { for (Idx 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_inline_impl.hh b/src/mesh/mesh_inline_impl.hh index 83c918fbc..eccac4722 100644 --- a/src/mesh/mesh_inline_impl.hh +++ b/src/mesh/mesh_inline_impl.hh @@ -1,728 +1,719 @@ /** * Copyright (©) 2010-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "aka_iterators.hh" #include "element_class.hh" #include "mesh.hh" /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ inline constexpr auto Mesh::getNbFacetsPerElement(ElementType type) -> Int { return tuple_dispatch( [&](auto && enum_type) -> Int { constexpr ElementType type = std::decay_t::value; return ElementClass::getNbFacetsPerElement(); }, type); } /* -------------------------------------------------------------------------- */ inline constexpr auto Mesh::getNbFacetsPerElement(ElementType type, Idx t) -> Int { return tuple_dispatch( [&](auto && enum_type) { constexpr ElementType type = std::decay_t::value; return ElementClass::getNbFacetsPerElement(t); }, type); } /* -------------------------------------------------------------------------- */ template auto Mesh::elementTypes(pack &&... _pack) const -> ElementTypesIteratorHelper { return connectivities.elementTypes(_pack...); } /* -------------------------------------------------------------------------- */ inline decltype(auto) Mesh::getConnectivity(const Element & element) const { return connectivities.get(element); } /* -------------------------------------------------------------------------- */ inline RemovedNodesEvent::RemovedNodesEvent(const Mesh & mesh, const std::string & origin) : MeshEvent(origin), new_numbering(mesh.getNbNodes(), 1, "new_numbering") {} /* -------------------------------------------------------------------------- */ inline RemovedElementsEvent::RemovedElementsEvent(const Mesh & mesh, const ID & new_numbering_id, const std::string & origin) : MeshEvent(origin), new_numbering(new_numbering_id, mesh.getID()) {} /* -------------------------------------------------------------------------- */ template <> inline void Mesh::sendEvent(NewElementsEvent & event) { this->fillNodesToElements(); EventHandlerManager::sendEvent(event); } -/* -------------------------------------------------------------------------- */ -template <> inline void Mesh::sendEvent(NewNodesEvent & event) { - this->computeBoundingBox(); - this->nodes_flags->resize(this->nodes->size(), NodeFlag::_normal); - GroupManager::onNodesAdded(event.getList(), event); - EventHandlerManager::sendEvent(event); -} - /* -------------------------------------------------------------------------- */ template <> inline void Mesh::sendEvent(RemovedElementsEvent & event) { this->connectivities.onElementsRemoved(event.getNewNumbering()); this->fillNodesToElements(); this->computeBoundingBox(); EventHandlerManager::sendEvent(event); } /* -------------------------------------------------------------------------- */ template <> inline void Mesh::sendEvent(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>> tmp( nodes_to_elements.size()); auto it = nodes_to_elements.begin(); Int new_nb_nodes = 0; for (auto new_i : new_numbering) { if (new_i != Int(-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::sendEvent(event); } /* -------------------------------------------------------------------------- */ template inline void Mesh::removeNodesFromArray(Array & vect, const Array & new_numbering) { Array tmp(vect.size(), vect.getNbComponent()); auto nb_component = vect.getNbComponent(); auto new_nb_nodes = 0; for (Int i = 0; i < new_numbering.size(); ++i) { auto new_i = new_numbering(i); if (new_i != Int(-1)) { T * to_copy = vect.data() + i * nb_component; std::uninitialized_copy(to_copy, to_copy + nb_component, tmp.data() + new_i * nb_component); ++new_nb_nodes; } } tmp.resize(new_nb_nodes); vect.copy(tmp); } /* -------------------------------------------------------------------------- */ inline auto Mesh::getNodesGlobalIdsPointer() -> Array & { AKANTU_DEBUG_IN(); if (not nodes_global_ids) { nodes_global_ids = std::make_shared>( 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; } /* -------------------------------------------------------------------------- */ template inline decltype(auto) Mesh::getDataPointer(const ID & data_name, ElementType el_type, GhostType ghost_type, Int nb_component, bool size_to_nb_element, bool resize_with_parent) { Array & tmp = this->getElementalDataArrayAlloc( 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 inline decltype(auto) Mesh::getDataPointer(const ID & data_name, ElementType el_type, GhostType ghost_type, Int nb_component, bool size_to_nb_element, bool resize_with_parent, const T & defaul_) { Array & tmp = this->getElementalDataArrayAlloc( 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 inline decltype(auto) Mesh::getData(const ID & data_name, ElementType el_type, GhostType ghost_type) const { return this->getElementalDataArray(data_name, el_type, ghost_type); } /* -------------------------------------------------------------------------- */ template inline decltype(auto) Mesh::getData(const ID & data_name, ElementType el_type, GhostType ghost_type) { return this->getElementalDataArray(data_name, el_type, ghost_type); } /* -------------------------------------------------------------------------- */ template inline decltype(auto) Mesh::getData(const ID & data_name) const { return this->getElementalData(data_name); } /* -------------------------------------------------------------------------- */ template inline decltype(auto) Mesh::getData(const ID & data_name) { return this->getElementalData(data_name); } /* -------------------------------------------------------------------------- */ inline auto Mesh::getConnectivityPointer(ElementType type, GhostType ghost_type) -> Array & { 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 decltype(auto) Mesh::getElementToSubelementPointer(ElementType type, GhostType ghost_type) { return getDataPointer>("element_to_subelement", type, ghost_type, 1, true); } /* -------------------------------------------------------------------------- */ inline decltype(auto) Mesh::getSubelementToElementPointer(ElementType type, GhostType ghost_type) { auto & array = getDataPointer( "subelement_to_element", type, ghost_type, getNbFacetsPerElement(type), false, is_mesh_facets, ElementNull); return array; } /* -------------------------------------------------------------------------- */ inline decltype(auto) Mesh::getElementToSubelement() const { return getData>("element_to_subelement"); } /* -------------------------------------------------------------------------- */ inline auto & Mesh::getElementToSubelementNC() { return getData>("element_to_subelement"); } /* -------------------------------------------------------------------------- */ inline const auto & Mesh::getElementToSubelement(ElementType type, GhostType ghost_type) const { return getData>("element_to_subelement", type, ghost_type); } /* -------------------------------------------------------------------------- */ inline auto & Mesh::getElementToSubelementNC(ElementType type, GhostType ghost_type) { return getData>("element_to_subelement", type, ghost_type); } /* -------------------------------------------------------------------------- */ inline decltype(auto) Mesh::getElementToSubelement(const Element & element) const { return getData>("element_to_subelement")(element, 0); } /* -------------------------------------------------------------------------- */ inline auto & Mesh::getElementToSubelementNC(const Element & element) { return getData>("element_to_subelement")(element, 0); } /* -------------------------------------------------------------------------- */ inline decltype(auto) Mesh::getSubelementToElement() const { return getData("subelement_to_element"); } /* -------------------------------------------------------------------------- */ inline auto & Mesh::getSubelementToElementNC() { return getData("subelement_to_element"); } /* -------------------------------------------------------------------------- */ inline const auto & Mesh::getSubelementToElement(ElementType type, GhostType ghost_type) const { return getData("subelement_to_element", type, ghost_type); } /* -------------------------------------------------------------------------- */ inline auto & Mesh::getSubelementToElementNC(ElementType type, GhostType ghost_type) { return getData("subelement_to_element", type, ghost_type); } /* -------------------------------------------------------------------------- */ inline decltype(auto) Mesh::getSubelementToElement(const Element & element) const { return this->getSubelementToElement().get(element); } /* -------------------------------------------------------------------------- */ -inline decltype(auto) -Mesh::getSubelementToElementNC(const Element & element) const { - return this->getSubelementToElement().get(element); +inline decltype(auto) Mesh::getSubelementToElementNC(const Element & element) { + return this->getSubelementToElementNC().get(element); } /* -------------------------------------------------------------------------- */ template > *> inline void Mesh::getBarycenter(const Element & element, const Eigen::MatrixBase & barycenter_) const { const auto && conn = getConnectivity(element); Matrix local_coord(spatial_dimension, conn.size()); auto node_begin = make_view(*nodes, spatial_dimension).begin(); for (auto && data : enumerate(conn)) { local_coord(std::get<0>(data)) = node_begin[std::get<1>(data)]; } auto & barycenter = const_cast &>(barycenter_); Math::barycenter(local_coord, barycenter); } /* -------------------------------------------------------------------------- */ inline Vector Mesh::getBarycenter(const Element & element) const { Vector tmp(spatial_dimension); getBarycenter(element, tmp); return tmp; } /* -------------------------------------------------------------------------- */ inline constexpr auto Mesh::getKind(ElementType type) -> ElementKind { return tuple_dispatch( [&](auto && enum_type) { constexpr ElementType type = aka::decay_v; return ElementClass::getKind(); }, type); } /* -------------------------------------------------------------------------- */ inline constexpr auto Element::kind() const -> ElementKind { return Mesh::getKind(type); } /* -------------------------------------------------------------------------- */ inline constexpr auto Mesh::getP1ElementType(ElementType type) -> ElementType { return tuple_dispatch_with_default( [&](auto && enum_type) { constexpr ElementType type = aka::decay_v; return ElementClass::getP1ElementType(); }, type, [](auto && /*enum_type*/) { return _not_defined; }); } /* -------------------------------------------------------------------------- */ inline constexpr auto Mesh::getSpatialDimension(ElementType type) -> Int { return tuple_dispatch_with_default( [&](auto && enum_type) { constexpr ElementType type = aka::decay_v; return ElementClass::getSpatialDimension(); }, type, [](auto && /*enum_type*/) { return 0; }); } /* -------------------------------------------------------------------------- */ inline constexpr auto Mesh::getNaturalSpaceDimension(ElementType type) -> Int { return tuple_dispatch_with_default( [&](auto && enum_type) { constexpr ElementType type = aka::decay_v; return ElementClass::getNaturalSpaceDimension(); }, type, [](auto && /*enum_type*/) { return 0; }); } /* -------------------------------------------------------------------------- */ inline constexpr auto Mesh::getNbFacetTypes(ElementType type, Idx /*t*/) -> Int { return tuple_dispatch_with_default( [&](auto && enum_type) { constexpr ElementType type = aka::decay_v; return ElementClass::getNbFacetTypes(); }, type, [](auto && /*enum_type*/) { return 0; }); } /* -------------------------------------------------------------------------- */ inline constexpr auto Mesh::getFacetType(ElementType type, Idx t) -> ElementType { return tuple_dispatch_with_default( [&](auto && enum_type) { constexpr ElementType type = aka::decay_v; return ElementClass::getFacetType(t); }, type, [](auto && /*enum_type*/) { return _not_defined; }); } /* -------------------------------------------------------------------------- */ inline decltype(auto) Mesh::getAllFacetTypes(ElementType type) { return tuple_dispatch( [&](auto && enum_type) { constexpr ElementType type = aka::decay_v; auto && map = ElementClass::getFacetTypes(); return Eigen::Map>( map.data(), map.rows(), map.cols()); }, type); } /* -------------------------------------------------------------------------- */ inline decltype(auto) Mesh::getFacetLocalConnectivity(ElementType type, Idx t) { return tuple_dispatch( [&](auto && enum_type) { constexpr ElementType type = aka::decay_v; return ElementClass::getFacetLocalConnectivityPerElement(t); }, type); } /* -------------------------------------------------------------------------- */ inline auto Mesh::getFacetConnectivity(const Element & element, Idx t) const -> Matrix { auto local_facets = getFacetLocalConnectivity(element.type, t); Matrix facets(local_facets.rows(), local_facets.cols()); const auto & conn = connectivities(element.type, element.ghost_type); for (Int f = 0; f < facets.rows(); ++f) { for (Int n = 0; n < facets.cols(); ++n) { facets(f, n) = conn(element.element, local_facets(f, n)); } } return facets; } /* -------------------------------------------------------------------------- */ inline decltype(auto) Mesh::getConnectivityNC(const Element & element) { return connectivities.get(element); } /* -------------------------------------------------------------------------- */ template > *> inline void Mesh::extractNodalValuesFromElement( const Array & nodal_values, Eigen::MatrixBase & elemental_values, const Eigen::MatrixBase & connectivity) const { static_assert(std::is_convertible::value, "Cannot extract the array to the vector"); AKANTU_DEBUG_ASSERT( nodal_values.getNbComponent() == elemental_values.rows(), "Cannot extract nodal values to a vector of different size"); auto nodal_values_it = make_view(nodal_values, elemental_values.rows()).begin(); for (auto && data : enumerate(connectivity)) { elemental_values(std::get<0>(data)) = nodal_values_it[std::get<1>(data)]; } } /* -------------------------------------------------------------------------- */ template inline decltype(auto) Mesh::extractNodalValuesFromElement(const Array & nodal_values, const Element & element) const { auto && conn = mesh.getConnectivity(element); Matrix elemental_values(nodal_values.getNbComponent(), Mesh::getNbNodesPerElement(element.type)); extractNodalValuesFromElement(nodal_values, elemental_values, conn); return elemental_values; } /* -------------------------------------------------------------------------- */ inline void Mesh::addConnectivityType(ElementType type, GhostType ghost_type) { getConnectivityPointer(type, ghost_type); } /* -------------------------------------------------------------------------- */ inline auto Mesh::isPureGhostNode(Idx n) const -> bool { return ((*nodes_flags)(n)&NodeFlag::_shared_mask) == NodeFlag::_pure_ghost; } /* -------------------------------------------------------------------------- */ inline auto Mesh::isLocalOrMasterNode(Idx n) const -> bool { return ((*nodes_flags)(n)&NodeFlag::_local_master_mask) == NodeFlag::_normal; } /* -------------------------------------------------------------------------- */ inline auto Mesh::isLocalNode(Idx n) const -> bool { return ((*nodes_flags)(n)&NodeFlag::_shared_mask) == NodeFlag::_normal; } /* -------------------------------------------------------------------------- */ inline auto Mesh::isMasterNode(Idx n) const -> bool { return ((*nodes_flags)(n)&NodeFlag::_shared_mask) == NodeFlag::_master; } /* -------------------------------------------------------------------------- */ inline auto Mesh::isSlaveNode(Idx n) const -> bool { return ((*nodes_flags)(n)&NodeFlag::_shared_mask) == NodeFlag::_slave; } /* -------------------------------------------------------------------------- */ inline auto Mesh::isPeriodicSlave(Idx n) const -> bool { return ((*nodes_flags)(n)&NodeFlag::_periodic_mask) == NodeFlag::_periodic_slave; } /* -------------------------------------------------------------------------- */ inline auto Mesh::isPeriodicMaster(Idx n) const -> bool { return ((*nodes_flags)(n)&NodeFlag::_periodic_mask) == NodeFlag::_periodic_master; } /* -------------------------------------------------------------------------- */ inline auto Mesh::getNodeFlag(Idx local_id) const -> NodeFlag { return (*nodes_flags)(local_id); } /* -------------------------------------------------------------------------- */ inline auto Mesh::getNodePrank(Idx local_id) const { auto it = nodes_prank.find(local_id); return it == nodes_prank.end() ? -1 : it->second; } /* -------------------------------------------------------------------------- */ inline auto Mesh::getNodeGlobalId(Idx local_id) const { return nodes_global_ids ? (*nodes_global_ids)(local_id) : local_id; } /* -------------------------------------------------------------------------- */ inline auto Mesh::getNodeLocalId(Idx global_id) const { if (nodes_global_ids == nullptr) { return global_id; } return nodes_global_ids->find(global_id); } /* -------------------------------------------------------------------------- */ inline auto Mesh::getNbGlobalNodes() const { return nodes_global_ids ? nb_global_nodes : nodes->size(); } /* -------------------------------------------------------------------------- */ inline auto Mesh::getNbNodesPerElementList(const Array & elements) -> Int { Int nb_nodes_per_element = 0; Int 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(Idx slave, Idx 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); } /* -------------------------------------------------------------------------- */ auto Mesh::getPeriodicMaster(Idx slave) const -> Idx { return periodic_slave_master.at(slave); } /* -------------------------------------------------------------------------- */ class Mesh::PeriodicSlaves { using internal_iterator = std::unordered_multimap::const_iterator; std::pair pair; public: PeriodicSlaves(const Mesh & mesh, Idx master) : pair(mesh.getPeriodicMasterSlaves().equal_range(master)) {} PeriodicSlaves(const PeriodicSlaves & other) = default; PeriodicSlaves(PeriodicSlaves && other) noexcept = default; auto operator=(const PeriodicSlaves & other) -> PeriodicSlaves & = 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; } 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(Idx master) const { return PeriodicSlaves(*this, master); } /* -------------------------------------------------------------------------- */ inline decltype(auto) Mesh::getConnectivityWithPeriodicity(const Element & element) const { - auto conn = getConnectivity(element); + Vector conn = connectivities.get(element); if (not isPeriodic()) { return conn; } for (auto && node : conn) { if (isPeriodicSlave(node)) { node = getPeriodicMaster(node); } } return conn; } /* -------------------------------------------------------------------------- */ inline decltype(auto) Mesh::getAssociatedElements(const Idx & node) const { return (*nodes_to_elements[node]); } } // namespace akantu diff --git a/src/mesh/node_group.hh b/src/mesh/node_group.hh index 7ec382d8f..39297e312 100644 --- a/src/mesh/node_group.hh +++ b/src/mesh/node_group.hh @@ -1,125 +1,127 @@ /** * Copyright (©) 2010-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "aka_array.hh" #include "aka_common.hh" #include "dumpable.hh" #include "mesh_filter.hh" /* -------------------------------------------------------------------------- */ #ifndef AKANTU_NODE_GROUP_HH_ #define AKANTU_NODE_GROUP_HH_ namespace akantu { class NodeGroup : public Dumpable { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: NodeGroup(const std::string & name, const Mesh & mesh, const std::string & id = "node_group"); ~NodeGroup() override; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ public: - using const_node_iterator = Array::const_scalar_iterator; - /// 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 [[nodiscard]] bool empty() const; /// iterator to the beginning of the node group [[nodiscard]] inline auto begin() const; /// iterator to the end of the node group [[nodiscard]] inline auto end() const; + /// iterator to the beginning of the node group + [[nodiscard]] inline auto cbegin() const; + /// iterator to the end of the node group + [[nodiscard]] inline auto cend() const; /// add a node and give the local position through an iterator inline auto add(Idx node, bool check_for_duplicate = true); /// remove a node inline void remove(Idx node); [[nodiscard]] inline decltype(auto) find(Idx 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 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_AUTO_NOT_CONST(Nodes, node_group); AKANTU_GET_MACRO_AUTO(Nodes, node_group); AKANTU_GET_MACRO_AUTO(Name, name); /// give the number of nodes in the current group [[nodiscard]] inline Idx size() const; // UInt * storage() { return node_group.data(); }; friend class GroupManager; /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ private: /// name of the group std::string name; /// list of nodes in the group Array 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/node_group_inline_impl.hh b/src/mesh/node_group_inline_impl.hh index 7072efae8..c7406a987 100644 --- a/src/mesh/node_group_inline_impl.hh +++ b/src/mesh/node_group_inline_impl.hh @@ -1,87 +1,92 @@ /** * Copyright (©) 2010-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "node_group.hh" namespace akantu { /* -------------------------------------------------------------------------- */ inline auto NodeGroup::begin() const { return node_group.begin(); } /* -------------------------------------------------------------------------- */ inline auto NodeGroup::end() const { return node_group.end(); } +/* -------------------------------------------------------------------------- */ +inline auto NodeGroup::cbegin() const { return node_group.cbegin(); } + +/* -------------------------------------------------------------------------- */ +inline auto NodeGroup::cend() const { return node_group.cend(); } + /* -------------------------------------------------------------------------- */ inline auto NodeGroup::add(Idx node, bool check_for_duplicate) { - const_node_iterator it; if (check_for_duplicate) { - it = std::find(begin(), end(), node); - if (it != node_group.end()) { + auto it = std::find(cbegin(), cend(), node); + if (it != node_group.cend()) { return it; } } node_group.push_back(node); - it = (node_group.end() - 1); + auto it = (node_group.cend() - 1); return it; } /* -------------------------------------------------------------------------- */ inline void NodeGroup::remove(Idx node) { auto it = this->node_group.begin(); auto end = this->node_group.end(); AKANTU_DEBUG_ASSERT(it != end, "The node group is empty!!"); for (; it != node_group.end(); ++it) { if (*it == node) { it = node_group.erase(it); } } AKANTU_DEBUG_ASSERT(it != end, "The node was not found!"); } /* -------------------------------------------------------------------------- */ inline bool NodeGroup::empty() const { return node_group.empty(); } /* -------------------------------------------------------------------------- */ inline Int NodeGroup::size() const { return node_group.size(); } /* -------------------------------------------------------------------------- */ struct FilterFunctor; template void NodeGroup::applyNodeFilter(T & filter) { AKANTU_DEBUG_IN(); AKANTU_DEBUG_ASSERT(T::type == FilterFunctor::_node_filter_functor, "NodeFilter can only apply node filter functor"); auto it = this->node_group.begin(); for (; it != node_group.end(); ++it) { /// filter == true -> keep node if (!filter(*it)) { it = node_group.erase(it); } } AKANTU_DEBUG_OUT(); } } // namespace akantu diff --git a/src/mesh_utils/cohesive_element_inserter_helper.cc b/src/mesh_utils/cohesive_element_inserter_helper.cc index 1752dc77a..43a3bdd75 100644 --- a/src/mesh_utils/cohesive_element_inserter_helper.cc +++ b/src/mesh_utils/cohesive_element_inserter_helper.cc @@ -1,935 +1,938 @@ /** * Copyright (©) 2020-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "cohesive_element_inserter_helper.hh" /* -------------------------------------------------------------------------- */ #include "data_accessor.hh" #include "element_synchronizer.hh" #include "fe_engine.hh" #include "mesh_accessor.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ CohesiveElementInserterHelper::CohesiveElementInserterHelper( Mesh & mesh, const ElementTypeMapArray & facet_insertion) : doubled_nodes(0, 2, "doubled_nodes"), mesh(mesh), mesh_facets(mesh.getMeshFacets()) { auto spatial_dimension = mesh_facets.getSpatialDimension(); for (auto gt : ghost_types) { for (auto type : mesh_facets.elementTypes(_ghost_type = gt)) { nb_new_facets(type, gt) = mesh_facets.getNbElement(type, gt); } } std::array nb_facet_to_insert{0, 0}; // creates a vector of the facets to insert std::vector potential_facets_to_double; for (auto gt_facet : ghost_types) { for (auto type_facet : mesh_facets.elementTypes(spatial_dimension - 1, gt_facet)) { const auto & f_insertion = facet_insertion(type_facet, gt_facet); auto & counter = nb_facet_to_insert[gt_facet == _not_ghost ? 0 : 1]; for (auto && data : enumerate(f_insertion)) { if (std::get<1>(data)) { auto el = std::get<0>(data); potential_facets_to_double.push_back({type_facet, el, gt_facet}); ++counter; } } } } // Defines a global order of insertion of cohesive element to ensure node // doubling happens in the same order, this is necessary for the global node // numbering if (mesh_facets.isDistributed()) { const auto & comm = mesh_facets.getCommunicator(); ElementTypeMapArray global_orderings; global_orderings.initialize(mesh_facets, _spatial_dimension = spatial_dimension - 1, _with_nb_element = true, _default_value = -1); auto starting_index = nb_facet_to_insert[0]; comm.exclusiveScan(starting_index); // define the local numbers for facet to insert for (auto gt_facet : ghost_types) { for (auto type_facet : mesh_facets.elementTypes(spatial_dimension - 1, gt_facet)) { for (auto data : zip(facet_insertion(type_facet, gt_facet), global_orderings(type_facet, gt_facet))) { if (std::get<0>(data)) { std::get<1>(data) = starting_index; ++starting_index; } } } } // retrieves the order number from neighboring processors auto && synchronizer = mesh_facets.getElementSynchronizer(); SimpleElementDataAccessor data_accessor( global_orderings, SynchronizationTag::_ce_insertion_order); synchronizer.synchronizeOnce(data_accessor, SynchronizationTag::_ce_insertion_order); // sort the facets to double based on this global ordering std::sort(potential_facets_to_double.begin(), potential_facets_to_double.end(), [&global_orderings](auto && el1, auto && el2) { return global_orderings(el1) < global_orderings(el2); }); } for (auto d : arange(spatial_dimension)) { facets_to_double_by_dim[d] = std::make_unique>( 0, 2, "facets_to_double_" + std::to_string(d)); } auto & facets_to_double = *facets_to_double_by_dim[spatial_dimension - 1]; MeshAccessor mesh_accessor(mesh_facets); auto & elements_to_subelements = mesh_accessor.getElementToSubelement(); for (auto && facet_to_double : potential_facets_to_double) { auto gt_facet = facet_to_double.ghost_type; auto type_facet = facet_to_double.type; auto & elements_to_facets = elements_to_subelements(type_facet, gt_facet); auto & elements_to_facet = elements_to_facets(facet_to_double.element); if (elements_to_facet[1].type == _not_defined #if defined(AKANTU_COHESIVE_ELEMENT) || elements_to_facet[1].kind() == _ek_cohesive #endif ) { AKANTU_DEBUG_WARNING("attempt to double a facet on the boundary"); continue; } auto new_facet = nb_new_facets(type_facet, gt_facet)++; facets_to_double.push_back(Vector{ {facet_to_double, Element{type_facet, new_facet, gt_facet}}}); /// update facet_to_element vector auto & element_to_update = elements_to_facet[1]; auto el = element_to_update.element; - const auto & facets_to_elements = mesh_facets.getSubelementToElement( + auto & facets_to_elements = mesh_accessor.getSubelementToElement( element_to_update.type, element_to_update.ghost_type); auto facets_to_element = make_view(facets_to_elements, facets_to_elements.getNbComponent()) .begin()[el]; auto facet_to_update = std::find(facets_to_element.begin(), facets_to_element.end(), facet_to_double); AKANTU_DEBUG_ASSERT(facet_to_update != facets_to_element.end(), "Facet not found"); facet_to_update->element = new_facet; /// update elements connected to facet const auto & first_facet_list = elements_to_facet; elements_to_facets.push_back(first_facet_list); /// set new and original facets as boundary facets elements_to_facets(new_facet)[0] = elements_to_facets(new_facet)[1]; elements_to_facets(new_facet)[1] = ElementNull; elements_to_facets(facet_to_double.element)[1] = ElementNull; } } /* -------------------------------------------------------------------------- */ inline auto CohesiveElementInserterHelper::hasElement(const Vector & nodes_element, const Vector & nodes) -> bool { // if one of the nodes of "nodes" is not in "nodes_element" it stops auto it = std::mismatch(nodes.begin(), nodes.end(), nodes_element.begin(), [&](auto && node, auto && /*node2*/) -> bool { auto it = std::find(nodes_element.begin(), nodes_element.end(), node); return (it != nodes_element.end()); }); // true if all "nodes" where found in "nodes_element" return (it.first == nodes.end()); } /* -------------------------------------------------------------------------- */ inline auto CohesiveElementInserterHelper::removeElementsInVector( const std::vector & elem_to_remove, std::vector & elem_list) -> bool { if (elem_list.size() <= elem_to_remove.size()) { return true; } auto el_it = elem_to_remove.begin(); auto el_last = elem_to_remove.end(); std::vector::iterator el_del; UInt deletions = 0; for (; el_it != el_last; ++el_it) { el_del = std::find(elem_list.begin(), elem_list.end(), *el_it); if (el_del != elem_list.end()) { elem_list.erase(el_del); ++deletions; } } AKANTU_DEBUG_ASSERT(deletions == 0 || deletions == elem_to_remove.size(), "Not all elements have been erased"); return deletions == 0; } /* -------------------------------------------------------------------------- */ void CohesiveElementInserterHelper::updateElementalConnectivity( Mesh & mesh, Idx old_node, Idx new_node, const std::vector & element_list, const std::vector * facet_list) { AKANTU_DEBUG_IN(); + MeshAccessor mesh_accessor(mesh); for (const auto & element : element_list) { if (element.type == _not_defined) { continue; } - auto && connectivity = mesh.getConnectivity(element); + auto && connectivity = mesh_accessor.getConnectivity(element); if (element.kind() == _ek_cohesive) { AKANTU_DEBUG_ASSERT( facet_list != nullptr, "Provide a facet list in order to update cohesive elements"); auto && facets = mesh_facets.getSubelementToElement(element); auto facet_nb_nodes = connectivity.size() / 2; /// loop over cohesive element's facets for (const auto & facet : enumerate(facets)) { /// skip facets if not present in the list if (std::find(facet_list->begin(), facet_list->end(), std::get<1>(facet)) == facet_list->end()) { continue; } auto n = std::get<0>(facet); auto begin = connectivity.begin() + static_cast(n * facet_nb_nodes); auto end = begin + facet_nb_nodes; auto it = std::find(begin, end, old_node); AKANTU_DEBUG_ASSERT(it != end, "Node not found in current element"); *it = new_node; } } else { auto it = std::find(connectivity.begin(), connectivity.end(), old_node); AKANTU_DEBUG_ASSERT(it != connectivity.end(), "Node not found in current element"); /// update connectivity *it = new_node; } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void CohesiveElementInserterHelper::updateSubelementToElement(Int dim, bool facet_mode) { auto & facets_to_double = *facets_to_double_by_dim[dim]; auto & facets_to_subfacets = elementsOfDimToElementsOfDim( dim + static_cast(facet_mode), dim); + MeshAccessor mesh_accessor(mesh_facets); + for (auto && data : zip(make_view(facets_to_double, 2), facets_to_subfacets)) { const auto & old_subfacet = std::get<0>(data)[0]; const auto & new_subfacet = std::get<0>(data)[1]; auto & facet_to_subfacets = std::get<1>(data); - MeshAccessor mesh_accessor(mesh_facets); for (auto & facet : facet_to_subfacets) { auto && subfacets = mesh_accessor.getSubelementToElement(facet); auto && subfacet_to_update_it = std::find(subfacets.begin(), subfacets.end(), old_subfacet); AKANTU_DEBUG_ASSERT(subfacet_to_update_it != subfacets.end(), "Subfacet not found"); *subfacet_to_update_it = new_subfacet; } } } /* -------------------------------------------------------------------------- */ void CohesiveElementInserterHelper::updateElementToSubelement(Int dim, bool facet_mode) { auto & facets_to_double = *facets_to_double_by_dim[dim]; auto & facets_to_subfacets = elementsOfDimToElementsOfDim( dim + static_cast(facet_mode), dim); MeshAccessor mesh_accessor(mesh_facets); // resize the arrays mesh_accessor.getElementToSubelement().initialize( mesh_facets, _spatial_dimension = dim, _with_nb_element = true); for (auto && data : zip(make_view(facets_to_double, 2), facets_to_subfacets)) { const auto & new_facet = std::get<0>(data)[1]; mesh_accessor.getElementToSubelement(new_facet) = std::get<1>(data); } } /* -------------------------------------------------------------------------- */ void CohesiveElementInserterHelper::updateQuadraticSegments(Int dim) { AKANTU_DEBUG_IN(); auto spatial_dimension = mesh.getSpatialDimension(); auto & facets_to_double = *facets_to_double_by_dim[dim]; MeshAccessor mesh_accessor(mesh_facets); auto & connectivities = mesh_accessor.getConnectivities(); /// this ones matter only for segments in 3D Array> * element_to_subfacet_double = nullptr; Array> * facet_to_subfacet_double = nullptr; if (dim == spatial_dimension - 2) { element_to_subfacet_double = &elementsOfDimToElementsOfDim(dim + 2, dim); facet_to_subfacet_double = &elementsOfDimToElementsOfDim(dim + 1, dim); } const auto & element_to_subelement = mesh_facets.getElementToSubelement(); std::vector middle_nodes; for (auto && facet_to_double : make_view(facets_to_double, 2)) { auto & old_facet = facet_to_double[0]; if (old_facet.type != _segment_3) { continue; } auto node = connectivities(old_facet, 2); if (not mesh.isPureGhostNode(node)) { middle_nodes.push_back(node); } } auto n = doubled_nodes.size(); doubleNodes(middle_nodes); for (auto && data : enumerate(make_view(facets_to_double, 2))) { auto facet = std::get<0>(data); auto & old_facet = std::get<1>(data)[0]; if (old_facet.type != _segment_3) { continue; } auto old_node = connectivities(old_facet, 2); if (mesh.isPureGhostNode(old_node)) { continue; } auto new_node = doubled_nodes(n, 1); auto & new_facet = std::get<1>(data)[1]; connectivities(new_facet, 2) = new_node; if (dim == spatial_dimension - 2) { updateElementalConnectivity(mesh_facets, old_node, new_node, element_to_subelement(new_facet, 0)); updateElementalConnectivity(mesh, old_node, new_node, (*element_to_subfacet_double)(facet), &(*facet_to_subfacet_double)(facet)); } else { updateElementalConnectivity(mesh, old_node, new_node, element_to_subelement(new_facet, 0)); } ++n; } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ Int CohesiveElementInserterHelper::insertCohesiveElement() { auto nb_new_facets = insertFacetsOnly(); if (nb_new_facets == 0) { return 0; } updateCohesiveData(); return nb_new_facets; } /* -------------------------------------------------------------------------- */ Int CohesiveElementInserterHelper::insertFacetsOnly() { auto spatial_dimension = mesh.getSpatialDimension(); if (facets_to_double_by_dim[spatial_dimension - 1]->empty()) { return 0; } if (spatial_dimension == 1) { doublePointFacet(); } else if (spatial_dimension == 2) { doubleFacets<1>(); findSubfacetToDouble<1>(); doubleSubfacet<2>(); } else if (spatial_dimension == 3) { doubleFacets<2>(); findSubfacetToDouble<2>(); doubleFacets<1>(); findSubfacetToDouble<1>(); doubleSubfacet<3>(); } return facets_to_double_by_dim[spatial_dimension - 1]->size(); } /* -------------------------------------------------------------------------- */ template void CohesiveElementInserterHelper::doubleFacets() { AKANTU_DEBUG_IN(); NewElementsEvent new_facets; auto spatial_dimension = mesh_facets.getSpatialDimension(); auto & facets_to_double = *facets_to_double_by_dim[dim]; MeshAccessor mesh_accessor(mesh_facets); for (auto && facet_to_double : make_view(facets_to_double, 2)) { auto && old_facet = facet_to_double[0]; auto && new_facet = facet_to_double[1]; auto & facets_connectivities = mesh_accessor.getConnectivity(old_facet.type, old_facet.ghost_type); facets_connectivities.resize( nb_new_facets(old_facet.type, old_facet.ghost_type)); auto facets_connectivities_begin = make_view(facets_connectivities, facets_connectivities.getNbComponent()) .begin(); // copy the connectivities auto && new_conn(facets_connectivities_begin[new_facet.element]); auto && old_conn(facets_connectivities_begin[old_facet.element]); new_conn = old_conn; // this will fail if multiple facet types exists for a given element type // \TODO handle multiple sub-facet types auto nb_subfacet_per_facet = Mesh::getNbFacetsPerElement(old_facet.type); auto & subfacets_to_facets = mesh_accessor.getSubelementToElementNC( old_facet.type, old_facet.ghost_type); subfacets_to_facets.resize( nb_new_facets(old_facet.type, old_facet.ghost_type), ElementNull); auto subfacets_to_facets_begin = make_view(subfacets_to_facets, nb_subfacet_per_facet).begin(); // copy the subfacet to facets information auto && old_subfacets_to_facet( subfacets_to_facets_begin[old_facet.element]); auto && new_subfacet_to_facet(subfacets_to_facets_begin[new_facet.element]); new_subfacet_to_facet = old_subfacets_to_facet; for (auto & subfacet : old_subfacets_to_facet) { if (subfacet == ElementNull) { continue; } /// update facet_to_subfacet array mesh_accessor.getElementToSubelement(subfacet).push_back(new_facet); } new_facets.getList().push_back(new_facet); } /// update facet_to_subfacet and _segment_3 facets if any if (dim != spatial_dimension - 1) { updateSubelementToElement(dim, true); updateElementToSubelement(dim, true); updateQuadraticSegments(dim); } else { updateQuadraticSegments(dim); } mesh_facets.sendEvent(new_facets); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void CohesiveElementInserterHelper::findSubfacetToDouble() { AKANTU_DEBUG_IN(); auto spatial_dimension = mesh_facets.getSpatialDimension(); MeshAccessor mesh_accessor(mesh_facets); auto & facets_to_double = *facets_to_double_by_dim[spatial_dimension - 1]; const auto & subfacets_to_facets = mesh_facets.getSubelementToElement(); auto & elements_to_facets = mesh_accessor.getElementToSubelement(); /// loop on every facet for (auto f : arange(2)) { for (auto && facet_to_double : make_view(facets_to_double, 2)) { auto old_facet = facet_to_double[f]; auto new_facet = facet_to_double[1 - f]; const auto & starting_element = elements_to_facets(new_facet, 0)[0]; auto current_facet = old_facet; auto && subfacets_to_facet = subfacets_to_facets.get(old_facet); /// loop on every subfacet for (auto & subfacet : subfacets_to_facet) { if (subfacet == ElementNull) { continue; } if (dim == spatial_dimension - 2) { auto && subsubfacets_to_subfacet = subfacets_to_facets.get(subfacet); /// loop on every subsubfacet for (auto & subsubfacet : subsubfacets_to_subfacet) { if (subsubfacet == ElementNull) { continue; } auto && subsubfacet_connectivity = mesh_facets.getConnectivity(subsubfacet); std::vector element_list; std::vector facet_list; std::vector subfacet_list; /// check if subsubfacet is to be doubled if (!findElementsAroundSubfacet( starting_element, current_facet, subsubfacet_connectivity, element_list, facet_list, &subfacet_list) and !removeElementsInVector(subfacet_list, elements_to_facets(subsubfacet))) { Element new_subsubfacet{ subsubfacet.type, nb_new_facets(subsubfacet.type, subsubfacet.ghost_type)++, subsubfacet.ghost_type}; facets_to_double_by_dim[dim - 1]->push_back( Vector{subsubfacet, new_subsubfacet}); elementsOfDimToElementsOfDim(dim - 1, dim - 1) .push_back(subfacet_list); elementsOfDimToElementsOfDim(dim, dim - 1).push_back(facet_list); elementsOfDimToElementsOfDim(dim + 1, dim - 1) .push_back(element_list); } } } else { std::vector element_list; std::vector facet_list; auto && subfacet_connectivity = mesh_facets.getConnectivity(subfacet); /// check if subfacet is to be doubled if (!findElementsAroundSubfacet(starting_element, current_facet, subfacet_connectivity, element_list, facet_list) and !removeElementsInVector(facet_list, elements_to_facets(subfacet))) { Element new_subfacet{ subfacet.type, nb_new_facets(subfacet.type, subfacet.ghost_type)++, subfacet.ghost_type}; facets_to_double_by_dim[dim - 1]->push_back( Vector{subfacet, new_subfacet}); elementsOfDimToElementsOfDim(dim, dim - 1).push_back(facet_list); elementsOfDimToElementsOfDim(dim + 1, dim - 1) .push_back(element_list); } } } } } } /* -------------------------------------------------------------------------- */ void CohesiveElementInserterHelper::doubleNodes( const std::vector & old_nodes) { auto & position = mesh.getNodes(); auto spatial_dimension = mesh.getSpatialDimension(); auto old_nb_nodes = position.size(); position.reserve(old_nb_nodes + old_nodes.size()); doubled_nodes.reserve(doubled_nodes.size() + old_nodes.size()); auto position_begin = position.begin(spatial_dimension); for (auto && data : enumerate(old_nodes)) { auto n = std::get<0>(data); auto old_node = std::get<1>(data); decltype(old_node) new_node = old_nb_nodes + n; /// store doubled nodes doubled_nodes.push_back(Vector{old_node, new_node}); /// update position auto && coords = position_begin[old_node]; position.push_back(coords); } } /* -------------------------------------------------------------------------- */ bool CohesiveElementInserterHelper::findElementsAroundSubfacet( const Element & starting_element, const Element & end_facet, const Vector & subfacet_connectivity, std::vector & element_list, std::vector & facet_list, std::vector * subfacet_list) { bool facet_matched = false; element_list.push_back(starting_element); std::queue elements_to_check; elements_to_check.push(starting_element); /// keep going as long as there are elements to check while (not elements_to_check.empty()) { /// check current element Element & current_element = elements_to_check.front(); auto && facets_to_element = mesh_facets.getSubelementToElement(current_element); // for every facet of the element for (const auto & current_facet : facets_to_element) { if (current_facet == ElementNull) { continue; } if (current_facet == end_facet) { facet_matched = true; } auto find_facet = std::find(facet_list.begin(), facet_list.end(), current_facet); // facet already listed or subfacet_connectivity is not in the // connectivity of current_facet; if ((find_facet != facet_list.end()) or not hasElement(mesh_facets.getConnectivity(current_facet), subfacet_connectivity)) { continue; } facet_list.push_back(current_facet); if (subfacet_list != nullptr) { auto && subfacets_of_facet = mesh_facets.getSubelementToElement(current_facet); /// check subfacets for (const auto & current_subfacet : subfacets_of_facet) { if (current_subfacet == ElementNull) { continue; } auto find_subfacet = std::find( subfacet_list->begin(), subfacet_list->end(), current_subfacet); if ((find_subfacet != subfacet_list->end()) or not hasElement(mesh_facets.getConnectivity(current_subfacet), subfacet_connectivity)) { continue; } subfacet_list->push_back(current_subfacet); } } /// consider opposing element const auto & elements_to_facet = mesh_facets.getElementToSubelement(current_facet); Idx opposing = 0; if (elements_to_facet[0] == current_element) { opposing = 1; } const auto & opposing_element = elements_to_facet[opposing]; /// skip null elements since they are on a boundary if (opposing_element == ElementNull) { continue; } /// skip this element if already added if (std::find(element_list.begin(), element_list.end(), opposing_element) != element_list.end()) { continue; } /// only regular elements have to be checked if (opposing_element.kind() == _ek_regular) { elements_to_check.push(opposing_element); } element_list.push_back(opposing_element); AKANTU_DEBUG_ASSERT(hasElement(mesh.getConnectivity(opposing_element), subfacet_connectivity), "Subfacet doesn't belong to this element"); } /// erased checked element from the list elements_to_check.pop(); } return facet_matched; } /* -------------------------------------------------------------------------- */ void CohesiveElementInserterHelper::updateCohesiveData() { auto spatial_dimension = mesh.getSpatialDimension(); auto third_dimension = spatial_dimension == 3; MeshAccessor mesh_accessor(mesh); MeshAccessor mesh_facets_accessor(mesh_facets); for (auto ghost_type : ghost_types) { for (auto cohesive_type : mesh.elementTypes(_element_kind = _ek_cohesive)) { auto nb_cohesive_elements = mesh.getNbElement(cohesive_type, ghost_type); nb_new_facets(cohesive_type, ghost_type) = nb_cohesive_elements; mesh_facets_accessor.getSubelementToElement(cohesive_type, ghost_type); } } auto & facets_to_double = *facets_to_double_by_dim[spatial_dimension - 1]; new_elements.reserve(new_elements.size() + facets_to_double.size()); std::array facets; auto & element_to_facet = mesh_facets_accessor.getElementToSubelement(); auto & facets_to_cohesive_elements = mesh_facets_accessor.getSubelementToElement(); for (auto && facet_to_double : make_view(facets_to_double, 2)) { auto & old_facet = facet_to_double[0]; /// (in 3D cohesive elements connectivity is inverted) facets[third_dimension ? 1 : 0] = old_facet; facets[third_dimension ? 0 : 1] = facet_to_double[1]; auto type_cohesive = FEEngine::getCohesiveElementType(old_facet.type); auto & facet_connectivities = mesh_facets.getConnectivity(old_facet.type, old_facet.ghost_type); auto facet_connectivity_it = facet_connectivities.begin(facet_connectivities.getNbComponent()); auto cohesive_element = Element{ type_cohesive, nb_new_facets(type_cohesive, old_facet.ghost_type)++, old_facet.ghost_type}; auto & cohesives_connectivities = mesh_accessor.getConnectivity(type_cohesive, old_facet.ghost_type); Matrix connectivity(facet_connectivities.getNbComponent(), 2); Vector facets_to_cohesive_element; for (auto s : arange(2)) { /// store doubled facets facets_to_cohesive_element[s] = facets[s]; // update connectivities connectivity(s) = facet_connectivity_it[facets[s].element]; /// update element_to_facet vectors element_to_facet(facets[s], 0)[1] = cohesive_element; } cohesives_connectivities.push_back(connectivity); facets_to_cohesive_elements(type_cohesive, old_facet.ghost_type) .push_back(facets_to_cohesive_element); /// add cohesive element to the element event list new_elements.push_back(cohesive_element); } } /* -------------------------------------------------------------------------- */ void CohesiveElementInserterHelper::doublePointFacet() { auto spatial_dimension = mesh.getSpatialDimension(); if (spatial_dimension != 1) { return; } NewElementsEvent new_facets_event; auto & facets_to_double = *facets_to_double_by_dim[spatial_dimension - 1]; const auto & element_to_facet = mesh_facets.getElementToSubelement(); auto & position = mesh.getNodes(); - MeshAccessor mesh_accessor(mesh_facets); + MeshAccessor mesh_facets_accessor(mesh_facets); + MeshAccessor mesh_accessor(mesh); for (auto ghost_type : ghost_types) { for (auto facet_type : nb_new_facets.elementTypes( spatial_dimension - 1, ghost_type, _ek_regular)) { auto nb_new_element = nb_new_facets(facet_type, ghost_type); auto & connectivities = - mesh_accessor.getConnectivity(facet_type, ghost_type); + mesh_facets_accessor.getConnectivity(facet_type, ghost_type); connectivities.resize(nb_new_element); } } position.reserve(position.size() + facets_to_double.size()); for (auto facet_to_double : make_view(facets_to_double, 2)) { auto & old_facet = facet_to_double[0]; auto & new_facet = facet_to_double[1]; auto element = element_to_facet(new_facet)[0]; - auto & facet_connectivities = - mesh_accessor.getConnectivity(old_facet.type, old_facet.ghost_type); + auto & facet_connectivities = mesh_facets_accessor.getConnectivity( + old_facet.type, old_facet.ghost_type); auto old_node = facet_connectivities(old_facet.element); auto new_node = position.size(); /// update position position.push_back(position(old_node)); facet_connectivities(new_facet.element) = new_node; - auto && segment_conectivity = mesh.getConnectivity(element); + auto && segment_conectivity = mesh_accessor.getConnectivity(element); /// update facet connectivity auto it = std::find(segment_conectivity.begin(), segment_conectivity.end(), old_node); *it = new_node; doubled_nodes.push_back(Vector{old_node, new_node}); new_facets_event.getList().push_back(new_facet); } mesh_facets.sendEvent(new_facets_event); } /* -------------------------------------------------------------------------- */ template void CohesiveElementInserterHelper::doubleSubfacet() { if (spatial_dimension == 1) { return; } NewElementsEvent new_facets_event; std::vector nodes_to_double; MeshAccessor mesh_accessor(mesh_facets); auto & connectivities = mesh_accessor.getConnectivities(); auto & facets_to_double = *facets_to_double_by_dim[0]; ElementTypeMap nb_new_elements; for (auto && facet_to_double : make_view(facets_to_double, 2)) { auto && old_element = facet_to_double[0]; nb_new_elements(old_element.type, old_element.ghost_type) = 0; } for (auto && facet_to_double : make_view(facets_to_double, 2)) { auto && old_element = facet_to_double[0]; ++nb_new_elements(old_element.type, old_element.ghost_type); } for (auto ghost_type : ghost_types) { for (auto facet_type : nb_new_elements.elementTypes(0, ghost_type, _ek_regular)) { auto & connectivities = mesh_accessor.getConnectivity(facet_type, ghost_type); connectivities.resize(connectivities.size() + nb_new_elements(facet_type, ghost_type)); } } for (auto && facet_to_double : make_view(facets_to_double, 2)) { auto & old_facet = facet_to_double(0); // auto & new_facet = facet_to_double(1); AKANTU_DEBUG_ASSERT( old_facet.type == _point_1, "Only _point_1 subfacet doubling is supported at the moment"); /// list nodes double nodes_to_double.push_back(connectivities(old_facet)); } auto old_nb_doubled_nodes = doubled_nodes.size(); doubleNodes(nodes_to_double); auto double_nodes_view = make_view(doubled_nodes, 2); for (auto && data : zip(make_view(facets_to_double, 2), range(double_nodes_view.begin() + old_nb_doubled_nodes, double_nodes_view.end()), arange(facets_to_double.size()))) { // auto & old_facet = std::get<0>(data)[0]; auto & new_facet = std::get<0>(data)[1]; new_facets_event.getList().push_back(new_facet); auto & nodes = std::get<1>(data); auto old_node = nodes(0); auto new_node = nodes(1); auto f = std::get<2>(data); /// add new nodes in connectivity connectivities(new_facet) = new_node; updateElementalConnectivity(mesh, old_node, new_node, elementsOfDimToElementsOfDim(2, 0)(f), &elementsOfDimToElementsOfDim(1, 0)(f)); updateElementalConnectivity(mesh_facets, old_node, new_node, elementsOfDimToElementsOfDim(1, 0)(f)); if (spatial_dimension == 3) { updateElementalConnectivity(mesh_facets, old_node, new_node, elementsOfDimToElementsOfDim(0, 0)(f)); } } updateSubelementToElement(0, spatial_dimension == 2); updateElementToSubelement(0, spatial_dimension == 2); mesh_facets.sendEvent(new_facets_event); } } // namespace akantu diff --git a/src/mesh_utils/mesh_utils.cc b/src/mesh_utils/mesh_utils.cc index 2cb606db9..0d7ccaa56 100644 --- a/src/mesh_utils/mesh_utils.cc +++ b/src/mesh_utils/mesh_utils.cc @@ -1,787 +1,787 @@ /** * Copyright (©) 2010-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "mesh_utils.hh" #include "element_synchronizer.hh" #include "fe_engine.hh" #include "mesh_accessor.hh" #include "mesh_iterators.hh" /* -------------------------------------------------------------------------- */ #include #include #include #include /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ void MeshUtils::buildNode2Elements(const Mesh & mesh, CSR & node_to_elem, Int spatial_dimension) { AKANTU_DEBUG_IN(); if (spatial_dimension == _all_dimensions) { spatial_dimension = mesh.getSpatialDimension(); } /// count number of occurrence of each node auto 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) { auto 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) { auto 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 & node_to_elem, ElementType type, GhostType ghost_type) { AKANTU_DEBUG_IN(); auto nb_nodes = mesh.getNbNodes(); auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type); auto nb_elements = mesh.getConnectivity(type, ghost_type).size(); auto * conn_val = mesh.getConnectivity(type, ghost_type).data(); /// array for the node-element list node_to_elem.resizeRows(nb_nodes); node_to_elem.clearRows(); /// count number of occurrence of each node for (Int el = 0; el < nb_elements; ++el) { auto el_offset = el * nb_nodes_per_element; for (Int 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 (Int el = 0; el < nb_elements; ++el) { auto el_offset = el * nb_nodes_per_element; for (Int 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(); auto 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, Int to_dimension) { AKANTU_DEBUG_IN(); auto spatial_dimension = mesh.getSpatialDimension(); buildAllFacets(mesh, mesh_facets, spatial_dimension, to_dimension); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void MeshUtils::buildAllFacets(const Mesh & mesh, Mesh & mesh_facets, Int from_dimension, Int to_dimension) { AKANTU_DEBUG_IN(); to_dimension = std::max(to_dimension, Int(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 (Int 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, Int 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); auto spatial_dimension = mesh.getSpatialDimension(); const Array & mesh_facets_nodes = mesh_facets.getNodes(); const auto mesh_facets_nodes_it = mesh_facets_nodes.begin(spatial_dimension); CSR node_to_elem; buildNode2Elements(mesh, node_to_elem, dimension); Array counter; std::vector 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 && facet_type : facet_types) { 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 facet(nb_nodes_per_facet); for (Int el = 0; el < nb_element; ++el) { current_element.element = el; Matrix facets = mesh.getFacetConnectivity(current_element, ft).transpose(); for (auto & facet : facets) { // facet = facets(f); auto 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 auto nb_element_connected_to_facet = 0; auto 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; Int 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 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 (auto && data : enumerate(connected_elements)) { gt[std::get<0>(data)] = std::get<1>(data).ghost_type; } if ((gt[0] == _not_ghost) xor (gt[1] == _not_ghost)) { Int prank[2]; for (Int 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 auto 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 & local_connectivities, Int nb_local_element, Int nb_ghost_element, ElementType type, Array & old_nodes_numbers) { AKANTU_DEBUG_IN(); auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type); std::map renumbering_map; for (Int 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) { std::copy_n(local_connectivities.data(), nb_local_element * nb_nodes_per_element, local_conn.data()); } auto & ghost_conn = mesh_accessor.getConnectivity(type, _ghost); ghost_conn.resize(nb_ghost_element); if (nb_ghost_element > 0) { std::copy_n(local_connectivities.data() + nb_local_element * nb_nodes_per_element, nb_ghost_element * nb_nodes_per_element, ghost_conn.data()); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void MeshUtils::renumberNodesInConnectivity( Array & list_nodes, Int nb_nodes, std::map & renumbering_map) { AKANTU_DEBUG_IN(); auto * connectivity = list_nodes.data(); auto new_node_num = renumbering_map.size(); for (Int n = 0; n < nb_nodes; ++n, ++connectivity) { auto & node = *connectivity; auto it = renumbering_map.find(node); if (it == renumbering_map.end()) { auto 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 renumbering_map; RemovedNodesEvent remove_nodes(mesh, AKANTU_CURRENT_FUNCTION); auto & nodes_removed = remove_nodes.getList(); for (auto ghost_type : ghost_types) { for (auto type : mesh.elementTypes(_all_dimensions, ghost_type, _ek_not_defined)) { auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type); auto & connectivity = mesh.getConnectivity(type, ghost_type); auto nb_element(connectivity.size()); renumberNodesInConnectivity( connectivity, nb_element * nb_nodes_per_element, renumbering_map); } } auto & new_numbering = remove_nodes.getNewNumbering(); std::fill(new_numbering.begin(), new_numbering.end(), Int(-1)); for (auto && pair : renumbering_map) { new_numbering(std::get<0>(pair)) = std::get<1>(pair); } for (Int i = 0; i < new_numbering.size(); ++i) { if (new_numbering(i) == Int(-1)) { nodes_removed.push_back(i); } } mesh.sendEvent(remove_nodes); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void MeshUtils::flipFacets( Mesh & mesh_facets, const ElementTypeMapArray & remote_global_connectivities, GhostType gt_facet) { AKANTU_DEBUG_IN(); auto spatial_dimension = mesh_facets.getSpatialDimension(); /// get global connectivity for local mesh ElementTypeMapArray local_global_connectivities( "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 Vector 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"); auto 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)); auto start = it - begin; decltype(start) n; /// 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; } Int spatial_dimension = mesh.getSpatialDimension(); ElementTypeMapArray 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; auto nb_element = mesh.getNbElement(type, ghost_type); auto & 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 (Int 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 (const 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 (const 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 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); for (auto && data : enumerate( make_view(connectivity, connectivity.getNbComponent()))) { const auto & facet = std::get<1>(data); facet_element.element = std::get<0>(data); std::map 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 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) { - auto subelements_to_element = - mesh.getSubelementToElement(connected_element); + auto && subelements_to_element = + mesh_accessor.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/boundary_condition/boundary_condition.hh b/src/model/common/boundary_condition/boundary_condition.hh index 3f449393e..69205decd 100644 --- a/src/model/common/boundary_condition/boundary_condition.hh +++ b/src/model/common/boundary_condition/boundary_condition.hh @@ -1,85 +1,85 @@ /** * Copyright (©) 2010-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #ifndef AKANTU_BOUNDARY_CONDITION_HH_ #define AKANTU_BOUNDARY_CONDITION_HH_ #include "aka_common.hh" #include "boundary_condition_functor.hh" #include "fe_engine.hh" #include "mesh.hh" /* -------------------------------------------------------------------------- */ namespace akantu { template class BoundaryCondition { /* ------------------------------------------------------------------------ */ /* Typedefs */ /* ------------------------------------------------------------------------ */ private: /* ------------------------------------------------------------------------ */ /* Constructors / Destructors / Initializers */ /* ------------------------------------------------------------------------ */ public: BoundaryCondition() : model(nullptr) {} /// Initialize the boundary conditions void initBC(ModelType & model, Array & primal, Array & dual); void initBC(ModelType & model, Array & primal, Array & primal_increment, Array & dual); /* ------------------------------------------------------------------------ */ /* Methods and accessors */ /* ------------------------------------------------------------------------ */ public: /// Apply the boundary conditions - template inline void applyBC(const FunctorType & func); + template inline void applyBC(FunctorType && func); template - inline void applyBC(const FunctorType & func, const std::string & group_name); + inline void applyBC(FunctorType && func, const std::string & group_name); template - inline void applyBC(const FunctorType & func, - const ElementGroup & element_group); + inline void applyBC(FunctorType && func, const ElementGroup & element_group); AKANTU_GET_MACRO_NOT_CONST(Model, *model, ModelType &); AKANTU_GET_MACRO_NOT_CONST(Primal, *primal, Array &); AKANTU_GET_MACRO_NOT_CONST(Dual, *dual, Array &); /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ public: - template + template ::type> struct TemplateFunctionWrapper; private: ModelType * model; Array * primal{nullptr}; Array * dual{nullptr}; Array * primal_increment{nullptr}; }; } // namespace akantu #include "boundary_condition_tmpl.hh" #endif /* AKANTU_BOUNDARY_CONDITION_HH_ */ diff --git a/src/model/common/boundary_condition/boundary_condition_functor.hh b/src/model/common/boundary_condition/boundary_condition_functor.hh index fcfdd717e..c207107cc 100644 --- a/src/model/common/boundary_condition/boundary_condition_functor.hh +++ b/src/model/common/boundary_condition/boundary_condition_functor.hh @@ -1,188 +1,206 @@ /** * Copyright (©) 2013-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "fe_engine.hh" #include "integration_point.hh" /* -------------------------------------------------------------------------- */ #ifndef AKANTU_BOUNDARY_CONDITION_FUNCTOR_HH_ #define AKANTU_BOUNDARY_CONDITION_FUNCTOR_HH_ /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ namespace BC { using Axis = ::akantu::SpatialDirection; /* ---------------------------------------------------------------------- */ struct Functor { enum Type { _dirichlet, _neumann }; virtual ~Functor() = default; }; /* ---------------------------------------------------------------------- */ namespace Dirichlet { class DirichletFunctor : public Functor { public: DirichletFunctor() = default; explicit DirichletFunctor(Axis ax) : axis(ax) {} + virtual void operator()(Idx node, VectorProxy & flags, + VectorProxy & primal, + const VectorProxy & coord) { + // Implementation with copies for backward compatibility + Vector flags_ = flags; + Vector primal_ = primal; + Vector coord_ = coord; + + (*this)(node, flags_, primal_, coord_); + + flags = flags_; + primal = primal_; + } + virtual void operator()(Idx /*node*/, Vector & /*flags*/, Vector & /*primal*/, - const Vector & /*coord*/) const { + const Vector & /*coord*/) { AKANTU_TO_IMPLEMENT(); } public: static const Type type = _dirichlet; protected: Axis axis{_x}; }; /* ---------------------------------------------------------------------- */ class FlagOnly : public DirichletFunctor { public: explicit FlagOnly(Axis ax = _x) : DirichletFunctor(ax) {} public: - inline void operator()(Idx node, Vector & flags, - Vector & primal, - const Vector & coord) const override; + inline void operator()(Idx node, VectorProxy & flags, + VectorProxy & primal, + const VectorProxy & coord) override; }; /* ---------------------------------------------------------------------- */ class FixedValue : public DirichletFunctor { public: FixedValue(Real val, Axis ax = _x) : DirichletFunctor(ax), value(val) {} public: - inline void operator()(Idx node, Vector & flags, - Vector & primal, - const Vector & coord) const override; + inline void operator()(Idx node, VectorProxy & flags, + VectorProxy & primal, + const VectorProxy & coord) override; protected: Real value; }; /* ---------------------------------------------------------------------- */ class IncrementValue : public DirichletFunctor { public: IncrementValue(Real val, Axis ax = _x) : DirichletFunctor(ax), value(val) {} public: - inline void operator()(Idx node, Vector & flags, - Vector & primal, - const Vector & coord) const override; + inline void operator()(Idx node, VectorProxy & flags, + VectorProxy & primal, + const VectorProxy & coord) override; inline void setIncrement(Real val) { this->value = val; } protected: Real value; }; /* ---------------------------------------------------------------------- */ class Increment : public DirichletFunctor { public: explicit Increment(const Vector & val) : DirichletFunctor(_x), value(val) {} public: - inline void operator()(Idx node, Vector & flags, - Vector & primal, - const Vector & coord) const override; + inline void operator()(Idx node, VectorProxy & flags, + VectorProxy & primal, + const VectorProxy & coord) override; inline void setIncrement(const Vector & val) { this->value = val; } protected: Vector value; }; } // namespace Dirichlet /* ------------------------------------------------------------------------ */ /* Neumann */ /* ------------------------------------------------------------------------ */ namespace Neumann { class NeumannFunctor : public Functor { protected: NeumannFunctor() = default; public: virtual void operator()(const IntegrationPoint & quad_point, - Vector & dual, const Vector & coord, - const Vector & normals) const = 0; + VectorProxy & dual, + const VectorProxy & coord, + const VectorProxy & normals) = 0; ~NeumannFunctor() override = default; public: static const Type type = _neumann; }; /* ---------------------------------------------------------------------- */ class FromHigherDim : public NeumannFunctor { public: explicit FromHigherDim(const Matrix & mat) : bc_data(mat) {} ~FromHigherDim() override = default; public: inline void operator()(const IntegrationPoint & quad_point, - Vector & dual, const Vector & coord, - const Vector & normals) const override; + VectorProxy & dual, + const VectorProxy & coord, + const VectorProxy & normals) override; protected: Matrix bc_data; }; /* ---------------------------------------------------------------------- */ class FromSameDim : public NeumannFunctor { public: explicit FromSameDim(const Vector & vec) : bc_data(vec) {} ~FromSameDim() override = default; public: inline void operator()(const IntegrationPoint & quad_point, - Vector & dual, const Vector & coord, - const Vector & normals) const override; + VectorProxy & dual, + const VectorProxy & coord, + const VectorProxy & normals) override; protected: Vector bc_data; }; /* ---------------------------------------------------------------------- */ class FreeBoundary : public NeumannFunctor { public: inline void operator()(const IntegrationPoint & quad_point, - Vector & dual, const Vector & coord, - const Vector & normals) const override; + VectorProxy & dual, + const VectorProxy & coord, + const VectorProxy & normals) override; }; } // namespace Neumann } // namespace BC } // namespace akantu #include "boundary_condition_functor_inline_impl.hh" #endif /* AKANTU_BOUNDARY_CONDITION_FUNCTOR_HH_ */ diff --git a/src/model/common/boundary_condition/boundary_condition_functor_inline_impl.hh b/src/model/common/boundary_condition/boundary_condition_functor_inline_impl.hh index 599a0ee6b..12127a030 100644 --- a/src/model/common/boundary_condition/boundary_condition_functor_inline_impl.hh +++ b/src/model/common/boundary_condition/boundary_condition_functor_inline_impl.hh @@ -1,113 +1,118 @@ /** * Copyright (©) 2013-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "boundary_condition_functor.hh" /* -------------------------------------------------------------------------- */ //#ifndef AKANTU_BOUNDARY_CONDITION_FUNCTOR_INLINE_IMPL_HH_ //#define AKANTU_BOUNDARY_CONDITION_FUNCTOR_INLINE_IMPL_HH_ /* -------------------------------------------------------------------------- */ #define DIRICHLET_SANITY_CHECK \ AKANTU_DEBUG_ASSERT( \ primal.size() <= flags.size(), \ "The primal vector and flags vectors given" \ << " to the boundary condition functor have different sizes!"); #define NEUMANN_SANITY_CHECK \ AKANTU_DEBUG_ASSERT( \ coord.size() <= normals.size(), \ "The coordinates and normals vectors given to the" \ << " boundary condition functor have different sizes!"); namespace akantu { namespace BC { /* ---------------------------------------------------------------------- */ namespace Dirichlet { - inline void FlagOnly::operator()(Idx /*node*/, Vector & flags, - [[gnu::unused]] Vector & primal, - const Vector & /*coord*/) const { + inline void + FlagOnly::operator()(Idx /*node*/, VectorProxy & flags, + VectorProxy & /*primal*/, + const VectorProxy & /*coord*/) { DIRICHLET_SANITY_CHECK; flags(this->axis) = true; } /* ---------------------------------------------------------------------- */ - inline void FixedValue::operator()(Idx /*node*/, Vector & flags, - [[gnu::unused]] Vector & primal, - const Vector & /*coord*/) const { + inline void + FixedValue::operator()(Idx /*node*/, VectorProxy & flags, + VectorProxy & primal, + const VectorProxy & /*coord*/) { DIRICHLET_SANITY_CHECK; flags(this->axis) = true; primal(this->axis) = value; } /* ---------------------------------------------------------------------- */ inline void - IncrementValue::operator()(Idx /*node*/, Vector & flags, - [[gnu::unused]] Vector & primal, - const Vector & /*coord*/) const { + IncrementValue::operator()(Idx /*node*/, VectorProxy & flags, + VectorProxy & primal, + const VectorProxy & /*coord*/) { DIRICHLET_SANITY_CHECK; flags(this->axis) = true; primal(this->axis) += value; } /* ---------------------------------------------------------------------- */ - inline void Increment::operator()(Int /*node*/, Vector & flags, - Vector & primal, - const Vector & /*coord*/) const { + inline void + Increment::operator()(Int /*node*/, VectorProxy & flags, + VectorProxy & primal, + const VectorProxy & /*coord*/) { DIRICHLET_SANITY_CHECK; flags.set(true); primal += value; } } // namespace Dirichlet /* ------------------------------------------------------------------------ */ /* Neumann */ /* ------------------------------------------------------------------------ */ namespace Neumann { inline void FreeBoundary::operator()(const IntegrationPoint & /*quad_point*/, - Vector & dual, - const Vector & /*coord*/, - const Vector & /*normals*/) const { + VectorProxy & dual, + const VectorProxy & /*coord*/, + const VectorProxy & /*normals*/) { for (Idx i(0); i < dual.size(); ++i) { dual(i) = 0.0; } } /* ---------------------------------------------------------------------- */ - inline void FromHigherDim::operator()( - const IntegrationPoint & /*quad_point*/, Vector & dual, - const Vector & /*coord*/, const Vector & normals) const { + inline void + FromHigherDim::operator()(const IntegrationPoint & /*quad_point*/, + VectorProxy & dual, + const VectorProxy & /*coord*/, + const VectorProxy & normals) { dual = this->bc_data * normals; } /* ---------------------------------------------------------------------- */ inline void - FromSameDim::operator()(const IntegrationPoint &, Vector & dual, - const Vector & /* coord */, - const Vector & /* normals */) const { + FromSameDim::operator()(const IntegrationPoint &, VectorProxy & dual, + const VectorProxy & /* coord */, + const VectorProxy & /* normals */) { dual = this->bc_data; } } // namespace Neumann } // namespace BC } // namespace akantu //#endif /* AKANTU_BOUNDARY_CONDITION_FUNCTOR_INLINE_IMPL_HH_ */ diff --git a/src/model/common/boundary_condition/boundary_condition_tmpl.hh b/src/model/common/boundary_condition/boundary_condition_tmpl.hh index c067c1ae1..1687f9d9f 100644 --- a/src/model/common/boundary_condition/boundary_condition_tmpl.hh +++ b/src/model/common/boundary_condition/boundary_condition_tmpl.hh @@ -1,231 +1,221 @@ /** * Copyright (©) 2013-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "boundary_condition.hh" #include "element_group.hh" /* -------------------------------------------------------------------------- */ #ifndef AKANTU_BOUNDARY_CONDITION_TMPL_HH_ #define AKANTU_BOUNDARY_CONDITION_TMPL_HH_ namespace akantu { /* -------------------------------------------------------------------------- */ template void BoundaryCondition::initBC(ModelType & model, Array & primal, Array & dual) { this->model = &model; this->primal = &primal; this->dual = &dual; } /* -------------------------------------------------------------------------- */ template void BoundaryCondition::initBC(ModelType & model, Array & primal, Array & primal_increment, Array & dual) { this->initBC(model, primal, dual); this->primal_increment = &primal_increment; } /* -------------------------------------------------------------------------- */ /* Partial specialization for DIRICHLET functors */ template template struct BoundaryCondition::TemplateFunctionWrapper< FunctorType, BC::Functor::_dirichlet> { - static inline void applyBC(const FunctorType & func, - const ElementGroup & group, + static inline void applyBC(FunctorType && func, const ElementGroup & group, BoundaryCondition & bc_instance) { auto & model = bc_instance.getModel(); auto & primal = bc_instance.getPrimal(); - - const auto & coords = model.getMesh().getNodes(); auto & boundary_flags = model.getBlockedDOFs(); - Int dim = model.getMesh().getSpatialDimension(); - auto primal_iter = primal.begin(primal.getNbComponent()); - auto coords_iter = coords.begin(dim); - auto flags_iter = boundary_flags.begin(boundary_flags.getNbComponent()); + FunctorType func_(func); - for (auto n : group.getNodeGroup()) { - auto && flags = flags_iter[n]; - auto && primal = primal_iter[n]; + const auto & coords = model.getMesh().getNodes(); + Int dim = model.getMesh().getSpatialDimension(); + auto it = zip(make_view(primal, dim), make_view(boundary_flags, dim), + make_const_view(coords, dim)) + .begin(); + for (auto && n : group.getNodeGroup()) { + auto && [primal_, flags_, coords_] = it[n]; // The copy it to avoid the user to template is functor - Vector flags_ = flags; - Vector primal_ = primal; - Vector coords = coords_iter[n]; - - func(n, flags_, primal_, coords); - - flags = flags_; - primal = primal_; + // + auto && primal = primal_; + auto && flags = flags_; + auto && coords = coords_; + func_(n, flags, primal, coords); } } }; /* -------------------------------------------------------------------------- */ /* Partial specialization for NEUMANN functors */ template template struct BoundaryCondition::TemplateFunctionWrapper< FunctorType, BC::Functor::_neumann> { - static inline void applyBC(const FunctorType & func, - const ElementGroup & group, + static inline void applyBC(FunctorType && func, const ElementGroup & group, BoundaryCondition & bc_instance) { auto dim = bc_instance.getModel().getSpatialDimension(); if (dim == 1) { AKANTU_TO_IMPLEMENT(); } - applyBC(func, group, bc_instance, _not_ghost); - applyBC(func, group, bc_instance, _ghost); + applyBC(std::forward(func), group, bc_instance, _not_ghost); + applyBC(std::forward(func), group, bc_instance, _ghost); } - static inline void applyBC(const FunctorType & func, - const ElementGroup & group, + static inline void applyBC(FunctorType && func, const ElementGroup & group, BoundaryCondition & bc_instance, GhostType ghost_type) { auto & model = bc_instance.getModel(); auto & dual = bc_instance.getDual(); const auto & mesh = model.getMesh(); const auto & nodes_coords = mesh.getNodes(); const auto & fem_boundary = model.getFEEngineBoundary(); + FunctorType func_(func); + Int dim = model.getSpatialDimension(); Int nb_degree_of_freedom = dual.getNbComponent(); IntegrationPoint quad_point; quad_point.ghost_type = ghost_type; // Loop over the boundary element types for (auto && type : group.elementTypes(dim - 1, ghost_type)) { const auto & element_ids = group.getElements(type, ghost_type); auto nb_quad_points = fem_boundary.getNbIntegrationPoints(type, ghost_type); auto nb_elements = element_ids.size(); auto nb_nodes_per_element = mesh.getNbNodesPerElement(type); Array dual_before_integ(nb_elements * nb_quad_points, nb_degree_of_freedom, 0.); Array quad_coords(nb_elements * nb_quad_points, dim); const auto & normals_on_quad = fem_boundary.getNormalsOnIntegrationPoints(type, ghost_type); fem_boundary.interpolateOnIntegrationPoints( nodes_coords, quad_coords, dim, type, ghost_type, element_ids); - auto normals_begin = normals_on_quad.begin(dim); + auto normals_begin = normals_on_quad.cbegin(dim); decltype(normals_begin) normals_iter; - auto quad_coords_iter = quad_coords.begin(dim); + auto quad_coords_iter = quad_coords.cbegin(dim); auto dual_iter = dual_before_integ.begin(nb_degree_of_freedom); quad_point.type = type; for (auto el : element_ids) { quad_point.element = el; normals_iter = normals_begin + el * nb_quad_points; for (auto q : arange(nb_quad_points)) { quad_point.num_point = q; - Vector dual(*dual_iter); - Vector quad_coords(*quad_coords_iter); - Vector normals(*normals_iter); - - func(quad_point, dual, quad_coords, normals); - - *dual_iter = dual; + func_(quad_point, *dual_iter, *quad_coords_iter, *normals_iter); ++dual_iter; ++quad_coords_iter; ++normals_iter; } } Array dual_by_shapes(nb_elements * nb_quad_points, nb_degree_of_freedom * nb_nodes_per_element); fem_boundary.computeNtb(dual_before_integ, dual_by_shapes, type, ghost_type, element_ids); Array dual_by_shapes_integ(nb_elements, nb_degree_of_freedom * nb_nodes_per_element); fem_boundary.integrate(dual_by_shapes, dual_by_shapes_integ, nb_degree_of_freedom * nb_nodes_per_element, type, ghost_type, element_ids); // assemble the result into force vector model.getDOFManager().assembleElementalArrayLocalArray( dual_by_shapes_integ, dual, type, ghost_type, 1., element_ids); } } }; /* -------------------------------------------------------------------------- */ template template -inline void BoundaryCondition::applyBC(const FunctorType & func) { +inline void BoundaryCondition::applyBC(FunctorType && func) { auto bit = model->getMesh().getGroupManager().element_group_begin(); auto bend = model->getMesh().getGroupManager().element_group_end(); for (; bit != bend; ++bit) { - applyBC(func, *bit); + applyBC(std::forward(func), *bit); } } /* -------------------------------------------------------------------------- */ template template inline void -BoundaryCondition::applyBC(const FunctorType & func, +BoundaryCondition::applyBC(FunctorType && func, const std::string & group_name) { try { const ElementGroup & element_group = model->getMesh().getElementGroup(group_name); - applyBC(func, element_group); + applyBC(std::forward(func), element_group); } catch (akantu::debug::Exception & e) { AKANTU_EXCEPTION("Error applying a boundary condition onto \"" << group_name << "\"! [" << e.what() << "]"); } } /* -------------------------------------------------------------------------- */ template template inline void -BoundaryCondition::applyBC(const FunctorType & func, +BoundaryCondition::applyBC(FunctorType && func, const ElementGroup & element_group) { #if !defined(AKANTU_NDEBUG) if (element_group.getDimension() != model->getSpatialDimension() - 1) { AKANTU_DEBUG_INFO("The group " << element_group.getName() << " does not contain only boundaries elements"); } #endif - TemplateFunctionWrapper::applyBC(func, element_group, *this); + TemplateFunctionWrapper::applyBC( + std::forward(func), element_group, *this); } #endif /* AKANTU_BOUNDARY_CONDITION_TMPL_HH_ */ } // namespace akantu diff --git a/src/model/common/dof_manager/dof_manager.cc b/src/model/common/dof_manager/dof_manager.cc index 9fc414f0b..d5a47ba38 100644 --- a/src/model/common/dof_manager/dof_manager.cc +++ b/src/model/common/dof_manager/dof_manager.cc @@ -1,1021 +1,1021 @@ /** * Copyright (©) 2015-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "dof_manager.hh" #include "communicator.hh" #include "mesh.hh" #include "mesh_utils.hh" #include "node_group.hh" #include "node_synchronizer.hh" #include "non_linear_solver.hh" #include "periodic_node_synchronizer.hh" #include "time_step_solver.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ DOFManager::DOFManager(const ID & id) : id(id), dofs_flag(0, 1, std::string(id + ":dofs_type")), global_equation_number(0, 1, "global_equation_number"), communicator(Communicator::getStaticCommunicator()) {} /* -------------------------------------------------------------------------- */ DOFManager::DOFManager(Mesh & mesh, const ID & id) : id(id), mesh(&mesh), dofs_flag(0, 1, std::string(id + ":dofs_type")), global_equation_number(0, 1, "global_equation_number"), communicator(mesh.getCommunicator()) { this->mesh->registerEventHandler(*this, _ehp_dof_manager); } /* -------------------------------------------------------------------------- */ DOFManager::~DOFManager() = default; /* -------------------------------------------------------------------------- */ std::vector DOFManager::getDOFIDs() const { std::vector keys; for (const auto & dof_data : this->dofs) { keys.push_back(dof_data.first); } return keys; } /* -------------------------------------------------------------------------- */ void DOFManager::assembleElementalArrayLocalArray( const Array & elementary_vect, Array & array_assembeled, ElementType type, GhostType ghost_type, Real scale_factor, const Array & filter_elements) { AKANTU_DEBUG_IN(); Int nb_element; auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type); auto nb_degree_of_freedom = elementary_vect.getNbComponent() / nb_nodes_per_element; - Idx * filter_it = nullptr; + const Idx * filter_it = nullptr; if (filter_elements != empty_filter) { nb_element = filter_elements.size(); filter_it = filter_elements.data(); } else { nb_element = this->mesh->getNbElement(type, ghost_type); } AKANTU_DEBUG_ASSERT(elementary_vect.size() == nb_element, "The vector elementary_vect(" << elementary_vect.getID() << ") has not the good size."); const auto & connectivity = this->mesh->getConnectivity(type, ghost_type); auto elem_it = make_view(elementary_vect, nb_degree_of_freedom, nb_nodes_per_element) .begin(); auto assemble_it = make_view(array_assembeled, nb_degree_of_freedom).begin(); for (Int el = 0; el < nb_element; ++el, ++elem_it) { auto element = el; if (filter_it != nullptr) { element = *filter_it; } // const Vector & conn = *conn_it; const auto & elemental_val = *elem_it; for (Int n = 0; n < nb_nodes_per_element; ++n) { auto node = connectivity(element, n); auto && assemble = assemble_it[node]; assemble += scale_factor * elemental_val(n); } if (filter_it != nullptr) { ++filter_it; } // else // ++conn_it; } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void DOFManager::assembleElementalArrayToResidual( const ID & dof_id, const Array & elementary_vect, ElementType type, GhostType ghost_type, Real scale_factor, const Array & filter_elements) { AKANTU_DEBUG_IN(); auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type); auto nb_degree_of_freedom = elementary_vect.getNbComponent() / nb_nodes_per_element; Array array_localy_assembeled(this->mesh->getNbNodes(), nb_degree_of_freedom); array_localy_assembeled.zero(); this->assembleElementalArrayLocalArray( elementary_vect, array_localy_assembeled, type, ghost_type, scale_factor, filter_elements); this->assembleToResidual(dof_id, array_localy_assembeled, 1); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void DOFManager::assembleElementalArrayToLumpedMatrix( const ID & dof_id, const Array & elementary_vect, const ID & lumped_mtx, ElementType type, GhostType ghost_type, Real scale_factor, const Array & filter_elements) { AKANTU_DEBUG_IN(); auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type); auto nb_degree_of_freedom = elementary_vect.getNbComponent() / nb_nodes_per_element; Array array_localy_assembeled(this->mesh->getNbNodes(), nb_degree_of_freedom); array_localy_assembeled.zero(); this->assembleElementalArrayLocalArray( elementary_vect, array_localy_assembeled, type, ghost_type, scale_factor, filter_elements); this->assembleToLumpedMatrix(dof_id, array_localy_assembeled, lumped_mtx, 1); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void DOFManager::assembleMatMulDOFsToResidual(const ID & A_id, Real scale_factor) { for (auto & pair : this->dofs) { const auto & dof_id = pair.first; auto & dof_data = *pair.second; this->assembleMatMulVectToResidual(dof_id, A_id, *dof_data.dof, scale_factor); } } /* -------------------------------------------------------------------------- */ void DOFManager::splitSolutionPerDOFs() { for (auto && data : this->dofs) { auto & dof_data = *data.second; dof_data.solution.resize(dof_data.dof->size() * dof_data.dof->getNbComponent()); this->getSolutionPerDOFs(data.first, dof_data.solution); } } /* -------------------------------------------------------------------------- */ void DOFManager::getSolutionPerDOFs(const ID & dof_id, Array & solution_array) { AKANTU_DEBUG_IN(); this->getArrayPerDOFs(dof_id, this->getSolution(), solution_array); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void DOFManager::getLumpedMatrixPerDOFs(const ID & dof_id, const ID & lumped_mtx, Array & lumped) { AKANTU_DEBUG_IN(); this->getArrayPerDOFs(dof_id, this->getLumpedMatrix(lumped_mtx), lumped); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void DOFManager::assembleToResidual(const ID & dof_id, Array & array_to_assemble, Real scale_factor) { AKANTU_DEBUG_IN(); // this->makeConsistentForPeriodicity(dof_id, array_to_assemble); this->assembleToGlobalArray(dof_id, array_to_assemble, this->getResidual(), scale_factor); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void DOFManager::assembleToLumpedMatrix(const ID & dof_id, Array & array_to_assemble, const ID & lumped_mtx, Real scale_factor) { AKANTU_DEBUG_IN(); // this->makeConsistentForPeriodicity(dof_id, array_to_assemble); auto & lumped = this->getLumpedMatrix(lumped_mtx); this->assembleToGlobalArray(dof_id, array_to_assemble, lumped, scale_factor); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ DOFManager::DOFData::DOFData(const ID & dof_id) : support_type(_dst_generic), group_support("__mesh__"), solution(0, 1, dof_id + ":solution"), local_equation_number(0, 1, dof_id + ":local_equation_number"), associated_nodes(0, 1, dof_id + "associated_nodes") {} /* -------------------------------------------------------------------------- */ DOFManager::DOFData::~DOFData() = default; /* -------------------------------------------------------------------------- */ template auto DOFManager::countDOFsForNodes(const DOFData & dof_data, Int nb_nodes, Func && getNode) { auto nb_local_dofs = nb_nodes; decltype(nb_local_dofs) nb_pure_local = 0; for (auto n : arange(nb_nodes)) { UInt node = getNode(n); // http://www.open-std.org/jtc1/sc22/open/n2356/conv.html // bool are by convention casted to 0 and 1 when promoted to int nb_pure_local += this->mesh->isLocalOrMasterNode(node); nb_local_dofs -= this->mesh->isPeriodicSlave(node); } const auto & dofs_array = *dof_data.dof; nb_pure_local *= dofs_array.getNbComponent(); nb_local_dofs *= dofs_array.getNbComponent(); return std::make_pair(nb_local_dofs, nb_pure_local); } /* -------------------------------------------------------------------------- */ auto DOFManager::getNewDOFDataInternal(const ID & dof_id) -> DOFData & { auto it = this->dofs.find(dof_id); if (it != this->dofs.end()) { AKANTU_EXCEPTION("This dof array has already been registered"); } std::unique_ptr dof_data_ptr = this->getNewDOFData(dof_id); DOFData & dof_data = *dof_data_ptr; this->dofs[dof_id] = std::move(dof_data_ptr); return dof_data; } /* -------------------------------------------------------------------------- */ void DOFManager::registerDOFs(const ID & dof_id, Array & dofs_array, DOFSupportType support_type) { auto & dofs_storage = this->getNewDOFDataInternal(dof_id); dofs_storage.support_type = support_type; this->registerDOFsInternal(dof_id, dofs_array); resizeGlobalArrays(); } /* -------------------------------------------------------------------------- */ void DOFManager::registerDOFs(const ID & dof_id, Array & dofs_array, const ID & support_group) { auto & dofs_storage = this->getNewDOFDataInternal(dof_id); dofs_storage.support_type = _dst_nodal; dofs_storage.group_support = support_group; this->registerDOFsInternal(dof_id, dofs_array); resizeGlobalArrays(); } /* -------------------------------------------------------------------------- */ std::tuple DOFManager::registerDOFsInternal(const ID & dof_id, Array & dofs_array) { DOFData & dof_data = this->getDOFData(dof_id); dof_data.dof = &dofs_array; Int nb_local_dofs = 0; Int nb_pure_local = 0; const auto & support_type = dof_data.support_type; switch (support_type) { case _dst_nodal: { const auto & group = dof_data.group_support; std::function getNode; if (group == "__mesh__") { AKANTU_DEBUG_ASSERT( dofs_array.size() == this->mesh->getNbNodes(), "The array of dof is too short to be associated to nodes."); std::tie(nb_local_dofs, nb_pure_local) = countDOFsForNodes( dof_data, this->mesh->getNbNodes(), [](auto && n) { return n; }); } else { const auto & node_group = this->mesh->getElementGroup(group).getNodeGroup().getNodes(); AKANTU_DEBUG_ASSERT( dofs_array.size() == node_group.size(), "The array of dof is too shot to be associated to nodes."); std::tie(nb_local_dofs, nb_pure_local) = countDOFsForNodes(dof_data, node_group.size(), [&node_group](auto && n) { return node_group(n); }); } break; } case _dst_generic: { nb_local_dofs = nb_pure_local = dofs_array.size() * dofs_array.getNbComponent(); break; } default: { AKANTU_EXCEPTION("This type of dofs is not handled yet."); } } dof_data.local_nb_dofs = nb_local_dofs; dof_data.pure_local_nb_dofs = nb_pure_local; dof_data.ghosts_nb_dofs = nb_local_dofs - nb_pure_local; this->pure_local_system_size += nb_pure_local; this->local_system_size += nb_local_dofs; auto nb_total_pure_local = nb_pure_local; communicator.allReduce(nb_total_pure_local, SynchronizerOperation::_sum); this->system_size += nb_total_pure_local; // updating the dofs data after counting is finished switch (support_type) { case _dst_nodal: { const auto & group = dof_data.group_support; if (group != "__mesh__") { auto & support_nodes = this->mesh->getElementGroup(group).getNodeGroup().getNodes(); this->updateDOFsData( dof_data, nb_local_dofs, nb_pure_local, support_nodes.size(), [&support_nodes](Idx node) -> Idx { return support_nodes[node]; }); } else { this->updateDOFsData(dof_data, nb_local_dofs, nb_pure_local, mesh->getNbNodes(), [](Idx node) -> Idx { return node; }); } break; } case _dst_generic: { this->updateDOFsData(dof_data, nb_local_dofs, nb_pure_local); break; } } return std::make_tuple(nb_local_dofs, nb_pure_local, nb_total_pure_local); } /* -------------------------------------------------------------------------- */ void DOFManager::registerDOFsPrevious(const ID & dof_id, Array & array) { DOFData & dof = this->getDOFData(dof_id); if (dof.previous != nullptr) { AKANTU_EXCEPTION("The previous dofs array for " << dof_id << " has already been registered"); } dof.previous = &array; } /* -------------------------------------------------------------------------- */ void DOFManager::registerDOFsIncrement(const ID & dof_id, Array & array) { DOFData & dof = this->getDOFData(dof_id); if (dof.increment != nullptr) { AKANTU_EXCEPTION("The dofs increment array for " << dof_id << " has already been registered"); } dof.increment = &array; } /* -------------------------------------------------------------------------- */ void DOFManager::registerDOFsDerivative(const ID & dof_id, Int order, Array & dofs_derivative) { auto & dof = this->getDOFData(dof_id); auto & derivatives = dof.dof_derivatives; if (Int(derivatives.size()) < order) { derivatives.resize(order, nullptr); } else { if (derivatives[order - 1] != nullptr) { AKANTU_EXCEPTION("The dof derivatives of order " << order << " already been registered for this dof (" << dof_id << ")"); } } derivatives[order - 1] = &dofs_derivative; } /* -------------------------------------------------------------------------- */ void DOFManager::registerBlockedDOFs(const ID & dof_id, Array & blocked_dofs) { auto & dof = this->getDOFData(dof_id); if (dof.blocked_dofs != nullptr) { AKANTU_EXCEPTION("The blocked dofs array for " << dof_id << " has already been registered"); } dof.blocked_dofs = &blocked_dofs; } /* -------------------------------------------------------------------------- */ SparseMatrix & DOFManager::registerSparseMatrix(const ID & matrix_id, std::unique_ptr & matrix) { auto it = this->matrices.find(matrix_id); if (it != this->matrices.end()) { AKANTU_EXCEPTION("The matrix " << matrix_id << " already exists in " << this->id); } auto & ret = *matrix; this->matrices[matrix_id] = std::move(matrix); return ret; } /* -------------------------------------------------------------------------- */ /// Get an instance of a new SparseMatrix SolverVector & DOFManager::registerLumpedMatrix(const ID & matrix_id, std::unique_ptr & matrix) { auto it = this->lumped_matrices.find(matrix_id); if (it != this->lumped_matrices.end()) { AKANTU_EXCEPTION("The lumped matrix " << matrix_id << " already exists in " << this->id); } auto & ret = *matrix; this->lumped_matrices[matrix_id] = std::move(matrix); ret.resize(); return ret; } /* -------------------------------------------------------------------------- */ NonLinearSolver & DOFManager::registerNonLinearSolver( const ID & non_linear_solver_id, std::unique_ptr & non_linear_solver) { NonLinearSolversMap::const_iterator it = this->non_linear_solvers.find(non_linear_solver_id); if (it != this->non_linear_solvers.end()) { AKANTU_EXCEPTION("The non linear solver " << non_linear_solver_id << " already exists in " << this->id); } NonLinearSolver & ret = *non_linear_solver; this->non_linear_solvers[non_linear_solver_id] = std::move(non_linear_solver); return ret; } /* -------------------------------------------------------------------------- */ TimeStepSolver & DOFManager::registerTimeStepSolver( const ID & time_step_solver_id, std::unique_ptr & time_step_solver) { TimeStepSolversMap::const_iterator it = this->time_step_solvers.find(time_step_solver_id); if (it != this->time_step_solvers.end()) { AKANTU_EXCEPTION("The non linear solver " << time_step_solver_id << " already exists in " << this->id); } TimeStepSolver & ret = *time_step_solver; this->time_step_solvers[time_step_solver_id] = std::move(time_step_solver); return ret; } /* -------------------------------------------------------------------------- */ SparseMatrix & DOFManager::getMatrix(const ID & id) { ID matrix_id = this->id + ":mtx:" + id; SparseMatricesMap::const_iterator it = this->matrices.find(matrix_id); if (it == this->matrices.end()) { AKANTU_SILENT_EXCEPTION("The matrix " << matrix_id << " does not exists in " << this->id); } return *(it->second); } /* -------------------------------------------------------------------------- */ bool DOFManager::hasMatrix(const ID & id) const { ID mtx_id = this->id + ":mtx:" + id; auto it = this->matrices.find(mtx_id); return it != this->matrices.end(); } /* -------------------------------------------------------------------------- */ SolverVector & DOFManager::getLumpedMatrix(const ID & id) { ID matrix_id = this->id + ":lumped_mtx:" + id; LumpedMatricesMap::const_iterator it = this->lumped_matrices.find(matrix_id); if (it == this->lumped_matrices.end()) { AKANTU_SILENT_EXCEPTION("The lumped matrix " << matrix_id << " does not exists in " << this->id); } return *(it->second); } /* -------------------------------------------------------------------------- */ const SolverVector & DOFManager::getLumpedMatrix(const ID & id) const { ID matrix_id = this->id + ":lumped_mtx:" + id; auto it = this->lumped_matrices.find(matrix_id); if (it == this->lumped_matrices.end()) { AKANTU_SILENT_EXCEPTION("The lumped matrix " << matrix_id << " does not exists in " << this->id); } return *(it->second); } /* -------------------------------------------------------------------------- */ bool DOFManager::hasLumpedMatrix(const ID & id) const { ID mtx_id = this->id + ":lumped_mtx:" + id; auto it = this->lumped_matrices.find(mtx_id); return it != this->lumped_matrices.end(); } /* -------------------------------------------------------------------------- */ NonLinearSolver & DOFManager::getNonLinearSolver(const ID & id) { ID non_linear_solver_id = this->id + ":nls:" + id; NonLinearSolversMap::const_iterator it = this->non_linear_solvers.find(non_linear_solver_id); if (it == this->non_linear_solvers.end()) { AKANTU_EXCEPTION("The non linear solver " << non_linear_solver_id << " does not exists in " << this->id); } return *(it->second); } /* -------------------------------------------------------------------------- */ bool DOFManager::hasNonLinearSolver(const ID & id) const { ID solver_id = this->id + ":nls:" + id; auto it = this->non_linear_solvers.find(solver_id); return it != this->non_linear_solvers.end(); } /* -------------------------------------------------------------------------- */ TimeStepSolver & DOFManager::getTimeStepSolver(const ID & id) { ID time_step_solver_id = this->id + ":tss:" + id; TimeStepSolversMap::const_iterator it = this->time_step_solvers.find(time_step_solver_id); if (it == this->time_step_solvers.end()) { AKANTU_EXCEPTION("The non linear solver " << time_step_solver_id << " does not exists in " << this->id); } return *(it->second); } /* -------------------------------------------------------------------------- */ bool DOFManager::hasTimeStepSolver(const ID & solver_id) const { ID time_step_solver_id = this->id + ":tss:" + solver_id; auto it = this->time_step_solvers.find(time_step_solver_id); return it != this->time_step_solvers.end(); } /* -------------------------------------------------------------------------- */ void DOFManager::savePreviousDOFs(const ID & dofs_id) { this->getPreviousDOFs(dofs_id).copy(this->getDOFs(dofs_id)); } /* -------------------------------------------------------------------------- */ void DOFManager::zeroResidual() { this->residual->zero(); } /* -------------------------------------------------------------------------- */ void DOFManager::zeroMatrix(const ID & mtx) { this->getMatrix(mtx).zero(); } /* -------------------------------------------------------------------------- */ void DOFManager::zeroLumpedMatrix(const ID & mtx) { this->getLumpedMatrix(mtx).zero(); } /* -------------------------------------------------------------------------- */ /* Mesh Events */ /* -------------------------------------------------------------------------- */ std::pair DOFManager::updateNodalDOFs(const ID & dof_id, const Array & nodes_list) { auto & dof_data = this->getDOFData(dof_id); Int nb_new_local_dofs, nb_new_pure_local; std::tie(nb_new_local_dofs, nb_new_pure_local) = countDOFsForNodes(dof_data, nodes_list.size(), [&nodes_list](auto && n) { return nodes_list(n); }); this->pure_local_system_size += nb_new_pure_local; this->local_system_size += nb_new_local_dofs; auto nb_new_global = nb_new_pure_local; communicator.allReduce(nb_new_global, SynchronizerOperation::_sum); this->system_size += nb_new_global; dof_data.solution.resize(local_system_size); updateDOFsData(dof_data, nb_new_local_dofs, nb_new_pure_local, nodes_list.size(), [&nodes_list](auto pos) -> UInt { return nodes_list[pos]; }); return std::make_pair(nb_new_local_dofs, nb_new_pure_local); } /* -------------------------------------------------------------------------- */ void DOFManager::resizeGlobalArrays() { // resize all relevant arrays this->residual->resize(); this->solution->resize(); this->data_cache->resize(); for (auto & lumped_matrix : lumped_matrices) { lumped_matrix.second->resize(); } for (auto & matrix : matrices) { matrix.second->clearProfile(); } } /* -------------------------------------------------------------------------- */ void DOFManager::onNodesAdded(const Array & nodes_list, const NewNodesEvent &) { for (auto & pair : this->dofs) { const auto & dof_id = pair.first; auto & dof_data = this->getDOFData(dof_id); if (dof_data.support_type != _dst_nodal) { continue; } const auto & group = dof_data.group_support; if (group == "__mesh__") { this->updateNodalDOFs(dof_id, nodes_list); } else { const auto & node_group = this->mesh->getElementGroup(group).getNodeGroup(); Array new_nodes_list; for (const auto & node : nodes_list) { if (node_group.find(node) != Int(-1)) { new_nodes_list.push_back(node); } } this->updateNodalDOFs(dof_id, new_nodes_list); } } this->resizeGlobalArrays(); } /* -------------------------------------------------------------------------- */ void DOFManager::onMeshIsDistributed(const MeshIsDistributedEvent & /*event*/) { AKANTU_DEBUG_ASSERT(this->mesh != nullptr, "The `Mesh` pointer is not set."); // check if the distributed state of the residual and the mesh are the same. if (this->mesh->isDistributed() != this->residual->isDistributed()) { // TODO: Allow to reallocate the internals, in that case one could actually // react on that event. auto is_or_is_not = [](bool q) { return ((q) ? std::string("is") : std::string("is not")); }; AKANTU_EXCEPTION("There is an inconsistency about the distribution state " "of the `DOFManager`." " It seams that the `Mesh` " << is_or_is_not(this->mesh->isDistributed()) << " distributed, but the `DOFManager`'s residual " << is_or_is_not(this->residual->isDistributed()) << ", which is of type " << debug::demangle(typeid(this->residual).name()) << "."); } } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ class GlobalDOFInfoDataAccessor : public DataAccessor { public: GlobalDOFInfoDataAccessor(DOFManager::DOFData & dof_data, DOFManager & dof_manager) : dof_data(dof_data), dof_manager(dof_manager) { for (auto && pair : zip(dof_data.local_equation_number, dof_data.associated_nodes)) { Idx node; Idx dof; std::tie(dof, node) = pair; dofs_per_node[node].push_back(dof); } } Int getNbData(const Array & nodes, const SynchronizationTag & tag) const override { if (tag == SynchronizationTag::_ask_nodes or tag == SynchronizationTag::_giu_global_conn) { return nodes.size() * dof_data.dof->getNbComponent() * sizeof(Idx); } return 0; } void packData(CommunicationBuffer & buffer, const Array & nodes, const SynchronizationTag & tag) const override { if (tag == SynchronizationTag::_ask_nodes or tag == SynchronizationTag::_giu_global_conn) { for (const auto & node : nodes) { const auto & dofs = dofs_per_node.at(node); for (const auto & dof : dofs) { buffer << dof_manager.global_equation_number(dof); } } } } void unpackData(CommunicationBuffer & buffer, const Array & nodes, const SynchronizationTag & tag) override { if (tag == SynchronizationTag::_ask_nodes or tag == SynchronizationTag::_giu_global_conn) { for (const auto & node : nodes) { const auto & dofs = dofs_per_node[node]; for (const auto & dof : dofs) { Idx global_dof; buffer >> global_dof; AKANTU_DEBUG_ASSERT( (dof_manager.global_equation_number(dof) == -1 or dof_manager.global_equation_number(dof) == global_dof), "This dof already had a global_dof_id which is different from " "the received one. " << dof_manager.global_equation_number(dof) << " != " << global_dof); dof_manager.global_equation_number(dof) = global_dof; dof_manager.global_to_local_mapping[global_dof] = dof; } } } } protected: std::unordered_map> dofs_per_node; DOFManager::DOFData & dof_data; DOFManager & dof_manager; }; /* -------------------------------------------------------------------------- */ auto DOFManager::computeFirstDOFIDs(Int nb_new_local_dofs, Int nb_new_pure_local) { // determine the first local/global dof id to use Int offset = 0; this->communicator.exclusiveScan(nb_new_pure_local, offset); auto first_global_dof_id = this->first_global_dof_id + offset; auto first_local_dof_id = this->local_system_size - nb_new_local_dofs; offset = nb_new_pure_local; this->communicator.allReduce(offset); this->first_global_dof_id += offset; return std::make_pair(first_local_dof_id, first_global_dof_id); } /* -------------------------------------------------------------------------- */ void DOFManager::updateDOFsData(DOFData & dof_data, Int nb_new_local_dofs, Int nb_new_pure_local, Int nb_node, const std::function & getNode) { auto nb_local_dofs_added = nb_node * dof_data.dof->getNbComponent(); auto first_dof_pos = dof_data.local_equation_number.size(); dof_data.local_equation_number.reserve(dof_data.local_equation_number.size() + nb_local_dofs_added); dof_data.associated_nodes.reserve(dof_data.associated_nodes.size() + nb_local_dofs_added); this->dofs_flag.resize(this->local_system_size, NodeFlag::_normal); this->global_equation_number.resize(this->local_system_size, -1); std::unordered_map, Idx> masters_dofs; // update per dof info Int local_eq_num, first_global_dof_id; std::tie(local_eq_num, first_global_dof_id) = computeFirstDOFIDs(nb_new_local_dofs, nb_new_pure_local); for (auto d : arange(nb_local_dofs_added)) { auto node = getNode(d / dof_data.dof->getNbComponent()); auto dof_flag = this->mesh->getNodeFlag(node); dof_data.associated_nodes.push_back(node); auto is_local_dof = this->mesh->isLocalOrMasterNode(node); auto is_periodic_slave = this->mesh->isPeriodicSlave(node); auto is_periodic_master = this->mesh->isPeriodicMaster(node); if (is_periodic_slave) { dof_data.local_equation_number.push_back(-1); continue; } // update equation numbers this->dofs_flag(local_eq_num) = dof_flag; dof_data.local_equation_number.push_back(local_eq_num); if (is_local_dof) { this->global_equation_number(local_eq_num) = first_global_dof_id; this->global_to_local_mapping[first_global_dof_id] = local_eq_num; ++first_global_dof_id; } else { this->global_equation_number(local_eq_num) = -1; } if (is_periodic_master) { auto node = getNode(d / dof_data.dof->getNbComponent()); auto dof = d % dof_data.dof->getNbComponent(); masters_dofs.insert( std::make_pair(std::make_pair(node, dof), local_eq_num)); } ++local_eq_num; } // correct periodic slave equation numbers if (this->mesh->isPeriodic()) { auto assoc_begin = dof_data.associated_nodes.begin(); for (auto d : arange(nb_local_dofs_added)) { auto node = dof_data.associated_nodes(first_dof_pos + d); if (not this->mesh->isPeriodicSlave(node)) { continue; } auto master_node = this->mesh->getPeriodicMaster(node); auto dof = d % dof_data.dof->getNbComponent(); dof_data.local_equation_number(first_dof_pos + d) = masters_dofs[std::make_pair(master_node, dof)]; } } // synchronize the global numbering for slaves nodes if (this->mesh->isDistributed()) { GlobalDOFInfoDataAccessor data_accessor(dof_data, *this); if (this->mesh->isPeriodic()) { mesh->getPeriodicNodeSynchronizer().synchronizeOnce( data_accessor, SynchronizationTag::_giu_global_conn); } auto & node_synchronizer = this->mesh->getNodeSynchronizer(); node_synchronizer.synchronizeOnce(data_accessor, SynchronizationTag::_ask_nodes); } } /* -------------------------------------------------------------------------- */ void DOFManager::updateDOFsData(DOFData & dof_data, Int nb_new_local_dofs, Int nb_new_pure_local) { dof_data.local_equation_number.reserve(dof_data.local_equation_number.size() + nb_new_local_dofs); Int first_local_dof_id, first_global_dof_id; std::tie(first_local_dof_id, first_global_dof_id) = computeFirstDOFIDs(nb_new_local_dofs, nb_new_pure_local); this->dofs_flag.resize(this->local_system_size, NodeFlag::_normal); this->global_equation_number.resize(this->local_system_size, -1); // update per dof info for (auto _ [[gnu::unused]] : arange(nb_new_local_dofs)) { // update equation numbers this->dofs_flag(first_local_dof_id) = NodeFlag::_normal; dof_data.local_equation_number.push_back(first_local_dof_id); this->global_equation_number(first_local_dof_id) = first_global_dof_id; this->global_to_local_mapping[first_global_dof_id] = first_local_dof_id; ++first_global_dof_id; ++first_local_dof_id; } } /* -------------------------------------------------------------------------- */ void DOFManager::onNodesRemoved(const Array &, const Array &, const RemovedNodesEvent &) {} /* -------------------------------------------------------------------------- */ void DOFManager::onElementsAdded(const Array & /*unused*/, const NewElementsEvent & /*unused*/) {} /* -------------------------------------------------------------------------- */ void DOFManager::onElementsRemoved(const Array &, const ElementTypeMapArray &, const RemovedElementsEvent &) {} /* -------------------------------------------------------------------------- */ void DOFManager::onElementsChanged(const Array &, const Array &, const ElementTypeMapArray &, const ChangedElementsEvent &) {} /* -------------------------------------------------------------------------- */ void DOFManager::updateGlobalBlockedDofs() { this->previous_global_blocked_dofs.copy(this->global_blocked_dofs); this->global_blocked_dofs.reserve(this->local_system_size, 0); this->previous_global_blocked_dofs_release = this->global_blocked_dofs_release; for (auto & pair : dofs) { if (not this->hasBlockedDOFs(pair.first)) { continue; } DOFData & dof_data = *pair.second; for (auto && data : zip(dof_data.getLocalEquationsNumbers(), make_view(*dof_data.blocked_dofs))) { const auto & dof = std::get<0>(data); const auto & is_blocked = std::get<1>(data); if (is_blocked) { this->global_blocked_dofs.push_back(dof); } } } std::sort(this->global_blocked_dofs.begin(), this->global_blocked_dofs.end()); auto last = std::unique(this->global_blocked_dofs.begin(), this->global_blocked_dofs.end()); this->global_blocked_dofs.resize(last - this->global_blocked_dofs.begin()); auto are_equal = global_blocked_dofs.size() == previous_global_blocked_dofs.size() and std::equal(global_blocked_dofs.begin(), global_blocked_dofs.end(), previous_global_blocked_dofs.begin()); if (not are_equal) { ++this->global_blocked_dofs_release; } } /* -------------------------------------------------------------------------- */ void DOFManager::applyBoundary(const ID & matrix_id) { auto & J = this->getMatrix(matrix_id); if (this->jacobian_release == J.getRelease()) { if (this->hasBlockedDOFsChanged()) { J.applyBoundary(); } previous_global_blocked_dofs.copy(global_blocked_dofs); } else { J.applyBoundary(); } this->jacobian_release = J.getRelease(); this->previous_global_blocked_dofs_release = this->global_blocked_dofs_release; } /* -------------------------------------------------------------------------- */ void DOFManager::assembleMatMulVectToGlobalArray(const ID & dof_id, const ID & A_id, const Array & x, SolverVector & array, Real scale_factor) { auto & A = this->getMatrix(A_id); data_cache->resize(); data_cache->zero(); this->assembleToGlobalArray(dof_id, x, *data_cache, 1.); A.matVecMul(*data_cache, array, scale_factor, 1.); } /* -------------------------------------------------------------------------- */ void DOFManager::assembleMatMulVectToResidual(const ID & dof_id, const ID & A_id, const Array & x, Real scale_factor) { assembleMatMulVectToGlobalArray(dof_id, A_id, x, *residual, scale_factor); } } // namespace akantu diff --git a/src/model/common/dof_manager/dof_manager_default.cc b/src/model/common/dof_manager/dof_manager_default.cc index c74e5cc5e..ee108e124 100644 --- a/src/model/common/dof_manager/dof_manager_default.cc +++ b/src/model/common/dof_manager/dof_manager_default.cc @@ -1,474 +1,474 @@ /** * Copyright (©) 2015-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "dof_manager_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 #include #include #include /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ DOFManagerDefault::DOFManagerDefault(const ID & id) : DOFManager(id), synchronizer(nullptr) { residual = std::make_unique( *this, std::string(id + ":residual")); solution = std::make_unique( *this, std::string(id + ":solution")); data_cache = std::make_unique( *this, std::string(id + ":data_cache")); } /* -------------------------------------------------------------------------- */ DOFManagerDefault::DOFManagerDefault(Mesh & mesh, const ID & id) : DOFManager(mesh, id), synchronizer(nullptr) { if (this->mesh->isDistributed()) { this->synchronizer = std::make_unique( *this, this->id + ":dof_synchronizer"); residual = std::make_unique( *this, std::string(id + ":residual")); solution = std::make_unique( *this, std::string(id + ":solution")); data_cache = std::make_unique( *this, std::string(id + ":data_cache")); } else { residual = std::make_unique( *this, std::string(id + ":residual")); solution = std::make_unique( *this, std::string(id + ":solution")); data_cache = std::make_unique( *this, std::string(id + ":data_cache")); } } /* -------------------------------------------------------------------------- */ DOFManagerDefault::~DOFManagerDefault() = default; /* -------------------------------------------------------------------------- */ void DOFManagerDefault::makeConsistentForPeriodicity(const ID & dof_id, SolverVector & array) { auto & dof_data = this->getDOFDataTyped(dof_id); if (dof_data.support_type != _dst_nodal) { return; } if (not mesh->isPeriodic()) { return; } this->mesh->getPeriodicNodeSynchronizer() .reduceSynchronizeWithPBCSlaves( aka::as_type(array).getVector()); } /* -------------------------------------------------------------------------- */ template void DOFManagerDefault::assembleToGlobalArray( const ID & dof_id, const Array & array_to_assemble, Array & global_array, T scale_factor) { AKANTU_DEBUG_IN(); auto & dof_data = this->getDOFDataTyped(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 & array_to_assemble, SolverVector & global_array_v, Real scale_factor) { assembleToGlobalArray( dof_id, array_to_assemble, aka::as_type(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 { return std::make_unique(dof_id); } /* -------------------------------------------------------------------------- */ std::tuple DOFManagerDefault::registerDOFsInternal(const ID & dof_id, Array & 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(*this, id, matrix_type); } /* -------------------------------------------------------------------------- */ SparseMatrix & DOFManagerDefault::getNewMatrix(const ID & id, const ID & matrix_to_copy_id) { return this->registerSparseMatrix(id, matrix_to_copy_id); } /* -------------------------------------------------------------------------- */ SolverVector & DOFManagerDefault::getNewLumpedMatrix(const ID & id) { return this->registerLumpedMatrix(*this, id); } /* -------------------------------------------------------------------------- */ SparseMatrixAIJ & DOFManagerDefault::getMatrix(const ID & id) { auto & matrix = DOFManager::getMatrix(id); return aka::as_type(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( *this, id, type); } case NonLinearSolverType::_linear: { return this->registerNonLinearSolver(*this, id, type); } #endif case NonLinearSolverType::_lumped: { return this->registerNonLinearSolver(*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( *this, id, type, non_linear_solver, solver_callback); } /* -------------------------------------------------------------------------- */ template void DOFManagerDefault::getArrayPerDOFs(const ID & dof_id, const Array & global_array, Array & local_array) const { AKANTU_DEBUG_IN(); const auto & equation_number = this->getLocalEquationsNumbers(dof_id); auto nb_degree_of_freedoms = equation_number.size(); local_array.resize(nb_degree_of_freedoms / local_array.getNbComponent()); for (auto data : zip(equation_number, make_view(local_array))) { std::get<1>(data) = global_array(std::get<0>(data)); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void DOFManagerDefault::getArrayPerDOFs(const ID & dof_id, const SolverVector & global_array, Array & local_array) { getArrayPerDOFs(dof_id, aka::as_type(global_array).getVector(), local_array); } /* -------------------------------------------------------------------------- */ void DOFManagerDefault::assembleLumpedMatMulVectToResidual( const ID & dof_id, const ID & A_id, const Array & x, Real scale_factor) { const auto & A = aka::as_type(this->getLumpedMatrix(A_id)).getVector(); auto & cache = aka::as_type(*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 & elementary_mat, ElementType type, GhostType ghost_type, const MatrixType & elemental_matrix_type, const Array & 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 & matrix_id, const TermsToAssemble & terms) { auto & A = getMatrix(matrix_id); DOFManager::assemblePreassembledMatrix_(A, terms); } /* -------------------------------------------------------------------------- */ void DOFManagerDefault::assembleMatMulVectToArray(const ID & dof_id, const ID & A_id, const Array & x, Array & array, Real scale_factor) { if (mesh->isDistributed()) { DOFManager::assembleMatMulVectToArray_( dof_id, A_id, x, array, scale_factor); } else { DOFManager::assembleMatMulVectToArray_( 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(); - Int * ge_it = nullptr; + const Int * 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.data(); nb_elements = group_elements.size(); } auto size_mat = nb_nodes_per_element * nb_degree_of_freedom_per_node; Vector element_eq_nb(size_mat); for (Int 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.begin(), element_eq_nb.end(), element_eq_nb.begin(), [&](auto & local) { return this->localToGlobalEquationNumber(local); }); if (ge_it != nullptr) { ++ge_it; } else { ++cit; } for (Int i = 0; i < size_mat; ++i) { auto c_irn = element_eq_nb(i); if (c_irn < size) { for (Int j = 0; j < size_mat; ++j) { auto 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 & DOFManagerDefault::getSolutionArray() { return dynamic_cast(this->solution.get())->getVector(); } /* -------------------------------------------------------------------------- */ const Array & DOFManagerDefault::getResidualArray() const { return dynamic_cast(this->residual.get())->getVector(); } /* -------------------------------------------------------------------------- */ Array & DOFManagerDefault::getResidualArray() { return dynamic_cast(this->residual.get())->getVector(); } /* -------------------------------------------------------------------------- */ void DOFManagerDefault::onNodesAdded(const Array & 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 & DOFManagerDefault::getBlockedDOFs() { return global_blocked_dofs_uint; } /* -------------------------------------------------------------------------- */ const Array & DOFManagerDefault::getBlockedDOFs() const { return global_blocked_dofs_uint; } /* -------------------------------------------------------------------------- */ static bool dof_manager_is_registered = DOFManagerFactory::getInstance().registerAllocator( "default", [](Mesh & mesh, const ID & id) -> std::unique_ptr { return std::make_unique(mesh, id); }); static bool dof_manager_is_registered_mumps = DOFManagerFactory::getInstance().registerAllocator( "mumps", [](Mesh & mesh, const ID & id) -> std::unique_ptr { return std::make_unique(mesh, id); }); } // namespace akantu diff --git a/src/model/common/dof_manager/dof_manager_inline_impl.hh b/src/model/common/dof_manager/dof_manager_inline_impl.hh index 210b62004..7aeedf31e 100644 --- a/src/model/common/dof_manager/dof_manager_inline_impl.hh +++ b/src/model/common/dof_manager/dof_manager_inline_impl.hh @@ -1,326 +1,326 @@ /** * Copyright (©) 2013-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "dof_manager.hh" #include "element_group.hh" #include "solver_vector.hh" #include "sparse_matrix.hh" #include "terms_to_assemble.hh" /* -------------------------------------------------------------------------- */ // #ifndef __AKANTU_DOF_MANAGER_INLINE_IMPL_CC__ // #define __AKANTU_DOF_MANAGER_INLINE_IMPL_CC__ namespace akantu { /* -------------------------------------------------------------------------- */ inline bool DOFManager::hasDOFs(const ID & dof_id) const { auto it = this->dofs.find(dof_id); return it != this->dofs.end(); } /* -------------------------------------------------------------------------- */ inline DOFManager::DOFData & DOFManager::getDOFData(const ID & dof_id) { auto it = this->dofs.find(dof_id); if (it == this->dofs.end()) { AKANTU_EXCEPTION("The dof " << dof_id << " does not exists in " << this->id); } return *it->second; } /* -------------------------------------------------------------------------- */ const DOFManager::DOFData & DOFManager::getDOFData(const ID & dof_id) const { auto it = this->dofs.find(dof_id); if (it == this->dofs.end()) { AKANTU_EXCEPTION("The dof " << dof_id << " does not exists in " << this->id); } return *it->second; } /* -------------------------------------------------------------------------- */ inline void DOFManager::extractElementEquationNumber( const Array & equation_numbers, const Vector & connectivity, Int nb_degree_of_freedom, Vector & element_equation_number) { for (Int i = 0, ld = 0; i < connectivity.size(); ++i) { auto n = connectivity(i); for (Int d = 0; d < nb_degree_of_freedom; ++d, ++ld) { element_equation_number(ld) = equation_numbers(n * nb_degree_of_freedom + d); } } } /* -------------------------------------------------------------------------- */ template inline DOFData_ & DOFManager::getDOFDataTyped(const ID & dof_id) { return aka::as_type(this->getDOFData(dof_id)); } /* -------------------------------------------------------------------------- */ template inline const DOFData_ & DOFManager::getDOFDataTyped(const ID & dof_id) const { return aka::as_type(this->getDOFData(dof_id)); } /* -------------------------------------------------------------------------- */ inline Array & DOFManager::getDOFs(const ID & dofs_id) { return *(this->getDOFData(dofs_id).dof); } /* -------------------------------------------------------------------------- */ inline DOFSupportType DOFManager::getSupportType(const ID & dofs_id) const { return this->getDOFData(dofs_id).support_type; } /* -------------------------------------------------------------------------- */ inline Array & DOFManager::getPreviousDOFs(const ID & dofs_id) { return *(this->getDOFData(dofs_id).previous); } /* -------------------------------------------------------------------------- */ inline bool DOFManager::hasPreviousDOFs(const ID & dofs_id) const { return (this->getDOFData(dofs_id).previous != nullptr); } /* -------------------------------------------------------------------------- */ inline Array & DOFManager::getDOFsIncrement(const ID & dofs_id) { return *(this->getDOFData(dofs_id).increment); } /* -------------------------------------------------------------------------- */ inline bool DOFManager::hasDOFsIncrement(const ID & dofs_id) const { return (this->getDOFData(dofs_id).increment != nullptr); } /* -------------------------------------------------------------------------- */ inline Array & DOFManager::getDOFsDerivatives(const ID & dofs_id, Int order) { if (order == 0) { return getDOFs(dofs_id); } auto & derivatives = this->getDOFData(dofs_id).dof_derivatives; if ((order > Int(derivatives.size())) || (derivatives[order - 1] == nullptr)) { AKANTU_EXCEPTION("No derivatives of order " << order << " present in " << this->id << " for dof " << dofs_id); } return *derivatives[order - 1]; } /* -------------------------------------------------------------------------- */ inline bool DOFManager::hasDOFsDerivatives(const ID & dofs_id, Int order) const { const auto & derivatives = this->getDOFData(dofs_id).dof_derivatives; return ((order < Int(derivatives.size())) && (derivatives[order - 1] != nullptr)); } /* -------------------------------------------------------------------------- */ inline const Array & DOFManager::getSolution(const ID & dofs_id) const { return this->getDOFData(dofs_id).solution; } /* -------------------------------------------------------------------------- */ inline Array & DOFManager::getSolution(const ID & dofs_id) { return this->getDOFData(dofs_id).solution; } /* -------------------------------------------------------------------------- */ inline const Array & DOFManager::getBlockedDOFs(const ID & dofs_id) const { return *(this->getDOFData(dofs_id).blocked_dofs); } /* -------------------------------------------------------------------------- */ inline bool DOFManager::hasBlockedDOFs(const ID & dofs_id) const { return (this->getDOFData(dofs_id).blocked_dofs != nullptr); } /* -------------------------------------------------------------------------- */ inline bool DOFManager::isLocalOrMasterDOF(Idx dof_num) { auto dof_flag = this->dofs_flag(dof_num); return (dof_flag & NodeFlag::_local_master_mask) == NodeFlag::_normal; } /* -------------------------------------------------------------------------- */ inline bool DOFManager::isSlaveDOF(Idx dof_num) { auto dof_flag = this->dofs_flag(dof_num); return (dof_flag & NodeFlag::_shared_mask) == NodeFlag::_slave; } /* -------------------------------------------------------------------------- */ inline bool DOFManager::isPureGhostDOF(Idx dof_num) { auto dof_flag = this->dofs_flag(dof_num); return (dof_flag & NodeFlag::_shared_mask) == NodeFlag::_pure_ghost; } /* -------------------------------------------------------------------------- */ inline Idx DOFManager::localToGlobalEquationNumber(Idx local) const { return this->global_equation_number(local); } /* -------------------------------------------------------------------------- */ inline bool DOFManager::hasGlobalEquationNumber(Idx global) const { auto it = this->global_to_local_mapping.find(global); return (it != this->global_to_local_mapping.end()); } /* -------------------------------------------------------------------------- */ inline Idx DOFManager::globalToLocalEquationNumber(Idx global) const { auto it = this->global_to_local_mapping.find(global); AKANTU_DEBUG_ASSERT(it != this->global_to_local_mapping.end(), "This global equation number " << global << " does not exists in " << this->id); return it->second; } /* -------------------------------------------------------------------------- */ inline NodeFlag DOFManager::getDOFFlag(Idx local_id) const { return this->dofs_flag(local_id); } /* -------------------------------------------------------------------------- */ inline decltype(auto) DOFManager::getDOFsAssociatedNodes(const ID & dof_id) const { const auto & dof_data = this->getDOFData(dof_id); return dof_data.associated_nodes; } /* -------------------------------------------------------------------------- */ decltype(auto) DOFManager::getLocalEquationsNumbers(const ID & dof_id) const { return getDOFData(dof_id).local_equation_number; } /* -------------------------------------------------------------------------- */ template void DOFManager::assembleMatMulVectToArray_(const ID & dof_id, const ID & A_id, const Array & x, Array & array, Real scale_factor) { Vec tmp_array(aka::as_type(*data_cache), this->id + ":tmp_array"); tmp_array.zero(); assembleMatMulVectToGlobalArray(dof_id, A_id, x, tmp_array, scale_factor); getArrayPerDOFs(dof_id, tmp_array, array); } /* -------------------------------------------------------------------------- */ template void DOFManager::assembleElementalMatricesToMatrix_( Mat & A, const ID & dof_id, const Array & elementary_mat, ElementType type, GhostType ghost_type, const MatrixType & elemental_matrix_type, const Array & filter_elements) { AKANTU_DEBUG_IN(); auto & dof_data = this->getDOFData(dof_id); AKANTU_DEBUG_ASSERT(dof_data.support_type == _dst_nodal, "This function applies only on Nodal dofs"); const auto & equation_number = this->getLocalEquationsNumbers(dof_id); Int nb_element; - Idx * filter_it = nullptr; + const Idx * filter_it = nullptr; if (filter_elements != empty_filter) { nb_element = filter_elements.size(); filter_it = filter_elements.data(); } else { if (dof_data.group_support != "__mesh__") { const auto & group_elements = this->mesh->getElementGroup(dof_data.group_support) .getElements(type, ghost_type); nb_element = group_elements.size(); filter_it = group_elements.data(); } else { nb_element = this->mesh->getNbElement(type, ghost_type); } } AKANTU_DEBUG_ASSERT(elementary_mat.size() == nb_element, "The vector elementary_mat(" << elementary_mat.getID() << ") has not the good size."); auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type); auto nb_degree_of_freedom = dof_data.dof->getNbComponent(); const auto & connectivity = this->mesh->getConnectivity(type, ghost_type); auto conn_begin = connectivity.begin(nb_nodes_per_element); auto conn_it = conn_begin; auto size_mat = nb_nodes_per_element * nb_degree_of_freedom; Vector element_eq_nb(nb_degree_of_freedom * nb_nodes_per_element); auto el_mat_it = elementary_mat.begin(size_mat, size_mat); for (Int e = 0; e < nb_element; ++e, ++el_mat_it) { if (filter_it) { conn_it = conn_begin + *filter_it; } this->extractElementEquationNumber(equation_number, *conn_it, nb_degree_of_freedom, element_eq_nb); std::transform(element_eq_nb.begin(), element_eq_nb.end(), element_eq_nb.begin(), [&](auto && local) { return this->localToGlobalEquationNumber(local); }); if (filter_it) { ++filter_it; } else { ++conn_it; } A.addValues(element_eq_nb, element_eq_nb, *el_mat_it, elemental_matrix_type); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void DOFManager::assemblePreassembledMatrix_(Mat & A, const TermsToAssemble & terms) { const auto & dof_id_m = terms.getDOFIdM(); const auto & dof_id_n = terms.getDOFIdN(); const auto & equation_number_m = this->getLocalEquationsNumbers(dof_id_m); const auto & equation_number_n = this->getLocalEquationsNumbers(dof_id_n); for (const auto & term : terms) { auto gi = this->localToGlobalEquationNumber(equation_number_m(term.i())); auto gj = this->localToGlobalEquationNumber(equation_number_n(term.j())); A.add(gi, gj, term); } } /* -------------------------------------------------------------------------- */ } // namespace akantu //#endif /* __AKANTU_DOF_MANAGER_INLINE_IMPL_CC__ */ diff --git a/src/model/common/integration_scheme/generalized_trapezoidal.cc b/src/model/common/integration_scheme/generalized_trapezoidal.cc index 9e8a696f3..948fc81aa 100644 --- a/src/model/common/integration_scheme/generalized_trapezoidal.cc +++ b/src/model/common/integration_scheme/generalized_trapezoidal.cc @@ -1,186 +1,186 @@ /** * Copyright (©) 2015-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "generalized_trapezoidal.hh" #include "aka_array.hh" #include "dof_manager.hh" #include "mesh.hh" #include "sparse_matrix.hh" /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ GeneralizedTrapezoidal::GeneralizedTrapezoidal(DOFManager & dof_manager, const ID & dof_id, Real alpha) : IntegrationScheme1stOrder(dof_manager, dof_id), alpha(alpha) { this->registerParam("alpha", this->alpha, alpha, _pat_parsmod, "The alpha parameter"); } /* -------------------------------------------------------------------------- */ void GeneralizedTrapezoidal::predictor(Real delta_t, Array & u, Array & u_dot, const Array & blocked_dofs) const { AKANTU_DEBUG_IN(); Int nb_nodes = u.size(); Int nb_degree_of_freedom = u.getNbComponent() * nb_nodes; Real * u_val = u.data(); Real * u_dot_val = u_dot.data(); - bool * blocked_dofs_val = blocked_dofs.data(); + const bool * blocked_dofs_val = blocked_dofs.data(); for (Int d = 0; d < nb_degree_of_freedom; d++) { if (!(*blocked_dofs_val)) { *u_val += (1. - alpha) * delta_t * *u_dot_val; } u_val++; u_dot_val++; blocked_dofs_val++; } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void GeneralizedTrapezoidal::corrector(const SolutionType & type, Real delta_t, Array & u, Array & u_dot, const Array & blocked_dofs, const Array & delta) const { AKANTU_DEBUG_IN(); switch (type) { case _temperature: this->allCorrector<_temperature>(delta_t, u, u_dot, blocked_dofs, delta); break; case _temperature_rate: this->allCorrector<_temperature_rate>(delta_t, u, u_dot, blocked_dofs, delta); break; default: AKANTU_EXCEPTION("The corrector type : " << type << " is not supported by this type of integration scheme"); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ Real GeneralizedTrapezoidal::getTemperatureCoefficient( const SolutionType & type, Real delta_t) const { switch (type) { case _temperature: return 1.; case _temperature_rate: return alpha * delta_t; default: AKANTU_EXCEPTION("The corrector type : " << type << " is not supported by this type of integration scheme"); } } /* -------------------------------------------------------------------------- */ Real GeneralizedTrapezoidal::getTemperatureRateCoefficient( const SolutionType & type, Real delta_t) const { switch (type) { case _temperature: return 1. / (alpha * delta_t); case _temperature_rate: return 1.; default: AKANTU_EXCEPTION("The corrector type : " << type << " is not supported by this type of integration scheme"); } } /* -------------------------------------------------------------------------- */ template void GeneralizedTrapezoidal::allCorrector(Real delta_t, Array & u, Array & u_dot, const Array & blocked_dofs, const Array & delta) const { AKANTU_DEBUG_IN(); Int nb_nodes = u.size(); Int nb_degree_of_freedom = u.getNbComponent() * nb_nodes; Real e = getTemperatureCoefficient(type, delta_t); Real d = getTemperatureRateCoefficient(type, delta_t); Real * u_val = u.data(); Real * u_dot_val = u_dot.data(); - Real * delta_val = delta.data(); - bool * blocked_dofs_val = blocked_dofs.data(); + const Real * delta_val = delta.data(); + const bool * blocked_dofs_val = blocked_dofs.data(); for (Int dof = 0; dof < nb_degree_of_freedom; dof++) { if (!(*blocked_dofs_val)) { *u_val += e * *delta_val; *u_dot_val += d * *delta_val; } u_val++; u_dot_val++; delta_val++; blocked_dofs_val++; } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void GeneralizedTrapezoidal::assembleJacobian(const SolutionType & type, Real delta_t) { AKANTU_DEBUG_IN(); SparseMatrix & J = this->dof_manager.getMatrix("J"); const SparseMatrix & M = this->dof_manager.getMatrix("M"); const SparseMatrix & K = this->dof_manager.getMatrix("K"); bool does_j_need_update = false; does_j_need_update |= M.getRelease() != m_release; does_j_need_update |= K.getRelease() != k_release; does_j_need_update |= this->dof_manager.hasBlockedDOFsChanged(); if (not does_j_need_update) { AKANTU_DEBUG_OUT(); return; } J.copyProfile(K); // J.zero(); Real d = this->getTemperatureRateCoefficient(type, delta_t); Real e = this->getTemperatureCoefficient(type, delta_t); J.add(M, d); J.add(K, e); m_release = M.getRelease(); k_release = K.getRelease(); 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 50a37986b..7ae6ffe8d 100644 --- a/src/model/common/non_local_toolbox/neighborhood_base.hh +++ b/src/model/common/non_local_toolbox/neighborhood_base.hh @@ -1,140 +1,140 @@ /** * Copyright (©) 2015-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #ifndef AKANTU_NEIGHBORHOOD_BASE_HH_ #define AKANTU_NEIGHBORHOOD_BASE_HH_ /* -------------------------------------------------------------------------- */ #include "aka_common.hh" #include "data_accessor.hh" #include "integration_point.hh" #include "synchronizer_registry.hh" /* -------------------------------------------------------------------------- */ namespace akantu { class Model; template class SpatialGrid; class GridSynchronizer; class RemovedElementsEvent; } // namespace akantu namespace akantu { class NeighborhoodBase : public DataAccessor, public SynchronizerRegistry { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: NeighborhoodBase(Model & model, const ElementTypeMapArray & quad_coordinates, const ID & id = "neighborhood"); ~NeighborhoodBase() override; using PairList = std::vector>; /* ------------------------------------------------------------------------ */ /* 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 Ref> & coords); + const VectorProxy & 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 & data_accessor, const SynchronizationTag & tag) = 0; /// inherited function from MeshEventHandler virtual void onElementsRemoved(const Array & element_list, const ElementTypeMapArray & new_numbering, const RemovedElementsEvent & event); protected: /// create the grid void createGrid(); /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: AKANTU_GET_MACRO(SpatialDimension, spatial_dimension, Int); 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{0.}; /** * the pairs of quadrature points * 0: not ghost to not ghost * 1: not ghost to ghost */ std::array pair_list; /// the regular grid to construct/update the pair lists std::unique_ptr> spatial_grid; bool is_creating_grid{false}; /// the grid synchronizer for parallel computations std::unique_ptr grid_synchronizer; /// the quadrature point positions const ElementTypeMapArray & quad_coordinates; /// the spatial dimension of the problem const Int spatial_dimension; }; } // namespace akantu #include "neighborhood_base_inline_impl.hh" #endif /* AKANTU_NEIGHBORHOOD_BASE_HH_ */ diff --git a/src/model/common/non_local_toolbox/neighborhood_base_inline_impl.hh b/src/model/common/non_local_toolbox/neighborhood_base_inline_impl.hh index f37c3481f..2ce5a3cd9 100644 --- a/src/model/common/non_local_toolbox/neighborhood_base_inline_impl.hh +++ b/src/model/common/non_local_toolbox/neighborhood_base_inline_impl.hh @@ -1,39 +1,38 @@ /** * Copyright (©) 2013-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "aka_grid_dynamic.hh" #include "neighborhood_base.hh" /* -------------------------------------------------------------------------- */ #ifndef AKANTU_NEIGHBORHOOD_BASE_INLINE_IMPL_HH_ #define AKANTU_NEIGHBORHOOD_BASE_INLINE_IMPL_HH_ namespace akantu { -inline void -NeighborhoodBase::insertIntegrationPoint(const IntegrationPoint & quad, - const Ref & coords) { +inline void NeighborhoodBase::insertIntegrationPoint( + const IntegrationPoint & quad, const VectorProxy & coords) { this->spatial_grid->insert(quad, coords); } } // namespace akantu #endif /* AKANTU_NEIGHBORHOOD_BASE_INLINE_IMPL_HH_ */ diff --git a/src/model/contact_mechanics/contact_mechanics_model.cc b/src/model/contact_mechanics/contact_mechanics_model.cc index da19cbe83..a2f2d0616 100644 --- a/src/model/contact_mechanics/contact_mechanics_model.cc +++ b/src/model/contact_mechanics/contact_mechanics_model.cc @@ -1,664 +1,669 @@ /** * Copyright (©) 2013-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "contact_mechanics_model.hh" #include "boundary_condition_functor.hh" #include "dumpable_inline_impl.hh" #include "group_manager_inline_impl.hh" #include "integrator_gauss.hh" #include "shape_lagrange.hh" /* -------------------------------------------------------------------------- */ #include "dumper_iohelper_paraview.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ ContactMechanicsModel::ContactMechanicsModel( Mesh & mesh, Int dim, const ID & id, std::shared_ptr dof_manager, const ModelType model_type) : Model(mesh, model_type, dim, id) { AKANTU_DEBUG_IN(); this->initDOFManager(dof_manager); this->registerFEEngineObject("ContactMechanicsModel", mesh, Model::spatial_dimension); this->mesh.registerDumper("contact_mechanics", id, true); this->mesh.addDumpMeshToDumper("contact_mechanics", mesh, Model::spatial_dimension - 1, _not_ghost, _ek_regular); this->registerDataAccessor(*this); this->detector = std::make_unique(this->mesh, id + ":contact_detector"); registerFEEngineObject("ContactFacetsFEEngine", mesh, Model::spatial_dimension - 1); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ ContactMechanicsModel::~ContactMechanicsModel() = default; /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::initFullImpl(const ModelOptions & options) { Model::initFullImpl(options); // initalize the resolutions if (not this->parser.getLastParsedFile().empty()) { this->instantiateResolutions(); this->initResolutions(); } this->initBC(*this, *displacement, *displacement_increment, *external_force); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::instantiateResolutions() { ParserSection model_section; bool is_empty; std::tie(model_section, is_empty) = this->getParserSection(); if (not is_empty) { auto model_resolutions = model_section.getSubSections(ParserType::_contact_resolution); for (const auto & section : model_resolutions) { this->registerNewResolution(section); } } auto sub_sections = this->parser.getSubSections(ParserType::_contact_resolution); for (const auto & section : sub_sections) { this->registerNewResolution(section); } if (resolutions.empty()) { AKANTU_EXCEPTION("No contact resolutions where instantiated for the model" << getID()); } are_resolutions_instantiated = true; } /* -------------------------------------------------------------------------- */ Resolution & ContactMechanicsModel::registerNewResolution(const ParserSection & section) { std::string res_name; std::string res_type = section.getName(); std::string opt_param = section.getOption(); try { std::string tmp = section.getParameter("name"); res_name = tmp; /** this can seem 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 contact resolution of type \'" << res_type << "\' in the input file has been defined without a name!"); } Resolution & res = this->registerNewResolution(res_name, res_type, opt_param); res.parseSection(section); return res; } /* -------------------------------------------------------------------------- */ Resolution & ContactMechanicsModel::registerNewResolution( const ID & res_name, const ID & res_type, const ID & opt_param) { AKANTU_DEBUG_ASSERT(resolutions_names_to_id.find(res_name) == resolutions_names_to_id.end(), "A resolution with this name '" << res_name << "' has already been registered. " << "Please use unique names for resolutions"); UInt res_count = resolutions.size(); resolutions_names_to_id[res_name] = res_count; std::stringstream sstr_res; sstr_res << this->id << ":" << res_count << ":" << res_type; ID res_id = sstr_res.str(); std::unique_ptr resolution = ResolutionFactory::getInstance().allocate(res_type, spatial_dimension, opt_param, *this, res_id); resolutions.push_back(std::move(resolution)); return *(resolutions.back()); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::initResolutions() { AKANTU_DEBUG_ASSERT(resolutions.size() != 0, "No resolutions to initialize !"); if (!are_resolutions_instantiated) { instantiateResolutions(); } } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::initModel() { AKANTU_DEBUG_IN(); getFEEngine("ContactMechanicsModel").initShapeFunctions(_not_ghost); getFEEngine("ContactMechanicsModel").initShapeFunctions(_ghost); getFEEngine("ContactFacetsFEEngine").initShapeFunctions(_not_ghost); getFEEngine("ContactFacetsFEEngine").initShapeFunctions(_ghost); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ FEEngine & ContactMechanicsModel::getFEEngineBoundary(const ID & name) { return dynamic_cast( getFEEngineClassBoundary(name)); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::initSolver( TimeStepSolverType /*time_step_solver_type*/, NonLinearSolverType /*unused*/) { // for alloc type of solvers this->allocNodalField(this->displacement, spatial_dimension, "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->normal_force, spatial_dimension, "normal_force"); this->allocNodalField(this->tangential_force, spatial_dimension, "tangential_force"); this->allocNodalField(this->gaps, 1, "gaps"); this->allocNodalField(this->nodal_area, 1, "areas"); this->allocNodalField(this->blocked_dofs, 1, "blocked_dofs"); this->allocNodalField(this->contact_state, 1, "contact_state"); this->allocNodalField(this->previous_master_elements, 1, "previous_master_elements"); this->allocNodalField(this->normals, spatial_dimension, "normals"); auto surface_dimension = spatial_dimension - 1; this->allocNodalField(this->tangents, surface_dimension * spatial_dimension, "tangents"); this->allocNodalField(this->projections, surface_dimension, "projections"); this->allocNodalField(this->previous_projections, surface_dimension, "previous_projections"); this->allocNodalField(this->previous_tangents, surface_dimension * spatial_dimension, "previous_tangents"); this->allocNodalField(this->tangential_tractions, surface_dimension, "tangential_tractions"); this->allocNodalField(this->previous_tangential_tractions, surface_dimension, "previous_tangential_tractions"); // todo register multipliers as dofs for lagrange multipliers } /* -------------------------------------------------------------------------- */ std::tuple ContactMechanicsModel::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); } } /* -------------------------------------------------------------------------- */ ModelSolverOptions ContactMechanicsModel::getDefaultSolverOptions( const TimeStepSolverType & type) const { ModelSolverOptions options; switch (type) { case TimeStepSolverType::_dynamic: { options.non_linear_solver_type = NonLinearSolverType::_lumped; options.integration_scheme_type["displacement"] = IntegrationSchemeType::_central_difference; options.solution_type["displacement"] = IntegrationScheme::_acceleration; break; } 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_contact; options.integration_scheme_type["displacement"] = IntegrationSchemeType::_pseudo_time; options.solution_type["displacement"] = IntegrationScheme::_not_defined; break; } default: AKANTU_EXCEPTION(type << " is not a valid time step solver type"); break; } return options; } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::assembleResidual() { AKANTU_DEBUG_IN(); /* ------------------------------------------------------------------------ */ // computes the internal forces this->assembleInternalForces(); /* ------------------------------------------------------------------------ */ this->getDOFManager().assembleToResidual("displacement", *this->internal_force, 1); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::assembleInternalForces() { AKANTU_DEBUG_IN(); AKANTU_DEBUG_INFO("Assemble the contact forces"); auto nb_nodes = mesh.getNbNodes(); this->internal_force->clear(); this->normal_force->clear(); this->tangential_force->clear(); internal_force->resize(nb_nodes, 0.); normal_force->resize(nb_nodes, 0.); tangential_force->resize(nb_nodes, 0.); // assemble the forces due to contact auto assemble = [&](auto && ghost_type) { for (auto & resolution : resolutions) { resolution->assembleInternalForces(ghost_type); } }; AKANTU_DEBUG_INFO("Assemble residual for local elements"); assemble(_not_ghost); // assemble the stresses due to ghost elements // AKANTU_DEBUG_INFO("Assemble residual for ghost elements"); // assemble(_ghost); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::search() { // save the previous state this->savePreviousState(); contact_elements.clear(); // this needs to be resized if cohesive elements are added UInt nb_nodes = mesh.getNbNodes(); auto resize_arrays = [&](auto & internal_array) { internal_array->resize(nb_nodes); internal_array->zero(); }; resize_arrays(gaps); resize_arrays(normals); resize_arrays(tangents); resize_arrays(projections); resize_arrays(tangential_tractions); resize_arrays(contact_state); resize_arrays(nodal_area); resize_arrays(external_force); this->detector->search(contact_elements, *gaps, *normals, *tangents, *projections); // interpenetration value must be positive for contact mechanics // model to work by default the gap value from detector is negative std::for_each((*gaps).begin(), (*gaps).end(), [](Real & gap) { gap *= -1.; }); if (not contact_elements.empty()) { this->computeNodalAreas(); } } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::savePreviousState() { AKANTU_DEBUG_IN(); // saving previous natural projections (*previous_projections).copy(*projections); // saving previous tangents (*previous_tangents).copy(*tangents); // saving previous tangential traction (*previous_tangential_tractions).copy(*tangential_tractions); previous_master_elements->clear(); previous_master_elements->resize(projections->size()); previous_master_elements->set(ElementNull); for (auto & element : contact_elements) { (*previous_master_elements)[element.slave] = element.master; } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::computeNodalAreas(GhostType ghost_type) { auto nb_nodes = mesh.getNbNodes(); nodal_area->resize(nb_nodes); nodal_area->zero(); external_force->resize(nb_nodes); external_force->zero(); auto & fem_boundary = getFEEngineClassBoundary("ContactMechanicsModel"); fem_boundary.initShapeFunctions(getPositions(), _not_ghost); fem_boundary.initShapeFunctions(getPositions(), _ghost); fem_boundary.computeNormalsOnIntegrationPoints(_not_ghost); fem_boundary.computeNormalsOnIntegrationPoints(_ghost); IntegrationPoint quad_point; quad_point.ghost_type = ghost_type; auto & group = mesh.getElementGroup("contact_surface"); UInt nb_degree_of_freedom = external_force->getNbComponent(); for (auto && type : group.elementTypes(spatial_dimension - 1, ghost_type)) { const auto & element_ids = group.getElements(type, ghost_type); auto nb_quad_points = fem_boundary.getNbIntegrationPoints(type, ghost_type); auto nb_elements = element_ids.size(); auto nb_nodes_per_element = mesh.getNbNodesPerElement(type); Array dual_before_integ(nb_elements * nb_quad_points, nb_degree_of_freedom); Array quad_coords(nb_elements * nb_quad_points, spatial_dimension); - const auto & normals_on_quad = + auto & normals_on_quad = fem_boundary.getNormalsOnIntegrationPoints(type, ghost_type); auto normals_begin = normals_on_quad.begin(spatial_dimension); decltype(normals_begin) normals_iter; auto quad_coords_iter = quad_coords.begin(spatial_dimension); auto dual_iter = dual_before_integ.begin(nb_degree_of_freedom); quad_point.type = type; Element subelement{type, 0, ghost_type}; for (auto el : element_ids) { subelement.element = el; auto inside_to_outside = GeometryUtils::outsideDirection(mesh, subelement); normals_iter = normals_begin + el * nb_quad_points; quad_point.element = el; for (auto q : arange(nb_quad_points)) { quad_point.num_point = q; auto & normal = *normals_iter; auto ddot = inside_to_outside.dot(normal); - if (ddot < 0) { - normal *= -1.0; - } - (*dual_iter) = Matrix::Identity(spatial_dimension, spatial_dimension) * normal; + if (ddot < 0) { + *dual_iter *= -1.0; + } + ++dual_iter; ++quad_coords_iter; ++normals_iter; } } Array dual_by_shapes(nb_elements * nb_quad_points, nb_degree_of_freedom * nb_nodes_per_element); fem_boundary.computeNtb(dual_before_integ, dual_by_shapes, type, ghost_type, element_ids); Array dual_by_shapes_integ(nb_elements, nb_degree_of_freedom * nb_nodes_per_element); fem_boundary.integrate(dual_by_shapes, dual_by_shapes_integ, nb_degree_of_freedom * nb_nodes_per_element, type, ghost_type, element_ids); this->getDOFManager().assembleElementalArrayLocalArray( dual_by_shapes_integ, *external_force, type, ghost_type, 1., element_ids); } for (auto && tuple : zip(*nodal_area, make_view(*external_force, spatial_dimension))) { auto & area = std::get<0>(tuple); auto & force = std::get<1>(tuple); area = force.norm(); } this->external_force->clear(); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::printself(std::ostream & stream, int indent) const { std::string space(indent, AKANTU_INDENT); stream << space << "Contact 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 << AKANTU_INDENT << "]" << std::endl; stream << space << " + resolutions [" << std::endl; for (const auto & resolution : resolutions) { resolution->printself(stream, indent + 1); } stream << space << AKANTU_INDENT << "]" << std::endl; stream << space << "]" << std::endl; } /* -------------------------------------------------------------------------- */ MatrixType ContactMechanicsModel::getMatrixType(const ID & matrix_id) const { if (matrix_id == "K") { return _symmetric; } return _mt_not_defined; } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::assembleMatrix(const ID & matrix_id) { if (matrix_id == "K") { this->assembleStiffnessMatrix(); } } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::assembleStiffnessMatrix() { AKANTU_DEBUG_INFO("Assemble the new stiffness matrix"); if (!this->getDOFManager().hasMatrix("K")) { this->getDOFManager().getNewMatrix("K", getMatrixType("K")); } for (auto & resolution : resolutions) { resolution->assembleStiffnessMatrix(_not_ghost); } } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::assembleLumpedMatrix(const ID & /*matrix_id*/) { AKANTU_TO_IMPLEMENT(); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::beforeSolveStep() { for (auto & resolution : resolutions) { resolution->beforeSolveStep(); } } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::afterSolveStep(bool converged) { for (auto & resolution : resolutions) { resolution->afterSolveStep(converged); } } /* -------------------------------------------------------------------------- */ std::shared_ptr -ContactMechanicsModel::createNodalFieldBool(const std::string & /*unused*/, - const std::string & /*unused*/, - bool /*unused*/) { - return nullptr; +ContactMechanicsModel::createNodalFieldBool(const std::string & field_name, + const std::string & group_name, + bool /*padding_flag*/) { + + std::map *> bool_nodal_fields; + bool_nodal_fields["blocked_dofs"] = blocked_dofs.get(); + + std::shared_ptr field; + field = mesh.createNodalField(bool_nodal_fields[field_name], group_name); + return field; } /* -------------------------------------------------------------------------- */ std::shared_ptr ContactMechanicsModel::createNodalFieldReal(const std::string & field_name, const std::string & group_name, bool padding_flag) { std::map *> real_nodal_fields; real_nodal_fields["contact_force"] = this->internal_force.get(); real_nodal_fields["normal_force"] = this->normal_force.get(); real_nodal_fields["tangential_force"] = this->tangential_force.get(); - real_nodal_fields["blocked_dofs"] = this->blocked_dofs.get(); real_nodal_fields["normals"] = this->normals.get(); real_nodal_fields["tangents"] = this->tangents.get(); real_nodal_fields["gaps"] = this->gaps.get(); real_nodal_fields["areas"] = this->nodal_area.get(); real_nodal_fields["tangential_traction"] = this->tangential_tractions.get(); std::shared_ptr field; if (padding_flag) { field = this->mesh.createNodalField(real_nodal_fields[field_name], group_name, 3); } else { field = this->mesh.createNodalField(real_nodal_fields[field_name], group_name); } return field; } /* -------------------------------------------------------------------------- */ std::shared_ptr ContactMechanicsModel::createNodalFieldInt(const std::string & field_name, const std::string & group_name, bool /*padding_flag*/) { std::shared_ptr field; if (field_name == "contact_state") { auto && func = std::make_unique>(); field = mesh.createNodalField(this->contact_state.get(), group_name); field = dumpers::FieldComputeProxy::createFieldCompute(field, std::move(func)); } return field; } /* -------------------------------------------------------------------------- */ Int ContactMechanicsModel::getNbData(const Array & elements, const SynchronizationTag & /*tag*/) const { Int size = 0; Int nb_nodes_per_element = 0; for (const Element & el : elements) { nb_nodes_per_element += Mesh::getNbNodesPerElement(el.type); } return size * nb_nodes_per_element; } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::packData(CommunicationBuffer & /*buffer*/, const Array & /*elements*/, const SynchronizationTag & /*tag*/) const { } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::unpackData(CommunicationBuffer & /*buffer*/, const Array & /*elements*/, const SynchronizationTag & /*tag*/) {} /* -------------------------------------------------------------------------- */ Int ContactMechanicsModel::getNbData(const Array & dofs, const SynchronizationTag & /*tag*/) const { UInt size = 0; return size * dofs.size(); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::packData(CommunicationBuffer & /*buffer*/, const Array & /*dofs*/, const SynchronizationTag & /*tag*/) const { } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::unpackData(CommunicationBuffer & /*buffer*/, const Array & /*dofs*/, const SynchronizationTag & /*tag*/) {} } // namespace akantu diff --git a/src/model/contact_mechanics/contact_mechanics_model.hh b/src/model/contact_mechanics/contact_mechanics_model.hh index 10ae62872..19198e481 100644 --- a/src/model/contact_mechanics/contact_mechanics_model.hh +++ b/src/model/contact_mechanics/contact_mechanics_model.hh @@ -1,361 +1,361 @@ /** * Copyright (©) 2010-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "boundary_condition.hh" #include "contact_detector.hh" #include "data_accessor.hh" #include "fe_engine.hh" #include "model.hh" /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_CONTACT_MECHANICS_MODEL_HH__ #define __AKANTU_CONTACT_MECHANICS_MODEL_HH__ namespace akantu { class Resolution; template class IntegratorGauss; template class ShapeLagrange; } // namespace akantu /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ class ContactMechanicsModel : public Model, public DataAccessor, public DataAccessor, public BoundaryCondition { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ using MyFEEngineType = FEEngineTemplate; public: ContactMechanicsModel( Mesh & mesh, Int dim = _all_dimensions, const ID & id = "contact_mechanics_model", std::shared_ptr dof_manager = nullptr, ModelType model_type = ModelType::_contact_mechanics_model); ~ContactMechanicsModel() override; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ protected: /// initialize completely the model void initFullImpl(const ModelOptions & options) override; /// allocate all vectors void initSolver(TimeStepSolverType /*unused*/, NonLinearSolverType /*unused*/) override; /// initialize all internal arrays for resolutions void initResolutions(); /// initialize the modelType void initModel() override; /// call back for the solver, computes the force residual void assembleResidual() override; /// get the type of matrix needed MatrixType getMatrixType(const ID & matrix_id) const override; /// callback for the solver, this assembles different matrices void assembleMatrix(const ID & matrix_id) override; /// callback for the solver, this assembles the stiffness matrix void assembleLumpedMatrix(const ID & matrix_id) override; /// get some default values for derived classes std::tuple getDefaultSolverID(const AnalysisMethod & method) override; ModelSolverOptions getDefaultSolverOptions(const TimeStepSolverType & type) const 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; /// function to print the containt of the class void printself(std::ostream & stream, int indent = 0) const override; /* ------------------------------------------------------------------------ */ /* Contact Detection */ /* ------------------------------------------------------------------------ */ public: void search(); void computeNodalAreas(GhostType ghost_type = _not_ghost); /* ------------------------------------------------------------------------ */ /* Contact Resolution */ /* ------------------------------------------------------------------------ */ public: /// register an empty contact resolution of a given type Resolution & registerNewResolution(const ID & res_name, const ID & res_type, const ID & opt_param); protected: /// register a resolution in the dynamic database Resolution & registerNewResolution(const ParserSection & res_section); /// read the resolution files to instantiate all the resolutions void instantiateResolutions(); /// save the parameters from previous state void savePreviousState(); /* ------------------------------------------------------------------------ */ /* Solver Interface */ /* ------------------------------------------------------------------------ */ public: /// assembles the contact stiffness matrix virtual void assembleStiffnessMatrix(); /// assembles the contant internal forces virtual void assembleInternalForces(); /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: FEEngine & getFEEngineBoundary(const ID & name = "") override; /* ------------------------------------------------------------------------ */ /* Dumpable interface */ /* ------------------------------------------------------------------------ */ public: std::shared_ptr createNodalFieldReal(const std::string & field_name, const std::string & group_name, bool padding_flag) override; std::shared_ptr createNodalFieldInt(const std::string & field_name, const std::string & group_name, bool padding_flag) override; std::shared_ptr createNodalFieldBool(const std::string & field_name, const std::string & group_name, bool padding_flag) override; /* ------------------------------------------------------------------------ */ /* Data Accessor inherited members */ /* ------------------------------------------------------------------------ */ public: Int getNbData(const Array & elements, const SynchronizationTag & tag) const override; void packData(CommunicationBuffer & buffer, const Array & elements, const SynchronizationTag & tag) const override; void unpackData(CommunicationBuffer & buffer, const Array & elements, const SynchronizationTag & tag) override; Int getNbData(const Array & dofs, const SynchronizationTag & tag) const override; void packData(CommunicationBuffer & buffer, const Array & dofs, const SynchronizationTag & tag) const override; void unpackData(CommunicationBuffer & buffer, const Array & dofs, const SynchronizationTag & tag) override; protected: /// contact detection class friend class ContactDetector; /// contact resolution class friend class Resolution; /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: /// get the ContactMechanicsModel::displacement vector AKANTU_GET_MACRO(Displacement, *displacement, Array &); /// get the ContactMechanicsModel::increment vector \warn only consistent /// if ContactMechanicsModel::setIncrementFlagOn has been called before AKANTU_GET_MACRO(Increment, *displacement_increment, Array &); /// get the ContactMechanics::internal_force vector (internal forces) AKANTU_GET_MACRO(InternalForce, *internal_force, Array &); /// get the ContactMechanicsModel::external_force vector (external forces) AKANTU_GET_MACRO(ExternalForce, *external_force, Array &); /// get the ContactMechanics::normal_force vector (normal forces) AKANTU_GET_MACRO(NormalForce, *normal_force, Array &); /// get the ContactMechanics::tangential_force vector (friction forces) AKANTU_GET_MACRO(TangentialForce, *tangential_force, Array &); /// get the ContactMechanics::traction vector (friction traction) AKANTU_GET_MACRO(TangentialTractions, *tangential_tractions, Array &); /// get the ContactMechanics::previous_tangential_tractions vector AKANTU_GET_MACRO(PreviousTangentialTractions, *previous_tangential_tractions, Array &); /// get the ContactMechanicsModel::force vector (external forces) Array & getForce() { AKANTU_DEBUG_WARNING("getForce was maintained for backward compatibility, " "use getExternalForce instead"); return *external_force; } /// get the ContactMechanics::blocked_dofs vector - AKANTU_GET_MACRO(BlockedDOFs, *blocked_dofs, Array &); + AKANTU_GET_MACRO(BlockedDOFs, *blocked_dofs, Array &); /// get the ContactMechanics::gaps (contact gaps) AKANTU_GET_MACRO(Gaps, *gaps, Array &); /// get the ContactMechanics::normals (normals on slave nodes) AKANTU_GET_MACRO(Normals, *normals, Array &); /// get the ContactMechanics::tangents (tangents on slave nodes) AKANTU_GET_MACRO(Tangents, *tangents, Array &); /// get the ContactMechanics::previous_tangents (tangents on slave nodes) AKANTU_GET_MACRO(PreviousTangents, *previous_tangents, Array &); /// get the ContactMechanics::areas (nodal areas) AKANTU_GET_MACRO(NodalArea, *nodal_area, Array &); /// get the ContactMechanics::previous_projections (previous_projections) AKANTU_GET_MACRO(PreviousProjections, *previous_projections, Array &); /// get the ContactMechanics::projections (projections) AKANTU_GET_MACRO(Projections, *projections, Array &); /// get the ContactMechanics::contact_state vector (no_contact/stick/slip /// state) AKANTU_GET_MACRO(ContactState, *contact_state, Array &); /// get the ContactMechanics::previous_master_elements AKANTU_GET_MACRO(PreviousMasterElements, *previous_master_elements, Array &); /// get contact detector AKANTU_GET_MACRO_NOT_CONST(ContactDetector, *detector, ContactDetector &); /// get the contact elements inline const Array & getContactElements() const { return contact_elements; } /// get the current positions of the nodes inline Array & getPositions() { return detector->getPositions(); } /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ private: /// tells if the resolutions are instantiated bool are_resolutions_instantiated; /// displacements array std::unique_ptr> displacement; /// increment of displacement std::unique_ptr> displacement_increment; /// contact forces array std::unique_ptr> internal_force; /// external forces array std::unique_ptr> external_force; /// normal force array std::unique_ptr> normal_force; /// friction force array std::unique_ptr> tangential_force; /// friction traction array std::unique_ptr> tangential_tractions; /// previous friction traction array std::unique_ptr> previous_tangential_tractions; /// boundary vector - std::unique_ptr> blocked_dofs; + std::unique_ptr> blocked_dofs; /// array to store gap between slave and master std::unique_ptr> gaps; /// array to store normals from master to slave std::unique_ptr> normals; /// array to store tangents on the master element std::unique_ptr> tangents; /// array to store previous tangents on the master element std::unique_ptr> previous_tangents; /// array to store nodal areas std::unique_ptr> nodal_area; /// array to store stick/slip state : std::unique_ptr> contact_state; /// array to store previous projections in covariant basis std::unique_ptr> previous_projections; // array to store projections in covariant basis std::unique_ptr> projections; /// contact detection std::unique_ptr detector; /// list of contact resolutions std::vector> resolutions; /// mapping between resolution name and resolution internal id std::map resolutions_names_to_id; /// array to store contact elements Array contact_elements; /// array to store previous master elements std::unique_ptr> previous_master_elements; }; } // namespace akantu /* ------------------------------------------------------------------------ */ /* inline functions */ /* ------------------------------------------------------------------------ */ #include "parser.hh" #include "resolution.hh" /* ------------------------------------------------------------------------ */ #endif /* __AKANTU_CONTACT_MECHANICS_MODEL_HH__ */ diff --git a/src/model/model_couplers/coupler_solid_contact.hh b/src/model/model_couplers/coupler_solid_contact.hh index 8649708b1..9c4831589 100644 --- a/src/model/model_couplers/coupler_solid_contact.hh +++ b/src/model/model_couplers/coupler_solid_contact.hh @@ -1,263 +1,260 @@ /** * Copyright (©) 2010-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "contact_mechanics_model.hh" #include "solid_mechanics_model.hh" #if defined(AKANTU_COHESIVE_ELEMENT) #include "solid_mechanics_model_cohesive.hh" #endif /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_COUPLER_SOLID_CONTACT_HH__ #define __AKANTU_COUPLER_SOLID_CONTACT_HH__ /* ------------------------------------------------------------------------ */ /* Coupling : Solid Mechanics / Contact Mechanics */ /* ------------------------------------------------------------------------ */ namespace akantu { /* -------------------------------------------------------------------------- */ template class CouplerSolidContactTemplate : public Model, public DataAccessor, public DataAccessor { static_assert( std::is_base_of::value, "SolidMechanicsModelType should be derived from SolidMechanicsModel"); /* ------------------------------------------------------------------------ */ /* Constructor/Destructor */ /* ------------------------------------------------------------------------ */ public: CouplerSolidContactTemplate( Mesh & mesh, Int dim = _all_dimensions, const ID & id = "coupler_solid_contact", std::shared_ptr dof_manager = nullptr); ~CouplerSolidContactTemplate() override; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ protected: /// initialize completely the model void initFullImpl(const ModelOptions & options) override; /// get some default values for derived classes std::tuple getDefaultSolverID(const AnalysisMethod & method) override; /* ------------------------------------------------------------------------ */ /* Solver Interface */ /* ------------------------------------------------------------------------ */ public: /// assembles the contact stiffness matrix virtual void assembleStiffnessMatrix(); /// assembles the contant internal forces virtual void assembleInternalForces(); #if defined(AKANTU_COHESIVE_ELEMENT) template ::value> * = nullptr> UInt checkCohesiveStress() { return solid->checkCohesiveStress(); } #endif - template - inline void applyBC(const FunctorType & func) { - solid->applyBC(func); + template inline void applyBC(FunctorType && func) { + solid->applyBC(std::forward(func)); } template - inline void applyBC(const FunctorType & func, - const std::string & group_name) { - solid->applyBC(func, group_name); + inline void applyBC(FunctorType && func, const std::string & group_name) { + solid->applyBC(std::forward(func), group_name); } template - inline void applyBC(const FunctorType & func, - const ElementGroup & element_group) { - solid->applyBC(func, element_group); + inline void applyBC(FunctorType && func, const ElementGroup & element_group) { + solid->applyBC(std::forward(func), element_group); } protected: /// callback for the solver, this adds f_{ext} - f_{int} to the residual void assembleResidual() override; /// callback for the solver, this adds f_{ext} or f_{int} to the residual void assembleResidual(const ID & residual_part) override; bool canSplitResidual() const override { return true; } /// get the type of matrix needed MatrixType getMatrixType(const ID & matrix_id) const override; /// callback for the solver, this assembles different matrices void assembleMatrix(const ID & matrix_id) override; /// callback for the solver, this assembles the stiffness matrix void assembleLumpedMatrix(const ID & matrix_id) override; /// callback for the solver, this is called at beginning of solve void predictor() override; /// callback for the solver, this is called at end of solve void corrector() override; /// callback for the solver, this is called at beginning of solve void beforeSolveStep() override; /// callback for the solver, this is called at end of solve void afterSolveStep(bool converged = true) override; /// callback for the model to instantiate the matricess when needed void initSolver(TimeStepSolverType time_step_solver_type, NonLinearSolverType non_linear_solver_type) override; /* ------------------------------------------------------------------------ */ /* Mass matrix for solid mechanics model */ /* ------------------------------------------------------------------------ */ public: /// assemble the lumped mass matrix void assembleMassLumped(); /// assemble the mass matrix for consistent mass resolutions void assembleMass(); protected: /// assemble the lumped mass matrix for local and ghost elements void assembleMassLumped(GhostType ghost_type); /// assemble the mass matrix for either _ghost or _not_ghost elements void assembleMass(GhostType ghost_type); protected: /* ------------------------------------------------------------------------ */ TimeStepSolverType getDefaultSolverType() const override; /* ------------------------------------------------------------------------ */ ModelSolverOptions getDefaultSolverOptions(const TimeStepSolverType & type) const override; public: bool isDefaultSolverExplicit() { return method == _explicit_lumped_mass; } /* ------------------------------------------------------------------------ */ public: // DataAccessor Int getNbData(const Array & /*elements*/, const SynchronizationTag & /*tag*/) const override { return 0; } void packData(CommunicationBuffer & /*buffer*/, const Array & /*elements*/, const SynchronizationTag & /*tag*/) const override {} void unpackData(CommunicationBuffer & /*buffer*/, const Array & /*elements*/, const SynchronizationTag & /*tag*/) override {} // DataAccessor nodes Int getNbData(const Array & /*nodes*/, const SynchronizationTag & /*tag*/) const override { return 0; } void packData(CommunicationBuffer & /*buffer*/, const Array & /*nodes*/, const SynchronizationTag & /*tag*/) const override {} void unpackData(CommunicationBuffer & /*buffer*/, const Array & /*nodes*/, const SynchronizationTag & /*tag*/) override {} /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: /// get the solid mechanics model #if defined(AKANTU_COHESIVE_ELEMENT) template ::value> * = nullptr> SolidMechanicsModelCohesive & getSolidMechanicsModelCohesive() { return *solid; } #endif template ::value> * = nullptr> SolidMechanicsModelType & getSolidMechanicsModel() { return *solid; } /// get the contact mechanics model AKANTU_GET_MACRO(ContactMechanicsModel, *contact, ContactMechanicsModel &) /* ------------------------------------------------------------------------ */ /* Dumpable interface */ /* ------------------------------------------------------------------------ */ public: std::shared_ptr createNodalFieldReal(const std::string & field_name, const std::string & group_name, bool padding_flag) override; std::shared_ptr createNodalFieldInt(const std::string & field_name, const std::string & group_name, bool padding_flag) override; std::shared_ptr createNodalFieldBool(const std::string & field_name, const std::string & group_name, bool padding_flag) override; std::shared_ptr createElementalField(const std::string & field_name, const std::string & group_name, bool padding_flag, Int spatial_dimension, ElementKind kind) override; void dump(const std::string & dumper_name) override; void dump(const std::string & dumper_name, Int step) override; void dump(const std::string & dumper_name, Real time, Int step) override; void dump() override; void dump(Int step) override; void dump(Real time, Int step) override; /* ------------------------------------------------------------------------ */ /* Members */ /* ------------------------------------------------------------------------ */ private: /// solid mechanics model std::unique_ptr solid; /// contact mechanics model std::unique_ptr contact; UInt step; }; using CouplerSolidContact = CouplerSolidContactTemplate; } // namespace akantu #include "coupler_solid_contact_tmpl.hh" #endif /* __COUPLER_SOLID_CONTACT_HH__ */ diff --git a/src/model/phase_field/phase_field_model.hh b/src/model/phase_field/phase_field_model.hh index 7f178fa32..1db482de4 100644 --- a/src/model/phase_field/phase_field_model.hh +++ b/src/model/phase_field/phase_field_model.hh @@ -1,367 +1,369 @@ /** * Copyright (©) 2018-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "boundary_condition.hh" #include "data_accessor.hh" #include "fe_engine.hh" #include "model.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_PHASE_FIELD_MODEL_HH__ #define __AKANTU_PHASE_FIELD_MODEL_HH__ namespace akantu { class PhaseField; class PhaseFieldSelector; template class IntegratorGauss; template class ShapeLagrange; } // namespace akantu /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ class PhaseFieldModel : public Model, public DataAccessor, public DataAccessor, public BoundaryCondition { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: using FEEngineType = FEEngineTemplate; PhaseFieldModel(Mesh & mesh, Int dim = _all_dimensions, const ID & id = "phase_field_model", std::shared_ptr dof_manager = nullptr, ModelType model_type = ModelType::_phase_field_model); ~PhaseFieldModel() override; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ protected: /// generic function to initialize everything ready for explicit dynamics void initFullImpl(const ModelOptions & options) override; /// initialize all internal array for phasefields void initPhaseFields(); /// allocate all vectors void initSolver(TimeStepSolverType /*unused*/, NonLinearSolverType /*unused*/) override; /// initialize the model void initModel() override; /// predictor void predictor() override; /// corrector void corrector() override; /// compute the heat flux void assembleResidual() override; /// get the type of matrix needed MatrixType getMatrixType(const ID & /*unused*/) const override; /// callback to assemble a Matrix void assembleMatrix(const ID & /*unused*/) override; /// callback to assemble a lumped Matrix void assembleLumpedMatrix(const ID & /*unused*/) override; /// function to print the containt of the class void printself(std::ostream & stream, int indent = 0) const override; protected: /* ------------------------------------------------------------------------ */ TimeStepSolverType getDefaultSolverType() const override; std::tuple getDefaultSolverID(const AnalysisMethod & method) override; ModelSolverOptions getDefaultSolverOptions(const TimeStepSolverType & type) const override; /* ------------------------------------------------------------------------ */ /* Materials (phase_field_model.cc) */ /* ------------------------------------------------------------------------ */ public: /// register an empty phasefield of a given type PhaseField & registerNewPhaseField(const ID & phase_name, const ID & phase_type, const ID & opt_param); /// reassigns phasefields depending on the phasefield selector void reassignPhaseField(); protected: /// register a phasefield in the dynamic database PhaseField & registerNewPhaseField(const ParserSection & phase_section); /// read the phasefield files to instantiate all the phasefields void instantiatePhaseFields(); /// set the element_id_by_phasefield and add the elements to the good /// phasefields void assignPhaseFieldToElements(const ElementTypeMapArray * filter = nullptr); /* ------------------------------------------------------------------------ */ /* Methods for static */ /* ------------------------------------------------------------------------ */ public: /// assembles the phasefield stiffness matrix virtual void assembleStiffnessMatrix(); /// compute the internal forces virtual void assembleInternalForces(); // compute the internal forces void assembleInternalForces(GhostType ghost_type); /* ------------------------------------------------------------------------ */ /* Methods for dynamic */ /* ------------------------------------------------------------------------ */ public: /// set the stable timestep void setTimeStep(Real time_step, const ID & solver_id = "") override; protected: /// 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; /* ------------------------------------------------------------------------ */ /* Data Accessor inherited members */ /* ------------------------------------------------------------------------ */ public: Int getNbData(const Array & elements, const SynchronizationTag & tag) const override; void packData(CommunicationBuffer & buffer, const Array & elements, const SynchronizationTag & tag) const override; void unpackData(CommunicationBuffer & buffer, const Array & elements, const SynchronizationTag & tag) override; Int getNbData(const Array & indexes, const SynchronizationTag & tag) const override; void packData(CommunicationBuffer & buffer, const Array & indexes, const SynchronizationTag & tag) const override; void unpackData(CommunicationBuffer & buffer, const Array & indexes, const SynchronizationTag & tag) override; /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ protected: /// Compute dissipated energy for an element type and phasefield index Real getEnergy(ElementType type, Idx index); public: /// return the damage array AKANTU_GET_MACRO_DEREF_PTR(Damage, damage); AKANTU_GET_MACRO_DEREF_PTR_NOT_CONST(Damage, damage); /// get the PhaseFieldModel::internal_force vector (internal forces) AKANTU_GET_MACRO_DEREF_PTR(InternalForce, internal_force); AKANTU_GET_MACRO_DEREF_PTR_NOT_CONST(InternalForce, internal_force); /// get the PhaseFieldModel::external_force vector (external forces) AKANTU_GET_MACRO_DEREF_PTR(ExternalForce, external_force); AKANTU_GET_MACRO_DEREF_PTR_NOT_CONST(ExternalForce, external_force); /// get the PhaseFieldModel::force vector (external forces) Array & getForce() { AKANTU_DEBUG_WARNING("getForce was maintained for backward compatibility, " "use getExternalForce instead"); return *external_force; } /// get the PhaseFieldModel::blocked_dofs vector AKANTU_GET_MACRO_DEREF_PTR(BlockedDOFs, blocked_dofs); + /// get the PhaseFieldModel::blocked_dofs vector + AKANTU_GET_MACRO_DEREF_PTR_NOT_CONST(BlockedDOFs, blocked_dofs); /// get an iterable on the phasefields inline decltype(auto) getPhaseFields(); /// get an iterable on the phasefields inline decltype(auto) getPhaseFields() const; /// get a particular phasefield (by phasefield index) inline PhaseField & getPhaseField(Idx mat_index); /// get a particular phasefield (by phasefield index) inline const PhaseField & getPhaseField(Idx mat_index) const; /// get a particular phasefield (by phasefield name) inline PhaseField & getPhaseField(const std::string & name); /// get a particular phasefield (by phasefield name) inline const PhaseField & getPhaseField(const std::string & name) const; /// get a particular phasefield id from is name inline Idx getPhaseFieldIndex(const std::string & name) const; /// give the number of phasefields inline Idx getNbPhaseFields() const { return phasefields.size(); } /// give the phasefield internal index from its id Idx getInternalIndexFromID(const ID & id) const; /** * @brief Returns the total dissipated energy * */ Real getEnergy(); /// Compute dissipated energy for an individual element Real getEnergy(const Element & element) { return getEnergy(element.type, element.element); } /// Compute dissipated energy for an element group Real getEnergy(const ID & group_id); AKANTU_GET_MACRO(PhaseFieldByElement, phasefield_index, const ElementTypeMapArray &); AKANTU_GET_MACRO(PhaseFieldLocalNumbering, phasefield_local_numbering, const ElementTypeMapArray &); /// vectors containing local material element index for each global element /// index AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(PhaseFieldByElement, phasefield_index, Idx); AKANTU_GET_MACRO_BY_ELEMENT_TYPE(PhaseFieldByElement, phasefield_index, Idx); AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(PhaseFieldLocalNumbering, phasefield_local_numbering, Idx); AKANTU_GET_MACRO_BY_ELEMENT_TYPE(PhaseFieldLocalNumbering, phasefield_local_numbering, Idx); AKANTU_GET_MACRO_NOT_CONST(PhaseFieldSelector, *phasefield_selector, PhaseFieldSelector &); AKANTU_SET_MACRO(PhaseFieldSelector, phasefield_selector, std::shared_ptr); FEEngine & getFEEngineBoundary(const ID & name = "") override; /* ------------------------------------------------------------------------ */ /* Dumpable Interface */ /* ------------------------------------------------------------------------ */ public: std::shared_ptr createNodalFieldReal(const std::string & field_name, const std::string & group_name, bool padding_flag) override; std::shared_ptr createNodalFieldBool(const std::string & field_name, const std::string & group_name, bool padding_flag) override; std::shared_ptr createElementalField(const std::string & field_name, const std::string & group_name, bool padding_flag, Int spatial_dimension, ElementKind kind) override; //! flatten a given phasefield internal field ElementTypeMapArray & flattenInternal(const std::string & field_name, ElementKind kind, GhostType ghost_type = _not_ghost); //! inverse operation of the flatten void inflateInternal(const std::string & field_name, const ElementTypeMapArray & field, ElementKind kind, GhostType ghost_type = _not_ghost); /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ private: /// number of iterations Int n_iter; /// damage array std::unique_ptr> damage; /// damage array at the previous time step std::unique_ptr> previous_damage; /// boundary vector std::unique_ptr> blocked_dofs; /// external force vector std::unique_ptr> external_force; /// residuals array std::unique_ptr> internal_force; /// Arrays containing the phasefield index for each element ElementTypeMapArray phasefield_index; /// Arrays containing the position in the element filter of the phasefield /// (phasefield's local numbering) ElementTypeMapArray phasefield_local_numbering; /// class defining of to choose a phasefield std::shared_ptr phasefield_selector; /// mapping between phasefield name and phasefield internal id std::map phasefields_names_to_id; /// list of used phasefields std::vector> phasefields; using flatten_internal_map = std::map, std::unique_ptr>>; /// tells if the phasefields are instantiated flatten_internal_map registered_internals; /// tells if the phasefield are instantiated bool are_phasefields_instantiated{false}; }; } // namespace akantu /* -------------------------------------------------------------------------- */ /* inline functions */ /* -------------------------------------------------------------------------- */ #include "parser.hh" #include "phasefield.hh" #include "phase_field_model_inline_impl.hh" /* -------------------------------------------------------------------------- */ #endif diff --git a/src/solver/sparse_matrix_aij.hh b/src/solver/sparse_matrix_aij.hh index e394b88c4..ec00cf4c3 100644 --- a/src/solver/sparse_matrix_aij.hh +++ b/src/solver/sparse_matrix_aij.hh @@ -1,192 +1,195 @@ /** * Copyright (©) 2010-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "aka_array.hh" #include "aka_common.hh" #include "sparse_matrix.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ #ifndef AKANTU_SPARSE_MATRIX_AIJ_HH_ #define AKANTU_SPARSE_MATRIX_AIJ_HH_ namespace akantu { class DOFManagerDefault; class TermsToAssemble; } // namespace akantu namespace akantu { class SparseMatrixAIJ : public SparseMatrix { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: SparseMatrixAIJ(DOFManagerDefault & dof_manager, const MatrixType & matrix_type, const ID & id = "sparse_matrix_aij"); SparseMatrixAIJ(const SparseMatrixAIJ & matrix, const ID & id = "sparse_matrix_aij"); ~SparseMatrixAIJ() override; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ public: /// remove the existing profile inline void clearProfile() override; /// add a non-zero element inline Idx add(Idx i, Idx j) override; /// set the matrix to 0 void set(Real val) override; /// assemble a local matrix in the sparse one inline void add(Idx i, Idx j, Real value) override; /// add a block of values inline void addValues(const Vector & is, const Vector & js, const Matrix & values, MatrixType values_type); /// set the size of the matrix void resize(Int size) { this->size_ = size; } /// modify the matrix to "remove" the blocked dof void applyBoundary(Real block_val = 1.) override; /// save the profil in a file using the MatrixMarket file format void saveProfile(const std::string & filename) const override; /// save the matrix in a file using the MatrixMarket file format void saveMatrix(const std::string & filename) const override; /// copy assuming the profile are the same virtual void copyContent(const SparseMatrix & matrix); /// multiply the matrix by a scalar void mul(Real alpha) override; /// Equivalent of *gemv in blas void matVecMul(const SolverVector & x, SolverVector & y, Real alpha = 1., Real beta = 0.) const override; void matVecMul(const Array & x, Array & y, Real alpha = 1., Real beta = 0.) const; /// copy the profile of another matrix void copyProfile(const SparseMatrix & other) override; /// Check if all entries are finite virtual bool isFinite() const override; /* ------------------------------------------------------------------------ */ /// accessor to A_{ij} - if (i, j) not present it returns 0 inline Real operator()(Idx i, Idx j) const override; /// accessor to A_{ij} - if (i, j) not present it fails, (i, j) should be /// first added to the profile inline Real & operator()(Idx i, Idx j) override; /// accessor to get the minimum value of A_{ij} inline Real min() override; protected: void addMeTo(SparseMatrix & B, Real alpha) const override; inline void addSymmetricValuesToSymmetric(const Vector & is, const Vector & js, const Matrix & values); inline void addUnsymmetricValuesToSymmetric(const Vector & is, const Vector & js, const Matrix & values); inline void addValuesToUnsymmetric(const Vector & is, const Vector & js, const Matrix & values); private: /// This is just to inline the addToMatrix function template void addMeToTemplated(MatrixType & B, Real alpha) const; /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: AKANTU_GET_MACRO_AUTO(IRN, irn); AKANTU_GET_MACRO_AUTO(JCN, jcn); AKANTU_GET_MACRO_AUTO(A, a); /// The release changes at each call of a function that changes the profile, /// it in increasing but could overflow so it should be checked as /// (my_release != release) and not as (my_release < release) AKANTU_GET_MACRO_AUTO(ProfileRelease, profile_release); AKANTU_GET_MACRO_AUTO(ValueRelease, value_release); Int getRelease() const override { return value_release; } protected: using KeyCOO = std::pair; using coordinate_list_map = std::unordered_map; /// get the pair corresponding to (i, j) inline KeyCOO key(Idx i, Idx j) const { if (this->matrix_type == _symmetric && (i > j)) { return std::make_pair(j, i); } return std::make_pair(i, j); } /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ private: DOFManagerDefault & dof_manager; /// row indexes Array irn; /// column indexes Array jcn; /// values : A[k] = Matrix[irn[k]][jcn[k]] Array a; /// Profile release Int profile_release{1}; /// Value release Int value_release{1}; /// map for (i, j) -> k correspondence coordinate_list_map irn_jcn_k; + + // need access to irn and jcn and a has not const + friend class SparseSolverMumps; }; } // namespace akantu /* -------------------------------------------------------------------------- */ /* inline functions */ /* -------------------------------------------------------------------------- */ #include "sparse_matrix_aij_inline_impl.hh" #endif /* AKANTU_SPARSE_MATRIX_AIJ_HH_ */ diff --git a/src/solver/sparse_solver_mumps.cc b/src/solver/sparse_solver_mumps.cc index 1bd08f6c9..f77d87d0b 100644 --- a/src/solver/sparse_solver_mumps.cc +++ b/src/solver/sparse_solver_mumps.cc @@ -1,386 +1,386 @@ /** * Copyright (©) 2010-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "aka_common.hh" #include "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) : 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; auto 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().data(); - this->mumps_data.jcn_loc = A.getJCN().data(); + this->mumps_data.irn_loc = A.irn.data(); + this->mumps_data.jcn_loc = A.jcn.data(); 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().data(); - this->mumps_data.jcn = A.getJCN().data(); + this->mumps_data.irn = A.irn.data(); + this->mumps_data.jcn = A.jcn.data(); } 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(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(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().data(); + this->mumps_data.a_loc = A.a.data(); } else { if (prank == 0) { - this->mumps_data.a = A.getA().data(); + this->mumps_data.a = A.a.data(); } } this->mumps_data.job = _smj_factorize; // factorize dmumps_c(&this->mumps_data); this->printError(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SparseSolverMumps::solve(Array & x, const Array & 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(this->dof_manager.getResidual()) .getGlobalVector()); this->solveInternal(); aka::as_type(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.data(); } this->mumps_data.job = _smj_solve; // solve dmumps_c(&this->mumps_data); this->printError(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SparseSolverMumps::printError() { Vector _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/synchronizer/communicator.cc b/src/synchronizer/communicator.cc index 8f73e9233..b39e01c69 100644 --- a/src/synchronizer/communicator.cc +++ b/src/synchronizer/communicator.cc @@ -1,181 +1,181 @@ /** * Copyright (©) 2010-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "communicator.hh" #if defined(AKANTU_USE_MPI) #include "mpi_communicator_data.hh" #endif /* -------------------------------------------------------------------------- */ namespace akantu { #if defined(AKANTU_USE_MPI) int MPICommunicatorData::is_externaly_initialized = 0; #endif Int InternalCommunicationRequest::counter = 0; /* -------------------------------------------------------------------------- */ InternalCommunicationRequest::InternalCommunicationRequest(Idx source, Idx dest) : source(source), destination(dest) { this->id = counter++; } /* -------------------------------------------------------------------------- */ InternalCommunicationRequest::~InternalCommunicationRequest() = default; /* -------------------------------------------------------------------------- */ void InternalCommunicationRequest::printself(std::ostream & stream, int indent) const { std::string space(indent, AKANTU_INDENT); stream << space << "CommunicationRequest [" << std::endl; stream << space << " + id : " << id << std::endl; stream << space << " + source : " << source << std::endl; stream << space << " + destination : " << destination << std::endl; stream << space << "]" << std::endl; } /* -------------------------------------------------------------------------- */ Communicator::~Communicator() { auto * event = new FinalizeCommunicatorEvent(*this); this->sendEvent(*event); delete event; } /* -------------------------------------------------------------------------- */ Communicator & Communicator::getStaticCommunicator() { AKANTU_DEBUG_IN(); if (!static_communicator) { int nb_args = 0; char ** null = nullptr; static_communicator = std::make_unique(nb_args, null, private_member{}); } AKANTU_DEBUG_OUT(); return *static_communicator; } /* -------------------------------------------------------------------------- */ Communicator & Communicator::getStaticCommunicator(int & argc, char **& argv) { if (!static_communicator) { static_communicator = std::make_unique(argc, argv, private_member{}); } return getStaticCommunicator(); } } // namespace akantu #ifdef AKANTU_USE_MPI #include "communicator_mpi_inline_impl.hh" #else #include "communicator_dummy_inline_impl.hh" #endif namespace akantu { /* -------------------------------------------------------------------------- */ /* Template instantiation */ /* -------------------------------------------------------------------------- */ #define AKANTU_COMM_INSTANTIATE(T) \ template void Communicator::probe(Int sender, Int tag, \ CommunicationStatus & status) const; \ template bool Communicator::asyncProbe( \ Int sender, Int tag, CommunicationStatus & status) const; \ template void Communicator::sendImpl( \ const T * buffer /*NOLINT*/, Int size, Int receiver, Int tag, \ const CommunicationMode & mode) const; \ template void Communicator::receiveImpl(T * buffer /*NOLINT*/, Int size, \ Int sender, Int tag) const; \ template CommunicationRequest Communicator::asyncSendImpl( \ const T * buffer /*NOLINT*/, Int size, Int receiver, Int tag, \ const CommunicationMode & mode) const; \ template CommunicationRequest Communicator::asyncReceiveImpl( \ T * buffer /* NOLINT */, Int size, Int sender, Int tag) const; \ template void Communicator::allGatherImpl(T * values /*NOLINT*/, \ int nb_values) const; \ template void Communicator::allGatherVImpl(T * values /*NOLINT*/, \ - int * nb_values) const; \ + const int * nb_values) const; \ template void Communicator::gatherImpl(T * values /*NOLINT*/, \ int nb_values, int root) const; \ template void Communicator::gatherImpl( \ T * values /*NOLINT*/, int nb_values, T * gathered /*NOLINT*/, \ int nb_gathered) const; \ template void Communicator::gatherVImpl(T * values /*NOLINT*/, \ int * nb_values, int root) const; \ template void Communicator::broadcastImpl(T * values /*NOLINT*/, \ int nb_values, int root) const; \ template void Communicator::allReduceImpl( \ T * values /*NOLINT*/, int nb_values, SynchronizerOperation op) const; \ template void Communicator::scanImpl(T * values /*NOLINT*/, \ T * /*NOLINT*/, int nb_values, \ SynchronizerOperation op) const; \ template void Communicator::exclusiveScanImpl( \ T * values /*NOLINT*/, T * /*NOLINT*/, int nb_values, \ SynchronizerOperation op) const #define MIN_MAX_REAL SCMinMaxLoc #if !defined(DOXYGEN) AKANTU_COMM_INSTANTIATE(bool); AKANTU_COMM_INSTANTIATE(Real); AKANTU_COMM_INSTANTIATE(UInt); AKANTU_COMM_INSTANTIATE(Int); AKANTU_COMM_INSTANTIATE(char); AKANTU_COMM_INSTANTIATE(NodeFlag); AKANTU_COMM_INSTANTIATE(MIN_MAX_REAL); AKANTU_COMM_INSTANTIATE(std::size_t); #if AKANTU_INTEGER_SIZE > 4 AKANTU_COMM_INSTANTIATE(int); #endif #endif // template void Communicator::send>( // SCMinMaxLoc * buffer, Int size, Int receiver, Int tag); // template void Communicator::receive>( // SCMinMaxLoc * buffer, Int size, Int sender, Int tag); // template CommunicationRequest // Communicator::asyncSend>( // SCMinMaxLoc * buffer, Int size, Int receiver, Int tag); // template CommunicationRequest // Communicator::asyncReceive>( // SCMinMaxLoc * buffer, Int size, Int sender, Int tag); // template void Communicator::probe>( // Int sender, Int tag, CommunicationStatus & status); // template void Communicator::allGather>( // SCMinMaxLoc * values, int nb_values); // template void Communicator::allGatherV>( // SCMinMaxLoc * values, int * nb_values); // template void Communicator::gather>( // SCMinMaxLoc * values, int nb_values, int root); // template void Communicator::gatherV>( // SCMinMaxLoc * values, int * nb_values, int root); // template void Communicator::broadcast>( // SCMinMaxLoc * values, int nb_values, int root); // template void Communicator::allReduce>( // SCMinMaxLoc * values, int nb_values, // const SynchronizerOperation & op); } // namespace akantu diff --git a/src/synchronizer/communicator.hh b/src/synchronizer/communicator.hh index b5f3e5dc4..588edf086 100644 --- a/src/synchronizer/communicator.hh +++ b/src/synchronizer/communicator.hh @@ -1,539 +1,540 @@ /** * Copyright (©) 2010-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "aka_array.hh" #include "aka_common.hh" #include "aka_event_handler_manager.hh" #include "communication_buffer.hh" #include "communication_request.hh" #include "communicator_event_handler.hh" /* -------------------------------------------------------------------------- */ #ifndef AKANTU_STATIC_COMMUNICATOR_HH_ #define AKANTU_STATIC_COMMUNICATOR_HH_ namespace akantu { namespace debug { class CommunicationException : public Exception { public: CommunicationException() : Exception("An exception happen during a communication process.") {} }; } // namespace debug /// @enum SynchronizerOperation reduce operation that the synchronizer can /// perform enum class SynchronizerOperation { _sum, _min, _max, _prod, _land, _band, _lor, _bor, _lxor, _bxor, _min_loc, _max_loc, _null }; enum class CommunicationMode { _auto, _synchronous, _ready }; namespace { int _any_source = -1; } } // namespace akantu namespace akantu { struct CommunicatorInternalData { virtual ~CommunicatorInternalData() = default; }; /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ class Communicator : public EventHandlerManager { struct private_member {}; /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: Communicator(int & argc, char **& argv, const private_member & /*m*/); Communicator(const private_member & /*unused*/ = private_member{}); ~Communicator() override; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ public: /* ------------------------------------------------------------------------ */ /* Point to Point */ /* ------------------------------------------------------------------------ */ template void probe(Int sender, Int tag, CommunicationStatus & status) const; template bool asyncProbe(Int sender, Int tag, CommunicationStatus & status) const; /* ------------------------------------------------------------------------ */ template inline void receive(Array & values, Int sender, Int tag) const { return this->receiveImpl( values.data(), values.size() * values.getNbComponent(), sender, tag); } template inline void receive(std::vector & values, Int sender, Int tag) const { return this->receiveImpl(values.data(), values.size(), sender, tag); } template inline void receive(Tensor & values, Int sender, Int tag, std::enable_if_t::value> * /*unused*/ = nullptr) const { return this->receiveImpl(values.data(), values.size(), sender, tag); } inline void receive(CommunicationBufferTemplated & values, Int sender, Int tag) const { return this->receiveImpl(values.data(), values.size(), sender, tag); } inline void receive(CommunicationBufferTemplated & values, Int sender, Int tag) const { CommunicationStatus status; this->probe(sender, tag, status); values.reserve(status.size()); return this->receiveImpl(values.data(), values.size(), sender, tag); } template inline void receive(T & values, Int sender, Int tag, std::enable_if_t::value> * /*unused*/ = nullptr) const { return this->receiveImpl(&values, 1, sender, tag); } /* ------------------------------------------------------------------------ */ template inline void send(const Array & values, Int receiver, Int tag, const CommunicationMode & mode = CommunicationMode::_auto) const { return this->sendImpl(values.data(), values.size() * values.getNbComponent(), receiver, tag, mode); } template inline void send(const std::vector & values, Int receiver, Int tag, const CommunicationMode & mode = CommunicationMode::_auto) const { return this->sendImpl(values.data(), values.size(), receiver, tag, mode); } template > * = nullptr> inline void send(const Tensor & values, Int receiver, Int tag, const CommunicationMode & mode = CommunicationMode::_auto) const { return this->sendImpl(values.data(), values.size(), receiver, tag, mode); } template inline void send(const CommunicationBufferTemplated & values, Int receiver, Int tag, const CommunicationMode & mode = CommunicationMode::_auto) const { return this->sendImpl(values.data(), values.size(), receiver, tag, mode); } template inline void send(const T & values, Int receiver, Int tag, const CommunicationMode & mode = CommunicationMode::_auto, std::enable_if_t::value> * /*unused*/ = nullptr) const { return this->sendImpl(&values, 1, receiver, tag, mode); } /* ------------------------------------------------------------------------ */ template inline CommunicationRequest asyncSend(const Array & values, Int receiver, Int tag, const CommunicationMode & mode = CommunicationMode::_auto) const { return this->asyncSendImpl(values.data(), values.size() * values.getNbComponent(), receiver, tag, mode); } template inline CommunicationRequest asyncSend(const std::vector & values, Int receiver, Int tag, const CommunicationMode & mode = CommunicationMode::_auto) const { return this->asyncSendImpl(values.data(), values.size(), receiver, tag, mode); } template inline CommunicationRequest asyncSend( const Tensor & values, Int receiver, Int tag, const CommunicationMode & mode = CommunicationMode::_auto, std::enable_if_t> * /*unused*/ = nullptr) const { return this->asyncSendImpl(values.data(), values.size(), receiver, tag, mode); } template inline CommunicationRequest asyncSend(const CommunicationBufferTemplated & values, Int receiver, Int tag, const CommunicationMode & mode = CommunicationMode::_auto) const { return this->asyncSendImpl(values.data(), values.size(), receiver, tag, mode); } template inline CommunicationRequest asyncSend(const T & values, Int receiver, Int tag, const CommunicationMode & mode = CommunicationMode::_auto, std::enable_if_t::value> * /*unused*/ = nullptr) const { return this->asyncSendImpl(&values, 1, receiver, tag, mode); } /* ------------------------------------------------------------------------ */ template inline CommunicationRequest asyncReceive(Array & values, Int sender, Int tag) const { return this->asyncReceiveImpl( values.data(), values.size() * values.getNbComponent(), sender, tag); } template inline CommunicationRequest asyncReceive(std::vector & values, Int sender, Int tag) const { return this->asyncReceiveImpl(values.data(), values.size(), sender, tag); } template >> inline CommunicationRequest asyncReceive(Tensor & values, Int sender, Int tag) const { return this->asyncReceiveImpl(values.data(), values.size(), sender, tag); } template inline CommunicationRequest asyncReceive(CommunicationBufferTemplated & values, Int sender, Int tag) const { return this->asyncReceiveImpl(values.data(), values.size(), sender, tag); } /* ------------------------------------------------------------------------ */ /* Collectives */ /* ------------------------------------------------------------------------ */ template inline void allReduce(Array & values, SynchronizerOperation op = SynchronizerOperation::_sum) const { this->allReduceImpl(values.data(), values.size() * values.getNbComponent(), op); } template inline void allReduce(Eigen::MatrixBase & values, SynchronizerOperation op = SynchronizerOperation::_sum) const { this->allReduceImpl(values.derived().data(), values.size(), op); } template inline void allReduce(T & values, SynchronizerOperation op = SynchronizerOperation::_sum, std::enable_if_t::value> * /*unused*/ = nullptr) const { this->allReduceImpl(&values, 1, op); } template inline void scan(Array & values, SynchronizerOperation op = SynchronizerOperation::_sum) const { this->scanImpl(values.data(), values.size() * values.getNbComponent(), op); } template inline void scan( Tensor & values, SynchronizerOperation op, std::enable_if_t> * /*unused*/ = nullptr) const { this->scanImpl(values.data(), values.data(), values.size(), op); } template inline void scan(T & values, SynchronizerOperation op = SynchronizerOperation::_sum, std::enable_if_t::value> * /*unused*/ = nullptr) const { this->scanImpl(&values, &values, 1, op); } template inline void exclusiveScan(Array & values, SynchronizerOperation op = SynchronizerOperation::_sum) const { this->exclusiveScanImpl(values.data(), values.data(), values.size() * values.getNbComponent(), op); } template inline void exclusiveScan(Tensor & values, SynchronizerOperation op = SynchronizerOperation::_sum, std::enable_if_t> * = nullptr) const { this->exclusiveScanImpl(values.data(), values.size(), op); } template inline void exclusiveScan(T & values, SynchronizerOperation op = SynchronizerOperation::_sum, std::enable_if_t::value> * /*unused*/ = nullptr) const { this->exclusiveScanImpl(&values, &values, 1, op); } template inline void exclusiveScan(T & values, T & result, SynchronizerOperation op = SynchronizerOperation::_sum, std::enable_if_t::value> * /*unused*/ = nullptr) const { this->exclusiveScanImpl(&values, &result, 1, op); } /* ------------------------------------------------------------------------ */ template inline void allGather(Array & values) const { AKANTU_DEBUG_ASSERT(getNbProc() == values.size(), "The array size is not correct"); this->allGatherImpl(values.data(), values.getNbComponent()); } template >> inline void allGather(Tensor & values) const { AKANTU_DEBUG_ASSERT(values.size() / getNbProc() > 0, "The vector size is not correct"); this->allGatherImpl(values.data(), values.size() / getNbProc()); } /* ------------------------------------------------------------------------ */ template inline void allGatherV(Array & values, const Array & sizes) const { this->allGatherVImpl(values.data(), sizes.data()); } /* ------------------------------------------------------------------------ */ template inline void reduce(Array & values, SynchronizerOperation op, int root = 0) const { this->reduceImpl(values.data(), values.size() * values.getNbComponent(), op, root); } /* ------------------------------------------------------------------------ */ template inline void gather( Tensor & values, int root = 0, std::enable_if_t> * /*unused*/ = nullptr) const { this->gatherImpl(values.data(), values.getNbComponent(), root); } template inline void gather(T values, int root = 0, std::enable_if_t::value> * /*unused*/ = nullptr) const { this->gatherImpl(&values, 1, root); } /* ------------------------------------------------------------------------ */ template inline void gather( Tensor & values, Array & gathered, std::enable_if_t> * /*unused*/ = nullptr) const { AKANTU_DEBUG_ASSERT(values.size() == gathered.getNbComponent(), "The array size is not correct"); gathered.resize(getNbProc()); this->gatherImpl(values.data(), values.size(), gathered.data(), gathered.getNbComponent()); } template inline void gather(T values, Array & gathered, std::enable_if_t::value> * /*unused*/ = nullptr) const { this->gatherImpl(&values, 1, gathered.data(), 1); } /* ------------------------------------------------------------------------ */ template inline void gatherV(Array & values, const Array & sizes, int root = 0) const { this->gatherVImpl(values.data(), sizes.data(), root); } /* ------------------------------------------------------------------------ */ template inline void broadcast(Array & values, int root = 0) const { this->broadcastImpl(values.data(), values.size() * values.getNbComponent(), root); } template inline void broadcast(std::vector & values, int root = 0) const { this->broadcastImpl(values.data(), values.size(), root); } inline void broadcast(CommunicationBufferTemplated & buffer, int root = 0) const { this->broadcastImpl(buffer.data(), buffer.size(), root); } inline void broadcast(CommunicationBufferTemplated & buffer, int root = 0) const { auto buffer_size = buffer.size(); this->broadcastImpl(&buffer_size, 1, root); if (whoAmI() != root) { buffer.reserve(buffer_size); } if (buffer_size == 0) { return; } this->broadcastImpl(buffer.data(), buffer.size(), root); } template inline void broadcast(T & values, int root = 0) const { this->broadcastImpl(&values, 1, root); } /* ------------------------------------------------------------------------ */ void barrier() const; CommunicationRequest asyncBarrier() const; /* ------------------------------------------------------------------------ */ /* Request handling */ /* ------------------------------------------------------------------------ */ static bool test(CommunicationRequest & request); static bool testAll(std::vector & request); static void wait(CommunicationRequest & request); static void waitAll(std::vector & requests); static Int waitAny(std::vector & requests); static inline void freeCommunicationRequest(CommunicationRequest & request); static inline void freeCommunicationRequest(std::vector & requests); template inline void receiveAnyNumber(std::vector & send_requests, MsgProcessor && processor, Int tag) const; protected: template void sendImpl(const T * buffer, Int size, Int receiver, Int tag, const CommunicationMode & mode = CommunicationMode::_auto) const; template void receiveImpl(T * buffer, Int size, Int sender, Int tag) const; template CommunicationRequest asyncSendImpl( const T * buffer, Int size, Int receiver, Int tag, const CommunicationMode & mode = CommunicationMode::_auto) const; template CommunicationRequest asyncReceiveImpl(T * buffer, Int size, Int sender, Int tag) const; template void allReduceImpl(T * values, int nb_values, SynchronizerOperation op) const; template void scanImpl(T * values, T * results, int nb_values, SynchronizerOperation op) const; template void exclusiveScanImpl(T * values, T * results, int nb_values, SynchronizerOperation op) const; template void allGatherImpl(T * values, int nb_values) const; - template void allGatherVImpl(T * values, int * nb_values) const; + template + void allGatherVImpl(T * values, const int * nb_values) const; template void reduceImpl(T * values, int nb_values, SynchronizerOperation op, int root = 0) const; template void gatherImpl(T * values, int nb_values, int root = 0) const; template void gatherImpl(T * values, int nb_values, T * gathered, int nb_gathered = 0) const; template void gatherVImpl(T * values, int * nb_values, int root = 0) const; template void broadcastImpl(T * values, int nb_values, int root = 0) const; /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: Int getNbProc() const; Int whoAmI() const; static Communicator & getStaticCommunicator(); static Communicator & getStaticCommunicator(int & argc, char **& argv); int getMaxTag() const; int getMinTag() const; AKANTU_GET_MACRO(CommunicatorData, (*communicator_data), decltype(auto)); /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ private: static std::unique_ptr static_communicator; protected: std::unique_ptr communicator_data; }; inline std::ostream & operator<<(std::ostream & stream, const CommunicationRequest & _this) { _this.printself(stream); return stream; } } // namespace akantu #include "communicator_inline_impl.hh" #endif /* AKANTU_STATIC_COMMUNICATOR_HH_ */ diff --git a/src/synchronizer/communicator_mpi_inline_impl.hh b/src/synchronizer/communicator_mpi_inline_impl.hh index 1da7dd25a..3c5d1e2c8 100644 --- a/src/synchronizer/communicator_mpi_inline_impl.hh +++ b/src/synchronizer/communicator_mpi_inline_impl.hh @@ -1,499 +1,499 @@ /** * Copyright (©) 2017-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "aka_iterators.hh" #include "communicator.hh" #include "mpi_communicator_data.hh" /* -------------------------------------------------------------------------- */ #include #include #include #include /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ #if (defined(__GNUC__) || defined(__GNUG__)) #if AKA_GCC_VERSION < 60000 namespace std { template <> struct hash { using argument_type = akantu::SynchronizerOperation; size_t operator()(const argument_type & e) const noexcept { auto ue = underlying_type_t(e); return uh(ue); } private: const hash> uh{}; }; } // namespace std #endif #endif namespace akantu { class CommunicationRequestMPI : public InternalCommunicationRequest { public: CommunicationRequestMPI(Idx source, Idx dest) : InternalCommunicationRequest(source, dest), request(std::make_unique()) {} MPI_Request & getMPIRequest() { return *request; }; private: std::unique_ptr request; }; namespace { template inline MPI_Datatype getMPIDatatype(); MPI_Op getMPISynchronizerOperation(SynchronizerOperation op) { std::unordered_map _operations{ {SynchronizerOperation::_sum, MPI_SUM}, {SynchronizerOperation::_min, MPI_MIN}, {SynchronizerOperation::_max, MPI_MAX}, {SynchronizerOperation::_prod, MPI_PROD}, {SynchronizerOperation::_land, MPI_LAND}, {SynchronizerOperation::_band, MPI_BAND}, {SynchronizerOperation::_lor, MPI_LOR}, {SynchronizerOperation::_bor, MPI_BOR}, {SynchronizerOperation::_lxor, MPI_LXOR}, {SynchronizerOperation::_bxor, MPI_BXOR}, {SynchronizerOperation::_min_loc, MPI_MINLOC}, {SynchronizerOperation::_max_loc, MPI_MAXLOC}, {SynchronizerOperation::_null, MPI_OP_NULL}}; return _operations[op]; } template MPI_Datatype inline getMPIDatatype() { return MPI_DATATYPE_NULL; } #define SPECIALIZE_MPI_DATATYPE(type, mpi_type) \ template <> MPI_Datatype inline getMPIDatatype() { return mpi_type; } #define COMMA , SPECIALIZE_MPI_DATATYPE(char, MPI_CHAR) SPECIALIZE_MPI_DATATYPE(std::uint8_t, MPI_UINT8_T) SPECIALIZE_MPI_DATATYPE(float, MPI_FLOAT) SPECIALIZE_MPI_DATATYPE(double, MPI_DOUBLE) SPECIALIZE_MPI_DATATYPE(long double, MPI_LONG_DOUBLE) SPECIALIZE_MPI_DATATYPE(signed int, MPI_INT) SPECIALIZE_MPI_DATATYPE(unsigned int, MPI_UNSIGNED) SPECIALIZE_MPI_DATATYPE(signed long int, MPI_LONG) SPECIALIZE_MPI_DATATYPE(unsigned long int, MPI_UNSIGNED_LONG) SPECIALIZE_MPI_DATATYPE(signed long long int, MPI_LONG_LONG) SPECIALIZE_MPI_DATATYPE(unsigned long long int, MPI_UNSIGNED_LONG_LONG) SPECIALIZE_MPI_DATATYPE(SCMinMaxLoc, MPI_DOUBLE_INT) SPECIALIZE_MPI_DATATYPE(SCMinMaxLoc, MPI_FLOAT_INT) SPECIALIZE_MPI_DATATYPE(bool, MPI_CXX_BOOL) template <> MPI_Datatype inline getMPIDatatype() { return getMPIDatatype>(); } inline int getMPISource(int src) { if (src == _any_source) { return MPI_ANY_SOURCE; } return src; } decltype(auto) convertRequests(std::vector & requests) { std::vector mpi_requests(requests.size()); for (auto && request_pair : zip(requests, mpi_requests)) { auto && req = std::get<0>(request_pair); auto && mpi_req = std::get<1>(request_pair); mpi_req = aka::as_type(req.getInternal()) .getMPIRequest(); } return mpi_requests; } } // namespace // this is ugly but shorten the code a lot #define MPIDATA \ (*reinterpret_cast(communicator_data.get())) /* -------------------------------------------------------------------------- */ /* Implementation */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ Communicator::Communicator(int & /*argc*/, char **& /*argv*/, const private_member & m) : Communicator(m) {} /* -------------------------------------------------------------------------- */ Communicator::Communicator(const private_member & /*unused*/) : communicator_data(std::make_unique()) {} /* -------------------------------------------------------------------------- */ template void Communicator::sendImpl(const T * buffer, Int size, Int receiver, Int tag, const CommunicationMode & mode) const { MPI_Comm communicator = MPIDATA.getMPICommunicator(); MPI_Datatype type = getMPIDatatype(); switch (mode) { case CommunicationMode::_auto: MPI_Send(buffer, size, type, receiver, tag, communicator); break; case CommunicationMode::_synchronous: MPI_Ssend(buffer, size, type, receiver, tag, communicator); break; case CommunicationMode::_ready: MPI_Rsend(buffer, size, type, receiver, tag, communicator); break; } } /* -------------------------------------------------------------------------- */ template void Communicator::receiveImpl(T * buffer, Int size, Int sender, Int tag) const { MPI_Comm communicator = MPIDATA.getMPICommunicator(); MPI_Status status; MPI_Datatype type = getMPIDatatype(); MPI_Recv(buffer, size, type, getMPISource(sender), tag, communicator, &status); } /* -------------------------------------------------------------------------- */ template CommunicationRequest Communicator::asyncSendImpl(const T * buffer, Int size, Int receiver, Int tag, const CommunicationMode & mode) const { MPI_Comm communicator = MPIDATA.getMPICommunicator(); auto * request = new CommunicationRequestMPI(whoAmI(), receiver); MPI_Request & req = request->getMPIRequest(); MPI_Datatype type = getMPIDatatype(); switch (mode) { case CommunicationMode::_auto: MPI_Isend(buffer, size, type, receiver, tag, communicator, &req); break; case CommunicationMode::_synchronous: MPI_Issend(buffer, size, type, receiver, tag, communicator, &req); break; case CommunicationMode::_ready: MPI_Irsend(buffer, size, type, receiver, tag, communicator, &req); break; } return std::shared_ptr(request); } /* -------------------------------------------------------------------------- */ template CommunicationRequest Communicator::asyncReceiveImpl(T * buffer, Int size, Int sender, Int tag) const { MPI_Comm communicator = MPIDATA.getMPICommunicator(); auto * request = new CommunicationRequestMPI(sender, whoAmI()); MPI_Datatype type = getMPIDatatype(); MPI_Request & req = request->getMPIRequest(); MPI_Irecv(buffer, size, type, getMPISource(sender), tag, communicator, &req); return std::shared_ptr(request); } /* -------------------------------------------------------------------------- */ template void Communicator::probe(Int sender, Int tag, CommunicationStatus & status) const { MPI_Comm communicator = MPIDATA.getMPICommunicator(); MPI_Status mpi_status; MPI_Probe(getMPISource(sender), tag, communicator, &mpi_status); MPI_Datatype type = getMPIDatatype(); int count; MPI_Get_count(&mpi_status, type, &count); status.setSource(mpi_status.MPI_SOURCE); status.setTag(mpi_status.MPI_TAG); status.setSize(count); } /* -------------------------------------------------------------------------- */ template bool Communicator::asyncProbe(Int sender, Int tag, CommunicationStatus & status) const { MPI_Comm communicator = MPIDATA.getMPICommunicator(); MPI_Status mpi_status; int test; MPI_Iprobe(getMPISource(sender), tag, communicator, &test, &mpi_status); if (not test) { return false; } MPI_Datatype type = getMPIDatatype(); int count; MPI_Get_count(&mpi_status, type, &count); status.setSource(mpi_status.MPI_SOURCE); status.setTag(mpi_status.MPI_TAG); status.setSize(count); return true; } /* -------------------------------------------------------------------------- */ bool Communicator::test(CommunicationRequest & request) { MPI_Status status; int flag; auto & req_mpi = aka::as_type(request.getInternal()); MPI_Request & req = req_mpi.getMPIRequest(); MPI_Test(&req, &flag, &status); return flag != 0; } /* -------------------------------------------------------------------------- */ bool Communicator::testAll(std::vector & requests) { // int are_finished; // auto && mpi_requests = convertRequests(requests); // MPI_Testall(mpi_requests.size(), mpi_requests.data(), &are_finished, // MPI_STATUSES_IGNORE); // return are_finished; for (auto & request : requests) { if (not test(request)) { return false; } } return true; } /* -------------------------------------------------------------------------- */ void Communicator::wait(CommunicationRequest & request) { MPI_Status status; auto & req_mpi = aka::as_type(request.getInternal()); MPI_Request & req = req_mpi.getMPIRequest(); MPI_Wait(&req, &status); } /* -------------------------------------------------------------------------- */ void Communicator::waitAll(std::vector & requests) { auto && mpi_requests = convertRequests(requests); MPI_Waitall(mpi_requests.size(), mpi_requests.data(), MPI_STATUSES_IGNORE); } /* -------------------------------------------------------------------------- */ Int Communicator::waitAny(std::vector & requests) { auto && mpi_requests = convertRequests(requests); int pos; MPI_Waitany(mpi_requests.size(), mpi_requests.data(), &pos, MPI_STATUSES_IGNORE); if (pos != MPI_UNDEFINED) { return pos; } return -1; } /* -------------------------------------------------------------------------- */ void Communicator::barrier() const { MPI_Comm communicator = MPIDATA.getMPICommunicator(); MPI_Barrier(communicator); } /* -------------------------------------------------------------------------- */ CommunicationRequest Communicator::asyncBarrier() const { MPI_Comm communicator = MPIDATA.getMPICommunicator(); auto * request = new CommunicationRequestMPI(0, 0); MPI_Request & req = request->getMPIRequest(); MPI_Ibarrier(communicator, &req); return std::shared_ptr(request); } /* -------------------------------------------------------------------------- */ template void Communicator::reduceImpl(T * values, int nb_values, SynchronizerOperation op, int root) const { MPI_Comm communicator = MPIDATA.getMPICommunicator(); MPI_Datatype type = getMPIDatatype(); MPI_Reduce(MPI_IN_PLACE, values, nb_values, type, getMPISynchronizerOperation(op), root, communicator); } /* -------------------------------------------------------------------------- */ template void Communicator::allReduceImpl(T * values, int nb_values, SynchronizerOperation op) const { MPI_Comm communicator = MPIDATA.getMPICommunicator(); MPI_Datatype type = getMPIDatatype(); MPI_Allreduce(MPI_IN_PLACE, values, nb_values, type, getMPISynchronizerOperation(op), communicator); } /* -------------------------------------------------------------------------- */ template void Communicator::scanImpl(T * values, T * result, int nb_values, SynchronizerOperation op) const { MPI_Comm communicator = MPIDATA.getMPICommunicator(); MPI_Datatype type = getMPIDatatype(); if (values == result) { values = reinterpret_cast(MPI_IN_PLACE); } MPI_Scan(values, result, nb_values, type, getMPISynchronizerOperation(op), communicator); } /* -------------------------------------------------------------------------- */ template void Communicator::exclusiveScanImpl(T * values, T * result, int nb_values, SynchronizerOperation op) const { MPI_Comm communicator = MPIDATA.getMPICommunicator(); MPI_Datatype type = getMPIDatatype(); if (values == result) { values = reinterpret_cast(MPI_IN_PLACE); } MPI_Exscan(values, result, nb_values, type, getMPISynchronizerOperation(op), communicator); if (whoAmI() == 0) { result[0] = T(); } } /* -------------------------------------------------------------------------- */ template void Communicator::allGatherImpl(T * values, int nb_values) const { MPI_Comm communicator = MPIDATA.getMPICommunicator(); MPI_Datatype type = getMPIDatatype(); MPI_Allgather(MPI_IN_PLACE, nb_values, type, values, nb_values, type, communicator); } /* -------------------------------------------------------------------------- */ template -void Communicator::allGatherVImpl(T * values, int * nb_values) const { +void Communicator::allGatherVImpl(T * values, const int * nb_values) const { MPI_Comm communicator = MPIDATA.getMPICommunicator(); std::vector displs(getNbProc()); displs[0] = 0; for (int i = 1; i < getNbProc(); ++i) { displs[i] = displs[i - 1] + nb_values[i - 1]; } MPI_Datatype type = getMPIDatatype(); MPI_Allgatherv(MPI_IN_PLACE, *nb_values, type, values, nb_values, displs.data(), type, communicator); } /* -------------------------------------------------------------------------- */ template void Communicator::gatherImpl(T * values, int nb_values, int root) const { MPI_Comm communicator = MPIDATA.getMPICommunicator(); T * send_buf = nullptr; T * recv_buf = nullptr; if (whoAmI() == root) { send_buf = (T *)MPI_IN_PLACE; recv_buf = values; } else { send_buf = values; } MPI_Datatype type = getMPIDatatype(); MPI_Gather(send_buf, nb_values, type, recv_buf, nb_values, type, root, communicator); } /* -------------------------------------------------------------------------- */ template void Communicator::gatherImpl(T * values, int nb_values, T * gathered, int nb_gathered) const { MPI_Comm communicator = MPIDATA.getMPICommunicator(); T * send_buf = values; T * recv_buf = gathered; if (nb_gathered == 0) { nb_gathered = nb_values; } MPI_Datatype type = getMPIDatatype(); MPI_Gather(send_buf, nb_values, type, recv_buf, nb_gathered, type, whoAmI(), communicator); } /* -------------------------------------------------------------------------- */ template void Communicator::gatherVImpl(T * values, int * nb_values, int root) const { MPI_Comm communicator = MPIDATA.getMPICommunicator(); int * displs = nullptr; auto psize = getNbProc(); auto prank = whoAmI(); if (prank == root) { displs = new int[psize]; displs[0] = 0; for (int i = 1; i < psize; ++i) { displs[i] = displs[i - 1] + nb_values[i - 1]; } } T * send_buf = nullptr; T * recv_buf = nullptr; if (prank == root) { send_buf = (T *)MPI_IN_PLACE; recv_buf = values; } else { send_buf = values; } MPI_Datatype type = getMPIDatatype(); MPI_Gatherv(send_buf, *nb_values, type, recv_buf, nb_values, displs, type, root, communicator); if (prank == root) { delete[] displs; } } /* -------------------------------------------------------------------------- */ template void Communicator::broadcastImpl(T * values, int nb_values, int root) const { MPI_Comm communicator = MPIDATA.getMPICommunicator(); MPI_Datatype type = getMPIDatatype(); MPI_Bcast(values, nb_values, type, root, communicator); } /* -------------------------------------------------------------------------- */ int Communicator::getMaxTag() const { return MPIDATA.getMaxTag(); } int Communicator:: getMinTag() // NOLINT(readability-convert-member-functions-to-static) const { return 0; } /* -------------------------------------------------------------------------- */ Int Communicator::getNbProc() const { return MPIDATA.size(); } Int Communicator::whoAmI() const { return MPIDATA.rank(); } } // namespace akantu diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8824e71b4..3ff3a4053 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,62 +1,67 @@ #=============================================================================== # Copyright (©) 2010-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) # Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) # # This file is part of Akantu # # Akantu is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # # Akantu is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License along # with Akantu. If not, see . # # @section DESCRIPTION # #=============================================================================== 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_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() + +option(AKANTU_BENCHMARK OFF) +if(AKANTU_BENCHMARK) + add_subdirectory(benchmark) +endif() diff --git a/test/test_common/test_array.cc b/test/test_common/test_array.cc index 61393ecca..6f1eabcd5 100644 --- a/test/test_common/test_array.cc +++ b/test/test_common/test_array.cc @@ -1,410 +1,410 @@ /** * Copyright (©) 2017-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "test_gtest_utils.hh" /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ #include #include #include #include /* -------------------------------------------------------------------------- */ using namespace akantu; namespace { class NonTrivial { public: NonTrivial() = default; NonTrivial(int a) : a(a){}; bool operator==(const NonTrivial & rhs) { return a == rhs.a; } int a{0}; }; bool operator==(const int & a, const NonTrivial & rhs) { return a == rhs.a; } std::ostream & operator<<(std::ostream & stream, const NonTrivial & _this) { stream << _this.a; return stream; } /* -------------------------------------------------------------------------- */ using TestTypes = ::testing::Types; /* -------------------------------------------------------------------------- */ ::testing::AssertionResult AssertType(const char * /*a_expr*/, const char * /*b_expr*/, const std::type_info & a, const std::type_info & b) { if (std::type_index(a) == std::type_index(b)) return ::testing::AssertionSuccess(); return ::testing::AssertionFailure() << debug::demangle(a.name()) << " != " << debug::demangle(b.name()) << ") are different"; } /* -------------------------------------------------------------------------- */ template class ArrayConstructor : public ::testing::Test { protected: using type = T; void SetUp() override { type_str = debug::demangle(typeid(T).name()); } template decltype(auto) construct(P &&... params) { return std::make_unique>(std::forward

(params)...); } protected: std::string type_str; }; TYPED_TEST_SUITE(ArrayConstructor, TestTypes, ); TYPED_TEST(ArrayConstructor, ConstructDefault1) { auto array = this->construct(); EXPECT_EQ(0, array->size()); EXPECT_EQ(1, array->getNbComponent()); EXPECT_STREQ("", array->getID().c_str()); } TYPED_TEST(ArrayConstructor, ConstructDefault2) { auto array = this->construct(1000); EXPECT_EQ(1000, array->size()); EXPECT_EQ(1, array->getNbComponent()); EXPECT_STREQ("", array->getID().c_str()); } TYPED_TEST(ArrayConstructor, ConstructDefault3) { auto array = this->construct(1000, 10); EXPECT_EQ(1000, array->size()); EXPECT_EQ(10, array->getNbComponent()); EXPECT_STREQ("", array->getID().c_str()); } TYPED_TEST(ArrayConstructor, ConstructDefault4) { auto array = this->construct(1000, 10, "test"); EXPECT_EQ(1000, array->size()); EXPECT_EQ(10, array->getNbComponent()); EXPECT_STREQ("test", array->getID().c_str()); } TYPED_TEST(ArrayConstructor, ConstructDefault5) { auto array = this->construct(1000, 10, 1); EXPECT_EQ(1000, array->size()); EXPECT_EQ(10, array->getNbComponent()); EXPECT_EQ(1, array->operator()(10, 6)); EXPECT_STREQ("", array->getID().c_str()); } /* -------------------------------------------------------------------------- */ template class ArrayFixture : public ArrayConstructor { public: void SetUp() override { ArrayConstructor::SetUp(); array = this->construct(1000, 10); } void TearDown() override { array.reset(nullptr); } protected: std::unique_ptr> array; }; TYPED_TEST_SUITE(ArrayFixture, TestTypes, ); TYPED_TEST(ArrayFixture, Copy) { Array copy(*this->array); EXPECT_EQ(1000, copy.size()); EXPECT_EQ(10, copy.getNbComponent()); EXPECT_NE(this->array->data(), copy.data()); } TYPED_TEST(ArrayFixture, Set) { auto & arr = *(this->array); arr.set(12); EXPECT_EQ(12, arr(156, 5)); EXPECT_EQ(12, arr(520, 7)); EXPECT_EQ(12, arr(999, 9)); } TYPED_TEST(ArrayFixture, Resize) { auto & arr = *(this->array); auto * ptr = arr.data(); arr.resize(0); EXPECT_EQ(0, arr.size()); EXPECT_TRUE(arr.data() == nullptr or arr.data() == ptr); EXPECT_LE(0, arr.getAllocatedSize()); arr.resize(3000); EXPECT_EQ(3000, arr.size()); EXPECT_LE(3000, arr.getAllocatedSize()); ptr = arr.data(); arr.resize(0); EXPECT_EQ(0, arr.size()); EXPECT_TRUE(arr.data() == nullptr or arr.data() == ptr); EXPECT_LE(0, arr.getAllocatedSize()); } TYPED_TEST(ArrayFixture, PushBack) { auto & arr = *(this->array); auto * ptr = arr.data(); arr.resize(0); EXPECT_EQ(0, arr.size()); EXPECT_TRUE(arr.data() == nullptr or arr.data() == ptr); EXPECT_LE(0, arr.getAllocatedSize()); arr.resize(3000); EXPECT_EQ(3000, arr.size()); EXPECT_LE(3000, arr.getAllocatedSize()); ptr = arr.data(); arr.resize(0); EXPECT_EQ(0, arr.size()); EXPECT_TRUE(arr.data() == nullptr or arr.data() == ptr); EXPECT_LE(0, arr.getAllocatedSize()); } TYPED_TEST(ArrayFixture, ViewVectorDynamic) { auto && view = make_view(*this->array, 10); EXPECT_NO_THROW(view.begin()); { auto it = view.begin(); EXPECT_EQ(10, it->size()); EXPECT_PRED_FORMAT2(AssertType, typeid(*it), typeid(VectorProxy)); EXPECT_PRED_FORMAT2(AssertType, typeid(it[0]), typeid(VectorProxy)); } } TYPED_TEST(ArrayFixture, ViewVectorStatic) { auto && view = make_view<10>(*this->array); EXPECT_NO_THROW(view.begin()); { auto it = view.begin(); EXPECT_EQ(10, it->size()); EXPECT_PRED_FORMAT2(AssertType, typeid(*it), typeid(VectorProxy)); EXPECT_PRED_FORMAT2(AssertType, typeid(it[0]), typeid(VectorProxy)); } } TYPED_TEST(ArrayFixture, ViewMatrixStatic) { auto && view = make_view(*this->array, 2, 5); EXPECT_NO_THROW(view.begin()); { auto it = view.begin(); EXPECT_EQ(10, it->size()); EXPECT_EQ(2, it->size(0)); EXPECT_EQ(5, it->size(1)); EXPECT_PRED_FORMAT2(AssertType, typeid(*it), typeid(MatrixProxy)); EXPECT_PRED_FORMAT2(AssertType, typeid(it[0]), typeid(MatrixProxy)); } } TYPED_TEST(ArrayFixture, ViewMatrixDynamic) { auto && view = make_view<2, 5>(*this->array); EXPECT_NO_THROW(view.begin()); { auto it = view.begin(); EXPECT_EQ(10, it->size()); EXPECT_EQ(2, it->size(0)); EXPECT_EQ(5, it->size(1)); EXPECT_EQ(2, it->rows()); EXPECT_EQ(5, it->cols()); EXPECT_PRED_FORMAT2(AssertType, typeid(*it), typeid(MatrixProxy)); EXPECT_PRED_FORMAT2(AssertType, typeid(it[0]), typeid(MatrixProxy)); } } TYPED_TEST(ArrayFixture, ViewVectorWrong) { auto && view = make_view(*this->array, 11); EXPECT_THROW(view.begin(), debug::ArrayException); } TYPED_TEST(ArrayFixture, ViewMatrixWrong) { auto && view = make_view(*this->array, 3, 7); EXPECT_THROW(view.begin(), debug::ArrayException); } TYPED_TEST(ArrayFixture, ViewMatrixIter) { std::size_t count = 0; for (auto && mat : make_view(*this->array, 10, 10)) { EXPECT_EQ(100, mat.size()); EXPECT_EQ(10, mat.size(0)); EXPECT_EQ(10, mat.size(1)); EXPECT_PRED_FORMAT2(AssertType, typeid(mat), typeid(MatrixProxy)); ++count; } EXPECT_EQ(100, count); } TYPED_TEST(ArrayFixture, ConstViewVector) { const auto & carray = *this->array; auto && view = make_view(carray, 10); EXPECT_NO_THROW(view.begin()); { auto it = view.begin(); EXPECT_EQ(10, it->size()); EXPECT_PRED_FORMAT2(AssertType, typeid(*it), - typeid(VectorProxy)); + typeid(VectorProxy)); EXPECT_PRED_FORMAT2(AssertType, typeid(it[0]), - typeid(VectorProxy)); + typeid(VectorProxy)); } } TYPED_TEST(ArrayFixture, EnumerateArray) { this->array->set(12); const auto & carray = *this->array; auto && view = enumerate(make_view(carray, 2)); int i = 0; for (auto && data : view) { EXPECT_EQ(i, std::get<0>(data)); EXPECT_EQ(12, std::get<1>(data)[0]); EXPECT_EQ(12, std::get<1>(data)[1]); ++i; } } TYPED_TEST(ArrayFixture, ZipArray) { this->array->set(12); const auto & carray = *this->array; auto && view = zip(arange(carray.size() * carray.getNbComponent() / 2), make_view(carray, 2)); int i = 0; for (auto && data : view) { EXPECT_EQ(i, std::get<0>(data)); EXPECT_EQ(12, std::get<1>(data)[0]); ++i; } } TYPED_TEST(ArrayFixture, IteratorIncrement) { this->array->set(12); auto it = make_view(*this->array, this->array->getNbComponent()).begin() + 10; EXPECT_EQ(12, (*it)[0]); } TYPED_TEST(ArrayFixture, IteratorFixSize) { this->array->set(12); auto it = make_view<10>(*this->array).begin(); EXPECT_EQ(12, (*it)[0]); auto && vect = *it; vect(0) = 120; EXPECT_EQ(120, (*this->array)(0, 0)); } TYPED_TEST(ArrayFixture, IteratorBracket) { this->array->set(12); auto && vect = make_view(*this->array, this->array->getNbComponent()).begin()[10]; EXPECT_EQ(12, vect[0]); } TYPED_TEST(ArrayFixture, IteratorSimple) { this->array->set(12); auto it = this->array->begin(this->array->getNbComponent()); EXPECT_EQ(12, (*it)[0]); } TYPED_TEST(ArrayFixture, IteratorThrow) { this->array->set(12); EXPECT_THROW(this->array->begin(2 * this->array->getNbComponent()), debug::Exception); } TYPED_TEST(ArrayFixture, IteratorRange) { this->array->set(12); auto && view = make_view(*this->array, this->array->getNbComponent()); auto begin = view.begin(); auto end = view.end(); for (auto && data : range(begin, end)) { EXPECT_EQ(12, data[0]); } } TYPED_TEST(ArrayFixture, DynamicSizeIteratorFilter) { this->array->set(12); (*this->array)(3) = 13; (*this->array)(50) = 13; std::vector list_filter{3, 50}; auto && view = make_view(*this->array, 10); for (auto && data : filter(list_filter, view)) { EXPECT_EQ(13, data[0]); EXPECT_EQ(12, data[1]); } } TYPED_TEST(ArrayFixture, IteratorFilter) { this->array->set(12); (*this->array)(3) = 13; (*this->array)(50) = 13; std::vector list_filter{3, 50}; auto && view = make_view<10>(*this->array); for (auto && data : filter(list_filter, view)) { EXPECT_EQ(13, data[0]); EXPECT_EQ(12, data[1]); } } } // namespace diff --git a/test/test_mesh_utils/test_buildfacets/test_buildfacets.cc b/test/test_mesh_utils/test_buildfacets/test_buildfacets.cc index fce9027d2..654f1e28c 100644 --- a/test/test_mesh_utils/test_buildfacets/test_buildfacets.cc +++ b/test/test_mesh_utils/test_buildfacets/test_buildfacets.cc @@ -1,181 +1,184 @@ /** * @file test_buildfacets_triangle_3.cc * * @author Mauro Corrado * * @date creation: Fri Sep 18 2015 * @date last modification: Wed Nov 08 2017 * * @brief Test to check the building of the facets. Mesh with triangles * * * @section LICENSE * * Copyright (©) 2015-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "test_gtest_utils.hh" /* -------------------------------------------------------------------------- */ #include "aka_common.hh" #include "mesh.hh" #include "mesh_utils.hh" /* -------------------------------------------------------------------------- */ using namespace akantu; template class TestBuildFacetFixture : public ::testing::Test { public: static constexpr const ElementType type1 = std::tuple_element_t<0, types_>::value; static constexpr const ElementType type2 = std::tuple_element_t<1, types_>::value; static constexpr const size_t dim = ElementClass::getSpatialDimension(); void SetUp() override { mesh = std::make_unique(dim); mesh->read(std::to_string(type1) + ((type2 != _not_defined) ? std::to_string(type2) : "") + ".msh"); } void TearDown() override { mesh.reset(nullptr); } template void print_connection(const std::string & name, ElementType type, const List & list) { std::cout << name << std::endl; for (auto && outer : enumerate(list)) { std::cout << type << " " << std::get<0>(outer) << " connected to "; for (auto && inner : std::get<1>(outer)) { std::cout << inner << ", "; } std::cout << " " << std::endl; } } protected: std::unique_ptr mesh; }; template constexpr const ElementType TestBuildFacetFixture::type1; template constexpr const ElementType TestBuildFacetFixture::type2; template constexpr const size_t TestBuildFacetFixture::dim; template > struct element_pair { using type = std::tuple; }; template > using element_pair_t = typename element_pair::type; using buildfacets_types = ::testing::Types< element_pair_t<_element_type_triangle_3>, element_pair_t<_element_type_triangle_6>, element_pair_t<_element_type_quadrangle_4>, element_pair_t<_element_type_quadrangle_8>, element_pair_t<_element_type_tetrahedron_10>, element_pair_t<_element_type_hexahedron_8>, element_pair_t<_element_type_hexahedron_20>, element_pair_t<_element_type_pentahedron_6>, element_pair_t<_element_type_pentahedron_15>, element_pair_t<_element_type_quadrangle_4, _element_type_triangle_3>, element_pair_t<_element_type_quadrangle_8, _element_type_triangle_6>, element_pair_t<_element_type_hexahedron_8, _element_type_pentahedron_6>, element_pair_t<_element_type_hexahedron_20, _element_type_pentahedron_15>>; TYPED_TEST_SUITE(TestBuildFacetFixture, buildfacets_types, ); TYPED_TEST(TestBuildFacetFixture, Buildfacets) { const auto & mesh_facets = this->mesh->initMeshFacets("mesh_facets"); /* ------------------------------------------------------------------------ */ /* Element to Subelement testing */ /* ------------------------------------------------------------------------ */ const auto types_facet1{this->mesh->getAllFacetTypes(this->type1)}; std::set types_facet; for (auto type : types_facet1) { types_facet.insert(type); } if (this->type2 != _not_defined) { const auto types_facet2{this->mesh->getAllFacetTypes(this->type2)}; for (auto type : types_facet2) { types_facet.insert(type); } } for (auto type_facet : types_facet) { const auto & el_to_subel1 = mesh_facets.getElementToSubelement(type_facet); this->print_connection("ElementToSubelement1", type_facet, el_to_subel1); } const auto type_subfacet = this->mesh->getFacetType(*types_facet.begin()); const auto & el_to_subel2 = mesh_facets.getElementToSubelement(type_subfacet); this->print_connection("ElementToSubelement2", type_subfacet, el_to_subel2); if (this->dim == 3) { const auto type_subsubfacet = this->mesh->getFacetType(type_subfacet); const auto & el_to_subel3 = mesh_facets.getElementToSubelement(type_subsubfacet); this->print_connection("ElementToSubelement3", type_subsubfacet, el_to_subel3); } /* ------------------------------------------------------------------------ */ /* Subelement to Element testing */ /* ------------------------------------------------------------------------ */ std::cout << std::endl; const auto & subel_to_el1 = mesh_facets.getSubelementToElement(this->type1); this->print_connection("SubelementToElement1", this->type1, - MatrixProxy(subel_to_el1.data(), - subel_to_el1.getNbComponent(), - subel_to_el1.size())); + MatrixProxy( + subel_to_el1.data(), subel_to_el1.getNbComponent(), + subel_to_el1.size())); if (this->type2 != _not_defined) { const auto & subel_to_el1 = mesh_facets.getSubelementToElement(this->type2); - this->print_connection("SubelementToElement1", this->type2, - MatrixProxy(subel_to_el1.data(), - subel_to_el1.getNbComponent(), - subel_to_el1.size())); + this->print_connection( + "SubelementToElement1", this->type2, + MatrixProxy(subel_to_el1.data(), + subel_to_el1.getNbComponent(), + subel_to_el1.size())); } for (auto type_facet : types_facet) { auto && subel_to_el2 = mesh_facets.getSubelementToElement(type_facet); - this->print_connection("SubelementToElement2", type_facet, - MatrixProxy(subel_to_el2.data(), - subel_to_el2.getNbComponent(), - subel_to_el2.size())); + this->print_connection( + "SubelementToElement2", type_facet, + MatrixProxy(subel_to_el2.data(), + subel_to_el2.getNbComponent(), + subel_to_el2.size())); } if (this->dim == 3) { const auto & subel_to_el3 = mesh_facets.getSubelementToElement(type_subfacet); - this->print_connection("SubelementToElement3", type_subfacet, - MatrixProxy(subel_to_el3.data(), - subel_to_el3.getNbComponent(), - subel_to_el3.size())); + this->print_connection( + "SubelementToElement3", type_subfacet, + MatrixProxy(subel_to_el3.data(), + subel_to_el3.getNbComponent(), + subel_to_el3.size())); } } diff --git a/test/test_model/patch_tests/patch_test_linear_fixture.hh b/test/test_model/patch_tests/patch_test_linear_fixture.hh index 969180b64..cfeb4365e 100644 --- a/test/test_model/patch_tests/patch_test_linear_fixture.hh +++ b/test/test_model/patch_tests/patch_test_linear_fixture.hh @@ -1,171 +1,172 @@ /** * Copyright (©) 2018-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "element_group.hh" #include "mesh_utils.hh" #include "model.hh" #include "test_gtest_utils.hh" /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ #ifndef AKANTU_PATCH_TEST_LINEAR_FIXTURE_HH_ #define AKANTU_PATCH_TEST_LINEAR_FIXTURE_HH_ //#define DEBUG_TEST using namespace akantu; template class TestPatchTestLinear : public ::testing::Test { public: static constexpr ElementType type = type_::value; static constexpr Int dim = ElementClass::getSpatialDimension(); virtual void SetUp() { mesh = std::make_unique(dim); mesh->read(std::to_string(type) + ".msh"); MeshUtils::buildFacets(*mesh); mesh->createBoundaryGroupFromGeometry(); model = std::make_unique(*mesh); } virtual void TearDown() { model.reset(nullptr); mesh.reset(nullptr); } - virtual void initModel(const AnalysisMethod &method, - const std::string &material_file) { + virtual void initModel(const AnalysisMethod & method, + const std::string & material_file) { debug::setDebugLevel(dblError); getStaticParser().parse(material_file); this->model->initFull(_analysis_method = method); this->applyBC(); if (method != _static) this->model->setTimeStep(0.8 * this->model->getStableTimeStep()); } virtual void applyBC() { - auto &boundary = this->model->getBlockedDOFs(); + auto & boundary = this->model->getBlockedDOFs(); - for (auto &eg : mesh->iterateElementGroups()) { - for (const auto &node : eg.getNodeGroup()) { + for (auto & eg : mesh->iterateElementGroups()) { + for (const auto & node : eg.getNodeGroup()) { for (Int s = 0; s < boundary.getNbComponent(); ++s) { boundary(node, s) = true; } } } } - virtual void applyBConDOFs(const Array &dofs) { - const auto &coordinates = this->mesh->getNodes(); - for (auto &eg : this->mesh->iterateElementGroups()) { - for (const auto &node : eg.getNodeGroup()) { + virtual void applyBConDOFs(Array & dofs) { + const auto & coordinates = this->mesh->getNodes(); + for (auto & eg : this->mesh->iterateElementGroups()) { + for (const auto & node : eg.getNodeGroup()) { this->setLinearDOF(dofs.begin(dofs.getNbComponent())[node], coordinates.begin(this->dim)[node]); } } } - template Matrix prescribed_gradient(const V &dof) { + template Matrix prescribed_gradient(const V & dof) { Matrix gradient(dof.getNbComponent(), dim); for (Int i = 0; i < gradient.rows(); ++i) { for (Int j = 0; j < gradient.cols(); ++j) { gradient(i, j) = alpha(i, j + 1); } } return gradient; } template - void checkGradient(const Gradient &gradient, const DOFs &dofs) { + void checkGradient(const Gradient & gradient, const DOFs & dofs) { auto pgrad = prescribed_gradient(dofs); - for (auto &grad : + for (auto & grad : make_view(gradient, gradient.getNbComponent() / dim, dim)) { auto diff = grad - pgrad; auto gradient_error = diff.template lpNorm() / grad.template lpNorm(); EXPECT_NEAR(0, gradient_error, gradient_tolerance); } } template - void checkResults(presult_func_t &&presult_func, const Result &results, - const DOFs &dofs) { + void checkResults(presult_func_t && presult_func, const Result & results, + const DOFs & dofs) { Matrix presult = presult_func(prescribed_gradient(dofs)); - for (auto &result : make_view(results, presult.rows(), presult.cols())) { + for (auto & result : make_view(results, presult.rows(), presult.cols())) { auto diff = result - presult; auto result_error = diff.template lpNorm() / presult.template lpNorm(); EXPECT_NEAR(0, result_error, result_tolerance); } } - template void setLinearDOF(V1 &&dof, V2 &&coord) { + template + void setLinearDOF(V1 && dof, V2 && coord) { for (Int i = 0; i < dof.size(); ++i) { dof(i) = this->alpha(i, 0); for (Int j = 0; j < coord.size(); ++j) { dof(i) += this->alpha(i, j + 1) * coord(j); } } } - template void checkDOFs(V &&dofs) { - const auto &coordinates = mesh->getNodes(); + template void checkDOFs(V && dofs) { + const auto & coordinates = mesh->getNodes(); Vector ref_dof(dofs.getNbComponent()); - for (auto &&tuple : zip(make_view(coordinates, dim), - make_view(dofs, dofs.getNbComponent()))) { + for (auto && tuple : zip(make_view(coordinates, dim), + make_view(dofs, dofs.getNbComponent()))) { setLinearDOF(ref_dof, std::get<0>(tuple)); auto diff = std::get<1>(tuple) - ref_dof; auto dofs_error = diff.template lpNorm(); EXPECT_NEAR(0, dofs_error, dofs_tolerance); } } protected: std::unique_ptr mesh; std::unique_ptr model; Matrix alpha{{0.01, 0.02, 0.03, 0.04}, {0.05, 0.06, 0.07, 0.08}, {0.09, 0.10, 0.11, 0.12}}; Real gradient_tolerance{1e-13}; Real result_tolerance{1e-13}; Real dofs_tolerance{1e-15}; }; template constexpr ElementType TestPatchTestLinear::type; template constexpr Int TestPatchTestLinear::dim; #endif /* AKANTU_PATCH_TEST_LINEAR_FIXTURE_HH_ */ diff --git a/test/test_model/test_common/test_model_solver/test_dof_manager_default.cc b/test/test_model/test_common/test_model_solver/test_dof_manager_default.cc index a6e9e1357..c76cab543 100644 --- a/test/test_model/test_common/test_model_solver/test_dof_manager_default.cc +++ b/test/test_model/test_common/test_model_solver/test_dof_manager_default.cc @@ -1,122 +1,122 @@ /** * Copyright (©) 2016-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "dof_manager_default.hh" #include "solver_callback.hh" #include "sparse_matrix_aij.hh" #include "time_step_solver.hh" using namespace akantu; /** * =\o-----o-----o-> F * | | * |---- L ----| */ class MySolverCallback : public SolverCallback { public: MySolverCallback(Real F, DOFManagerDefault & dof_manager, Int nb_dofs = 3) : dof_manager(dof_manager), dispacement(nb_dofs, 1, "disp"), blocked(nb_dofs, 1), forces(nb_dofs, 1), nb_dofs(nb_dofs) { dof_manager.registerDOFs("disp", dispacement, _dst_generic); dof_manager.registerBlockedDOFs("disp", blocked); dispacement.set(0.); forces.set(0.); blocked.set(false); forces(nb_dofs - 1, _x) = F; blocked(0, _x) = true; } void assembleMatrix(const ID & matrix_id) override { if (matrix_id != "K") return; auto & K = dynamic_cast(dof_manager.getMatrix("K")); K.zero(); for (Int i = 1; i < nb_dofs - 1; ++i) K.add(i, i, 2.); for (Int i = 0; i < nb_dofs - 1; ++i) K.add(i, i + 1, -1.); K.add(0, 0, 1); K.add(nb_dofs - 1, nb_dofs - 1, 1); // K *= 1 / L_{el} K *= nb_dofs - 1; } MatrixType getMatrixType(const ID & matrix_id) const override { if (matrix_id == "K") return _symmetric; return _mt_not_defined; } void assembleLumpedMatrix(const ID &) override {} void assembleResidual() override { dof_manager.assembleToResidual("disp", forces); } void predictor() override {} void corrector() override {} DOFManagerDefault & dof_manager; Array dispacement; Array blocked; Array forces; Int nb_dofs; }; int main(int argc, char * argv[]) { initialize(argc, argv); DOFManagerDefault dof_manager("test_dof_manager"); MySolverCallback callback(10., dof_manager, 11); NonLinearSolver & nls = dof_manager.getNewNonLinearSolver("my_nls", NonLinearSolverType::_linear); TimeStepSolver & tss = dof_manager.getNewTimeStepSolver( "my_tss", TimeStepSolverType::_static, nls, callback); tss.setIntegrationScheme("disp", IntegrationSchemeType::_pseudo_time); tss.solveStep(callback); dof_manager.getMatrix("K").saveMatrix("K_dof_manager_default.mtx"); - Array::const_scalar_iterator disp_it = callback.dispacement.begin(); - Array::const_scalar_iterator force_it = callback.forces.begin(); - Array::const_scalar_iterator blocked_it = callback.blocked.begin(); + auto disp_it = callback.dispacement.begin(); + auto force_it = callback.forces.begin(); + auto blocked_it = callback.blocked.begin(); std::cout << std::setw(8) << "disp" << " " << std::setw(8) << "force" << " " << std::setw(8) << "blocked" << std::endl; for (; disp_it != callback.dispacement.end(); ++disp_it, ++force_it, ++blocked_it) { std::cout << std::setw(8) << *disp_it << " " << std::setw(8) << *force_it << " " << std::setw(8) << std::boolalpha << *blocked_it << std::endl; } finalize(); return EXIT_SUCCESS; } diff --git a/test/test_model/test_common/test_model_solver/test_model_solver_dynamic.cc b/test/test_model/test_common/test_model_solver/test_model_solver_dynamic.cc index bb50af869..dec40f4ef 100644 --- a/test/test_model/test_common/test_model_solver/test_model_solver_dynamic.cc +++ b/test/test_model/test_common/test_model_solver/test_model_solver_dynamic.cc @@ -1,236 +1,236 @@ /** * Copyright (©) 2016-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "communicator.hh" #include "element_group.hh" #include "mesh.hh" #include "mesh_accessor.hh" #include "non_linear_solver.hh" /* -------------------------------------------------------------------------- */ #include "dumpable_inline_impl.hh" #include "dumper_element_partition.hh" #include "dumper_iohelper_paraview.hh" /* -------------------------------------------------------------------------- */ #include "test_model_solver_my_model.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ #ifndef EXPLICIT #define EXPLICIT true #endif using namespace akantu; class Sinusoidal : public BC::Dirichlet::DirichletFunctor { public: Sinusoidal(MyModel & model, Real amplitude, Real pulse_width, Real t) : model(model), A(amplitude), k(2 * M_PI / pulse_width), t(t), v{std::sqrt(model.E / model.rho)} {} - + using DirichletFunctor::operator(); void operator()(Idx n, Vector & /*flags*/, Vector & disp, - const Vector & coord) const { + const Vector & coord) override { auto x = coord(_x); model.velocity(n, _x) = k * v * A * sin(k * (x - v * t)); disp(_x) = A * cos(k * (x - v * t)); } private: MyModel & model; Real A{1.}; Real k{2 * M_PI}; Real t{1.}; Real v{1.}; }; static void genMesh(Mesh & mesh, Int nb_nodes); /* -------------------------------------------------------------------------- */ int main(int argc, char * argv[]) { initialize(argc, argv); Int prank = Communicator::getStaticCommunicator().whoAmI(); Int global_nb_nodes = 201; Int max_steps = 400; Real time_step = 0.001; Mesh mesh(1); Real F = -9.81; bool _explicit = EXPLICIT; const Real pulse_width = 0.2; const Real A = 0.01; ID dof_manager_type = "default"; #if defined(DOF_MANAGER_TYPE) dof_manager_type = DOF_MANAGER_TYPE; #endif if (prank == 0) genMesh(mesh, global_nb_nodes); mesh.distribute(); // mesh.makePeriodic(_x); MyModel model(F, mesh, _explicit, dof_manager_type); model.forces.zero(); model.blocked.zero(); model.applyBC(Sinusoidal(model, A, pulse_width, 0.), "all"); model.applyBC(BC::Dirichlet::FlagOnly(_x), "border"); if (!_explicit) { model.getNewSolver("dynamic", TimeStepSolverType::_dynamic, NonLinearSolverType::_newton_raphson); model.setIntegrationScheme("dynamic", "disp", IntegrationSchemeType::_trapezoidal_rule_2, IntegrationScheme::_displacement); } else { model.getNewSolver("dynamic", TimeStepSolverType::_dynamic_lumped, NonLinearSolverType::_lumped); model.setIntegrationScheme("dynamic", "disp", IntegrationSchemeType::_central_difference, IntegrationScheme::_acceleration); } model.setTimeStep(time_step); if (prank == 0) { std::cout << std::scientific; std::cout << std::setw(14) << "time" << "," << std::setw(14) << "wext" << "," << std::setw(14) << "epot" << "," << std::setw(14) << "ekin" << "," << std::setw(14) << "total" << "," << std::setw(14) << "max_disp" << "," << std::setw(14) << "min_disp" << std::endl; } Real wext = 0.; model.getDOFManager().zeroResidual(); model.assembleResidual(); Real epot = 0; // model.getPotentialEnergy(); Real ekin = 0; // model.getKineticEnergy(); Real einit = ekin + epot; Real etot = ekin + epot - wext - einit; Real max_disp = 0., min_disp = 0.; for (auto && disp : model.displacement) { max_disp = std::max(max_disp, disp); min_disp = std::min(min_disp, disp); } if (prank == 0) { std::cout << std::setw(14) << 0. << "," << std::setw(14) << wext << "," << std::setw(14) << epot << "," << std::setw(14) << ekin << "," << std::setw(14) << etot << "," << std::setw(14) << max_disp << "," << std::setw(14) << min_disp << std::endl; } #if EXPLICIT == false NonLinearSolver & solver = model.getDOFManager().getNonLinearSolver("dynamic"); solver.set("max_iterations", 20); #endif auto && dumper = std::make_shared("dynamic", "./paraview"); mesh.registerExternalDumper(dumper, "dynamic", true); mesh.addDumpMesh(mesh); mesh.addDumpFieldExternalToDumper("dynamic", "displacement", model.displacement); mesh.addDumpFieldExternalToDumper("dynamic", "velocity", model.velocity); mesh.addDumpFieldExternalToDumper("dynamic", "forces", model.forces); mesh.addDumpFieldExternalToDumper("dynamic", "internal_forces", model.internal_forces); mesh.addDumpFieldExternalToDumper("dynamic", "acceleration", model.acceleration); mesh.dump(); for (Int i = 1; i < max_steps + 1; ++i) { model.applyBC(Sinusoidal(model, A, pulse_width, time_step * (i - 1)), "border"); model.solveStep("dynamic"); mesh.dump(); epot = model.getPotentialEnergy(); ekin = model.getKineticEnergy(); wext += model.getExternalWorkIncrement(); etot = ekin + epot - wext - einit; Real max_disp = 0., min_disp = 0.; for (auto && disp : model.displacement) { max_disp = std::max(max_disp, disp); min_disp = std::min(min_disp, disp); } if (prank == 0) { std::cout << std::setw(14) << time_step * i << "," << std::setw(14) << wext << "," << std::setw(14) << epot << "," << std::setw(14) << ekin << "," << std::setw(14) << etot << "," << std::setw(14) << max_disp << "," << std::setw(14) << min_disp << std::endl; } } // output.close(); finalize(); return EXIT_SUCCESS; } /* -------------------------------------------------------------------------- */ void genMesh(Mesh & mesh, Int nb_nodes) { MeshAccessor mesh_accessor(mesh); auto & nodes = mesh_accessor.getNodes(); auto & conn = mesh_accessor.getConnectivity(_segment_2); nodes.resize(nb_nodes); auto & all = mesh.createNodeGroup("all_nodes"); for (Int n = 0; n < nb_nodes; ++n) { nodes(n, _x) = n * (1. / (nb_nodes - 1)); all.add(n); } mesh.createElementGroupFromNodeGroup("all", "all_nodes"); conn.resize(nb_nodes - 1); for (Int n = 0; n < nb_nodes - 1; ++n) { conn(n, 0) = n; conn(n, 1) = n + 1; } auto & conn_points = mesh_accessor.getConnectivity(_point_1); conn_points.resize(2); conn_points(0, 0) = 0; conn_points(1, 0) = nb_nodes - 1; auto & border = mesh.createElementGroup("border", 0); border.add({_point_1, 0, _not_ghost}, true); border.add({_point_1, 1, _not_ghost}, true); mesh_accessor.makeReady(); } 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 5ca2c1fe1..794700b3f 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,116 +1,116 @@ /** * Copyright (©) 2017-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "integrator_gauss.hh" #include "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; public: - MyModel(Mesh &mesh, Int spatial_dimension) + MyModel(Mesh & mesh, Int spatial_dimension) : Model(mesh, ModelType::_model, spatial_dimension), manager(*this, *this) { registerFEEngineObject("FEEngine", mesh, spatial_dimension); manager.registerNeighborhood("test_region", "test_region"); getFEEngine().initShapeFunctions(); manager.initialize(); } void initModel() override {} MatrixType getMatrixType(const ID &) const override { return _mt_not_defined; } std::tuple 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 &, const NewNodesEvent &) override {} void onNodesRemoved(const Array &, const Array &, const RemovedNodesEvent &) override {} void onElementsAdded(const Array &, const NewElementsEvent &) override {} void onElementsRemoved(const Array &, const ElementTypeMapArray &, const RemovedElementsEvent &) override {} void onElementsChanged(const Array &, const Array &, const ElementTypeMapArray &, const ChangedElementsEvent &) override {} void insertIntegrationPointsInNeighborhoods(GhostType ghost_type) override { ElementTypeMapArray quadrature_points_coordinates( "quadrature_points_coordinates_tmp_nl", this->id); quadrature_points_coordinates.initialize(this->getFEEngine(), _nb_component = spatial_dimension, _ghost_type = ghost_type); IntegrationPoint q; q.ghost_type = ghost_type; q.global_num = 0; - auto &neighborhood = manager.getNeighborhood("test_region"); + auto & neighborhood = manager.getNeighborhood("test_region"); - for (const auto &type : quadrature_points_coordinates.elementTypes( + for (const auto & type : quadrature_points_coordinates.elementTypes( spatial_dimension, ghost_type)) { q.type = type; - auto &quads = quadrature_points_coordinates(type, ghost_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()); + auto quad_it = quads.cbegin(quads.getNbComponent()); + auto quad_end = quads.cend(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; } + const auto & getNonLocalManager() const { return manager; } private: NonLocalManager manager; }; diff --git a/test/test_model/test_solid_mechanics_model/test_cohesive/test_cohesive_fixture.hh b/test/test_model/test_solid_mechanics_model/test_cohesive/test_cohesive_fixture.hh index cac5447e9..199a2cc28 100644 --- a/test/test_model/test_solid_mechanics_model/test_cohesive/test_cohesive_fixture.hh +++ b/test/test_model/test_solid_mechanics_model/test_cohesive/test_cohesive_fixture.hh @@ -1,341 +1,342 @@ /** * Copyright (©) 2018-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "communicator.hh" #include "solid_mechanics_model_cohesive.hh" #include "test_gtest_utils.hh" /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ #ifndef AKANTU_TEST_COHESIVE_FIXTURE_HH_ #define AKANTU_TEST_COHESIVE_FIXTURE_HH_ using namespace akantu; template <::akantu::AnalysisMethod t> using analysis_method_t = std::integral_constant<::akantu::AnalysisMethod, t>; class StrainIncrement : public BC::Functor { public: StrainIncrement(const Matrix & strain, BC::Axis dir) : strain_inc(strain), dir(dir) {} - void operator()(UInt /*node*/, Vector & flags, Vector & primal, - const Vector & coord) const { + void operator()(Idx /*node*/, VectorProxy & flags, + VectorProxy & primal, + const VectorProxy & coord) { if (std::abs(coord(dir)) < 1e-8) { return; } flags.set(true); primal += strain_inc * coord; } static const BC::Functor::Type type = BC::Functor::_dirichlet; private: Matrix strain_inc; BC::Axis dir; }; template class TestSMMCFixture : public ::testing::Test { public: static constexpr ElementType cohesive_type = std::tuple_element_t<0, param_>::value; static constexpr ElementType type_1 = std::tuple_element_t<1, param_>::value; static constexpr ElementType type_2 = std::tuple_element_t<2, param_>::value; static constexpr size_t dim = ElementClass::getSpatialDimension(); void SetUp() { mesh = std::make_unique(this->dim); if (Communicator::getStaticCommunicator().whoAmI() == 0) { mesh->read(this->mesh_name); } mesh->distribute(); } void TearDown() { model.reset(nullptr); mesh.reset(nullptr); } void createModel() { model = std::make_unique(*mesh); model->initFull(_analysis_method = this->analysis_method, _is_extrinsic = this->is_extrinsic); auto time_step = this->model->getStableTimeStep() * 0.01; this->model->setTimeStep(time_step); if (dim == 1) { surface = 1; group_size = 1; return; } auto facet_type = mesh->getFacetType(this->cohesive_type); auto & fe_engine = model->getFEEngineBoundary(); const auto & group = mesh->getElementGroup("insertion"); group_size = group.size(_ghost_type = _not_ghost); const auto & elements = group.getElements(facet_type); Array ones(fe_engine.getNbIntegrationPoints(facet_type) * group_size); ones.set(1.); surface = fe_engine.integrate(ones, facet_type, _not_ghost, elements); mesh->getCommunicator().allReduce(surface, SynchronizerOperation::_sum); mesh->getCommunicator().allReduce(group_size, SynchronizerOperation::_sum); #define debug_ 0 #if debug_ auto size = mesh->getCommunicator().getNbProc(); this->model->setBaseName("solid_mechanics_model_cohesive_" + std::to_string(size)); // this->model->addDumpField("partition"); this->model->addDumpFieldVector("displacement"); this->model->addDumpFieldVector("velocity"); this->model->addDumpFieldVector("internal_force"); this->model->addDumpFieldVector("external_force"); this->model->addDumpField("blocked_dofs"); this->model->addDumpField("stress"); this->model->addDumpField("strain"); this->model->assembleInternalForces(); this->model->setBaseNameToDumper( "cohesive elements", "cohesive_elements_" + std::to_string(size)); this->model->addDumpFieldVectorToDumper("cohesive elements", "displacement"); this->model->addDumpFieldToDumper("cohesive elements", "damage"); this->model->addDumpFieldToDumper("cohesive elements", "tractions"); this->model->addDumpFieldToDumper("cohesive elements", "opening"); this->model->dump(); this->model->dump("cohesive elements"); #endif } void setInitialCondition(const Matrix & strain) { for (auto && data : zip(make_view(this->mesh->getNodes(), this->dim), make_view(this->model->getDisplacement(), this->dim))) { const auto & pos = std::get<0>(data); auto & disp = std::get<1>(data); disp = strain * pos; } } bool checkDamaged() { UInt nb_damaged = 0; auto & damage = model->getMaterial("insertion").getArray("damage", cohesive_type); for (auto d : damage) { if (d >= .99) { ++nb_damaged; } } return (nb_damaged == group_size); } void steps(const Matrix & strain) { StrainIncrement functor((1. / 300) * strain, this->dim == 1 ? _x : _y); for (auto _ [[gnu::unused]] : arange(nb_steps)) { this->model->applyBC(functor, "loading"); this->model->applyBC(functor, "fixed"); if (this->is_extrinsic) { this->model->checkCohesiveStress(); } this->model->solveStep(); #if debug_ this->model->dump(); this->model->dump("cohesive elements"); #endif } } void checkInsertion() { auto nb_cohesive_element = this->mesh->getNbElement(cohesive_type); mesh->getCommunicator().allReduce(nb_cohesive_element, SynchronizerOperation::_sum); EXPECT_EQ(nb_cohesive_element, group_size); } void checkDissipated(Real expected_density) { Real edis = this->model->getEnergy("dissipated"); EXPECT_NEAR(this->surface * expected_density, edis, 5e-1); } void testModeI() { this->createModel(); auto & mat_el = this->model->getMaterial("body"); auto speed = mat_el.getPushWaveSpeed(Element()); auto direction = _y; if (dim == 1) { direction = _x; } auto length = mesh->getUpperBounds()(direction) - mesh->getLowerBounds()(direction); nb_steps = length / speed / model->getTimeStep(); SCOPED_TRACE(std::to_string(this->dim) + "D - " + std::to_string(type_1) + ":" + std::to_string(type_2)); auto & mat_co = this->model->getMaterial("insertion"); Real sigma_c = mat_co.get("sigma_c"); Real E = mat_el.get("E"); Real nu = mat_el.get("nu"); Matrix strain; if (dim == 1) { strain = Matrix{{1.}}; } else if (dim == 2) { strain = Matrix{{-nu, 0.}, {0., 1. - nu}}; strain *= (1. + nu); } else if (dim == 3) { strain = Matrix{{-nu, 0., 0.}, {0., 1., 0.}, {0., 0., -nu}}; } strain *= sigma_c / E; this->setInitialCondition((1 - 1e-5) * strain); this->steps(2e-2 * strain); } void testModeII() { this->createModel(); auto & mat_el = this->model->getMaterial("body"); Real speed; try { speed = mat_el.getShearWaveSpeed(Element()); // the slowest speed if exists } catch (...) { speed = mat_el.getPushWaveSpeed(Element()); } auto direction = _y; if (dim == 1) direction = _x; auto length = mesh->getUpperBounds()(direction) - mesh->getLowerBounds()(direction); nb_steps = 2 * length / 2. / speed / model->getTimeStep(); SCOPED_TRACE(std::to_string(this->dim) + "D - " + std::to_string(type_1) + ":" + std::to_string(type_2)); if (this->dim > 1) this->model->applyBC(BC::Dirichlet::FlagOnly(_y), "sides"); if (this->dim > 2) this->model->applyBC(BC::Dirichlet::FlagOnly(_z), "sides"); auto & mat_co = this->model->getMaterial("insertion"); Real sigma_c = mat_co.get("sigma_c"); Real beta = mat_co.get("beta"); // Real G_c = mat_co.get("G_c"); Real E = mat_el.get("E"); Real nu = mat_el.get("nu"); Matrix strain; if (dim == 1) { strain = Matrix{{1.}}; } else if (dim == 2) { strain = Matrix{{0., 1.}, {0., 0.}}; strain *= (1. + nu); } else if (dim == 3) { strain = Matrix{{0., 1., 0.}, {0., 0., 0.}, {0., 0., 0.}}; strain *= (1. + nu); } strain *= 2 * beta * beta * sigma_c / E; // nb_steps *= 5; this->setInitialCondition((1. - 1e-5) * strain); this->steps(0.005 * strain); } protected: std::unique_ptr mesh; std::unique_ptr model; std::string mesh_name{std::to_string(cohesive_type) + std::to_string(type_1) + (type_1 == type_2 ? "" : std::to_string(type_2)) + ".msh"}; bool is_extrinsic; AnalysisMethod analysis_method; Real surface{0}; UInt nb_steps{1000}; UInt group_size{10000}; }; /* -------------------------------------------------------------------------- */ template constexpr ElementType TestSMMCFixture::cohesive_type; template constexpr ElementType TestSMMCFixture::type_1; template constexpr ElementType TestSMMCFixture::type_2; template constexpr size_t TestSMMCFixture::dim; /* -------------------------------------------------------------------------- */ using IsExtrinsicTypes = std::tuple; using AnalysisMethodTypes = std::tuple>; using coh_types = gtest_list_t, std::tuple<_element_type_cohesive_2d_4, _element_type_triangle_3, _element_type_triangle_3>, std::tuple<_element_type_cohesive_2d_4, _element_type_quadrangle_4, _element_type_quadrangle_4>, std::tuple<_element_type_cohesive_2d_4, _element_type_triangle_3, _element_type_quadrangle_4>, std::tuple<_element_type_cohesive_2d_6, _element_type_triangle_6, _element_type_triangle_6>, std::tuple<_element_type_cohesive_2d_6, _element_type_quadrangle_8, _element_type_quadrangle_8>, std::tuple<_element_type_cohesive_2d_6, _element_type_triangle_6, _element_type_quadrangle_8>, std::tuple<_element_type_cohesive_3d_6, _element_type_tetrahedron_4, _element_type_tetrahedron_4>, std::tuple<_element_type_cohesive_3d_12, _element_type_tetrahedron_10, _element_type_tetrahedron_10> /*, std::tuple<_element_type_cohesive_3d_8, _element_type_hexahedron_8, _element_type_hexahedron_8>, std::tuple<_element_type_cohesive_3d_16, _element_type_hexahedron_20, _element_type_hexahedron_20>*/>>; TYPED_TEST_SUITE(TestSMMCFixture, coh_types, ); #endif /* AKANTU_TEST_COHESIVE_FIXTURE_HH_ */ diff --git a/test/test_model/test_solid_mechanics_model/test_solid_mechanics_model_dynamics.cc b/test/test_model/test_solid_mechanics_model/test_solid_mechanics_model_dynamics.cc index 8053ce6bd..d101e4963 100644 --- a/test/test_model/test_solid_mechanics_model/test_solid_mechanics_model_dynamics.cc +++ b/test/test_model/test_solid_mechanics_model/test_solid_mechanics_model_dynamics.cc @@ -1,321 +1,322 @@ /** * Copyright (©) 2017-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . */ /* -------------------------------------------------------------------------- */ #include "boundary_condition_functor.hh" #include "test_solid_mechanics_model_fixture.hh" /* -------------------------------------------------------------------------- */ using namespace akantu; namespace { const Real A = 1e-1; const Real E = 1.; const Real poisson = 3. / 10; const auto lambda = E * poisson / (1 + poisson) / (1 - 2 * poisson); const auto mu = E / 2 / (1. + poisson); const Real rho = 1.; const auto cp = std::sqrt((lambda + 2 * mu) / rho); const auto cs = std::sqrt(mu / rho); const auto c = std::sqrt(E / rho); const Vector k{.5, 0., 0.}; const Vector psi1{0., 0., 1.}; const Vector psi2{0., 1., 0.}; const auto knorm = k.norm(); /* -------------------------------------------------------------------------- */ template struct Verification {}; /* -------------------------------------------------------------------------- */ template <> struct Verification<1> { template > * = nullptr> void displacement(Eigen::MatrixBase & disp, const Eigen::MatrixBase & coord, Real current_time) { const auto & x = coord(_x); const auto omega = c * k[0]; disp(_x) = A * std::cos(k[0] * x - omega * current_time); } template > * = nullptr> void velocity(Eigen::MatrixBase & vel, const Eigen::MatrixBase & coord, Real current_time) { const auto & x = coord(_x); const auto omega = c * k[0]; vel(_x) = omega * A * std::sin(k[0] * x - omega * current_time); } }; /* -------------------------------------------------------------------------- */ template <> struct Verification<2> { template > * = nullptr> void displacement(Eigen::MatrixBase & disp, const Eigen::MatrixBase & X, Real current_time) { Vector kshear{k[1], k[0]}; Vector kpush{k[0], k[1]}; const auto omega_p = knorm * cp; const auto omega_s = knorm * cs; auto phase_p = X.dot(kpush) - omega_p * current_time; auto phase_s = X.dot(kpush) - omega_s * current_time; disp = A * (kpush * std::cos(phase_p) + kshear * std::cos(phase_s)); } template > * = nullptr> void velocity(Eigen::MatrixBase & vel, const Eigen::MatrixBase & X, Real current_time) { Vector kshear{k[1], k[0]}; Vector kpush{k[0], k[1]}; const auto omega_p = knorm * cp; const auto omega_s = knorm * cs; auto phase_p = X.dot(kpush) - omega_p * current_time; auto phase_s = X.dot(kpush) - omega_s * current_time; vel = A * (kpush * omega_p * std::cos(phase_p) + kshear * omega_s * std::cos(phase_s)); } }; /* -------------------------------------------------------------------------- */ template <> struct Verification<3> { template > * = nullptr> void displacement(Eigen::MatrixBase & disp, const Eigen::MatrixBase & coord, Real current_time) { const auto & X = coord; auto kpush = k; Vector kshear1 = k.cross(psi1); Vector kshear2 = k.cross(psi2); const auto omega_p = knorm * cp; const auto omega_s = knorm * cs; auto phase_p = X.dot(kpush) - omega_p * current_time; auto phase_s = X.dot(kpush) - omega_s * current_time; disp = A * (kpush * std::cos(phase_p) + kshear1 * std::cos(phase_s) + kshear2 * std::cos(phase_s)); } template > * = nullptr> void velocity(Eigen::MatrixBase & vel, const Eigen::MatrixBase & coord, Real current_time) { const auto & X = coord; auto kpush = k; Vector kshear1 = k.cross(psi1); Vector kshear2 = k.cross(psi2); const auto omega_p = knorm * cp; const auto omega_s = knorm * cs; auto phase_p = X.dot(kpush) - omega_p * current_time; auto phase_s = X.dot(kpush) - omega_s * current_time; vel = A * (kpush * omega_p * std::cos(phase_p) + kshear1 * omega_s * std::cos(phase_s) + kshear2 * omega_s * std::cos(phase_s)); } }; /* -------------------------------------------------------------------------- */ template class SolutionFunctor : public BC::Dirichlet::DirichletFunctor { public: SolutionFunctor(Real current_time, SolidMechanicsModel & model) : current_time(current_time), model(model) {} public: static constexpr auto dim = ElementClass<_type>::getSpatialDimension(); - inline void operator()(Int node, Vector & flags, Vector & primal, - const Vector & coord) const { + inline void operator()(Int node, VectorProxy & flags, + VectorProxy & primal, + const VectorProxy & coord) { flags(0) = true; - const auto & vel = model.getVelocity(); + auto & vel = model.getVelocity(); auto it = vel.begin(model.getSpatialDimension()); auto v = it[node]; Verification verif; verif.displacement(primal, coord, current_time); verif.velocity(v, coord, current_time); } private: Real current_time; SolidMechanicsModel & model; }; /* -------------------------------------------------------------------------- */ // This fixture uses somewhat finer meshes representing bars. template class TestSMMFixtureBar : public TestSMMFixture { using parent = TestSMMFixture; public: void SetUp() override { this->mesh_file = "../patch_tests/data/bar" + std::to_string(this->type) + ".msh"; parent::SetUp(); auto analysis_method = analysis_method_::value; this->initModel("test_solid_mechanics_model_" "dynamics_material.dat", analysis_method); const auto & position = this->mesh->getNodes(); auto & displacement = this->model->getDisplacement(); auto & velocity = this->model->getVelocity(); constexpr auto dim = parent::spatial_dimension; Verification verif; for (auto && tuple : zip(make_view(position, dim), make_view(displacement, dim), make_view(velocity, dim))) { verif.displacement(std::get<1>(tuple), std::get<0>(tuple), 0.); verif.velocity(std::get<2>(tuple), std::get<0>(tuple), 0.); } if (this->dump_paraview) { this->model->dump(); } /// boundary conditions this->model->applyBC(SolutionFunctor(0., *this->model), "BC"); wave_velocity = 1.; // sqrt(E/rho) = sqrt(1/1) = 1 simulation_time = 5 / wave_velocity; time_step = this->model->getTimeStep(); max_steps = simulation_time / time_step; // 100 } void solveStep() { constexpr auto dim = parent::spatial_dimension; Real current_time = 0.; const auto & position = this->mesh->getNodes(); const auto & displacement = this->model->getDisplacement(); auto nb_nodes = this->mesh->getNbNodes(); auto nb_global_nodes = this->mesh->getNbGlobalNodes(); max_error = -1.; Array displacement_solution(nb_nodes, dim); Verification verif; auto ndump = 50; auto dump_freq = max_steps / ndump; for (Int s = 0; s < this->max_steps; ++s, current_time += this->time_step) { if (s % dump_freq == 0 && this->dump_paraview) { this->model->dump(); } /// boundary conditions this->model->applyBC( SolutionFunctor(current_time, *this->model), "BC"); // compute the disp solution for (auto && tuple : zip(make_view(position, dim), make_view(displacement_solution, dim))) { verif.displacement(std::get<1>(tuple), std::get<0>(tuple), current_time); } // compute the error solution Real disp_error = 0.; auto n = 0; for (auto && tuple : zip(make_view(displacement, dim), make_view(displacement_solution, dim))) { if (this->mesh->isLocalOrMasterNode(n)) { auto diff = std::get<1>(tuple) - std::get<0>(tuple); disp_error += diff.dot(diff); } ++n; } this->mesh->getCommunicator().allReduce(disp_error, SynchronizerOperation::_sum); disp_error = sqrt(disp_error) / nb_global_nodes; max_error = std::max(disp_error, max_error); this->model->solveStep(); } } protected: Real time_step; Real wave_velocity; Real simulation_time; Int max_steps; Real max_error{-1}; }; template using analysis_method_t = std::integral_constant; using TestTypes = gtest_list_t; template using TestSMMFixtureBarExplicit = TestSMMFixtureBar>; TYPED_TEST_SUITE(TestSMMFixtureBarExplicit, TestTypes, ); /* -------------------------------------------------------------------------- */ TYPED_TEST(TestSMMFixtureBarExplicit, Dynamics) { this->solveStep(); EXPECT_NEAR(this->max_error, 0., 2e-3); // std::cout << "max error: " << max_error << std::endl; } /* -------------------------------------------------------------------------- */ #if defined(AKANTU_IMPLICIT) template using TestSMMFixtureBarImplicit = TestSMMFixtureBar>; TYPED_TEST_SUITE(TestSMMFixtureBarImplicit, TestTypes, ); TYPED_TEST(TestSMMFixtureBarImplicit, Dynamics) { if (this->type == _segment_2 and (this->mesh->getCommunicator().getNbProc() > 2)) { // The error are just to high after (hopefully because of the two small test // case) SUCCEED(); return; } this->solveStep(); EXPECT_NEAR(this->max_error, 0., 2e-3); } #endif } // namespace