diff --git a/src/common/T4_map_proxy.hh b/src/common/T4_map_proxy.hh index d151f66..3ea93c8 100644 --- a/src/common/T4_map_proxy.hh +++ b/src/common/T4_map_proxy.hh @@ -1,192 +1,193 @@ /** * file T4_map_proxy.hh * * @author Till Junge * * @date 19 Nov 2017 * * @brief Map type to allow fourth-order tensor-like maps on 2D matrices * * @section LICENCE * * Copyright (C) 2017 Till Junge * * µSpectre is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3, or (at * your option) any later version. * * µSpectre is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Emacs; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include #include "common/eigen_tools.hh" #ifndef T4_MAP_PROXY_H #define T4_MAP_PROXY_H namespace muSpectre { /** * simple adapter function to create a matrix that can be mapped as a tensor */ template using T4Mat = Eigen::Matrix; template using T4MatMap = std::conditional_t>, Eigen::Map>>; template - inline decltype(auto) get(T4&& t4, Dim_t i, Dim_t j, Dim_t k, Dim_t l) { + inline auto get(T4&& t4, Dim_t i, Dim_t j, Dim_t k, Dim_t l) + -> decltype(t4.coeffRef(i,j)) { constexpr Dim_t Dim{EigenCheck::Tensor4Dim(t4)}; const auto myColStride{ (t4.colStride() == 1) ? t4.colStride(): t4.colStride()/Dim}; const auto myRowStride{ (t4.rowStride() == 1) ? t4.rowStride(): t4.rowStride()/Dim}; return t4.coeffRef(i * myRowStride + j * myColStride, k * myRowStride + l * myColStride); } /* ---------------------------------------------------------------------- */ /** Proxy class mapping a fourth-order tensor onto a 2D matrix (in order to avoid the use of Eigen::Tensor. This class is, however byte-compatible with Tensors (i.e., you can map this onto a tensor instead of a matrix) **/ template > class T4Map: public Eigen::MapBase> { public: typedef Eigen::MapBase Base; EIGEN_DENSE_PUBLIC_INTERFACE(T4Map); using matrix_type = Eigen::Matrix; using PlainObjectType = std::conditional_t; using ConstType = T4Map; using Base::colStride; using Base::rowStride; using Base::IsRowMajor; typedef typename Base::PointerType PointerType; typedef PointerType PointerArgType; using trueScalar = std::conditional_t; EIGEN_DEVICE_FUNC inline PointerType cast_to_pointer_type(PointerArgType ptr) { return ptr; } EIGEN_DEVICE_FUNC inline Eigen::Index innerStride() const { return StrideType::InnerStrideAtCompileTime != 0 ? m_stride.inner() : 1; } template inline T4Map & operator=(const Eigen::MatrixBase & other) { this->map = other; return *this; } EIGEN_DEVICE_FUNC inline Eigen::Index outerStride() const { return StrideType::OuterStrideAtCompileTime != 0 ? m_stride.outer() : IsVectorAtCompileTime ? this->size() : int(Flags)&Eigen::RowMajorBit ? this->cols() : this->rows(); } /** Constructor in the fixed-size case. * * \param dataPtr pointer to the array to map * \param stride optional Stride object, passing the strides. */ EIGEN_DEVICE_FUNC explicit inline T4Map(PointerArgType dataPtr, const StrideType& stride = StrideType()) : Base(cast_to_pointer_type(dataPtr)), m_stride(stride), map(cast_to_pointer_type(dataPtr)) { PlainObjectType::Base::_check_template_params(); } EIGEN_INHERIT_ASSIGNMENT_OPERATORS(T4Map); /** My accessor to mimick tensorial access **/ inline const Scalar& operator()(Dim_t i, Dim_t j, Dim_t k, Dim_t l ) const { const auto myColStride{ (colStride() == 1) ? colStride(): colStride()/Dim}; const auto myRowStride{ (rowStride() == 1) ? rowStride(): rowStride()/Dim}; return this->map.coeff(i * myRowStride + j * myColStride, k * myRowStride + l * myColStride); } inline trueScalar& operator()(Dim_t i, Dim_t j, Dim_t k, Dim_t l ) { const auto myColStride{ (colStride() == 1) ? colStride(): colStride()/Dim}; const auto myRowStride{ (rowStride() == 1) ? rowStride(): rowStride()/Dim}; return this->map.coeffRef(i * myRowStride + j * myColStride, k * myRowStride + l * myColStride); } protected: StrideType m_stride; Eigen::Map map; }; } // muSpectre namespace Eigen { //! forward declarations template struct traits; /* ---------------------------------------------------------------------- */ namespace internal { template struct traits > : public traits> { using PlainObjectType = Matrix; typedef traits TraitsBase; enum { InnerStrideAtCompileTime = StrideType::InnerStrideAtCompileTime == 0 ? int(PlainObjectType::InnerStrideAtCompileTime) : int(StrideType::InnerStrideAtCompileTime), OuterStrideAtCompileTime = StrideType::OuterStrideAtCompileTime == 0 ? int(PlainObjectType::OuterStrideAtCompileTime) : int(StrideType::OuterStrideAtCompileTime), Alignment = int(MapOptions)&int(AlignedMask), Flags0 = TraitsBase::Flags & (~NestByRefBit), Flags = is_lvalue::value ? int(Flags0) : (int(Flags0) & ~LvalueBit) }; private: enum { Options }; // Expressions don't have Options }; } // namespace internal } // namespace Eigen #endif /* T4_MAP_PROXY_H */ diff --git a/src/common/tensor_algebra.hh b/src/common/tensor_algebra.hh index f2d3c83..4404214 100644 --- a/src/common/tensor_algebra.hh +++ b/src/common/tensor_algebra.hh @@ -1,278 +1,280 @@ /** * file tensor_algebra.hh * * @author Till Junge * * @date 05 Nov 2017 * * @brief collection of compile-time quantities and algrebraic functions for * tensor operations * * @section LICENCE * * Copyright (C) 2017 Till Junge * * µSpectre is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3, or (at * your option) any later version. * * µSpectre is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Emacs; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef TENSOR_ALGEBRA_H #define TENSOR_ALGEBRA_H #include #include #include #include "common/T4_map_proxy.hh" #include "common/common.hh" #include "common/eigen_tools.hh" namespace muSpectre { namespace Tensors { template using Tens2_t = Eigen::TensorFixedSize>; template using Tens4_t = Eigen::TensorFixedSize>; //----------------------------------------------------------------------------// //! compile-time second-order identity template constexpr inline Tens2_t I2() { Tens2_t T; using Mat_t = Eigen::Matrix; Eigen::Map(&T(0, 0)) = Mat_t::Identity(); return T; } /* ---------------------------------------------------------------------- */ //! Check whether a given expression represents a Tensor specified order template struct is_tensor { constexpr static bool value = (std::is_convertible >::value || std::is_convertible >::value || std::is_convertible >::value); }; /* ---------------------------------------------------------------------- */ /** compile-time outer tensor product as defined by Curnier * R_ijkl = A_ij.B_klxx * 0123 01 23 */ template constexpr inline decltype(auto) outer(T1 && A, T2 && B) { // Just make sure that the right type of parameters have been given constexpr Dim_t order{2}; static_assert(is_tensor::value, "T1 needs to be convertible to a second order Tensor"); static_assert(is_tensor::value, "T2 needs to be convertible to a second order Tensor"); // actual function std::array, 0> dims{}; return A.contract(B, dims); } /* ---------------------------------------------------------------------- */ /** compile-time underlined outer tensor product as defined by Curnier * R_ijkl = A_ik.B_jlxx * 0123 02 13 * 0213 01 23 <- this defines the shuffle order */ template constexpr inline decltype(auto) outer_under(T1 && A, T2 && B) { constexpr size_t order{4}; return outer(A, B).shuffle(std::array{0, 2, 1, 3}); } /* ---------------------------------------------------------------------- */ /** compile-time overlined outer tensor product as defined by Curnier * R_ijkl = A_il.B_jkxx * 0123 03 12 * 0231 01 23 <- this defines the shuffle order */ template constexpr inline decltype(auto) outer_over(T1 && A, T2 && B) { constexpr size_t order{4}; return outer(A, B).shuffle(std::array{0, 2, 3, 1}); } //! compile-time fourth-order symmetrising identity template constexpr inline Tens4_t I4S() { auto I = I2(); return 0.5*(outer_under(I, I) + outer_over(I, I)); } } // Tensors + + namespace Matrices { template using Tens2_t = Eigen::Matrix; template using Tens4_t = T4Mat; //----------------------------------------------------------------------------// //! compile-time second-order identity template constexpr inline Tens2_t I2() { return Tens2_t::Identity(); } /* ---------------------------------------------------------------------- */ /** compile-time outer tensor product as defined by Curnier * R_ijkl = A_ij.B_klxx * 0123 01 23 */ template constexpr inline decltype(auto) outer(T1 && A, T2 && B) { // Just make sure that the right type of parameters have been given constexpr Dim_t dim{EigenCheck::TensorDim(A)}; static_assert((dim == EigenCheck::TensorDim(B)), "A and B do not have the same dimension"); Tens4_t product; for (Dim_t i = 0; i < dim; ++i) { for (Dim_t j = 0; j < dim; ++j) { for (Dim_t k = 0; k < dim; ++k) { for (Dim_t l = 0; l < dim; ++l) { get(product, i, j, k, l) = A(i, j) * B(k, l); } } } } return product; } /* ---------------------------------------------------------------------- */ /** compile-time underlined outer tensor product as defined by Curnier * R_ijkl = A_ik.B_jlxx * 0123 02 13 * 0213 01 23 <- this defines the shuffle order */ template constexpr inline decltype(auto) outer_under(T1 && A, T2 && B) { // Just make sure that the right type of parameters have been given constexpr Dim_t dim{EigenCheck::TensorDim(A)}; static_assert((dim == EigenCheck::TensorDim(B)), "A and B do not have the same dimension"); Tens4_t product; for (Dim_t i = 0; i < dim; ++i) { for (Dim_t j = 0; j < dim; ++j) { for (Dim_t k = 0; k < dim; ++k) { for (Dim_t l = 0; l < dim; ++l) { get(product, i, j, k, l) = A(i, k) * B(j, l); } } } } return product; } /* ---------------------------------------------------------------------- */ /** compile-time overlined outer tensor product as defined by Curnier * R_ijkl = A_il.B_jkxx * 0123 03 12 * 0231 01 23 <- this defines the shuffle order */ template constexpr inline decltype(auto) outer_over(T1 && A, T2 && B) { // Just make sure that the right type of parameters have been given constexpr Dim_t dim{EigenCheck::TensorDim(A)}; static_assert((dim == EigenCheck::TensorDim(B)), "A and B do not have the same dimension"); Tens4_t product; for (Dim_t i = 0; i < dim; ++i) { for (Dim_t j = 0; j < dim; ++j) { for (Dim_t k = 0; k < dim; ++k) { for (Dim_t l = 0; l < dim; ++l) { get(product, i, j, k, l) = A(i, l) * B(j, k); } } } } return product; } /** * Standart tensor multiplication */ template constexpr inline decltype(auto) tensmult(T4 && A, T2 && B) { constexpr Dim_t dim{EigenCheck::Tensor4Dim(A)}; static_assert((dim == EigenCheck::TensorDim(B)), "Dimensionality check failed. Expects A to be a fourth-" "order tensor of dimension dim and B to be a second-order " "Tensor of same dimension"); Tens2_t result; result.setZero(); for (Dim_t i = 0; i < dim; ++i) { for (Dim_t j = 0; j < dim; ++j) { for (Dim_t k = 0; k < dim; ++k) { for (Dim_t l = 0; l < dim; ++l) { result(i,j) +=get(A, i, j, k, l) * B(k, l); } } } } return result; } //! compile-time fourth-order tracer template constexpr inline Tens4_t Itrac() { auto I = I2(); return outer(I,I); } //! compile-time fourth-order identity template constexpr inline Tens4_t Iiden() { auto I = I2(); return outer_under(I,I); } //! compile-time fourth-order transposer template constexpr inline Tens4_t Itrns() { auto I = I2(); return outer_over(I,I); } //! compile-time fourth-order symmetriser template constexpr inline Tens4_t Isymm() { auto I = I2(); return 0.5*(outer_under(I, I) + outer_over(I, I)); } } // Matrices } // muSpectre #endif /* TENSOR_ALGEBRA_H */ diff --git a/src/common/test_goodies.hh b/src/common/test_goodies.hh index 47e5e68..fbb0e15 100644 --- a/src/common/test_goodies.hh +++ b/src/common/test_goodies.hh @@ -1,70 +1,79 @@ /** * file test_goodies.hh * * @author Till Junge * * @date 27 Sep 2017 * * @brief helpers for testing * * @section LICENCE * * Copyright (C) 2017 Till Junge * * µSpectre is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3, or (at * your option) any later version. * * µSpectre is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Emacs; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #ifndef TEST_GOODIES_H #define TEST_GOODIES_H namespace muSpectre { namespace testGoodies { + template + struct dimFixture{ + constexpr static Dim_t dim{Dim}; + }; + + using dimlist = boost::mpl::list, + dimFixture, + dimFixture>; + template class RandRange { public: RandRange(): rd(), gen(rd()) {} template std::enable_if_t::value, dummyT> randval(T&& lower, T&& upper) { static_assert(std::is_same::value); auto distro = std::uniform_real_distribution(lower, upper); return distro(this->gen); } template std::enable_if_t::value, dummyT> randval(T&& lower, T&& upper) { static_assert(std::is_same::value); auto distro = std::uniform_int_distribution(lower, upper); return distro(this->gen); } private: std::random_device rd; std::default_random_engine gen; }; } // testGoodies } // muSpectre #endif /* TEST_GOODIES_H */ diff --git a/tests/test_materials_toolbox.cc b/tests/test_materials_toolbox.cc index 3a42ad1..49e2b8d 100644 --- a/tests/test_materials_toolbox.cc +++ b/tests/test_materials_toolbox.cc @@ -1,172 +1,166 @@ /** * file test_materials_toolbox.cc * * @author Till Junge * * @date 05 Nov 2017 * * @brief Tests for the materials toolbox * * @section LICENCE * * Copyright (C) 2017 Till Junge * * µSpectre is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3, or (at * your option) any later version. * * µSpectre is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Emacs; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include "tests.hh" #include "materials/materials_toolbox.hh" #include "common/T4_map_proxy.hh" #include "common/tensor_algebra.hh" #include "common/test_goodies.hh" namespace muSpectre { BOOST_AUTO_TEST_SUITE(materials_toolbox) - template - struct dimFixture{ - constexpr static Dim_t dim{Dim}; - }; - - using dimlist = boost::mpl::list, - dimFixture, - dimFixture>; - - BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_strain_conversion, Fix, dimlist, Fix){ + BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_strain_conversion, Fix, + testGoodies::dimlist, Fix){ constexpr Dim_t dim{Fix::dim}; using T2 = Eigen::Matrix; T2 F; F.setRandom(); auto Eref = .5*(F.transpose()*F-T2::Identity()); auto E_tb = MatTB::convert_strain (Eigen::Map>(F.data())); auto error = (Eref-E_tb).norm(); BOOST_CHECK_LT(error, tol); auto F_tb = MatTB::convert_strain(F); error = (F-F_tb).norm(); BOOST_CHECK_LT(error, tol); } - BOOST_FIXTURE_TEST_CASE_TEMPLATE(dumb_tensor_mult_test, Fix, dimlist, Fix) { + BOOST_FIXTURE_TEST_CASE_TEMPLATE(dumb_tensor_mult_test, Fix, + testGoodies::dimlist, Fix) { constexpr Dim_t dim{Fix::dim}; using T4 = T4Mat; T4 A,B, R1, R2; A.setRandom(); B.setRandom(); R1 = A*B; R2.setZero(); for (Dim_t i = 0; i < dim; ++i) { for (Dim_t j = 0; j < dim; ++j) { for (Dim_t a = 0; a < dim; ++a) { for (Dim_t b = 0; b < dim; ++b) { for (Dim_t k = 0; k < dim; ++k) { for (Dim_t l = 0; l < dim; ++l) { get(R2,i,j,k,l) += get(A, i,j,a,b)*get(B, a,b, k,l); } } } } } } auto error{(R1-R2).norm()}; BOOST_CHECK_LT(error, tol); } - BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_PK1_stress, Fix, dimlist, Fix) { + BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_PK1_stress, Fix, + testGoodies::dimlist, Fix) { using namespace Matrices; constexpr Dim_t dim{Fix::dim}; using T2 = Eigen::Matrix; using T4 = T4Mat; testGoodies::RandRange rng; T2 F=T2::Identity()*2 ; //F.setRandom(); T2 E_tb = MatTB::convert_strain (Eigen::Map>(F.data())); Real lambda = 3;//rng.randval(1, 2); Real mu = 4;//rng.randval(1,2); T4 J = Itrac(); T2 I = I2(); T4 I4 = Isymm(); T4 C = lambda*J + 2*mu*I4; T2 S = tensmult(C, E_tb); T2 Sref = lambda*E_tb.trace()*I + 2*mu*E_tb; auto error{(Sref-S).norm()}; BOOST_CHECK_LT(error, tol); T4 K = outer_under(I,S) + outer_under(F,I)*C*outer_under(F.transpose(),I); // See Curnier, 2000, "Méthodes numériques en mécanique des solides", p 252 T4 Kref; Real Fkrkr = (F.array()*F.array()).sum(); T2 Fkmkn = F.transpose()*F; T2 Fisjs = F*F.transpose(); Kref.setZero(); for (Dim_t i = 0; i < dim; ++i) { for (Dim_t j = 0; j < dim; ++j) { for (Dim_t m = 0; m < dim; ++m) { for (Dim_t n = 0; n < dim; ++n) { get(Kref, i, m, j, n) = (lambda*((Fkrkr-dim)/2 * I(i,j)*I(m,n) + F(i,m)*F(j,n)) + mu * (I(i,j)*Fkmkn(m,n) + Fisjs(i,j)*I(m,n) - I(i,j) *I(m,n) + F(i,n)*F(j,m))); } } } } error = (Kref-K).norm(); BOOST_CHECK_LT(error, tol); T2 P = MatTB::PK1_stress(F, S); T2 Pref = F*S; error = (P-Pref).norm(); BOOST_CHECK_LT(error, tol); auto && stress_tgt = MatTB::PK1_stress(F, S, C); T2 P_t = std::move(std::get<0>(stress_tgt)); T4 K_t = std::move(std::get<1>(stress_tgt)); error = (P_t-Pref).norm(); BOOST_CHECK_LT(error, tol); error = (K_t-Kref).norm(); BOOST_CHECK_LT(error, tol); auto && stress_tgt_trivial = MatTB::PK1_stress(F, P, K); T2 P_u = std::move(std::get<0>(stress_tgt_trivial)); T4 K_u = std::move(std::get<1>(stress_tgt_trivial)); error = (P_u-Pref).norm(); BOOST_CHECK_LT(error, tol); error = (K_u-Kref).norm(); BOOST_CHECK_LT(error, tol); } BOOST_AUTO_TEST_SUITE_END(); } // muSpectre diff --git a/tests/test_tensor_algebra.cc b/tests/test_tensor_algebra.cc index 18f4cac..550ee61 100644 --- a/tests/test_tensor_algebra.cc +++ b/tests/test_tensor_algebra.cc @@ -1,190 +1,304 @@ /** * file test_tensor_algebra.cc * * @author Till Junge * * @date 05 Nov 2017 * * @brief Tests for the tensor algebra functions * * @section LICENCE * * Copyright (C) 2017 Till Junge * * µSpectre is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3, or (at * your option) any later version. * * µSpectre is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Emacs; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include "common/tensor_algebra.hh" #include "tests.hh" +#include "common/test_goodies.hh" namespace muSpectre { BOOST_AUTO_TEST_SUITE(tensor_algebra) auto TerrNorm = [](auto && t){ return Eigen::Tensor(t.abs().sum())(); }; /* ---------------------------------------------------------------------- */ - BOOST_AUTO_TEST_CASE(outer_product_test) { + BOOST_AUTO_TEST_CASE(tensor_outer_product_test) { constexpr Dim_t dim{2}; Eigen::TensorFixedSize> A, B; // use prime numbers so that every multiple is uniquely identifiable A.setValues({{1, 2}, {3 ,7}}); B.setValues({{11, 13}, {17 ,19}}); Eigen::TensorFixedSize> Res1, Res2, Res3; for (Dim_t i = 0; i < dim; ++i) { for (Dim_t j = 0; j < dim; ++j) { for (Dim_t k = 0; k < dim; ++k) { for (Dim_t l = 0; l < dim; ++l) { Res1(i, j, k, l) = A(i, j)*B(k, l); Res2(i, j, k, l) = A(i, k)*B(j, l); Res3(i, j, k, l) = A(i, l)*B(j, k); } } } } Real error = TerrNorm(Res1 - Tensors::outer(A,B)); BOOST_CHECK_LT(error, tol); error = TerrNorm(Res2 - Tensors::outer_under(A, B)); BOOST_CHECK_LT(error, tol); error = TerrNorm(Res3 - Tensors::outer_over(A, B)); if (error > tol) { std::cout << "reference:" << std::endl << Res3 << std::endl; std::cout << "result:" << std::endl << Tensors::outer_over(A, B) << std::endl; std::cout << "A:" << std::endl << A << std::endl; std::cout << "B" << std::endl << B << std::endl; decltype(Res3) tmp = Tensors::outer_over(A, B); for (Dim_t i = 0; i < dim; ++i) { for (Dim_t j = 0; j < dim; ++j) { for (Dim_t k = 0; k < dim; ++k) { for (Dim_t l = 0; l < dim; ++l) { std::cout << "for (" << i << ", " << j << ", " << k << ", " << l << "), ref: " << std::setw(3)<< Res3(i,j,k,l) << ", res: " << std::setw(3)<< tmp(i,j,k,l) << std::endl; } } } } } BOOST_CHECK_LT(error, tol); error = TerrNorm(Res3 - Tensors::outer(A, B)); BOOST_CHECK_GT(error, tol); }; + BOOST_FIXTURE_TEST_CASE_TEMPLATE(outer_products, Fix, + testGoodies::dimlist, Fix) { + constexpr auto dim{Fix::dim}; + using T2 = Tensors::Tens2_t; + using M2 = Matrices::Tens2_t; + using Map2 = Eigen::Map; + using T4 = Tensors::Tens4_t; + using M4 = Matrices::Tens4_t; + using Map4 = Eigen::Map; + T2 A, B; + T4 RT; + A.setRandom(); + B.setRandom(); + Map2 Amap(A.data()); + Map2 Bmap(B.data()); + M2 C, D; + M4 RM; + C = Amap; + D = Bmap; + + auto error =[](const T4& A, const M4& B) {return (B - Map4(A.data())).norm();}; + + // Check outer product + RT = Tensors::outer(A, B); + RM = Matrices::outer(C, D); + BOOST_CHECK_LT(error(RT, RM), tol); + + // Check outer_under product + RT = Tensors::outer_under(A, B); + RM = Matrices::outer_under(C, D); + BOOST_CHECK_LT(error(RT, RM), tol); + + // Check outer_over product + RT = Tensors::outer_over(A, B); + RM = Matrices::outer_over(C, D); + BOOST_CHECK_LT(error(RT, RM), tol); + + } + BOOST_AUTO_TEST_CASE(tensor_multiplication) { constexpr Dim_t dim{2}; using Strain_t = Eigen::TensorFixedSize>; Strain_t A, B; A.setValues({{1, 2}, {3 ,7}}); B.setValues({{11, 13}, {17 ,19}}); Strain_t FF1 = A*B; // element-wise multiplication std::array, 1> prod_dims { Eigen::IndexPair{1, 0}}; Strain_t FF2 = A.contract(B, prod_dims); // correct option 1 Strain_t FF3; using Mat_t = Eigen::Map>; // following only works for evaluated tensors (which already have data()) Mat_t(FF3.data()) = Mat_t(A.data()) * Mat_t(B.data()); Strain_t ref; ref.setZero(); for (Dim_t i = 0; i < dim; ++i) { for (Dim_t j = 0; j < dim; ++j) { for (Dim_t a = 0; a < dim; ++a) { ref(i,j) += A(i, a) * B(a, j); } } } using T = Eigen::TensorFixedSize >; constexpr Dim_t my_size = T::NumIndices; std::cout << "size = " << my_size << std::endl; using Strain_tw = Eigen::TensorFixedSize>; Strain_tw C; C.setConstant(100); auto a = T::Dimensions::total_size; auto b = Strain_tw::Dimensions::total_size; std::cout << "a, b = " << a << ", " << b << std::endl; //static_assert(!std::is_convertible::value, // "Tensors not size-protected"); if (std::is_convertible::value) { std::cout << "this is not good, should I abandon Tensors?"; } // this test seems useless. I use to detect if Eigen changed the // default tensor product Real error = TerrNorm(FF1-ref); if (error < tol) { std::cout << "A =" << std::endl << A << std::endl; std::cout << "B =" << std::endl << B << std::endl; std::cout << "FF1 =" << std::endl << FF1 << std::endl; std::cout << "ref =" << std::endl << ref << std::endl; } BOOST_CHECK_GT(error, tol); error = TerrNorm(FF2-ref); if (error > tol) { std::cout << "A =" << std::endl << A << std::endl; std::cout << "B =" << std::endl << B << std::endl; std::cout << "FF2 =" << std::endl << FF2 << std::endl; std::cout << "ref =" << std::endl << ref << std::endl; } BOOST_CHECK_LT(error, tol); error = TerrNorm(FF3-ref); if (error > tol) { std::cout << "A =" << std::endl << A << std::endl; std::cout << "B =" << std::endl << B << std::endl; std::cout << "FF3 =" << std::endl << FF3 << std::endl; std::cout << "ref =" << std::endl << ref << std::endl; } BOOST_CHECK_LT(error, tol); } + + BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensmult, Fix, + testGoodies::dimlist, Fix) { + using namespace Matrices; + constexpr Dim_t dim{Fix::dim}; + using T4 = Tens4_t; + using T2 = Tens2_t; + using V2 = Eigen::Matrix; + T4 C; + C.setRandom(); + T2 E; + E.setRandom(); + Eigen::Map Ev(E.data()); + T2 R = tensmult(C, E); + + auto error = (Eigen::Map(R.data())-C*Ev).norm(); + BOOST_CHECK_LT(error, tol); + } + + BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tracer, Fix, + testGoodies::dimlist, Fix) { + using namespace Matrices; + constexpr Dim_t dim{Fix::dim}; + using T2 = Tens2_t; + auto tracer = Itrac(); + T2 F; + F.setRandom(); + auto Ftrac = tensmult(tracer, F); + auto error = (Ftrac-F.trace()*F.Identity()).norm(); + BOOST_CHECK_LT(error, tol); + } + + + BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_identity, Fix, + testGoodies::dimlist, Fix) { + using namespace Matrices; + constexpr Dim_t dim{Fix::dim}; + using T2 = Tens2_t; + auto ident = Iiden(); + T2 F; + F.setRandom(); + auto Fiden = tensmult(ident, F); + auto error = (Fiden-F).norm(); + BOOST_CHECK_LT(error, tol); + } + + BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_transposer, Fix, + testGoodies::dimlist, Fix) { + using namespace Matrices; + constexpr Dim_t dim{Fix::dim}; + using T2 = Tens2_t; + auto trnst = Itrns(); + T2 F; + F.setRandom(); + auto Ftrns = tensmult(trnst, F); + auto error = (Ftrns-F.transpose()).norm(); + BOOST_CHECK_LT(error, tol); + } + + BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_symmetriser, Fix, + testGoodies::dimlist, Fix) { + using namespace Matrices; + constexpr Dim_t dim{Fix::dim}; + using T2 = Tens2_t; + auto symmt = Isymm(); + T2 F; + F.setRandom(); + auto Fsymm = tensmult(symmt, F); + auto error = (Fsymm-.5*(F+F.transpose())).norm(); + BOOST_CHECK_LT(error, tol); + std::cout << " F =" << std::endl << F << std::endl; + std::cout << "IF =" << std::endl << Fsymm << std::endl; + } + BOOST_AUTO_TEST_SUITE_END(); } // muSpectre diff --git a/tests/tests.hh b/tests/tests.hh index 03aba7d..cddf95e 100644 --- a/tests/tests.hh +++ b/tests/tests.hh @@ -1,44 +1,45 @@ /** * file tests.hh * * @author Till Junge * * @date 10 May 2017 * * @brief common defs for tests * * @section LICENCE * * Copyright (C) 2017 Till Junge * * µSpectre is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3, or (at * your option) any later version. * * µSpectre is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Emacs; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "common/common.hh" #include #include #ifndef TESTS_H #define TESTS_H namespace muSpectre { + const Real tol = 1e-14*100; //it's in percent } // muSpectre #endif /* TESTS_H */