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 <guillaume.anciaux@epfl.ch> # @author Nicolas Richart <nicolas.richart@epfl.ch> # # @date creation: Mon Nov 21 2011 # @date last modification: Mon Jan 18 2016 # # @brief package description for core # # @section LICENSE # # Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de # Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des # Solides) # # Akantu is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # # Akantu is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with Akantu. If not, see <http://www.gnu.org/licenses/>. # #=============================================================================== package_declare(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_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 <nicolas.richart@epfl.ch> * * @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 <http://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #include "aka_error.hh" #include "aka_fwd.hh" #include "aka_math.hh" /* -------------------------------------------------------------------------- */ #include <initializer_list> #include <iomanip> #include <type_traits> /* -------------------------------------------------------------------------- */ #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 <UInt dim> 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 <typename T, UInt ndim, class RetType> class TensorStorage; /* -------------------------------------------------------------------------- */ /* Proxy classes */ /* -------------------------------------------------------------------------- */ namespace tensors { template <class A, class B> struct is_copyable { enum : bool { value = false }; }; template <class A> struct is_copyable<A, A> { enum : bool { value = true }; }; template <class A> struct is_copyable<A, typename A::RetType> { enum : bool { value = true }; }; template <class A> struct is_copyable<A, typename A::RetType::proxy> { 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<T>::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 <typename T, UInt ndim, class _RetType> class TensorProxy { protected: using RetTypeProxy = typename _RetType::proxy; constexpr TensorProxy(T * data, UInt m, UInt n, UInt p) { DimHelper<ndim>::setDims(m, n, p, this->n); this->values = data; } #ifndef SWIG template <class Other, typename = std::enable_if_t< tensors::is_copyable<TensorProxy, Other>::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 <class Other, typename = std::enable_if_t< tensors::is_copyable<TensorProxy, Other>::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 <class Other, typename = std::enable_if_t< // tensors::is_copyable<TensorProxy, Other>::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 <typename O> inline RetTypeProxy & operator*=(const O & o) { RetType(*this) *= o; return static_cast<RetTypeProxy &>(*this); } template <typename O> inline RetTypeProxy & operator/=(const O & o) { RetType(*this) /= o; return static_cast<RetTypeProxy &>(*this); } protected: T * values; UInt n[ndim]; }; /* -------------------------------------------------------------------------- */ template <typename T> class VectorProxy : public TensorProxy<T, 1, Vector<T>> { using parent = TensorProxy<T, 1, Vector<T>>; using type = Vector<T>; public: constexpr VectorProxy(T * data, UInt n) : parent(data, n, 0, 0) {} template <class Other> explicit VectorProxy(Other & src) : parent(src) {} /* ---------------------------------------------------------------------- */ template <class Other> inline VectorProxy<T> & operator=(const Other & other) { parent::operator=(other); return *this; } // inline VectorProxy<T> & 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 <typename T> class MatrixProxy : public TensorProxy<T, 2, Matrix<T>> { using parent = TensorProxy<T, 2, Matrix<T>>; using type = Matrix<T>; public: MatrixProxy(T * data, UInt m, UInt n) : parent(data, m, n, 0) {} template <class Other> explicit MatrixProxy(Other & src) : parent(src) {} /* ---------------------------------------------------------------------- */ template <class Other> inline MatrixProxy<T> & operator=(const Other & other) { parent::operator=(other); return *this; } }; template <typename T> class Tensor3Proxy : public TensorProxy<T, 3, Tensor3<T>> { using parent = TensorProxy<T, 3, Tensor3<T>>; using type = Tensor3<T>; 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<T> & src) : parent(src) {} /* ---------------------------------------------------------------------- */ template <class Other> inline Tensor3Proxy<T> & operator=(const Other & other) { parent::operator=(other); return *this; } }; /* -------------------------------------------------------------------------- */ /* Tensor base class */ /* -------------------------------------------------------------------------- */ template <typename T, UInt ndim, class RetType> class TensorStorage : public TensorTrait { public: using value_type = T; friend class Array<T>; protected: template <class TensorType> 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<T, ndim, RetType> & 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<T>{}, "Cannot create a tensor on non trivial types"); DimHelper<ndim>::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<ndim>::setDims(m, n, p, this->n); this->computeSize(); this->values = data; this->wrapped = true; } public: /* ------------------------------------------------------------------------ */ template <class TensorType> inline void shallowCopy(const TensorType & src) { this->copySize(src); if (!this->wrapped) delete[] this->values; this->values = src.storage(); this->wrapped = true; } /* ------------------------------------------------------------------------ */ template <class TensorType> inline void deepCopy(const TensorType & src) { this->copySize(src); if (!this->wrapped) delete[] this->values; static_assert(std::is_trivially_constructible<T>{}, "Cannot create a tensor on non trivial types"); this->values = new T[this->_size]; static_assert(std::is_trivially_copyable<T>{}, "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<RetType &>(src)); } /* ------------------------------------------------------------------------ */ inline TensorStorage & operator=(const RetType & src) { if (this != &src) { if (this->wrapped) { static_assert(std::is_trivially_copyable<T>{}, "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 <class R> inline RetType & operator+=(const TensorStorage<T, ndim, R> & 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<RetType *>(this)); } /* ------------------------------------------------------------------------ */ template <class R> inline RetType & operator-=(const TensorStorage<T, ndim, R> & 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<RetType *>(this)); } /* ------------------------------------------------------------------------ */ inline RetType & operator+=(const T & x) { T * a = this->values; for (UInt i = 0; i < _size; ++i) *(a++) += x; return *(static_cast<RetType *>(this)); } /* ------------------------------------------------------------------------ */ inline RetType & operator-=(const T & x) { T * a = this->values; for (UInt i = 0; i < _size; ++i) *(a++) -= x; return *(static_cast<RetType *>(this)); } /* ------------------------------------------------------------------------ */ inline RetType & operator*=(const T & x) { T * a = this->storage(); for (UInt i = 0; i < _size; ++i) *(a++) *= x; return *(static_cast<RetType *>(this)); } /* ---------------------------------------------------------------------- */ inline RetType & operator/=(const T & x) { T * a = this->values; for (UInt i = 0; i < _size; ++i) *(a++) /= x; return *(static_cast<RetType *>(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<RetType *>(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 <class TensorType> 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 <typename R, NormType norm_type> struct NormHelper { template <class Ten> 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 <typename R> struct NormHelper<R, L_1> { template <class Ten> 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 <typename R> struct NormHelper<R, L_2> { template <class Ten> 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 <typename R> struct NormHelper<R, L_inf> { template <class Ten> 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<L_p> @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 <NormType norm_type> inline T norm() const { return NormHelper<T, norm_type>::norm(*this); } protected: UInt n[ndim]; UInt _size; T * values; bool wrapped{false}; }; /* -------------------------------------------------------------------------- */ /* Vector */ /* -------------------------------------------------------------------------- */ template <typename T> class Vector : public TensorStorage<T, 1, Vector<T>> { using parent = TensorStorage<T, 1, Vector<T>>; public: using value_type = typename parent::value_type; using proxy = VectorProxy<T>; 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<T, 1, Vector> & src) : parent(src) {} Vector(std::initializer_list<T> 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<T> & operator*=(Real x) { return parent::operator*=(x); } inline Vector<T> & operator/=(Real x) { return parent::operator/=(x); } /* ------------------------------------------------------------------------ */ inline Vector<T> & operator*=(const Vector<T> & 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<T> & 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<T> & v1, const Vector<T> & 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<T> & v) { Vector<T> tmp(this->size()); tmp.crossProduct(*this, v); return tmp; } /* ------------------------------------------------------------------------ */ inline void solve(const Matrix<T> & A, const Vector<T> & 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 <bool tr_A> inline void mul(const Matrix<T> & A, const Vector<T> & x, Real alpha = 1.0); /* ------------------------------------------------------------------------ */ inline Real norm() const { return parent::template norm<L_2>(); } template <NormType nt> inline Real norm() const { return parent::template norm<nt>(); } /* ------------------------------------------------------------------------ */ inline Vector<Real> & normalize() { Real n = norm(); operator/=(n); return *this; } /* ------------------------------------------------------------------------ */ /// norm of (*this - x) inline Real distance(const Vector<T> & 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<T> & 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<T> & 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<T> & v) const { return equal(v); } inline bool operator!=(const Vector<T> & v) const { return !operator==(v); } inline bool operator<(const Vector<T> & v) const { return compare(v) == -1; } inline bool operator>(const Vector<T> & 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<T>; }; using RVector = Vector<Real>; /* ------------------------------------------------------------------------ */ template <> inline bool Vector<UInt>::equal(const Vector<UInt> & 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 <typename T> class Matrix : public TensorStorage<T, 2, Matrix<T>> { using parent = TensorStorage<T, 2, Matrix<T>>; public: using value_type = typename parent::value_type; using proxy = MatrixProxy<T>; 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<T> & src) : parent(src) {} Matrix(std::initializer_list<std::initializer_list<T>> list) { static_assert(std::is_trivially_copyable<T>{}, "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<T> 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<T>(this->values + j * this->n[0], this->n[0]); } inline const VectorProxy<T> 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<T>(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<false, false>(*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<false, false>(C, B); return *this; } /* ---------------------------------------------------------------------- */ template <bool tr_A, bool tr_B> 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<tr_A, tr_B>(this->rows(), this->cols(), k, alpha, A.storage(), B.storage(), 0., this->storage()); } /* ---------------------------------------------------------------------- */ inline void outerProduct(const Vector<T> & A, const Vector<T> & 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<T> & eigs) : eigs(eigs) {} bool operator()(const UInt & a, const UInt & b) const { return (eigs(a) > eigs(b)); } private: const Vector<T> & eigs; }; public: /* ---------------------------------------------------------------------- */ inline void eig(Vector<T> & eigenvalues, Matrix<T> & 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<T> tmp = *this; Vector<T> tmp_eigs(eigenvalues.size()); Matrix<T> 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<UInt> 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<T> & eigenvalues) const { Matrix<T> 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<T> eye(UInt m, T alpha = 1.) { Matrix<T> 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<T> & 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 <typename T> template <bool tr_A> inline void Vector<T>::mul(const Matrix<T> & A, const Vector<T> & 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<tr_A>(A.rows(), A.cols(), alpha, A.storage(), x.storage(), 0., this->storage()); } /* -------------------------------------------------------------------------- */ template <typename T> inline std::ostream & operator<<(std::ostream & stream, const Matrix<T> & _this) { _this.printself(stream); return stream; } /* -------------------------------------------------------------------------- */ template <typename T> inline std::ostream & operator<<(std::ostream & stream, const Vector<T> & _this) { _this.printself(stream); return stream; } /* ------------------------------------------------------------------------ */ /* Tensor3 */ /* ------------------------------------------------------------------------ */ template <typename T> class Tensor3 : public TensorStorage<T, 3, Tensor3<T>> { using parent = TensorStorage<T, 3, Tensor3<T>>; public: using value_type = typename parent::value_type; using proxy = Tensor3Proxy<T>; 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<T> 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<T>(this->values + k * this->n[0] * this->n[1], this->n[0], this->n[1]); } inline const MatrixProxy<T> 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<T>(this->values + k * this->n[0] * this->n[1], this->n[0], this->n[1]); } inline MatrixProxy<T> operator[](UInt k) { return MatrixProxy<T>(this->values + k * this->n[0] * this->n[1], this->n[0], this->n[1]); } inline const MatrixProxy<T> operator[](UInt k) const { return MatrixProxy<T>(this->values + k * this->n[0] * this->n[1], this->n[0], this->n[1]); } }; /* -------------------------------------------------------------------------- */ // support operations for the creation of other vectors /* -------------------------------------------------------------------------- */ template <typename T> Vector<T> operator*(const T & scalar, const Vector<T> & a) { Vector<T> r(a); r *= scalar; return r; } template <typename T> Vector<T> operator*(const Vector<T> & a, const T & scalar) { Vector<T> r(a); r *= scalar; return r; } template <typename T> Vector<T> operator/(const Vector<T> & a, const T & scalar) { Vector<T> r(a); r /= scalar; return r; } template <typename T> Vector<T> operator*(const Vector<T> & a, const Vector<T> & b) { Vector<T> r(a); r *= b; return r; } template <typename T> Vector<T> operator+(const Vector<T> & a, const Vector<T> & b) { Vector<T> r(a); r += b; return r; } template <typename T> Vector<T> operator-(const Vector<T> & a, const Vector<T> & b) { Vector<T> r(a); r -= b; return r; } template <typename T> Vector<T> operator*(const Matrix<T> & A, const Vector<T> & b) { Vector<T> r(b.size()); r.template mul<false>(A, b); return r; } /* -------------------------------------------------------------------------- */ template <typename T> Matrix<T> operator*(const T & scalar, const Matrix<T> & a) { Matrix<T> r(a); r *= scalar; return r; } template <typename T> Matrix<T> operator*(const Matrix<T> & a, const T & scalar) { Matrix<T> r(a); r *= scalar; return r; } template <typename T> Matrix<T> operator/(const Matrix<T> & a, const T & scalar) { Matrix<T> r(a); r /= scalar; return r; } template <typename T> Matrix<T> operator+(const Matrix<T> & a, const Matrix<T> & b) { Matrix<T> r(a); r += b; return r; } template <typename T> Matrix<T> operator-(const Matrix<T> & a, const Matrix<T> & b) { Matrix<T> 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 <fabian.barras@epfl.ch> * @author Nicolas Richart <nicolas.richart@epfl.ch> * @author Damien Spielmann <damien.spielmann@epfl.ch> * * @date creation: Thu Feb 21 2013 * @date last modification: 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 <http://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #include "element_class.hh" /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_ELEMENT_CLASS_STRUCTURAL_HH__ #define __AKANTU_ELEMENT_CLASS_STRUCTURAL_HH__ namespace akantu { /// Macro to generate the InterpolationProperty structures for different /// interpolation types #define AKANTU_DEFINE_STRUCTURAL_INTERPOLATION_TYPE_PROPERTY( \ - itp_type, itp_geom_type, ndof, nb_stress) \ + itp_type, itp_geom_type, ndof, nb_stress, nb_dnds_cols) \ template <> struct InterpolationProperty<itp_type> { \ static const InterpolationKind kind{_itk_structural}; \ static const UInt nb_nodes_per_element{ \ InterpolationProperty<itp_geom_type>::nb_nodes_per_element}; \ static const InterpolationType itp_geometry_type{itp_geom_type}; \ static const UInt natural_space_dimension{ \ InterpolationProperty<itp_geom_type>::natural_space_dimension}; \ static const UInt nb_degree_of_freedom{ndof}; \ static const UInt nb_stress_components{nb_stress}; \ + static const UInt dnds_columns{nb_dnds_cols}; \ } /* -------------------------------------------------------------------------- */ template <InterpolationType interpolation_type> class InterpolationElement<interpolation_type, _itk_structural> { public: using interpolation_property = InterpolationProperty<interpolation_type>; /// compute the shape values for a given set of points in natural coordinates static inline void computeShapes(const Matrix<Real> & natural_coord, const Matrix<Real> & real_coord, Tensor3<Real> & N) { for (UInt i = 0; i < natural_coord.cols(); ++i) { Matrix<Real> 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<Real> & natural_coord, const Matrix<Real> & real_coord, Matrix<Real> & N); /// compute shape derivatives (input is dxds) for a set of points static inline void computeShapeDerivatives(const Tensor3<Real> & Js, const Tensor3<Real> & DNDSs, const Matrix<Real> & R, Tensor3<Real> & Bs) { for (UInt i = 0; i < Js.size(2); ++i) { Matrix<Real> J = Js(i); Matrix<Real> DNDS = DNDSs(i); - Matrix<Real> DNDS_R(DNDS.rows(), DNDS.cols()); - DNDS_R.mul<false, false>(DNDS, R); - Matrix<Real> B = Bs(i); + Matrix<Real> DNDX(DNDS.rows(), DNDS.cols()); auto inv_J = J.inverse(); - Matrix<Real> 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<false, false>(inv_J_full, DNDS_R); + DNDX.mul<false, false>(inv_J, DNDS); + Matrix<Real> B_R = Bs(i); + Matrix<Real> B(B_R.rows(), B_R.cols()); + arrangeInVoigt(DNDX, B); + B_R.mul<false, false>(B, R); } } /** * compute @f$ B_{ij} = \frac{\partial N_j}{\partial S_i} @f$ the variation of * shape functions along with variation of natural coordinates on a given set * of points in natural coordinates */ static inline void computeDNDS(const Matrix<Real> & natural_coord, const Matrix<Real> & real_coord, Tensor3<Real> & dnds) { for (UInt i = 0; i < natural_coord.cols(); ++i) { Matrix<Real> dnds_t = dnds(i); computeDNDS(natural_coord(i), real_coord, dnds_t); } } /** * compute @f$ B_{ij} = \frac{\partial N_j}{\partial S_i} @f$ the variation of * shape functions along with * variation of natural coordinates on a given point in natural * coordinates */ static inline void computeDNDS(const Vector<Real> & natural_coord, const Matrix<Real> & real_coord, Matrix<Real> & dnds); + /** + * arrange B in Voigt notation from DNDS + */ + static inline void arrangeInVoigt(const Matrix<Real> & dnds, + Matrix<Real> & B) { + // Default implementation assumes dnds is already in Voigt notation + B.deepCopy(dnds); + } + public: static 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<elem_type> { \ static const GeometricalType geometrical_type{geom_type}; \ static const InterpolationType interpolation_type{interp_type}; \ static const ElementType parent_element_type{parent_el_type}; \ static const ElementKind element_kind{elem_kind}; \ static const UInt spatial_dimension{sp}; \ static const GaussIntegrationType gauss_integration_type{gauss_int_type}; \ static const UInt polynomial_degree{min_int_order}; \ } /* -------------------------------------------------------------------------- */ /* ElementClass for structural elements */ /* -------------------------------------------------------------------------- */ template <ElementType element_type> class ElementClass<element_type, _ek_structural> : public GeometricalElement< ElementClassProperty<element_type>::geometrical_type>, public InterpolationElement< ElementClassProperty<element_type>::interpolation_type> { protected: using geometrical_element = GeometricalElement<ElementClassProperty<element_type>::geometrical_type>; using interpolation_element = InterpolationElement< ElementClassProperty<element_type>::interpolation_type>; using parent_element = ElementClass<ElementClassProperty<element_type>::parent_element_type>; public: static inline void computeRotationMatrix(Matrix<Real> & /*R*/, const Matrix<Real> & /*X*/, const Vector<Real> & /*extra_normal*/) { AKANTU_DEBUG_TO_IMPLEMENT(); } /// compute jacobian (or integration variable change factor) for a given point - static inline void computeJMat(const Matrix<Real> & DNDS, + static inline void computeJMat(const Vector<Real> & natural_coords, const Matrix<Real> & Xs, Matrix<Real> & J) { - auto nb_nodes = Xs.cols(); - auto dim = Xs.rows(); - auto nb_dof = - interpolation_element::interpolation_property::nb_degree_of_freedom; - Matrix<Real> 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<false, true>(B, Xs); + Matrix<Real> dnds(Xs.rows(), Xs.cols()); + parent_element::computeDNDS(natural_coords, dnds); + J.mul<false, true>(dnds, Xs); } - static inline void computeJMat(const Tensor3<Real> & DNDSs, + static inline void computeJMat(const Matrix<Real> & natural_coords, const Matrix<Real> & Xs, Tensor3<Real> & 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<Real> B(dim, nb_nodes); - - for (UInt i = 0; i < Xs.cols(); ++i) { - Matrix<Real> 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<Real> 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<Real>(natural_coords(i)), Xs, J); } } static inline void computeJacobian(const Matrix<Real> & natural_coords, const Matrix<Real> & node_coords, Vector<Real> & jacobians) { using itp = typename interpolation_element::interpolation_property; - UInt nb_points = natural_coords.cols(); - Matrix<Real> dnds(itp::natural_space_dimension, itp::nb_nodes_per_element); - Matrix<Real> 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<Real> ncoord_p(natural_coords(p)); - parent_element::computeDNDS(ncoord_p, dnds); - parent_element::computeJMat(dnds, x, J); - jacobians(p) = J.det(); + Tensor3<Real> Js(itp::natural_space_dimension, itp::natural_space_dimension, + natural_coords.cols()); + computeJMat(natural_coords, node_coords, Js); + for (UInt i = 0; i < natural_coords.cols(); ++i) { + Matrix<Real> J = Js(i); + jacobians(i) = J.det(); } } static inline void computeRotation(const Matrix<Real> & node_coords, Matrix<Real> & rotation); public: static AKANTU_GET_MACRO_NOT_CONST(Kind, _ek_structural, ElementKind); static AKANTU_GET_MACRO_NOT_CONST(P1ElementType, _not_defined, ElementType); static AKANTU_GET_MACRO_NOT_CONST(FacetType, _not_defined, ElementType); static constexpr auto getFacetType(__attribute__((unused)) UInt t = 0) { return _not_defined; } static constexpr AKANTU_GET_MACRO_NOT_CONST( SpatialDimension, ElementClassProperty<element_type>::spatial_dimension, UInt); static constexpr auto getFacetTypes() { return ElementClass<_not_defined>::getFacetTypes(); } }; } // namespace akantu /* -------------------------------------------------------------------------- */ #include "element_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 <fabian.barras@epfl.ch> * * @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 <http://www.gnu.org/licenses/>. * * @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<Real> & natural_coords, const Matrix<Real> & real_coord, Matrix<Real> & N) { Vector<Real> L(2); InterpolationElement<_itp_lagrange_segment_2, _itk_lagrangian>::computeShapes( natural_coords, L); Matrix<Real> H(2, 4); InterpolationElement<_itp_hermite_2, _itk_structural>::computeShapes( natural_coords, real_coord, H); // clang-format off // u1 v1 t1 u2 v2 t2 N = {{L(0), 0 , 0 , L(1), 0 , 0 }, // u {0 , H(0, 0), H(0, 1), 0 , H(0, 2), H(0, 3)}, // v {0 , H(1, 0), H(1, 1), 0 , H(1, 2), H(1, 3)}}; // theta // clang-format on } template <> inline void InterpolationElement<_itp_bernoulli_beam_3, _itk_structural>::computeShapes( const Vector<Real> & natural_coords, const Matrix<Real> & real_coord, Matrix<Real> & N) { Vector<Real> L(2); InterpolationElement<_itp_lagrange_segment_2, _itk_lagrangian>::computeShapes( natural_coords, L); Matrix<Real> H(2, 4); InterpolationElement<_itp_hermite_2, _itk_structural>::computeShapes( natural_coords, real_coord, H); // clang-format off // u1 v1 w1 x1 y1 z1 u2 v2 w2 x2 y2 z2 N = {{L(0), 0 , 0 , 0 , 0 , 0 , L(1), 0 , 0 , 0 , 0 , 0 }, // u {0 , H(0, 0), 0 , 0 , H(0, 1), 0 , 0 , H(0, 2), 0 , 0 , H(0, 3), 0 }, // v {0 , 0 , H(0, 0), 0 , 0 , H(0, 1), 0 , 0 , H(0, 2), 0 , 0 , H(0, 3)}, // w {0 , 0 , 0 , L(0), 0 , 0 , 0 , 0 , 0 , L(1), 0 , 0 }, // thetax {0 , H(1, 0), 0 , 0 , H(1, 1), 0 , 0 , H(1, 2), 0 , 0 , H(1, 3), 0 }, // thetay {0 , 0 , H(1, 0), 0 , 0 , H(1, 1), 0 , 0 , H(1, 2), 0 , 0 , H(1, 3)}}; // thetaz // clang-format on } /* -------------------------------------------------------------------------- */ template <> inline void InterpolationElement<_itp_bernoulli_beam_2, _itk_structural>::computeDNDS( const Vector<Real> & natural_coords, const Matrix<Real> & real_coord, - Matrix<Real> & B) { + Matrix<Real> & dnds) { Matrix<Real> L(1, 2); InterpolationElement<_itp_lagrange_segment_2, _itk_lagrangian>::computeDNDS( natural_coords, L); Matrix<Real> H(1, 4); InterpolationElement<_itp_hermite_2, _itk_structural>::computeDNDS( natural_coords, real_coord, H); + // Storing the derivatives in dnds + dnds.block(L, 0, 0); + dnds.block(H, 0, 2); +} + +/* -------------------------------------------------------------------------- */ +template <> +inline void +InterpolationElement<_itp_bernoulli_beam_2, _itk_structural>::arrangeInVoigt( + const Matrix<Real> & dnds, Matrix<Real> & B) { + auto L = dnds.block(0, 0, 1, 2); // Lagrange shape derivatives + auto H = dnds.block(0, 2, 1, 4); // Hermite shape derivatives // clang-format off - // u1 v1 t1 u2 v2 t2 - B = {{L(0, 0), 0 , 0 , L(0, 1), 0 , 0 }, // 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<Real> & natural_coords, const Matrix<Real> & real_coord, Matrix<Real> & B) { - Matrix<Real> L(1, 2); - InterpolationElement<_itp_lagrange_segment_2, _itk_lagrangian>::computeDNDS( - natural_coords, L); - Matrix<Real> H(1, 4); - InterpolationElement<_itp_hermite_2, _itk_structural>::computeDNDS( - natural_coords, real_coord, H); + const Vector<Real> & natural_coords, const Matrix<Real> & real_coord, + Matrix<Real> & dnds) { + InterpolationElement<_itp_bernoulli_beam_2, _itk_structural>::computeDNDS( + natural_coords, real_coord, dnds); +} + +/* -------------------------------------------------------------------------- */ +template <> +inline void +InterpolationElement<_itp_bernoulli_beam_3, _itk_structural>::arrangeInVoigt( + const Matrix<Real> & dnds, Matrix<Real> & B) { + auto L = dnds.block(0, 0, 1, 2); // Lagrange shape derivatives + auto H = dnds.block(0, 2, 1, 4); // Hermite shape derivatives + // clang-format off // u1 v1 w1 x1 y1 z1 u2 v2 w2 x2 y2 z2 B = {{L(0, 0), 0 , 0 , 0 , 0 , 0 , L(0, 1), 0 , 0 , 0 , 0 , 0 }, // eps {0 , H(0, 0), 0 , 0 , 0 , H(0, 1), 0 , H(0, 2), 0 , 0 , 0 , H(0, 3)}, // chi strong axis {0 , 0 ,-H(0, 0), 0 , H(0, 1), 0 , 0 , 0 ,-H(0, 2), 0 , H(0, 3), 0 }, // chi weak axis {0 , 0 , 0 , L(0, 0), 0 , 0 , 0 , 0 , 0 , L(0, 1), 0 , 0 }}; // chi torsion // clang-format on } /* -------------------------------------------------------------------------- */ template <> inline void ElementClass<_bernoulli_beam_2>::computeRotationMatrix( Matrix<Real> & R, const Matrix<Real> & X, const Vector<Real> &) { Vector<Real> x2 = X(1); // X2 Vector<Real> x1 = X(0); // X1 auto cs = (x2 - x1); cs.normalize(); auto c = cs(0); auto s = cs(1); // clang-format off /// Definition of the rotation matrix R = {{ c, s, 0.}, {-s, c, 0.}, { 0., 0., 1.}}; // clang-format on } /* -------------------------------------------------------------------------- */ template <> inline void ElementClass<_bernoulli_beam_3>::computeRotationMatrix( Matrix<Real> & R, const Matrix<Real> & X, const Vector<Real> & n) { Vector<Real> x2 = X(1); // X2 Vector<Real> x1 = X(0); // X1 auto dim = X.rows(); auto x = (x2 - x1); x.normalize(); auto x_n = x.crossProduct(n); Matrix<Real> Pe = {{1., 0., 0.}, {0., -1., 0.}, {0., 0., 1.}}; Matrix<Real> Pg(dim, dim); Pg(0) = x; Pg(1) = x_n; Pg(2) = n; Pe *= Pg.inverse(); R.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 <fabian.barras@epfl.ch> * @author Lucas Frérot <lucas.frerot@epfl.ch> * * @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 <http://www.gnu.org/licenses/>. * * @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> & real_coord) { Vector<Real> x1 = real_coord(0); Vector<Real> x2 = real_coord(1); return x1.distance(x2); } inline void computeShapes(const Vector<Real> & natural_coords, Real a, Matrix<Real> & N) { /// natural coordinate Real xi = natural_coords(0); // 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<Real> & natural_coords, Real a, Matrix<Real> & B) { // natural coordinate Real xi = natural_coords(0); // Derivatives with respect to xi for rotations auto M1__ = 3. / 2. * xi; auto M2__ = 3. / 2. * (-xi); auto L1__ = a / 2. * (3 * xi - 1); auto L2__ = a / 2. * (3 * xi + 1); // v1 t1 v2 t2 B = {{M1__, L1__, M2__, L2__}}; // computing curvature : {chi} = [B]{d} B /= a; // to account for first order deriv w/r to x } } // namespace details } // namespace /* -------------------------------------------------------------------------- */ template <> inline void InterpolationElement<_itp_hermite_2, _itk_structural>::computeShapes( const Vector<Real> & natural_coords, const Matrix<Real> & real_coord, Matrix<Real> & N) { auto L = details::computeLength(real_coord); details::computeShapes(natural_coords, L / 2, N); } /* -------------------------------------------------------------------------- */ template <> inline void InterpolationElement<_itp_hermite_2, _itk_structural>::computeDNDS( const Vector<Real> & natural_coords, const Matrix<Real> & real_coord, Matrix<Real> & B) { auto L = details::computeLength(real_coord); details::computeDNDS(natural_coords, L / 2, B); } } // namespace akantu #endif /* __AKANTU_ELEMENT_CLASS_HERMITE_INLINE_IMPL_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 65b452d57..6818c8ddc 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,189 +1,211 @@ /** * @file element_class_kirchhoff_shell_inline_impl.cc * * @author Damien Spielmann <damien.spielmann@epfl.ch> * * @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 <http://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #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); + _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<Real> & P, const Matrix<Real> & X) { Vector<Real> X1 = X(0); Vector<Real> X2 = X(1); Vector<Real> X3 = X(2); Vector<Real> a1 = X2 - X1; Vector<Real> a2 = X3 - X1; a1.normalize(); Vector<Real> e3 = a1.crossProduct(a2); e3.normalize(); Vector<Real> e2 = e3.crossProduct(a1); P(0) = a1; P(1) = e2; P(2) = e3; } } /* -------------------------------------------------------------------------- */ template <> inline void ElementClass<_discrete_kirchhoff_triangle_18>::computeRotationMatrix( Matrix<Real> & R, const Matrix<Real> & X, const Vector<Real> &) { auto dim = X.rows(); Matrix<Real> P(dim, dim); 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); } /* -------------------------------------------------------------------------- */ template <> inline void InterpolationElement<_itp_discrete_kirchhoff_triangle_18>::computeShapes( const Vector<Real> & /*natural_coords*/, const Matrix<Real> & /*real_coord*/, Matrix<Real> & /*N*/) {} /* -------------------------------------------------------------------------- */ template <> inline void InterpolationElement<_itp_discrete_kirchhoff_triangle_18>::computeDNDS( const Vector<Real> & natural_coords, const Matrix<Real> & real_coordinates, Matrix<Real> & B) { auto dim = real_coordinates.cols(); Matrix<Real> P(dim, dim); detail::computeBasisChangeMatrix(P, real_coordinates); auto X = P * real_coordinates; Vector<Real> X1 = X(0); Vector<Real> X2 = X(1); Vector<Real> X3 = X(2); std::array<Vector<Real>, 3> A = {X2 - X1, X3 - X2, X1 - X3}; std::array<Real, 3> 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(), [](Vector<Real> & a) { return a.norm<L_2>(); }); // Computing cosines std::transform(A.begin(), A.end(), L.begin(), C.begin(), [](Vector<Real> & a, Real & l) { return a(0) / l; }); // Computing sines std::transform(A.begin(), A.end(), L.begin(), S.begin(), [](Vector<Real> & a, Real & l) { return a(1) / l; }); // Natural coordinates Real xi = natural_coords(0); Real eta = natural_coords(1); // Derivative of quadratic interpolation functions Matrix<Real> dP = {{4 * (1 - 2 * xi - eta), 4 * eta, -4 * eta}, {-4 * xi, 4 * xi, 4 * (1 - xi - 2 * eta)}}; Matrix<Real> 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<Real> 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<Real> 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<Real> 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<Real> dNy2 = dNx3; Matrix<Real> 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 // Derivative of linear (membrane mode) functions - Matrix<Real> dNm = {{1, -1, 0}, {-1, 0, 1}}; + Matrix<Real> dNm(2, 3); + InterpolationElement<_itp_lagrange_triangle_3, _itk_lagrangian>::computeDNDS( + natural_coords, dNm); + + UInt i = 0; + for (const Matrix<Real> & 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<Real> & dnds, + Matrix<Real> & B) { + Matrix<Real> 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<Real> * mat : {&dNm, &dNx1, &dNx2, &dNx3, &dNy1, &dNy2, &dNy3}) { + *mat = dnds.block(0, i, 2, 3); + i += mat->cols(); + } // clang-format off B = { // Membrane shape functions; TODO use the triangle_3 shapes {dNm(0, 0), 0, 0, 0, 0, 0, dNm(0, 1), 0, 0, 0, 0, 0, dNm(0, 2), 0, 0, 0, 0, 0, }, {0, dNm(1, 0), 0, 0, 0, 0, 0, dNm(1, 1), 0, 0, 0, 0, 0, dNm(1, 2), 0, 0, 0, 0, }, {dNm(1, 0), dNm(0, 0), 0, 0, 0, 0, dNm(1, 1), dNm(0, 1), 0, 0, 0, 0, dNm(1, 2), dNm(0, 2), 0, 0, 0, 0, }, // Bending shape functions {0, 0, dNx1(0, 0), -dNx3(0, 0), dNx2(0, 0), 0, 0, 0, dNx1(0, 1), -dNx3(0, 1), dNx2(0, 1), 0, 0, 0, dNx1(0, 2), -dNx3(0, 2), dNx2(0, 2), 0}, {0, 0, dNy1(1, 0), -dNy3(1, 0), dNy2(1, 0), 0, 0, 0, dNy1(1, 1), -dNy3(1, 1), dNy2(1, 1), 0, 0, 0, dNy1(1, 2), -dNy3(1, 2), dNy2(1, 2), 0}, {0, 0, dNx1(1, 0) + dNy1(0, 0), -dNx3(1, 0) - dNy3(0, 0), dNx2(1, 0) + dNy2(1, 0), 0, 0, 0, dNx1(1, 1) + dNy1(0, 1), -dNx3(1, 1) - dNy3(0, 0), dNx2(1, 1) + dNy2(1, 1), 0, 0, 0, dNx1(1, 2) + dNy1(0, 2), -dNx3(1, 2) - dNy3(0, 2), dNx2(1, 2) + dNy2(1, 2), 0}}; // clang-format on } } // namespace akantu #endif /* __AKANTU_ELEMENT_CLASS_KIRCHHOFF_SHELL_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..5792b0d34 100644 --- a/src/fe_engine/shape_structural_inline_impl.cc +++ b/src/fe_engine/shape_structural_inline_impl.cc @@ -1,383 +1,389 @@ /** * @file shape_structural_inline_impl.cc * * @author Fabian Barras <fabian.barras@epfl.ch> * @author Nicolas Richart <nicolas.richart@epfl.ch> * * @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 <http://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #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 <ElementType type> std::unique_ptr<Array<Real>> getNodesPerElement(const Mesh & mesh, const Array<Real> & nodes, const GhostType & ghost_type) { const auto dim = ElementClass<type>::getSpatialDimension(); const auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type); auto nodes_per_element = std::make_unique<Array<Real>>(0, dim * nb_nodes_per_element); FEEngine::extractNodalToElementField(mesh, nodes, *nodes_per_element, type, ghost_type); return nodes_per_element; } } template <ElementKind kind> inline void ShapeStructural<kind>::initShapeFunctions( const Array<Real> & /* unused */, const Matrix<Real> & /* unused */, const ElementType & /* unused */, const GhostType & /* unused */) { AKANTU_DEBUG_TO_IMPLEMENT(); } /* -------------------------------------------------------------------------- */ #define INIT_SHAPE_FUNCTIONS(type) \ setIntegrationPointsByType<type>(integration_points, ghost_type); \ precomputeRotationMatrices<type>(nodes, ghost_type); \ precomputeShapesOnIntegrationPoints<type>(nodes, ghost_type); \ precomputeShapeDerivativesOnIntegrationPoints<type>(nodes, ghost_type); template <> inline void ShapeStructural<_ek_structural>::initShapeFunctions( const Array<Real> & nodes, const Matrix<Real> & integration_points, const ElementType & type, const GhostType & ghost_type) { AKANTU_BOOST_STRUCTURAL_ELEMENT_SWITCH(INIT_SHAPE_FUNCTIONS); } #undef INIT_SHAPE_FUNCTIONS /* -------------------------------------------------------------------------- */ template <> template <ElementType type> void ShapeStructural<_ek_structural>::computeShapesOnIntegrationPoints( const Array<Real> & nodes, const Matrix<Real> & integration_points, Array<Real> & shapes, const GhostType & ghost_type, const Array<UInt> & 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<type>::getNbDegreeOfFreedom(); #if !defined(AKANTU_NDEBUG) UInt size_of_shapes = ElementClass<type>::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<type>::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<type>(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<Real> & N = *shapes_it; auto & real_coord = *nodes_it; ElementClass<type>::computeShapes(integration_points, real_coord, N); if (filter_elements == empty_filter) ++shapes_it; } } /* -------------------------------------------------------------------------- */ template <ElementKind kind> template <ElementType type> void ShapeStructural<kind>::precomputeRotationMatrices( const Array<Real> & 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<type>::getNbDegreeOfFreedom(); if (not this->rotation_matrices.exists(type, ghost_type)) { this->rotation_matrices.alloc(0, nb_dof * nb_dof, type, ghost_type); } auto & rot_matrices = this->rotation_matrices(type, ghost_type); rot_matrices.resize(nb_element); Array<Real> x_el(0, spatial_dimension * nb_nodes_per_element); FEEngine::extractNodalToElementField(mesh, nodes, x_el, type, ghost_type); bool has_extra_normal = mesh.hasData<Real>("extra_normal", type, ghost_type); Array<Real>::const_vector_iterator extra_normal; if (has_extra_normal) extra_normal = mesh.getData<Real>("extra_normal", type, ghost_type) .begin(spatial_dimension); for (auto && tuple : zip(make_view(x_el, spatial_dimension, nb_nodes_per_element), make_view(rot_matrices, nb_dof, nb_dof))) { // compute shape derivatives auto & X = std::get<0>(tuple); auto & R = std::get<1>(tuple); if (has_extra_normal) { ElementClass<type>::computeRotationMatrix(R, X, *extra_normal); ++extra_normal; } else { ElementClass<type>::computeRotationMatrix( R, X, Vector<Real>(spatial_dimension)); } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template <ElementKind kind> template <ElementType type> void ShapeStructural<kind>::precomputeShapesOnIntegrationPoints( const Array<Real> & nodes, 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<type>::getNbDegreeOfFreedom(); const auto dim = ElementClass<type>::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<type>(mesh, nodes, ghost_type); for (auto && tuple : zip(make_view(shapes_, nb_dof, nb_dof * nb_nodes_per_element, nb_points), make_view(*nodes_per_element, dim, nb_nodes_per_element))) { auto & N = std::get<0>(tuple); auto & real_coord = std::get<1>(tuple); ElementClass<type>::computeShapes(natural_coords, real_coord, N); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template <ElementKind kind> template <ElementType type> void ShapeStructural<kind>::precomputeShapeDerivativesOnIntegrationPoints( const Array<Real> & 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<type>::getNaturalSpaceDimension(); const auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type); const auto nb_points = natural_coords.cols(); const auto nb_dof = ElementClass<type>::getNbDegreeOfFreedom(); const auto nb_element = mesh.getNbElement(type, ghost_type); const auto nb_stress_components = ElementClass<type>::getNbStressComponents(); auto itp_type = FEEngine::getInterpolationType(type); if (not this->shapes_derivatives.exists(itp_type, ghost_type)) { auto size_of_shapesd = this->getShapeDerivativesSize(type); this->shapes_derivatives.alloc(0, size_of_shapesd, itp_type, ghost_type); } auto & rot_matrices = this->rotation_matrices(type, ghost_type); Array<Real> x_el(0, spatial_dimension * nb_nodes_per_element); FEEngine::extractNodalToElementField(mesh, nodes, x_el, type, ghost_type); auto & shapesd = this->shapes_derivatives(itp_type, ghost_type); shapesd.resize(nb_element * nb_points); for (auto && tuple : zip(make_view(x_el, spatial_dimension, nb_nodes_per_element), make_view(shapesd, nb_stress_components, nb_nodes_per_element * nb_dof, nb_points), make_view(rot_matrices, nb_dof, nb_dof))) { // compute shape derivatives auto & X = std::get<0>(tuple); auto & B = std::get<1>(tuple); auto & RDOFs = std::get<2>(tuple); + + // /!\ This is a temporary variable with no specified size for columns ! + // It is up to the element to resize + Tensor3<Real> dnds(natural_spatial_dimension, + ElementClass<type>::interpolation_property::dnds_columns, + B.size(2)); + ElementClass<type>::computeDNDS(natural_coords, X, dnds); + + Tensor3<Real> J(natural_spatial_dimension, natural_coords.rows(), natural_coords.cols()); + + // Computing the coordinates of the element in the natural space auto R = RDOFs.block(0, 0, spatial_dimension, spatial_dimension); Matrix<Real> T(B.size(1), B.size(1)); T.block(RDOFs, 0, 0); T.block(RDOFs, RDOFs.rows(), RDOFs.rows()); // Rotate to local basis auto x = (R * X).block(0, 0, natural_spatial_dimension, nb_nodes_per_element); - Tensor3<Real> dnds(B.size(0), B.size(1), B.size(2)); - ElementClass<type>::computeDNDS(natural_coords, X, dnds); - - Tensor3<Real> J(x.rows(), natural_coords.rows(), natural_coords.cols()); - - ElementClass<type>::computeJMat(dnds, x, J); + ElementClass<type>::computeJMat(natural_coords, x, J); ElementClass<type>::computeShapeDerivatives(J, dnds, T, B); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template <ElementKind kind> template <ElementType type> void ShapeStructural<kind>::interpolateOnIntegrationPoints( const Array<Real> & in_u, Array<Real> & out_uq, UInt nb_dof, const GhostType & ghost_type, const Array<UInt> & filter_elements) const { AKANTU_DEBUG_IN(); AKANTU_DEBUG_ASSERT(out_uq.getNbComponent() == nb_dof, "The output array shape is not correct"); auto itp_type = FEEngine::getInterpolationType(type); const auto & shapes_ = shapes(itp_type, ghost_type); auto nb_element = mesh.getNbElement(type, ghost_type); auto nb_nodes_per_element = ElementClass<type>::getNbNodesPerElement(); auto nb_quad_points_per_element = integration_points(type, ghost_type).cols(); Array<Real> u_el(0, nb_nodes_per_element * nb_dof); FEEngine::extractNodalToElementField(mesh, in_u, u_el, type, ghost_type, filter_elements); auto nb_quad_points = nb_quad_points_per_element * u_el.size(); out_uq.resize(nb_quad_points); auto out_it = out_uq.begin_reinterpret(nb_dof, 1, nb_quad_points_per_element, u_el.size()); auto shapes_it = shapes_.begin_reinterpret(nb_dof, nb_dof * nb_nodes_per_element, nb_quad_points_per_element, nb_element); auto u_it = u_el.begin_reinterpret(nb_dof * nb_nodes_per_element, 1, nb_quad_points_per_element, u_el.size()); for_each_element(nb_element, filter_elements, [&](auto && el) { auto & uq = *out_it; const auto & u = *u_it; auto N = Tensor3<Real>(shapes_it[el]); for (auto && q : arange(uq.size(2))) { auto uq_q = Matrix<Real>(uq(q)); auto u_q = Matrix<Real>(u(q)); auto N_q = Matrix<Real>(N(q)); uq_q.mul<false, false>(N_q, u_q); } ++out_it; ++u_it; }); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template <ElementKind kind> template <ElementType type> void ShapeStructural<kind>::gradientOnIntegrationPoints( const Array<Real> & in_u, Array<Real> & out_nablauq, UInt nb_dof, const GhostType & ghost_type, const Array<UInt> & filter_elements) const { AKANTU_DEBUG_IN(); auto itp_type = FEEngine::getInterpolationType(type); const auto & shapesd = shapes_derivatives(itp_type, ghost_type); auto nb_element = mesh.getNbElement(type, ghost_type); auto element_dimension = ElementClass<type>::getSpatialDimension(); auto nb_quad_points_per_element = integration_points(type, ghost_type).cols(); auto nb_nodes_per_element = ElementClass<type>::getNbNodesPerElement(); Array<Real> u_el(0, nb_nodes_per_element * nb_dof); FEEngine::extractNodalToElementField(mesh, in_u, u_el, type, ghost_type, filter_elements); auto nb_quad_points = nb_quad_points_per_element * u_el.size(); out_nablauq.resize(nb_quad_points); auto out_it = out_nablauq.begin_reinterpret( element_dimension, 1, nb_quad_points_per_element, u_el.size()); auto shapesd_it = shapesd.begin_reinterpret( element_dimension, nb_dof * nb_nodes_per_element, nb_quad_points_per_element, nb_element); auto u_it = u_el.begin_reinterpret(nb_dof * nb_nodes_per_element, 1, nb_quad_points_per_element, u_el.size()); for_each_element(nb_element, filter_elements, [&](auto && el) { auto & nablau = *out_it; const auto & u = *u_it; auto B = Tensor3<Real>(shapesd_it[el]); for (auto && q : arange(nablau.size(2))) { auto nablau_q = Matrix<Real>(nablau(q)); auto u_q = Matrix<Real>(u(q)); auto B_q = Matrix<Real>(B(q)); nablau_q.mul<false, false>(B_q, u_q); } ++out_it; ++u_it; }); AKANTU_DEBUG_OUT(); } } // namespace akantu #endif /* __AKANTU_SHAPE_STRUCTURAL_INLINE_IMPL_CC__ */ diff --git a/src/model/solid_mechanics/material.hh b/src/model/solid_mechanics/material.hh index 91f3a1581..a3f68b552 100644 --- a/src/model/solid_mechanics/material.hh +++ b/src/model/solid_mechanics/material.hh @@ -1,682 +1,683 @@ /** * @file material.hh * * @author Daniel Pino Muñoz <daniel.pinomunoz@epfl.ch> * @author Nicolas Richart <nicolas.richart@epfl.ch> * @author Marco Vocialta <marco.vocialta@epfl.ch> * * @date creation: Fri Jun 18 2010 * @date last modification: Wed 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 <http://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #include "aka_factory.hh" #include "aka_voigthelper.hh" #include "data_accessor.hh" #include "integration_point.hh" #include "parsable.hh" #include "parser.hh" /* -------------------------------------------------------------------------- */ #include "internal_field.hh" #include "random_internal_field.hh" /* -------------------------------------------------------------------------- */ #include "mesh_events.hh" #include "solid_mechanics_model_event_handler.hh" /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_MATERIAL_HH__ #define __AKANTU_MATERIAL_HH__ /* -------------------------------------------------------------------------- */ namespace akantu { class Model; class SolidMechanicsModel; } // 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<Real> & tangent_matrix, * GhostType ghost_type = _not_ghost); * \endcode * */ class Material : public Memory, public DataAccessor<Element>, 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<Real> & 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<Real> & 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<Real> & points, Matrix<Real> & 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 <typename T> void registerInternal(__attribute__((unused)) InternalField<T> & vect) { AKANTU_DEBUG_TO_IMPLEMENT(); } template <typename T> void unregisterInternal(__attribute__((unused)) InternalField<T> & 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<Element> & elements_to_add); /// remove many element at once void removeElements(const Array<Element> & elements_to_remove); /// function to print the contain of the class void printself(std::ostream & stream, int indent = 0) const override; /** * interpolate stress on given positions for each element by means * of a geometrical interpolation on quadrature points */ void interpolateStress(ElementTypeMapArray<Real> & result, 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<Real> & result, ElementTypeMapArray<Real> & 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<Real> & 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 <UInt dim> 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 <UInt dim> void computeCauchyStress(ElementType el_type, GhostType ghost_type = _not_ghost); /// Computation the Cauchy stress the 2nd Piola-Kirchhoff and the deformation /// gradient template <UInt dim> inline void computeCauchyStressOnQuad(const Matrix<Real> & F, const Matrix<Real> & S, Matrix<Real> & cauchy, const Real & C33 = 1.0) const; template <UInt dim> void computeAllStressesFromTangentModuli(const ElementType & type, GhostType ghost_type); template <UInt dim> void assembleStiffnessMatrix(const ElementType & type, GhostType ghost_type); /// assembling in finite deformation template <UInt dim> void assembleStiffnessMatrixNL(const ElementType & type, GhostType ghost_type); template <UInt dim> 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 <UInt dim> inline void setCauchyStressMatrix(const Matrix<Real> & S_t, Matrix<Real> & sigma); /// write the stress tensor in the Voigt notation. template <UInt dim> inline void setCauchyStressArray(const Matrix<Real> & S_t, Matrix<Real> & sigma_voight); /* ------------------------------------------------------------------------ */ /* Conversion functions */ /* ------------------------------------------------------------------------ */ public: template <UInt dim> static inline void gradUToF(const Matrix<Real> & grad_u, Matrix<Real> & F); static inline void rightCauchy(const Matrix<Real> & F, Matrix<Real> & C); static inline void leftCauchy(const Matrix<Real> & F, Matrix<Real> & B); template <UInt dim> static inline void gradUToEpsilon(const Matrix<Real> & grad_u, Matrix<Real> & epsilon); template <UInt dim> static inline void gradUToGreenStrain(const Matrix<Real> & grad_u, Matrix<Real> & epsilon); static inline Real stressToVonMises(const Matrix<Real> & stress); protected: /// converts global element to local element inline Element convertToLocalElement(const Element & global_element) const; /// converts local element to global element inline Element convertToGlobalElement(const Element & local_element) const; /// converts global quadrature point to local quadrature point inline IntegrationPoint convertToLocalPoint(const IntegrationPoint & global_point) const; /// converts local quadrature point to global quadrature point inline IntegrationPoint convertToGlobalPoint(const IntegrationPoint & local_point) const; /* ------------------------------------------------------------------------ */ /* DataAccessor inherited members */ /* ------------------------------------------------------------------------ */ public: inline UInt getNbData(const Array<Element> & elements, const SynchronizationTag & tag) const override; inline void packData(CommunicationBuffer & buffer, const Array<Element> & elements, const SynchronizationTag & tag) const override; inline void unpackData(CommunicationBuffer & buffer, const Array<Element> & elements, const SynchronizationTag & tag) override; template <typename T> inline void packElementDataHelper(const ElementTypeMapArray<T> & data_to_pack, CommunicationBuffer & buffer, const Array<Element> & elements, const ID & fem_id = ID()) const; template <typename T> inline void unpackElementDataHelper(ElementTypeMapArray<T> & data_to_unpack, CommunicationBuffer & buffer, const Array<Element> & elements, const ID & fem_id = ID()); /* ------------------------------------------------------------------------ */ /* MeshEventHandler inherited members */ /* ------------------------------------------------------------------------ */ public: /* ------------------------------------------------------------------------ */ void onNodesAdded(const Array<UInt> &, const NewNodesEvent &) override{}; void onNodesRemoved(const Array<UInt> &, const Array<UInt> &, const RemovedNodesEvent &) override{}; void onElementsAdded(const Array<Element> & element_list, const NewElementsEvent & event) override; void onElementsRemoved(const Array<Element> & element_list, const ElementTypeMapArray<UInt> & new_numbering, const RemovedElementsEvent & event) override; void onElementsChanged(const Array<Element> &, const Array<Element> &, const ElementTypeMapArray<UInt> &, 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<Real> &); AKANTU_GET_MACRO(Stress, stress, const ElementTypeMapArray<Real> &); AKANTU_GET_MACRO(ElementFilter, element_filter, const ElementTypeMapArray<UInt> &); AKANTU_GET_MACRO(FEEngine, fem, FEEngine &); bool isNonLocal() const { return is_non_local; } template <typename T> const Array<T> & getArray(const ID & id, const ElementType & type, const GhostType & ghost_type = _not_ghost) const; template <typename T> Array<T> & getArray(const ID & id, const ElementType & type, const GhostType & ghost_type = _not_ghost); template <typename T> const InternalField<T> & getInternal(const ID & id) const; template <typename T> InternalField<T> & getInternal(const ID & id); template <typename T> inline bool isInternal(const ID & id, const ElementKind & element_kind) const; template <typename T> ElementTypeMap<UInt> getInternalDataPerElem(const ID & id, const ElementKind & element_kind) const; bool isFiniteDeformation() const { return finite_deformation; } bool isInelasticDeformation() const { return inelastic_deformation; } template <typename T> inline void setParam(const ID & param, T value); + template <typename T> inline void setParamNoUpdate(const ID & param, T value); inline const Parameter & getParam(const ID & param) const; template <typename T> void flattenInternal(const std::string & field_id, ElementTypeMapArray<T> & internal_flat, 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<Real> & 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<ID, InternalField<Real> *> internal_vectors_real; std::map<ID, InternalField<UInt> *> internal_vectors_uint; std::map<ID, InternalField<bool> *> 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<UInt> element_filter; /// stresses arrays ordered by element types InternalField<Real> stress; /// eigengrad_u arrays ordered by element types InternalField<Real> eigengradu; /// grad_u arrays ordered by element types InternalField<Real> gradu; /// Green Lagrange strain (Finite deformation) InternalField<Real> green_strain; /// Second Piola-Kirchhoff stress tensor arrays ordered by element types /// (Finite deformation) InternalField<Real> piola_kirchhoff_2; /// potential energy by element InternalField<Real> potential_energy; /// tell if using in non local mode or not bool is_non_local; /// 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<Real> interpolation_inverse_coordinates; /// elemental field interpolation points InternalField<Real> interpolation_points_matrices; /// vector that contains the names of all the internals that need to /// be transferred when material interfaces move std::vector<ID> internals_to_transfer; }; /// 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<Real>::matrix_iterator gradu_it = \ this->gradu(el_type, ghost_type) \ .begin(this->spatial_dimension, this->spatial_dimension); \ Array<Real>::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<Real>::iterator<Matrix<Real>> 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<Real> & __attribute__((unused)) grad_u = *gradu_it; \ Matrix<Real> & __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<Real>::matrix_iterator gradu_it = \ this->gradu(el_type, ghost_type) \ .begin(this->spatial_dimension, this->spatial_dimension); \ Array<Real>::matrix_iterator gradu_end = \ this->gradu(el_type, ghost_type) \ .end(this->spatial_dimension, this->spatial_dimension); \ Array<Real>::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<Real>::matrix_iterator tangent_it = \ tangent_mat.begin(tangent_size, tangent_size); \ \ for (; gradu_it != gradu_end; ++gradu_it, ++sigma_it, ++tangent_it) { \ Matrix<Real> & __attribute__((unused)) grad_u = *gradu_it; \ Matrix<Real> & __attribute__((unused)) sigma_tensor = *sigma_it; \ Matrix<Real> & tangent = *tangent_it #define MATERIAL_TANGENT_QUADRATURE_POINT_LOOP_END } /* -------------------------------------------------------------------------- */ namespace akantu { using MaterialFactory = Factory<Material, ID, UInt, const ID &, SolidMechanicsModel &, const ID &>; } // 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<Material> { \ switch (dim) { \ case 1: \ return std::make_unique<mat_name<1>>(model, id); \ case 2: \ return std::make_unique<mat_name<2>>(model, id); \ case 3: \ return std::make_unique<mat_name<3>>(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 4b06b4d13..1f6d1c0dd 100644 --- a/src/model/solid_mechanics/material_inline_impl.cc +++ b/src/model/solid_mechanics/material_inline_impl.cc @@ -1,464 +1,475 @@ /** * @file material_inline_impl.cc * * @author Daniel Pino Muñoz <daniel.pinomunoz@epfl.ch> * @author Nicolas Richart <nicolas.richart@epfl.ch> * @author Marco Vocialta <marco.vocialta@epfl.ch> * * @date creation: Tue Jul 27 2010 * @date last modification: Wed 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 <http://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #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<UInt> & 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 <UInt dim> inline void Material::gradUToF(const Matrix<Real> & grad_u, Matrix<Real> & 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 <UInt dim> inline void Material::computeCauchyStressOnQuad(const Matrix<Real> & F, const Matrix<Real> & piola, Matrix<Real> & sigma, const Real & C33) const { Real J = F.det() * sqrt(C33); Matrix<Real> F_S(dim, dim); F_S.mul<false, false>(F, piola); Real constant = J ? 1. / J : 0; sigma.mul<false, true>(F_S, F, constant); } /* -------------------------------------------------------------------------- */ inline void Material::rightCauchy(const Matrix<Real> & F, Matrix<Real> & C) { C.mul<true, false>(F, F); } /* -------------------------------------------------------------------------- */ inline void Material::leftCauchy(const Matrix<Real> & F, Matrix<Real> & B) { B.mul<false, true>(F, F); } /* -------------------------------------------------------------------------- */ template <UInt dim> inline void Material::gradUToEpsilon(const Matrix<Real> & grad_u, Matrix<Real> & 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 <UInt dim> inline void Material::gradUToGreenStrain(const Matrix<Real> & grad_u, Matrix<Real> & epsilon) { epsilon.mul<true, false>(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<Real> & stress) { // compute deviatoric stress UInt dim = stress.cols(); Matrix<Real> deviatoric_stress = Matrix<Real>::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 <UInt dim> inline void Material::setCauchyStressArray(const Matrix<Real> & S_t, Matrix<Real> & 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 <UInt dim> inline void Material::setCauchyStressMatrix(const Matrix<Real> & S_t, Matrix<Real> & 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<const Element &>(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<const Element &>(local_point)); IntegrationPoint tmp_quad(el, local_point.num_point, nb_quad); return tmp_quad; } /* -------------------------------------------------------------------------- */ inline UInt Material::getNbData(const Array<Element> & 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<Element> & 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<Element> & 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 <typename T> inline void Material::setParam(const ID & param, T value) { try { set<T>(param, value); } catch (...) { AKANTU_EXCEPTION("No parameter " << param << " in the material " << getID()); } updateInternalParameters(); } +/* -------------------------------------------------------------------------- */ +template <typename T> +inline void Material::setParamNoUpdate(const ID & param, T value) { + try { + set<T>(param, value); + } catch (...) { + AKANTU_EXCEPTION("No parameter " << param << " in the material " + << getID()); + } +} + /* -------------------------------------------------------------------------- */ template <typename T> inline void Material::packElementDataHelper( const ElementTypeMapArray<T> & data_to_pack, CommunicationBuffer & buffer, const Array<Element> & elements, const ID & fem_id) const { DataAccessor::packElementalDataHelper<T>(data_to_pack, buffer, elements, true, model.getFEEngine(fem_id)); } /* -------------------------------------------------------------------------- */ template <typename T> inline void Material::unpackElementDataHelper( ElementTypeMapArray<T> & data_to_unpack, CommunicationBuffer & buffer, const Array<Element> & elements, const ID & fem_id) { DataAccessor::unpackElementalDataHelper<T>(data_to_unpack, buffer, elements, true, model.getFEEngine(fem_id)); } /* -------------------------------------------------------------------------- */ template <> inline void Material::registerInternal<Real>(InternalField<Real> & vect) { internal_vectors_real[vect.getID()] = &vect; } template <> inline void Material::registerInternal<UInt>(InternalField<UInt> & vect) { internal_vectors_uint[vect.getID()] = &vect; } template <> inline void Material::registerInternal<bool>(InternalField<bool> & vect) { internal_vectors_bool[vect.getID()] = &vect; } /* -------------------------------------------------------------------------- */ template <> inline void Material::unregisterInternal<Real>(InternalField<Real> & vect) { internal_vectors_real.erase(vect.getID()); } template <> inline void Material::unregisterInternal<UInt>(InternalField<UInt> & vect) { internal_vectors_uint.erase(vect.getID()); } template <> inline void Material::unregisterInternal<bool>(InternalField<bool> & vect) { internal_vectors_bool.erase(vect.getID()); } /* -------------------------------------------------------------------------- */ template <typename T> inline bool Material::isInternal(__attribute__((unused)) const ID & id, __attribute__((unused)) const ElementKind & element_kind) const { AKANTU_DEBUG_TO_IMPLEMENT(); } template <> inline bool Material::isInternal<Real>(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 <typename T> inline ElementTypeMap<UInt> Material::getInternalDataPerElem(const ID & field_id, const ElementKind & element_kind) const { if (!this->template isInternal<T>(field_id, element_kind)) AKANTU_EXCEPTION("Cannot find internal field " << id << " in material " << this->name); const InternalField<T> & internal_field = this->template getInternal<T>(field_id); const FEEngine & fe_engine = internal_field.getFEEngine(); UInt nb_data_per_quad = internal_field.getNbComponent(); ElementTypeMap<UInt> res; for (ghost_type_t::iterator gt = ghost_type_t::begin(); gt != ghost_type_t::end(); ++gt) { using type_iterator = typename InternalField<T>::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 <typename T> void Material::flattenInternal(const std::string & field_id, ElementTypeMapArray<T> & internal_flat, const GhostType ghost_type, ElementKind element_kind) const { if (!this->template isInternal<T>(field_id, element_kind)) AKANTU_EXCEPTION("Cannot find internal field " << id << " in material " << this->name); const InternalField<T> & internal_field = this->template getInternal<T>(field_id); const FEEngine & fe_engine = internal_field.getFEEngine(); const Mesh & mesh = fe_engine.getMesh(); using type_iterator = typename InternalField<T>::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<Real> & src_vect = internal_field(type, ghost_type); const Array<UInt> & 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<Real> & dst_vect = internal_flat(type, ghost_type); dst_vect.resize(nb_element_dst * nb_quad_per_elem); Array<UInt>::const_scalar_iterator it = filter.begin(); Array<UInt>::const_scalar_iterator end = filter.end(); Array<Real>::const_vector_iterator it_src = src_vect.begin_reinterpret(nb_data, nb_element_src); Array<Real>::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 <till.junge@epfl.ch> * @author Nicolas Richart <nicolas.richart@epfl.ch> * * @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 <http://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ #include "material_elastic_linear_anisotropic.hh" #include "solid_mechanics_model.hh" #include <algorithm> #include <sstream> namespace akantu { /* -------------------------------------------------------------------------- */ template <UInt dim> MaterialElasticLinearAnisotropic<dim>::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<Vector<Real>>(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<Vector<Real>>(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<Vector<Real>>(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 <UInt dim> void MaterialElasticLinearAnisotropic<dim>::initMaterial() { AKANTU_DEBUG_IN(); Material::initMaterial(); updateInternalParameters(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template <UInt dim> void MaterialElasticLinearAnisotropic<dim>::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 <UInt Dim> void MaterialElasticLinearAnisotropic<Dim>::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<Real> 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<Real> test_axis(3); Vector<Real> 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<Real>::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<Real> rotator(Dim * Dim, Dim * Dim); Matrix<Real> 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<Real> 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 <UInt dim> -void MaterialElasticLinearAnisotropic<dim>::computeStress( - ElementType el_type, GhostType ghost_type) { +void MaterialElasticLinearAnisotropic<dim>::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<Real>::iterator<Matrix<Real>> gradu_it = - this->gradu(el_type, ghost_type).begin(dim, dim); - Array<Real>::iterator<Matrix<Real>> 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<Real> voigt_strains(voigt_h::size, nb_quad_pts); - Matrix<Real> voigt_stresses(voigt_h::size, nb_quad_pts); - - // copy strains Matrix<Real> strain(dim, dim); - for (UInt q = 0; gradu_it != gradu_end; ++gradu_it, ++q) { - Matrix<Real> & 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<Real> strain_rate(0, dim * dim, "strain_rate"); - - Array<Real> & velocity = this->model.getVelocity(); - const Array<UInt> & 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<Real>::matrix_iterator gradu_dot_it = strain_rate.begin(dim, dim); - Array<Real>::matrix_iterator gradu_dot_end = strain_rate.end(dim, dim); - - Matrix<Real> strain_dot(dim, dim); - for (UInt q = 0; gradu_dot_it != gradu_dot_end; ++gradu_dot_it, ++q) { - Matrix<Real> & 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 <UInt dim> +void MaterialElasticLinearAnisotropic<dim>::computeTangentModuli( + const ElementType & el_type, Array<Real> & tangent_matrix, + GhostType ghost_type) { + AKANTU_DEBUG_IN(); - // copy stresses back - Array<Real>::iterator<Matrix<Real>> stress_it = - this->stress(el_type, ghost_type).begin(dim, dim); + MATERIAL_TANGENT_QUADRATURE_POINT_LOOP_BEGIN(tangent_matrix); - Array<Real>::iterator<Matrix<Real>> 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<Real> & 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 <UInt dim> -void MaterialElasticLinearAnisotropic<dim>::computeTangentModuli( - const ElementType & el_type, Array<Real> & tangent_matrix, - GhostType ghost_type) { +void MaterialElasticLinearAnisotropic<dim>::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<Real>::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 <UInt dim> Real MaterialElasticLinearAnisotropic<dim>::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 <till.junge@epfl.ch> * * @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 <http://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #include "aka_common.hh" #include "material.hh" #include "material_elastic.hh" /* -------------------------------------------------------------------------- */ #include <vector> /* -------------------------------------------------------------------------- */ #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 <UInt Dim> 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<Real> & 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<Real> & grad_u, + Matrix<Real> & sigma) const; + + /// tangent matrix for a given quadrature point + inline void computeTangentModuliOnQuad(Matrix<Real> & tangent) const; + + inline void computePotentialEnergyOnQuad(const Matrix<Real> & grad_u, + const Matrix<Real> & sigma, + Real & epot); + /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: /// compute max wave celerity Real getCelerity(const Element & element) const override; AKANTU_GET_MACRO(VoigtStiffness, C, Matrix<Real>); /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ protected: using voigt_h = VoigtHelper<Dim>; /// direction matrix and vectors std::vector<std::unique_ptr<Vector<Real>>> dir_vecs; Matrix<Real> rot_mat; /// Elastic stiffness tensor in material frame and full vectorised notation Matrix<Real> Cprime; /// Elastic stiffness tensor in voigt notation Matrix<Real> C; /// eigenvalues of stiffness tensor Vector<Real> 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 <enrico.milanese@epfl.ch> + * @author Nicolas Richart <nicolas.richart@epfl.ch> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ + +/* -------------------------------------------------------------------------- */ +#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 <UInt dim> +inline void MaterialElasticLinearAnisotropic<dim>::computeStressOnQuad(const Matrix<Real> & grad_u, + Matrix<Real> & sigma) const { + // Wikipedia convention: + // 2*eps_ij (i!=j) = voigt_eps_I + // http://en.wikipedia.org/wiki/Voigt_notation + Vector<Real> voigt_strain(voigt_h::size); + Vector<Real> 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 <UInt dim> +inline void MaterialElasticLinearAnisotropic<dim>::computeTangentModuliOnQuad(Matrix<Real> & tangent) const { + + tangent.copy(this->C); + +} + +/* -------------------------------------------------------------------------- */ +template <UInt dim> +inline void MaterialElasticLinearAnisotropic<dim>::computePotentialEnergyOnQuad( + const Matrix<Real> & grad_u, const Matrix<Real> & 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 <till.junge@epfl.ch> * @author Nicolas Richart <nicolas.richart@epfl.ch> * @author Marco Vocialta <marco.vocialta@epfl.ch> * * @date creation: Fri Jun 18 2010 * @date last modification: 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 <http://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ #include "material_elastic_orthotropic.hh" #include "solid_mechanics_model.hh" #include <algorithm> namespace akantu { /* -------------------------------------------------------------------------- */ template <UInt Dim> MaterialElasticOrthotropic<Dim>::MaterialElasticOrthotropic( SolidMechanicsModel & model, const ID & id) : MaterialElasticLinearAnisotropic<Dim>(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 <UInt Dim> void MaterialElasticOrthotropic<Dim>::initMaterial() { AKANTU_DEBUG_IN(); Material::initMaterial(); updateInternalParameters(); AKANTU_DEBUG_OUT(); } -/* -------------------------------------------------------------------------- */ -inline Real vector_norm(Vector<Real> & vec) { - Real norm = 0; - for (UInt i = 0; i < vec.size(); ++i) { - norm += vec(i) * vec(i); - } - return std::sqrt(norm); -} - /* -------------------------------------------------------------------------- */ template <UInt Dim> void MaterialElasticOrthotropic<Dim>::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 <UInt dim> -inline void MaterialElasticOrthotropic<dim>::computePotentialEnergyOnQuad( - const Matrix<Real> & grad_u, const Matrix<Real> & sigma, Real & epot) { - epot = .5 * sigma.doubleDot(grad_u); -} - -/* -------------------------------------------------------------------------- */ -template <UInt spatial_dimension> -void MaterialElasticOrthotropic<spatial_dimension>::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<Real>::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 <UInt spatial_dimension> void MaterialElasticOrthotropic<spatial_dimension>:: computePotentialEnergyByElement(ElementType type, UInt index, Vector<Real> & epot_on_quad_points) { AKANTU_DEBUG_ASSERT(!this->finite_deformation, "finite deformation not possible in material orthotropic " "(TO BE IMPLEMENTED)"); Array<Real>::matrix_iterator gradu_it = this->gradu(type).begin(spatial_dimension, spatial_dimension); Array<Real>::matrix_iterator gradu_end = this->gradu(type).begin(spatial_dimension, spatial_dimension); Array<Real>::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<Real> 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 <till.junge@epfl.ch> * @author Marco Vocialta <marco.vocialta@epfl.ch> * * @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 <http://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ #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 <UInt Dim> class MaterialElasticOrthotropic : public MaterialElasticLinearAnisotropic<Dim> { /* ------------------------------------------------------------------------ */ /* 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<Real> & epot_on_quad_points) override; -protected: - inline void computePotentialEnergyOnQuad(const Matrix<Real> & grad_u, - const Matrix<Real> & 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/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 <gtest/gtest.h> /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_TEST_FE_ENGINE_FIXTURE_HH__ #define __AKANTU_TEST_FE_ENGINE_FIXTURE_HH__ using namespace akantu; /// Generic class for FEEngine tests template <typename type_, template <ElementKind> 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<type>::getSpatialDimension(); using FEM = FEEngineTemplate<IntegratorGauss, shape_t, kind>; /// 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<Mesh>(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<FEM>(*mesh, dim, "my_fem"); - fem->initShapeFunctions(); - - nb_quadrature_points_total = fem->getNbIntegrationPoints(type) * nb_element; + nb_quadrature_points_total = + GaussIntegrationElement<type>::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> fem; std::unique_ptr<Mesh> mesh; UInt nb_element; UInt nb_quadrature_points_total; Vector<Real> lower; Vector<Real> upper; }; template <typename type_, template <ElementKind> class shape_t, ElementKind kind> constexpr const ElementType TestFEMBaseFixture<type_, shape_t, kind>::type; template <typename type_, template <ElementKind> class shape_t, ElementKind kind> constexpr const size_t TestFEMBaseFixture<type_, shape_t, kind>::dim; /* -------------------------------------------------------------------------- */ /// Base class for test with Lagrange FEEngine and regular elements template <typename type_> using TestFEMFixture = TestFEMBaseFixture<type_, ShapeLagrange>; /* -------------------------------------------------------------------------- */ using types = gtest_list_t<TestElementTypes>; 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 <nicolas.richart@epfl.ch> * * @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 <http://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #include "test_fe_engine_fixture.hh" /* -------------------------------------------------------------------------- */ #include <gtest/gtest.h> #include <iostream> /* -------------------------------------------------------------------------- */ using namespace akantu; namespace { /* -------------------------------------------------------------------------- */ template <size_t t> using degree_t = std::integral_constant<size_t, t>; /* -------------------------------------------------------------------------- */ using TestDegreeTypes = std::tuple<degree_t<0>, degree_t<1>, degree_t<2>, degree_t<3>, degree_t<4>, degree_t<5>>; std::array<Polynomial<5>, 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 <typename T> class TestGaussIntegrationFixture : public TestFEMFixture<std::tuple_element_t<0, T>> { protected: using parent = TestFEMFixture<std::tuple_element_t<0, T>>; 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<parent::type>::getShapeSize(); Array<Real> shapes(0, shapes_size); this->fem->getShapeFunctions() .template computeShapesOnIntegrationPoints<parent::type>( 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<parent::type>( 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<Real> 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<parent::type, (degree == 0 ? 1 : degree)>(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<Array<Real>, parent::dim> polynomial; Array<Real> integration_points_pos; std::array<Polynomial<5>, 3> polys; }; template <typename T> constexpr size_t TestGaussIntegrationFixture<T>::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<tuple_split_t<50, cross_product_t<TestElementTypes, TestDegreeTypes>>>; INSTANTIATE_TYPED_TEST_CASE_P(Split1, TestGaussIntegrationFixture, TestTypes); using TestTypesTail = gtest_list_t<tuple_split_tail_t<50, cross_product_t<TestElementTypes, TestDegreeTypes>>>; 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 <nicolas.richart@epfl.ch> * * @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 <http://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #include "pybind11_akantu.hh" #include "test_fe_engine_fixture.hh" /* -------------------------------------------------------------------------- */ #include <pybind11/embed.h> #include <pybind11/numpy.h> /* -------------------------------------------------------------------------- */ using namespace akantu; namespace py = pybind11; using namespace py::literals; template <typename type_> class TestFEMPyFixture : public TestFEMFixture<type_> { using parent = TestFEMFixture<type_>; 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<Array<Real>>( 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<Real>(X(s)) = Vector<Real>(nodes[conn(s)]); } } } void TearDown() override { parent::TearDown(); coordinates.reset(nullptr); } protected: std::unique_ptr<Array<Real>> 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<Real> ref_N(this->nb_quadrature_points_total, N.getNbComponent()); // Array<Real> ref_B(this->nb_quadrature_points_total, B.getNbComponent()); Array<Real> 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<L_inf>(); 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 5593e14c0..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,123 +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 <http://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #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<element_type_t<_bernoulli_beam_3>> { using parent = TestFEMStructuralFixture<element_type_t<_bernoulli_beam_3>>; public: /// Load the mesh and provide extra normal direction void readMesh(std::string filename) override { parent::readMesh(filename); auto & normals = this->mesh->registerData<Real>("extra_normal") .alloc(0, dim, type, _not_ghost); Vector<Real> normal = {-36. / 65, -48. / 65, 5. / 13}; normals.push_back(normal); } }; /* -------------------------------------------------------------------------- */ /// Type alias using TestBernoulliB2 = TestFEMStructuralFixture<element_type_t<_bernoulli_beam_2>>; using TestDKT18 = TestFEMStructuralFixture<element_type_t<_discrete_kirchhoff_triangle_18>>; /* -------------------------------------------------------------------------- */ /// Solution for 2D rotation matrices Matrix<Real> 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<const ShapeStruct &>(fem->getShapeFunctions()); auto & rot = shape.getRotations(type); Real a = std::atan(4. / 3); std::vector<Real> 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<L_2>(); EXPECT_NEAR(rotation_error, 0., Math::getTolerance()); } } /* -------------------------------------------------------------------------- */ TEST_F(TestBernoulliB3, PrecomputeRotations) { + this->fem->initShapeFunctions(); using ShapeStruct = ShapeStructural<_ek_structural>; auto & shape = dynamic_cast<const ShapeStruct &>(fem->getShapeFunctions()); auto & rot = shape.getRotations(type); Matrix<Real> ref = {{3. / 13, 4. / 13, 12. / 13}, {-4. / 5, 3. / 5, 0}, {-36. / 65, -48. / 65, 5. / 13}}; Matrix<Real> 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<L_2>(); EXPECT_NEAR(rotation_error, 0., Math::getTolerance()); } } /* -------------------------------------------------------------------------- */ TEST_F(TestDKT18, PrecomputeRotations) { + this->fem->initShapeFunctions(); using ShapeStruct = ShapeStructural<_ek_structural>; auto & shape = dynamic_cast<const ShapeStruct &>(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 <nicolas.richart@epfl.ch> * @author Peter Spijker <peter.spijker@epfl.ch> * * @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 <http://www.gnu.org/licenses/>. * * @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 <cstdlib> #include <iostream> /* -------------------------------------------------------------------------- */ 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<Real> 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<Real> 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<Real> 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<Real>::eye(UInt(dim)); for(auto && grad : make_view(grad_coord_on_quad, dim, dim)) { auto diff = (I - grad).template norm<L_inf>(); 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 <guillaume.anciaux@epfl.ch> * @author Nicolas Richart <nicolas.richart@epfl.ch> * @author Peter Spijker <peter.spijker@epfl.ch> * * @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 <http://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #include "test_fe_engine_fixture.hh" /* -------------------------------------------------------------------------- */ #include <cstdlib> #include <iostream> /* -------------------------------------------------------------------------- */ using namespace akantu; namespace { TYPED_TEST(TestFEMFixture, IntegrateConstant) { + this->fem->initShapeFunctions(); + const auto type = this->type; const auto & position = this->fem->getMesh().getNodes(); Array<Real> const_val(position.size(), 2, "const_val"); Array<Real> val_on_quad(this->nb_quadrature_points_total, 2, "val_on_quad"); Vector<Real> 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<Real> 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<Real> sum{0., 0.}; for (auto && int_ : make_view(int_val_on_elem, 2)) { sum += int_; } auto diff = (value - sum).template norm<L_inf>(); 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 <guillaume.anciaux@epfl.ch> * * @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 <http://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #include "test_fe_engine_fixture.hh" /* -------------------------------------------------------------------------- */ using namespace akantu; namespace { TYPED_TEST(TestFEMFixture, InverseMap) { + this->fem->initShapeFunctions(); + Matrix<Real> quad = GaussIntegrationElement<TestFixture::type>::getQuadraturePoints(); const auto & position = this->fem->getMesh().getNodes(); /// get the quadrature points coordinates Array<Real> 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<Real> natural_coords(this->dim); auto length = (this->upper - this->lower).template norm<L_inf>(); 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<Real> quad_coord = quads_coords(q); Vector<Real> 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_materials/test_elastic_materials.cc b/test/test_model/test_solid_mechanics_model/test_materials/test_elastic_materials.cc index 98b0cb329..ed3baeb2f 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,879 @@ /* -------------------------------------------------------------------------- */ #include "material_elastic.hh" #include "material_elastic_orthotropic.hh" #include "solid_mechanics_model.hh" #include "test_material_fixtures.hh" /* -------------------------------------------------------------------------- */ #include <gtest/gtest.h> #include <type_traits> /* -------------------------------------------------------------------------- */ using namespace akantu; using types = ::testing::Types<Traits<MaterialElastic, 1>, Traits<MaterialElastic, 2>, Traits<MaterialElastic, 3>, Traits<MaterialElasticOrthotropic, 1>, Traits<MaterialElasticOrthotropic, 2>, Traits<MaterialElasticOrthotropic, 3>, Traits<MaterialElasticLinearAnisotropic, 1>, Traits<MaterialElasticLinearAnisotropic, 2>, Traits<MaterialElasticLinearAnisotropic, 3>>; /* -------------------------------------------------------------------------- */ -template <> void FriendMaterial<MaterialElastic<3>>::testPushWaveSpeed() { +template <> void FriendMaterial<MaterialElastic<1>>::testComputeStress() { + Real E = 3.; + setParam("E", E); + + Matrix<Real> eps = {{2}}; + Matrix<Real> 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<MaterialElastic<1>>::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<MaterialElastic<1>>::testComputeTangentModuli() { + Real E = 2; + setParam("E", E); + Matrix<Real> tangent(1, 1); + this->computeTangentModuliOnQuad(tangent); + ASSERT_NEAR(tangent(0, 0), E, 1e-14); +} + +/* -------------------------------------------------------------------------- */ +template <> void FriendMaterial<MaterialElastic<1>>::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<MaterialElastic<1>>::testShearWaveSpeed() { + ASSERT_THROW(this->getShearWaveSpeed(Element()), debug::Exception); +} + +/* -------------------------------------------------------------------------- */ +template <> void FriendMaterial<MaterialElastic<2>>::testComputeStress() { + Real E = 1.; + Real nu = .3; + Real sigma_th = 0.3; // thermal stress + setParam("E", E); + setParam("nu", nu); + + Matrix<Real> rotation_matrix = getRandomRotation2d(); + + auto grad_u = this->getDeviatoricStrain(1.).block(0, 0, 2, 2); + + auto grad_u_rot = this->applyRotation(grad_u, rotation_matrix); + + Matrix<Real> sigma_rot(2, 2); + this->computeStressOnQuad(grad_u_rot, sigma_rot, sigma_th); + + auto sigma = this->reverseRotation(sigma_rot, rotation_matrix); + + Matrix<Real> identity(2, 2); + identity.eye(); + + Matrix<Real> sigma_expected = + 0.5 * E / (1 + nu) * (grad_u + grad_u.transpose()) + sigma_th * identity; + + auto diff = sigma - sigma_expected; + Real stress_error = diff.norm<L_inf>(); + ASSERT_NEAR(stress_error, 0., 1e-14); +} + +/* -------------------------------------------------------------------------- */ +template <> void FriendMaterial<MaterialElastic<2>>::testEnergyDensity() { + Matrix<Real> sigma = {{1, 2}, {2, 4}}; + Matrix<Real> 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<MaterialElastic<2>>::testComputeTangentModuli() { + Real E = 1.; + Real nu = .3; + setParam("E", E); + setParam("nu", nu); + Matrix<Real> tangent(3, 3); + + /* Plane Strain */ + // clang-format off + Matrix<Real> 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<L_2>(); + 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); + + this->computeTangentModuliOnQuad(tangent); + tangent_error = (tangent - solution).norm<L_2>(); + ASSERT_NEAR(tangent_error, 0, 1e-14); +} + +/* -------------------------------------------------------------------------- */ +template <> void FriendMaterial<MaterialElastic<2>>::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()); Real K = E / (3 * (1 - 2 * nu)); Real mu = E / (2 * (1 + nu)); Real sol = std::sqrt((K + 4. / 3 * mu) / rho); ASSERT_NEAR(wave_speed, sol, 1e-14); } /* -------------------------------------------------------------------------- */ -template <> void FriendMaterial<MaterialElastic<3>>::testShearWaveSpeed() { +template <> void FriendMaterial<MaterialElastic<2>>::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()); Real mu = E / (2 * (1 + nu)); Real sol = std::sqrt(mu / rho); ASSERT_NEAR(wave_speed, sol, 1e-14); } /* -------------------------------------------------------------------------- */ template <> void FriendMaterial<MaterialElastic<3>>::testComputeStress() { Real E = 1.; Real nu = .3; Real sigma_th = 0.3; // thermal stress setParam("E", E); setParam("nu", nu); Matrix<Real> rotation_matrix = getRandomRotation3d(); auto grad_u = this->getDeviatoricStrain(1.); auto grad_u_rot = this->applyRotation(grad_u, rotation_matrix); Matrix<Real> sigma_rot(3, 3); this->computeStressOnQuad(grad_u_rot, sigma_rot, sigma_th); auto sigma = this->reverseRotation(sigma_rot, rotation_matrix); Matrix<Real> identity(3, 3); identity.eye(); Matrix<Real> sigma_expected = 0.5 * E / (1 + nu) * (grad_u + grad_u.transpose()) + sigma_th * identity; auto diff = sigma - sigma_expected; Real stress_error = diff.norm<L_inf>(); ASSERT_NEAR(stress_error, 0., 1e-14); } +/* -------------------------------------------------------------------------- */ +template <> void FriendMaterial<MaterialElastic<3>>::testEnergyDensity() { + Matrix<Real> sigma = {{1, 2, 3}, {2, 4, 5}, {3, 5, 6}}; + Matrix<Real> 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<MaterialElastic<3>>::testComputeTangentModuli() { Real E = 1.; Real nu = .3; setParam("E", E); setParam("nu", nu); Matrix<Real> tangent(6, 6); // clang-format off Matrix<Real> 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<L_2>(); ASSERT_NEAR(tangent_error, 0, 1e-14); } /* -------------------------------------------------------------------------- */ -template <> void FriendMaterial<MaterialElastic<3>>::testEnergyDensity() { - Matrix<Real> sigma = {{1, 2, 3}, {2, 4, 5}, {3, 5, 6}}; - Matrix<Real> 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<MaterialElastic<1>>::testComputeStress() { - Real E = 3.; - setParam("E", E); - - Matrix<Real> eps = {{2}}; - Matrix<Real> 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<MaterialElastic<1>>::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<MaterialElastic<1>>::testComputeTangentModuli() { - Real E = 2; - setParam("E", E); - Matrix<Real> tangent(1, 1); - this->computeTangentModuliOnQuad(tangent); - ASSERT_NEAR(tangent(0, 0), E, 1e-14); -} - -/* -------------------------------------------------------------------------- */ -template <> void FriendMaterial<MaterialElastic<1>>::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<MaterialElastic<1>>::testShearWaveSpeed() { - ASSERT_THROW(this->getShearWaveSpeed(Element()), debug::Exception); -} - -/* -------------------------------------------------------------------------- */ -template <> void FriendMaterial<MaterialElastic<2>>::testPushWaveSpeed() { +template <> void FriendMaterial<MaterialElastic<3>>::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()); Real K = E / (3 * (1 - 2 * nu)); Real mu = E / (2 * (1 + nu)); Real sol = std::sqrt((K + 4. / 3 * mu) / rho); ASSERT_NEAR(wave_speed, sol, 1e-14); } /* -------------------------------------------------------------------------- */ -template <> void FriendMaterial<MaterialElastic<2>>::testShearWaveSpeed() { +template <> void FriendMaterial<MaterialElastic<3>>::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()); Real mu = E / (2 * (1 + nu)); Real sol = std::sqrt(mu / rho); ASSERT_NEAR(wave_speed, sol, 1e-14); } /* -------------------------------------------------------------------------- */ -template <> void FriendMaterial<MaterialElastic<2>>::testComputeStress() { - Real E = 1.; - Real nu = .3; - Real sigma_th = 0.3; // thermal stress - setParam("E", E); - setParam("nu", nu); - +template <> void FriendMaterial<MaterialElasticOrthotropic<2>>::testComputeStress() { + UInt Dim = 2; + Real E1 = 1.; + Real E2 = 2.; + Real nu12 = 0.1; + Real G12 = 2.; + + setParamNoUpdate("E1", E1); + setParamNoUpdate("E2", E2); + setParamNoUpdate("nu12", nu12); + setParamNoUpdate("G12", G12); + + // material frame of reference is rotate by rotation_matrix starting from + // canonical basis Matrix<Real> 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 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<Real> v1(Dim); + Vector<Real> 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(); - auto grad_u_rot = this->applyRotation(grad_u, rotation_matrix); + // gradient in material frame of reference + auto grad_u = ( this->getDeviatoricStrain(2.) + this->getHydrostaticStrain(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<Real> 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<Real> identity(2, 2); - identity.eye(); +// 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<Real> sigma_expected = - 0.5 * E / (1 + nu) * (grad_u + grad_u.transpose()) + sigma_th * identity; + Matrix<Real> 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<Real> epsilon = 0.5 * ( grad_u + grad_u.transpose() ); +// sigma_expected is computed directly in the *material* frame of reference + Matrix<Real> 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<L_inf>(); - ASSERT_NEAR(stress_error, 0., 1e-14); + ASSERT_NEAR(stress_error, 0., 1e-13); } /* -------------------------------------------------------------------------- */ -template <> -void FriendMaterial<MaterialElastic<2>>::testComputeTangentModuli() { - Real E = 1.; - Real nu = .3; - setParam("E", E); - setParam("nu", nu); - Matrix<Real> tangent(3, 3); +template <> void FriendMaterial<MaterialElasticOrthotropic<2>>::testEnergyDensity() { + Matrix<Real> sigma = {{1, 2}, {2, 4}}; + Matrix<Real> 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<Real> 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)); +/* -------------------------------------------------------------------------- */ +template <> void FriendMaterial<MaterialElasticOrthotropic<2>>::testComputeTangentModuli() { + + // Note: for this test material and canonical basis coincide + Vector<Real> n1 = {1, 0}; + Vector<Real> 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<Real> 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<Real> tangent(3,3); this->computeTangentModuliOnQuad(tangent); - Real tangent_error = (tangent - solution).norm<L_2>(); + + Real tangent_error = (tangent - C_expected).norm<L_2>(); ASSERT_NEAR(tangent_error, 0, 1e-14); +} - /* Plane Stress */ - this->plane_stress = true; +/* -------------------------------------------------------------------------- */ +template <> void FriendMaterial<MaterialElasticOrthotropic<3>>::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<Real> 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<Real> v1(Dim); + Vector<Real> v2(Dim); + Vector<Real> 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(); - // clang-format off - solution = { - {1, nu, 0}, - {nu, 1, 0}, - {0, 0, (1 - nu) / 2}, - }; - // clang-format on - solution *= E / (1 - nu * nu); + // gradient in material frame of reference + auto grad_u = this->getDeviatoricStrain(2.) + this->getHydrostaticStrain(1.); + + // 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<Real> 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<Real> 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<Real> epsilon = 0.5 * ( grad_u + grad_u.transpose() ); + + // sigma_expected is computed directly in the *material* frame of reference + Matrix<Real> 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<L_inf>(); + ASSERT_NEAR(stress_error, 0., 1e-13); +} + +/* -------------------------------------------------------------------------- */ +template <> void FriendMaterial<MaterialElasticOrthotropic<3>>::testEnergyDensity() { + Matrix<Real> sigma = {{1, 2, 3}, {2, 4, 5}, {3, 5, 6}}; + Matrix<Real> 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<MaterialElasticOrthotropic<3>>::testComputeTangentModuli() { + + // Note: for this test material and canonical basis coincide + UInt Dim = 3; + Vector<Real> n1 = {1, 0, 0}; + Vector<Real> n2 = {0, 1, 0}; + Vector<Real> 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<Real> 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; + + Matrix<Real> tangent(6,6); this->computeTangentModuliOnQuad(tangent); - tangent_error = (tangent - solution).norm<L_2>(); + + Real tangent_error = (tangent - C_expected).norm<L_2>(); ASSERT_NEAR(tangent_error, 0, 1e-14); } /* -------------------------------------------------------------------------- */ -template <> void FriendMaterial<MaterialElastic<2>>::testEnergyDensity() { +template <> void FriendMaterial<MaterialElasticLinearAnisotropic<2>>::testComputeStress() { + UInt Dim = 2; + Matrix<Real> 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<Real> rotation_matrix = getRandomRotation2d(); + + // 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<Real> v1(Dim); + Vector<Real> 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->getDeviatoricStrain(2.) + this->getHydrostaticStrain(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<Real> 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); + + // epsilon is computed directly in the *material* frame of reference + Matrix<Real> epsilon = 0.5 * ( grad_u + grad_u.transpose() ); + Vector<Real> epsilon_voigt(3); + epsilon_voigt(0) = epsilon(0,0); + epsilon_voigt(1) = epsilon(1,1); + epsilon_voigt(2) = 2 * epsilon(0,1); + + // sigma_expected is computed directly in the *material* frame of reference + Vector<Real> sigma_voigt = C * epsilon_voigt; + Matrix<Real> 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<L_inf>(); + ASSERT_NEAR(stress_error, 0., 1e-13); +} + +/* -------------------------------------------------------------------------- */ +template <> void FriendMaterial<MaterialElasticLinearAnisotropic<2>>::testEnergyDensity() { Matrix<Real> sigma = {{1, 2}, {2, 4}}; Matrix<Real> 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<MaterialElasticLinearAnisotropic<2>>::testComputeTangentModuli() { + + // Note: for this test material and canonical basis coincide + Matrix<Real> 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)); + + // set internal Cijkl matrix expressed in the canonical frame of reference + this->updateInternalParameters(); + + Matrix<Real> tangent(3,3); + this->computeTangentModuliOnQuad(tangent); + + Real tangent_error = (tangent - C).norm<L_2>(); + ASSERT_NEAR(tangent_error, 0, 1e-14); +} + +/* -------------------------------------------------------------------------- */ +template <> void FriendMaterial<MaterialElasticLinearAnisotropic<3>>::testComputeStress() { + UInt Dim = 3; + Matrix<Real> 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)); + + // material frame of reference is rotate by rotation_matrix starting from + // canonical basis + Matrix<Real> 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<Real> v1(Dim); + Vector<Real> v2(Dim); + Vector<Real> 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->getDeviatoricStrain(2.) + this->getHydrostaticStrain(1.); + + // 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<Real> 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<Real> epsilon = 0.5 * ( grad_u + grad_u.transpose() ); + Vector<Real> 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<Real> sigma_voigt = C * epsilon_voigt; + Matrix<Real> 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<L_inf>(); + ASSERT_NEAR(stress_error, 0., 1e-13); +} + +/* -------------------------------------------------------------------------- */ +template <> void FriendMaterial<MaterialElasticLinearAnisotropic<3>>::testEnergyDensity() { + Matrix<Real> sigma = {{1, 2, 3}, {2, 4, 5}, {3, 5, 6}}; + Matrix<Real> 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<MaterialElasticLinearAnisotropic<3>>::testComputeTangentModuli() { + + // Note: for this test material and canonical basis coincide + Matrix<Real> 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<Real> tangent(6,6); + this->computeTangentModuliOnQuad(tangent); + + Real tangent_error = (tangent - C).norm<L_2>(); + ASSERT_NEAR(tangent_error, 0, 1e-14); +} + /* -------------------------------------------------------------------------- */ namespace { template <typename T> class TestElasticMaterialFixture : public ::TestMaterialFixture<T> {}; 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, ElasticComputeShearWaveSpeed) { this->material->testShearWaveSpeed(); } } // namespace 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..c0a846674 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,181 @@ /* -------------------------------------------------------------------------- */ #include "material_elastic.hh" #include "solid_mechanics_model.hh" /* -------------------------------------------------------------------------- */ #include <gtest/gtest.h> #include <random> #include <type_traits> /* -------------------------------------------------------------------------- */ #define TO_IMPLEMENT AKANTU_EXCEPTION("TEST TO IMPLEMENT") using namespace akantu; /* -------------------------------------------------------------------------- */ template <typename T> 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; } static inline Matrix<Real> getDeviatoricStrain(Real intensity); static inline Matrix<Real> getHydrostaticStrain(Real intensity); static inline const Matrix<Real> reverseRotation(Matrix<Real> mat, Matrix<Real> rotation_matrix) { Matrix<Real> R = rotation_matrix; Matrix<Real> m2 = mat * R; Matrix<Real> m1 = R.transpose(); return m1 * m2; }; static inline const Matrix<Real> applyRotation(Matrix<Real> mat, const Matrix<Real> rotation_matrix) { Matrix<Real> R = rotation_matrix; Matrix<Real> m2 = mat * R.transpose(); Matrix<Real> m1 = R; return m1 * m2; }; static inline Matrix<Real> getRandomRotation3d(); static inline Matrix<Real> getRandomRotation2d(); using T::T; }; /* -------------------------------------------------------------------------- */ template <typename T> Matrix<Real> FriendMaterial<T>::getDeviatoricStrain(Real intensity) { Matrix<Real> dev = {{0, 1, 2}, {1, 0, 3}, {2, 3, 0}}; dev *= intensity; return dev; } /* -------------------------------------------------------------------------- */ template <typename T> Matrix<Real> FriendMaterial<T>::getHydrostaticStrain(Real intensity) { Matrix<Real> dev = {{1, 0, 0}, {0, 2, 0}, {0, 0, 3}}; dev *= intensity; return dev; } /* -------------------------------------------------------------------------- */ template <typename T> Matrix<Real> FriendMaterial<T>::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<Real> v1(Dim); Vector<Real> v2(Dim); Vector<Real> 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<Real> test_axis(3); + test_axis.crossProduct(v1, v2); + test_axis -= v3; + if (test_axis.norm() > 8 * std::numeric_limits<Real>::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<Real> 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 <typename T> Matrix<Real> FriendMaterial<T>::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<Real> v1(Dim); - Vector<Real> 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<Real> v1(3); + Vector<Real> v2(3); + Vector<Real> 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<Real> mat(Dim, Dim); for (UInt i = 0; i < Dim; ++i) { mat(0, i) = v1[i]; mat(1, i) = v2[i]; } return mat; } /* -------------------------------------------------------------------------- */ template <typename T> 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<Mesh>(spatial_dimension); model = std::make_unique<SolidMechanicsModel>(*mesh); material = std::make_unique<friend_class>(*model); } void TearDown() override { material.reset(nullptr); model.reset(nullptr); mesh.reset(nullptr); } using friend_class = FriendMaterial<mat_class>; protected: std::unique_ptr<Mesh> mesh; std::unique_ptr<SolidMechanicsModel> model; std::unique_ptr<friend_class> material; }; /* -------------------------------------------------------------------------- */ template <template <UInt> class T, UInt _Dim> struct Traits { using mat_class = T<_Dim>; static constexpr UInt Dim = _Dim; }; /* -------------------------------------------------------------------------- */ diff --git a/test/test_model/test_structural_mechanics_model/test_structural_mechanics_model_discrete_kirchhoff_triangle_18.cc b/test/test_model/test_structural_mechanics_model/test_structural_mechanics_model_discrete_kirchhoff_triangle_18.cc index bac1f8c70..b2d1070d9 100644 --- a/test/test_model/test_structural_mechanics_model/test_structural_mechanics_model_discrete_kirchhoff_triangle_18.cc +++ b/test/test_model/test_structural_mechanics_model/test_structural_mechanics_model_discrete_kirchhoff_triangle_18.cc @@ -1,101 +1,99 @@ /** * @file test_structural_mechanics_model_bernoulli_beam_2.cc * * @author Fabian Barras <fabian.barras@epfl.ch> * @author Lucas Frérot <lucas.frerot@epfl.ch> * * @date creation: Fri Jul 15 2011 * @date last modification: Sun Oct 19 2014 * * @brief Computation of the analytical exemple 1.1 in the TGC vol 6 * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see <http://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #include "test_structural_mechanics_model_fixture.hh" /* -------------------------------------------------------------------------- */ #include <gtest/gtest.h> using namespace akantu; /* -------------------------------------------------------------------------- */ class TestStructDKT18 : public TestStructuralFixture<element_type_t<_discrete_kirchhoff_triangle_18>> { using parent = TestStructuralFixture<element_type_t<_discrete_kirchhoff_triangle_18>>; public: void addMaterials() override { mat.E = 1; mat.t = 1; mat.nu = 0.3; this->model->addMaterial(mat); } void assignMaterials() override { auto & materials = this->model->getElementMaterial(parent::type); std::fill(materials.begin(), materials.end(), 0); } void setDirichlets() override { - std::fill(this->model->getBlockedDOFs().begin(), - this->model->getBlockedDOFs().end(), true); + this->model->getBlockedDOFs().set(true); auto center_node = this->model->getBlockedDOFs().end(parent::ndof) - 1; - // clang-format off *center_node = {false, false, false, false, false, true}; - // clang-format on this->model->getDisplacement().clear(); auto disp = ++this->model->getDisplacement().begin(parent::ndof); // Displacement field from Batoz Vol. 2 p. 392 // with theta to beta correction from discrete Kirchhoff condition - //clang-format off + // clang-format off *disp = {40, 20, -800 , -20, 40, 0}; ++disp; *disp = {50, 40, -1400, -40, 50, 0}; ++disp; *disp = {10, 20, -200 , -20, 10, 0}; ++disp; - //clang-format on + // clang-format on } void setNeumanns() override {} protected: StructuralMaterial mat; }; /* -------------------------------------------------------------------------- */ // Batoz Vol 2. patch test, ISBN 2-86601-259-3 - TEST_F(TestStructDKT18, TestDisplacements) { model->solveStep(); Vector<Real> solution = {22.5, 22.5, -337.5, -22.5, 22.5, 0}; + auto nb_nodes = this->model->getDisplacement().size(); Vector<Real> center_node_disp = - model->getDisplacement().end(model->getSpatialDimension())[-1]; + model->getDisplacement().begin(solution.size())[nb_nodes - 1]; auto error = solution - center_node_disp; + std::cout << center_node_disp << std::endl; Real tol = Math::getTolerance(); EXPECT_NEAR(error.norm<L_2>(), 0., tol); }