diff --git a/.gitignore b/.gitignore index a3fea6cfd..be850e87b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,16 +1,17 @@ build* .dir-locals.el TAGS third-party/blackdynamite/ third-party/pybind11 third-party/google-test third-party/cpp-array/ *~ release .*.swp *.tar.gz *.tgz *.tbz *.tar.bz2 .idea -__pycache__ \ No newline at end of file +__pycache__ +.mailmap diff --git a/examples/python/CMakeLists.txt b/examples/python/CMakeLists.txt index e7439f6f2..9bea05474 100644 --- a/examples/python/CMakeLists.txt +++ b/examples/python/CMakeLists.txt @@ -1,6 +1,6 @@ add_subdirectory(plate-hole) -add_subdirectory(custom-material-dynamics) +add_subdirectory(custom-material) package_add_files_to_package( examples/python/README.rst ) diff --git a/examples/python/custom-material/CMakeLists.txt b/examples/python/custom-material/CMakeLists.txt index b9e6e0952..abaacb68f 100644 --- a/examples/python/custom-material/CMakeLists.txt +++ b/examples/python/custom-material/CMakeLists.txt @@ -1,3 +1,7 @@ -register_example(newmark - SCRIPT newmark.py +register_example(bi-material + SCRIPT bi-material.py + FILES_TO_COPY material.dat square.geo) + +register_example(custom-material + SCRIPT custom-material.py FILES_TO_COPY material.dat bar.geo) diff --git a/packages/solid_mechanics.cmake b/packages/solid_mechanics.cmake index c64a05a82..27a98df70 100644 --- a/packages/solid_mechanics.cmake +++ b/packages/solid_mechanics.cmake @@ -1,128 +1,129 @@ #=============================================================================== # @file solid_mechanics.cmake # # @author Guillaume Anciaux # @author Nicolas Richart # # @date creation: Mon Nov 21 2011 # @date last modification: Mon Jan 18 2016 # # @brief package description for core # # @section LICENSE # # Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de # Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des # Solides) # # Akantu is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # # Akantu is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with Akantu. If not, see . # #=============================================================================== package_declare(solid_mechanics DEFAULT ON DESCRIPTION "Solid mechanics model" DEPENDS core ) package_declare_sources(solid_mechanics model/solid_mechanics/material.cc model/solid_mechanics/material.hh model/solid_mechanics/material_inline_impl.cc model/solid_mechanics/material_selector.hh model/solid_mechanics/material_selector_tmpl.hh model/solid_mechanics/materials/internal_field.hh model/solid_mechanics/materials/internal_field_tmpl.hh model/solid_mechanics/materials/random_internal_field.hh model/solid_mechanics/materials/random_internal_field_tmpl.hh model/solid_mechanics/solid_mechanics_model.cc model/solid_mechanics/solid_mechanics_model.hh model/solid_mechanics/solid_mechanics_model_inline_impl.cc model/solid_mechanics/solid_mechanics_model_io.cc model/solid_mechanics/solid_mechanics_model_mass.cc model/solid_mechanics/solid_mechanics_model_material.cc model/solid_mechanics/solid_mechanics_model_tmpl.hh model/solid_mechanics/solid_mechanics_model_event_handler.hh model/solid_mechanics/materials/plane_stress_toolbox.hh model/solid_mechanics/materials/plane_stress_toolbox_tmpl.hh model/solid_mechanics/materials/material_core_includes.hh model/solid_mechanics/materials/material_elastic.cc model/solid_mechanics/materials/material_elastic.hh model/solid_mechanics/materials/material_elastic_inline_impl.cc model/solid_mechanics/materials/material_thermal.cc model/solid_mechanics/materials/material_thermal.hh model/solid_mechanics/materials/material_elastic_linear_anisotropic.cc model/solid_mechanics/materials/material_elastic_linear_anisotropic.hh + model/solid_mechanics/materials/material_elastic_linear_anisotropic_inline_impl.cc model/solid_mechanics/materials/material_elastic_orthotropic.cc model/solid_mechanics/materials/material_elastic_orthotropic.hh model/solid_mechanics/materials/material_damage/material_damage.hh model/solid_mechanics/materials/material_damage/material_damage_tmpl.hh model/solid_mechanics/materials/material_damage/material_marigo.cc model/solid_mechanics/materials/material_damage/material_marigo.hh model/solid_mechanics/materials/material_damage/material_marigo_inline_impl.cc model/solid_mechanics/materials/material_damage/material_mazars.cc model/solid_mechanics/materials/material_damage/material_mazars.hh model/solid_mechanics/materials/material_damage/material_mazars_inline_impl.cc model/solid_mechanics/materials/material_finite_deformation/material_neohookean.cc model/solid_mechanics/materials/material_finite_deformation/material_neohookean.hh model/solid_mechanics/materials/material_finite_deformation/material_neohookean_inline_impl.cc model/solid_mechanics/materials/material_plastic/material_plastic.cc model/solid_mechanics/materials/material_plastic/material_plastic.hh model/solid_mechanics/materials/material_plastic/material_plastic_inline_impl.cc model/solid_mechanics/materials/material_plastic/material_linear_isotropic_hardening.cc model/solid_mechanics/materials/material_plastic/material_linear_isotropic_hardening.hh model/solid_mechanics/materials/material_plastic/material_linear_isotropic_hardening_inline_impl.cc model/solid_mechanics/materials/material_viscoelastic/material_standard_linear_solid_deviatoric.cc model/solid_mechanics/materials/material_viscoelastic/material_standard_linear_solid_deviatoric.hh model/solid_mechanics/materials/material_non_local.hh model/solid_mechanics/materials/material_non_local_tmpl.hh model/solid_mechanics/materials/material_non_local_includes.hh ) package_declare_material_infos(solid_mechanics LIST AKANTU_CORE_MATERIAL_LIST INCLUDE material_core_includes.hh ) package_declare_documentation_files(solid_mechanics manual-solidmechanicsmodel.tex manual-constitutive-laws.tex manual-lumping.tex manual-appendix-materials.tex figures/dynamic_analysis.png figures/explicit_dynamic.pdf figures/explicit_dynamic.svg figures/static.pdf figures/static.svg figures/hooke_law.pdf figures/implicit_dynamic.pdf figures/implicit_dynamic.svg figures/problemDomain.pdf_tex figures/problemDomain.pdf figures/static_analysis.png figures/stress_strain_el.pdf figures/tangent.pdf figures/tangent.svg figures/stress_strain_neo.pdf figures/visco_elastic_law.pdf figures/isotropic_hardening_plasticity.pdf figures/stress_strain_visco.pdf ) package_declare_extra_files_to_package(solid_mechanics SOURCES model/solid_mechanics/material_list.hh.in ) diff --git a/src/common/aka_extern.cc b/src/common/aka_extern.cc index ca6b5a5e7..78ae76cfa 100644 --- a/src/common/aka_extern.cc +++ b/src/common/aka_extern.cc @@ -1,109 +1,109 @@ /** * @file aka_extern.cc * * @author Nicolas Richart * * @date creation: Mon Jun 14 2010 * @date last modification: Thu Nov 19 2015 * * @brief initialisation of all global variables * to insure the order of creation * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_array.hh" #include "aka_common.hh" #include "aka_math.hh" #include "aka_named_argument.hh" #include "aka_random_generator.hh" #include "communication_tag.hh" #include "cppargparse.hh" #include "parser.hh" #include "solid_mechanics_model.hh" #if defined(AKANTU_COHESIVE_ELEMENT) #include "solid_mechanics_model_cohesive.hh" #endif /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ #if defined(AKANTU_DEBUG_TOOLS) #include "aka_debug_tools.hh" #endif namespace akantu { /* -------------------------------------------------------------------------- */ /* error.hpp variables */ /* -------------------------------------------------------------------------- */ namespace debug { /** \todo write function to get this * values from the environment or a config file */ /// standard output for debug messages std::ostream * _akantu_debug_cout = &std::cerr; /// standard output for normal messages std::ostream & _akantu_cout = std::cout; /// parallel context used in debug messages std::string _parallel_context = ""; Debugger debugger; #if defined(AKANTU_DEBUG_TOOLS) DebugElementManager element_manager; #endif } // namespace debug /* -------------------------------------------------------------------------- */ /// list of ghost iterable types ghost_type_t ghost_types(_casper); /* -------------------------------------------------------------------------- */ /// Paser for commandline arguments ::cppargparse::ArgumentParser static_argparser; /// Parser containing the information parsed by the input file given to initFull Parser static_parser; bool Parser::permissive_parser = false; /* -------------------------------------------------------------------------- */ -Real Math::tolerance = std::numeric_limits::epsilon(); +Real Math::tolerance = 1e2 * std::numeric_limits::epsilon(); /* -------------------------------------------------------------------------- */ const UInt _all_dimensions = UInt(-1); /* -------------------------------------------------------------------------- */ const Array empty_filter(0, 1, "empty_filter"); /* -------------------------------------------------------------------------- */ template <> long int RandomGenerator::_seed = 5489u; template <> std::default_random_engine RandomGenerator::generator(5489u); /* -------------------------------------------------------------------------- */ int Tag::max_tag = 0; /* -------------------------------------------------------------------------- */ } // namespace akantu diff --git a/src/common/aka_types.hh b/src/common/aka_types.hh index 62d998fde..203d4d318 100644 --- a/src/common/aka_types.hh +++ b/src/common/aka_types.hh @@ -1,1302 +1,1302 @@ /** * @file aka_types.hh * * @author Nicolas Richart * * @date creation: Thu Feb 17 2011 * @date last modification: Fri Jan 22 2016 * * @brief description of the "simple" types * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_error.hh" #include "aka_fwd.hh" #include "aka_math.hh" /* -------------------------------------------------------------------------- */ #include #include #include /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_AKA_TYPES_HH__ #define __AKANTU_AKA_TYPES_HH__ namespace akantu { enum NormType { L_1 = 1, L_2 = 2, L_inf = UInt(-1) }; /** * DimHelper is a class to generalize the setup of a dim array from 3 * values. This gives a common interface in the TensorStorage class * independently of its derived inheritance (Vector, Matrix, Tensor3) * @tparam dim */ template struct DimHelper { static inline void setDims(UInt m, UInt n, UInt p, UInt dims[dim]); }; /* -------------------------------------------------------------------------- */ template <> struct DimHelper<1> { static inline void setDims(UInt m, __attribute__((unused)) UInt n, __attribute__((unused)) UInt p, UInt dims[1]) { dims[0] = m; } }; /* -------------------------------------------------------------------------- */ template <> struct DimHelper<2> { static inline void setDims(UInt m, UInt n, __attribute__((unused)) UInt p, UInt dims[2]) { dims[0] = m; dims[1] = n; } }; /* -------------------------------------------------------------------------- */ template <> struct DimHelper<3> { static inline void setDims(UInt m, UInt n, UInt p, UInt dims[3]) { dims[0] = m; dims[1] = n; dims[2] = p; } }; /* -------------------------------------------------------------------------- */ template class TensorStorage; /* -------------------------------------------------------------------------- */ /* Proxy classes */ /* -------------------------------------------------------------------------- */ namespace tensors { template struct is_copyable { enum : bool { value = false }; }; template struct is_copyable { enum : bool { value = true }; }; template struct is_copyable { enum : bool { value = true }; }; template struct is_copyable { enum : bool { value = true }; }; } // namespace tensors /** * @class TensorProxy aka_types.hh * @desc The TensorProxy class is a proxy class to the TensorStorage it handles * the * wrapped case. That is to say if an accessor should give access to a Tensor * wrapped on some data, like the Array::iterator they can return a * TensorProxy that will be automatically transformed as a TensorStorage wrapped * on the same data * @tparam T stored type * @tparam ndim order of the tensor * @tparam RetType real derived type */ template class TensorProxy { protected: using RetTypeProxy = typename _RetType::proxy; constexpr TensorProxy(T * data, UInt m, UInt n, UInt p) { DimHelper::setDims(m, n, p, this->n); this->values = data; } #ifndef SWIG template ::value>> explicit TensorProxy(const Other & other) { this->values = other.storage(); for (UInt i = 0; i < ndim; ++i) this->n[i] = other.size(i); } #endif public: using RetType = _RetType; UInt size(UInt i) const { AKANTU_DEBUG_ASSERT(i < ndim, "This tensor has only " << ndim << " dimensions, not " << (i + 1)); return n[i]; } inline UInt size() const { UInt _size = 1; for (UInt d = 0; d < ndim; ++d) _size *= this->n[d]; return _size; } T * storage() const { return values; } #ifndef SWIG template ::value>> inline TensorProxy & operator=(const Other & other) { AKANTU_DEBUG_ASSERT( other.size() == this->size(), "You are trying to copy two tensors with different sizes"); memcpy(this->values, other.storage(), this->size() * sizeof(T)); return *this; } #endif // template ::value>> // inline TensorProxy & operator=(const Other && other) { // AKANTU_DEBUG_ASSERT( // other.size() == this->size(), // "You are trying to copy two tensors with different sizes"); // memcpy(this->values, other.storage(), this->size() * sizeof(T)); // return *this; // } template inline RetTypeProxy & operator*=(const O & o) { RetType(*this) *= o; return static_cast(*this); } template inline RetTypeProxy & operator/=(const O & o) { RetType(*this) /= o; return static_cast(*this); } protected: T * values; UInt n[ndim]; }; /* -------------------------------------------------------------------------- */ template class VectorProxy : public TensorProxy> { using parent = TensorProxy>; using type = Vector; public: constexpr VectorProxy(T * data, UInt n) : parent(data, n, 0, 0) {} template explicit VectorProxy(Other & src) : parent(src) {} /* ---------------------------------------------------------------------- */ template inline VectorProxy & operator=(const Other & other) { parent::operator=(other); return *this; } // inline VectorProxy & operator=(const VectorProxy && other) { // parent::operator=(other); // return *this; // } /* ------------------------------------------------------------------------ */ T & operator()(UInt index) { return this->values[index]; }; const T & operator()(UInt index) const { return this->values[index]; }; }; template class MatrixProxy : public TensorProxy> { using parent = TensorProxy>; using type = Matrix; public: MatrixProxy(T * data, UInt m, UInt n) : parent(data, m, n, 0) {} template explicit MatrixProxy(Other & src) : parent(src) {} /* ---------------------------------------------------------------------- */ template inline MatrixProxy & operator=(const Other & other) { parent::operator=(other); return *this; } }; template class Tensor3Proxy : public TensorProxy> { using parent = TensorProxy>; using type = Tensor3; public: Tensor3Proxy(const T * data, UInt m, UInt n, UInt k) : parent(data, m, n, k) {} Tensor3Proxy(const Tensor3Proxy & src) : parent(src) {} Tensor3Proxy(const Tensor3 & src) : parent(src) {} /* ---------------------------------------------------------------------- */ template inline Tensor3Proxy & operator=(const Other & other) { parent::operator=(other); return *this; } }; /* -------------------------------------------------------------------------- */ /* Tensor base class */ /* -------------------------------------------------------------------------- */ template class TensorStorage : public TensorTrait { public: using value_type = T; friend class Array; protected: template void copySize(const TensorType & src) { for (UInt d = 0; d < ndim; ++d) this->n[d] = src.size(d); this->_size = src.size(); } TensorStorage() : values(nullptr) { for (UInt d = 0; d < ndim; ++d) this->n[d] = 0; _size = 0; } TensorStorage(const TensorProxy & proxy) { this->copySize(proxy); this->values = proxy.storage(); this->wrapped = true; } public: TensorStorage(const TensorStorage & src) = delete; TensorStorage(const TensorStorage & src, bool deep_copy) : values(nullptr) { if (deep_copy) this->deepCopy(src); else this->shallowCopy(src); } protected: TensorStorage(UInt m, UInt n, UInt p, const T & def) { static_assert(std::is_trivially_constructible{}, "Cannot create a tensor on non trivial types"); DimHelper::setDims(m, n, p, this->n); this->computeSize(); this->values = new T[this->_size]; this->set(def); this->wrapped = false; } TensorStorage(T * data, UInt m, UInt n, UInt p) { DimHelper::setDims(m, n, p, this->n); this->computeSize(); this->values = data; this->wrapped = true; } public: /* ------------------------------------------------------------------------ */ template inline void shallowCopy(const TensorType & src) { this->copySize(src); if (!this->wrapped) delete[] this->values; this->values = src.storage(); this->wrapped = true; } /* ------------------------------------------------------------------------ */ template inline void deepCopy(const TensorType & src) { this->copySize(src); if (!this->wrapped) delete[] this->values; static_assert(std::is_trivially_constructible{}, "Cannot create a tensor on non trivial types"); this->values = new T[this->_size]; static_assert(std::is_trivially_copyable{}, "Cannot copy a tensor on non trivial types"); memcpy((void *)this->values, (void *)src.storage(), this->_size * sizeof(T)); this->wrapped = false; } virtual ~TensorStorage() { if (!this->wrapped) delete[] this->values; } /* ------------------------------------------------------------------------ */ inline TensorStorage & operator=(const TensorStorage & src) { return this->operator=(dynamic_cast(src)); } /* ------------------------------------------------------------------------ */ inline TensorStorage & operator=(const RetType & src) { if (this != &src) { if (this->wrapped) { static_assert(std::is_trivially_copyable{}, "Cannot copy a tensor on non trivial types"); // this test is not sufficient for Tensor of order higher than 1 AKANTU_DEBUG_ASSERT(this->_size == src.size(), "Tensors of different size"); memcpy((void *)this->values, (void *)src.storage(), this->_size * sizeof(T)); } else { deepCopy(src); } } return *this; } /* ------------------------------------------------------------------------ */ template inline RetType & operator+=(const TensorStorage & other) { T * a = this->storage(); T * b = other.storage(); AKANTU_DEBUG_ASSERT( _size == other.size(), "The two tensors do not have the same size, they cannot be subtracted"); for (UInt i = 0; i < _size; ++i) *(a++) += *(b++); return *(static_cast(this)); } /* ------------------------------------------------------------------------ */ template inline RetType & operator-=(const TensorStorage & other) { T * a = this->storage(); T * b = other.storage(); AKANTU_DEBUG_ASSERT( _size == other.size(), "The two tensors do not have the same size, they cannot be subtracted"); for (UInt i = 0; i < _size; ++i) *(a++) -= *(b++); return *(static_cast(this)); } /* ------------------------------------------------------------------------ */ inline RetType & operator+=(const T & x) { T * a = this->values; for (UInt i = 0; i < _size; ++i) *(a++) += x; return *(static_cast(this)); } /* ------------------------------------------------------------------------ */ inline RetType & operator-=(const T & x) { T * a = this->values; for (UInt i = 0; i < _size; ++i) *(a++) -= x; return *(static_cast(this)); } /* ------------------------------------------------------------------------ */ inline RetType & operator*=(const T & x) { T * a = this->storage(); for (UInt i = 0; i < _size; ++i) *(a++) *= x; return *(static_cast(this)); } /* ---------------------------------------------------------------------- */ inline RetType & operator/=(const T & x) { T * a = this->values; for (UInt i = 0; i < _size; ++i) *(a++) /= x; return *(static_cast(this)); } /// Y = \alpha X + Y inline RetType & aXplusY(const TensorStorage & other, const T & alpha = 1.) { AKANTU_DEBUG_ASSERT( _size == other.size(), "The two tensors do not have the same size, they cannot be subtracted"); Math::aXplusY(this->_size, alpha, other.storage(), this->storage()); return *(static_cast(this)); } /* ------------------------------------------------------------------------ */ T * storage() const { return values; } UInt size() const { return _size; } UInt size(UInt i) const { AKANTU_DEBUG_ASSERT(i < ndim, "This tensor has only " << ndim << " dimensions, not " << (i + 1)); return n[i]; }; /* ------------------------------------------------------------------------ */ inline void clear() { memset(values, 0, _size * sizeof(T)); }; inline void set(const T & t) { std::fill_n(values, _size, t); }; template inline void copy(const TensorType & other) { AKANTU_DEBUG_ASSERT( _size == other.size(), "The two tensors do not have the same size, they cannot be copied"); memcpy(values, other.storage(), _size * sizeof(T)); } bool isWrapped() const { return this->wrapped; } protected: inline void computeSize() { _size = 1; for (UInt d = 0; d < ndim; ++d) _size *= this->n[d]; } protected: template struct NormHelper { template static R norm(const Ten & ten) { R _norm = 0.; R * it = ten.storage(); R * end = ten.storage() + ten.size(); for (; it < end; ++it) _norm += std::pow(std::abs(*it), norm_type); return std::pow(_norm, 1. / norm_type); } }; template struct NormHelper { template static R norm(const Ten & ten) { R _norm = 0.; R * it = ten.storage(); R * end = ten.storage() + ten.size(); for (; it < end; ++it) _norm += std::abs(*it); return _norm; } }; template struct NormHelper { template static R norm(const Ten & ten) { R _norm = 0.; R * it = ten.storage(); R * end = ten.storage() + ten.size(); for (; it < end; ++it) _norm += *it * *it; return sqrt(_norm); } }; template struct NormHelper { template static R norm(const Ten & ten) { R _norm = 0.; R * it = ten.storage(); R * end = ten.storage() + ten.size(); for (; it < end; ++it) _norm = std::max(std::abs(*it), _norm); return _norm; } }; public: /*----------------------------------------------------------------------- */ /// "Entrywise" norm norm @f[ \|\boldsymbol{T}\|_p = \left( /// \sum_i^{n[0]}\sum_j^{n[1]}\sum_k^{n[2]} |T_{ijk}|^p \right)^{\frac{1}{p}} /// @f] template inline T norm() const { return NormHelper::norm(*this); } protected: UInt n[ndim]; UInt _size; T * values; bool wrapped{false}; }; /* -------------------------------------------------------------------------- */ /* Vector */ /* -------------------------------------------------------------------------- */ template class Vector : public TensorStorage> { using parent = TensorStorage>; public: using value_type = typename parent::value_type; using proxy = VectorProxy; public: Vector() : parent() {} explicit Vector(UInt n, const T & def = T()) : parent(n, 0, 0, def) {} Vector(T * data, UInt n) : parent(data, n, 0, 0) {} Vector(const Vector & src, bool deep_copy = true) : parent(src, deep_copy) {} Vector(const TensorProxy & src) : parent(src) {} Vector(std::initializer_list list) : parent(list.size(), 0, 0, T()) { UInt i = 0; for (auto val : list) { operator()(i++) = val; } } public: ~Vector() override = default; /* ------------------------------------------------------------------------ */ inline Vector & operator=(const Vector & src) { parent::operator=(src); return *this; } /* ------------------------------------------------------------------------ */ inline T & operator()(UInt i) { AKANTU_DEBUG_ASSERT((i < this->n[0]), "Access out of the vector! " << "Index (" << i << ") is out of the vector of size (" << this->n[0] << ")"); return *(this->values + i); } inline const T & operator()(UInt i) const { AKANTU_DEBUG_ASSERT((i < this->n[0]), "Access out of the vector! " << "Index (" << i << ") is out of the vector of size (" << this->n[0] << ")"); return *(this->values + i); } inline T & operator[](UInt i) { return this->operator()(i); } inline const T & operator[](UInt i) const { return this->operator()(i); } /* ------------------------------------------------------------------------ */ inline Vector & operator*=(Real x) { return parent::operator*=(x); } inline Vector & operator/=(Real x) { return parent::operator/=(x); } /* ------------------------------------------------------------------------ */ inline Vector & operator*=(const Vector & vect) { T * a = this->storage(); T * b = vect.storage(); for (UInt i = 0; i < this->_size; ++i) *(a++) *= *(b++); return *this; } /* ------------------------------------------------------------------------ */ inline Real dot(const Vector & vect) const { return Math::vectorDot(this->values, vect.storage(), this->_size); } /* ------------------------------------------------------------------------ */ inline Real mean() const { Real mean = 0; T * a = this->storage(); for (UInt i = 0; i < this->_size; ++i) mean += *(a++); return mean / this->_size; } /* ------------------------------------------------------------------------ */ inline Vector & crossProduct(const Vector & v1, const Vector & v2) { AKANTU_DEBUG_ASSERT(this->size() == 3, "crossProduct is only defined in 3D (n=" << this->size() << ")"); AKANTU_DEBUG_ASSERT( this->size() == v1.size() && this->size() == v2.size(), "crossProduct is not a valid operation non matching size vectors"); Math::vectorProduct3(v1.storage(), v2.storage(), this->values); return *this; } inline Vector crossProduct(const Vector & v) { Vector tmp(this->size()); tmp.crossProduct(*this, v); return tmp; } /* ------------------------------------------------------------------------ */ inline void solve(const Matrix & A, const Vector & b) { AKANTU_DEBUG_ASSERT( this->size() == A.rows() && this->_size == A.cols(), "The size of the solution vector mismatches the size of the matrix"); AKANTU_DEBUG_ASSERT( this->_size == b._size, "The rhs vector has a mismatch in size with the matrix"); Math::solve(this->_size, A.storage(), this->values, b.storage()); } /* ------------------------------------------------------------------------ */ template inline void mul(const Matrix & A, const Vector & x, Real alpha = 1.0); /* ------------------------------------------------------------------------ */ inline Real norm() const { return parent::template norm(); } template inline Real norm() const { return parent::template norm(); } /* ------------------------------------------------------------------------ */ inline Vector & normalize() { Real n = norm(); operator/=(n); return *this; } /* ------------------------------------------------------------------------ */ /// norm of (*this - x) inline Real distance(const Vector & y) const { Real * vx = this->values; Real * vy = y.storage(); Real sum_2 = 0; for (UInt i = 0; i < this->_size; ++i, ++vx, ++vy) sum_2 += (*vx - *vy) * (*vx - *vy); return sqrt(sum_2); } /* ------------------------------------------------------------------------ */ inline bool equal(const Vector & v, Real tolerance = Math::getTolerance()) const { T * a = this->storage(); T * b = v.storage(); UInt i = 0; while (i < this->_size && (std::abs(*(a++) - *(b++)) < tolerance)) ++i; return i == this->_size; } /* ------------------------------------------------------------------------ */ inline short compare(const Vector & v, Real tolerance = Math::getTolerance()) const { T * a = this->storage(); T * b = v.storage(); for (UInt i(0); i < this->_size; ++i, ++a, ++b) { if (std::abs(*a - *b) > tolerance) return (((*a - *b) > tolerance) ? 1 : -1); } return 0; } /* ------------------------------------------------------------------------ */ inline bool operator==(const Vector & v) const { return equal(v); } inline bool operator!=(const Vector & v) const { return !operator==(v); } inline bool operator<(const Vector & v) const { return compare(v) == -1; } inline bool operator>(const Vector & v) const { return compare(v) == 1; } /* ------------------------------------------------------------------------ */ /// function to print the containt of the class virtual void printself(std::ostream & stream, int indent = 0) const { std::string space; for (Int i = 0; i < indent; i++, space += AKANTU_INDENT) ; stream << "["; for (UInt i = 0; i < this->_size; ++i) { if (i != 0) stream << ", "; stream << this->values[i]; } stream << "]"; } // friend class ::akantu::Array; }; using RVector = Vector; /* ------------------------------------------------------------------------ */ template <> inline bool Vector::equal(const Vector & v, __attribute__((unused)) Real tolerance) const { UInt * a = this->storage(); UInt * b = v.storage(); UInt i = 0; while (i < this->_size && (*(a++) == *(b++))) ++i; return i == this->_size; } /* ------------------------------------------------------------------------ */ /* Matrix */ /* ------------------------------------------------------------------------ */ template class Matrix : public TensorStorage> { using parent = TensorStorage>; public: using value_type = typename parent::value_type; using proxy = MatrixProxy; public: Matrix() : parent() {} Matrix(UInt m, UInt n, const T & def = T()) : parent(m, n, 0, def) {} Matrix(T * data, UInt m, UInt n) : parent(data, m, n, 0) {} Matrix(const Matrix & src, bool deep_copy = true) : parent(src, deep_copy) {} Matrix(const MatrixProxy & src) : parent(src) {} Matrix(std::initializer_list> list) { static_assert(std::is_trivially_copyable{}, "Cannot create a tensor on non trivial types"); std::size_t n = 0; std::size_t m = list.size(); for (auto row : list) { n = std::max(n, row.size()); } DimHelper<2>::setDims(m, n, 0, this->n); this->computeSize(); this->values = new T[this->_size]; this->set(0); UInt i = 0, j = 0; for (auto & row : list) { for (auto & val : row) { at(i, j++) = val; } ++i; j = 0; } } ~Matrix() override = default; /* ------------------------------------------------------------------------ */ inline Matrix & operator=(const Matrix & src) { parent::operator=(src); return *this; } public: /* ---------------------------------------------------------------------- */ UInt rows() const { return this->n[0]; } UInt cols() const { return this->n[1]; } /* ---------------------------------------------------------------------- */ inline T & at(UInt i, UInt j) { AKANTU_DEBUG_ASSERT(((i < this->n[0]) && (j < this->n[1])), "Access out of the matrix! " << "Index (" << i << ", " << j << ") is out of the matrix of size (" << this->n[0] << ", " << this->n[1] << ")"); return *(this->values + i + j * this->n[0]); } inline const T & at(UInt i, UInt j) const { AKANTU_DEBUG_ASSERT(((i < this->n[0]) && (j < this->n[1])), "Access out of the matrix! " << "Index (" << i << ", " << j << ") is out of the matrix of size (" << this->n[0] << ", " << this->n[1] << ")"); return *(this->values + i + j * this->n[0]); } /* ------------------------------------------------------------------------ */ inline T & operator()(UInt i, UInt j) { return this->at(i, j); } inline const T & operator()(UInt i, UInt j) const { return this->at(i, j); } /// give a line vector wrapped on the column i inline VectorProxy operator()(UInt j) { AKANTU_DEBUG_ASSERT(j < this->n[1], "Access out of the matrix! " << "You are trying to access the column vector " << j << " in a matrix of size (" << this->n[0] << ", " << this->n[1] << ")"); return VectorProxy(this->values + j * this->n[0], this->n[0]); } inline const VectorProxy operator()(UInt j) const { AKANTU_DEBUG_ASSERT(j < this->n[1], "Access out of the matrix! " << "You are trying to access the column vector " << j << " in a matrix of size (" << this->n[0] << ", " << this->n[1] << ")"); return VectorProxy(this->values + j * this->n[0], this->n[0]); } - inline void block(Matrix & block, UInt pos_i, UInt pos_j) { + inline void block(const Matrix & block, UInt pos_i, UInt pos_j) { AKANTU_DEBUG_ASSERT(pos_i + block.rows() <= rows(), "The block size or position are not correct"); AKANTU_DEBUG_ASSERT(pos_i + block.cols() <= cols(), "The block size or position are not correct"); for (UInt i = 0; i < block.rows(); ++i) for (UInt j = 0; j < block.cols(); ++j) this->at(i + pos_i, j + pos_j) = block(i, j); } inline Matrix block(UInt pos_i, UInt pos_j, UInt block_rows, UInt block_cols) const { AKANTU_DEBUG_ASSERT(pos_i + block_rows <= rows(), "The block size or position are not correct"); AKANTU_DEBUG_ASSERT(pos_i + block_cols <= cols(), "The block size or position are not correct"); Matrix block(block_rows, block_cols); for (UInt i = 0; i < block_rows; ++i) for (UInt j = 0; j < block_cols; ++j) block(i, j) = this->at(i + pos_i, j + pos_j); return block; } inline T & operator[](UInt idx) { return *(this->values + idx); }; inline const T & operator[](UInt idx) const { return *(this->values + idx); }; /* ---------------------------------------------------------------------- */ inline Matrix operator*(const Matrix & B) { Matrix C(this->rows(), B.cols()); C.mul(*this, B); return C; } /* ----------------------------------------------------------------------- */ inline Matrix & operator*=(const T & x) { return parent::operator*=(x); } inline Matrix & operator*=(const Matrix & B) { Matrix C(*this); this->mul(C, B); return *this; } /* ---------------------------------------------------------------------- */ template inline void mul(const Matrix & A, const Matrix & B, T alpha = 1.0) { UInt k = A.cols(); if (tr_A) k = A.rows(); #ifndef AKANTU_NDEBUG if (tr_B) { AKANTU_DEBUG_ASSERT(k == B.cols(), "matrices to multiply have no fit dimensions"); AKANTU_DEBUG_ASSERT(this->cols() == B.rows(), "matrices to multiply have no fit dimensions"); } else { AKANTU_DEBUG_ASSERT(k == B.rows(), "matrices to multiply have no fit dimensions"); AKANTU_DEBUG_ASSERT(this->cols() == B.cols(), "matrices to multiply have no fit dimensions"); } if (tr_A) { AKANTU_DEBUG_ASSERT(this->rows() == A.cols(), "matrices to multiply have no fit dimensions"); } else { AKANTU_DEBUG_ASSERT(this->rows() == A.rows(), "matrices to multiply have no fit dimensions"); } #endif // AKANTU_NDEBUG Math::matMul(this->rows(), this->cols(), k, alpha, A.storage(), B.storage(), 0., this->storage()); } /* ---------------------------------------------------------------------- */ inline void outerProduct(const Vector & A, const Vector & B) { AKANTU_DEBUG_ASSERT( A.size() == this->rows() && B.size() == this->cols(), "A and B are not compatible with the size of the matrix"); for (UInt i = 0; i < this->rows(); ++i) { for (UInt j = 0; j < this->cols(); ++j) { this->values[i + j * this->rows()] += A[i] * B[j]; } } } private: class EigenSorter { public: EigenSorter(const Vector & eigs) : eigs(eigs) {} bool operator()(const UInt & a, const UInt & b) const { return (eigs(a) > eigs(b)); } private: const Vector & eigs; }; public: /* ---------------------------------------------------------------------- */ inline void eig(Vector & eigenvalues, Matrix & eigenvectors) const { AKANTU_DEBUG_ASSERT(this->cols() == this->rows(), "eig is not a valid operation on a rectangular matrix"); AKANTU_DEBUG_ASSERT(eigenvalues.size() == this->cols(), "eigenvalues should be of size " << this->cols() << "."); #ifndef AKANTU_NDEBUG if (eigenvectors.storage() != nullptr) AKANTU_DEBUG_ASSERT((eigenvectors.rows() == eigenvectors.cols()) && (eigenvectors.rows() == this->cols()), "Eigenvectors needs to be a square matrix of size " << this->cols() << " x " << this->cols() << "."); #endif Matrix tmp = *this; Vector tmp_eigs(eigenvalues.size()); Matrix tmp_eig_vects(eigenvectors.rows(), eigenvectors.cols()); if (tmp_eig_vects.rows() == 0 || tmp_eig_vects.cols() == 0) Math::matrixEig(tmp.cols(), tmp.storage(), tmp_eigs.storage()); else Math::matrixEig(tmp.cols(), tmp.storage(), tmp_eigs.storage(), tmp_eig_vects.storage()); Vector perm(eigenvalues.size()); for (UInt i = 0; i < perm.size(); ++i) perm(i) = i; std::sort(perm.storage(), perm.storage() + perm.size(), EigenSorter(tmp_eigs)); for (UInt i = 0; i < perm.size(); ++i) eigenvalues(i) = tmp_eigs(perm(i)); if (tmp_eig_vects.rows() != 0 && tmp_eig_vects.cols() != 0) for (UInt i = 0; i < perm.size(); ++i) { for (UInt j = 0; j < eigenvectors.rows(); ++j) { eigenvectors(j, i) = tmp_eig_vects(j, perm(i)); } } } /* ---------------------------------------------------------------------- */ inline void eig(Vector & eigenvalues) const { Matrix empty; eig(eigenvalues, empty); } /* ---------------------------------------------------------------------- */ inline void eye(T alpha = 1.) { AKANTU_DEBUG_ASSERT(this->cols() == this->rows(), "eye is not a valid operation on a rectangular matrix"); this->clear(); for (UInt i = 0; i < this->cols(); ++i) { this->values[i + i * this->rows()] = alpha; } } /* ---------------------------------------------------------------------- */ static inline Matrix eye(UInt m, T alpha = 1.) { Matrix tmp(m, m); tmp.eye(alpha); return tmp; } /* ---------------------------------------------------------------------- */ inline T trace() const { AKANTU_DEBUG_ASSERT( this->cols() == this->rows(), "trace is not a valid operation on a rectangular matrix"); T trace = 0.; for (UInt i = 0; i < this->rows(); ++i) { trace += this->values[i + i * this->rows()]; } return trace; } /* ---------------------------------------------------------------------- */ inline Matrix transpose() const { Matrix tmp(this->cols(), this->rows()); for (UInt i = 0; i < this->rows(); ++i) { for (UInt j = 0; j < this->cols(); ++j) { tmp(j, i) = operator()(i, j); } } return tmp; } /* ---------------------------------------------------------------------- */ inline void inverse(const Matrix & A) { AKANTU_DEBUG_ASSERT(A.cols() == A.rows(), "inv is not a valid operation on a rectangular matrix"); AKANTU_DEBUG_ASSERT(this->cols() == A.cols(), "the matrix should have the same size as its inverse"); if (this->cols() == 1) *this->values = 1. / *A.storage(); else if (this->cols() == 2) Math::inv2(A.storage(), this->values); else if (this->cols() == 3) Math::inv3(A.storage(), this->values); else Math::inv(this->cols(), A.storage(), this->values); } inline Matrix inverse() { Matrix inv(this->rows(), this->cols()); inv.inverse(*this); return inv; } /* --------------------------------------------------------------------- */ inline T det() const { AKANTU_DEBUG_ASSERT(this->cols() == this->rows(), "inv is not a valid operation on a rectangular matrix"); if (this->cols() == 1) return *(this->values); else if (this->cols() == 2) return Math::det2(this->values); else if (this->cols() == 3) return Math::det3(this->values); else return Math::det(this->cols(), this->values); } /* --------------------------------------------------------------------- */ inline T doubleDot(const Matrix & other) const { AKANTU_DEBUG_ASSERT( this->cols() == this->rows(), "doubleDot is not a valid operation on a rectangular matrix"); if (this->cols() == 1) return *(this->values) * *(other.storage()); else if (this->cols() == 2) return Math::matrixDoubleDot22(this->values, other.storage()); else if (this->cols() == 3) return Math::matrixDoubleDot33(this->values, other.storage()); else AKANTU_DEBUG_ERROR("doubleDot is not defined for other spatial dimensions" << " than 1, 2 or 3."); return T(); } /* ---------------------------------------------------------------------- */ /// function to print the containt of the class virtual void printself(std::ostream & stream, int indent = 0) const { std::string space; for (Int i = 0; i < indent; i++, space += AKANTU_INDENT) ; stream << "["; for (UInt i = 0; i < this->n[0]; ++i) { if (i != 0) stream << ", "; stream << "["; for (UInt j = 0; j < this->n[1]; ++j) { if (j != 0) stream << ", "; stream << operator()(i, j); } stream << "]"; } stream << "]"; }; }; /* ------------------------------------------------------------------------ */ template template inline void Vector::mul(const Matrix & A, const Vector & x, Real alpha) { #ifndef AKANTU_NDEBUG UInt n = x.size(); if (tr_A) { AKANTU_DEBUG_ASSERT(n == A.rows(), "matrix and vector to multiply have no fit dimensions"); AKANTU_DEBUG_ASSERT(this->size() == A.cols(), "matrix and vector to multiply have no fit dimensions"); } else { AKANTU_DEBUG_ASSERT(n == A.cols(), "matrix and vector to multiply have no fit dimensions"); AKANTU_DEBUG_ASSERT(this->size() == A.rows(), "matrix and vector to multiply have no fit dimensions"); } #endif Math::matVectMul(A.rows(), A.cols(), alpha, A.storage(), x.storage(), 0., this->storage()); } /* -------------------------------------------------------------------------- */ template inline std::ostream & operator<<(std::ostream & stream, const Matrix & _this) { _this.printself(stream); return stream; } /* -------------------------------------------------------------------------- */ template inline std::ostream & operator<<(std::ostream & stream, const Vector & _this) { _this.printself(stream); return stream; } /* ------------------------------------------------------------------------ */ /* Tensor3 */ /* ------------------------------------------------------------------------ */ template class Tensor3 : public TensorStorage> { using parent = TensorStorage>; public: using value_type = typename parent::value_type; using proxy = Tensor3Proxy; public: Tensor3() : parent(){}; Tensor3(UInt m, UInt n, UInt p, const T & def = T()) : parent(m, n, p, def) {} Tensor3(T * data, UInt m, UInt n, UInt p) : parent(data, m, n, p) {} Tensor3(const Tensor3 & src, bool deep_copy = true) : parent(src, deep_copy) {} Tensor3(const proxy & src) : parent(src) {} public: /* ------------------------------------------------------------------------ */ inline Tensor3 & operator=(const Tensor3 & src) { parent::operator=(src); return *this; } /* ---------------------------------------------------------------------- */ inline T & operator()(UInt i, UInt j, UInt k) { AKANTU_DEBUG_ASSERT( (i < this->n[0]) && (j < this->n[1]) && (k < this->n[2]), "Access out of the tensor3! " << "You are trying to access the element " << "(" << i << ", " << j << ", " << k << ") in a tensor of size (" << this->n[0] << ", " << this->n[1] << ", " << this->n[2] << ")"); return *(this->values + (k * this->n[0] + i) * this->n[1] + j); } inline const T & operator()(UInt i, UInt j, UInt k) const { AKANTU_DEBUG_ASSERT( (i < this->n[0]) && (j < this->n[1]) && (k < this->n[2]), "Access out of the tensor3! " << "You are trying to access the element " << "(" << i << ", " << j << ", " << k << ") in a tensor of size (" << this->n[0] << ", " << this->n[1] << ", " << this->n[2] << ")"); return *(this->values + (k * this->n[0] + i) * this->n[1] + j); } inline MatrixProxy operator()(UInt k) { AKANTU_DEBUG_ASSERT((k < this->n[2]), "Access out of the tensor3! " << "You are trying to access the slice " << k << " in a tensor3 of size (" << this->n[0] << ", " << this->n[1] << ", " << this->n[2] << ")"); return MatrixProxy(this->values + k * this->n[0] * this->n[1], this->n[0], this->n[1]); } inline const MatrixProxy operator()(UInt k) const { AKANTU_DEBUG_ASSERT((k < this->n[2]), "Access out of the tensor3! " << "You are trying to access the slice " << k << " in a tensor3 of size (" << this->n[0] << ", " << this->n[1] << ", " << this->n[2] << ")"); return MatrixProxy(this->values + k * this->n[0] * this->n[1], this->n[0], this->n[1]); } inline MatrixProxy operator[](UInt k) { return MatrixProxy(this->values + k * this->n[0] * this->n[1], this->n[0], this->n[1]); } inline const MatrixProxy operator[](UInt k) const { return MatrixProxy(this->values + k * this->n[0] * this->n[1], this->n[0], this->n[1]); } }; /* -------------------------------------------------------------------------- */ // support operations for the creation of other vectors /* -------------------------------------------------------------------------- */ template Vector operator*(const T & scalar, const Vector & a) { Vector r(a); r *= scalar; return r; } template Vector operator*(const Vector & a, const T & scalar) { Vector r(a); r *= scalar; return r; } template Vector operator/(const Vector & a, const T & scalar) { Vector r(a); r /= scalar; return r; } template Vector operator*(const Vector & a, const Vector & b) { Vector r(a); r *= b; return r; } template Vector operator+(const Vector & a, const Vector & b) { Vector r(a); r += b; return r; } template Vector operator-(const Vector & a, const Vector & b) { Vector r(a); r -= b; return r; } template Vector operator*(const Matrix & A, const Vector & b) { Vector r(b.size()); r.template mul(A, b); return r; } /* -------------------------------------------------------------------------- */ template Matrix operator*(const T & scalar, const Matrix & a) { Matrix r(a); r *= scalar; return r; } template Matrix operator*(const Matrix & a, const T & scalar) { Matrix r(a); r *= scalar; return r; } template Matrix operator/(const Matrix & a, const T & scalar) { Matrix r(a); r /= scalar; return r; } template Matrix operator+(const Matrix & a, const Matrix & b) { Matrix r(a); r += b; return r; } template Matrix operator-(const Matrix & a, const Matrix & b) { Matrix r(a); r -= b; return r; } } // namespace akantu #endif /* __AKANTU_AKA_TYPES_HH__ */ diff --git a/src/fe_engine/element_class_structural.hh b/src/fe_engine/element_class_structural.hh index 1e3a79b47..3dbefe93d 100644 --- a/src/fe_engine/element_class_structural.hh +++ b/src/fe_engine/element_class_structural.hh @@ -1,277 +1,250 @@ /** * @file element_class_structural.hh * * @author Fabian Barras * @author Nicolas Richart * @author Damien Spielmann * * @date creation: Thu Feb 21 2013 * @date last modification: Thu Oct 22 2015 * * @brief Specialization of the element classes for structural elements * * @section LICENSE * * Copyright (©) 2014, 2015 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "element_class.hh" /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_ELEMENT_CLASS_STRUCTURAL_HH__ #define __AKANTU_ELEMENT_CLASS_STRUCTURAL_HH__ namespace akantu { /// Macro to generate the InterpolationProperty structures for different /// interpolation types #define AKANTU_DEFINE_STRUCTURAL_INTERPOLATION_TYPE_PROPERTY( \ - itp_type, itp_geom_type, ndof, nb_stress) \ + itp_type, itp_geom_type, ndof, nb_stress, nb_dnds_cols) \ template <> struct InterpolationProperty { \ static const InterpolationKind kind{_itk_structural}; \ static const UInt nb_nodes_per_element{ \ InterpolationProperty::nb_nodes_per_element}; \ static const InterpolationType itp_geometry_type{itp_geom_type}; \ static const UInt natural_space_dimension{ \ InterpolationProperty::natural_space_dimension}; \ static const UInt nb_degree_of_freedom{ndof}; \ static const UInt nb_stress_components{nb_stress}; \ + static const UInt dnds_columns{nb_dnds_cols}; \ } /* -------------------------------------------------------------------------- */ template class InterpolationElement { public: using interpolation_property = InterpolationProperty; /// compute the shape values for a given set of points in natural coordinates static inline void computeShapes(const Matrix & natural_coord, const Matrix & real_coord, Tensor3 & N) { for (UInt i = 0; i < natural_coord.cols(); ++i) { Matrix n_t = N(i); computeShapes(natural_coord(i), real_coord, n_t); } } /// compute the shape values for a given point in natural coordinates static inline void computeShapes(const Vector & natural_coord, const Matrix & real_coord, Matrix & N); /// compute shape derivatives (input is dxds) for a set of points static inline void computeShapeDerivatives(const Tensor3 & Js, const Tensor3 & DNDSs, const Matrix & R, Tensor3 & Bs) { for (UInt i = 0; i < Js.size(2); ++i) { Matrix J = Js(i); Matrix DNDS = DNDSs(i); - Matrix DNDS_R(DNDS.rows(), DNDS.cols()); - DNDS_R.mul(DNDS, R); - Matrix B = Bs(i); + Matrix DNDX(DNDS.rows(), DNDS.cols()); auto inv_J = J.inverse(); - Matrix inv_J_full(DNDS.rows(), DNDS.rows()); - - // Gotta repeat J^-1 for each stress component - for (UInt k = 0, pos = 0; k < getNbStressComponents(); - k++, pos += inv_J.rows()) { - inv_J_full.block(inv_J, pos, pos); - } - - B.mul(inv_J_full, DNDS_R); + DNDX.mul(inv_J, DNDS); + Matrix B_R = Bs(i); + Matrix B(B_R.rows(), B_R.cols()); + arrangeInVoigt(DNDX, B); + B_R.mul(B, R); } } /** * compute @f$ B_{ij} = \frac{\partial N_j}{\partial S_i} @f$ the variation of * shape functions along with variation of natural coordinates on a given set * of points in natural coordinates */ static inline void computeDNDS(const Matrix & natural_coord, const Matrix & real_coord, Tensor3 & dnds) { for (UInt i = 0; i < natural_coord.cols(); ++i) { Matrix dnds_t = dnds(i); computeDNDS(natural_coord(i), real_coord, dnds_t); } } /** * compute @f$ B_{ij} = \frac{\partial N_j}{\partial S_i} @f$ the variation of * shape functions along with * variation of natural coordinates on a given point in natural * coordinates */ static inline void computeDNDS(const Vector & natural_coord, const Matrix & real_coord, Matrix & dnds); + /** + * arrange B in Voigt notation from DNDS + */ + static inline void arrangeInVoigt(const Matrix & dnds, + Matrix & B) { + // Default implementation assumes dnds is already in Voigt notation + B.deepCopy(dnds); + } + public: static AKANTU_GET_MACRO_NOT_CONST( NbNodesPerInterpolationElement, interpolation_property::nb_nodes_per_element, UInt); static AKANTU_GET_MACRO_NOT_CONST( ShapeSize, (interpolation_property::nb_nodes_per_element * interpolation_property::nb_degree_of_freedom * interpolation_property::nb_degree_of_freedom), UInt); static AKANTU_GET_MACRO_NOT_CONST( ShapeDerivativesSize, (interpolation_property::nb_nodes_per_element * interpolation_property::nb_degree_of_freedom * interpolation_property::nb_stress_components), UInt); static AKANTU_GET_MACRO_NOT_CONST( NaturalSpaceDimension, interpolation_property::natural_space_dimension, UInt); static AKANTU_GET_MACRO_NOT_CONST( NbDegreeOfFreedom, interpolation_property::nb_degree_of_freedom, UInt); static AKANTU_GET_MACRO_NOT_CONST( NbStressComponents, interpolation_property::nb_stress_components, UInt); }; /// Macro to generate the element class structures for different structural /// element types /* -------------------------------------------------------------------------- */ #define AKANTU_DEFINE_STRUCTURAL_ELEMENT_CLASS_PROPERTY( \ elem_type, geom_type, interp_type, parent_el_type, elem_kind, sp, \ gauss_int_type, min_int_order) \ template <> struct ElementClassProperty { \ static const GeometricalType geometrical_type{geom_type}; \ static const InterpolationType interpolation_type{interp_type}; \ static const ElementType parent_element_type{parent_el_type}; \ static const ElementKind element_kind{elem_kind}; \ static const UInt spatial_dimension{sp}; \ static const GaussIntegrationType gauss_integration_type{gauss_int_type}; \ static const UInt polynomial_degree{min_int_order}; \ } /* -------------------------------------------------------------------------- */ /* ElementClass for structural elements */ /* -------------------------------------------------------------------------- */ template class ElementClass : public GeometricalElement< ElementClassProperty::geometrical_type>, public InterpolationElement< ElementClassProperty::interpolation_type> { protected: using geometrical_element = GeometricalElement::geometrical_type>; using interpolation_element = InterpolationElement< ElementClassProperty::interpolation_type>; using parent_element = ElementClass::parent_element_type>; public: static inline void computeRotationMatrix(Matrix & /*R*/, const Matrix & /*X*/, const Vector & /*extra_normal*/) { AKANTU_DEBUG_TO_IMPLEMENT(); } /// compute jacobian (or integration variable change factor) for a given point - static inline void computeJMat(const Matrix & DNDS, + static inline void computeJMat(const Vector & natural_coords, const Matrix & Xs, Matrix & J) { - auto nb_nodes = Xs.cols(); - auto dim = Xs.rows(); - auto nb_dof = - interpolation_element::interpolation_property::nb_degree_of_freedom; - Matrix B(dim, nb_nodes); - for (UInt s = 0; s < dim; ++s) { - for (UInt n = 0; n < nb_nodes; ++n) { - B(s, n) = DNDS(s, n * nb_dof + s); - } - } - - J.mul(B, Xs); + Matrix dnds(Xs.rows(), Xs.cols()); + parent_element::computeDNDS(natural_coords, dnds); + J.mul(dnds, Xs); } - static inline void computeJMat(const Tensor3 & DNDSs, + static inline void computeJMat(const Matrix & natural_coords, const Matrix & Xs, Tensor3 & Js) { - using itp = typename interpolation_element::interpolation_property; - auto nb_nodes = Xs.cols(); - auto dim = Xs.rows(); - auto nb_dof = itp::nb_degree_of_freedom; - Matrix B(dim, nb_nodes); - - for (UInt i = 0; i < Xs.cols(); ++i) { - Matrix DNDS = DNDSs(i); + for (UInt i = 0 ; i < natural_coords.cols(); ++i) { + // because non-const l-value reference does not bind to r-value Matrix J = Js(i); - - B.clear(); - for (UInt s = 0; s < dim; ++s) { - for (UInt n = 0; n < nb_nodes; ++n) { - B(s, n) = DNDS(s, n * nb_dof + s); - } - } - - parent_element::computeJMat(B, Xs, J); - // computeJMat(DNDS, Xs, J); + computeJMat(Vector(natural_coords(i)), Xs, J); } } static inline void computeJacobian(const Matrix & natural_coords, const Matrix & node_coords, Vector & jacobians) { using itp = typename interpolation_element::interpolation_property; - UInt nb_points = natural_coords.cols(); - Matrix dnds(itp::natural_space_dimension, itp::nb_nodes_per_element); - Matrix J(natural_coords.rows(), itp::natural_space_dimension); - - // Extract relevant first lines - auto x = node_coords.block(0, 0, itp::natural_space_dimension, - itp::nb_nodes_per_element); - - for (UInt p = 0; p < nb_points; ++p) { - Vector ncoord_p(natural_coords(p)); - parent_element::computeDNDS(ncoord_p, dnds); - parent_element::computeJMat(dnds, x, J); - jacobians(p) = J.det(); + Tensor3 Js(itp::natural_space_dimension, itp::natural_space_dimension, + natural_coords.cols()); + computeJMat(natural_coords, node_coords, Js); + for (UInt i = 0; i < natural_coords.cols(); ++i) { + Matrix J = Js(i); + jacobians(i) = J.det(); } } static inline void computeRotation(const Matrix & node_coords, Matrix & rotation); public: static AKANTU_GET_MACRO_NOT_CONST(Kind, _ek_structural, ElementKind); static AKANTU_GET_MACRO_NOT_CONST(P1ElementType, _not_defined, ElementType); static AKANTU_GET_MACRO_NOT_CONST(FacetType, _not_defined, ElementType); static constexpr auto getFacetType(__attribute__((unused)) UInt t = 0) { return _not_defined; } static constexpr AKANTU_GET_MACRO_NOT_CONST( SpatialDimension, ElementClassProperty::spatial_dimension, UInt); static constexpr auto getFacetTypes() { return ElementClass<_not_defined>::getFacetTypes(); } }; } // namespace akantu /* -------------------------------------------------------------------------- */ #include "element_classes/element_class_hermite_inline_impl.cc" /* keep order */ #include "element_classes/element_class_bernoulli_beam_inline_impl.cc" #include "element_classes/element_class_kirchhoff_shell_inline_impl.cc" /* -------------------------------------------------------------------------- */ #endif /* __AKANTU_ELEMENT_CLASS_STRUCTURAL_HH__ */ diff --git a/src/fe_engine/element_classes/element_class_bernoulli_beam_inline_impl.cc b/src/fe_engine/element_classes/element_class_bernoulli_beam_inline_impl.cc index f7ca15021..407cb0af0 100644 --- a/src/fe_engine/element_classes/element_class_bernoulli_beam_inline_impl.cc +++ b/src/fe_engine/element_classes/element_class_bernoulli_beam_inline_impl.cc @@ -1,202 +1,222 @@ /** * @file element_class_bernoulli_beam_inline_impl.cc * * @author Fabian Barras * * @date creation: Fri Jul 15 2011 * @date last modification: Sun Oct 19 2014 * * @brief Specialization of the element_class class for the type _bernoulli_beam_2 * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * * @section DESCRIPTION * * @verbatim --x-----q1----|----q2-----x---> x -1 0 1 @endverbatim * */ /* -------------------------------------------------------------------------- */ #include "aka_static_if.hh" #include "element_class_structural.hh" //#include "aka_element_classes_info.hh" /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_ELEMENT_CLASS_BERNOULLI_BEAM_INLINE_IMPL_CC__ #define __AKANTU_ELEMENT_CLASS_BERNOULLI_BEAM_INLINE_IMPL_CC__ namespace akantu { /* -------------------------------------------------------------------------- */ AKANTU_DEFINE_STRUCTURAL_INTERPOLATION_TYPE_PROPERTY(_itp_bernoulli_beam_2, _itp_lagrange_segment_2, 3, - 2); + 2, 6); AKANTU_DEFINE_STRUCTURAL_INTERPOLATION_TYPE_PROPERTY(_itp_bernoulli_beam_3, _itp_lagrange_segment_2, 6, - 4); + 4, 6); AKANTU_DEFINE_STRUCTURAL_ELEMENT_CLASS_PROPERTY(_bernoulli_beam_2, _gt_segment_2, _itp_bernoulli_beam_2, _segment_2, _ek_structural, 2, _git_segment, 3); AKANTU_DEFINE_STRUCTURAL_ELEMENT_CLASS_PROPERTY(_bernoulli_beam_3, _gt_segment_2, _itp_bernoulli_beam_3, _segment_2, _ek_structural, 3, _git_segment, 3); /* -------------------------------------------------------------------------- */ template <> inline void InterpolationElement<_itp_bernoulli_beam_2, _itk_structural>::computeShapes( const Vector & natural_coords, const Matrix & real_coord, Matrix & N) { Vector L(2); InterpolationElement<_itp_lagrange_segment_2, _itk_lagrangian>::computeShapes( natural_coords, L); Matrix H(2, 4); InterpolationElement<_itp_hermite_2, _itk_structural>::computeShapes( natural_coords, real_coord, H); // clang-format off // u1 v1 t1 u2 v2 t2 N = {{L(0), 0 , 0 , L(1), 0 , 0 }, // u {0 , H(0, 0), H(0, 1), 0 , H(0, 2), H(0, 3)}, // v {0 , H(1, 0), H(1, 1), 0 , H(1, 2), H(1, 3)}}; // theta // clang-format on } template <> inline void InterpolationElement<_itp_bernoulli_beam_3, _itk_structural>::computeShapes( const Vector & natural_coords, const Matrix & real_coord, Matrix & N) { Vector L(2); InterpolationElement<_itp_lagrange_segment_2, _itk_lagrangian>::computeShapes( natural_coords, L); Matrix H(2, 4); InterpolationElement<_itp_hermite_2, _itk_structural>::computeShapes( natural_coords, real_coord, H); // clang-format off // u1 v1 w1 x1 y1 z1 u2 v2 w2 x2 y2 z2 N = {{L(0), 0 , 0 , 0 , 0 , 0 , L(1), 0 , 0 , 0 , 0 , 0 }, // u {0 , H(0, 0), 0 , 0 , H(0, 1), 0 , 0 , H(0, 2), 0 , 0 , H(0, 3), 0 }, // v {0 , 0 , H(0, 0), 0 , 0 , H(0, 1), 0 , 0 , H(0, 2), 0 , 0 , H(0, 3)}, // w {0 , 0 , 0 , L(0), 0 , 0 , 0 , 0 , 0 , L(1), 0 , 0 }, // thetax {0 , H(1, 0), 0 , 0 , H(1, 1), 0 , 0 , H(1, 2), 0 , 0 , H(1, 3), 0 }, // thetay {0 , 0 , H(1, 0), 0 , 0 , H(1, 1), 0 , 0 , H(1, 2), 0 , 0 , H(1, 3)}}; // thetaz // clang-format on } /* -------------------------------------------------------------------------- */ template <> inline void InterpolationElement<_itp_bernoulli_beam_2, _itk_structural>::computeDNDS( const Vector & natural_coords, const Matrix & real_coord, - Matrix & B) { + Matrix & dnds) { Matrix L(1, 2); InterpolationElement<_itp_lagrange_segment_2, _itk_lagrangian>::computeDNDS( natural_coords, L); Matrix H(1, 4); InterpolationElement<_itp_hermite_2, _itk_structural>::computeDNDS( natural_coords, real_coord, H); + // Storing the derivatives in dnds + dnds.block(L, 0, 0); + dnds.block(H, 0, 2); +} + +/* -------------------------------------------------------------------------- */ +template <> +inline void +InterpolationElement<_itp_bernoulli_beam_2, _itk_structural>::arrangeInVoigt( + const Matrix & dnds, Matrix & B) { + auto L = dnds.block(0, 0, 1, 2); // Lagrange shape derivatives + auto H = dnds.block(0, 2, 1, 4); // Hermite shape derivatives // clang-format off - // u1 v1 t1 u2 v2 t2 - B = {{L(0, 0), 0 , 0 , L(0, 1), 0 , 0 }, // epsilon - {0 , H(0, 0), H(0, 1), 0 , H(0, 2), H(0, 3)}}; // chi (curv.) + // u1 v1 t1 u2 v2 t2 + B = {{L(0, 0), 0, 0, L(0, 1), 0, 0 }, + {0, H(0, 0), H(0, 1), 0, H(0, 2), H(0, 3)}}; // clang-format on } +/* -------------------------------------------------------------------------- */ template <> inline void InterpolationElement<_itp_bernoulli_beam_3, _itk_structural>::computeDNDS( - const Vector & natural_coords, const Matrix & real_coord, Matrix & B) { - Matrix L(1, 2); - InterpolationElement<_itp_lagrange_segment_2, _itk_lagrangian>::computeDNDS( - natural_coords, L); - Matrix H(1, 4); - InterpolationElement<_itp_hermite_2, _itk_structural>::computeDNDS( - natural_coords, real_coord, H); + const Vector & natural_coords, const Matrix & real_coord, + Matrix & dnds) { + InterpolationElement<_itp_bernoulli_beam_2, _itk_structural>::computeDNDS( + natural_coords, real_coord, dnds); +} + +/* -------------------------------------------------------------------------- */ +template <> +inline void +InterpolationElement<_itp_bernoulli_beam_3, _itk_structural>::arrangeInVoigt( + const Matrix & dnds, Matrix & B) { + auto L = dnds.block(0, 0, 1, 2); // Lagrange shape derivatives + auto H = dnds.block(0, 2, 1, 4); // Hermite shape derivatives + // clang-format off // u1 v1 w1 x1 y1 z1 u2 v2 w2 x2 y2 z2 B = {{L(0, 0), 0 , 0 , 0 , 0 , 0 , L(0, 1), 0 , 0 , 0 , 0 , 0 }, // eps {0 , H(0, 0), 0 , 0 , 0 , H(0, 1), 0 , H(0, 2), 0 , 0 , 0 , H(0, 3)}, // chi strong axis {0 , 0 ,-H(0, 0), 0 , H(0, 1), 0 , 0 , 0 ,-H(0, 2), 0 , H(0, 3), 0 }, // chi weak axis {0 , 0 , 0 , L(0, 0), 0 , 0 , 0 , 0 , 0 , L(0, 1), 0 , 0 }}; // chi torsion // clang-format on } /* -------------------------------------------------------------------------- */ template <> inline void ElementClass<_bernoulli_beam_2>::computeRotationMatrix( Matrix & R, const Matrix & X, const Vector &) { Vector x2 = X(1); // X2 Vector x1 = X(0); // X1 auto cs = (x2 - x1); cs.normalize(); auto c = cs(0); auto s = cs(1); // clang-format off /// Definition of the rotation matrix R = {{ c, s, 0.}, {-s, c, 0.}, { 0., 0., 1.}}; // clang-format on } /* -------------------------------------------------------------------------- */ template <> inline void ElementClass<_bernoulli_beam_3>::computeRotationMatrix( Matrix & R, const Matrix & X, const Vector & n) { Vector x2 = X(1); // X2 Vector x1 = X(0); // X1 auto dim = X.rows(); auto x = (x2 - x1); x.normalize(); auto x_n = x.crossProduct(n); Matrix Pe = {{1., 0., 0.}, {0., -1., 0.}, {0., 0., 1.}}; Matrix Pg(dim, dim); Pg(0) = x; Pg(1) = x_n; Pg(2) = n; Pe *= Pg.inverse(); R.clear(); /// Definition of the rotation matrix for (UInt i = 0; i < dim; ++i) for (UInt j = 0; j < dim; ++j) R(i + dim, j + dim) = R(i, j) = Pe(i, j); } } // namespace akantu #endif /* __AKANTU_ELEMENT_CLASS_BERNOULLI_BEAM_INLINE_IMPL_CC__ */ diff --git a/src/fe_engine/element_classes/element_class_hermite_inline_impl.cc b/src/fe_engine/element_classes/element_class_hermite_inline_impl.cc index ba268905d..8d79e01c5 100644 --- a/src/fe_engine/element_classes/element_class_hermite_inline_impl.cc +++ b/src/fe_engine/element_classes/element_class_hermite_inline_impl.cc @@ -1,180 +1,180 @@ /** * @file element_class_hermite_inline_impl.cc * * @author Fabian Barras * @author Lucas Frérot * * @date creation: Fri Jul 15 2011 * @date last modification: Sun Oct 19 2014 * * @brief Specialization of the element_class class for the type _hermite * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * * @section DESCRIPTION * * @verbatim --x-----q1----|----q2-----x---> x -1 0 1 @endverbatim * * @subsection shapes Shape functions * @f[ * \begin{array}{ll} * M_1(\xi) &= 1/4(\xi^{3}/-3\xi+2)\\ * M_2(\xi) &= -1/4(\xi^{3}-3\xi-2) * \end{array} * * \begin{array}{ll} * L_1(\xi) &= 1/4(\xi^{3}-\xi^{2}-\xi+1)\\ * L_2(\xi) &= 1/4(\xi^{3}+\xi^{2}-\xi-1) * \end{array} * * \begin{array}{ll} * M'_1(\xi) &= 3/4(\xi^{2}-1)\\ * M'_2(\xi) &= -3/4(\xi^{2}-1) * \end{array} * * \begin{array}{ll} * L'_1(\xi) &= 1/4(3\xi^{2}-2\xi-1)\\ * L'_2(\xi) &= 1/4(3\xi^{2}+2\xi-1) * \end{array} *@f] * * @subsection dnds Shape derivatives * *@f[ * \begin{array}{ll} * N'_1(\xi) &= -1/2\\ * N'_2(\xi) &= 1/2 * \end{array}] * * \begin{array}{ll} * -M''_1(\xi) &= -3\xi/2\\ * -M''_2(\xi) &= 3\xi/2\\ * \end{array} * * \begin{array}{ll} * -L''_1(\xi) &= -1/2a(3\xi/a-1)\\ * -L''_2(\xi) &= -1/2a(3\xi/a+1) * \end{array} *@f] * */ /* -------------------------------------------------------------------------- */ #include "aka_static_if.hh" #include "element_class_structural.hh" /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_ELEMENT_CLASS_HERMITE_INLINE_IMPL_CC__ #define __AKANTU_ELEMENT_CLASS_HERMITE_INLINE_IMPL_CC__ namespace akantu { /* -------------------------------------------------------------------------- */ AKANTU_DEFINE_STRUCTURAL_INTERPOLATION_TYPE_PROPERTY(_itp_hermite_2, _itp_lagrange_segment_2, 2, - 1); + 1, 4); /* -------------------------------------------------------------------------- */ namespace { namespace details { inline Real computeLength(const Matrix & real_coord) { Vector x1 = real_coord(0); Vector x2 = real_coord(1); return x1.distance(x2); } inline void computeShapes(const Vector & natural_coords, Real a, Matrix & N) { /// natural coordinate Real xi = natural_coords(0); // Cubic Hermite splines interpolating displacement auto M1 = 1. / 4. * Math::pow<2>(xi - 1) * (xi + 2); auto M2 = 1. / 4. * Math::pow<2>(xi + 1) * (2 - xi); auto L1 = a / 4. * Math::pow<2>(xi - 1) * (xi + 1); auto L2 = a / 4. * Math::pow<2>(xi + 1) * (xi - 1); #if 1 // Version where we also interpolate the rotations // Derivatives (with respect to x) of previous functions interpolating // rotations auto M1_ = 3. / (4. * a) * (xi * xi - 1); auto M2_ = 3. / (4. * a) * (1 - xi * xi); auto L1_ = 1 / 4. * (3 * xi * xi - 2 * xi - 1); auto L2_ = 1 / 4. * (3 * xi * xi + 2 * xi - 1); // clang-format off // v1 t1 v2 t2 N = {{M1 , L1 , M2 , L2}, // displacement interpolation {M1_, L1_, M2_, L2_}}; // rotation interpolation // clang-format on #else // Version where we only interpolate displacements // clang-format off // v1 t1 v2 t2 N = {{M1, L1, M2, L2}}; // clang-format on #endif } /* ---------------------------------------------------------------------- */ inline void computeDNDS(const Vector & natural_coords, Real a, Matrix & B) { // natural coordinate Real xi = natural_coords(0); // Derivatives with respect to xi for rotations auto M1__ = 3. / 2. * xi; auto M2__ = 3. / 2. * (-xi); auto L1__ = a / 2. * (3 * xi - 1); auto L2__ = a / 2. * (3 * xi + 1); // v1 t1 v2 t2 B = {{M1__, L1__, M2__, L2__}}; // computing curvature : {chi} = [B]{d} B /= a; // to account for first order deriv w/r to x } } // namespace details } // namespace /* -------------------------------------------------------------------------- */ template <> inline void InterpolationElement<_itp_hermite_2, _itk_structural>::computeShapes( const Vector & natural_coords, const Matrix & real_coord, Matrix & N) { auto L = details::computeLength(real_coord); details::computeShapes(natural_coords, L / 2, N); } /* -------------------------------------------------------------------------- */ template <> inline void InterpolationElement<_itp_hermite_2, _itk_structural>::computeDNDS( const Vector & natural_coords, const Matrix & real_coord, Matrix & B) { auto L = details::computeLength(real_coord); details::computeDNDS(natural_coords, L / 2, B); } } // namespace akantu #endif /* __AKANTU_ELEMENT_CLASS_HERMITE_INLINE_IMPL_CC__ */ diff --git a/src/fe_engine/element_classes/element_class_kirchhoff_shell_inline_impl.cc b/src/fe_engine/element_classes/element_class_kirchhoff_shell_inline_impl.cc index b88d904c8..08fcce9f9 100644 --- a/src/fe_engine/element_classes/element_class_kirchhoff_shell_inline_impl.cc +++ b/src/fe_engine/element_classes/element_class_kirchhoff_shell_inline_impl.cc @@ -1,316 +1,212 @@ /** * @file element_class_kirchhoff_shell_inline_impl.cc * * @author Damien Spielmann * * @date creation: Fri Jul 04 2014 * @date last modification: Sun Oct 19 2014 * * @brief Element class Kirchhoff Shell * * @section LICENSE * * Copyright (©) 2014, 2015 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "element_class_structural.hh" /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_ELEMENT_CLASS_KIRCHHOFF_SHELL_INLINE_IMPL_CC__ #define __AKANTU_ELEMENT_CLASS_KIRCHHOFF_SHELL_INLINE_IMPL_CC__ namespace akantu { /* -------------------------------------------------------------------------- */ -AKANTU_DEFINE_STRUCTURAL_INTERPOLATION_TYPE_PROPERTY(_itp_discrete_kirchhoff_triangle_18, - _itp_lagrange_triangle_3, - 6, 6); -AKANTU_DEFINE_STRUCTURAL_ELEMENT_CLASS_PROPERTY(_discrete_kirchhoff_triangle_18, - _gt_triangle_3, - _itp_discrete_kirchhoff_triangle_18, - _triangle_3, _ek_structural, 3, - _git_triangle, 2); +AKANTU_DEFINE_STRUCTURAL_INTERPOLATION_TYPE_PROPERTY( + _itp_discrete_kirchhoff_triangle_18, _itp_lagrange_triangle_3, 6, 6, 21); +AKANTU_DEFINE_STRUCTURAL_ELEMENT_CLASS_PROPERTY( + _discrete_kirchhoff_triangle_18, _gt_triangle_3, + _itp_discrete_kirchhoff_triangle_18, _triangle_3, _ek_structural, 3, + _git_triangle, 2); + +/* -------------------------------------------------------------------------- */ +namespace detail { + inline void computeBasisChangeMatrix(Matrix & P, + const Matrix & X) { + Vector X1 = X(0); + Vector X2 = X(1); + Vector X3 = X(2); + + Vector a1 = X2 - X1; + Vector a2 = X3 - X1; + + a1.normalize(); + Vector e3 = a1.crossProduct(a2); + e3.normalize(); + Vector e2 = e3.crossProduct(a1); + + P(0) = a1; + P(1) = e2; + P(2) = e3; + P = P.transpose(); + } +} /* -------------------------------------------------------------------------- */ template <> -inline void ElementClass<_discrete_kirchhoff_triangle_18>::computeRotationMatrix( +inline void +ElementClass<_discrete_kirchhoff_triangle_18>::computeRotationMatrix( Matrix & R, const Matrix & X, const Vector &) { auto dim = X.rows(); - Vector X1 = X(0); - Vector X2 = X(1); - Vector X3 = X(2); - - Vector a1 = X2 - X1; - Vector a2 = X3 - X1; - - a1.normalize(); - Vector e3 = a1.crossProduct(a2); - e3.normalize(); - Vector e2 = e3.crossProduct(a1); - Matrix P(dim, dim); - P(0) = a1; - P(1) = e2; - P(2) = e3; + detail::computeBasisChangeMatrix(P, X); R.clear(); for (UInt i = 0; i < dim; ++i) for (UInt j = 0; j < dim; ++j) - R(i + dim, j + dim) = R(i, j) = P(j, i); + R(i + dim, j + dim) = R(i, j) = P(i, j); } /* -------------------------------------------------------------------------- */ template <> inline void InterpolationElement<_itp_discrete_kirchhoff_triangle_18>::computeShapes( const Vector & /*natural_coords*/, - const Matrix & /*real_coord*/, Matrix & /*N*/) { -// // projected_coord (x1 x2 x3) (y1 y2 y3) - -#if 0 - // natural coordinate - Real xi = natural_coords(0); - Real eta = natural_coords(1); - - Real x21 = projected_coord(0, 0) - projected_coord(0, 1); // x1-x2 - Real x32 = projected_coord(0, 1) - projected_coord(0, 2); - Real x13 = projected_coord(0, 2) - projected_coord(0, 0); - - Real y21 = projected_coord(1, 0) - projected_coord(1, 1); // y1-y2 - Real y32 = projected_coord(1, 1) - projected_coord(1, 2); - Real y13 = projected_coord(1, 2) - projected_coord(1, 0); - - /* Real x21=projected_coord(0,1)-projected_coord(0,0); - Real x32=projected_coord(0,2)-projected_coord(0,1); - Real x13=projected_coord(0,0)-projected_coord(0,2); - - Real y21=projected_coord(1,1)-projected_coord(1,0); - Real y32=projected_coord(1,2)-projected_coord(1,1); - Real y13=projected_coord(1,0)-projected_coord(1,2);*/ - - // natural triangle side length - Real L4 = std::sqrt(x21 * x21 + y21 * y21); - Real L5 = std::sqrt(x32 * x32 + y32 * y32); - Real L6 = std::sqrt(x13 * x13 + y13 * y13); - - // sinus and cosinus - Real C4 = x21 / L4; // 1. - Real C5 = x32 / L5; //-1./std::sqrt(2); - Real C6 = x13 / L6; // 0. - Real S4 = y21 / L4; // 0.; - Real S5 = y32 / L5; // 1./std::sqrt(2); - Real S6 = y13 / L6; //-1.; - - Real N1 = 1. - xi - eta; - Real N2 = xi; - Real N3 = eta; - - Real P4 = 4. * xi * (1. - xi - eta); - Real P5 = 4. * xi * eta; - Real P6 = 4. * eta * (1. - xi - eta); - - Vector N0{N1, N2, N3}; - Vector Nw2{-(1. / 8.) * P4 * L4 * C4 + (1. / 8.) * P6 * L6 * C6, - -(1. / 8.) * P5 * L5 * C5 + (1. / 8.) * P4 * L4 * C4, - -(1. / 8.) * P6 * L6 * C6 + (1. / 8.) * P5 * L5 * C5}; - Vector Nw3{-(1. / 8.) * P4 * L4 * S4 + (1. / 8.) * P6 * L6 * S6, - -(1. / 8.) * P5 * L5 * S5 + (1. / 8.) * P4 * L4 * S4, - -(1. / 8.) * P6 * L6 * S6 + (1. / 8.) * P5 * L5 * S5}; - Vector Nx1{3. / (2. * L4) * P4 * C4 - 3. / (2. * L6) * P6 * C6, - 3. / (2. * L5) * P5 * C5 - 3. / (2. * L4) * P4 * C4, - 3. / (2. * L6) * P6 * C6 - 3. / (2. * L5) * P5 * C5}; - Vector Nx2{N1 - (3. / 4.) * P4 * C4 * C4 - (3. / 4.) * P6 * C6 * C6, - N2 - (3. / 4.) * P5 * C5 * C5 - (3. / 4.) * P4 * C4 * C4, - N3 - (3. / 4.) * P6 * C6 * C6 - (3. / 4.) * P5 * C5 * C5}; - Vector Nx3{-(3. / 4.) * P4 * C4 * S4 - (3. / 4.) * P6 * C6 * S6, - -(3. / 4.) * P5 * C5 * S5 - (3. / 4.) * P4 * C4 * S4, - -(3. / 4.) * P6 * C6 * S6 - (3. / 4.) * P5 * C5 * S5}; - Vector Ny1{3. / (2. * L4) * P4 * S4 - 3. / (2. * L6) * P6 * S6, - 3. / (2. * L5) * P5 * S5 - 3. / (2. * L4) * P4 * S4, - 3. / (2. * L6) * P6 * S6 - 3. / (2. * L5) * P5 * S5}; - Vector Ny2{-(3. / 4.) * P4 * C4 * S4 - (3. / 4.) * P6 * C6 * S6, - -(3. / 4.) * P5 * C5 * S5 - (3. / 4.) * P4 * C4 * S4, - -(3. / 4.) * P6 * C6 * S6 - (3. / 4.) * P5 * C5 * S5}; - Vector Ny3{N1 - (3. / 4.) * P4 * S4 * S4 - (3. / 4.) * P6 * S6 * S6, - N2 - (3. / 4.) * P5 * S5 * S5 - (3. / 4.) * P4 * S4 * S4, - N3 - (3. / 4.) * P6 * S6 * S6 - (3. / 4.) * P5 * S5 * S5}; - - // clang-format off - N = Matrix { - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 - {N0(0), 0., 0., 0., 0., N0(1), 0., 0., 0., 0., N0(2), 0., 0., 0., 0.}, // 0 - { 0., N0(0), 0., 0., 0., 0., N0(1), 0., 0., 0., 0., N0(2), 0., 0., 0.}, // 1 - { 0., 0., N0(0), Nw2(0), Nw3(0), 0., 0., N0(1), Nw2(1), Nw3(1), 0., 0., N0(2), Nw2(2), Nw3(2)}, // 2 - { 0., 0., Nx1(0), Nx2(0), Nx3(0), 0., 0., Nx1(1), Nx2(1), Nx3(1), 0., 0., Nx1(2), Nx2(2), Nx3(2)}, // 3 - { 0., 0., Ny1(0), Ny2(0), Ny3(0), 0., 0., Ny1(1), Ny2(1), Ny3(1), 0., 0., Ny1(2), Ny2(2), Ny3(2)}, // 4 - { 0, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.}}; // 5 ??? - // clang-format on - -#endif -} + const Matrix & /*real_coord*/, Matrix & /*N*/) {} /* -------------------------------------------------------------------------- */ template <> inline void InterpolationElement<_itp_discrete_kirchhoff_triangle_18>::computeDNDS( - const Vector & /*natural_coords*/, - const Matrix & /*real_coord*/, Matrix & /*B*/) { + const Vector & natural_coords, const Matrix & real_coordinates, + Matrix & B) { + + auto dim = real_coordinates.cols(); + Matrix P(dim, dim); + detail::computeBasisChangeMatrix(P, real_coordinates); + auto X = P * real_coordinates; + Vector X1 = X(0); + Vector X2 = X(1); + Vector X3 = X(2); -#if 0 - // natural coordinate + std::array, 3> A = {X2 - X1, X3 - X2, X1 - X3}; + std::array L, C, S; + + // Setting all last coordinates to 0 + std::for_each(A.begin(), A.end(), [](auto & a) { a(2) = 0; }); + // Computing lengths + std::transform(A.begin(), A.end(), L.begin(), + [](auto & a) { return a.template norm(); }); + // Computing cosines + std::transform(A.begin(), A.end(), L.begin(), C.begin(), + [](auto & a, auto & l) { return a(0) / l; }); + // Computing sines + std::transform(A.begin(), A.end(), L.begin(), S.begin(), + [](auto & a, auto & l) { return a(1) / l; }); + + // Natural coordinates Real xi = natural_coords(0); Real eta = natural_coords(1); - // projected_coord (x1 x2 x3) (y1 y2 y3) - - // donne juste pour pour le patch test 4_5_5 mais donne quelque changement - // de signe dans la matrice de rotation - - Real x21 = projected_coord(0, 0) - projected_coord(0, 1); // x1-x2 - Real x32 = projected_coord(0, 1) - projected_coord(0, 2); - Real x13 = projected_coord(0, 2) - projected_coord(0, 0); - - Real y21 = projected_coord(1, 0) - projected_coord(1, 1); // y1-y2 - Real y32 = projected_coord(1, 1) - projected_coord(1, 2); - Real y13 = projected_coord(1, 2) - projected_coord(1, 0); - - // donne juste pour la matrice de rigidité... mais pas pour le patch test - // 4_5_5 - - /* Real x21=projected_coord(0,1)-projected_coord(0,0); - Real x32=projected_coord(0,2)-projected_coord(0,1); - Real x13=projected_coord(0,0)-projected_coord(0,2); - - Real y21=projected_coord(1,1)-projected_coord(1,0); - Real y32=projected_coord(1,2)-projected_coord(1,1); - Real y13=projected_coord(1,0)-projected_coord(1,2);*/ - - // natural triangle side length - Real L4 = std::sqrt(x21 * x21 + y21 * y21); - Real L5 = std::sqrt(x32 * x32 + y32 * y32); - Real L6 = std::sqrt(x13 * x13 + y13 * y13); - - // sinus and cosinus - Real C4 = x21 / L4; - Real C5 = x32 / L5; - Real C6 = x13 / L6; - Real S4 = y21 / L4; - Real S5 = y32 / L5; - Real S6 = y13 / L6; - - Real dN1xi = -1; - Real dN2xi = 1; - Real dN3xi = 0; - - Real dN1eta = -1; - Real dN2eta = 0; - Real dN3eta = 1; - - Real dP4xi = 4 - 8. * xi - 4 * eta; - Real dP5xi = 4 * eta; - Real dP6xi = -4 * eta; - - Real dP4eta = -4 * xi; - Real dP5eta = 4 * xi; - Real dP6eta = 4 - 4 * xi - 8. * eta; - - // N'xi - auto Np00 = dN1xi; - auto Np01 = dN2xi; - auto Np02 = dN3xi; - // N'eta - auto Np10 = dN1eta; - auto Np11 = dN2eta; - auto Np12 = dN3eta; - - // Nxi1'xi - auto Nx1p00 = 3. / (2 * L4) * dP4xi * C4 - 3. / (2. * L6) * dP6xi * C6; - auto Nx1p01 = 3. / (2 * L5) * dP5xi * C5 - 3. / (2. * L4) * dP4xi * C4; - auto Nx1p02 = 3. / (2 * L6) * dP6xi * C6 - 3. / (2. * L5) * dP5xi * C5; - // Nxi1'eta - auto Nx1p10 = 3. / (2 * L4) * dP4eta * C4 - 3. / (2. * L6) * dP6eta * C6; - auto Nx1p11 = 3. / (2 * L5) * dP5eta * C5 - 3. / (2. * L4) * dP4eta * C4; - auto Nx1p12 = 3. / (2 * L6) * dP6eta * C6 - 3. / (2. * L5) * dP5eta * C5; - - // Nxi2'xi - auto Nx2p00 = -1 - (3. / 4.) * dP4xi * C4 * C4 - (3. / 4.) * dP6xi * C6 * C6; - auto Nx2p01 = 1 - (3. / 4.) * dP5xi * C5 * C5 - (3. / 4.) * dP4xi * C4 * C4; - auto Nx2p02 = -(3. / 4.) * dP6xi * C6 * C6 - (3. / 4.) * dP5xi * C5 * C5; - // Nxi2'eta - auto Nx2p10 = - -1 - (3. / 4.) * dP4eta * C4 * C4 - (3. / 4.) * dP6eta * C6 * C6; - auto Nx2p11 = -(3. / 4.) * dP5eta * C5 * C5 - (3. / 4.) * dP4eta * C4 * C4; - auto Nx2p12 = 1 - (3. / 4.) * dP6eta * C6 * C6 - (3. / 4.) * dP5eta * C5 * C5; - - // Nxi3'xi - auto Nx3p00 = -(3. / 4.) * dP4xi * C4 * S4 - (3. / 4.) * dP6xi * C6 * S6; - auto Nx3p01 = -(3. / 4.) * dP5xi * C5 * S5 - (3. / 4.) * dP4xi * C4 * S4; - auto Nx3p02 = -(3. / 4.) * dP6xi * C6 * S6 - (3. / 4.) * dP5xi * C5 * S5; - // Nxi3'eta - auto Nx3p10 = -(3. / 4.) * dP4eta * C4 * S4 - (3. / 4.) * dP6eta * C6 * S6; - auto Nx3p11 = -(3. / 4.) * dP5eta * C5 * S5 - (3. / 4.) * dP4eta * C4 * S4; - auto Nx3p12 = -(3. / 4.) * dP6eta * C6 * S6 - (3. / 4.) * dP5eta * C5 * S5; - - // Nyi1'xi - auto Ny1p00 = 3 / (2 * L4) * dP4xi * S4 - 3 / (2 * L6) * dP6xi * S6; - auto Ny1p01 = 3 / (2 * L5) * dP5xi * S5 - 3 / (2 * L4) * dP4xi * S4; - auto Ny1p02 = 3 / (2 * L6) * dP6xi * S6 - 3 / (2 * L5) * dP5xi * S5; - // Nyi1'eta - auto Ny1p10 = 3 / (2 * L4) * dP4eta * S4 - 3 / (2 * L6) * dP6eta * S6; - auto Ny1p11 = 3 / (2 * L5) * dP5eta * S5 - 3 / (2 * L4) * dP4eta * S4; - auto Ny1p12 = 3 / (2 * L6) * dP6eta * S6 - 3 / (2 * L5) * dP5eta * S5; - - // Nyi2'xi - auto Ny2p00 = -(3. / 4.) * dP4xi * C4 * S4 - (3. / 4.) * dP6xi * C6 * S6; - auto Ny2p01 = -(3. / 4.) * dP5xi * C5 * S5 - (3. / 4.) * dP4xi * C4 * S4; - auto Ny2p02 = -(3. / 4.) * dP6xi * C6 * S6 - (3. / 4.) * dP5xi * C5 * S5; - // Nyi2'eta - auto Ny2p10 = -(3. / 4.) * dP4eta * C4 * S4 - (3. / 4.) * dP6eta * C6 * S6; - auto Ny2p11 = -(3. / 4.) * dP5eta * C5 * S5 - (3. / 4.) * dP4eta * C4 * S4; - auto Ny2p12 = -(3. / 4.) * dP6eta * C6 * S6 - (3. / 4.) * dP5eta * C5 * S5; - - // Nyi3'xi - auto Ny3p00 = - dN1xi - (3. / 4.) * dP4xi * S4 * S4 - (3. / 4.) * dP6xi * S6 * S6; - auto Ny3p01 = - dN2xi - (3. / 4.) * dP5xi * S5 * S5 - (3. / 4.) * dP4xi * S4 * S4; - auto Ny3p02 = - dN3xi - (3. / 4.) * dP6xi * S6 * S6 - (3. / 4.) * dP5xi * S5 * S5; - // Nyi3'eta - auto Ny3p10 = - dN1eta - (3. / 4.) * dP4eta * S4 * S4 - (3. / 4.) * dP6eta * S6 * S6; - auto Ny3p11 = - dN2eta - (3. / 4.) * dP5eta * S5 * S5 - (3. / 4.) * dP4eta * S4 * S4; - auto Ny3p12 = - dN3eta - (3. / 4.) * dP6eta * S6 * S6 - (3. / 4.) * dP5eta * S5 * S5; - - // clang-format off - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 - B = {{Np00, 0., 0., 0., 0., Np01, 0., 0., 0., 0., Np02, 0., 0., 0., 0.}, // 0 - { 0., Np10, 0., 0., 0., 0., Np11, 0., 0., 0., 0., Np12, 0., 0., 0.}, // 1 - {Np10, Np00, 0., 0., 0., Np11, Np01, 0., 0., 0., Np12, Np02, 0., 0., 0.}, // 2 - { 0., 0., Nx1p00, Nx2p00, Nx3p00, 0., 0., Nx1p01, Nx2p01, Nx3p01, 0., 0., Nx1p02, Nx2p02, Nx3p02}, // 3 - { 0., 0., Ny1p10, Ny2p10, Ny3p10, 0., 0., Ny1p11, Ny2p11, Ny3p11, 0., 0., Ny1p12, Ny2p12, Ny3p12}, // 4 - { 0., 0., Nx1p10 + Ny1p00, Nx2p10 + Ny2p00, Nx3p10 + Ny3p00, 0., 0., Nx1p11 + Ny1p01, Nx2p11 + Ny2p01, Nx3p11 + Ny3p01, 0., 0., Nx1p12 + Ny1p02, Nx2p12 + Ny2p02, Nx3p12 + Ny3p02}}; // 5 + // Derivative of quadratic interpolation functions + Matrix dP = {{4 * (1 - 2 * xi - eta), 4 * eta, -4 * eta}, + {-4 * xi, 4 * xi, 4 * (1 - xi - 2 * eta)}}; + + Matrix dNx1 = { + {3. / 2 * (dP(0, 0) * C[0] / L[0] - dP(0, 2) * C[2] / L[2]), + 3. / 2 * (dP(0, 1) * C[1] / L[1] - dP(0, 0) * C[0] / L[0]), + 3. / 2 * (dP(0, 2) * C[2] / L[2] - dP(0, 1) * C[1] / L[1])}, + {3. / 2 * (dP(1, 0) * C[0] / L[0] - dP(1, 2) * C[2] / L[2]), + 3. / 2 * (dP(1, 1) * C[1] / L[1] - dP(1, 0) * C[0] / L[0]), + 3. / 2 * (dP(1, 2) * C[2] / L[2] - dP(1, 1) * C[1] / L[1])}}; + Matrix dNx2 = { + // clang-format off + {-1 - 3. / 4 * (dP(0, 0) * C[0] * C[0] + dP(0, 2) * C[2] * C[2]), + 1 - 3. / 4 * (dP(0, 1) * C[1] * C[1] + dP(0, 0) * C[0] * C[0]), + - 3. / 4 * (dP(0, 2) * C[2] * C[2] + dP(0, 1) * C[1] * C[1])}, + {-1 - 3. / 4 * (dP(1, 0) * C[0] * C[0] + dP(1, 2) * C[2] * C[2]), + - 3. / 4 * (dP(1, 1) * C[1] * C[1] + dP(1, 0) * C[0] * C[0]), + 1 - 3. / 4 * (dP(1, 2) * C[2] * C[2] + dP(1, 1) * C[1] * C[1])}}; + // clang-format on + Matrix dNx3 = { + {-3. / 4 * (dP(0, 0) * C[0] * S[0] + dP(0, 2) * C[2] * S[2]), + -3. / 4 * (dP(0, 1) * C[1] * S[1] + dP(0, 0) * C[0] * S[0]), + -3. / 4 * (dP(0, 2) * C[2] * S[2] + dP(0, 1) * C[1] * S[1])}, + {-3. / 4 * (dP(1, 0) * C[0] * S[0] + dP(1, 2) * C[2] * S[2]), + -3. / 4 * (dP(1, 1) * C[1] * S[1] + dP(1, 0) * C[0] * S[0]), + -3. / 4 * (dP(1, 2) * C[2] * S[2] + dP(1, 1) * C[1] * S[1])}}; + Matrix dNy1 = { + {3. / 2 * (dP(0, 0) * S[0] / L[0] - dP(0, 2) * S[2] / L[2]), + 3. / 2 * (dP(0, 1) * S[1] / L[1] - dP(0, 0) * S[0] / L[0]), + 3. / 2 * (dP(0, 2) * S[2] / L[2] - dP(0, 1) * S[1] / L[1])}, + {3. / 2 * (dP(1, 0) * S[0] / L[0] - dP(1, 2) * S[2] / L[2]), + 3. / 2 * (dP(1, 1) * S[1] / L[1] - dP(1, 0) * S[0] / L[0]), + 3. / 2 * (dP(1, 2) * S[2] / L[2] - dP(1, 1) * S[1] / L[1])}}; + Matrix dNy2 = dNx3; + Matrix dNy3 = { + // clang-format off + {-1 - 3. / 4 * (dP(0, 0) * S[0] * S[0] + dP(0, 2) * S[2] * S[2]), + 1 - 3. / 4 * (dP(0, 1) * S[1] * S[1] + dP(0, 0) * S[0] * S[0]), + - 3. / 4 * (dP(0, 2) * S[2] * S[2] + dP(0, 1) * S[1] * S[1])}, + {-1 - 3. / 4 * (dP(1, 0) * S[0] * S[0] + dP(1, 2) * S[2] * S[2]), + - 3. / 4 * (dP(1, 1) * S[1] * S[1] + dP(1, 0) * S[0] * S[0]), + 1 - 3. / 4 * (dP(1, 2) * S[2] * S[2] + dP(1, 1) * S[1] * S[1])}}; // clang-format on -#endif + + // Derivative of linear (membrane mode) functions + Matrix dNm(2, 3); + InterpolationElement<_itp_lagrange_triangle_3, _itk_lagrangian>::computeDNDS( + natural_coords, dNm); + + UInt i = 0; + for (const Matrix & mat : {dNm, dNx1, dNx2, dNx3, dNy1, dNy2, dNy3}) { + B.block(mat, 0, i); + i += mat.cols(); + } +} +/* -------------------------------------------------------------------------- */ +template <> +inline void +InterpolationElement<_itp_discrete_kirchhoff_triangle_18, + _itk_structural>::arrangeInVoigt(const Matrix & dnds, + Matrix & B) { + Matrix dNm(2, 3), dNx1(2, 3), dNx2(2, 3), dNx3(2, 3), dNy1(2, 3), + dNy2(2, 3), dNy3(2, 3); + UInt i = 0; + for (Matrix * mat : {&dNm, &dNx1, &dNx2, &dNx3, &dNy1, &dNy2, &dNy3}) { + *mat = dnds.block(0, i, 2, 3); + i += mat->cols(); + } + + for (UInt i = 0; i < 3; ++i) { + // clang-format off + Matrix Bm = {{dNm(0, i), 0, 0, 0, 0, 0}, + {0, dNm(1, i), 0, 0, 0, 0}, + {dNm(1, i), dNm(0, i), 0, 0, 0, 0}}; + Matrix Bf = {{0, 0, dNx1(0, i), -dNx3(0, i), dNx2(0, i), 0}, + {0, 0, dNy1(1, i), -dNy3(1, i), dNy2(1, i), 0}, + {0, 0, dNx1(1, i) + dNy1(0, i), -dNx3(1, i) - dNy3(0, i), dNx2(1, i) + dNy2(0, i), 0}}; + // clang-format on + B.block(Bm, 0, i * 6); + B.block(Bf, 3, i * 6); + } } } // namespace akantu #endif /* __AKANTU_ELEMENT_CLASS_KIRCHHOFF_SHELL_INLINE_IMPL_CC__ */ diff --git a/src/fe_engine/fe_engine_template_cohesive.cc b/src/fe_engine/fe_engine_template_cohesive.cc index b9475e0e1..d247ac9d5 100644 --- a/src/fe_engine/fe_engine_template_cohesive.cc +++ b/src/fe_engine/fe_engine_template_cohesive.cc @@ -1,177 +1,133 @@ /** * @file fe_engine_template_cohesive.cc * * @author Nicolas Richart * @author Marco Vocialta * * @date creation: Wed Oct 31 2012 * @date last modification: Thu Oct 15 2015 * * @brief Specialization of the FEEngineTemplate for cohesive element * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "fe_engine_template.hh" #include "shape_cohesive.hh" #include "integrator_gauss.hh" /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ /* compatibility functions */ /* -------------------------------------------------------------------------- */ template <> Real FEEngineTemplate:: integrate(const Array & f, const ElementType & type, const GhostType & ghost_type, const Array & filter_elements) const { AKANTU_DEBUG_IN(); #ifndef AKANTU_NDEBUG UInt nb_element = mesh.getNbElement(type, ghost_type); if (filter_elements != empty_filter) nb_element = filter_elements.size(); UInt nb_quadrature_points = getNbIntegrationPoints(type); AKANTU_DEBUG_ASSERT(f.size() == nb_element * nb_quadrature_points, "The vector f(" << f.getID() << ") has not the good size."); AKANTU_DEBUG_ASSERT(f.getNbComponent() == 1, "The vector f(" << f.getID() << ") has not the good number of component."); #endif Real integral = 0.; #define INTEGRATE(type) \ integral = integrator.integrate(f, ghost_type, filter_elements); AKANTU_BOOST_COHESIVE_ELEMENT_SWITCH(INTEGRATE); #undef INTEGRATE AKANTU_DEBUG_OUT(); return integral; } /* -------------------------------------------------------------------------- */ template <> void FEEngineTemplate:: integrate(const Array & f, Array & intf, UInt nb_degree_of_freedom, const ElementType & type, const GhostType & ghost_type, const Array & filter_elements) const { #ifndef AKANTU_NDEBUG UInt nb_element = mesh.getNbElement(type, ghost_type); if (filter_elements == filter_elements) nb_element = filter_elements.size(); UInt nb_quadrature_points = getNbIntegrationPoints(type); AKANTU_DEBUG_ASSERT(f.size() == nb_element * nb_quadrature_points, "The vector f(" << f.getID() << " size " << f.size() << ") has not the good size (" << nb_element << ")."); AKANTU_DEBUG_ASSERT(f.getNbComponent() == nb_degree_of_freedom, "The vector f(" << f.getID() << ") has not the good number of component."); AKANTU_DEBUG_ASSERT(intf.getNbComponent() == nb_degree_of_freedom, "The vector intf(" << intf.getID() << ") has not the good number of component."); AKANTU_DEBUG_ASSERT(intf.size() == nb_element, "The vector intf(" << intf.getID() << ") has not the good size."); #endif #define INTEGRATE(type) \ integrator.integrate(f, intf, nb_degree_of_freedom, ghost_type, \ filter_elements); AKANTU_BOOST_COHESIVE_ELEMENT_SWITCH(INTEGRATE); #undef INTEGRATE } /* -------------------------------------------------------------------------- */ template <> void FEEngineTemplate:: gradientOnIntegrationPoints(const Array &, Array &, const UInt, const ElementType &, const GhostType &, const Array &) const { AKANTU_DEBUG_TO_IMPLEMENT(); } /* -------------------------------------------------------------------------- */ -template <> -template <> -void FEEngineTemplate:: - computeNormalsOnIntegrationPoints<_cohesive_1d_2>( - const Array &, Array & normal, - const GhostType & ghost_type) const { - AKANTU_DEBUG_IN(); - - AKANTU_DEBUG_ASSERT( - mesh.getSpatialDimension() == 1, - "Mesh dimension must be 1 to compute normals on 1D cohesive elements!"); - const ElementType type = _cohesive_1d_2; - const ElementType facet_type = Mesh::getFacetType(type); - - UInt nb_element = mesh.getConnectivity(type, ghost_type).size(); - normal.resize(nb_element); - - Array & facets = - mesh.getMeshFacets().getSubelementToElement(type, ghost_type); - Array > & segments = - mesh.getMeshFacets().getElementToSubelement(facet_type, ghost_type); - - Real values[2]; - - for (UInt elem = 0; elem < nb_element; ++elem) { - - for (UInt p = 0; p < 2; ++p) { - Element f = facets(elem, p); - Element seg = segments(f.element)[0]; - Vector barycenter(values + p, 1); - mesh.getBarycenter(seg, barycenter); - } - - Real difference = values[0] - values[1]; - - AKANTU_DEBUG_ASSERT(difference != 0., - "Error in normal computation for cohesive elements"); - - normal(elem) = difference / std::abs(difference); - } - - AKANTU_DEBUG_OUT(); -} } // akantu diff --git a/src/fe_engine/shape_cohesive_inline_impl.cc b/src/fe_engine/shape_cohesive_inline_impl.cc index 9477d6021..df81e9a8f 100644 --- a/src/fe_engine/shape_cohesive_inline_impl.cc +++ b/src/fe_engine/shape_cohesive_inline_impl.cc @@ -1,301 +1,328 @@ /** * @file shape_cohesive_inline_impl.cc * * @author Nicolas Richart * @author Marco Vocialta * * @date creation: Fri Feb 03 2012 * @date last modification: Thu Oct 15 2015 * * @brief ShapeCohesive inline implementation * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "shape_cohesive.hh" /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_SHAPE_COHESIVE_INLINE_IMPL_CC__ #define __AKANTU_SHAPE_COHESIVE_INLINE_IMPL_CC__ namespace akantu { /* -------------------------------------------------------------------------- */ inline ShapeLagrange<_ek_cohesive>::ShapeLagrange(const Mesh & mesh, const ID & id, const MemoryID & memory_id) : ShapeLagrangeBase(mesh, _ek_cohesive, id, memory_id) {} #define INIT_SHAPE_FUNCTIONS(type) \ setIntegrationPointsByType(integration_points, ghost_type); \ precomputeShapesOnIntegrationPoints(nodes, ghost_type); \ precomputeShapeDerivativesOnIntegrationPoints(nodes, ghost_type); /* -------------------------------------------------------------------------- */ inline void ShapeLagrange<_ek_cohesive>::initShapeFunctions( const Array & nodes, const Matrix & integration_points, const ElementType & type, const GhostType & ghost_type) { AKANTU_BOOST_COHESIVE_ELEMENT_SWITCH(INIT_SHAPE_FUNCTIONS); } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ template void ShapeLagrange<_ek_cohesive>::computeShapeDerivativesOnIntegrationPoints( const Array &, const Matrix & integration_points, Array & shape_derivatives, const GhostType & ghost_type, const Array & filter_elements) const { AKANTU_DEBUG_IN(); - UInt size_of_shapesd = ElementClass::getShapeDerivativesSize(); - UInt spatial_dimension = ElementClass::getNaturalSpaceDimension(); - UInt nb_nodes_per_element = ElementClass::getNbNodesPerInterpolationElement(); + UInt size_of_shapesd = ElementClass::getShapeDerivativesSize(); + UInt spatial_dimension = ElementClass::getNaturalSpaceDimension(); + UInt nb_nodes_per_element = + ElementClass::getNbNodesPerInterpolationElement(); UInt nb_points = integration_points.cols(); UInt nb_element = mesh.getConnectivity(type, ghost_type).size(); AKANTU_DEBUG_ASSERT(shape_derivatives.getNbComponent() == size_of_shapesd, "The shapes_derivatives array does not have the correct " << "number of component"); shape_derivatives.resize(nb_element * nb_points); Real * shapesd_val = shape_derivatives.storage(); auto compute = [&](const auto & el) { auto ptr = shapesd_val + el * nb_points * size_of_shapesd; Tensor3 B(ptr, spatial_dimension, nb_nodes_per_element, nb_points); ElementClass::computeDNDS(integration_points, B); }; for_each_element(nb_element, filter_elements, compute); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ inline void ShapeLagrange<_ek_cohesive>::computeShapeDerivativesOnIntegrationPoints( const Array & nodes, const Matrix & integration_points, Array & shape_derivatives, const ElementType & type, const GhostType & ghost_type, const Array & filter_elements) const { #define AKANTU_COMPUTE_SHAPES(type) \ computeShapeDerivativesOnIntegrationPoints( \ nodes, integration_points, shape_derivatives, ghost_type, \ filter_elements); AKANTU_BOOST_COHESIVE_ELEMENT_SWITCH(AKANTU_COMPUTE_SHAPES); #undef AKANTU_COMPUTE_SHAPES } /* -------------------------------------------------------------------------- */ template void ShapeLagrange<_ek_cohesive>::precomputeShapesOnIntegrationPoints( const Array & nodes, GhostType ghost_type) { AKANTU_DEBUG_IN(); InterpolationType itp_type = ElementClassProperty::interpolation_type; Matrix & natural_coords = integration_points(type, ghost_type); UInt size_of_shapes = ElementClass::getShapeSize(); Array & shapes_tmp = shapes.alloc(0, size_of_shapes, itp_type, ghost_type); this->computeShapesOnIntegrationPoints(nodes, natural_coords, shapes_tmp, ghost_type); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void ShapeLagrange<_ek_cohesive>::precomputeShapeDerivativesOnIntegrationPoints( const Array & nodes, GhostType ghost_type) { AKANTU_DEBUG_IN(); InterpolationType itp_type = ElementClassProperty::interpolation_type; Matrix & natural_coords = integration_points(type, ghost_type); UInt size_of_shapesd = ElementClass::getShapeDerivativesSize(); Array & shapes_derivatives_tmp = shapes_derivatives.alloc(0, size_of_shapesd, itp_type, ghost_type); this->computeShapeDerivativesOnIntegrationPoints( nodes, natural_coords, shapes_derivatives_tmp, ghost_type); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void ShapeLagrange<_ek_cohesive>::extractNodalToElementField( const Array & nodal_f, Array & elemental_f, const GhostType & ghost_type, const Array & filter_elements) const { AKANTU_DEBUG_IN(); UInt nb_nodes_per_itp_element = ElementClass::getNbNodesPerInterpolationElement(); UInt nb_degree_of_freedom = nodal_f.getNbComponent(); UInt nb_element = this->mesh.getNbElement(type, ghost_type); const auto & conn_array = this->mesh.getConnectivity(type, ghost_type); auto conn = conn_array.begin(conn_array.getNbComponent() / 2, 2); if (filter_elements != empty_filter) { nb_element = filter_elements.size(); } elemental_f.resize(nb_element); Array::matrix_iterator u_it = elemental_f.begin(nb_degree_of_freedom, nb_nodes_per_itp_element); ReduceFunction reduce_function; auto compute = [&](const auto & el) { Matrix & u = *u_it; Matrix el_conn(conn[el]); // compute the average/difference of the nodal field loaded from cohesive // element for (UInt n = 0; n < el_conn.rows(); ++n) { UInt node_plus = el_conn(n, 0); UInt node_minus = el_conn(n, 1); for (UInt d = 0; d < nb_degree_of_freedom; ++d) { Real u_plus = nodal_f(node_plus, d); Real u_minus = nodal_f(node_minus, d); u(d, n) = reduce_function(u_plus, u_minus); } } ++u_it; }; for_each_element(nb_element, filter_elements, compute); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void ShapeLagrange<_ek_cohesive>::interpolateOnIntegrationPoints( const Array & in_u, Array & out_uq, UInt nb_degree_of_freedom, GhostType ghost_type, const Array & filter_elements) const { AKANTU_DEBUG_IN(); InterpolationType itp_type = ElementClassProperty::interpolation_type; AKANTU_DEBUG_ASSERT(this->shapes.exists(itp_type, ghost_type), "No shapes for the type " << this->shapes.printType(itp_type, ghost_type)); UInt nb_nodes_per_element = ElementClass::getNbNodesPerInterpolationElement(); Array u_el(0, nb_degree_of_freedom * nb_nodes_per_element); this->extractNodalToElementField(in_u, u_el, ghost_type, filter_elements); this->template interpolateElementalFieldOnIntegrationPoints( u_el, out_uq, ghost_type, shapes(itp_type, ghost_type), filter_elements); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void ShapeLagrange<_ek_cohesive>::variationOnIntegrationPoints( const Array & in_u, Array & nablauq, UInt nb_degree_of_freedom, GhostType ghost_type, const Array & filter_elements) const { AKANTU_DEBUG_IN(); InterpolationType itp_type = ElementClassProperty::interpolation_type; AKANTU_DEBUG_ASSERT( this->shapes_derivatives.exists(itp_type, ghost_type), "No shapes for the type " << this->shapes_derivatives.printType(itp_type, ghost_type)); UInt nb_nodes_per_element = ElementClass::getNbNodesPerInterpolationElement(); Array u_el(0, nb_degree_of_freedom * nb_nodes_per_element); this->extractNodalToElementField(in_u, u_el, ghost_type, filter_elements); this->template gradientElementalFieldOnIntegrationPoints( u_el, nablauq, ghost_type, shapes_derivatives(itp_type, ghost_type), filter_elements); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ - template void ShapeLagrange<_ek_cohesive>::computeNormalsOnIntegrationPoints( const Array & u, Array & normals_u, GhostType ghost_type, const Array & filter_elements) const { AKANTU_DEBUG_IN(); UInt nb_element = this->mesh.getNbElement(type, ghost_type); UInt nb_points = this->integration_points(type, ghost_type).cols(); UInt spatial_dimension = this->mesh.getSpatialDimension(); if (filter_elements != empty_filter) nb_element = filter_elements.size(); normals_u.resize(nb_points * nb_element); - Array tangents_u(nb_element * nb_points, - (spatial_dimension * (spatial_dimension - 1))); - - this->template variationOnIntegrationPoints( - u, tangents_u, spatial_dimension, ghost_type, filter_elements); + Array tangents_u(0, (spatial_dimension * (spatial_dimension - 1))); - Array::vector_iterator normal = normals_u.begin(spatial_dimension); - Array::vector_iterator normal_end = normals_u.end(spatial_dimension); + if (spatial_dimension > 1) { + tangents_u.resize(nb_element * nb_points); + this->template variationOnIntegrationPoints( + u, tangents_u, spatial_dimension, ghost_type, filter_elements); + } Real * tangent = tangents_u.storage(); - if (spatial_dimension == 3) - for (; normal != normal_end; ++normal) { + if (spatial_dimension == 3) { + for (auto & normal : make_view(normals_u, spatial_dimension)) { Math::vectorProduct3(tangent, tangent + spatial_dimension, - normal->storage()); + normal.storage()); - (*normal) /= normal->norm(); + normal /= normal.norm(); tangent += spatial_dimension * 2; } - else if (spatial_dimension == 2) - for (; normal != normal_end; ++normal) { + } else if (spatial_dimension == 2) { + for (auto & normal : make_view(normals_u, spatial_dimension)) { Vector a1(tangent, spatial_dimension); - (*normal)(0) = -a1(1); - (*normal)(1) = a1(0); - (*normal) /= normal->norm(); + normal(0) = -a1(1); + normal(1) = a1(0); + normal.normalize(); tangent += spatial_dimension; } + } else if (spatial_dimension == 1) { + const auto facet_type = Mesh::getFacetType(type); + const auto & mesh_facets = mesh.getMeshFacets(); + const auto & facets = mesh_facets.getSubelementToElement(type, ghost_type); + const auto & segments = + mesh_facets.getElementToSubelement(facet_type, ghost_type); + + Real values[2]; + + for (auto el : arange(nb_element)) { + if (filter_elements != empty_filter) + el = filter_elements(el); + + for (UInt p = 0; p < 2; ++p) { + Element facet = facets(el, p); + Element segment = segments(facet.element)[0]; + Vector barycenter(values + p, 1); + mesh.getBarycenter(segment, barycenter); + } + + Real difference = values[0] - values[1]; + + AKANTU_DEBUG_ASSERT(difference != 0., + "Error in normal computation for cohesive elements"); + + normals_u(el) = difference / std::abs(difference); + } + } AKANTU_DEBUG_OUT(); } } // namespace akantu #endif /* __AKANTU_SHAPE_COHESIVE_INLINE_IMPL_CC__ */ diff --git a/src/fe_engine/shape_structural_inline_impl.cc b/src/fe_engine/shape_structural_inline_impl.cc index 0eefa86d8..21832df0c 100644 --- a/src/fe_engine/shape_structural_inline_impl.cc +++ b/src/fe_engine/shape_structural_inline_impl.cc @@ -1,383 +1,432 @@ /** * @file shape_structural_inline_impl.cc * * @author Fabian Barras * @author Nicolas Richart * * @date creation: Mon Dec 13 2010 * @date last modification: Thu Oct 15 2015 * * @brief ShapeStructural inline implementation * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "mesh_iterators.hh" #include "shape_structural.hh" /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_SHAPE_STRUCTURAL_INLINE_IMPL_CC__ #define __AKANTU_SHAPE_STRUCTURAL_INLINE_IMPL_CC__ namespace akantu { namespace { /// Extract nodal coordinates per elements template std::unique_ptr> getNodesPerElement(const Mesh & mesh, const Array & nodes, const 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; } } template inline void ShapeStructural::initShapeFunctions( const Array & /* unused */, const Matrix & /* unused */, const ElementType & /* unused */, const GhostType & /* unused */) { AKANTU_DEBUG_TO_IMPLEMENT(); } /* -------------------------------------------------------------------------- */ #define INIT_SHAPE_FUNCTIONS(type) \ setIntegrationPointsByType(integration_points, ghost_type); \ precomputeRotationMatrices(nodes, ghost_type); \ precomputeShapesOnIntegrationPoints(nodes, ghost_type); \ precomputeShapeDerivativesOnIntegrationPoints(nodes, ghost_type); template <> inline void ShapeStructural<_ek_structural>::initShapeFunctions( const Array & nodes, const Matrix & integration_points, const ElementType & type, const GhostType & ghost_type) { AKANTU_BOOST_STRUCTURAL_ELEMENT_SWITCH(INIT_SHAPE_FUNCTIONS); } #undef INIT_SHAPE_FUNCTIONS /* -------------------------------------------------------------------------- */ template <> template void ShapeStructural<_ek_structural>::computeShapesOnIntegrationPoints( const Array & nodes, const Matrix & integration_points, Array & shapes, const GhostType & ghost_type, const Array & filter_elements) const { UInt nb_points = integration_points.cols(); UInt nb_element = mesh.getConnectivity(type, ghost_type).size(); shapes.resize(nb_element * nb_points); UInt ndof = ElementClass::getNbDegreeOfFreedom(); #if !defined(AKANTU_NDEBUG) UInt size_of_shapes = ElementClass::getShapeSize(); AKANTU_DEBUG_ASSERT(shapes.getNbComponent() == size_of_shapes, "The shapes array does not have the correct " << "number of component"); #endif auto shapes_it = shapes.begin_reinterpret( ElementClass::getNbNodesPerInterpolationElement(), ndof, nb_points, nb_element); auto shapes_begin = shapes_it; if (filter_elements != empty_filter) { nb_element = filter_elements.size(); } auto nodes_per_element = getNodesPerElement(mesh, nodes, ghost_type); auto nodes_it = nodes_per_element->begin(mesh.getSpatialDimension(), Mesh::getNbNodesPerElement(type)); auto nodes_begin = nodes_it; for (UInt elem = 0; elem < nb_element; ++elem) { if (filter_elements != empty_filter) { shapes_it = shapes_begin + filter_elements(elem); nodes_it = nodes_begin + filter_elements(elem); } Tensor3 & N = *shapes_it; auto & real_coord = *nodes_it; ElementClass::computeShapes(integration_points, real_coord, N); if (filter_elements == empty_filter) ++shapes_it; } } /* -------------------------------------------------------------------------- */ template template void ShapeStructural::precomputeRotationMatrices( const Array & nodes, const 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, const 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(); 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 & 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))) { auto & N = std::get<0>(tuple); auto & real_coord = std::get<1>(tuple); ElementClass::computeShapes(natural_coords, real_coord, N); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template template void ShapeStructural::precomputeShapeDerivativesOnIntegrationPoints( const Array & nodes, const 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(); 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_nodes_per_element * nb_dof, nb_points), make_view(rot_matrices, nb_dof, nb_dof))) { // compute shape derivatives auto & X = std::get<0>(tuple); auto & B = std::get<1>(tuple); auto & RDOFs = std::get<2>(tuple); + + Tensor3 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 auto R = RDOFs.block(0, 0, spatial_dimension, spatial_dimension); - Matrix T(B.size(1), B.size(1)); - T.block(RDOFs, 0, 0); - T.block(RDOFs, RDOFs.rows(), RDOFs.rows()); + Matrix T(B.size(1), B.size(1), 0); + + for (UInt i = 0; i < nb_nodes_per_element; ++i) { + T.block(RDOFs, i * RDOFs.rows(), i * RDOFs.rows()); + } // Rotate to local basis auto x = (R * X).block(0, 0, natural_spatial_dimension, nb_nodes_per_element); - Tensor3 dnds(B.size(0), B.size(1), B.size(2)); - ElementClass::computeDNDS(natural_coords, X, dnds); - - Tensor3 J(x.rows(), natural_coords.rows(), natural_coords.cols()); - - ElementClass::computeJMat(dnds, x, J); + 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, UInt nb_dof, const 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 = out_uq.begin_reinterpret(nb_dof, 1, nb_quad_points_per_element, u_el.size()); auto shapes_it = shapes_.begin_reinterpret(nb_dof, nb_dof * nb_nodes_per_element, nb_quad_points_per_element, nb_element); auto u_it = u_el.begin_reinterpret(nb_dof * nb_nodes_per_element, 1, nb_quad_points_per_element, u_el.size()); for_each_element(nb_element, filter_elements, [&](auto && el) { auto & uq = *out_it; const auto & u = *u_it; auto N = Tensor3(shapes_it[el]); for (auto && q : arange(uq.size(2))) { auto uq_q = Matrix(uq(q)); auto u_q = Matrix(u(q)); auto N_q = Matrix(N(q)); uq_q.mul(N_q, u_q); } ++out_it; ++u_it; }); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template template void ShapeStructural::gradientOnIntegrationPoints( const Array & in_u, Array & out_nablauq, UInt nb_dof, const 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 = out_nablauq.begin_reinterpret( element_dimension, 1, nb_quad_points_per_element, u_el.size()); auto shapesd_it = shapesd.begin_reinterpret( element_dimension, nb_dof * nb_nodes_per_element, nb_quad_points_per_element, nb_element); auto u_it = u_el.begin_reinterpret(nb_dof * nb_nodes_per_element, 1, nb_quad_points_per_element, u_el.size()); for_each_element(nb_element, filter_elements, [&](auto && el) { auto & nablau = *out_it; const auto & u = *u_it; auto B = Tensor3(shapesd_it[el]); for (auto && q : arange(nablau.size(2))) { auto nablau_q = Matrix(nablau(q)); auto u_q = Matrix(u(q)); auto B_q = Matrix(B(q)); nablau_q.mul(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.template mul(B, D); + } +} + } // namespace akantu #endif /* __AKANTU_SHAPE_STRUCTURAL_INLINE_IMPL_CC__ */ diff --git a/src/io/dumper/dumper_iohelper.cc b/src/io/dumper/dumper_iohelper.cc index 9ea6c8642..534c2bad2 100644 --- a/src/io/dumper/dumper_iohelper.cc +++ b/src/io/dumper/dumper_iohelper.cc @@ -1,302 +1,305 @@ /** * @file dumper_iohelper.cc * * @author Guillaume Anciaux * @author Dana Christen * @author David Simon Kammer * @author Nicolas Richart * * @date creation: Fri Oct 26 2012 * @date last modification: Thu Sep 17 2015 * * @brief implementation of DumperIOHelper * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include #include "dumper_iohelper.hh" #include "dumper_elemental_field.hh" #include "dumper_nodal_field.hh" #include "dumper_filtered_connectivity.hh" #include "dumper_variable.hh" #include "mesh.hh" #if defined(AKANTU_IGFEM) #include "dumper_igfem_connectivity.hh" #endif /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ DumperIOHelper::DumperIOHelper() = default; /* -------------------------------------------------------------------------- */ DumperIOHelper::~DumperIOHelper() { for (auto it = fields.begin(); it != fields.end(); ++it) { delete it->second; } delete dumper; } /* -------------------------------------------------------------------------- */ void DumperIOHelper::setParallelContext(bool is_parallel) { UInt whoami = Communicator::getStaticCommunicator().whoAmI(); UInt nb_proc = Communicator::getStaticCommunicator().getNbProc(); if(is_parallel) dumper->setParallelContext(whoami, nb_proc); else dumper->setParallelContext(0, 1); } /* -------------------------------------------------------------------------- */ void DumperIOHelper::setDirectory(const std::string & directory) { this->directory = directory; dumper->setPrefix(directory); } /* -------------------------------------------------------------------------- */ void DumperIOHelper::setBaseName(const std::string & basename) { filename = basename; } /* -------------------------------------------------------------------------- */ void DumperIOHelper::setTimeStep(Real time_step) { if(!time_activated) this->dumper->activateTimeDescFiles(time_step); else this->dumper->setTimeStep(time_step); } /* -------------------------------------------------------------------------- */ void DumperIOHelper::dump() { try { dumper->dump(filename, count); } catch (iohelper::IOHelperException & e) { AKANTU_DEBUG_ERROR("I was not able to dump your data with a Dumper: " << e.what()); } ++count; } /* -------------------------------------------------------------------------- */ void DumperIOHelper::dump(UInt step) { this->count = step; this->dump(); } /* -------------------------------------------------------------------------- */ void DumperIOHelper::dump(Real current_time, UInt step) { this->dumper->setCurrentTime(current_time); this->dump(step); } /* -------------------------------------------------------------------------- */ void DumperIOHelper::registerMesh(const Mesh & mesh, UInt spatial_dimension, const GhostType & ghost_type, const ElementKind & element_kind) { #if defined(AKANTU_IGFEM) if (element_kind == _ek_igfem) { registerField("connectivities", new dumper::IGFEMConnectivityField(mesh.getConnectivities(), spatial_dimension, ghost_type)); } else #endif registerField("connectivities", new dumper::ElementalField(mesh.getConnectivities(), spatial_dimension, ghost_type, element_kind)); registerField("positions", new dumper::NodalField(mesh.getNodes())); } /* -------------------------------------------------------------------------- */ void DumperIOHelper::registerFilteredMesh(const Mesh & mesh, const ElementTypeMapArray & elements_filter, const Array & nodes_filter, UInt spatial_dimension, const GhostType & ghost_type, const ElementKind & element_kind) { auto * f_connectivities = new ElementTypeMapArrayFilter( mesh.getConnectivities(), elements_filter); this->registerField("connectivities", new dumper::FilteredConnectivityField(*f_connectivities, nodes_filter, spatial_dimension, ghost_type, element_kind)); this->registerField("positions",new dumper::NodalField( mesh.getNodes(), 0, 0, &nodes_filter)); } /* -------------------------------------------------------------------------- */ void DumperIOHelper::registerField(const std::string & field_id, dumper::Field * field) { auto it = fields.find(field_id); if(it != fields.end()) { AKANTU_DEBUG_WARNING("The field " << field_id << " is already registered in this Dumper. Field ignored."); return; } fields[field_id] = field; field->registerToDumper(field_id, *dumper); } /* -------------------------------------------------------------------------- */ void DumperIOHelper::unRegisterField(const std::string & field_id) { auto it = fields.find(field_id); if(it == fields.end()) { AKANTU_DEBUG_WARNING("The field " << field_id << " is not registered in this Dumper. Nothing to do."); return; } delete it->second; fields.erase(it); } /* -------------------------------------------------------------------------- */ void DumperIOHelper::registerVariable(const std::string & variable_id, dumper::VariableBase * variable) { auto it = variables.find(variable_id); if(it != variables.end()) { AKANTU_DEBUG_WARNING("The Variable " << variable_id << " is already registered in this Dumper. Variable ignored."); return; } variables[variable_id] = variable; variable->registerToDumper(variable_id, *dumper); } /* -------------------------------------------------------------------------- */ void DumperIOHelper::unRegisterVariable(const std::string & variable_id) { auto it = variables.find(variable_id); if(it == variables.end()) { AKANTU_DEBUG_WARNING("The variable " << variable_id << " is not registered in this Dumper. Nothing to do."); return; } delete it->second; variables.erase(it); } /* -------------------------------------------------------------------------- */ template iohelper::ElemType getIOHelperType() { AKANTU_DEBUG_TO_IMPLEMENT(); return iohelper::MAX_ELEM_TYPE; } template <> iohelper::ElemType getIOHelperType<_point_1>() { return iohelper::POINT_SET; } template <> iohelper::ElemType getIOHelperType<_segment_2>() { return iohelper::LINE1; } template <> iohelper::ElemType getIOHelperType<_segment_3>() { return iohelper::LINE2; } template <> iohelper::ElemType getIOHelperType<_triangle_3>() { return iohelper::TRIANGLE1; } template <> iohelper::ElemType getIOHelperType<_triangle_6>() { return iohelper::TRIANGLE2; } template <> iohelper::ElemType getIOHelperType<_quadrangle_4>() { return iohelper::QUAD1; } template <> iohelper::ElemType getIOHelperType<_quadrangle_8>() { return iohelper::QUAD2; } template <> iohelper::ElemType getIOHelperType<_tetrahedron_4>() { return iohelper::TETRA1; } template <> iohelper::ElemType getIOHelperType<_tetrahedron_10>() { return iohelper::TETRA2; } template <> iohelper::ElemType getIOHelperType<_hexahedron_8>() { return iohelper::HEX1; } template <> iohelper::ElemType getIOHelperType<_hexahedron_20>() { return iohelper::HEX2; } template <> iohelper::ElemType getIOHelperType<_pentahedron_6>() { return iohelper::PRISM1; } template <> iohelper::ElemType getIOHelperType<_pentahedron_15>() { return iohelper::PRISM2; } #if defined(AKANTU_COHESIVE_ELEMENT) +template <> +iohelper::ElemType getIOHelperType<_cohesive_1d_2>() { return iohelper::COH1D2; } + template <> iohelper::ElemType getIOHelperType<_cohesive_2d_4>() { return iohelper::COH2D4; } template <> iohelper::ElemType getIOHelperType<_cohesive_2d_6>() { return iohelper::COH2D6; } template <> iohelper::ElemType getIOHelperType<_cohesive_3d_6>() { return iohelper::COH3D6; } template <> iohelper::ElemType getIOHelperType<_cohesive_3d_12>() { return iohelper::COH3D12; } template <> iohelper::ElemType getIOHelperType<_cohesive_3d_8>() { return iohelper::COH3D8; } //template <> //iohelper::ElemType getIOHelperType<_cohesive_3d_16>() { return iohelper::COH3D16; } #endif #if defined(AKANTU_STRUCTURAL_MECHANICS) template <> iohelper::ElemType getIOHelperType<_bernoulli_beam_2>() { return iohelper::BEAM2; } template <> iohelper::ElemType getIOHelperType<_bernoulli_beam_3>() { return iohelper::BEAM3; } #endif /* -------------------------------------------------------------------------- */ UInt getIOHelperType(ElementType type) { UInt ioh_type = iohelper::MAX_ELEM_TYPE; #define GET_IOHELPER_TYPE(type) \ ioh_type = getIOHelperType(); AKANTU_BOOST_ALL_ELEMENT_SWITCH(GET_IOHELPER_TYPE); #undef GET_IOHELPER_TYPE return ioh_type; } /* -------------------------------------------------------------------------- */ } // akantu namespace iohelper { template<> DataType getDataType() { return _int; } } diff --git a/src/mesh/element_type_map.cc b/src/mesh/element_type_map.cc index 10ed4c342..ee0fc8cca 100644 --- a/src/mesh/element_type_map.cc +++ b/src/mesh/element_type_map.cc @@ -1,59 +1,71 @@ /** * @file element_type_map.cc * * @author Nicolas Richart * * @date creation Tue Jun 27 2017 * * @brief A Documented file. * * @section LICENSE * * Copyright (©) 2010-2011 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "fe_engine.hh" #include "mesh.hh" /* -------------------------------------------------------------------------- */ namespace akantu { -FEEngineElementTypeMapArrayInializer::FEEngineElementTypeMapArrayInializer( +FEEngineElementTypeMapArrayInitializer::FEEngineElementTypeMapArrayInitializer( const FEEngine & fe_engine, UInt nb_component, UInt spatial_dimension, const GhostType & ghost_type, const ElementKind & element_kind) - : MeshElementTypeMapArrayInializer( - fe_engine.getMesh(), - nb_component, + : MeshElementTypeMapArrayInitializer( + fe_engine.getMesh(), nb_component, spatial_dimension == UInt(-2) ? fe_engine.getMesh().getSpatialDimension() : spatial_dimension, ghost_type, element_kind, true, false), fe_engine(fe_engine) {} -UInt FEEngineElementTypeMapArrayInializer::size( +FEEngineElementTypeMapArrayInitializer::FEEngineElementTypeMapArrayInitializer( + const FEEngine & fe_engine, + const ElementTypeMapArrayInitializer::CompFunc & nb_component, + UInt spatial_dimension, const GhostType & ghost_type, + const ElementKind & element_kind) + : MeshElementTypeMapArrayInitializer( + fe_engine.getMesh(), nb_component, + spatial_dimension == UInt(-2) + ? fe_engine.getMesh().getSpatialDimension() + : spatial_dimension, + ghost_type, element_kind, true, false), + fe_engine(fe_engine) {} + +UInt FEEngineElementTypeMapArrayInitializer::size( const ElementType & type) const { - return MeshElementTypeMapArrayInializer::size(type) * + return MeshElementTypeMapArrayInitializer::size(type) * fe_engine.getNbIntegrationPoints(type, this->ghost_type); } -FEEngineElementTypeMapArrayInializer::ElementTypesIteratorHelper -FEEngineElementTypeMapArrayInializer::elementTypes() const { +FEEngineElementTypeMapArrayInitializer::ElementTypesIteratorHelper +FEEngineElementTypeMapArrayInitializer::elementTypes() const { return this->fe_engine.elementTypes(spatial_dimension, ghost_type, element_kind); } } // namespace akantu diff --git a/src/mesh/element_type_map.hh b/src/mesh/element_type_map.hh index 470987bd4..f3aefd368 100644 --- a/src/mesh/element_type_map.hh +++ b/src/mesh/element_type_map.hh @@ -1,440 +1,449 @@ /** * @file element_type_map.hh * * @author Nicolas Richart * * @date creation: Wed Aug 31 2011 * @date last modification: Fri Oct 02 2015 * * @brief storage class by element type * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_array.hh" #include "aka_memory.hh" #include "aka_named_argument.hh" #include "element.hh" /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_ELEMENT_TYPE_MAP_HH__ #define __AKANTU_ELEMENT_TYPE_MAP_HH__ namespace akantu { class FEEngine; } // namespace akantu namespace akantu { namespace { DECLARE_NAMED_ARGUMENT(all_ghost_types); DECLARE_NAMED_ARGUMENT(default_value); DECLARE_NAMED_ARGUMENT(element_kind); DECLARE_NAMED_ARGUMENT(ghost_type); DECLARE_NAMED_ARGUMENT(nb_component); + DECLARE_NAMED_ARGUMENT(nb_component_functor); DECLARE_NAMED_ARGUMENT(with_nb_element); DECLARE_NAMED_ARGUMENT(with_nb_nodes_per_element); DECLARE_NAMED_ARGUMENT(spatial_dimension); DECLARE_NAMED_ARGUMENT(do_not_default); } // namespace template class ElementTypeMap; /* -------------------------------------------------------------------------- */ /* ElementTypeMapBase */ /* -------------------------------------------------------------------------- */ /// Common non templated base class for the ElementTypeMap class class ElementTypeMapBase { public: virtual ~ElementTypeMapBase() = default; }; /* -------------------------------------------------------------------------- */ /* ElementTypeMap */ /* -------------------------------------------------------------------------- */ template class ElementTypeMap : public ElementTypeMapBase { public: ElementTypeMap(); ~ElementTypeMap() override; inline static std::string printType(const SupportType & type, const GhostType & ghost_type); /*! Tests whether a type is present in the object * @param type the type to check for * @param ghost_type optional: by default, the data map for non-ghost * elements is searched * @return true if the type is present. */ inline bool exists(const SupportType & type, const GhostType & ghost_type = _not_ghost) const; /*! get the stored data corresponding to a type * @param type the type to check for * @param ghost_type optional: by default, the data map for non-ghost * elements is searched * @return stored data corresponding to type. */ inline const Stored & operator()(const SupportType & type, const GhostType & ghost_type = _not_ghost) const; /*! get the stored data corresponding to a type * @param type the type to check for * @param ghost_type optional: by default, the data map for non-ghost * elements is searched * @return stored data corresponding to type. */ inline Stored & operator()(const SupportType & type, const GhostType & ghost_type = _not_ghost); /*! insert data of a new type (not yet present) into the map. THIS METHOD IS * NOT ARRAY SAFE, when using ElementTypeMapArray, use setArray instead * @param data to insert * @param type type of data (if this type is already present in the map, * an exception is thrown). * @param ghost_type optional: by default, the data map for non-ghost * elements is searched * @return stored data corresponding to type. */ template inline Stored & operator()(U && insertee, const SupportType & type, const GhostType & ghost_type = _not_ghost); public: - /// print helper virtual void printself(std::ostream & stream, int indent = 0) const; /* ------------------------------------------------------------------------ */ /* Element type Iterator */ /* ------------------------------------------------------------------------ */ /*! iterator allows to iterate over type-data pairs of the map. The interface * expects the SupportType to be ElementType. */ using DataMap = std::map; class type_iterator : private std::iterator { public: using value_type = const SupportType; using pointer = const SupportType *; using reference = const SupportType &; protected: using DataMapIterator = typename ElementTypeMap::DataMap::const_iterator; public: type_iterator(DataMapIterator & list_begin, DataMapIterator & list_end, UInt dim, ElementKind ek); type_iterator(const type_iterator & it); type_iterator() = default; inline reference operator*(); inline reference operator*() const; inline type_iterator & operator++(); type_iterator operator++(int); inline bool operator==(const type_iterator & other) const; inline bool operator!=(const type_iterator & other) const; type_iterator & operator=(const type_iterator & other); private: DataMapIterator list_begin; DataMapIterator list_end; UInt dim; ElementKind kind; }; /// helper class to use in range for constructions class ElementTypesIteratorHelper { public: using Container = ElementTypeMap; using iterator = typename Container::type_iterator; ElementTypesIteratorHelper(const Container & container, UInt dim, GhostType ghost_type, ElementKind kind) : container(std::cref(container)), dim(dim), ghost_type(ghost_type), kind(kind) {} template ElementTypesIteratorHelper(const Container & container, use_named_args_t, pack &&... _pack) : ElementTypesIteratorHelper( container, OPTIONAL_NAMED_ARG(spatial_dimension, _all_dimensions), OPTIONAL_NAMED_ARG(ghost_type, _not_ghost), OPTIONAL_NAMED_ARG(element_kind, _ek_regular)) {} ElementTypesIteratorHelper(const ElementTypesIteratorHelper &) = default; ElementTypesIteratorHelper & operator=(const ElementTypesIteratorHelper &) = default; ElementTypesIteratorHelper & operator=(ElementTypesIteratorHelper &&) = default; iterator begin() { return container.get().firstType(dim, ghost_type, kind); } iterator end() { return container.get().lastType(dim, ghost_type, kind); } private: std::reference_wrapper container; UInt dim; GhostType ghost_type; ElementKind kind; }; private: ElementTypesIteratorHelper elementTypesImpl(UInt dim = _all_dimensions, GhostType ghost_type = _not_ghost, ElementKind kind = _ek_regular) const; template ElementTypesIteratorHelper elementTypesImpl(const use_named_args_t & /*unused*/, pack &&... _pack) const; public: + /*! + * \param _spatial_dimension optional: filter for elements of given spatial + * dimension + * \param _ghost_type optional: filter for a certain ghost_type + * \param _element_kind optional: filter for elements of given kind + */ template std::enable_if_t::value, ElementTypesIteratorHelper> elementTypes(pack &&... _pack) const { return elementTypesImpl(use_named_args, std::forward(_pack)...); } template std::enable_if_t::value, ElementTypesIteratorHelper> elementTypes(pack &&... _pack) const { return elementTypesImpl(std::forward(_pack)...); } public: /*! Get an iterator to the beginning of a subset datamap. This method expects * the SupportType to be ElementType. * @param dim optional: iterate over data of dimension dim (e.g. when * iterating over (surface) facets of a 3D mesh, dim would be 2). * by default, all dimensions are considered. * @param ghost_type optional: by default, the data map for non-ghost * elements is iterated over. * @param kind optional: the kind of element to search for (see * aka_common.hh), by default all kinds are considered * @return an iterator to the first stored data matching the filters * or an iterator to the end of the map if none match*/ inline type_iterator firstType(UInt dim = _all_dimensions, GhostType ghost_type = _not_ghost, ElementKind kind = _ek_not_defined) const; /*! Get an iterator to the end of a subset datamap. This method expects * the SupportType to be ElementType. * @param dim optional: iterate over data of dimension dim (e.g. when * iterating over (surface) facets of a 3D mesh, dim would be 2). * by default, all dimensions are considered. * @param ghost_type optional: by default, the data map for non-ghost * elements is iterated over. * @param kind optional: the kind of element to search for (see * aka_common.hh), by default all kinds are considered * @return an iterator to the last stored data matching the filters * or an iterator to the end of the map if none match */ inline type_iterator lastType(UInt dim = _all_dimensions, GhostType ghost_type = _not_ghost, ElementKind kind = _ek_not_defined) const; protected: /*! Direct access to the underlying data map. for internal use by daughter * classes only * @param ghost_type whether to return the data map or the ghost_data map * @return the raw map */ inline DataMap & getData(GhostType ghost_type); /*! Direct access to the underlying data map. for internal use by daughter * classes only * @param ghost_type whether to return the data map or the ghost_data map * @return the raw map */ inline const DataMap & getData(GhostType ghost_type) const; /* ------------------------------------------------------------------------ */ protected: DataMap data; DataMap ghost_data; }; /* -------------------------------------------------------------------------- */ /* Some typedefs */ /* -------------------------------------------------------------------------- */ template class ElementTypeMapArray : public ElementTypeMap *, SupportType>, public Memory { public: using type = T; using array_type = Array; protected: using parent = ElementTypeMap *, SupportType>; using DataMap = typename parent::DataMap; public: using type_iterator = typename parent::type_iterator; /// standard assigment (copy) operator void operator=(const ElementTypeMapArray &) = delete; ElementTypeMapArray(const ElementTypeMapArray &) = delete; + /// explicit copy + void copy(const ElementTypeMapArray & other); + /*! Constructor * @param id optional: identifier (string) * @param parent_id optional: parent identifier. for organizational purposes * only * @param memory_id optional: choose a specific memory, defaults to memory 0 */ ElementTypeMapArray(const ID & id = "by_element_type_array", const ID & parent_id = "no_parent", const MemoryID & memory_id = 0) : parent(), Memory(parent_id + ":" + id, memory_id), name(id){}; /*! allocate memory for a new array * @param size number of tuples of the new array * @param nb_component tuple size * @param type the type under which the array is indexed in the map * @param ghost_type whether to add the field to the data map or the * ghost_data map * @return a reference to the allocated array */ inline Array & alloc(UInt size, UInt nb_component, const SupportType & type, const GhostType & ghost_type, const T & default_value = T()); /*! allocate memory for a new array in both the data and the ghost_data map * @param size number of tuples of the new array * @param nb_component tuple size * @param type the type under which the array is indexed in the map*/ inline void alloc(UInt size, UInt nb_component, const SupportType & type, const T & default_value = T()); /* get a reference to the array of certain type * @param type data filed under type is returned * @param ghost_type optional: by default the non-ghost map is searched * @return a reference to the array */ inline const Array & operator()(const SupportType & type, const GhostType & ghost_type = _not_ghost) const; /// access the data of an element, this combine the map and array accessor - inline const T & operator()(const Element & element, UInt component = 0) const; + inline const T & operator()(const Element & element, + UInt component = 0) const; /// access the data of an element, this combine the map and array accessor inline T & operator()(const Element & element, UInt component = 0); /* get a reference to the array of certain type * @param type data filed under type is returned * @param ghost_type optional: by default the non-ghost map is searched * @return a const reference to the array */ inline Array & operator()(const SupportType & type, const GhostType & ghost_type = _not_ghost); /*! insert data of a new type (not yet present) into the map. * @param type type of data (if this type is already present in the map, * an exception is thrown). * @param ghost_type optional: by default, the data map for non-ghost * elements is searched * @param vect the vector to include into the map * @return stored data corresponding to type. */ inline void setArray(const SupportType & type, const GhostType & ghost_type, const Array & vect); /*! frees all memory related to the data*/ inline void free(); /*! set all values in the ElementTypeMap to zero*/ inline void clear(); /*! set all values in the ElementTypeMap to value */ - template - inline void set(const ST & value); + template inline void set(const ST & value); /*! deletes and reorders entries in the stored arrays * @param new_numbering a ElementTypeMapArray of new indices. UInt(-1) * indicates * deleted entries. */ inline void onElementsRemoved(const ElementTypeMapArray & new_numbering); /// text output helper void printself(std::ostream & stream, int indent = 0) const override; /*! set the id * @param id the new name */ inline void setID(const ID & id) { this->id = id; } ElementTypeMap getNbComponents(UInt dim = _all_dimensions, GhostType ghost_type = _not_ghost, ElementKind kind = _ek_not_defined) const { ElementTypeMap nb_components; for (auto & type : this->elementTypes(dim, ghost_type, kind)) { UInt nb_comp = (*this)(type, ghost_type).getNbComponent(); nb_components(type, ghost_type) = nb_comp; } return nb_components; } /* ------------------------------------------------------------------------ */ /* more evolved allocators */ /* ------------------------------------------------------------------------ */ public: /// initialize the arrays in accordance to a functor template void initialize(const Func & f, const T & default_value, bool do_not_default); /// initialize with sizes and number of components in accordance of a mesh /// content template void initialize(const Mesh & mesh, pack &&... _pack); /// initialize with sizes and number of components in accordance of a fe /// engine content (aka integration points) template void initialize(const FEEngine & fe_engine, pack &&... _pack); /* ------------------------------------------------------------------------ */ /* Accesssors */ /* ------------------------------------------------------------------------ */ public: /// get the name of the internal field AKANTU_GET_MACRO(Name, name, ID); /// name of the elment type map: e.g. connectivity, grad_u ID name; }; /// to store data Array by element type using ElementTypeMapReal = ElementTypeMapArray; /// to store data Array by element type using ElementTypeMapInt = ElementTypeMapArray; /// to store data Array by element type using ElementTypeMapUInt = ElementTypeMapArray; /// Map of data of type UInt stored in a mesh using UIntDataMap = std::map *>; using ElementTypeMapUIntDataMap = ElementTypeMap; } // namespace akantu #endif /* __AKANTU_ELEMENT_TYPE_MAP_HH__ */ diff --git a/src/mesh/element_type_map_tmpl.hh b/src/mesh/element_type_map_tmpl.hh index cedbb7f98..23a34e1fe 100644 --- a/src/mesh/element_type_map_tmpl.hh +++ b/src/mesh/element_type_map_tmpl.hh @@ -1,662 +1,747 @@ /** * @file element_type_map_tmpl.hh * * @author Nicolas Richart * * @date creation: Wed Aug 31 2011 * @date last modification: Fri Oct 02 2015 * * @brief implementation of template functions of the ElementTypeMap and * ElementTypeMapArray classes * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_static_if.hh" #include "element_type_map.hh" #include "mesh.hh" /* -------------------------------------------------------------------------- */ #include "element_type_conversion.hh" /* -------------------------------------------------------------------------- */ +#include +/* -------------------------------------------------------------------------- */ #ifndef __AKANTU_ELEMENT_TYPE_MAP_TMPL_HH__ #define __AKANTU_ELEMENT_TYPE_MAP_TMPL_HH__ namespace akantu { /* -------------------------------------------------------------------------- */ /* ElementTypeMap */ /* -------------------------------------------------------------------------- */ template inline std::string ElementTypeMap::printType(const SupportType & type, const GhostType & ghost_type) { std::stringstream sstr; sstr << "(" << ghost_type << ":" << type << ")"; return sstr.str(); } /* -------------------------------------------------------------------------- */ template inline bool ElementTypeMap::exists( const SupportType & type, const GhostType & ghost_type) const { return this->getData(ghost_type).find(type) != this->getData(ghost_type).end(); } /* -------------------------------------------------------------------------- */ template inline const Stored & ElementTypeMap:: operator()(const SupportType & type, const GhostType & ghost_type) const { auto it = this->getData(ghost_type).find(type); if (it == this->getData(ghost_type).end()) AKANTU_SILENT_EXCEPTION("No element of type " << ElementTypeMap::printType(type, ghost_type) << " in this ElementTypeMap<" << debug::demangle(typeid(Stored).name()) << "> class"); return it->second; } /* -------------------------------------------------------------------------- */ template inline Stored & ElementTypeMap:: operator()(const SupportType & type, const GhostType & ghost_type) { return this->getData(ghost_type)[type]; } - /* -------------------------------------------------------------------------- */ template template -inline Stored & ElementTypeMap::operator()( - U && insertee, const SupportType & type, const GhostType & ghost_type) { +inline Stored & ElementTypeMap:: +operator()(U && insertee, const SupportType & type, + const GhostType & ghost_type) { auto it = this->getData(ghost_type).find(type); if (it != this->getData(ghost_type).end()) { AKANTU_SILENT_EXCEPTION("Element of type " << ElementTypeMap::printType(type, ghost_type) << " already in this ElementTypeMap<" << debug::demangle(typeid(Stored).name()) << "> class"); } else { auto & data = this->getData(ghost_type); const auto & res = data.insert(std::make_pair(type, std::forward(insertee))); it = res.first; } return it->second; } /* -------------------------------------------------------------------------- */ template inline typename ElementTypeMap::DataMap & ElementTypeMap::getData(GhostType ghost_type) { if (ghost_type == _not_ghost) return data; return ghost_data; } /* -------------------------------------------------------------------------- */ template inline const typename ElementTypeMap::DataMap & ElementTypeMap::getData(GhostType ghost_type) const { if (ghost_type == _not_ghost) return data; return ghost_data; } /* -------------------------------------------------------------------------- */ /// Works only if stored is a pointer to a class with a printself method template void ElementTypeMap::printself(std::ostream & stream, int indent) const { std::string space; for (Int i = 0; i < indent; i++, space += AKANTU_INDENT) ; stream << space << "ElementTypeMap<" << debug::demangle(typeid(Stored).name()) << "> [" << std::endl; for (auto gt : ghost_types) { const DataMap & data = getData(gt); for (auto & pair : data) { stream << space << space << ElementTypeMap::printType(pair.first, gt) << std::endl; } } stream << space << "]" << std::endl; } /* -------------------------------------------------------------------------- */ template ElementTypeMap::ElementTypeMap() = default; /* -------------------------------------------------------------------------- */ template ElementTypeMap::~ElementTypeMap() = default; /* -------------------------------------------------------------------------- */ /* ElementTypeMapArray */ +/* -------------------------------------------------------------------------- */ +template +void ElementTypeMapArray::copy( + const ElementTypeMapArray & other) { + for (auto && ghost_type : ghost_types) { + for (auto && type : + this->elementTypes(_all_dimensions, ghost_types, _ek_not_defined)) { + const auto & array_to_copy = other(type, ghost_type); + auto & array = + this->alloc(0, array_to_copy.getNbComponent(), type, ghost_type); + array.copy(array_to_copy); + } + } +} + /* -------------------------------------------------------------------------- */ template inline Array & ElementTypeMapArray::alloc( UInt size, UInt nb_component, const SupportType & type, const GhostType & ghost_type, const T & default_value) { std::string ghost_id = ""; if (ghost_type == _ghost) ghost_id = ":ghost"; Array * tmp; auto it = this->getData(ghost_type).find(type); if (it == this->getData(ghost_type).end()) { std::stringstream sstr; sstr << this->id << ":" << type << ghost_id; tmp = &(Memory::alloc(sstr.str(), size, nb_component, default_value)); std::stringstream sstrg; sstrg << ghost_type; // tmp->setTag(sstrg.str()); this->getData(ghost_type)[type] = tmp; } else { AKANTU_DEBUG_INFO( "The vector " << this->id << this->printType(type, ghost_type) << " already exists, it is resized instead of allocated."); tmp = it->second; it->second->resize(size); } return *tmp; } /* -------------------------------------------------------------------------- */ template inline void ElementTypeMapArray::alloc(UInt size, UInt nb_component, const SupportType & type, const T & default_value) { this->alloc(size, nb_component, type, _not_ghost, default_value); this->alloc(size, nb_component, type, _ghost, default_value); } /* -------------------------------------------------------------------------- */ template inline void ElementTypeMapArray::free() { AKANTU_DEBUG_IN(); for (auto gt : ghost_types) { auto & data = this->getData(gt); for (auto & pair : data) { dealloc(pair.second->getID()); } data.clear(); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template inline void ElementTypeMapArray::clear() { for (auto gt : ghost_types) { auto & data = this->getData(gt); for (auto & vect : data) { vect.second->clear(); } } } /* -------------------------------------------------------------------------- */ template -template +template inline void ElementTypeMapArray::set(const ST & value) { for (auto gt : ghost_types) { auto & data = this->getData(gt); for (auto & vect : data) { vect.second->set(value); } } } /* -------------------------------------------------------------------------- */ template inline const Array & ElementTypeMapArray:: operator()(const SupportType & type, const GhostType & ghost_type) const { auto it = this->getData(ghost_type).find(type); if (it == this->getData(ghost_type).end()) AKANTU_SILENT_EXCEPTION("No element of type " << ElementTypeMapArray::printType(type, ghost_type) << " in this const ElementTypeMapArray<" << debug::demangle(typeid(T).name()) << "> class(\"" << this->id << "\")"); return *(it->second); } /* -------------------------------------------------------------------------- */ template inline Array & ElementTypeMapArray:: operator()(const SupportType & type, const GhostType & ghost_type) { auto it = this->getData(ghost_type).find(type); if (it == this->getData(ghost_type).end()) AKANTU_SILENT_EXCEPTION("No element of type " << ElementTypeMapArray::printType(type, ghost_type) << " in this ElementTypeMapArray<" << debug::demangle(typeid(T).name()) << "> class (\"" << this->id << "\")"); return *(it->second); } /* -------------------------------------------------------------------------- */ template inline void ElementTypeMapArray::setArray(const SupportType & type, const GhostType & ghost_type, const Array & vect) { auto it = this->getData(ghost_type).find(type); if (AKANTU_DEBUG_TEST(dblWarning) && it != this->getData(ghost_type).end() && it->second != &vect) { AKANTU_DEBUG_WARNING( "The Array " << this->printType(type, ghost_type) << " is already registred, this call can lead to a memory leak."); } this->getData(ghost_type)[type] = &(const_cast &>(vect)); } /* -------------------------------------------------------------------------- */ template inline void ElementTypeMapArray::onElementsRemoved( const ElementTypeMapArray & new_numbering) { for (auto gt : ghost_types) { for (auto & type : new_numbering.elementTypes(_all_dimensions, gt, _ek_not_defined)) { auto support_type = convertType(type); if (this->exists(support_type, gt)) { const auto & renumbering = new_numbering(type, gt); if (renumbering.size() == 0) continue; auto & vect = this->operator()(support_type, gt); auto nb_component = vect.getNbComponent(); Array tmp(renumbering.size(), nb_component); UInt new_size = 0; for (UInt i = 0; i < vect.size(); ++i) { UInt new_i = renumbering(i); if (new_i != UInt(-1)) { memcpy(tmp.storage() + new_i * nb_component, vect.storage() + i * nb_component, nb_component * sizeof(T)); ++new_size; } } tmp.resize(new_size); vect.copy(tmp); } } } } /* -------------------------------------------------------------------------- */ template void ElementTypeMapArray::printself(std::ostream & stream, int indent) const { std::string space; for (Int i = 0; i < indent; i++, space += AKANTU_INDENT) ; stream << space << "ElementTypeMapArray<" << debug::demangle(typeid(T).name()) << "> [" << std::endl; for (UInt g = _not_ghost; g <= _ghost; ++g) { auto gt = (GhostType)g; const DataMap & data = this->getData(gt); typename DataMap::const_iterator it; for (it = data.begin(); it != data.end(); ++it) { stream << space << space << ElementTypeMapArray::printType(it->first, gt) << " [" << std::endl; it->second->printself(stream, indent + 3); stream << space << space << " ]" << std::endl; } } stream << space << "]" << std::endl; } /* -------------------------------------------------------------------------- */ /* SupportType Iterator */ /* -------------------------------------------------------------------------- */ template ElementTypeMap::type_iterator::type_iterator( DataMapIterator & list_begin, DataMapIterator & list_end, UInt dim, ElementKind ek) : list_begin(list_begin), list_end(list_end), dim(dim), kind(ek) {} /* -------------------------------------------------------------------------- */ template ElementTypeMap::type_iterator::type_iterator( const type_iterator & it) : list_begin(it.list_begin), list_end(it.list_end), dim(it.dim), kind(it.kind) {} /* -------------------------------------------------------------------------- */ template typename ElementTypeMap::type_iterator & ElementTypeMap::type_iterator:: operator=(const type_iterator & it) { if (this != &it) { list_begin = it.list_begin; list_end = it.list_end; dim = it.dim; kind = it.kind; } return *this; } /* -------------------------------------------------------------------------- */ template inline typename ElementTypeMap::type_iterator::reference ElementTypeMap::type_iterator::operator*() { return list_begin->first; } /* -------------------------------------------------------------------------- */ template inline typename ElementTypeMap::type_iterator::reference ElementTypeMap::type_iterator::operator*() const { return list_begin->first; } /* -------------------------------------------------------------------------- */ template inline typename ElementTypeMap::type_iterator & ElementTypeMap::type_iterator::operator++() { ++list_begin; while ((list_begin != list_end) && (((dim != _all_dimensions) && (dim != Mesh::getSpatialDimension(list_begin->first))) || ((kind != _ek_not_defined) && (kind != Mesh::getKind(list_begin->first))))) ++list_begin; return *this; } /* -------------------------------------------------------------------------- */ template typename ElementTypeMap::type_iterator ElementTypeMap::type_iterator::operator++(int) { type_iterator tmp(*this); operator++(); return tmp; } /* -------------------------------------------------------------------------- */ template inline bool ElementTypeMap::type_iterator:: operator==(const type_iterator & other) const { return this->list_begin == other.list_begin; } /* -------------------------------------------------------------------------- */ template inline bool ElementTypeMap::type_iterator:: operator!=(const type_iterator & other) const { return this->list_begin != other.list_begin; } /* -------------------------------------------------------------------------- */ template typename ElementTypeMap::ElementTypesIteratorHelper ElementTypeMap::elementTypesImpl(UInt dim, GhostType ghost_type, ElementKind kind) const { return ElementTypesIteratorHelper(*this, dim, ghost_type, kind); } /* -------------------------------------------------------------------------- */ template template typename ElementTypeMap::ElementTypesIteratorHelper ElementTypeMap::elementTypesImpl( const use_named_args_t & unused, pack &&... _pack) const { return ElementTypesIteratorHelper(*this, unused, _pack...); } /* -------------------------------------------------------------------------- */ template inline typename ElementTypeMap::type_iterator ElementTypeMap::firstType(UInt dim, GhostType ghost_type, ElementKind kind) const { typename DataMap::const_iterator b, e; b = getData(ghost_type).begin(); e = getData(ghost_type).end(); // loop until the first valid type while ((b != e) && (((dim != _all_dimensions) && (dim != Mesh::getSpatialDimension(b->first))) || ((kind != _ek_not_defined) && (kind != Mesh::getKind(b->first))))) ++b; return typename ElementTypeMap::type_iterator(b, e, dim, kind); } /* -------------------------------------------------------------------------- */ template inline typename ElementTypeMap::type_iterator ElementTypeMap::lastType(UInt dim, GhostType ghost_type, ElementKind kind) const { typename DataMap::const_iterator e; e = getData(ghost_type).end(); return typename ElementTypeMap::type_iterator(e, e, dim, kind); } /* -------------------------------------------------------------------------- */ /// standard output stream operator template inline std::ostream & operator<<(std::ostream & stream, const ElementTypeMap & _this) { _this.printself(stream); return stream; } /* -------------------------------------------------------------------------- */ -class ElementTypeMapArrayInializer { +class ElementTypeMapArrayInitializer { +protected: + using CompFunc = std::function; + public: - ElementTypeMapArrayInializer(UInt spatial_dimension = _all_dimensions, - UInt nb_component = 1, - const GhostType & ghost_type = _not_ghost, - const ElementKind & element_kind = _ek_regular) - : spatial_dimension(spatial_dimension), nb_component(nb_component), + ElementTypeMapArrayInitializer(const CompFunc & comp_func, + UInt spatial_dimension = _all_dimensions, + const GhostType & ghost_type = _not_ghost, + const ElementKind & element_kind = _ek_regular) + : comp_func(comp_func), spatial_dimension(spatial_dimension), ghost_type(ghost_type), element_kind(element_kind) {} const GhostType & ghostType() const { return ghost_type; } + virtual UInt nbComponent(const ElementType & type) const { + return comp_func(type, ghostType()); + } + protected: + CompFunc comp_func; UInt spatial_dimension; - UInt nb_component; GhostType ghost_type; ElementKind element_kind; }; /* -------------------------------------------------------------------------- */ -class MeshElementTypeMapArrayInializer : public ElementTypeMapArrayInializer { +class MeshElementTypeMapArrayInitializer + : public ElementTypeMapArrayInitializer { + using CompFunc = ElementTypeMapArrayInitializer::CompFunc; + public: - MeshElementTypeMapArrayInializer( + MeshElementTypeMapArrayInitializer( const Mesh & mesh, UInt nb_component = 1, UInt spatial_dimension = _all_dimensions, const GhostType & ghost_type = _not_ghost, const ElementKind & element_kind = _ek_regular, bool with_nb_element = false, bool with_nb_nodes_per_element = false) - : ElementTypeMapArrayInializer(spatial_dimension, nb_component, - ghost_type, element_kind), + : MeshElementTypeMapArrayInitializer( + mesh, + [nb_component](const ElementType &, const GhostType &) -> UInt { + return nb_component; + }, + spatial_dimension, ghost_type, element_kind, with_nb_element, + with_nb_nodes_per_element) {} + + MeshElementTypeMapArrayInitializer( + const Mesh & mesh, const CompFunc & comp_func, + UInt spatial_dimension = _all_dimensions, + const GhostType & ghost_type = _not_ghost, + const ElementKind & element_kind = _ek_regular, + bool with_nb_element = false, bool with_nb_nodes_per_element = false) + : ElementTypeMapArrayInitializer(comp_func, spatial_dimension, ghost_type, + element_kind), mesh(mesh), with_nb_element(with_nb_element), with_nb_nodes_per_element(with_nb_nodes_per_element) {} decltype(auto) elementTypes() const { return mesh.elementTypes(this->spatial_dimension, this->ghost_type, this->element_kind); } virtual UInt size(const ElementType & type) const { if (with_nb_element) return mesh.getNbElement(type, this->ghost_type); return 0; } - UInt nbComponent(const ElementType & type) const { + UInt nbComponent(const ElementType & type) const override { + UInt res = ElementTypeMapArrayInitializer::nbComponent(type); if (with_nb_nodes_per_element) - return (this->nb_component * mesh.getNbNodesPerElement(type)); + return (res * mesh.getNbNodesPerElement(type)); - return this->nb_component; + return res; } protected: const Mesh & mesh; bool with_nb_element; bool with_nb_nodes_per_element; }; /* -------------------------------------------------------------------------- */ -class FEEngineElementTypeMapArrayInializer - : public MeshElementTypeMapArrayInializer { +class FEEngineElementTypeMapArrayInitializer + : public MeshElementTypeMapArrayInitializer { public: - FEEngineElementTypeMapArrayInializer( + FEEngineElementTypeMapArrayInitializer( const FEEngine & fe_engine, UInt nb_component = 1, UInt spatial_dimension = _all_dimensions, const GhostType & ghost_type = _not_ghost, const ElementKind & element_kind = _ek_regular); + FEEngineElementTypeMapArrayInitializer( + const FEEngine & fe_engine, + const ElementTypeMapArrayInitializer::CompFunc & nb_component, + UInt spatial_dimension = _all_dimensions, + const GhostType & ghost_type = _not_ghost, + const ElementKind & element_kind = _ek_regular); + UInt size(const ElementType & type) const override; using ElementTypesIteratorHelper = ElementTypeMapArray::ElementTypesIteratorHelper; ElementTypesIteratorHelper elementTypes() const; protected: const FEEngine & fe_engine; }; /* -------------------------------------------------------------------------- */ template template void ElementTypeMapArray::initialize(const Func & f, const T & default_value, bool do_not_default) { + auto ghost_type = f.ghostType(); for (auto & type : f.elementTypes()) { - if (not this->exists(type, f.ghostType())) + if (not this->exists(type, ghost_type)) if (do_not_default) { - auto & array = this->alloc(0, f.nbComponent(type), type, f.ghostType()); + auto & array = this->alloc(0, f.nbComponent(type), type, ghost_type); array.resize(f.size(type)); } else { - this->alloc(f.size(type), f.nbComponent(type), type, f.ghostType(), + this->alloc(f.size(type), f.nbComponent(type), type, ghost_type, default_value); } else { - auto & array = this->operator()(type, f.ghostType()); + auto & array = this->operator()(type, ghost_type); if (not do_not_default) array.resize(f.size(type), default_value); else array.resize(f.size(type)); } } } /* -------------------------------------------------------------------------- */ +/** + * All parameters are named optionals + * \param _nb_component a functor giving the number of components per + * (ElementType, GhostType) pair or a scalar giving a unique number of + * components + * regardless of type + * \param _spatial_dimension a filter for the elements of a specific dimension + * \param _element_kind filter with element kind (_ek_regular, _ek_structural, + * ...) + * \param _with_nb_element allocate the arrays with the number of elements for + * each + * type in the mesh + * \param _with_nb_nodes_per_element multiply the number of components by the + * number of nodes per element + * \param _default_value default inital value + * \param _do_not_default do not initialize the allocated arrays + * \param _ghost_type filter a type of ghost + */ template template void ElementTypeMapArray::initialize(const Mesh & mesh, pack &&... _pack) { GhostType requested_ghost_type = OPTIONAL_NAMED_ARG(ghost_type, _casper); bool all_ghost_types = requested_ghost_type == _casper; for (auto ghost_type : ghost_types) { if ((not(ghost_type == requested_ghost_type)) and (not all_ghost_types)) continue; - this->initialize( - MeshElementTypeMapArrayInializer( - mesh, OPTIONAL_NAMED_ARG(nb_component, 1), - OPTIONAL_NAMED_ARG(spatial_dimension, mesh.getSpatialDimension()), - ghost_type, OPTIONAL_NAMED_ARG(element_kind, _ek_regular), - OPTIONAL_NAMED_ARG(with_nb_element, false), - OPTIONAL_NAMED_ARG(with_nb_nodes_per_element, false)), - OPTIONAL_NAMED_ARG(default_value, T()), - OPTIONAL_NAMED_ARG(do_not_default, false)); + // This thing should have a UInt or functor type + auto && nb_components = OPTIONAL_NAMED_ARG(nb_component, 1); + auto functor = MeshElementTypeMapArrayInitializer( + mesh, std::forward(nb_components), + OPTIONAL_NAMED_ARG(spatial_dimension, mesh.getSpatialDimension()), + ghost_type, OPTIONAL_NAMED_ARG(element_kind, _ek_regular), + OPTIONAL_NAMED_ARG(with_nb_element, false), + OPTIONAL_NAMED_ARG(with_nb_nodes_per_element, false)); + + this->initialize(functor, OPTIONAL_NAMED_ARG(default_value, T()), + OPTIONAL_NAMED_ARG(do_not_default, false)); } } /* -------------------------------------------------------------------------- */ +/** + * All parameters are named optionals + * \param _nb_component a functor giving the number of components per + * (ElementType, GhostType) pair or a scalar giving a unique number of + * components + * regardless of type + * \param _spatial_dimension a filter for the elements of a specific dimension + * \param _element_kind filter with element kind (_ek_regular, _ek_structural, + * ...) + * \param _default_value default inital value + * \param _do_not_default do not initialize the allocated arrays + * \param _ghost_type filter a specific ghost type + * \param _all_ghost_types get all ghost types + */ template template void ElementTypeMapArray::initialize(const FEEngine & fe_engine, pack &&... _pack) { bool all_ghost_types = OPTIONAL_NAMED_ARG(all_ghost_types, true); GhostType requested_ghost_type = OPTIONAL_NAMED_ARG(ghost_type, _not_ghost); for (auto ghost_type : ghost_types) { if ((not(ghost_type == requested_ghost_type)) and (not all_ghost_types)) continue; - this->initialize(FEEngineElementTypeMapArrayInializer( - fe_engine, OPTIONAL_NAMED_ARG(nb_component, 1), - OPTIONAL_NAMED_ARG(spatial_dimension, UInt(-2)), - ghost_type, - OPTIONAL_NAMED_ARG(element_kind, _ek_regular)), - OPTIONAL_NAMED_ARG(default_value, T()), + auto && nb_components = OPTIONAL_NAMED_ARG(nb_component, 1); + auto functor = FEEngineElementTypeMapArrayInitializer( + fe_engine, std::forward(nb_components), + OPTIONAL_NAMED_ARG(spatial_dimension, UInt(-2)), ghost_type, + OPTIONAL_NAMED_ARG(element_kind, _ek_regular)); + + this->initialize(functor, OPTIONAL_NAMED_ARG(default_value, T()), OPTIONAL_NAMED_ARG(do_not_default, false)); } } /* -------------------------------------------------------------------------- */ template inline T & ElementTypeMapArray:: operator()(const Element & element, UInt component) { return this->operator()(element.type, element.ghost_type)(element.element, component); } /* -------------------------------------------------------------------------- */ template inline const T & ElementTypeMapArray:: operator()(const Element & element, UInt component) const { return this->operator()(element.type, element.ghost_type)(element.element, component); } } // namespace akantu #endif /* __AKANTU_ELEMENT_TYPE_MAP_TMPL_HH__ */ diff --git a/src/mesh/mesh.cc b/src/mesh/mesh.cc index 0991360f0..49cd44069 100644 --- a/src/mesh/mesh.cc +++ b/src/mesh/mesh.cc @@ -1,545 +1,571 @@ /** * @file mesh.cc * * @author Guillaume Anciaux * @author David Simon Kammer * @author Nicolas Richart * @author Marco Vocialta * * @date creation: Fri Jun 18 2010 * @date last modification: Fri Jan 22 2016 * * @brief class handling meshes * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_config.hh" /* -------------------------------------------------------------------------- */ #include "element_class.hh" #include "group_manager_inline_impl.cc" #include "mesh.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" /* -------------------------------------------------------------------------- */ #ifdef AKANTU_USE_IOHELPER #include "dumper_field.hh" #include "dumper_internal_material_field.hh" #endif /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ Mesh::Mesh(UInt spatial_dimension, const ID & id, const MemoryID & memory_id, Communicator & communicator) : Memory(id, memory_id), GroupManager(*this, id + ":group_manager", memory_id), nodes_type(0, 1, id + ":nodes_type"), connectivities("connectivities", id, memory_id), ghosts_counters("ghosts_counters", id, memory_id), normals("normals", id, memory_id), spatial_dimension(spatial_dimension), lower_bounds(spatial_dimension, 0.), upper_bounds(spatial_dimension, 0.), size(spatial_dimension, 0.), local_lower_bounds(spatial_dimension, 0.), local_upper_bounds(spatial_dimension, 0.), mesh_data("mesh_data", id, memory_id), communicator(&communicator) { AKANTU_DEBUG_IN(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ Mesh::Mesh(UInt spatial_dimension, Communicator & communicator, const ID & id, const MemoryID & memory_id) : Mesh(spatial_dimension, id, memory_id, communicator) { AKANTU_DEBUG_IN(); this->nodes = std::make_shared>(0, spatial_dimension, id + ":coordinates"); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ Mesh::Mesh(UInt spatial_dimension, const ID & id, const MemoryID & memory_id) : Mesh(spatial_dimension, Communicator::getStaticCommunicator(), id, memory_id) {} /* -------------------------------------------------------------------------- */ Mesh::Mesh(UInt spatial_dimension, const std::shared_ptr> & nodes, const ID & id, const MemoryID & memory_id) : Mesh(spatial_dimension, id, memory_id, Communicator::getStaticCommunicator()) { this->nodes = nodes; this->nb_global_nodes = this->nodes->size(); this->nodes_to_elements.resize(nodes->size()); for (auto & node_set : nodes_to_elements) { node_set = std::make_unique>(); } this->computeBoundingBox(); } +/* -------------------------------------------------------------------------- */ +void Mesh::getBarycenters(Array & barycenter, const ElementType & type, + const GhostType & ghost_type) const { + barycenter.resize(getNbElement(type, ghost_type)); + for (auto && data : enumerate(make_view(barycenter, spatial_dimension))) { + getBarycenter(Element{type, UInt(std::get<0>(data)), ghost_type}, + std::get<1>(data)); + } +} + /* -------------------------------------------------------------------------- */ Mesh & Mesh::initMeshFacets(const ID & id) { AKANTU_DEBUG_IN(); if (!mesh_facets) { mesh_facets = std::make_unique(spatial_dimension, this->nodes, getID() + ":" + id, getMemoryID()); mesh_facets->mesh_parent = this; mesh_facets->is_mesh_facets = true; 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()); } /// transfers the the mesh physical names to the mesh facets if (not this->hasData("physical_names")) { AKANTU_DEBUG_OUT(); return *mesh_facets; } if (not mesh_facets->hasData("physical_names")) { mesh_facets->registerData("physical_names"); } auto & mesh_phys_data = this->getData("physical_names"); auto & phys_data = mesh_facets->getData("physical_names"); phys_data.initialize(*mesh_facets, _spatial_dimension = spatial_dimension - 1, _with_nb_element = true); + ElementTypeMapArray barycenters(getID(), "temporary_barycenters"); + barycenters.initialize(*mesh_facets, _nb_component = spatial_dimension, + _spatial_dimension = spatial_dimension - 1, + _with_nb_element = true); + + for (auto && ghost_type : ghost_types) { + for (auto && type : barycenters.elementTypes( + spatial_dimension - 1, ghost_type)) { + mesh_facets->getBarycenters(barycenters(type, ghost_type), type, + ghost_type); + } + } + for_each_element( mesh, [&](auto && element) { Vector barycenter(spatial_dimension); mesh.getBarycenter(element, barycenter); auto norm_barycenter = barycenter.norm(); + auto tolerance = Math::getTolerance(); + if (norm_barycenter > tolerance) + tolerance *= norm_barycenter; const auto & element_to_facet = mesh_facets->getElementToSubelement( element.type, element.ghost_type); Vector barycenter_facet(spatial_dimension); - auto nb_facet = - mesh_facets->getNbElement(element.type, element.ghost_type); - auto range = arange(nb_facet); + 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 && facet) { + std::find_if(range.begin(), range.end(), [&](auto && data) { + auto facet = std::get<0>(data); if (element_to_facet(facet)[1] == ElementNull) return false; - mesh_facets->getBarycenter( - Element{element.type, facet, element.ghost_type}, - barycenter_facet); - auto norm_distance = barycenter_facet.distance(barycenter); + auto norm_distance = barycenter.distance(std::get<1>(data)); #ifndef AKANTU_NDEBUG min_dist = std::min(min_dist, norm_distance); #endif - return (norm_distance < - (Math::getTolerance() * norm_barycenter)); + 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); + 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 - phys_data(Element{element.type, *facet, element.ghost_type}) = - mesh_phys_data(element); + phys_data(Element{element.type, UInt(std::get<0>(*facet)), + element.ghost_type}) = mesh_phys_data(element); }, _spatial_dimension = spatial_dimension - 1); mesh_facets->createGroupsFromMeshData("physical_names"); } AKANTU_DEBUG_OUT(); return *mesh_facets; } /* -------------------------------------------------------------------------- */ void Mesh::defineMeshParent(const Mesh & mesh) { AKANTU_DEBUG_IN(); this->mesh_parent = &mesh; this->is_mesh_facets = true; AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ Mesh::~Mesh() = default; /* -------------------------------------------------------------------------- */ void Mesh::read(const std::string & filename, const MeshIOType & mesh_io_type) { MeshIO mesh_io; mesh_io.read(filename, *this, mesh_io_type); type_iterator it = this->firstType(spatial_dimension, _not_ghost, _ek_not_defined); type_iterator last = this->lastType(spatial_dimension, _not_ghost, _ek_not_defined); if (it == last) AKANTU_EXCEPTION( "The mesh contained in the file " << filename << " does not seem to be of the good dimension." << " No element of dimension " << spatial_dimension << " where read."); this->nb_global_nodes = this->nodes->size(); this->computeBoundingBox(); this->nodes_to_elements.resize(nodes->size()); for (auto & node_set : nodes_to_elements) { node_set = std::make_unique>(); } } /* -------------------------------------------------------------------------- */ void Mesh::write(const std::string & filename, const MeshIOType & mesh_io_type) { MeshIO mesh_io; mesh_io.write(filename, *this, mesh_io_type); } /* -------------------------------------------------------------------------- */ void Mesh::printself(std::ostream & stream, int indent) const { std::string space; for (Int i = 0; i < indent; i++, space += 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(); for (UInt k = 0; k < spatial_dimension; ++k) { local_lower_bounds(k) = std::numeric_limits::max(); local_upper_bounds(k) = -std::numeric_limits::max(); } for (UInt i = 0; i < nodes->size(); ++i) { // if(!isPureGhostNode(i)) for (UInt k = 0; k < spatial_dimension; ++k) { local_lower_bounds(k) = std::min(local_lower_bounds[k], (*nodes)(i, k)); local_upper_bounds(k) = std::max(local_upper_bounds[k], (*nodes)(i, k)); } } if (this->is_distributed) { Matrix reduce_bounds(spatial_dimension, 2); for (UInt k = 0; k < spatial_dimension; ++k) { reduce_bounds(k, 0) = local_lower_bounds(k); reduce_bounds(k, 1) = -local_upper_bounds(k); } communicator->allReduce(reduce_bounds, SynchronizerOperation::_min); for (UInt k = 0; k < spatial_dimension; ++k) { lower_bounds(k) = reduce_bounds(k, 0); upper_bounds(k) = -reduce_bounds(k, 1); } } else { this->lower_bounds = this->local_lower_bounds; this->upper_bounds = this->local_upper_bounds; } size = upper_bounds - lower_bounds; AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void Mesh::initNormals() { normals.initialize(*this, _nb_component = spatial_dimension, _spatial_dimension = spatial_dimension, _element_kind = _ek_not_defined); } /* -------------------------------------------------------------------------- */ void Mesh::getGlobalConnectivity( ElementTypeMapArray & global_connectivity) { AKANTU_DEBUG_IN(); for (auto && ghost_type : ghost_types) { for (auto type : - global_connectivity.elementTypes(_ghost_type = ghost_type)) { + global_connectivity.elementTypes(_spatial_dimension = _all_dimensions, + _element_kind = _ek_not_defined, + _ghost_type = ghost_type)) { if (not connectivities.exists(type, ghost_type)) continue; auto & local_conn = connectivities(type, ghost_type); auto & g_connectivity = global_connectivity(type, ghost_type); UInt nb_nodes = local_conn.size() * local_conn.getNbComponent(); if (not nodes_global_ids && is_mesh_facets) { std::transform( local_conn.begin_reinterpret(nb_nodes), local_conn.end_reinterpret(nb_nodes), g_connectivity.begin_reinterpret(nb_nodes), [& node_ids = *mesh_parent->nodes_global_ids](UInt l)->UInt { return node_ids(l); }); } else { std::transform(local_conn.begin_reinterpret(nb_nodes), local_conn.end_reinterpret(nb_nodes), g_connectivity.begin_reinterpret(nb_nodes), [& node_ids = *nodes_global_ids](UInt l)->UInt { return node_ids(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); else return element_groups[group_name]->getDumper(dumper_name); } /* -------------------------------------------------------------------------- */ template ElementTypeMap Mesh::getNbDataPerElem(ElementTypeMapArray & arrays, const ElementKind & element_kind) { ElementTypeMap nb_data_per_elem; for (auto type : elementTypes(spatial_dimension, _not_ghost, element_kind)) { UInt nb_elements = this->getNbElement(type); auto & array = arrays(type); nb_data_per_elem(type) = array.getNbComponent() * array.size(); nb_data_per_elem(type) /= nb_elements; } return nb_data_per_elem; } /* -------------------------------------------------------------------------- */ template ElementTypeMap Mesh::getNbDataPerElem(ElementTypeMapArray & array, const ElementKind & element_kind); template ElementTypeMap Mesh::getNbDataPerElem(ElementTypeMapArray & array, const ElementKind & element_kind); /* -------------------------------------------------------------------------- */ #ifdef AKANTU_USE_IOHELPER template dumper::Field * Mesh::createFieldFromAttachedData(const std::string & field_id, const std::string & group_name, const ElementKind & element_kind) { dumper::Field * field = nullptr; ElementTypeMapArray * internal = nullptr; try { internal = &(this->getData(field_id)); } catch (...) { return nullptr; } ElementTypeMap nb_data_per_elem = this->getNbDataPerElem(*internal, element_kind); field = this->createElementalField( *internal, group_name, this->spatial_dimension, element_kind, nb_data_per_elem); return field; } template dumper::Field * Mesh::createFieldFromAttachedData(const std::string & field_id, const std::string & group_name, const ElementKind & element_kind); template dumper::Field * Mesh::createFieldFromAttachedData(const std::string & field_id, const std::string & group_name, const ElementKind & element_kind); #endif /* -------------------------------------------------------------------------- */ void Mesh::distribute() { this->distribute(Communicator::getStaticCommunicator()); } /* -------------------------------------------------------------------------- */ void Mesh::distribute(Communicator & communicator) { 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", this->getMemoryID(), true); this->node_synchronizer = std::make_unique( *this, this->getID() + ":node_synchronizer", this->getMemoryID(), true); Int psize = this->communicator->getNbProc(); #ifdef AKANTU_USE_SCOTCH Int prank = this->communicator->whoAmI(); if (prank == 0) { MeshPartitionScotch partition(*this, spatial_dimension); partition.partitionate(psize); MeshUtilsDistribution::distributeMeshCentralized(*this, 0, partition); } else { MeshUtilsDistribution::distributeMeshCentralized(*this, 0); } #else if (!(psize == 1)) { AKANTU_DEBUG_ERROR("Cannot distribute a mesh without a partitioning tool"); } #endif this->is_distributed = true; this->computeBoundingBox(); } /* -------------------------------------------------------------------------- */ 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::fillNodesToElements() { Element e; UInt nb_nodes = nodes->size(); for (UInt n = 0; n < nb_nodes; ++n) { if (this->nodes_to_elements[n]) this->nodes_to_elements[n]->clear(); else this->nodes_to_elements[n] = std::make_unique>(); } for (auto ghost_type : ghost_types) { e.ghost_type = ghost_type; for (const auto & type : elementTypes(spatial_dimension, ghost_type, _ek_not_defined)) { e.type = type; UInt nb_element = this->getNbElement(type, ghost_type); Array::const_iterator> conn_it = connectivities(type, ghost_type) .begin(Mesh::getNbNodesPerElement(type)); for (UInt el = 0; el < nb_element; ++el, ++conn_it) { e.element = el; const Vector & conn = *conn_it; for (UInt n = 0; n < conn.size(); ++n) nodes_to_elements[conn(n)]->insert(e); } } } } /* -------------------------------------------------------------------------- */ void Mesh::eraseElements(const Array & elements) { ElementTypeMap last_element; RemovedElementsEvent event(*this); auto & remove_list = event.getList(); auto & new_numbering = event.getNewNumbering(); for (auto && el : elements) { if (el.ghost_type != _not_ghost) { auto & count = ghosts_counters(el); --count; if (count > 0) continue; } remove_list.push_back(el); if (not last_element.exists(el.type, el.ghost_type)) { UInt nb_element = mesh.getNbElement(el.type, el.ghost_type); last_element(nb_element - 1, el.type, el.ghost_type); auto & numbering = new_numbering.alloc(nb_element, 1, el.type, el.ghost_type); for (auto && pair : enumerate(numbering)) { std::get<1>(pair) = std::get<0>(pair); } } UInt & pos = last_element(el.type, el.ghost_type); auto & numbering = new_numbering(el.type, el.ghost_type); numbering(el.element) = UInt(-1); numbering(pos) = el.element; --pos; } this->sendEvent(event); } } // namespace akantu diff --git a/src/mesh/mesh.hh b/src/mesh/mesh.hh index 06d3a8481..ffdd11510 100644 --- a/src/mesh/mesh.hh +++ b/src/mesh/mesh.hh @@ -1,610 +1,613 @@ /** * @file mesh.hh * * @author Guillaume Anciaux * @author Dana Christen * @author David Simon Kammer * @author Nicolas Richart * @author Marco Vocialta * * @date creation: Fri Jun 18 2010 * @date last modification: Thu Jan 14 2016 * * @brief the class representing the meshes * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_MESH_HH__ #define __AKANTU_MESH_HH__ /* -------------------------------------------------------------------------- */ #include "aka_array.hh" #include "aka_event_handler_manager.hh" #include "aka_memory.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 /* -------------------------------------------------------------------------- */ namespace akantu { class Communicator; class ElementSynchronizer; class NodeSynchronizer; } // namespace akantu namespace akantu { /* -------------------------------------------------------------------------- */ /* Mesh */ /* -------------------------------------------------------------------------- */ /** * @class Mesh this contain the coordinates of the nodes in the Mesh.nodes * Array, and the connectivity. The connectivity are stored in by element * types. * * In order to loop on all element you have to loop on all types like this : * @code{.cpp} for(auto & type : mesh.elementTypes()) { UInt nb_element = mesh.getNbElement(type); const Array & conn = mesh.getConnectivity(type); for(UInt e = 0; e < nb_element; ++e) { ... } } or for_each_element(mesh, [](Element & element) { std::cout << element << std::endl }); @endcode */ class Mesh : protected Memory, public EventHandlerManager, public GroupManager, public Dumpable { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ private: /// default constructor used for chaining, the last parameter is just to /// differentiate constructors Mesh(UInt spatial_dimension, const ID & id, const MemoryID & memory_id, Communicator & communicator); public: /// constructor that create nodes coordinates array Mesh(UInt spatial_dimension, const ID & id = "mesh", const MemoryID & memory_id = 0); /// mesh not distributed and not using the default communicator Mesh(UInt spatial_dimension, Communicator & communicator, const ID & id = "mesh", const MemoryID & memory_id = 0); /// constructor that use an existing nodes coordinates array, by knowing its /// ID // Mesh(UInt spatial_dimension, const ID & nodes_id, const ID & id, // const MemoryID & memory_id = 0); /** * constructor that use an existing nodes coordinates * array, by getting the vector of coordinates */ Mesh(UInt spatial_dimension, const std::shared_ptr> & nodes, const ID & id = "mesh", const MemoryID & memory_id = 0); ~Mesh() override; /// @typedef ConnectivityTypeList list of the types present in a Mesh using ConnectivityTypeList = std::set; /// 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); private: /// initialize the connectivity to NULL and other stuff void init(); /// function that computes the bounding box (fills xmin, xmax) void computeBoundingBox(); /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ public: /// patitionate the mesh among the processors involved in their computation virtual void distribute(Communicator & communicator); virtual void distribute(); /// 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 inline void extractNodalValuesFromElement(const Array & nodal_values, T * elemental_values, UInt * connectivity, UInt n_nodes, UInt nb_degree_of_freedom) const; // /// extract coordinates of nodes from a reversed element // inline void extractNodalCoordinatesFromPBCElement(Real * local_coords, // UInt * connectivity, // UInt n_nodes); /// add a Array of connectivity for the type . inline void addConnectivityType(const ElementType & type, const 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); /// initialize normals void initNormals(); /// init facets' mesh Mesh & initMeshFacets(const ID & id = "mesh_facets"); /// define parent mesh void defineMeshParent(const Mesh & mesh); /// get global connectivity array void getGlobalConnectivity(ElementTypeMapArray & global_connectivity); public: void getAssociatedElements(const Array & node_list, Array & elements); private: /// fills the nodes_to_elements structure void fillNodesToElements(); /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: /// get the id of the mesh AKANTU_GET_MACRO(ID, Memory::id, const ID &); /// get the id of the mesh AKANTU_GET_MACRO(MemoryID, Memory::memory_id, const MemoryID &); /// get the spatial dimension of the mesh = number of component of the /// coordinates AKANTU_GET_MACRO(SpatialDimension, spatial_dimension, UInt); /// get the nodes Array aka coordinates AKANTU_GET_MACRO(Nodes, *nodes, const Array &); AKANTU_GET_MACRO_NOT_CONST(Nodes, *nodes, Array &); /// get the normals for the elements AKANTU_GET_MACRO_BY_ELEMENT_TYPE(Normals, normals, Real); /// get the number of nodes AKANTU_GET_MACRO(NbNodes, nodes->size(), UInt); /// get the Array of global ids of the nodes (only used in parallel) AKANTU_GET_MACRO(GlobalNodesIds, *nodes_global_ids, const Array &); AKANTU_GET_MACRO_NOT_CONST(GlobalNodesIds, *nodes_global_ids, Array &); /// get the global id of a node inline UInt getNodeGlobalId(UInt local_id) const; /// get the global id of a node inline UInt getNodeLocalId(UInt global_id) const; /// get the global number of nodes inline UInt getNbGlobalNodes() const; /// get the nodes type Array AKANTU_GET_MACRO(NodesType, nodes_type, const Array &); protected: AKANTU_GET_MACRO_NOT_CONST(NodesType, nodes_type, Array &); public: inline NodeType getNodeType(UInt local_id) const; /// say if a node is a pure ghost node inline bool isPureGhostNode(UInt n) const; /// say if a node is pur local or master node inline bool isLocalOrMasterNode(UInt n) const; inline bool isLocalNode(UInt n) const; inline bool isMasterNode(UInt n) const; inline bool isSlaveNode(UInt n) const; AKANTU_GET_MACRO(LowerBounds, lower_bounds, const Vector &); AKANTU_GET_MACRO(UpperBounds, upper_bounds, const Vector &); AKANTU_GET_MACRO(LocalLowerBounds, local_lower_bounds, const Vector &); AKANTU_GET_MACRO(LocalUpperBounds, local_upper_bounds, const Vector &); /// get the connectivity Array for a given type AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(Connectivity, connectivities, UInt); AKANTU_GET_MACRO_BY_ELEMENT_TYPE(Connectivity, connectivities, UInt); AKANTU_GET_MACRO(Connectivities, connectivities, const ElementTypeMapArray &); /// get the number of element of a type in the mesh inline UInt getNbElement(const ElementType & type, const GhostType & ghost_type = _not_ghost) const; /// get the number of element for a given ghost_type and a given dimension inline UInt getNbElement(const UInt spatial_dimension = _all_dimensions, const GhostType & ghost_type = _not_ghost, const ElementKind & kind = _ek_not_defined) const; // /// get the connectivity list either for the elements or the ghost elements // inline const ConnectivityTypeList & // getConnectivityTypeList(const GhostType & ghost_type = _not_ghost) const; /// compute the barycenter of a given element private: inline void getBarycenter(UInt element, const ElementType & type, Real * barycenter, GhostType ghost_type = _not_ghost) const; public: inline void getBarycenter(const Element & element, Vector & barycenter) const; + void getBarycenters(Array & barycenter, const ElementType & type, + const GhostType & ghost_type) const; + #ifndef SWIG /// get the element connected to a subelement const auto & getElementToSubelement() const; - + /// get the element connected to a subelement const auto & getElementToSubelement(const ElementType & el_type, const GhostType & ghost_type = _not_ghost) const; /// get the element connected to a subelement auto & getElementToSubelement(const ElementType & el_type, const GhostType & ghost_type = _not_ghost); /// get the subelement connected to an element const auto & getSubelementToElement(const ElementType & el_type, const GhostType & ghost_type = _not_ghost) const; /// get the subelement connected to an element auto & getSubelementToElement(const ElementType & el_type, const GhostType & ghost_type = _not_ghost); #endif /// register a new ElementalTypeMap in the MeshData template inline ElementTypeMapArray & registerData(const std::string & data_name); template inline bool hasData(const ID & data_name, const ElementType & el_type, const GhostType & ghost_type = _not_ghost) const; /// get a name field associated to the mesh template inline const Array & getData(const ID & data_name, const ElementType & el_type, const GhostType & ghost_type = _not_ghost) const; /// get a name field associated to the mesh template inline Array & getData(const ID & data_name, const ElementType & el_type, const GhostType & ghost_type = _not_ghost); /// tells if the data data_name is contained in the mesh or not inline bool hasData(const ID & data_name) const; /// get a name field associated to the mesh template inline const ElementTypeMapArray & getData(const ID & data_name) const; /// get a name field associated to the mesh template inline ElementTypeMapArray & getData(const ID & data_name); template ElementTypeMap getNbDataPerElem(ElementTypeMapArray & array, const ElementKind & element_kind); template dumper::Field * createFieldFromAttachedData(const std::string & field_id, const std::string & group_name, const ElementKind & element_kind); /// templated getter returning the pointer to data in MeshData (modifiable) template inline Array & getDataPointer(const std::string & data_name, const ElementType & el_type, const GhostType & ghost_type = _not_ghost, UInt nb_component = 1, bool size_to_nb_element = true, bool resize_with_parent = false); /// Facets mesh accessor inline const Mesh & getMeshFacets() const; inline Mesh & getMeshFacets(); /// Parent mesh accessor inline const Mesh & getMeshParent() const; inline bool isMeshFacets() const { return this->is_mesh_facets; } /// defines is the mesh is distributed or not inline bool isDistributed() const { return this->is_distributed; } #ifndef SWIG /// return the dumper from a group and and a dumper name DumperIOHelper & getGroupDumper(const std::string & dumper_name, const std::string & group_name); #endif /* ------------------------------------------------------------------------ */ /* Wrappers on ElementClass functions */ /* ------------------------------------------------------------------------ */ public: /// get the number of nodes per element for a given element type static inline UInt getNbNodesPerElement(const ElementType & type); /// get the number of nodes per element for a given element type considered as /// a first order element static inline ElementType getP1ElementType(const ElementType & type); /// get the kind of the element type static inline ElementKind getKind(const ElementType & type); /// get spatial dimension of a type of element static inline UInt getSpatialDimension(const ElementType & type); /// get number of facets of a given element type static inline UInt getNbFacetsPerElement(const ElementType & type); /// get number of facets of a given element type static inline UInt getNbFacetsPerElement(const ElementType & type, UInt t); #ifndef SWIG /// get local connectivity of a facet for a given facet type static inline auto getFacetLocalConnectivity(const ElementType & type, UInt t = 0); /// get connectivity of facets for a given element inline auto getFacetConnectivity(const Element & element, UInt t = 0) const; #endif /// get the number of type of the surface element associated to a given /// element type static inline UInt getNbFacetTypes(const ElementType & type, UInt t = 0); #ifndef SWIG /// get the type of the surface element associated to a given element static inline constexpr auto getFacetType(const ElementType & type, UInt t = 0); /// get all the type of the surface element associated to a given element static inline constexpr auto getAllFacetTypes(const ElementType & type); #endif /// get the number of nodes in the given element list static inline UInt getNbNodesPerElementList(const Array & elements); /* ------------------------------------------------------------------------ */ /* Element type Iterator */ /* ------------------------------------------------------------------------ */ using type_iterator = ElementTypeMapArray::type_iterator; using ElementTypesIteratorHelper = ElementTypeMapArray::ElementTypesIteratorHelper; template ElementTypesIteratorHelper elementTypes(pack &&... _pack) const; inline type_iterator firstType(UInt dim = _all_dimensions, GhostType ghost_type = _not_ghost, ElementKind kind = _ek_regular) const { return connectivities.firstType(dim, ghost_type, kind); } inline type_iterator lastType(UInt dim = _all_dimensions, GhostType ghost_type = _not_ghost, ElementKind kind = _ek_regular) const { return connectivities.lastType(dim, ghost_type, kind); } AKANTU_GET_MACRO(ElementSynchronizer, *element_synchronizer, const ElementSynchronizer &); AKANTU_GET_MACRO_NOT_CONST(ElementSynchronizer, *element_synchronizer, ElementSynchronizer &); AKANTU_GET_MACRO(NodeSynchronizer, *node_synchronizer, const NodeSynchronizer &); AKANTU_GET_MACRO_NOT_CONST(NodeSynchronizer, *node_synchronizer, NodeSynchronizer &); // AKANTU_GET_MACRO_NOT_CONST(Communicator, *communicator, StaticCommunicator // &); #ifndef SWIG AKANTU_GET_MACRO(Communicator, *communicator, const auto &); AKANTU_GET_MACRO_NOT_CONST(Communicator, *communicator, auto &); #endif /* ------------------------------------------------------------------------ */ /* Private methods for friends */ /* ------------------------------------------------------------------------ */ private: friend class MeshAccessor; AKANTU_GET_MACRO(NodesPointer, *nodes, Array &); /// get a pointer to the nodes_global_ids Array and create it if /// necessary inline Array & getNodesGlobalIdsPointer(); /// get a pointer to the nodes_type Array and create it if necessary inline Array & getNodesTypePointer(); /// get a pointer to the connectivity Array for the given type and create it /// if necessary inline Array & getConnectivityPointer(const ElementType & type, const GhostType & ghost_type = _not_ghost); /// get the ghost element counter inline Array & getGhostsCounters(const ElementType & type, const GhostType & ghost_type = _ghost) { AKANTU_DEBUG_ASSERT(ghost_type != _not_ghost, "No ghost counter for _not_ghost elements"); return ghosts_counters(type, ghost_type); } /// get a pointer to the element_to_subelement Array for the given type and /// create it if necessary inline Array> & getElementToSubelementPointer(const ElementType & type, const GhostType & ghost_type = _not_ghost); /// get a pointer to the subelement_to_element Array for the given type and /// create it if necessary inline Array & getSubelementToElementPointer(const ElementType & type, const GhostType & ghost_type = _not_ghost); AKANTU_GET_MACRO_NOT_CONST(MeshData, mesh_data, MeshData &); /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ private: /// array of the nodes coordinates std::shared_ptr> nodes; /// global node ids std::unique_ptr> nodes_global_ids; /// node type, -3 pure ghost, -2 master for the node, -1 normal node, i in /// [0-N] slave node and master is proc i Array nodes_type; /// global number of nodes; UInt 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; /// map to normals for all class of elements present in this mesh ElementTypeMapArray normals; /// list of all existing types in the mesh // ConnectivityTypeList type_set; /// the spatial dimension of this mesh UInt spatial_dimension{0}; /// types offsets // Array types_offsets; // /// list of all existing types in the mesh // ConnectivityTypeList ghost_type_set; // /// ghost types offsets // Array ghost_types_offsets; /// min of coordinates Vector lower_bounds; /// max of coordinates Vector upper_bounds; /// size covered by the mesh on each direction Vector size; /// local min of coordinates Vector local_lower_bounds; /// local max of coordinates Vector local_upper_bounds; /// 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}; /// Communicator on which mesh is distributed Communicator * communicator; /// Element synchronizer std::unique_ptr element_synchronizer; /// Node synchronizer std::unique_ptr node_synchronizer; using NodesToElements = std::vector>>; /// This info is stored to simplify the dynamic changes NodesToElements nodes_to_elements; }; /// standard output stream operator inline std::ostream & operator<<(std::ostream & stream, const Mesh & _this) { _this.printself(stream); return stream; } } // namespace akantu /* -------------------------------------------------------------------------- */ /* Inline functions */ /* -------------------------------------------------------------------------- */ #include "element_type_map_tmpl.hh" #include "mesh_inline_impl.cc" #endif /* __AKANTU_MESH_HH__ */ diff --git a/src/mesh_utils/cohesive_element_inserter.cc b/src/mesh_utils/cohesive_element_inserter.cc index 12bf9bdd4..7be630cf2 100644 --- a/src/mesh_utils/cohesive_element_inserter.cc +++ b/src/mesh_utils/cohesive_element_inserter.cc @@ -1,278 +1,281 @@ /** * @file cohesive_element_inserter.cc * * @author Marco Vocialta * * @date creation: Wed Dec 04 2013 * @date last modification: Sun Oct 04 2015 * * @brief Cohesive element inserter functions * * @section LICENSE * * Copyright (©) 2014, 2015 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "cohesive_element_inserter.hh" #include "communicator.hh" #include "element_group.hh" #include "global_ids_updater.hh" #include "mesh_iterators.hh" /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ namespace akantu { CohesiveElementInserter::CohesiveElementInserter(Mesh & mesh, const ID & id) : Parsable(ParserType::_cohesive_inserter), id(id), mesh(mesh), mesh_facets(mesh.initMeshFacets()), insertion_facets("insertion_facets", id), insertion_limits(mesh.getSpatialDimension(), 2), check_facets("check_facets", id) { this->registerParam("cohesive_surfaces", physical_groups, _pat_parsable, "List of groups to consider for insertion"); this->registerParam("bounding_box", insertion_limits, _pat_parsable, "Global limit for insertion"); UInt spatial_dimension = mesh.getSpatialDimension(); MeshUtils::resetFacetToDouble(mesh_facets); /// init insertion limits for (UInt dim = 0; dim < spatial_dimension; ++dim) { insertion_limits(dim, 0) = std::numeric_limits::max() * Real(-1.); insertion_limits(dim, 1) = std::numeric_limits::max(); } insertion_facets.initialize(mesh_facets, _spatial_dimension = spatial_dimension - 1, _with_nb_element = true, _default_value = false); } /* -------------------------------------------------------------------------- */ CohesiveElementInserter::~CohesiveElementInserter() = default; /* -------------------------------------------------------------------------- */ void CohesiveElementInserter::parseSection(const ParserSection & section) { Parsable::parseSection(section); if (is_extrinsic) limitCheckFacets(this->check_facets); } /* -------------------------------------------------------------------------- */ void CohesiveElementInserter::limitCheckFacets() { limitCheckFacets(this->check_facets); } /* -------------------------------------------------------------------------- */ void CohesiveElementInserter::setLimit(SpacialDirection axis, Real first_limit, Real second_limit) { AKANTU_DEBUG_ASSERT( axis < mesh.getSpatialDimension(), "You are trying to limit insertion in a direction that doesn't exist"); insertion_limits(axis, 0) = std::min(first_limit, second_limit); insertion_limits(axis, 1) = std::max(first_limit, second_limit); } /* -------------------------------------------------------------------------- */ UInt CohesiveElementInserter::insertIntrinsicElements() { limitCheckFacets(insertion_facets); return insertElements(); } /* -------------------------------------------------------------------------- */ void CohesiveElementInserter::limitCheckFacets( ElementTypeMapArray & check_facets) { AKANTU_DEBUG_IN(); UInt spatial_dimension = mesh.getSpatialDimension(); check_facets.initialize(mesh_facets, _spatial_dimension = spatial_dimension - 1, _with_nb_element = true, _default_value = true); check_facets.set(true); // remove the pure ghost elements for_each_element( mesh_facets, [&](auto && facet) { const auto & element_to_facet = mesh_facets.getElementToSubelement( facet.type, facet.ghost_type)(facet.element); auto & left = element_to_facet[0]; auto & right = element_to_facet[1]; if (right == ElementNull || (left.ghost_type == _ghost && right.ghost_type == _ghost)) { check_facets(facet) = false; return; } if (left.kind() == _ek_cohesive or right.kind() == _ek_cohesive) { check_facets(facet) = false; } }, _spatial_dimension = spatial_dimension - 1); Vector bary_facet(spatial_dimension); // set the limits to the bounding box for_each_element(mesh_facets, [&](auto && facet) { auto & need_check = check_facets(facet); if (not need_check) return; mesh_facets.getBarycenter(facet, bary_facet); UInt coord_in_limit = 0; while (coord_in_limit < spatial_dimension && bary_facet(coord_in_limit) > insertion_limits(coord_in_limit, 0) && bary_facet(coord_in_limit) < insertion_limits(coord_in_limit, 1)) ++coord_in_limit; if (coord_in_limit != spatial_dimension) need_check = false; }, _spatial_dimension = spatial_dimension - 1); if (not mesh_facets.hasData("physical_names")) { + AKANTU_DEBUG_ASSERT( + physical_groups.size() == 0, + "No physical names in the mesh but insertion limited to a group"); AKANTU_DEBUG_OUT(); return; } const auto & physical_ids = mesh_facets.getData("physical_names"); // set the limits to the physical surfaces for_each_element(mesh_facets, - [&](auto && facet) { - auto & need_check = check_facets(facet); - if (not need_check) - return; - - const auto & physical_id = physical_ids(facet); - auto it = find(physical_groups.begin(), - physical_groups.end(), physical_id); - - need_check = (it != physical_groups.end()); - }, - _spatial_dimension = spatial_dimension - 1); + [&](auto && facet) { + auto & need_check = check_facets(facet); + if (not need_check) + return; + + const auto & physical_id = physical_ids(facet); + auto it = find(physical_groups.begin(), + physical_groups.end(), physical_id); + + need_check = (it != physical_groups.end()); + }, + _spatial_dimension = spatial_dimension - 1); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ UInt CohesiveElementInserter::insertElements(bool only_double_facets) { CohesiveNewNodesEvent node_event; NewElementsEvent element_event; Array new_pairs(0, 2); UInt nb_new_elements = MeshUtils::insertCohesiveElements( mesh, mesh_facets, insertion_facets, new_pairs, element_event.getList(), only_double_facets); UInt nb_new_nodes = new_pairs.size(); node_event.getList().reserve(nb_new_nodes); node_event.getOldNodesList().reserve(nb_new_nodes); for (UInt n = 0; n < nb_new_nodes; ++n) { node_event.getList().push_back(new_pairs(n, 1)); node_event.getOldNodesList().push_back(new_pairs(n, 0)); } if (mesh.isDistributed()) { /// update nodes type updateNodesType(mesh, node_event); updateNodesType(mesh_facets, node_event); /// update global ids nb_new_nodes = this->updateGlobalIDs(node_event); /// compute total number of new elements const auto & comm = mesh.getCommunicator(); comm.allReduce(nb_new_elements, SynchronizerOperation::_sum); } if (nb_new_nodes > 0) mesh.sendEvent(node_event); if (nb_new_elements > 0) { updateInsertionFacets(); // mesh.updateTypesOffsets(_not_ghost); mesh.sendEvent(element_event); MeshUtils::resetFacetToDouble(mesh_facets); } if (mesh.isDistributed()) { /// update global ids this->synchronizeGlobalIDs(node_event); } return nb_new_elements; } /* -------------------------------------------------------------------------- */ void CohesiveElementInserter::updateInsertionFacets() { AKANTU_DEBUG_IN(); UInt spatial_dimension = mesh.getSpatialDimension(); for (auto && facet_gt : ghost_types) { for (auto && facet_type : mesh_facets.elementTypes(spatial_dimension - 1, facet_gt)) { auto & ins_facets = insertion_facets(facet_type, facet_gt); // this is the intrinsic case if (not is_extrinsic) continue; auto & f_check = check_facets(facet_type, facet_gt); for (auto && pair : zip(ins_facets, f_check)) { bool & ins = std::get<0>(pair); bool & check = std::get<1>(pair); if (ins) ins = check = false; } } } // resize for the newly added facets insertion_facets.initialize(mesh_facets, _spatial_dimension = spatial_dimension - 1, _with_nb_element = true, _default_value = false); // resize for the newly added facets if (is_extrinsic) { check_facets.initialize(mesh_facets, _spatial_dimension = spatial_dimension - 1, _with_nb_element = true, _default_value = false); } else { insertion_facets.set(false); } AKANTU_DEBUG_OUT(); } } // namespace akantu diff --git a/src/mesh_utils/mesh_utils.cc b/src/mesh_utils/mesh_utils.cc index 1cb298d0d..93f8e8090 100644 --- a/src/mesh_utils/mesh_utils.cc +++ b/src/mesh_utils/mesh_utils.cc @@ -1,2172 +1,2173 @@ /** * @file mesh_utils.cc * * @author Guillaume Anciaux * @author Dana Christen * @author David Simon Kammer * @author Nicolas Richart * @author Leonardo Snozzi * @author Marco Vocialta * * @date creation: Fri Aug 20 2010 * @date last modification: Fri Jan 22 2016 * * @brief All mesh utils necessary for various tasks * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "mesh_utils.hh" #include "element_synchronizer.hh" #include "fe_engine.hh" #include "mesh_accessor.hh" /* -------------------------------------------------------------------------- */ #include #include #include #include /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ void MeshUtils::buildNode2Elements(const Mesh & mesh, CSR & node_to_elem, UInt spatial_dimension) { AKANTU_DEBUG_IN(); if (spatial_dimension == _all_dimensions) spatial_dimension = mesh.getSpatialDimension(); /// count number of occurrence of each node UInt nb_nodes = mesh.getNbNodes(); /// array for the node-element list node_to_elem.resizeRows(nb_nodes); node_to_elem.clearRows(); AKANTU_DEBUG_ASSERT( mesh.firstType(spatial_dimension) != mesh.lastType(spatial_dimension), "Some elements must be found in right dimension to compute facets!"); for (ghost_type_t::iterator gt = ghost_type_t::begin(); gt != ghost_type_t::end(); ++gt) { Mesh::type_iterator first = mesh.firstType(spatial_dimension, *gt, _ek_not_defined); Mesh::type_iterator last = mesh.lastType(spatial_dimension, *gt, _ek_not_defined); for (; first != last; ++first) { ElementType type = *first; UInt nb_element = mesh.getNbElement(type, *gt); Array::const_iterator> conn_it = mesh.getConnectivity(type, *gt).begin( Mesh::getNbNodesPerElement(type)); for (UInt el = 0; el < nb_element; ++el, ++conn_it) for (UInt n = 0; n < conn_it->size(); ++n) ++node_to_elem.rowOffset((*conn_it)(n)); } } node_to_elem.countToCSR(); node_to_elem.resizeCols(); /// rearrange element to get the node-element list Element e; node_to_elem.beginInsertions(); for (ghost_type_t::iterator gt = ghost_type_t::begin(); gt != ghost_type_t::end(); ++gt) { Mesh::type_iterator first = mesh.firstType(spatial_dimension, *gt, _ek_not_defined); Mesh::type_iterator last = mesh.lastType(spatial_dimension, *gt, _ek_not_defined); e.ghost_type = *gt; for (; first != last; ++first) { ElementType type = *first; e.type = type; UInt nb_element = mesh.getNbElement(type, *gt); Array::const_iterator> conn_it = mesh.getConnectivity(type, *gt).begin( Mesh::getNbNodesPerElement(type)); for (UInt el = 0; el < nb_element; ++el, ++conn_it) { e.element = el; for (UInt n = 0; n < conn_it->size(); ++n) node_to_elem.insertInRow((*conn_it)(n), e); } } } node_to_elem.endInsertions(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ /** * This function should disappear in the future (used in mesh partitioning) */ // void MeshUtils::buildNode2Elements(const Mesh & mesh, CSR & // node_to_elem, // UInt spatial_dimension) { // AKANTU_DEBUG_IN(); // if (spatial_dimension == _all_dimensions) // spatial_dimension = mesh.getSpatialDimension(); // UInt nb_nodes = mesh.getNbNodes(); // const Mesh::ConnectivityTypeList & type_list = // mesh.getConnectivityTypeList(); Mesh::ConnectivityTypeList::const_iterator // it; // UInt nb_types = type_list.size(); // UInt nb_good_types = 0; // Vector nb_nodes_per_element(nb_types); // UInt ** conn_val = new UInt *[nb_types]; // Vector nb_element(nb_types); // for (it = type_list.begin(); it != type_list.end(); ++it) { // ElementType type = *it; // if (Mesh::getSpatialDimension(type) != spatial_dimension) // continue; // nb_nodes_per_element[nb_good_types] = Mesh::getNbNodesPerElement(type); // conn_val[nb_good_types] = mesh.getConnectivity(type, // _not_ghost).storage(); nb_element[nb_good_types] = // mesh.getConnectivity(type, _not_ghost).size(); // nb_good_types++; // } // AKANTU_DEBUG_ASSERT( // nb_good_types != 0, // "Some elements must be found in right dimension to compute facets!"); // /// array for the node-element list // node_to_elem.resizeRows(nb_nodes); // node_to_elem.clearRows(); // /// count number of occurrence of each node // for (UInt t = 0; t < nb_good_types; ++t) { // for (UInt el = 0; el < nb_element[t]; ++el) { // UInt el_offset = el * nb_nodes_per_element[t]; // for (UInt n = 0; n < nb_nodes_per_element[t]; ++n) { // ++node_to_elem.rowOffset(conn_val[t][el_offset + n]); // } // } // } // node_to_elem.countToCSR(); // node_to_elem.resizeCols(); // node_to_elem.beginInsertions(); // /// rearrange element to get the node-element list // for (UInt t = 0, linearized_el = 0; t < nb_good_types; ++t) // for (UInt el = 0; el < nb_element[t]; ++el, ++linearized_el) { // UInt el_offset = el * nb_nodes_per_element[t]; // for (UInt n = 0; n < nb_nodes_per_element[t]; ++n) // node_to_elem.insertInRow(conn_val[t][el_offset + n], linearized_el); // } // node_to_elem.endInsertions(); // delete[] conn_val; // AKANTU_DEBUG_OUT(); // } /* -------------------------------------------------------------------------- */ void MeshUtils::buildNode2ElementsElementTypeMap(const Mesh & mesh, CSR & node_to_elem, const ElementType & type, const GhostType & ghost_type) { AKANTU_DEBUG_IN(); UInt nb_nodes = mesh.getNbNodes(); UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type); UInt nb_elements = mesh.getConnectivity(type, ghost_type).size(); UInt * conn_val = mesh.getConnectivity(type, ghost_type).storage(); /// array for the node-element list node_to_elem.resizeRows(nb_nodes); node_to_elem.clearRows(); /// count number of occurrence of each node for (UInt el = 0; el < nb_elements; ++el) { UInt el_offset = el * nb_nodes_per_element; for (UInt n = 0; n < nb_nodes_per_element; ++n) ++node_to_elem.rowOffset(conn_val[el_offset + n]); } /// convert the occurrence array in a csr one node_to_elem.countToCSR(); node_to_elem.resizeCols(); node_to_elem.beginInsertions(); /// save the element index in the node-element list for (UInt el = 0; el < nb_elements; ++el) { UInt el_offset = el * nb_nodes_per_element; for (UInt n = 0; n < nb_nodes_per_element; ++n) { node_to_elem.insertInRow(conn_val[el_offset + n], el); } } node_to_elem.endInsertions(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void MeshUtils::buildFacets(Mesh & mesh) { AKANTU_DEBUG_IN(); UInt spatial_dimension = mesh.getSpatialDimension(); for (ghost_type_t::iterator gt = ghost_type_t::begin(); gt != ghost_type_t::end(); ++gt) { GhostType gt_facet = *gt; Mesh::type_iterator it = mesh.firstType(spatial_dimension - 1, gt_facet); Mesh::type_iterator end = mesh.lastType(spatial_dimension - 1, gt_facet); for (; it != end; ++it) { mesh.getConnectivity(*it, *gt).resize(0); // \todo inform the mesh event handler } } buildFacetsDimension(mesh, mesh, true, spatial_dimension); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void MeshUtils::buildAllFacets(const Mesh & mesh, Mesh & mesh_facets, UInt to_dimension) { AKANTU_DEBUG_IN(); UInt spatial_dimension = mesh.getSpatialDimension(); buildAllFacets(mesh, mesh_facets, spatial_dimension, to_dimension); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void MeshUtils::buildAllFacets(const Mesh & mesh, Mesh & mesh_facets, UInt from_dimension, UInt to_dimension) { AKANTU_DEBUG_IN(); AKANTU_DEBUG_ASSERT( mesh_facets.isMeshFacets(), "The mesh_facets should be initialized with initMeshFacets"); /// generate facets buildFacetsDimension(mesh, mesh_facets, false, from_dimension); /// copy nodes type MeshAccessor mesh_accessor_facets(mesh_facets); mesh_accessor_facets.getNodesType().copy(mesh.getNodesType()); /// sort facets and generate sub-facets for (UInt i = from_dimension - 1; i > to_dimension; --i) { buildFacetsDimension(mesh_facets, mesh_facets, false, i); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void MeshUtils::buildFacetsDimension(const Mesh & mesh, Mesh & mesh_facets, bool boundary_only, UInt dimension) { AKANTU_DEBUG_IN(); // save the current parent of mesh_facets and set it temporarly to mesh since // mesh is the one containing the elements for which mesh_facets has the // sub-elements // example: if the function is called with mesh = mesh_facets const Mesh * mesh_facets_parent = nullptr; try { mesh_facets_parent = &mesh_facets.getMeshParent(); } catch (...) { } mesh_facets.defineMeshParent(mesh); MeshAccessor mesh_accessor(mesh_facets); UInt spatial_dimension = mesh.getSpatialDimension(); const Array & mesh_facets_nodes = mesh_facets.getNodes(); const Array::const_vector_iterator 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; // init the SubelementToElement data to improve performance for (auto && ghost_type : ghost_types) { for (auto && type : mesh.elementTypes(dimension, ghost_type)) { mesh_accessor.getSubelementToElement(type, ghost_type); auto facet_types = mesh.getAllFacetTypes(type); for (auto && ft : arange(facet_types.size())) { auto facet_type = facet_types(ft); mesh_accessor.getElementToSubelement(facet_type, ghost_type); mesh_accessor.getConnectivity(facet_type, ghost_type); } } } const ElementSynchronizer * synchronizer = nullptr; if (mesh.isDistributed()) { synchronizer = &(mesh.getElementSynchronizer()); } Element current_element; for (auto && ghost_type : ghost_types) { GhostType facet_ghost_type = ghost_type; current_element.ghost_type = ghost_type; for (auto && type : mesh.elementTypes(dimension, ghost_type)) { auto facet_types = mesh.getAllFacetTypes(type); current_element.type = type; for (auto && ft : arange(facet_types.size())) { auto facet_type = facet_types(ft); auto nb_element = mesh.getNbElement(type, ghost_type); auto element_to_subelement = &mesh_facets.getElementToSubelement(facet_type, ghost_type); auto connectivity_facets = &mesh_facets.getConnectivity(facet_type, ghost_type); auto nb_facet_per_element = mesh.getNbFacetsPerElement(type, ft); const auto & element_connectivity = mesh.getConnectivity(type, ghost_type); Matrix facet_local_connectivity( mesh.getFacetLocalConnectivity(type, ft)); auto nb_nodes_per_facet = connectivity_facets->getNbComponent(); Vector facet(nb_nodes_per_facet); for (UInt el = 0; el < nb_element; ++el) { current_element.element = el; for (UInt f = 0; f < nb_facet_per_element; ++f) { for (UInt n = 0; n < nb_nodes_per_facet; ++n) facet(n) = element_connectivity(el, facet_local_connectivity(f, n)); UInt first_node_nb_elements = node_to_elem.getNbCols(facet(0)); counter.resize(first_node_nb_elements); counter.clear(); // loop over the other nodes to search intersecting elements, // which are the elements that share another node with the // starting element after first_node UInt local_el = 0; auto first_node_elements = node_to_elem.begin(facet(0)); auto first_node_elements_end = node_to_elem.end(facet(0)); for (; first_node_elements != first_node_elements_end; ++first_node_elements, ++local_el) { for (UInt n = 1; n < nb_nodes_per_facet; ++n) { auto node_elements_begin = node_to_elem.begin(facet(n)); auto node_elements_end = node_to_elem.end(facet(n)); counter(local_el) += std::count(node_elements_begin, node_elements_end, *first_node_elements); } } // counting the number of elements connected to the facets and // taking the minimum element number, because the facet should // be inserted just once UInt nb_element_connected_to_facet = 0; Element minimum_el = ElementNull; connected_elements.clear(); for (UInt el_f = 0; el_f < first_node_nb_elements; el_f++) { Element real_el = node_to_elem(facet(0), el_f); if (not(counter(el_f) == nb_nodes_per_facet - 1)) continue; ++nb_element_connected_to_facet; minimum_el = std::min(minimum_el, real_el); connected_elements.push_back(real_el); } if (minimum_el != current_element) continue; bool full_ghost_facet = false; UInt n = 0; while (n < nb_nodes_per_facet && 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.push_back(current_element); if (nb_element_connected_to_facet == 1) { /// boundary facet elements.push_back(ElementNull); } else if (nb_element_connected_to_facet == 2) { /// internal facet elements.push_back(connected_elements[1]); /// check if facet is in between ghost and normal /// elements: if it's the case, the facet is either /// ghost or not ghost. The criterion to decide this /// is arbitrary. It was chosen to check the processor /// id (prank) of the two neighboring elements. If /// prank of the ghost element is lower than prank of /// the normal one, the facet is not ghost, otherwise /// it's ghost GhostType gt[2] = {_not_ghost, _not_ghost}; for (UInt el = 0; el < connected_elements.size(); ++el) gt[el] = connected_elements[el].ghost_type; if (gt[0] != gt[1] and (gt[0] == _not_ghost or gt[1] == _not_ghost)) { UInt prank[2]; for (UInt el = 0; el < 2; ++el) { prank[el] = synchronizer->getRank(connected_elements[el]); } // ugly trick from Marco detected :P bool ghost_one = (gt[0] != _ghost); if (prank[ghost_one] > prank[!ghost_one]) facet_ghost_type = _not_ghost; else facet_ghost_type = _ghost; connectivity_facets = &mesh_facets.getConnectivity(facet_type, facet_ghost_type); element_to_subelement = &mesh_facets.getElementToSubelement( facet_type, facet_ghost_type); } } else { /// facet of facet for (UInt i = 1; i < nb_element_connected_to_facet; ++i) { elements.push_back(connected_elements[i]); } } element_to_subelement->push_back(elements); connectivity_facets->push_back(facet); /// current facet index UInt current_facet = connectivity_facets->size() - 1; /// loop on every element connected to current facet and /// insert current facet in the first free spot of the /// subelement_to_element vector for (UInt elem = 0; elem < elements.size(); ++elem) { Element loc_el = elements[elem]; if (loc_el.type == _not_defined) continue; Array & subelement_to_element = mesh_facets.getSubelementToElement(loc_el.type, loc_el.ghost_type); UInt nb_facet_per_loc_element = subelement_to_element.getNbComponent(); for (UInt f_in = 0; f_in < nb_facet_per_loc_element; ++f_in) { auto & el = subelement_to_element(loc_el.element, f_in); if (el.type != _not_defined) continue; el.type = facet_type; el.element = current_facet; el.ghost_type = facet_ghost_type; 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); } } } } } } // restore the parent of mesh_facet if (mesh_facets_parent) mesh_facets.defineMeshParent(*mesh_facets_parent); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void MeshUtils::renumberMeshNodes(Mesh & mesh, Array & local_connectivities, UInt nb_local_element, UInt nb_ghost_element, ElementType type, Array & old_nodes_numbers) { AKANTU_DEBUG_IN(); UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type); std::map renumbering_map; for (UInt i = 0; i < old_nodes_numbers.size(); ++i) { renumbering_map[old_nodes_numbers(i)] = i; } /// renumber the nodes renumberNodesInConnectivity(local_connectivities, (nb_local_element + nb_ghost_element) * nb_nodes_per_element, renumbering_map); old_nodes_numbers.resize(renumbering_map.size()); for (auto & renumber_pair : renumbering_map) { old_nodes_numbers(renumber_pair.second) = renumber_pair.first; } renumbering_map.clear(); MeshAccessor mesh_accessor(mesh); /// copy the renumbered connectivity to the right place auto & local_conn = mesh_accessor.getConnectivity(type); local_conn.resize(nb_local_element); memcpy(local_conn.storage(), local_connectivities.storage(), nb_local_element * nb_nodes_per_element * sizeof(UInt)); auto & ghost_conn = mesh_accessor.getConnectivity(type, _ghost); ghost_conn.resize(nb_ghost_element); std::memcpy(ghost_conn.storage(), local_connectivities.storage() + nb_local_element * nb_nodes_per_element, nb_ghost_element * nb_nodes_per_element * sizeof(UInt)); auto & ghost_counter = mesh_accessor.getGhostsCounters(type, _ghost); ghost_counter.resize(nb_ghost_element, 1); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void MeshUtils::renumberNodesInConnectivity( Array & list_nodes, UInt nb_nodes, std::map & renumbering_map) { AKANTU_DEBUG_IN(); UInt * connectivity = list_nodes.storage(); UInt new_node_num = renumbering_map.size(); for (UInt n = 0; n < nb_nodes; ++n, ++connectivity) { UInt & node = *connectivity; auto it = renumbering_map.find(node); if (it == renumbering_map.end()) { UInt old_node = node; renumbering_map[old_node] = new_node_num; node = new_node_num; ++new_node_num; } else { node = it->second; } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void MeshUtils::purifyMesh(Mesh & mesh) { AKANTU_DEBUG_IN(); std::map renumbering_map; RemovedNodesEvent remove_nodes(mesh); Array & nodes_removed = remove_nodes.getList(); for (UInt gt = _not_ghost; gt <= _ghost; ++gt) { auto ghost_type = (GhostType)gt; Mesh::type_iterator it = mesh.firstType(_all_dimensions, ghost_type, _ek_not_defined); Mesh::type_iterator end = mesh.lastType(_all_dimensions, ghost_type, _ek_not_defined); for (; it != end; ++it) { ElementType type(*it); UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type); Array & connectivity = mesh.getConnectivity(type, ghost_type); UInt nb_element(connectivity.size()); renumberNodesInConnectivity( connectivity, nb_element * nb_nodes_per_element, renumbering_map); } } Array & new_numbering = remove_nodes.getNewNumbering(); std::fill(new_numbering.begin(), new_numbering.end(), UInt(-1)); auto it = renumbering_map.begin(); auto end = renumbering_map.end(); for (; it != end; ++it) { new_numbering(it->first) = it->second; } for (UInt i = 0; i < new_numbering.size(); ++i) { if (new_numbering(i) == UInt(-1)) nodes_removed.push_back(i); } mesh.sendEvent(remove_nodes); AKANTU_DEBUG_OUT(); } #if defined(AKANTU_COHESIVE_ELEMENT) /* -------------------------------------------------------------------------- */ UInt MeshUtils::insertCohesiveElements( Mesh & mesh, Mesh & mesh_facets, const ElementTypeMapArray & facet_insertion, Array & doubled_nodes, Array & new_elements, bool only_double_facets) { UInt spatial_dimension = mesh.getSpatialDimension(); UInt elements_to_insert = updateFacetToDouble(mesh_facets, facet_insertion); if (elements_to_insert > 0) { if (spatial_dimension == 1) { doublePointFacet(mesh, mesh_facets, doubled_nodes); } else { doubleFacet(mesh, mesh_facets, spatial_dimension - 1, doubled_nodes, true); findSubfacetToDouble(mesh, mesh_facets); if (spatial_dimension == 2) { doubleSubfacet<2>(mesh, mesh_facets, doubled_nodes); } else if (spatial_dimension == 3) { doubleFacet(mesh, mesh_facets, 1, doubled_nodes, false); findSubfacetToDouble(mesh, mesh_facets); doubleSubfacet<3>(mesh, mesh_facets, doubled_nodes); } } if (!only_double_facets) updateCohesiveData(mesh, mesh_facets, new_elements); } return elements_to_insert; } #endif /* -------------------------------------------------------------------------- */ void MeshUtils::doubleNodes(Mesh & mesh, const std::vector & old_nodes, Array & doubled_nodes) { AKANTU_DEBUG_IN(); Array & position = mesh.getNodes(); UInt spatial_dimension = mesh.getSpatialDimension(); UInt old_nb_nodes = position.size(); UInt new_nb_nodes = old_nb_nodes + old_nodes.size(); UInt old_nb_doubled_nodes = doubled_nodes.size(); UInt new_nb_doubled_nodes = old_nb_doubled_nodes + old_nodes.size(); position.resize(new_nb_nodes); doubled_nodes.resize(new_nb_doubled_nodes); Array::iterator> position_begin = position.begin(spatial_dimension); for (UInt n = 0; n < old_nodes.size(); ++n) { UInt new_node = old_nb_nodes + n; /// store doubled nodes doubled_nodes(old_nb_doubled_nodes + n, 0) = old_nodes[n]; doubled_nodes(old_nb_doubled_nodes + n, 1) = new_node; /// update position std::copy(position_begin + old_nodes[n], position_begin + old_nodes[n] + 1, position_begin + new_node); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void MeshUtils::doubleFacet(Mesh & mesh, Mesh & mesh_facets, UInt facet_dimension, Array & doubled_nodes, bool facet_mode) { AKANTU_DEBUG_IN(); for (ghost_type_t::iterator gt = ghost_type_t::begin(); gt != ghost_type_t::end(); ++gt) { GhostType gt_facet = *gt; Mesh::type_iterator it = mesh_facets.firstType(facet_dimension, gt_facet); Mesh::type_iterator end = mesh_facets.lastType(facet_dimension, gt_facet); for (; it != end; ++it) { ElementType type_facet = *it; Array & f_to_double = mesh_facets.getData("facet_to_double", type_facet, gt_facet); UInt nb_facet_to_double = f_to_double.size(); if (nb_facet_to_double == 0) continue; ElementType type_subfacet = Mesh::getFacetType(type_facet); const UInt nb_subfacet_per_facet = Mesh::getNbFacetsPerElement(type_facet); GhostType gt_subfacet = _casper; Array> * f_to_subfacet = nullptr; Array & subfacet_to_facet = mesh_facets.getSubelementToElement(type_facet, gt_facet); Array & conn_facet = mesh_facets.getConnectivity(type_facet, gt_facet); UInt nb_nodes_per_facet = conn_facet.getNbComponent(); UInt old_nb_facet = conn_facet.size(); UInt new_nb_facet = old_nb_facet + nb_facet_to_double; conn_facet.resize(new_nb_facet); subfacet_to_facet.resize(new_nb_facet); UInt new_facet = old_nb_facet - 1; Element new_facet_el{type_facet, 0, gt_facet}; Array::iterator> subfacet_to_facet_begin = subfacet_to_facet.begin(nb_subfacet_per_facet); Array::iterator> conn_facet_begin = conn_facet.begin(nb_nodes_per_facet); for (UInt facet = 0; facet < nb_facet_to_double; ++facet) { UInt old_facet = f_to_double(facet); ++new_facet; /// adding a new facet by copying original one /// copy connectivity in new facet std::copy(conn_facet_begin + old_facet, conn_facet_begin + old_facet + 1, conn_facet_begin + new_facet); /// update subfacet_to_facet std::copy(subfacet_to_facet_begin + old_facet, subfacet_to_facet_begin + old_facet + 1, subfacet_to_facet_begin + new_facet); new_facet_el.element = new_facet; /// loop on every subfacet for (UInt sf = 0; sf < nb_subfacet_per_facet; ++sf) { Element & subfacet = subfacet_to_facet(old_facet, sf); if (subfacet == ElementNull) continue; if (gt_subfacet != subfacet.ghost_type) { gt_subfacet = subfacet.ghost_type; f_to_subfacet = &mesh_facets.getElementToSubelement( type_subfacet, subfacet.ghost_type); } /// update facet_to_subfacet array (*f_to_subfacet)(subfacet.element).push_back(new_facet_el); } } /// update facet_to_subfacet and _segment_3 facets if any if (!facet_mode) { updateSubfacetToFacet(mesh_facets, type_facet, gt_facet, true); updateFacetToSubfacet(mesh_facets, type_facet, gt_facet, true); updateQuadraticSegments(mesh, mesh_facets, type_facet, gt_facet, doubled_nodes); } else updateQuadraticSegments(mesh, mesh_facets, type_facet, gt_facet, doubled_nodes); } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ UInt MeshUtils::updateFacetToDouble( Mesh & mesh_facets, const ElementTypeMapArray & facet_insertion) { AKANTU_DEBUG_IN(); UInt spatial_dimension = mesh_facets.getSpatialDimension(); UInt nb_facets_to_double = 0.; for (ghost_type_t::iterator gt = ghost_type_t::begin(); gt != ghost_type_t::end(); ++gt) { GhostType gt_facet = *gt; Mesh::type_iterator it = mesh_facets.firstType(spatial_dimension - 1, gt_facet); Mesh::type_iterator end = mesh_facets.lastType(spatial_dimension - 1, gt_facet); for (; it != end; ++it) { ElementType type_facet = *it; const Array & f_insertion = facet_insertion(type_facet, gt_facet); Array & f_to_double = mesh_facets.getData("facet_to_double", type_facet, gt_facet); Array> & element_to_facet = mesh_facets.getElementToSubelement(type_facet, gt_facet); ElementType el_type = _not_defined; GhostType el_gt = _casper; UInt nb_facet_per_element = 0; Element old_facet_el{type_facet, 0, gt_facet}; Array * facet_to_element = nullptr; for (UInt f = 0; f < f_insertion.size(); ++f) { if (f_insertion(f) == false) continue; ++nb_facets_to_double; if (element_to_facet(f)[1].type == _not_defined #if defined(AKANTU_COHESIVE_ELEMENT) || element_to_facet(f)[1].kind() == _ek_cohesive #endif ) { AKANTU_DEBUG_WARNING("attempt to double a facet on the boundary"); continue; } f_to_double.push_back(f); UInt new_facet = mesh_facets.getNbElement(type_facet, gt_facet) + f_to_double.size() - 1; old_facet_el.element = f; /// update facet_to_element vector Element & elem_to_update = element_to_facet(f)[1]; UInt el = elem_to_update.element; if (elem_to_update.ghost_type != el_gt || elem_to_update.type != el_type) { el_type = elem_to_update.type; el_gt = elem_to_update.ghost_type; facet_to_element = &mesh_facets.getSubelementToElement(el_type, el_gt); nb_facet_per_element = facet_to_element->getNbComponent(); } Element * f_update = std::find(facet_to_element->storage() + el * nb_facet_per_element, facet_to_element->storage() + el * nb_facet_per_element + nb_facet_per_element, old_facet_el); AKANTU_DEBUG_ASSERT( facet_to_element->storage() + el * nb_facet_per_element != facet_to_element->storage() + el * nb_facet_per_element + nb_facet_per_element, "Facet not found"); f_update->element = new_facet; /// update elements connected to facet std::vector first_facet_list = element_to_facet(f); element_to_facet.push_back(first_facet_list); /// set new and original facets as boundary facets element_to_facet(new_facet)[0] = element_to_facet(new_facet)[1]; element_to_facet(f)[1] = ElementNull; element_to_facet(new_facet)[1] = ElementNull; } } } AKANTU_DEBUG_OUT(); return nb_facets_to_double; } /* -------------------------------------------------------------------------- */ void MeshUtils::resetFacetToDouble(Mesh & mesh_facets) { AKANTU_DEBUG_IN(); for (UInt g = _not_ghost; g <= _ghost; ++g) { auto gt = (GhostType)g; Mesh::type_iterator it = mesh_facets.firstType(_all_dimensions, gt); Mesh::type_iterator end = mesh_facets.lastType(_all_dimensions, gt); for (; it != end; ++it) { ElementType type = *it; mesh_facets.getDataPointer("facet_to_double", type, gt, 1, false); mesh_facets.getDataPointer>( "facets_to_subfacet_double", type, gt, 1, false); mesh_facets.getDataPointer>( "elements_to_subfacet_double", type, gt, 1, false); mesh_facets.getDataPointer>( "subfacets_to_subsubfacet_double", type, gt, 1, false); } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void MeshUtils::findSubfacetToDouble(Mesh & mesh, Mesh & mesh_facets) { AKANTU_DEBUG_IN(); UInt spatial_dimension = mesh_facets.getSpatialDimension(); if (spatial_dimension == 1) return; for (ghost_type_t::iterator gt = ghost_type_t::begin(); gt != ghost_type_t::end(); ++gt) { GhostType gt_facet = *gt; Mesh::type_iterator it = mesh_facets.firstType(spatial_dimension - 1, gt_facet); Mesh::type_iterator end = mesh_facets.lastType(spatial_dimension - 1, gt_facet); for (; it != end; ++it) { ElementType type_facet = *it; Array & f_to_double = mesh_facets.getData("facet_to_double", type_facet, gt_facet); UInt nb_facet_to_double = f_to_double.size(); if (nb_facet_to_double == 0) continue; ElementType type_subfacet = Mesh::getFacetType(type_facet); GhostType gt_subfacet = _casper; ElementType type_subsubfacet = Mesh::getFacetType(type_subfacet); GhostType gt_subsubfacet = _casper; Array * conn_subfacet = nullptr; Array * sf_to_double = nullptr; Array> * sf_to_subfacet_double = nullptr; Array> * f_to_subfacet_double = nullptr; Array> * el_to_subfacet_double = nullptr; UInt nb_subfacet = Mesh::getNbFacetsPerElement(type_facet); UInt nb_subsubfacet; UInt nb_nodes_per_sf_el; if (subsubfacet_mode) { nb_nodes_per_sf_el = Mesh::getNbNodesPerElement(type_subsubfacet); nb_subsubfacet = Mesh::getNbFacetsPerElement(type_subfacet); } else nb_nodes_per_sf_el = Mesh::getNbNodesPerElement(type_subfacet); Array & subfacet_to_facet = mesh_facets.getSubelementToElement(type_facet, gt_facet); Array> & element_to_facet = mesh_facets.getElementToSubelement(type_facet, gt_facet); Array * subsubfacet_to_subfacet = nullptr; UInt old_nb_facet = subfacet_to_facet.size() - nb_facet_to_double; Element current_facet{type_facet, 0, gt_facet}; std::vector element_list; std::vector facet_list; std::vector * subfacet_list; if (subsubfacet_mode) subfacet_list = new std::vector; /// map to filter subfacets Array> * facet_to_subfacet = nullptr; /// this is used to make sure that both new and old facets are /// checked UInt facets[2]; /// loop on every facet for (UInt f_index = 0; f_index < 2; ++f_index) { for (UInt facet = 0; facet < nb_facet_to_double; ++facet) { facets[bool(f_index)] = f_to_double(facet); facets[!bool(f_index)] = old_nb_facet + facet; UInt old_facet = facets[0]; UInt new_facet = facets[1]; Element & starting_element = element_to_facet(new_facet)[0]; current_facet.element = old_facet; /// loop on every subfacet for (UInt sf = 0; sf < nb_subfacet; ++sf) { Element & subfacet = subfacet_to_facet(old_facet, sf); if (subfacet == ElementNull) continue; if (gt_subfacet != subfacet.ghost_type) { gt_subfacet = subfacet.ghost_type; if (subsubfacet_mode) { subsubfacet_to_subfacet = &mesh_facets.getSubelementToElement( type_subfacet, gt_subfacet); } else { conn_subfacet = &mesh_facets.getConnectivity(type_subfacet, gt_subfacet); sf_to_double = &mesh_facets.getData( "facet_to_double", type_subfacet, gt_subfacet); f_to_subfacet_double = &mesh_facets.getData>( "facets_to_subfacet_double", type_subfacet, gt_subfacet); el_to_subfacet_double = &mesh_facets.getData>( "elements_to_subfacet_double", type_subfacet, gt_subfacet); facet_to_subfacet = &mesh_facets.getElementToSubelement( type_subfacet, gt_subfacet); } } if (subsubfacet_mode) { /// loop on every subsubfacet for (UInt ssf = 0; ssf < nb_subsubfacet; ++ssf) { Element & subsubfacet = (*subsubfacet_to_subfacet)(subfacet.element, ssf); if (subsubfacet == ElementNull) continue; if (gt_subsubfacet != subsubfacet.ghost_type) { gt_subsubfacet = subsubfacet.ghost_type; conn_subfacet = &mesh_facets.getConnectivity(type_subsubfacet, gt_subsubfacet); sf_to_double = &mesh_facets.getData( "facet_to_double", type_subsubfacet, gt_subsubfacet); sf_to_subfacet_double = &mesh_facets.getData>( "subfacets_to_subsubfacet_double", type_subsubfacet, gt_subsubfacet); f_to_subfacet_double = &mesh_facets.getData>( "facets_to_subfacet_double", type_subsubfacet, gt_subsubfacet); el_to_subfacet_double = &mesh_facets.getData>( "elements_to_subfacet_double", type_subsubfacet, gt_subsubfacet); facet_to_subfacet = &mesh_facets.getElementToSubelement( type_subsubfacet, gt_subsubfacet); } UInt global_ssf = subsubfacet.element; Vector subsubfacet_connectivity( conn_subfacet->storage() + global_ssf * nb_nodes_per_sf_el, nb_nodes_per_sf_el); /// check if subsubfacet is to be doubled if (findElementsAroundSubfacet( mesh, mesh_facets, starting_element, current_facet, subsubfacet_connectivity, element_list, facet_list, subfacet_list) == false && removeElementsInVector(*subfacet_list, (*facet_to_subfacet)(global_ssf)) == false) { sf_to_double->push_back(global_ssf); sf_to_subfacet_double->push_back(*subfacet_list); f_to_subfacet_double->push_back(facet_list); el_to_subfacet_double->push_back(element_list); } } } else { const UInt global_sf = subfacet.element; Vector subfacet_connectivity( conn_subfacet->storage() + global_sf * nb_nodes_per_sf_el, nb_nodes_per_sf_el); /// check if subfacet is to be doubled if (findElementsAroundSubfacet( mesh, mesh_facets, starting_element, current_facet, subfacet_connectivity, element_list, facet_list) == false && removeElementsInVector( facet_list, (*facet_to_subfacet)(global_sf)) == false) { sf_to_double->push_back(global_sf); f_to_subfacet_double->push_back(facet_list); el_to_subfacet_double->push_back(element_list); } } } } } if (subsubfacet_mode) delete subfacet_list; } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ #if defined(AKANTU_COHESIVE_ELEMENT) void MeshUtils::updateCohesiveData(Mesh & mesh, Mesh & mesh_facets, Array & new_elements) { AKANTU_DEBUG_IN(); UInt spatial_dimension = mesh.getSpatialDimension(); bool third_dimension = spatial_dimension == 3; MeshAccessor mesh_facets_accessor(mesh_facets); for (auto gt_facet : ghost_types) { for (auto type_facet : mesh_facets.elementTypes(spatial_dimension - 1, gt_facet)) { Array & f_to_double = mesh_facets.getData("facet_to_double", type_facet, gt_facet); UInt nb_facet_to_double = f_to_double.size(); if (nb_facet_to_double == 0) continue; ElementType type_cohesive = FEEngine::getCohesiveElementType(type_facet); auto & facet_to_coh_element = mesh_facets_accessor.getSubelementToElement(type_cohesive, gt_facet); auto & conn_facet = mesh_facets.getConnectivity(type_facet, gt_facet); auto & conn_cohesive = mesh.getConnectivity(type_cohesive, gt_facet); UInt nb_nodes_per_facet = Mesh::getNbNodesPerElement(type_facet); Array> & element_to_facet = mesh_facets.getElementToSubelement(type_facet, gt_facet); UInt old_nb_cohesive_elements = conn_cohesive.size(); UInt new_nb_cohesive_elements = conn_cohesive.size() + nb_facet_to_double; UInt old_nb_facet = element_to_facet.size() - nb_facet_to_double; facet_to_coh_element.resize(new_nb_cohesive_elements); conn_cohesive.resize(new_nb_cohesive_elements); UInt new_elements_old_size = new_elements.size(); new_elements.resize(new_elements_old_size + nb_facet_to_double); Element c_element{type_cohesive, 0, gt_facet}; Element f_element{type_facet, 0, gt_facet}; UInt facets[2]; for (UInt facet = 0; facet < nb_facet_to_double; ++facet) { /// (in 3D cohesive elements connectivity is inverted) facets[third_dimension] = f_to_double(facet); facets[!third_dimension] = old_nb_facet + facet; UInt cohesive_element = old_nb_cohesive_elements + facet; /// store doubled facets f_element.element = facets[0]; facet_to_coh_element(cohesive_element, 0) = f_element; f_element.element = facets[1]; facet_to_coh_element(cohesive_element, 1) = f_element; /// modify cohesive elements' connectivity for (UInt n = 0; n < nb_nodes_per_facet; ++n) { conn_cohesive(cohesive_element, n) = conn_facet(facets[0], n); conn_cohesive(cohesive_element, n + nb_nodes_per_facet) = conn_facet(facets[1], n); } /// update element_to_facet vectors c_element.element = cohesive_element; element_to_facet(facets[0])[1] = c_element; element_to_facet(facets[1])[1] = c_element; /// add cohesive element to the element event list new_elements(new_elements_old_size + facet) = c_element; } } } AKANTU_DEBUG_OUT(); } #endif /* -------------------------------------------------------------------------- */ void MeshUtils::doublePointFacet(Mesh & mesh, Mesh & mesh_facets, Array & doubled_nodes) { AKANTU_DEBUG_IN(); UInt spatial_dimension = mesh.getSpatialDimension(); if (spatial_dimension != 1) return; Array & position = mesh.getNodes(); for (ghost_type_t::iterator gt = ghost_type_t::begin(); gt != ghost_type_t::end(); ++gt) { GhostType gt_facet = *gt; Mesh::type_iterator it = mesh_facets.firstType(spatial_dimension - 1, gt_facet); Mesh::type_iterator end = mesh_facets.lastType(spatial_dimension - 1, gt_facet); for (; it != end; ++it) { ElementType type_facet = *it; Array & conn_facet = mesh_facets.getConnectivity(type_facet, gt_facet); Array> & element_to_facet = mesh_facets.getElementToSubelement(type_facet, gt_facet); const Array & f_to_double = mesh_facets.getData("facet_to_double", type_facet, gt_facet); UInt nb_facet_to_double = f_to_double.size(); UInt old_nb_facet = element_to_facet.size() - nb_facet_to_double; UInt new_nb_facet = element_to_facet.size(); UInt old_nb_nodes = position.size(); UInt new_nb_nodes = old_nb_nodes + nb_facet_to_double; position.resize(new_nb_nodes); conn_facet.resize(new_nb_facet); UInt old_nb_doubled_nodes = doubled_nodes.size(); doubled_nodes.resize(old_nb_doubled_nodes + nb_facet_to_double); for (UInt facet = 0; facet < nb_facet_to_double; ++facet) { UInt old_facet = f_to_double(facet); UInt new_facet = old_nb_facet + facet; ElementType type = element_to_facet(new_facet)[0].type; UInt el = element_to_facet(new_facet)[0].element; GhostType gt = element_to_facet(new_facet)[0].ghost_type; UInt old_node = conn_facet(old_facet); UInt new_node = old_nb_nodes + facet; /// update position position(new_node) = position(old_node); conn_facet(new_facet) = new_node; Array & conn_segment = mesh.getConnectivity(type, gt); UInt nb_nodes_per_segment = conn_segment.getNbComponent(); /// update facet connectivity UInt i; for (i = 0; conn_segment(el, i) != old_node && i <= nb_nodes_per_segment; ++i) ; conn_segment(el, i) = new_node; doubled_nodes(old_nb_doubled_nodes + facet, 0) = old_node; doubled_nodes(old_nb_doubled_nodes + facet, 1) = new_node; } } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void MeshUtils::updateQuadraticSegments(Mesh & mesh, Mesh & mesh_facets, ElementType type_facet, GhostType gt_facet, Array & doubled_nodes) { AKANTU_DEBUG_IN(); if (type_facet != _segment_3) return; Array & f_to_double = mesh_facets.getData("facet_to_double", type_facet, gt_facet); UInt nb_facet_to_double = f_to_double.size(); UInt old_nb_facet = mesh_facets.getNbElement(type_facet, gt_facet) - nb_facet_to_double; Array & conn_facet = mesh_facets.getConnectivity(type_facet, gt_facet); Array> & element_to_facet = mesh_facets.getElementToSubelement(type_facet, gt_facet); /// this ones matter only for segments in 3D Array> * el_to_subfacet_double = nullptr; Array> * f_to_subfacet_double = nullptr; if (third_dim_segments) { el_to_subfacet_double = &mesh_facets.getData>( "elements_to_subfacet_double", type_facet, gt_facet); f_to_subfacet_double = &mesh_facets.getData>( "facets_to_subfacet_double", type_facet, gt_facet); } std::vector middle_nodes; for (UInt facet = 0; facet < nb_facet_to_double; ++facet) { UInt old_facet = f_to_double(facet); UInt node = conn_facet(old_facet, 2); if (!mesh.isPureGhostNode(node)) middle_nodes.push_back(node); } UInt n = doubled_nodes.size(); doubleNodes(mesh, middle_nodes, doubled_nodes); for (UInt facet = 0; facet < nb_facet_to_double; ++facet) { UInt old_facet = f_to_double(facet); UInt old_node = conn_facet(old_facet, 2); if (mesh.isPureGhostNode(old_node)) continue; UInt new_node = doubled_nodes(n, 1); UInt new_facet = old_nb_facet + facet; conn_facet(new_facet, 2) = new_node; if (third_dim_segments) { updateElementalConnectivity(mesh_facets, old_node, new_node, element_to_facet(new_facet)); updateElementalConnectivity(mesh, old_node, new_node, (*el_to_subfacet_double)(facet), &(*f_to_subfacet_double)(facet)); } else { updateElementalConnectivity(mesh, old_node, new_node, element_to_facet(new_facet)); } ++n; } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void MeshUtils::updateSubfacetToFacet(Mesh & mesh_facets, ElementType type_subfacet, GhostType gt_subfacet, bool facet_mode) { AKANTU_DEBUG_IN(); Array & sf_to_double = mesh_facets.getData("facet_to_double", type_subfacet, gt_subfacet); UInt nb_subfacet_to_double = sf_to_double.size(); /// update subfacet_to_facet vector ElementType type_facet = _not_defined; GhostType gt_facet = _casper; Array * subfacet_to_facet = nullptr; UInt nb_subfacet_per_facet = 0; UInt old_nb_subfacet = mesh_facets.getNbElement(type_subfacet, gt_subfacet) - nb_subfacet_to_double; Array> * facet_list = nullptr; if (facet_mode) facet_list = &mesh_facets.getData>( "facets_to_subfacet_double", type_subfacet, gt_subfacet); else facet_list = &mesh_facets.getData>( "subfacets_to_subsubfacet_double", type_subfacet, gt_subfacet); Element old_subfacet_el{type_subfacet, 0, gt_subfacet}; Element new_subfacet_el{type_subfacet, 0, gt_subfacet}; for (UInt sf = 0; sf < nb_subfacet_to_double; ++sf) { old_subfacet_el.element = sf_to_double(sf); new_subfacet_el.element = old_nb_subfacet + sf; for (UInt f = 0; f < (*facet_list)(sf).size(); ++f) { Element & facet = (*facet_list)(sf)[f]; if (facet.type != type_facet || facet.ghost_type != gt_facet) { type_facet = facet.type; gt_facet = facet.ghost_type; subfacet_to_facet = &mesh_facets.getSubelementToElement(type_facet, gt_facet); nb_subfacet_per_facet = subfacet_to_facet->getNbComponent(); } Element * sf_update = std::find( subfacet_to_facet->storage() + facet.element * nb_subfacet_per_facet, subfacet_to_facet->storage() + facet.element * nb_subfacet_per_facet + nb_subfacet_per_facet, old_subfacet_el); AKANTU_DEBUG_ASSERT(subfacet_to_facet->storage() + facet.element * nb_subfacet_per_facet != subfacet_to_facet->storage() + facet.element * nb_subfacet_per_facet + nb_subfacet_per_facet, "Subfacet not found"); *sf_update = new_subfacet_el; } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void MeshUtils::updateFacetToSubfacet(Mesh & mesh_facets, ElementType type_subfacet, GhostType gt_subfacet, bool facet_mode) { AKANTU_DEBUG_IN(); Array & sf_to_double = mesh_facets.getData("facet_to_double", type_subfacet, gt_subfacet); UInt nb_subfacet_to_double = sf_to_double.size(); Array> & facet_to_subfacet = mesh_facets.getElementToSubelement(type_subfacet, gt_subfacet); Array> * facet_to_subfacet_double = nullptr; if (facet_mode) { facet_to_subfacet_double = &mesh_facets.getData>( "facets_to_subfacet_double", type_subfacet, gt_subfacet); } else { facet_to_subfacet_double = &mesh_facets.getData>( "subfacets_to_subsubfacet_double", type_subfacet, gt_subfacet); } UInt old_nb_subfacet = facet_to_subfacet.size(); facet_to_subfacet.resize(old_nb_subfacet + nb_subfacet_to_double); for (UInt sf = 0; sf < nb_subfacet_to_double; ++sf) facet_to_subfacet(old_nb_subfacet + sf) = (*facet_to_subfacet_double)(sf); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void MeshUtils::doubleSubfacet(Mesh & mesh, Mesh & mesh_facets, Array & doubled_nodes) { AKANTU_DEBUG_IN(); if (spatial_dimension == 1) return; for (ghost_type_t::iterator gt = ghost_type_t::begin(); gt != ghost_type_t::end(); ++gt) { GhostType gt_subfacet = *gt; Mesh::type_iterator it = mesh_facets.firstType(0, gt_subfacet); Mesh::type_iterator end = mesh_facets.lastType(0, gt_subfacet); for (; it != end; ++it) { ElementType type_subfacet = *it; Array & sf_to_double = mesh_facets.getData( "facet_to_double", type_subfacet, gt_subfacet); UInt nb_subfacet_to_double = sf_to_double.size(); if (nb_subfacet_to_double == 0) continue; AKANTU_DEBUG_ASSERT( type_subfacet == _point_1, "Only _point_1 subfacet doubling is supported at the moment"); Array & conn_subfacet = mesh_facets.getConnectivity(type_subfacet, gt_subfacet); UInt old_nb_subfacet = conn_subfacet.size(); UInt new_nb_subfacet = old_nb_subfacet + nb_subfacet_to_double; conn_subfacet.resize(new_nb_subfacet); std::vector nodes_to_double; UInt old_nb_doubled_nodes = doubled_nodes.size(); /// double nodes for (UInt sf = 0; sf < nb_subfacet_to_double; ++sf) { UInt old_subfacet = sf_to_double(sf); nodes_to_double.push_back(conn_subfacet(old_subfacet)); } doubleNodes(mesh, nodes_to_double, doubled_nodes); /// add new nodes in connectivity for (UInt sf = 0; sf < nb_subfacet_to_double; ++sf) { UInt new_subfacet = old_nb_subfacet + sf; UInt new_node = doubled_nodes(old_nb_doubled_nodes + sf, 1); conn_subfacet(new_subfacet) = new_node; } /// update facet and element connectivity Array> & f_to_subfacet_double = mesh_facets.getData>("facets_to_subfacet_double", type_subfacet, gt_subfacet); Array> & el_to_subfacet_double = mesh_facets.getData>( "elements_to_subfacet_double", type_subfacet, gt_subfacet); Array> * sf_to_subfacet_double = nullptr; if (spatial_dimension == 3) sf_to_subfacet_double = &mesh_facets.getData>( "subfacets_to_subsubfacet_double", type_subfacet, gt_subfacet); for (UInt sf = 0; sf < nb_subfacet_to_double; ++sf) { UInt old_node = doubled_nodes(old_nb_doubled_nodes + sf, 0); UInt new_node = doubled_nodes(old_nb_doubled_nodes + sf, 1); updateElementalConnectivity(mesh, old_node, new_node, el_to_subfacet_double(sf), &f_to_subfacet_double(sf)); updateElementalConnectivity(mesh_facets, old_node, new_node, f_to_subfacet_double(sf)); if (spatial_dimension == 3) updateElementalConnectivity(mesh_facets, old_node, new_node, (*sf_to_subfacet_double)(sf)); } if (spatial_dimension == 2) { updateSubfacetToFacet(mesh_facets, type_subfacet, gt_subfacet, true); updateFacetToSubfacet(mesh_facets, type_subfacet, gt_subfacet, true); } else if (spatial_dimension == 3) { updateSubfacetToFacet(mesh_facets, type_subfacet, gt_subfacet, false); updateFacetToSubfacet(mesh_facets, type_subfacet, gt_subfacet, false); } } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void MeshUtils::flipFacets( Mesh & mesh_facets, const ElementTypeMapArray & global_connectivity, GhostType gt_facet) { AKANTU_DEBUG_IN(); UInt spatial_dimension = mesh_facets.getSpatialDimension(); /// get global connectivity for local mesh ElementTypeMapArray global_connectivity_tmp("global_connectivity_tmp", mesh_facets.getID(), mesh_facets.getMemoryID()); global_connectivity_tmp.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(global_connectivity_tmp); /// loop on every facet for (auto type_facet : mesh_facets.elementTypes(spatial_dimension - 1, gt_facet)) { auto & connectivity = mesh_facets.getConnectivity(type_facet, gt_facet); const auto & g_connectivity = global_connectivity(type_facet, gt_facet); auto & el_to_f = mesh_facets.getElementToSubelement(type_facet, gt_facet); auto & subfacet_to_facet = mesh_facets.getSubelementToElement(type_facet, gt_facet); UInt nb_subfacet_per_facet = subfacet_to_facet.getNbComponent(); UInt nb_nodes_per_facet = connectivity.getNbComponent(); UInt nb_facet = connectivity.size(); UInt nb_nodes_per_P1_facet = Mesh::getNbNodesPerElement(Mesh::getP1ElementType(type_facet)); auto & global_conn_tmp = global_connectivity_tmp(type_facet, gt_facet); auto conn_it = connectivity.begin(nb_nodes_per_facet); auto gconn_tmp_it = global_conn_tmp.begin(nb_nodes_per_facet); auto conn_glob_it = g_connectivity.begin(nb_nodes_per_facet); auto subf_to_f = subfacet_to_facet.begin(nb_subfacet_per_facet); auto * conn_tmp_pointer = new UInt[nb_nodes_per_facet]; Vector conn_tmp(conn_tmp_pointer, nb_nodes_per_facet); Element el_tmp; auto * subf_tmp_pointer = new Element[nb_subfacet_per_facet]; Vector subf_tmp(subf_tmp_pointer, nb_subfacet_per_facet); for (UInt f = 0; f < nb_facet; ++f, ++conn_it, ++gconn_tmp_it, ++subf_to_f, ++conn_glob_it) { Vector & gconn_tmp = *gconn_tmp_it; const Vector & conn_glob = *conn_glob_it; Vector & conn_local = *conn_it; /// skip facet if connectivities are the same if (gconn_tmp == conn_glob) continue; /// re-arrange connectivity conn_tmp = conn_local; UInt * begin = conn_glob.storage(); UInt * end = conn_glob.storage() + nb_nodes_per_facet; for (UInt n = 0; n < nb_nodes_per_facet; ++n) { UInt * new_node = std::find(begin, end, gconn_tmp(n)); AKANTU_DEBUG_ASSERT(new_node != end, "Node not found"); UInt new_position = new_node - begin; conn_local(new_position) = conn_tmp(n); } /// if 3D, check if facets are just rotated if (spatial_dimension == 3) { /// find first node UInt * new_node = std::find(begin, end, gconn_tmp(0)); AKANTU_DEBUG_ASSERT(new_node != end, "Node not found"); UInt new_position = new_node - begin; UInt n = 1; /// count how many nodes in the received connectivity follow /// the same order of those in the local connectivity for (; n < nb_nodes_per_P1_facet && gconn_tmp(n) == conn_glob((new_position + 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 el_tmp = el_to_f(f)[0]; el_to_f(f)[0] = el_to_f(f)[1]; el_to_f(f)[1] = el_tmp; subf_tmp = (*subf_to_f); (*subf_to_f)(0) = subf_tmp(1); (*subf_to_f)(1) = subf_tmp(0); } delete[] subf_tmp_pointer; delete[] conn_tmp_pointer; } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void MeshUtils::fillElementToSubElementsData(Mesh & mesh) { AKANTU_DEBUG_IN(); if (mesh.getNbElement(mesh.getSpatialDimension() - 1) == 0) { AKANTU_DEBUG_INFO("There are not facets, add them in the mesh file or call " "the buildFacet method."); return; } UInt spatial_dimension = mesh.getSpatialDimension(); ElementTypeMapArray barycenters("barycenter_tmp", mesh.getID(), mesh.getMemoryID()); barycenters.initialize(mesh, _nb_component = spatial_dimension, _spatial_dimension = _all_dimensions); // mesh.initElementTypeMapArray(barycenters, spatial_dimension, // _all_dimensions); Element element; for (auto ghost_type : ghost_types) { element.ghost_type = ghost_type; for (auto & type : mesh.elementTypes(_all_dimensions, ghost_type)) { element.type = type; UInt nb_element = mesh.getNbElement(type, ghost_type); Array & barycenters_arr = barycenters(type, ghost_type); barycenters_arr.resize(nb_element); auto bary = barycenters_arr.begin(spatial_dimension); auto bary_end = barycenters_arr.end(spatial_dimension); for (UInt el = 0; bary != bary_end; ++bary, ++el) { element.element = el; mesh.getBarycenter(element, *bary); } } } MeshAccessor mesh_accessor(mesh); for (Int sp(spatial_dimension); sp >= 1; --sp) { if (mesh.getNbElement(sp) == 0) continue; for (auto ghost_type : ghost_types) { for (auto & type : mesh.elementTypes(sp, ghost_type)) { mesh_accessor.getSubelementToElement(type, ghost_type) .resize(mesh.getNbElement(type, ghost_type)); mesh_accessor.getSubelementToElement(type, ghost_type).clear(); } for (auto & type : mesh.elementTypes(sp - 1, ghost_type)) { mesh_accessor.getElementToSubelement(type, ghost_type) .resize(mesh.getNbElement(type, ghost_type)); mesh.getElementToSubelement(type, ghost_type).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 (auto & type : mesh.elementTypes(sp - 1, ghost_type)) { facet_element.type = type; auto & element_to_subelement = mesh.getElementToSubelement(type, ghost_type); const auto & connectivity = mesh.getConnectivity(type, ghost_type); auto fit = connectivity.begin(mesh.getNbNodesPerElement(type)); auto fend = connectivity.end(mesh.getNbNodesPerElement(type)); UInt fid = 0; for (; fit != fend; ++fit, ++fid) { const Vector & facet = *fit; facet_element.element = fid; std::map element_seen_counter; UInt nb_nodes_per_facet = mesh.getNbNodesPerElement(Mesh::getP1ElementType(type)); for (UInt n(0); n < nb_nodes_per_facet; ++n) { auto eit = nodes_to_elements.begin(facet(n)); auto eend = nodes_to_elements.end(facet(n)); for (; eit != eend; ++eit) { auto & elem = *eit; auto cit = element_seen_counter.find(elem); if (cit != element_seen_counter.end()) { cit->second++; } else { element_seen_counter[elem] = 1; } } } std::vector connected_elements; auto cit = element_seen_counter.begin(); auto cend = element_seen_counter.end(); for (; cit != cend; ++cit) { if (cit->second == nb_nodes_per_facet) connected_elements.push_back(cit->first); } auto ceit = connected_elements.begin(); auto ceend = connected_elements.end(); for (; ceit != ceend; ++ceit) element_to_subelement(fid).push_back(*ceit); for (UInt ce = 0; ce < connected_elements.size(); ++ce) { Element & elem = connected_elements[ce]; Array & subelement_to_element = mesh_accessor.getSubelementToElement(elem.type, elem.ghost_type); UInt f(0); for (; f < mesh.getNbFacetsPerElement(elem.type) && subelement_to_element(elem.element, f) != ElementNull; ++f) ; AKANTU_DEBUG_ASSERT( f < mesh.getNbFacetsPerElement(elem.type), "The element " << elem << " seems to have too many facets!! (" << f << " < " << mesh.getNbFacetsPerElement(elem.type) << ")"); subelement_to_element(elem.element, f) = facet_element; } } } } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template bool MeshUtils::findElementsAroundSubfacet( const Mesh & mesh, const Mesh & mesh_facets, const Element & starting_element, const Element & end_facet, const Vector & subfacet_connectivity, std::vector & elem_list, std::vector & facet_list, std::vector * subfacet_list) { AKANTU_DEBUG_IN(); /// preallocated stuff before starting bool facet_matched = false; elem_list.clear(); facet_list.clear(); if (third_dim_points) subfacet_list->clear(); elem_list.push_back(starting_element); const Array * facet_connectivity = nullptr; const Array * sf_connectivity = nullptr; const Array * facet_to_element = nullptr; const Array * subfacet_to_facet = nullptr; ElementType current_type = _not_defined; GhostType current_ghost_type = _casper; ElementType current_facet_type = _not_defined; GhostType current_facet_ghost_type = _casper; ElementType current_subfacet_type = _not_defined; GhostType current_subfacet_ghost_type = _casper; const Array> * element_to_facet = nullptr; const Element * opposing_el = nullptr; std::queue elements_to_check; elements_to_check.push(starting_element); /// keep going until there are elements to check while (!elements_to_check.empty()) { /// check current element Element & current_el = elements_to_check.front(); if (current_el.type != current_type || current_el.ghost_type != current_ghost_type) { current_type = current_el.type; current_ghost_type = current_el.ghost_type; facet_to_element = &mesh_facets.getSubelementToElement(current_type, current_ghost_type); } /// loop over each facet of the element for (UInt f = 0; f < facet_to_element->getNbComponent(); ++f) { const Element & current_facet = (*facet_to_element)(current_el.element, f); if (current_facet == ElementNull) continue; if (current_facet_type != current_facet.type || current_facet_ghost_type != current_facet.ghost_type) { current_facet_type = current_facet.type; current_facet_ghost_type = current_facet.ghost_type; element_to_facet = &mesh_facets.getElementToSubelement( current_facet_type, current_facet_ghost_type); facet_connectivity = &mesh_facets.getConnectivity( current_facet_type, current_facet_ghost_type); if (third_dim_points) subfacet_to_facet = &mesh_facets.getSubelementToElement( current_facet_type, current_facet_ghost_type); } /// check if end facet is reached if (current_facet == end_facet) facet_matched = true; /// add this facet if not already passed if (std::find(facet_list.begin(), facet_list.end(), current_facet) == facet_list.end() && hasElement(*facet_connectivity, current_facet, subfacet_connectivity)) { facet_list.push_back(current_facet); if (third_dim_points) { /// check subfacets for (UInt sf = 0; sf < subfacet_to_facet->getNbComponent(); ++sf) { const Element & current_subfacet = (*subfacet_to_facet)(current_facet.element, sf); if (current_subfacet == ElementNull) continue; if (current_subfacet_type != current_subfacet.type || current_subfacet_ghost_type != current_subfacet.ghost_type) { current_subfacet_type = current_subfacet.type; current_subfacet_ghost_type = current_subfacet.ghost_type; sf_connectivity = &mesh_facets.getConnectivity( current_subfacet_type, current_subfacet_ghost_type); } if (std::find(subfacet_list->begin(), subfacet_list->end(), current_subfacet) == subfacet_list->end() && hasElement(*sf_connectivity, current_subfacet, subfacet_connectivity)) subfacet_list->push_back(current_subfacet); } } } else continue; /// consider opposing element if ((*element_to_facet)(current_facet.element)[0] == current_el) opposing_el = &(*element_to_facet)(current_facet.element)[1]; else opposing_el = &(*element_to_facet)(current_facet.element)[0]; /// skip null elements since they are on a boundary if (*opposing_el == ElementNull) continue; /// skip this element if already added if (std::find(elem_list.begin(), elem_list.end(), *opposing_el) != elem_list.end()) continue; /// only regular elements have to be checked if (opposing_el->kind() == _ek_regular) elements_to_check.push(*opposing_el); elem_list.push_back(*opposing_el); #ifndef AKANTU_NDEBUG const Array & conn_elem = mesh.getConnectivity(opposing_el->type, opposing_el->ghost_type); AKANTU_DEBUG_ASSERT( hasElement(conn_elem, *opposing_el, subfacet_connectivity), "Subfacet doesn't belong to this element"); #endif } /// erased checked element from the list elements_to_check.pop(); } AKANTU_DEBUG_OUT(); return facet_matched; } /* -------------------------------------------------------------------------- */ UInt MeshUtils::updateLocalMasterGlobalConnectivity(Mesh & mesh, UInt local_nb_new_nodes) { const auto & comm = mesh.getCommunicator(); Int rank = comm.whoAmI(); Int nb_proc = comm.getNbProc(); if (nb_proc == 1) return local_nb_new_nodes; /// resize global ids array Array & nodes_global_ids = mesh.getGlobalNodesIds(); UInt old_nb_nodes = mesh.getNbNodes() - local_nb_new_nodes; nodes_global_ids.resize(mesh.getNbNodes()); /// compute the number of global nodes based on the number of old nodes Vector old_local_master_nodes(nb_proc); for (UInt n = 0; n < old_nb_nodes; ++n) if (mesh.isLocalOrMasterNode(n)) ++old_local_master_nodes(rank); comm.allGather(old_local_master_nodes); UInt old_global_nodes = std::accumulate(old_local_master_nodes.storage(), old_local_master_nodes.storage() + nb_proc, 0); /// compute amount of local or master doubled nodes Vector local_master_nodes(nb_proc); for (UInt n = old_nb_nodes; n < mesh.getNbNodes(); ++n) if (mesh.isLocalOrMasterNode(n)) ++local_master_nodes(rank); comm.allGather(local_master_nodes); /// update global number of nodes UInt total_nb_new_nodes = std::accumulate( local_master_nodes.storage(), local_master_nodes.storage() + nb_proc, 0); if (total_nb_new_nodes == 0) return 0; /// set global ids of local and master nodes UInt starting_index = std::accumulate(local_master_nodes.storage(), local_master_nodes.storage() + rank, old_global_nodes); for (UInt n = old_nb_nodes; n < mesh.getNbNodes(); ++n) { if (mesh.isLocalOrMasterNode(n)) { nodes_global_ids(n) = starting_index; ++starting_index; } } MeshAccessor mesh_accessor(mesh); mesh_accessor.setNbGlobalNodes(old_global_nodes + total_nb_new_nodes); return total_nb_new_nodes; } /* -------------------------------------------------------------------------- */ void MeshUtils::updateElementalConnectivity( Mesh & mesh, UInt old_node, UInt new_node, const std::vector & element_list, __attribute__((unused)) const std::vector * facet_list) { AKANTU_DEBUG_IN(); ElementType el_type = _not_defined; GhostType gt_type = _casper; Array * conn_elem = nullptr; #if defined(AKANTU_COHESIVE_ELEMENT) const Array * cohesive_facets = nullptr; #endif UInt nb_nodes_per_element = 0; UInt * n_update = nullptr; for (UInt el = 0; el < element_list.size(); ++el) { const Element & elem = element_list[el]; if (elem.type == _not_defined) continue; if (elem.type != el_type || elem.ghost_type != gt_type) { el_type = elem.type; gt_type = elem.ghost_type; conn_elem = &mesh.getConnectivity(el_type, gt_type); nb_nodes_per_element = conn_elem->getNbComponent(); #if defined(AKANTU_COHESIVE_ELEMENT) if (elem.kind() == _ek_cohesive) cohesive_facets = &mesh.getMeshFacets().getSubelementToElement(el_type, gt_type); #endif } #if defined(AKANTU_COHESIVE_ELEMENT) if (elem.kind() == _ek_cohesive) { AKANTU_DEBUG_ASSERT( facet_list != nullptr, "Provide a facet list in order to update cohesive elements"); /// loop over cohesive element's facets for (UInt f = 0, n = 0; f < 2; ++f, n += nb_nodes_per_element / 2) { const Element & facet = (*cohesive_facets)(elem.element, f); /// skip facets if not present in the list if (std::find(facet_list->begin(), facet_list->end(), facet) == facet_list->end()) continue; n_update = std::find( conn_elem->storage() + elem.element * nb_nodes_per_element + n, conn_elem->storage() + elem.element * nb_nodes_per_element + n + nb_nodes_per_element / 2, old_node); AKANTU_DEBUG_ASSERT(n_update != conn_elem->storage() + elem.element * nb_nodes_per_element + n + nb_nodes_per_element / 2, "Node not found in current element"); /// update connectivity *n_update = new_node; } } else { #endif n_update = std::find(conn_elem->storage() + elem.element * nb_nodes_per_element, conn_elem->storage() + elem.element * nb_nodes_per_element + nb_nodes_per_element, old_node); AKANTU_DEBUG_ASSERT(n_update != conn_elem->storage() + elem.element * nb_nodes_per_element + nb_nodes_per_element, "Node not found in current element"); /// update connectivity *n_update = new_node; #if defined(AKANTU_COHESIVE_ELEMENT) } #endif } AKANTU_DEBUG_OUT(); } } // namespace akantu diff --git a/src/model/boundary_condition_functor.hh b/src/model/boundary_condition_functor.hh index 2c177874d..743bd322d 100644 --- a/src/model/boundary_condition_functor.hh +++ b/src/model/boundary_condition_functor.hh @@ -1,197 +1,217 @@ /** * @file boundary_condition_functor.hh * * @author Dana Christen * @author David Simon Kammer * @author Nicolas Richart * * @date creation: Fri May 03 2013 * @date last modification: Thu Oct 15 2015 * * @brief Definitions of the functors to apply boundary conditions * * @section LICENSE * * Copyright (©) 2014, 2015 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_common.hh" #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::SpacialDirection; struct Functor { enum Type { _dirichlet, _neumann }; -}; - -/* ------------------------------------------------------------------------ */ -/* Dirichlet */ -/* ------------------------------------------------------------------------ */ -namespace Dirichlet { - /* ---------------------------------------------------------------------- */ - class DirichletFunctor : public Functor { - protected: - DirichletFunctor() = default; - explicit DirichletFunctor(Axis ax) : axis(ax) {} - - public: - void operator()(__attribute__((unused)) UInt node, - __attribute__((unused)) Vector & flags, - __attribute__((unused)) Vector & primal, - __attribute__((unused)) const Vector & coord) const { - AKANTU_DEBUG_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()(UInt node, Vector & flags, - Vector & primal, - const Vector & coord) const; - }; - - /* ---------------------------------------------------------------------- */ - class FreeBoundary : public DirichletFunctor { - public: - explicit FreeBoundary(Axis ax = _x) : DirichletFunctor(ax) {} - - public: - inline void operator()(UInt node, Vector & flags, - Vector & primal, - const Vector & coord) const; - }; - - /* ---------------------------------------------------------------------- */ - class FixedValue : public DirichletFunctor { - public: - FixedValue(Real val, Axis ax = _x) : DirichletFunctor(ax), value(val) {} - - public: - inline void operator()(UInt node, Vector & flags, - Vector & primal, - const Vector & coord) const; - - protected: - Real value; - }; - - /* ---------------------------------------------------------------------- */ - class IncrementValue : public DirichletFunctor { - public: - IncrementValue(Real val, Axis ax = _x) : DirichletFunctor(ax), value(val) {} - - public: - inline void operator()(UInt node, Vector & flags, - Vector & primal, - const Vector & coord) const; - - inline void setIncrement(Real val) { this->value = val; } - - protected: - Real value; - }; -} // end 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; - - virtual ~NeumannFunctor() = 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; - - 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; - - 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; - }; -} // end namespace Neumann + /* ------------------------------------------------------------------------ */ + /* Dirichlet */ + /* ------------------------------------------------------------------------ */ + namespace Dirichlet { + /* ---------------------------------------------------------------------- */ + class DirichletFunctor : public Functor { + protected: + DirichletFunctor() = default; + explicit DirichletFunctor(Axis ax) : axis(ax) {} + + public: + void operator()(__attribute__((unused)) UInt node, + __attribute__((unused)) Vector & flags, + __attribute__((unused)) Vector & primal, + __attribute__((unused)) + const Vector & coord) const { + AKANTU_DEBUG_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()(UInt node, Vector & flags, + Vector & primal, + const Vector & coord) const; + }; + + /* ---------------------------------------------------------------------- */ + class FreeBoundary : public DirichletFunctor { + public: + explicit FreeBoundary(Axis ax = _x) : DirichletFunctor(ax) {} + + public: + inline void operator()(UInt node, Vector & flags, + Vector & primal, + const Vector & coord) const; + }; + + /* ---------------------------------------------------------------------- */ + class FixedValue : public DirichletFunctor { + public: + FixedValue(Real val, Axis ax = _x) : DirichletFunctor(ax), value(val) {} + + public: + inline void operator()(UInt node, Vector & flags, + Vector & primal, + const Vector & coord) const; + + protected: + Real value; + }; + + /* ---------------------------------------------------------------------- */ + class IncrementValue : public DirichletFunctor { + public: + IncrementValue(Real val, Axis ax = _x) + : DirichletFunctor(ax), value(val) {} + + public: + inline void operator()(UInt node, Vector & flags, + Vector & primal, + const Vector & coord) const; + + 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()(UInt node, Vector & flags, + Vector & primal, + const Vector & coord) const; + + inline void setIncrement(const Vector & val) { this->value = val; } + + protected: + Vector value; + }; + + } // end 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; + + virtual ~NeumannFunctor() = 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; + + 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; + + 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; + }; + } // end namespace Neumann } // end namespace BC } // akantu #include "boundary_condition_functor_inline_impl.cc" #endif /* __AKANTU_BOUNDARY_CONDITION_FUNCTOR_HH__ */ diff --git a/src/model/boundary_condition_functor_inline_impl.cc b/src/model/boundary_condition_functor_inline_impl.cc index 2d497c547..abe306794 100644 --- a/src/model/boundary_condition_functor_inline_impl.cc +++ b/src/model/boundary_condition_functor_inline_impl.cc @@ -1,144 +1,156 @@ /** * @file boundary_condition_functor_inline_impl.cc * * @author Dana Christen * * @date creation: Fri May 03 2013 * @date last modification: Thu Oct 15 2015 * * @brief implementation of the BC::Functors * * @section LICENSE * * Copyright (©) 2014, 2015 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "boundary_condition_functor.hh" /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_BOUNDARY_CONDITION_FUNCTOR_INLINE_IMPL_CC__ #define __AKANTU_BOUNDARY_CONDITION_FUNCTOR_INLINE_IMPL_CC__ /* -------------------------------------------------------------------------- */ #define DIRICHLET_SANITY_CHECK \ AKANTU_DEBUG_ASSERT( \ coord.size() <= flags.size(), \ "The coordinates and flags vectors given to the boundary" \ << " condition functor have different sizes!"); \ AKANTU_DEBUG_ASSERT( \ primal.size() <= coord.size(), \ "The primal vector and coordinates vector 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!"); \ AKANTU_DEBUG_ASSERT( \ dual.size() <= coord.size(), \ "The dual vector and coordinates vector given to" \ << " the boundary condition functor have different sizes!"); namespace akantu { namespace BC { namespace Dirichlet { /* ---------------------------------------------------------------------- */ inline void FlagOnly:: operator()(__attribute__((unused)) UInt node, Vector & flags, __attribute__((unused)) Vector & primal, __attribute__((unused)) const Vector & coord) const { DIRICHLET_SANITY_CHECK; flags(this->axis) = true; } /* ---------------------------------------------------------------------- */ inline void FreeBoundary:: operator()(__attribute__((unused)) UInt node, Vector & flags, __attribute__((unused)) Vector & primal, __attribute__((unused)) const Vector & coord) const { DIRICHLET_SANITY_CHECK; flags(this->axis) = false; } /* ---------------------------------------------------------------------- */ inline void FixedValue::operator()(__attribute__((unused)) UInt node, Vector & flags, Vector & primal, __attribute__((unused)) const Vector & coord) const { DIRICHLET_SANITY_CHECK; flags(this->axis) = true; primal(this->axis) = value; } /* ---------------------------------------------------------------------- */ inline void IncrementValue::operator()(__attribute__((unused)) UInt node, Vector & flags, Vector & primal, __attribute__((unused)) const Vector & coord) const { DIRICHLET_SANITY_CHECK; flags(this->axis) = true; primal(this->axis) += value; } + + /* ---------------------------------------------------------------------- */ + inline void Increment::operator()(__attribute__((unused)) UInt node, + Vector & flags, + Vector & primal, + __attribute__((unused)) + const Vector & coord) const { + DIRICHLET_SANITY_CHECK; + flags.set(true); + primal += value; + } + } // end namespace Dirichlet /* ------------------------------------------------------------------------ */ /* Neumann */ /* ------------------------------------------------------------------------ */ namespace Neumann { /* ---------------------------------------------------------------------- */ inline void FreeBoundary:: operator()(__attribute__((unused)) const IntegrationPoint & quad_point, Vector & dual, __attribute__((unused)) const Vector & coord, __attribute__((unused)) const Vector & normals) const { for (UInt i(0); i < dual.size(); ++i) { dual(i) = 0.0; } } /* ---------------------------------------------------------------------- */ inline void FromHigherDim::operator()(__attribute__((unused)) const IntegrationPoint & quad_point, Vector & dual, __attribute__((unused)) const Vector & coord, const Vector & normals) const { dual.mul(this->bc_data, normals); } /* ---------------------------------------------------------------------- */ inline void FromSameDim:: operator()(__attribute__((unused)) const IntegrationPoint & quad_point, Vector & dual, __attribute__((unused)) const Vector & coord, __attribute__((unused)) const Vector & normals) const { dual = this->bc_data; } } // namespace Neumann } // namespace BC } // namespace akantu #endif /* __AKANTU_BOUNDARY_CONDITION_FUNCTOR_INLINE_IMPL_CC__ */ diff --git a/src/model/dof_manager.cc b/src/model/dof_manager.cc index 6e28f5d79..0f87c01d9 100644 --- a/src/model/dof_manager.cc +++ b/src/model/dof_manager.cc @@ -1,592 +1,604 @@ /** * @file dof_manager.cc * * @author Nicolas Richart * * @date Wed Aug 12 09:52:30 2015 * * @brief Implementation of the common parts of the DOFManagers * * @section LICENSE * * Copyright (©) 2010-2011 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "dof_manager.hh" +#include "communicator.hh" #include "element_group.hh" #include "mesh.hh" #include "mesh_utils.hh" #include "node_group.hh" #include "non_linear_solver.hh" #include "sparse_matrix.hh" -#include "communicator.hh" #include "time_step_solver.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ DOFManager::DOFManager(const ID & id, const MemoryID & memory_id) : Memory(id, memory_id), communicator(Communicator::getStaticCommunicator()) {} /* -------------------------------------------------------------------------- */ DOFManager::DOFManager(Mesh & mesh, const ID & id, const MemoryID & memory_id) : Memory(id, memory_id), mesh(&mesh), local_system_size(0), pure_local_system_size(0), system_size(0), communicator(mesh.getCommunicator()) { this->mesh->registerEventHandler(*this, _ehp_dof_manager); } /* -------------------------------------------------------------------------- */ DOFManager::~DOFManager() = default; /* -------------------------------------------------------------------------- */ // void DOFManager::getEquationsNumbers(const ID &, Array &) { // AKANTU_DEBUG_TO_IMPLEMENT(); // } /* -------------------------------------------------------------------------- */ 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, const ElementType & type, const GhostType & ghost_type, Real scale_factor, const Array & filter_elements) { AKANTU_DEBUG_IN(); UInt nb_element; UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type); UInt nb_degree_of_freedom = elementary_vect.getNbComponent() / nb_nodes_per_element; UInt * filter_it = nullptr; if (filter_elements != empty_filter) { nb_element = filter_elements.size(); filter_it = filter_elements.storage(); } else { nb_element = this->mesh->getNbElement(type, ghost_type); } AKANTU_DEBUG_ASSERT(elementary_vect.size() == nb_element, "The vector elementary_vect(" << elementary_vect.getID() << ") has not the good size."); const Array & connectivity = this->mesh->getConnectivity(type, ghost_type); // Array::const_vector_iterator conn_begin = // connectivity.begin(nb_nodes_per_element); // Array::const_vector_iterator conn_it = conn_begin; Array::const_matrix_iterator elem_it = elementary_vect.begin(nb_degree_of_freedom, nb_nodes_per_element); for (UInt el = 0; el < nb_element; ++el, ++elem_it) { UInt element = el; if (filter_it != nullptr) { // conn_it = conn_begin + *filter_it; element = *filter_it; } // const Vector & conn = *conn_it; const Matrix & elemental_val = *elem_it; for (UInt n = 0; n < nb_nodes_per_element; ++n) { UInt offset_node = connectivity(element, n) * nb_degree_of_freedom; Vector assemble(array_assembeled.storage() + offset_node, nb_degree_of_freedom); Vector elem_val = elemental_val(n); assemble.aXplusY(elem_val, scale_factor); } if (filter_it != nullptr) ++filter_it; // else // ++conn_it; } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void DOFManager::assembleElementalArrayToResidual( const ID & dof_id, const Array & elementary_vect, const ElementType & type, const GhostType & ghost_type, Real scale_factor, const Array & filter_elements) { AKANTU_DEBUG_IN(); UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type); UInt nb_degree_of_freedom = elementary_vect.getNbComponent() / nb_nodes_per_element; Array array_localy_assembeled(this->mesh->getNbNodes(), nb_degree_of_freedom); array_localy_assembeled.clear(); 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, const ElementType & type, const GhostType & ghost_type, Real scale_factor, const Array & filter_elements) { AKANTU_DEBUG_IN(); UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type); UInt nb_degree_of_freedom = elementary_vect.getNbComponent() / nb_nodes_per_element; Array array_localy_assembeled(this->mesh->getNbNodes(), nb_degree_of_freedom); array_localy_assembeled.clear(); 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); + } +} + /* -------------------------------------------------------------------------- */ DOFManager::DOFData::DOFData(const ID & dof_id) : support_type(_dst_generic), group_support("__mesh__"), dof(nullptr), blocked_dofs(nullptr), increment(nullptr), previous(nullptr), solution(0, 1, dof_id + ":solution"), local_equation_number(0, 1, dof_id + ":local_equation_number") {} /* -------------------------------------------------------------------------- */ DOFManager::DOFData::~DOFData() = default; /* -------------------------------------------------------------------------- */ DOFManager::DOFData & DOFManager::getNewDOFData(const ID & dof_id) { auto it = this->dofs.find(dof_id); if (it != this->dofs.end()) { AKANTU_EXCEPTION("This dof array has already been registered"); } std::unique_ptr dofs_storage = std::make_unique(dof_id); this->dofs[dof_id] = std::move(dofs_storage); return *dofs_storage; } /* -------------------------------------------------------------------------- */ void DOFManager::registerDOFsInternal(const ID & dof_id, Array & dofs_array) { DOFData & dofs_storage = this->getDOFData(dof_id); dofs_storage.dof = &dofs_array; UInt nb_local_dofs = 0; UInt nb_pure_local = 0; const DOFSupportType & support_type = dofs_storage.support_type; switch (support_type) { case _dst_nodal: { UInt nb_nodes = 0; const ID & group = dofs_storage.group_support; NodeGroup * node_group = nullptr; if (group == "__mesh__") { nb_nodes = this->mesh->getNbNodes(); } else { node_group = &this->mesh->getElementGroup(group).getNodeGroup(); nb_nodes = node_group->size(); } nb_local_dofs = nb_nodes; AKANTU_DEBUG_ASSERT( dofs_array.size() == nb_local_dofs, "The array of dof is too shot to be associated to nodes."); for (UInt n = 0; n < nb_nodes; ++n) { UInt node = n; if (node_group) node = node_group->getNodes()(n); nb_pure_local += this->mesh->isLocalOrMasterNode(node) ? 1 : 0; } nb_pure_local *= dofs_array.getNbComponent(); nb_local_dofs *= dofs_array.getNbComponent(); 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."); } } this->pure_local_system_size += nb_pure_local; this->local_system_size += nb_local_dofs; communicator.allReduce(nb_pure_local, SynchronizerOperation::_sum); this->system_size += nb_pure_local; } /* -------------------------------------------------------------------------- */ void DOFManager::registerDOFs(const ID & dof_id, Array & dofs_array, const DOFSupportType & support_type) { DOFData & dofs_storage = this->getNewDOFData(dof_id); dofs_storage.support_type = support_type; this->registerDOFsInternal(dof_id, dofs_array); } /* -------------------------------------------------------------------------- */ void DOFManager::registerDOFs(const ID & dof_id, Array & dofs_array, const ID & support_group) { DOFData & dofs_storage = this->getNewDOFData(dof_id); dofs_storage.support_type = _dst_nodal; dofs_storage.group_support = support_group; this->registerDOFsInternal(dof_id, dofs_array); } /* -------------------------------------------------------------------------- */ 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, UInt order, Array & dofs_derivative) { DOFData & dof = this->getDOFData(dof_id); std::vector *> & derivatives = dof.dof_derivatives; if (derivatives.size() < order) { derivatives.resize(order, nullptr); } else { if (derivatives[order - 1] != nullptr) { AKANTU_EXCEPTION("The dof derivatives of order " << order << " already been registered for this dof (" << dof_id << ")"); } } derivatives[order - 1] = &dofs_derivative; } /* -------------------------------------------------------------------------- */ void DOFManager::registerBlockedDOFs(const ID & dof_id, Array & blocked_dofs) { DOFData & dof = this->getDOFData(dof_id); if (dof.blocked_dofs != nullptr) { AKANTU_EXCEPTION("The blocked dofs array for " << dof_id << " has already been registered"); } dof.blocked_dofs = &blocked_dofs; } /* -------------------------------------------------------------------------- */ void DOFManager::splitSolutionPerDOFs() { auto it = this->dofs.begin(); auto end = this->dofs.end(); for (; it != end; ++it) { DOFData & dof_data = *it->second; dof_data.solution.resize(dof_data.dof->size() * dof_data.dof->getNbComponent()); this->getSolutionPerDOFs(it->first, dof_data.solution); } } /* -------------------------------------------------------------------------- */ SparseMatrix & DOFManager::registerSparseMatrix(const ID & matrix_id, std::unique_ptr & matrix) { SparseMatricesMap::const_iterator it = this->matrices.find(matrix_id); if (it != this->matrices.end()) { AKANTU_EXCEPTION("The matrix " << matrix_id << " already exists in " << this->id); } SparseMatrix & ret = *matrix; this->matrices[matrix_id] = std::move(matrix); return ret; } /* -------------------------------------------------------------------------- */ /// Get an instance of a new SparseMatrix Array & DOFManager::getNewLumpedMatrix(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_EXCEPTION("The lumped matrix " << matrix_id << " already exists in " << this->id); } auto mtx = std::make_unique>(this->local_system_size, 1, matrix_id); this->lumped_matrices[matrix_id] = std::move(mtx); return *this->lumped_matrices[matrix_id]; } /* -------------------------------------------------------------------------- */ 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(); } /* -------------------------------------------------------------------------- */ Array & 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 Array & 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)); } /* -------------------------------------------------------------------------- */ /* Mesh Events */ /* -------------------------------------------------------------------------- */ std::pair DOFManager::updateNodalDOFs(const ID & dof_id, const Array & nodes_list) { auto & dof_data = this->getDOFData(dof_id); UInt nb_new_local_dofs = 0; UInt nb_new_pure_local = 0; nb_new_local_dofs = nodes_list.size(); for (const auto & node : nodes_list) { nb_new_pure_local += this->mesh->isLocalOrMasterNode(node) ? 1 : 0; } const auto & dof_array = *dof_data.dof; nb_new_pure_local *= dof_array.getNbComponent(); nb_new_local_dofs *= dof_array.getNbComponent(); this->pure_local_system_size += nb_new_pure_local; this->local_system_size += nb_new_local_dofs; UInt nb_new_global = nb_new_pure_local; communicator.allReduce(nb_new_global, SynchronizerOperation::_sum); this->system_size += nb_new_global; return std::make_pair(nb_new_local_dofs, nb_new_pure_local); } /* -------------------------------------------------------------------------- */ 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) != UInt(-1)) new_nodes_list.push_back(node); } this->updateNodalDOFs(dof_id, new_nodes_list); } } } /* -------------------------------------------------------------------------- */ void DOFManager::onNodesRemoved(const Array &, const Array &, const RemovedNodesEvent &) {} /* -------------------------------------------------------------------------- */ void DOFManager::onElementsAdded(const Array &, const NewElementsEvent &) {} /* -------------------------------------------------------------------------- */ void DOFManager::onElementsRemoved(const Array &, const ElementTypeMapArray &, const RemovedElementsEvent &) {} /* -------------------------------------------------------------------------- */ void DOFManager::onElementsChanged(const Array &, const Array &, const ElementTypeMapArray &, const ChangedElementsEvent &) {} /* -------------------------------------------------------------------------- */ } // namespace akantu diff --git a/src/model/dof_manager.hh b/src/model/dof_manager.hh index 47b4516ef..a6e5d903f 100644 --- a/src/model/dof_manager.hh +++ b/src/model/dof_manager.hh @@ -1,468 +1,472 @@ /** * @file dof_manager.hh * * @author Nicolas Richart * * @date Wed Jul 22 11:43:43 2015 * * @brief Class handling the different types of dofs * * @section LICENSE * * Copyright (©) 2010-2011 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_memory.hh" #include "aka_factory.hh" #include "mesh.hh" /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_DOF_MANAGER_HH__ #define __AKANTU_DOF_MANAGER_HH__ namespace akantu { class TermsToAssemble; class NonLinearSolver; class TimeStepSolver; class SparseMatrix; } // namespace akantu namespace akantu { class DOFManager : protected Memory, protected MeshEventHandler { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: DOFManager(const ID & id = "dof_manager", const MemoryID & memory_id = 0); DOFManager(Mesh & mesh, const ID & id = "dof_manager", const MemoryID & memory_id = 0); ~DOFManager() override; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ private: /// common function to help registering dofs void registerDOFsInternal(const ID & dof_id, Array & dofs_array); public: /// register an array of degree of freedom virtual void registerDOFs(const ID & dof_id, Array & dofs_array, const DOFSupportType & support_type); /// the dof as an implied type of _dst_nodal and is defined only on a subset /// of nodes virtual void registerDOFs(const ID & dof_id, Array & dofs_array, const ID & group_support); /// register an array of previous values of the degree of freedom virtual void registerDOFsPrevious(const ID & dof_id, Array & dofs_array); /// register an array of increment of degree of freedom virtual void registerDOFsIncrement(const ID & dof_id, Array & dofs_array); /// register an array of derivatives for a particular dof array virtual void registerDOFsDerivative(const ID & dof_id, UInt order, Array & dofs_derivative); /// register array representing the blocked degree of freedoms virtual void registerBlockedDOFs(const ID & dof_id, Array & blocked_dofs); /// Assemble an array to the global residual array virtual void assembleToResidual(const ID & dof_id, const Array & array_to_assemble, Real scale_factor = 1.) = 0; /// Assemble an array to the global lumped matrix array virtual void assembleToLumpedMatrix(const ID & dof_id, const Array & array_to_assemble, const ID & lumped_mtx, Real scale_factor = 1.) = 0; /** * Assemble elementary values to a local array of the size nb_nodes * * nb_dof_per_node. The dof number is implicitly considered as * conn(el, n) * nb_nodes_per_element + d. * With 0 < n < nb_nodes_per_element and 0 < d < nb_dof_per_node **/ virtual void assembleElementalArrayLocalArray( const Array & elementary_vect, Array & array_assembeled, const ElementType & type, const GhostType & ghost_type, Real scale_factor = 1., const Array & filter_elements = empty_filter); /** * Assemble elementary values to the global residual array. The dof number is * implicitly considered as conn(el, n) * nb_nodes_per_element + d. * With 0 < n < nb_nodes_per_element and 0 < d < nb_dof_per_node **/ virtual void assembleElementalArrayToResidual( const ID & dof_id, const Array & elementary_vect, const ElementType & type, const GhostType & ghost_type, Real scale_factor = 1., const Array & filter_elements = empty_filter); /** * Assemble elementary values to a global array corresponding to a lumped * matrix */ virtual void assembleElementalArrayToLumpedMatrix( const ID & dof_id, const Array & elementary_vect, const ID & lumped_mtx, const ElementType & type, const GhostType & ghost_type, Real scale_factor = 1., const Array & filter_elements = empty_filter); /** * Assemble elementary values to the global residual array. The dof number is * implicitly considered as conn(el, n) * nb_nodes_per_element + d. With 0 < * n < nb_nodes_per_element and 0 < d < nb_dof_per_node **/ virtual void assembleElementalMatricesToMatrix( const ID & matrix_id, const ID & dof_id, const Array & elementary_mat, const ElementType & type, const GhostType & ghost_type = _not_ghost, const MatrixType & elemental_matrix_type = _symmetric, const Array & filter_elements = empty_filter) = 0; /// multiply a vector by a matrix and assemble the result to the residual virtual void assembleMatMulVectToResidual(const ID & dof_id, const ID & A_id, const Array & x, Real scale_factor = 1) = 0; + /// multiply the dofs by a matrix and assemble the result to the residual + virtual void assembleMatMulDOFsToResidual(const ID & A_id, + Real scale_factor = 1); + /// multiply a vector by a lumped matrix and assemble the result to the /// residual virtual void assembleLumpedMatMulVectToResidual(const ID & dof_id, const ID & A_id, const Array & x, Real scale_factor = 1) = 0; /// assemble coupling terms between to dofs virtual void assemblePreassembledMatrix(const ID & dof_id_m, const ID & dof_id_n, const ID & matrix_id, const TermsToAssemble & terms) = 0; /// sets the residual to 0 virtual void clearResidual() = 0; /// sets the matrix to 0 virtual void clearMatrix(const ID & mtx) = 0; /// sets the lumped matrix to 0 virtual void clearLumpedMatrix(const ID & mtx) = 0; /// splits the solution storage from a global view to the per dof storages void splitSolutionPerDOFs(); /// extract a lumped matrix part corresponding to a given dof virtual void getLumpedMatrixPerDOFs(const ID & dof_id, const ID & lumped_mtx, Array & lumped) = 0; protected: /// minimum functionality to implement per derived version of the DOFManager /// to allow the splitSolutionPerDOFs function to work virtual void getSolutionPerDOFs(const ID & dof_id, Array & solution_array) = 0; protected: /* ------------------------------------------------------------------------ */ /// register a matrix SparseMatrix & registerSparseMatrix(const ID & matrix_id, std::unique_ptr & matrix); /// register a non linear solver instantiated by a derived class NonLinearSolver & registerNonLinearSolver(const ID & non_linear_solver_id, std::unique_ptr & non_linear_solver); /// register a time step solver instantiated by a derived class TimeStepSolver & registerTimeStepSolver(const ID & time_step_solver_id, std::unique_ptr & time_step_solver); /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: /// Get the equation numbers corresponding to a dof_id. This might be used to /// access the matrix. inline const Array & getEquationsNumbers(const ID & dof_id) const; /// Global number of dofs AKANTU_GET_MACRO(SystemSize, this->system_size, UInt); /// Local number of dofs AKANTU_GET_MACRO(LocalSystemSize, this->local_system_size, UInt); /// Retrieve all the registered DOFs std::vector getDOFIDs() const; /* ------------------------------------------------------------------------ */ /* DOFs and derivatives accessors */ /* ------------------------------------------------------------------------ */ /// Get a reference to the registered dof array for a given id inline Array & getDOFs(const ID & dofs_id); /// Get the support type of a given dof inline DOFSupportType getSupportType(const ID & dofs_id) const; /// are the dofs registered inline bool hasDOFs(const ID & dofs_id) const; /// Get a reference to the registered dof derivatives array for a given id inline Array & getDOFsDerivatives(const ID & dofs_id, UInt order); /// Does the dof has derivatives inline bool hasDOFsDerivatives(const ID & dofs_id, UInt order) const; /// Get a reference to the blocked dofs array registered for the given id inline const Array & getBlockedDOFs(const ID & dofs_id) const; /// Does the dof has a blocked array inline bool hasBlockedDOFs(const ID & dofs_id) const; /// Get a reference to the registered dof increment array for a given id inline Array & getDOFsIncrement(const ID & dofs_id); /// Does the dof has a increment array inline bool hasDOFsIncrement(const ID & dofs_id) const; /// Does the dof has a previous array inline Array & getPreviousDOFs(const ID & dofs_id); /// Get a reference to the registered dof array for previous step values a /// given id inline bool hasPreviousDOFs(const ID & dofs_id) const; /// saves the values from dofs to previous dofs virtual void savePreviousDOFs(const ID & dofs_id); /// Get a reference to the solution array registered for the given id inline const Array & getSolution(const ID & dofs_id) const; /* ------------------------------------------------------------------------ */ /* Matrices accessors */ /* ------------------------------------------------------------------------ */ /// Get an instance of a new SparseMatrix virtual SparseMatrix & getNewMatrix(const ID & matrix_id, const MatrixType & matrix_type) = 0; /// Get an instance of a new SparseMatrix as a copy of the SparseMatrix /// matrix_to_copy_id virtual SparseMatrix & getNewMatrix(const ID & matrix_id, const ID & matrix_to_copy_id) = 0; /// Get the reference of an existing matrix SparseMatrix & getMatrix(const ID & matrix_id); /// check if the given matrix exists bool hasMatrix(const ID & matrix_id) const; /// Get an instance of a new lumped matrix virtual Array & getNewLumpedMatrix(const ID & matrix_id); /// Get the lumped version of a given matrix const Array & getLumpedMatrix(const ID & matrix_id) const; /// Get the lumped version of a given matrix Array & getLumpedMatrix(const ID & matrix_id); /// check if the given matrix exists bool hasLumpedMatrix(const ID & matrix_id) const; /* ------------------------------------------------------------------------ */ /* Non linear system solver */ /* ------------------------------------------------------------------------ */ /// Get instance of a non linear solver virtual NonLinearSolver & getNewNonLinearSolver( const ID & nls_solver_id, const NonLinearSolverType & _non_linear_solver_type) = 0; /// get instance of a non linear solver virtual NonLinearSolver & getNonLinearSolver(const ID & nls_solver_id); /// check if the given solver exists bool hasNonLinearSolver(const ID & solver_id) const; /* ------------------------------------------------------------------------ */ /* Time-Step Solver */ /* ------------------------------------------------------------------------ */ /// Get instance of a time step solver virtual TimeStepSolver & getNewTimeStepSolver(const ID & time_step_solver_id, const TimeStepSolverType & type, NonLinearSolver & non_linear_solver) = 0; /// get instance of a time step solver virtual TimeStepSolver & getTimeStepSolver(const ID & time_step_solver_id); /// check if the given solver exists bool hasTimeStepSolver(const ID & solver_id) const; /* ------------------------------------------------------------------------ */ const Mesh & getMesh() { if (mesh) { return *mesh; } else { AKANTU_EXCEPTION("No mesh registered in this dof manager"); } } /* ------------------------------------------------------------------------ */ AKANTU_GET_MACRO(Communicator, communicator, const auto &); AKANTU_GET_MACRO_NOT_CONST(Communicator, communicator, auto &); /* ------------------------------------------------------------------------ */ /* MeshEventHandler interface */ /* ------------------------------------------------------------------------ */ protected: /// helper function for the DOFManager::onNodesAdded method virtual std::pair updateNodalDOFs(const ID & dof_id, const Array & nodes_list); public: /// function to implement to react on akantu::NewNodesEvent void onNodesAdded(const Array & nodes_list, const NewNodesEvent & event) override; /// function to implement to react on akantu::RemovedNodesEvent void onNodesRemoved(const Array & nodes_list, const Array & new_numbering, const RemovedNodesEvent & event) override; /// function to implement to react on akantu::NewElementsEvent void onElementsAdded(const Array & elements_list, const NewElementsEvent & event) override; /// function to implement to react on akantu::RemovedElementsEvent void onElementsRemoved(const Array & elements_list, const ElementTypeMapArray & new_numbering, const RemovedElementsEvent & event) override; /// function to implement to react on akantu::ChangedElementsEvent void onElementsChanged(const Array & old_elements_list, const Array & new_elements_list, const ElementTypeMapArray & new_numbering, const ChangedElementsEvent & event) override; protected: struct DOFData; inline DOFData & getDOFData(const ID & dof_id); inline const DOFData & getDOFData(const ID & dof_id) const; template inline _DOFData & getDOFDataTyped(const ID & dof_id); template inline const _DOFData & getDOFDataTyped(const ID & dof_id) const; virtual DOFData & getNewDOFData(const ID & dof_id); /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ protected: /// dof representations in the dof manager struct DOFData { DOFData() = delete; explicit DOFData(const ID & dof_id); virtual ~DOFData(); /// DOF support type (nodal, general) this is needed to determine how the /// dof are shared among processors DOFSupportType support_type; ID group_support; /// Degree of freedom array Array * dof; /// Blocked degree of freedoms array Array * blocked_dofs; /// Degree of freedoms increment Array * increment; /// Degree of freedoms at previous step Array * previous; /// Solution associated to the dof Array solution; /// local numbering equation numbers Array local_equation_number; /* ---------------------------------------------------------------------- */ /* data for dynamic simulations */ /* ---------------------------------------------------------------------- */ /// Degree of freedom derivatives arrays std::vector *> dof_derivatives; }; /// type to store dofs information using DOFStorage = std::map>; /// type to store all the matrices using SparseMatricesMap = std::map>; /// type to store all the lumped matrices using LumpedMatricesMap = std::map>>; /// type to store all the non linear solver using NonLinearSolversMap = std::map>; /// type to store all the time step solver using TimeStepSolversMap = std::map>; /// store a reference to the dof arrays DOFStorage dofs; /// list of sparse matrices that where created SparseMatricesMap matrices; /// list of lumped matrices LumpedMatricesMap lumped_matrices; /// non linear solvers storage NonLinearSolversMap non_linear_solvers; /// time step solvers storage TimeStepSolversMap time_step_solvers; /// reference to the underlying mesh Mesh * mesh{nullptr}; /// Total number of degrees of freedom (size with the ghosts) UInt local_system_size{0}; /// Number of purely local dofs (size without the ghosts) UInt pure_local_system_size{0}; /// Total number of degrees of freedom UInt system_size{0}; /// Communicator used for this manager, should be the same as in the mesh if a /// mesh is registered Communicator & communicator; }; using DefaultDOFManagerFactory = Factory; using DOFManagerFactory = Factory; } // namespace akantu #include "dof_manager_inline_impl.cc" #endif /* __AKANTU_DOF_MANAGER_HH__ */ diff --git a/src/model/model_solver.cc b/src/model/model_solver.cc index f4ac5415c..78e97c629 100644 --- a/src/model/model_solver.cc +++ b/src/model/model_solver.cc @@ -1,366 +1,364 @@ /** * @file model_solver.cc * * @author Nicolas Richart * * @date Wed Aug 12 13:31:56 2015 * * @brief Implementation of ModelSolver * * @section LICENSE * * Copyright (©) 2010-2011 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "model_solver.hh" #include "dof_manager.hh" #include "dof_manager_default.hh" #include "mesh.hh" #include "non_linear_solver.hh" #include "time_step_solver.hh" #if defined(AKANTU_USE_PETSC) #include "dof_manager_petsc.hh" #endif /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ template static T getOptionToType(const std::string & opt_str) { std::stringstream sstr(opt_str); T opt; sstr >> opt; return opt; } /* -------------------------------------------------------------------------- */ ModelSolver::ModelSolver(Mesh & mesh, const ModelType & type, const ID & id, UInt memory_id) : Parsable(ParserType::_model, id), SolverCallback(), model_type(type), parent_id(id), parent_memory_id(memory_id), mesh(mesh), dof_manager(nullptr), default_solver_id("") {} /* -------------------------------------------------------------------------- */ ModelSolver::~ModelSolver() = default; /* -------------------------------------------------------------------------- */ std::tuple ModelSolver::getParserSection() { auto sub_sections = getStaticParser().getSubSections(ParserType::_model); auto it = std::find_if( sub_sections.begin(), sub_sections.end(), [&](auto && section) { ModelType type = getOptionToType(section.getName()); // default id should be the model type if not defined std::string name = section.getParameter("name", this->parent_id); return type == model_type and name == this->parent_id; }); if (it == sub_sections.end()) return std::make_tuple(ParserSection(), true); return std::make_tuple(*it, false); } /* -------------------------------------------------------------------------- */ void ModelSolver::initDOFManager() { // default without external solver activated at compilation same as mumps that // is the historical solver but with only the lumped solver ID solver_type = "default"; #if defined(AKANTU_USE_MUMPS) solver_type = "default"; #elif defined(AKANTU_USE_PETSC) solver_type = "petsc"; #endif ParserSection section; bool is_empty; std::tie(section, is_empty) = this->getParserSection(); if (not is_empty) { solver_type = section.getOption(solver_type); this->initDOFManager(section, solver_type); } else { this->initDOFManager(solver_type); } } /* -------------------------------------------------------------------------- */ void ModelSolver::initDOFManager(const ID & solver_type) { - try { this->dof_manager = DOFManagerFactory::getInstance().allocate( solver_type, mesh, this->parent_id + ":dof_manager" + solver_type, this->parent_memory_id); } catch (...) { AKANTU_EXCEPTION( "To use the solver " << solver_type << " you will have to code it. This is an unknown solver type."); } this->setDOFManager(*this->dof_manager); } /* -------------------------------------------------------------------------- */ void ModelSolver::initDOFManager(const ParserSection & section, const ID & solver_type) { this->initDOFManager(solver_type); auto sub_sections = section.getSubSections(ParserType::_time_step_solver); // parsing the time step solvers for (auto && section : sub_sections) { - ID type = section.getName(); ID solver_id = section.getParameter("name", type); auto tss_type = getOptionToType(type); auto tss_options = this->getDefaultSolverOptions(tss_type); auto sub_solvers_sect = section.getSubSections(ParserType::_non_linear_solver); auto nb_non_linear_solver_section = section.getNbSubSections(ParserType::_non_linear_solver); auto nls_type = tss_options.non_linear_solver_type; if (nb_non_linear_solver_section == 1) { auto && nls_section = *(sub_solvers_sect.first); nls_type = getOptionToType(nls_section.getName()); } else if (nb_non_linear_solver_section > 0) { AKANTU_EXCEPTION("More than one non linear solver are provided for the " "time step solver " << solver_id); } this->getNewSolver(solver_id, tss_type, nls_type); if (nb_non_linear_solver_section == 1) { const auto & nls_section = *(sub_solvers_sect.first); this->dof_manager->getNonLinearSolver(solver_id).parseSection( nls_section); } auto sub_integrator_sections = section.getSubSections(ParserType::_integration_scheme); for (auto && is_section : sub_integrator_sections) { const auto & dof_type_str = is_section.getName(); ID dof_id; try { ID tmp = is_section.getParameter("name"); dof_id = tmp; } catch (...) { AKANTU_EXCEPTION("No degree of freedom name specified for the " "integration scheme of type " << dof_type_str); } auto it_type = getOptionToType(dof_type_str); IntegrationScheme::SolutionType s_type = is_section.getParameter( "solution_type", tss_options.solution_type[dof_id]); this->setIntegrationScheme(solver_id, dof_id, it_type, s_type); } for (auto & is_type : tss_options.integration_scheme_type) { if (!this->hasIntegrationScheme(solver_id, is_type.first)) { this->setIntegrationScheme(solver_id, is_type.first, is_type.second, tss_options.solution_type[is_type.first]); } } } if (section.hasParameter("default_solver")) { ID default_solver = section.getParameter("default_solver"); if (this->hasSolver(default_solver)) { this->setDefaultSolver(default_solver); } else AKANTU_EXCEPTION( "The solver \"" << default_solver << "\" was not created, it cannot be set as default solver"); } } /* -------------------------------------------------------------------------- */ TimeStepSolver & ModelSolver::getSolver(const ID & solver_id) { ID tmp_solver_id = solver_id; if (tmp_solver_id == "") tmp_solver_id = this->default_solver_id; TimeStepSolver & tss = this->dof_manager->getTimeStepSolver(tmp_solver_id); return tss; } /* -------------------------------------------------------------------------- */ const TimeStepSolver & ModelSolver::getSolver(const ID & solver_id) const { ID tmp_solver_id = solver_id; if (solver_id == "") tmp_solver_id = this->default_solver_id; const TimeStepSolver & tss = this->dof_manager->getTimeStepSolver(tmp_solver_id); return tss; } /* -------------------------------------------------------------------------- */ TimeStepSolver & ModelSolver::getTimeStepSolver(const ID & solver_id) { return this->getSolver(solver_id); } /* -------------------------------------------------------------------------- */ const TimeStepSolver & ModelSolver::getTimeStepSolver(const ID & solver_id) const { return this->getSolver(solver_id); } /* -------------------------------------------------------------------------- */ NonLinearSolver & ModelSolver::getNonLinearSolver(const ID & solver_id) { return this->getSolver(solver_id).getNonLinearSolver(); } /* -------------------------------------------------------------------------- */ const NonLinearSolver & ModelSolver::getNonLinearSolver(const ID & solver_id) const { return this->getSolver(solver_id).getNonLinearSolver(); } /* -------------------------------------------------------------------------- */ bool ModelSolver::hasSolver(const ID & solver_id) const { ID tmp_solver_id = solver_id; if (solver_id == "") tmp_solver_id = this->default_solver_id; if (not this->dof_manager) { AKANTU_EXCEPTION("No DOF manager was initialized"); } return this->dof_manager->hasTimeStepSolver(tmp_solver_id); } /* -------------------------------------------------------------------------- */ void ModelSolver::setDefaultSolver(const ID & solver_id) { AKANTU_DEBUG_ASSERT( this->hasSolver(solver_id), "Cannot set the default solver to a solver that does not exists"); this->default_solver_id = solver_id; } /* -------------------------------------------------------------------------- */ void ModelSolver::solveStep(const ID & solver_id) { AKANTU_DEBUG_IN(); TimeStepSolver & tss = this->getSolver(solver_id); // make one non linear solve tss.solveStep(*this); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void ModelSolver::getNewSolver(const ID & solver_id, TimeStepSolverType time_step_solver_type, NonLinearSolverType non_linear_solver_type) { if (this->default_solver_id == "") { this->default_solver_id = solver_id; } if (non_linear_solver_type == _nls_auto) { switch (time_step_solver_type) { case _tsst_dynamic: case _tsst_static: non_linear_solver_type = _nls_newton_raphson; break; case _tsst_dynamic_lumped: non_linear_solver_type = _nls_lumped; break; case _tsst_not_defined: AKANTU_EXCEPTION(time_step_solver_type << " is not a valid time step solver type"); break; } } this->initSolver(time_step_solver_type, non_linear_solver_type); NonLinearSolver & nls = this->dof_manager->getNewNonLinearSolver( solver_id, non_linear_solver_type); this->dof_manager->getNewTimeStepSolver(solver_id, time_step_solver_type, nls); } /* -------------------------------------------------------------------------- */ Real ModelSolver::getTimeStep(const ID & solver_id) const { const TimeStepSolver & tss = this->getSolver(solver_id); return tss.getTimeStep(); } /* -------------------------------------------------------------------------- */ void ModelSolver::setTimeStep(Real time_step, const ID & solver_id) { TimeStepSolver & tss = this->getSolver(solver_id); return tss.setTimeStep(time_step); } /* -------------------------------------------------------------------------- */ void ModelSolver::setIntegrationScheme( const ID & solver_id, const ID & dof_id, const IntegrationSchemeType & integration_scheme_type, IntegrationScheme::SolutionType solution_type) { TimeStepSolver & tss = this->dof_manager->getTimeStepSolver(solver_id); tss.setIntegrationScheme(dof_id, integration_scheme_type, solution_type); } /* -------------------------------------------------------------------------- */ bool ModelSolver::hasDefaultSolver() const { return (this->default_solver_id != ""); } /* -------------------------------------------------------------------------- */ bool ModelSolver::hasIntegrationScheme(const ID & solver_id, const ID & dof_id) const { TimeStepSolver & tss = this->dof_manager->getTimeStepSolver(solver_id); return tss.hasIntegrationScheme(dof_id); } /* -------------------------------------------------------------------------- */ void ModelSolver::predictor() {} /* -------------------------------------------------------------------------- */ void ModelSolver::corrector() {} /* -------------------------------------------------------------------------- */ TimeStepSolverType ModelSolver::getDefaultSolverType() const { return _tsst_dynamic_lumped; } /* -------------------------------------------------------------------------- */ ModelSolverOptions ModelSolver::getDefaultSolverOptions(__attribute__((unused)) const TimeStepSolverType & type) const { ModelSolverOptions options; options.non_linear_solver_type = _nls_auto; return options; } } // namespace akantu diff --git a/src/model/non_linear_solver.cc b/src/model/non_linear_solver.cc index 41e34238f..a6333d41e 100644 --- a/src/model/non_linear_solver.cc +++ b/src/model/non_linear_solver.cc @@ -1,66 +1,78 @@ /** * @file non_linear_solver.cc * * @author Nicolas Richart * * @date Tue Oct 13 15:34:43 2015 * * @brief Implementation of the base class NonLinearSolver * * @section LICENSE * * Copyright (©) 2010-2011 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "non_linear_solver.hh" +#include "dof_manager.hh" #include "solver_callback.hh" /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ NonLinearSolver::NonLinearSolver( DOFManager & dof_manager, const NonLinearSolverType & non_linear_solver_type, const ID & id, UInt memory_id) : Memory(id, memory_id), Parsable(ParserType::_non_linear_solver, id), _dof_manager(dof_manager), non_linear_solver_type(non_linear_solver_type) { this->registerParam("type", this->non_linear_solver_type, _pat_parsable, "Non linear solver type"); } /* -------------------------------------------------------------------------- */ NonLinearSolver::~NonLinearSolver() = default; /* -------------------------------------------------------------------------- */ void NonLinearSolver::checkIfTypeIsSupported() { if (this->supported_type.find(this->non_linear_solver_type) == this->supported_type.end()) { AKANTU_EXCEPTION("The resolution method " << this->non_linear_solver_type << " is not implemented in the non linear solver " << this->id << "!"); } } /* -------------------------------------------------------------------------- */ +void NonLinearSolver::assembleResidual(SolverCallback & solver_callback) { + if (solver_callback.canSplitResidual() and + non_linear_solver_type == _nls_linear) { + this->_dof_manager.clearResidual(); + solver_callback.assembleResidual("external"); + this->_dof_manager.assembleMatMulDOFsToResidual("K", -1.); + solver_callback.assembleResidual("inertial"); + } else { + solver_callback.assembleResidual(); + } +} } // akantu diff --git a/src/model/non_linear_solver.hh b/src/model/non_linear_solver.hh index 56c8691ec..2cea85168 100644 --- a/src/model/non_linear_solver.hh +++ b/src/model/non_linear_solver.hh @@ -1,96 +1,98 @@ /** * @file non_linear_solver.hh * * @author Nicolas Richart * * @date Mon Aug 24 23:48:41 2015 * * @brief Non linear solver interface * * @section LICENSE * * Copyright (©) 2010-2011 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_common.hh" #include "aka_memory.hh" #include "parsable.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_NON_LINEAR_SOLVER_HH__ #define __AKANTU_NON_LINEAR_SOLVER_HH__ namespace akantu { class DOFManager; class SolverCallback; } namespace akantu { class NonLinearSolver : private Memory, public Parsable { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: NonLinearSolver(DOFManager & dof_manager, const NonLinearSolverType & non_linear_solver_type, const ID & id = "non_linear_solver", UInt memory_id = 0); ~NonLinearSolver() override; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ public: /// solve the system described by the jacobian matrix, and rhs contained in /// the dof manager virtual void solve(SolverCallback & callback) = 0; protected: void checkIfTypeIsSupported(); + void assembleResidual(SolverCallback & callback); + /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ protected: DOFManager & _dof_manager; /// type of non linear solver NonLinearSolverType non_linear_solver_type; /// list of supported non linear solver types std::set supported_type; }; namespace debug { class NLSNotConvergedException : public Exception { public: NLSNotConvergedException(Real threshold, UInt niter, Real error) : Exception("The non linear solver did not converge."), threshold(threshold), niter(niter), error(error) {} Real threshold; UInt niter; Real error; }; } } // akantu #endif /* __AKANTU_NON_LINEAR_SOLVER_HH__ */ diff --git a/src/model/non_linear_solver_linear.cc b/src/model/non_linear_solver_linear.cc index 8a8411b6e..4a5704d52 100644 --- a/src/model/non_linear_solver_linear.cc +++ b/src/model/non_linear_solver_linear.cc @@ -1,70 +1,73 @@ /** * @file non_linear_solver_linear.cc * * @author Nicolas Richart * * @date Tue Aug 25 00:57:00 2015 * * @brief Implementation of the default NonLinearSolver * * @section LICENSE * * Copyright (©) 2010-2011 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "non_linear_solver_linear.hh" #include "dof_manager_default.hh" #include "solver_callback.hh" /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ NonLinearSolverLinear::NonLinearSolverLinear( DOFManagerDefault & dof_manager, const NonLinearSolverType & non_linear_solver_type, const ID & id, UInt memory_id) : NonLinearSolver(dof_manager, non_linear_solver_type, id, memory_id), dof_manager(dof_manager), solver(dof_manager, "J", id + ":sparse_solver", memory_id) { this->supported_type.insert(_nls_linear); this->checkIfTypeIsSupported(); } /* -------------------------------------------------------------------------- */ NonLinearSolverLinear::~NonLinearSolverLinear() = default; /* ------------------------------------------------------------------------ */ void NonLinearSolverLinear::solve(SolverCallback & solver_callback) { this->dof_manager.updateGlobalBlockedDofs(); solver_callback.predictor(); - solver_callback.assembleResidual(); solver_callback.assembleMatrix("J"); + // Residual computed after J to allow the model to use K to compute the + // residual + this->assembleResidual(solver_callback); + this->solver.solve(); solver_callback.corrector(); } /* -------------------------------------------------------------------------- */ } // akantu diff --git a/src/model/non_linear_solver_newton_raphson.cc b/src/model/non_linear_solver_newton_raphson.cc index 11d5c44e1..540ac8dd0 100644 --- a/src/model/non_linear_solver_newton_raphson.cc +++ b/src/model/non_linear_solver_newton_raphson.cc @@ -1,186 +1,191 @@ /** * @file non_linear_solver_newton_raphson.cc * * @author Nicolas Richart * * @date Tue Aug 25 00:57:00 2015 * * @brief Implementation of the default NonLinearSolver * * @section LICENSE * * Copyright (©) 2010-2011 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "non_linear_solver_newton_raphson.hh" #include "communicator.hh" #include "dof_manager_default.hh" #include "solver_callback.hh" #include "sparse_solver_mumps.hh" /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ NonLinearSolverNewtonRaphson::NonLinearSolverNewtonRaphson( DOFManagerDefault & dof_manager, const NonLinearSolverType & non_linear_solver_type, const ID & id, UInt memory_id) : NonLinearSolver(dof_manager, non_linear_solver_type, id, memory_id), dof_manager(dof_manager), solver(std::make_unique( dof_manager, "J", id + ":sparse_solver", memory_id)) { this->supported_type.insert(_nls_newton_raphson_modified); this->supported_type.insert(_nls_newton_raphson); this->supported_type.insert(_nls_linear); this->checkIfTypeIsSupported(); this->registerParam("threshold", convergence_criteria, 1e-10, _pat_parsmod, "Threshold to consider results as converged"); this->registerParam("convergence_type", convergence_criteria_type, _scc_solution, _pat_parsmod, "Type of convergence criteria"); this->registerParam("max_iterations", max_iterations, 10, _pat_parsmod, "Max number of iterations"); this->registerParam("error", error, _pat_readable, "Last reached error"); this->registerParam("nb_iterations", n_iter, _pat_readable, "Last reached number of iterations"); this->registerParam("converged", converged, _pat_readable, "Did last solve converged"); this->registerParam("force_linear_recompute", force_linear_recompute, true, _pat_modifiable, "Force reassembly of the jacobian matrix"); } /* -------------------------------------------------------------------------- */ NonLinearSolverNewtonRaphson::~NonLinearSolverNewtonRaphson() = default; /* ------------------------------------------------------------------------ */ void NonLinearSolverNewtonRaphson::solve(SolverCallback & solver_callback) { this->dof_manager.updateGlobalBlockedDofs(); solver_callback.predictor(); - solver_callback.assembleResidual(); + if (non_linear_solver_type == _nls_linear and + solver_callback.canSplitResidual()) + solver_callback.assembleMatrix("K"); + + this->assembleResidual(solver_callback); if (this->non_linear_solver_type == _nls_newton_raphson_modified || (this->non_linear_solver_type == _nls_linear && this->force_linear_recompute)) { solver_callback.assembleMatrix("J"); this->force_linear_recompute = false; } this->n_iter = 0; this->converged = false; if (this->convergence_criteria_type == _scc_residual) { this->converged = this->testConvergence(this->dof_manager.getResidual()); if (this->converged) return; } do { if (this->non_linear_solver_type == _nls_newton_raphson) solver_callback.assembleMatrix("J"); this->solver->solve(); solver_callback.corrector(); // EventManager::sendEvent(NonLinearSolver::AfterSparseSolve(method)); if (this->convergence_criteria_type == _scc_residual) { - solver_callback.assembleResidual(); + this->assembleResidual(solver_callback); this->converged = this->testConvergence(this->dof_manager.getResidual()); } else { this->converged = this->testConvergence(this->dof_manager.getGlobalSolution()); } - if (this->convergence_criteria_type == _scc_solution && !this->converged) - solver_callback.assembleResidual(); + if (this->convergence_criteria_type == _scc_solution and + not this->converged) + this->assembleResidual(solver_callback); // this->dump(); this->n_iter++; AKANTU_DEBUG_INFO( "[" << this->convergence_criteria_type << "] Convergence iteration " << std::setw(std::log10(this->max_iterations)) << this->n_iter << ": error " << this->error << (this->converged ? " < " : " > ") << this->convergence_criteria); - } while (!this->converged && this->n_iter < this->max_iterations); + } while (not this->converged and this->n_iter < this->max_iterations); // this makes sure that you have correct strains and stresses after the // solveStep function (e.g., for dumping) if (this->convergence_criteria_type == _scc_solution) - solver_callback.assembleResidual(); + this->assembleResidual(solver_callback); if (this->converged) { // this->sendEvent(NonLinearSolver::ConvergedEvent(method)); } else if (this->n_iter == this->max_iterations) { AKANTU_CUSTOM_EXCEPTION(debug::NLSNotConvergedException( this->convergence_criteria, this->n_iter, this->error)); AKANTU_DEBUG_WARNING("[" << this->convergence_criteria_type << "] Convergence not reached after " << std::setw(std::log10(this->max_iterations)) << this->n_iter << " iteration" << (this->n_iter == 1 ? "" : "s") << "!"); } return; } /* -------------------------------------------------------------------------- */ bool NonLinearSolverNewtonRaphson::testConvergence(const Array & array) { AKANTU_DEBUG_IN(); const Array & blocked_dofs = this->dof_manager.getGlobalBlockedDOFs(); UInt nb_degree_of_freedoms = array.size(); auto arr_it = array.begin(); auto bld_it = blocked_dofs.begin(); Real norm = 0.; for (UInt n = 0; n < nb_degree_of_freedoms; ++n, ++arr_it, ++bld_it) { bool is_local_node = this->dof_manager.isLocalOrMasterDOF(n); if ((!*bld_it) && is_local_node) { norm += *arr_it * *arr_it; } } dof_manager.getCommunicator().allReduce(norm, SynchronizerOperation::_sum); norm = std::sqrt(norm); AKANTU_DEBUG_ASSERT(!Math::isnan(norm), "Something went wrong in the solve phase"); this->error = norm; return (error < this->convergence_criteria); } /* -------------------------------------------------------------------------- */ } // akantu diff --git a/src/model/solid_mechanics/material.cc b/src/model/solid_mechanics/material.cc index 51cbada6c..16058656f 100644 --- a/src/model/solid_mechanics/material.cc +++ b/src/model/solid_mechanics/material.cc @@ -1,1553 +1,1554 @@ /** * @file material.cc * * @author Aurelia Isabel Cuba Ramos * @author Daniel Pino Muñoz * @author Nicolas Richart * @author Marco Vocialta * * @date creation: Tue Jul 27 2010 * @date last modification: Tue Nov 24 2015 * * @brief Implementation of the common part of the material class * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "material.hh" #include "solid_mechanics_model.hh" /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ Material::Material(SolidMechanicsModel & model, const ID & id) : Memory(id, model.getMemoryID()), Parsable(ParserType::_material, id), is_init(false), fem(model.getFEEngine()), finite_deformation(false), name(""), model(model), spatial_dimension(this->model.getSpatialDimension()), element_filter("element_filter", id, this->memory_id), stress("stress", *this), eigengradu("eigen_grad_u", *this), gradu("grad_u", *this), green_strain("green_strain", *this), piola_kirchhoff_2("piola_kirchhoff_2", *this), potential_energy("potential_energy", *this), is_non_local(false), use_previous_stress(false), use_previous_gradu(false), interpolation_inverse_coordinates("interpolation inverse coordinates", *this), interpolation_points_matrices("interpolation points matrices", *this) { AKANTU_DEBUG_IN(); /// for each connectivity types allocate the element filer array of the /// material element_filter.initialize(model.getMesh(), _spatial_dimension = spatial_dimension); // model.getMesh().initElementTypeMapArray(element_filter, 1, // spatial_dimension, // false, _ek_regular); this->initialize(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ Material::Material(SolidMechanicsModel & model, UInt dim, const Mesh & mesh, FEEngine & fe_engine, const ID & id) : Memory(id, model.getMemoryID()), Parsable(ParserType::_material, id), is_init(false), fem(fe_engine), finite_deformation(false), name(""), model(model), spatial_dimension(dim), element_filter("element_filter", id, this->memory_id), stress("stress", *this, dim, fe_engine, this->element_filter), eigengradu("eigen_grad_u", *this, dim, fe_engine, this->element_filter), gradu("gradu", *this, dim, fe_engine, this->element_filter), green_strain("green_strain", *this, dim, fe_engine, this->element_filter), piola_kirchhoff_2("piola_kirchhoff_2", *this, dim, fe_engine, this->element_filter), potential_energy("potential_energy", *this, dim, fe_engine, this->element_filter), is_non_local(false), use_previous_stress(false), use_previous_gradu(false), interpolation_inverse_coordinates("interpolation inverse_coordinates", *this, dim, fe_engine, this->element_filter), interpolation_points_matrices("interpolation points matrices", *this, dim, fe_engine, this->element_filter) { AKANTU_DEBUG_IN(); element_filter.initialize(mesh, _spatial_dimension = spatial_dimension); // mesh.initElementTypeMapArray(element_filter, 1, spatial_dimension, false, // _ek_regular); this->initialize(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ Material::~Material() = default; /* -------------------------------------------------------------------------- */ void Material::initialize() { registerParam("rho", rho, Real(0.), _pat_parsable | _pat_modifiable, "Density"); registerParam("name", name, std::string(), _pat_parsable | _pat_readable); registerParam("finite_deformation", finite_deformation, false, _pat_parsable | _pat_readable, "Is finite deformation"); registerParam("inelastic_deformation", inelastic_deformation, false, _pat_internal, "Is inelastic deformation"); /// allocate gradu stress for local elements eigengradu.initialize(spatial_dimension * spatial_dimension); gradu.initialize(spatial_dimension * spatial_dimension); stress.initialize(spatial_dimension * spatial_dimension); potential_energy.initialize(1); this->model.registerEventHandler(*this); } /* -------------------------------------------------------------------------- */ void Material::initMaterial() { AKANTU_DEBUG_IN(); if (finite_deformation) { this->piola_kirchhoff_2.initialize(spatial_dimension * spatial_dimension); if (use_previous_stress) this->piola_kirchhoff_2.initializeHistory(); this->green_strain.initialize(spatial_dimension * spatial_dimension); } if (use_previous_stress) this->stress.initializeHistory(); if (use_previous_gradu) this->gradu.initializeHistory(); for (auto it = internal_vectors_real.begin(); it != internal_vectors_real.end(); ++it) it->second->resize(); for (auto it = internal_vectors_uint.begin(); it != internal_vectors_uint.end(); ++it) it->second->resize(); for (auto it = internal_vectors_bool.begin(); it != internal_vectors_bool.end(); ++it) it->second->resize(); is_init = true; AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void Material::savePreviousState() { AKANTU_DEBUG_IN(); for (auto it = internal_vectors_real.begin(); it != internal_vectors_real.end(); ++it) { if (it->second->hasHistory()) it->second->saveCurrentValues(); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ /** * Compute the residual by assembling @f$\int_{e} \sigma_e \frac{\partial * \varphi}{\partial X} dX @f$ * * @param[in] displacements nodes displacements * @param[in] ghost_type compute the residual for _ghost or _not_ghost element */ // void Material::updateResidual(GhostType ghost_type) { // AKANTU_DEBUG_IN(); // computeAllStresses(ghost_type); // assembleResidual(ghost_type); // AKANTU_DEBUG_OUT(); // } /* -------------------------------------------------------------------------- */ void Material::assembleInternalForces(GhostType ghost_type) { AKANTU_DEBUG_IN(); UInt spatial_dimension = model.getSpatialDimension(); if (!finite_deformation) { auto & internal_force = const_cast &>(model.getInternalForce()); // Mesh & mesh = fem.getMesh(); for (auto && type : element_filter.elementTypes(spatial_dimension, ghost_type)) { Array & elem_filter = element_filter(type, ghost_type); UInt nb_element = elem_filter.size(); if (nb_element == 0) continue; const Array & shapes_derivatives = fem.getShapesDerivatives(type, ghost_type); UInt size_of_shapes_derivatives = shapes_derivatives.getNbComponent(); UInt nb_quadrature_points = fem.getNbIntegrationPoints(type, ghost_type); UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type); /// compute @f$\sigma \frac{\partial \varphi}{\partial X}@f$ by /// @f$\mathbf{B}^t \mathbf{\sigma}_q@f$ Array * sigma_dphi_dx = new Array(nb_element * nb_quadrature_points, size_of_shapes_derivatives, "sigma_x_dphi_/_dX"); fem.computeBtD(stress(type, ghost_type), *sigma_dphi_dx, type, ghost_type, elem_filter); // Array * shapesd_filtered = // new Array(0, size_of_shapes_derivatives, "filtered shapesd"); // FEEngine::filterElementalData(mesh, shapes_derivatives, // *shapesd_filtered, // *it, ghost_type, elem_filter); // Array & stress_vect = this->stress(*it, ghost_type); // Array::matrix_iterator sigma = // stress_vect.begin(spatial_dimension, spatial_dimension); // Array::matrix_iterator B = // shapesd_filtered->begin(spatial_dimension, nb_nodes_per_element); // Array::matrix_iterator Bt_sigma_it = // sigma_dphi_dx->begin(spatial_dimension, nb_nodes_per_element); // for (UInt q = 0; q < nb_element * nb_quadrature_points; // ++q, ++sigma, ++B, ++Bt_sigma_it) // Bt_sigma_it->mul(*sigma, *B); // delete shapesd_filtered; /** * compute @f$\int \sigma * \frac{\partial \varphi}{\partial X}dX@f$ by * @f$ \sum_q \mathbf{B}^t * \mathbf{\sigma}_q \overline w_q J_q@f$ */ Array * int_sigma_dphi_dx = new Array(nb_element, nb_nodes_per_element * spatial_dimension, "int_sigma_x_dphi_/_dX"); fem.integrate(*sigma_dphi_dx, *int_sigma_dphi_dx, size_of_shapes_derivatives, type, ghost_type, elem_filter); delete sigma_dphi_dx; /// assemble model.getDOFManager().assembleElementalArrayLocalArray( *int_sigma_dphi_dx, internal_force, type, ghost_type, -1, elem_filter); delete int_sigma_dphi_dx; } } else { switch (spatial_dimension) { case 1: this->assembleInternalForces<1>(ghost_type); break; case 2: this->assembleInternalForces<2>(ghost_type); break; case 3: this->assembleInternalForces<3>(ghost_type); break; } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ /** * Compute the stress from the gradu * * @param[in] current_position nodes postition + displacements * @param[in] ghost_type compute the residual for _ghost or _not_ghost element */ void Material::computeAllStresses(GhostType ghost_type) { AKANTU_DEBUG_IN(); UInt spatial_dimension = model.getSpatialDimension(); for (const auto & type : element_filter.elementTypes(spatial_dimension, ghost_type)) { Array & elem_filter = element_filter(type, ghost_type); if (elem_filter.size() == 0) continue; Array & gradu_vect = gradu(type, ghost_type); /// compute @f$\nabla u@f$ fem.gradientOnIntegrationPoints(model.getDisplacement(), gradu_vect, spatial_dimension, type, ghost_type, elem_filter); gradu_vect -= eigengradu(type, ghost_type); /// compute @f$\mathbf{\sigma}_q@f$ from @f$\nabla u@f$ computeStress(type, ghost_type); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void Material::computeAllCauchyStresses(GhostType ghost_type) { AKANTU_DEBUG_IN(); AKANTU_DEBUG_ASSERT(finite_deformation, "The Cauchy stress can only be " "computed if you are working in " "finite deformation."); for (auto type : element_filter.elementTypes(spatial_dimension, ghost_type)) { switch (spatial_dimension) { case 1: this->computeCauchyStress<1>(type, ghost_type); break; case 2: this->computeCauchyStress<2>(type, ghost_type); break; case 3: this->computeCauchyStress<3>(type, ghost_type); break; } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void Material::computeCauchyStress(ElementType el_type, GhostType ghost_type) { AKANTU_DEBUG_IN(); Array::matrix_iterator gradu_it = this->gradu(el_type, ghost_type).begin(dim, dim); Array::matrix_iterator gradu_end = this->gradu(el_type, ghost_type).end(dim, dim); Array::matrix_iterator piola_it = this->piola_kirchhoff_2(el_type, ghost_type).begin(dim, dim); Array::matrix_iterator stress_it = this->stress(el_type, ghost_type).begin(dim, dim); Matrix F_tensor(dim, dim); for (; gradu_it != gradu_end; ++gradu_it, ++piola_it, ++stress_it) { Matrix & grad_u = *gradu_it; Matrix & piola = *piola_it; Matrix & sigma = *stress_it; gradUToF(grad_u, F_tensor); this->computeCauchyStressOnQuad(F_tensor, piola, sigma); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void Material::setToSteadyState(GhostType ghost_type) { AKANTU_DEBUG_IN(); const Array & displacement = model.getDisplacement(); // resizeInternalArray(gradu); UInt spatial_dimension = model.getSpatialDimension(); for (auto type : element_filter.elementTypes(spatial_dimension, ghost_type)) { Array & elem_filter = element_filter(type, ghost_type); Array & gradu_vect = gradu(type, ghost_type); /// compute @f$\nabla u@f$ fem.gradientOnIntegrationPoints(displacement, gradu_vect, spatial_dimension, type, ghost_type, elem_filter); setToSteadyState(type, ghost_type); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ /** * Compute the stiffness matrix by assembling @f$\int_{\omega} B^t \times D * \times B d\omega @f$ * * @param[in] current_position nodes postition + displacements * @param[in] ghost_type compute the residual for _ghost or _not_ghost element */ void Material::assembleStiffnessMatrix(GhostType ghost_type) { AKANTU_DEBUG_IN(); UInt spatial_dimension = model.getSpatialDimension(); for (auto type : element_filter.elementTypes(spatial_dimension, ghost_type)) { if (finite_deformation) { switch (spatial_dimension) { case 1: { assembleStiffnessMatrixNL<1>(type, ghost_type); assembleStiffnessMatrixL2<1>(type, ghost_type); break; } case 2: { assembleStiffnessMatrixNL<2>(type, ghost_type); assembleStiffnessMatrixL2<2>(type, ghost_type); break; } case 3: { assembleStiffnessMatrixNL<3>(type, ghost_type); assembleStiffnessMatrixL2<3>(type, ghost_type); break; } } } else { switch (spatial_dimension) { case 1: { assembleStiffnessMatrix<1>(type, ghost_type); break; } case 2: { assembleStiffnessMatrix<2>(type, ghost_type); break; } case 3: { assembleStiffnessMatrix<3>(type, ghost_type); break; } } } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void Material::assembleStiffnessMatrix(const ElementType & type, GhostType ghost_type) { AKANTU_DEBUG_IN(); Array & elem_filter = element_filter(type, ghost_type); if (elem_filter.size() == 0) { AKANTU_DEBUG_OUT(); return; } // const Array & shapes_derivatives = // fem.getShapesDerivatives(type, ghost_type); Array & gradu_vect = gradu(type, ghost_type); UInt nb_element = elem_filter.size(); UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type); UInt nb_quadrature_points = fem.getNbIntegrationPoints(type, ghost_type); gradu_vect.resize(nb_quadrature_points * nb_element); fem.gradientOnIntegrationPoints(model.getDisplacement(), gradu_vect, dim, type, ghost_type, elem_filter); UInt tangent_size = getTangentStiffnessVoigtSize(dim); Array * tangent_stiffness_matrix = new Array(nb_element * nb_quadrature_points, tangent_size * tangent_size, "tangent_stiffness_matrix"); tangent_stiffness_matrix->clear(); computeTangentModuli(type, *tangent_stiffness_matrix, ghost_type); // Array * shapesd_filtered = new Array( // nb_element, dim * nb_nodes_per_element, "filtered shapesd"); // fem.filterElementalData(fem.getMesh(), shapes_derivatives, // *shapesd_filtered, type, ghost_type, // elem_filter); /// compute @f$\mathbf{B}^t * \mathbf{D} * \mathbf{B}@f$ UInt bt_d_b_size = dim * nb_nodes_per_element; Array * bt_d_b = new Array(nb_element * nb_quadrature_points, bt_d_b_size * bt_d_b_size, "B^t*D*B"); fem.computeBtDB(*tangent_stiffness_matrix, *bt_d_b, 4, type, ghost_type, elem_filter); // Matrix B(tangent_size, dim * nb_nodes_per_element); // Matrix Bt_D(dim * nb_nodes_per_element, tangent_size); // Array::matrix_iterator shapes_derivatives_filtered_it = // shapesd_filtered->begin(dim, nb_nodes_per_element); // Array::matrix_iterator Bt_D_B_it = // bt_d_b->begin(dim * nb_nodes_per_element, dim * nb_nodes_per_element); // Array::matrix_iterator D_it = // tangent_stiffness_matrix->begin(tangent_size, tangent_size); // Array::matrix_iterator D_end = // tangent_stiffness_matrix->end(tangent_size, tangent_size); // for (; D_it != D_end; ++D_it, ++Bt_D_B_it, // ++shapes_derivatives_filtered_it) { // Matrix & D = *D_it; // Matrix & Bt_D_B = *Bt_D_B_it; // VoigtHelper::transferBMatrixToSymVoigtBMatrix( // *shapes_derivatives_filtered_it, B, nb_nodes_per_element); // Bt_D.mul(B, D); // Bt_D_B.mul(Bt_D, B); // } delete tangent_stiffness_matrix; // delete shapesd_filtered; /// compute @f$ k_e = \int_e \mathbf{B}^t * \mathbf{D} * \mathbf{B}@f$ Array * K_e = new Array(nb_element, bt_d_b_size * bt_d_b_size, "K_e"); fem.integrate(*bt_d_b, *K_e, bt_d_b_size * bt_d_b_size, type, ghost_type, elem_filter); delete bt_d_b; model.getDOFManager().assembleElementalMatricesToMatrix( "K", "displacement", *K_e, type, ghost_type, _symmetric, elem_filter); delete K_e; AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void Material::assembleStiffnessMatrixNL(const ElementType & type, GhostType ghost_type) { AKANTU_DEBUG_IN(); const Array & shapes_derivatives = fem.getShapesDerivatives(type, ghost_type); Array & elem_filter = element_filter(type, ghost_type); // Array & gradu_vect = delta_gradu(type, ghost_type); UInt nb_element = elem_filter.size(); UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type); UInt nb_quadrature_points = fem.getNbIntegrationPoints(type, ghost_type); Array * shapes_derivatives_filtered = new Array( nb_element * nb_quadrature_points, dim * nb_nodes_per_element, "shapes derivatives filtered"); fem.filterElementalData(fem.getMesh(), shapes_derivatives, *shapes_derivatives_filtered, type, ghost_type, elem_filter); /// compute @f$\mathbf{B}^t * \mathbf{D} * \mathbf{B}@f$ UInt bt_s_b_size = dim * nb_nodes_per_element; Array * bt_s_b = new Array(nb_element * nb_quadrature_points, bt_s_b_size * bt_s_b_size, "B^t*D*B"); UInt piola_matrix_size = getCauchyStressMatrixSize(dim); Matrix B(piola_matrix_size, bt_s_b_size); Matrix Bt_S(bt_s_b_size, piola_matrix_size); Matrix S(piola_matrix_size, piola_matrix_size); auto shapes_derivatives_filtered_it = shapes_derivatives_filtered->begin( spatial_dimension, nb_nodes_per_element); auto Bt_S_B_it = bt_s_b->begin(bt_s_b_size, bt_s_b_size); auto Bt_S_B_end = bt_s_b->end(bt_s_b_size, bt_s_b_size); auto piola_it = piola_kirchhoff_2(type, ghost_type).begin(dim, dim); for (; Bt_S_B_it != Bt_S_B_end; ++Bt_S_B_it, ++shapes_derivatives_filtered_it, ++piola_it) { auto & Bt_S_B = *Bt_S_B_it; const auto & Piola_kirchhoff_matrix = *piola_it; setCauchyStressMatrix(Piola_kirchhoff_matrix, S); VoigtHelper::transferBMatrixToBNL(*shapes_derivatives_filtered_it, B, nb_nodes_per_element); Bt_S.template mul(B, S); Bt_S_B.template mul(Bt_S, B); } delete shapes_derivatives_filtered; /// compute @f$ k_e = \int_e \mathbf{B}^t * \mathbf{D} * \mathbf{B}@f$ Array * K_e = new Array(nb_element, bt_s_b_size * bt_s_b_size, "K_e"); fem.integrate(*bt_s_b, *K_e, bt_s_b_size * bt_s_b_size, type, ghost_type, elem_filter); delete bt_s_b; model.getDOFManager().assembleElementalMatricesToMatrix( "K", "displacement", *K_e, type, ghost_type, _symmetric, elem_filter); delete K_e; AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void Material::assembleStiffnessMatrixL2(const ElementType & type, GhostType ghost_type) { AKANTU_DEBUG_IN(); const Array & shapes_derivatives = fem.getShapesDerivatives(type, ghost_type); Array & elem_filter = element_filter(type, ghost_type); Array & gradu_vect = gradu(type, ghost_type); UInt nb_element = elem_filter.size(); UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type); UInt nb_quadrature_points = fem.getNbIntegrationPoints(type, ghost_type); gradu_vect.resize(nb_quadrature_points * nb_element); fem.gradientOnIntegrationPoints(model.getDisplacement(), gradu_vect, dim, type, ghost_type, elem_filter); UInt tangent_size = getTangentStiffnessVoigtSize(dim); Array * tangent_stiffness_matrix = new Array(nb_element * nb_quadrature_points, tangent_size * tangent_size, "tangent_stiffness_matrix"); tangent_stiffness_matrix->clear(); computeTangentModuli(type, *tangent_stiffness_matrix, ghost_type); Array * shapes_derivatives_filtered = new Array( nb_element * nb_quadrature_points, dim * nb_nodes_per_element, "shapes derivatives filtered"); fem.filterElementalData(fem.getMesh(), shapes_derivatives, *shapes_derivatives_filtered, type, ghost_type, elem_filter); /// compute @f$\mathbf{B}^t * \mathbf{D} * \mathbf{B}@f$ UInt bt_d_b_size = dim * nb_nodes_per_element; Array * bt_d_b = new Array(nb_element * nb_quadrature_points, bt_d_b_size * bt_d_b_size, "B^t*D*B"); Matrix B(tangent_size, dim * nb_nodes_per_element); Matrix B2(tangent_size, dim * nb_nodes_per_element); Matrix Bt_D(dim * nb_nodes_per_element, tangent_size); auto shapes_derivatives_filtered_it = shapes_derivatives_filtered->begin( spatial_dimension, nb_nodes_per_element); auto Bt_D_B_it = bt_d_b->begin(bt_d_b_size, bt_d_b_size); auto grad_u_it = gradu_vect.begin(dim, dim); auto D_it = tangent_stiffness_matrix->begin(tangent_size, tangent_size); auto D_end = tangent_stiffness_matrix->end(tangent_size, tangent_size); for (; D_it != D_end; ++D_it, ++Bt_D_B_it, ++shapes_derivatives_filtered_it, ++grad_u_it) { const auto & grad_u = *grad_u_it; const auto & D = *D_it; auto & Bt_D_B = *Bt_D_B_it; // transferBMatrixToBL1 (*shapes_derivatives_filtered_it, B, // nb_nodes_per_element); VoigtHelper::transferBMatrixToSymVoigtBMatrix( *shapes_derivatives_filtered_it, B, nb_nodes_per_element); VoigtHelper::transferBMatrixToBL2(*shapes_derivatives_filtered_it, grad_u, B2, nb_nodes_per_element); B += B2; Bt_D.template mul(B, D); Bt_D_B.template mul(Bt_D, B); } delete tangent_stiffness_matrix; delete shapes_derivatives_filtered; /// compute @f$ k_e = \int_e \mathbf{B}^t * \mathbf{D} * \mathbf{B}@f$ Array * K_e = new Array(nb_element, bt_d_b_size * bt_d_b_size, "K_e"); fem.integrate(*bt_d_b, *K_e, bt_d_b_size * bt_d_b_size, type, ghost_type, elem_filter); delete bt_d_b; model.getDOFManager().assembleElementalMatricesToMatrix( "K", "displacement", *K_e, type, ghost_type, _symmetric, elem_filter); delete K_e; AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void Material::assembleInternalForces(GhostType ghost_type) { AKANTU_DEBUG_IN(); Array & internal_force = model.getInternalForce(); Mesh & mesh = fem.getMesh(); for (auto type : element_filter.elementTypes(spatial_dimension, ghost_type)) { const Array & shapes_derivatives = fem.getShapesDerivatives(type, ghost_type); Array & elem_filter = element_filter(type, ghost_type); if (elem_filter.size() == 0) continue; UInt size_of_shapes_derivatives = shapes_derivatives.getNbComponent(); UInt nb_element = elem_filter.size(); UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type); UInt nb_quadrature_points = fem.getNbIntegrationPoints(type, ghost_type); Array * shapesd_filtered = new Array( nb_element, size_of_shapes_derivatives, "filtered shapesd"); fem.filterElementalData(mesh, shapes_derivatives, *shapesd_filtered, type, ghost_type, elem_filter); Array::matrix_iterator shapes_derivatives_filtered_it = shapesd_filtered->begin(dim, nb_nodes_per_element); // Set stress vectors UInt stress_size = getTangentStiffnessVoigtSize(dim); // Set matrices B and BNL* UInt bt_s_size = dim * nb_nodes_per_element; auto * bt_s = new Array(nb_element * nb_quadrature_points, bt_s_size, "B^t*S"); auto grad_u_it = this->gradu(type, ghost_type).begin(dim, dim); auto grad_u_end = this->gradu(type, ghost_type).end(dim, dim); auto stress_it = this->piola_kirchhoff_2(type, ghost_type).begin(dim, dim); shapes_derivatives_filtered_it = shapesd_filtered->begin(dim, nb_nodes_per_element); Array::matrix_iterator bt_s_it = bt_s->begin(bt_s_size, 1); Matrix S_vect(stress_size, 1); Matrix B_tensor(stress_size, bt_s_size); Matrix B2_tensor(stress_size, bt_s_size); for (; grad_u_it != grad_u_end; ++grad_u_it, ++stress_it, ++shapes_derivatives_filtered_it, ++bt_s_it) { auto & grad_u = *grad_u_it; auto & r_it = *bt_s_it; auto & S_it = *stress_it; setCauchyStressArray(S_it, S_vect); VoigtHelper::transferBMatrixToSymVoigtBMatrix( *shapes_derivatives_filtered_it, B_tensor, nb_nodes_per_element); VoigtHelper::transferBMatrixToBL2(*shapes_derivatives_filtered_it, grad_u, B2_tensor, nb_nodes_per_element); B_tensor += B2_tensor; r_it.template mul(B_tensor, S_vect); } delete shapesd_filtered; /// compute @f$ k_e = \int_e \mathbf{B}^t * \mathbf{D} * \mathbf{B}@f$ Array * r_e = new Array(nb_element, bt_s_size, "r_e"); fem.integrate(*bt_s, *r_e, bt_s_size, type, ghost_type, elem_filter); delete bt_s; model.getDOFManager().assembleElementalArrayLocalArray( *r_e, internal_force, type, ghost_type, -1., elem_filter); delete r_e; } AKANTU_DEBUG_OUT(); } // /* -------------------------------------------------------------------------- */ // void Material::computeAllStressesFromTangentModuli(GhostType ghost_type) { // AKANTU_DEBUG_IN(); // UInt spatial_dimension = model.getSpatialDimension(); // for (auto type : element_filter.elementTypes(spatial_dimension, ghost_type)) { // switch (spatial_dimension) { // case 1: { // computeAllStressesFromTangentModuli<1>(type, ghost_type); // break; // } // case 2: { // computeAllStressesFromTangentModuli<2>(type, ghost_type); // break; // } // case 3: { // computeAllStressesFromTangentModuli<3>(type, ghost_type); // break; // } // } // } // AKANTU_DEBUG_OUT(); // } // /* -------------------------------------------------------------------------- */ // template // void Material::computeAllStressesFromTangentModuli(const ElementType & type, // GhostType ghost_type) { // AKANTU_DEBUG_IN(); // const Array & shapes_derivatives = // fem.getShapesDerivatives(type, ghost_type); // Array & elem_filter = element_filter(type, ghost_type); // Array & gradu_vect = gradu(type, ghost_type); // UInt nb_element = elem_filter.size(); // if (nb_element) { // UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type); // UInt nb_quadrature_points = fem.getNbIntegrationPoints(type, ghost_type); // gradu_vect.resize(nb_quadrature_points * nb_element); // Array & disp = model.getDisplacement(); // fem.gradientOnIntegrationPoints(disp, gradu_vect, dim, type, ghost_type, // elem_filter); // UInt tangent_moduli_size = getTangentStiffnessVoigtSize(dim); // Array * tangent_moduli_tensors = new Array( // nb_element * nb_quadrature_points, // tangent_moduli_size * tangent_moduli_size, "tangent_moduli_tensors"); // tangent_moduli_tensors->clear(); // computeTangentModuli(type, *tangent_moduli_tensors, ghost_type); // Array * shapesd_filtered = new Array( // nb_element, dim * nb_nodes_per_element, "filtered shapesd"); // FEEngine::filterElementalData(fem.getMesh(), shapes_derivatives, // *shapesd_filtered, type, ghost_type, // elem_filter); // Array filtered_u(nb_element, // nb_nodes_per_element * spatial_dimension); // fem.extractNodalToElementField(fem.getMesh(), disp, filtered_u, type, // ghost_type, elem_filter); // /// compute @f$\mathbf{D} \mathbf{B} \mathbf{u}@f$ // auto shapes_derivatives_filtered_it = // shapesd_filtered->begin(dim, nb_nodes_per_element); // auto D_it = // tangent_moduli_tensors->begin(tangent_moduli_size, tangent_moduli_size); // auto sigma_it = // stress(type, ghost_type).begin(spatial_dimension, spatial_dimension); // auto u_it = filtered_u.begin(spatial_dimension * nb_nodes_per_element); // Matrix B(tangent_moduli_size, // spatial_dimension * nb_nodes_per_element); // Vector Bu(tangent_moduli_size); // Vector DBu(tangent_moduli_size); // for (UInt e = 0; e < nb_element; ++e, ++u_it) { // for (UInt q = 0; q < nb_quadrature_points; // ++q, ++D_it, ++shapes_derivatives_filtered_it, ++sigma_it) { // const auto & u = *u_it; // const auto & D = *D_it; // auto & sigma = *sigma_it; // VoigtHelper::transferBMatrixToSymVoigtBMatrix( // *shapes_derivatives_filtered_it, B, nb_nodes_per_element); // Bu.mul(B, u); // DBu.mul(D, Bu); // // Voigt notation to full symmetric tensor // for (UInt i = 0; i < dim; ++i) // sigma(i, i) = DBu(i); // if (dim == 2) { // sigma(0, 1) = sigma(1, 0) = DBu(2); // } else if (dim == 3) { // sigma(1, 2) = sigma(2, 1) = DBu(3); // sigma(0, 2) = sigma(2, 0) = DBu(4); // sigma(0, 1) = sigma(1, 0) = DBu(5); // } // } // } // } // AKANTU_DEBUG_OUT(); // } /* -------------------------------------------------------------------------- */ void Material::computePotentialEnergyByElements() { AKANTU_DEBUG_IN(); for (auto type : element_filter.elementTypes(spatial_dimension, _not_ghost)) { computePotentialEnergy(type); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void Material::computePotentialEnergy(ElementType, GhostType) { AKANTU_DEBUG_IN(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ Real Material::getPotentialEnergy() { AKANTU_DEBUG_IN(); Real epot = 0.; computePotentialEnergyByElements(); /// integrate the potential energy for each type of elements for (auto type : element_filter.elementTypes(spatial_dimension, _not_ghost)) { epot += fem.integrate(potential_energy(type, _not_ghost), type, _not_ghost, element_filter(type, _not_ghost)); } AKANTU_DEBUG_OUT(); return epot; } /* -------------------------------------------------------------------------- */ Real Material::getPotentialEnergy(ElementType & type, UInt index) { AKANTU_DEBUG_IN(); Real epot = 0.; Vector epot_on_quad_points(fem.getNbIntegrationPoints(type)); computePotentialEnergyByElement(type, index, epot_on_quad_points); epot = fem.integrate(epot_on_quad_points, type, element_filter(type)(index)); AKANTU_DEBUG_OUT(); return epot; } /* -------------------------------------------------------------------------- */ Real Material::getEnergy(const std::string & type) { AKANTU_DEBUG_IN(); if (type == "potential") return getPotentialEnergy(); AKANTU_DEBUG_OUT(); return 0.; } /* -------------------------------------------------------------------------- */ Real Material::getEnergy(const std::string & energy_id, ElementType type, UInt index) { AKANTU_DEBUG_IN(); if (energy_id == "potential") return getPotentialEnergy(type, index); AKANTU_DEBUG_OUT(); return 0.; } /* -------------------------------------------------------------------------- */ void Material::initElementalFieldInterpolation( const ElementTypeMapArray & interpolation_points_coordinates) { AKANTU_DEBUG_IN(); this->fem.initElementalFieldInterpolationFromIntegrationPoints( interpolation_points_coordinates, this->interpolation_points_matrices, this->interpolation_inverse_coordinates, &(this->element_filter)); + AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void Material::interpolateStress(ElementTypeMapArray & result, const GhostType ghost_type) { this->fem.interpolateElementalFieldFromIntegrationPoints( this->stress, this->interpolation_points_matrices, this->interpolation_inverse_coordinates, result, ghost_type, &(this->element_filter)); } /* -------------------------------------------------------------------------- */ void Material::interpolateStressOnFacets( ElementTypeMapArray & result, ElementTypeMapArray & by_elem_result, const GhostType ghost_type) { interpolateStress(by_elem_result, ghost_type); UInt stress_size = this->stress.getNbComponent(); const Mesh & mesh = this->model.getMesh(); const Mesh & mesh_facets = mesh.getMeshFacets(); for (auto type : element_filter.elementTypes(spatial_dimension, ghost_type)) { Array & elem_fil = element_filter(type, ghost_type); Array & by_elem_res = by_elem_result(type, ghost_type); UInt nb_element = elem_fil.size(); UInt nb_element_full = this->model.getMesh().getNbElement(type, ghost_type); UInt nb_interpolation_points_per_elem = by_elem_res.size() / nb_element_full; const Array & facet_to_element = mesh_facets.getSubelementToElement(type, ghost_type); ElementType type_facet = Mesh::getFacetType(type); UInt nb_facet_per_elem = facet_to_element.getNbComponent(); UInt nb_quad_per_facet = nb_interpolation_points_per_elem / nb_facet_per_elem; Element element_for_comparison{type, 0, ghost_type}; const Array> * element_to_facet = nullptr; GhostType current_ghost_type = _casper; Array * result_vec = nullptr; Array::const_matrix_iterator result_it = by_elem_res.begin_reinterpret( stress_size, nb_interpolation_points_per_elem, nb_element_full); for (UInt el = 0; el < nb_element; ++el) { UInt global_el = elem_fil(el); element_for_comparison.element = global_el; for (UInt f = 0; f < nb_facet_per_elem; ++f) { Element facet_elem = facet_to_element(global_el, f); UInt global_facet = facet_elem.element; if (facet_elem.ghost_type != current_ghost_type) { current_ghost_type = facet_elem.ghost_type; element_to_facet = &mesh_facets.getElementToSubelement( type_facet, current_ghost_type); result_vec = &result(type_facet, current_ghost_type); } bool is_second_element = (*element_to_facet)(global_facet)[0] != element_for_comparison; for (UInt q = 0; q < nb_quad_per_facet; ++q) { Vector result_local(result_vec->storage() + (global_facet * nb_quad_per_facet + q) * result_vec->getNbComponent() + is_second_element * stress_size, stress_size); const Matrix & result_tmp(result_it[global_el]); result_local = result_tmp(f * nb_quad_per_facet + q); } } } } } /* -------------------------------------------------------------------------- */ template const Array & Material::getArray(__attribute__((unused)) const ID & vect_id, __attribute__((unused)) const ElementType & type, __attribute__((unused)) const GhostType & ghost_type) const { AKANTU_DEBUG_TO_IMPLEMENT(); return NULL; } /* -------------------------------------------------------------------------- */ template Array & Material::getArray(__attribute__((unused)) const ID & vect_id, __attribute__((unused)) const ElementType & type, __attribute__((unused)) const GhostType & ghost_type) { AKANTU_DEBUG_TO_IMPLEMENT(); return NULL; } /* -------------------------------------------------------------------------- */ template <> const Array & Material::getArray(const ID & vect_id, const ElementType & type, const GhostType & ghost_type) const { std::stringstream sstr; std::string ghost_id = ""; if (ghost_type == _ghost) ghost_id = ":ghost"; sstr << getID() << ":" << vect_id << ":" << type << ghost_id; ID fvect_id = sstr.str(); try { return Memory::getArray(fvect_id); } catch (debug::Exception & e) { AKANTU_SILENT_EXCEPTION("The material " << name << "(" << getID() << ") does not contain a vector " << vect_id << "(" << fvect_id << ") [" << e << "]"); } } /* -------------------------------------------------------------------------- */ template <> Array & Material::getArray(const ID & vect_id, const ElementType & type, const GhostType & ghost_type) { std::stringstream sstr; std::string ghost_id = ""; if (ghost_type == _ghost) ghost_id = ":ghost"; sstr << getID() << ":" << vect_id << ":" << type << ghost_id; ID fvect_id = sstr.str(); try { return Memory::getArray(fvect_id); } catch (debug::Exception & e) { AKANTU_SILENT_EXCEPTION("The material " << name << "(" << getID() << ") does not contain a vector " << vect_id << "(" << fvect_id << ") [" << e << "]"); } } /* -------------------------------------------------------------------------- */ template <> const Array & Material::getArray(const ID & vect_id, const ElementType & type, const GhostType & ghost_type) const { std::stringstream sstr; std::string ghost_id = ""; if (ghost_type == _ghost) ghost_id = ":ghost"; sstr << getID() << ":" << vect_id << ":" << type << ghost_id; ID fvect_id = sstr.str(); try { return Memory::getArray(fvect_id); } catch (debug::Exception & e) { AKANTU_SILENT_EXCEPTION("The material " << name << "(" << getID() << ") does not contain a vector " << vect_id << "(" << fvect_id << ") [" << e << "]"); } } /* -------------------------------------------------------------------------- */ template <> Array & Material::getArray(const ID & vect_id, const ElementType & type, const GhostType & ghost_type) { std::stringstream sstr; std::string ghost_id = ""; if (ghost_type == _ghost) ghost_id = ":ghost"; sstr << getID() << ":" << vect_id << ":" << type << ghost_id; ID fvect_id = sstr.str(); try { return Memory::getArray(fvect_id); } catch (debug::Exception & e) { AKANTU_SILENT_EXCEPTION("The material " << name << "(" << getID() << ") does not contain a vector " << vect_id << "(" << fvect_id << ") [" << e << "]"); } } /* -------------------------------------------------------------------------- */ template const InternalField & Material::getInternal(__attribute__((unused)) const ID & int_id) const { AKANTU_DEBUG_TO_IMPLEMENT(); return NULL; } /* -------------------------------------------------------------------------- */ template InternalField & Material::getInternal(__attribute__((unused)) const ID & int_id) { AKANTU_DEBUG_TO_IMPLEMENT(); return NULL; } /* -------------------------------------------------------------------------- */ template <> const InternalField & Material::getInternal(const ID & int_id) const { auto it = internal_vectors_real.find(getID() + ":" + int_id); if (it == internal_vectors_real.end()) { AKANTU_SILENT_EXCEPTION("The material " << name << "(" << getID() << ") does not contain an internal " << int_id << " (" << (getID() + ":" + int_id) << ")"); } return *it->second; } /* -------------------------------------------------------------------------- */ template <> InternalField & Material::getInternal(const ID & int_id) { auto it = internal_vectors_real.find(getID() + ":" + int_id); if (it == internal_vectors_real.end()) { AKANTU_SILENT_EXCEPTION("The material " << name << "(" << getID() << ") does not contain an internal " << int_id << " (" << (getID() + ":" + int_id) << ")"); } return *it->second; } /* -------------------------------------------------------------------------- */ template <> const InternalField & Material::getInternal(const ID & int_id) const { auto it = internal_vectors_uint.find(getID() + ":" + int_id); if (it == internal_vectors_uint.end()) { AKANTU_SILENT_EXCEPTION("The material " << name << "(" << getID() << ") does not contain an internal " << int_id << " (" << (getID() + ":" + int_id) << ")"); } return *it->second; } /* -------------------------------------------------------------------------- */ template <> InternalField & Material::getInternal(const ID & int_id) { auto it = internal_vectors_uint.find(getID() + ":" + int_id); if (it == internal_vectors_uint.end()) { AKANTU_SILENT_EXCEPTION("The material " << name << "(" << getID() << ") does not contain an internal " << int_id << " (" << (getID() + ":" + int_id) << ")"); } return *it->second; } /* -------------------------------------------------------------------------- */ void Material::addElements(const Array & elements_to_add) { AKANTU_DEBUG_IN(); UInt mat_id = model.getInternalIndexFromID(getID()); Array::const_iterator el_begin = elements_to_add.begin(); Array::const_iterator el_end = elements_to_add.end(); for (; el_begin != el_end; ++el_begin) { const Element & element = *el_begin; Array & mat_indexes = model.getMaterialByElement(element.type, element.ghost_type); Array & mat_loc_num = model.getMaterialLocalNumbering(element.type, element.ghost_type); UInt index = this->addElement(element.type, element.element, element.ghost_type); mat_indexes(element.element) = mat_id; mat_loc_num(element.element) = index; } this->resizeInternals(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void Material::removeElements(const Array & elements_to_remove) { AKANTU_DEBUG_IN(); Array::const_iterator el_begin = elements_to_remove.begin(); Array::const_iterator el_end = elements_to_remove.end(); if (el_begin == el_end) return; ElementTypeMapArray material_local_new_numbering( "remove mat filter elem", getID(), getMemoryID()); Element element; for (ghost_type_t::iterator gt = ghost_type_t::begin(); gt != ghost_type_t::end(); ++gt) { GhostType ghost_type = *gt; element.ghost_type = ghost_type; ElementTypeMapArray::type_iterator it = element_filter.firstType(_all_dimensions, ghost_type, _ek_not_defined); ElementTypeMapArray::type_iterator end = element_filter.lastType(_all_dimensions, ghost_type, _ek_not_defined); for (; it != end; ++it) { ElementType type = *it; element.type = type; Array & elem_filter = this->element_filter(type, ghost_type); Array & mat_loc_num = this->model.getMaterialLocalNumbering(type, ghost_type); if (!material_local_new_numbering.exists(type, ghost_type)) material_local_new_numbering.alloc(elem_filter.size(), 1, type, ghost_type); Array & mat_renumbering = material_local_new_numbering(type, ghost_type); UInt nb_element = elem_filter.size(); Array elem_filter_tmp; UInt new_id = 0; for (UInt el = 0; el < nb_element; ++el) { element.element = elem_filter(el); if (std::find(el_begin, el_end, element) == el_end) { elem_filter_tmp.push_back(element.element); mat_renumbering(el) = new_id; mat_loc_num(element.element) = new_id; ++new_id; } else { mat_renumbering(el) = UInt(-1); } } elem_filter.resize(elem_filter_tmp.size()); elem_filter.copy(elem_filter_tmp); } } for (auto it = internal_vectors_real.begin(); it != internal_vectors_real.end(); ++it) it->second->removeIntegrationPoints(material_local_new_numbering); for (auto it = internal_vectors_uint.begin(); it != internal_vectors_uint.end(); ++it) it->second->removeIntegrationPoints(material_local_new_numbering); for (auto it = internal_vectors_bool.begin(); it != internal_vectors_bool.end(); ++it) it->second->removeIntegrationPoints(material_local_new_numbering); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void Material::resizeInternals() { AKANTU_DEBUG_IN(); for (auto it = internal_vectors_real.begin(); it != internal_vectors_real.end(); ++it) it->second->resize(); for (auto it = internal_vectors_uint.begin(); it != internal_vectors_uint.end(); ++it) it->second->resize(); for (auto it = internal_vectors_bool.begin(); it != internal_vectors_bool.end(); ++it) it->second->resize(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void Material::onElementsAdded(const Array &, const NewElementsEvent &) { this->resizeInternals(); } /* -------------------------------------------------------------------------- */ void Material::onElementsRemoved( const Array & element_list, const ElementTypeMapArray & new_numbering, __attribute__((unused)) const RemovedElementsEvent & event) { UInt my_num = model.getInternalIndexFromID(getID()); ElementTypeMapArray material_local_new_numbering( "remove mat filter elem", getID(), getMemoryID()); Array::const_iterator el_begin = element_list.begin(); Array::const_iterator el_end = element_list.end(); for (ghost_type_t::iterator g = ghost_type_t::begin(); g != ghost_type_t::end(); ++g) { GhostType gt = *g; ElementTypeMapArray::type_iterator it = new_numbering.firstType(_all_dimensions, gt, _ek_not_defined); ElementTypeMapArray::type_iterator end = new_numbering.lastType(_all_dimensions, gt, _ek_not_defined); for (; it != end; ++it) { ElementType type = *it; if (element_filter.exists(type, gt) && element_filter(type, gt).size()) { Array & elem_filter = element_filter(type, gt); Array & mat_indexes = this->model.getMaterialByElement(*it, gt); Array & mat_loc_num = this->model.getMaterialLocalNumbering(*it, gt); UInt nb_element = this->model.getMesh().getNbElement(type, gt); // all materials will resize of the same size... mat_indexes.resize(nb_element); mat_loc_num.resize(nb_element); if (!material_local_new_numbering.exists(type, gt)) material_local_new_numbering.alloc(elem_filter.size(), 1, type, gt); Array & mat_renumbering = material_local_new_numbering(type, gt); const Array & renumbering = new_numbering(type, gt); Array elem_filter_tmp; UInt ni = 0; Element el{type, 0, gt}; for (UInt i = 0; i < elem_filter.size(); ++i) { el.element = elem_filter(i); if (std::find(el_begin, el_end, el) == el_end) { UInt new_el = renumbering(el.element); AKANTU_DEBUG_ASSERT( new_el != UInt(-1), "A not removed element as been badly renumbered"); elem_filter_tmp.push_back(new_el); mat_renumbering(i) = ni; mat_indexes(new_el) = my_num; mat_loc_num(new_el) = ni; ++ni; } else { mat_renumbering(i) = UInt(-1); } } elem_filter.resize(elem_filter_tmp.size()); elem_filter.copy(elem_filter_tmp); } } } for (auto it = internal_vectors_real.begin(); it != internal_vectors_real.end(); ++it) it->second->removeIntegrationPoints(material_local_new_numbering); for (auto it = internal_vectors_uint.begin(); it != internal_vectors_uint.end(); ++it) it->second->removeIntegrationPoints(material_local_new_numbering); for (auto it = internal_vectors_bool.begin(); it != internal_vectors_bool.end(); ++it) it->second->removeIntegrationPoints(material_local_new_numbering); } /* -------------------------------------------------------------------------- */ void Material::beforeSolveStep() { this->savePreviousState(); } /* -------------------------------------------------------------------------- */ void Material::afterSolveStep() { for (auto & type : element_filter.elementTypes(_all_dimensions, _not_ghost, _ek_not_defined)) { this->updateEnergies(type, _not_ghost); } } /* -------------------------------------------------------------------------- */ void Material::onDamageIteration() { this->savePreviousState(); } /* -------------------------------------------------------------------------- */ void Material::onDamageUpdate() { ElementTypeMapArray::type_iterator it = this->element_filter.firstType( _all_dimensions, _not_ghost, _ek_not_defined); ElementTypeMapArray::type_iterator end = element_filter.lastType(_all_dimensions, _not_ghost, _ek_not_defined); for (; it != end; ++it) { this->updateEnergiesAfterDamage(*it, _not_ghost); } } /* -------------------------------------------------------------------------- */ void Material::onDump() { if (this->isFiniteDeformation()) this->computeAllCauchyStresses(_not_ghost); } /* -------------------------------------------------------------------------- */ void Material::printself(std::ostream & stream, int indent) const { std::string space; for (Int i = 0; i < indent; i++, space += AKANTU_INDENT) ; std::string type = getID().substr(getID().find_last_of(':') + 1); stream << space << "Material " << type << " [" << std::endl; Parsable::printself(stream, indent); stream << space << "]" << std::endl; } /* -------------------------------------------------------------------------- */ /// extrapolate internal values void Material::extrapolateInternal(const ID & id, const Element & element, __attribute__((unused)) const Matrix & point, Matrix & extrapolated) { if (this->isInternal(id, element.kind())) { UInt nb_element = this->element_filter(element.type, element.ghost_type).size(); const ID name = this->getID() + ":" + id; UInt nb_quads = this->internal_vectors_real[name]->getFEEngine().getNbIntegrationPoints( element.type, element.ghost_type); const Array & internal = this->getArray(id, element.type, element.ghost_type); UInt nb_component = internal.getNbComponent(); Array::const_matrix_iterator internal_it = internal.begin_reinterpret(nb_component, nb_quads, nb_element); Element local_element = this->convertToLocalElement(element); /// instead of really extrapolating, here the value of the first GP /// is copied into the result vector. This works only for linear /// elements /// @todo extrapolate!!!! AKANTU_DEBUG_WARNING("This is a fix, values are not truly extrapolated"); const Matrix & values = internal_it[local_element.element]; UInt index = 0; Vector tmp(nb_component); for (UInt j = 0; j < values.cols(); ++j) { tmp = values(j); if (tmp.norm() > 0) { index = j; break; } } for (UInt i = 0; i < extrapolated.size(); ++i) { extrapolated(i) = values(index); } } else { Matrix default_values(extrapolated.rows(), extrapolated.cols(), 0.); extrapolated = default_values; } } /* -------------------------------------------------------------------------- */ void Material::applyEigenGradU(const Matrix & prescribed_eigen_grad_u, const GhostType ghost_type) { for (auto && type : element_filter.elementTypes(_all_dimensions, _not_ghost, _ek_not_defined)) { if (!element_filter(type, ghost_type).size()) continue; auto eigen_it = this->eigengradu(type, ghost_type) .begin(spatial_dimension, spatial_dimension); auto eigen_end = this->eigengradu(type, ghost_type) .end(spatial_dimension, spatial_dimension); for (; eigen_it != eigen_end; ++eigen_it) { auto & current_eigengradu = *eigen_it; current_eigengradu = prescribed_eigen_grad_u; } } } } // namespace akantu diff --git a/src/model/solid_mechanics/material.hh b/src/model/solid_mechanics/material.hh index 0a0505dee..a3f68b552 100644 --- a/src/model/solid_mechanics/material.hh +++ b/src/model/solid_mechanics/material.hh @@ -1,681 +1,683 @@ + /** * @file material.hh * * @author Daniel Pino Muñoz * @author Nicolas Richart * @author Marco Vocialta * * @date creation: Fri Jun 18 2010 * @date last modification: Wed Nov 25 2015 * * @brief Mother class for all materials * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_factory.hh" #include "aka_voigthelper.hh" #include "data_accessor.hh" #include "integration_point.hh" #include "parsable.hh" #include "parser.hh" /* -------------------------------------------------------------------------- */ #include "internal_field.hh" #include "random_internal_field.hh" /* -------------------------------------------------------------------------- */ #include "mesh_events.hh" #include "solid_mechanics_model_event_handler.hh" /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_MATERIAL_HH__ #define __AKANTU_MATERIAL_HH__ /* -------------------------------------------------------------------------- */ namespace akantu { class Model; class SolidMechanicsModel; } // namespace akantu namespace akantu { /** * Interface of all materials * Prerequisites for a new material * - inherit from this class * - implement the following methods: * \code * virtual Real getStableTimeStep(Real h, const Element & element = * ElementNull); * * virtual void computeStress(ElementType el_type, * GhostType ghost_type = _not_ghost); * * virtual void computeTangentStiffness(const ElementType & el_type, * Array & tangent_matrix, * GhostType ghost_type = _not_ghost); * \endcode * */ class Material : public Memory, public DataAccessor, public Parsable, public MeshEventHandler, protected SolidMechanicsModelEventHandler { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: #if __cplusplus > 199711L Material(const Material & mat) = delete; Material & operator=(const Material & mat) = delete; #endif /// Initialize material with defaults Material(SolidMechanicsModel & model, const ID & id = ""); /// Initialize material with custom mesh & fe_engine Material(SolidMechanicsModel & model, UInt dim, const Mesh & mesh, FEEngine & fe_engine, const ID & id = ""); /// Destructor ~Material() override; protected: void initialize(); /* ------------------------------------------------------------------------ */ /* Function that materials can/should reimplement */ /* ------------------------------------------------------------------------ */ protected: /// constitutive law virtual void computeStress(__attribute__((unused)) ElementType el_type, __attribute__((unused)) GhostType ghost_type = _not_ghost) { AKANTU_DEBUG_TO_IMPLEMENT(); } /// compute the tangent stiffness matrix virtual void computeTangentModuli(__attribute__((unused)) const ElementType & el_type, __attribute__((unused)) Array & tangent_matrix, __attribute__((unused)) GhostType ghost_type = _not_ghost) { AKANTU_DEBUG_TO_IMPLEMENT(); } /// compute the potential energy virtual void computePotentialEnergy(ElementType el_type, GhostType ghost_type = _not_ghost); /// compute the potential energy for an element virtual void computePotentialEnergyByElement(__attribute__((unused)) ElementType type, __attribute__((unused)) UInt index, __attribute__((unused)) Vector & epot_on_quad_points) { AKANTU_DEBUG_TO_IMPLEMENT(); } virtual void updateEnergies(__attribute__((unused)) ElementType el_type, __attribute__((unused)) GhostType ghost_type = _not_ghost) {} virtual void updateEnergiesAfterDamage(__attribute__((unused)) ElementType el_type, __attribute__((unused)) GhostType ghost_type = _not_ghost) {} /// set the material to steady state (to be implemented for materials that /// need it) virtual void setToSteadyState(__attribute__((unused)) ElementType el_type, __attribute__((unused)) GhostType ghost_type = _not_ghost) {} /// function called to update the internal parameters when the modifiable /// parameters are modified virtual void updateInternalParameters() {} public: /// extrapolate internal values virtual void extrapolateInternal(const ID & id, const Element & element, const Matrix & points, Matrix & extrapolated); /// compute the p-wave speed in the material virtual Real getPushWaveSpeed(__attribute__((unused)) const Element & element) const { AKANTU_DEBUG_TO_IMPLEMENT(); } /// compute the s-wave speed in the material virtual Real getShearWaveSpeed(__attribute__((unused)) const Element & element) const { AKANTU_DEBUG_TO_IMPLEMENT(); } /// get a material celerity to compute the stable time step (default: is the /// push wave speed) virtual Real getCelerity(const Element & element) const { return getPushWaveSpeed(element); } /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ public: template void registerInternal(__attribute__((unused)) InternalField & vect) { AKANTU_DEBUG_TO_IMPLEMENT(); } template void unregisterInternal(__attribute__((unused)) InternalField & vect) { AKANTU_DEBUG_TO_IMPLEMENT(); } /// initialize the material computed parameter virtual void initMaterial(); /// compute the residual for this material // virtual void updateResidual(GhostType ghost_type = _not_ghost); /// assemble the residual for this material virtual void assembleInternalForces(GhostType ghost_type); /// save the stress in the previous_stress if needed virtual void savePreviousState(); /// compute the stresses for this material virtual void computeAllStresses(GhostType ghost_type = _not_ghost); // virtual void // computeAllStressesFromTangentModuli(GhostType ghost_type = _not_ghost); virtual void computeAllCauchyStresses(GhostType ghost_type = _not_ghost); /// set material to steady state void setToSteadyState(GhostType ghost_type = _not_ghost); /// compute the stiffness matrix virtual void assembleStiffnessMatrix(GhostType ghost_type); /// add an element to the local mesh filter inline UInt addElement(const ElementType & type, UInt element, const GhostType & ghost_type); inline UInt addElement(const Element & element); /// add many elements at once void addElements(const Array & elements_to_add); /// remove many element at once void removeElements(const Array & elements_to_remove); /// function to print the contain of the class void printself(std::ostream & stream, int indent = 0) const override; /** * interpolate stress on given positions for each element by means * of a geometrical interpolation on quadrature points */ void interpolateStress(ElementTypeMapArray & result, const GhostType ghost_type = _not_ghost); /** * interpolate stress on given positions for each element by means * of a geometrical interpolation on quadrature points and store the * results per facet */ void interpolateStressOnFacets(ElementTypeMapArray & result, ElementTypeMapArray & by_elem_result, const GhostType ghost_type = _not_ghost); /** * function to initialize the elemental field interpolation * function by inverting the quadrature points' coordinates */ void initElementalFieldInterpolation( const ElementTypeMapArray & interpolation_points_coordinates); /* ------------------------------------------------------------------------ */ /* Common part */ /* ------------------------------------------------------------------------ */ protected: /* ------------------------------------------------------------------------ */ inline UInt getTangentStiffnessVoigtSize(UInt spatial_dimension) const; /// compute the potential energy by element void computePotentialEnergyByElements(); /// resize the intenals arrays virtual void resizeInternals(); /* ------------------------------------------------------------------------ */ /* Finite deformation functions */ /* This functions area implementing what is described in the paper of Bathe */ /* et al, in IJNME, Finite Element Formulations For Large Deformation */ /* Dynamic Analysis, Vol 9, 353-386, 1975 */ /* ------------------------------------------------------------------------ */ protected: /// assemble the residual template void assembleInternalForces(GhostType ghost_type); /// Computation of Cauchy stress tensor in the case of finite deformation from /// the 2nd Piola-Kirchhoff for a given element type template void computeCauchyStress(ElementType el_type, GhostType ghost_type = _not_ghost); /// Computation the Cauchy stress the 2nd Piola-Kirchhoff and the deformation /// gradient template inline void computeCauchyStressOnQuad(const Matrix & F, const Matrix & S, Matrix & cauchy, const Real & C33 = 1.0) const; template void computeAllStressesFromTangentModuli(const ElementType & type, GhostType ghost_type); template void assembleStiffnessMatrix(const ElementType & type, GhostType ghost_type); /// assembling in finite deformation template void assembleStiffnessMatrixNL(const ElementType & type, GhostType ghost_type); template void assembleStiffnessMatrixL2(const ElementType & type, GhostType ghost_type); /// Size of the Stress matrix for the case of finite deformation see: Bathe et /// al, IJNME, Vol 9, 353-386, 1975 inline UInt getCauchyStressMatrixSize(UInt spatial_dimension) const; /// Sets the stress matrix according to Bathe et al, IJNME, Vol 9, 353-386, /// 1975 template inline void setCauchyStressMatrix(const Matrix & S_t, Matrix & sigma); /// write the stress tensor in the Voigt notation. template inline void setCauchyStressArray(const Matrix & S_t, Matrix & sigma_voight); /* ------------------------------------------------------------------------ */ /* Conversion functions */ /* ------------------------------------------------------------------------ */ public: template static inline void gradUToF(const Matrix & grad_u, Matrix & F); static inline void rightCauchy(const Matrix & F, Matrix & C); static inline void leftCauchy(const Matrix & F, Matrix & B); template static inline void gradUToEpsilon(const Matrix & grad_u, Matrix & epsilon); template static inline void gradUToGreenStrain(const Matrix & grad_u, Matrix & epsilon); static inline Real stressToVonMises(const Matrix & stress); protected: /// converts global element to local element inline Element convertToLocalElement(const Element & global_element) const; /// converts local element to global element inline Element convertToGlobalElement(const Element & local_element) const; /// converts global quadrature point to local quadrature point inline IntegrationPoint convertToLocalPoint(const IntegrationPoint & global_point) const; /// converts local quadrature point to global quadrature point inline IntegrationPoint convertToGlobalPoint(const IntegrationPoint & local_point) const; /* ------------------------------------------------------------------------ */ /* DataAccessor inherited members */ /* ------------------------------------------------------------------------ */ public: inline UInt getNbData(const Array & elements, const SynchronizationTag & tag) const override; inline void packData(CommunicationBuffer & buffer, const Array & elements, const SynchronizationTag & tag) const override; inline void unpackData(CommunicationBuffer & buffer, const Array & elements, const SynchronizationTag & tag) override; template inline void packElementDataHelper(const ElementTypeMapArray & data_to_pack, CommunicationBuffer & buffer, const Array & elements, const ID & fem_id = ID()) const; template inline void unpackElementDataHelper(ElementTypeMapArray & data_to_unpack, CommunicationBuffer & buffer, const Array & elements, const ID & fem_id = ID()); /* ------------------------------------------------------------------------ */ /* MeshEventHandler inherited members */ /* ------------------------------------------------------------------------ */ public: /* ------------------------------------------------------------------------ */ void onNodesAdded(const Array &, const NewNodesEvent &) override{}; void onNodesRemoved(const Array &, const Array &, const RemovedNodesEvent &) override{}; void onElementsAdded(const Array & element_list, const NewElementsEvent & event) override; void onElementsRemoved(const Array & element_list, const ElementTypeMapArray & new_numbering, const RemovedElementsEvent & event) override; void onElementsChanged(const Array &, const Array &, const ElementTypeMapArray &, const ChangedElementsEvent &) override{}; /* ------------------------------------------------------------------------ */ /* SolidMechanicsModelEventHandler inherited members */ /* ------------------------------------------------------------------------ */ public: virtual void beforeSolveStep(); virtual void afterSolveStep(); void onDamageIteration() override; void onDamageUpdate() override; void onDump() override; /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: AKANTU_GET_MACRO(Name, name, const std::string &); AKANTU_GET_MACRO(Model, model, const SolidMechanicsModel &) AKANTU_GET_MACRO(ID, Memory::getID(), const ID &); AKANTU_GET_MACRO(Rho, rho, Real); AKANTU_SET_MACRO(Rho, rho, Real); AKANTU_GET_MACRO(SpatialDimension, spatial_dimension, UInt); /// return the potential energy for the subset of elements contained by the /// material Real getPotentialEnergy(); /// return the potential energy for the provided element Real getPotentialEnergy(ElementType & type, UInt index); /// return the energy (identified by id) for the subset of elements contained /// by the material virtual Real getEnergy(const std::string & energy_id); /// return the energy (identified by id) for the provided element virtual Real getEnergy(const std::string & energy_id, ElementType type, UInt index); AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(ElementFilter, element_filter, UInt); AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(GradU, gradu, Real); AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(Stress, stress, Real); AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(PotentialEnergy, potential_energy, Real); AKANTU_GET_MACRO(GradU, gradu, const ElementTypeMapArray &); AKANTU_GET_MACRO(Stress, stress, const ElementTypeMapArray &); AKANTU_GET_MACRO(ElementFilter, element_filter, const ElementTypeMapArray &); AKANTU_GET_MACRO(FEEngine, fem, FEEngine &); bool isNonLocal() const { return is_non_local; } template const Array & getArray(const ID & id, const ElementType & type, const GhostType & ghost_type = _not_ghost) const; template Array & getArray(const ID & id, const ElementType & type, const GhostType & ghost_type = _not_ghost); template const InternalField & getInternal(const ID & id) const; template InternalField & getInternal(const ID & id); template inline bool isInternal(const ID & id, const ElementKind & element_kind) const; template ElementTypeMap getInternalDataPerElem(const ID & id, const ElementKind & element_kind) const; bool isFiniteDeformation() const { return finite_deformation; } bool isInelasticDeformation() const { return inelastic_deformation; } template inline void setParam(const ID & param, T value); + template inline void setParamNoUpdate(const ID & param, T value); inline const Parameter & getParam(const ID & param) const; template void flattenInternal(const std::string & field_id, ElementTypeMapArray & internal_flat, const GhostType ghost_type = _not_ghost, ElementKind element_kind = _ek_not_defined) const; /// apply a constant eigengrad_u everywhere in the material virtual void applyEigenGradU(const Matrix & prescribed_eigen_grad_u, const GhostType = _not_ghost); /// specify if the matrix need to be recomputed for this material virtual bool hasStiffnessMatrixChanged() { return true; } protected: bool isInit() const { return is_init; } /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ protected: /// boolean to know if the material has been initialized bool is_init; std::map *> internal_vectors_real; std::map *> internal_vectors_uint; std::map *> internal_vectors_bool; protected: /// Link to the fem object in the model FEEngine & fem; /// Finite deformation bool finite_deformation; /// Finite deformation bool inelastic_deformation; /// material name std::string name; /// The model to witch the material belong SolidMechanicsModel & model; /// density : rho Real rho; /// spatial dimension UInt spatial_dimension; /// list of element handled by the material ElementTypeMapArray element_filter; /// stresses arrays ordered by element types InternalField stress; /// eigengrad_u arrays ordered by element types InternalField eigengradu; /// grad_u arrays ordered by element types InternalField gradu; /// Green Lagrange strain (Finite deformation) InternalField green_strain; /// Second Piola-Kirchhoff stress tensor arrays ordered by element types /// (Finite deformation) InternalField piola_kirchhoff_2; /// potential energy by element InternalField potential_energy; /// tell if using in non local mode or not bool is_non_local; /// tell if the material need the previous stress state bool use_previous_stress; /// tell if the material need the previous strain state bool use_previous_gradu; /// elemental field interpolation coordinates InternalField interpolation_inverse_coordinates; /// elemental field interpolation points InternalField interpolation_points_matrices; /// vector that contains the names of all the internals that need to /// be transferred when material interfaces move std::vector internals_to_transfer; }; /// standard output stream operator inline std::ostream & operator<<(std::ostream & stream, const Material & _this) { _this.printself(stream); return stream; } } // namespace akantu #include "material_inline_impl.cc" #include "internal_field_tmpl.hh" #include "random_internal_field_tmpl.hh" /* -------------------------------------------------------------------------- */ /* Auto loop */ /* -------------------------------------------------------------------------- */ /// This can be used to automatically write the loop on quadrature points in /// functions such as computeStress. This macro in addition to write the loop /// provides two tensors (matrices) sigma and grad_u #define MATERIAL_STRESS_QUADRATURE_POINT_LOOP_BEGIN(el_type, ghost_type) \ Array::matrix_iterator gradu_it = \ this->gradu(el_type, ghost_type) \ .begin(this->spatial_dimension, this->spatial_dimension); \ Array::matrix_iterator gradu_end = \ this->gradu(el_type, ghost_type) \ .end(this->spatial_dimension, this->spatial_dimension); \ \ this->stress(el_type, ghost_type) \ .resize(this->gradu(el_type, ghost_type).size()); \ \ Array::iterator> stress_it = \ this->stress(el_type, ghost_type) \ .begin(this->spatial_dimension, this->spatial_dimension); \ \ if (this->isFiniteDeformation()) { \ this->piola_kirchhoff_2(el_type, ghost_type) \ .resize(this->gradu(el_type, ghost_type).size()); \ stress_it = this->piola_kirchhoff_2(el_type, ghost_type) \ .begin(this->spatial_dimension, this->spatial_dimension); \ } \ \ for (; gradu_it != gradu_end; ++gradu_it, ++stress_it) { \ Matrix & __attribute__((unused)) grad_u = *gradu_it; \ Matrix & __attribute__((unused)) sigma = *stress_it #define MATERIAL_STRESS_QUADRATURE_POINT_LOOP_END } /// This can be used to automatically write the loop on quadrature points in /// functions such as computeTangentModuli. This macro in addition to write the /// loop provides two tensors (matrices) sigma_tensor, grad_u, and a matrix /// where the elemental tangent moduli should be stored in Voigt Notation #define MATERIAL_TANGENT_QUADRATURE_POINT_LOOP_BEGIN(tangent_mat) \ Array::matrix_iterator gradu_it = \ this->gradu(el_type, ghost_type) \ .begin(this->spatial_dimension, this->spatial_dimension); \ Array::matrix_iterator gradu_end = \ this->gradu(el_type, ghost_type) \ .end(this->spatial_dimension, this->spatial_dimension); \ Array::matrix_iterator sigma_it = \ this->stress(el_type, ghost_type) \ .begin(this->spatial_dimension, this->spatial_dimension); \ \ tangent_mat.resize(this->gradu(el_type, ghost_type).size()); \ \ UInt tangent_size = \ this->getTangentStiffnessVoigtSize(this->spatial_dimension); \ Array::matrix_iterator tangent_it = \ tangent_mat.begin(tangent_size, tangent_size); \ \ for (; gradu_it != gradu_end; ++gradu_it, ++sigma_it, ++tangent_it) { \ Matrix & __attribute__((unused)) grad_u = *gradu_it; \ Matrix & __attribute__((unused)) sigma_tensor = *sigma_it; \ Matrix & tangent = *tangent_it #define MATERIAL_TANGENT_QUADRATURE_POINT_LOOP_END } /* -------------------------------------------------------------------------- */ namespace akantu { using MaterialFactory = Factory; } // namespace akantu #define INSTANTIATE_MATERIAL_ONLY(mat_name) \ template class mat_name<1>; \ template class mat_name<2>; \ template class mat_name<3> #define MATERIAL_DEFAULT_PER_DIM_ALLOCATOR(id, mat_name) \ [](UInt dim, const ID &, SolidMechanicsModel & model, \ const ID & id) -> std::unique_ptr { \ switch (dim) { \ case 1: \ return std::make_unique>(model, id); \ case 2: \ return std::make_unique>(model, id); \ case 3: \ return std::make_unique>(model, id); \ default: \ AKANTU_EXCEPTION("The dimension " \ << dim << "is not a valid dimension for the material " \ << #id); \ } \ } #define INSTANTIATE_MATERIAL(id, mat_name) \ INSTANTIATE_MATERIAL_ONLY(mat_name); \ static bool material_is_alocated_##id [[gnu::unused]] = \ MaterialFactory::getInstance().registerAllocator( \ #id, MATERIAL_DEFAULT_PER_DIM_ALLOCATOR(id, mat_name)) #endif /* __AKANTU_MATERIAL_HH__ */ diff --git a/src/model/solid_mechanics/material_inline_impl.cc b/src/model/solid_mechanics/material_inline_impl.cc index f6141480e..1f6d1c0dd 100644 --- a/src/model/solid_mechanics/material_inline_impl.cc +++ b/src/model/solid_mechanics/material_inline_impl.cc @@ -1,463 +1,475 @@ /** * @file material_inline_impl.cc * * @author Daniel Pino Muñoz * @author Nicolas Richart * @author Marco Vocialta * * @date creation: Tue Jul 27 2010 * @date last modification: Wed Nov 25 2015 * * @brief Implementation of the inline functions of the class material * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "solid_mechanics_model.hh" /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_MATERIAL_INLINE_IMPL_CC__ #define __AKANTU_MATERIAL_INLINE_IMPL_CC__ namespace akantu { /* -------------------------------------------------------------------------- */ inline UInt Material::addElement(const ElementType & type, UInt element, const GhostType & ghost_type) { Array & el_filter = this->element_filter(type, ghost_type); el_filter.push_back(element); return el_filter.size() - 1; } /* -------------------------------------------------------------------------- */ inline UInt Material::addElement(const Element & element) { return this->addElement(element.type, element.element, element.ghost_type); } /* -------------------------------------------------------------------------- */ inline UInt Material::getTangentStiffnessVoigtSize(UInt dim) const { return (dim * (dim - 1) / 2 + dim); } /* -------------------------------------------------------------------------- */ inline UInt Material::getCauchyStressMatrixSize(UInt dim) const { return (dim * dim); } /* -------------------------------------------------------------------------- */ template inline void Material::gradUToF(const Matrix & grad_u, Matrix & F) { AKANTU_DEBUG_ASSERT(F.size() >= grad_u.size() && grad_u.size() == dim * dim, "The dimension of the tensor F should be greater or " "equal to the dimension of the tensor grad_u."); F.eye(); for (UInt i = 0; i < dim; ++i) for (UInt j = 0; j < dim; ++j) F(i, j) += grad_u(i, j); } /* -------------------------------------------------------------------------- */ template inline void Material::computeCauchyStressOnQuad(const Matrix & F, const Matrix & piola, Matrix & sigma, const Real & C33) const { Real J = F.det() * sqrt(C33); Matrix F_S(dim, dim); F_S.mul(F, piola); Real constant = J ? 1. / J : 0; sigma.mul(F_S, F, constant); } /* -------------------------------------------------------------------------- */ inline void Material::rightCauchy(const Matrix & F, Matrix & C) { C.mul(F, F); } /* -------------------------------------------------------------------------- */ inline void Material::leftCauchy(const Matrix & F, Matrix & B) { B.mul(F, F); } /* -------------------------------------------------------------------------- */ template inline void Material::gradUToEpsilon(const Matrix & grad_u, Matrix & epsilon) { for (UInt i = 0; i < dim; ++i) for (UInt j = 0; j < dim; ++j) epsilon(i, j) = 0.5 * (grad_u(i, j) + grad_u(j, i)); } /* -------------------------------------------------------------------------- */ template inline void Material::gradUToGreenStrain(const Matrix & grad_u, Matrix & epsilon) { epsilon.mul(grad_u, grad_u, .5); for (UInt i = 0; i < dim; ++i) for (UInt j = 0; j < dim; ++j) epsilon(i, j) += 0.5 * (grad_u(i, j) + grad_u(j, i)); } /* -------------------------------------------------------------------------- */ inline Real Material::stressToVonMises(const Matrix & stress) { // compute deviatoric stress UInt dim = stress.cols(); Matrix deviatoric_stress = Matrix::eye(dim, -1. * stress.trace() / 3.); for (UInt i = 0; i < dim; ++i) for (UInt j = 0; j < dim; ++j) deviatoric_stress(i, j) += stress(i, j); // return Von Mises stress return std::sqrt(3. * deviatoric_stress.doubleDot(deviatoric_stress) / 2.); } /* ---------------------------------------------------------------------------*/ template inline void Material::setCauchyStressArray(const Matrix & S_t, Matrix & sigma_voight) { AKANTU_DEBUG_IN(); sigma_voight.clear(); // see Finite element formulations for large deformation dynamic analysis, // Bathe et al. IJNME vol 9, 1975, page 364 ^t\tau /* * 1d: [ s11 ]' * 2d: [ s11 s22 s12 ]' * 3d: [ s11 s22 s33 s23 s13 s12 ] */ for (UInt i = 0; i < dim; ++i) // diagonal terms sigma_voight(i, 0) = S_t(i, i); for (UInt i = 1; i < dim; ++i) // term s12 in 2D and terms s23 s13 in 3D sigma_voight(dim + i - 1, 0) = S_t(dim - i - 1, dim - 1); for (UInt i = 2; i < dim; ++i) // term s13 in 3D sigma_voight(dim + i, 0) = S_t(0, 1); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template inline void Material::setCauchyStressMatrix(const Matrix & S_t, Matrix & sigma) { AKANTU_DEBUG_IN(); sigma.clear(); /// see Finite ekement formulations for large deformation dynamic analysis, /// Bathe et al. IJNME vol 9, 1975, page 364 ^t\tau for (UInt i = 0; i < dim; ++i) { for (UInt m = 0; m < dim; ++m) { for (UInt n = 0; n < dim; ++n) { sigma(i * dim + m, i * dim + n) = S_t(m, n); } } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ inline Element Material::convertToLocalElement(const Element & global_element) const { UInt ge = global_element.element; #ifndef AKANTU_NDEBUG UInt model_mat_index = this->model.getMaterialByElement( global_element.type, global_element.ghost_type)(ge); UInt mat_index = this->model.getMaterialIndex(this->name); AKANTU_DEBUG_ASSERT(model_mat_index == mat_index, "Conversion of a global element in a local element for " "the wrong material " << this->name << std::endl); #endif UInt le = this->model.getMaterialLocalNumbering( global_element.type, global_element.ghost_type)(ge); Element tmp_quad{global_element.type, le, global_element.ghost_type}; return tmp_quad; } /* -------------------------------------------------------------------------- */ inline Element Material::convertToGlobalElement(const Element & local_element) const { UInt le = local_element.element; UInt ge = this->element_filter(local_element.type, local_element.ghost_type)(le); Element tmp_quad{local_element.type, ge, local_element.ghost_type}; return tmp_quad; } /* -------------------------------------------------------------------------- */ inline IntegrationPoint Material::convertToLocalPoint(const IntegrationPoint & global_point) const { const FEEngine & fem = this->model.getFEEngine(); UInt nb_quad = fem.getNbIntegrationPoints(global_point.type); Element el = this->convertToLocalElement(static_cast(global_point)); IntegrationPoint tmp_quad(el, global_point.num_point, nb_quad); return tmp_quad; } /* -------------------------------------------------------------------------- */ inline IntegrationPoint Material::convertToGlobalPoint(const IntegrationPoint & local_point) const { const FEEngine & fem = this->model.getFEEngine(); UInt nb_quad = fem.getNbIntegrationPoints(local_point.type); Element el = this->convertToGlobalElement(static_cast(local_point)); IntegrationPoint tmp_quad(el, local_point.num_point, nb_quad); return tmp_quad; } /* -------------------------------------------------------------------------- */ inline UInt Material::getNbData(const Array & elements, const SynchronizationTag & tag) const { if (tag == _gst_smm_stress) { return (this->isFiniteDeformation() ? 3 : 1) * spatial_dimension * spatial_dimension * sizeof(Real) * this->getModel().getNbIntegrationPoints(elements); } return 0; } /* -------------------------------------------------------------------------- */ inline void Material::packData(CommunicationBuffer & buffer, const Array & elements, const SynchronizationTag & tag) const { if (tag == _gst_smm_stress) { if (this->isFiniteDeformation()) { packElementDataHelper(piola_kirchhoff_2, buffer, elements); packElementDataHelper(gradu, buffer, elements); } packElementDataHelper(stress, buffer, elements); } } /* -------------------------------------------------------------------------- */ inline void Material::unpackData(CommunicationBuffer & buffer, const Array & elements, const SynchronizationTag & tag) { if (tag == _gst_smm_stress) { if (this->isFiniteDeformation()) { unpackElementDataHelper(piola_kirchhoff_2, buffer, elements); unpackElementDataHelper(gradu, buffer, elements); } unpackElementDataHelper(stress, buffer, elements); } } /* -------------------------------------------------------------------------- */ inline const Parameter & Material::getParam(const ID & param) const { try { return get(param); } catch (...) { AKANTU_EXCEPTION("No parameter " << param << " in the material " << getID()); } } /* -------------------------------------------------------------------------- */ template inline void Material::setParam(const ID & param, T value) { try { set(param, value); } catch (...) { AKANTU_EXCEPTION("No parameter " << param << " in the material " << getID()); } updateInternalParameters(); } +/* -------------------------------------------------------------------------- */ +template +inline void Material::setParamNoUpdate(const ID & param, T value) { + try { + set(param, value); + } catch (...) { + AKANTU_EXCEPTION("No parameter " << param << " in the material " + << getID()); + } +} + /* -------------------------------------------------------------------------- */ template inline void Material::packElementDataHelper( const ElementTypeMapArray & data_to_pack, CommunicationBuffer & buffer, const Array & elements, const ID & fem_id) const { DataAccessor::packElementalDataHelper(data_to_pack, buffer, elements, true, model.getFEEngine(fem_id)); } /* -------------------------------------------------------------------------- */ template inline void Material::unpackElementDataHelper( ElementTypeMapArray & data_to_unpack, CommunicationBuffer & buffer, const Array & elements, const ID & fem_id) { DataAccessor::unpackElementalDataHelper(data_to_unpack, buffer, elements, true, model.getFEEngine(fem_id)); } /* -------------------------------------------------------------------------- */ template <> inline void Material::registerInternal(InternalField & vect) { internal_vectors_real[vect.getID()] = &vect; } template <> inline void Material::registerInternal(InternalField & vect) { internal_vectors_uint[vect.getID()] = &vect; } template <> inline void Material::registerInternal(InternalField & vect) { internal_vectors_bool[vect.getID()] = &vect; } /* -------------------------------------------------------------------------- */ template <> inline void Material::unregisterInternal(InternalField & vect) { internal_vectors_real.erase(vect.getID()); } template <> inline void Material::unregisterInternal(InternalField & vect) { internal_vectors_uint.erase(vect.getID()); } template <> inline void Material::unregisterInternal(InternalField & vect) { internal_vectors_bool.erase(vect.getID()); } /* -------------------------------------------------------------------------- */ template inline bool Material::isInternal(__attribute__((unused)) const ID & id, - __attribute__((unused)) const ElementKind & element_kind) const { + __attribute__((unused)) + const ElementKind & element_kind) const { AKANTU_DEBUG_TO_IMPLEMENT(); } template <> inline bool Material::isInternal(const ID & id, const ElementKind & element_kind) const { auto internal_array = internal_vectors_real.find(this->getID() + ":" + id); if (internal_array == internal_vectors_real.end() || internal_array->second->getElementKind() != element_kind) return false; return true; } /* -------------------------------------------------------------------------- */ template inline ElementTypeMap Material::getInternalDataPerElem(const ID & field_id, const ElementKind & element_kind) const { if (!this->template isInternal(field_id, element_kind)) AKANTU_EXCEPTION("Cannot find internal field " << id << " in material " << this->name); const InternalField & internal_field = this->template getInternal(field_id); const FEEngine & fe_engine = internal_field.getFEEngine(); UInt nb_data_per_quad = internal_field.getNbComponent(); ElementTypeMap res; for (ghost_type_t::iterator gt = ghost_type_t::begin(); gt != ghost_type_t::end(); ++gt) { using type_iterator = typename InternalField::type_iterator; type_iterator tit = internal_field.firstType(*gt); type_iterator tend = internal_field.lastType(*gt); for (; tit != tend; ++tit) { UInt nb_quadrature_points = fe_engine.getNbIntegrationPoints(*tit, *gt); res(*tit, *gt) = nb_data_per_quad * nb_quadrature_points; } } return res; } /* -------------------------------------------------------------------------- */ template void Material::flattenInternal(const std::string & field_id, ElementTypeMapArray & internal_flat, const GhostType ghost_type, ElementKind element_kind) const { if (!this->template isInternal(field_id, element_kind)) AKANTU_EXCEPTION("Cannot find internal field " << id << " in material " << this->name); const InternalField & internal_field = this->template getInternal(field_id); const FEEngine & fe_engine = internal_field.getFEEngine(); const Mesh & mesh = fe_engine.getMesh(); using type_iterator = typename InternalField::filter_type_iterator; type_iterator tit = internal_field.filterFirstType(ghost_type); type_iterator tend = internal_field.filterLastType(ghost_type); for (; tit != tend; ++tit) { ElementType type = *tit; const Array & src_vect = internal_field(type, ghost_type); const Array & filter = internal_field.getFilter(type, ghost_type); // total number of elements in the corresponding mesh UInt nb_element_dst = mesh.getNbElement(type, ghost_type); // number of element in the internal field UInt nb_element_src = filter.size(); // number of quadrature points per elem UInt nb_quad_per_elem = fe_engine.getNbIntegrationPoints(type); // number of data per quadrature point UInt nb_data_per_quad = internal_field.getNbComponent(); if (!internal_flat.exists(type, ghost_type)) { internal_flat.alloc(nb_element_dst * nb_quad_per_elem, nb_data_per_quad, type, ghost_type); } if (nb_element_src == 0) continue; // number of data per element UInt nb_data = nb_quad_per_elem * nb_data_per_quad; Array & dst_vect = internal_flat(type, ghost_type); dst_vect.resize(nb_element_dst * nb_quad_per_elem); Array::const_scalar_iterator it = filter.begin(); Array::const_scalar_iterator end = filter.end(); Array::const_vector_iterator it_src = src_vect.begin_reinterpret(nb_data, nb_element_src); Array::vector_iterator it_dst = dst_vect.begin_reinterpret(nb_data, nb_element_dst); for (; it != end; ++it, ++it_src) { it_dst[*it] = *it_src; } } } } // akantu #endif /* __AKANTU_MATERIAL_INLINE_IMPL_CC__ */ diff --git a/src/model/solid_mechanics/materials/material_elastic_linear_anisotropic.cc b/src/model/solid_mechanics/materials/material_elastic_linear_anisotropic.cc index e24a266cd..5f08a8369 100644 --- a/src/model/solid_mechanics/materials/material_elastic_linear_anisotropic.cc +++ b/src/model/solid_mechanics/materials/material_elastic_linear_anisotropic.cc @@ -1,308 +1,266 @@ /** * @file material_elastic_linear_anisotropic.cc * * @author Till Junge * @author Nicolas Richart * * @date creation: Wed Sep 25 2013 * @date last modification: Thu Oct 15 2015 * * @brief Anisotropic elastic material * * @section LICENSE * * Copyright (©) 2014, 2015 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ #include "material_elastic_linear_anisotropic.hh" #include "solid_mechanics_model.hh" #include #include namespace akantu { /* -------------------------------------------------------------------------- */ template MaterialElasticLinearAnisotropic::MaterialElasticLinearAnisotropic( SolidMechanicsModel & model, const ID & id, bool symmetric) : Material(model, id), rot_mat(dim, dim), Cprime(dim * dim, dim * dim), C(voigt_h::size, voigt_h::size), eigC(voigt_h::size), symmetric(symmetric), alpha(0), was_stiffness_assembled(false) { AKANTU_DEBUG_IN(); this->dir_vecs.push_back(std::make_unique>(dim)); (*this->dir_vecs.back())[0] = 1.; this->registerParam("n1", *(this->dir_vecs.back()), _pat_parsmod, "Direction of main material axis"); if (dim > 1) { this->dir_vecs.push_back(std::make_unique>(dim)); (*this->dir_vecs.back())[1] = 1.; this->registerParam("n2", *(this->dir_vecs.back()), _pat_parsmod, "Direction of secondary material axis"); } if (dim > 2) { this->dir_vecs.push_back(std::make_unique>(dim)); (*this->dir_vecs.back())[2] = 1.; this->registerParam("n3", *(this->dir_vecs.back()), _pat_parsmod, "Direction of tertiary material axis"); } for (UInt i = 0; i < voigt_h::size; ++i) { UInt start = 0; if (this->symmetric) { start = i; } for (UInt j = start; j < voigt_h::size; ++j) { std::stringstream param("C"); param << "C" << i + 1 << j + 1; this->registerParam(param.str(), this->Cprime(i, j), Real(0.), _pat_parsmod, "Coefficient " + param.str()); } } this->registerParam("alpha", this->alpha, _pat_parsmod, "Proportion of viscous stress"); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void MaterialElasticLinearAnisotropic::initMaterial() { AKANTU_DEBUG_IN(); Material::initMaterial(); updateInternalParameters(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void MaterialElasticLinearAnisotropic::updateInternalParameters() { Material::updateInternalParameters(); if (this->symmetric) { for (UInt i = 0; i < voigt_h::size; ++i) { for (UInt j = i + 1; j < voigt_h::size; ++j) { this->Cprime(j, i) = this->Cprime(i, j); } } } this->rotateCprime(); this->C.eig(this->eigC); this->was_stiffness_assembled = false; } /* -------------------------------------------------------------------------- */ template void MaterialElasticLinearAnisotropic::rotateCprime() { // start by filling the empty parts fo Cprime UInt diff = Dim * Dim - voigt_h::size; for (UInt i = voigt_h::size; i < Dim * Dim; ++i) { for (UInt j = 0; j < Dim * Dim; ++j) { this->Cprime(i, j) = this->Cprime(i - diff, j); } } for (UInt i = 0; i < Dim * Dim; ++i) { for (UInt j = voigt_h::size; j < Dim * Dim; ++j) { this->Cprime(i, j) = this->Cprime(i, j - diff); } } // construction of rotator tensor // normalise rotation matrix for (UInt j = 0; j < Dim; ++j) { Vector rot_vec = this->rot_mat(j); rot_vec = *this->dir_vecs[j]; rot_vec.normalize(); } // make sure the vectors form a right-handed base Vector test_axis(3); Vector v1(3), v2(3), v3(3); if (Dim == 2) { for (UInt i = 0; i < Dim; ++i) { v1[i] = this->rot_mat(0, i); v2[i] = this->rot_mat(1, i); v3[i] = 0.; } v3[2] = 1.; v1[2] = 0.; v2[2] = 0.; } else if (Dim == 3) { v1 = this->rot_mat(0); v2 = this->rot_mat(1); v3 = this->rot_mat(2); } test_axis.crossProduct(v1, v2); test_axis -= v3; if (test_axis.norm() > 8 * std::numeric_limits::epsilon()) { AKANTU_DEBUG_ERROR("The axis vectors do not form a right-handed coordinate " << "system. I. e., ||n1 x n2 - n3|| should be zero, but " << "it is " << test_axis.norm() << "."); } // create the rotator and the reverse rotator Matrix rotator(Dim * Dim, Dim * Dim); Matrix revrotor(Dim * Dim, Dim * Dim); for (UInt i = 0; i < Dim; ++i) { for (UInt j = 0; j < Dim; ++j) { for (UInt k = 0; k < Dim; ++k) { for (UInt l = 0; l < Dim; ++l) { UInt I = voigt_h::mat[i][j]; UInt J = voigt_h::mat[k][l]; rotator(I, J) = this->rot_mat(k, i) * this->rot_mat(l, j); revrotor(I, J) = this->rot_mat(i, k) * this->rot_mat(j, l); } } } } // create the full rotated matrix Matrix Cfull(Dim * Dim, Dim * Dim); Cfull = rotator * Cprime * revrotor; for (UInt i = 0; i < voigt_h::size; ++i) { for (UInt j = 0; j < voigt_h::size; ++j) { this->C(i, j) = Cfull(i, j); } } } /* -------------------------------------------------------------------------- */ template -void MaterialElasticLinearAnisotropic::computeStress( - ElementType el_type, GhostType ghost_type) { +void MaterialElasticLinearAnisotropic::computeStress(ElementType el_type, + GhostType ghost_type) { // Wikipedia convention: // 2*eps_ij (i!=j) = voigt_eps_I // http://en.wikipedia.org/wiki/Voigt_notation AKANTU_DEBUG_IN(); - Array::iterator> gradu_it = - this->gradu(el_type, ghost_type).begin(dim, dim); - Array::iterator> gradu_end = - this->gradu(el_type, ghost_type).end(dim, dim); - - UInt nb_quad_pts = gradu_end - gradu_it; - - // create array for strains and stresses of all dof of all gauss points - // for efficient computation of stress - Matrix voigt_strains(voigt_h::size, nb_quad_pts); - Matrix voigt_stresses(voigt_h::size, nb_quad_pts); - - // copy strains Matrix strain(dim, dim); - for (UInt q = 0; gradu_it != gradu_end; ++gradu_it, ++q) { - Matrix & grad_u = *gradu_it; - - for (UInt I = 0; I < voigt_h::size; ++I) { - Real voigt_factor = voigt_h::factors[I]; - UInt i = voigt_h::vec[I][0]; - UInt j = voigt_h::vec[I][1]; - - voigt_strains(I, q) = voigt_factor * (grad_u(i, j) + grad_u(j, i)) / 2.; - } - } - - // compute the strain rate proportional part if needed - // bool viscous = this->alpha == 0.; // only works if default value - bool viscous = false; - if (viscous) { - Array strain_rate(0, dim * dim, "strain_rate"); - - Array & velocity = this->model.getVelocity(); - const Array & elem_filter = this->element_filter(el_type, ghost_type); + MATERIAL_STRESS_QUADRATURE_POINT_LOOP_BEGIN(el_type, ghost_type); - this->fem.gradientOnIntegrationPoints(velocity, strain_rate, dim, el_type, - ghost_type, elem_filter); + this->computeStressOnQuad(strain,sigma); - Array::matrix_iterator gradu_dot_it = strain_rate.begin(dim, dim); - Array::matrix_iterator gradu_dot_end = strain_rate.end(dim, dim); - - Matrix strain_dot(dim, dim); - for (UInt q = 0; gradu_dot_it != gradu_dot_end; ++gradu_dot_it, ++q) { - Matrix & grad_u_dot = *gradu_dot_it; - - for (UInt I = 0; I < voigt_h::size; ++I) { - Real voigt_factor = voigt_h::factors[I]; - UInt i = voigt_h::vec[I][0]; - UInt j = voigt_h::vec[I][1]; - - voigt_strains(I, q) = this->alpha * voigt_factor * - (grad_u_dot(i, j) + grad_u_dot(j, i)) / 2.; - } - } - } + MATERIAL_STRESS_QUADRATURE_POINT_LOOP_END; +} - // compute stresses - voigt_stresses = this->C * voigt_strains; +/* -------------------------------------------------------------------------- */ +template +void MaterialElasticLinearAnisotropic::computeTangentModuli( + const ElementType & el_type, Array & tangent_matrix, + GhostType ghost_type) { + AKANTU_DEBUG_IN(); - // copy stresses back - Array::iterator> stress_it = - this->stress(el_type, ghost_type).begin(dim, dim); + MATERIAL_TANGENT_QUADRATURE_POINT_LOOP_BEGIN(tangent_matrix); - Array::iterator> stress_end = - this->stress(el_type, ghost_type).end(dim, dim); + this->computeTangentModuliOnQuad(tangent); - for (UInt q = 0; stress_it != stress_end; ++stress_it, ++q) { - Matrix & stress = *stress_it; + MATERIAL_TANGENT_QUADRATURE_POINT_LOOP_END; - for (UInt I = 0; I < voigt_h::size; ++I) { - UInt i = voigt_h::vec[I][0]; - UInt j = voigt_h::vec[I][1]; - stress(i, j) = stress(j, i) = voigt_stresses(I, q); - } - } + this->was_stiffness_assembled = true; AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template -void MaterialElasticLinearAnisotropic::computeTangentModuli( - const ElementType & el_type, Array & tangent_matrix, - GhostType ghost_type) { +void MaterialElasticLinearAnisotropic::computePotentialEnergy( + ElementType el_type, GhostType ghost_type) { AKANTU_DEBUG_IN(); - MATERIAL_TANGENT_QUADRATURE_POINT_LOOP_BEGIN(tangent_matrix); - tangent.copy(this->C); - MATERIAL_TANGENT_QUADRATURE_POINT_LOOP_END; + Material::computePotentialEnergy(el_type, ghost_type); - this->was_stiffness_assembled = true; + AKANTU_DEBUG_ASSERT(!this->finite_deformation, + "finite deformation not possible in material anisotropic " + "(TO BE IMPLEMENTED)"); + + if (ghost_type != _not_ghost) + return; + Array::scalar_iterator epot = + this->potential_energy(el_type, ghost_type).begin(); + + MATERIAL_STRESS_QUADRATURE_POINT_LOOP_BEGIN(el_type, ghost_type); + + computePotentialEnergyOnQuad(grad_u, sigma, *epot); + ++epot; + + MATERIAL_STRESS_QUADRATURE_POINT_LOOP_END; AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template Real MaterialElasticLinearAnisotropic::getCelerity( __attribute__((unused)) const Element & element) const { return std::sqrt(this->eigC(0) / rho); } /* -------------------------------------------------------------------------- */ INSTANTIATE_MATERIAL(elastic_anisotropic, MaterialElasticLinearAnisotropic); } // akantu diff --git a/src/model/solid_mechanics/materials/material_elastic_linear_anisotropic.hh b/src/model/solid_mechanics/materials/material_elastic_linear_anisotropic.hh index dec6765fd..2cfa119bb 100644 --- a/src/model/solid_mechanics/materials/material_elastic_linear_anisotropic.hh +++ b/src/model/solid_mechanics/materials/material_elastic_linear_anisotropic.hh @@ -1,122 +1,139 @@ /** * @file material_elastic_linear_anisotropic.hh * * @author Till Junge * * @date creation: Fri Jun 18 2010 * @date last modification: Tue Aug 18 2015 * * @brief Orthotropic elastic material * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_common.hh" #include "material.hh" #include "material_elastic.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_MATERIAL_ELASTIC_LINEAR_ANISOTROPIC_HH__ #define __AKANTU_MATERIAL_ELASTIC_LINEAR_ANISOTROPIC_HH__ namespace akantu { /** * General linear anisotropic elastic material * The only constraint on the elastic tensor is that it can be represented * as a symmetric 6x6 matrix (3D) or 3x3 (2D). * * parameters in the material files : * - rho : density (default: 0) * - C_ij : entry on the stiffness */ template class MaterialElasticLinearAnisotropic : public Material { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: MaterialElasticLinearAnisotropic(SolidMechanicsModel & model, const ID & id = "", bool symmetric = true); /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ public: void initMaterial() override; /// constitutive law for all element of a type void computeStress(ElementType el_type, GhostType ghost_type = _not_ghost) override; /// compute the tangent stiffness matrix for an element type void computeTangentModuli(const ElementType & el_type, Array & tangent_matrix, GhostType ghost_type = _not_ghost) override; + /// compute the elastic potential energy + void computePotentialEnergy(ElementType el_type, + GhostType ghost_type = _not_ghost) override; + void updateInternalParameters() override; bool hasStiffnessMatrixChanged() override { return (!was_stiffness_assembled); } protected: // compute C from Cprime void rotateCprime(); + /// constitutive law for a given quadrature point + inline void computeStressOnQuad(const Matrix & grad_u, + Matrix & sigma) const; + + /// tangent matrix for a given quadrature point + inline void computeTangentModuliOnQuad(Matrix & tangent) const; + + inline void computePotentialEnergyOnQuad(const Matrix & grad_u, + const Matrix & sigma, + Real & epot); + /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: /// compute max wave celerity Real getCelerity(const Element & element) const override; AKANTU_GET_MACRO(VoigtStiffness, C, Matrix); /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ protected: using voigt_h = VoigtHelper; /// direction matrix and vectors std::vector>> dir_vecs; Matrix rot_mat; /// Elastic stiffness tensor in material frame and full vectorised notation Matrix Cprime; /// Elastic stiffness tensor in voigt notation Matrix C; /// eigenvalues of stiffness tensor Vector eigC; bool symmetric; /// viscous proportion Real alpha; /// defines if the stiffness was computed bool was_stiffness_assembled; }; } // akantu +#include "material_elastic_linear_anisotropic_inline_impl.cc" + #endif /* __AKANTU_MATERIAL_ELASTIC_LINEAR_ANISOTROPIC_HH__ */ diff --git a/src/model/solid_mechanics/materials/material_elastic_linear_anisotropic_inline_impl.cc b/src/model/solid_mechanics/materials/material_elastic_linear_anisotropic_inline_impl.cc new file mode 100644 index 000000000..900b8d5ae --- /dev/null +++ b/src/model/solid_mechanics/materials/material_elastic_linear_anisotropic_inline_impl.cc @@ -0,0 +1,96 @@ +/** + * @file material_elastic_linear_anisotropic_inline_impl.cc + * + * @author Enrico Milanese + * @author Nicolas Richart + * + * @date creation: Mon Feb 12 2018 + * @date last modification: Mon Feb 12 2018 + * + * @brief Implementation of the inline functions of the material elastic linear + * anisotropic + * + * @section LICENSE + * + * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de + * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des + * Solides) + * + * Akantu is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Akantu. If not, see . + * + */ + +/* -------------------------------------------------------------------------- */ +#include "material_elastic_linear_anisotropic.hh" +/* -------------------------------------------------------------------------- */ + +#ifndef __AKANTU_MATERIAL_ELASTIC_LINEAR_ANISOTROPIC_INLINE_IMPL_CC__ +#define __AKANTU_MATERIAL_ELASTIC_LINEAR_ANISOTROPIC_INLINE_IMPL_CC__ + +namespace akantu { + +/* -------------------------------------------------------------------------- */ +template +inline void MaterialElasticLinearAnisotropic::computeStressOnQuad(const Matrix & grad_u, + Matrix & sigma) const { + // Wikipedia convention: + // 2*eps_ij (i!=j) = voigt_eps_I + // http://en.wikipedia.org/wiki/Voigt_notation + Vector voigt_strain(voigt_h::size); + Vector voigt_stress(voigt_h::size); + + for (UInt I = 0; I < voigt_h::size; ++I){ + Real voigt_factor = voigt_h::factors[I]; + UInt i = voigt_h::vec[I][0]; + UInt j = voigt_h::vec[I][1]; + + voigt_strain(I) = voigt_factor * (grad_u(i, j) + grad_u(j, i)) / 2.; + + } + + voigt_stress = this->C * voigt_strain; + + for (UInt I = 0; I < voigt_h::size; ++I){ + UInt i = voigt_h::vec[I][0]; + UInt j = voigt_h::vec[I][1]; + + sigma(i, j) = sigma(j, i) = voigt_stress(I); + + } + +} + +/* -------------------------------------------------------------------------- */ +template +inline void MaterialElasticLinearAnisotropic::computeTangentModuliOnQuad(Matrix & tangent) const { + + tangent.copy(this->C); + +} + +/* -------------------------------------------------------------------------- */ +template +inline void MaterialElasticLinearAnisotropic::computePotentialEnergyOnQuad( + const Matrix & grad_u, const Matrix & sigma, Real & epot) { + + AKANTU_DEBUG_ASSERT(this->symmetric, + "The elastic constants matrix is not symmetric," + "energy is not path independent."); + + epot = .5 * sigma.doubleDot(grad_u); +} + +} // akantu + +#endif /* __AKANTU_MATERIAL_ELASTIC_LINEAR_ANISOTROPIC_INLINE_IMPL_CC__ */ diff --git a/src/model/solid_mechanics/materials/material_elastic_orthotropic.cc b/src/model/solid_mechanics/materials/material_elastic_orthotropic.cc index 22815792a..331a25fbe 100644 --- a/src/model/solid_mechanics/materials/material_elastic_orthotropic.cc +++ b/src/model/solid_mechanics/materials/material_elastic_orthotropic.cc @@ -1,212 +1,168 @@ /** * @file material_elastic_orthotropic.cc * * @author Till Junge * @author Nicolas Richart * @author Marco Vocialta * * @date creation: Fri Jun 18 2010 * @date last modification: Thu Oct 15 2015 * * @brief Orthotropic elastic material * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ #include "material_elastic_orthotropic.hh" #include "solid_mechanics_model.hh" #include namespace akantu { /* -------------------------------------------------------------------------- */ template MaterialElasticOrthotropic::MaterialElasticOrthotropic( SolidMechanicsModel & model, const ID & id) : MaterialElasticLinearAnisotropic(model, id) { AKANTU_DEBUG_IN(); this->registerParam("E1", E1, Real(0.), _pat_parsmod, "Young's modulus (n1)"); this->registerParam("E2", E2, Real(0.), _pat_parsmod, "Young's modulus (n2)"); this->registerParam("nu12", nu12, Real(0.), _pat_parsmod, "Poisson's ratio (12)"); this->registerParam("G12", G12, Real(0.), _pat_parsmod, "Shear modulus (12)"); if (Dim > 2) { this->registerParam("E3", E3, Real(0.), _pat_parsmod, "Young's modulus (n3)"); this->registerParam("nu13", nu13, Real(0.), _pat_parsmod, "Poisson's ratio (13)"); this->registerParam("nu23", nu23, Real(0.), _pat_parsmod, "Poisson's ratio (23)"); this->registerParam("G13", G13, Real(0.), _pat_parsmod, "Shear modulus (13)"); this->registerParam("G23", G23, Real(0.), _pat_parsmod, "Shear modulus (23)"); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void MaterialElasticOrthotropic::initMaterial() { AKANTU_DEBUG_IN(); Material::initMaterial(); updateInternalParameters(); AKANTU_DEBUG_OUT(); } -/* -------------------------------------------------------------------------- */ -inline Real vector_norm(Vector & vec) { - Real norm = 0; - for (UInt i = 0; i < vec.size(); ++i) { - norm += vec(i) * vec(i); - } - return std::sqrt(norm); -} - /* -------------------------------------------------------------------------- */ template void MaterialElasticOrthotropic::updateInternalParameters() { /* 1) construction of temporary material frame stiffness tensor------------ */ // http://solidmechanics.org/Text/Chapter3_2/Chapter3_2.php#Sect3_2_13 Real nu21 = nu12 * E2 / E1; Real nu31 = nu13 * E3 / E1; Real nu32 = nu23 * E3 / E2; // Full (i.e. dim^2 by dim^2) stiffness tensor in material frame if (Dim == 1) { AKANTU_DEBUG_ERROR("Dimensions 1 not implemented: makes no sense to have " "orthotropy for 1D"); } Real Gamma; if (Dim == 3) Gamma = 1 / (1 - nu12 * nu21 - nu23 * nu32 - nu31 * nu13 - 2 * nu21 * nu32 * nu13); if (Dim == 2) Gamma = 1 / (1 - nu12 * nu21); // Lamé's first parameters this->Cprime(0, 0) = E1 * (1 - nu23 * nu32) * Gamma; this->Cprime(1, 1) = E2 * (1 - nu13 * nu31) * Gamma; if (Dim == 3) this->Cprime(2, 2) = E3 * (1 - nu12 * nu21) * Gamma; // normalised poisson's ratio's this->Cprime(1, 0) = this->Cprime(0, 1) = E1 * (nu21 + nu31 * nu23) * Gamma; if (Dim == 3) { this->Cprime(2, 0) = this->Cprime(0, 2) = E1 * (nu31 + nu21 * nu32) * Gamma; this->Cprime(2, 1) = this->Cprime(1, 2) = E2 * (nu32 + nu12 * nu31) * Gamma; } // Lamé's second parameters (shear moduli) if (Dim == 3) { this->Cprime(3, 3) = G23; this->Cprime(4, 4) = G13; this->Cprime(5, 5) = G12; } else this->Cprime(2, 2) = G12; /* 1) rotation of C into the global frame */ this->rotateCprime(); this->C.eig(this->eigC); } -/* -------------------------------------------------------------------------- */ - -template -inline void MaterialElasticOrthotropic::computePotentialEnergyOnQuad( - const Matrix & grad_u, const Matrix & sigma, Real & epot) { - epot = .5 * sigma.doubleDot(grad_u); -} - -/* -------------------------------------------------------------------------- */ -template -void MaterialElasticOrthotropic::computePotentialEnergy( - ElementType el_type, GhostType ghost_type) { - AKANTU_DEBUG_IN(); - - Material::computePotentialEnergy(el_type, ghost_type); - - AKANTU_DEBUG_ASSERT(!this->finite_deformation, - "finite deformation not possible in material orthotropic " - "(TO BE IMPLEMENTED)"); - - if (ghost_type != _not_ghost) - return; - Array::scalar_iterator epot = - this->potential_energy(el_type, ghost_type).begin(); - - MATERIAL_STRESS_QUADRATURE_POINT_LOOP_BEGIN(el_type, ghost_type); - - computePotentialEnergyOnQuad(grad_u, sigma, *epot); - ++epot; - - MATERIAL_STRESS_QUADRATURE_POINT_LOOP_END; - - AKANTU_DEBUG_OUT(); -} - /* -------------------------------------------------------------------------- */ template void MaterialElasticOrthotropic:: computePotentialEnergyByElement(ElementType type, UInt index, Vector & epot_on_quad_points) { AKANTU_DEBUG_ASSERT(!this->finite_deformation, "finite deformation not possible in material orthotropic " "(TO BE IMPLEMENTED)"); Array::matrix_iterator gradu_it = this->gradu(type).begin(spatial_dimension, spatial_dimension); Array::matrix_iterator gradu_end = this->gradu(type).begin(spatial_dimension, spatial_dimension); Array::matrix_iterator stress_it = this->stress(type).begin(spatial_dimension, spatial_dimension); UInt nb_quadrature_points = this->fem.getNbIntegrationPoints(type); gradu_it += index * nb_quadrature_points; gradu_end += (index + 1) * nb_quadrature_points; stress_it += index * nb_quadrature_points; Real * epot_quad = epot_on_quad_points.storage(); Matrix grad_u(spatial_dimension, spatial_dimension); for (; gradu_it != gradu_end; ++gradu_it, ++stress_it, ++epot_quad) { grad_u.copy(*gradu_it); - computePotentialEnergyOnQuad(grad_u, *stress_it, *epot_quad); + this->computePotentialEnergyOnQuad(grad_u, *stress_it, *epot_quad); } } /* -------------------------------------------------------------------------- */ INSTANTIATE_MATERIAL(elastic_orthotropic, MaterialElasticOrthotropic); } // akantu diff --git a/src/model/solid_mechanics/materials/material_elastic_orthotropic.hh b/src/model/solid_mechanics/materials/material_elastic_orthotropic.hh index f5bad0fe2..60110342c 100644 --- a/src/model/solid_mechanics/materials/material_elastic_orthotropic.hh +++ b/src/model/solid_mechanics/materials/material_elastic_orthotropic.hh @@ -1,145 +1,136 @@ /** * @file material_elastic_orthotropic.hh * * @author Till Junge * @author Marco Vocialta * * @date creation: Fri Jun 18 2010 * @date last modification: Sun Oct 19 2014 * * @brief Orthotropic elastic material * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ #include "aka_common.hh" #include "material_elastic_linear_anisotropic.hh" /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_MATERIAL_ELASTIC_ORTHOTROPIC_HH__ #define __AKANTU_MATERIAL_ELASTIC_ORTHOTROPIC_HH__ namespace akantu { /** * Orthotropic elastic material * * parameters in the material files : * - n1 : direction of x-axis in material base, normalisation not necessary * (default: {1, 0, 0}) * - n2 : direction of y-axis in material base, normalisation not necessary * (default: {0, 1, 0}) * - n3 : direction of z-axis in material base, normalisation not necessary * (default: {0, 0, 1}) * - rho : density (default: 0) * - E1 : Young's modulus along n1 (default: 0) * - E2 : Young's modulus along n2 (default: 0) * - E3 : Young's modulus along n3 (default: 0) * - nu12 : Poisson's ratio along 12 (default: 0) * - nu13 : Poisson's ratio along 13 (default: 0) * - nu23 : Poisson's ratio along 23 (default: 0) * - G12 : Shear modulus along 12 (default: 0) * - G13 : Shear modulus along 13 (default: 0) * - G23 : Shear modulus along 23 (default: 0) */ template class MaterialElasticOrthotropic : public MaterialElasticLinearAnisotropic { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: MaterialElasticOrthotropic(SolidMechanicsModel & model, const ID & id = ""); /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ public: void initMaterial() override; void updateInternalParameters() override; - /// compute the elastic potential energy - void computePotentialEnergy(ElementType el_type, - GhostType ghost_type = _not_ghost) override; - void computePotentialEnergyByElement(ElementType type, UInt index, Vector & epot_on_quad_points) override; -protected: - inline void computePotentialEnergyOnQuad(const Matrix & grad_u, - const Matrix & sigma, - Real & epot); - /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: AKANTU_GET_MACRO(E1, E1, Real); AKANTU_GET_MACRO(E2, E2, Real); AKANTU_GET_MACRO(E3, E3, Real); AKANTU_GET_MACRO(Nu12, nu12, Real); AKANTU_GET_MACRO(Nu13, nu13, Real); AKANTU_GET_MACRO(Nu23, nu23, Real); AKANTU_GET_MACRO(G12, G12, Real); AKANTU_GET_MACRO(G13, G13, Real); AKANTU_GET_MACRO(G23, G23, Real); /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ protected: /// the n1 young modulus Real E1; /// the n2 young modulus Real E2; /// the n3 young modulus Real E3; /// 12 Poisson coefficient Real nu12; /// 13 Poisson coefficient Real nu13; /// 23 Poisson coefficient Real nu23; /// 12 shear modulus Real G12; /// 13 shear modulus Real G13; /// 23 shear modulus Real G23; }; } // akantu #endif /* __AKANTU_MATERIAL_ELASTIC_ORTHOTROPIC_HH__ */ diff --git a/src/model/solid_mechanics/materials/material_embedded/material_reinforcement_tmpl.hh b/src/model/solid_mechanics/materials/material_embedded/material_reinforcement_tmpl.hh index 09caa4037..4dc96d2e7 100644 --- a/src/model/solid_mechanics/materials/material_embedded/material_reinforcement_tmpl.hh +++ b/src/model/solid_mechanics/materials/material_embedded/material_reinforcement_tmpl.hh @@ -1,818 +1,803 @@ /** * @file material_reinforcement_tmpl.hh * * @author Lucas Frerot * * @date creation: Thu Feb 1 2018 * * @brief Reinforcement material * * @section LICENSE * * Copyright (©) 2015 EPFL (Ecole Polytechnique Fédérale de Lausanne) Laboratory * (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_common.hh" #include "aka_voigthelper.hh" #include "material_reinforcement.hh" namespace akantu { /* -------------------------------------------------------------------------- */ template MaterialReinforcement::MaterialReinforcement( EmbeddedInterfaceModel & model, const ID & id) : Mat(model, 1, model.getInterfaceMesh(), model.getFEEngine("EmbeddedInterfaceFEEngine"), id), emodel(model), gradu_embedded("gradu_embedded", *this, 1, model.getFEEngine("EmbeddedInterfaceFEEngine"), this->element_filter), directing_cosines("directing_cosines", *this, 1, model.getFEEngine("EmbeddedInterfaceFEEngine"), this->element_filter), pre_stress("pre_stress", *this, 1, model.getFEEngine("EmbeddedInterfaceFEEngine"), this->element_filter), area(1.0), shape_derivatives() { AKANTU_DEBUG_IN(); this->initialize(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void MaterialReinforcement::initialize() { AKANTU_DEBUG_IN(); this->registerParam("area", area, _pat_parsable | _pat_modifiable, "Reinforcement cross-sectional area"); this->registerParam("pre_stress", pre_stress, _pat_parsable | _pat_modifiable, "Uniform pre-stress"); // this->unregisterInternal(this->stress); // Fool the AvgHomogenizingFunctor // stress.initialize(dim * dim); // Reallocate the element filter this->element_filter.initialize(this->emodel.getInterfaceMesh(), _spatial_dimension = 1); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template MaterialReinforcement::~MaterialReinforcement() { AKANTU_DEBUG_IN(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void MaterialReinforcement::initMaterial() { Mat::initMaterial(); gradu_embedded.initialize(dim * dim); pre_stress.initialize(1); /// We initialise the stuff that is not going to change during the simulation this->initFilters(); this->allocBackgroundShapeDerivatives(); this->initBackgroundShapeDerivatives(); this->initDirectingCosines(); } -/* -------------------------------------------------------------------------- */ - -namespace detail { - class FilterInitializer : public MeshElementTypeMapArrayInializer { - public: - FilterInitializer(EmbeddedInterfaceModel & emodel, - const GhostType & ghost_type) - : MeshElementTypeMapArrayInializer(emodel.getMesh(), - 1, emodel.getSpatialDimension(), - ghost_type, _ek_regular) {} - - UInt size(const ElementType & /*bgtype*/) const override { return 0; } - }; -} - /* -------------------------------------------------------------------------- */ /// Initialize the filter for background elements template void MaterialReinforcement::initFilters() { for (auto gt : ghost_types) { for (auto && type : emodel.getInterfaceMesh().elementTypes(1, gt)) { std::string shaped_id = "filter"; if (gt == _ghost) shaped_id += ":ghost"; auto & background = background_filter(std::make_unique>( "bg_" + shaped_id, this->name), type, gt); auto & foreground = foreground_filter( std::make_unique>(shaped_id, this->name), type, gt); - foreground->initialize(detail::FilterInitializer(emodel, gt), 0, true); - background->initialize(detail::FilterInitializer(emodel, gt), 0, true); + foreground->initialize(emodel.getMesh(), _nb_component = 1, + _ghost_type = gt); + background->initialize(emodel.getMesh(), _nb_component = 1, + _ghost_type = gt); // Computing filters for (auto && bg_type : background->elementTypes(dim, gt)) { filterInterfaceBackgroundElements( (*foreground)(bg_type), (*background)(bg_type), bg_type, type, gt); } } } } /* -------------------------------------------------------------------------- */ /// Construct a filter for a (interface_type, background_type) pair template void MaterialReinforcement::filterInterfaceBackgroundElements( Array & foreground, Array & background, const ElementType & type, const ElementType & interface_type, GhostType ghost_type) { AKANTU_DEBUG_IN(); foreground.resize(0); background.resize(0); Array & elements = emodel.getInterfaceAssociatedElements(interface_type, ghost_type); Array & elem_filter = this->element_filter(interface_type, ghost_type); for (auto & elem_id : elem_filter) { Element & elem = elements(elem_id); if (elem.type == type) { background.push_back(elem.element); foreground.push_back(elem_id); } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ namespace detail { - class BackgroundShapeDInitializer : public ElementTypeMapArrayInializer { + class BackgroundShapeDInitializer : public ElementTypeMapArrayInitializer { public: - BackgroundShapeDInitializer(UInt spatial_dimension, - FEEngine & engine, - const ElementType & foreground_type, - ElementTypeMapArray & filter, + BackgroundShapeDInitializer(UInt spatial_dimension, FEEngine & engine, + const ElementType & foreground_type, + const ElementTypeMapArray & filter, const GhostType & ghost_type) - : ElementTypeMapArrayInializer(spatial_dimension, 0, ghost_type, - _ek_regular) { + : ElementTypeMapArrayInitializer( + [](const ElementType & bgtype, const GhostType &) { + return ShapeFunctions::getShapeDerivativesSize(bgtype); + }, + spatial_dimension, ghost_type, _ek_regular) { auto nb_quad = engine.getNbIntegrationPoints(foreground_type); // Counting how many background elements are affected by elements of // interface_type for (auto type : filter.elementTypes(this->spatial_dimension)) { // Inserting size array_size_per_bg_type(filter(type).size() * nb_quad, type, this->ghost_type); } } auto elementTypes() const -> decltype(auto) { return array_size_per_bg_type.elementTypes(); } UInt size(const ElementType & bgtype) const { return array_size_per_bg_type(bgtype, this->ghost_type); } - UInt nbComponent(const ElementType & bgtype) const { - return ShapeFunctions::getShapeDerivativesSize(bgtype); - } - protected: ElementTypeMap array_size_per_bg_type; }; } /** * Background shape derivatives need to be stored per background element * types but also per embedded element type, which is why they are stored * in an ElementTypeMap *>. The outer ElementTypeMap * refers to the embedded types, and the inner refers to the background types. */ template void MaterialReinforcement::allocBackgroundShapeDerivatives() { AKANTU_DEBUG_IN(); for (auto gt : ghost_types) { for (auto && type : emodel.getInterfaceMesh().elementTypes(1, gt)) { std::string shaped_id = "embedded_shape_derivatives"; if (gt == _ghost) shaped_id += ":ghost"; auto & shaped_etma = shape_derivatives( std::make_unique>(shaped_id, this->name), type, gt); shaped_etma->initialize( detail::BackgroundShapeDInitializer( emodel.getSpatialDimension(), emodel.getFEEngine("EmbeddedInterfaceFEEngine"), type, *background_filter(type, gt), gt), 0, true); } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void MaterialReinforcement::initBackgroundShapeDerivatives() { AKANTU_DEBUG_IN(); for (auto interface_type : this->element_filter.elementTypes(this->spatial_dimension)) { for (auto type : background_filter(interface_type)->elementTypes(dim)) { computeBackgroundShapeDerivatives(interface_type, type, _not_ghost, this->element_filter(interface_type)); } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void MaterialReinforcement::computeBackgroundShapeDerivatives( const ElementType & interface_type, const ElementType & bg_type, GhostType ghost_type, const Array & filter) { auto & interface_engine = emodel.getFEEngine("EmbeddedInterfaceFEEngine"); auto & engine = emodel.getFEEngine(); auto & interface_mesh = emodel.getInterfaceMesh(); const auto nb_nodes_elem_bg = Mesh::getNbNodesPerElement(bg_type); // const auto nb_strss = VoigtHelper::size; const auto nb_quads_per_elem = interface_engine.getNbIntegrationPoints(interface_type); Array quad_pos(0, dim, "interface_quad_pos"); interface_engine.interpolateOnIntegrationPoints(interface_mesh.getNodes(), quad_pos, dim, interface_type, ghost_type, filter); auto & background_shapesd = (*shape_derivatives(interface_type, ghost_type))(bg_type, ghost_type); auto & background_elements = (*background_filter(interface_type, ghost_type))(bg_type, ghost_type); auto & foreground_elements = (*foreground_filter(interface_type, ghost_type))(bg_type, ghost_type); auto shapesd_begin = background_shapesd.begin(dim, nb_nodes_elem_bg, nb_quads_per_elem); auto quad_begin = quad_pos.begin(dim, nb_quads_per_elem); for (auto && tuple : zip(background_elements, foreground_elements)) { UInt bg = std::get<0>(tuple), fg = std::get<1>(tuple); for (UInt i = 0; i < nb_quads_per_elem; ++i) { Matrix shapesd = Tensor3(shapesd_begin[fg])(i); Vector quads = Matrix(quad_begin[fg])(i); engine.computeShapeDerivatives(quads, bg, bg_type, shapesd, ghost_type); } } } /* -------------------------------------------------------------------------- */ template void MaterialReinforcement::initDirectingCosines() { AKANTU_DEBUG_IN(); Mesh & mesh = emodel.getInterfaceMesh(); Mesh::type_iterator type_it = mesh.firstType(1, _not_ghost); Mesh::type_iterator type_end = mesh.lastType(1, _not_ghost); const UInt voigt_size = VoigtHelper::size; directing_cosines.initialize(voigt_size); for (; type_it != type_end; ++type_it) { computeDirectingCosines(*type_it, _not_ghost); // computeDirectingCosines(*type_it, _ghost); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void MaterialReinforcement::assembleStiffnessMatrix( GhostType ghost_type) { AKANTU_DEBUG_IN(); Mesh & interface_mesh = emodel.getInterfaceMesh(); Mesh::type_iterator type_it = interface_mesh.firstType(1, _not_ghost); Mesh::type_iterator type_end = interface_mesh.lastType(1, _not_ghost); for (; type_it != type_end; ++type_it) { assembleStiffnessMatrix(*type_it, ghost_type); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void MaterialReinforcement::assembleInternalForces( GhostType ghost_type) { AKANTU_DEBUG_IN(); Mesh & interface_mesh = emodel.getInterfaceMesh(); Mesh::type_iterator type_it = interface_mesh.firstType(1, _not_ghost); Mesh::type_iterator type_end = interface_mesh.lastType(1, _not_ghost); for (; type_it != type_end; ++type_it) { this->assembleInternalForces(*type_it, ghost_type); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void MaterialReinforcement::computeAllStresses(GhostType ghost_type) { AKANTU_DEBUG_IN(); Mesh::type_iterator it = emodel.getInterfaceMesh().firstType(); Mesh::type_iterator last_type = emodel.getInterfaceMesh().lastType(); for (; it != last_type; ++it) { computeGradU(*it, ghost_type); this->computeStress(*it, ghost_type); addPrestress(*it, ghost_type); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void MaterialReinforcement::addPrestress(const ElementType & type, GhostType ghost_type) { auto & stress = this->stress(type, ghost_type); auto & sigma_p = this->pre_stress(type, ghost_type); for (auto && tuple : zip(stress, sigma_p)) { std::get<0>(tuple) += std::get<1>(tuple); } } /* -------------------------------------------------------------------------- */ template void MaterialReinforcement::assembleInternalForces( const ElementType & type, GhostType ghost_type) { AKANTU_DEBUG_IN(); Mesh & mesh = emodel.getMesh(); Mesh::type_iterator type_it = mesh.firstType(dim, ghost_type); Mesh::type_iterator type_end = mesh.lastType(dim, ghost_type); for (; type_it != type_end; ++type_it) { assembleInternalForcesInterface(type, *type_it, ghost_type); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ /** * Computes and assemble the residual. Residual in reinforcement is computed as: * * \f[ * \vec{r} = A_s \int_S{\mathbf{B}^T\mathbf{C}^T \vec{\sigma_s}\,\mathrm{d}s} * \f] */ template void MaterialReinforcement::assembleInternalForcesInterface( const ElementType & interface_type, const ElementType & background_type, GhostType ghost_type) { AKANTU_DEBUG_IN(); UInt voigt_size = VoigtHelper::size; FEEngine & interface_engine = emodel.getFEEngine("EmbeddedInterfaceFEEngine"); Array & elem_filter = this->element_filter(interface_type, ghost_type); UInt nodes_per_background_e = Mesh::getNbNodesPerElement(background_type); UInt nb_quadrature_points = interface_engine.getNbIntegrationPoints(interface_type, ghost_type); UInt nb_element = elem_filter.size(); UInt back_dof = dim * nodes_per_background_e; Array & shapesd = (*shape_derivatives(interface_type, ghost_type))( background_type, ghost_type); Array integrant(nb_quadrature_points * nb_element, back_dof, "integrant"); Array::vector_iterator integrant_it = integrant.begin(back_dof); Array::vector_iterator integrant_end = integrant.end(back_dof); Array::matrix_iterator B_it = shapesd.begin(dim, nodes_per_background_e); Array::vector_iterator C_it = directing_cosines(interface_type, ghost_type).begin(voigt_size); auto sigma_it = this->stress(interface_type, ghost_type).begin(); Matrix Bvoigt(voigt_size, back_dof); for (; integrant_it != integrant_end; ++integrant_it, ++B_it, ++C_it, ++sigma_it) { VoigtHelper::transferBMatrixToSymVoigtBMatrix(*B_it, Bvoigt, nodes_per_background_e); Vector & C = *C_it; Vector & BtCt_sigma = *integrant_it; BtCt_sigma.mul(Bvoigt, C); BtCt_sigma *= *sigma_it * area; } Array residual_interface(nb_element, back_dof, "residual_interface"); interface_engine.integrate(integrant, residual_interface, back_dof, interface_type, ghost_type, elem_filter); integrant.resize(0); Array background_filter(nb_element, 1, "background_filter"); auto & filter = getBackgroundFilter(interface_type, background_type, ghost_type); emodel.getDOFManager().assembleElementalArrayLocalArray( residual_interface, emodel.getInternalForce(), background_type, ghost_type, -1., filter); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void MaterialReinforcement::computeDirectingCosines( const ElementType & type, GhostType ghost_type) { AKANTU_DEBUG_IN(); Mesh & interface_mesh = emodel.getInterfaceMesh(); const UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type); const UInt steel_dof = dim * nb_nodes_per_element; const UInt voigt_size = VoigtHelper::size; const UInt nb_quad_points = emodel.getFEEngine("EmbeddedInterfaceFEEngine") .getNbIntegrationPoints(type, ghost_type); Array node_coordinates(this->element_filter(type, ghost_type).size(), steel_dof); this->emodel.getFEEngine().template extractNodalToElementField( interface_mesh, interface_mesh.getNodes(), node_coordinates, type, ghost_type, this->element_filter(type, ghost_type)); Array::matrix_iterator directing_cosines_it = directing_cosines(type, ghost_type).begin(1, voigt_size); Array::matrix_iterator node_coordinates_it = node_coordinates.begin(dim, nb_nodes_per_element); Array::matrix_iterator node_coordinates_end = node_coordinates.end(dim, nb_nodes_per_element); for (; node_coordinates_it != node_coordinates_end; ++node_coordinates_it) { for (UInt i = 0; i < nb_quad_points; i++, ++directing_cosines_it) { Matrix & nodes = *node_coordinates_it; Matrix & cosines = *directing_cosines_it; computeDirectingCosinesOnQuad(nodes, cosines); } } // Mauro: the directing_cosines internal is defined on the quadrature points // of each element AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void MaterialReinforcement::assembleStiffnessMatrix( const ElementType & type, GhostType ghost_type) { AKANTU_DEBUG_IN(); Mesh & mesh = emodel.getMesh(); Mesh::type_iterator type_it = mesh.firstType(dim, ghost_type); Mesh::type_iterator type_end = mesh.lastType(dim, ghost_type); for (; type_it != type_end; ++type_it) { assembleStiffnessMatrixInterface(type, *type_it, ghost_type); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ /** * Computes the reinforcement stiffness matrix (Gomes & Awruch, 2001) * \f[ * \mathbf{K}_e = \sum_{i=1}^R{A_i\int_{S_i}{\mathbf{B}^T * \mathbf{C}_i^T \mathbf{D}_{s, i} \mathbf{C}_i \mathbf{B}\,\mathrm{d}s}} * \f] */ template void MaterialReinforcement::assembleStiffnessMatrixInterface( const ElementType & interface_type, const ElementType & background_type, GhostType ghost_type) { AKANTU_DEBUG_IN(); UInt voigt_size = VoigtHelper::size; FEEngine & interface_engine = emodel.getFEEngine("EmbeddedInterfaceFEEngine"); Array & elem_filter = this->element_filter(interface_type, ghost_type); Array & grad_u = gradu_embedded(interface_type, ghost_type); UInt nb_element = elem_filter.size(); UInt nodes_per_background_e = Mesh::getNbNodesPerElement(background_type); UInt nb_quadrature_points = interface_engine.getNbIntegrationPoints(interface_type, ghost_type); UInt back_dof = dim * nodes_per_background_e; UInt integrant_size = back_dof; grad_u.resize(nb_quadrature_points * nb_element); Array tangent_moduli(nb_element * nb_quadrature_points, 1, "interface_tangent_moduli"); this->computeTangentModuli(interface_type, tangent_moduli, ghost_type); Array & shapesd = (*shape_derivatives(interface_type, ghost_type))( background_type, ghost_type); Array integrant(nb_element * nb_quadrature_points, integrant_size * integrant_size, "B^t*C^t*D*C*B"); /// Temporary matrices for integrant product Matrix Bvoigt(voigt_size, back_dof); Matrix DCB(1, back_dof); Matrix CtDCB(voigt_size, back_dof); Array::scalar_iterator D_it = tangent_moduli.begin(); Array::scalar_iterator D_end = tangent_moduli.end(); Array::matrix_iterator C_it = directing_cosines(interface_type, ghost_type).begin(1, voigt_size); Array::matrix_iterator B_it = shapesd.begin(dim, nodes_per_background_e); Array::matrix_iterator integrant_it = integrant.begin(integrant_size, integrant_size); for (; D_it != D_end; ++D_it, ++C_it, ++B_it, ++integrant_it) { Real & D = *D_it; Matrix & C = *C_it; Matrix & B = *B_it; Matrix & BtCtDCB = *integrant_it; VoigtHelper::transferBMatrixToSymVoigtBMatrix(B, Bvoigt, nodes_per_background_e); DCB.mul(C, Bvoigt); DCB *= D * area; CtDCB.mul(C, DCB); BtCtDCB.mul(Bvoigt, CtDCB); } tangent_moduli.resize(0); Array K_interface(nb_element, integrant_size * integrant_size, "K_interface"); interface_engine.integrate(integrant, K_interface, integrant_size * integrant_size, interface_type, ghost_type, elem_filter); integrant.resize(0); // Mauro: Here K_interface contains the local stiffness matrices, // directing_cosines contains the information about the orientation // of the reinforcements, any rotation of the local stiffness matrix // can be done here auto & filter = getBackgroundFilter(interface_type, background_type, ghost_type); emodel.getDOFManager().assembleElementalMatricesToMatrix( "K", "displacement", K_interface, background_type, ghost_type, _symmetric, filter); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template Real MaterialReinforcement::getEnergy(const std::string & id) { AKANTU_DEBUG_IN(); if (id == "potential") { Real epot = 0.; this->computePotentialEnergyByElements(); Mesh::type_iterator it = this->element_filter.firstType( this->spatial_dimension), end = this->element_filter.lastType( this->spatial_dimension); for (; it != end; ++it) { FEEngine & interface_engine = emodel.getFEEngine("EmbeddedInterfaceFEEngine"); epot += interface_engine.integrate( this->potential_energy(*it, _not_ghost), *it, _not_ghost, this->element_filter(*it, _not_ghost)); epot *= area; } return epot; } AKANTU_DEBUG_OUT(); return 0; } /* -------------------------------------------------------------------------- */ template void MaterialReinforcement::computeGradU( const ElementType & interface_type, GhostType ghost_type) { // Looping over background types for (auto && bg_type : background_filter(interface_type, ghost_type)->elementTypes(dim)) { const UInt nodes_per_background_e = Mesh::getNbNodesPerElement(bg_type); const UInt voigt_size = VoigtHelper::size; auto & bg_shapesd = (*shape_derivatives(interface_type, ghost_type))(bg_type, ghost_type); auto & filter = getBackgroundFilter(interface_type, bg_type, ghost_type); Array disp_per_element(0, dim * nodes_per_background_e, "disp_elem"); FEEngine::extractNodalToElementField( emodel.getMesh(), emodel.getDisplacement(), disp_per_element, bg_type, ghost_type, filter); Matrix concrete_du(dim, dim); Matrix epsilon(dim, dim); Vector evoigt(voigt_size); for (auto && tuple : zip(make_view(disp_per_element, dim, nodes_per_background_e), make_view(bg_shapesd, dim, nodes_per_background_e), this->gradu(interface_type, ghost_type), make_view(this->directing_cosines(interface_type, ghost_type), voigt_size))) { auto & u = std::get<0>(tuple); auto & B = std::get<1>(tuple); auto & du = std::get<2>(tuple); auto & C = std::get<3>(tuple); concrete_du.mul(u, B); auto epsilon = 0.5 * (concrete_du + concrete_du.transpose()); strainTensorToVoigtVector(epsilon, evoigt); du = C.dot(evoigt); } } } /* -------------------------------------------------------------------------- */ /** * The structure of the directing cosines matrix is : * \f{eqnarray*}{ * C_{1,\cdot} & = & (l^2, m^2, n^2, mn, ln, lm) \\ * C_{i,j} & = & 0 * \f} * * with : * \f[ * (l, m, n) = \frac{1}{\|\frac{\mathrm{d}\vec{r}(s)}{\mathrm{d}s}\|} \cdot * \frac{\mathrm{d}\vec{r}(s)}{\mathrm{d}s} * \f] */ template inline void MaterialReinforcement::computeDirectingCosinesOnQuad( const Matrix & nodes, Matrix & cosines) { AKANTU_DEBUG_IN(); AKANTU_DEBUG_ASSERT(nodes.cols() == 2, "Higher order reinforcement elements not implemented"); const Vector a = nodes(0), b = nodes(1); Vector delta = b - a; Real sq_length = 0.; for (UInt i = 0; i < dim; i++) { sq_length += delta(i) * delta(i); } if (dim == 2) { cosines(0, 0) = delta(0) * delta(0); // l^2 cosines(0, 1) = delta(1) * delta(1); // m^2 cosines(0, 2) = delta(0) * delta(1); // lm } else if (dim == 3) { cosines(0, 0) = delta(0) * delta(0); // l^2 cosines(0, 1) = delta(1) * delta(1); // m^2 cosines(0, 2) = delta(2) * delta(2); // n^2 cosines(0, 3) = delta(1) * delta(2); // mn cosines(0, 4) = delta(0) * delta(2); // ln cosines(0, 5) = delta(0) * delta(1); // lm } cosines /= sq_length; AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template inline void MaterialReinforcement::stressTensorToVoigtVector( const Matrix & tensor, Vector & vector) { AKANTU_DEBUG_IN(); for (UInt i = 0; i < dim; i++) { vector(i) = tensor(i, i); } if (dim == 2) { vector(2) = tensor(0, 1); } else if (dim == 3) { vector(3) = tensor(1, 2); vector(4) = tensor(0, 2); vector(5) = tensor(0, 1); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template inline void MaterialReinforcement::strainTensorToVoigtVector( const Matrix & tensor, Vector & vector) { AKANTU_DEBUG_IN(); for (UInt i = 0; i < dim; i++) { vector(i) = tensor(i, i); } if (dim == 2) { vector(2) = 2 * tensor(0, 1); } else if (dim == 3) { vector(3) = 2 * tensor(1, 2); vector(4) = 2 * tensor(0, 2); vector(5) = 2 * tensor(0, 1); } AKANTU_DEBUG_OUT(); } } // akantu diff --git a/src/model/solid_mechanics/solid_mechanics_model.cc b/src/model/solid_mechanics/solid_mechanics_model.cc index ed881af42..cf61034fa 100644 --- a/src/model/solid_mechanics/solid_mechanics_model.cc +++ b/src/model/solid_mechanics/solid_mechanics_model.cc @@ -1,1195 +1,1219 @@ /** * @file solid_mechanics_model.cc * * @author Ramin Aghababaei * @author Guillaume Anciaux * @author Aurelia Isabel Cuba Ramos * @author David Simon Kammer * @author Daniel Pino Muñoz * @author Nicolas Richart * @author Clement Roux * @author Marco Vocialta * * @date creation: Tue Jul 27 2010 * @date last modification: Tue Jan 19 2016 * * @brief Implementation of the SolidMechanicsModel class * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "solid_mechanics_model.hh" #include "integrator_gauss.hh" #include "shape_lagrange.hh" #include "solid_mechanics_model_tmpl.hh" #include "communicator.hh" #include "element_synchronizer.hh" #include "sparse_matrix.hh" #include "synchronizer_registry.hh" #include "dumpable_inline_impl.hh" #ifdef AKANTU_USE_IOHELPER #include "dumper_iohelper_paraview.hh" #endif #include "material_non_local.hh" /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ /** * A solid mechanics model need a mesh and a dimension to be created. the model * by it self can not do a lot, the good init functions should be called in * order to configure the model depending on what we want to do. * * @param mesh mesh representing the model we want to simulate * @param dim spatial dimension of the problem, if dim = 0 (default value) the * dimension of the problem is assumed to be the on of the mesh * @param id an id to identify the model */ SolidMechanicsModel::SolidMechanicsModel(Mesh & mesh, UInt dim, const ID & id, - const MemoryID & memory_id, const ModelType model_type) + const MemoryID & memory_id, + const ModelType model_type) : Model(mesh, model_type, dim, id, memory_id), BoundaryCondition(), f_m2a(1.0), displacement(nullptr), previous_displacement(nullptr), displacement_increment(nullptr), mass(nullptr), velocity(nullptr), acceleration(nullptr), external_force(nullptr), internal_force(nullptr), blocked_dofs(nullptr), current_position(nullptr), material_index("material index", id, memory_id), material_local_numbering("material local numbering", id, memory_id), - increment_flag(false), - are_materials_instantiated(false) { + increment_flag(false), are_materials_instantiated(false) { AKANTU_DEBUG_IN(); this->registerFEEngineObject("SolidMechanicsFEEngine", mesh, Model::spatial_dimension); #if defined(AKANTU_USE_IOHELPER) this->mesh.registerDumper("paraview_all", id, true); this->mesh.addDumpMesh(mesh, Model::spatial_dimension, _not_ghost, _ek_regular); #endif material_selector = std::make_shared(material_index), this->initDOFManager(); this->registerDataAccessor(*this); if (this->mesh.isDistributed()) { auto & synchronizer = this->mesh.getElementSynchronizer(); this->registerSynchronizer(synchronizer, _gst_material_id); this->registerSynchronizer(synchronizer, _gst_smm_mass); this->registerSynchronizer(synchronizer, _gst_smm_stress); this->registerSynchronizer(synchronizer, _gst_smm_boundary); this->registerSynchronizer(synchronizer, _gst_for_dump); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ SolidMechanicsModel::~SolidMechanicsModel() { AKANTU_DEBUG_IN(); for (auto & internal : this->registered_internals) { delete internal.second; } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::setTimeStep(Real time_step, const ID & solver_id) { Model::setTimeStep(time_step, solver_id); #if defined(AKANTU_USE_IOHELPER) this->mesh.getDumper().setTimeStep(time_step); #endif } /* -------------------------------------------------------------------------- */ /* Initialization */ /* -------------------------------------------------------------------------- */ /** * This function groups many of the initialization in on function. For most of * basics case the function should be enough. The functions initialize the * model, the internal vectors, set them to 0, and depending on the parameters * it also initialize the explicit or implicit solver. * * @param material_file the file containing the materials to use * @param method the analysis method wanted. See the akantu::AnalysisMethod for * the different possibilities */ void SolidMechanicsModel::initFullImpl(const ModelOptions & options) { material_index.initialize(mesh, _element_kind = _ek_not_defined, _default_value = UInt(-1), _with_nb_element = true); material_local_numbering.initialize(mesh, _element_kind = _ek_not_defined, _with_nb_element = true); Model::initFullImpl(options); // initialize pbc if (this->pbc_pair.size() != 0) this->initPBC(); // initialize the materials if (this->parser.getLastParsedFile() != "") { this->instantiateMaterials(); } this->initMaterials(); this->initBC(*this, *displacement, *displacement_increment, *external_force); } /* -------------------------------------------------------------------------- */ TimeStepSolverType SolidMechanicsModel::getDefaultSolverType() const { return _tsst_dynamic_lumped; } /* -------------------------------------------------------------------------- */ ModelSolverOptions SolidMechanicsModel::getDefaultSolverOptions( const TimeStepSolverType & type) const { ModelSolverOptions options; switch (type) { case _tsst_dynamic_lumped: { options.non_linear_solver_type = _nls_lumped; options.integration_scheme_type["displacement"] = _ist_central_difference; options.solution_type["displacement"] = IntegrationScheme::_acceleration; break; } case _tsst_static: { options.non_linear_solver_type = _nls_newton_raphson; options.integration_scheme_type["displacement"] = _ist_pseudo_time; options.solution_type["displacement"] = IntegrationScheme::_not_defined; break; } case _tsst_dynamic: { if (this->method == _explicit_consistent_mass) { options.non_linear_solver_type = _nls_newton_raphson; options.integration_scheme_type["displacement"] = _ist_central_difference; options.solution_type["displacement"] = IntegrationScheme::_acceleration; } else { options.non_linear_solver_type = _nls_newton_raphson; options.integration_scheme_type["displacement"] = _ist_trapezoidal_rule_2; options.solution_type["displacement"] = IntegrationScheme::_displacement; } break; } default: AKANTU_EXCEPTION(type << " is not a valid time step solver type"); } return options; } /* -------------------------------------------------------------------------- */ std::tuple SolidMechanicsModel::getDefaultSolverID(const AnalysisMethod & method) { switch (method) { case _explicit_lumped_mass: { return std::make_tuple("explicit_lumped", _tsst_dynamic_lumped); } case _explicit_consistent_mass: { return std::make_tuple("explicit", _tsst_dynamic); } case _static: { return std::make_tuple("static", _tsst_static); } case _implicit_dynamic: { return std::make_tuple("implicit", _tsst_dynamic); } default: return std::make_tuple("unknown", _tsst_not_defined); } } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::initSolver(TimeStepSolverType time_step_solver_type, NonLinearSolverType) { auto & dof_manager = this->getDOFManager(); /* ------------------------------------------------------------------------ */ // for alloc type of solvers this->allocNodalField(this->displacement, spatial_dimension, "displacement"); this->allocNodalField(this->previous_displacement, spatial_dimension, "previous_displacement"); this->allocNodalField(this->displacement_increment, spatial_dimension, "displacement_increment"); this->allocNodalField(this->internal_force, spatial_dimension, "internal_force"); this->allocNodalField(this->external_force, spatial_dimension, "external_force"); this->allocNodalField(this->blocked_dofs, spatial_dimension, "blocked_dofs"); this->allocNodalField(this->current_position, spatial_dimension, "current_position"); // initialize the current positions this->current_position->copy(this->mesh.getNodes()); /* ------------------------------------------------------------------------ */ if (!dof_manager.hasDOFs("displacement")) { dof_manager.registerDOFs("displacement", *this->displacement, _dst_nodal); dof_manager.registerBlockedDOFs("displacement", *this->blocked_dofs); dof_manager.registerDOFsIncrement("displacement", *this->displacement_increment); dof_manager.registerDOFsPrevious("displacement", *this->previous_displacement); } /* ------------------------------------------------------------------------ */ // for dynamic if (time_step_solver_type == _tsst_dynamic || time_step_solver_type == _tsst_dynamic_lumped) { this->allocNodalField(this->velocity, spatial_dimension, "velocity"); this->allocNodalField(this->acceleration, spatial_dimension, "acceleration"); if (!dof_manager.hasDOFsDerivatives("displacement", 1)) { dof_manager.registerDOFsDerivative("displacement", 1, *this->velocity); dof_manager.registerDOFsDerivative("displacement", 2, *this->acceleration); } } } /* -------------------------------------------------------------------------- */ /** * Initialize the model,basically it pre-compute the shapes, shapes derivatives * and jacobian */ void SolidMechanicsModel::initModel() { /// \todo add the current position as a parameter to initShapeFunctions for /// large deformation getFEEngine().initShapeFunctions(_not_ghost); getFEEngine().initShapeFunctions(_ghost); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::assembleResidual() { AKANTU_DEBUG_IN(); /* ------------------------------------------------------------------------ */ // computes the internal forces this->assembleInternalForces(); /* ------------------------------------------------------------------------ */ this->getDOFManager().assembleToResidual("displacement", *this->external_force, 1); this->getDOFManager().assembleToResidual("displacement", *this->internal_force, 1); AKANTU_DEBUG_OUT(); } +/* -------------------------------------------------------------------------- */ +void SolidMechanicsModel::assembleResidual(const ID & residual_part) { + AKANTU_DEBUG_IN(); + + if ("external" == residual_part) { + this->getDOFManager().assembleToResidual("displacement", + *this->external_force, 1); + AKANTU_DEBUG_OUT(); + return; + } + + if ("internal" == residual_part) { + this->getDOFManager().assembleToResidual("displacement", + *this->internal_force, 1); + AKANTU_DEBUG_OUT(); + return; + } + + AKANTU_CUSTOM_EXCEPTION( + debug::SolverCallbackResidualPartUnknown(residual_part)); + + AKANTU_DEBUG_OUT(); +} + /* -------------------------------------------------------------------------- */ MatrixType SolidMechanicsModel::getMatrixType(const ID & matrix_id) { // \TODO check the materials to know what is the correct answer if (matrix_id == "C") return _mt_not_defined; return _symmetric; } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::assembleMatrix(const ID & matrix_id) { if (matrix_id == "K") { this->assembleStiffnessMatrix(); } else if (matrix_id == "M") { this->assembleMass(); } } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::assembleLumpedMatrix(const ID & matrix_id) { if (matrix_id == "M") { this->assembleMassLumped(); } } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::beforeSolveStep() { for (auto & material : materials) material->beforeSolveStep(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::afterSolveStep() { for (auto & material : materials) material->afterSolveStep(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::predictor() { ++displacement_release; } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::corrector() { ++displacement_release; } /* -------------------------------------------------------------------------- */ /** * This function computes the internal forces as F_{int} = \int_{\Omega} N * \sigma d\Omega@f$ */ void SolidMechanicsModel::assembleInternalForces() { AKANTU_DEBUG_IN(); AKANTU_DEBUG_INFO("Assemble the internal forces"); this->internal_force->clear(); // compute the stresses of local elements AKANTU_DEBUG_INFO("Compute local stresses"); for (auto & material : materials) { material->computeAllStresses(_not_ghost); } /* ------------------------------------------------------------------------ */ /* Computation of the non local part */ if (this->non_local_manager) this->non_local_manager->computeAllNonLocalStresses(); // communicate the stresses AKANTU_DEBUG_INFO("Send data for residual assembly"); this->asynchronousSynchronize(_gst_smm_stress); // assemble the forces due to local stresses AKANTU_DEBUG_INFO("Assemble residual for local elements"); for (auto & material : materials) { material->assembleInternalForces(_not_ghost); } // finalize communications AKANTU_DEBUG_INFO("Wait distant stresses"); this->waitEndSynchronize(_gst_smm_stress); // assemble the stresses due to ghost elements AKANTU_DEBUG_INFO("Assemble residual for ghost elements"); for (auto & material : materials) { material->assembleInternalForces(_ghost); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::assembleStiffnessMatrix() { AKANTU_DEBUG_IN(); AKANTU_DEBUG_INFO("Assemble the new stiffness matrix."); // Check if materials need to recompute the matrix bool need_to_reassemble = false; for (auto & material : materials) { need_to_reassemble |= material->hasStiffnessMatrixChanged(); } if (need_to_reassemble) { this->getDOFManager().getMatrix("K").clear(); // call compute stiffness matrix on each local elements for (auto & material : materials) { material->assembleStiffnessMatrix(_not_ghost); } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::updateCurrentPosition() { if (this->current_position_release == this->displacement_release) return; this->current_position->copy(this->mesh.getNodes()); auto cpos_it = this->current_position->begin(Model::spatial_dimension); auto cpos_end = this->current_position->end(Model::spatial_dimension); auto disp_it = this->displacement->begin(Model::spatial_dimension); for (; cpos_it != cpos_end; ++cpos_it, ++disp_it) { *cpos_it += *disp_it; } this->current_position_release = this->displacement_release; } /* -------------------------------------------------------------------------- */ const Array & SolidMechanicsModel::getCurrentPosition() { this->updateCurrentPosition(); return *this->current_position; } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::updateDataForNonLocalCriterion( ElementTypeMapReal & criterion) { const ID field_name = criterion.getName(); for (auto & material : materials) { if (!material->isInternal(field_name, _ek_regular)) continue; for (auto ghost_type : ghost_types) { material->flattenInternal(field_name, criterion, ghost_type, _ek_regular); } } } /* -------------------------------------------------------------------------- */ /* Information */ /* -------------------------------------------------------------------------- */ Real SolidMechanicsModel::getStableTimeStep() { AKANTU_DEBUG_IN(); Real min_dt = getStableTimeStep(_not_ghost); /// reduction min over all processors mesh.getCommunicator().allReduce(min_dt, SynchronizerOperation::_min); AKANTU_DEBUG_OUT(); return min_dt; } /* -------------------------------------------------------------------------- */ Real SolidMechanicsModel::getStableTimeStep(const GhostType & ghost_type) { AKANTU_DEBUG_IN(); Real min_dt = std::numeric_limits::max(); this->updateCurrentPosition(); Element elem; elem.ghost_type = ghost_type; for (auto type : mesh.elementTypes(Model::spatial_dimension, ghost_type, _ek_regular)) { elem.type = type; UInt nb_nodes_per_element = mesh.getNbNodesPerElement(type); UInt nb_element = mesh.getNbElement(type); auto mat_indexes = material_index(type, ghost_type).begin(); auto mat_loc_num = material_local_numbering(type, ghost_type).begin(); Array X(0, nb_nodes_per_element * Model::spatial_dimension); FEEngine::extractNodalToElementField(mesh, *current_position, X, type, _not_ghost); auto X_el = X.begin(Model::spatial_dimension, nb_nodes_per_element); for (UInt el = 0; el < nb_element; ++el, ++X_el, ++mat_indexes, ++mat_loc_num) { elem.element = *mat_loc_num; Real el_h = getFEEngine().getElementInradius(*X_el, type); Real el_c = this->materials[*mat_indexes]->getCelerity(elem); Real el_dt = el_h / el_c; min_dt = std::min(min_dt, el_dt); } } AKANTU_DEBUG_OUT(); return min_dt; } /* -------------------------------------------------------------------------- */ Real SolidMechanicsModel::getKineticEnergy() { AKANTU_DEBUG_IN(); Real ekin = 0.; UInt nb_nodes = mesh.getNbNodes(); if (this->getDOFManager().hasLumpedMatrix("M")) { auto m_it = this->mass->begin(Model::spatial_dimension); auto m_end = this->mass->end(Model::spatial_dimension); auto v_it = this->velocity->begin(Model::spatial_dimension); for (UInt n = 0; m_it != m_end; ++n, ++m_it, ++v_it) { const Vector & v = *v_it; const Vector & m = *m_it; Real mv2 = 0; bool is_local_node = mesh.isLocalOrMasterNode(n); // bool is_not_pbc_slave_node = !isPBCSlaveNode(n); bool count_node = is_local_node; // && is_not_pbc_slave_node; if (count_node) { for (UInt i = 0; i < Model::spatial_dimension; ++i) { if (m(i) > std::numeric_limits::epsilon()) mv2 += v(i) * v(i) * m(i); } } ekin += mv2; } } else if (this->getDOFManager().hasMatrix("M")) { Array Mv(nb_nodes, Model::spatial_dimension); this->getDOFManager().getMatrix("M").matVecMul(*this->velocity, Mv); auto mv_it = Mv.begin(Model::spatial_dimension); auto mv_end = Mv.end(Model::spatial_dimension); auto v_it = this->velocity->begin(Model::spatial_dimension); for (; mv_it != mv_end; ++mv_it, ++v_it) { ekin += v_it->dot(*mv_it); } } else { AKANTU_DEBUG_ERROR("No function called to assemble the mass matrix."); } mesh.getCommunicator().allReduce(ekin, SynchronizerOperation::_sum); AKANTU_DEBUG_OUT(); return ekin * .5; } /* -------------------------------------------------------------------------- */ Real SolidMechanicsModel::getKineticEnergy(const ElementType & type, UInt index) { AKANTU_DEBUG_IN(); UInt nb_quadrature_points = getFEEngine().getNbIntegrationPoints(type); Array vel_on_quad(nb_quadrature_points, Model::spatial_dimension); Array filter_element(1, 1, index); getFEEngine().interpolateOnIntegrationPoints(*velocity, vel_on_quad, Model::spatial_dimension, type, _not_ghost, filter_element); Array::vector_iterator vit = vel_on_quad.begin(Model::spatial_dimension); Array::vector_iterator vend = vel_on_quad.end(Model::spatial_dimension); Vector rho_v2(nb_quadrature_points); Real rho = materials[material_index(type)(index)]->getRho(); for (UInt q = 0; vit != vend; ++vit, ++q) { rho_v2(q) = rho * vit->dot(*vit); } AKANTU_DEBUG_OUT(); return .5 * getFEEngine().integrate(rho_v2, type, index); } /* -------------------------------------------------------------------------- */ Real SolidMechanicsModel::getExternalWork() { AKANTU_DEBUG_IN(); auto ext_force_it = external_force->begin(Model::spatial_dimension); auto int_force_it = internal_force->begin(Model::spatial_dimension); auto boun_it = blocked_dofs->begin(Model::spatial_dimension); decltype(ext_force_it) incr_or_velo_it; if (this->method == _static) { incr_or_velo_it = this->displacement_increment->begin(Model::spatial_dimension); } else { incr_or_velo_it = this->velocity->begin(Model::spatial_dimension); } Real work = 0.; UInt nb_nodes = this->mesh.getNbNodes(); for (UInt n = 0; n < nb_nodes; ++n, ++ext_force_it, ++int_force_it, ++boun_it, ++incr_or_velo_it) { const auto & int_force = *int_force_it; const auto & ext_force = *ext_force_it; const auto & boun = *boun_it; const auto & incr_or_velo = *incr_or_velo_it; bool is_local_node = this->mesh.isLocalOrMasterNode(n); // bool is_not_pbc_slave_node = !this->isPBCSlaveNode(n); bool count_node = is_local_node; // && is_not_pbc_slave_node; if (count_node) { for (UInt i = 0; i < Model::spatial_dimension; ++i) { if (boun(i)) work -= int_force(i) * incr_or_velo(i); else work += ext_force(i) * incr_or_velo(i); } } } mesh.getCommunicator().allReduce(work, SynchronizerOperation::_sum); if (this->method != _static) work *= this->getTimeStep(); AKANTU_DEBUG_OUT(); return work; } /* -------------------------------------------------------------------------- */ Real SolidMechanicsModel::getEnergy(const std::string & energy_id) { AKANTU_DEBUG_IN(); if (energy_id == "kinetic") { return getKineticEnergy(); } else if (energy_id == "external work") { return getExternalWork(); } Real energy = 0.; for (auto & material : materials) energy += material->getEnergy(energy_id); /// reduction sum over all processors mesh.getCommunicator().allReduce(energy, SynchronizerOperation::_sum); AKANTU_DEBUG_OUT(); return energy; } /* -------------------------------------------------------------------------- */ Real SolidMechanicsModel::getEnergy(const std::string & energy_id, const ElementType & type, UInt index) { AKANTU_DEBUG_IN(); if (energy_id == "kinetic") { return getKineticEnergy(type, index); } UInt mat_index = this->material_index(type, _not_ghost)(index); UInt mat_loc_num = this->material_local_numbering(type, _not_ghost)(index); Real energy = this->materials[mat_index]->getEnergy(energy_id, type, mat_loc_num); AKANTU_DEBUG_OUT(); return energy; } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::onElementsAdded(const Array & element_list, const NewElementsEvent & event) { AKANTU_DEBUG_IN(); this->material_index.initialize(mesh, _element_kind = _ek_not_defined, _with_nb_element = true, _default_value = UInt(-1)); this->material_local_numbering.initialize( mesh, _element_kind = _ek_not_defined, _with_nb_element = true, _default_value = UInt(-1)); ElementTypeMapArray filter("new_element_filter", this->getID(), this->getMemoryID()); for (auto & elem : element_list) { if (!filter.exists(elem.type, elem.ghost_type)) filter.alloc(0, 1, elem.type, elem.ghost_type); filter(elem.type, elem.ghost_type).push_back(elem.element); } this->assignMaterialToElements(&filter); for (auto & material : materials) material->onElementsAdded(element_list, event); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::onElementsRemoved( const Array & element_list, const ElementTypeMapArray & new_numbering, const RemovedElementsEvent & event) { for (auto & material : materials) { material->onElementsRemoved(element_list, new_numbering, event); } } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::onNodesAdded(const Array & nodes_list, const NewNodesEvent & event) { AKANTU_DEBUG_IN(); UInt nb_nodes = mesh.getNbNodes(); if (displacement) { displacement->resize(nb_nodes, 0.); ++displacement_release; } if (mass) mass->resize(nb_nodes, 0.); if (velocity) velocity->resize(nb_nodes, 0.); if (acceleration) acceleration->resize(nb_nodes, 0.); if (external_force) external_force->resize(nb_nodes, 0.); if (internal_force) internal_force->resize(nb_nodes, 0.); if (blocked_dofs) blocked_dofs->resize(nb_nodes, 0.); if (current_position) current_position->resize(nb_nodes, 0.); if (previous_displacement) previous_displacement->resize(nb_nodes, 0.); if (displacement_increment) displacement_increment->resize(nb_nodes, 0.); for (auto & material : materials) { material->onNodesAdded(nodes_list, event); } need_to_reassemble_lumped_mass = true; need_to_reassemble_mass = true; AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::onNodesRemoved(const Array & /*element_list*/, const Array & new_numbering, const RemovedNodesEvent & /*event*/) { if (displacement) { mesh.removeNodesFromArray(*displacement, new_numbering); ++displacement_release; } if (mass) mesh.removeNodesFromArray(*mass, new_numbering); if (velocity) mesh.removeNodesFromArray(*velocity, new_numbering); if (acceleration) mesh.removeNodesFromArray(*acceleration, new_numbering); if (internal_force) mesh.removeNodesFromArray(*internal_force, new_numbering); if (external_force) mesh.removeNodesFromArray(*external_force, new_numbering); if (blocked_dofs) mesh.removeNodesFromArray(*blocked_dofs, new_numbering); // if (increment_acceleration) // mesh.removeNodesFromArray(*increment_acceleration, new_numbering); if (displacement_increment) mesh.removeNodesFromArray(*displacement_increment, new_numbering); if (previous_displacement) mesh.removeNodesFromArray(*previous_displacement, new_numbering); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::printself(std::ostream & stream, int indent) const { std::string space; for (Int i = 0; i < indent; i++, space += AKANTU_INDENT) ; stream << space << "Solid Mechanics Model [" << std::endl; stream << space << " + id : " << id << std::endl; stream << space << " + spatial dimension : " << Model::spatial_dimension << std::endl; stream << space << " + fem [" << std::endl; getFEEngine().printself(stream, indent + 2); stream << space << AKANTU_INDENT << "]" << std::endl; stream << space << " + nodals information [" << std::endl; displacement->printself(stream, indent + 2); if (velocity) velocity->printself(stream, indent + 2); if (acceleration) acceleration->printself(stream, indent + 2); if (mass) mass->printself(stream, indent + 2); external_force->printself(stream, indent + 2); internal_force->printself(stream, indent + 2); blocked_dofs->printself(stream, indent + 2); stream << space << AKANTU_INDENT << "]" << std::endl; stream << space << " + material information [" << std::endl; material_index.printself(stream, indent + 2); stream << space << AKANTU_INDENT << "]" << std::endl; stream << space << " + materials [" << std::endl; for (auto & material : materials) { material->printself(stream, indent + 1); } stream << space << AKANTU_INDENT << "]" << std::endl; stream << space << "]" << std::endl; } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::initializeNonLocal() { this->non_local_manager->synchronize(*this, _gst_material_id); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::insertIntegrationPointsInNeighborhoods( const GhostType & ghost_type) { for (auto & mat : materials) { MaterialNonLocalInterface * mat_non_local; if ((mat_non_local = dynamic_cast(mat.get())) == nullptr) continue; ElementTypeMapArray quadrature_points_coordinates( "quadrature_points_coordinates_tmp_nl", this->id, this->memory_id); quadrature_points_coordinates.initialize(this->getFEEngine(), _nb_component = spatial_dimension, _ghost_type = ghost_type); for (auto & type : quadrature_points_coordinates.elementTypes( Model::spatial_dimension, ghost_type)) { this->getFEEngine().computeIntegrationPointsCoordinates( quadrature_points_coordinates(type, ghost_type), type, ghost_type); } mat_non_local->initMaterialNonLocal(); mat_non_local->insertIntegrationPointsInNeighborhoods( ghost_type, quadrature_points_coordinates); } } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::computeNonLocalStresses( const GhostType & ghost_type) { for (auto & mat : materials) { if (dynamic_cast(mat.get()) == nullptr) continue; auto & mat_non_local = dynamic_cast(*mat); mat_non_local.computeNonLocalStresses(ghost_type); } } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::updateLocalInternal( ElementTypeMapReal & internal_flat, const GhostType & ghost_type, const ElementKind & kind) { const ID field_name = internal_flat.getName(); for (auto & material : materials) { if (material->isInternal(field_name, kind)) material->flattenInternal(field_name, internal_flat, ghost_type, kind); } } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::updateNonLocalInternal( ElementTypeMapReal & internal_flat, const GhostType & ghost_type, const ElementKind & kind) { const ID field_name = internal_flat.getName(); for (auto & mat : materials) { if (dynamic_cast(mat.get()) == nullptr) continue; auto & mat_non_local = dynamic_cast(*mat); mat_non_local.updateNonLocalInternals(internal_flat, field_name, ghost_type, kind); } } /* -------------------------------------------------------------------------- */ FEEngine & SolidMechanicsModel::getFEEngineBoundary(const ID & name) { return dynamic_cast( getFEEngineClassBoundary(name)); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::splitElementByMaterial( const Array & elements, std::vector> & elements_per_mat) const { ElementType current_element_type = _not_defined; GhostType current_ghost_type = _casper; const Array * mat_indexes = nullptr; const Array * mat_loc_num = nullptr; for (const auto & el : elements) { if (el.type != current_element_type || el.ghost_type != current_ghost_type) { current_element_type = el.type; current_ghost_type = el.ghost_type; mat_indexes = &(this->material_index(el.type, el.ghost_type)); mat_loc_num = &(this->material_local_numbering(el.type, el.ghost_type)); } Element mat_el = el; mat_el.element = (*mat_loc_num)(el.element); elements_per_mat[(*mat_indexes)(el.element)].push_back(mat_el); } } /* -------------------------------------------------------------------------- */ UInt SolidMechanicsModel::getNbData(const Array & elements, const SynchronizationTag & tag) const { AKANTU_DEBUG_IN(); UInt size = 0; UInt nb_nodes_per_element = 0; for (const Element & el : elements) { nb_nodes_per_element += Mesh::getNbNodesPerElement(el.type); } switch (tag) { case _gst_material_id: { size += elements.size() * sizeof(UInt); break; } case _gst_smm_mass: { size += nb_nodes_per_element * sizeof(Real) * Model::spatial_dimension; // mass vector break; } case _gst_smm_for_gradu: { size += nb_nodes_per_element * Model::spatial_dimension * sizeof(Real); // displacement break; } case _gst_smm_boundary: { // force, displacement, boundary size += nb_nodes_per_element * Model::spatial_dimension * (2 * sizeof(Real) + sizeof(bool)); break; } case _gst_for_dump: { // displacement, velocity, acceleration, residual, force size += nb_nodes_per_element * Model::spatial_dimension * sizeof(Real) * 5; break; } default: {} } if (tag != _gst_material_id) { splitByMaterial(elements, [&](auto && mat, auto && elements) { size += mat.getNbData(elements, tag); }); } AKANTU_DEBUG_OUT(); return size; } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::packData(CommunicationBuffer & buffer, const Array & elements, const SynchronizationTag & tag) const { AKANTU_DEBUG_IN(); switch (tag) { case _gst_material_id: { this->packElementalDataHelper(material_index, buffer, elements, false, getFEEngine()); break; } case _gst_smm_mass: { packNodalDataHelper(*mass, buffer, elements, mesh); break; } case _gst_smm_for_gradu: { packNodalDataHelper(*displacement, buffer, elements, mesh); break; } case _gst_for_dump: { packNodalDataHelper(*displacement, buffer, elements, mesh); packNodalDataHelper(*velocity, buffer, elements, mesh); packNodalDataHelper(*acceleration, buffer, elements, mesh); packNodalDataHelper(*internal_force, buffer, elements, mesh); packNodalDataHelper(*external_force, buffer, elements, mesh); break; } case _gst_smm_boundary: { packNodalDataHelper(*external_force, buffer, elements, mesh); packNodalDataHelper(*velocity, buffer, elements, mesh); packNodalDataHelper(*blocked_dofs, buffer, elements, mesh); break; } default: {} } if (tag != _gst_material_id) { splitByMaterial(elements, [&](auto && mat, auto && elements) { mat.packData(buffer, elements, tag); }); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::unpackData(CommunicationBuffer & buffer, const Array & elements, const SynchronizationTag & tag) { AKANTU_DEBUG_IN(); switch (tag) { case _gst_material_id: { for (auto && element : elements) { UInt recv_mat_index; buffer >> recv_mat_index; UInt & mat_index = material_index(element); if (mat_index != UInt(-1)) continue; // add ghosts element to the correct material mat_index = recv_mat_index; UInt index = materials[mat_index]->addElement(element); material_local_numbering(element) = index; } break; } case _gst_smm_mass: { unpackNodalDataHelper(*mass, buffer, elements, mesh); break; } case _gst_smm_for_gradu: { unpackNodalDataHelper(*displacement, buffer, elements, mesh); break; } case _gst_for_dump: { unpackNodalDataHelper(*displacement, buffer, elements, mesh); unpackNodalDataHelper(*velocity, buffer, elements, mesh); unpackNodalDataHelper(*acceleration, buffer, elements, mesh); unpackNodalDataHelper(*internal_force, buffer, elements, mesh); unpackNodalDataHelper(*external_force, buffer, elements, mesh); break; } case _gst_smm_boundary: { unpackNodalDataHelper(*external_force, buffer, elements, mesh); unpackNodalDataHelper(*velocity, buffer, elements, mesh); unpackNodalDataHelper(*blocked_dofs, buffer, elements, mesh); break; } default: {} } if (tag != _gst_material_id) { splitByMaterial(elements, [&](auto && mat, auto && elements) { mat.unpackData(buffer, elements, tag); }); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ UInt SolidMechanicsModel::getNbData(const Array & dofs, const SynchronizationTag & tag) const { AKANTU_DEBUG_IN(); UInt size = 0; // UInt nb_nodes = mesh.getNbNodes(); switch (tag) { case _gst_smm_uv: { size += sizeof(Real) * Model::spatial_dimension * 2; break; } case _gst_smm_res: { size += sizeof(Real) * Model::spatial_dimension; break; } case _gst_smm_mass: { size += sizeof(Real) * Model::spatial_dimension; break; } case _gst_for_dump: { size += sizeof(Real) * Model::spatial_dimension * 5; break; } default: { AKANTU_DEBUG_ERROR("Unknown ghost synchronization tag : " << tag); } } AKANTU_DEBUG_OUT(); return size * dofs.size(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::packData(CommunicationBuffer & buffer, const Array & dofs, const SynchronizationTag & tag) const { AKANTU_DEBUG_IN(); switch (tag) { case _gst_smm_uv: { packDOFDataHelper(*displacement, buffer, dofs); packDOFDataHelper(*velocity, buffer, dofs); break; } case _gst_smm_res: { packDOFDataHelper(*internal_force, buffer, dofs); break; } case _gst_smm_mass: { packDOFDataHelper(*mass, buffer, dofs); break; } case _gst_for_dump: { packDOFDataHelper(*displacement, buffer, dofs); packDOFDataHelper(*velocity, buffer, dofs); packDOFDataHelper(*acceleration, buffer, dofs); packDOFDataHelper(*internal_force, buffer, dofs); packDOFDataHelper(*external_force, buffer, dofs); break; } default: { AKANTU_DEBUG_ERROR("Unknown ghost synchronization tag : " << tag); } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::unpackData(CommunicationBuffer & buffer, const Array & dofs, const SynchronizationTag & tag) { AKANTU_DEBUG_IN(); switch (tag) { case _gst_smm_uv: { unpackDOFDataHelper(*displacement, buffer, dofs); unpackDOFDataHelper(*velocity, buffer, dofs); break; } case _gst_smm_res: { unpackDOFDataHelper(*internal_force, buffer, dofs); break; } case _gst_smm_mass: { unpackDOFDataHelper(*mass, buffer, dofs); break; } case _gst_for_dump: { unpackDOFDataHelper(*displacement, buffer, dofs); unpackDOFDataHelper(*velocity, buffer, dofs); unpackDOFDataHelper(*acceleration, buffer, dofs); unpackDOFDataHelper(*internal_force, buffer, dofs); unpackDOFDataHelper(*external_force, buffer, dofs); break; } default: { AKANTU_DEBUG_ERROR("Unknown ghost synchronization tag : " << tag); } } AKANTU_DEBUG_OUT(); } } // namespace akantu diff --git a/src/model/solid_mechanics/solid_mechanics_model.hh b/src/model/solid_mechanics/solid_mechanics_model.hh index 368f5fbe1..f42929e96 100644 --- a/src/model/solid_mechanics/solid_mechanics_model.hh +++ b/src/model/solid_mechanics/solid_mechanics_model.hh @@ -1,556 +1,560 @@ /** * @file solid_mechanics_model.hh * * @author Guillaume Anciaux * @author Daniel Pino Muñoz * @author Nicolas Richart * * @date creation: Tue Jul 27 2010 * @date last modification: Tue Jan 19 2016 * * @brief Model of Solid Mechanics * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "boundary_condition.hh" #include "data_accessor.hh" #include "fe_engine.hh" #include "model.hh" #include "non_local_manager.hh" #include "solid_mechanics_model_event_handler.hh" /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_SOLID_MECHANICS_MODEL_HH__ #define __AKANTU_SOLID_MECHANICS_MODEL_HH__ namespace akantu { class Material; class MaterialSelector; class DumperIOHelper; class NonLocalManager; template class IntegratorGauss; template class ShapeLagrange; } // namespace akantu /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ class SolidMechanicsModel : public Model, public DataAccessor, public DataAccessor, public BoundaryCondition, public NonLocalManagerCallback, public EventHandlerManager { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: class NewMaterialElementsEvent : public NewElementsEvent { public: AKANTU_GET_MACRO_NOT_CONST(MaterialList, material, Array &); AKANTU_GET_MACRO(MaterialList, material, const Array &); protected: Array material; }; using MyFEEngineType = FEEngineTemplate; protected: using EventManager = EventHandlerManager; public: SolidMechanicsModel( Mesh & mesh, UInt spatial_dimension = _all_dimensions, const ID & id = "solid_mechanics_model", const MemoryID & memory_id = 0, const ModelType model_type = ModelType::_solid_mechanics_model); ~SolidMechanicsModel() override; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ protected: /// initialize completely the model void initFullImpl( const ModelOptions & options = SolidMechanicsModelOptions()) override; /// initialize all internal arrays for materials virtual void initMaterials(); /// initialize the model void initModel() override; /// function to print the containt of the class void printself(std::ostream & stream, int indent = 0) const override; /// get some default values for derived classes std::tuple getDefaultSolverID(const AnalysisMethod & method) override; /* ------------------------------------------------------------------------ */ /* Solver interface */ /* ------------------------------------------------------------------------ */ public: /// assembles the stiffness matrix, virtual void assembleStiffnessMatrix(); /// assembles the internal forces in the array internal_forces virtual void assembleInternalForces(); protected: /// callback for the solver, this adds f_{ext} - f_{int} to the residual void assembleResidual() override; + /// callback for the solver, this adds f_{ext} or f_{int} to the residual + void assembleResidual(const ID & residual_part) override; + bool canSplitResidual() override { return true; } + /// get the type of matrix needed MatrixType getMatrixType(const ID & matrix_id) override; /// callback for the solver, this assembles different matrices void assembleMatrix(const ID & matrix_id) override; /// callback for the solver, this assembles the stiffness matrix void assembleLumpedMatrix(const ID & matrix_id) override; /// callback for the solver, this is called at beginning of solve void predictor() override; /// callback for the solver, this is called at end of solve void corrector() override; /// callback for the solver, this is called at beginning of solve void beforeSolveStep() override; /// callback for the solver, this is called at end of solve void afterSolveStep() override; /// Callback for the model to instantiate the matricees when needed void initSolver(TimeStepSolverType time_step_solver_type, NonLinearSolverType non_linear_solver_type) override; protected: /* ------------------------------------------------------------------------ */ TimeStepSolverType getDefaultSolverType() const override; /* ------------------------------------------------------------------------ */ ModelSolverOptions getDefaultSolverOptions(const TimeStepSolverType & type) const override; public: bool isDefaultSolverExplicit() { return method == _explicit_lumped_mass || method == _explicit_consistent_mass; } protected: /// update the current position vector void updateCurrentPosition(); /* ------------------------------------------------------------------------ */ /* Materials (solid_mechanics_model_material.cc) */ /* ------------------------------------------------------------------------ */ public: /// register an empty material of a given type Material & registerNewMaterial(const ID & mat_name, const ID & mat_type, const ID & opt_param); /// reassigns materials depending on the material selector virtual void reassignMaterial(); /// apply a constant eigen_grad_u on all quadrature points of a given material virtual void applyEigenGradU(const Matrix & prescribed_eigen_grad_u, const ID & material_name, const GhostType ghost_type = _not_ghost); protected: /// register a material in the dynamic database Material & registerNewMaterial(const ParserSection & mat_section); /// read the material files to instantiate all the materials void instantiateMaterials(); /// set the element_id_by_material and add the elements to the good materials virtual void assignMaterialToElements(const ElementTypeMapArray * filter = nullptr); /* ------------------------------------------------------------------------ */ /* Mass (solid_mechanics_model_mass.cc) */ /* ------------------------------------------------------------------------ */ public: /// assemble the lumped mass matrix void assembleMassLumped(); /// assemble the mass matrix for consistent mass resolutions void assembleMass(); 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); /// fill a vector of rho void computeRho(Array & rho, ElementType type, GhostType ghost_type); /// compute the kinetic energy Real getKineticEnergy(); Real getKineticEnergy(const ElementType & type, UInt index); /// compute the external work (for impose displacement, the velocity should be /// given too) Real getExternalWork(); /* ------------------------------------------------------------------------ */ /* NonLocalManager inherited members */ /* ------------------------------------------------------------------------ */ protected: void initializeNonLocal() override; void updateDataForNonLocalCriterion(ElementTypeMapReal & criterion) override; void computeNonLocalStresses(const GhostType & ghost_type) override; void insertIntegrationPointsInNeighborhoods(const GhostType & ghost_type) override; /// update the values of the non local internal void updateLocalInternal(ElementTypeMapReal & internal_flat, const GhostType & ghost_type, const ElementKind & kind) override; /// copy the results of the averaging in the materials void updateNonLocalInternal(ElementTypeMapReal & internal_flat, const GhostType & ghost_type, const ElementKind & kind) override; /* ------------------------------------------------------------------------ */ /* Data Accessor inherited members */ /* ------------------------------------------------------------------------ */ public: UInt 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; UInt getNbData(const Array & dofs, const SynchronizationTag & tag) const override; void packData(CommunicationBuffer & buffer, const Array & dofs, const SynchronizationTag & tag) const override; void unpackData(CommunicationBuffer & buffer, const Array & dofs, const SynchronizationTag & tag) override; protected: void splitElementByMaterial(const Array & elements, std::vector> & elements_per_mat) const; template void splitByMaterial(const Array & elements, Operation && op) const; /* ------------------------------------------------------------------------ */ /* Mesh Event Handler inherited members */ /* ------------------------------------------------------------------------ */ protected: void onNodesAdded(const Array & nodes_list, const NewNodesEvent & event) override; void onNodesRemoved(const Array & element_list, const Array & new_numbering, const RemovedNodesEvent & event) override; void onElementsAdded(const Array & nodes_list, const NewElementsEvent & event) override; void onElementsRemoved(const Array & element_list, const ElementTypeMapArray & new_numbering, const RemovedElementsEvent & event) override; void onElementsChanged(const Array &, const Array &, const ElementTypeMapArray &, const ChangedElementsEvent &) override{}; /* ------------------------------------------------------------------------ */ /* Dumpable interface (kept for convenience) and dumper relative functions */ /* ------------------------------------------------------------------------ */ public: virtual void onDump(); //! decide wether a field is a material internal or not bool isInternal(const std::string & field_name, const ElementKind & element_kind); #ifndef SWIG //! give the amount of data per element virtual ElementTypeMap getInternalDataPerElem(const std::string & field_name, const ElementKind & kind); //! flatten a given material internal field ElementTypeMapArray & flattenInternal(const std::string & field_name, const ElementKind & kind, const GhostType ghost_type = _not_ghost); //! flatten all the registered material internals void flattenAllRegisteredInternals(const ElementKind & kind); #endif dumper::Field * createNodalFieldReal(const std::string & field_name, const std::string & group_name, bool padding_flag) override; dumper::Field * createNodalFieldBool(const std::string & field_name, const std::string & group_name, bool padding_flag) override; dumper::Field * createElementalField(const std::string & field_name, const std::string & group_name, bool padding_flag, const UInt & spatial_dimension, const ElementKind & kind) override; virtual void dump(const std::string & dumper_name); virtual void dump(const std::string & dumper_name, UInt step); virtual void dump(const std::string & dumper_name, Real time, UInt step); void dump() override; virtual void dump(UInt step); virtual void dump(Real time, UInt step); /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: /// return the dimension of the system space AKANTU_GET_MACRO(SpatialDimension, Model::spatial_dimension, UInt); /// set the value of the time step void setTimeStep(Real time_step, const ID & solver_id = "") override; /// get the value of the conversion from forces/ mass to acceleration AKANTU_GET_MACRO(F_M2A, f_m2a, Real); /// set the value of the conversion from forces/ mass to acceleration AKANTU_SET_MACRO(F_M2A, f_m2a, Real); /// get the SolidMechanicsModel::displacement vector AKANTU_GET_MACRO(Displacement, *displacement, Array &); /// get the SolidMechanicsModel::previous_displacement vector AKANTU_GET_MACRO(PreviousDisplacement, *previous_displacement, Array &); /// get the SolidMechanicsModel::current_position vector \warn only consistent /// after a call to SolidMechanicsModel::updateCurrentPosition const Array & getCurrentPosition(); /// get the SolidMechanicsModel::increment vector \warn only consistent if /// SolidMechanicsModel::setIncrementFlagOn has been called before AKANTU_GET_MACRO(Increment, *displacement_increment, Array &); /// get the lumped SolidMechanicsModel::mass vector AKANTU_GET_MACRO(Mass, *mass, Array &); /// get the SolidMechanicsModel::velocity vector AKANTU_GET_MACRO(Velocity, *velocity, Array &); /// get the SolidMechanicsModel::acceleration vector, updated by /// SolidMechanicsModel::updateAcceleration AKANTU_GET_MACRO(Acceleration, *acceleration, Array &); /// get the SolidMechanicsModel::force vector (external forces) AKANTU_GET_MACRO(Force, *external_force, Array &); /// get the SolidMechanicsModel::internal_force vector (internal forces) AKANTU_GET_MACRO(InternalForce, *internal_force, Array &); /// get the SolidMechanicsModel::blocked_dofs vector AKANTU_GET_MACRO(BlockedDOFs, *blocked_dofs, Array &); /// get the value of the SolidMechanicsModel::increment_flag AKANTU_GET_MACRO(IncrementFlag, increment_flag, bool); /// get a particular material (by material index) inline Material & getMaterial(UInt mat_index); /// get a particular material (by material index) inline const Material & getMaterial(UInt mat_index) const; /// get a particular material (by material name) inline Material & getMaterial(const std::string & name); /// get a particular material (by material name) inline const Material & getMaterial(const std::string & name) const; /// get a particular material id from is name inline UInt getMaterialIndex(const std::string & name) const; /// give the number of materials inline UInt getNbMaterials() const { return materials.size(); } /// give the material internal index from its id Int getInternalIndexFromID(const ID & id) const; /// compute the stable time step Real getStableTimeStep(); /// get the energies Real getEnergy(const std::string & energy_id); /// compute the energy for energy Real getEnergy(const std::string & energy_id, const ElementType & type, UInt index); AKANTU_GET_MACRO(MaterialByElement, material_index, const ElementTypeMapArray &); /// vectors containing local material element index for each global element /// index AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(MaterialByElement, material_index, UInt); AKANTU_GET_MACRO_BY_ELEMENT_TYPE(MaterialByElement, material_index, UInt); AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(MaterialLocalNumbering, material_local_numbering, UInt); AKANTU_GET_MACRO_BY_ELEMENT_TYPE(MaterialLocalNumbering, material_local_numbering, UInt); AKANTU_GET_MACRO_NOT_CONST(MaterialSelector, *material_selector, MaterialSelector &); AKANTU_SET_MACRO(MaterialSelector, material_selector, std::shared_ptr); /// Access the non_local_manager interface AKANTU_GET_MACRO(NonLocalManager, *non_local_manager, NonLocalManager &); /// get the FEEngine object to integrate or interpolate on the boundary FEEngine & getFEEngineBoundary(const ID & name = "") override; protected: friend class Material; protected: /// compute the stable time step Real getStableTimeStep(const GhostType & ghost_type); /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ protected: /// conversion coefficient form force/mass to acceleration Real f_m2a; /// displacements array Array * displacement; UInt displacement_release{0}; /// displacements array at the previous time step (used in finite deformation) Array * previous_displacement{nullptr}; /// increment of displacement Array * displacement_increment{nullptr}; /// lumped mass array Array * mass{nullptr}; /// Check if materials need to recompute the mass array bool need_to_reassemble_lumped_mass{true}; /// Check if materials need to recompute the mass matrix bool need_to_reassemble_mass{true}; /// velocities array Array * velocity{nullptr}; /// accelerations array Array * acceleration{nullptr}; /// accelerations array // Array * increment_acceleration; /// external forces array Array * external_force{nullptr}; /// internal forces array Array * internal_force{nullptr}; /// array specifing if a degree of freedom is blocked or not Array * blocked_dofs{nullptr}; /// array of current position used during update residual Array * current_position{nullptr}; UInt current_position_release{0}; /// Arrays containing the material index for each element ElementTypeMapArray material_index; /// Arrays containing the position in the element filter of the material /// (material's local numbering) ElementTypeMapArray material_local_numbering; /// list of used materials std::vector> materials; /// mapping between material name and material internal id std::map materials_names_to_id; /// class defining of to choose a material std::shared_ptr material_selector; /// flag defining if the increment must be computed or not bool increment_flag; /// tells if the material are instantiated bool are_materials_instantiated; using flatten_internal_map = std::map, ElementTypeMapArray *>; /// map a registered internals to be flattened for dump purposes flatten_internal_map registered_internals; /// non local manager std::unique_ptr non_local_manager; }; /* -------------------------------------------------------------------------- */ namespace BC { namespace Neumann { using FromStress = FromHigherDim; using FromTraction = FromSameDim; } // namespace Neumann } // namespace BC } // namespace akantu /* -------------------------------------------------------------------------- */ /* inline functions */ /* -------------------------------------------------------------------------- */ #include "material.hh" #include "parser.hh" #include "solid_mechanics_model_inline_impl.cc" #include "solid_mechanics_model_tmpl.hh" /* -------------------------------------------------------------------------- */ #endif /* __AKANTU_SOLID_MECHANICS_MODEL_HH__ */ diff --git a/src/model/solid_mechanics/solid_mechanics_model_cohesive/materials/constitutive_laws/material_cohesive_linear.cc b/src/model/solid_mechanics/solid_mechanics_model_cohesive/materials/constitutive_laws/material_cohesive_linear.cc index 61d01bcbd..5780ec6e0 100644 --- a/src/model/solid_mechanics/solid_mechanics_model_cohesive/materials/constitutive_laws/material_cohesive_linear.cc +++ b/src/model/solid_mechanics/solid_mechanics_model_cohesive/materials/constitutive_laws/material_cohesive_linear.cc @@ -1,653 +1,651 @@ /** * @file material_cohesive_linear.cc * * @author Mauro Corrado * @author Marco Vocialta * * @date creation: Wed Feb 22 2012 * @date last modification: Thu Jan 14 2016 * * @brief Linear irreversible cohesive law of mixed mode loading with * random stress definition for extrinsic type * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ #include "dof_synchronizer.hh" #include "material_cohesive_linear.hh" #include "solid_mechanics_model_cohesive.hh" #include "sparse_matrix.hh" namespace akantu { /* -------------------------------------------------------------------------- */ template MaterialCohesiveLinear::MaterialCohesiveLinear( SolidMechanicsModel & model, const ID & id) : MaterialCohesive(model, id), sigma_c_eff("sigma_c_eff", *this), delta_c_eff("delta_c_eff", *this), insertion_stress("insertion_stress", *this), opening_prec("opening_prec", *this), reduction_penalty("reduction_penalty", *this) { AKANTU_DEBUG_IN(); this->registerParam("beta", beta, Real(0.), _pat_parsable | _pat_readable, "Beta parameter"); this->registerParam("G_c", G_c, Real(0.), _pat_parsable | _pat_readable, "Mode I fracture energy"); this->registerParam("penalty", penalty, Real(0.), _pat_parsable | _pat_readable, "Penalty coefficient"); this->registerParam("volume_s", volume_s, Real(0.), _pat_parsable | _pat_readable, "Reference volume for sigma_c scaling"); this->registerParam("m_s", m_s, Real(1.), _pat_parsable | _pat_readable, "Weibull exponent for sigma_c scaling"); this->registerParam("kappa", kappa, Real(1.), _pat_parsable | _pat_readable, "Kappa parameter"); this->registerParam( "contact_after_breaking", contact_after_breaking, false, _pat_parsable | _pat_readable, "Activation of contact when the elements are fully damaged"); this->registerParam("max_quad_stress_insertion", max_quad_stress_insertion, false, _pat_parsable | _pat_readable, "Insertion of cohesive element when stress is high " "enough just on one quadrature point"); this->registerParam("recompute", recompute, false, _pat_parsable | _pat_modifiable, "recompute solution"); this->use_previous_delta_max = true; AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void MaterialCohesiveLinear::initMaterial() { AKANTU_DEBUG_IN(); MaterialCohesive::initMaterial(); /// compute scalars beta2_kappa2 = beta * beta / kappa / kappa; beta2_kappa = beta * beta / kappa; if (Math::are_float_equal(beta, 0)) beta2_inv = 0; else beta2_inv = 1. / beta / beta; sigma_c_eff.initialize(1); delta_c_eff.initialize(1); insertion_stress.initialize(spatial_dimension); opening_prec.initialize(spatial_dimension); reduction_penalty.initialize(1); if (!Math::are_float_equal(delta_c, 0.)) delta_c_eff.setDefaultValue(delta_c); else delta_c_eff.setDefaultValue(2 * G_c / sigma_c); if (model->getIsExtrinsic()) scaleInsertionTraction(); opening_prec.initializeHistory(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void MaterialCohesiveLinear::scaleInsertionTraction() { AKANTU_DEBUG_IN(); // do nothing if volume_s hasn't been specified by the user if (Math::are_float_equal(volume_s, 0.)) return; const Mesh & mesh_facets = model->getMeshFacets(); const FEEngine & fe_engine = model->getFEEngine(); const FEEngine & fe_engine_facet = model->getFEEngine("FacetsFEEngine"); // loop over facet type Mesh::type_iterator first = mesh_facets.firstType(spatial_dimension - 1); Mesh::type_iterator last = mesh_facets.lastType(spatial_dimension - 1); Real base_sigma_c = sigma_c; for (; first != last; ++first) { ElementType type_facet = *first; const Array> & facet_to_element = mesh_facets.getElementToSubelement(type_facet); UInt nb_facet = facet_to_element.size(); UInt nb_quad_per_facet = fe_engine_facet.getNbIntegrationPoints(type_facet); // iterator to modify sigma_c for all the quadrature points of a facet Array::vector_iterator sigma_c_iterator = sigma_c(type_facet).begin_reinterpret(nb_quad_per_facet, nb_facet); for (UInt f = 0; f < nb_facet; ++f, ++sigma_c_iterator) { const std::vector & element_list = facet_to_element(f); // compute bounding volume Real volume = 0; auto elem = element_list.begin(); auto elem_end = element_list.end(); for (; elem != elem_end; ++elem) { if (*elem == ElementNull) continue; // unit vector for integration in order to obtain the volume UInt nb_quadrature_points = fe_engine.getNbIntegrationPoints(elem->type); Vector unit_vector(nb_quadrature_points, 1); volume += fe_engine.integrate(unit_vector, elem->type, elem->element, elem->ghost_type); } // scale sigma_c *sigma_c_iterator -= base_sigma_c; *sigma_c_iterator *= std::pow(volume_s / volume, 1. / m_s); *sigma_c_iterator += base_sigma_c; } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void MaterialCohesiveLinear::checkInsertion( bool check_only) { AKANTU_DEBUG_IN(); const Mesh & mesh_facets = model->getMeshFacets(); CohesiveElementInserter & inserter = model->getElementInserter(); - Real tolerance = Math::getTolerance(); - for (auto && type_facet : mesh_facets.elementTypes(spatial_dimension - 1)) { ElementType type_cohesive = FEEngine::getCohesiveElementType(type_facet); const Array & facets_check = inserter.getCheckFacets(type_facet); auto & f_insertion = inserter.getInsertionFacets(type_facet); auto & f_filter = facet_filter(type_facet); auto & sig_c_eff = sigma_c_eff(type_cohesive); auto & del_c = delta_c_eff(type_cohesive); auto & ins_stress = insertion_stress(type_cohesive); - auto & trac_old = tractions_old(type_cohesive); + auto & trac_old = tractions.previous(type_cohesive); auto & open_prec = opening_prec(type_cohesive); auto & red_penalty = reduction_penalty(type_cohesive); const auto & f_stress = model->getStressOnFacets(type_facet); const auto & sigma_lim = sigma_c(type_facet); Real max_ratio = 0.; UInt index_f = 0; UInt index_filter = 0; UInt nn = 0; UInt nb_quad_facet = model->getFEEngine("FacetsFEEngine").getNbIntegrationPoints(type_facet); UInt nb_quad_cohesive = model->getFEEngine("CohesiveFEEngine") .getNbIntegrationPoints(type_cohesive); AKANTU_DEBUG_ASSERT(nb_quad_cohesive == nb_quad_facet, "The cohesive element and the corresponding facet do " "not have the same numbers of integration points"); UInt nb_facet = f_filter.size(); // if (nb_facet == 0) continue; auto sigma_lim_it = sigma_lim.begin(); Matrix stress_tmp(spatial_dimension, spatial_dimension); Matrix normal_traction(spatial_dimension, nb_quad_facet); Vector stress_check(nb_quad_facet); UInt sp2 = spatial_dimension * spatial_dimension; const auto & tangents = model->getTangents(type_facet); const auto & normals = model->getFEEngine("FacetsFEEngine") .getNormalsOnIntegrationPoints(type_facet); auto normal_begin = normals.begin(spatial_dimension); auto tangent_begin = tangents.begin(tangents.getNbComponent()); auto facet_stress_begin = f_stress.begin(spatial_dimension, spatial_dimension * 2); std::vector new_sigmas; std::vector> new_normal_traction; std::vector new_delta_c; // loop over each facet belonging to this material for (UInt f = 0; f < nb_facet; ++f, ++sigma_lim_it) { UInt facet = f_filter(f); // skip facets where check shouldn't be realized if (!facets_check(facet)) continue; // compute the effective norm on each quadrature point of the facet for (UInt q = 0; q < nb_quad_facet; ++q) { UInt current_quad = facet * nb_quad_facet + q; const Vector & normal = normal_begin[current_quad]; const Vector & tangent = tangent_begin[current_quad]; const Matrix & facet_stress_it = facet_stress_begin[current_quad]; // compute average stress on the current quadrature point Matrix stress_1(facet_stress_it.storage(), spatial_dimension, spatial_dimension); Matrix stress_2(facet_stress_it.storage() + sp2, spatial_dimension, spatial_dimension); stress_tmp.copy(stress_1); stress_tmp += stress_2; stress_tmp /= 2.; Vector normal_traction_vec(normal_traction(q)); // compute normal and effective stress stress_check(q) = computeEffectiveNorm(stress_tmp, normal, tangent, normal_traction_vec); } // verify if the effective stress overcomes the threshold Real final_stress = stress_check.mean(); if (max_quad_stress_insertion) final_stress = *std::max_element( stress_check.storage(), stress_check.storage() + nb_quad_facet); - if (final_stress > (*sigma_lim_it - tolerance)) { + if (final_stress > *sigma_lim_it) { if (model->isDefaultSolverExplicit()) { f_insertion(facet) = true; if (check_only) continue; // store the new cohesive material parameters for each quadrature // point for (UInt q = 0; q < nb_quad_facet; ++q) { Real new_sigma = stress_check(q); Vector normal_traction_vec(normal_traction(q)); if (spatial_dimension != 3) normal_traction_vec *= -1.; new_sigmas.push_back(new_sigma); new_normal_traction.push_back(normal_traction_vec); Real new_delta; // set delta_c in function of G_c or a given delta_c value if (Math::are_float_equal(delta_c, 0.)) new_delta = 2 * G_c / new_sigma; else new_delta = (*sigma_lim_it) / new_sigma * delta_c; new_delta_c.push_back(new_delta); } } else { Real ratio = final_stress / (*sigma_lim_it); if (ratio > max_ratio) { ++nn; max_ratio = ratio; index_f = f; index_filter = f_filter(f); } } } } /// Insertion of only 1 cohesive element in case of implicit approach. The /// one subjected to the highest stress. if (!model->isDefaultSolverExplicit()) { const Communicator & comm = model->getMesh().getCommunicator(); Array abs_max(comm.getNbProc()); abs_max(comm.whoAmI()) = max_ratio; comm.allGather(abs_max); auto it = std::max_element(abs_max.begin(), abs_max.end()); Int pos = it - abs_max.begin(); if (pos != comm.whoAmI()) { AKANTU_DEBUG_OUT(); return; } if (nn) { f_insertion(index_filter) = true; if (!check_only) { // Array::iterator > normal_traction_it = // normal_traction.begin_reinterpret(nb_quad_facet, // spatial_dimension, nb_facet); Array::const_iterator sigma_lim_it = sigma_lim.begin(); for (UInt q = 0; q < nb_quad_cohesive; ++q) { Real new_sigma = (sigma_lim_it[index_f]); Vector normal_traction_vec(spatial_dimension, 0.0); new_sigmas.push_back(new_sigma); new_normal_traction.push_back(normal_traction_vec); Real new_delta; // set delta_c in function of G_c or a given delta_c value if (!Math::are_float_equal(delta_c, 0.)) new_delta = delta_c; else new_delta = 2 * G_c / (new_sigma); new_delta_c.push_back(new_delta); } } } } // update material data for the new elements UInt old_nb_quad_points = sig_c_eff.size(); UInt new_nb_quad_points = new_sigmas.size(); sig_c_eff.resize(old_nb_quad_points + new_nb_quad_points); ins_stress.resize(old_nb_quad_points + new_nb_quad_points); trac_old.resize(old_nb_quad_points + new_nb_quad_points); del_c.resize(old_nb_quad_points + new_nb_quad_points); open_prec.resize(old_nb_quad_points + new_nb_quad_points); red_penalty.resize(old_nb_quad_points + new_nb_quad_points); for (UInt q = 0; q < new_nb_quad_points; ++q) { sig_c_eff(old_nb_quad_points + q) = new_sigmas[q]; del_c(old_nb_quad_points + q) = new_delta_c[q]; red_penalty(old_nb_quad_points + q) = false; for (UInt dim = 0; dim < spatial_dimension; ++dim) { ins_stress(old_nb_quad_points + q, dim) = new_normal_traction[q](dim); trac_old(old_nb_quad_points + q, dim) = new_normal_traction[q](dim); open_prec(old_nb_quad_points + q, dim) = 0.; } } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void MaterialCohesiveLinear::computeTraction( const Array & normal, ElementType el_type, GhostType ghost_type) { AKANTU_DEBUG_IN(); /// define iterators auto traction_it = tractions(el_type, ghost_type).begin(spatial_dimension); auto opening_it = opening(el_type, ghost_type).begin(spatial_dimension); /// opening_prec is the opening of the previous step in the /// Newton-Raphson loop auto opening_prec_it = opening_prec(el_type, ghost_type).begin(spatial_dimension); auto contact_traction_it = contact_tractions(el_type, ghost_type).begin(spatial_dimension); auto contact_opening_it = contact_opening(el_type, ghost_type).begin(spatial_dimension); auto normal_it = normal.begin(spatial_dimension); auto traction_end = tractions(el_type, ghost_type).end(spatial_dimension); auto sigma_c_it = sigma_c_eff(el_type, ghost_type).begin(); auto delta_max_it = delta_max(el_type, ghost_type).begin(); auto delta_max_prev_it = delta_max.previous(el_type, ghost_type).begin(); auto delta_c_it = delta_c_eff(el_type, ghost_type).begin(); auto damage_it = damage(el_type, ghost_type).begin(); auto insertion_stress_it = insertion_stress(el_type, ghost_type).begin(spatial_dimension); auto reduction_penalty_it = reduction_penalty(el_type, ghost_type).begin(); Vector normal_opening(spatial_dimension); Vector tangential_opening(spatial_dimension); if (!this->model->isDefaultSolverExplicit()) this->delta_max(el_type, ghost_type) .copy(this->delta_max.previous(el_type, ghost_type)); /// loop on each quadrature point for (; traction_it != traction_end; ++traction_it, ++opening_it, ++opening_prec_it, ++normal_it, ++sigma_c_it, ++delta_max_it, ++delta_c_it, ++damage_it, ++contact_traction_it, ++insertion_stress_it, ++contact_opening_it, ++delta_max_prev_it, ++reduction_penalty_it) { Real normal_opening_norm, tangential_opening_norm; bool penetration; Real current_penalty = 0.; this->computeTractionOnQuad( *traction_it, *delta_max_it, *delta_max_prev_it, *delta_c_it, *insertion_stress_it, *sigma_c_it, *opening_it, *opening_prec_it, *normal_it, normal_opening, tangential_opening, normal_opening_norm, tangential_opening_norm, *damage_it, penetration, *reduction_penalty_it, current_penalty, *contact_traction_it, *contact_opening_it); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void MaterialCohesiveLinear::checkDeltaMax( GhostType ghost_type) { AKANTU_DEBUG_IN(); /** * This function set a predefined value to the parameter * delta_max_prev of the elements that have been inserted in the * last loading step for which convergence has not been * reached. This is done before reducing the loading and re-doing * the step. Otherwise, the updating of delta_max_prev would be * done with reference to the non-convergent solution. In this * function also other variables related to the contact and * friction behavior are correctly set. */ Mesh & mesh = fem_cohesive.getMesh(); Mesh::type_iterator it = mesh.firstType(spatial_dimension, ghost_type, _ek_cohesive); Mesh::type_iterator last_type = mesh.lastType(spatial_dimension, ghost_type, _ek_cohesive); /** * the variable "recompute" is set to true to activate the * procedure that reduce the penalty parameter for * compression. This procedure is set true only during the phase of * load_reduction, that has to be set in the maiin file. The * penalty parameter will be reduced only for the elements having * reduction_penalty = true. */ recompute = true; for (; it != last_type; ++it) { Array & elem_filter = element_filter(*it, ghost_type); UInt nb_element = elem_filter.size(); if (nb_element == 0) continue; ElementType el_type = *it; /// define iterators auto delta_max_it = delta_max(el_type, ghost_type).begin(); auto delta_max_end = delta_max(el_type, ghost_type).end(); auto delta_max_prev_it = delta_max.previous(el_type, ghost_type).begin(); auto delta_c_it = delta_c_eff(el_type, ghost_type).begin(); auto opening_prec_it = opening_prec(el_type, ghost_type).begin(spatial_dimension); auto opening_prec_prev_it = opening_prec.previous(el_type, ghost_type).begin(spatial_dimension); /// loop on each quadrature point for (; delta_max_it != delta_max_end; ++delta_max_it, ++delta_max_prev_it, ++delta_c_it, ++opening_prec_it, ++opening_prec_prev_it) { if (*delta_max_prev_it == 0) /// elements inserted in the last incremental step, that did /// not converge *delta_max_it = *delta_c_it / 1000; else /// elements introduced in previous incremental steps, for /// which a correct value of delta_max_prev already exists *delta_max_it = *delta_max_prev_it; /// in case convergence is not reached, set opening_prec to the /// value referred to the previous incremental step *opening_prec_it = *opening_prec_prev_it; } } } /* -------------------------------------------------------------------------- */ template void MaterialCohesiveLinear::resetVariables( GhostType ghost_type) { AKANTU_DEBUG_IN(); /** * This function set the variables "recompute" and * "reduction_penalty" to false. It is called by solveStepCohesive * when convergence is reached. Such variables, in fact, have to be * false at the beginning of a new incremental step. */ Mesh & mesh = fem_cohesive.getMesh(); Mesh::type_iterator it = mesh.firstType(spatial_dimension, ghost_type, _ek_cohesive); Mesh::type_iterator last_type = mesh.lastType(spatial_dimension, ghost_type, _ek_cohesive); recompute = false; for (; it != last_type; ++it) { Array & elem_filter = element_filter(*it, ghost_type); UInt nb_element = elem_filter.size(); if (nb_element == 0) continue; ElementType el_type = *it; auto reduction_penalty_it = reduction_penalty(el_type, ghost_type).begin(); auto reduction_penalty_end = reduction_penalty(el_type, ghost_type).end(); /// loop on each quadrature point for (; reduction_penalty_it != reduction_penalty_end; ++reduction_penalty_it) { *reduction_penalty_it = false; } } } /* -------------------------------------------------------------------------- */ template void MaterialCohesiveLinear::computeTangentTraction( const ElementType & el_type, Array & tangent_matrix, const Array & normal, GhostType ghost_type) { AKANTU_DEBUG_IN(); /// define iterators auto tangent_it = tangent_matrix.begin(spatial_dimension, spatial_dimension); auto tangent_end = tangent_matrix.end(spatial_dimension, spatial_dimension); auto normal_it = normal.begin(spatial_dimension); auto opening_it = opening(el_type, ghost_type).begin(spatial_dimension); /// NB: delta_max_it points on delta_max_previous, i.e. the /// delta_max related to the solution of the previous incremental /// step auto delta_max_it = delta_max.previous(el_type, ghost_type).begin(); auto sigma_c_it = sigma_c_eff(el_type, ghost_type).begin(); auto delta_c_it = delta_c_eff(el_type, ghost_type).begin(); auto damage_it = damage(el_type, ghost_type).begin(); auto contact_opening_it = contact_opening(el_type, ghost_type).begin(spatial_dimension); auto reduction_penalty_it = reduction_penalty(el_type, ghost_type).begin(); Vector normal_opening(spatial_dimension); Vector tangential_opening(spatial_dimension); for (; tangent_it != tangent_end; ++tangent_it, ++normal_it, ++opening_it, ++delta_max_it, ++sigma_c_it, ++delta_c_it, ++damage_it, ++contact_opening_it, ++reduction_penalty_it) { Real normal_opening_norm, tangential_opening_norm; bool penetration; Real current_penalty = 0.; this->computeTangentTractionOnQuad( *tangent_it, *delta_max_it, *delta_c_it, *sigma_c_it, *opening_it, *normal_it, normal_opening, tangential_opening, normal_opening_norm, tangential_opening_norm, *damage_it, penetration, *reduction_penalty_it, current_penalty, *contact_opening_it); // check if the tangential stiffness matrix is symmetric // for (UInt h = 0; h < spatial_dimension; ++h){ // for (UInt l = h; l < spatial_dimension; ++l){ // if (l > h){ // Real k_ls = (*tangent_it)[spatial_dimension*h+l]; // Real k_us = (*tangent_it)[spatial_dimension*l+h]; // // std::cout << "k_ls = " << k_ls << std::endl; // // std::cout << "k_us = " << k_us << std::endl; // if (std::abs(k_ls) > 1e-13 && std::abs(k_us) > 1e-13){ // Real error = std::abs((k_ls - k_us) / k_us); // if (error > 1e-10){ // std::cout << "non symmetric cohesive matrix" << std::endl; // // std::cout << "error " << error << std::endl; // } // } // } // } // } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ INSTANTIATE_MATERIAL(cohesive_linear, MaterialCohesiveLinear); } // akantu diff --git a/src/model/solid_mechanics/solid_mechanics_model_cohesive/materials/material_cohesive.cc b/src/model/solid_mechanics/solid_mechanics_model_cohesive/materials/material_cohesive.cc index de473fdf3..130bf7a92 100644 --- a/src/model/solid_mechanics/solid_mechanics_model_cohesive/materials/material_cohesive.cc +++ b/src/model/solid_mechanics/solid_mechanics_model_cohesive/materials/material_cohesive.cc @@ -1,581 +1,572 @@ /** * @file material_cohesive.cc * * @author Nicolas Richart * @author Seyedeh Mohadeseh Taheri Mousavi * @author Marco Vocialta * * @date creation: Wed Feb 22 2012 * @date last modification: Tue Jan 12 2016 * * @brief Specialization of the material class for cohesive elements * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "material_cohesive.hh" #include "aka_random_generator.hh" #include "dof_synchronizer.hh" #include "fe_engine_template.hh" #include "integrator_gauss.hh" #include "shape_cohesive.hh" #include "solid_mechanics_model_cohesive.hh" #include "sparse_matrix.hh" /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ MaterialCohesive::MaterialCohesive(SolidMechanicsModel & model, const ID & id) : Material(model, id), facet_filter("facet_filter", id, this->getMemoryID()), fem_cohesive( model.getFEEngineClass("CohesiveFEEngine")), reversible_energy("reversible_energy", *this), total_energy("total_energy", *this), opening("opening", *this), - opening_old("opening (old)", *this), tractions("tractions", *this), - tractions_old("tractions (old)", *this), + tractions("tractions", *this), contact_tractions("contact_tractions", *this), contact_opening("contact_opening", *this), delta_max("delta max", *this), use_previous_delta_max(false), use_previous_opening(false), damage("damage", *this), sigma_c("sigma_c", *this), normal(0, spatial_dimension, "normal") { AKANTU_DEBUG_IN(); this->model = dynamic_cast(&model); this->registerParam("sigma_c", sigma_c, _pat_parsable | _pat_readable, "Critical stress"); this->registerParam("delta_c", delta_c, Real(0.), _pat_parsable | _pat_readable, "Critical displacement"); this->element_filter.initialize(this->model->getMesh(), _spatial_dimension = spatial_dimension, _element_kind = _ek_cohesive); // this->model->getMesh().initElementTypeMapArray( // this->element_filter, 1, spatial_dimension, false, _ek_cohesive); if (this->model->getIsExtrinsic()) this->facet_filter.initialize(this->model->getMeshFacets(), _spatial_dimension = spatial_dimension - 1, _element_kind = _ek_regular); // this->model->getMeshFacets().initElementTypeMapArray(facet_filter, 1, // spatial_dimension - // 1); this->reversible_energy.initialize(1); this->total_energy.initialize(1); - this->tractions_old.initialize(spatial_dimension); + this->tractions.initialize(spatial_dimension); - this->opening_old.initialize(spatial_dimension); + this->tractions.initializeHistory(); + this->contact_tractions.initialize(spatial_dimension); this->contact_opening.initialize(spatial_dimension); + this->opening.initialize(spatial_dimension); + this->opening.initializeHistory(); + this->delta_max.initialize(1); this->damage.initialize(1); if (this->model->getIsExtrinsic()) this->sigma_c.initialize(1); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ MaterialCohesive::~MaterialCohesive() { AKANTU_DEBUG_IN(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void MaterialCohesive::initMaterial() { AKANTU_DEBUG_IN(); Material::initMaterial(); if (this->use_previous_delta_max) this->delta_max.initializeHistory(); if (this->use_previous_opening) this->opening.initializeHistory(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void MaterialCohesive::assembleInternalForces(GhostType ghost_type) { AKANTU_DEBUG_IN(); #if defined(AKANTU_DEBUG_TOOLS) debug::element_manager.printData(debug::_dm_material_cohesive, "Cohesive Tractions", tractions); #endif auto & internal_force = const_cast &>(model->getInternalForce()); for (auto type : element_filter.elementTypes(spatial_dimension, ghost_type, _ek_cohesive)) { auto & elem_filter = element_filter(type, ghost_type); UInt nb_element = elem_filter.size(); if (nb_element == 0) continue; const auto & shapes = fem_cohesive.getShapes(type, ghost_type); auto & traction = tractions(type, ghost_type); auto & contact_traction = contact_tractions(type, ghost_type); UInt size_of_shapes = shapes.getNbComponent(); UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type); UInt nb_quadrature_points = fem_cohesive.getNbIntegrationPoints(type, ghost_type); /// compute @f$t_i N_a@f$ Array * traction_cpy = new Array( nb_element * nb_quadrature_points, spatial_dimension * size_of_shapes); auto traction_it = traction.begin(spatial_dimension, 1); auto contact_traction_it = contact_traction.begin(spatial_dimension, 1); auto shapes_filtered_begin = shapes.begin(1, size_of_shapes); auto traction_cpy_it = traction_cpy->begin(spatial_dimension, size_of_shapes); Matrix traction_tmp(spatial_dimension, 1); for (UInt el = 0; el < nb_element; ++el) { UInt current_quad = elem_filter(el) * nb_quadrature_points; for (UInt q = 0; q < nb_quadrature_points; ++q, ++traction_it, ++contact_traction_it, ++current_quad, ++traction_cpy_it) { const Matrix & shapes_filtered = shapes_filtered_begin[current_quad]; traction_tmp.copy(*traction_it); traction_tmp += *contact_traction_it; traction_cpy_it->mul(traction_tmp, shapes_filtered); } } /** * compute @f$\int t \cdot N\, dS@f$ by @f$ \sum_q \mathbf{N}^t * \mathbf{t}_q \overline w_q J_q@f$ */ Array * int_t_N = new Array( nb_element, spatial_dimension * size_of_shapes, "int_t_N"); fem_cohesive.integrate(*traction_cpy, *int_t_N, spatial_dimension * size_of_shapes, type, ghost_type, elem_filter); delete traction_cpy; int_t_N->extendComponentsInterlaced(2, int_t_N->getNbComponent()); Real * int_t_N_val = int_t_N->storage(); for (UInt el = 0; el < nb_element; ++el) { for (UInt n = 0; n < size_of_shapes * spatial_dimension; ++n) int_t_N_val[n] *= -1.; int_t_N_val += nb_nodes_per_element * spatial_dimension; } /// assemble model->getDOFManager().assembleElementalArrayLocalArray( *int_t_N, internal_force, type, ghost_type, -1, elem_filter); delete int_t_N; } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void MaterialCohesive::assembleStiffnessMatrix(GhostType ghost_type) { AKANTU_DEBUG_IN(); for (auto type : element_filter.elementTypes(spatial_dimension, ghost_type, _ek_cohesive)) { UInt nb_quadrature_points = fem_cohesive.getNbIntegrationPoints(type, ghost_type); UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type); const Array & shapes = fem_cohesive.getShapes(type, ghost_type); Array & elem_filter = element_filter(type, ghost_type); UInt nb_element = elem_filter.size(); if (!nb_element) continue; UInt size_of_shapes = shapes.getNbComponent(); Array * shapes_filtered = new Array( nb_element * nb_quadrature_points, size_of_shapes, "filtered shapes"); Real * shapes_filtered_val = shapes_filtered->storage(); UInt * elem_filter_val = elem_filter.storage(); for (UInt el = 0; el < nb_element; ++el) { - auto shapes_val = shapes.storage() + - elem_filter_val[el] * size_of_shapes * nb_quadrature_points; + auto shapes_val = + shapes.storage() + + elem_filter_val[el] * size_of_shapes * nb_quadrature_points; memcpy(shapes_filtered_val, shapes_val, size_of_shapes * nb_quadrature_points * sizeof(Real)); shapes_filtered_val += size_of_shapes * nb_quadrature_points; } /** * compute A matrix @f$ \mathbf{A} = \left[\begin{array}{c c c c c c c c c c *c c} * 1 & 0 & 0 & 0 & 0 & 0 & -1 & 0 & 0 & 0 & 0 & 0 \\ * 0 & 1 & 0 & 0 & 0 & 0 & 0 & -1 & 0 & 0 & 0 & 0 \\ * 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & -1 & 0 & 0 & 0 \\ * 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & -1 & 0 & 0 \\ * 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & -1 & 0 \\ * 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & -1 * \end{array} \right]@f$ **/ // UInt size_of_A = // spatial_dimension*size_of_shapes*spatial_dimension*nb_nodes_per_element; // Real * A = new Real[size_of_A]; // memset(A, 0, size_of_A*sizeof(Real)); Matrix A(spatial_dimension * size_of_shapes, spatial_dimension * nb_nodes_per_element); for (UInt i = 0; i < spatial_dimension * size_of_shapes; ++i) { A(i, i) = 1; A(i, i + spatial_dimension * size_of_shapes) = -1; } // compute traction. This call is not necessary for the linear // cohesive law that, currently, is the only one used for the // extrinsic approach. // if (!model->getIsExtrinsic()){ // computeTraction(ghost_type); //} /// get the tangent matrix @f$\frac{\partial{(t/\delta)}}{\partial{\delta}} /// @f$ Array * tangent_stiffness_matrix = new Array( nb_element * nb_quadrature_points, spatial_dimension * spatial_dimension, "tangent_stiffness_matrix"); // Array * normal = new Array(nb_element * // nb_quadrature_points, spatial_dimension, "normal"); normal.resize(nb_quadrature_points); computeNormal(model->getCurrentPosition(), normal, type, ghost_type); /// compute openings @f$\mathbf{\delta}@f$ // computeOpening(model->getDisplacement(), opening(type, ghost_type), type, // ghost_type); tangent_stiffness_matrix->clear(); computeTangentTraction(type, *tangent_stiffness_matrix, normal, ghost_type); // delete normal; UInt size_at_nt_d_n_a = spatial_dimension * nb_nodes_per_element * spatial_dimension * nb_nodes_per_element; Array * at_nt_d_n_a = new Array( nb_element * nb_quadrature_points, size_at_nt_d_n_a, "A^t*N^t*D*N*A"); Array::iterator> shapes_filt_it = shapes_filtered->begin(size_of_shapes); Array::matrix_iterator D_it = tangent_stiffness_matrix->begin(spatial_dimension, spatial_dimension); Array::matrix_iterator At_Nt_D_N_A_it = at_nt_d_n_a->begin(spatial_dimension * nb_nodes_per_element, spatial_dimension * nb_nodes_per_element); Array::matrix_iterator At_Nt_D_N_A_end = at_nt_d_n_a->end(spatial_dimension * nb_nodes_per_element, spatial_dimension * nb_nodes_per_element); Matrix N(spatial_dimension, spatial_dimension * size_of_shapes); Matrix N_A(spatial_dimension, spatial_dimension * nb_nodes_per_element); Matrix D_N_A(spatial_dimension, spatial_dimension * nb_nodes_per_element); for (; At_Nt_D_N_A_it != At_Nt_D_N_A_end; ++At_Nt_D_N_A_it, ++D_it, ++shapes_filt_it) { N.clear(); /** * store the shapes in voigt notations matrix @f$\mathbf{N} = * \begin{array}{cccccc} N_0(\xi) & 0 & N_1(\xi) &0 & N_2(\xi) & 0 \\ * 0 & * N_0(\xi)& 0 &N_1(\xi)& 0 & N_2(\xi) \end{array} @f$ **/ for (UInt i = 0; i < spatial_dimension; ++i) for (UInt n = 0; n < size_of_shapes; ++n) N(i, i + spatial_dimension * n) = (*shapes_filt_it)(n); /** * compute stiffness matrix @f$ \mathbf{K} = \delta \mathbf{U}^T * \int_{\Gamma_c} {\mathbf{P}^t \frac{\partial{\mathbf{t}}} *{\partial{\delta}} * \mathbf{P} d\Gamma \Delta \mathbf{U}} @f$ **/ N_A.mul(N, A); D_N_A.mul(*D_it, N_A); (*At_Nt_D_N_A_it).mul(D_N_A, N_A); } delete tangent_stiffness_matrix; delete shapes_filtered; Array * K_e = new Array(nb_element, size_at_nt_d_n_a, "K_e"); fem_cohesive.integrate(*at_nt_d_n_a, *K_e, size_at_nt_d_n_a, type, ghost_type, elem_filter); delete at_nt_d_n_a; model->getDOFManager().assembleElementalMatricesToMatrix( "K", "displacement", *K_e, type, ghost_type, _unsymmetric, elem_filter); delete K_e; } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- * * Compute traction from displacements * * @param[in] ghost_type compute the residual for _ghost or _not_ghost element */ void MaterialCohesive::computeTraction(GhostType ghost_type) { AKANTU_DEBUG_IN(); #if defined(AKANTU_DEBUG_TOOLS) debug::element_manager.printData(debug::_dm_material_cohesive, "Cohesive Openings", opening); #endif for (auto & type : element_filter.elementTypes(spatial_dimension, ghost_type, _ek_cohesive)) { Array & elem_filter = element_filter(type, ghost_type); UInt nb_element = elem_filter.size(); if (nb_element == 0) continue; UInt nb_quadrature_points = nb_element * fem_cohesive.getNbIntegrationPoints(type, ghost_type); normal.resize(nb_quadrature_points); /// compute normals @f$\mathbf{n}@f$ computeNormal(model->getCurrentPosition(), normal, type, ghost_type); /// compute openings @f$\mathbf{\delta}@f$ computeOpening(model->getDisplacement(), opening(type, ghost_type), type, ghost_type); /// compute traction @f$\mathbf{t}@f$ computeTraction(normal, type, ghost_type); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void MaterialCohesive::computeNormal(const Array & position, Array & normal, ElementType type, GhostType ghost_type) { AKANTU_DEBUG_IN(); auto & fem_cohesive = this->model->getFEEngineClass("CohesiveFEEngine"); - if (type == _cohesive_1d_2) - fem_cohesive.computeNormalsOnIntegrationPoints(position, normal, type, - ghost_type); - else { #define COMPUTE_NORMAL(type) \ fem_cohesive.getShapeFunctions() \ .computeNormalsOnIntegrationPoints( \ position, normal, ghost_type, element_filter(type, ghost_type)); AKANTU_BOOST_COHESIVE_ELEMENT_SWITCH(COMPUTE_NORMAL); #undef COMPUTE_NORMAL - } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void MaterialCohesive::computeOpening(const Array & displacement, Array & opening, ElementType type, GhostType ghost_type) { AKANTU_DEBUG_IN(); auto & fem_cohesive = this->model->getFEEngineClass("CohesiveFEEngine"); #define COMPUTE_OPENING(type) \ fem_cohesive.getShapeFunctions() \ .interpolateOnIntegrationPoints( \ displacement, opening, spatial_dimension, ghost_type, \ element_filter(type, ghost_type)); AKANTU_BOOST_COHESIVE_ELEMENT_SWITCH(COMPUTE_OPENING); #undef COMPUTE_OPENING AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ -void MaterialCohesive::computeEnergies() { +void MaterialCohesive::updateEnergies(ElementType type, + GhostType ghost_type) { AKANTU_DEBUG_IN(); + if(Mesh::getKind(type) != _ek_cohesive) return; + Vector b(spatial_dimension); Vector h(spatial_dimension); - - for (auto & type : element_filter.elementTypes(spatial_dimension, _not_ghost, - _ek_cohesive)) { - auto erev = reversible_energy(type, _not_ghost).begin(); - auto etot = total_energy(type, _not_ghost).begin(); - auto traction_it = tractions(type, _not_ghost).begin(spatial_dimension); - auto traction_old_it = - tractions_old(type, _not_ghost).begin(spatial_dimension); - auto opening_it = opening(type, _not_ghost).begin(spatial_dimension); - auto opening_old_it = - opening_old(type, _not_ghost).begin(spatial_dimension); - - auto traction_end = tractions(type, _not_ghost).end(spatial_dimension); - - /// loop on each quadrature point - for (; traction_it != traction_end; ++traction_it, ++traction_old_it, - ++opening_it, ++opening_old_it, ++erev, - ++etot) { - /// trapezoidal integration - b = *opening_it; - b -= *opening_old_it; - - h = *traction_old_it; - h += *traction_it; - - *etot += .5 * b.dot(h); - *erev = .5 * traction_it->dot(*opening_it); - } + auto erev = reversible_energy(type, ghost_type).begin(); + auto etot = total_energy(type, ghost_type).begin(); + auto traction_it = tractions(type, ghost_type).begin(spatial_dimension); + auto traction_old_it = + tractions.previous(type, ghost_type).begin(spatial_dimension); + auto opening_it = opening(type, ghost_type).begin(spatial_dimension); + auto opening_old_it = opening.previous(type, ghost_type).begin(spatial_dimension); + + auto traction_end = tractions(type, ghost_type).end(spatial_dimension); + + /// loop on each quadrature point + for (; traction_it != traction_end; ++traction_it, ++traction_old_it, + ++opening_it, ++opening_old_it, ++erev, + ++etot) { + /// trapezoidal integration + b = *opening_it; + b -= *opening_old_it; + + h = *traction_old_it; + h += *traction_it; + + *etot += .5 * b.dot(h); + *erev = .5 * traction_it->dot(*opening_it); } /// update old values - GhostType ghost_type = _not_ghost; - for (auto & type : element_filter.elementTypes(spatial_dimension, _not_ghost, - _ek_cohesive)) { - tractions_old(type, ghost_type).copy(tractions(type, ghost_type)); - opening_old(type, ghost_type).copy(opening(type, ghost_type)); - } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ Real MaterialCohesive::getReversibleEnergy() { AKANTU_DEBUG_IN(); Real erev = 0.; /// integrate reversible energy for each type of elements for (auto & type : element_filter.elementTypes(spatial_dimension, _not_ghost, _ek_cohesive)) { erev += fem_cohesive.integrate(reversible_energy(type, _not_ghost), type, _not_ghost, element_filter(type, _not_ghost)); } AKANTU_DEBUG_OUT(); return erev; } /* -------------------------------------------------------------------------- */ Real MaterialCohesive::getDissipatedEnergy() { AKANTU_DEBUG_IN(); Real edis = 0.; /// integrate dissipated energy for each type of elements for (auto & type : element_filter.elementTypes(spatial_dimension, _not_ghost, _ek_cohesive)) { Array dissipated_energy(total_energy(type, _not_ghost)); dissipated_energy -= reversible_energy(type, _not_ghost); edis += fem_cohesive.integrate(dissipated_energy, type, _not_ghost, element_filter(type, _not_ghost)); } AKANTU_DEBUG_OUT(); return edis; } /* -------------------------------------------------------------------------- */ Real MaterialCohesive::getContactEnergy() { AKANTU_DEBUG_IN(); Real econ = 0.; /// integrate contact energy for each type of elements for (auto & type : element_filter.elementTypes(spatial_dimension, _not_ghost, _ek_cohesive)) { auto & el_filter = element_filter(type, _not_ghost); UInt nb_quad_per_el = fem_cohesive.getNbIntegrationPoints(type, _not_ghost); UInt nb_quad_points = el_filter.size() * nb_quad_per_el; Array contact_energy(nb_quad_points); auto contact_traction_it = contact_tractions(type, _not_ghost).begin(spatial_dimension); auto contact_opening_it = contact_opening(type, _not_ghost).begin(spatial_dimension); /// loop on each quadrature point for (UInt q = 0; q < nb_quad_points; ++contact_traction_it, ++contact_opening_it, ++q) { contact_energy(q) = .5 * contact_traction_it->dot(*contact_opening_it); } econ += fem_cohesive.integrate(contact_energy, type, _not_ghost, el_filter); } AKANTU_DEBUG_OUT(); return econ; } /* -------------------------------------------------------------------------- */ Real MaterialCohesive::getEnergy(const std::string & type) { AKANTU_DEBUG_IN(); if (type == "reversible") return getReversibleEnergy(); else if (type == "dissipated") return getDissipatedEnergy(); else if (type == "cohesive contact") return getContactEnergy(); AKANTU_DEBUG_OUT(); return 0.; } /* -------------------------------------------------------------------------- */ } // namespace akantu diff --git a/src/model/solid_mechanics/solid_mechanics_model_cohesive/materials/material_cohesive.hh b/src/model/solid_mechanics/solid_mechanics_model_cohesive/materials/material_cohesive.hh index 4643f98ac..596297cc9 100644 --- a/src/model/solid_mechanics/solid_mechanics_model_cohesive/materials/material_cohesive.hh +++ b/src/model/solid_mechanics/solid_mechanics_model_cohesive/materials/material_cohesive.hh @@ -1,256 +1,251 @@ /** * @file material_cohesive.hh * * @author Nicolas Richart * @author Seyedeh Mohadeseh Taheri Mousavi * @author Marco Vocialta * * @date creation: Fri Jun 18 2010 * @date last modification: Tue Jan 12 2016 * * @brief Specialization of the material class for cohesive elements * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "material.hh" /* -------------------------------------------------------------------------- */ #include "cohesive_internal_field.hh" /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_MATERIAL_COHESIVE_HH__ #define __AKANTU_MATERIAL_COHESIVE_HH__ /* -------------------------------------------------------------------------- */ namespace akantu { class SolidMechanicsModelCohesive; } namespace akantu { class MaterialCohesive : public Material { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: using MyFEEngineCohesiveType = FEEngineTemplate; public: MaterialCohesive(SolidMechanicsModel & model, const ID & id = ""); ~MaterialCohesive() override; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ public: /// initialize the material computed parameter void initMaterial() override; /// compute tractions (including normals and openings) void computeTraction(GhostType ghost_type = _not_ghost); /// assemble residual void assembleInternalForces(GhostType ghost_type = _not_ghost) override; - /// compute reversible and total energies by element - void computeEnergies(); - - /// check stress for cohesive elements' insertion, by default it + /// check stress for cohesive elements' insertion, by default it /// also updates the cohesive elements' data virtual void checkInsertion(bool /*check_only*/ = false) { AKANTU_DEBUG_TO_IMPLEMENT(); } /// check delta_max for cohesive elements in case of no convergence /// in the solveStep (only for extrinsic-implicit) virtual void checkDeltaMax(GhostType /*ghost_type*/ = _not_ghost) { AKANTU_DEBUG_TO_IMPLEMENT(); } /// reset variables when convergence is reached (only for /// extrinsic-implicit) virtual void resetVariables(GhostType /*ghost_type*/ = _not_ghost) { AKANTU_DEBUG_TO_IMPLEMENT(); } /// interpolate stress on given positions for each element (empty /// implemantation to avoid the generic call to be done on cohesive elements) virtual void interpolateStress(const ElementType /*type*/, Array & /*result*/){}; /// compute the stresses void computeAllStresses(GhostType /*ghost_type*/ = _not_ghost) override{}; // add the facet to be handled by the material UInt addFacet(const Element & element); protected: virtual void computeTangentTraction(const ElementType & /*el_type*/, Array & /*tangent_matrix*/, const Array & /*normal*/, GhostType /*ghost_type*/ = _not_ghost) { AKANTU_DEBUG_TO_IMPLEMENT(); } /// compute the normal void computeNormal(const Array & position, Array & normal, ElementType type, GhostType ghost_type); /// compute the opening void computeOpening(const Array & displacement, Array & opening, ElementType type, GhostType ghost_type); template void computeNormal(const Array & position, Array & normal, GhostType ghost_type); /// assemble stiffness void assembleStiffnessMatrix(GhostType ghost_type) override; /// constitutive law virtual void computeTraction(const Array & normal, ElementType el_type, GhostType ghost_type = _not_ghost) = 0; /// parallelism functions inline UInt getNbDataForElements(const Array & elements, SynchronizationTag tag) const; inline void packElementData(CommunicationBuffer & buffer, const Array & elements, SynchronizationTag tag) const; inline void unpackElementData(CommunicationBuffer & buffer, const Array & elements, SynchronizationTag tag); +protected: + void updateEnergies(ElementType el_type, GhostType ghost_type) override; + /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: /// get the opening AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(Opening, opening, Real); /// get the traction AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(Traction, tractions, Real); /// get damage AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(Damage, damage, Real); /// get facet filter AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(FacetFilter, facet_filter, UInt); AKANTU_GET_MACRO_BY_ELEMENT_TYPE(FacetFilter, facet_filter, UInt); AKANTU_GET_MACRO(FacetFilter, facet_filter, const ElementTypeMapArray &); // AKANTU_GET_MACRO(ElementFilter, element_filter, const // ElementTypeMapArray &); /// compute reversible energy Real getReversibleEnergy(); /// compute dissipated energy Real getDissipatedEnergy(); /// compute contact energy Real getContactEnergy(); /// get energy - Real getEnergy(const std::string &type) override; + Real getEnergy(const std::string & type) override; /// return the energy (identified by id) for the provided element - Real getEnergy(const std::string &energy_id, ElementType type, UInt index) override { + Real getEnergy(const std::string & energy_id, ElementType type, + UInt index) override { return Material::getEnergy(energy_id, type, index); } /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ protected: /// list of facets assigned to this material ElementTypeMapArray facet_filter; /// Link to the cohesive fem object in the model FEEngine & fem_cohesive; private: /// reversible energy by quadrature point CohesiveInternalField reversible_energy; /// total energy by quadrature point CohesiveInternalField total_energy; protected: /// opening in all elements and quadrature points CohesiveInternalField opening; - /// opening in all elements and quadrature points (previous time step) - CohesiveInternalField opening_old; - /// traction in all elements and quadrature points CohesiveInternalField tractions; - /// traction in all elements and quadrature points (previous time step) - CohesiveInternalField tractions_old; - /// traction due to contact CohesiveInternalField contact_tractions; /// normal openings for contact tractions CohesiveInternalField contact_opening; /// maximum displacement CohesiveInternalField delta_max; /// tell if the previous delta_max state is needed (in iterative schemes) bool use_previous_delta_max; /// tell if the previous opening state is needed (in iterative schemes) bool use_previous_opening; /// damage CohesiveInternalField damage; /// pointer to the solid mechanics model for cohesive elements SolidMechanicsModelCohesive * model; /// critical stress RandomInternalField sigma_c; /// critical displacement Real delta_c; /// array to temporarily store the normals Array normal; }; /* -------------------------------------------------------------------------- */ /* inline functions */ /* -------------------------------------------------------------------------- */ #include "material_cohesive_inline_impl.cc" } // namespace akantu #include "cohesive_internal_field_tmpl.hh" #endif /* __AKANTU_MATERIAL_COHESIVE_HH__ */ diff --git a/src/model/solid_mechanics/solid_mechanics_model_cohesive/materials/material_cohesive_inline_impl.cc b/src/model/solid_mechanics/solid_mechanics_model_cohesive/materials/material_cohesive_inline_impl.cc index 3a03852bf..5e6a2cf69 100644 --- a/src/model/solid_mechanics/solid_mechanics_model_cohesive/materials/material_cohesive_inline_impl.cc +++ b/src/model/solid_mechanics/solid_mechanics_model_cohesive/materials/material_cohesive_inline_impl.cc @@ -1,95 +1,102 @@ /** * @file material_cohesive_inline_impl.cc * * @author Nicolas Richart * @author Marco Vocialta * * @date creation: Wed Aug 04 2010 * @date last modification: Thu Oct 15 2015 * * @brief MaterialCohesive inline implementation * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ inline UInt MaterialCohesive::addFacet(const Element & element) { Array & f_filter = facet_filter(element.type, element.ghost_type); f_filter.push_back(element.element); - return f_filter.size()-1; + return f_filter.size() - 1; } - /* -------------------------------------------------------------------------- */ -template +template void MaterialCohesive::computeNormal(const Array & /*position*/, - Array & /*normal*/, - GhostType /*ghost_type*/) { -} + Array & /*normal*/, + GhostType /*ghost_type*/) {} /* -------------------------------------------------------------------------- */ -inline UInt MaterialCohesive::getNbDataForElements(const Array & elements, - SynchronizationTag tag) const { +inline UInt +MaterialCohesive::getNbDataForElements(const Array & elements, + SynchronizationTag tag) const { switch (tag) { case _gst_smm_stress: { - return 2 * spatial_dimension * sizeof(Real) * this->getModel().getNbIntegrationPoints(elements, "CohesiveFEEngine"); + return 2 * spatial_dimension * sizeof(Real) * + this->getModel().getNbIntegrationPoints(elements, + "CohesiveFEEngine"); } case _gst_smmc_damage: { - return sizeof(Real) * this->getModel().getNbIntegrationPoints(elements, "CohesiveFEEngine"); + return sizeof(Real) * + this->getModel().getNbIntegrationPoints(elements, + "CohesiveFEEngine"); } default: {} } return 0; } /* -------------------------------------------------------------------------- */ inline void MaterialCohesive::packElementData(CommunicationBuffer & buffer, - const Array & elements, - SynchronizationTag tag) const { + const Array & elements, + SynchronizationTag tag) const { switch (tag) { case _gst_smm_stress: { packElementDataHelper(tractions, buffer, elements, "CohesiveFEEngine"); - packElementDataHelper(contact_tractions, buffer, elements, "CohesiveFEEngine"); + packElementDataHelper(contact_tractions, buffer, elements, + "CohesiveFEEngine"); break; } case _gst_smmc_damage: - packElementDataHelper(damage, buffer, elements, "CohesiveFEEngine"); break; + packElementDataHelper(damage, buffer, elements, "CohesiveFEEngine"); + break; default: {} } } /* -------------------------------------------------------------------------- */ inline void MaterialCohesive::unpackElementData(CommunicationBuffer & buffer, - const Array & elements, - SynchronizationTag tag) { + const Array & elements, + SynchronizationTag tag) { switch (tag) { case _gst_smm_stress: { unpackElementDataHelper(tractions, buffer, elements, "CohesiveFEEngine"); - unpackElementDataHelper(contact_tractions, buffer, elements, "CohesiveFEEngine"); + unpackElementDataHelper(contact_tractions, buffer, elements, + "CohesiveFEEngine"); break; } case _gst_smmc_damage: - unpackElementDataHelper(damage, buffer, elements, "CohesiveFEEngine"); break; + unpackElementDataHelper(damage, buffer, elements, "CohesiveFEEngine"); + break; default: {} } } diff --git a/src/model/solid_mechanics/solid_mechanics_model_cohesive/solid_mechanics_model_cohesive.cc b/src/model/solid_mechanics/solid_mechanics_model_cohesive/solid_mechanics_model_cohesive.cc index 4af001104..ab54a0dc6 100644 --- a/src/model/solid_mechanics/solid_mechanics_model_cohesive/solid_mechanics_model_cohesive.cc +++ b/src/model/solid_mechanics/solid_mechanics_model_cohesive/solid_mechanics_model_cohesive.cc @@ -1,752 +1,713 @@ /** * @file solid_mechanics_model_cohesive.cc * * @author Mauro Corrado * @author Nicolas Richart * @author Marco Vocialta * * @date creation: Tue May 08 2012 * @date last modification: Wed Jan 13 2016 * * @brief Solid mechanics model for cohesive elements * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "solid_mechanics_model_cohesive.hh" #include "aka_iterators.hh" #include "cohesive_element_inserter.hh" #include "element_synchronizer.hh" #include "facet_synchronizer.hh" #include "fe_engine_template.hh" #include "integrator_gauss.hh" #include "material_cohesive.hh" #include "parser.hh" #include "shape_cohesive.hh" #include "dumpable_inline_impl.hh" #ifdef AKANTU_USE_IOHELPER #include "dumper_iohelper_paraview.hh" #endif /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ namespace akantu { -const SolidMechanicsModelCohesiveOptions - default_solid_mechanics_model_cohesive_options(_explicit_lumped_mass, - false); - /* -------------------------------------------------------------------------- */ SolidMechanicsModelCohesive::SolidMechanicsModelCohesive( Mesh & mesh, UInt dim, const ID & id, const MemoryID & memory_id) : SolidMechanicsModel(mesh, dim, id, memory_id, ModelType::_solid_mechanics_model_cohesive), tangents("tangents", id), facet_stress("facet_stress", id), facet_material("facet_material", id) { AKANTU_DEBUG_IN(); auto && tmp_material_selector = std::make_shared(*this); tmp_material_selector->setFallback(this->material_selector); this->material_selector = tmp_material_selector; #if defined(AKANTU_USE_IOHELPER) this->mesh.registerDumper("cohesive elements", id); this->mesh.addDumpMeshToDumper("cohesive elements", mesh, Model::spatial_dimension, _not_ghost, _ek_cohesive); #endif if (this->mesh.isDistributed()) { /// create the distributed synchronizer for cohesive elements this->cohesive_synchronizer = std::make_unique( mesh, "cohesive_distributed_synchronizer"); auto & synchronizer = mesh.getElementSynchronizer(); this->cohesive_synchronizer->split(synchronizer, [](auto && el) { return Mesh::getKind(el.type) == _ek_cohesive; }); this->registerSynchronizer(*cohesive_synchronizer, _gst_material_id); this->registerSynchronizer(*cohesive_synchronizer, _gst_smm_stress); this->registerSynchronizer(*cohesive_synchronizer, _gst_smm_boundary); } this->inserter = std::make_unique( this->mesh, id + ":cohesive_element_inserter"); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ SolidMechanicsModelCohesive::~SolidMechanicsModelCohesive() = default; /* -------------------------------------------------------------------------- */ void SolidMechanicsModelCohesive::setTimeStep(Real time_step, const ID & solver_id) { SolidMechanicsModel::setTimeStep(time_step, solver_id); #if defined(AKANTU_USE_IOHELPER) this->mesh.getDumper("cohesive elements").setTimeStep(time_step); #endif } /* -------------------------------------------------------------------------- */ void SolidMechanicsModelCohesive::initFullImpl(const ModelOptions & options) { AKANTU_DEBUG_IN(); const auto & smmc_options = dynamic_cast(options); this->is_extrinsic = smmc_options.extrinsic; inserter->setIsExtrinsic(is_extrinsic); if (mesh.isDistributed()) { auto & mesh_facets = inserter->getMeshFacets(); auto & synchronizer = dynamic_cast(mesh_facets.getElementSynchronizer()); this->registerSynchronizer(synchronizer, _gst_smmc_facets); this->registerSynchronizer(synchronizer, _gst_smmc_facets_conn); synchronizeGhostFacetsConnectivity(); /// create the facet synchronizer for extrinsic simulations if (is_extrinsic) { - facet_stress_synchronizer = - std::make_unique(mesh_facets); - - facet_stress_synchronizer->updateSchemes( - [&](auto & scheme, auto & proc, auto & direction) { - scheme.copy(const_cast(synchronizer) - .getCommunications() - .getScheme(proc, direction)); - }); + facet_stress_synchronizer = std::make_unique( + synchronizer, id + ":facet_stress_synchronizer"); + this->registerSynchronizer(*facet_stress_synchronizer, _gst_smmc_facets_stress); } inserter->initParallel(*cohesive_synchronizer); } ParserSection section; bool is_empty; std::tie(section, is_empty) = this->getParserSection(); if (not is_empty) { auto inserter_section = section.getSubSections(ParserType::_cohesive_inserter); if (inserter_section.begin() != inserter_section.end()) { inserter->parseSection(*inserter_section.begin()); } } SolidMechanicsModel::initFullImpl(options); AKANTU_DEBUG_OUT(); } // namespace akantu /* -------------------------------------------------------------------------- */ void SolidMechanicsModelCohesive::initMaterials() { AKANTU_DEBUG_IN(); // make sure the material are instantiated if (!are_materials_instantiated) instantiateMaterials(); /// find the first cohesive material UInt cohesive_index = UInt(-1); for (auto && material : enumerate(materials)) { if (dynamic_cast(std::get<1>(material).get())) { cohesive_index = std::get<0>(material); break; } } if (cohesive_index == UInt(-1)) AKANTU_EXCEPTION("No cohesive materials in the material input file"); material_selector->setFallback(cohesive_index); // set the facet information in the material in case of dynamic insertion // to know what material to call for stress checks if (is_extrinsic) { this->initExtrinsicMaterials(); } else { this->initIntrinsicMaterials(); } AKANTU_DEBUG_OUT(); } // namespace akantu /* -------------------------------------------------------------------------- */ void SolidMechanicsModelCohesive::initExtrinsicMaterials() { const Mesh & mesh_facets = inserter->getMeshFacets(); facet_material.initialize( mesh_facets, _spatial_dimension = spatial_dimension - 1, _with_nb_element = true, _default_value = material_selector->getFallbackValue()); - for_each_element(mesh_facets, - [&](auto && element) { - auto mat_index = (*material_selector)(element); - auto & mat = dynamic_cast( - *materials[mat_index]); - facet_material(element) = mat_index; - mat.addFacet(element); - }, - _spatial_dimension = spatial_dimension - 1); + for_each_element( + mesh_facets, + [&](auto && element) { + auto mat_index = (*material_selector)(element); + auto & mat = dynamic_cast(*materials[mat_index]); + facet_material(element) = mat_index; + mat.addFacet(element); + }, + _spatial_dimension = spatial_dimension - 1, _ghost_type = _not_ghost); SolidMechanicsModel::initMaterials(); this->initAutomaticInsertion(); } /* -------------------------------------------------------------------------- */ #if 0 void SolidMechanicsModelCohesive::initIntrinsicCohesiveMaterials( const std::string & cohesive_surfaces) { AKANTU_DEBUG_IN(); #if defined(AKANTU_PARALLEL_COHESIVE_ELEMENT) if (facet_synchronizer != nullptr) inserter->initParallel(facet_synchronizer, cohesive_element_synchronizer); // inserter->initParallel(facet_synchronizer, synch_parallel); #endif std::istringstream split(cohesive_surfaces); std::string physname; while (std::getline(split, physname, ',')) { AKANTU_DEBUG_INFO( "Pre-inserting cohesive elements along facets from physical surface: " << physname); insertElementsFromMeshData(physname); } synchronizeInsertionData(); SolidMechanicsModel::initMaterials(); auto && tmp = std::make_shared(*this); tmp->setFallback(material_selector->getFallbackValue()); tmp->setFallback(material_selector->getFallbackSelector()); material_selector = tmp; // UInt nb_new_elements = inserter->insertElements(); // if (nb_new_elements > 0) { // this->reinitializeSolver(); // } AKANTU_DEBUG_OUT(); } #endif /* -------------------------------------------------------------------------- */ void SolidMechanicsModelCohesive::synchronizeInsertionData() { if (inserter->getMeshFacets().isDistributed()) { inserter->getMeshFacets().getElementSynchronizer().synchronizeOnce( *inserter, _gst_ce_groups); } } /* -------------------------------------------------------------------------- */ void SolidMechanicsModelCohesive::initIntrinsicMaterials() { AKANTU_DEBUG_IN(); SolidMechanicsModel::initMaterials(); this->insertIntrinsicElements(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ /** * Initialize the model,basically it pre-compute the shapes, shapes derivatives * and jacobian */ void SolidMechanicsModelCohesive::initModel() { AKANTU_DEBUG_IN(); SolidMechanicsModel::initModel(); registerFEEngineObject("CohesiveFEEngine", mesh, Model::spatial_dimension); /// add cohesive type connectivity ElementType type = _not_defined; for (auto && type_ghost : ghost_types) { for (const auto & tmp_type : mesh.elementTypes(spatial_dimension, type_ghost)) { const auto & connectivity = mesh.getConnectivity(tmp_type, type_ghost); if (connectivity.size() == 0) continue; type = tmp_type; auto type_facet = Mesh::getFacetType(type); auto type_cohesive = FEEngine::getCohesiveElementType(type_facet); mesh.addConnectivityType(type_cohesive, type_ghost); } } AKANTU_DEBUG_ASSERT(type != _not_defined, "No elements in the mesh"); getFEEngine("CohesiveFEEngine").initShapeFunctions(_not_ghost); getFEEngine("CohesiveFEEngine").initShapeFunctions(_ghost); registerFEEngineObject( "FacetsFEEngine", mesh.getMeshFacets(), Model::spatial_dimension - 1); if (is_extrinsic) { getFEEngine("FacetsFEEngine").initShapeFunctions(_not_ghost); getFEEngine("FacetsFEEngine").initShapeFunctions(_ghost); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModelCohesive::insertIntrinsicElements() { AKANTU_DEBUG_IN(); inserter->insertIntrinsicElements(); AKANTU_DEBUG_OUT(); } -/* -------------------------------------------------------------------------- */ -void SolidMechanicsModelCohesive::updateFacetStressSynchronizer() { - if (facet_stress_synchronizer != nullptr) { - const auto & rank_to_element = - mesh.getElementSynchronizer().getElementToRank(); - const auto & facet_checks = inserter->getCheckFacets(); - const auto & element_to_facet = mesh.getElementToSubelement(); - UInt rank = mesh.getCommunicator().whoAmI(); - - facet_stress_synchronizer->updateSchemes( - [&](auto & scheme, auto & proc, auto & /*direction*/) { - UInt el = 0; - for (auto && element : scheme) { - if (not facet_checks(element)) - continue; - - const auto & next_el = element_to_facet(element); - UInt rank_left = rank_to_element(next_el[0]); - UInt rank_right = rank_to_element(next_el[1]); - - if ((rank_left == rank and rank_right == proc) or - (rank_left == proc and rank_right == rank)) { - scheme[el] = element; - ++el; - } - } - scheme.resize(el); - }); - } -} - /* -------------------------------------------------------------------------- */ void SolidMechanicsModelCohesive::initAutomaticInsertion() { AKANTU_DEBUG_IN(); this->inserter->limitCheckFacets(); - this->updateFacetStressSynchronizer(); - this->resizeFacetStress(); /// compute normals on facets this->computeNormals(); this->initStressInterpolation(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModelCohesive::updateAutomaticInsertion() { AKANTU_DEBUG_IN(); this->inserter->limitCheckFacets(); this->updateFacetStressSynchronizer(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModelCohesive::initStressInterpolation() { Mesh & mesh_facets = inserter->getMeshFacets(); /// compute quadrature points coordinates on facets Array & position = mesh.getNodes(); ElementTypeMapArray quad_facets("quad_facets", id); quad_facets.initialize(mesh_facets, _nb_component = Model::spatial_dimension, _spatial_dimension = Model::spatial_dimension - 1); // mesh_facets.initElementTypeMapArray(quad_facets, Model::spatial_dimension, // Model::spatial_dimension - 1); getFEEngine("FacetsFEEngine") .interpolateOnIntegrationPoints(position, quad_facets); /// compute elements quadrature point positions and build /// element-facet quadrature points data structure ElementTypeMapArray elements_quad_facets("elements_quad_facets", id); elements_quad_facets.initialize( mesh, _nb_component = Model::spatial_dimension, _spatial_dimension = Model::spatial_dimension); // mesh.initElementTypeMapArray(elements_quad_facets, // Model::spatial_dimension, // Model::spatial_dimension); for (auto elem_gt : ghost_types) { for (auto & type : mesh.elementTypes(Model::spatial_dimension, elem_gt)) { UInt nb_element = mesh.getNbElement(type, elem_gt); if (nb_element == 0) continue; /// compute elements' quadrature points and list of facet /// quadrature points positions by element - Array & facet_to_element = + const auto & facet_to_element = mesh_facets.getSubelementToElement(type, elem_gt); - UInt nb_facet_per_elem = facet_to_element.getNbComponent(); - Array & el_q_facet = elements_quad_facets(type, elem_gt); - ElementType facet_type = Mesh::getFacetType(type); - UInt nb_quad_per_facet = - getFEEngine("FacetsFEEngine").getNbIntegrationPoints(facet_type); + auto & el_q_facet = elements_quad_facets(type, elem_gt); - el_q_facet.resize(nb_element * nb_facet_per_elem * nb_quad_per_facet); - - for (UInt el = 0; el < nb_element; ++el) { - for (UInt f = 0; f < nb_facet_per_elem; ++f) { - Element global_facet_elem = facet_to_element(el, f); - UInt global_facet = global_facet_elem.element; - GhostType facet_gt = global_facet_elem.ghost_type; - const Array & quad_f = quad_facets(facet_type, facet_gt); - - for (UInt q = 0; q < nb_quad_per_facet; ++q) { - for (UInt s = 0; s < Model::spatial_dimension; ++s) { - el_q_facet(el * nb_facet_per_elem * nb_quad_per_facet + - f * nb_quad_per_facet + q, - s) = quad_f(global_facet * nb_quad_per_facet + q, s); - } - } - } + auto facet_type = Mesh::getFacetType(type); + auto nb_quad_per_facet = + getFEEngine("FacetsFEEngine").getNbIntegrationPoints(facet_type); + auto nb_facet_per_elem = facet_to_element.getNbComponent(); + + // small hack in the loop to skip boundary elements, they are silently + // initialized to NaN to see if this causes problems + el_q_facet.resize(nb_element * nb_facet_per_elem * nb_quad_per_facet, + std::numeric_limits::quiet_NaN()); + + for (auto && data : + zip(make_view(facet_to_element), + make_view(el_q_facet, spatial_dimension, nb_quad_per_facet))) { + const auto & global_facet = std::get<0>(data); + auto & el_q = std::get<1>(data); + + if (global_facet == ElementNull) + continue; + + Matrix quad_f = + make_view(quad_facets(global_facet.type, global_facet.ghost_type), + spatial_dimension, nb_quad_per_facet) + .begin()[global_facet.element]; + + el_q = quad_f; + + // for (UInt q = 0; q < nb_quad_per_facet; ++q) { + // for (UInt s = 0; s < Model::spatial_dimension; ++s) { + // el_q_facet(el * nb_facet_per_elem * nb_quad_per_facet + + // f * nb_quad_per_facet + q, + // s) = quad_f(global_facet * nb_quad_per_facet + q, + // s); + // } + // } + //} } } } /// loop over non cohesive materials for (auto && material : materials) { if (dynamic_cast(material.get())) continue; /// initialize the interpolation function material->initElementalFieldInterpolation(elements_quad_facets); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModelCohesive::assembleInternalForces() { AKANTU_DEBUG_IN(); // f_int += f_int_cohe for (auto & material : this->materials) { try { auto & mat = dynamic_cast(*material); mat.computeTraction(_not_ghost); } catch (std::bad_cast & bce) { } } SolidMechanicsModel::assembleInternalForces(); - if (isDefaultSolverExplicit()) { - for (auto & material : materials) { - try { - auto & mat = dynamic_cast(*material); - mat.computeEnergies(); - } catch (std::bad_cast & bce) { - } - } - } - AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModelCohesive::computeNormals() { AKANTU_DEBUG_IN(); Mesh & mesh_facets = this->inserter->getMeshFacets(); this->getFEEngine("FacetsFEEngine") .computeNormalsOnIntegrationPoints(_not_ghost); /** * @todo store tangents while computing normals instead of * recomputing them as follows: */ /* ------------------------------------------------------------------------ */ UInt tangent_components = Model::spatial_dimension * (Model::spatial_dimension - 1); tangents.initialize(mesh_facets, _nb_component = tangent_components, _spatial_dimension = Model::spatial_dimension - 1); // mesh_facets.initElementTypeMapArray(tangents, tangent_components, // Model::spatial_dimension - 1); for (auto facet_type : mesh_facets.elementTypes(Model::spatial_dimension - 1)) { const Array & normals = this->getFEEngine("FacetsFEEngine") .getNormalsOnIntegrationPoints(facet_type); Array & tangents = this->tangents(facet_type); Math::compute_tangents(normals, tangents); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModelCohesive::interpolateStress() { ElementTypeMapArray by_elem_result("temporary_stress_by_facets", id); for (auto & material : materials) { - try { - auto & mat __attribute__((unused)) = - dynamic_cast(*material); - } catch (std::bad_cast &) { + auto * mat = dynamic_cast(material.get()); + if (mat == nullptr) /// interpolate stress on facet quadrature points positions material->interpolateStressOnFacets(facet_stress, by_elem_result); - } } #if defined(AKANTU_DEBUG_TOOLS) debug::element_manager.printData( debug::_dm_model_cohesive, "Interpolated stresses before", facet_stress); #endif this->synchronize(_gst_smmc_facets_stress); #if defined(AKANTU_DEBUG_TOOLS) debug::element_manager.printData(debug::_dm_model_cohesive, "Interpolated stresses", facet_stress); #endif } /* -------------------------------------------------------------------------- */ UInt SolidMechanicsModelCohesive::checkCohesiveStress() { + AKANTU_DEBUG_IN(); + interpolateStress(); for (auto & mat : materials) { - try { - auto & mat_cohesive = dynamic_cast(*mat); + auto * mat_cohesive = dynamic_cast(mat.get()); + if(mat_cohesive) { /// check which not ghost cohesive elements are to be created - mat_cohesive.checkInsertion(); - } catch (std::bad_cast &) { + mat_cohesive->checkInsertion(); } } /// communicate data among processors this->synchronize(_gst_smmc_facets); /// insert cohesive elements UInt nb_new_elements = inserter->insertElements(); // if (nb_new_elements > 0) { // this->reinitializeSolver(); // } + AKANTU_DEBUG_OUT(); + return nb_new_elements; } /* -------------------------------------------------------------------------- */ void SolidMechanicsModelCohesive::onElementsAdded( const Array & element_list, const NewElementsEvent & event) { AKANTU_DEBUG_IN(); updateCohesiveSynchronizers(); SolidMechanicsModel::onElementsAdded(element_list, event); if (cohesive_synchronizer != nullptr) cohesive_synchronizer->computeAllBufferSizes(*this); if (is_extrinsic) resizeFacetStress(); /// if (method != _explicit_lumped_mass) { /// this->initSolver(); /// } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModelCohesive::onNodesAdded(const Array & new_nodes, const NewNodesEvent & event) { AKANTU_DEBUG_IN(); // Array nodes_list(nb_new_nodes); // for (UInt n = 0; n < nb_new_nodes; ++n) // nodes_list(n) = doubled_nodes(n, 1); SolidMechanicsModel::onNodesAdded(new_nodes, event); UInt new_node, old_node; try { const auto & cohesive_event = dynamic_cast(event); const auto & old_nodes = cohesive_event.getOldNodesList(); auto copy = [this, &new_node, &old_node](auto & arr) { for (UInt s = 0; s < spatial_dimension; ++s) { arr(new_node, s) = arr(old_node, s); } }; for (auto && pair : zip(new_nodes, old_nodes)) { std::tie(new_node, old_node) = pair; copy(*displacement); copy(*blocked_dofs); if (velocity) copy(*velocity); if (acceleration) copy(*acceleration); if (current_position) copy(*current_position); if (previous_displacement) copy(*previous_displacement); } // if (this->getDOFManager().hasMatrix("M")) { // this->assembleMass(old_nodes); // } // if (this->getDOFManager().hasLumpedMatrix("M")) { // this->assembleMassLumped(old_nodes); // } } catch (std::bad_cast &) { } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModelCohesive::onEndSolveStep(const AnalysisMethod &) { AKANTU_DEBUG_IN(); /* * This is required because the Cauchy stress is the stress measure that * is used to check the insertion of cohesive elements */ for (auto & mat : materials) { if (mat->isFiniteDeformation()) mat->computeAllCauchyStresses(_not_ghost); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModelCohesive::printself(std::ostream & stream, int indent) const { std::string space; for (Int i = 0; i < indent; i++, space += AKANTU_INDENT) ; stream << space << "SolidMechanicsModelCohesive [" << std::endl; SolidMechanicsModel::printself(stream, indent + 1); stream << space << "]" << std::endl; } /* -------------------------------------------------------------------------- */ void SolidMechanicsModelCohesive::resizeFacetStress() { AKANTU_DEBUG_IN(); this->facet_stress.initialize(getFEEngine("FacetsFEEngine"), _nb_component = 2 * spatial_dimension * spatial_dimension, _spatial_dimension = spatial_dimension - 1); // for (auto && ghost_type : ghost_types) { // for (const auto & type : // mesh_facets.elementTypes(spatial_dimension - 1, ghost_type)) { // UInt nb_facet = mesh_facets.getNbElement(type, ghost_type); // UInt nb_quadrature_points = getFEEngine("FacetsFEEngine") // .getNbIntegrationPoints(type, // ghost_type); // UInt new_size = nb_facet * nb_quadrature_points; // facet_stress(type, ghost_type).resize(new_size); // } // } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModelCohesive::addDumpGroupFieldToDumper( const std::string & dumper_name, const std::string & field_id, const std::string & group_name, const ElementKind & element_kind, bool padding_flag) { AKANTU_DEBUG_IN(); UInt spatial_dimension = Model::spatial_dimension; ElementKind _element_kind = element_kind; if (dumper_name == "cohesive elements") { _element_kind = _ek_cohesive; } else if (dumper_name == "facets") { spatial_dimension = Model::spatial_dimension - 1; } SolidMechanicsModel::addDumpGroupFieldToDumper(dumper_name, field_id, group_name, spatial_dimension, _element_kind, padding_flag); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModelCohesive::onDump() { this->flattenAllRegisteredInternals(_ek_cohesive); SolidMechanicsModel::onDump(); } /* -------------------------------------------------------------------------- */ } // namespace akantu diff --git a/src/model/solid_mechanics/solid_mechanics_model_cohesive/solid_mechanics_model_cohesive_parallel.cc b/src/model/solid_mechanics/solid_mechanics_model_cohesive/solid_mechanics_model_cohesive_parallel.cc index c9deec2a9..ba6037031 100644 --- a/src/model/solid_mechanics/solid_mechanics_model_cohesive/solid_mechanics_model_cohesive_parallel.cc +++ b/src/model/solid_mechanics/solid_mechanics_model_cohesive/solid_mechanics_model_cohesive_parallel.cc @@ -1,399 +1,484 @@ /** * @file solid_mechanics_model_cohesive_parallel.cc * * @author Marco Vocialta * * * @brief Functions for parallel cohesive elements * * @section LICENSE * * Copyright (©) 2010-2012, 2014 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * */ /* -------------------------------------------------------------------------- */ +#include "communicator.hh" #include "element_synchronizer.hh" +#include "material_cohesive.hh" #include "solid_mechanics_model_cohesive.hh" #include "solid_mechanics_model_tmpl.hh" -#include "communicator.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ void SolidMechanicsModelCohesive::synchronizeGhostFacetsConnectivity() { AKANTU_DEBUG_IN(); const Communicator & comm = mesh.getCommunicator(); Int psize = comm.getNbProc(); if (psize > 1) { /// get global connectivity for not ghost facets global_connectivity = new ElementTypeMapArray("global_connectivity", id); auto & mesh_facets = inserter->getMeshFacets(); global_connectivity->initialize( mesh_facets, _spatial_dimension = spatial_dimension - 1, _with_nb_element = true, _with_nb_nodes_per_element = true, - _element_kind = _ek_regular, - _ghost_type = _not_ghost); + _element_kind = _ek_regular); mesh_facets.getGlobalConnectivity(*global_connectivity); /// communicate synchronize(_gst_smmc_facets_conn); /// flip facets MeshUtils::flipFacets(mesh_facets, *global_connectivity, _ghost); delete global_connectivity; } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModelCohesive::updateCohesiveSynchronizers() { /// update synchronizers if needed if (not mesh.isDistributed()) return; auto & mesh_facets = inserter->getMeshFacets(); auto & facet_synchronizer = mesh_facets.getElementSynchronizer(); const auto & cfacet_synchronizer = facet_synchronizer; // update the cohesive element synchronizer - cohesive_synchronizer->updateSchemes( - [&](auto && scheme, auto && proc, auto && direction) { - auto & facet_scheme = - cfacet_synchronizer.getCommunications().getScheme(proc, direction); - - for (auto && facet : facet_scheme) { - const auto & connected_elements = mesh_facets.getElementToSubelement( - facet.type, - facet.ghost_type)(facet.element); // slow access here - const auto & cohesive_element = connected_elements[1]; - - auto && cohesive_type = FEEngine::getCohesiveElementType(facet.type); - auto old_nb_cohesive_elements = - mesh.getNbElement(cohesive_type, facet.ghost_type); - old_nb_cohesive_elements -= - mesh.getData("facet_to_double", facet.type, - facet.ghost_type).size(); - - if (cohesive_element.kind() == _ek_cohesive and - cohesive_element.element >= old_nb_cohesive_elements) { - scheme.push_back(cohesive_element); - } - } - }); + cohesive_synchronizer->updateSchemes([&](auto && scheme, auto && proc, + auto && direction) { + auto & facet_scheme = + cfacet_synchronizer.getCommunications().getScheme(proc, direction); + + for (auto && facet : facet_scheme) { + const auto & connected_elements = mesh_facets.getElementToSubelement( + facet.type, + facet.ghost_type)(facet.element); // slow access here + const auto & cohesive_element = connected_elements[1]; + + auto && cohesive_type = FEEngine::getCohesiveElementType(facet.type); + auto old_nb_cohesive_elements = + mesh.getNbElement(cohesive_type, facet.ghost_type); + old_nb_cohesive_elements -= + mesh.getData("facet_to_double", facet.type, facet.ghost_type) + .size(); + + if (cohesive_element.kind() == _ek_cohesive and + cohesive_element.element >= old_nb_cohesive_elements) { + scheme.push_back(cohesive_element); + } + } + }); const auto & comm = mesh.getCommunicator(); auto && my_rank = comm.whoAmI(); // update the facet stress synchronizer - facet_stress_synchronizer->updateSchemes([&](auto && scheme, auto && proc, - auto && /*direction*/) { - auto it_element = scheme.begin(); - for (auto && element : scheme) { - auto && facet_check = inserter->getCheckFacets( - element.type, element.ghost_type)(element.element); // slow access - // here - - if (facet_check) { - auto && connected_elements = mesh_facets.getElementToSubelement( + if (facet_stress_synchronizer) + facet_stress_synchronizer->updateSchemes([&](auto && scheme, auto && proc, + auto && /*direction*/) { + auto it_element = scheme.begin(); + for (auto && element : scheme) { + auto && facet_check = inserter->getCheckFacets( element.type, element.ghost_type)(element.element); // slow access // here - auto && rank_left = facet_synchronizer.getRank(connected_elements[0]); - auto && rank_right = facet_synchronizer.getRank(connected_elements[1]); - - // keep element if the element is still a boundary element between two - // processors - if ((rank_left == Int(proc) and rank_right == my_rank) or - (rank_left == my_rank and rank_right == Int(proc))) { - *it_element = element; - ++it_element; + + if (facet_check) { + auto && connected_elements = mesh_facets.getElementToSubelement( + element.type, element.ghost_type)(element.element); // slow access + // here + auto && rank_left = facet_synchronizer.getRank(connected_elements[0]); + auto && rank_right = + facet_synchronizer.getRank(connected_elements[1]); + + // keep element if the element is still a boundary element between two + // processors + if ((rank_left == Int(proc) and rank_right == my_rank) or + (rank_left == my_rank and rank_right == Int(proc))) { + *it_element = element; + ++it_element; + } } } - } - scheme.resize(it_element - scheme.begin()); - }); + scheme.resize(it_element - scheme.begin()); + }); +} + +/* -------------------------------------------------------------------------- */ +void SolidMechanicsModelCohesive::updateFacetStressSynchronizer() { + if (facet_stress_synchronizer != nullptr) { + const auto & rank_to_element = + mesh.getElementSynchronizer().getElementToRank(); + const auto & facet_checks = inserter->getCheckFacets(); + const auto & mesh_facets = inserter->getMeshFacets(); + const auto & element_to_facet = mesh_facets.getElementToSubelement(); + UInt rank = mesh.getCommunicator().whoAmI(); + + facet_stress_synchronizer->updateSchemes( + [&](auto & scheme, auto & proc, auto & /*direction*/) { + UInt el = 0; + for (auto && element : scheme) { + if (not facet_checks(element)) + continue; + + const auto & next_el = element_to_facet(element); + UInt rank_left = rank_to_element(next_el[0]); + UInt rank_right = rank_to_element(next_el[1]); + + if ((rank_left == rank and rank_right == proc) or + (rank_left == proc and rank_right == rank)) { + scheme[el] = element; + ++el; + } + } + scheme.resize(el); + }); + } } /* -------------------------------------------------------------------------- */ template void SolidMechanicsModelCohesive::packFacetStressDataHelper( const ElementTypeMapArray & data_to_pack, CommunicationBuffer & buffer, const Array & elements) const { packUnpackFacetStressDataHelper( const_cast &>(data_to_pack), buffer, elements); } /* -------------------------------------------------------------------------- */ template void SolidMechanicsModelCohesive::unpackFacetStressDataHelper( ElementTypeMapArray & data_to_unpack, CommunicationBuffer & buffer, const Array & elements) const { packUnpackFacetStressDataHelper(data_to_unpack, buffer, elements); } /* -------------------------------------------------------------------------- */ template void SolidMechanicsModelCohesive::packUnpackFacetStressDataHelper( ElementTypeMapArray & data_to_pack, CommunicationBuffer & buffer, const Array & elements) const { ElementType current_element_type = _not_defined; GhostType current_ghost_type = _casper; UInt nb_quad_per_elem = 0; UInt sp2 = spatial_dimension * spatial_dimension; UInt nb_component = sp2 * 2; bool element_rank = false; Mesh & mesh_facets = inserter->getMeshFacets(); Array * vect = nullptr; Array> * element_to_facet = nullptr; auto & fe_engine = this->getFEEngine("FacetsFEEngine"); for (auto && el : elements) { if (el.type == _not_defined) - AKANTU_EXCEPTION("packUnpackFacetStressDataHelper called with wrong inputs"); + AKANTU_EXCEPTION( + "packUnpackFacetStressDataHelper called with wrong inputs"); if (el.type != current_element_type || el.ghost_type != current_ghost_type) { current_element_type = el.type; current_ghost_type = el.ghost_type; vect = &data_to_pack(el.type, el.ghost_type); element_to_facet = &(mesh_facets.getElementToSubelement(el.type, el.ghost_type)); nb_quad_per_elem = fe_engine.getNbIntegrationPoints(el.type, el.ghost_type); } if (pack_helper) element_rank = (*element_to_facet)(el.element)[0].ghost_type != _not_ghost; else element_rank = (*element_to_facet)(el.element)[0].ghost_type == _not_ghost; for (UInt q = 0; q < nb_quad_per_elem; ++q) { Vector data(vect->storage() + (el.element * nb_quad_per_elem + q) * nb_component + element_rank * sp2, sp2); if (pack_helper) buffer << data; else buffer >> data; } } } /* -------------------------------------------------------------------------- */ UInt SolidMechanicsModelCohesive::getNbQuadsForFacetCheck( const Array & elements) const { UInt nb_quads = 0; UInt nb_quad_per_facet = 0; ElementType current_element_type = _not_defined; GhostType current_ghost_type = _casper; auto & fe_engine = this->getFEEngine("FacetsFEEngine"); - for(auto & el : elements) { + for (auto & el : elements) { if (el.type != current_element_type || el.ghost_type != current_ghost_type) { current_element_type = el.type; current_ghost_type = el.ghost_type; - nb_quad_per_facet = fe_engine - .getNbIntegrationPoints(el.type, el.ghost_type); + nb_quad_per_facet = + fe_engine.getNbIntegrationPoints(el.type, el.ghost_type); } nb_quads += nb_quad_per_facet; } return nb_quads; } /* -------------------------------------------------------------------------- */ UInt SolidMechanicsModelCohesive::getNbData( const Array & elements, const SynchronizationTag & tag) const { AKANTU_DEBUG_IN(); UInt size = 0; if (elements.size() == 0) return 0; /// regular element case if (elements(0).kind() == _ek_regular) { switch (tag) { case _gst_smmc_facets: { size += elements.size() * sizeof(bool); break; } case _gst_smmc_facets_conn: { UInt nb_nodes = Mesh::getNbNodesPerElementList(elements); size += nb_nodes * sizeof(UInt); break; } case _gst_smmc_facets_stress: { UInt nb_quads = getNbQuadsForFacetCheck(elements); size += nb_quads * spatial_dimension * spatial_dimension * sizeof(Real); break; } + case _gst_material_id: { + for (auto && element : elements) { + if (Mesh::getSpatialDimension(element.type) == (spatial_dimension - 1)) + size += sizeof(UInt); + } + + size += SolidMechanicsModel::getNbData(elements, tag); + break; + } + default: { size += SolidMechanicsModel::getNbData(elements, tag); } } } /// cohesive element case else if (elements(0).kind() == _ek_cohesive) { switch (tag) { case _gst_material_id: { size += elements.size() * sizeof(UInt); break; } case _gst_smm_boundary: { UInt nb_nodes_per_element = 0; for (auto && el : elements) { nb_nodes_per_element += Mesh::getNbNodesPerElement(el.type); } // force, displacement, boundary size += nb_nodes_per_element * spatial_dimension * (2 * sizeof(Real) + sizeof(bool)); break; } default: break; } if (tag != _gst_material_id && tag != _gst_smmc_facets) { splitByMaterial(elements, [&](auto && mat, auto && elements) { size += mat.getNbData(elements, tag); }); } } AKANTU_DEBUG_OUT(); return size; } /* -------------------------------------------------------------------------- */ void SolidMechanicsModelCohesive::packData( CommunicationBuffer & buffer, const Array & elements, const SynchronizationTag & tag) const { AKANTU_DEBUG_IN(); if (elements.size() == 0) return; if (elements(0).kind() == _ek_regular) { switch (tag) { case _gst_smmc_facets: { packElementalDataHelper(inserter->getInsertionFacetsByElement(), buffer, elements, false, getFEEngine()); break; } case _gst_smmc_facets_conn: { packElementalDataHelper(*global_connectivity, buffer, elements, false, getFEEngine()); break; } case _gst_smmc_facets_stress: { packFacetStressDataHelper(facet_stress, buffer, elements); break; } + case _gst_material_id: { + for (auto && element : elements) { + if (Mesh::getSpatialDimension(element.type) != (spatial_dimension - 1)) + continue; + buffer << material_index(element); + } + + SolidMechanicsModel::packData(buffer, elements, tag); + break; + } default: { SolidMechanicsModel::packData(buffer, elements, tag); } } - } else if (elements(0).kind() == _ek_cohesive) { + + AKANTU_DEBUG_OUT(); + return; + } + + if (elements(0).kind() == _ek_cohesive) { switch (tag) { case _gst_material_id: { packElementalDataHelper(material_index, buffer, elements, false, getFEEngine("CohesiveFEEngine")); break; } case _gst_smm_boundary: { packNodalDataHelper(*internal_force, buffer, elements, mesh); packNodalDataHelper(*velocity, buffer, elements, mesh); packNodalDataHelper(*blocked_dofs, buffer, elements, mesh); break; } default: {} } if (tag != _gst_material_id && tag != _gst_smmc_facets) { splitByMaterial(elements, [&](auto && mat, auto && elements) { mat.packData(buffer, elements, tag); }); } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModelCohesive::unpackData(CommunicationBuffer & buffer, const Array & elements, const SynchronizationTag & tag) { AKANTU_DEBUG_IN(); if (elements.size() == 0) return; if (elements(0).kind() == _ek_regular) { switch (tag) { case _gst_smmc_facets: { unpackElementalDataHelper(inserter->getInsertionFacetsByElement(), buffer, elements, false, getFEEngine()); break; } case _gst_smmc_facets_conn: { unpackElementalDataHelper(*global_connectivity, buffer, elements, false, getFEEngine()); break; } case _gst_smmc_facets_stress: { unpackFacetStressDataHelper(facet_stress, buffer, elements); break; } + case _gst_material_id: { + for (auto && element : elements) { + if (Mesh::getSpatialDimension(element.type) != (spatial_dimension - 1)) + continue; + + UInt recv_mat_index; + buffer >> recv_mat_index; + UInt & mat_index = material_index(element); + if (mat_index != UInt(-1)) + continue; + + // add ghosts element to the correct material + mat_index = recv_mat_index; + auto & mat = dynamic_cast(*materials[mat_index]); + mat.addFacet(element); + facet_material(element) = recv_mat_index; + } + SolidMechanicsModel::unpackData(buffer, elements, tag); + break; + } default: { SolidMechanicsModel::unpackData(buffer, elements, tag); } } - } else if (elements(0).kind() == _ek_cohesive) { + + AKANTU_DEBUG_OUT(); + return; + } + + if (elements(0).kind() == _ek_cohesive) { switch (tag) { case _gst_material_id: { unpackElementalDataHelper(material_index, buffer, elements, false, getFEEngine("CohesiveFEEngine")); break; } case _gst_smm_boundary: { unpackNodalDataHelper(*internal_force, buffer, elements, mesh); unpackNodalDataHelper(*velocity, buffer, elements, mesh); unpackNodalDataHelper(*blocked_dofs, buffer, elements, mesh); break; } default: {} } if (tag != _gst_material_id && tag != _gst_smmc_facets) { splitByMaterial(elements, [&](auto && mat, auto && elements) { mat.unpackData(buffer, elements, tag); }); } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ } // namespace akantu diff --git a/src/model/solid_mechanics/solid_mechanics_model_embedded_interface/embedded_interface_model.cc b/src/model/solid_mechanics/solid_mechanics_model_embedded_interface/embedded_interface_model.cc index 81df39886..39e0c4939 100644 --- a/src/model/solid_mechanics/solid_mechanics_model_embedded_interface/embedded_interface_model.cc +++ b/src/model/solid_mechanics/solid_mechanics_model_embedded_interface/embedded_interface_model.cc @@ -1,178 +1,176 @@ /** * @file embedded_interface_model.cc * * @author Lucas Frerot * * @date creation: Fri Mar 13 2015 * @date last modification: Mon Dec 14 2015 * * @brief Model of Solid Mechanics with embedded interfaces * * @section LICENSE * * Copyright (©) 2015 EPFL (Ecole Polytechnique Fédérale de Lausanne) Laboratory * (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "embedded_interface_model.hh" #include "material_reinforcement.hh" #include "material_elastic.hh" #include "mesh_iterators.hh" #include "integrator_gauss.hh" #include "shape_lagrange.hh" #ifdef AKANTU_USE_IOHELPER # include "dumper_iohelper_paraview.hh" # include "dumpable_inline_impl.hh" #endif /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ EmbeddedInterfaceModel::EmbeddedInterfaceModel(Mesh & mesh, Mesh & primitive_mesh, UInt spatial_dimension, const ID & id, const MemoryID & memory_id) : SolidMechanicsModel(mesh, spatial_dimension, id, memory_id), intersector(mesh, primitive_mesh), interface_mesh(nullptr), primitive_mesh(primitive_mesh), interface_material_selector(nullptr) { this->model_type = ModelType::_embedded_model; // This pointer should be deleted by ~SolidMechanicsModel() auto mat_sel_pointer = std::make_shared>("physical_names", *this); this->setMaterialSelector(mat_sel_pointer); interface_mesh = &(intersector.getInterfaceMesh()); // Create 1D FEEngine on the interface mesh registerFEEngineObject("EmbeddedInterfaceFEEngine", *interface_mesh, 1); // Registering allocator for material reinforcement MaterialFactory::getInstance().registerAllocator( "reinforcement", [&](UInt dim, const ID & constitutive, SolidMechanicsModel &, const ID & id) -> std::unique_ptr { if (constitutive == "elastic") { using mat = MaterialElastic<1>; switch (dim) { case 2: - return std::unique_ptr>{ - new MaterialReinforcement(*this, id)}; + return std::make_unique>(*this, id); case 3: - return std::unique_ptr>{ - new MaterialReinforcement(*this, id)}; + return std::make_unique>(*this, id); default: AKANTU_EXCEPTION("Dimension 1 is invalid for reinforcements"); } } else { AKANTU_EXCEPTION("Reinforcement type" << constitutive << " is not recognized"); } }); } /* -------------------------------------------------------------------------- */ EmbeddedInterfaceModel::~EmbeddedInterfaceModel() { delete interface_material_selector; } /* -------------------------------------------------------------------------- */ void EmbeddedInterfaceModel::initFullImpl(const ModelOptions & options) { const auto & eim_options = dynamic_cast(options); // Do no initialize interface_mesh if told so if (eim_options.has_intersections) intersector.constructData(); SolidMechanicsModel::initFullImpl(options); #if defined(AKANTU_USE_IOHELPER) this->mesh.registerDumper("reinforcement", id); this->mesh.addDumpMeshToDumper("reinforcement", *interface_mesh, 1, _not_ghost, _ek_regular); #endif } void EmbeddedInterfaceModel::initModel() { // Initialize interface FEEngine SolidMechanicsModel::initModel(); FEEngine & engine = getFEEngine("EmbeddedInterfaceFEEngine"); engine.initShapeFunctions(_not_ghost); engine.initShapeFunctions(_ghost); } /* -------------------------------------------------------------------------- */ void EmbeddedInterfaceModel::assignMaterialToElements( const ElementTypeMapArray * filter) { delete interface_material_selector; interface_material_selector = new InterfaceMeshDataMaterialSelector("physical_names", *this); for_each_element(getInterfaceMesh(), [&](auto && element) { auto mat_index = (*interface_material_selector)(element); // material_index(element) = mat_index; materials[mat_index]->addElement(element); // this->material_local_numbering(element) = index; }, _element_filter = filter, _spatial_dimension = 1); SolidMechanicsModel::assignMaterialToElements(filter); } /* -------------------------------------------------------------------------- */ void EmbeddedInterfaceModel::addDumpGroupFieldToDumper(const std::string & dumper_name, const std::string & field_id, const std::string & group_name, const ElementKind & element_kind, bool padding_flag) { #ifdef AKANTU_USE_IOHELPER dumper::Field * field = NULL; // If dumper is reinforcement, create a 1D elemental field if (dumper_name == "reinforcement") field = this->createElementalField(field_id, group_name, padding_flag, 1, element_kind); else { try { SolidMechanicsModel::addDumpGroupFieldToDumper(dumper_name, field_id, group_name, element_kind, padding_flag); } catch (...) {} } if (field) { DumperIOHelper & dumper = mesh.getGroupDumper(dumper_name,group_name); Model::addDumpGroupFieldToDumper(field_id,field,dumper); } #endif } } // akantu diff --git a/src/model/solver_callback.hh b/src/model/solver_callback.hh index 9ab943c99..f986d9908 100644 --- a/src/model/solver_callback.hh +++ b/src/model/solver_callback.hh @@ -1,92 +1,108 @@ /** * @file solver_callback.hh * * @author Nicolas Richart * * @date Tue Sep 15 22:45:27 2015 * * @brief Class defining the interface for non_linear_solver callbacks * * @section LICENSE * * Copyright (©) 2010-2011 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_common.hh" /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_SOLVER_CALLBACK_HH__ #define __AKANTU_SOLVER_CALLBACK_HH__ namespace akantu { class DOFManager; } namespace akantu { class SolverCallback { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: explicit SolverCallback(DOFManager & dof_manager); explicit SolverCallback(); /* ------------------------------------------------------------------------ */ virtual ~SolverCallback(); protected: void setDOFManager(DOFManager & dof_manager); /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ public: /// get the type of matrix needed virtual MatrixType getMatrixType(const ID &) = 0; /// callback to assemble a Matrix virtual void assembleMatrix(const ID &) = 0; /// callback to assemble a lumped Matrix virtual void assembleLumpedMatrix(const ID &) = 0; /// callback to assemble the residual (rhs) virtual void assembleResidual() = 0; + /// callback to assemble the rhs parts, (e.g. internal_forces + + /// external_forces) + virtual void assembleResidual(const ID & /*residual_part*/) {} + /* ------------------------------------------------------------------------ */ /* Dynamic simulations part */ /* ------------------------------------------------------------------------ */ /// callback for the predictor (in case of dynamic simulation) - virtual void predictor() { } + virtual void predictor() {} /// callback for the corrector (in case of dynamic simulation) - virtual void corrector() { } + virtual void corrector() {} + + /// tells if the residual can be computed in separated parts + virtual bool canSplitResidual() { return false; } /* ------------------------------------------------------------------------ */ /* management callbacks */ /* ------------------------------------------------------------------------ */ - virtual void beforeSolveStep() { }; - virtual void afterSolveStep() { }; + virtual void beforeSolveStep(){}; + virtual void afterSolveStep(){}; + protected: /// DOFManager prefixed to avoid collision in multiple inheritance cases DOFManager * sc_dof_manager{nullptr}; }; +namespace debug { + class SolverCallbackResidualPartUnknown : public Exception { + public: + SolverCallbackResidualPartUnknown(const ID & residual_part) + : Exception(residual_part + " is not known here.") {} + }; +} + } // namespace akantu #endif /* __AKANTU_SOLVER_CALLBACK_HH__ */ diff --git a/src/model/structural_mechanics/structural_elements/structural_element_kirchhoff_shell.hh b/src/model/structural_mechanics/structural_elements/structural_element_kirchhoff_shell.hh index 6e9806e72..c916d77bb 100644 --- a/src/model/structural_mechanics/structural_elements/structural_element_kirchhoff_shell.hh +++ b/src/model/structural_mechanics/structural_elements/structural_element_kirchhoff_shell.hh @@ -1,75 +1,76 @@ /** * @file structural_element_bernoulli_kirchhoff_shell.hh * * @author Fabian Barras * @author Sébastien Hartmann * @author Nicolas Richart * @author Damien Spielmann * * @date creation Tue Sep 19 2017 * * @brief Specific functions for bernoulli kirchhoff shell * * @section LICENSE * * Copyright (©) 2010-2011 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_STRUCTURAL_ELEMENT_BERNOULLI_KIRCHHOFF_SHELL_HH__ #define __AKANTU_STRUCTURAL_ELEMENT_BERNOULLI_KIRCHHOFF_SHELL_HH__ #include "structural_mechanics_model.hh" namespace akantu { /* -------------------------------------------------------------------------- */ template <> inline void StructuralMechanicsModel::assembleMass<_discrete_kirchhoff_triangle_18>() { AKANTU_DEBUG_TO_IMPLEMENT(); } /* -------------------------------------------------------------------------- */ template <> void StructuralMechanicsModel::computeTangentModuli< _discrete_kirchhoff_triangle_18>(Array & tangent_moduli) { auto tangent_size = ElementClass<_discrete_kirchhoff_triangle_18>::getNbStressComponents(); auto nb_quad = getFEEngine().getNbIntegrationPoints(_discrete_kirchhoff_triangle_18); auto H_it = tangent_moduli.begin(tangent_size, tangent_size); for (UInt mat : element_material(_discrete_kirchhoff_triangle_18, _not_ghost)) { auto & m = materials[mat]; for (UInt q = 0; q < nb_quad; ++q, ++H_it) { auto & H = *H_it; + H.clear(); Matrix D = {{1, m.nu, 0}, {m.nu, 1, 0}, {0, 0, (1 - m.nu) / 2}}; - D *= m.E / (1 - m.nu * m.nu); - H.block(0, 0, 3, 3) = D; // in plane membrane behavior - H.block(3, 3, 3, 3) = D * Math::pow<3>(m.t) / 12.; // bending behavior + D *= m.E * m.t / (1 - m.nu * m.nu); + H.block(D, 0, 0); // in plane membrane behavior + H.block(D * Math::pow<3>(m.t) / 12., 3, 3); // bending behavior } } } } // akantu #endif /* __AKANTU_STRUCTURAL_ELEMENT_BERNOULLI_DISCRETE_KIRCHHOFF_TRIANGLE_18_HH__ */ diff --git a/src/model/structural_mechanics/structural_mechanics_model.cc b/src/model/structural_mechanics/structural_mechanics_model.cc index dc3e5e8d2..4acf94ed0 100644 --- a/src/model/structural_mechanics/structural_mechanics_model.cc +++ b/src/model/structural_mechanics/structural_mechanics_model.cc @@ -1,442 +1,489 @@ /** * @file structural_mechanics_model.cc * * @author Fabian Barras * @author Sébastien Hartmann * @author Nicolas Richart * @author Damien Spielmann * * @date creation: Fri Jul 15 2011 * @date last modification: Thu Oct 15 2015 * * @brief Model implementation for StucturalMechanics elements * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "structural_mechanics_model.hh" #include "dof_manager.hh" #include "integrator_gauss.hh" #include "mesh.hh" #include "shape_structural.hh" #include "sparse_matrix.hh" +#include "time_step_solver.hh" /* -------------------------------------------------------------------------- */ #ifdef AKANTU_USE_IOHELPER #include "dumpable_inline_impl.hh" #include "dumper_elemental_field.hh" #include "dumper_iohelper_paraview.hh" #include "group_manager_inline_impl.cc" #endif /* -------------------------------------------------------------------------- */ #include "structural_mechanics_model_inline_impl.cc" /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ StructuralMechanicsModel::StructuralMechanicsModel(Mesh & mesh, UInt dim, const ID & id, const MemoryID & memory_id) : Model(mesh, ModelType::_structural_mechanics_model, dim, id, memory_id), time_step(NAN), f_m2a(1.0), stress("stress", id, memory_id), element_material("element_material", id, memory_id), set_ID("beam sets", id, memory_id), rotation_matrix("rotation_matices", id, memory_id) { AKANTU_DEBUG_IN(); registerFEEngineObject("StructuralMechanicsFEEngine", mesh, spatial_dimension); if (spatial_dimension == 2) nb_degree_of_freedom = 3; else if (spatial_dimension == 3) nb_degree_of_freedom = 6; else { AKANTU_DEBUG_TO_IMPLEMENT(); } #ifdef AKANTU_USE_IOHELPER this->mesh.registerDumper("paraview_all", id, true); #endif this->mesh.addDumpMesh(mesh, spatial_dimension, _not_ghost, _ek_structural); this->initDOFManager(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ StructuralMechanicsModel::~StructuralMechanicsModel() = default; /* -------------------------------------------------------------------------- */ void StructuralMechanicsModel::initFullImpl(const ModelOptions & options) { // <<<< This is the SolidMechanicsModel implementation for future ref >>>> // material_index.initialize(mesh, _element_kind = _ek_not_defined, // _default_value = UInt(-1), _with_nb_element = // true); // material_local_numbering.initialize(mesh, _element_kind = _ek_not_defined, // _with_nb_element = true); // Model::initFullImpl(options); // // initialize pbc // if (this->pbc_pair.size() != 0) // this->initPBC(); // // initialize the materials // if (this->parser.getLastParsedFile() != "") { // this->instantiateMaterials(); // } // this->initMaterials(); // this->initBC(*this, *displacement, *displacement_increment, // *external_force); // <<<< END >>>> Model::initFullImpl(options); + + // Initializing stresses + ElementTypeMap stress_components; + + /// TODO this is ugly af, maybe add a function to FEEngine + for (auto & type : mesh.elementTypes(_spatial_dimension = _all_dimensions, + _element_kind = _ek_structural)) { + UInt nb_components = 0; + + // Getting number of components for each element type +#define GET_(type) nb_components = ElementClass::getNbStressComponents() + AKANTU_BOOST_STRUCTURAL_ELEMENT_SWITCH(GET_); +#undef GET_ + + stress_components(nb_components, type); + } + + stress.initialize(getFEEngine(), _spatial_dimension = _all_dimensions, + _element_kind = _ek_structural, + _all_ghost_types = true, + _nb_component = [&stress_components]( + const ElementType & type, const GhostType &) -> UInt { + return stress_components(type); + }); } /* -------------------------------------------------------------------------- */ void StructuralMechanicsModel::initFEEngineBoundary() { /// TODO: this function should not be reimplemented /// we're just avoiding a call to Model::initFEEngineBoundary() } /* -------------------------------------------------------------------------- */ // void StructuralMechanicsModel::setTimeStep(Real time_step) { // this->time_step = time_step; // #if defined(AKANTU_USE_IOHELPER) // this->mesh.getDumper().setTimeStep(time_step); // #endif // } /* -------------------------------------------------------------------------- */ /* Initialisation */ /* -------------------------------------------------------------------------- */ void StructuralMechanicsModel::initSolver( TimeStepSolverType time_step_solver_type, NonLinearSolverType) { AKANTU_DEBUG_IN(); this->allocNodalField(displacement_rotation, nb_degree_of_freedom, "displacement"); - this->allocNodalField(external_force_momentum, nb_degree_of_freedom, + this->allocNodalField(external_force, nb_degree_of_freedom, "external_force"); - this->allocNodalField(internal_force_momentum, nb_degree_of_freedom, + this->allocNodalField(internal_force, nb_degree_of_freedom, "internal_force"); this->allocNodalField(blocked_dofs, nb_degree_of_freedom, "blocked_dofs"); auto & dof_manager = this->getDOFManager(); if (!dof_manager.hasDOFs("displacement")) { dof_manager.registerDOFs("displacement", *displacement_rotation, _dst_nodal); dof_manager.registerBlockedDOFs("displacement", *this->blocked_dofs); } if (time_step_solver_type == _tsst_dynamic || time_step_solver_type == _tsst_dynamic_lumped) { this->allocNodalField(velocity, spatial_dimension, "velocity"); this->allocNodalField(acceleration, spatial_dimension, "acceleration"); if (!dof_manager.hasDOFsDerivatives("displacement", 1)) { dof_manager.registerDOFsDerivative("displacement", 1, *this->velocity); dof_manager.registerDOFsDerivative("displacement", 2, *this->acceleration); } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void StructuralMechanicsModel::initModel() { for (auto && type : mesh.elementTypes(_element_kind = _ek_structural)) { // computeRotationMatrix(type); element_material.alloc(mesh.getNbElement(type), 1, type); } getFEEngine().initShapeFunctions(_not_ghost); getFEEngine().initShapeFunctions(_ghost); } /* -------------------------------------------------------------------------- */ void StructuralMechanicsModel::assembleStiffnessMatrix() { AKANTU_DEBUG_IN(); getDOFManager().getMatrix("K").clear(); for (auto & type : mesh.elementTypes(spatial_dimension, _not_ghost, _ek_structural)) { #define ASSEMBLE_STIFFNESS_MATRIX(type) assembleStiffnessMatrix(); AKANTU_BOOST_STRUCTURAL_ELEMENT_SWITCH(ASSEMBLE_STIFFNESS_MATRIX); #undef ASSEMBLE_STIFFNESS_MATRIX } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void StructuralMechanicsModel::computeStresses() { AKANTU_DEBUG_IN(); for (auto & type : mesh.elementTypes(spatial_dimension, _not_ghost, _ek_structural)) { #define COMPUTE_STRESS_ON_QUAD(type) computeStressOnQuad(); AKANTU_BOOST_STRUCTURAL_ELEMENT_SWITCH(COMPUTE_STRESS_ON_QUAD); #undef COMPUTE_STRESS_ON_QUAD } AKANTU_DEBUG_OUT(); } -/* -------------------------------------------------------------------------- */ -void StructuralMechanicsModel::updateResidual() { - AKANTU_DEBUG_IN(); - - auto & K = getDOFManager().getMatrix("K"); - - internal_force_momentum->clear(); - K.matVecMul(*displacement_rotation, *internal_force_momentum); - *internal_force_momentum *= -1.; - - getDOFManager().assembleToResidual("displacement", *external_force_momentum, - 1.); - - getDOFManager().assembleToResidual("displacement", *internal_force_momentum, - 1.); - - AKANTU_DEBUG_OUT(); -} - /* -------------------------------------------------------------------------- */ void StructuralMechanicsModel::computeRotationMatrix(const ElementType & type) { Mesh & mesh = getFEEngine().getMesh(); UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type); UInt nb_element = mesh.getNbElement(type); if (!rotation_matrix.exists(type)) { rotation_matrix.alloc(nb_element, nb_degree_of_freedom * nb_nodes_per_element * nb_degree_of_freedom * nb_nodes_per_element, type); } else { rotation_matrix(type).resize(nb_element); } rotation_matrix(type).clear(); Array rotations(nb_element, nb_degree_of_freedom * nb_degree_of_freedom); rotations.clear(); #define COMPUTE_ROTATION_MATRIX(type) computeRotationMatrix(rotations); AKANTU_BOOST_STRUCTURAL_ELEMENT_SWITCH(COMPUTE_ROTATION_MATRIX); #undef COMPUTE_ROTATION_MATRIX auto R_it = rotations.begin(nb_degree_of_freedom, nb_degree_of_freedom); auto T_it = rotation_matrix(type).begin(nb_degree_of_freedom * nb_nodes_per_element, nb_degree_of_freedom * nb_nodes_per_element); for (UInt el = 0; el < nb_element; ++el, ++R_it, ++T_it) { auto & T = *T_it; auto & R = *R_it; for (UInt k = 0; k < nb_nodes_per_element; ++k) { for (UInt i = 0; i < nb_degree_of_freedom; ++i) for (UInt j = 0; j < nb_degree_of_freedom; ++j) T(k * nb_degree_of_freedom + i, k * nb_degree_of_freedom + j) = R(i, j); } } } /* -------------------------------------------------------------------------- */ dumper::Field * StructuralMechanicsModel::createNodalFieldBool( const std::string & field_name, const std::string & group_name, __attribute__((unused)) bool padding_flag) { std::map *> uint_nodal_fields; uint_nodal_fields["blocked_dofs"] = blocked_dofs; dumper::Field * field = NULL; field = mesh.createNodalField(uint_nodal_fields[field_name], group_name); return field; } /* -------------------------------------------------------------------------- */ dumper::Field * StructuralMechanicsModel::createNodalFieldReal(const std::string & field_name, const std::string & group_name, bool padding_flag) { UInt n; if (spatial_dimension == 2) { n = 2; } else { n = 3; } if (field_name == "displacement") { return mesh.createStridedNodalField(displacement_rotation, group_name, n, 0, padding_flag); } if (field_name == "rotation") { return mesh.createStridedNodalField(displacement_rotation, group_name, nb_degree_of_freedom - n, n, padding_flag); } if (field_name == "force") { - return mesh.createStridedNodalField(external_force_momentum, group_name, n, + return mesh.createStridedNodalField(external_force, group_name, n, 0, padding_flag); } if (field_name == "momentum") { - return mesh.createStridedNodalField(external_force_momentum, group_name, + return mesh.createStridedNodalField(external_force, group_name, nb_degree_of_freedom - n, n, padding_flag); } if (field_name == "internal_force") { - return mesh.createStridedNodalField(internal_force_momentum, group_name, n, + return mesh.createStridedNodalField(internal_force, group_name, n, 0, padding_flag); } if (field_name == "internal_momentum") { - return mesh.createStridedNodalField(internal_force_momentum, group_name, + return mesh.createStridedNodalField(internal_force, group_name, nb_degree_of_freedom - n, n, padding_flag); } return nullptr; } /* -------------------------------------------------------------------------- */ dumper::Field * StructuralMechanicsModel::createElementalField( const std::string & field_name, const std::string & group_name, bool, const ElementKind & kind, const std::string &) { dumper::Field * field = NULL; if (field_name == "element_index_by_material") field = mesh.createElementalField( field_name, group_name, this->spatial_dimension, kind); return field; } /* -------------------------------------------------------------------------- */ /* Virtual methods from SolverCallback */ /* -------------------------------------------------------------------------- */ /// get the type of matrix needed MatrixType StructuralMechanicsModel::getMatrixType(const ID & /*id*/) { return _symmetric; } /// callback to assemble a Matrix void StructuralMechanicsModel::assembleMatrix(const ID & id) { if (id == "K") assembleStiffnessMatrix(); } /// callback to assemble a lumped Matrix void StructuralMechanicsModel::assembleLumpedMatrix(const ID & /*id*/) {} /// callback to assemble the residual StructuralMechanicsModel::(rhs) void StructuralMechanicsModel::assembleResidual() { - this->getDOFManager().assembleToResidual("displacement", - *this->external_force_momentum, 1); + AKANTU_DEBUG_IN(); + + auto & dof_manager = getDOFManager(); + + internal_force->clear(); + computeStresses(); + assembleInternalForce(); + dof_manager.assembleToResidual("displacement", *internal_force, -1); + dof_manager.assembleToResidual("displacement", *external_force, 1); + + AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ /* Virtual methods from MeshEventHandler */ /* -------------------------------------------------------------------------- */ /// function to implement to react on akantu::NewNodesEvent void StructuralMechanicsModel::onNodesAdded(const Array & /*nodes_list*/, const NewNodesEvent & /*event*/) {} /// function to implement to react on akantu::RemovedNodesEvent void StructuralMechanicsModel::onNodesRemoved( const Array & /*nodes_list*/, const Array & /*new_numbering*/, const RemovedNodesEvent & /*event*/) {} /// function to implement to react on akantu::NewElementsEvent void StructuralMechanicsModel::onElementsAdded( const Array & /*elements_list*/, const NewElementsEvent & /*event*/) {} /// function to implement to react on akantu::RemovedElementsEvent void StructuralMechanicsModel::onElementsRemoved( const Array & /*elements_list*/, const ElementTypeMapArray & /*new_numbering*/, const RemovedElementsEvent & /*event*/) {} /// function to implement to react on akantu::ChangedElementsEvent void StructuralMechanicsModel::onElementsChanged( const Array & /*old_elements_list*/, const Array & /*new_elements_list*/, const ElementTypeMapArray & /*new_numbering*/, const ChangedElementsEvent & /*event*/) {} /* -------------------------------------------------------------------------- */ /* Virtual methods from Model */ /* -------------------------------------------------------------------------- */ /// get some default values for derived classes std::tuple StructuralMechanicsModel::getDefaultSolverID(const AnalysisMethod & method) { switch (method) { case _static: { return std::make_tuple("static", _tsst_static); } case _implicit_dynamic: { return std::make_tuple("implicit", _tsst_dynamic); } default: return std::make_tuple("unknown", _tsst_not_defined); } } /* ------------------------------------------------------------------------ */ ModelSolverOptions StructuralMechanicsModel::getDefaultSolverOptions( const TimeStepSolverType & type) const { ModelSolverOptions options; switch (type) { case _tsst_static: { options.non_linear_solver_type = _nls_linear; options.integration_scheme_type["displacement"] = _ist_pseudo_time; options.solution_type["displacement"] = IntegrationScheme::_not_defined; break; } default: AKANTU_EXCEPTION(type << " is not a valid time step solver type"); } return options; } + +/* -------------------------------------------------------------------------- */ +void StructuralMechanicsModel::assembleInternalForce() { + for (auto type : mesh.elementTypes(_spatial_dimension = _all_dimensions, + _element_kind = _ek_structural)) { + assembleInternalForce(type, _not_ghost); + // assembleInternalForce(type, _ghost); + } +} + +/* -------------------------------------------------------------------------- */ +void StructuralMechanicsModel::assembleInternalForce(const ElementType & type, + GhostType gt) { + auto & fem = getFEEngine(); + auto & sigma = stress(type, gt); + auto ndof = getNbDegreeOfFreedom(type); + auto nb_nodes = mesh.getNbNodesPerElement(type); + auto ndof_per_elem = ndof * nb_nodes; + + Array BtSigma(fem.getNbIntegrationPoints(type) * + mesh.getNbElement(type), + ndof_per_elem, "BtSigma"); + fem.computeBtD(sigma, BtSigma, type, gt); + + Array intBtSigma(0, ndof_per_elem, + "intBtSigma"); + fem.integrate(BtSigma, intBtSigma, ndof_per_elem, type, gt); + BtSigma.resize(0); + + getDOFManager().assembleElementalArrayLocalArray(intBtSigma, *internal_force, + type, gt, 1); +} /* -------------------------------------------------------------------------- */ } // namespace akantu diff --git a/src/model/structural_mechanics/structural_mechanics_model.hh b/src/model/structural_mechanics/structural_mechanics_model.hh index fcb036768..b06e1ea41 100644 --- a/src/model/structural_mechanics/structural_mechanics_model.hh +++ b/src/model/structural_mechanics/structural_mechanics_model.hh @@ -1,327 +1,333 @@ /** * @file structural_mechanics_model.hh * * @author Fabian Barras * @author Sébastien Hartmann * @author Nicolas Richart * @author Damien Spielmann * * @date creation: Fri Jul 15 2011 * @date last modification: Thu Jan 21 2016 * * @brief Particular implementation of the structural elements in the * StructuralMechanicsModel * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_named_argument.hh" #include "boundary_condition.hh" #include "model.hh" /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_STRUCTURAL_MECHANICS_MODEL_HH__ #define __AKANTU_STRUCTURAL_MECHANICS_MODEL_HH__ /* -------------------------------------------------------------------------- */ namespace akantu { class Material; class MaterialSelector; class DumperIOHelper; class NonLocalManager; template class IntegratorGauss; template class ShapeStructural; } // namespace akantu namespace akantu { struct StructuralMaterial { Real E{0}; Real A{1}; Real I{0}; Real Iz{0}; Real Iy{0}; Real GJ{0}; Real rho{0}; Real t{0}; Real nu{0}; }; class StructuralMechanicsModel : public Model { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: using MyFEEngineType = FEEngineTemplate; StructuralMechanicsModel(Mesh & mesh, UInt spatial_dimension = _all_dimensions, const ID & id = "structural_mechanics_model", const MemoryID & memory_id = 0); virtual ~StructuralMechanicsModel(); /// Init full model void initFullImpl(const ModelOptions & options) override; /// Init boundary FEEngine void initFEEngineBoundary() override; /* ------------------------------------------------------------------------ */ /* Virtual methods from SolverCallback */ /* ------------------------------------------------------------------------ */ /// get the type of matrix needed MatrixType getMatrixType(const ID &) override; /// callback to assemble a Matrix void assembleMatrix(const ID &) override; /// callback to assemble a lumped Matrix void assembleLumpedMatrix(const ID &) override; /// callback to assemble the residual (rhs) void assembleResidual() override; /* ------------------------------------------------------------------------ */ /* Virtual methods from MeshEventHandler */ /* ------------------------------------------------------------------------ */ /// function to implement to react on akantu::NewNodesEvent void onNodesAdded(const Array & nodes_list, const NewNodesEvent & event) override; /// function to implement to react on akantu::RemovedNodesEvent void onNodesRemoved(const Array & nodes_list, const Array & new_numbering, const RemovedNodesEvent & event) override; /// function to implement to react on akantu::NewElementsEvent void onElementsAdded(const Array & elements_list, const NewElementsEvent & event) override; /// function to implement to react on akantu::RemovedElementsEvent void onElementsRemoved(const Array & elements_list, const ElementTypeMapArray & new_numbering, const RemovedElementsEvent & event) override; /// function to implement to react on akantu::ChangedElementsEvent void onElementsChanged(const Array & old_elements_list, const Array & new_elements_list, const ElementTypeMapArray & new_numbering, const ChangedElementsEvent & event) override; /* ------------------------------------------------------------------------ */ /* Virtual methods from Model */ /* ------------------------------------------------------------------------ */ protected: /// get some default values for derived classes std::tuple getDefaultSolverID(const AnalysisMethod & method) override; ModelSolverOptions getDefaultSolverOptions(const TimeStepSolverType & type) const override; + UInt getNbDegreeOfFreedom(const ElementType & type) const; + /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ void initSolver(TimeStepSolverType, NonLinearSolverType) override; /// initialize the model void initModel() override; /// compute the stresses per elements void computeStresses(); + /// compute the nodal forces + void assembleInternalForce(); + + /// compute the nodal forces for an element type + void assembleInternalForce(const ElementType & type, GhostType gt); + /// assemble the stiffness matrix void assembleStiffnessMatrix(); /// assemble the mass matrix for consistent mass resolutions void assembleMass(); - /// update the residual vector - void updateResidual(); - + /// TODO remove void computeRotationMatrix(const ElementType & type); protected: /// compute Rotation Matrices template void computeRotationMatrix(__attribute__((unused)) Array & rotations) {} /* ------------------------------------------------------------------------ */ /* Mass (structural_mechanics_model_mass.cc) */ /* ------------------------------------------------------------------------ */ /// assemble the mass matrix for either _ghost or _not_ghost elements void assembleMass(GhostType ghost_type); /// computes rho void computeRho(Array & rho, ElementType type, GhostType ghost_type); /// finish the computation of residual to solve in increment void updateResidualInternal(); /* ------------------------------------------------------------------------ */ private: template void assembleStiffnessMatrix(); template void assembleMass(); template void computeStressOnQuad(); template void computeTangentModuli(Array & tangent_moduli); /* ------------------------------------------------------------------------ */ /* Dumpable interface */ /* ------------------------------------------------------------------------ */ public: virtual dumper::Field * createNodalFieldReal(const std::string & field_name, const std::string & group_name, bool padding_flag); virtual dumper::Field * createNodalFieldBool(const std::string & field_name, const std::string & group_name, bool padding_flag); virtual dumper::Field * createElementalField(const std::string & field_name, const std::string & group_name, bool padding_flag, const ElementKind & kind, const std::string & fe_engine_id = ""); /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: /// set the value of the time step // void setTimeStep(Real time_step, const ID & solver_id = "") override; /// return the dimension of the system space AKANTU_GET_MACRO(SpatialDimension, spatial_dimension, UInt); /// get the StructuralMechanicsModel::displacement vector AKANTU_GET_MACRO(Displacement, *displacement_rotation, Array &); /// get the StructuralMechanicsModel::velocity vector AKANTU_GET_MACRO(Velocity, *velocity, Array &); /// get the StructuralMechanicsModel::acceleration vector, updated /// by /// StructuralMechanicsModel::updateAcceleration AKANTU_GET_MACRO(Acceleration, *acceleration, Array &); /// get the StructuralMechanicsModel::external_force vector - AKANTU_GET_MACRO(ExternalForce, *external_force_momentum, Array &); + AKANTU_GET_MACRO(ExternalForce, *external_force, Array &); /// get the StructuralMechanicsModel::internal_force vector (boundary forces) - AKANTU_GET_MACRO(InternalForce, *internal_force_momentum, Array &); + AKANTU_GET_MACRO(InternalForce, *internal_force, Array &); /// get the StructuralMechanicsModel::boundary vector AKANTU_GET_MACRO(BlockedDOFs, *blocked_dofs, Array &); AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(RotationMatrix, rotation_matrix, Real); AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(Stress, stress, Real); AKANTU_GET_MACRO_BY_ELEMENT_TYPE(ElementMaterial, element_material, UInt); AKANTU_GET_MACRO_BY_ELEMENT_TYPE(Set_ID, set_ID, UInt); void addMaterial(StructuralMaterial & material) { materials.push_back(material); } const StructuralMaterial & getMaterial(const Element & element) const { return materials[element_material(element)]; } /* ------------------------------------------------------------------------ */ /* Boundaries (structural_mechanics_model_boundary.cc) */ /* ------------------------------------------------------------------------ */ public: /// Compute Linear load function set in global axis template void computeForcesByGlobalTractionArray(const Array & tractions); /// Compute Linear load function set in local axis template void computeForcesByLocalTractionArray(const Array & tractions); /// compute force vector from a function(x,y,momentum) that describe stresses // template // void computeForcesFromFunction(BoundaryFunction in_function, // BoundaryFunctionType function_type); /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ private: /// time step Real time_step; /// conversion coefficient form force/mass to acceleration Real f_m2a; /// displacements array Array * displacement_rotation{nullptr}; /// velocities array Array * velocity{nullptr}; /// accelerations array Array * acceleration{nullptr}; /// forces array - Array * internal_force_momentum{nullptr}; + Array * internal_force{nullptr}; /// forces array - Array * external_force_momentum{nullptr}; + Array * external_force{nullptr}; /// lumped mass array Array * mass{nullptr}; /// boundaries array Array * blocked_dofs{nullptr}; /// stress array ElementTypeMapArray stress; ElementTypeMapArray element_material; // Define sets of beams ElementTypeMapArray set_ID; /// number of degre of freedom UInt nb_degree_of_freedom; // Rotation matrix ElementTypeMapArray rotation_matrix; // /// analysis method check the list in akantu::AnalysisMethod // AnalysisMethod method; /// flag defining if the increment must be computed or not bool increment_flag; /* ------------------------------------------------------------------------ */ std::vector materials; }; } // namespace akantu #endif /* __AKANTU_STRUCTURAL_MECHANICS_MODEL_HH__ */ diff --git a/src/model/structural_mechanics/structural_mechanics_model_inline_impl.cc b/src/model/structural_mechanics/structural_mechanics_model_inline_impl.cc index ec27c7ede..7ef7ed0cc 100644 --- a/src/model/structural_mechanics/structural_mechanics_model_inline_impl.cc +++ b/src/model/structural_mechanics/structural_mechanics_model_inline_impl.cc @@ -1,361 +1,370 @@ /** * @file structural_mechanics_model_inline_impl.cc * * @author Fabian Barras * @author Sébastien Hartmann * @author Nicolas Richart * @author Damien Spielmann * * @date creation: Fri Jul 15 2011 * @date last modification: Thu Oct 15 2015 * * @brief Implementation of inline functions of StructuralMechanicsModel * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "structural_mechanics_model.hh" /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_STRUCTURAL_MECHANICS_MODEL_INLINE_IMPL_CC__ #define __AKANTU_STRUCTURAL_MECHANICS_MODEL_INLINE_IMPL_CC__ namespace akantu { +/* -------------------------------------------------------------------------- */ +inline UInt +StructuralMechanicsModel::getNbDegreeOfFreedom(const ElementType & type) const { + UInt ndof = 0; +#define GET_(type) ndof = ElementClass::getNbDegreeOfFreedom() + AKANTU_BOOST_KIND_ELEMENT_SWITCH(GET_, _ek_structural); +#undef GET_ + + return ndof; +} + /* -------------------------------------------------------------------------- */ template void StructuralMechanicsModel::computeTangentModuli( Array & /*tangent_moduli*/) { AKANTU_DEBUG_TO_IMPLEMENT(); } } // namespace akantu #include "structural_element_bernoulli_beam_2.hh" #include "structural_element_bernoulli_beam_3.hh" #include "structural_element_kirchhoff_shell.hh" namespace akantu { /* -------------------------------------------------------------------------- */ template void StructuralMechanicsModel::assembleStiffnessMatrix() { AKANTU_DEBUG_IN(); auto nb_element = getFEEngine().getMesh().getNbElement(type); auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type); auto nb_quadrature_points = getFEEngine().getNbIntegrationPoints(type); auto tangent_size = ElementClass::getNbStressComponents(); auto tangent_moduli = std::make_unique>( nb_element * nb_quadrature_points, tangent_size * tangent_size, "tangent_stiffness_matrix"); computeTangentModuli(*tangent_moduli); /// compute @f$\mathbf{B}^t * \mathbf{D} * \mathbf{B}@f$ UInt bt_d_b_size = nb_degree_of_freedom * nb_nodes_per_element; auto bt_d_b = std::make_unique>( nb_element * nb_quadrature_points, bt_d_b_size * bt_d_b_size, "B^t*D*B"); const auto & b = getFEEngine().getShapesDerivatives(type); Matrix BtD(bt_d_b_size, tangent_size); for (auto && tuple : zip(make_view(b, tangent_size, bt_d_b_size), make_view(*tangent_moduli, tangent_size, tangent_size), make_view(*bt_d_b, bt_d_b_size, bt_d_b_size))) { auto & B = std::get<0>(tuple); auto & D = std::get<1>(tuple); auto & BtDB = std::get<2>(tuple); BtD.mul(B, D); BtDB.template mul(BtD, B); } /// compute @f$ k_e = \int_e \mathbf{B}^t * \mathbf{D} * \mathbf{B}@f$ auto int_bt_d_b = std::make_unique>( nb_element, bt_d_b_size * bt_d_b_size, "int_B^t*D*B"); getFEEngine().integrate(*bt_d_b, *int_bt_d_b, bt_d_b_size * bt_d_b_size, type); getDOFManager().assembleElementalMatricesToMatrix( "K", "displacement", *int_bt_d_b, type, _not_ghost, _symmetric); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void StructuralMechanicsModel::computeStressOnQuad() { AKANTU_DEBUG_IN(); Array & sigma = stress(type, _not_ghost); auto nb_element = mesh.getNbElement(type); auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type); auto nb_quadrature_points = getFEEngine().getNbIntegrationPoints(type); auto tangent_size = ElementClass::getNbStressComponents(); auto tangent_moduli = std::make_unique>( nb_element * nb_quadrature_points, tangent_size * tangent_size, "tangent_stiffness_matrix"); computeTangentModuli(*tangent_moduli); /// compute DB auto d_b_size = nb_degree_of_freedom * nb_nodes_per_element; auto d_b = std::make_unique>(nb_element * nb_quadrature_points, d_b_size * tangent_size, "D*B"); const auto & b = getFEEngine().getShapesDerivatives(type); auto B = b.begin(tangent_size, d_b_size); auto D = tangent_moduli->begin(tangent_size, tangent_size); auto D_B = d_b->begin(tangent_size, d_b_size); for (UInt e = 0; e < nb_element; ++e) { for (UInt q = 0; q < nb_quadrature_points; ++q, ++B, ++D, ++D_B) { D_B->template mul(*D, *B); } } /// compute DBu D_B = d_b->begin(tangent_size, d_b_size); auto DBu = sigma.begin(tangent_size); - Vector ul(d_b_size); Array u_el(0, d_b_size); FEEngine::extractNodalToElementField(mesh, *displacement_rotation, u_el, type); auto ug = u_el.begin(d_b_size); - auto T = rotation_matrix(type).begin(d_b_size, d_b_size); - for (UInt e = 0; e < nb_element; ++e, ++T, ++ug) { - ul.mul(*T, *ug); + // No need to rotate because B is post-multiplied + for (UInt e = 0; e < nb_element; ++e, ++ug) { for (UInt q = 0; q < nb_quadrature_points; ++q, ++D_B, ++DBu) { - DBu->template mul(*D_B, ul); + DBu->template mul(*D_B, *ug); } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void StructuralMechanicsModel::computeForcesByLocalTractionArray( const Array & tractions) { AKANTU_DEBUG_IN(); #if 0 UInt nb_element = getFEEngine().getMesh().getNbElement(type); UInt nb_nodes_per_element = getFEEngine().getMesh().getNbNodesPerElement(type); UInt nb_quad = getFEEngine().getNbIntegrationPoints(type); // check dimension match AKANTU_DEBUG_ASSERT( Mesh::getSpatialDimension(type) == getFEEngine().getElementDimension(), "element type dimension does not match the dimension of boundaries : " << getFEEngine().getElementDimension() << " != " << Mesh::getSpatialDimension(type)); // check size of the vector AKANTU_DEBUG_ASSERT( tractions.size() == nb_quad * nb_element, "the size of the vector should be the total number of quadrature points"); // check number of components AKANTU_DEBUG_ASSERT(tractions.getNbComponent() == nb_degree_of_freedom, "the number of components should be the spatial " "dimension of the problem"); Array Nvoigt(nb_element * nb_quad, nb_degree_of_freedom * nb_degree_of_freedom * nb_nodes_per_element); transferNMatrixToSymVoigtNMatrix(Nvoigt); Array::const_matrix_iterator N_it = Nvoigt.begin( nb_degree_of_freedom, nb_degree_of_freedom * nb_nodes_per_element); Array::const_matrix_iterator T_it = rotation_matrix(type).begin(nb_degree_of_freedom * nb_nodes_per_element, nb_degree_of_freedom * nb_nodes_per_element); Array::const_vector_iterator te_it = tractions.begin(nb_degree_of_freedom); Array funct(nb_element * nb_quad, nb_degree_of_freedom * nb_nodes_per_element, 0.); Array::iterator> Fe_it = funct.begin(nb_degree_of_freedom * nb_nodes_per_element); Vector fe(nb_degree_of_freedom * nb_nodes_per_element); for (UInt e = 0; e < nb_element; ++e, ++T_it) { const Matrix & T = *T_it; for (UInt q = 0; q < nb_quad; ++q, ++N_it, ++te_it, ++Fe_it) { const Matrix & N = *N_it; const Vector & te = *te_it; Vector & Fe = *Fe_it; // compute N^t tl fe.mul(N, te); // turn N^t tl back in the global referential Fe.mul(T, fe); } } // allocate the vector that will contain the integrated values std::stringstream name; name << id << type << ":integral_boundary"; Array int_funct(nb_element, nb_degree_of_freedom * nb_nodes_per_element, name.str()); // do the integration getFEEngine().integrate(funct, int_funct, nb_degree_of_freedom * nb_nodes_per_element, type); // assemble the result into force vector getFEEngine().assembleArray(int_funct, *force_momentum, dof_synchronizer->getLocalDOFEquationNumbers(), nb_degree_of_freedom, type); #endif AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void StructuralMechanicsModel::computeForcesByGlobalTractionArray( const Array & traction_global) { AKANTU_DEBUG_IN(); #if 0 UInt nb_element = mesh.getNbElement(type); UInt nb_quad = getFEEngine().getNbIntegrationPoints(type); UInt nb_nodes_per_element = getFEEngine().getMesh().getNbNodesPerElement(type); std::stringstream name; name << id << ":structuralmechanics:imposed_linear_load"; Array traction_local(nb_element * nb_quad, nb_degree_of_freedom, name.str()); Array::const_matrix_iterator T_it = rotation_matrix(type).begin(nb_degree_of_freedom * nb_nodes_per_element, nb_degree_of_freedom * nb_nodes_per_element); Array::const_iterator> Te_it = traction_global.begin(nb_degree_of_freedom); Array::iterator> te_it = traction_local.begin(nb_degree_of_freedom); Matrix R(nb_degree_of_freedom, nb_degree_of_freedom); for (UInt e = 0; e < nb_element; ++e, ++T_it) { const Matrix & T = *T_it; for (UInt i = 0; i < nb_degree_of_freedom; ++i) for (UInt j = 0; j < nb_degree_of_freedom; ++j) R(i, j) = T(i, j); for (UInt q = 0; q < nb_quad; ++q, ++Te_it, ++te_it) { const Vector & Te = *Te_it; Vector & te = *te_it; // turn the traction in the local referential te.mul(R, Te); } } computeForcesByLocalTractionArray(traction_local); #endif AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ /** * @param myf pointer to a function that fills a vector/tensor with respect to * passed coordinates */ #if 0 template inline void StructuralMechanicsModel::computeForcesFromFunction( BoundaryFunction myf, BoundaryFunctionType function_type) { /** function type is ** _bft_forces : linear load is given ** _bft_stress : stress function is given -> Not already done for this kind *of model */ std::stringstream name; name << id << ":structuralmechanics:imposed_linear_load"; Array lin_load(0, nb_degree_of_freedom, name.str()); name.clear(); UInt offset = nb_degree_of_freedom; // prepare the loop over element types UInt nb_quad = getFEEngine().getNbIntegrationPoints(type); UInt nb_element = getFEEngine().getMesh().getNbElement(type); name.clear(); name << id << ":structuralmechanics:quad_coords"; Array quad_coords(nb_element * nb_quad, spatial_dimension, "quad_coords"); getFEEngineClass() .getShapeFunctions() .interpolateOnIntegrationPoints(getFEEngine().getMesh().getNodes(), quad_coords, spatial_dimension); getFEEngineClass() .getShapeFunctions() .interpolateOnIntegrationPoints( getFEEngine().getMesh().getNodes(), quad_coords, spatial_dimension, _not_ghost, empty_filter, true, 0, 1, 1); if (spatial_dimension == 3) getFEEngineClass() .getShapeFunctions() .interpolateOnIntegrationPoints( getFEEngine().getMesh().getNodes(), quad_coords, spatial_dimension, _not_ghost, empty_filter, true, 0, 2, 2); lin_load.resize(nb_element * nb_quad); Real * imposed_val = lin_load.storage(); /// sigma/load on each quadrature points Real * qcoord = quad_coords.storage(); for (UInt el = 0; el < nb_element; ++el) { for (UInt q = 0; q < nb_quad; ++q) { myf(qcoord, imposed_val, NULL, 0); imposed_val += offset; qcoord += spatial_dimension; } } switch (function_type) { case _bft_traction_local: computeForcesByLocalTractionArray(lin_load); break; case _bft_traction: computeForcesByGlobalTractionArray(lin_load); break; default: break; } } #endif } // namespace akantu #endif /* __AKANTU_STRUCTURAL_MECHANICS_MODEL_INLINE_IMPL_CC__ */ diff --git a/src/model/time_step_solver.hh b/src/model/time_step_solver.hh index fab8342e3..07fab9115 100644 --- a/src/model/time_step_solver.hh +++ b/src/model/time_step_solver.hh @@ -1,132 +1,136 @@ /** * @file time_step_solver.hh * * @author Nicolas Richart * * @date Mon Aug 24 12:42:04 2015 * * @brief This corresponding to the time step evolution solver * * @section LICENSE * * Copyright (©) 2010-2011 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_array.hh" #include "aka_memory.hh" #include "integration_scheme.hh" #include "parameter_registry.hh" #include "solver_callback.hh" /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_TIME_STEP_SOLVER_HH__ #define __AKANTU_TIME_STEP_SOLVER_HH__ namespace akantu { class DOFManager; class NonLinearSolver; } namespace akantu { class TimeStepSolver : public Memory, public ParameterRegistry, public SolverCallback { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: TimeStepSolver(DOFManager & dof_manager, const TimeStepSolverType & type, NonLinearSolver & non_linear_solver, const ID & id, UInt memory_id); ~TimeStepSolver() override; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ public: /// solves on step virtual void solveStep(SolverCallback & solver_callback) = 0; /// register an integration scheme for a given dof virtual void setIntegrationScheme(const ID & dof_id, const IntegrationSchemeType & type, IntegrationScheme::SolutionType solution_type = IntegrationScheme::_not_defined) = 0; /// replies if a integration scheme has been set virtual bool hasIntegrationScheme(const ID & dof_id) const = 0; /* ------------------------------------------------------------------------ */ /* Solver Callback interface */ /* ------------------------------------------------------------------------ */ public: /// implementation of the SolverCallback::getMatrixType() MatrixType getMatrixType(const ID &) final { return _mt_not_defined; } /// implementation of the SolverCallback::predictor() void predictor() override; /// implementation of the SolverCallback::corrector() void corrector() override; /// implementation of the SolverCallback::assembleJacobian() void assembleMatrix(const ID & matrix_id) override; /// implementation of the SolverCallback::assembleJacobian() void assembleLumpedMatrix(const ID & matrix_id) final; /// implementation of the SolverCallback::assembleResidual() void assembleResidual() override; + /// implementation of the SolverCallback::assembleResidual() + void assembleResidual(const ID & residual_part) override; void beforeSolveStep() override; void afterSolveStep() override; + + bool canSplitResidual() { return solver_callback->canSplitResidual(); } /* ------------------------------------------------------------------------ */ /* Accessor */ /* ------------------------------------------------------------------------ */ public: AKANTU_GET_MACRO(TimeStep, time_step, Real); AKANTU_SET_MACRO(TimeStep, time_step, Real); AKANTU_GET_MACRO(NonLinearSolver, non_linear_solver, const NonLinearSolver &); AKANTU_GET_MACRO_NOT_CONST(NonLinearSolver, non_linear_solver, NonLinearSolver &); protected: MatrixType getCommonMatrixType(); /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ protected: /// Underlying dof manager containing the dof to treat DOFManager & _dof_manager; /// Type of solver TimeStepSolverType type; /// The time step for this solver Real time_step; /// Temporary storage for solver callback SolverCallback * solver_callback; /// NonLinearSolver used by this tome step solver NonLinearSolver & non_linear_solver; /// List of required matrices std::map needed_matrices; }; } // akantu #endif /* __AKANTU_TIME_STEP_SOLVER_HH__ */ diff --git a/src/model/time_step_solvers/time_step_solver.cc b/src/model/time_step_solvers/time_step_solver.cc index 43f429c2a..1cb645ef3 100644 --- a/src/model/time_step_solvers/time_step_solver.cc +++ b/src/model/time_step_solvers/time_step_solver.cc @@ -1,167 +1,176 @@ /** * @file time_step_solver.cc * * @author Nicolas Richart * * @date Mon Oct 12 16:56:43 2015 * * @brief Implementation of common part of TimeStepSolvers * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "time_step_solver.hh" #include "dof_manager.hh" #include "non_linear_solver.hh" /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ TimeStepSolver::TimeStepSolver(DOFManager & dof_manager, const TimeStepSolverType & type, NonLinearSolver & non_linear_solver, const ID & id, UInt memory_id) : Memory(id, memory_id), SolverCallback(dof_manager), _dof_manager(dof_manager), type(type), time_step(0.), solver_callback(nullptr), non_linear_solver(non_linear_solver) { this->registerSubRegistry("non_linear_solver", non_linear_solver); } /* -------------------------------------------------------------------------- */ TimeStepSolver::~TimeStepSolver() = default; /* -------------------------------------------------------------------------- */ MatrixType TimeStepSolver::getCommonMatrixType() { MatrixType common_type = _mt_not_defined; for (auto & pair : needed_matrices) { auto & type = pair.second; if (type == _mt_not_defined) { type = this->solver_callback->getMatrixType(pair.first); } common_type = std::min(common_type, type); } AKANTU_DEBUG_ASSERT(common_type != _mt_not_defined, "No type defined for the matrices"); return common_type; } /* -------------------------------------------------------------------------- */ void TimeStepSolver::predictor() { AKANTU_DEBUG_ASSERT( this->solver_callback != nullptr, "This function cannot be called if the solver_callback is not set"); this->solver_callback->predictor(); } /* -------------------------------------------------------------------------- */ void TimeStepSolver::corrector() { AKANTU_DEBUG_ASSERT( this->solver_callback != nullptr, "This function cannot be called if the solver_callback is not set"); this->solver_callback->corrector(); } /* -------------------------------------------------------------------------- */ void TimeStepSolver::beforeSolveStep() { AKANTU_DEBUG_ASSERT( this->solver_callback != nullptr, "This function cannot be called if the solver_callback is not set"); this->solver_callback->beforeSolveStep(); } /* -------------------------------------------------------------------------- */ void TimeStepSolver::afterSolveStep() { AKANTU_DEBUG_ASSERT( this->solver_callback != nullptr, "This function cannot be called if the solver_callback is not set"); this->solver_callback->afterSolveStep(); } /* -------------------------------------------------------------------------- */ void TimeStepSolver::assembleLumpedMatrix(const ID & matrix_id) { AKANTU_DEBUG_ASSERT( this->solver_callback != nullptr, "This function cannot be called if the solver_callback is not set"); if (not _dof_manager.hasLumpedMatrix(matrix_id)) _dof_manager.getNewLumpedMatrix(matrix_id); this->solver_callback->assembleLumpedMatrix(matrix_id); } /* -------------------------------------------------------------------------- */ void TimeStepSolver::assembleMatrix(const ID & matrix_id) { AKANTU_DEBUG_ASSERT( this->solver_callback != nullptr, "This function cannot be called if the solver_callback is not set"); auto common_type = this->getCommonMatrixType(); if (matrix_id != "J") { auto type = needed_matrices[matrix_id]; if (type == _mt_not_defined) return; if (not _dof_manager.hasMatrix(matrix_id)) { _dof_manager.getNewMatrix(matrix_id, type); } this->solver_callback->assembleMatrix(matrix_id); return; } if (not _dof_manager.hasMatrix("J")) _dof_manager.getNewMatrix("J", common_type); for (auto & pair : needed_matrices) { auto type = pair.second; if (type == _mt_not_defined) continue; auto name = pair.first; if (not _dof_manager.hasMatrix(name)) _dof_manager.getNewMatrix(name, type); this->solver_callback->assembleMatrix(name); } } /* -------------------------------------------------------------------------- */ void TimeStepSolver::assembleResidual() { AKANTU_DEBUG_ASSERT( this->solver_callback != nullptr, "This function cannot be called if the solver_callback is not set"); this->_dof_manager.clearResidual(); this->solver_callback->assembleResidual(); } +/* -------------------------------------------------------------------------- */ +void TimeStepSolver::assembleResidual(const ID & residual_part) { + AKANTU_DEBUG_ASSERT( + this->solver_callback != nullptr, + "This function cannot be called if the solver_callback is not set"); + + this->solver_callback->assembleResidual(residual_part); +} + /* -------------------------------------------------------------------------- */ } // namespace akantu diff --git a/src/model/time_step_solvers/time_step_solver_default.cc b/src/model/time_step_solvers/time_step_solver_default.cc index 747378f1d..b97b04eff 100644 --- a/src/model/time_step_solvers/time_step_solver_default.cc +++ b/src/model/time_step_solvers/time_step_solver_default.cc @@ -1,282 +1,309 @@ /** * @file time_step_solver_default.cc * * @author Nicolas Richart * * @date Wed Sep 16 10:20:55 2015 * * @brief Default implementation of the time step solver * * @section LICENSE * * Copyright (©) 2010-2011 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "time_step_solver_default.hh" #include "dof_manager_default.hh" #include "integration_scheme_1st_order.hh" #include "integration_scheme_2nd_order.hh" #include "mesh.hh" #include "non_linear_solver.hh" #include "pseudo_time.hh" #include "sparse_matrix_aij.hh" /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ TimeStepSolverDefault::TimeStepSolverDefault( DOFManagerDefault & dof_manager, const TimeStepSolverType & type, NonLinearSolver & non_linear_solver, const ID & id, UInt memory_id) : TimeStepSolver(dof_manager, type, non_linear_solver, id, memory_id), dof_manager(dof_manager), is_mass_lumped(false) { switch (type) { case _tsst_dynamic: break; case _tsst_dynamic_lumped: this->is_mass_lumped = true; break; case _tsst_static: /// initialize a static time solver for callback dofs break; default: AKANTU_DEBUG_TO_IMPLEMENT(); } } /* -------------------------------------------------------------------------- */ void TimeStepSolverDefault::setIntegrationScheme( const ID & dof_id, const IntegrationSchemeType & type, IntegrationScheme::SolutionType solution_type) { if (this->integration_schemes.find(dof_id) != this->integration_schemes.end()) { AKANTU_EXCEPTION("Their DOFs " << dof_id << " have already an integration scheme associated"); } std::unique_ptr integration_scheme; if (this->is_mass_lumped) { switch (type) { case _ist_forward_euler: { integration_scheme = std::make_unique(dof_manager, dof_id); break; } case _ist_central_difference: { integration_scheme = std::make_unique(dof_manager, dof_id); break; } default: AKANTU_EXCEPTION( "This integration scheme cannot be used in lumped dynamic"); } } else { switch (type) { case _ist_pseudo_time: { integration_scheme = std::make_unique(dof_manager, dof_id); break; } case _ist_forward_euler: { integration_scheme = std::make_unique(dof_manager, dof_id); break; } case _ist_trapezoidal_rule_1: { integration_scheme = std::make_unique(dof_manager, dof_id); break; } case _ist_backward_euler: { integration_scheme = std::make_unique(dof_manager, dof_id); break; } case _ist_central_difference: { integration_scheme = std::make_unique(dof_manager, dof_id); break; } case _ist_fox_goodwin: { integration_scheme = std::make_unique(dof_manager, dof_id); break; } case _ist_trapezoidal_rule_2: { integration_scheme = std::make_unique(dof_manager, dof_id); break; } case _ist_linear_acceleration: { integration_scheme = std::make_unique(dof_manager, dof_id); break; } case _ist_generalized_trapezoidal: { integration_scheme = std::make_unique(dof_manager, dof_id); break; } case _ist_newmark_beta: integration_scheme = std::make_unique(dof_manager, dof_id); break; } } AKANTU_DEBUG_ASSERT(integration_scheme, "No integration scheme was found for the provided types"); auto && matrices_names = integration_scheme->getNeededMatrixList(); for (auto && name : matrices_names) { needed_matrices.insert({name, _mt_not_defined}); } this->integration_schemes[dof_id] = std::move(integration_scheme); this->solution_types[dof_id] = solution_type; this->integration_schemes_owner.insert(dof_id); } /* -------------------------------------------------------------------------- */ bool TimeStepSolverDefault::hasIntegrationScheme(const ID & dof_id) const { return this->integration_schemes.find(dof_id) != this->integration_schemes.end(); } /* -------------------------------------------------------------------------- */ TimeStepSolverDefault::~TimeStepSolverDefault() = default; /* -------------------------------------------------------------------------- */ void TimeStepSolverDefault::solveStep(SolverCallback & solver_callback) { this->solver_callback = &solver_callback; this->solver_callback->beforeSolveStep(); this->non_linear_solver.solve(*this); this->solver_callback->afterSolveStep(); this->solver_callback = nullptr; } /* -------------------------------------------------------------------------- */ void TimeStepSolverDefault::predictor() { AKANTU_DEBUG_IN(); TimeStepSolver::predictor(); for (auto & pair : this->integration_schemes) { auto & dof_id = pair.first; auto & integration_scheme = pair.second; if (this->dof_manager.hasPreviousDOFs(dof_id)) { this->dof_manager.savePreviousDOFs(dof_id); } /// integrator predictor integration_scheme->predictor(this->time_step); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void TimeStepSolverDefault::corrector() { AKANTU_DEBUG_IN(); TimeStepSolver::corrector(); for (auto & pair : this->integration_schemes) { auto & dof_id = pair.first; auto & integration_scheme = pair.second; const auto & solution_type = this->solution_types[dof_id]; integration_scheme->corrector(solution_type, this->time_step); /// computing the increment of dof if needed if (this->dof_manager.hasDOFsIncrement(dof_id)) { if (!this->dof_manager.hasPreviousDOFs(dof_id)) { AKANTU_DEBUG_WARNING("In order to compute the increment of " << dof_id << " a 'previous' has to be registered"); continue; } Array & increment = this->dof_manager.getDOFsIncrement(dof_id); Array & previous = this->dof_manager.getPreviousDOFs(dof_id); UInt dof_array_comp = this->dof_manager.getDOFs(dof_id).getNbComponent(); auto prev_dof_it = previous.begin(dof_array_comp); auto incr_it = increment.begin(dof_array_comp); auto incr_end = increment.end(dof_array_comp); increment.copy(this->dof_manager.getDOFs(dof_id)); for (; incr_it != incr_end; ++incr_it, ++prev_dof_it) { *incr_it -= *prev_dof_it; } } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void TimeStepSolverDefault::assembleMatrix(const ID & matrix_id) { AKANTU_DEBUG_IN(); TimeStepSolver::assembleMatrix(matrix_id); if (matrix_id != "J") return; for (auto & pair : this->integration_schemes) { auto & dof_id = pair.first; auto & integration_scheme = pair.second; const auto & solution_type = this->solution_types[dof_id]; integration_scheme->assembleJacobian(solution_type, this->time_step); } this->dof_manager.applyBoundary("J"); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void TimeStepSolverDefault::assembleResidual() { AKANTU_DEBUG_IN(); if (this->needed_matrices.find("M") != needed_matrices.end()) { if (this->is_mass_lumped) { this->assembleLumpedMatrix("M"); } else { this->assembleMatrix("M"); } } TimeStepSolver::assembleResidual(); for (auto & pair : this->integration_schemes) { auto & integration_scheme = pair.second; integration_scheme->assembleResidual(this->is_mass_lumped); } AKANTU_DEBUG_OUT(); } +/* -------------------------------------------------------------------------- */ +void TimeStepSolverDefault::assembleResidual(const ID & residual_part) { + AKANTU_DEBUG_IN(); + + if (this->needed_matrices.find("M") != needed_matrices.end()) { + if (this->is_mass_lumped) { + this->assembleLumpedMatrix("M"); + } else { + this->assembleMatrix("M"); + } + } + + if (residual_part != "inertial") { + TimeStepSolver::assembleResidual(residual_part); + } + + if(residual_part == "inertial") { + for (auto & pair : this->integration_schemes) { + auto & integration_scheme = pair.second; + + integration_scheme->assembleResidual(this->is_mass_lumped); + } + } + + AKANTU_DEBUG_OUT(); +} + /* -------------------------------------------------------------------------- */ } // namespace akantu diff --git a/src/model/time_step_solvers/time_step_solver_default.hh b/src/model/time_step_solvers/time_step_solver_default.hh index 1d5fcff70..7513f2526 100644 --- a/src/model/time_step_solvers/time_step_solver_default.hh +++ b/src/model/time_step_solvers/time_step_solver_default.hh @@ -1,109 +1,110 @@ /** * @file time_step_solver_default.hh * * @author Nicolas Richart * * @date Mon Aug 24 17:10:29 2015 * * @brief Default implementation for the time stepper * * @section LICENSE * * Copyright (©) 2010-2011 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "integration_scheme.hh" #include "time_step_solver.hh" /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_TIME_STEP_SOLVER_DEFAULT_HH__ #define __AKANTU_TIME_STEP_SOLVER_DEFAULT_HH__ namespace akantu { class DOFManagerDefault; } namespace akantu { class TimeStepSolverDefault : public TimeStepSolver { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: TimeStepSolverDefault(DOFManagerDefault & dof_manager, const TimeStepSolverType & type, NonLinearSolver & non_linear_solver, const ID & id, UInt memory_id); ~TimeStepSolverDefault() override; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ public: /// registers an integration scheme for a given dof void setIntegrationScheme(const ID & dof_id, const IntegrationSchemeType & type, IntegrationScheme::SolutionType solution_type = IntegrationScheme::_not_defined) override; bool hasIntegrationScheme(const ID & dof_id) const override; /// implementation of the TimeStepSolver::predictor() void predictor() override; /// implementation of the TimeStepSolver::corrector() void corrector() override; /// implementation of the TimeStepSolver::assembleMatrix() void assembleMatrix(const ID & matrix_id) override; /// implementation of the TimeStepSolver::assembleResidual() void assembleResidual() override; + void assembleResidual(const ID & residual_part) override; /// implementation of the generic TimeStepSolver::solveStep() void solveStep(SolverCallback & solver_callback) override; /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ private: using DOFsIntegrationSchemes = std::map>; using DOFsIntegrationSchemesSolutionTypes = std::map; using DOFsIntegrationSchemesOwner = std::set; /// DOFManager with its real type DOFManagerDefault & dof_manager; /// Underlying integration scheme per dof, \todo check what happens in dynamic /// in case of coupled equations DOFsIntegrationSchemes integration_schemes; /// defines if the solver is owner of the memory or not DOFsIntegrationSchemesOwner integration_schemes_owner; /// Type of corrector to use DOFsIntegrationSchemesSolutionTypes solution_types; /// define if the mass matrix is lumped or not bool is_mass_lumped; }; } // namespace akantu #endif /* __AKANTU_TIME_STEP_SOLVER_DEFAULT_HH__ */ diff --git a/src/synchronizer/communications.hh b/src/synchronizer/communications.hh index 528a8274f..58f56ca01 100644 --- a/src/synchronizer/communications.hh +++ b/src/synchronizer/communications.hh @@ -1,272 +1,273 @@ /** * @file communications.hh * * @author Nicolas Richart * * @date Wed Aug 24 13:56:14 2016 * * @brief * * @section LICENSE * * Copyright (©) 2010-2011 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "communication_descriptor.hh" #include "communicator.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_COMMUNICATIONS_HH__ #define __AKANTU_COMMUNICATIONS_HH__ namespace akantu { /* -------------------------------------------------------------------------- */ template class Communications { public: using Scheme = Array; protected: using CommunicationPerProcs = std::map; using CommunicationsPerTags = std::map; using CommunicationSchemes = std::map; using Request = std::map>; friend class CommunicationDescriptor; public: using scheme_iterator = typename CommunicationSchemes::iterator; using const_scheme_iterator = typename CommunicationSchemes::const_iterator; /* ------------------------------------------------------------------------ */ class iterator; class tag_iterator; /* ------------------------------------------------------------------------ */ public: CommunicationPerProcs & getCommunications(const SynchronizationTag & tag, const CommunicationSendRecv & sr); /* ------------------------------------------------------------------------ */ bool hasPending(const SynchronizationTag & tag, const CommunicationSendRecv & sr) const; UInt getPending(const SynchronizationTag & tag, const CommunicationSendRecv & sr) const; /* ------------------------------------------------------------------------ */ iterator begin(const SynchronizationTag & tag, const CommunicationSendRecv & sr); iterator end(const SynchronizationTag & tag, const CommunicationSendRecv & sr); /* ------------------------------------------------------------------------ */ iterator waitAny(const SynchronizationTag & tag, const CommunicationSendRecv & sr); /* ------------------------------------------------------------------------ */ void waitAll(const SynchronizationTag & tag, const CommunicationSendRecv & sr); void incrementPending(const SynchronizationTag & tag, const CommunicationSendRecv & sr); void decrementPending(const SynchronizationTag & tag, const CommunicationSendRecv & sr); void freeRequests(const SynchronizationTag & tag, const CommunicationSendRecv & sr); /* ------------------------------------------------------------------------ */ Scheme & createScheme(UInt proc, const CommunicationSendRecv & sr); void resetSchemes(const CommunicationSendRecv & sr); /* ------------------------------------------------------------------------ */ void setCommunicationSize(const SynchronizationTag & tag, UInt proc, UInt size, const CommunicationSendRecv & sr); public: explicit Communications(const Communicator & communicator); + explicit Communications(const Communications & communications); /* ------------------------------------------------------------------------ */ class IterableCommunicationDesc { public: IterableCommunicationDesc(Communications & communications, SynchronizationTag tag, CommunicationSendRecv sr) : communications(communications), tag(std::move(tag)), sr(std::move(sr)) {} auto begin() { return communications.begin(tag, sr); } auto end() { return communications.end(tag, sr); } private: Communications & communications; SynchronizationTag tag; CommunicationSendRecv sr; }; auto iterateRecv(const SynchronizationTag & tag) { return IterableCommunicationDesc(*this, tag, _recv); } auto iterateSend(const SynchronizationTag & tag) { return IterableCommunicationDesc(*this, tag, _send); } /* ------------------------------------------------------------------------ */ // iterator begin_send(const SynchronizationTag & tag); // iterator end_send(const SynchronizationTag & tag); /* ------------------------------------------------------------------------ */ // iterator begin_recv(const SynchronizationTag & tag); // iterator end_recv(const SynchronizationTag & tag); /* ------------------------------------------------------------------------ */ class IterableTags { public: explicit IterableTags(Communications & communications) : communications(communications) {} decltype(auto) begin() { return communications.begin_tag(); } decltype(auto) end() { return communications.end_tag(); } private: Communications & communications; }; decltype(auto) iterateTags() { return IterableTags(*this); } tag_iterator begin_tag(); tag_iterator end_tag(); /* ------------------------------------------------------------------------ */ bool hasCommunication(const SynchronizationTag & tag) const; void incrementCounter(const SynchronizationTag & tag); UInt getCounter(const SynchronizationTag & tag) const; bool hasCommunicationSize(const SynchronizationTag & tag) const; void invalidateSizes(); /* ------------------------------------------------------------------------ */ bool hasPendingRecv(const SynchronizationTag & tag) const; bool hasPendingSend(const SynchronizationTag & tag) const; const auto & getCommunicator() const; /* ------------------------------------------------------------------------ */ iterator waitAnyRecv(const SynchronizationTag & tag); iterator waitAnySend(const SynchronizationTag & tag); void waitAllRecv(const SynchronizationTag & tag); void waitAllSend(const SynchronizationTag & tag); void freeSendRequests(const SynchronizationTag & tag); void freeRecvRequests(const SynchronizationTag & tag); /* ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ */ class IterableSchemes { public: IterableSchemes(Communications & communications, CommunicationSendRecv sr) : communications(communications), sr(std::move(sr)) {} decltype(auto) begin() { return communications.begin_scheme(sr); } decltype(auto) end() { return communications.end_scheme(sr); } private: Communications & communications; CommunicationSendRecv sr; }; class ConstIterableSchemes { public: ConstIterableSchemes(const Communications & communications, CommunicationSendRecv sr) : communications(communications), sr(std::move(sr)) {} decltype(auto) begin() const { return communications.begin_scheme(sr); } decltype(auto) end() const { return communications.end_scheme(sr); } private: const Communications & communications; CommunicationSendRecv sr; }; decltype(auto) iterateSchemes(const CommunicationSendRecv & sr) { return IterableSchemes(*this, sr); } decltype(auto) iterateSchemes(const CommunicationSendRecv & sr) const { return ConstIterableSchemes(*this, sr); } decltype(auto) iterateSendSchemes() { return IterableSchemes(*this, _send); } decltype(auto) iterateSendSchemes() const { return ConstIterableSchemes(*this, _send); } decltype(auto) iterateRecvSchemes() { return IterableSchemes(*this, _recv); } decltype(auto) iterateRecvSchemes() const { return ConstIterableSchemes(*this, _recv); } scheme_iterator begin_scheme(const CommunicationSendRecv & sr); scheme_iterator end_scheme(const CommunicationSendRecv & sr); const_scheme_iterator begin_scheme(const CommunicationSendRecv & sr) const; const_scheme_iterator end_scheme(const CommunicationSendRecv & sr) const; /* ------------------------------------------------------------------------ */ scheme_iterator begin_send_scheme(); scheme_iterator end_send_scheme(); const_scheme_iterator begin_send_scheme() const; const_scheme_iterator end_send_scheme() const; /* ------------------------------------------------------------------------ */ scheme_iterator begin_recv_scheme(); scheme_iterator end_recv_scheme(); const_scheme_iterator begin_recv_scheme() const; const_scheme_iterator end_recv_scheme() const; /* ------------------------------------------------------------------------ */ Scheme & createSendScheme(UInt proc); Scheme & createRecvScheme(UInt proc); /* ------------------------------------------------------------------------ */ Scheme & getScheme(UInt proc, const CommunicationSendRecv & sr); const Scheme & getScheme(UInt proc, const CommunicationSendRecv & sr) const; /* ------------------------------------------------------------------------ */ void resetSchemes(); /* ------------------------------------------------------------------------ */ void setSendCommunicationSize(const SynchronizationTag & tag, UInt proc, UInt size); void setRecvCommunicationSize(const SynchronizationTag & tag, UInt proc, UInt size); void initializeCommunications(const SynchronizationTag & tag); protected: CommunicationSchemes schemes[2]; CommunicationsPerTags communications[2]; std::map comm_counter; std::map pending_communications[2]; std::map comm_size_computed; const Communicator & communicator; }; } // namespace akantu #include "communications_tmpl.hh" #endif /* __AKANTU_COMMUNICATIONS_HH__ */ diff --git a/src/synchronizer/communications_tmpl.hh b/src/synchronizer/communications_tmpl.hh index 5bef47632..f87ced61a 100644 --- a/src/synchronizer/communications_tmpl.hh +++ b/src/synchronizer/communications_tmpl.hh @@ -1,534 +1,550 @@ /** * @file communications_tmpl.hh * * @author Nicolas Richart * * @date Tue Sep 6 17:14:06 2016 * * @brief Implementation of Communications * * @section LICENSE * * Copyright (©) 2010-2011 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "communications.hh" /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_COMMUNICATIONS_TMPL_HH__ #define __AKANTU_COMMUNICATIONS_TMPL_HH__ namespace akantu { +/* -------------------------------------------------------------------------- */ +template +Communications::Communications(const Communicator & communicator) + : communicator(communicator) {} + +/* -------------------------------------------------------------------------- */ +template +Communications::Communications(const Communications & other) + : communicator(other.communicator) { + for(auto && sr : iterate_send_recv) { + for (const auto & scheme_pair : other.iterateSchemes(sr)) { + auto proc = scheme_pair.first; + auto & other_scheme = scheme_pair.second; + auto & scheme = this->createScheme(proc, sr); + scheme.copy(other_scheme); + } + } + + this->invalidateSizes(); +} + /* -------------------------------------------------------------------------- */ template class Communications::iterator { using communication_iterator = typename std::map::iterator; public: iterator() : communications(nullptr) {} iterator(scheme_iterator scheme_it, communication_iterator comm_it, Communications & communications, const SynchronizationTag & tag) : scheme_it(scheme_it), comm_it(comm_it), communications(&communications), tag(tag) {} iterator & operator=(const iterator & other) { if (this != &other) { this->scheme_it = other.scheme_it; this->comm_it = other.comm_it; this->communications = other.communications; this->tag = other.tag; } return *this; } iterator & operator++() { ++scheme_it; ++comm_it; return *this; } CommunicationDescriptor operator*() { AKANTU_DEBUG_ASSERT( scheme_it->first == comm_it->first, "The two iterators are not in phase, something wrong" << " happened, time to take out your favorite debugger (" << scheme_it->first << " != " << comm_it->first << ")"); return CommunicationDescriptor(comm_it->second, scheme_it->second, *communications, tag, scheme_it->first); } bool operator==(const iterator & other) const { return scheme_it == other.scheme_it && comm_it == other.comm_it; } bool operator!=(const iterator & other) const { return scheme_it != other.scheme_it || comm_it != other.comm_it; } private: scheme_iterator scheme_it; communication_iterator comm_it; Communications * communications; SynchronizationTag tag; }; /* -------------------------------------------------------------------------- */ template class Communications::tag_iterator { using internal_iterator = std::map::const_iterator; public: tag_iterator(const internal_iterator & it) : it(it) {} tag_iterator & operator++() { ++it; return *this; } SynchronizationTag operator*() { return it->first; } bool operator==(const tag_iterator & other) const { return it == other.it; } bool operator!=(const tag_iterator & other) const { return it != other.it; } private: internal_iterator it; }; /* -------------------------------------------------------------------------- */ template typename Communications::CommunicationPerProcs & Communications::getCommunications(const SynchronizationTag & tag, const CommunicationSendRecv & sr) { auto comm_it = this->communications[sr].find(tag); if (comm_it == this->communications[sr].end()) AKANTU_CUSTOM_EXCEPTION_INFO( debug::CommunicationException(), "No known communications for the tag: " << tag); return comm_it->second; } /* ---------------------------------------------------------------------- */ template UInt Communications::getPending( const SynchronizationTag & tag, const CommunicationSendRecv & sr) const { const std::map & pending = pending_communications[sr]; auto it = pending.find(tag); if (it == pending.end()) return 0; return it->second; } /* -------------------------------------------------------------------------- */ template bool Communications::hasPending( const SynchronizationTag & tag, const CommunicationSendRecv & sr) const { return this->hasCommunication(tag) && (this->getPending(tag, sr) != 0); } /* ---------------------------------------------------------------------- */ template typename Communications::iterator Communications::begin(const SynchronizationTag & tag, const CommunicationSendRecv & sr) { auto & comms = this->getCommunications(tag, sr); return iterator(this->schemes[sr].begin(), comms.begin(), *this, tag); } template typename Communications::iterator Communications::end(const SynchronizationTag & tag, const CommunicationSendRecv & sr) { auto & comms = this->getCommunications(tag, sr); return iterator(this->schemes[sr].end(), comms.end(), *this, tag); } /* ---------------------------------------------------------------------- */ template typename Communications::iterator Communications::waitAny(const SynchronizationTag & tag, const CommunicationSendRecv & sr) { auto & comms = this->getCommunications(tag, sr); auto it = comms.begin(); auto end = comms.end(); std::vector requests; for (; it != end; ++it) { auto & request = it->second.request(); if (!request.isFreed()) requests.push_back(request); } UInt req_id = communicator.waitAny(requests); if (req_id != UInt(-1)) { auto & request = requests[req_id]; UInt proc = sr == _recv ? request.getSource() : request.getDestination(); return iterator(this->schemes[sr].find(proc), comms.find(proc), *this, tag); } else { return this->end(tag, sr); } } /* ---------------------------------------------------------------------- */ template void Communications::waitAll(const SynchronizationTag & tag, const CommunicationSendRecv & sr) { auto & comms = this->getCommunications(tag, sr); auto it = comms.begin(); auto end = comms.end(); std::vector requests; for (; it != end; ++it) { requests.push_back(it->second.request()); } communicator.waitAll(requests); } template void Communications::incrementPending( const SynchronizationTag & tag, const CommunicationSendRecv & sr) { ++(pending_communications[sr][tag]); } template void Communications::decrementPending( const SynchronizationTag & tag, const CommunicationSendRecv & sr) { --(pending_communications[sr][tag]); } template void Communications::freeRequests(const SynchronizationTag & tag, const CommunicationSendRecv & sr) { iterator it = this->begin(tag, sr); iterator end = this->end(tag, sr); for (; it != end; ++it) { (*it).freeRequest(); } } /* -------------------------------------------------------------------------- */ template typename Communications::Scheme & Communications::createScheme(UInt proc, const CommunicationSendRecv & sr) { // scheme_iterator it = schemes[sr].find(proc); // if (it != schemes[sr].end()) { // AKANTU_CUSTOM_EXCEPTION_INFO(debug::CommunicationException(), // "Communication scheme(" // << sr // << ") already created for proc: " << // proc); // } return schemes[sr][proc]; } template void Communications::resetSchemes(const CommunicationSendRecv & sr) { auto it = this->schemes[sr].begin(); auto end = this->schemes[sr].end(); for (; it != end; ++it) { it->second.resize(0); } } /* -------------------------------------------------------------------------- */ template void Communications::setCommunicationSize( const SynchronizationTag & tag, UInt proc, UInt size, const CommunicationSendRecv & sr) { // accessor that fails if it does not exists comm_size_computed[tag] = true; // TODO: need perhaps to be split based on sr auto & comms = this->communications[sr]; auto & comms_per_tag = comms.at(tag); comms_per_tag.at(proc).resize(size); } /* -------------------------------------------------------------------------- */ template void Communications::initializeCommunications( const SynchronizationTag & tag) { for (auto t = send_recv_t::begin(); t != send_recv_t::end(); ++t) { pending_communications[*t].insert(std::make_pair(tag, 0)); auto & comms = this->communications[*t]; auto & comms_per_tag = comms.insert(std::make_pair(tag, CommunicationPerProcs())) .first->second; for (auto pair : this->schemes[*t]) { comms_per_tag.emplace(std::piecewise_construct, std::forward_as_tuple(pair.first), std::forward_as_tuple(*t)); } } comm_counter.insert(std::make_pair(tag, 0)); } -/* -------------------------------------------------------------------------- */ -template -Communications::Communications(const Communicator & communicator) - : communicator(communicator) {} - /* -------------------------------------------------------------------------- */ template typename Communications::tag_iterator Communications::begin_tag() { return tag_iterator(comm_counter.begin()); } template typename Communications::tag_iterator Communications::end_tag() { return tag_iterator(comm_counter.end()); } /* -------------------------------------------------------------------------- */ template typename Communications::scheme_iterator Communications::begin_scheme(const CommunicationSendRecv & sr) { return this->schemes[sr].begin(); } template typename Communications::scheme_iterator Communications::end_scheme(const CommunicationSendRecv & sr) { return this->schemes[sr].end(); } /* -------------------------------------------------------------------------- */ template typename Communications::const_scheme_iterator Communications::begin_scheme(const CommunicationSendRecv & sr) const { return this->schemes[sr].begin(); } template typename Communications::const_scheme_iterator Communications::end_scheme(const CommunicationSendRecv & sr) const { return this->schemes[sr].end(); } /* -------------------------------------------------------------------------- */ template typename Communications::scheme_iterator Communications::begin_send_scheme() { return this->begin_scheme(_send); } template typename Communications::scheme_iterator Communications::end_send_scheme() { return this->end_scheme(_send); } /* -------------------------------------------------------------------------- */ template typename Communications::const_scheme_iterator Communications::begin_send_scheme() const { return this->begin_scheme(_send); } template typename Communications::const_scheme_iterator Communications::end_send_scheme() const { return this->end_scheme(_send); } /* -------------------------------------------------------------------------- */ template typename Communications::scheme_iterator Communications::begin_recv_scheme() { return this->begin_scheme(_recv); } template typename Communications::scheme_iterator Communications::end_recv_scheme() { return this->end_scheme(_recv); } /* -------------------------------------------------------------------------- */ template typename Communications::const_scheme_iterator Communications::begin_recv_scheme() const { return this->begin_scheme(_recv); } template typename Communications::const_scheme_iterator Communications::end_recv_scheme() const { return this->end_scheme(_recv); } /* ------------------------------------------------------------------------ */ template bool Communications::hasCommunication( const SynchronizationTag & tag) const { return (communications[_send].find(tag) != communications[_send].end()); } template void Communications::incrementCounter(const SynchronizationTag & tag) { auto it = comm_counter.find(tag); if (it == comm_counter.end()) { AKANTU_CUSTOM_EXCEPTION_INFO( debug::CommunicationException(), "No counter initialized in communications for the tags: " << tag); } ++(it->second); } template UInt Communications::getCounter(const SynchronizationTag & tag) const { auto it = comm_counter.find(tag); if (it == comm_counter.end()) { AKANTU_CUSTOM_EXCEPTION_INFO( debug::CommunicationException(), "No counter initialized in communications for the tags: " << tag); } return it->second; } template bool Communications::hasCommunicationSize( const SynchronizationTag & tag) const { auto it = comm_size_computed.find(tag); if (it == comm_size_computed.end()) { return false; } return it->second; } template void Communications::invalidateSizes() { for (auto && pair : comm_size_computed) { pair.second = true; } } template bool Communications::hasPendingRecv( const SynchronizationTag & tag) const { return this->hasPending(tag, _recv); } template bool Communications::hasPendingSend( const SynchronizationTag & tag) const { return this->hasPending(tag, _send); } template const auto & Communications::getCommunicator() const { return communicator; } /* -------------------------------------------------------------------------- */ template typename Communications::iterator Communications::waitAnyRecv(const SynchronizationTag & tag) { return this->waitAny(tag, _recv); } template typename Communications::iterator Communications::waitAnySend(const SynchronizationTag & tag) { return this->waitAny(tag, _send); } template void Communications::waitAllRecv(const SynchronizationTag & tag) { this->waitAll(tag, _recv); } template void Communications::waitAllSend(const SynchronizationTag & tag) { this->waitAll(tag, _send); } template void Communications::freeSendRequests(const SynchronizationTag & tag) { this->freeRequests(tag, _send); } template void Communications::freeRecvRequests(const SynchronizationTag & tag) { this->freeRequests(tag, _recv); } /* -------------------------------------------------------------------------- */ template typename Communications::Scheme & Communications::createSendScheme(UInt proc) { return createScheme(proc, _send); } template typename Communications::Scheme & Communications::createRecvScheme(UInt proc) { return createScheme(proc, _recv); } /* -------------------------------------------------------------------------- */ template void Communications::resetSchemes() { resetSchemes(_send); resetSchemes(_recv); } /* -------------------------------------------------------------------------- */ template typename Communications::Scheme & Communications::getScheme(UInt proc, const CommunicationSendRecv & sr) { return this->schemes[sr].find(proc)->second; } /* -------------------------------------------------------------------------- */ template const typename Communications::Scheme & Communications::getScheme(UInt proc, const CommunicationSendRecv & sr) const { return this->schemes[sr].find(proc)->second; } /* -------------------------------------------------------------------------- */ template void Communications::setSendCommunicationSize( const SynchronizationTag & tag, UInt proc, UInt size) { this->setCommunicationSize(tag, proc, size, _send); } template void Communications::setRecvCommunicationSize( const SynchronizationTag & tag, UInt proc, UInt size) { this->setCommunicationSize(tag, proc, size, _recv); } } // namespace akantu #endif /* __AKANTU_COMMUNICATIONS_TMPL_HH__ */ diff --git a/src/synchronizer/data_accessor.cc b/src/synchronizer/data_accessor.cc index c4855e9ab..3a51a7016 100644 --- a/src/synchronizer/data_accessor.cc +++ b/src/synchronizer/data_accessor.cc @@ -1,163 +1,155 @@ /** * @file data_accessor.cc * * @author Guillaume Anciaux * @author Nicolas Richart * * @date creation: Fri Jun 18 2010 * @date last modification: Sun Oct 19 2014 * * @brief data accessors constructor functions * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "data_accessor.hh" #include "fe_engine.hh" /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ template void DataAccessor::packUnpackNodalDataHelper( Array & data, CommunicationBuffer & buffer, const Array & elements, const Mesh & mesh) { UInt nb_component = data.getNbComponent(); UInt nb_nodes_per_element = 0; ElementType current_element_type = _not_defined; GhostType current_ghost_type = _casper; UInt * conn = nullptr; - Array::const_iterator it = elements.begin(); - Array::const_iterator end = elements.end(); - for (; it != end; ++it) { - const Element & el = *it; + for (auto & el : elements) { if (el.type != current_element_type || el.ghost_type != current_ghost_type) { current_element_type = el.type; current_ghost_type = el.ghost_type; conn = mesh.getConnectivity(el.type, el.ghost_type).storage(); nb_nodes_per_element = Mesh::getNbNodesPerElement(el.type); } UInt el_offset = el.element * nb_nodes_per_element; for (UInt n = 0; n < nb_nodes_per_element; ++n) { UInt offset_conn = conn[el_offset + n]; Vector data_vect(data.storage() + offset_conn * nb_component, nb_component); if (pack_helper) buffer << data_vect; else buffer >> data_vect; } } } /* ------------------------------------------------------------------------ */ template void DataAccessor::packUnpackElementalDataHelper( ElementTypeMapArray & data_to_pack, CommunicationBuffer & buffer, const Array & element, bool per_quadrature_point_data, const FEEngine & fem) { ElementType current_element_type = _not_defined; GhostType current_ghost_type = _casper; UInt nb_quad_per_elem = 0; UInt nb_component = 0; Array * vect = nullptr; - Array::const_iterator it = element.begin(); - Array::const_iterator end = element.end(); - for (; it != end; ++it) { - const Element & el = *it; + for (auto & el : element) { if (el.type != current_element_type || el.ghost_type != current_ghost_type) { current_element_type = el.type; current_ghost_type = el.ghost_type; vect = &data_to_pack(el.type, el.ghost_type); - if (per_quadrature_point_data) - nb_quad_per_elem = fem.getNbIntegrationPoints(el.type, el.ghost_type); - else - nb_quad_per_elem = 1; + + nb_quad_per_elem = + per_quadrature_point_data + ? fem.getNbIntegrationPoints(el.type, el.ghost_type) + : 1; nb_component = vect->getNbComponent(); } Vector data(vect->storage() + el.element * nb_component * nb_quad_per_elem, nb_component * nb_quad_per_elem); if (pack_helper) buffer << data; else buffer >> data; } } /* -------------------------------------------------------------------------- */ template void DataAccessor::packUnpackDOFDataHelper(Array & data, CommunicationBuffer & buffer, const Array & dofs) { - Array::const_scalar_iterator it_dof = dofs.begin(); - Array::const_scalar_iterator end_dof = dofs.end(); T * data_ptr = data.storage(); - - for (; it_dof != end_dof; ++it_dof) { + for (const auto & dof : dofs) { if (pack_helper) - buffer << data_ptr[*it_dof]; + buffer << data_ptr[dof]; else - buffer >> data_ptr[*it_dof]; + buffer >> data_ptr[dof]; } } /* -------------------------------------------------------------------------- */ #define DECLARE_HELPERS(T) \ template void DataAccessor::packUnpackNodalDataHelper( \ Array & data, CommunicationBuffer & buffer, \ const Array & elements, const Mesh & mesh); \ template void DataAccessor::packUnpackNodalDataHelper( \ Array & data, CommunicationBuffer & buffer, \ const Array & elements, const Mesh & mesh); \ template void \ DataAccessor::packUnpackElementalDataHelper( \ ElementTypeMapArray & data_to_pack, CommunicationBuffer & buffer, \ const Array & element, bool per_quadrature_point_data, \ const FEEngine & fem); \ template void DataAccessor::packUnpackElementalDataHelper( \ ElementTypeMapArray & data_to_pack, CommunicationBuffer & buffer, \ const Array & element, bool per_quadrature_point_data, \ const FEEngine & fem); \ template void DataAccessor::packUnpackDOFDataHelper( \ Array & data, CommunicationBuffer & buffer, \ const Array & dofs); \ template void DataAccessor::packUnpackDOFDataHelper( \ Array & data, CommunicationBuffer & buffer, const Array & dofs) /* -------------------------------------------------------------------------- */ DECLARE_HELPERS(Real); DECLARE_HELPERS(UInt); DECLARE_HELPERS(bool); /* -------------------------------------------------------------------------- */ } // akantu diff --git a/src/synchronizer/element_synchronizer.cc b/src/synchronizer/element_synchronizer.cc index 9ffc5f5f8..32698e302 100644 --- a/src/synchronizer/element_synchronizer.cc +++ b/src/synchronizer/element_synchronizer.cc @@ -1,353 +1,369 @@ /** * @file element_synchronizer.cc * * @author Guillaume Anciaux * @author Dana Christen * @author Aurelia Isabel Cuba Ramos * @author Nicolas Richart * @author Marco Vocialta * * @date creation: Wed Sep 01 2010 * @date last modification: Fri Jan 22 2016 * * @brief implementation of a communicator using a static_communicator for * real * send/receive * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "element_synchronizer.hh" #include "aka_common.hh" #include "mesh.hh" #include "mesh_utils.hh" /* -------------------------------------------------------------------------- */ #include #include #include /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ ElementSynchronizer::ElementSynchronizer(Mesh & mesh, const ID & id, MemoryID memory_id, bool register_to_event_manager, EventHandlerPriority event_priority) : SynchronizerImpl(mesh.getCommunicator(), id, memory_id), mesh(mesh), element_to_prank("element_to_prank", id, memory_id) { + AKANTU_DEBUG_IN(); + + if (register_to_event_manager) + this->mesh.registerEventHandler(*this, event_priority); + AKANTU_DEBUG_OUT(); +} + +/* -------------------------------------------------------------------------- */ +ElementSynchronizer::ElementSynchronizer(const ElementSynchronizer & other, + const ID & id, + bool register_to_event_manager, + EventHandlerPriority event_priority) + : SynchronizerImpl(other, id), mesh(other.mesh), + element_to_prank("element_to_prank", id, other.memory_id) { AKANTU_DEBUG_IN(); + element_to_prank.copy(other.element_to_prank); + if (register_to_event_manager) this->mesh.registerEventHandler(*this, event_priority); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ ElementSynchronizer::~ElementSynchronizer() = default; /* -------------------------------------------------------------------------- */ void ElementSynchronizer::substituteElements( const std::map & old_to_new_elements) { auto found_element_end = old_to_new_elements.end(); // substitute old elements with new ones for (auto && sr : iterate_send_recv) { for (auto && scheme_pair : communications.iterateSchemes(sr)) { auto & list = scheme_pair.second; for (auto & el : list) { auto found_element_it = old_to_new_elements.find(el); if (found_element_it != found_element_end) el = found_element_it->second; } } } } /* -------------------------------------------------------------------------- */ void ElementSynchronizer::onElementsChanged( const Array & old_elements_list, const Array & new_elements_list, const ElementTypeMapArray &, const ChangedElementsEvent &) { // create a map to link old elements to new ones std::map old_to_new_elements; for (UInt el = 0; el < old_elements_list.size(); ++el) { AKANTU_DEBUG_ASSERT(old_to_new_elements.find(old_elements_list(el)) == old_to_new_elements.end(), "The same element cannot appear twice in the list"); old_to_new_elements[old_elements_list(el)] = new_elements_list(el); } substituteElements(old_to_new_elements); communications.invalidateSizes(); } /* -------------------------------------------------------------------------- */ void ElementSynchronizer::onElementsRemoved( const Array & element_to_remove, const ElementTypeMapArray & new_numbering, const RemovedElementsEvent &) { AKANTU_DEBUG_IN(); this->removeElements(element_to_remove); this->renumberElements(new_numbering); communications.invalidateSizes(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void ElementSynchronizer::buildElementToPrank() { AKANTU_DEBUG_IN(); UInt spatial_dimension = mesh.getSpatialDimension(); element_to_prank.initialize(mesh, _spatial_dimension = spatial_dimension, _element_kind = _ek_not_defined, _with_nb_element = true, _default_value = rank); /// assign prank to all ghost elements for (auto && scheme : communications.iterateSchemes(_recv)) { auto & recv = scheme.second; auto proc = scheme.first; for (auto & element : recv) { element_to_prank(element) = proc; } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ Int ElementSynchronizer::getRank(const Element & element) const { if (not element_to_prank.exists(element.type, element.ghost_type)) { // Nicolas: Ok This is nasty I know.... const_cast(this)->buildElementToPrank(); } return element_to_prank(element); } /* -------------------------------------------------------------------------- */ void ElementSynchronizer::reset() { AKANTU_DEBUG_IN(); communications.resetSchemes(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void ElementSynchronizer::removeElements( const Array & element_to_remove) { AKANTU_DEBUG_IN(); std::vector send_requests; std::map> list_of_elements_per_proc; auto filter_list = [](const Array & keep, Array & list) { Array new_list; for (UInt e = 0; e < keep.size() - 1; ++e) { Element & el = list(keep(e)); new_list.push_back(el); } list.copy(new_list); }; // Handling ghost elements for (auto && scheme_pair : communications.iterateSchemes(_recv)) { auto proc = scheme_pair.first; auto & recv = scheme_pair.second; Array & keep_list = list_of_elements_per_proc[proc]; auto rem_it = element_to_remove.begin(); auto rem_end = element_to_remove.end(); for (auto && pair : enumerate(recv)) { const auto & el = std::get<1>(pair); auto pos = std::find(rem_it, rem_end, el); if (pos == rem_end) { keep_list.push_back(std::get<0>(pair)); } } keep_list.push_back(UInt(-1)); // To no send empty arrays auto && tag = Tag::genTag(proc, 0, Tag::_MODIFY_SCHEME, this->hash_id); AKANTU_DEBUG_INFO("Sending a message of size " << keep_list.size() << " to proc " << proc << " TAG(" << tag << ")"); send_requests.push_back(this->communicator.asyncSend(keep_list, proc, tag)); #ifndef AKANTU_NDEBUG auto old_size = recv.size(); #endif filter_list(keep_list, recv); AKANTU_DEBUG_INFO("I had " << old_size << " elements to recv from proc " << proc << " and " << keep_list.size() << " elements to keep. I have " << recv.size() << " elements left."); } for (auto && scheme_pair : communications.iterateSchemes(_send)) { auto proc = scheme_pair.first; auto & send = scheme_pair.second; CommunicationStatus status; auto && tag = Tag::genTag(rank, 0, Tag::_MODIFY_SCHEME, this->hash_id); AKANTU_DEBUG_INFO("Getting number of elements of proc " << proc << " to keep - TAG(" << tag << ")"); this->communicator.probe(proc, tag, status); Array keep_list(status.size()); AKANTU_DEBUG_INFO("Receiving list of elements (" << keep_list.size() << " elements) to keep for proc " << proc << " TAG(" << tag << ")"); this->communicator.receive(keep_list, proc, tag); #ifndef AKANTU_NDEBUG auto old_size = send.size(); #endif filter_list(keep_list, send); AKANTU_DEBUG_INFO("I had " << old_size << " elements to send to proc " << proc << " and " << keep_list.size() << " elements to keep. I have " << send.size() << " elements left."); } this->communicator.waitAll(send_requests); this->communicator.freeCommunicationRequest(send_requests); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void ElementSynchronizer::renumberElements( const ElementTypeMapArray & new_numbering) { for (auto && sr : iterate_send_recv) { for (auto && scheme_pair : communications.iterateSchemes(sr)) { auto & list = scheme_pair.second; for (auto && el : list) { if (new_numbering.exists(el.type, el.ghost_type)) el.element = new_numbering(el); } } } } /* -------------------------------------------------------------------------- */ UInt ElementSynchronizer::sanityCheckDataSize( const Array & elements, const SynchronizationTag &) const { UInt size = 0; size += sizeof(SynchronizationTag); // tag size += sizeof(UInt); // comm_desc.getNbData(); size += sizeof(UInt); // comm_desc.getProc(); size += sizeof(UInt); // mesh.getCommunicator().whoAmI(); // barycenters size += (elements.size() * mesh.getSpatialDimension() * sizeof(Real)); return size; } /* -------------------------------------------------------------------------- */ void ElementSynchronizer::packSanityCheckData( CommunicationDescriptor & comm_desc) const { auto & buffer = comm_desc.getBuffer(); buffer << comm_desc.getTag(); buffer << comm_desc.getNbData(); buffer << comm_desc.getProc(); buffer << mesh.getCommunicator().whoAmI(); auto & send_element = comm_desc.getScheme(); /// pack barycenters in debug mode for (auto && element : send_element) { Vector barycenter(mesh.getSpatialDimension()); mesh.getBarycenter(element, barycenter); buffer << barycenter; } } /* -------------------------------------------------------------------------- */ void ElementSynchronizer::unpackSanityCheckData( CommunicationDescriptor & comm_desc) const { auto & buffer = comm_desc.getBuffer(); const auto & tag = comm_desc.getTag(); auto nb_data = comm_desc.getNbData(); auto proc = comm_desc.getProc(); auto rank = mesh.getCommunicator().whoAmI(); decltype(nb_data) recv_nb_data; decltype(proc) recv_proc; decltype(rank) recv_rank; SynchronizationTag t; buffer >> t; buffer >> recv_nb_data; buffer >> recv_proc; buffer >> recv_rank; AKANTU_DEBUG_ASSERT( t == tag, "The tag received does not correspond to the tag expected"); AKANTU_DEBUG_ASSERT( nb_data == recv_nb_data, "The nb_data received does not correspond to the nb_data expected"); AKANTU_DEBUG_ASSERT(UInt(recv_rank) == proc, "The rank received does not correspond to the proc"); AKANTU_DEBUG_ASSERT(recv_proc == UInt(rank), "The proc received does not correspond to the rank"); auto & recv_element = comm_desc.getScheme(); auto spatial_dimension = mesh.getSpatialDimension(); for (auto && element : recv_element) { Vector barycenter_loc(spatial_dimension); mesh.getBarycenter(element, barycenter_loc); Vector barycenter(spatial_dimension); buffer >> barycenter; auto dist = barycenter_loc.distance(barycenter); if (not Math::are_float_equal(dist, 0.)) AKANTU_EXCEPTION("Unpacking an unknown value for the element " << element << "(barycenter " << barycenter_loc << " != buffer " << barycenter << ") [" << dist << "] - tag: " << tag << " comm from " << proc << " to " << rank); } } /* -------------------------------------------------------------------------- */ } // namespace akantu diff --git a/src/synchronizer/element_synchronizer.hh b/src/synchronizer/element_synchronizer.hh index 53fbc6e76..8764beb79 100644 --- a/src/synchronizer/element_synchronizer.hh +++ b/src/synchronizer/element_synchronizer.hh @@ -1,211 +1,216 @@ /** * @file element_synchronizer.hh * * @author Guillaume Anciaux * @author Dana Christen * @author Aurelia Isabel Cuba Ramos * @author Nicolas Richart * @author Marco Vocialta * * @date creation: Fri Jun 18 2010 * @date last modification: Tue Dec 08 2015 * * @brief Main element synchronizer * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_ELEMENT_SYNCHRONIZER_HH__ #define __AKANTU_ELEMENT_SYNCHRONIZER_HH__ /* -------------------------------------------------------------------------- */ #include "aka_array.hh" #include "aka_common.hh" #include "mesh_partition.hh" #include "synchronizer_impl.hh" namespace akantu { class Mesh; } /* -------------------------------------------------------------------------- */ namespace akantu { class ElementSynchronizer : public SynchronizerImpl, public MeshEventHandler { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: ElementSynchronizer(Mesh & mesh, const ID & id = "element_synchronizer", MemoryID memory_id = 0, bool register_to_event_manager = true, EventHandlerPriority event_priority = _ehp_synchronizer); + ElementSynchronizer(const ElementSynchronizer & other, + const ID & id = "element_synchronizer_copy", + bool register_to_event_manager = true, + EventHandlerPriority event_priority = _ehp_synchronizer); + public: ~ElementSynchronizer() override; friend class ElementInfoPerProc; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ public: /* ------------------------------------------------------------------------ */ /// mesh event handler onElementsChanged void onElementsChanged(const Array & old_elements_list, const Array & new_elements_list, const ElementTypeMapArray & new_numbering, const ChangedElementsEvent & event) override; /// mesh event handler onRemovedElement void onElementsRemoved(const Array & element_list, const ElementTypeMapArray & new_numbering, const RemovedElementsEvent & event) override; /// mesh event handler onNodesAdded void onNodesAdded(const Array & /* nodes_list*/, const NewNodesEvent & /*event*/) override{}; /// mesh event handler onRemovedNodes void onNodesRemoved(const Array & /*nodes_list*/, const Array & /*new_numbering*/, const RemovedNodesEvent & /*event*/) override{}; /// mesh event handler onElementsAdded void onElementsAdded(const Array & /*elements_list*/, const NewElementsEvent & /*event*/) override{}; protected: /// reset send and recv element lists void reset(); /// remove elements from the synchronizer without renumbering them void removeElements(const Array & element_to_remove); /// renumber the elements in the synchronizer void renumberElements(const ElementTypeMapArray & new_numbering); /// build processor to element correspondence void buildElementToPrank(); protected: /// fill the nodes type vector void fillNodesType(const MeshData & mesh_data, DynamicCommunicationBuffer * buffers, const std::string & tag_name, const ElementType & el_type, const Array & partition_num); template void fillTagBufferTemplated(const MeshData & mesh_data, DynamicCommunicationBuffer * buffers, const std::string & tag_name, const ElementType & el_type, const Array & partition_num, const CSR & ghost_partition); void fillTagBuffer(const MeshData & mesh_data, DynamicCommunicationBuffer * buffers, const std::string & tag_name, const ElementType & el_type, const Array & partition_num, const CSR & ghost_partition); /// function that handels the MeshData to be split (root side) static void synchronizeTagsSend(ElementSynchronizer & communicator, UInt root, Mesh & mesh, UInt nb_tags, const ElementType & type, const Array & partition_num, const CSR & ghost_partition, UInt nb_local_element, UInt nb_ghost_element); /// function that handles the MeshData to be split (other nodes) static void synchronizeTagsRecv(ElementSynchronizer & communicator, UInt root, Mesh & mesh, UInt nb_tags, const ElementType & type, UInt nb_local_element, UInt nb_ghost_element); /// function that handles the preexisting groups in the mesh static void synchronizeElementGroups(ElementSynchronizer & communicator, UInt root, Mesh & mesh, const ElementType & type, const Array & partition_num, const CSR & ghost_partition, UInt nb_element); /// function that handles the preexisting groups in the mesh static void synchronizeElementGroups(ElementSynchronizer & communicator, UInt root, Mesh & mesh, const ElementType & type); /// function that handles the preexisting groups in the mesh static void synchronizeNodeGroupsMaster(ElementSynchronizer & communicator, UInt root, Mesh & mesh); /// function that handles the preexisting groups in the mesh static void synchronizeNodeGroupsSlaves(ElementSynchronizer & communicator, UInt root, Mesh & mesh); template static void fillNodeGroupsFromBuffer(ElementSynchronizer & communicator, Mesh & mesh, CommunicationBuffer & buffer); /// substitute elements in the send and recv arrays void substituteElements(const std::map & old_to_new_elements); /* ------------------------------------------------------------------------ */ /* Sanity checks */ /* ------------------------------------------------------------------------ */ UInt sanityCheckDataSize(const Array & elements, const SynchronizationTag & tag) const override; /* ------------------------------------------------------------------------ */ void packSanityCheckData( CommunicationDescriptor & comm_desc) const override; /* ------------------------------------------------------------------------ */ void unpackSanityCheckData( CommunicationDescriptor & comm_desc) const override; /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: AKANTU_GET_MACRO(Mesh, mesh, Mesh &); AKANTU_GET_MACRO(ElementToRank, element_to_prank, const ElementTypeMapArray &); Int getRank(const Element & element) const final; /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ protected: /// reference to the underlying mesh Mesh & mesh; friend class FacetSynchronizer; ElementTypeMapArray element_to_prank; }; /* -------------------------------------------------------------------------- */ } // namespace akantu #endif /* __AKANTU_ELEMENT_SYNCHRONIZER_HH__ */ diff --git a/src/synchronizer/synchronizer.hh b/src/synchronizer/synchronizer.hh index 0f809526e..8f3501158 100644 --- a/src/synchronizer/synchronizer.hh +++ b/src/synchronizer/synchronizer.hh @@ -1,123 +1,125 @@ /** * @file synchronizer.hh * * @author Guillaume Anciaux * @author Aurelia Isabel Cuba Ramos * @author Nicolas Richart * * @date creation: Fri Jun 18 2010 * @date last modification: Thu Dec 10 2015 * * @brief Common interface for synchronizers * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "aka_memory.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_SYNCHRONIZER_HH__ #define __AKANTU_SYNCHRONIZER_HH__ namespace akantu { class Communicator; } namespace akantu { /* -------------------------------------------------------------------------- */ /* Base class for synchronizers */ /* -------------------------------------------------------------------------- */ class Synchronizer : protected Memory { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: Synchronizer(const Communicator & comm, const ID & id = "synchronizer", MemoryID memory_id = 0); + Synchronizer(const Synchronizer & other) = default; + ~Synchronizer() override = default; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ public: /// synchronize ghosts without state template void synchronizeOnce(DataAccessor & data_accessor, const SynchronizationTag & tag) const; /// synchronize ghosts template void synchronize(DataAccessor & data_accessor, const SynchronizationTag & tag); /// asynchronous synchronization of ghosts template void asynchronousSynchronize(const DataAccessor & data_accessor, const SynchronizationTag & tag); /// wait end of asynchronous synchronization of ghosts template void waitEndSynchronize(DataAccessor & data_accessor, const SynchronizationTag & tag); /// compute buffer size for a given tag and data accessor template void computeBufferSize(const DataAccessor & data_accessor, const SynchronizationTag & tag); /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: AKANTU_GET_MACRO(Communicator, communicator, const Communicator &); /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ protected: /// id of the synchronizer ID id; /// hashed version of the id int hash_id; /// message counter per tag std::map tag_counter; /// the static memory instance const Communicator & communicator; /// nb processors in the communicator UInt nb_proc; /// rank in the communicator UInt rank; }; } // namespace akantu #include "synchronizer_tmpl.hh" #endif /* __AKANTU_SYNCHRONIZER_HH__ */ diff --git a/src/synchronizer/synchronizer_impl.hh b/src/synchronizer/synchronizer_impl.hh index b4b87c96d..fcfedb3e8 100644 --- a/src/synchronizer/synchronizer_impl.hh +++ b/src/synchronizer/synchronizer_impl.hh @@ -1,121 +1,123 @@ /** * @file synchronizer_impl.hh * * @author Nicolas Richart * * @date Wed Aug 24 13:26:47 2016 * * @brief Implementation of the generic part of synchronizers * * @section LICENSE * * Copyright (©) 2010-2011 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "communications.hh" #include "synchronizer.hh" /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_SYNCHRONIZER_IMPL_HH__ #define __AKANTU_SYNCHRONIZER_IMPL_HH__ namespace akantu { template class SynchronizerImpl : public Synchronizer { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: SynchronizerImpl(const Communicator & communicator, const ID & id = "synchronizer", MemoryID memory_id = 0); + SynchronizerImpl(const SynchronizerImpl & other, const ID & id); + ~SynchronizerImpl() override = default; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ public: /// synchronous synchronization without state virtual void synchronizeOnceImpl(DataAccessor & data_accessor, const SynchronizationTag & tag) const; /// asynchronous synchronization of ghosts virtual void asynchronousSynchronizeImpl(const DataAccessor & data_accessor, const SynchronizationTag & tag); /// wait end of asynchronous synchronization of ghosts virtual void waitEndSynchronizeImpl(DataAccessor & data_accessor, const SynchronizationTag & tag); /// compute all buffer sizes virtual void computeAllBufferSizes(const DataAccessor & data_accessor); /// compute buffer size for a given tag and data accessor virtual void computeBufferSizeImpl(const DataAccessor & data_accessor, const SynchronizationTag & tag); /* ------------------------------------------------------------------------ */ virtual void synchronizeImpl(DataAccessor & data_accessor, const SynchronizationTag & tag) { this->asynchronousSynchronizeImpl(data_accessor, tag); this->waitEndSynchronizeImpl(data_accessor, tag); } /* ------------------------------------------------------------------------ */ /// extract the elements that have a true predicate from in_synchronizer and /// store them in the current synchronizer template void split(SynchronizerImpl & in_synchronizer, Pred && pred); /// update schemes in a synchronizer template void updateSchemes(Updater && scheme_updater); /* ------------------------------------------------------------------------ */ virtual UInt sanityCheckDataSize(const Array & elements, const SynchronizationTag & tag) const; virtual void packSanityCheckData(CommunicationDescriptor & comm_desc) const; virtual void unpackSanityCheckData(CommunicationDescriptor & comm_desc) const; public: AKANTU_GET_MACRO(Communications, communications, const Communications &); protected: AKANTU_GET_MACRO_NOT_CONST(Communications, communications, Communications &); virtual Int getRank(const Entity & entity) const = 0; /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ protected: /// information on the communications Communications communications; }; } // namespace akantu #include "synchronizer_impl_tmpl.hh" #endif /* __AKANTU_SYNCHRONIZER_IMPL_HH__ */ diff --git a/src/synchronizer/synchronizer_impl_tmpl.hh b/src/synchronizer/synchronizer_impl_tmpl.hh index 8cd7a2cdf..b8129ce99 100644 --- a/src/synchronizer/synchronizer_impl_tmpl.hh +++ b/src/synchronizer/synchronizer_impl_tmpl.hh @@ -1,305 +1,314 @@ /** * @file synchronizer_impl_tmpl.hh * * @author Nicolas Richart * * @date Wed Aug 24 13:29:47 2016 * * @brief Implementation of the SynchronizerImpl * * @section LICENSE * * Copyright (©) 2010-2011 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "synchronizer_impl.hh" /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_SYNCHRONIZER_IMPL_TMPL_HH__ #define __AKANTU_SYNCHRONIZER_IMPL_TMPL_HH__ namespace akantu { /* -------------------------------------------------------------------------- */ template -SynchronizerImpl::SynchronizerImpl(const Communicator & comm, const ID & id, MemoryID memory_id) +SynchronizerImpl::SynchronizerImpl(const Communicator & comm, + const ID & id, MemoryID memory_id) : Synchronizer(comm, id, memory_id), communications(comm) {} +/* -------------------------------------------------------------------------- */ +template +SynchronizerImpl::SynchronizerImpl(const SynchronizerImpl & other, + const ID & id) + : Synchronizer(other), communications(other.communications) { + this->id = id; +} + /* -------------------------------------------------------------------------- */ template void SynchronizerImpl::synchronizeOnceImpl( DataAccessor & data_accessor, const SynchronizationTag & tag) const { // no need to synchronize if (this->nb_proc == 1) return; using CommunicationRequests = std::vector; using CommunicationBuffers = std::map; CommunicationRequests send_requests, recv_requests; CommunicationBuffers send_buffers, recv_buffers; auto postComm = [&](const CommunicationSendRecv & sr, CommunicationBuffers & buffers, CommunicationRequests & requests) -> void { for (auto && pair : communications.iterateSchemes(sr)) { auto & proc = pair.first; const auto & scheme = pair.second; auto & buffer = buffers[proc]; UInt buffer_size = data_accessor.getNbData(scheme, tag); buffer.resize(buffer_size); if (sr == _recv) { requests.push_back(communicator.asyncReceive( buffer, proc, Tag::genTag(this->rank, 0, Tag::_SYNCHRONIZE, this->hash_id))); } else { data_accessor.packData(buffer, scheme, tag); send_requests.push_back(communicator.asyncSend( buffer, proc, Tag::genTag(proc, 0, Tag::_SYNCHRONIZE, this->hash_id))); } } }; // post the receive requests postComm(_recv, recv_buffers, recv_requests); // post the send data requests postComm(_send, send_buffers, send_requests); // treat the receive requests UInt request_ready; while ((request_ready = communicator.waitAny(recv_requests)) != UInt(-1)) { CommunicationRequest & req = recv_requests[request_ready]; UInt proc = req.getSource(); CommunicationBuffer & buffer = recv_buffers[proc]; const auto & scheme = this->communications.getScheme(proc, _recv); data_accessor.unpackData(buffer, scheme, tag); req.free(); recv_requests.erase(recv_requests.begin() + request_ready); } communicator.waitAll(send_requests); communicator.freeCommunicationRequest(send_requests); } /* -------------------------------------------------------------------------- */ template void SynchronizerImpl::asynchronousSynchronizeImpl( const DataAccessor & data_accessor, const SynchronizationTag & tag) { AKANTU_DEBUG_IN(); if (not this->communications.hasCommunicationSize(tag)) this->computeBufferSize(data_accessor, tag); this->communications.incrementCounter(tag); // Posting the receive ------------------------------------------------------- if (this->communications.hasPendingRecv(tag)) { AKANTU_CUSTOM_EXCEPTION_INFO( debug::CommunicationException(), "There must still be some pending receive communications." << " Tag is " << tag << " Cannot start new ones"); } for (auto && comm_desc : this->communications.iterateRecv(tag)) { comm_desc.postRecv(this->hash_id); } // Posting the sends ------------------------------------------------------- if (communications.hasPendingSend(tag)) { AKANTU_CUSTOM_EXCEPTION_INFO( debug::CommunicationException(), "There must be some pending sending communications." << " Tag is " << tag); } for (auto && comm_desc : this->communications.iterateSend(tag)) { comm_desc.resetBuffer(); #ifndef AKANTU_NDEBUG this->packSanityCheckData(comm_desc); #endif comm_desc.packData(data_accessor); comm_desc.postSend(this->hash_id); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void SynchronizerImpl::waitEndSynchronizeImpl( DataAccessor & data_accessor, const SynchronizationTag & tag) { AKANTU_DEBUG_IN(); #ifndef AKANTU_NDEBUG if (this->communications.begin(tag, _recv) != - this->communications.end(tag, _recv) && + this->communications.end(tag, _recv) && !this->communications.hasPendingRecv(tag)) AKANTU_CUSTOM_EXCEPTION_INFO(debug::CommunicationException(), "No pending communication with the tag \"" << tag); #endif auto recv_end = this->communications.end(tag, _recv); decltype(recv_end) recv_it; while ((recv_it = this->communications.waitAnyRecv(tag)) != recv_end) { auto && comm_desc = *recv_it; #ifndef AKANTU_NDEBUG this->unpackSanityCheckData(comm_desc); #endif comm_desc.unpackData(data_accessor); comm_desc.resetBuffer(); comm_desc.freeRequest(); } this->communications.waitAllSend(tag); this->communications.freeSendRequests(tag); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void SynchronizerImpl::computeAllBufferSizes( const DataAccessor & data_accessor) { for (auto && tag : this->communications.iterateTags()) { this->computeBufferSize(data_accessor, tag); } } /* -------------------------------------------------------------------------- */ template void SynchronizerImpl::computeBufferSizeImpl( const DataAccessor & data_accessor, const SynchronizationTag & tag) { AKANTU_DEBUG_IN(); if (not this->communications.hasCommunication(tag)) { this->communications.initializeCommunications(tag); AKANTU_DEBUG_ASSERT(communications.hasCommunication(tag) == true, "Communications where not properly initialized"); } for (auto sr : iterate_send_recv) { for (auto && pair : this->communications.iterateSchemes(sr)) { auto proc = pair.first; const auto & scheme = pair.second; UInt size = 0; #ifndef AKANTU_NDEBUG size += this->sanityCheckDataSize(scheme, tag); #endif size += data_accessor.getNbData(scheme, tag); AKANTU_DEBUG_INFO("I have " << size << "(" << printMemorySize(size) << " - " << scheme.size() << " element(s)) data to " << std::string(sr == _recv ? "receive from" : "send to") << proc << " for tag " << tag); this->communications.setCommunicationSize(tag, proc, size, sr); } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template template void SynchronizerImpl::split(SynchronizerImpl & in_synchronizer, Pred && pred) { AKANTU_DEBUG_IN(); auto filter_list = [&](auto & list, auto & new_list) { auto copy = list; list.resize(0); new_list.resize(0); for (auto && entity : copy) { if (std::forward(pred)(entity)) { new_list.push_back(entity); } else { list.push_back(entity); } } }; for (auto sr : iterate_send_recv) { for (auto & scheme_pair : in_synchronizer.communications.iterateSchemes(sr)) { auto proc = scheme_pair.first; auto & scheme = scheme_pair.second; auto & new_scheme = communications.createScheme(proc, sr); - filter_list(new_scheme, scheme); + filter_list(scheme, new_scheme); } } in_synchronizer.communications.invalidateSizes(); communications.invalidateSizes(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template template void SynchronizerImpl::updateSchemes(Updater && scheme_updater) { for (auto sr : iterate_send_recv) { for (auto & scheme_pair : communications.iterateSchemes(sr)) { auto proc = scheme_pair.first; auto & scheme = scheme_pair.second; std::forward(scheme_updater)(scheme, proc, sr); } } communications.invalidateSizes(); } /* -------------------------------------------------------------------------- */ template UInt SynchronizerImpl::sanityCheckDataSize( const Array &, const SynchronizationTag &) const { return 0; } /* -------------------------------------------------------------------------- */ template void SynchronizerImpl::packSanityCheckData( CommunicationDescriptor &) const {} /* -------------------------------------------------------------------------- */ template void SynchronizerImpl::unpackSanityCheckData( CommunicationDescriptor &) const {} /* -------------------------------------------------------------------------- */ } // namespace akantu #endif /* __AKANTU_SYNCHRONIZER_IMPL_TMPL_HH__ */ diff --git a/test/test_fe_engine/_discrete_kirchhoff_triangle_18.msh b/test/test_fe_engine/_discrete_kirchhoff_triangle_18.msh index 741047074..7422ecdb9 100644 --- a/test/test_fe_engine/_discrete_kirchhoff_triangle_18.msh +++ b/test/test_fe_engine/_discrete_kirchhoff_triangle_18.msh @@ -1,50 +1,271 @@ $MeshFormat 2.2 0 8 $EndMeshFormat $Nodes -13 +100 1 0 0 0 2 1 0 0 3 1 1 0 4 0 1 0 -5 0.4999999999990217 0 0 -6 1 0.4999999999990217 0 -7 0.5000000000013878 1 0 -8 0 0.5000000000013878 0 -9 0.5000000000000262 0.5000000000000762 0 -10 0.2500000000004626 0.7500000000004626 0 -11 0.7500000000004626 0.7499999999996739 0 -12 0.7499999999996739 0.2499999999996739 0 -13 0.24999999999961 0.2500000000004752 0 +5 0.1111111111109082 0 0 +6 0.2222222222217143 0 0 +7 0.333333333332501 0 0 +8 0.4444444444432878 0 0 +9 0.5555555555543833 0 0 +10 0.6666666666657874 0 0 +11 0.7777777777771916 0 0 +12 0.8888888888885959 0 0 +13 1 0.1111111111109082 0 +14 1 0.2222222222217143 0 +15 1 0.333333333332501 0 +16 1 0.4444444444432878 0 +17 1 0.5555555555543833 0 +18 1 0.6666666666657874 0 +19 1 0.7777777777771916 0 +20 1 0.8888888888885959 0 +21 0.8888888888884262 1 0 +22 0.777777777777932 1 0 +23 0.6666666666675918 1 0 +24 0.5555555555572513 1 0 +25 0.4444444444462943 1 0 +26 0.3333333333347207 1 0 +27 0.2222222222231471 1 0 +28 0.1111111111115736 1 0 +29 0 0.8888888888884262 0 +30 0 0.777777777777932 0 +31 0 0.6666666666675918 0 +32 0 0.5555555555572513 0 +33 0 0.4444444444462943 0 +34 0 0.3333333333347207 0 +35 0 0.2222222222231471 0 +36 0 0.1111111111115736 0 +37 0.1111111111109822 0.1111111111114996 0 +38 0.1111111111110561 0.2222222222229879 0 +39 0.11111111111113 0.3333333333344741 0 +40 0.1111111111112039 0.4444444444459603 0 +41 0.1111111111112778 0.5555555555569326 0 +42 0.1111111111113518 0.6666666666673913 0 +43 0.1111111111114257 0.7777777777778498 0 +44 0.1111111111114996 0.8888888888884451 0 +45 0.2222222222218735 0.1111111111114257 0 +46 0.2222222222220326 0.2222222222228287 0 +47 0.2222222222221919 0.3333333333342274 0 +48 0.2222222222223511 0.4444444444456261 0 +49 0.2222222222225103 0.5555555555566142 0 +50 0.2222222222226695 0.6666666666671908 0 +51 0.2222222222228287 0.7777777777777674 0 +52 0.2222222222229879 0.888888888888464 0 +53 0.3333333333327477 0.1111111111113518 0 +54 0.3333333333329943 0.2222222222226695 0 +55 0.3333333333332409 0.3333333333339809 0 +56 0.3333333333334876 0.4444444444452921 0 +57 0.3333333333337343 0.5555555555562953 0 +58 0.3333333333339809 0.6666666666669903 0 +59 0.3333333333342274 0.7777777777776852 0 +60 0.3333333333344741 0.8888888888884829 0 +61 0.4444444444436218 0.1111111111112778 0 +62 0.444444444443956 0.2222222222225103 0 +63 0.44444444444429 0.3333333333337343 0 +64 0.4444444444446239 0.4444444444449581 0 +65 0.4444444444449581 0.5555555555559767 0 +66 0.4444444444452921 0.66666666666679 0 +67 0.4444444444456261 0.7777777777776029 0 +68 0.4444444444459603 0.8888888888885014 0 +69 0.5555555555547019 0.1111111111112039 0 +70 0.5555555555550206 0.2222222222223511 0 +71 0.5555555555553394 0.3333333333334876 0 +72 0.5555555555556581 0.444444444444624 0 +73 0.5555555555559767 0.5555555555556581 0 +74 0.5555555555562953 0.6666666666665895 0 +75 0.555555555556614 0.7777777777775207 0 +76 0.5555555555569326 0.8888888888885206 0 +77 0.6666666666659877 0.11111111111113 0 +78 0.6666666666661885 0.2222222222221919 0 +79 0.6666666666663887 0.3333333333332409 0 +80 0.6666666666665892 0.44444444444429 0 +81 0.66666666666679 0.5555555555553392 0 +82 0.6666666666669905 0.6666666666663887 0 +83 0.6666666666671908 0.7777777777774383 0 +84 0.6666666666673913 0.8888888888885393 0 +85 0.7777777777772739 0.1111111111110561 0 +86 0.7777777777773561 0.2222222222220327 0 +87 0.7777777777774385 0.3333333333329943 0 +88 0.7777777777775208 0.4444444444439559 0 +89 0.7777777777776028 0.5555555555550208 0 +90 0.7777777777776852 0.6666666666661885 0 +91 0.7777777777777674 0.7777777777773561 0 +92 0.7777777777778497 0.8888888888885581 0 +93 0.888888888888577 0.1111111111109821 0 +94 0.8888888888885583 0.2222222222218735 0 +95 0.8888888888885393 0.3333333333327477 0 +96 0.8888888888885205 0.4444444444436219 0 +97 0.8888888888885017 0.5555555555547019 0 +98 0.8888888888884829 0.666666666665988 0 +99 0.888888888888464 0.777777777777274 0 +100 0.8888888888884451 0.888888888888577 0 $EndNodes $Elements -28 -1 15 2 0 101 1 -2 15 2 0 102 2 -3 15 2 0 103 3 -4 15 2 0 104 4 -5 1 2 0 101 1 5 -6 1 2 0 101 5 2 -7 1 2 0 102 2 6 -8 1 2 0 102 6 3 -9 1 2 0 103 3 7 -10 1 2 0 103 7 4 -11 1 2 0 104 4 8 -12 1 2 0 104 8 1 -13 2 2 0 101 4 10 7 -14 2 2 0 101 1 13 8 -15 2 2 0 101 3 11 6 -16 2 2 0 101 2 12 5 -17 2 2 0 101 8 9 10 -18 2 2 0 101 7 10 9 -19 2 2 0 101 7 9 11 -20 2 2 0 101 8 13 9 -21 2 2 0 101 6 11 9 -22 2 2 0 101 5 9 13 -23 2 2 0 101 5 12 9 -24 2 2 0 101 6 9 12 -25 2 2 0 101 1 5 13 -26 2 2 0 101 4 8 10 -27 2 2 0 101 2 6 12 -28 2 2 0 101 3 7 11 +162 +1 2 2 1 101 1 5 36 +2 2 2 1 101 36 5 37 +3 2 2 1 101 36 37 35 +4 2 2 1 101 35 37 38 +5 2 2 1 101 35 38 34 +6 2 2 1 101 34 38 39 +7 2 2 1 101 34 39 33 +8 2 2 1 101 33 39 40 +9 2 2 1 101 33 40 32 +10 2 2 1 101 32 40 41 +11 2 2 1 101 32 41 31 +12 2 2 1 101 31 41 42 +13 2 2 1 101 31 42 30 +14 2 2 1 101 30 42 43 +15 2 2 1 101 30 43 29 +16 2 2 1 101 29 43 44 +17 2 2 1 101 29 44 4 +18 2 2 1 101 4 44 28 +19 2 2 1 101 5 6 37 +20 2 2 1 101 37 6 45 +21 2 2 1 101 37 45 38 +22 2 2 1 101 38 45 46 +23 2 2 1 101 38 46 39 +24 2 2 1 101 39 46 47 +25 2 2 1 101 39 47 40 +26 2 2 1 101 40 47 48 +27 2 2 1 101 40 48 41 +28 2 2 1 101 41 48 49 +29 2 2 1 101 41 49 42 +30 2 2 1 101 42 49 50 +31 2 2 1 101 42 50 43 +32 2 2 1 101 43 50 51 +33 2 2 1 101 43 51 44 +34 2 2 1 101 44 51 52 +35 2 2 1 101 44 52 28 +36 2 2 1 101 28 52 27 +37 2 2 1 101 6 7 45 +38 2 2 1 101 45 7 53 +39 2 2 1 101 45 53 46 +40 2 2 1 101 46 53 54 +41 2 2 1 101 46 54 47 +42 2 2 1 101 47 54 55 +43 2 2 1 101 47 55 48 +44 2 2 1 101 48 55 56 +45 2 2 1 101 48 56 49 +46 2 2 1 101 49 56 57 +47 2 2 1 101 49 57 50 +48 2 2 1 101 50 57 58 +49 2 2 1 101 50 58 51 +50 2 2 1 101 51 58 59 +51 2 2 1 101 51 59 52 +52 2 2 1 101 52 59 60 +53 2 2 1 101 52 60 27 +54 2 2 1 101 27 60 26 +55 2 2 1 101 7 8 53 +56 2 2 1 101 53 8 61 +57 2 2 1 101 53 61 54 +58 2 2 1 101 54 61 62 +59 2 2 1 101 54 62 55 +60 2 2 1 101 55 62 63 +61 2 2 1 101 55 63 56 +62 2 2 1 101 56 63 64 +63 2 2 1 101 56 64 57 +64 2 2 1 101 57 64 65 +65 2 2 1 101 57 65 58 +66 2 2 1 101 58 65 66 +67 2 2 1 101 58 66 59 +68 2 2 1 101 59 66 67 +69 2 2 1 101 59 67 60 +70 2 2 1 101 60 67 68 +71 2 2 1 101 60 68 26 +72 2 2 1 101 26 68 25 +73 2 2 1 101 8 9 61 +74 2 2 1 101 61 9 69 +75 2 2 1 101 61 69 62 +76 2 2 1 101 62 69 70 +77 2 2 1 101 62 70 63 +78 2 2 1 101 63 70 71 +79 2 2 1 101 63 71 64 +80 2 2 1 101 64 71 72 +81 2 2 1 101 64 72 65 +82 2 2 1 101 65 72 73 +83 2 2 1 101 65 73 66 +84 2 2 1 101 66 73 74 +85 2 2 1 101 66 74 67 +86 2 2 1 101 67 74 75 +87 2 2 1 101 67 75 68 +88 2 2 1 101 68 75 76 +89 2 2 1 101 68 76 25 +90 2 2 1 101 25 76 24 +91 2 2 1 101 9 10 69 +92 2 2 1 101 69 10 77 +93 2 2 1 101 69 77 70 +94 2 2 1 101 70 77 78 +95 2 2 1 101 70 78 71 +96 2 2 1 101 71 78 79 +97 2 2 1 101 71 79 72 +98 2 2 1 101 72 79 80 +99 2 2 1 101 72 80 73 +100 2 2 1 101 73 80 81 +101 2 2 1 101 73 81 74 +102 2 2 1 101 74 81 82 +103 2 2 1 101 74 82 75 +104 2 2 1 101 75 82 83 +105 2 2 1 101 75 83 76 +106 2 2 1 101 76 83 84 +107 2 2 1 101 76 84 24 +108 2 2 1 101 24 84 23 +109 2 2 1 101 10 11 77 +110 2 2 1 101 77 11 85 +111 2 2 1 101 77 85 78 +112 2 2 1 101 78 85 86 +113 2 2 1 101 78 86 79 +114 2 2 1 101 79 86 87 +115 2 2 1 101 79 87 80 +116 2 2 1 101 80 87 88 +117 2 2 1 101 80 88 81 +118 2 2 1 101 81 88 89 +119 2 2 1 101 81 89 82 +120 2 2 1 101 82 89 90 +121 2 2 1 101 82 90 83 +122 2 2 1 101 83 90 91 +123 2 2 1 101 83 91 84 +124 2 2 1 101 84 91 92 +125 2 2 1 101 84 92 23 +126 2 2 1 101 23 92 22 +127 2 2 1 101 11 12 85 +128 2 2 1 101 85 12 93 +129 2 2 1 101 85 93 86 +130 2 2 1 101 86 93 94 +131 2 2 1 101 86 94 87 +132 2 2 1 101 87 94 95 +133 2 2 1 101 87 95 88 +134 2 2 1 101 88 95 96 +135 2 2 1 101 88 96 89 +136 2 2 1 101 89 96 97 +137 2 2 1 101 89 97 90 +138 2 2 1 101 90 97 98 +139 2 2 1 101 90 98 91 +140 2 2 1 101 91 98 99 +141 2 2 1 101 91 99 92 +142 2 2 1 101 92 99 100 +143 2 2 1 101 92 100 22 +144 2 2 1 101 22 100 21 +145 2 2 1 101 12 2 93 +146 2 2 1 101 93 2 13 +147 2 2 1 101 93 13 94 +148 2 2 1 101 94 13 14 +149 2 2 1 101 94 14 95 +150 2 2 1 101 95 14 15 +151 2 2 1 101 95 15 96 +152 2 2 1 101 96 15 16 +153 2 2 1 101 96 16 97 +154 2 2 1 101 97 16 17 +155 2 2 1 101 97 17 98 +156 2 2 1 101 98 17 18 +157 2 2 1 101 98 18 99 +158 2 2 1 101 99 18 19 +159 2 2 1 101 99 19 100 +160 2 2 1 101 100 19 20 +161 2 2 1 101 100 20 21 +162 2 2 1 101 21 20 3 $EndElements diff --git a/test/test_fe_engine/test_fe_engine_fixture.hh b/test/test_fe_engine/test_fe_engine_fixture.hh index 822bf968c..8e2ba7b74 100644 --- a/test/test_fe_engine/test_fe_engine_fixture.hh +++ b/test/test_fe_engine/test_fe_engine_fixture.hh @@ -1,84 +1,83 @@ /* -------------------------------------------------------------------------- */ #include "element_class.hh" #include "fe_engine.hh" #include "integrator_gauss.hh" #include "shape_lagrange.hh" #include "test_gtest_utils.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_TEST_FE_ENGINE_FIXTURE_HH__ #define __AKANTU_TEST_FE_ENGINE_FIXTURE_HH__ using namespace akantu; /// Generic class for FEEngine tests template class shape_t, ElementKind kind = _ek_regular> class TestFEMBaseFixture : public ::testing::Test { public: static constexpr const ElementType type = type_::value; static constexpr const size_t dim = ElementClass::getSpatialDimension(); using FEM = FEEngineTemplate; /// Setup reads mesh corresponding to element type and initializes an FEEngine void SetUp() override { const auto dim = this->dim; const auto type = this->type; mesh = std::make_unique(dim); std::stringstream meshfilename; meshfilename << type << ".msh"; this->readMesh(meshfilename.str()); lower = mesh->getLowerBounds(); upper = mesh->getUpperBounds(); nb_element = this->mesh->getNbElement(type); fem = std::make_unique(*mesh, dim, "my_fem"); - fem->initShapeFunctions(); - - nb_quadrature_points_total = fem->getNbIntegrationPoints(type) * nb_element; + nb_quadrature_points_total = + GaussIntegrationElement::getNbQuadraturePoints() * nb_element; SCOPED_TRACE(aka::to_string(type)); } void TearDown() override { fem.reset(nullptr); mesh.reset(nullptr); } /// Should be reimplemented if further treatment of the mesh is needed virtual void readMesh(std::string file_name) { mesh->read(file_name); } protected: std::unique_ptr fem; std::unique_ptr mesh; UInt nb_element; UInt nb_quadrature_points_total; Vector lower; Vector upper; }; template class shape_t, ElementKind kind> constexpr const ElementType TestFEMBaseFixture::type; template class shape_t, ElementKind kind> constexpr const size_t TestFEMBaseFixture::dim; /* -------------------------------------------------------------------------- */ /// Base class for test with Lagrange FEEngine and regular elements template using TestFEMFixture = TestFEMBaseFixture; /* -------------------------------------------------------------------------- */ using types = gtest_list_t; TYPED_TEST_CASE(TestFEMFixture, types); #endif /* __AKANTU_TEST_FE_ENGINE_FIXTURE_HH__ */ diff --git a/test/test_fe_engine/test_fe_engine_gauss_integration.cc b/test/test_fe_engine/test_fe_engine_gauss_integration.cc index 9e44bc23e..48fee7c9f 100644 --- a/test/test_fe_engine/test_fe_engine_gauss_integration.cc +++ b/test/test_fe_engine/test_fe_engine_gauss_integration.cc @@ -1,153 +1,155 @@ /** * @file test_fe_engine_precomputation.cc * * @author Nicolas Richart * * @date creation: Mon Jun 14 2010 * @date last modification: Mon Jul 13 2015 * * @brief test integration on elements, this test consider that mesh is a cube * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "test_fe_engine_fixture.hh" /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ using namespace akantu; namespace { /* -------------------------------------------------------------------------- */ template using degree_t = std::integral_constant; /* -------------------------------------------------------------------------- */ using TestDegreeTypes = std::tuple, degree_t<1>, degree_t<2>, degree_t<3>, degree_t<4>, degree_t<5>>; std::array, 3> global_polys{ {{0.40062394, 0.13703225, 0.51731446, 0.87830084, 0.5410543, 0.71842292}, {0.41861835, 0.11080576, 0.49874043, 0.49077504, 0.85073835, 0.66259755}, {0.92620845, 0.7503478, 0.62962232, 0.31662719, 0.64069644, 0.30878135}}}; template class TestGaussIntegrationFixture : public TestFEMFixture> { protected: using parent = TestFEMFixture>; static constexpr size_t degree{std::tuple_element_t<1, T>::value}; public: TestGaussIntegrationFixture() : integration_points_pos(0, parent::dim) {} void SetUp() override { parent::SetUp(); + this->fem->initShapeFunctions(); + auto integration_points = this->fem->getIntegrator().template getIntegrationPoints < parent::type, degree == 0 ? 1 : degree > (); nb_integration_points = integration_points.cols(); auto shapes_size = ElementClass::getShapeSize(); Array shapes(0, shapes_size); this->fem->getShapeFunctions() .template computeShapesOnIntegrationPoints( this->mesh->getNodes(), integration_points, shapes, _not_ghost); auto vect_size = this->nb_integration_points * this->nb_element; integration_points_pos.resize(vect_size); this->fem->getShapeFunctions() .template interpolateOnIntegrationPoints( this->mesh->getNodes(), integration_points_pos, this->dim, shapes); for (size_t d = 0; d < this->dim; ++d) { polys[d] = global_polys[d].extract(degree); } } void testIntegrate() { std::stringstream sstr; sstr << this->type << ":" << this->degree; SCOPED_TRACE(sstr.str().c_str()); auto vect_size = this->nb_integration_points * this->nb_element; Array polynomial(vect_size); size_t dim = parent::dim; for (size_t d = 0; d < dim; ++d) { auto poly = this->polys[d]; for (auto && pair : zip(polynomial, make_view(this->integration_points_pos, dim))) { auto && p = std::get<0>(pair); auto & x = std::get<1>(pair); p = poly(x(d)); } auto res = this->fem->getIntegrator() .template integrate(polynomial); auto expect = poly.integrate(this->lower(d), this->upper(d)); for (size_t o = 0; o < dim; ++o) { if (o == d) continue; expect *= this->upper(d) - this->lower(d); } EXPECT_NEAR(expect, res, 5e-14); } } protected: UInt nb_integration_points; std::array, parent::dim> polynomial; Array integration_points_pos; std::array, 3> polys; }; template constexpr size_t TestGaussIntegrationFixture::degree; /* -------------------------------------------------------------------------- */ /* Tests */ /* -------------------------------------------------------------------------- */ TYPED_TEST_CASE_P(TestGaussIntegrationFixture); TYPED_TEST_P(TestGaussIntegrationFixture, ArbitraryOrder) { this->testIntegrate(); } REGISTER_TYPED_TEST_CASE_P(TestGaussIntegrationFixture, ArbitraryOrder); using TestTypes = gtest_list_t>>; INSTANTIATE_TYPED_TEST_CASE_P(Split1, TestGaussIntegrationFixture, TestTypes); using TestTypesTail = gtest_list_t>>; INSTANTIATE_TYPED_TEST_CASE_P(Split2, TestGaussIntegrationFixture, TestTypesTail); } diff --git a/test/test_fe_engine/test_fe_engine_precomputation.cc b/test/test_fe_engine/test_fe_engine_precomputation.cc index c66054cc6..149785f5b 100644 --- a/test/test_fe_engine/test_fe_engine_precomputation.cc +++ b/test/test_fe_engine/test_fe_engine_precomputation.cc @@ -1,112 +1,113 @@ /** * @file test_fe_engine_precomputation.cc * * @author Nicolas Richart * * @date creation: Mon Jun 14 2010 * @date last modification: Mon Jul 13 2015 * * @brief test of the fem class * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "pybind11_akantu.hh" #include "test_fe_engine_fixture.hh" /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ using namespace akantu; namespace py = pybind11; using namespace py::literals; template class TestFEMPyFixture : public TestFEMFixture { using parent = TestFEMFixture; public: void SetUp() override { parent::SetUp(); const auto & connectivities = this->mesh->getConnectivity(this->type); const auto & nodes = this->mesh->getNodes().begin(this->dim); coordinates = std::make_unique>( connectivities.size(), connectivities.getNbComponent() * this->dim); for (auto && tuple : zip(make_view(connectivities, connectivities.getNbComponent()), make_view(*coordinates, this->dim, connectivities.getNbComponent()))) { const auto & conn = std::get<0>(tuple); const auto & X = std::get<1>(tuple); for (auto s : arange(conn.size())) { Vector(X(s)) = Vector(nodes[conn(s)]); } } } void TearDown() override { parent::TearDown(); coordinates.reset(nullptr); } protected: std::unique_ptr> coordinates; }; TYPED_TEST_CASE(TestFEMPyFixture, types); TYPED_TEST(TestFEMPyFixture, Precompute) { SCOPED_TRACE(aka::to_string(this->type)); + this->fem->initShapeFunctions(); const auto & N = this->fem->getShapeFunctions().getShapes(this->type); const auto & B = this->fem->getShapeFunctions().getShapesDerivatives(this->type); const auto & j = this->fem->getIntegrator().getJacobians(this->type); // Array ref_N(this->nb_quadrature_points_total, N.getNbComponent()); // Array ref_B(this->nb_quadrature_points_total, B.getNbComponent()); Array ref_j(this->nb_quadrature_points_total, j.getNbComponent()); auto ref_N(N); auto ref_B(B); py::module py_engine = py::module::import("py_engine"); auto py_shape = py_engine.attr("Shapes")(py::str(aka::to_string(this->type))); auto kwargs = py::dict( "N"_a = make_proxy(ref_N), "B"_a = make_proxy(ref_B), "j"_a = make_proxy(ref_j), "X"_a = make_proxy(*this->coordinates), "Q"_a = make_proxy( this->fem->getIntegrationPoints(this->type))); auto ret = py_shape.attr("precompute")(**kwargs); auto check = [&](auto & ref_A, auto & A, const auto & id) { SCOPED_TRACE(aka::to_string(this->type) + " " + id); for (auto && n : zip(make_view(ref_A, ref_A.getNbComponent()), make_view(A, A.getNbComponent()))) { auto diff = (std::get<0>(n) - std::get<1>(n)).template norm(); EXPECT_NEAR(0., diff, 1e-10); } }; check(ref_N, N, "N"); check(ref_B, B, "B"); check(ref_j, j, "j"); } diff --git a/test/test_fe_engine/test_fe_engine_precomputation_structural.cc b/test/test_fe_engine/test_fe_engine_precomputation_structural.cc index fc69aaa56..fc7e3b8e5 100644 --- a/test/test_fe_engine/test_fe_engine_precomputation_structural.cc +++ b/test/test_fe_engine/test_fe_engine_precomputation_structural.cc @@ -1,109 +1,126 @@ /** * @file test_fe_engine_precomputation_structural.cc * * @author Lucas Frérot * * @date creation: Fri Feb 26 2018 * * @brief test of the structural precomputations * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "fe_engine.hh" #include "integrator_gauss.hh" #include "shape_structural.hh" #include "test_fe_engine_structural_fixture.hh" /* -------------------------------------------------------------------------- */ using namespace akantu; /* -------------------------------------------------------------------------- */ // Need a special fixture for the extra normal class TestBernoulliB3 : public TestFEMStructuralFixture> { using parent = TestFEMStructuralFixture>; public: /// Load the mesh and provide extra normal direction void readMesh(std::string filename) override { parent::readMesh(filename); auto & normals = this->mesh->registerData("extra_normal") .alloc(0, dim, type, _not_ghost); Vector normal = {-36. / 65, -48. / 65, 5. / 13}; normals.push_back(normal); } }; /* -------------------------------------------------------------------------- */ /// Type alias using TestBernoulliB2 = TestFEMStructuralFixture>; + using TestDKT18 = + TestFEMStructuralFixture>; /* -------------------------------------------------------------------------- */ /// Solution for 2D rotation matrices Matrix globalToLocalRotation(Real theta) { auto c = std::cos(theta); auto s = std::sin(theta); return {{c, s, 0}, {-s, c, 0}, {0, 0, 1}}; } /* -------------------------------------------------------------------------- */ TEST_F(TestBernoulliB2, PrecomputeRotations) { + this->fem->initShapeFunctions(); using ShapeStruct = ShapeStructural<_ek_structural>; auto & shape = dynamic_cast(fem->getShapeFunctions()); auto & rot = shape.getRotations(type); Real a = std::atan(4. / 3); std::vector angles = {a, -a, 0}; Math::setTolerance(1e-15); for (auto && tuple : zip(make_view(rot, ndof, ndof), angles)) { auto rotation = std::get<0>(tuple); auto angle = std::get<1>(tuple); auto rotation_error = (rotation - globalToLocalRotation(angle)).norm(); EXPECT_NEAR(rotation_error, 0., Math::getTolerance()); } } /* -------------------------------------------------------------------------- */ TEST_F(TestBernoulliB3, PrecomputeRotations) { + this->fem->initShapeFunctions(); using ShapeStruct = ShapeStructural<_ek_structural>; auto & shape = dynamic_cast(fem->getShapeFunctions()); auto & rot = shape.getRotations(type); Matrix ref = {{3. / 13, 4. / 13, 12. / 13}, {-4. / 5, 3. / 5, 0}, {-36. / 65, -48. / 65, 5. / 13}}; Matrix solution{ndof, ndof}; solution.block(ref, 0, 0); solution.block(ref, dim, dim); // The default tolerance is too much, really Math::setTolerance(1e-15); for (auto & rotation : make_view(rot, ndof, ndof)) { auto rotation_error = (rotation - solution).norm(); EXPECT_NEAR(rotation_error, 0., Math::getTolerance()); } } + +/* -------------------------------------------------------------------------- */ +TEST_F(TestDKT18, PrecomputeRotations) { + this->fem->initShapeFunctions(); + using ShapeStruct = ShapeStructural<_ek_structural>; + auto & shape = dynamic_cast(fem->getShapeFunctions()); + auto & rot = shape.getRotations(type); + + for (auto & rotation : make_view(rot, ndof, ndof)) { + std::cout << rotation << "\n"; + } + std::cout.flush(); +} diff --git a/test/test_fe_engine/test_gradient.cc b/test/test_fe_engine/test_gradient.cc index edd30e6a9..4afb40172 100644 --- a/test/test_fe_engine/test_gradient.cc +++ b/test/test_fe_engine/test_gradient.cc @@ -1,103 +1,105 @@ /** * @file test_gradient.cc * * @author Nicolas Richart * @author Peter Spijker * * @date creation: Fri Sep 03 2010 * @date last modification: Thu Oct 15 2015 * * @brief test of the fem class * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * * @section DESCRIPTION * * This code is computing the gradient of a linear field and check that it gives * a constant result. It also compute the gradient the coordinates of the mesh * and check that it gives the identity * */ /* -------------------------------------------------------------------------- */ #include "test_fe_engine_fixture.hh" /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ using namespace akantu; namespace { TYPED_TEST(TestFEMFixture, GradientPoly) { + this->fem->initShapeFunctions(); Real alpha[2][3] = {{13, 23, 31}, {11, 7, 5}}; const auto dim = this->dim; const auto type = this->type; const auto & position = this->fem->getMesh().getNodes(); Array const_val(this->fem->getMesh().getNbNodes(), 2, "const_val"); for(auto && pair : zip(make_view(position, dim), make_view(const_val, 2))) { auto & pos = std::get<0>(pair); auto & const_ = std::get<1>(pair); const_.set(0.); for (UInt d = 0; d < dim; ++d) { const_(0) += alpha[0][d] * pos(d); const_(1) += alpha[1][d] * pos(d); } } /// compute the gradient Array grad_on_quad(this->nb_quadrature_points_total, 2 * dim, "grad_on_quad"); this->fem->gradientOnIntegrationPoints(const_val, grad_on_quad, 2, type); /// check the results for(auto && grad : make_view(grad_on_quad, 2, dim)) { for (UInt d = 0; d < dim; ++d) { EXPECT_NEAR(grad(0, d), alpha[0][d], 5e-13); EXPECT_NEAR(grad(1, d), alpha[1][d], 5e-13); } } } TYPED_TEST(TestFEMFixture, GradientPositions) { + this->fem->initShapeFunctions(); const auto dim = this->dim; const auto type = this->type; UInt nb_quadrature_points = this->fem->getNbIntegrationPoints(type) * this->nb_element; Array grad_coord_on_quad(nb_quadrature_points, dim * dim, "grad_coord_on_quad"); const auto & position = this->mesh->getNodes(); this->fem->gradientOnIntegrationPoints(position, grad_coord_on_quad, dim, type); auto I = Matrix::eye(UInt(dim)); for(auto && grad : make_view(grad_coord_on_quad, dim, dim)) { auto diff = (I - grad).template norm(); EXPECT_NEAR(0., diff, 2e-14); } } } diff --git a/test/test_fe_engine/test_integrate.cc b/test/test_fe_engine/test_integrate.cc index 40200579e..74ad310ad 100644 --- a/test/test_fe_engine/test_integrate.cc +++ b/test/test_fe_engine/test_integrate.cc @@ -1,75 +1,77 @@ /** * @file test_integrate.cc * * @author Guillaume Anciaux * @author Nicolas Richart * @author Peter Spijker * * @date creation: Fri Sep 03 2010 * @date last modification: Thu Oct 15 2015 * * @brief test of the fem class * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "test_fe_engine_fixture.hh" /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ using namespace akantu; namespace { TYPED_TEST(TestFEMFixture, IntegrateConstant) { + this->fem->initShapeFunctions(); + const auto type = this->type; const auto & position = this->fem->getMesh().getNodes(); Array const_val(position.size(), 2, "const_val"); Array val_on_quad(this->nb_quadrature_points_total, 2, "val_on_quad"); Vector value{1, 2}; for (auto && const_ : make_view(const_val, 2)) { const_ = value; } // interpolate function on quadrature points this->fem->interpolateOnIntegrationPoints(const_val, val_on_quad, 2, type); // integrate function on elements Array int_val_on_elem(this->nb_element, 2, "int_val_on_elem"); this->fem->integrate(val_on_quad, int_val_on_elem, 2, type); // get global integration value Vector sum{0., 0.}; for (auto && int_ : make_view(int_val_on_elem, 2)) { sum += int_; } auto diff = (value - sum).template norm(); EXPECT_NEAR(0, diff, 1e-14); } } // namespace diff --git a/test/test_fe_engine/test_inverse_map.cc b/test/test_fe_engine/test_inverse_map.cc index b4a0e9518..6da60b1df 100644 --- a/test/test_fe_engine/test_inverse_map.cc +++ b/test/test_fe_engine/test_inverse_map.cc @@ -1,69 +1,71 @@ /** * @file test_inverse_map.cc * * @author Guillaume Anciaux * * @date creation: Fri Sep 03 2010 * @date last modification: Thu Oct 15 2015 * * @brief test of the fem class * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "test_fe_engine_fixture.hh" /* -------------------------------------------------------------------------- */ using namespace akantu; namespace { TYPED_TEST(TestFEMFixture, InverseMap) { + this->fem->initShapeFunctions(); + Matrix quad = GaussIntegrationElement::getQuadraturePoints(); const auto & position = this->fem->getMesh().getNodes(); /// get the quadrature points coordinates Array coord_on_quad(quad.cols() * this->nb_element, this->dim, "coord_on_quad"); this->fem->interpolateOnIntegrationPoints(position, coord_on_quad, this->dim, this->type); Vector natural_coords(this->dim); auto length = (this->upper - this->lower).template norm(); for(auto && enum_ : enumerate(make_view(coord_on_quad, this->dim, quad.cols()))) { auto el = std::get<0>(enum_); const auto & quads_coords = std::get<1>(enum_); for (auto q : arange(quad.cols())) { Vector quad_coord = quads_coords(q); Vector ref_quad_coord = quad(q); this->fem->inverseMap(quad_coord, el, this->type, natural_coords); auto dis_normalized = ref_quad_coord.distance(natural_coords) / length; EXPECT_NEAR(0., dis_normalized, 5e-12); } } } } // namespace diff --git a/test/test_model/test_solid_mechanics_model/test_cohesive/CMakeLists.txt b/test/test_model/test_solid_mechanics_model/test_cohesive/CMakeLists.txt index bce3d66b5..816e1ed21 100644 --- a/test/test_model/test_solid_mechanics_model/test_cohesive/CMakeLists.txt +++ b/test/test_model/test_solid_mechanics_model/test_cohesive/CMakeLists.txt @@ -1,55 +1,110 @@ #=============================================================================== # @file CMakeLists.txt # # @author Marco Vocialta # # @date creation: Fri Oct 22 2010 # @date last modification: Thu Jan 14 2016 # # @brief configuration for cohesive elements tests # # @section LICENSE # # Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de # Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des # Solides) # # Akantu is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # # Akantu is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with Akantu. If not, see . # # @section DESCRIPTION # #=============================================================================== #add_akantu_test(test_cohesive_intrinsic "test_cohesive_intrinsic") #add_akantu_test(test_cohesive_extrinsic "test_cohesive_extrinsic") #add_akantu_test(test_cohesive_intrinsic_impl "test_cohesive_intrinsic_impl") #add_akantu_test(test_cohesive_1d_element "test_cohesive_1d_element") #add_akantu_test(test_cohesive_extrinsic_implicit "test_cohesive_extrinsic_implicit") add_akantu_test(test_cohesive_buildfragments "test_cohesive_buildfragments") add_akantu_test(test_cohesive_insertion "test_cohesive_insertion") add_akantu_test(test_cohesive_linear_friction "test_cohesive_linear_friction") #add_akantu_test(test_parallel_cohesive "parallel_cohesive_test") -add_mesh(cohesive_2d_4 data/cohesive_strait_2D.geo 2 1 OUTPUT _cohesive_2d_4.msh) -add_mesh(cohesive_2d_6 data/cohesive_strait_2D.geo 2 2 OUTPUT _cohesive_2d_6.msh) +set(_meshes) + +add_mesh(cohesive_1d_2_seg data/cohesive_1D.geo + DIM 2 ORDER 1 + OUTPUT _cohesive_1d_2_segment_2.msh) +list(APPEND _meshes cohesive_1d_2_seg) + +add_mesh(cohesive_2d_4_tri data/cohesive_strait_2D.geo + DIM 2 ORDER 1 + OUTPUT _cohesive_2d_4_triangle_3.msh) +list(APPEND _meshes cohesive_2d_4_tri) + +add_mesh(cohesive_2d_6_tri data/cohesive_strait_2D.geo + DIM 2 ORDER 2 + OUTPUT _cohesive_2d_6_triangle_6.msh) +list(APPEND _meshes cohesive_2d_6_tri) + +add_mesh(cohesive_2d_4_quad data/cohesive_strait_2D_structured.geo + DIM 2 ORDER 1 + OUTPUT _cohesive_2d_4_quadrangle_4.msh) +list(APPEND _meshes cohesive_2d_4_quad) + +add_mesh(cohesive_2d_6_quad data/cohesive_strait_2D_structured.geo + DIM 2 ORDER 2 + OUTPUT _cohesive_2d_6_quadrangle_8.msh) +list(APPEND _meshes cohesive_2d_6_quad) + +add_mesh(cohesive_2d_4_tri_quad data/cohesive_strait_2D_mixte.geo + DIM 2 ORDER 1 + OUTPUT _cohesive_2d_4_triangle_3_quadrangle_4.msh) +list(APPEND _meshes cohesive_2d_4_tri_quad) + +add_mesh(cohesive_2d_6_tri_quad data/cohesive_strait_2D_mixte.geo + DIM 2 ORDER 2 + OUTPUT _cohesive_2d_6_triangle_6_quadrangle_8.msh) +list(APPEND _meshes cohesive_2d_6_tri_quad) + +add_mesh(cohesive_3d_6_tet data/cohesive_strait_3D.geo + DIM 3 ORDER 1 + OUTPUT _cohesive_3d_6_tetrahedron_4.msh) +list(APPEND _meshes cohesive_3d_6_tet) + +add_mesh(cohesive_3d_12_tet data/cohesive_strait_3D.geo + DIM 3 ORDER 2 + OUTPUT _cohesive_3d_12_tetrahedron_10.msh) +list(APPEND _meshes cohesive_3d_12_tet) + +add_mesh(cohesive_3d_8_hex data/cohesive_strait_3D_structured.geo + DIM 3 ORDER 1 + OUTPUT _cohesive_3d_8_hexahedron_8.msh) +list(APPEND _meshes cohesive_3d_8_hex) + +add_mesh(cohesive_3d_16_hex data/cohesive_strait_3D_structured.geo + DIM 3 ORDER 2 + OUTPUT _cohesive_3d_16_hexahedron_20.msh) +list(APPEND _meshes cohesive_3d_16_hex) register_gtest_sources( SOURCES test_cohesive.cc PACKAGE cohesive_element - DEPENDS cohesive_2d_4 cohesive_2d_6 + DEPENDS ${_meshes} + FILES_TO_COPY material_0.dat material_1.dat ) register_gtest_test(test_solid_mechanics_model_cohesive) #=============================================================================== diff --git a/test/test_model/test_solid_mechanics_model/test_cohesive/data/cohesive_1D.geo b/test/test_model/test_solid_mechanics_model/test_cohesive/data/cohesive_1D.geo new file mode 100644 index 000000000..f52f56ea0 --- /dev/null +++ b/test/test_model/test_solid_mechanics_model/test_cohesive/data/cohesive_1D.geo @@ -0,0 +1,15 @@ +h = 0.5; + +Point(1) = { 1., 0., 0., h}; +Point(3) = { 0., 0., 0., h}; +Point(2) = {-1., 0., 0., h}; + +Line(1) = {2, 3}; +Line(2) = {3, 1}; + +Physical Point ("loading") = {1}; +Physical Point ("fixed") = {2}; + +Physical Point ("insertion") = {3}; + +Physical Line ("body") = {1, 2}; diff --git a/test/test_model/test_solid_mechanics_model/test_cohesive/data/cohesive_strait_2D.geo b/test/test_model/test_solid_mechanics_model/test_cohesive/data/cohesive_strait_2D.geo index be9cd3a9b..65c7a7dd0 100644 --- a/test/test_model/test_solid_mechanics_model/test_cohesive/data/cohesive_strait_2D.geo +++ b/test/test_model/test_solid_mechanics_model/test_cohesive/data/cohesive_strait_2D.geo @@ -1,29 +1,30 @@ -h = 1.; +h = 0.5; Point(1) = { 1, 1, 0, h}; Point(2) = {-1, 1, 0, h}; Point(3) = {-1,-1, 0, h}; Point(4) = { 1,-1, 0, h}; Point(5) = {-1, 0, 0, h}; Point(6) = { 1, 0, 0, h}; Line(1) = {1, 2}; Line(2) = {2, 5}; Line(3) = {5, 6}; Line(4) = {6, 1}; Line(5) = {5, 3}; Line(6) = {3, 4}; Line(7) = {4, 6}; Line Loop(1) = {1, 2, 3, 4}; Line Loop(2) = {-3, 5, 6, 7}; Plane Surface(1) = {1}; Plane Surface(2) = {2}; Physical Line("fixed") = {6}; Physical Line("loading") = {1}; Physical Line("insertion") = {3}; +Physical Line("sides") = {2, 5, 7, 4}; Physical Surface("body") = {1, 2}; diff --git a/test/test_model/test_solid_mechanics_model/test_cohesive/data/cohesive_strait_2D.geo b/test/test_model/test_solid_mechanics_model/test_cohesive/data/cohesive_strait_2D_mixte.geo similarity index 80% copy from test/test_model/test_solid_mechanics_model/test_cohesive/data/cohesive_strait_2D.geo copy to test/test_model/test_solid_mechanics_model/test_cohesive/data/cohesive_strait_2D_mixte.geo index be9cd3a9b..5550c66de 100644 --- a/test/test_model/test_solid_mechanics_model/test_cohesive/data/cohesive_strait_2D.geo +++ b/test/test_model/test_solid_mechanics_model/test_cohesive/data/cohesive_strait_2D_mixte.geo @@ -1,29 +1,34 @@ -h = 1.; +h = 0.4; Point(1) = { 1, 1, 0, h}; Point(2) = {-1, 1, 0, h}; Point(3) = {-1,-1, 0, h}; Point(4) = { 1,-1, 0, h}; Point(5) = {-1, 0, 0, h}; Point(6) = { 1, 0, 0, h}; Line(1) = {1, 2}; Line(2) = {2, 5}; Line(3) = {5, 6}; Line(4) = {6, 1}; Line(5) = {5, 3}; Line(6) = {3, 4}; Line(7) = {4, 6}; Line Loop(1) = {1, 2, 3, 4}; Line Loop(2) = {-3, 5, 6, 7}; Plane Surface(1) = {1}; Plane Surface(2) = {2}; Physical Line("fixed") = {6}; Physical Line("loading") = {1}; Physical Line("insertion") = {3}; +Physical Line("sides") = {2, 5, 7, 4}; Physical Surface("body") = {1, 2}; + +Recombine Surface {2}; +Transfinite Surface {2}; +Mesh.SecondOrderIncomplete = 1; diff --git a/test/test_model/test_solid_mechanics_model/test_cohesive/data/cohesive_strait_2D.geo b/test/test_model/test_solid_mechanics_model/test_cohesive/data/cohesive_strait_2D_structured.geo similarity index 80% copy from test/test_model/test_solid_mechanics_model/test_cohesive/data/cohesive_strait_2D.geo copy to test/test_model/test_solid_mechanics_model/test_cohesive/data/cohesive_strait_2D_structured.geo index be9cd3a9b..c571a062e 100644 --- a/test/test_model/test_solid_mechanics_model/test_cohesive/data/cohesive_strait_2D.geo +++ b/test/test_model/test_solid_mechanics_model/test_cohesive/data/cohesive_strait_2D_structured.geo @@ -1,29 +1,34 @@ -h = 1.; +h = 0.2; Point(1) = { 1, 1, 0, h}; Point(2) = {-1, 1, 0, h}; Point(3) = {-1,-1, 0, h}; Point(4) = { 1,-1, 0, h}; Point(5) = {-1, 0, 0, h}; Point(6) = { 1, 0, 0, h}; Line(1) = {1, 2}; Line(2) = {2, 5}; Line(3) = {5, 6}; Line(4) = {6, 1}; Line(5) = {5, 3}; Line(6) = {3, 4}; Line(7) = {4, 6}; Line Loop(1) = {1, 2, 3, 4}; Line Loop(2) = {-3, 5, 6, 7}; Plane Surface(1) = {1}; Plane Surface(2) = {2}; Physical Line("fixed") = {6}; Physical Line("loading") = {1}; Physical Line("insertion") = {3}; +Physical Line("sides") = {2, 5, 7, 4}; Physical Surface("body") = {1, 2}; + +Recombine Surface "*"; +Transfinite Surface "*"; +Mesh.SecondOrderIncomplete = 1; diff --git a/test/test_model/test_solid_mechanics_model/test_cohesive/data/cohesive_strait_3D.geo b/test/test_model/test_solid_mechanics_model/test_cohesive/data/cohesive_strait_3D.geo new file mode 100644 index 000000000..ad9490148 --- /dev/null +++ b/test/test_model/test_solid_mechanics_model/test_cohesive/data/cohesive_strait_3D.geo @@ -0,0 +1,33 @@ +h = 0.5; + +Point(1) = { 1, 1, -.5, h}; +Point(2) = {-1, 1, -.5, h}; +Point(3) = {-1,-1, -.5, h}; +Point(4) = { 1,-1, -.5, h}; + +Point(5) = {-1, 0, -.5, h}; +Point(6) = { 1, 0, -.5, h}; + +Line(1) = {1, 2}; +Line(2) = {2, 5}; +Line(3) = {5, 6}; +Line(4) = {6, 1}; + +Line(5) = {5, 3}; +Line(6) = {3, 4}; +Line(7) = {4, 6}; + +Line Loop(1) = {1, 2, 3, 4}; +Line Loop(2) = {-3, 5, 6, 7}; +Plane Surface(1) = {1}; +Plane Surface(2) = {2}; +Extrude {0, 0, 1} { + Surface{1}; Surface{2}; +} + +Physical Surface("fixed") = {46}; +Physical Surface("insertion") = {24}; +Physical Surface("loading") = {16}; +Physical Surface("sides") = {1, 20, 29, 28, 50, 2, 51, 42}; + +Physical Volume("body") = {1, 2}; diff --git a/test/test_model/test_solid_mechanics_model/test_cohesive/data/cohesive_strait_3D_structured.geo b/test/test_model/test_solid_mechanics_model/test_cohesive/data/cohesive_strait_3D_structured.geo new file mode 100644 index 000000000..c54ed8705 --- /dev/null +++ b/test/test_model/test_solid_mechanics_model/test_cohesive/data/cohesive_strait_3D_structured.geo @@ -0,0 +1,40 @@ +h = 0.5; + +Point(1) = { 1, 1, -.5, h}; +Point(2) = {-1, 1, -.5, h}; +Point(3) = {-1,-1, -.5, h}; +Point(4) = { 1,-1, -.5, h}; + +Point(5) = {-1, 0, -.5, h}; +Point(6) = { 1, 0, -.5, h}; + +Line(1) = {1, 2}; +Line(2) = {2, 5}; +Line(3) = {5, 6}; +Line(4) = {6, 1}; + +Line(5) = {5, 3}; +Line(6) = {3, 4}; +Line(7) = {4, 6}; + +Line Loop(1) = {1, 2, 3, 4}; +Line Loop(2) = {-3, 5, 6, 7}; +Plane Surface(1) = {1}; +Plane Surface(2) = {2}; +Extrude {0, 0, 1} { + Surface{1}; Surface{2}; +} + +Physical Surface("fixed") = {46}; +Physical Surface("insertion") = {24}; +Physical Surface("loading") = {16}; +Physical Surface("sides") = {1, 20, 29, 28, 50, 2, 51, 42}; + +Physical Volume("body") = {1, 2}; + +Transfinite Surface "*"; +Transfinite Volume "*"; + +Recombine Surface "*"; + +Mesh.SecondOrderIncomplete = 1; diff --git a/test/test_model/test_solid_mechanics_model/test_cohesive/material.dat b/test/test_model/test_solid_mechanics_model/test_cohesive/material.dat index ad4749045..5e89a3873 100644 --- a/test/test_model/test_solid_mechanics_model/test_cohesive/material.dat +++ b/test/test_model/test_solid_mechanics_model/test_cohesive/material.dat @@ -1,19 +1,19 @@ model solid_mechanics_model_cohesive [ cohesive_inserter [ cohesive_surfaces = [insertion] ] material elastic [ - name = steel + name = body rho = 1e3 # density - E = 1e3 # young's modulus - nu = 0.001 # poisson's ratio + E = 1e9 # young's modulus + nu = 0.2 # poisson's ratio ] material cohesive_linear [ name = insertion beta = 1 - G_c = 100 - sigma_c = 100 + G_c = 10 + sigma_c = 1e6 ] ] diff --git a/test/test_model/test_solid_mechanics_model/test_cohesive/material.dat b/test/test_model/test_solid_mechanics_model/test_cohesive/material_0.dat similarity index 65% copy from test/test_model/test_solid_mechanics_model/test_cohesive/material.dat copy to test/test_model/test_solid_mechanics_model/test_cohesive/material_0.dat index ad4749045..5e89a3873 100644 --- a/test/test_model/test_solid_mechanics_model/test_cohesive/material.dat +++ b/test/test_model/test_solid_mechanics_model/test_cohesive/material_0.dat @@ -1,19 +1,19 @@ model solid_mechanics_model_cohesive [ cohesive_inserter [ cohesive_surfaces = [insertion] ] material elastic [ - name = steel + name = body rho = 1e3 # density - E = 1e3 # young's modulus - nu = 0.001 # poisson's ratio + E = 1e9 # young's modulus + nu = 0.2 # poisson's ratio ] material cohesive_linear [ name = insertion beta = 1 - G_c = 100 - sigma_c = 100 + G_c = 10 + sigma_c = 1e6 ] ] diff --git a/test/test_model/test_solid_mechanics_model/test_cohesive/material.dat b/test/test_model/test_solid_mechanics_model/test_cohesive/material_1.dat similarity index 54% copy from test/test_model/test_solid_mechanics_model/test_cohesive/material.dat copy to test/test_model/test_solid_mechanics_model/test_cohesive/material_1.dat index ad4749045..1d5cc574d 100644 --- a/test/test_model/test_solid_mechanics_model/test_cohesive/material.dat +++ b/test/test_model/test_solid_mechanics_model/test_cohesive/material_1.dat @@ -1,19 +1,20 @@ model solid_mechanics_model_cohesive [ cohesive_inserter [ cohesive_surfaces = [insertion] ] material elastic [ - name = steel + name = body rho = 1e3 # density - E = 1e3 # young's modulus - nu = 0.001 # poisson's ratio + E = 1e9 # young's modulus + nu = 0.2 # poisson's ratio ] - material cohesive_linear [ + material cohesive_bilinear [ name = insertion beta = 1 - G_c = 100 - sigma_c = 100 + G_c = 10 + sigma_c = 1e6 + delta_0 = 1e-7 ] ] diff --git a/test/test_model/test_solid_mechanics_model/test_cohesive/test_cohesive.cc b/test/test_model/test_solid_mechanics_model/test_cohesive/test_cohesive.cc index 593c99e1d..043b77e70 100644 --- a/test/test_model/test_solid_mechanics_model/test_cohesive/test_cohesive.cc +++ b/test/test_model/test_solid_mechanics_model/test_cohesive/test_cohesive.cc @@ -1,50 +1,64 @@ /* -------------------------------------------------------------------------- */ -#include "test_cohesive_fixture.hh" #include "aka_iterators.hh" +#include "test_cohesive_fixture.hh" /* -------------------------------------------------------------------------- */ -TYPED_TEST(TestSMMCFixture, ModeI) { - auto max_steps = 100; - auto disp_inc = 0.1; - for(auto _ [[gnu::unused]] : arange(max_steps)) { - this->model->applyBC(BC::Dirichlet::IncrementValue(disp_inc, _x), "loading"); - if(this->is_extrinsic) - this->model->checkCohesiveStress(); +TYPED_TEST(TestSMMCFixture, ExtrinsicModeI) { + getStaticParser().parse("material_0.dat"); + this->is_extrinsic = true; + this->analysis_method = _explicit_lumped_mass; + + this->testModeI(); + + this->checkInsertion(); + + auto & mat_co = this->model->getMaterial("insertion"); + Real G_c = mat_co.get("G_c"); + + this->checkDissipated(G_c); +} + +TYPED_TEST(TestSMMCFixture, ExtrinsicModeII) { + getStaticParser().parse("material_0.dat"); + this->is_extrinsic = true; + this->analysis_method = _explicit_lumped_mass; + + this->testModeII(); + + this->checkInsertion(); + + auto & mat_co = this->model->getMaterial("insertion"); + Real G_c = mat_co.get("G_c"); + + this->checkDissipated(G_c); +} + +TYPED_TEST(TestSMMCFixture, IntrinsicModeI) { + getStaticParser().parse("material_1.dat"); + this->is_extrinsic = false; + this->analysis_method = _explicit_lumped_mass; - this->model->solveStep(); - } + this->testModeI(); - auto nb_cohesive_element = this->mesh->getNbElement(TestFixture::cohesive_type); - auto & mesh_facets = this->mesh->getMeshFacets(); - auto facet_type = mesh_facets.getFacetType(this->cohesive_type); - const auto & group = mesh_facets.getElementGroup("insertion").getElements(facet_type); + this->checkInsertion(); - std::cout << nb_cohesive_element << " " << group.size() << std::endl; + auto & mat_co = this->model->getMaterial("insertion"); + Real G_c = mat_co.get("G_c"); - Real sigma = this->model->getMaterial("insertion").get("sigma_c"); - Real edis = this->model->getEnergy("dissipated"); - EXPECT_NEAR(this->surface * sigma, edis, 1e-8); + this->checkDissipated(G_c); } -TYPED_TEST(TestSMMCFixture, ModeII) { - auto max_steps = 100; - auto disp_inc = 0.1; - for(auto _ [[gnu::unused]] : arange(max_steps)) { - this->model->applyBC(BC::Dirichlet::IncrementValue(disp_inc, _y ), "loading"); - if(this->is_extrinsic) - this->model->checkCohesiveStress(); +TYPED_TEST(TestSMMCFixture, IntrinsicModeII) { + getStaticParser().parse("material_1.dat"); + this->is_extrinsic = false; + this->analysis_method = _explicit_lumped_mass; - this->model->solveStep(); - } + this->testModeII(); - auto nb_cohesive_element = this->mesh->getNbElement(TestFixture::cohesive_type); - auto & mesh_facets = this->mesh->getMeshFacets(); - auto facet_type = mesh_facets.getFacetType(this->cohesive_type); - const auto & group = mesh_facets.getElementGroup("insertion").getElements(facet_type); + this->checkInsertion(); - std::cout << nb_cohesive_element << " " << group.size() << std::endl; + auto & mat_co = this->model->getMaterial("insertion"); + Real G_c = mat_co.get("G_c"); - Real sigma = this->model->getMaterial("insertion").get("sigma_c"); - Real edis = this->model->getEnergy("dissipated"); - EXPECT_NEAR(this->surface * sigma, edis, 1e-8); + this->checkDissipated(G_c); } 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 7888df67b..9d97a7293 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,78 +1,274 @@ /* -------------------------------------------------------------------------- */ +#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>; + 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(); - static constexpr bool is_extrinsic = std::tuple_element_t<1, param_>::value; void SetUp() override { - getStaticParser().parse(this->input_file); + normal = Vector(this->dim, 0.); + if (dim == 1) + normal(_x) = 1.; + else + normal(_y) = 1.; mesh = std::make_unique(this->dim); - EXPECT_NO_THROW({ mesh->read(this->mesh_name); }); + if (Communicator::getStaticCommunicator().whoAmI() == 0) { + EXPECT_NO_THROW({ mesh->read(this->mesh_name); }); + } + mesh->distribute(); + } + + void TearDown() override { + 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); - model->applyBC(BC::Dirichlet::FlagOnly(_x), "fixed"); - if (this->dim > 1) - model->applyBC(BC::Dirichlet::FlagOnly(_y), "fixed"); - if (this->dim > 2) - model->applyBC(BC::Dirichlet::FlagOnly(_z), "fixed"); + // auto stable_time_step = this->model->getStableTimeStep(); + this->model->setTimeStep(4e-6); + + // std::cout << stable_time_step *0.0 << std::endl; + if (dim == 1) { + surface = 1; + return; + } + + auto facet_type = mesh->getFacetType(this->cohesive_type); - auto & fe_engine = model->getFEEngine("FacetsFEEngine"); - auto & mesh_facets = fe_engine.getMesh(); - auto facet_type = mesh_facets.getFacetType(this->cohesive_type); - auto & group = - mesh_facets.getElementGroup("insertion").getElements(facet_type); + auto & fe_engine = model->getFEEngineBoundary(); + auto & group = mesh->getElementGroup("insertion").getElements(facet_type); Array ones(fe_engine.getNbIntegrationPoints(facet_type) * group.size()); ones.set(1.); surface = fe_engine.integrate(ones, facet_type, _not_ghost, group); + mesh->getCommunicator().allReduce(surface, SynchronizerOperation::_sum); } - void TearDown() override { - model.reset(nullptr); - mesh.reset(nullptr); + void setInitialCondition(const Vector & direction, Real strain) { + auto lower = this->mesh->getLowerBounds().dot(normal); + auto upper = this->mesh->getUpperBounds().dot(normal); + + Real L = upper - lower; + + 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); + + auto x = pos.dot(normal) - (upper + lower) / 2.; + disp = direction * (x * 2. * strain / L); + } + } + +#define debug 1 + + void steps(const Vector & displacement_max) { +#if debug + this->model->addDumpFieldVector("displacement"); + this->model->addDumpFieldVector("velocity"); + this->model->addDumpField("stress"); + this->model->addDumpField("strain"); + this->model->assembleInternalForces(); + this->model->setBaseNameToDumper("cohesive elements", "cohesive_elements"); + 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 + auto inc_load = BC::Dirichlet::Increment(displacement_max / Real(nb_steps)); + auto inc_fix = + BC::Dirichlet::Increment(-1. / Real(nb_steps) * displacement_max); + + for (auto _[[gnu::unused]] : arange(nb_steps)) { + this->model->applyBC(inc_load, "loading"); + this->model->applyBC(inc_fix, "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); + + auto facet_type = this->mesh->getFacetType(this->cohesive_type); + const auto & group = + this->mesh->getElementGroup("insertion").getElements(facet_type); + auto group_size = group.size(); + mesh->getCommunicator().allReduce(group_size, 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, 4e-1); + } + + void testModeI() { + Vector direction(this->dim, 0.); + if (dim == 1) + direction(_x) = 1.; + else + direction(_y) = 1.; + + // EXPECT_NO_THROW(this->createModel()); + this->createModel(); + + if (this->dim > 1) + this->model->applyBC(BC::Dirichlet::FlagOnly(_x), "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 G_c = mat_co.get("G_c"); + + auto & mat_el = this->model->getMaterial("body"); + Real E = mat_el.get("E"); + Real nu = mat_el.get("nu"); + + auto delta_c = 2. * G_c / sigma_c; + Real strain = sigma_c / E; + + if (dim == 1) + strain *= .9999; + else + strain *= .935; // there must be an error in my computations + + if (this->dim == 2) + strain *= (1. - nu) * (1. + nu); + + auto max_travel = .5 * delta_c; + this->setInitialCondition(direction, strain); + this->steps(direction * max_travel); + } + + void testModeII() { + Vector direction(this->dim, 0.); + direction(_x) = 1.; + + EXPECT_NO_THROW(this->createModel()); + + 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 G_c = mat_co.get("G_c"); + Real beta = mat_co.get("beta"); + + auto & mat_el = this->model->getMaterial("body"); + Real E = mat_el.get("E"); + Real nu = mat_el.get("nu"); + + auto L = this->mesh->getUpperBounds().dot(direction) - + this->mesh->getLowerBounds().dot(direction); + + auto delta_c = 2. * G_c / sigma_c; + Real strain = .99999 * L * beta * beta * sigma_c / E; + if (this->dim > 1) { + strain *= (1. + nu); + } + auto max_travel = 1.2 * delta_c; + this->setInitialCondition(direction, strain); + this->steps(direction * max_travel); } protected: std::unique_ptr mesh; std::unique_ptr model; - AnalysisMethod analysis_method{_explicit_lumped_mass}; - std::string mesh_name{aka::to_string(cohesive_type) + ".msh"}; - std::string input_file{"material.dat"}; + std::string mesh_name{aka::to_string(cohesive_type) + aka::to_string(type_1) + + (type_1 == type_2 ? "" : aka::to_string(type_2)) + + ".msh"}; + + bool is_extrinsic; + AnalysisMethod analysis_method; + + Vector normal; Real surface{0}; + UInt nb_steps{300}; }; /* -------------------------------------------------------------------------- */ template constexpr ElementType TestSMMCFixture::cohesive_type; +template +constexpr ElementType TestSMMCFixture::type_1; +template +constexpr ElementType TestSMMCFixture::type_2; + template constexpr size_t TestSMMCFixture::dim; -template constexpr bool TestSMMCFixture::is_extrinsic; /* -------------------------------------------------------------------------- */ using IsExtrinsicTypes = std::tuple; -using types = - gtest_list_t>; +using AnalysisMethodTypes = + std::tuple>; + +using types = gtest_list_t, element_type_t<_segment_2>, + element_type_t<_segment_2>>, + std::tuple, element_type_t<_triangle_3>, + element_type_t<_triangle_3>>, + std::tuple, element_type_t<_quadrangle_4>, + element_type_t<_quadrangle_4>>, + std::tuple, element_type_t<_triangle_3>, + element_type_t<_quadrangle_4>>, + std::tuple, element_type_t<_triangle_6>, + element_type_t<_triangle_6>>, + std::tuple, element_type_t<_quadrangle_8>, + element_type_t<_quadrangle_8>>, + std::tuple, element_type_t<_triangle_6>, + element_type_t<_quadrangle_8>>, + std::tuple, element_type_t<_tetrahedron_4>, + element_type_t<_tetrahedron_4>>, + std::tuple, element_type_t<_tetrahedron_10>, + element_type_t<_tetrahedron_10>>, + std::tuple, element_type_t<_hexahedron_8>, + element_type_t<_hexahedron_8>>, + std::tuple, element_type_t<_hexahedron_20>, + element_type_t<_hexahedron_20>>>>; TYPED_TEST_CASE(TestSMMCFixture, types); #endif /* __AKANTU_TEST_COHESIVE_FIXTURE_HH__ */ diff --git a/test/test_model/test_solid_mechanics_model/test_materials/test_damage_materials.cc b/test/test_model/test_solid_mechanics_model/test_materials/test_damage_materials.cc index ed8a61267..24445e0f4 100644 --- a/test/test_model/test_solid_mechanics_model/test_materials/test_damage_materials.cc +++ b/test/test_model/test_solid_mechanics_model/test_materials/test_damage_materials.cc @@ -1,48 +1,43 @@ /* -------------------------------------------------------------------------- */ #include "material_marigo.hh" #include "material_mazars.hh" #include "solid_mechanics_model.hh" #include "test_material_fixtures.hh" #include #include /* -------------------------------------------------------------------------- */ using namespace akantu; using types = ::testing::Types< Traits, Traits, Traits, Traits, Traits, Traits>; - /*****************************************************************/ namespace { template class TestDamageMaterialFixture : public ::TestMaterialFixture {}; TYPED_TEST_CASE(TestDamageMaterialFixture, types); TYPED_TEST(TestDamageMaterialFixture, DamageComputeStress) { this->material->testComputeStress(); } TYPED_TEST(TestDamageMaterialFixture, DamageEnergyDensity) { this->material->testEnergyDensity(); } TYPED_TEST(TestDamageMaterialFixture, DamageComputeTangentModuli) { this->material->testComputeTangentModuli(); } -TYPED_TEST(TestDamageMaterialFixture, DamageComputePushWaveSpeed) { - this->material->testPushWaveSpeed(); -} - -TYPED_TEST(TestDamageMaterialFixture, DamageComputeShearWaveSpeed) { - this->material->testShearWaveSpeed(); +TYPED_TEST(TestDamageMaterialFixture, DamageComputeCelerity) { + this->material->testCelerity(); } } /*****************************************************************/ diff --git a/test/test_model/test_solid_mechanics_model/test_materials/test_elastic_materials.cc b/test/test_model/test_solid_mechanics_model/test_materials/test_elastic_materials.cc index 98b0cb329..f89fcb12d 100644 --- a/test/test_model/test_solid_mechanics_model/test_materials/test_elastic_materials.cc +++ b/test/test_model/test_solid_mechanics_model/test_materials/test_elastic_materials.cc @@ -1,313 +1,1074 @@ /* -------------------------------------------------------------------------- */ #include "material_elastic.hh" #include "material_elastic_orthotropic.hh" #include "solid_mechanics_model.hh" #include "test_material_fixtures.hh" /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ using namespace akantu; using types = ::testing::Types, Traits, Traits, - Traits, Traits, Traits, - Traits, Traits, Traits>; /* -------------------------------------------------------------------------- */ -template <> void FriendMaterial>::testPushWaveSpeed() { +template <> void FriendMaterial>::testComputeStress() { + Real E = 3.; + setParam("E", E); + + Matrix eps = {{2}}; + Matrix sigma(1, 1); + Real sigma_th = 2; + this->computeStressOnQuad(eps, sigma, sigma_th); + + auto solution = E * eps(0, 0) + sigma_th; + ASSERT_NEAR(sigma(0, 0), solution, 1e-14); +} + +/* -------------------------------------------------------------------------- */ +template <> void FriendMaterial>::testEnergyDensity() { + Real eps = 2, sigma = 2; + Real epot = 0; + this->computePotentialEnergyOnQuad({{eps}}, {{sigma}}, epot); + Real solution = 2; + ASSERT_NEAR(epot, solution, 1e-14); +} + +/* -------------------------------------------------------------------------- */ +template <> +void FriendMaterial>::testComputeTangentModuli() { + Real E = 2; + setParam("E", E); + Matrix tangent(1, 1); + this->computeTangentModuliOnQuad(tangent); + ASSERT_NEAR(tangent(0, 0), E, 1e-14); +} + +/* -------------------------------------------------------------------------- */ +template <> void FriendMaterial>::testCelerity() { + Real E = 3., rho = 2.; + setParam("E", E); + setParam("rho", rho); + + auto wave_speed = this->getCelerity(Element()); + auto solution = std::sqrt(E / rho); + ASSERT_NEAR(wave_speed, solution, 1e-14); +} + +/* -------------------------------------------------------------------------- */ +template <> void FriendMaterial>::testComputeStress() { Real E = 1.; Real nu = .3; - Real rho = 2; + Real sigma_th = 0.3; // thermal stress setParam("E", E); setParam("nu", nu); - setParam("rho", rho); - auto wave_speed = this->getPushWaveSpeed(Element()); - Real K = E / (3 * (1 - 2 * nu)); - Real mu = E / (2 * (1 + nu)); - Real sol = std::sqrt((K + 4. / 3 * mu) / rho); + Real bulk_modulus_K = E / 3. / (1 - 2. * nu); + Real shear_modulus_mu = 0.5 * E / (1 + nu); + + Matrix rotation_matrix = getRandomRotation2d(); + + auto grad_u = this->getComposedStrain(1.).block(0, 0, 2, 2); + + auto grad_u_rot = this->applyRotation(grad_u, rotation_matrix); + + Matrix sigma_rot(2, 2); + this->computeStressOnQuad(grad_u_rot, sigma_rot, sigma_th); + + auto sigma = this->reverseRotation(sigma_rot, rotation_matrix); + + Matrix identity(2, 2); + identity.eye(); + + Matrix strain = 0.5 * (grad_u + grad_u.transpose()); + Matrix deviatoric_strain = strain - 1. / 3. * strain.trace() * identity; + + Matrix sigma_expected = 2 * shear_modulus_mu * deviatoric_strain + + (sigma_th + 2. * bulk_modulus_K) * identity; + + auto diff = sigma - sigma_expected; + Real stress_error = diff.norm(); + ASSERT_NEAR(stress_error, 0., 1e-14); +} + +/* -------------------------------------------------------------------------- */ +template <> void FriendMaterial>::testEnergyDensity() { + Matrix sigma = {{1, 2}, {2, 4}}; + Matrix eps = {{1, 0}, {0, 1}}; + Real epot = 0; + Real solution = 2.5; + this->computePotentialEnergyOnQuad(eps, sigma, epot); + ASSERT_NEAR(epot, solution, 1e-14); +} + +/* -------------------------------------------------------------------------- */ +template <> +void FriendMaterial>::testComputeTangentModuli() { + Real E = 1.; + Real nu = .3; + setParam("E", E); + setParam("nu", nu); + Matrix tangent(3, 3); + + /* Plane Strain */ + // clang-format off + Matrix solution = { + {1 - nu, nu, 0}, + {nu, 1 - nu, 0}, + {0, 0, (1 - 2 * nu) / 2}, + }; + // clang-format on + solution *= E / ((1 + nu) * (1 - 2 * nu)); + + this->computeTangentModuliOnQuad(tangent); + Real tangent_error = (tangent - solution).norm(); + ASSERT_NEAR(tangent_error, 0, 1e-14); + + /* Plane Stress */ + this->plane_stress = true; + this->updateInternalParameters(); + // clang-format off + solution = { + {1, nu, 0}, + {nu, 1, 0}, + {0, 0, (1 - nu) / 2}, + }; + // clang-format on + solution *= E / (1 - nu * nu); - ASSERT_NEAR(wave_speed, sol, 1e-14); + this->computeTangentModuliOnQuad(tangent); + tangent_error = (tangent - solution).norm(); + ASSERT_NEAR(tangent_error, 0, 1e-14); } /* -------------------------------------------------------------------------- */ -template <> void FriendMaterial>::testShearWaveSpeed() { +template <> void FriendMaterial>::testCelerity() { Real E = 1.; Real nu = .3; Real rho = 2; setParam("E", E); setParam("nu", nu); setParam("rho", rho); - auto wave_speed = this->getShearWaveSpeed(Element()); + auto push_wave_speed = this->getPushWaveSpeed(Element()); + auto celerity = this->getCelerity(Element()); + Real K = E / (3 * (1 - 2 * nu)); Real mu = E / (2 * (1 + nu)); - Real sol = std::sqrt(mu / rho); + Real sol = std::sqrt((K + 4. / 3 * mu) / rho); + + ASSERT_NEAR(push_wave_speed, sol, 1e-14); + ASSERT_NEAR(celerity, sol, 1e-14); + + auto shear_wave_speed = this->getShearWaveSpeed(Element()); - ASSERT_NEAR(wave_speed, sol, 1e-14); + sol = std::sqrt(mu / rho); + + ASSERT_NEAR(shear_wave_speed, sol, 1e-14); } /* -------------------------------------------------------------------------- */ + template <> void FriendMaterial>::testComputeStress() { Real E = 1.; Real nu = .3; Real sigma_th = 0.3; // thermal stress setParam("E", E); setParam("nu", nu); + Real bulk_modulus_K = E / 3. / (1 - 2. * nu); + Real shear_modulus_mu = 0.5 * E / (1 + nu); + Matrix rotation_matrix = getRandomRotation3d(); - auto grad_u = this->getDeviatoricStrain(1.); + auto grad_u = this->getComposedStrain(1.); auto grad_u_rot = this->applyRotation(grad_u, rotation_matrix); Matrix sigma_rot(3, 3); this->computeStressOnQuad(grad_u_rot, sigma_rot, sigma_th); auto sigma = this->reverseRotation(sigma_rot, rotation_matrix); Matrix identity(3, 3); identity.eye(); - Matrix sigma_expected = - 0.5 * E / (1 + nu) * (grad_u + grad_u.transpose()) + sigma_th * identity; + Matrix strain = 0.5 * (grad_u + grad_u.transpose()); + Matrix deviatoric_strain = strain - 1. / 3. * strain.trace() * identity; + + Matrix sigma_expected = 2 * shear_modulus_mu * deviatoric_strain + + (sigma_th + 3. * bulk_modulus_K) * identity; auto diff = sigma - sigma_expected; Real stress_error = diff.norm(); ASSERT_NEAR(stress_error, 0., 1e-14); } +/* -------------------------------------------------------------------------- */ +template <> void FriendMaterial>::testEnergyDensity() { + Matrix sigma = {{1, 2, 3}, {2, 4, 5}, {3, 5, 6}}; + Matrix eps = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}; + Real epot = 0; + Real solution = 5.5; + this->computePotentialEnergyOnQuad(eps, sigma, epot); + ASSERT_NEAR(epot, solution, 1e-14); +} + /* -------------------------------------------------------------------------- */ template <> void FriendMaterial>::testComputeTangentModuli() { Real E = 1.; Real nu = .3; setParam("E", E); setParam("nu", nu); Matrix tangent(6, 6); // clang-format off Matrix solution = { {1 - nu, nu, nu, 0, 0, 0}, {nu, 1 - nu, nu, 0, 0, 0}, {nu, nu, 1 - nu, 0, 0, 0}, {0, 0, 0, (1 - 2 * nu) / 2, 0, 0}, {0, 0, 0, 0, (1 - 2 * nu) / 2, 0}, {0, 0, 0, 0, 0, (1 - 2 * nu) / 2}, }; // clang-format on solution *= E / ((1 + nu) * (1 - 2 * nu)); this->computeTangentModuliOnQuad(tangent); Real tangent_error = (tangent - solution).norm(); ASSERT_NEAR(tangent_error, 0, 1e-14); } /* -------------------------------------------------------------------------- */ -template <> void FriendMaterial>::testEnergyDensity() { - Matrix sigma = {{1, 2, 3}, {2, 4, 5}, {3, 5, 6}}; - Matrix eps = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}; - Real epot = 0; - Real solution = 5.5; - this->computePotentialEnergyOnQuad(eps, sigma, epot); - ASSERT_NEAR(epot, solution, 1e-14); +template <> void FriendMaterial>::testCelerity() { + Real E = 1.; + Real nu = .3; + Real rho = 2; + setParam("E", E); + setParam("nu", nu); + setParam("rho", rho); + + auto push_wave_speed = this->getPushWaveSpeed(Element()); + auto celerity = this->getCelerity(Element()); + + Real K = E / (3 * (1 - 2 * nu)); + Real mu = E / (2 * (1 + nu)); + Real sol = std::sqrt((K + 4. / 3 * mu) / rho); + + ASSERT_NEAR(push_wave_speed, sol, 1e-14); + ASSERT_NEAR(celerity, sol, 1e-14); + + auto shear_wave_speed = this->getShearWaveSpeed(Element()); + + sol = std::sqrt(mu / rho); + + ASSERT_NEAR(shear_wave_speed, sol, 1e-14); } /* -------------------------------------------------------------------------- */ -template <> void FriendMaterial>::testComputeStress() { - Real E = 3.; - setParam("E", E); +template <> +void FriendMaterial>::testComputeStress() { + UInt Dim = 2; + Real E1 = 1.; + Real E2 = 2.; + Real nu12 = 0.1; + Real G12 = 2.; - Matrix eps = {{2}}; - Matrix sigma(1, 1); - Real sigma_th = 2; - this->computeStressOnQuad(eps, sigma, sigma_th); + setParamNoUpdate("E1", E1); + setParamNoUpdate("E2", E2); + setParamNoUpdate("nu12", nu12); + setParamNoUpdate("G12", G12); - auto solution = E * eps(0, 0) + sigma_th; - ASSERT_NEAR(sigma(0, 0), solution, 1e-14); + // material frame of reference is rotate by rotation_matrix starting from + // canonical basis + Matrix rotation_matrix = getRandomRotation2d(); + + // canonical basis as expressed in the material frame of reference, as + // required by MaterialElasticOrthotropic class (it is simply given by the + // columns of the rotation_matrix; the lines give the material basis expressed + // in the canonical frame of reference) + Vector v1(Dim); + Vector v2(Dim); + for (UInt i = 0; i < Dim; ++i) { + v1[i] = rotation_matrix(i, 0); + v2[i] = rotation_matrix(i, 1); + } + + setParamNoUpdate("n1", v1.normalize()); + setParamNoUpdate("n2", v2.normalize()); + + // set internal Cijkl matrix expressed in the canonical frame of reference + this->updateInternalParameters(); + + // gradient in material frame of reference + auto grad_u = this->getComposedStrain(2.).block(0, 0, 2, 2); + + // gradient in canonical basis (we need to rotate *back* to the canonical + // basis) + auto grad_u_rot = this->reverseRotation(grad_u, rotation_matrix); + + // stress in the canonical basis + Matrix sigma_rot(2, 2); + this->computeStressOnQuad(grad_u_rot, sigma_rot); + + // stress in the material reference (we need to apply the rotation) + auto sigma = this->applyRotation(sigma_rot, rotation_matrix); + + // construction of Cijkl engineering tensor in the *material* frame of + // reference + // ref: http://solidmechanics.org/Text/Chapter3_2/Chapter3_2.php#Sect3_2_13 + Real nu21 = nu12 * E2 / E1; + Real gamma = 1 / (1 - nu12 * nu21); + + Matrix C_expected(2 * Dim, 2 * Dim, 0); + C_expected(0, 0) = gamma * E1; + C_expected(1, 1) = gamma * E2; + C_expected(2, 2) = G12; + + C_expected(1, 0) = C_expected(0, 1) = gamma * E1 * nu21; + + // epsilon is computed directly in the *material* frame of reference + Matrix epsilon = 0.5 * (grad_u + grad_u.transpose()); + + // sigma_expected is computed directly in the *material* frame of reference + Matrix sigma_expected(Dim, Dim); + for (UInt i = 0; i < Dim; ++i) { + for (UInt j = 0; j < Dim; ++j) { + sigma_expected(i, i) += C_expected(i, j) * epsilon(j, j); + } + } + + sigma_expected(0, 1) = sigma_expected(1, 0) = + C_expected(2, 2) * 2 * epsilon(0, 1); + + // sigmas are checked in the *material* frame of reference + auto diff = sigma - sigma_expected; + Real stress_error = diff.norm(); + ASSERT_NEAR(stress_error, 0., 1e-13); } /* -------------------------------------------------------------------------- */ -template <> void FriendMaterial>::testEnergyDensity() { - Real eps = 2, sigma = 2; +template <> +void FriendMaterial>::testEnergyDensity() { + Matrix sigma = {{1, 2}, {2, 4}}; + Matrix eps = {{1, 0}, {0, 1}}; Real epot = 0; - this->computePotentialEnergyOnQuad({{eps}}, {{sigma}}, epot); - Real solution = 2; + Real solution = 2.5; + this->computePotentialEnergyOnQuad(eps, sigma, epot); ASSERT_NEAR(epot, solution, 1e-14); } /* -------------------------------------------------------------------------- */ template <> -void FriendMaterial>::testComputeTangentModuli() { - Real E = 2; - setParam("E", E); - Matrix tangent(1, 1); +void FriendMaterial>::testComputeTangentModuli() { + + // Note: for this test material and canonical basis coincide + Vector n1 = {1, 0}; + Vector n2 = {0, 1}; + Real E1 = 1.; + Real E2 = 2.; + Real nu12 = 0.1; + Real G12 = 2.; + + setParamNoUpdate("n1", n1); + setParamNoUpdate("n2", n2); + setParamNoUpdate("E1", E1); + setParamNoUpdate("E2", E2); + setParamNoUpdate("nu12", nu12); + setParamNoUpdate("G12", G12); + + // set internal Cijkl matrix expressed in the canonical frame of reference + this->updateInternalParameters(); + + // construction of Cijkl engineering tensor in the *material* frame of + // reference + // ref: http://solidmechanics.org/Text/Chapter3_2/Chapter3_2.php#Sect3_2_13 + Real nu21 = nu12 * E2 / E1; + Real gamma = 1 / (1 - nu12 * nu21); + + Matrix C_expected(3, 3); + C_expected(0, 0) = gamma * E1; + C_expected(1, 1) = gamma * E2; + C_expected(2, 2) = G12; + + C_expected(1, 0) = C_expected(0, 1) = gamma * E1 * nu21; + + Matrix tangent(3, 3); this->computeTangentModuliOnQuad(tangent); - ASSERT_NEAR(tangent(0, 0), E, 1e-14); + + Real tangent_error = (tangent - C_expected).norm(); + ASSERT_NEAR(tangent_error, 0, 1e-14); } /* -------------------------------------------------------------------------- */ -template <> void FriendMaterial>::testPushWaveSpeed() { - Real E = 3., rho = 2.; - setParam("E", E); - setParam("rho", rho); - auto wave_speed = this->getPushWaveSpeed(Element()); - auto solution = std::sqrt(E / rho); - ASSERT_NEAR(wave_speed, solution, 1e-14); +template <> void FriendMaterial>::testCelerity() { + + // Note: for this test material and canonical basis coincide + Vector n1 = {1, 0}; + Vector n2 = {0, 1}; + Real E1 = 1.; + Real E2 = 2.; + Real nu12 = 0.1; + Real G12 = 2.; + Real rho = 2.5; + + setParamNoUpdate("n1", n1); + setParamNoUpdate("n2", n2); + setParamNoUpdate("E1", E1); + setParamNoUpdate("E2", E2); + setParamNoUpdate("nu12", nu12); + setParamNoUpdate("G12", G12); + setParamNoUpdate("rho", rho); + + // set internal Cijkl matrix expressed in the canonical frame of reference + this->updateInternalParameters(); + + // construction of Cijkl engineering tensor in the *material* frame of + // reference + // ref: http://solidmechanics.org/Text/Chapter3_2/Chapter3_2.php#Sect3_2_13 + Real nu21 = nu12 * E2 / E1; + Real gamma = 1 / (1 - nu12 * nu21); + + Matrix C_expected(3, 3); + C_expected(0, 0) = gamma * E1; + C_expected(1, 1) = gamma * E2; + C_expected(2, 2) = G12; + + C_expected(1, 0) = C_expected(0, 1) = gamma * E1 * nu21; + + Vector eig_expected(3); + C_expected.eig(eig_expected); + + auto celerity_expected = std::sqrt(eig_expected(0) / rho); + + auto celerity = this->getCelerity(Element()); + + ASSERT_NEAR(celerity_expected, celerity, 1e-14); } /* -------------------------------------------------------------------------- */ -template <> void FriendMaterial>::testShearWaveSpeed() { - ASSERT_THROW(this->getShearWaveSpeed(Element()), debug::Exception); +template <> +void FriendMaterial>::testComputeStress() { + UInt Dim = 3; + Real E1 = 1.; + Real E2 = 2.; + Real E3 = 3.; + Real nu12 = 0.1; + Real nu13 = 0.2; + Real nu23 = 0.3; + Real G12 = 2.; + Real G13 = 3.; + Real G23 = 1.; + + setParamNoUpdate("E1", E1); + setParamNoUpdate("E2", E2); + setParamNoUpdate("E3", E3); + setParamNoUpdate("nu12", nu12); + setParamNoUpdate("nu13", nu13); + setParamNoUpdate("nu23", nu23); + setParamNoUpdate("G12", G12); + setParamNoUpdate("G13", G13); + setParamNoUpdate("G23", G23); + + // material frame of reference is rotate by rotation_matrix starting from + // canonical basis + Matrix rotation_matrix = getRandomRotation3d(); + + // canonical basis as expressed in the material frame of reference, as + // required by MaterialElasticOrthotropic class (it is simply given by the + // columns of the rotation_matrix; the lines give the material basis expressed + // in the canonical frame of reference) + Vector v1(Dim); + Vector v2(Dim); + Vector v3(Dim); + for (UInt i = 0; i < Dim; ++i) { + v1[i] = rotation_matrix(i, 0); + v2[i] = rotation_matrix(i, 1); + v3[i] = rotation_matrix(i, 2); + } + + setParamNoUpdate("n1", v1.normalize()); + setParamNoUpdate("n2", v2.normalize()); + setParamNoUpdate("n3", v3.normalize()); + + // set internal Cijkl matrix expressed in the canonical frame of reference + this->updateInternalParameters(); + + // gradient in material frame of reference + auto grad_u = this->getComposedStrain(2.); + + // gradient in canonical basis (we need to rotate *back* to the canonical + // basis) + auto grad_u_rot = this->reverseRotation(grad_u, rotation_matrix); + + // stress in the canonical basis + Matrix sigma_rot(3, 3); + this->computeStressOnQuad(grad_u_rot, sigma_rot); + + // stress in the material reference (we need to apply the rotation) + auto sigma = this->applyRotation(sigma_rot, rotation_matrix); + + // construction of Cijkl engineering tensor in the *material* frame of + // reference + // ref: http://solidmechanics.org/Text/Chapter3_2/Chapter3_2.php#Sect3_2_13 + Real nu21 = nu12 * E2 / E1; + Real nu31 = nu13 * E3 / E1; + Real nu32 = nu23 * E3 / E2; + Real gamma = 1 / (1 - nu12 * nu21 - nu23 * nu32 - nu31 * nu13 - + 2 * nu21 * nu32 * nu13); + + Matrix C_expected(6, 6); + C_expected(0, 0) = gamma * E1 * (1 - nu23 * nu32); + C_expected(1, 1) = gamma * E2 * (1 - nu13 * nu31); + C_expected(2, 2) = gamma * E3 * (1 - nu12 * nu21); + + C_expected(1, 0) = C_expected(0, 1) = gamma * E1 * (nu21 + nu31 * nu23); + C_expected(2, 0) = C_expected(0, 2) = gamma * E1 * (nu31 + nu21 * nu32); + C_expected(2, 1) = C_expected(1, 2) = gamma * E2 * (nu32 + nu12 * nu31); + + C_expected(3, 3) = G23; + C_expected(4, 4) = G13; + C_expected(5, 5) = G12; + + // epsilon is computed directly in the *material* frame of reference + Matrix epsilon = 0.5 * (grad_u + grad_u.transpose()); + + // sigma_expected is computed directly in the *material* frame of reference + Matrix sigma_expected(Dim, Dim); + for (UInt i = 0; i < Dim; ++i) { + for (UInt j = 0; j < Dim; ++j) { + sigma_expected(i, i) += C_expected(i, j) * epsilon(j, j); + } + } + + sigma_expected(0, 1) = C_expected(5, 5) * 2 * epsilon(0, 1); + sigma_expected(0, 2) = C_expected(4, 4) * 2 * epsilon(0, 2); + sigma_expected(1, 2) = C_expected(3, 3) * 2 * epsilon(1, 2); + sigma_expected(1, 0) = sigma_expected(0, 1); + sigma_expected(2, 0) = sigma_expected(0, 2); + sigma_expected(2, 1) = sigma_expected(1, 2); + + // sigmas are checked in the *material* frame of reference + auto diff = sigma - sigma_expected; + Real stress_error = diff.norm(); + ASSERT_NEAR(stress_error, 0., 1e-13); } /* -------------------------------------------------------------------------- */ -template <> void FriendMaterial>::testPushWaveSpeed() { - Real E = 1.; - Real nu = .3; - Real rho = 2; - setParam("E", E); - setParam("nu", nu); - setParam("rho", rho); - auto wave_speed = this->getPushWaveSpeed(Element()); +template <> +void FriendMaterial>::testEnergyDensity() { + Matrix sigma = {{1, 2, 3}, {2, 4, 5}, {3, 5, 6}}; + Matrix eps = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}; + Real epot = 0; + Real solution = 5.5; + this->computePotentialEnergyOnQuad(eps, sigma, epot); + ASSERT_NEAR(epot, solution, 1e-14); +} - Real K = E / (3 * (1 - 2 * nu)); - Real mu = E / (2 * (1 + nu)); - Real sol = std::sqrt((K + 4. / 3 * mu) / rho); +/* -------------------------------------------------------------------------- */ +template <> +void FriendMaterial>::testComputeTangentModuli() { + + // Note: for this test material and canonical basis coincide + UInt Dim = 3; + Vector n1 = {1, 0, 0}; + Vector n2 = {0, 1, 0}; + Vector n3 = {0, 0, 1}; + Real E1 = 1.; + Real E2 = 2.; + Real E3 = 3.; + Real nu12 = 0.1; + Real nu13 = 0.2; + Real nu23 = 0.3; + Real G12 = 2.; + Real G13 = 3.; + Real G23 = 1.; + + setParamNoUpdate("n1", n1); + setParamNoUpdate("n2", n2); + setParamNoUpdate("n3", n3); + setParamNoUpdate("E1", E1); + setParamNoUpdate("E2", E2); + setParamNoUpdate("E3", E3); + setParamNoUpdate("nu12", nu12); + setParamNoUpdate("nu13", nu13); + setParamNoUpdate("nu23", nu23); + setParamNoUpdate("G12", G12); + setParamNoUpdate("G13", G13); + setParamNoUpdate("G23", G23); + + // set internal Cijkl matrix expressed in the canonical frame of reference + this->updateInternalParameters(); + + // construction of Cijkl engineering tensor in the *material* frame of + // reference + // ref: http://solidmechanics.org/Text/Chapter3_2/Chapter3_2.php#Sect3_2_13 + Real nu21 = nu12 * E2 / E1; + Real nu31 = nu13 * E3 / E1; + Real nu32 = nu23 * E3 / E2; + Real gamma = 1 / (1 - nu12 * nu21 - nu23 * nu32 - nu31 * nu13 - + 2 * nu21 * nu32 * nu13); + + Matrix C_expected(2 * Dim, 2 * Dim, 0); + C_expected(0, 0) = gamma * E1 * (1 - nu23 * nu32); + C_expected(1, 1) = gamma * E2 * (1 - nu13 * nu31); + C_expected(2, 2) = gamma * E3 * (1 - nu12 * nu21); - ASSERT_NEAR(wave_speed, sol, 1e-14); + C_expected(1, 0) = C_expected(0, 1) = gamma * E1 * (nu21 + nu31 * nu23); + C_expected(2, 0) = C_expected(0, 2) = gamma * E1 * (nu31 + nu21 * nu32); + C_expected(2, 1) = C_expected(1, 2) = gamma * E2 * (nu32 + nu12 * nu31); + + C_expected(3, 3) = G23; + C_expected(4, 4) = G13; + C_expected(5, 5) = G12; + + Matrix tangent(6, 6); + this->computeTangentModuliOnQuad(tangent); + + Real tangent_error = (tangent - C_expected).norm(); + ASSERT_NEAR(tangent_error, 0, 1e-14); } /* -------------------------------------------------------------------------- */ -template <> void FriendMaterial>::testShearWaveSpeed() { - Real E = 1.; - Real nu = .3; - Real rho = 2; - setParam("E", E); - setParam("nu", nu); - setParam("rho", rho); - auto wave_speed = this->getShearWaveSpeed(Element()); +template <> void FriendMaterial>::testCelerity() { - Real mu = E / (2 * (1 + nu)); - Real sol = std::sqrt(mu / rho); + // Note: for this test material and canonical basis coincide + UInt Dim = 3; + Vector n1 = {1, 0, 0}; + Vector n2 = {0, 1, 0}; + Vector n3 = {0, 0, 1}; + Real E1 = 1.; + Real E2 = 2.; + Real E3 = 3.; + Real nu12 = 0.1; + Real nu13 = 0.2; + Real nu23 = 0.3; + Real G12 = 2.; + Real G13 = 3.; + Real G23 = 1.; + Real rho = 2.3; + + setParamNoUpdate("n1", n1); + setParamNoUpdate("n2", n2); + setParamNoUpdate("n3", n3); + setParamNoUpdate("E1", E1); + setParamNoUpdate("E2", E2); + setParamNoUpdate("E3", E3); + setParamNoUpdate("nu12", nu12); + setParamNoUpdate("nu13", nu13); + setParamNoUpdate("nu23", nu23); + setParamNoUpdate("G12", G12); + setParamNoUpdate("G13", G13); + setParamNoUpdate("G23", G23); + setParamNoUpdate("rho", rho); + + // set internal Cijkl matrix expressed in the canonical frame of reference + this->updateInternalParameters(); + + // construction of Cijkl engineering tensor in the *material* frame of + // reference + // ref: http://solidmechanics.org/Text/Chapter3_2/Chapter3_2.php#Sect3_2_13 + Real nu21 = nu12 * E2 / E1; + Real nu31 = nu13 * E3 / E1; + Real nu32 = nu23 * E3 / E2; + Real gamma = 1 / (1 - nu12 * nu21 - nu23 * nu32 - nu31 * nu13 - + 2 * nu21 * nu32 * nu13); + + Matrix C_expected(2 * Dim, 2 * Dim, 0); + C_expected(0, 0) = gamma * E1 * (1 - nu23 * nu32); + C_expected(1, 1) = gamma * E2 * (1 - nu13 * nu31); + C_expected(2, 2) = gamma * E3 * (1 - nu12 * nu21); + + C_expected(1, 0) = C_expected(0, 1) = gamma * E1 * (nu21 + nu31 * nu23); + C_expected(2, 0) = C_expected(0, 2) = gamma * E1 * (nu31 + nu21 * nu32); + C_expected(2, 1) = C_expected(1, 2) = gamma * E2 * (nu32 + nu12 * nu31); + + C_expected(3, 3) = G23; + C_expected(4, 4) = G13; + C_expected(5, 5) = G12; - ASSERT_NEAR(wave_speed, sol, 1e-14); + Vector eig_expected(6); + C_expected.eig(eig_expected); + + auto celerity_expected = std::sqrt(eig_expected(0) / rho); + + auto celerity = this->getCelerity(Element()); + + ASSERT_NEAR(celerity_expected, celerity, 1e-14); } /* -------------------------------------------------------------------------- */ -template <> void FriendMaterial>::testComputeStress() { - Real E = 1.; - Real nu = .3; - Real sigma_th = 0.3; // thermal stress - setParam("E", E); - setParam("nu", nu); +template <> +void FriendMaterial>::testComputeStress() { + UInt Dim = 2; + Matrix C = { + {1.0, 0.3, 0.4}, {0.3, 2.0, 0.1}, {0.4, 0.1, 1.5}, + }; + + setParamNoUpdate("C11", C(0, 0)); + setParamNoUpdate("C12", C(0, 1)); + setParamNoUpdate("C13", C(0, 2)); + setParamNoUpdate("C22", C(1, 1)); + setParamNoUpdate("C23", C(1, 2)); + setParamNoUpdate("C33", C(2, 2)); + // material frame of reference is rotate by rotation_matrix starting from + // canonical basis Matrix rotation_matrix = getRandomRotation2d(); - auto grad_u = this->getDeviatoricStrain(1.).block(0, 0, 2, 2); + // canonical basis as expressed in the material frame of reference, as + // required by MaterialElasticLinearAnisotropic class (it is simply given by + // the columns of the rotation_matrix; the lines give the material basis + // expressed in the canonical frame of reference) + Vector v1(Dim); + Vector v2(Dim); + for (UInt i = 0; i < Dim; ++i) { + v1[i] = rotation_matrix(i, 0); + v2[i] = rotation_matrix(i, 1); + } - auto grad_u_rot = this->applyRotation(grad_u, rotation_matrix); + setParamNoUpdate("n1", v1.normalize()); + setParamNoUpdate("n2", v2.normalize()); + // set internal Cijkl matrix expressed in the canonical frame of reference + this->updateInternalParameters(); + + // gradient in material frame of reference + auto grad_u = this->getComposedStrain(1.).block(0, 0, 2, 2); + + // gradient in canonical basis (we need to rotate *back* to the canonical + // basis) + auto grad_u_rot = this->reverseRotation(grad_u, rotation_matrix); + + // stress in the canonical basis Matrix sigma_rot(2, 2); - this->computeStressOnQuad(grad_u_rot, sigma_rot, sigma_th); + this->computeStressOnQuad(grad_u_rot, sigma_rot); - auto sigma = this->reverseRotation(sigma_rot, rotation_matrix); + // stress in the material reference (we need to apply the rotation) + auto sigma = this->applyRotation(sigma_rot, rotation_matrix); - Matrix identity(2, 2); - identity.eye(); + // epsilon is computed directly in the *material* frame of reference + Matrix epsilon = 0.5 * (grad_u + grad_u.transpose()); + Vector epsilon_voigt(3); + epsilon_voigt(0) = epsilon(0, 0); + epsilon_voigt(1) = epsilon(1, 1); + epsilon_voigt(2) = 2 * epsilon(0, 1); - Matrix sigma_expected = - 0.5 * E / (1 + nu) * (grad_u + grad_u.transpose()) + sigma_th * identity; + // sigma_expected is computed directly in the *material* frame of reference + Vector sigma_voigt = C * epsilon_voigt; + Matrix sigma_expected(Dim, Dim); + sigma_expected(0, 0) = sigma_voigt(0); + sigma_expected(1, 1) = sigma_voigt(1); + sigma_expected(0, 1) = sigma_expected(1, 0) = sigma_voigt(2); + // sigmas are checked in the *material* frame of reference auto diff = sigma - sigma_expected; Real stress_error = diff.norm(); - ASSERT_NEAR(stress_error, 0., 1e-14); + ASSERT_NEAR(stress_error, 0., 1e-13); } /* -------------------------------------------------------------------------- */ template <> -void FriendMaterial>::testComputeTangentModuli() { - Real E = 1.; - Real nu = .3; - setParam("E", E); - setParam("nu", nu); - Matrix tangent(3, 3); +void FriendMaterial>::testEnergyDensity() { + Matrix sigma = {{1, 2}, {2, 4}}; + Matrix eps = {{1, 0}, {0, 1}}; + Real epot = 0; + Real solution = 2.5; + this->computePotentialEnergyOnQuad(eps, sigma, epot); + ASSERT_NEAR(epot, solution, 1e-14); +} - /* Plane Strain */ - // clang-format off - Matrix solution = { - {1 - nu, nu, 0}, - {nu, 1 - nu, 0}, - {0, 0, (1 - 2 * nu) / 2}, +/* -------------------------------------------------------------------------- */ +template <> +void FriendMaterial< + MaterialElasticLinearAnisotropic<2>>::testComputeTangentModuli() { + + // Note: for this test material and canonical basis coincide + Matrix C = { + {1.0, 0.3, 0.4}, {0.3, 2.0, 0.1}, {0.4, 0.1, 1.5}, }; - // clang-format on - solution *= E / ((1 + nu) * (1 - 2 * nu)); + setParamNoUpdate("C11", C(0, 0)); + setParamNoUpdate("C12", C(0, 1)); + setParamNoUpdate("C13", C(0, 2)); + setParamNoUpdate("C22", C(1, 1)); + setParamNoUpdate("C23", C(1, 2)); + setParamNoUpdate("C33", C(2, 2)); + + // set internal Cijkl matrix expressed in the canonical frame of reference + this->updateInternalParameters(); + + Matrix tangent(3, 3); this->computeTangentModuliOnQuad(tangent); - Real tangent_error = (tangent - solution).norm(); + + Real tangent_error = (tangent - C).norm(); ASSERT_NEAR(tangent_error, 0, 1e-14); +} - /* Plane Stress */ - this->plane_stress = true; +/* -------------------------------------------------------------------------- */ +template <> +void FriendMaterial>::testCelerity() { + + // Note: for this test material and canonical basis coincide + Matrix C = { + {1.0, 0.3, 0.4}, {0.3, 2.0, 0.1}, {0.4, 0.1, 1.5}, + }; + + Real rho = 2.7; + + setParamNoUpdate("C11", C(0, 0)); + setParamNoUpdate("C12", C(0, 1)); + setParamNoUpdate("C13", C(0, 2)); + setParamNoUpdate("C22", C(1, 1)); + setParamNoUpdate("C23", C(1, 2)); + setParamNoUpdate("C33", C(2, 2)); + setParamNoUpdate("rho", rho); + + // set internal Cijkl matrix expressed in the canonical frame of reference this->updateInternalParameters(); - // clang-format off - solution = { - {1, nu, 0}, - {nu, 1, 0}, - {0, 0, (1 - nu) / 2}, + + Vector eig_expected(3); + C.eig(eig_expected); + + auto celerity_expected = std::sqrt(eig_expected(0) / rho); + + auto celerity = this->getCelerity(Element()); + + ASSERT_NEAR(celerity_expected, celerity, 1e-14); +} + +/* -------------------------------------------------------------------------- */ +template <> +void FriendMaterial>::testComputeStress() { + UInt Dim = 3; + Matrix C = { + {1.0, 0.3, 0.4, 0.3, 0.2, 0.1}, {0.3, 2.0, 0.1, 0.2, 0.3, 0.2}, + {0.4, 0.1, 1.5, 0.1, 0.4, 0.3}, {0.3, 0.2, 0.1, 2.4, 0.1, 0.4}, + {0.2, 0.3, 0.4, 0.1, 0.9, 0.1}, {0.1, 0.2, 0.3, 0.4, 0.1, 1.2}, }; - // clang-format on - solution *= E / (1 - nu * nu); - this->computeTangentModuliOnQuad(tangent); - tangent_error = (tangent - solution).norm(); - ASSERT_NEAR(tangent_error, 0, 1e-14); + setParamNoUpdate("C11", C(0, 0)); + setParamNoUpdate("C12", C(0, 1)); + setParamNoUpdate("C13", C(0, 2)); + setParamNoUpdate("C14", C(0, 3)); + setParamNoUpdate("C15", C(0, 4)); + setParamNoUpdate("C16", C(0, 5)); + setParamNoUpdate("C22", C(1, 1)); + setParamNoUpdate("C23", C(1, 2)); + setParamNoUpdate("C24", C(1, 3)); + setParamNoUpdate("C25", C(1, 4)); + setParamNoUpdate("C26", C(1, 5)); + setParamNoUpdate("C33", C(2, 2)); + setParamNoUpdate("C34", C(2, 3)); + setParamNoUpdate("C35", C(2, 4)); + setParamNoUpdate("C36", C(2, 5)); + setParamNoUpdate("C44", C(3, 3)); + setParamNoUpdate("C45", C(3, 4)); + setParamNoUpdate("C46", C(3, 5)); + setParamNoUpdate("C55", C(4, 4)); + setParamNoUpdate("C56", C(4, 5)); + setParamNoUpdate("C66", C(5, 5)); + + // material frame of reference is rotate by rotation_matrix starting from + // canonical basis + Matrix rotation_matrix = getRandomRotation3d(); + + // canonical basis as expressed in the material frame of reference, as + // required by MaterialElasticLinearAnisotropic class (it is simply given by + // the columns of the rotation_matrix; the lines give the material basis + // expressed in the canonical frame of reference) + Vector v1(Dim); + Vector v2(Dim); + Vector v3(Dim); + for (UInt i = 0; i < Dim; ++i) { + v1[i] = rotation_matrix(i, 0); + v2[i] = rotation_matrix(i, 1); + v3[i] = rotation_matrix(i, 2); + } + + setParamNoUpdate("n1", v1.normalize()); + setParamNoUpdate("n2", v2.normalize()); + setParamNoUpdate("n3", v3.normalize()); + + // set internal Cijkl matrix expressed in the canonical frame of reference + this->updateInternalParameters(); + + // gradient in material frame of reference + auto grad_u = this->getComposedStrain(2.); + + // gradient in canonical basis (we need to rotate *back* to the canonical + // basis) + auto grad_u_rot = this->reverseRotation(grad_u, rotation_matrix); + + // stress in the canonical basis + Matrix sigma_rot(3, 3); + this->computeStressOnQuad(grad_u_rot, sigma_rot); + + // stress in the material reference (we need to apply the rotation) + auto sigma = this->applyRotation(sigma_rot, rotation_matrix); + + // epsilon is computed directly in the *material* frame of reference + Matrix epsilon = 0.5 * (grad_u + grad_u.transpose()); + Vector epsilon_voigt(6); + epsilon_voigt(0) = epsilon(0, 0); + epsilon_voigt(1) = epsilon(1, 1); + epsilon_voigt(2) = epsilon(2, 2); + epsilon_voigt(3) = 2 * epsilon(1, 2); + epsilon_voigt(4) = 2 * epsilon(0, 2); + epsilon_voigt(5) = 2 * epsilon(0, 1); + + // sigma_expected is computed directly in the *material* frame of reference + Vector sigma_voigt = C * epsilon_voigt; + Matrix sigma_expected(Dim, Dim); + sigma_expected(0, 0) = sigma_voigt(0); + sigma_expected(1, 1) = sigma_voigt(1); + sigma_expected(2, 2) = sigma_voigt(2); + sigma_expected(1, 2) = sigma_expected(2, 1) = sigma_voigt(3); + sigma_expected(0, 2) = sigma_expected(2, 0) = sigma_voigt(4); + sigma_expected(0, 1) = sigma_expected(1, 0) = sigma_voigt(5); + + // sigmas are checked in the *material* frame of reference + auto diff = sigma - sigma_expected; + Real stress_error = diff.norm(); + ASSERT_NEAR(stress_error, 0., 1e-13); } /* -------------------------------------------------------------------------- */ -template <> void FriendMaterial>::testEnergyDensity() { - Matrix sigma = {{1, 2}, {2, 4}}; - Matrix eps = {{1, 0}, {0, 1}}; +template <> +void FriendMaterial>::testEnergyDensity() { + Matrix sigma = {{1, 2, 3}, {2, 4, 5}, {3, 5, 6}}; + Matrix eps = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}; Real epot = 0; - Real solution = 2.5; + Real solution = 5.5; this->computePotentialEnergyOnQuad(eps, sigma, epot); ASSERT_NEAR(epot, solution, 1e-14); } /* -------------------------------------------------------------------------- */ +template <> +void FriendMaterial< + MaterialElasticLinearAnisotropic<3>>::testComputeTangentModuli() { + + // Note: for this test material and canonical basis coincide + Matrix C = { + {1.0, 0.3, 0.4, 0.3, 0.2, 0.1}, {0.3, 2.0, 0.1, 0.2, 0.3, 0.2}, + {0.4, 0.1, 1.5, 0.1, 0.4, 0.3}, {0.3, 0.2, 0.1, 2.4, 0.1, 0.4}, + {0.2, 0.3, 0.4, 0.1, 0.9, 0.1}, {0.1, 0.2, 0.3, 0.4, 0.1, 1.2}, + }; + + setParamNoUpdate("C11", C(0, 0)); + setParamNoUpdate("C12", C(0, 1)); + setParamNoUpdate("C13", C(0, 2)); + setParamNoUpdate("C14", C(0, 3)); + setParamNoUpdate("C15", C(0, 4)); + setParamNoUpdate("C16", C(0, 5)); + setParamNoUpdate("C22", C(1, 1)); + setParamNoUpdate("C23", C(1, 2)); + setParamNoUpdate("C24", C(1, 3)); + setParamNoUpdate("C25", C(1, 4)); + setParamNoUpdate("C26", C(1, 5)); + setParamNoUpdate("C33", C(2, 2)); + setParamNoUpdate("C34", C(2, 3)); + setParamNoUpdate("C35", C(2, 4)); + setParamNoUpdate("C36", C(2, 5)); + setParamNoUpdate("C44", C(3, 3)); + setParamNoUpdate("C45", C(3, 4)); + setParamNoUpdate("C46", C(3, 5)); + setParamNoUpdate("C55", C(4, 4)); + setParamNoUpdate("C56", C(4, 5)); + setParamNoUpdate("C66", C(5, 5)); + + // set internal Cijkl matrix expressed in the canonical frame of reference + this->updateInternalParameters(); + + Matrix tangent(6, 6); + this->computeTangentModuliOnQuad(tangent); + + Real tangent_error = (tangent - C).norm(); + ASSERT_NEAR(tangent_error, 0, 1e-14); +} + +/* -------------------------------------------------------------------------- */ +template <> +void FriendMaterial>::testCelerity() { + + // Note: for this test material and canonical basis coincide + Matrix C = { + {1.0, 0.3, 0.4, 0.3, 0.2, 0.1}, {0.3, 2.0, 0.1, 0.2, 0.3, 0.2}, + {0.4, 0.1, 1.5, 0.1, 0.4, 0.3}, {0.3, 0.2, 0.1, 2.4, 0.1, 0.4}, + {0.2, 0.3, 0.4, 0.1, 0.9, 0.1}, {0.1, 0.2, 0.3, 0.4, 0.1, 1.2}, + }; + Real rho = 2.9; + + setParamNoUpdate("C11", C(0, 0)); + setParamNoUpdate("C12", C(0, 1)); + setParamNoUpdate("C13", C(0, 2)); + setParamNoUpdate("C14", C(0, 3)); + setParamNoUpdate("C15", C(0, 4)); + setParamNoUpdate("C16", C(0, 5)); + setParamNoUpdate("C22", C(1, 1)); + setParamNoUpdate("C23", C(1, 2)); + setParamNoUpdate("C24", C(1, 3)); + setParamNoUpdate("C25", C(1, 4)); + setParamNoUpdate("C26", C(1, 5)); + setParamNoUpdate("C33", C(2, 2)); + setParamNoUpdate("C34", C(2, 3)); + setParamNoUpdate("C35", C(2, 4)); + setParamNoUpdate("C36", C(2, 5)); + setParamNoUpdate("C44", C(3, 3)); + setParamNoUpdate("C45", C(3, 4)); + setParamNoUpdate("C46", C(3, 5)); + setParamNoUpdate("C55", C(4, 4)); + setParamNoUpdate("C56", C(4, 5)); + setParamNoUpdate("C66", C(5, 5)); + setParamNoUpdate("rho", rho); + + // set internal Cijkl matrix expressed in the canonical frame of reference + this->updateInternalParameters(); + + Vector eig_expected(6); + C.eig(eig_expected); + + auto celerity_expected = std::sqrt(eig_expected(0) / rho); + + auto celerity = this->getCelerity(Element()); + + ASSERT_NEAR(celerity_expected, celerity, 1e-14); +} + +/* -------------------------------------------------------------------------- */ + namespace { template class TestElasticMaterialFixture : public ::TestMaterialFixture {}; TYPED_TEST_CASE(TestElasticMaterialFixture, types); TYPED_TEST(TestElasticMaterialFixture, ElasticComputeStress) { this->material->testComputeStress(); } TYPED_TEST(TestElasticMaterialFixture, ElasticEnergyDensity) { this->material->testEnergyDensity(); } TYPED_TEST(TestElasticMaterialFixture, ElasticComputeTangentModuli) { this->material->testComputeTangentModuli(); } -TYPED_TEST(TestElasticMaterialFixture, ElasticComputePushWaveSpeed) { - this->material->testPushWaveSpeed(); +TYPED_TEST(TestElasticMaterialFixture, ElasticComputeCelerity) { + this->material->testCelerity(); } -TYPED_TEST(TestElasticMaterialFixture, ElasticComputeShearWaveSpeed) { - this->material->testShearWaveSpeed(); -} } // namespace diff --git a/test/test_model/test_solid_mechanics_model/test_materials/test_finite_def_materials.cc b/test/test_model/test_solid_mechanics_model/test_materials/test_finite_def_materials.cc index b1f0219b8..570314f6a 100644 --- a/test/test_model/test_solid_mechanics_model/test_materials/test_finite_def_materials.cc +++ b/test/test_model/test_solid_mechanics_model/test_materials/test_finite_def_materials.cc @@ -1,61 +1,57 @@ /* -------------------------------------------------------------------------- */ #include "material_neohookean.hh" #include "solid_mechanics_model.hh" #include "test_material_fixtures.hh" #include #include /* -------------------------------------------------------------------------- */ using namespace akantu; using types = ::testing::Types< Traits, Traits, Traits>; /*****************************************************************/ template <> void FriendMaterial>::testComputeStress() { TO_IMPLEMENT; } /*****************************************************************/ template <> void FriendMaterial>::testComputeTangentModuli() { TO_IMPLEMENT; } /*****************************************************************/ template <> void FriendMaterial>::testEnergyDensity() { TO_IMPLEMENT; } /*****************************************************************/ namespace { template class TestFiniteDefMaterialFixture : public ::TestMaterialFixture {}; TYPED_TEST_CASE(TestFiniteDefMaterialFixture, types); TYPED_TEST(TestFiniteDefMaterialFixture, FiniteDefComputeStress) { this->material->testComputeStress(); } TYPED_TEST(TestFiniteDefMaterialFixture, FiniteDefEnergyDensity) { this->material->testEnergyDensity(); } TYPED_TEST(TestFiniteDefMaterialFixture, FiniteDefComputeTangentModuli) { this->material->testComputeTangentModuli(); } -TYPED_TEST(TestFiniteDefMaterialFixture, FiniteDefComputePushWaveSpeed) { - this->material->testPushWaveSpeed(); -} - -TYPED_TEST(TestFiniteDefMaterialFixture, FiniteDefComputeShearWaveSpeed) { - this->material->testShearWaveSpeed(); +TYPED_TEST(TestFiniteDefMaterialFixture, FiniteDefComputeCelerity) { + this->material->testCelerity(); } } /*****************************************************************/ diff --git a/test/test_model/test_solid_mechanics_model/test_materials/test_material_fixtures.hh b/test/test_model/test_solid_mechanics_model/test_materials/test_material_fixtures.hh index eb4d93b77..97d814c89 100644 --- a/test/test_model/test_solid_mechanics_model/test_materials/test_material_fixtures.hh +++ b/test/test_model/test_solid_mechanics_model/test_materials/test_material_fixtures.hh @@ -1,171 +1,190 @@ /* -------------------------------------------------------------------------- */ #include "material_elastic.hh" #include "solid_mechanics_model.hh" /* -------------------------------------------------------------------------- */ #include #include #include /* -------------------------------------------------------------------------- */ #define TO_IMPLEMENT AKANTU_EXCEPTION("TEST TO IMPLEMENT") using namespace akantu; /* -------------------------------------------------------------------------- */ template class FriendMaterial : public T { public: ~FriendMaterial() = default; - + virtual void testComputeStress() { TO_IMPLEMENT; }; virtual void testComputeTangentModuli() { TO_IMPLEMENT; }; virtual void testEnergyDensity() { TO_IMPLEMENT; }; - virtual void testPushWaveSpeed() { TO_IMPLEMENT; } - virtual void testShearWaveSpeed() { TO_IMPLEMENT; } + virtual void testCelerity() { TO_IMPLEMENT; } static inline Matrix getDeviatoricStrain(Real intensity); - static inline Matrix getHydrostaticStrain(Real intensity); + static inline Matrix getComposedStrain(Real intensity); static inline const Matrix reverseRotation(Matrix mat, Matrix rotation_matrix) { Matrix R = rotation_matrix; Matrix m2 = mat * R; Matrix m1 = R.transpose(); return m1 * m2; }; static inline const Matrix applyRotation(Matrix mat, const Matrix rotation_matrix) { Matrix R = rotation_matrix; Matrix m2 = mat * R.transpose(); Matrix m1 = R; return m1 * m2; }; static inline Matrix getRandomRotation3d(); static inline Matrix getRandomRotation2d(); using T::T; }; /* -------------------------------------------------------------------------- */ template Matrix FriendMaterial::getDeviatoricStrain(Real intensity) { Matrix dev = {{0, 1, 2}, {1, 0, 3}, {2, 3, 0}}; dev *= intensity; return dev; } /* -------------------------------------------------------------------------- */ template Matrix FriendMaterial::getHydrostaticStrain(Real intensity) { - Matrix dev = {{1, 0, 0}, {0, 2, 0}, {0, 0, 3}}; + Matrix dev = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}; dev *= intensity; return dev; } +/* -------------------------------------------------------------------------- */ + +template +Matrix FriendMaterial::getComposedStrain(Real intensity) { + Matrix s = FriendMaterial::getHydrostaticStrain(intensity) + + FriendMaterial::getDeviatoricStrain(intensity); + s *= intensity; + return s; +} + /* -------------------------------------------------------------------------- */ template Matrix FriendMaterial::getRandomRotation3d() { constexpr UInt Dim = 3; // Seed with a real random value, if available std::random_device rd; std::mt19937 gen(rd()); // Standard mersenne_twister_engine seeded with rd() std::uniform_real_distribution<> dis; Vector v1(Dim); Vector v2(Dim); Vector v3(Dim); for (UInt i = 0; i < Dim; ++i) { v1(i) = dis(gen); v2(i) = dis(gen); } v1.normalize(); v2.normalize(); auto d = v1.dot(v2); v2 -= v1 * d; v2.normalize(); d = v1.dot(v2); v2 -= v1 * d; v2.normalize(); - v3.crossProduct(v2, v1); + v3.crossProduct(v1, v2); + + Vector test_axis(3); + test_axis.crossProduct(v1, v2); + test_axis -= v3; + if (test_axis.norm() > 8 * std::numeric_limits::epsilon()) { + AKANTU_DEBUG_ERROR("The axis vectors do not form a right-handed coordinate " + << "system. I. e., ||n1 x n2 - n3|| should be zero, but " + << "it is " << test_axis.norm() << "."); + } Matrix mat(Dim, Dim); for (UInt i = 0; i < Dim; ++i) { mat(0, i) = v1[i]; mat(1, i) = v2[i]; mat(2, i) = v3[i]; } return mat; } /* -------------------------------------------------------------------------- */ template Matrix FriendMaterial::getRandomRotation2d() { constexpr UInt Dim = 2; // Seed with a real random value, if available std::random_device rd; std::mt19937 gen(rd()); // Standard mersenne_twister_engine seeded with rd() std::uniform_real_distribution<> dis; - Vector v1(Dim); - Vector v2(Dim); + // v1 and v2 are such that they form a right-hand basis with prescribed v3, + // it's need (at least) for 2d linear elastic anisotropic and (orthotropic) + // materials to rotate the tangent stiffness + + Vector v1(3); + Vector v2(3); + Vector v3 = {0, 0, 1}; for (UInt i = 0; i < Dim; ++i) { v1(i) = dis(gen); - v2(i) = dis(gen); + // v2(i) = dis(gen); } v1.normalize(); - v2.normalize(); - - auto d = v1.dot(v2); - v2 -= v1 * d; - v2.normalize(); + v2.crossProduct(v3, v1); Matrix mat(Dim, Dim); for (UInt i = 0; i < Dim; ++i) { mat(0, i) = v1[i]; mat(1, i) = v2[i]; } return mat; } /* -------------------------------------------------------------------------- */ template class TestMaterialFixture : public ::testing::Test { public: using mat_class = typename T::mat_class; void SetUp() override { constexpr auto spatial_dimension = T::Dim; mesh = std::make_unique(spatial_dimension); model = std::make_unique(*mesh); material = std::make_unique(*model); } void TearDown() override { material.reset(nullptr); model.reset(nullptr); mesh.reset(nullptr); } using friend_class = FriendMaterial; protected: std::unique_ptr mesh; std::unique_ptr model; std::unique_ptr material; }; /* -------------------------------------------------------------------------- */ template