diff --git a/MathLib/KelvinVector.cpp b/MathLib/KelvinVector.cpp index 94ccd6482bf1dd0cc2cd7105190f89e804aca713..0c6718ddb8f9b2d73b066ed5cf3420ab59d19240 100644 --- a/MathLib/KelvinVector.cpp +++ b/MathLib/KelvinVector.cpp @@ -80,6 +80,37 @@ Eigen::Matrix<double, 3, 3> kelvinVectorToTensor( return m; } +template <> +KelvinVectorType<2> tensorToKelvin<2>(Eigen::Matrix<double, 3, 3> const& m) +{ + assert(std::abs(m(0, 1) - m(1, 0)) < + std::numeric_limits<double>::epsilon()); + assert(m(0, 2) == 0); + assert(m(1, 2) == 0); + assert(m(2, 0) == 0); + assert(m(2, 1) == 0); + + KelvinVectorType<2> v; + v << m(0, 0), m(1, 1), m(2, 2), m(0, 1) * std::sqrt(2.); + return v; +} + +template <> +KelvinVectorType<3> tensorToKelvin<3>(Eigen::Matrix<double, 3, 3> const& m) +{ + assert(std::abs(m(0, 1) - m(1, 0)) < + std::numeric_limits<double>::epsilon()); + assert(std::abs(m(1, 2) - m(2, 1)) < + std::numeric_limits<double>::epsilon()); + assert(std::abs(m(0, 2) - m(2, 0)) < + std::numeric_limits<double>::epsilon()); + + KelvinVectorType<3> v; + v << m(0, 0), m(1, 1), m(2, 2), m(0, 1) * std::sqrt(2.), + m(1, 2) * std::sqrt(2.), m(0, 2) * std::sqrt(2.); + return v; +} + template <> Eigen::Matrix<double, 4, 1> kelvinVectorToSymmetricTensor( Eigen::Matrix<double, 4, 1, Eigen::ColMajor, 4, 1> const& v) @@ -124,6 +155,5 @@ kelvinVectorToSymmetricTensor(Eigen::Matrix<double, v.size()); } } - } // namespace KelvinVector } // namespace MathLib diff --git a/MathLib/KelvinVector.h b/MathLib/KelvinVector.h index d57a1eac3b84d9ec53af4bf6106616fa20a9d8fb..bf77b34534b7311b5461e92b36c458941bfe7b01 100644 --- a/MathLib/KelvinVector.h +++ b/MathLib/KelvinVector.h @@ -9,6 +9,7 @@ #pragma once #include <Eigen/Dense> +#include "BaseLib/Error.h" namespace MathLib { @@ -125,6 +126,12 @@ Eigen::Matrix<double, 3, 3> kelvinVectorToTensor(Eigen::Matrix<double, KelvinVectorSize, 1> const& v); +/// Conversion of a 3x3 matrix to a Kelvin vector. +/// Only implementations for KelvinVectorSize 4 and 6 are provided. +template <int DisplacementDim> +KelvinVectorType<DisplacementDim> tensorToKelvin( + Eigen::Matrix<double, 3, 3> const& m); + /// Conversion of a Kelvin vector to a short vector representation of a /// symmetric 3x3 matrix. /// @@ -132,6 +139,8 @@ Eigen::Matrix<double, 3, 3> kelvinVectorToTensor(Eigen::Matrix<double, /// In the 3D case the entries for the xx, yy, zz, xy, yz, and xz components in /// that particular order are stored. /// +/// This is opposite of the symmetricTensorToKelvinVector() +/// /// Only implementations for KelvinVectorSize 4 and 6, and dynamic size vectors /// are provided. template <int KelvinVectorSize> @@ -143,6 +152,50 @@ kelvinVectorToSymmetricTensor(Eigen::Matrix<double, KelvinVectorSize, 1> const& v); +/// Conversion of a short vector representation of a +/// symmetric 3x3 matrix to a Kelvin vector. +/// +/// This is opposite of the kelvinVectorToSymmetricTensor() +/// +/// Only implementations for KelvinVectorSize 4 and 6, and dynamic size vectors +/// are provided. +template <typename Derived> +Eigen::Matrix<double, Eigen::MatrixBase<Derived>::RowsAtCompileTime, 1> +symmetricTensorToKelvinVector(Eigen::MatrixBase<Derived> const& v) +{ + static_assert( + (Eigen::MatrixBase<Derived>::ColsAtCompileTime == 1) || + (Eigen::MatrixBase<Derived>::ColsAtCompileTime == Eigen::Dynamic), + "KelvinVector must be a column vector"); + if (v.cols() != 1) + { + OGS_FATAL( + "KelvinVector must be a column vector, but input has %d columns.", + v.cols()); + } + + Eigen::Matrix<double, Eigen::MatrixBase<Derived>::RowsAtCompileTime, 1> + result; + if (v.rows() == 4) + { + result.resize(4, 1); + result << v[0], v[1], v[2], v[3] * std::sqrt(2.); + } + else if (v.rows() == 6) + { + result.resize(6, 1); + result << v[0], v[1], v[2], v[3] * std::sqrt(2.), v[4] * std::sqrt(2.), + v[5] * std::sqrt(2.); + } + else + { + OGS_FATAL( + "Symmetric tensor to Kelvin vector conversion expected an input " + "vector of size 4 or 6, but a vector of size %d was given.", + v.size()); + } + return result; +} } // namespace KelvinVector } // namespace MathLib diff --git a/Tests/MaterialLib/KelvinVector.cpp b/Tests/MathLib/KelvinVector.cpp similarity index 73% rename from Tests/MaterialLib/KelvinVector.cpp rename to Tests/MathLib/KelvinVector.cpp index b7be7b8115346045c651e153dc9dd9feea8475bc..2c84be49035ef68630e20f941a5b4ea460bea9d7 100644 --- a/Tests/MaterialLib/KelvinVector.cpp +++ b/Tests/MathLib/KelvinVector.cpp @@ -15,39 +15,6 @@ using namespace MathLib::KelvinVector; namespace ac = autocheck; -template <int Size> -using KelvinVector = Eigen::Matrix<double, Size, 1, Eigen::ColMajor, Size, 1>; - -template <int Size> -KelvinVector<Size> tensorToKelvin(Eigen::Matrix<double, 3, 3> const& m); - -template <> -KelvinVector<4> tensorToKelvin(Eigen::Matrix<double, 3, 3> const& m) -{ - EXPECT_NEAR(m(0, 1), m(1, 0), std::numeric_limits<double>::epsilon()); - EXPECT_EQ(m(0, 2), 0); - EXPECT_EQ(m(1, 2), 0); - EXPECT_EQ(m(2, 0), 0); - EXPECT_EQ(m(2, 1), 0); - - KelvinVector<4> v; - v << m(0, 0), m(1, 1), m(2, 2), m(0, 1) * std::sqrt(2.); - return v; -} - -template <> -KelvinVector<6> tensorToKelvin(Eigen::Matrix<double, 3, 3> const& m) -{ - EXPECT_NEAR(m(0, 1), m(1, 0), std::numeric_limits<double>::epsilon()); - EXPECT_NEAR(m(1, 2), m(2, 1), std::numeric_limits<double>::epsilon()); - EXPECT_NEAR(m(0, 2), m(2, 0), std::numeric_limits<double>::epsilon()); - - KelvinVector<6> v; - v << m(0, 0), m(1, 1), m(2, 2), m(0, 1) * std::sqrt(2.), - m(1, 2) * std::sqrt(2.), m(0, 2) * std::sqrt(2.); - return v; -} - struct MaterialLibSolidsKelvinVector4 : public ::testing::Test { static const int size = 4; @@ -71,23 +38,23 @@ struct MaterialLibSolidsKelvinVector6 : public ::testing::Test TEST_F(MaterialLibSolidsKelvinVector4, SelfTestMappingKelvinToTensor) { - auto f = [](KelvinVector<4> const& v) { - return (v - tensorToKelvin<4>(kelvinVectorToTensor(v))).norm() <= + auto f = [](KelvinVectorType<2> const& v) { + return (v - tensorToKelvin<2>(kelvinVectorToTensor(v))).norm() <= 2 * std::numeric_limits<double>::epsilon() * v.norm(); }; - ac::check<KelvinVector<4>>( + ac::check<KelvinVectorType<2>>( f, 1000, ac::make_arbitrary(kelvinVectorGenerator), gtest_reporter); } TEST_F(MaterialLibSolidsKelvinVector6, SelfTestMappingKelvinToTensor) { - auto f = [](KelvinVector<6> const& v) { - return (v - tensorToKelvin<6>(kelvinVectorToTensor(v))).norm() <= + auto f = [](KelvinVectorType<3> const& v) { + return (v - tensorToKelvin<3>(kelvinVectorToTensor(v))).norm() <= 1.5 * std::numeric_limits<double>::epsilon() * v.norm(); }; - ac::check<KelvinVector<6>>( + ac::check<KelvinVectorType<3>>( f, 1000, ac::make_arbitrary(kelvinVectorGenerator), gtest_reporter); } @@ -97,27 +64,27 @@ TEST_F(MaterialLibSolidsKelvinVector6, SelfTestMappingKelvinToTensor) TEST_F(MaterialLibSolidsKelvinVector4, Determinant) { - auto f = [](KelvinVector<4> const& v) { + auto f = [](KelvinVectorType<2> const& v) { return std::abs(Invariants<4>::determinant(v) - kelvinVectorToTensor(v).determinant()) <= std::numeric_limits<double>::epsilon() * std::pow(v.norm(), 3.07); }; - ac::check<KelvinVector<4>>( + ac::check<KelvinVectorType<2>>( f, 1000, ac::make_arbitrary(kelvinVectorGenerator), gtest_reporter); } TEST_F(MaterialLibSolidsKelvinVector6, Determinant) { - auto f = [](KelvinVector<6> const& v) { + auto f = [](KelvinVectorType<3> const& v) { return std::abs(Invariants<6>::determinant(v) - kelvinVectorToTensor(v).determinant()) <= std::numeric_limits<double>::epsilon() * std::pow(v.norm(), 3.07); }; - ac::check<KelvinVector<6>>( + ac::check<KelvinVectorType<3>>( f, 1000, ac::make_arbitrary(kelvinVectorGenerator), gtest_reporter); } @@ -127,18 +94,18 @@ TEST_F(MaterialLibSolidsKelvinVector6, Determinant) TEST_F(MaterialLibSolidsKelvinVector4, Inverse) { - auto f = [](KelvinVector<4> const& v) { + auto f = [](KelvinVectorType<2> const& v) { auto const error = - (inverse(v) - tensorToKelvin<4>(kelvinVectorToTensor(v).inverse())) + (inverse(v) - tensorToKelvin<2>(kelvinVectorToTensor(v).inverse())) .norm(); // The error is only weekly depending on the input vector norm. return error < 1e-6 && error < 1e-8 * std::pow(v.norm(), 1.4); }; - ac::check<KelvinVector<4>>( + ac::check<KelvinVectorType<2>>( f, 1000, ac::make_arbitrary(kelvinVectorGenerator) - .discard_if([](KelvinVector<4> const& v) { + .discard_if([](KelvinVectorType<2> const& v) { // only invertable matrices return (std::abs(kelvinVectorToTensor(v).determinant()) == 0); }), @@ -147,18 +114,18 @@ TEST_F(MaterialLibSolidsKelvinVector4, Inverse) TEST_F(MaterialLibSolidsKelvinVector6, Inverse) { - auto f = [](KelvinVector<6> const& v) { + auto f = [](KelvinVectorType<3> const& v) { auto const error = - (inverse(v) - tensorToKelvin<6>(kelvinVectorToTensor(v).inverse())) + (inverse(v) - tensorToKelvin<3>(kelvinVectorToTensor(v).inverse())) .norm(); // The error is only weekly depending on the input vector norm. return error < 1e-6 && error < 1e-8 * std::pow(v.norm(), 1.4); }; - ac::check<KelvinVector<6>>( + ac::check<KelvinVectorType<3>>( f, 1000, ac::make_arbitrary(kelvinVectorGenerator) - .discard_if([](KelvinVector<6> const& v) { + .discard_if([](KelvinVectorType<3> const& v) { // only invertable matrices return (std::abs(kelvinVectorToTensor(v).determinant()) == 0); }),