diff --git a/src/algebra/EigenvalueSolver.cpp b/src/algebra/EigenvalueSolver.cpp index b17e650af775530d3e61f4561b7676dccf1efdd4..fbe0e344bb8a0d011f30eabcd84d9406cdc1bee7 100644 --- a/src/algebra/EigenvalueSolver.cpp +++ b/src/algebra/EigenvalueSolver.cpp @@ -1,9 +1,10 @@ #include <algebra/EigenvalueSolver.hpp> +#include <cmath> #include <utils/pugs_config.hpp> - #ifdef PUGS_HAS_SLEPC #include <algebra/PETScUtils.hpp> #include <slepc.h> +#include <slepceps.h> #endif // PUGS_HAS_SLEPC #ifdef PUGS_HAS_EIGEN3 @@ -233,6 +234,368 @@ EigenvalueSolver::_slepscComputeForSymmetricMatrix(const CRSMatrix<double>& A, S { Internals::slepscComputeForSymmetricMatrix(A, eigenvalues); } +#endif // PUGS_HAS_SLEPC + +template <typename T> +PUGS_INLINE TinyMatrix<3, 3, T> +EigenvalueSolver::swap(TinyMatrix<3, 3, T>& matrix, size_t i, size_t j) const +{ + TinyMatrix<3, 3, T> swapMat = matrix; + for (size_t k = 0; k < 3; k++) { + double valuei = matrix(i, k); + swapMat(i, k) = matrix(j, k); + swapMat(j, k) = valuei; + } + return swapMat; +} + +template <typename T> +PUGS_INLINE constexpr TinyMatrix<3, 3, T> +EigenvalueSolver::rowReduce(const TinyMatrix<3, 3, T>& matrix, const double epsilon) const +{ + TinyMatrix<3, 3> redmat = matrix; + for (size_t i = 0; i < 3; ++i) { + size_t pivotRow = i; + while (pivotRow < 3 && std::fabs(redmat(pivotRow, i)) < epsilon) { + ++pivotRow; + } + if (pivotRow == 3) { + continue; + } + // std::cout << "before: " + // << "i = " << i << " pivotRow = " << pivotRow << " " << redmat << "\n"; + redmat = swap(redmat, i, pivotRow); + // std::cout << "after: " << redmat << "\n"; + + double pivotValue = redmat(i, i); + for (size_t j = i; j < 3; ++j) { + redmat(i, j) /= pivotValue; + } + + for (size_t k = i + 1; k < 3; ++k) { + double factor = redmat(k, i); + for (size_t j = i; j < 3; ++j) { + redmat(k, j) -= factor * redmat(i, j); + } + } + } + return redmat; +} + +TinyMatrix<3, 3> +EigenvalueSolver::findEigenvector(const TinyMatrix<3, 3>& A, + const double eigenvalue, + const size_t space_size, + const double epsilon) const +{ + TinyMatrix<3, 3> eigenvectors = zero; + if (space_size == 3) { + // std::cout << "space_size = 3 " + // << "\n"; + return eigenvectors; + } + TinyMatrix<3, 3> B = A; + + for (size_t i = 0; i < 3; ++i) { + B(i, i) -= eigenvalue; + } + + // std::cout << " Avant " << B << "\n" + // << "\n"; + + B = rowReduce(B, epsilon); + + // std::cout << " Après " << B << "\n" + // << "\n"; + for (size_t i = 0; i < 3; ++i) { + bool isFreeVariableRow = true; + for (size_t j = 0; j < 3; ++j) { + if (std::fabs(B(i, j)) > epsilon) { + isFreeVariableRow = false; + // std::cout << " B(" << i << "," << j << ") = " << B(i, j) << "\n"; + break; + } + } + + if (isFreeVariableRow) { + TinyVector<3> eigenvector = zero; + eigenvector[i] = 1.0; + // std::cout << i << " is free " + // << "\n"; + // std::cout << std::flush; + for (int k = i - 1; k >= 0; --k) { + double sum = 0.0; + for (size_t j = k + 1; j < 3; ++j) { + sum += B(k, j) * eigenvector[j]; + } + eigenvector[k] = -sum; + } + + eigenvector = 1. / l2Norm(eigenvector) * eigenvector; + + for (size_t j = 0; j < 3; ++j) { + eigenvectors(i, j) = eigenvector[j]; + } + } + } + // std::cout << " Before orthorgonalization " << B << "\n"; + // std::cout << " Before orthorgonalization " << eigenvectors << "\n"; + + if (space_size == 2) { + TinyVector<3> first = zero; + TinyVector<3> second = zero; + // TinyVector<3> new_second =zero; + // size_t rank1 = 0; + size_t rank2 = 1; + for (size_t j = 0; j < 3; ++j) { + first[j] = eigenvectors(0, j); + second[j] = eigenvectors(1, j); + } + if (l2Norm(first) < 1e-3) { + for (size_t j = 0; j < 3; ++j) { + first[j] = eigenvectors(2, j); + // rank1 = 2; + } + } + if (l2Norm(second) < 1e-3) { + for (size_t j = 0; j < 3; ++j) { + second[j] = eigenvectors(2, j); + rank2 = 2; + } + } + double scalprod = dot(first, second); + double norm2 = dot(first, first); + TinyVector<3> normal_vector = second - scalprod / norm2 * first; + normal_vector = 1. / l2Norm(normal_vector) * normal_vector; + for (size_t j = 0; j < 3; ++j) { + eigenvectors(rank2, j) = normal_vector[j]; + } + // std::cout << " rank1 : " << rank1 << " rank2 " << rank2 << "\n"; + } + // std::cout << "In findEigenvectors for eigenvalue " << eigenvalue << " eigenvectors " << eigenvectors << "\n"; + return eigenvectors; +} + +bool +EigenvalueSolver::isDiagonal(const TinyMatrix<3, 3>& A, const double epsilon) +{ + bool isDiag = true; + for (size_t i = 0; i < 2; i++) { + for (size_t j = i + 1; j < 3; j++) { + isDiag = !(fabs(A(i, j)) > epsilon); + return isDiag; + } + } + return isDiag; +} + +TinyMatrix<3, 3> +EigenvalueSolver::findEigenvectors(const TinyMatrix<3, 3>& A, TinyVector<3> eigenvalues, const double epsilon) const +{ + TinyMatrix<3> eigenMatrix = zero; + size_t count = 0; + TinyVector<3, int> space_size = zero; + for (size_t i = 0; i < 3; i++) { + space_size[i] = 1; + for (size_t j = i + 1; j < 3; j++) { + if (fabs(eigenvalues[i] - eigenvalues[j]) < epsilon) { + double temp = eigenvalues[i + 1]; + eigenvalues[i + 1] = eigenvalues[j]; + eigenvalues[j] = temp; + space_size[i] += 1; + i += 1; + } + } + } + // std::cout << "space_size: " << space_size << "\n"; + // std::cout << "eigenvalues: " << eigenvalues << "\n"; + size_t it = 0; + while (count < 3 && it < 3) { + TinyMatrix<3> Try = findEigenvector(A, eigenvalues[count], space_size[count], epsilon); + // std::cout << eigenvalues[count] << " Frobenius norm try: " << frobeniusNorm(Try) << "\n"; + Assert(frobeniusNorm(Try) > 1e-3, " Error : no eigenvector corresponds to eigenvalue "); + if (frobeniusNorm(Try) < 1e-3) { + std::cout << " Error : no eigenvector corresponds to eigenvalue \n"; + exit(0); + } + + TinyVector<3> eigenvector = zero; + int count2 = 0; + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; j++) { + eigenvector[j] = Try(i, j); + } + // std::cout << " count, i: " << count << ", " << i << " l2Norm(eigenvector) " << l2Norm(eigenvector) << "\n"; + if (l2Norm(eigenvector) > 1e-3) { + for (size_t j = 0; j < 3; j++) { + eigenMatrix(count, j) = eigenvector[j]; + } + count += 1; + count2 += 1; + } + } + Assert(count2 == space_size[count - count2], " eigenvector space size is not what was expected "); + if (count2 != space_size[count - count2]) { + std::cout << " eigenvector space size is not what was expected \n"; + exit(0); + } + it++; + } + // std ::cout << " eigenMatrix " << eigenMatrix << "\n"; + return eigenMatrix; +} + +std::tuple<TinyVector<3>, TinyMatrix<3>> +EigenvalueSolver::findEigen(const TinyMatrix<3> A) +{ + constexpr TinyVector<3> eigenvalues0 = zero; + constexpr TinyMatrix<3> eigenvectors0 = identity; + const double epsilon = 1.e-6; // * l2Norm(eigenvalues); + if (frobeniusNorm(A) < 1.e-15) { + // std::cout << " Frobenius norm 0 " << frobeniusNorm(A) << "\n"; + return std::make_tuple(eigenvalues0, eigenvectors0); + } + TinyMatrix<3> C = 1. / frobeniusNorm(A) * A; + if (isDiagonal(C, epsilon)) { + std::cout << "Matrix C " << C << " is diagonal " + << "\n"; + const TinyVector<3> eigenvalues(A(0, 0), A(1, 1), A(2, 2)); + TinyMatrix<3> Diag = zero; + for (size_t i = 0; i < 3; ++i) { + Diag(i, i) = eigenvalues[i]; + } + TinyMatrix<3> B = eigenvectors0 * Diag * transpose(eigenvectors0); + // std::cout << "\n" + // << B << ", " << A << " Diff " + // << " A - B " << 1 / frobeniusNorm(A) * (A - B) << "\n"; + return std::make_tuple(eigenvalues, eigenvectors0); + } + const TinyVector<3> eigenvalues = _findEigenValues(C, epsilon); + // std::cout << std::setprecision(15) << eigenvalues << "\n"; + TinyMatrix<3> Eigenmatrix = transpose(findEigenvectors(C, eigenvalues, epsilon)); + TinyMatrix<3> Diag = zero; + for (size_t i = 0; i < 3; ++i) { + Diag(i, i) = frobeniusNorm(A) * eigenvalues[i]; + } + TinyMatrix<3> B = Eigenmatrix * Diag * transpose(Eigenmatrix); + // std::cout << "\n" + // << B << ", " << A << " Diff " + // << " A - B " << 1 / frobeniusNorm(A) * (A - B) << "\n"; + return std::make_tuple(frobeniusNorm(A) * eigenvalues, Eigenmatrix); +} +TinyVector<3> +EigenvalueSolver::_findEigenValues(const TinyMatrix<3>& A, const double epsilon) const +{ + // std::cout << " Matrix " << A << "\n"; + TinyVector<3> eigenvalues(0., 0., 0.); + double b = -trace(A); + double c = 0.5 * (trace(A) * trace(A) - trace(A * A)); + double d = -det(A); + Polynomial<3> P(d, c, b, 1.); + // std::cout << P << "\n"; + Polynomial<2> Q(c, b, 1.); + if (fabs(d) > 1e-11) { + eigenvalues[0] = _findFirstRoot(P); + Polynomial<1> Q1(-eigenvalues[0], 1); + Polynomial<3> S; + Polynomial<3> R; + divide(P, Q1, S, R); + // std::cout << "Q1 : " << Q1 << "\n"; + // std::cout << "R : " << R << "\n"; + // std::cout << "S : " << S << "\n"; + Q.coefficients()[0] = S.coefficients()[0]; + Q.coefficients()[1] = S.coefficients()[1]; + Q.coefficients()[2] = S.coefficients()[2]; + } + TinyVector<2> last_eigenvalues = _findLastRoots(Q, epsilon); + eigenvalues[1] = last_eigenvalues[0]; + eigenvalues[2] = last_eigenvalues[1]; + TinyVector<3, int> space_size = zero; + for (size_t i = 0; i < 3; i++) { + space_size[i] = 1; + for (size_t j = i + 1; j < 3; j++) { + if (eigenvalues[i] == eigenvalues[j]) { + double temp = eigenvalues[i + 1]; + eigenvalues[i + 1] = eigenvalues[j]; + eigenvalues[j] = temp; + space_size[i] += 1; + i += 1; + } + } + } + return eigenvalues; +} + +double +EigenvalueSolver::_findFirstRoot(Polynomial<3> P) const +{ + double guess = -P.coefficients()[2] / 3.; + double old_guess = -P.coefficients()[2] / 3.; + // std::cout << " coefs P " << P.coefficients() << "\n"; + // double delta = pow(2.*P.coefficients()[2],2) - 4*3.*P.coefficients()[3]*P.coefficients()[1]; + Polynomial<2> Q = derivative(P); + Polynomial<1> R = derivative(Q); + // size_t iteration = 0; + double residu = 0.; + size_t iteration = 0; + do { + // guess -= P(guess) / Q(guess); + old_guess = guess; + guess -= 2 * P(guess) * Q(guess) / (2 * Q(guess) * Q(guess) - P(guess) * R(guess)); + // std::cout << "guess = " << guess << " old_guess " << old_guess << "\n"; + // std::cout << "g(guess) = " << Q(guess) << "\n"; + residu = P(guess); + // std::cout << "residu = " << residu << " delta " << fabs(old_guess - guess) << "\n"; + iteration += 1; + } while (((fabs(residu) > 1.e-16) or (fabs(old_guess - guess) > 1.e-10)) and (iteration < 10000)); + if (iteration == 100) { + std::cout << " nb Newton iterations reached, residu " << residu << "\n"; + } + return guess; +} + +TinyVector<2> +EigenvalueSolver::_findLastRoots(Polynomial<2> P, const double epsilon) const +{ + TinyVector<2> roots(0., 0.); + double delta = P.coefficients()[1] * P.coefficients()[1] - 4 * P.coefficients()[2] * P.coefficients()[0]; + // if (fabs(delta) > epsilon) { + // std::cout << "Find roots is only for symmetric matrix \n"; + // exit(0); + // } + Assert(delta > -epsilon, "Find roots is only for symmetric matrix"); + if (fabs(delta) < epsilon) { + roots[0] = -P.coefficients()[1] / (2. * P.coefficients()[2]); + roots[1] = roots[0]; + } + if (delta >= 0.) { + roots[0] = (-P.coefficients()[1] - std::sqrt(delta)) / (2. * P.coefficients()[2]); + roots[1] = (-P.coefficients()[1] + std::sqrt(delta)) / (2. * P.coefficients()[2]); + } + return roots; +} + +TinyMatrix<3> +EigenvalueSolver::computeExpForTinyMatrix(const TinyMatrix<3>& A) +{ + TinyMatrix<3> expA; + auto [eigenvalues, eigenvectors] = findEigen(A); + TinyMatrix<3> D = zero; + for (size_t i = 0; i < 3; ++i) { + if (std::abs(eigenvalues[i]) > 200) { + std::cout << "Warning: large eigenvalue for matrix " << A << " eigenvalues " << eigenvalues << "\n"; + // eigenvalues[i] = eigenvalues[i] / eigenvalues[i] * 200.; + // exit(0); + } + } + for (size_t i = 0; i < 3; ++i) { + D(i, i) = std::exp(eigenvalues[i]); + } + expA = eigenvectors * D * transpose(eigenvectors); + return expA; +} + +#ifdef PUGS_HAS_SLEPC void EigenvalueSolver::_slepscComputeForSymmetricMatrix(const DenseMatrixWrapper<double>& A, SmallArray<double>& eigenvalues) diff --git a/src/algebra/EigenvalueSolver.hpp b/src/algebra/EigenvalueSolver.hpp index 6567adcdc12caabae69baede83e74166696baaf0..98696152215164477c34e4743accb5ad4659e85d 100644 --- a/src/algebra/EigenvalueSolver.hpp +++ b/src/algebra/EigenvalueSolver.hpp @@ -8,6 +8,7 @@ #include <algebra/SmallMatrix.hpp> #include <algebra/SmallVector.hpp> #include <algebra/TinyMatrix.hpp> +#include <analysis/Polynomial.hpp> #include <utils/Exceptions.hpp> #include <utils/SmallArray.hpp> @@ -127,6 +128,41 @@ class EigenvalueSolver } } + template <typename MatrixType> + void + computeExpForSymmetricMatrix([[maybe_unused]] const MatrixType& A, [[maybe_unused]] SmallMatrix<double>& expA) + { +#ifdef PUGS_HAS_SLEPC + this->computeExpForSymmetricMatrix(PETScAijMatrixEmbedder{A}, expA); +#else // PUGS_HAS_SLEPC + throw NotImplementedError("SLEPc is required to solve eigenvalue problems"); +#endif // PUGS_HAS_SLEPC + } + + template <typename T> + PUGS_INLINE TinyMatrix<3, 3, T> swap(TinyMatrix<3, 3, T>& matrix, size_t i, size_t j) const; + + template <typename T> + PUGS_INLINE constexpr TinyMatrix<3, 3, T> rowReduce(const TinyMatrix<3, 3, T>& matrix, const double epsilon) const; + + TinyMatrix<3, 3> findEigenvector(const TinyMatrix<3, 3>& A, + const double eigenvalue, + const size_t space_size, + const double epsilon) const; + + bool isDiagonal(const TinyMatrix<3, 3>& A, const double epsilon); + + TinyMatrix<3, 3> findEigenvectors(const TinyMatrix<3, 3>& A, TinyVector<3> eigenvalues, const double epsilon) const; + + std::tuple<TinyVector<3>, TinyMatrix<3>> findEigen(const TinyMatrix<3> A); + TinyVector<3> _findEigenValues(const TinyMatrix<3>& A, const double epsilon) const; + + double _findFirstRoot(Polynomial<3> P) const; + + TinyVector<2> _findLastRoots(Polynomial<2> P, const double epsilon) const; + + TinyMatrix<3> computeExpForTinyMatrix(const TinyMatrix<3>& A); + EigenvalueSolver(const EigenvalueSolverOptions& options = EigenvalueSolverOptions::default_options); ~EigenvalueSolver() = default; }; diff --git a/src/analysis/Polynomial.hpp b/src/analysis/Polynomial.hpp new file mode 100644 index 0000000000000000000000000000000000000000..cf716a246a4bfa81d2de207c0101f23ea4aa7c09 --- /dev/null +++ b/src/analysis/Polynomial.hpp @@ -0,0 +1,494 @@ +#ifndef POLYNOMIAL_HPP +#define POLYNOMIAL_HPP + +#include <algebra/TinyVector.hpp> + +template <size_t N> +class Polynomial +{ + private: + using Coefficients = TinyVector<N + 1, double>; + Coefficients m_coefficients; + static_assert(N >= 0, "Polynomial degree must be non-negative"); + + public: + PUGS_INLINE + constexpr size_t + degree() const + { + return N; + } + + PUGS_INLINE + constexpr size_t + realDegree() const + { + for (size_t j = N; j > 0; j--) { + if (std::abs(coefficients()[j]) > 1.e-14) { + return j; + } + } + return 0; + } + + PUGS_INLINE + constexpr const Coefficients& + coefficients() const + { + return m_coefficients; + } + + PUGS_INLINE + constexpr Coefficients& + coefficients() + { + return m_coefficients; + } + + PUGS_INLINE constexpr bool + operator==(const Polynomial& q) const + { + return m_coefficients == q.m_coefficients; + } + + PUGS_INLINE + constexpr bool + operator!=(const Polynomial& q) const + { + return not(*this == q); + } + + template <size_t M> + PUGS_INLINE constexpr Polynomial<std::max(M, N)> + operator+(const Polynomial<M>& Q) const + { + Polynomial<std::max(M, N)> P; + if constexpr (M > N) { + P.coefficients() = Q.coefficients(); + for (size_t i = 0; i <= N; ++i) { + P.coefficients()[i] += coefficients()[i]; + } + } else { + P.coefficients() = coefficients(); + for (size_t i = 0; i <= M; ++i) { + P.coefficients()[i] += Q.coefficients()[i]; + } + } + return P; + } + + PUGS_INLINE constexpr Polynomial + operator-() const + { + return Polynomial{-m_coefficients}; + } + + template <size_t M> + PUGS_INLINE constexpr Polynomial<std::max(M, N)> + operator-(const Polynomial<M>& Q) const + { + Polynomial<std::max(M, N)> P; + if constexpr (M > N) { + P.coefficients() = -Q.coefficients(); + for (size_t i = 0; i <= N; ++i) { + P.coefficients()[i] += coefficients()[i]; + } + } else { + P.coefficients() = coefficients(); + for (size_t i = 0; i <= M; ++i) { + P.coefficients()[i] -= Q.coefficients()[i]; + } + } + return P; + } + + template <size_t M> + PUGS_INLINE constexpr Polynomial& + operator=(const Polynomial<M>& Q) + { + coefficients() = zero; + for (size_t i = N + 1; i <= M; ++i) { + Assert(Q.coefficients()[i] == 0, "degree of polynomial to small in assignation"); + } + // static_assert(N >= M, "degree of polynomial to small in assignation"); + for (size_t i = 0; i <= std::min(M, N); ++i) { + coefficients()[i] = Q.coefficients()[i]; + } + return *this; + } + + template <size_t M> + PUGS_INLINE constexpr Polynomial& + operator+=(const Polynomial<M>& Q) + { + static_assert(N >= M, "Polynomial degree to small in affectation addition"); + for (size_t i = 0; i <= M; ++i) { + coefficients()[i] += Q.coefficients()[i]; + } + return *this; + } + + template <size_t M> + PUGS_INLINE constexpr Polynomial& + operator-=(const Polynomial<M>& Q) + { + static_assert(N >= M, "Polynomial degree to small in affectation addition"); + for (size_t i = 0; i <= M; ++i) { + coefficients()[i] -= Q.coefficients()[i]; + } + return *this; + } + + template <size_t M> + PUGS_INLINE constexpr Polynomial<M + N> + operator*(const Polynomial<M>& Q) const + { + Polynomial<M + N> P; + P.coefficients() = zero; + for (size_t i = 0; i <= N; ++i) { + for (size_t j = 0; j <= M; ++j) { + P.coefficients()[i + j] += coefficients()[i] * Q.coefficients()[j]; + } + } + return P; + } + + template <size_t M> + PUGS_INLINE constexpr Polynomial& + operator*=(const Polynomial<M>& Q) + { + static_assert(N >= M, "Degree to small in affectation product"); + for (size_t i = N - M + 1; i <= N; ++i) { + Assert(coefficients()[i] == 0, "Degree of affectation product greater than the degree of input polynomial"); + } + Polynomial P(zero); + for (size_t i = 0; i <= N - M; ++i) { + for (size_t j = 0; j <= M; ++j) { + P.coefficients()[i + j] += coefficients()[i] * Q.coefficients()[j]; + } + } + coefficients() = P.coefficients(); + return *this; + } + + PUGS_INLINE + constexpr Polynomial + operator*(const double& lambda) const + { + return Polynomial(lambda * m_coefficients); + } + + template <size_t M> + PUGS_INLINE constexpr Polynomial<M * N> + compose(const Polynomial<M>& Q) const + { + Polynomial<M * N> P; + P.coefficients() = zero; + Polynomial<M * N> R; + R.coefficients() = zero; + + for (size_t i = 0; i <= N; ++i) { + R = Q.template pow<N>(i) * coefficients()[i]; + P += R; // R; + } + return P; + } + + template <size_t M, size_t I> + PUGS_INLINE constexpr Polynomial<M * N> + power(const Polynomial<M>& Q) const + { + return coefficients()[I] * Q.template pow<N>(I); + } + + template <size_t M, size_t... I> + PUGS_INLINE constexpr Polynomial<M * N> + compose2(const Polynomial<M>& Q, std::index_sequence<I...>) const + { + Polynomial<M * N> P; + P.coefficients() = zero; + P = (power<M, I>(Q) + ...); + return P; + } + + template <size_t M> + PUGS_INLINE constexpr Polynomial<M * N> + compose2(const Polynomial<M>& Q) const + { + Polynomial<M * N> P; + P.coefficients() = zero; + using IndexSequence = std::make_index_sequence<N + 1>; + return compose2<M>(Q, IndexSequence{}); + // for (size_t i = 0; i <= N; ++i) { + // P += Q.template pow<N>(i) * coefficients()[i]; + // } + return P; + } + + template <size_t M> + PUGS_INLINE constexpr Polynomial<M * N> + operator()(const Polynomial<M>& Q) const + { + Polynomial<M * N> P; + P.coefficients() = zero; + Polynomial<M * N> R; + R.coefficients() = zero; + + for (size_t i = 0; i <= N; ++i) { + R = Q.template pow<N>(i) * coefficients()[i]; + P += R; // R; + } + return P; + } + + template <size_t M> + PUGS_INLINE constexpr Polynomial<M * N> + pow(size_t power) const + { + Assert(power <= M, "You declared a polynomial of degree too small for return of the pow function"); + Polynomial<M * N> R; + R.coefficients() = zero; + if (power == 0) { + R.coefficients()[0] = 1; + } else { + R = *this; + for (size_t i = 1; i < power; ++i) { + R = R * *this; + } + } + return R; + } + + PUGS_INLINE + constexpr friend Polynomial + operator*(const double& lambda, const Polynomial& P) + { + return P * lambda; + } + + PUGS_INLINE + constexpr double + operator()(double x) const + { + double p_x = m_coefficients[N]; + for (size_t i = N; i > 0; --i) { + p_x *= x; + p_x += m_coefficients[i - 1]; + } + return p_x; + } + + template <size_t M> + PUGS_INLINE constexpr friend void + divide(const Polynomial<N>& P1, const Polynomial<M>& P2, Polynomial<N>& Q, Polynomial<N>& R) + { + const size_t Nr = P1.realDegree(); + const size_t Mr = P2.realDegree(); + R.coefficients() = P1.coefficients(); + Q.coefficients() = zero; + for (size_t k = Nr - Mr + 1; k > 0; --k) { + Q.coefficients()[k - 1] = R.coefficients()[Mr + k - 1] / P2.coefficients()[Mr]; + Polynomial<N - M> Q1; + Q1.coefficients() = zero; + Q1.coefficients()[k - 1] = Q.coefficients()[k - 1]; + R -= Q1 * P2; + } + for (size_t j = Mr + 1; j < Nr + 1; ++j) { + R.coefficients()[j] = 0; + } + } + PUGS_INLINE + constexpr friend Polynomial<N + 1> + primitive(const Polynomial& P) + { + TinyVector<N + 2> coefs; + for (size_t i = 0; i < N + 1; ++i) { + coefs[i + 1] = P.coefficients()[i] / double(i + 1); + } + coefs[0] = 0; + return Polynomial<N + 1>{coefs}; + } + + PUGS_INLINE + constexpr friend std::ostream& + operator<<(std::ostream& os, const Polynomial& P) + { + // os << "P(x) = "; + bool all_coef_zero = true; + if (N == 0) { + os << P.coefficients()[0]; + return os; + } + if (N != 1) { + if (P.coefficients()[N] != 0.) { + if (P.coefficients()[N] < 0.) { + os << "- "; + } + if (P.coefficients()[N] != 1 && P.coefficients()[N] != -1) { + os << std::abs(P.coefficients()[N]); + } + os << "x^" << N; + all_coef_zero = false; + } + } + for (size_t i = N - 1; i > 1; --i) { + if (P.coefficients()[i] != 0.) { + if (P.coefficients()[i] > 0.) { + os << " + "; + } else if (P.coefficients()[i] < 0.) { + os << " - "; + } + if (P.coefficients()[i] != 1 && P.coefficients()[i] != -1) { + os << std::abs(P.coefficients()[i]); + } + os << "x^" << i; + all_coef_zero = false; + } + } + if (P.coefficients()[1] != 0.) { + if (P.coefficients()[1] > 0. && N != 1) { + os << " + "; + } else if (P.coefficients()[1] < 0.) { + os << " - "; + } + if (P.coefficients()[1] != 1 && P.coefficients()[1] != -1) { + os << std::abs(P.coefficients()[1]); + } + os << "x"; + all_coef_zero = false; + } + if (P.coefficients()[0] != 0. || all_coef_zero) { + if (P.coefficients()[0] > 0.) { + os << " + "; + } else if (P.coefficients()[0] < 0.) { + os << " - "; + } + os << std::abs(P.coefficients()[0]); + } + return os; + } + + PUGS_INLINE + constexpr friend void + lagrangeBasis(const TinyVector<N + 1> zeros, TinyVector<N + 1, Polynomial<N>>& basis) + { + Polynomial<N> lj; + for (size_t j = 0; j < N + 1; ++j) { + basis[j] = lagrangePolynomial(zeros, j); + } + } + + PUGS_INLINE + constexpr friend Polynomial<N> + lagrangeToCanonical(const TinyVector<N + 1> lagrange_coefs, const std::array<Polynomial<N>, N + 1>& basis) + { + Polynomial<N> P(zero); + // lagrangeBasis({0, 0, 0}, basis); + for (size_t j = 0; j < N + 1; ++j) { + P += basis[j] * lagrange_coefs[j]; + } + return P; + } + + PUGS_INLINE constexpr Polynomial& operator=(const Polynomial&) = default; + PUGS_INLINE constexpr Polynomial& operator=(Polynomial&&) = default; + + PUGS_INLINE + constexpr Polynomial(const TinyVector<N + 1>& coefficients) noexcept : m_coefficients(coefficients) {} + + PUGS_INLINE + constexpr Polynomial(const Polynomial&) noexcept = default; + + PUGS_INLINE + constexpr Polynomial(Polynomial&&) noexcept = default; + + template <typename... T> + explicit PUGS_INLINE constexpr Polynomial(T&&... coefficients) noexcept : m_coefficients(coefficients...) + { + // static_assert(sizeof...(T) == N + 1, "invalid number of parameters"); + // static_assert((std::is_convertible_v<T, double> and ...), "arguments must be convertible to double"); + } + + PUGS_INLINE + constexpr Polynomial() noexcept = default; + + ~Polynomial() = default; +}; + +// template <size_t N> +// template <> +// PUGS_INLINE constexpr Polynomial(TinyVector<N + 1>&& coefficients) noexcept : m_coefficients{coefficients} +// {} + +template <size_t N> +PUGS_INLINE constexpr Polynomial<N> lagrangePolynomial(const TinyVector<N + 1> zeros, const size_t k); + +template <size_t N> +PUGS_INLINE constexpr std::array<Polynomial<N - 1>, N> +lagrangeBasis(const TinyVector<N>& zeros) +{ + static_assert(N >= 1, "invalid degree"); + std::array<Polynomial<N - 1>, N> basis; + for (size_t j = 0; j < N; ++j) { + basis[j] = lagrangePolynomial<N - 1>(zeros, j); + } + return basis; +} + +template <size_t N> +PUGS_INLINE constexpr double +integrate(const Polynomial<N>& P, const double& xinf, const double& xsup) +{ + Polynomial<N + 1> Q = primitive(P); + return (Q(xsup) - Q(xinf)); +} + +template <size_t N> +PUGS_INLINE constexpr double +symmetricIntegrate(const Polynomial<N>& P, const double& delta) +{ + Assert(delta > 0, "A positive delta is needed for symmetricIntegrate"); + double integral = 0.; + for (size_t i = 0; i <= N; ++i) { + if (i % 2 == 0) + integral += 2. * P.coefficients()[i] * std::pow(delta, i + 1) / (i + 1); + } + return integral; +} + +template <size_t N> +PUGS_INLINE constexpr auto +derivative(const Polynomial<N>& P) +{ + if constexpr (N == 0) { + return Polynomial<0>(zero); + } else { + TinyVector<N> coefs; + for (size_t i = 0; i < N; ++i) { + coefs[i] = double(i + 1) * P.coefficients()[i + 1]; + } + return Polynomial<N - 1>(coefs); + } +} + +template <size_t N> +PUGS_INLINE constexpr Polynomial<N> +lagrangePolynomial(const TinyVector<N + 1> zeros, const size_t k) +{ + for (size_t i = 0; i < N; ++i) { + Assert(zeros[i] < zeros[i + 1], "Interpolation values must be strictly increasing in Lagrange polynomials"); + } + Polynomial<N> lk; + lk.coefficients() = zero; + lk.coefficients()[0] = 1; + for (size_t i = 0; i < N + 1; ++i) { + if (k == i) + continue; + double factor = 1. / (zeros[k] - zeros[i]); + Polynomial<1> P(TinyVector<2>{-zeros[i] * factor, factor}); + lk *= P; + } + return lk; +} + +#endif // POLYNOMIAL_HPP diff --git a/src/analysis/PolynomialBasis.hpp b/src/analysis/PolynomialBasis.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a0b07ecafd27bbdc24dc41b4f210a2861cac46c1 --- /dev/null +++ b/src/analysis/PolynomialBasis.hpp @@ -0,0 +1,160 @@ +#ifndef POLYNOMIAL_BASIS_HPP +#define POLYNOMIAL_BASIS_HPP + +#include <algebra/TinyVector.hpp> +#include <analysis/Polynomial.hpp> +#include <utils/Messenger.hpp> + +enum class BasisType +{ + undefined, + lagrange, + canonical, + taylor +}; + +template <size_t N> +class PolynomialBasis +{ + private: + static_assert((N >= 0), "Number of elements in the basis must be non-negative"); + std::array<Polynomial<N>, N + 1> m_elements; + BasisType m_basis_type; + PUGS_INLINE + constexpr PolynomialBasis<N>& + _buildCanonicalBasis() + { + for (size_t i = 0; i <= N; i++) { + TinyVector<N + 1> coefficients(zero); + coefficients[i] = 1; + elements()[i] = Polynomial<N>(coefficients); + } + return *this; + } + + PUGS_INLINE + constexpr PolynomialBasis<N>& + _buildTaylorBasis(const double& shift) + { + TinyVector<N + 1> coefficients(zero); + elements()[0] = Polynomial<N>(coefficients); + elements()[0] += Polynomial<0>(1); + Polynomial<1> unit(-shift, 1); + for (size_t i = 1; i <= N; i++) { + elements()[i] = elements()[i - 1] * unit; + } + return *this; + } + + PUGS_INLINE + constexpr PolynomialBasis<N>& + _buildLagrangeBasis(const TinyVector<N + 1>& zeros) + { + if constexpr (N == 0) { + elements()[0] = Polynomial<0>(1); + return *this; + } else { + for (size_t i = 0; i < N; ++i) { + Assert(zeros[i] < zeros[i + 1], "Interpolation values must be strictly increasing in Lagrange polynomials"); + } + for (size_t i = 0; i <= N; ++i) { + TinyVector<N + 1> coefficients(zero); + elements()[i] = Polynomial<N>(coefficients); + elements()[i].coefficients()[0] = 1; + for (size_t j = 0; j < N + 1; ++j) { + if (i == j) + continue; + double adim = 1. / (zeros[i] - zeros[j]); + elements()[i] *= Polynomial<1>{-zeros[j] * adim, adim}; + } + } + return *this; + } + } + + public: + PUGS_INLINE + constexpr size_t + size() const + { + return N + 1; + } + + PUGS_INLINE + constexpr size_t + degree() const + { + return N; + } + + PUGS_INLINE + constexpr BasisType& + type() + { + return m_basis_type; + } + + PUGS_INLINE + std::string_view + displayType() + { + switch (m_basis_type) { + case BasisType::lagrange: + return "lagrange"; + case BasisType::canonical: + return "canonical"; + case BasisType::taylor: + return "taylor"; + case BasisType::undefined: + return "undefined"; + default: + return "unknown basis type"; + } + } + + PUGS_INLINE + constexpr const std::array<Polynomial<N>, N + 1>& + elements() const + { + return m_elements; + } + + PUGS_INLINE + constexpr std::array<Polynomial<N>, N + 1>& + elements() + { + return m_elements; + } + + PUGS_INLINE + constexpr PolynomialBasis<N>& + build(BasisType basis_type, const double& shift = 0, const TinyVector<N + 1>& zeros = zero) + { + type() = basis_type; + switch (basis_type) { + case BasisType::lagrange: { + return _buildLagrangeBasis(zeros); + break; + } + case BasisType::canonical: { + return _buildCanonicalBasis(); + break; + } + case BasisType::taylor: { + return _buildTaylorBasis(shift); + break; + } + // LCOV_EXCL_START + default: { + throw UnexpectedError("unknown basis type"); + } + // LCOV_EXCL_STOP + } + } + + PUGS_INLINE + constexpr PolynomialBasis() noexcept : m_basis_type{BasisType::undefined} {} + + ~PolynomialBasis() = default; +}; +#endif // POLYNOMIAL_BASIS_HPP diff --git a/src/analysis/PolynomialP.hpp b/src/analysis/PolynomialP.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1665d27e57feca7ffaa7c7bbc1190a6c2de99cab --- /dev/null +++ b/src/analysis/PolynomialP.hpp @@ -0,0 +1,658 @@ +#ifndef POLYNOMIALP_HPP +#define POLYNOMIALP_HPP + +#include <algebra/TinyVector.hpp> +#include <analysis/CubeGaussQuadrature.hpp> +#include <analysis/GaussLegendreQuadratureDescriptor.hpp> +#include <analysis/GaussQuadratureDescriptor.hpp> +#include <analysis/QuadratureManager.hpp> +#include <analysis/SquareGaussQuadrature.hpp> +#include <analysis/TriangleGaussQuadrature.hpp> +#include <geometry/LineTransformation.hpp> +#include <geometry/SquareTransformation.hpp> +#include <geometry/TriangleTransformation.hpp> +#include <utils/Exceptions.hpp> + +template <size_t N, size_t Dimension> +class PolynomialP +{ + private: + static constexpr size_t size_coef = [] { + if constexpr (Dimension == 1) { + return N + 1; + } else if constexpr (Dimension == 2) { + return (N + 1) * (N + 2) / 2; + } else { + static_assert(Dimension == 3); + return (N + 1) * (N + 2) * (N + 3) / 6; + } + }(); + + using Coefficients = TinyVector<size_coef, double>; + Coefficients m_coefficients; + static_assert((N >= 0), "PolynomialP degree must be non-negative"); + static_assert((Dimension > 0), "PolynomialP dimension must be positive"); + static_assert((Dimension <= 3), "PolynomialP dimension must no greater than three"); + + public: + PUGS_INLINE + constexpr size_t + degree() const + { + return N; + } + + constexpr size_t + dim() const + { + return Dimension; + } + + PUGS_INLINE + constexpr const TinyVector<size_coef, double>& + coefficients() const + { + return m_coefficients; + } + + PUGS_INLINE + constexpr TinyVector<size_coef, double>& + coefficients() + { + return m_coefficients; + } + + PUGS_INLINE constexpr bool + operator==(const PolynomialP& q) const + { + return m_coefficients == q.m_coefficients; + } + + PUGS_INLINE constexpr PolynomialP(const TinyVector<size_coef, double>& coefficients) noexcept + : m_coefficients{coefficients} + {} + + PUGS_INLINE + constexpr PolynomialP(TinyVector<size_coef, double>&& coefficients) noexcept : m_coefficients{coefficients} {} + + PUGS_INLINE + constexpr bool + operator!=(const PolynomialP& q) const + { + return not this->operator==(q); + } + + PUGS_INLINE constexpr PolynomialP + operator+(const PolynomialP Q) const + { + PolynomialP<N, Dimension> P(m_coefficients); + for (size_t i = 0; i < size_coef; ++i) { + P.coefficients()[i] += Q.coefficients()[i]; + } + return P; + } + + PUGS_INLINE constexpr PolynomialP + operator-() const + { + PolynomialP<N, Dimension> P; + P.coefficients() = -coefficients(); + return P; + } + + PUGS_INLINE constexpr PolynomialP + operator-(const PolynomialP Q) const + { + PolynomialP<N, Dimension> P(m_coefficients); + P = P + (-Q); + return P; + } + + template <size_t M, size_t Dim> + PUGS_INLINE constexpr PolynomialP& + operator=(const PolynomialP<M, Dim>& Q) + { + coefficients() = zero; + for (size_t i = 0; i < size_coef; ++i) { + coefficients()[i] = Q.coefficients()[i]; + } + return *this; + } + + PUGS_INLINE constexpr PolynomialP& + operator+=(const PolynomialP& Q) + { + m_coefficients += Q.coefficients(); + return *this; + } + + template <size_t M> + PUGS_INLINE constexpr PolynomialP& + operator-=(const PolynomialP& Q) + { + m_coefficients -= Q.coefficients(); + return *this; + } + + PUGS_INLINE + constexpr PolynomialP + operator*(const double& lambda) const + { + TinyVector<size_coef> mult_coefs = lambda * m_coefficients; + PolynomialP<N, Dimension> M(mult_coefs); + return M; + } + + PUGS_INLINE + constexpr friend PolynomialP<N, Dimension> + operator*(const double& lambda, const PolynomialP<N, Dimension> P) + { + return P * lambda; + } + + PUGS_INLINE + constexpr double + operator[](const TinyVector<Dimension, size_t> relative_pos) const + { + size_t total_degree_chk = 0; + for (size_t i = 0; i < Dimension; ++i) { + Assert((relative_pos[i] <= N), + "You are looking for a coefficient of order greater than the degree of the polynomial"); + total_degree_chk += relative_pos[i]; + } + Assert((total_degree_chk <= N), "The sum of the degrees of the coefficient you are looking for is greater than the " + "degree of the polynomial"); + TinyVector<size_coef> absolute_coefs = this->coefficients(); + size_t absolute_position = 0; + if constexpr (Dimension == 1) { + absolute_position = relative_pos[0]; + } else if constexpr (Dimension == 2) { + size_t total_degree = relative_pos[0] + relative_pos[1]; + absolute_position = total_degree * (total_degree + 1) / 2 + relative_pos[1]; + } else { + // throw NotImplementedError("Not yet Available in 3D"); + static_assert(Dimension == 3); + size_t total_degree = relative_pos[0] + relative_pos[1] + relative_pos[2]; + size_t total_sub_degree = relative_pos[1] + relative_pos[2]; + absolute_position = total_degree * (total_degree + 1) * (total_degree + 2) / 6 + + total_sub_degree * (total_sub_degree + 1) / 2 + relative_pos[2]; + } + + return absolute_coefs[absolute_position]; + } + + PUGS_INLINE + constexpr double + operator[](const TinyVector<Dimension, size_t> relative_pos) + { + size_t total_degree_chk = 0; + for (size_t i = 0; i < Dimension; ++i) { + Assert((relative_pos[i] <= N), + "You are looking for a coefficient of order greater than the degree of the polynomial"); + total_degree_chk += relative_pos[i]; + } + Assert((total_degree_chk <= N), "The sum of the degrees of the coefficient you are looking for is greater than the " + "degree of the polynomial"); + TinyVector<size_coef> absolute_coefs = this->coefficients(); + size_t absolute_position = 0; + if constexpr (Dimension == 1) { + absolute_position = relative_pos[0]; + } else if constexpr (Dimension == 2) { + size_t total_degree = relative_pos[0] + relative_pos[1]; + absolute_position = total_degree * (total_degree + 1) / 2 + relative_pos[1]; + } else { + // throw NotImplementedError("Not yet Available in 3D"); + static_assert(Dimension == 3); + size_t total_degree = relative_pos[0] + relative_pos[1] + relative_pos[2]; + size_t total_sub_degree = relative_pos[1] + relative_pos[2]; + absolute_position = total_degree * (total_degree + 1) * (total_degree + 2) / 6 + + total_sub_degree * (total_sub_degree + 1) / 2 + relative_pos[2]; + } + + return absolute_coefs[absolute_position]; + } + + PUGS_INLINE + constexpr double + absolute_position(const TinyVector<Dimension, size_t> relative_pos) const + { + size_t total_degree = 0; + for (size_t i = 0; i < Dimension; ++i) { + Assert((relative_pos[i] <= N), + "You are looking for a coefficient of order greater than the degree of the polynomial"); + total_degree += relative_pos[i]; + } + Assert((total_degree <= N), "The sum of the degrees of the coefficient you are looking for is greater than the " + "degree of the polynomial"); + size_t abs_pos = 0; + if constexpr (Dimension == 1) { + abs_pos = relative_pos[0]; + } else if constexpr (Dimension == 2) { + abs_pos = total_degree * (total_degree + 1) / 2 + relative_pos[1]; + } else { + static_assert(Dimension == 3); + total_degree = relative_pos[0] + relative_pos[1] + relative_pos[2]; + size_t total_sub_degree = relative_pos[1] + relative_pos[2]; + abs_pos = total_degree * (total_degree + 1) * (total_degree + 2) / 6 + + total_sub_degree * (total_sub_degree + 1) / 2 + relative_pos[2]; + // throw NotImplementedError("Not yet Available in 3D"); + } + + return abs_pos; + } + + PUGS_INLINE + constexpr double + operator()(const TinyVector<Dimension> x) const + { + const auto& P = *this; + double value = 0.; + if constexpr (Dimension == 1) { + value = m_coefficients[N]; + for (size_t i = N; i > 0; --i) { + value *= x; + value += m_coefficients[i - 1]; + } + } else if constexpr (Dimension == 2) { + TinyVector<Dimension, size_t> relative_pos(0, N); + value = P[relative_pos]; + for (size_t i = N; i > 0; --i) { + value *= x[1]; + relative_pos = TinyVector<Dimension, size_t>(N - i + 1, i - 1); + double valuex = P[relative_pos]; + for (size_t j = N - i + 1; j > 0; --j) { + valuex *= x[0]; + relative_pos = TinyVector<Dimension, size_t>(j - 1, i - 1); + valuex += P[relative_pos]; + } + value += valuex; + } + } else { + TinyVector<Dimension, size_t> relative_pos(0, 0, N); + value = P[relative_pos]; + for (size_t i = N; i > 0; --i) { + value *= x[2]; + relative_pos = TinyVector<Dimension, size_t>(0, N - i + 1, i - 1); + double valuey = P[relative_pos]; + for (size_t j = N - i + 1; j > 0; --j) { + valuey *= x[1]; + relative_pos = TinyVector<Dimension, size_t>(N - i - j + 2, j - 1, i - 1); + double valuex = P[relative_pos]; + for (size_t k = N - i - j + 2; k > 0; --k) { + valuex *= x[0]; + relative_pos = TinyVector<Dimension, size_t>(k - 1, j - 1, i - 1); + + valuex += P[relative_pos]; + } + valuey += valuex; + } + value += valuey; + // throw NotImplementedError("Not yet Available in 3D"); + } + } + return value; + } + + PUGS_INLINE size_t + find_size_coef(const size_t degree) + { + if constexpr (Dimension == 1) { + return degree + 1; + } else if constexpr (Dimension == 2) { + return (degree + 1) * (degree + 2) / 2; + } else { + static_assert(Dimension == 3); + return (degree + 1) * (degree + 2) * (degree + 3) / 6; + } + } + + PUGS_INLINE constexpr PolynomialP<N, Dimension> + derivative(const size_t var) const + { + const auto P = *this; + TinyVector<size_coef> coefs(zero); + PolynomialP<N, Dimension> Q(coefs); + if constexpr (N != 0) { + Assert(var < Dimension, + "You can not derive a polynomial with respect to a variable of rank greater than the dimension"); + if constexpr (Dimension == 1) { + for (size_t i = 0; i < size_coef - 1; ++i) { + coefs[i] = double(i + 1) * P.coefficients()[i + 1]; + } + } else if constexpr (Dimension == 2) { + if (var == 0) { + for (size_t i = 0; i < N; ++i) { + for (size_t j = 0; j < N - i; ++j) { + TinyVector<Dimension, size_t> relative_pos(i, j); + TinyVector<Dimension, size_t> relative_posp(i + 1, j); + size_t absolute_position = Q.absolute_position(relative_pos); + size_t absolute_positionp = P.absolute_position(relative_posp); + Q.coefficients()[absolute_position] = double(i + 1) * m_coefficients[absolute_positionp]; + } + } + } else { + for (size_t i = 0; i < N; ++i) { + for (size_t j = 0; j < N - i; ++j) { + TinyVector<Dimension, size_t> relative_pos(i, j); + TinyVector<Dimension, size_t> relative_posp(i, j + 1); + size_t absolute_position = Q.absolute_position(relative_pos); + size_t absolute_positionp = P.absolute_position(relative_posp); + Q.coefficients()[absolute_position] = double(j + 1) * m_coefficients[absolute_positionp]; + } + } + } + } else { + static_assert(Dimension == 3); + if (var == 0) { + for (size_t i = 0; i < N; ++i) { + for (size_t j = 0; j < N - i; ++j) { + for (size_t k = 0; k < N - i - j; ++k) { + TinyVector<Dimension, size_t> relative_pos(i, j, k); + TinyVector<Dimension, size_t> relative_posp(i + 1, j, k); + size_t absolute_position = Q.absolute_position(relative_pos); + size_t absolute_positionp = P.absolute_position(relative_posp); + Q.coefficients()[absolute_position] = double(i + 1) * m_coefficients[absolute_positionp]; + } + } + } + } else if (var == 1) { + for (size_t i = 0; i < N; ++i) { + for (size_t j = 0; j < N - i; ++j) { + for (size_t k = 0; k < N - i - j; ++k) { + TinyVector<Dimension, size_t> relative_pos(i, j, k); + TinyVector<Dimension, size_t> relative_posp(i, j + 1, k); + size_t absolute_position = Q.absolute_position(relative_pos); + size_t absolute_positionp = P.absolute_position(relative_posp); + Q.coefficients()[absolute_position] = double(j + 1) * m_coefficients[absolute_positionp]; + } + } + } + } else { + for (size_t i = 0; i < N; ++i) { + for (size_t j = 0; j < N - i; ++j) { + for (size_t k = 0; k < N - i - j; ++k) { + TinyVector<Dimension, size_t> relative_pos(i, j, k); + TinyVector<Dimension, size_t> relative_posp(i, j, k + 1); + size_t absolute_position = Q.absolute_position(relative_pos); + size_t absolute_positionp = P.absolute_position(relative_posp); + Q.coefficients()[absolute_position] = double(k + 1) * m_coefficients[absolute_positionp]; + } + } + } + } + // throw NotImplementedError("Not yet Available in 3D"); + } + } + return Q; + } + + PUGS_INLINE + constexpr friend std::ostream& + operator<<(std::ostream& os, const PolynomialP<N, Dimension>& P) + { + // os << "P(x) = "; + bool all_coef_zero = true; + if (N == 0) { + os << P.coefficients()[0]; + return os; + } + if constexpr (Dimension == 1) { + if (N != 1) { + if (P.coefficients()[N] != 0.) { + if (P.coefficients()[N] < 0.) { + os << "- "; + } + if (P.coefficients()[N] != 1 && P.coefficients()[N] != -1) { + os << std::abs(P.coefficients()[N]); + } + os << "x^" << N; + all_coef_zero = false; + } + } + for (size_t i = N - 1; i > 1; --i) { + if (P.coefficients()[i] != 0.) { + if (P.coefficients()[i] > 0.) { + os << " + "; + } else if (P.coefficients()[i] < 0.) { + os << " - "; + } + if (P.coefficients()[i] != 1 && P.coefficients()[i] != -1) { + os << std::abs(P.coefficients()[i]); + } + os << "x^" << i; + all_coef_zero = false; + } + } + if (P.coefficients()[1] != 0.) { + if (P.coefficients()[1] > 0. && N != 1) { + os << " + "; + } else if (P.coefficients()[1] < 0.) { + os << " - "; + } + if (P.coefficients()[1] != 1 && P.coefficients()[1] != -1) { + os << std::abs(P.coefficients()[1]); + } + os << "x"; + all_coef_zero = false; + } + if (P.coefficients()[0] != 0. || all_coef_zero) { + if (P.coefficients()[0] > 0.) { + os << " + "; + } else if (P.coefficients()[0] < 0.) { + os << " - "; + } + os << std::abs(P.coefficients()[0]); + } + return os; + } else if constexpr (Dimension == 2) { + size_t i = 0; + size_t j = N; + TinyVector<Dimension, size_t> rel_pos(i, j); + double coef = P[rel_pos]; + if (coef != 0.) { + if (coef < 0.) { + os << " - "; + } + if (coef != 1 && coef != -1) { + os << std::abs(coef); + } + os << "y^" << N; + } + size_t degree = N; + for (size_t k = size_coef - 1; k > 0; --k) { + if (j > 0) { + j--; + i++; + } else { + degree--; + j = degree; + i = 0; + } + rel_pos = TinyVector<Dimension, size_t>(i, j); + coef = P[rel_pos]; + if (coef != 0.) { + if (coef > 0.) { + os << " + "; + } else if (coef < 0.) { + os << " - "; + } + if ((coef != 1 && coef != -1) || (i == 0 && j == 0)) { + os << std::abs(coef); + } + if (i == 0 && j == 0) + continue; + if (i == 0) { + if (j != 1) { + os << "y^" << j; + } else { + os << "y"; + } + } else if (j == 0) { + if (i == 1) { + os << "x"; + } else { + os << "x^" << i; + } + } else { + if (i == 1 && j == 1) { + os << "xy"; + } else if (i == 1) { + os << "x" + << "y^" << j; + } else if (j == 1) { + os << "x^" << i << "y"; + } else { + os << "x^" << i << "y^" << j; + } + } + all_coef_zero = false; + } + } + + return os; + } else { + // size_t i = 0; + // size_t j = 0; + // size_t k = N; + // TinyVector<Dimension, size_t> rel_pos(i, j, k); + // double coef = P[rel_pos]; + // if (coef != 0.) { + // if (coef < 0.) { + // os << " - "; + // } + // if (coef != 1 && coef != -1) { + // os << std::abs(coef); + // } + // os << "z^" << N; + // } + // size_t degree = N; + // for (size_t l = size_coef - 1; l > 0; --l) { + // if (k > 0) { + // k--; + // if (j < k) { + // j++; + // } else { + // j--; + // i++; + // } + // } else { + // degree--; + // k = degree; + // i = 0; + // j = 0; + // } + + // rel_pos = TinyVector<Dimension, size_t>(i, j, k); + // double coef = P[rel_pos]; + // if (coef != 0.) { + // if (coef > 0.) { + // os << " + "; + // } else if (coef < 0.) { + // os << " - "; + // } + // if ((coef != 1 && coef != -1) || (i == 0 && j == 0 && k == 0)) { + // os << std::abs(coef); + // } + // if (i == 0 && j == 0 && k == 0) + // continue; + // if (i == 0 && j == 0) { + // if (k != 1) { + // os << "z^" << j; + // } else { + // os << "z"; + // } + // } else if (i == 0 && k == 0) { + // if (j == 1) { + // os << "y"; + // } else { + // os << "y^" << i; + // } + // } else if (j == 0 && k == 0) { + // if (i == 1) { + // os << "x"; + // } else { + // os << "x^" << i; + // } + // } else { + // if (i == 1 && j == 1 && k == 1) { + // os << "xyz"; + // } else if (i == 1) { + // os << "x" + // << "y^" << j << "z^" << k; + // } else if (j == 1) { + // os << "x^" << i << "y" + // << "z^" << k; + // } else if (k == 1) { + // os << "x^" << i << "y^" << j << "z"; + // } else { + // os << "x^" << i << "y^" << j << "z^" << k; + // } + // } + // all_coef_zero = false; + // } + // + for (size_t l = 0; l < size_coef; ++l) { + double coef = P.coefficients()[l]; + os << coef << " "; + } + return os; + // throw NotImplementedError("Not yet Available in 3D"); + } + } + + PUGS_INLINE constexpr PolynomialP() noexcept = default; + ~PolynomialP() = default; +}; + +template <size_t N, size_t Dimension, size_t P> +PUGS_INLINE double +integrate(const PolynomialP<N, Dimension> Q, const std::array<TinyVector<Dimension>, P>& positions) +{ + double integral = 0.; + static_assert(P > 1, "For the integration, number of positions should be greater or equal to 2"); + static_assert(N >= 0, "invalid degree"); + if constexpr (Dimension == 1) { + static_assert(P == 2, "In 1D number of positions should be 2"); + throw NotImplementedError("Not yet Available in 1D"); + } else if constexpr (Dimension == 2) { + static_assert(P <= 4, "In 2D number of positions should be lesser or equal to 4"); + if constexpr (P == 2) { + const QuadratureFormula<1>& lN = + QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(N + 1)); + const LineTransformation<2> t{positions[0], positions[1]}; + double value = 0.; + for (size_t i = 0; i < lN.numberOfPoints(); ++i) { + value += lN.weight(i) * t.velocityNorm() * Q(t(lN.point(i))); + } + integral = value; + + } else if constexpr (P == 3) { + const QuadratureFormula<2>& lN = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(N)); + auto point_list = lN.pointList(); + auto weight_list = lN.weightList(); + TriangleTransformation<2> t{positions[0], positions[1], positions[2]}; + auto value = weight_list[0] * Q(t(point_list[0])); + for (size_t i = 1; i < weight_list.size(); ++i) { + value += weight_list[i] * Q(t(point_list[i])); + } + integral = t.jacobianDeterminant() * value; + } else { + const QuadratureFormula<2>& lN = + QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor(N + 1)); + auto point_list = lN.pointList(); + auto weight_list = lN.weightList(); + SquareTransformation<2> s{positions[0], positions[1], positions[2], positions[3]}; + auto value = weight_list[0] * s.jacobianDeterminant(point_list[0]) * Q(s(point_list[0])); + for (size_t i = 1; i < weight_list.size(); ++i) { + value += weight_list[i] * s.jacobianDeterminant(point_list[i]) * Q(s(point_list[i])); + } + integral = value; + } + } else { + static_assert(Dimension == 3, "Dimension should be <=3"); + throw NotImplementedError("Not yet Available in 3D"); + } + return integral; +} + +#endif // POLYNOMIALP_HPP diff --git a/src/analysis/TaylorPolynomial.hpp b/src/analysis/TaylorPolynomial.hpp new file mode 100644 index 0000000000000000000000000000000000000000..dc1a607b31769b62ac7c061af1ee69a851868934 --- /dev/null +++ b/src/analysis/TaylorPolynomial.hpp @@ -0,0 +1,681 @@ +#ifndef TAYLOR_POLYNOMIAL_HPP +#define TAULOR_POLYNOMIAL_HPP + +#include <algebra/TinyVector.hpp> +#include <analysis/CubeGaussQuadrature.hpp> +#include <analysis/GaussLegendreQuadratureDescriptor.hpp> +#include <analysis/GaussQuadratureDescriptor.hpp> +#include <analysis/QuadratureManager.hpp> +#include <analysis/SquareGaussQuadrature.hpp> +#include <analysis/TriangleGaussQuadrature.hpp> +#include <geometry/LineTransformation.hpp> +#include <geometry/SquareTransformation.hpp> +#include <geometry/TriangleTransformation.hpp> +#include <utils/Exceptions.hpp> + +template <size_t N, size_t Dimension> +class TaylorPolynomial +{ + private: + static constexpr size_t size_coef = [] { + if constexpr (Dimension == 1) { + return N + 1; + } else if constexpr (Dimension == 2) { + return (N + 1) * (N + 2) / 2; + } else { + static_assert(Dimension == 3); + return (N + 1) * (N + 2) * (N + 3) / 6; + } + }(); + + using Coefficients = TinyVector<size_coef, double>; + Coefficients m_coefficients; + TinyVector<Dimension> m_x0; + static_assert((N >= 0), "TaylorPolynomial degree must be non-negative"); + static_assert((Dimension > 0), "TaylorPolynomial dimension must be positive"); + static_assert((Dimension <= 3), "TaylorPolynomial dimension must no greater than three"); + + public: + PUGS_INLINE + constexpr size_t + degree() const + { + return N; + } + + constexpr size_t + dim() const + { + return Dimension; + } + + PUGS_INLINE + constexpr const TinyVector<size_coef, double>& + coefficients() const + { + return m_coefficients; + } + + PUGS_INLINE + constexpr const TinyVector<Dimension, double>& + x0() const + { + return m_x0; + } + + PUGS_INLINE + constexpr TinyVector<size_coef, double>& + coefficients() + { + return m_coefficients; + } + + PUGS_INLINE constexpr bool + operator==(const TaylorPolynomial& q) const + { + return (m_coefficients == q.m_coefficients && m_x0 == q.m_x0); + } + + PUGS_INLINE constexpr TaylorPolynomial(const TinyVector<size_coef, double>& coefficients, + const TinyVector<Dimension, double>& x0) noexcept + : m_coefficients{coefficients}, m_x0(x0) + {} + + PUGS_INLINE + constexpr TaylorPolynomial(TinyVector<size_coef, double>&& coefficients, + const TinyVector<Dimension, double>&& x0) noexcept + : m_coefficients{coefficients}, m_x0(x0) + {} + + PUGS_INLINE + constexpr bool + operator!=(const TaylorPolynomial& q) const + { + return not this->operator==(q); + } + + PUGS_INLINE constexpr TaylorPolynomial + operator+(const TaylorPolynomial Q) const + { + Assert(m_x0 == Q.m_x0, "You cannot add Taylor polynomials with different origins"); + TaylorPolynomial<N, Dimension> P(m_coefficients, m_x0); + for (size_t i = 0; i < size_coef; ++i) { + P.coefficients()[i] += Q.coefficients()[i]; + } + return P; + } + + PUGS_INLINE constexpr TaylorPolynomial + operator-() const + { + TaylorPolynomial<N, Dimension> P; + P.coefficients() = -coefficients(); + P.m_x0 = m_x0; + return P; + } + + PUGS_INLINE constexpr TaylorPolynomial + operator-(const TaylorPolynomial Q) const + { + Assert(m_x0 == Q.m_x0, "You cannot subtract Taylor polynomials with different origins"); + TaylorPolynomial<N, Dimension> P(m_coefficients, m_x0); + P = P + (-Q); + return P; + } + + template <size_t M, size_t Dim> + PUGS_INLINE constexpr TaylorPolynomial& + operator=(const TaylorPolynomial<M, Dim>& Q) + { + coefficients() = zero; + for (size_t i = 0; i < size_coef; ++i) { + coefficients()[i] = Q.coefficients()[i]; + } + m_x0 = Q.m_x0; + return *this; + } + + PUGS_INLINE constexpr TaylorPolynomial& + operator+=(const TaylorPolynomial& Q) + { + Assert(m_x0 == Q.m_x0, "You cannot add Taylor polynomials with different origins"); + m_coefficients += Q.coefficients(); + return *this; + } + + template <size_t M> + PUGS_INLINE constexpr TaylorPolynomial& + operator-=(const TaylorPolynomial& Q) + { + Assert(m_x0 == Q.m_x0, "You cannot subtract Taylor polynomials with different origins"); + m_coefficients -= Q.coefficients(); + return *this; + } + + PUGS_INLINE + constexpr TaylorPolynomial + operator*(const double& lambda) const + { + TinyVector<size_coef> mult_coefs = lambda * m_coefficients; + TaylorPolynomial<N, Dimension> M(mult_coefs, m_x0); + return M; + } + + PUGS_INLINE + constexpr friend TaylorPolynomial<N, Dimension> + operator*(const double& lambda, const TaylorPolynomial<N, Dimension> P) + { + return P * lambda; + } + + PUGS_INLINE + constexpr double + operator[](const TinyVector<Dimension, size_t>& relative_pos) const + { + size_t total_degree_chk = 0; + for (size_t i = 0; i < Dimension; ++i) { + Assert((relative_pos[i] <= N), + "You are looking for a coefficient of order greater than the degree of the polynomial"); + total_degree_chk += relative_pos[i]; + } + Assert((total_degree_chk <= N), "The sum of the degrees of the coefficient you are looking for is greater than the " + "degree of the polynomial"); + size_t absolute_position = 0; + if constexpr (Dimension == 1) { + absolute_position = relative_pos[0]; + } else if constexpr (Dimension == 2) { + size_t total_degree = relative_pos[0] + relative_pos[1]; + absolute_position = total_degree * (total_degree + 1) / 2 + relative_pos[1]; + } else { + // throw NotImplementedError("Not yet Available in 3D"); + static_assert(Dimension == 3); + size_t total_degree = relative_pos[0] + relative_pos[1] + relative_pos[2]; + size_t total_sub_degree = relative_pos[1] + relative_pos[2]; + absolute_position = total_degree * (total_degree + 1) * (total_degree + 2) / 6 + + total_sub_degree * (total_sub_degree + 1) / 2 + relative_pos[2]; + } + + return m_coefficients[absolute_position]; + } + + PUGS_INLINE + constexpr double& + operator[](const TinyVector<Dimension, size_t>& relative_pos) + { + size_t total_degree_chk = 0; + for (size_t i = 0; i < Dimension; ++i) { + Assert((relative_pos[i] <= N), + "You are looking for a coefficient of order greater than the degree of the polynomial"); + total_degree_chk += relative_pos[i]; + } + Assert((total_degree_chk <= N), "The sum of the degrees of the coefficient you are looking for is greater than the " + "degree of the polynomial"); + size_t absolute_position = 0; + if constexpr (Dimension == 1) { + absolute_position = relative_pos[0]; + } else if constexpr (Dimension == 2) { + size_t total_degree = relative_pos[0] + relative_pos[1]; + absolute_position = total_degree * (total_degree + 1) / 2 + relative_pos[1]; + } else { + // throw NotImplementedError("Not yet Available in 3D"); + static_assert(Dimension == 3); + size_t total_degree = relative_pos[0] + relative_pos[1] + relative_pos[2]; + size_t total_sub_degree = relative_pos[1] + relative_pos[2]; + absolute_position = total_degree * (total_degree + 1) * (total_degree + 2) / 6 + + total_sub_degree * (total_sub_degree + 1) / 2 + relative_pos[2]; + } + + return m_coefficients[absolute_position]; + } + + PUGS_INLINE + constexpr double + absolute_position(const TinyVector<Dimension, size_t> relative_pos) const + { + size_t total_degree = 0; + for (size_t i = 0; i < Dimension; ++i) { + Assert((relative_pos[i] <= N), + "You are looking for a coefficient of order greater than the degree of the polynomial"); + total_degree += relative_pos[i]; + } + Assert((total_degree <= N), "The sum of the degrees of the coefficient you are looking for is greater than the " + "degree of the polynomial"); + size_t abs_pos = 0; + if constexpr (Dimension == 1) { + abs_pos = relative_pos[0]; + } else if constexpr (Dimension == 2) { + abs_pos = total_degree * (total_degree + 1) / 2 + relative_pos[1]; + } else { + static_assert(Dimension == 3); + total_degree = relative_pos[0] + relative_pos[1] + relative_pos[2]; + size_t total_sub_degree = relative_pos[1] + relative_pos[2]; + abs_pos = total_degree * (total_degree + 1) * (total_degree + 2) / 6 + + total_sub_degree * (total_sub_degree + 1) / 2 + relative_pos[2]; + // throw NotImplementedError("Not yet Available in 3D"); + } + + return abs_pos; + } + + PUGS_INLINE + constexpr double + operator()(const TinyVector<Dimension> x) const + { + const auto& P = *this; + double value = 0.; + if constexpr (Dimension == 1) { + value = m_coefficients[N]; + for (size_t i = N; i > 0; --i) { + value *= x - m_x0; + value += m_coefficients[i - 1]; + } + } else if constexpr (Dimension == 2) { + TinyVector<Dimension, size_t> relative_pos(0, N); + value = P[relative_pos]; + for (size_t i = N; i > 0; --i) { + value *= (x[1] - m_x0[1]); + relative_pos = TinyVector<Dimension, size_t>(N - i + 1, i - 1); + double valuex = P[relative_pos]; + for (size_t j = N - i + 1; j > 0; --j) { + valuex *= (x[0] - m_x0[0]); + relative_pos = TinyVector<Dimension, size_t>(j - 1, i - 1); + valuex += P[relative_pos]; + } + value += valuex; + } + } else { + TinyVector<Dimension, size_t> relative_pos(0, 0, N); + value = P[relative_pos]; + for (size_t i = N; i > 0; --i) { + value *= (x[2] - m_x0[2]); + relative_pos = TinyVector<Dimension, size_t>(0, N - i + 1, i - 1); + double valuey = P[relative_pos]; + for (size_t j = N - i + 1; j > 0; --j) { + valuey *= (x[1] - m_x0[1]); + relative_pos = TinyVector<Dimension, size_t>(N - i - j + 2, j - 1, i - 1); + double valuex = P[relative_pos]; + for (size_t k = N - i - j + 2; k > 0; --k) { + valuex *= (x[0] - m_x0[0]); + relative_pos = TinyVector<Dimension, size_t>(k - 1, j - 1, i - 1); + + valuex += P[relative_pos]; + } + valuey += valuex; + } + value += valuey; + // throw NotImplementedError("Not yet Available in 3D"); + } + } + + return value; + } + + PUGS_INLINE size_t + find_size_coef(const size_t degree) + { + if constexpr (Dimension == 1) { + return degree + 1; + } else if constexpr (Dimension == 2) { + return (degree + 1) * (degree + 2) / 2; + } else { + static_assert(Dimension == 3); + return (degree + 1) * (degree + 2) * (degree + 3) / 6; + } + } + + PUGS_INLINE constexpr TaylorPolynomial<N, Dimension> + derivative(const size_t var) const + { + const auto P = *this; + TinyVector<size_coef> coefs(zero); + TaylorPolynomial<N, Dimension> Q(coefs, m_x0); + if constexpr (N != 0) { + Assert(var < Dimension, + "You can not derive a polynomial with respect to a variable of rank greater than the dimension"); + if constexpr (Dimension == 1) { + for (size_t i = 0; i < size_coef - 1; ++i) { + coefs[i] = double(i + 1) * P.coefficients()[i + 1]; + } + } else if constexpr (Dimension == 2) { + if (var == 0) { + for (size_t i = 0; i < N; ++i) { + for (size_t j = 0; j < N - i; ++j) { + TinyVector<Dimension, size_t> relative_pos(i, j); + TinyVector<Dimension, size_t> relative_posp(i + 1, j); + size_t absolute_position = Q.absolute_position(relative_pos); + size_t absolute_positionp = P.absolute_position(relative_posp); + Q.coefficients()[absolute_position] = double(i + 1) * m_coefficients[absolute_positionp]; + } + } + } else { + for (size_t i = 0; i < N; ++i) { + for (size_t j = 0; j < N - i; ++j) { + TinyVector<Dimension, size_t> relative_pos(i, j); + TinyVector<Dimension, size_t> relative_posp(i, j + 1); + size_t absolute_position = Q.absolute_position(relative_pos); + size_t absolute_positionp = P.absolute_position(relative_posp); + Q.coefficients()[absolute_position] = double(j + 1) * m_coefficients[absolute_positionp]; + } + } + } + } else { + static_assert(Dimension == 3); + if (var == 0) { + for (size_t i = 0; i < N; ++i) { + for (size_t j = 0; j < N - i; ++j) { + for (size_t k = 0; k < N - i - j; ++k) { + TinyVector<Dimension, size_t> relative_pos(i, j, k); + TinyVector<Dimension, size_t> relative_posp(i + 1, j, k); + size_t absolute_position = Q.absolute_position(relative_pos); + size_t absolute_positionp = P.absolute_position(relative_posp); + Q.coefficients()[absolute_position] = double(i + 1) * m_coefficients[absolute_positionp]; + } + } + } + } else if (var == 1) { + for (size_t i = 0; i < N; ++i) { + for (size_t j = 0; j < N - i; ++j) { + for (size_t k = 0; k < N - i - j; ++k) { + TinyVector<Dimension, size_t> relative_pos(i, j, k); + TinyVector<Dimension, size_t> relative_posp(i, j + 1, k); + size_t absolute_position = Q.absolute_position(relative_pos); + size_t absolute_positionp = P.absolute_position(relative_posp); + Q.coefficients()[absolute_position] = double(j + 1) * m_coefficients[absolute_positionp]; + } + } + } + } else { + for (size_t i = 0; i < N; ++i) { + for (size_t j = 0; j < N - i; ++j) { + for (size_t k = 0; k < N - i - j; ++k) { + TinyVector<Dimension, size_t> relative_pos(i, j, k); + TinyVector<Dimension, size_t> relative_posp(i, j, k + 1); + size_t absolute_position = Q.absolute_position(relative_pos); + size_t absolute_positionp = P.absolute_position(relative_posp); + Q.coefficients()[absolute_position] = double(k + 1) * m_coefficients[absolute_positionp]; + } + } + } + } + // throw NotImplementedError("Not yet Available in 3D"); + } + } + return Q; + } + + PUGS_INLINE + constexpr friend std::ostream& + operator<<(std::ostream& os, const TaylorPolynomial<N, Dimension>& P) + { + // os << "P(x) = "; + bool all_coef_zero = true; + if (N == 0) { + os << P.coefficients()[0]; + return os; + } + if constexpr (Dimension == 1) { + if (N != 1) { + if (P.coefficients()[N] != 0.) { + if (P.coefficients()[N] < 0.) { + os << "- "; + } + if (P.coefficients()[N] != 1 && P.coefficients()[N] != -1) { + os << std::abs(P.coefficients()[N]); + } + os << "(x - " << P.x0()[0] << ")" + << "^" << N; + all_coef_zero = false; + } + } + for (size_t i = N - 1; i > 1; --i) { + if (P.coefficients()[i] != 0.) { + if (P.coefficients()[i] > 0.) { + os << " + "; + } else if (P.coefficients()[i] < 0.) { + os << " - "; + } + if (P.coefficients()[i] != 1 && P.coefficients()[i] != -1) { + os << std::abs(P.coefficients()[i]); + } + os << "(x - " << P.x0()[0] << ")" + << "^" << i; + all_coef_zero = false; + } + } + if (P.coefficients()[1] != 0.) { + if (P.coefficients()[1] > 0. && N != 1) { + os << " + "; + } else if (P.coefficients()[1] < 0.) { + os << " - "; + } + if (P.coefficients()[1] != 1 && P.coefficients()[1] != -1) { + os << std::abs(P.coefficients()[1]); + } + os << "(x - " << P.x0()[0] << ")"; + all_coef_zero = false; + } + if (P.coefficients()[0] != 0. || all_coef_zero) { + if (P.coefficients()[0] > 0.) { + os << " + "; + } else if (P.coefficients()[0] < 0.) { + os << " - "; + } + os << std::abs(P.coefficients()[0]); + } + return os; + } else if constexpr (Dimension == 2) { + size_t i = 0; + size_t j = N; + TinyVector<Dimension, size_t> rel_pos(i, j); + double coef = P[rel_pos]; + if (coef != 0.) { + if (coef < 0.) { + os << " - "; + } + if (coef != 1 && coef != -1) { + os << std::abs(coef); + } + os << "(y - " << P.x0()[1] << ")" + << "^" << N; + } + size_t degree = N; + for (size_t k = size_coef - 1; k > 0; --k) { + if (j > 0) { + j--; + i++; + } else { + degree--; + j = degree; + i = 0; + } + rel_pos = TinyVector<Dimension, size_t>(i, j); + coef = P[rel_pos]; + if (coef != 0.) { + if (coef > 0.) { + os << " + "; + } else if (coef < 0.) { + os << " - "; + } + if ((coef != 1 && coef != -1) || (i == 0 && j == 0)) { + os << std::abs(coef); + } + if (i == 0 && j == 0) + continue; + if (i == 0) { + if (j != 1) { + os << "(y - " << P.x0()[1] << ")" + << "^" << j; + } else { + os << "(y - " << P.x0()[1] << ")"; + } + } else if (j == 0) { + if (i == 1) { + os << "(x - " << P.x0()[0] << ")"; + } else { + os << "(x - " << P.x0()[0] << ")" + << "^" << i; + } + } else { + if (i == 1 && j == 1) { + os << "(x - " << P.x0()[0] << ")" + << "(y - " << P.x0()[1] << ")"; + } else if (i == 1) { + os << "(x - " << P.x0()[0] << ")" + << "(y - " << P.x0()[1] << ")^" << j; + } else if (j == 1) { + os << "(x - " << P.x0()[0] << ")" + << "^" << i << "(y - " << P.x0()[1] << ")"; + } else { + os << "(x - " << P.x0()[0] << ")" + << "^" << i << "(y - " << P.x0()[1] << ")^" << j; + } + } + all_coef_zero = false; + } + } + + return os; + } else { + // size_t i = 0; + // size_t j = 0; + // size_t k = N; + // TinyVector<Dimension, size_t> rel_pos(i, j, k); + // double coef = P[rel_pos]; + // if (coef != 0.) { + // if (coef < 0.) { + // os << " - "; + // } + // if (coef != 1 && coef != -1) { + // os << std::abs(coef); + // } + // os << "z^" << N; + // } + // size_t degree = N; + // for (size_t l = size_coef - 1; l > 0; --l) { + // if (k > 0) { + // k--; + // if (j < k) { + // j++; + // } else { + // j--; + // i++; + // } + // } else { + // degree--; + // k = degree; + // i = 0; + // j = 0; + // } + + // rel_pos = TinyVector<Dimension, size_t>(i, j, k); + // double coef = P[rel_pos]; + // if (coef != 0.) { + // if (coef > 0.) { + // os << " + "; + // } else if (coef < 0.) { + // os << " - "; + // } + // if ((coef != 1 && coef != -1) || (i == 0 && j == 0 && k == 0)) { + // os << std::abs(coef); + // } + // if (i == 0 && j == 0 && k == 0) + // continue; + // if (i == 0 && j == 0) { + // if (k != 1) { + // os << "z^" << j; + // } else { + // os << "z"; + // } + // } else if (i == 0 && k == 0) { + // if (j == 1) { + // os << "y"; + // } else { + // os << "y^" << i; + // } + // } else if (j == 0 && k == 0) { + // if (i == 1) { + // os << "x"; + // } else { + // os << "x^" << i; + // } + // } else { + // if (i == 1 && j == 1 && k == 1) { + // os << "xyz"; + // } else if (i == 1) { + // os << "x" + // << "y^" << j << "z^" << k; + // } else if (j == 1) { + // os << "x^" << i << "y" + // << "z^" << k; + // } else if (k == 1) { + // os << "x^" << i << "y^" << j << "z"; + // } else { + // os << "x^" << i << "y^" << j << "z^" << k; + // } + // } + // all_coef_zero = false; + // } + // + for (size_t l = 0; l < size_coef; ++l) { + double coef = P.coefficients()[l]; + os << coef << " "; + } + return os; + // throw NotImplementedError("Not yet Available in 3D"); + } + } + + PUGS_INLINE constexpr TaylorPolynomial() noexcept = default; + ~TaylorPolynomial() = default; +}; + +template <size_t N, size_t Dimension, size_t P> +PUGS_INLINE double +integrate(const TaylorPolynomial<N, Dimension> Q, const std::array<TinyVector<Dimension>, P>& positions) +{ + double integral = 0.; + static_assert(P > 1, "For the integration, number of positions should be greater or equal to 2"); + static_assert(N >= 0, "invalid degree"); + if constexpr (Dimension == 1) { + static_assert(P == 2, "In 1D number of positions should be 2"); + throw NotImplementedError("Not yet Available in 1D"); + } else if constexpr (Dimension == 2) { + static_assert(P <= 4, "In 2D number of positions should be lesser or equal to 4"); + if constexpr (P == 2) { + const QuadratureFormula<1>& lN = QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor(N)); + const LineTransformation<2> t{positions[0], positions[1]}; + double value = 0.; + for (size_t i = 0; i < lN.numberOfPoints(); ++i) { + value += lN.weight(i) * t.velocityNorm() * Q(t(lN.point(i))); + } + integral = value; + } else if constexpr (P == 3) { + const QuadratureFormula<2>& lN = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(N)); + auto point_list = lN.pointList(); + auto weight_list = lN.weightList(); + TriangleTransformation<2> t{positions[0], positions[1], positions[2]}; + auto value = weight_list[0] * Q(t(point_list[0])); + for (size_t i = 1; i < weight_list.size(); ++i) { + value += weight_list[i] * Q(t(point_list[i])); + } + integral = t.jacobianDeterminant() * value; + } else { + const QuadratureFormula<2>& lN = + QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor(N + 1)); + auto point_list = lN.pointList(); + auto weight_list = lN.weightList(); + SquareTransformation<2> s{positions[0], positions[1], positions[2], positions[3]}; + auto value = weight_list[0] * s.jacobianDeterminant(point_list[0]) * Q(s(point_list[0])); + for (size_t i = 1; i < weight_list.size(); ++i) { + value += weight_list[i] * s.jacobianDeterminant(point_list[i]) * Q(s(point_list[i])); + } + integral = value; + } + } else { + static_assert(Dimension == 3, "Dimension should be <=3"); + throw NotImplementedError("Not yet Available in 3D"); + } + return integral; +} + +#endif // POLYNOMIALP_HPP diff --git a/src/language/algorithms/CMakeLists.txt b/src/language/algorithms/CMakeLists.txt index 9612143a50b926c5bd96a8e46f8b87d746812ae5..7466aab0d4938c298702cb61dd3e452249a8a5ac 100644 --- a/src/language/algorithms/CMakeLists.txt +++ b/src/language/algorithms/CMakeLists.txt @@ -3,3 +3,7 @@ add_library(PugsLanguageAlgorithms INTERFACE # THIS SHOULD BE REMOVED IF FILES ARE FINALY PROVIDED ) + +add_dependencies(PugsLanguageAlgorithms + PugsUtils + PugsMesh) diff --git a/src/language/algorithms/ODEDiscontinuousGalerkin1D.cpp b/src/language/algorithms/ODEDiscontinuousGalerkin1D.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9ed431f8aef454ed751fc52f6d4d84df97172c87 --- /dev/null +++ b/src/language/algorithms/ODEDiscontinuousGalerkin1D.cpp @@ -0,0 +1,98 @@ +#include <language/algorithms/ODEDiscontinuousGalerkin1D.hpp> +#include <language/utils/InterpolateArray.hpp> +#include <scheme/ODEGD1D.hpp> + +#include <fstream> + +template <size_t Dimension, size_t Degree> +ODEDiscontinuousGalerkin1D<Dimension, Degree>::ODEDiscontinuousGalerkin1D(const BasisType& basis_type, + std::shared_ptr<const IMesh> i_mesh, + const FunctionSymbolId& uex_id) +{ + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + std::shared_ptr<const MeshType> mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh); + static_assert(Dimension == 1, "Dimension have to be 1 for DiscontinuousGalerkin1D"); + using R1 = TinyVector<Dimension>; + ODEDG1D<Degree> odedg1d(basis_type, mesh); + const NodeValue<const R1> xr = mesh->xr(); + const auto& cell_to_node_matrix = mesh->connectivity().cellToNodeMatrix(); + Array<double> my_array(100 * mesh->numberOfCells() + 1); + + for (CellId j = 0; j < mesh->numberOfCells(); ++j) { + const auto& cell_nodes = cell_to_node_matrix[j]; + const NodeId r1 = cell_nodes[0]; + const NodeId r2 = cell_nodes[1]; + const TinyVector<Dimension> xr1 = xr[r1]; + const TinyVector<Dimension> xr2 = xr[r2]; + const double deltax = (xr2[0] - xr1[0]) / 100; + bool start = (j == 0); + for (size_t i = (!start); i <= 100; ++i) { + const double x = xr1[0] + deltax * i; + my_array[100 * j + i] = x; + } + } + + Array<double> ej = InterpolateArray<double(double)>::interpolate(uex_id, my_array); + // Array<TinyVector<Dimension>> ej = InterpolateArray<TinyVector<Dimension>(double)>::interpolate(uex_id, my_array); + + // InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(uex_id, mesh_data.xj()); + + CellValue<Polynomial<Degree>> U(mesh->connectivity()); + odedg1d.globalSolve(U); + PolynomialBasis<Degree> B; + TinyVector<Degree + 1> zeros; + for (size_t i = 0; i <= Degree; i++) { + zeros[i] = i; + } + B.build(basis_type, 0.5, zeros); + std::string filename = "result-basis-"; + filename += B.displayType(); + filename += "-degree-"; + filename += std::to_string(Degree); + filename += "-dof-"; + filename += std::to_string(mesh->numberOfCells()); + std::string filename2 = filename + "-polynomials"; + std::ofstream sout(filename.c_str()); + std::ofstream sout2(filename2.c_str()); + for (CellId j = 0; j < mesh->numberOfCells(); ++j) { + const auto& cell_nodes = cell_to_node_matrix[j]; + const NodeId r1 = cell_nodes[0]; + const NodeId r2 = cell_nodes[1]; + const TinyVector<Dimension> xr1 = xr[r1]; + const TinyVector<Dimension> xr2 = xr[r2]; + const double deltax = (xr2[0] - xr1[0]) / 100; + bool start = (j == 0); + double norm1error = 0.; + for (size_t i = (!start); i <= 100; ++i) { + const double x = xr1[0] + deltax * i; + sout2 << x << " " << U[j](x) << " " << ej[100 * j + i] << " " << std::abs(U[j](x) - ej[100 * j + i]) << '\n'; + norm1error += std::abs(U[j](x) - ej[100 * j + i]); + } + double x13 = (2 * xr1[0] + xr2[0]) / 3; + double x23 = (xr1[0] + 2 * xr2[0]) / 3; + + double uex = std::exp(xr1[0]) + 3 * (std::exp(x13) + std::exp(x23)) + std::exp(xr2[0]); + uex /= 8; + double ucalc = integrate(U[j], xr1[0], xr2[0]) / (xr2[0] - xr1[0]); + sout << (0.5 * (xr1[0] + xr2[0])) << " " << uex << " " << ucalc << " " << std::abs(ucalc - uex) << " " << norm1error + << '\n'; + } +} + +template ODEDiscontinuousGalerkin1D<1, 0>::ODEDiscontinuousGalerkin1D(const BasisType& basis_type, + std::shared_ptr<const IMesh>, + const FunctionSymbolId&); +template ODEDiscontinuousGalerkin1D<1, 1>::ODEDiscontinuousGalerkin1D(const BasisType& basis_type, + std::shared_ptr<const IMesh>, + const FunctionSymbolId&); +template ODEDiscontinuousGalerkin1D<1, 2>::ODEDiscontinuousGalerkin1D(const BasisType& basis_type, + std::shared_ptr<const IMesh>, + const FunctionSymbolId&); +template ODEDiscontinuousGalerkin1D<1, 3>::ODEDiscontinuousGalerkin1D(const BasisType& basis_type, + std::shared_ptr<const IMesh>, + const FunctionSymbolId&); +template ODEDiscontinuousGalerkin1D<1, 4>::ODEDiscontinuousGalerkin1D(const BasisType& basis_type, + std::shared_ptr<const IMesh>, + const FunctionSymbolId&); diff --git a/src/language/algorithms/ODEDiscontinuousGalerkin1D.hpp b/src/language/algorithms/ODEDiscontinuousGalerkin1D.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f7425d8d9586b6db1d8a823706c5356e9c645384 --- /dev/null +++ b/src/language/algorithms/ODEDiscontinuousGalerkin1D.hpp @@ -0,0 +1,16 @@ +#ifndef ORDINARY_DIFFERENTIAL_EQUATION_DISCONTINUOUS_GALERKIN_1D_HPP +#define ORDINARY_DIFFERENTIAL_EQUATION_DISCONTINUOUS_GALERKIN_1D_HPP + +#include <analysis/PolynomialBasis.hpp> +#include <language/utils/FunctionSymbolId.hpp> +#include <mesh/IMesh.hpp> + +template <size_t Dimension, size_t Degree> +struct ODEDiscontinuousGalerkin1D +{ + ODEDiscontinuousGalerkin1D(const BasisType& basis_type, + std::shared_ptr<const IMesh> i_mesh, + const FunctionSymbolId& uex_id); +}; + +#endif // ORDINARY_DIFFERENTIAL_EQUATION_DISCONTINUOUS_GALERKIN_1D_HPP diff --git a/src/language/modules/SchemeModule.cpp b/src/language/modules/SchemeModule.cpp index b9b789cfa8e3d7bc5328062c6085217dbe142120..c383baffaa7b3df583e09ed0c4965cb783a8ccde 100644 --- a/src/language/modules/SchemeModule.cpp +++ b/src/language/modules/SchemeModule.cpp @@ -42,6 +42,8 @@ #include <scheme/FourierBoundaryConditionDescriptor.hpp> #include <scheme/FreeBoundaryConditionDescriptor.hpp> #include <scheme/HyperelasticSolver.hpp> +#include <scheme/HyperplasticSolver.hpp> +#include <scheme/HyperplasticSolverO2.hpp> #include <scheme/IBoundaryConditionDescriptor.hpp> #include <scheme/IDiscreteFunctionDescriptor.hpp> #include <scheme/InflowBoundaryConditionDescriptor.hpp> @@ -76,6 +78,8 @@ SchemeModule::SchemeModule() this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const IBoundaryConditionDescriptor>>); this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const VariableBCDescriptor>>); + // this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const BasisType>>); + this->_addBuiltinFunction("P0", std::function( []() -> std::shared_ptr<const IDiscreteFunctionDescriptor> { @@ -654,6 +658,753 @@ SchemeModule::SchemeModule() )); + this->_addBuiltinFunction("hyperplastic_eucclhyd_fluxes", + std::function( + + [](const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& aL, + const std::shared_ptr<const DiscreteFunctionVariant>& aT, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list) + -> std::tuple<std::shared_ptr<const ItemValueVariant>, + std::shared_ptr<const SubItemValuePerItemVariant>> { + return HyperplasticSolverHandler{getCommonMesh({rho, aL, aT, u, sigma})} + .solver() + .compute_fluxes(HyperplasticSolverHandler::SolverType::Eucclhyd, rho, aL, aT, u, + sigma, bc_descriptor_list); + } + + )); + + this->_addBuiltinFunction("hyperplastic_eucclhyd_solver_implicit", + std::function( + + [](const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, + const std::shared_ptr<const DiscreteFunctionVariant>& yield, + const std::shared_ptr<const DiscreteFunctionVariant>& aL, + const std::shared_ptr<const DiscreteFunctionVariant>& aT, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list, + const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> { + return HyperplasticSolverHandler{ + getCommonMesh({rho, u, E, Fe, zeta, yield, aL, aT, sigma})} + .solver() + .apply(HyperplasticSolverHandler::SolverType::Eucclhyd, + HyperplasticSolverHandler::RelaxationType::Implicit, dt, rho, u, E, Fe, zeta, + yield, aL, aT, sigma, bc_descriptor_list); + } + + )); + + this->_addBuiltinFunction("hyperplastic_eucclhyd_solver_exponential", + std::function( + + [](const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, + const std::shared_ptr<const DiscreteFunctionVariant>& yield, + const std::shared_ptr<const DiscreteFunctionVariant>& aL, + const std::shared_ptr<const DiscreteFunctionVariant>& aT, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list, + const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> { + return HyperplasticSolverHandler{ + getCommonMesh({rho, u, E, Fe, zeta, yield, aL, aT, sigma})} + .solver() + .apply(HyperplasticSolverHandler::SolverType::Eucclhyd, + HyperplasticSolverHandler::RelaxationType::Exponential, dt, rho, u, E, Fe, + zeta, yield, aL, aT, sigma, bc_descriptor_list); + } + + )); + + this->_addBuiltinFunction("hyperplastic_eucclhyd_solver_cauchygreen", + std::function( + + [](const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, + const std::shared_ptr<const DiscreteFunctionVariant>& yield, + const std::shared_ptr<const DiscreteFunctionVariant>& aL, + const std::shared_ptr<const DiscreteFunctionVariant>& aT, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list, + const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> { + return HyperplasticSolverHandler{ + getCommonMesh({rho, u, E, Fe, zeta, yield, aL, aT, sigma})} + .solver() + .apply(HyperplasticSolverHandler::SolverType::Eucclhyd, + HyperplasticSolverHandler::RelaxationType::CauchyGreen, dt, rho, u, E, Fe, + zeta, yield, aL, aT, sigma, bc_descriptor_list); + } + + )); + + this->_addBuiltinFunction("hyperplastic_plastic_step_implicit", + std::function([](const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, + const std::shared_ptr<const DiscreteFunctionVariant>& yield, + const std::shared_ptr<const DiscreteFunctionVariant>& sigman, + const std::shared_ptr<const DiscreteFunctionVariant>& sigmanp1, + const double& dt) -> std::shared_ptr<const DiscreteFunctionVariant> { + return HyperplasticSolverHandler{getCommonMesh({Fe, zeta, yield, sigman, sigmanp1})} + .solver() + .apply_plastic_relaxation(HyperplasticSolverHandler::RelaxationType::Implicit, dt, Fe, + zeta, yield, sigman, sigmanp1); + } + + )); + + this->_addBuiltinFunction("hyperplastic_plastic_step_exponential", + std::function([](const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, + const std::shared_ptr<const DiscreteFunctionVariant>& yield, + const std::shared_ptr<const DiscreteFunctionVariant>& sigman, + const std::shared_ptr<const DiscreteFunctionVariant>& sigmanp1, + const double& dt) -> std::shared_ptr<const DiscreteFunctionVariant> { + return HyperplasticSolverHandler{getCommonMesh({Fe, zeta, yield, sigman, sigmanp1})} + .solver() + .apply_plastic_relaxation(HyperplasticSolverHandler::RelaxationType::Exponential, dt, + Fe, zeta, yield, sigman, sigmanp1); + } + + )); + + this->_addBuiltinFunction("hyperplastic_elastic_step_eucclhyd", + std::function( + + [](const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& aL, + const std::shared_ptr<const DiscreteFunctionVariant>& aT, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list, + const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> { + return HyperplasticSolverHandler{getCommonMesh({rho, u, E, Fe, aL, aT, sigma})} + .solver() + .apply_elastic(HyperplasticSolverHandler::SolverType::Eucclhyd, dt, rho, u, E, Fe, aL, + aT, sigma, bc_descriptor_list); + } + + )); + + this->_addBuiltinFunction("hyperplastic_glace_fluxes", + std::function( + + [](const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& aL, + const std::shared_ptr<const DiscreteFunctionVariant>& aT, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list) + -> std::tuple<std::shared_ptr<const ItemValueVariant>, + std::shared_ptr<const SubItemValuePerItemVariant>> { + return HyperplasticSolverHandler{getCommonMesh({rho, aL, aT, u, sigma})} + .solver() + .compute_fluxes(HyperplasticSolverHandler::SolverType::Glace, // + rho, aL, aT, u, sigma, bc_descriptor_list); + } + + )); + + this->_addBuiltinFunction("hyperplastic_elastic_step_glace", + std::function( + + [](const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& aL, + const std::shared_ptr<const DiscreteFunctionVariant>& aT, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list, + const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> { + return HyperplasticSolverHandler{getCommonMesh({rho, u, E, Fe, aL, aT, sigma})} + .solver() + .apply_elastic(HyperplasticSolverHandler::SolverType::Glace, dt, rho, u, E, Fe, aL, + aT, sigma, bc_descriptor_list); + } + + )); + + this->_addBuiltinFunction("hyperplastic_glace_solver_implicit", + std::function( + + [](const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, + const std::shared_ptr<const DiscreteFunctionVariant>& yield, + const std::shared_ptr<const DiscreteFunctionVariant>& aL, + const std::shared_ptr<const DiscreteFunctionVariant>& aT, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list, + const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> { + return HyperplasticSolverHandler{ + getCommonMesh({rho, u, E, Fe, zeta, yield, aL, aT, sigma})} + .solver() + .apply(HyperplasticSolverHandler::SolverType::Glace, + HyperplasticSolverHandler::RelaxationType::Implicit, dt, rho, u, E, Fe, zeta, + yield, aL, aT, sigma, bc_descriptor_list); + } + + )); + + this->_addBuiltinFunction("hyperplastic_glace_solver_exponential", + std::function( + + [](const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, + const std::shared_ptr<const DiscreteFunctionVariant>& yield, + const std::shared_ptr<const DiscreteFunctionVariant>& aL, + const std::shared_ptr<const DiscreteFunctionVariant>& aT, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list, + const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> { + return HyperplasticSolverHandler{ + getCommonMesh({rho, u, E, Fe, zeta, yield, aL, aT, sigma})} + .solver() + .apply(HyperplasticSolverHandler::SolverType::Glace, + HyperplasticSolverHandler::RelaxationType::Exponential, dt, rho, u, E, Fe, + zeta, yield, aL, aT, sigma, bc_descriptor_list); + } + + )); + + this->_addBuiltinFunction("hyperplastic_glace_solver_cauchygreen", + std::function( + + [](const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, + const std::shared_ptr<const DiscreteFunctionVariant>& yield, + const std::shared_ptr<const DiscreteFunctionVariant>& aL, + const std::shared_ptr<const DiscreteFunctionVariant>& aT, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list, + const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> { + return HyperplasticSolverHandler{ + getCommonMesh({rho, u, E, Fe, zeta, yield, aL, aT, sigma})} + .solver() + .apply(HyperplasticSolverHandler::SolverType::Glace, + HyperplasticSolverHandler::RelaxationType::CauchyGreen, dt, rho, u, E, Fe, + zeta, yield, aL, aT, sigma, bc_descriptor_list); + } + + )); + + this->_addBuiltinFunction("apply_hyperplastic_fluxes_implicit", + std::function( + + [](const std::shared_ptr<const DiscreteFunctionVariant>& rho, // + const std::shared_ptr<const DiscreteFunctionVariant>& u, // + const std::shared_ptr<const DiscreteFunctionVariant>& E, // + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, // + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, // + const std::shared_ptr<const DiscreteFunctionVariant>& yield, // + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, // + const std::shared_ptr<const ItemValueVariant>& ur, // + const std::shared_ptr<const SubItemValuePerItemVariant>& Fjr, // + const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> { + return HyperplasticSolverHandler{getCommonMesh({rho, u, E, Fe})} // + .solver() + .apply_fluxes(dt, rho, u, E, Fe, zeta, yield, sigma, ur, Fjr, + HyperplasticSolverHandler::RelaxationType::Implicit); + } + + )); + + this->_addBuiltinFunction("apply_hyperplastic_fluxes_cauchygreen", + std::function( + + [](const std::shared_ptr<const DiscreteFunctionVariant>& rho, // + const std::shared_ptr<const DiscreteFunctionVariant>& u, // + const std::shared_ptr<const DiscreteFunctionVariant>& E, // + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, // + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, // + const std::shared_ptr<const DiscreteFunctionVariant>& yield, // + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, // + const std::shared_ptr<const ItemValueVariant>& ur, // + const std::shared_ptr<const SubItemValuePerItemVariant>& Fjr, // + const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> { + return HyperplasticSolverHandler{getCommonMesh({rho, u, E, Fe})} // + .solver() + .apply_fluxes(dt, rho, u, E, Fe, zeta, yield, sigma, ur, Fjr, + HyperplasticSolverHandler::RelaxationType::CauchyGreen); + } + + )); + + this->_addBuiltinFunction("apply_hyperplastic_fluxes_exponential", + std::function( + + [](const std::shared_ptr<const DiscreteFunctionVariant>& rho, // + const std::shared_ptr<const DiscreteFunctionVariant>& u, // + const std::shared_ptr<const DiscreteFunctionVariant>& E, // + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, // + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, // + const std::shared_ptr<const DiscreteFunctionVariant>& yield, // + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, // + const std::shared_ptr<const ItemValueVariant>& ur, // + const std::shared_ptr<const SubItemValuePerItemVariant>& Fjr, // + const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> { + return HyperplasticSolverHandler{getCommonMesh({rho, u, E, Fe})} // + .solver() + .apply_fluxes(dt, rho, u, E, Fe, zeta, yield, sigma, ur, Fjr, + HyperplasticSolverHandler::RelaxationType::Exponential); + } + + )); + + this->_addBuiltinFunction("hyperplastic_eucclhyd_fluxes_o2", + std::function( + + [](const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& aL, + const std::shared_ptr<const DiscreteFunctionVariant>& aT, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list) + -> std::tuple<std::shared_ptr<const ItemValueVariant>, + std::shared_ptr<const SubItemValuePerItemVariant>> { + return HyperplasticSolverO2Handler{getCommonMesh({rho, aL, aT, u, sigma})} + .solver() + .compute_fluxes(HyperplasticSolverO2Handler::SolverType::Eucclhyd, rho, aL, aT, u, + sigma, bc_descriptor_list); + } + + )); + + this->_addBuiltinFunction("hyperplastic_eucclhyd_solver_implicit_o2", + std::function( + + [](const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, + const std::shared_ptr<const DiscreteFunctionVariant>& yield, + const std::shared_ptr<const DiscreteFunctionVariant>& aL, + const std::shared_ptr<const DiscreteFunctionVariant>& aT, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list, + const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> { + return HyperplasticSolverO2Handler{ + getCommonMesh({rho, u, E, Fe, zeta, yield, aL, aT, sigma})} + .solver() + .apply(HyperplasticSolverO2Handler::SolverType::Eucclhyd, + HyperplasticSolverO2Handler::RelaxationType::Implicit, dt, rho, u, E, Fe, zeta, + yield, aL, aT, sigma, bc_descriptor_list); + } + + )); + + this->_addBuiltinFunction("hyperplastic_eucclhyd_solver_exponential_o2", + std::function( + + [](const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, + const std::shared_ptr<const DiscreteFunctionVariant>& yield, + const std::shared_ptr<const DiscreteFunctionVariant>& aL, + const std::shared_ptr<const DiscreteFunctionVariant>& aT, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list, + const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> { + return HyperplasticSolverO2Handler{ + getCommonMesh({rho, u, E, Fe, zeta, yield, aL, aT, sigma})} + .solver() + .apply(HyperplasticSolverO2Handler::SolverType::Eucclhyd, + HyperplasticSolverO2Handler::RelaxationType::Exponential, dt, rho, u, E, Fe, + zeta, yield, aL, aT, sigma, bc_descriptor_list); + } + + )); + + this->_addBuiltinFunction("hyperplastic_eucclhyd_solver_cauchygreen_o2", + std::function( + + [](const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, + const std::shared_ptr<const DiscreteFunctionVariant>& yield, + const std::shared_ptr<const DiscreteFunctionVariant>& aL, + const std::shared_ptr<const DiscreteFunctionVariant>& aT, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list, + const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> { + return HyperplasticSolverO2Handler{ + getCommonMesh({rho, u, E, Fe, zeta, yield, aL, aT, sigma})} + .solver() + .apply(HyperplasticSolverO2Handler::SolverType::Eucclhyd, + HyperplasticSolverO2Handler::RelaxationType::CauchyGreen, dt, rho, u, E, Fe, + zeta, yield, aL, aT, sigma, bc_descriptor_list); + } + + )); + + this->_addBuiltinFunction("hyperplastic_plastic_step_implicit_o2", + std::function([](const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, + const std::shared_ptr<const DiscreteFunctionVariant>& yield, + const std::shared_ptr<const DiscreteFunctionVariant>& sigman, + const std::shared_ptr<const DiscreteFunctionVariant>& sigmanp1, + const double& dt) -> std::shared_ptr<const DiscreteFunctionVariant> { + return HyperplasticSolverO2Handler{getCommonMesh({Fe, zeta, yield, sigman, sigmanp1})} + .solver() + .apply_plastic_relaxation(HyperplasticSolverO2Handler::RelaxationType::Implicit, dt, Fe, + zeta, yield, sigman, sigmanp1); + } + + )); + + this->_addBuiltinFunction("hyperplastic_plastic_step_exponential_o2", + std::function([](const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, + const std::shared_ptr<const DiscreteFunctionVariant>& yield, + const std::shared_ptr<const DiscreteFunctionVariant>& sigman, + const std::shared_ptr<const DiscreteFunctionVariant>& sigmanp1, + const double& dt) -> std::shared_ptr<const DiscreteFunctionVariant> { + return HyperplasticSolverO2Handler{getCommonMesh({Fe, zeta, yield, sigman, sigmanp1})} + .solver() + .apply_plastic_relaxation(HyperplasticSolverO2Handler::RelaxationType::Exponential, dt, + Fe, zeta, yield, sigman, sigmanp1); + } + + )); + + this->_addBuiltinFunction("hyperplastic_elastic_step_eucclhyd_o2", + std::function( + + [](const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& aL, + const std::shared_ptr<const DiscreteFunctionVariant>& aT, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list, + const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> { + return HyperplasticSolverO2Handler{getCommonMesh({rho, u, E, Fe, aL, aT, sigma})} + .solver() + .apply_elastic(HyperplasticSolverO2Handler::SolverType::Eucclhyd, dt, rho, u, E, Fe, + aL, aT, sigma, bc_descriptor_list); + } + + )); + + this->_addBuiltinFunction("hyperplastic_glace_fluxes_o2", + std::function( + + [](const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& aL, + const std::shared_ptr<const DiscreteFunctionVariant>& aT, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list) + -> std::tuple<std::shared_ptr<const ItemValueVariant>, + std::shared_ptr<const SubItemValuePerItemVariant>> { + return HyperplasticSolverO2Handler{getCommonMesh({rho, aL, aT, u, sigma})} + .solver() + .compute_fluxes(HyperplasticSolverO2Handler::SolverType::Glace, // + rho, aL, aT, u, sigma, bc_descriptor_list); + } + + )); + + this->_addBuiltinFunction("hyperplastic_elastic_step_glace_o2", + std::function( + + [](const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& aL, + const std::shared_ptr<const DiscreteFunctionVariant>& aT, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list, + const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> { + return HyperplasticSolverO2Handler{getCommonMesh({rho, u, E, Fe, aL, aT, sigma})} + .solver() + .apply_elastic(HyperplasticSolverO2Handler::SolverType::Glace, dt, rho, u, E, Fe, aL, + aT, sigma, bc_descriptor_list); + } + + )); + + this->_addBuiltinFunction("hyperplastic_glace_solver_implicit_o2", + std::function( + + [](const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, + const std::shared_ptr<const DiscreteFunctionVariant>& yield, + const std::shared_ptr<const DiscreteFunctionVariant>& aL, + const std::shared_ptr<const DiscreteFunctionVariant>& aT, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list, + const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> { + return HyperplasticSolverO2Handler{ + getCommonMesh({rho, u, E, Fe, zeta, yield, aL, aT, sigma})} + .solver() + .apply(HyperplasticSolverO2Handler::SolverType::Glace, + HyperplasticSolverO2Handler::RelaxationType::Implicit, dt, rho, u, E, Fe, zeta, + yield, aL, aT, sigma, bc_descriptor_list); + } + + )); + + this->_addBuiltinFunction("hyperplastic_glace_solver_exponential_o2", + std::function( + + [](const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, + const std::shared_ptr<const DiscreteFunctionVariant>& yield, + const std::shared_ptr<const DiscreteFunctionVariant>& aL, + const std::shared_ptr<const DiscreteFunctionVariant>& aT, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list, + const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> { + return HyperplasticSolverO2Handler{ + getCommonMesh({rho, u, E, Fe, zeta, yield, aL, aT, sigma})} + .solver() + .apply(HyperplasticSolverO2Handler::SolverType::Glace, + HyperplasticSolverO2Handler::RelaxationType::Exponential, dt, rho, u, E, Fe, + zeta, yield, aL, aT, sigma, bc_descriptor_list); + } + + )); + + this->_addBuiltinFunction("hyperplastic_glace_solver_cauchygreen_o2", + std::function( + + [](const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, + const std::shared_ptr<const DiscreteFunctionVariant>& yield, + const std::shared_ptr<const DiscreteFunctionVariant>& aL, + const std::shared_ptr<const DiscreteFunctionVariant>& aT, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list, + const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> { + return HyperplasticSolverO2Handler{ + getCommonMesh({rho, u, E, Fe, zeta, yield, aL, aT, sigma})} + .solver() + .apply(HyperplasticSolverO2Handler::SolverType::Glace, + HyperplasticSolverO2Handler::RelaxationType::CauchyGreen, dt, rho, u, E, Fe, + zeta, yield, aL, aT, sigma, bc_descriptor_list); + } + + )); + + this->_addBuiltinFunction("apply_hyperplastic_fluxes_implicit_o2", + std::function( + + [](const std::shared_ptr<const DiscreteFunctionVariant>& rho, // + const std::shared_ptr<const DiscreteFunctionVariant>& u, // + const std::shared_ptr<const DiscreteFunctionVariant>& E, // + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, // + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, // + const std::shared_ptr<const DiscreteFunctionVariant>& yield, // + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, // + const std::shared_ptr<const ItemValueVariant>& ur, // + const std::shared_ptr<const SubItemValuePerItemVariant>& Fjr, // + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list, // + const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> { + return HyperplasticSolverO2Handler{getCommonMesh({rho, u, E, Fe})} // + .solver() + .apply_fluxes(dt, rho, u, E, Fe, zeta, yield, sigma, ur, Fjr, + HyperplasticSolverO2Handler::RelaxationType::Implicit, + bc_descriptor_list); + } + + )); + + this->_addBuiltinFunction("apply_hyperplastic_fluxes_cauchygreen_o2", + std::function( + + [](const std::shared_ptr<const DiscreteFunctionVariant>& rho, // + const std::shared_ptr<const DiscreteFunctionVariant>& u, // + const std::shared_ptr<const DiscreteFunctionVariant>& E, // + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, // + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, // + const std::shared_ptr<const DiscreteFunctionVariant>& yield, // + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, // + const std::shared_ptr<const ItemValueVariant>& ur, // + const std::shared_ptr<const SubItemValuePerItemVariant>& Fjr, // + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list, // + const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> { + return HyperplasticSolverO2Handler{getCommonMesh({rho, u, E, Fe})} // + .solver() + .apply_fluxes(dt, rho, u, E, Fe, zeta, yield, sigma, ur, Fjr, + HyperplasticSolverO2Handler::RelaxationType::CauchyGreen, + bc_descriptor_list); + } + + )); + + this->_addBuiltinFunction("apply_hyperplastic_fluxes_exponential_o2", + std::function( + + [](const std::shared_ptr<const DiscreteFunctionVariant>& rho, // + const std::shared_ptr<const DiscreteFunctionVariant>& u, // + const std::shared_ptr<const DiscreteFunctionVariant>& E, // + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, // + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, // + const std::shared_ptr<const DiscreteFunctionVariant>& yield, // + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, // + const std::shared_ptr<const ItemValueVariant>& ur, // + const std::shared_ptr<const SubItemValuePerItemVariant>& Fjr, // + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list, // + const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> { + return HyperplasticSolverO2Handler{getCommonMesh({rho, u, E, Fe})} // + .solver() + .apply_fluxes(dt, rho, u, E, Fe, zeta, yield, sigma, ur, Fjr, + HyperplasticSolverO2Handler::RelaxationType::Exponential, + bc_descriptor_list); + } + + )); + this->_addBuiltinFunction("lagrangian", std::function( @@ -721,6 +1472,14 @@ SchemeModule::SchemeModule() )); + this->_addBuiltinFunction("hyperplastic_dt", std::function( + + [](const std::shared_ptr<const DiscreteFunctionVariant>& c) -> double { + return hyperplastic_dt(c); + } + + )); + this->_addBuiltinFunction("fluxing_advection", std::function( [](const std::shared_ptr<const MeshVariant> new_mesh, diff --git a/src/language/modules/SchemeModule.hpp b/src/language/modules/SchemeModule.hpp index cf169175c4b46f7364799e16bdb4b84633d1f61c..6cd0b03427463debf965b9f5662fd21b33f502e3 100644 --- a/src/language/modules/SchemeModule.hpp +++ b/src/language/modules/SchemeModule.hpp @@ -1,6 +1,7 @@ #ifndef SCHEME_MODULE_HPP #define SCHEME_MODULE_HPP +#include <analysis/PolynomialBasis.hpp> #include <language/modules/BuiltinModule.hpp> class SchemeModule : public BuiltinModule diff --git a/src/language/utils/InterpolateArray.hpp b/src/language/utils/InterpolateArray.hpp new file mode 100644 index 0000000000000000000000000000000000000000..be5c114f51ffe31da3cf26df690924bd7ef04668 --- /dev/null +++ b/src/language/utils/InterpolateArray.hpp @@ -0,0 +1,44 @@ +#ifndef INTERPOLATE_ARRAY_HPP +#define INTERPOLATE_ARRAY_HPP + +#include <language/utils/PugsFunctionAdapter.hpp> + +template <typename T> +class InterpolateArray; +template <typename OutputType, typename InputType> +class InterpolateArray<OutputType(InputType)> : public PugsFunctionAdapter<OutputType(InputType)> +{ + static constexpr size_t Dimension = OutputType::Dimension; + using Adapter = PugsFunctionAdapter<OutputType(InputType)>; + + public: + static inline Array<OutputType> + interpolate(const FunctionSymbolId& function_symbol_id, const Array<const InputType>& position) + { + auto& expression = Adapter::getFunctionExpression(function_symbol_id); + auto convert_result = Adapter::getResultConverter(expression.m_data_type); + + Array<ExecutionPolicy> context_list = Adapter::getContextList(expression); + + using execution_space = typename Kokkos::DefaultExecutionSpace::execution_space; + Kokkos::Experimental::UniqueToken<execution_space, Kokkos::Experimental::UniqueTokenScope::Global> tokens; + + Array<OutputType> value(position.size()); + + parallel_for(position.size(), [=, &expression, &tokens](size_t i) { + const int32_t t = tokens.acquire(); + + auto& execution_policy = context_list[t]; + + Adapter::convertArgs(execution_policy.currentContext(), position[i]); + auto result = expression.execute(execution_policy); + value[i] = convert_result(std::move(result)); + + tokens.release(t); + }); + + return value; + } +}; + +#endif /* INTERPOLATE_ARRAY_HPP */ diff --git a/src/scheme/CMakeLists.txt b/src/scheme/CMakeLists.txt index 692a36598aceda4189be3d4701699933e63caee8..6e49cfd695124e7377cd618117ea1dbe9179a5de 100644 --- a/src/scheme/CMakeLists.txt +++ b/src/scheme/CMakeLists.txt @@ -3,6 +3,9 @@ add_library( PugsScheme AcousticSolver.cpp + HyperelasticSolver.cpp + HyperplasticSolver.cpp + HyperplasticSolverO2.cpp DiscreteFunctionIntegrator.cpp DiscreteFunctionInterpoler.cpp DiscreteFunctionUtils.cpp diff --git a/src/scheme/DiscontinuousGalerkin1DTools.hpp b/src/scheme/DiscontinuousGalerkin1DTools.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d28a0337053b0804f3a70d63915edc45accc56c3 --- /dev/null +++ b/src/scheme/DiscontinuousGalerkin1DTools.hpp @@ -0,0 +1,77 @@ +#ifndef DISCONTINUOUS_GALERKIN_1D_TOOLS +#define DISCONTINUOUS_GALERKIN_1D_TOOLS + +#include <rang.hpp> + +#include <utils/ArrayUtils.hpp> + +#include <utils/PugsAssert.hpp> + +#include <mesh/Mesh.hpp> +#include <mesh/MeshData.hpp> +#include <mesh/MeshDataManager.hpp> +#include <mesh/MeshNodeBoundary.hpp> + +#include <algebra/TinyMatrix.hpp> +#include <algebra/TinyVector.hpp> + +#include <analysis/Polynomial.hpp> +#include <analysis/PolynomialBasis.hpp> + +#include <mesh/ItemValueUtils.hpp> +#include <mesh/SubItemValuePerItem.hpp> + +#include <utils/Exceptions.hpp> +#include <utils/Messenger.hpp> + +#include <iostream> + +template <size_t Degree> +class DiscontinuousGalerkin1DTools +{ + constexpr static size_t Dimension = 1; + + using Rd = TinyVector<Degree>; + using Rdd = TinyMatrix<Degree>; + + using R1 = TinyVector<Dimension>; + + public: + int + inverse() const + { + return 0; + } + + void + elementarySolve(Polynomial<Degree>& U, + const TinyVector<Degree + 1> moments, + const PolynomialBasis<Degree> Basis, + const double& xinf, + const double& xsup) const + { + TinyMatrix<Degree + 1> M(zero); + for (size_t i = 0; i <= Degree; ++i) { + for (size_t j = 0; j <= Degree; ++j) { + Polynomial<2 * Degree> P = Basis.elements()[i] * Basis.elements()[j]; + M(i, j) = integrate(P, xinf, xsup); + } + } + TinyMatrix inv_M = ::inverse(M); + U.coefficients() = inv_M * moments; + } + + // void + // integrate() const + // {} + + public: + // TODO add a flux manager to constructor + // TODO add a basis to constructor + DiscontinuousGalerkin1DTools() + { + ; + } +}; + +#endif // DISCONTINUOUS_GALERKIN_1D_TOOLS diff --git a/src/scheme/HyperelasticSolver.cpp b/src/scheme/HyperelasticSolver.cpp index 7bc6cd8c0dc854ecaa8caa2d8809891ed328790d..8354de88b5e8f0f7233a2ea0d150f44bc5b73e75 100644 --- a/src/scheme/HyperelasticSolver.cpp +++ b/src/scheme/HyperelasticSolver.cpp @@ -541,6 +541,7 @@ class HyperelasticSolverHandler::HyperelasticSolver final : public HyperelasticS const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const { auto [ur, Fjr] = compute_fluxes(solver_type, rho, aL, aT, u, sigma, bc_descriptor_list); + // return apply_fluxes(dt, rho, u, E, CG, ur, Fjr); return apply_fluxes(dt, rho, u, E, CG, ur, Fjr); } diff --git a/src/scheme/HyperplasticSolver.cpp b/src/scheme/HyperplasticSolver.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d0c45c2239a34ce4ba8751e4f40cec69b9a62919 --- /dev/null +++ b/src/scheme/HyperplasticSolver.cpp @@ -0,0 +1,1534 @@ +#include <algebra/CRSMatrix.hpp> +#include <algebra/CRSMatrixDescriptor.hpp> +#include <algebra/EigenvalueSolver.hpp> +#include <algebra/SmallMatrix.hpp> +#include <analysis/Polynomial.hpp> +#include <scheme/HyperplasticSolver.hpp> + +#include <language/utils/InterpolateItemValue.hpp> +#include <mesh/ItemValueUtils.hpp> +#include <mesh/ItemValueVariant.hpp> +#include <mesh/MeshFaceBoundary.hpp> +#include <mesh/MeshFlatNodeBoundary.hpp> +#include <mesh/MeshNodeBoundary.hpp> +#include <mesh/MeshTraits.hpp> +#include <mesh/SubItemValuePerItemVariant.hpp> +#include <scheme/DirichletBoundaryConditionDescriptor.hpp> +#include <scheme/DiscreteFunctionP0.hpp> +#include <scheme/DiscreteFunctionUtils.hpp> +#include <scheme/DiscreteFunctionVariant.hpp> +#include <scheme/ExternalBoundaryConditionDescriptor.hpp> +#include <scheme/FixedBoundaryConditionDescriptor.hpp> +#include <scheme/IBoundaryConditionDescriptor.hpp> +#include <scheme/IDiscreteFunctionDescriptor.hpp> +#include <scheme/SymmetryBoundaryConditionDescriptor.hpp> +#include <utils/Socket.hpp> +#include <variant> +#include <vector> + +double +hyperplastic_dt(const std::shared_ptr<const DiscreteFunctionVariant>& c_v) +{ + const auto& c = c_v->get<DiscreteFunctionP0<const double>>(); + + return std::visit( + [&](auto&& p_mesh) -> double { + const auto& mesh = *p_mesh; + + using MeshType = decltype(mesh); + if constexpr (is_polygonal_mesh_v<MeshType>) { + const auto Vj = MeshDataManager::instance().getMeshData(mesh).Vj(); + const auto Sj = MeshDataManager::instance().getMeshData(mesh).sumOverRLjr(); + + CellValue<double> local_dt{mesh.connectivity()}; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { local_dt[j] = 2 * Vj[j] / (Sj[j] * c[j]); }); + + return min(local_dt); + } else { + throw NormalError("unexpected mesh type"); + } + }, + c.meshVariant()->variant()); +} + +template <MeshConcept MeshType> +class HyperplasticSolverHandler::HyperplasticSolver final : public HyperplasticSolverHandler::IHyperplasticSolver +{ + private: + static constexpr size_t Dimension = MeshType::Dimension; + using Rdxd = TinyMatrix<Dimension>; + using R3x3 = TinyMatrix<3>; + using Rd = TinyVector<Dimension>; + using R3 = TinyVector<3>; + + using MeshDataType = MeshData<MeshType>; + + using DiscreteScalarFunction = DiscreteFunctionP0<const double>; + using DiscreteVectorFunction = DiscreteFunctionP0<const Rd>; + using DiscreteTensorFunction = DiscreteFunctionP0<const Rdxd>; + using DiscreteTensorFunction3d = DiscreteFunctionP0<const R3x3>; + + class FixedBoundaryCondition; + class PressureBoundaryCondition; + class NormalStressBoundaryCondition; + class SymmetryBoundaryCondition; + class VelocityBoundaryCondition; + + using BoundaryCondition = std::variant<FixedBoundaryCondition, + PressureBoundaryCondition, + NormalStressBoundaryCondition, + SymmetryBoundaryCondition, + VelocityBoundaryCondition>; + + using BoundaryConditionList = std::vector<BoundaryCondition>; + R3x3 + to3D(const Rdxd tensor) const + { + R3x3 tensor3d = zero; + for (size_t i = 0; i < Dimension; i++) { + for (size_t j = 0; j < Dimension; j++) { + tensor3d(i, j) = tensor(i, j); + } + } + return tensor3d; + } + + R3 + to3D(const Rd vector) const + { + R3 vector3d = zero; + for (size_t i = 0; i < Dimension; i++) { + vector3d[i] = vector[i]; + } + return vector3d; + } + + CellValue<const R3x3> + to3D(const MeshType& mesh, const CellValue<Rdxd>& tensor) const + { + CellValue<R3x3> tensor3d{mesh.connectivity()}; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { tensor3d[j] = to3D(tensor[j]); }); + return tensor3d; + } + + CellValue<const R3> + to3D(const MeshType& mesh, const CellValue<Rd> vector) const + { + CellValue<R3> vector3d{mesh.connectivity()}; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { vector3d[j] = to3D(vector[j]); }); + return vector3d; + } + + Rdxd + toDimension(const R3x3 tensor3d) const + { + Rdxd tensor = zero; + for (size_t i = 0; i < Dimension; i++) { + for (size_t j = 0; j < Dimension; j++) { + tensor(i, j) = tensor3d(i, j); + } + } + return tensor; + } + + Rd + toDimension(const R3 vector3d) const + { + Rd vector = zero; + for (size_t i = 0; i < Dimension; i++) { + vector[i] = vector3d[i]; + } + return vector; + } + + CellValue<const Rdxd> + toDimension(const MeshType& mesh, const CellValue<R3x3>& tensor3d) const + { + CellValue<Rdxd> tensor{mesh.connectivity()}; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { tensor[j] = toDimension(tensor3d[j]); }); + return tensor; + } + + CellValue<const Rd> + toDimension(const MeshType& mesh, const CellValue<R3>& vector3d) const + { + CellValue<Rd> vector{mesh.connectivity()}; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { vector[j] = to3D(vector3d[j]); }); + return vector; + } + + double + _compute_chi(const R3x3 S, const double yield) const + { + const R3x3 Id = identity; + const R3x3 Sd = S - 1. / 3 * trace(S) * Id; + double chi = 0; + const double normS2 = frobeniusNorm(Sd) * frobeniusNorm(Sd); + double limit2 = 2. / 3 * yield * yield; + const double power = 0.5; + if ((normS2 - limit2) > 0) { + chi = std::pow((normS2 - limit2), power); + } + // chi = std::max(0., std::sqrt(normS2) - std::sqrt(limit2)); + return chi; + } + + double + _compute_chi2(const R3x3 S, const double yield) const + { + const R3x3 Id = identity; + const R3x3 Sd = S - 1. / 3 * trace(S) * Id; + double chi = 0; + const double normS2 = frobeniusNorm(Sd) * frobeniusNorm(Sd); + double limit2 = 2. / 3 * yield * yield; + double alpha = (normS2 - limit2); + double sign = alpha / std::abs(alpha); + if (sign < 0.) { + return chi; + } + int power = 10; + double ratio = normS2 / limit2; + chi = ratio; + for (int i = 0; i < power; i++) { + chi *= ratio; + } + return chi; + } + + NodeValuePerCell<const Rdxd> + _computeGlaceAjr(const MeshType& mesh, const DiscreteScalarFunction& rhoaL, const DiscreteScalarFunction& rhoaT) const + { + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(mesh); + + const NodeValuePerCell<const Rd> Cjr = mesh_data.Cjr(); + const NodeValuePerCell<const Rd> njr = mesh_data.njr(); + + NodeValuePerCell<Rdxd> Ajr{mesh.connectivity()}; + const Rdxd I = identity; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + const size_t& nb_nodes = Ajr.numberOfSubValues(j); + const double& rhoaL_j = rhoaL[j]; + const double& rhoaT_j = rhoaT[j]; + for (size_t r = 0; r < nb_nodes; ++r) { + const Rdxd& M = tensorProduct(Cjr(j, r), njr(j, r)); + Ajr(j, r) = rhoaL_j * M + rhoaT_j * (l2Norm(Cjr(j, r)) * I - M); + } + }); + + return Ajr; + } + + NodeValuePerCell<const Rdxd> + _computeEucclhydAjr(const MeshType& mesh, + const DiscreteScalarFunction& rhoaL, + const DiscreteScalarFunction& rhoaT) const + { + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(mesh); + + const NodeValuePerFace<const Rd> Nlr = mesh_data.Nlr(); + const NodeValuePerFace<const Rd> nlr = mesh_data.nlr(); + + const auto& face_to_node_matrix = mesh.connectivity().faceToNodeMatrix(); + const auto& cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + const auto& cell_to_face_matrix = mesh.connectivity().cellToFaceMatrix(); + + NodeValuePerCell<Rdxd> Ajr{mesh.connectivity()}; + + parallel_for( + Ajr.numberOfValues(), PUGS_LAMBDA(size_t jr) { Ajr[jr] = zero; }); + const Rdxd I = identity; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + const auto& cell_nodes = cell_to_node_matrix[j]; + + const auto& cell_faces = cell_to_face_matrix[j]; + + const double& rho_aL = rhoaL[j]; + const double& rho_aT = rhoaT[j]; + + for (size_t L = 0; L < cell_faces.size(); ++L) { + const FaceId& l = cell_faces[L]; + const auto& face_nodes = face_to_node_matrix[l]; + + auto local_node_number_in_cell = [&](NodeId node_number) { + for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { + if (node_number == cell_nodes[i_node]) { + return i_node; + } + } + return std::numeric_limits<size_t>::max(); + }; + + for (size_t rl = 0; rl < face_nodes.size(); ++rl) { + const size_t R = local_node_number_in_cell(face_nodes[rl]); + const Rdxd& M = tensorProduct(Nlr(l, rl), nlr(l, rl)); + Ajr(j, R) += rho_aL * M + rho_aT * (l2Norm(Nlr(l, rl)) * I - M); + } + } + }); + + return Ajr; + } + + NodeValuePerCell<const Rdxd> + _computeAjr(const SolverType& solver_type, + const MeshType& mesh, + const DiscreteScalarFunction& rhoaL, + const DiscreteScalarFunction& rhoaT) const + { + if constexpr (Dimension == 1) { + return _computeGlaceAjr(mesh, rhoaL, rhoaT); + } else { + switch (solver_type) { + case SolverType::Glace: { + return _computeGlaceAjr(mesh, rhoaL, rhoaT); + } + case SolverType::Eucclhyd: { + return _computeEucclhydAjr(mesh, rhoaL, rhoaT); + } + default: { + throw UnexpectedError("invalid solver type"); + } + } + } + } + + NodeValue<Rdxd> + _computeAr(const MeshType& mesh, const NodeValuePerCell<const Rdxd>& Ajr) const + { + const auto& node_to_cell_matrix = mesh.connectivity().nodeToCellMatrix(); + const auto& node_local_numbers_in_their_cells = mesh.connectivity().nodeLocalNumbersInTheirCells(); + + NodeValue<Rdxd> Ar{mesh.connectivity()}; + + parallel_for( + mesh.numberOfNodes(), PUGS_LAMBDA(NodeId r) { + Rdxd sum = zero; + const auto& node_to_cell = node_to_cell_matrix[r]; + const auto& node_local_number_in_its_cells = node_local_numbers_in_their_cells.itemArray(r); + + for (size_t j = 0; j < node_to_cell.size(); ++j) { + const CellId J = node_to_cell[j]; + const unsigned int R = node_local_number_in_its_cells[j]; + sum += Ajr(J, R); + } + Ar[r] = sum; + }); + + return Ar; + } + + NodeValue<Rd> + _computeBr(const Mesh<Dimension>& mesh, + const NodeValuePerCell<const Rdxd>& Ajr, + const DiscreteVectorFunction& u, + const DiscreteTensorFunction& sigma) const + { + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(mesh); + + const NodeValuePerCell<const Rd>& Cjr = mesh_data.Cjr(); + + const auto& node_to_cell_matrix = mesh.connectivity().nodeToCellMatrix(); + const auto& node_local_numbers_in_their_cells = mesh.connectivity().nodeLocalNumbersInTheirCells(); + + NodeValue<Rd> b{mesh.connectivity()}; + + parallel_for( + mesh.numberOfNodes(), PUGS_LAMBDA(NodeId r) { + const auto& node_to_cell = node_to_cell_matrix[r]; + const auto& node_local_number_in_its_cells = node_local_numbers_in_their_cells.itemArray(r); + + Rd br = zero; + for (size_t j = 0; j < node_to_cell.size(); ++j) { + const CellId J = node_to_cell[j]; + const unsigned int R = node_local_number_in_its_cells[j]; + br += Ajr(J, R) * u[J] - sigma[J] * Cjr(J, R); + } + + b[r] = br; + }); + + return b; + } + + BoundaryConditionList + _getBCList(const MeshType& mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const + { + BoundaryConditionList bc_list; + + for (const auto& bc_descriptor : bc_descriptor_list) { + bool is_valid_boundary_condition = true; + + switch (bc_descriptor->type()) { + case IBoundaryConditionDescriptor::Type::symmetry: { + bc_list.emplace_back( + SymmetryBoundaryCondition(getMeshFlatNodeBoundary(mesh, bc_descriptor->boundaryDescriptor()))); + break; + } + case IBoundaryConditionDescriptor::Type::fixed: { + bc_list.emplace_back(FixedBoundaryCondition(getMeshNodeBoundary(mesh, bc_descriptor->boundaryDescriptor()))); + break; + } + case IBoundaryConditionDescriptor::Type::dirichlet: { + const DirichletBoundaryConditionDescriptor& dirichlet_bc_descriptor = + dynamic_cast<const DirichletBoundaryConditionDescriptor&>(*bc_descriptor); + if (dirichlet_bc_descriptor.name() == "velocity") { + MeshNodeBoundary mesh_node_boundary = getMeshNodeBoundary(mesh, dirichlet_bc_descriptor.boundaryDescriptor()); + + Array<const Rd> value_list = + InterpolateItemValue<Rd(Rd)>::template interpolate<ItemType::node>(dirichlet_bc_descriptor.rhsSymbolId(), + mesh.xr(), + mesh_node_boundary.nodeList()); + + bc_list.emplace_back(VelocityBoundaryCondition{mesh_node_boundary, value_list}); + } else if (dirichlet_bc_descriptor.name() == "pressure") { + const FunctionSymbolId pressure_id = dirichlet_bc_descriptor.rhsSymbolId(); + + if constexpr (Dimension == 1) { + MeshNodeBoundary mesh_node_boundary = getMeshNodeBoundary(mesh, bc_descriptor->boundaryDescriptor()); + + Array<const double> node_values = + InterpolateItemValue<double(Rd)>::template interpolate<ItemType::node>(pressure_id, mesh.xr(), + mesh_node_boundary.nodeList()); + + bc_list.emplace_back(PressureBoundaryCondition{mesh_node_boundary, node_values}); + } else { + MeshFaceBoundary mesh_face_boundary = getMeshFaceBoundary(mesh, bc_descriptor->boundaryDescriptor()); + + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(mesh); + Array<const double> face_values = + InterpolateItemValue<double(Rd)>::template interpolate<ItemType::face>(pressure_id, mesh_data.xl(), + mesh_face_boundary.faceList()); + bc_list.emplace_back(PressureBoundaryCondition{mesh_face_boundary, face_values}); + } + + } else if (dirichlet_bc_descriptor.name() == "normal-stress") { + const FunctionSymbolId normal_stress_id = dirichlet_bc_descriptor.rhsSymbolId(); + + if constexpr (Dimension == 1) { + MeshNodeBoundary mesh_node_boundary = getMeshNodeBoundary(mesh, bc_descriptor->boundaryDescriptor()); + + Array<const Rdxd> node_values = + InterpolateItemValue<Rdxd(Rd)>::template interpolate<ItemType::node>(normal_stress_id, mesh.xr(), + mesh_node_boundary.nodeList()); + + bc_list.emplace_back(NormalStressBoundaryCondition{mesh_node_boundary, node_values}); + } else { + MeshFaceBoundary mesh_face_boundary = getMeshFaceBoundary(mesh, bc_descriptor->boundaryDescriptor()); + + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(mesh); + Array<const Rdxd> face_values = + InterpolateItemValue<Rdxd(Rd)>::template interpolate<ItemType::face>(normal_stress_id, mesh_data.xl(), + mesh_face_boundary.faceList()); + bc_list.emplace_back(NormalStressBoundaryCondition{mesh_face_boundary, face_values}); + } + + } else { + is_valid_boundary_condition = false; + } + break; + } + default: { + is_valid_boundary_condition = false; + } + } + if (not is_valid_boundary_condition) { + std::ostringstream error_msg; + error_msg << *bc_descriptor << " is an invalid boundary condition for hyperplastic solver"; + throw NormalError(error_msg.str()); + } + } + + return bc_list; + } + + void _applyPressureBC(const BoundaryConditionList& bc_list, const MeshType& mesh, NodeValue<Rd>& br) const; + void _applyNormalStressBC(const BoundaryConditionList& bc_list, const MeshType& mesh, NodeValue<Rd>& br) const; + void _applySymmetryBC(const BoundaryConditionList& bc_list, NodeValue<Rdxd>& Ar, NodeValue<Rd>& br) const; + void _applyVelocityBC(const BoundaryConditionList& bc_list, NodeValue<Rdxd>& Ar, NodeValue<Rd>& br) const; + void + _applyBoundaryConditions(const BoundaryConditionList& bc_list, + const MeshType& mesh, + NodeValue<Rdxd>& Ar, + NodeValue<Rd>& br) const + { + this->_applyPressureBC(bc_list, mesh, br); + this->_applyNormalStressBC(bc_list, mesh, br); + this->_applySymmetryBC(bc_list, Ar, br); + this->_applyVelocityBC(bc_list, Ar, br); + } + + NodeValue<const Rd> + _computeUr(const MeshType& mesh, const NodeValue<Rdxd>& Ar, const NodeValue<Rd>& br) const + { + NodeValue<Rd> u{mesh.connectivity()}; + parallel_for( + mesh.numberOfNodes(), PUGS_LAMBDA(NodeId r) { u[r] = inverse(Ar[r]) * br[r]; }); + + return u; + } + + NodeValuePerCell<Rd> + _computeFjr(const MeshType& mesh, + const NodeValuePerCell<const Rdxd>& Ajr, + const NodeValue<const Rd>& ur, + const DiscreteVectorFunction& u, + const DiscreteTensorFunction& sigma) const + { + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(mesh); + + const NodeValuePerCell<const Rd> Cjr = mesh_data.Cjr(); + + const auto& cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + + NodeValuePerCell<Rd> F{mesh.connectivity()}; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + const auto& cell_nodes = cell_to_node_matrix[j]; + + for (size_t r = 0; r < cell_nodes.size(); ++r) { + F(j, r) = -Ajr(j, r) * (u[j] - ur[cell_nodes[r]]) + sigma[j] * Cjr(j, r); + } + }); + + return F; + } + + public: + std::tuple<const std::shared_ptr<const ItemValueVariant>, const std::shared_ptr<const SubItemValuePerItemVariant>> + compute_fluxes(const SolverType& solver_type, + const std::shared_ptr<const DiscreteFunctionVariant>& rho_v, + const std::shared_ptr<const DiscreteFunctionVariant>& aL_v, + const std::shared_ptr<const DiscreteFunctionVariant>& aT_v, + const std::shared_ptr<const DiscreteFunctionVariant>& u_v, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma_v, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const + { + std::shared_ptr mesh_v = getCommonMesh({rho_v, aL_v, aT_v, u_v, sigma_v}); + if (not mesh_v) { + throw NormalError("discrete functions are not defined on the same mesh"); + } + + if (not checkDiscretizationType({rho_v, aL_v, u_v, sigma_v}, DiscreteFunctionType::P0)) { + throw NormalError("hyperplastic solver expects P0 functions"); + } + + const MeshType& mesh = *mesh_v->get<MeshType>(); + const DiscreteScalarFunction& rho = rho_v->get<DiscreteScalarFunction>(); + const DiscreteVectorFunction& u = u_v->get<DiscreteVectorFunction>(); + const DiscreteScalarFunction& aL = aL_v->get<DiscreteScalarFunction>(); + const DiscreteScalarFunction& aT = aT_v->get<DiscreteScalarFunction>(); + const DiscreteTensorFunction3d& sigma3d = sigma_v->get<DiscreteTensorFunction3d>(); + const R3x3 I = identity; + + // std::shared_ptr<const MeshType> p_mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh); + CellValue<R3x3> tensor_values3d{mesh.connectivity()}; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { tensor_values3d[j] = sigma3d[j]; }); + CellValue<const Rdxd> tensor_values = toDimension(mesh, tensor_values3d); + DiscreteTensorFunction sigma(mesh_v, tensor_values); + + NodeValuePerCell<const Rdxd> Ajr = this->_computeAjr(solver_type, mesh, rho * aL, rho * aT); + + NodeValue<Rdxd> Ar = this->_computeAr(mesh, Ajr); + NodeValue<Rd> br = this->_computeBr(mesh, Ajr, u, sigma); + + const BoundaryConditionList bc_list = this->_getBCList(mesh, bc_descriptor_list); + this->_applyBoundaryConditions(bc_list, mesh, Ar, br); + + synchronize(Ar); + synchronize(br); + + NodeValue<const Rd> ur = this->_computeUr(mesh, Ar, br); + NodeValuePerCell<const Rd> Fjr = this->_computeFjr(mesh, Ajr, ur, u, sigma); + + return std::make_tuple(std::make_shared<const ItemValueVariant>(ur), + std::make_shared<const SubItemValuePerItemVariant>(Fjr)); + } + + std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> + apply_fluxes(const double& dt, + const MeshType& mesh, + const DiscreteScalarFunction& rho, + const DiscreteVectorFunction& u, + const DiscreteScalarFunction& E, + const DiscreteTensorFunction3d& Fe, + const DiscreteScalarFunction& zeta, + const DiscreteScalarFunction& yield, + const DiscreteTensorFunction3d& sigma, + const NodeValue<const Rd>& ur, + const NodeValuePerCell<const Rd>& Fjr) const + { + const auto& cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + + if ((mesh.shared_connectivity() != ur.connectivity_ptr()) or + (mesh.shared_connectivity() != Fjr.connectivity_ptr())) { + throw NormalError("fluxes are not defined on the same connectivity than the mesh"); + } + + NodeValue<Rd> new_xr = copy(mesh.xr()); + parallel_for( + mesh.numberOfNodes(), PUGS_LAMBDA(NodeId r) { new_xr[r] += dt * ur[r]; }); + + std::shared_ptr<const MeshType> new_mesh = std::make_shared<MeshType>(mesh.shared_connectivity(), new_xr); + + CellValue<const double> Vj = MeshDataManager::instance().getMeshData(mesh).Vj(); + const NodeValuePerCell<const Rd> Cjr = MeshDataManager::instance().getMeshData(mesh).Cjr(); + + CellValue<double> new_rho = copy(rho.cellValues()); + CellValue<Rd> new_u = copy(u.cellValues()); + CellValue<double> new_E = copy(E.cellValues()); + CellValue<R3x3> new_Fe = copy(Fe.cellValues()); + CellValue<double> new_zeta = copy(zeta.cellValues()); + CellValue<double> new_yield = copy(yield.cellValues()); + CellValue<R3x3> sigma_s = copy(sigma.cellValues()); + CellValue<double> chi{mesh.connectivity()}; + CellValue<double> rationorm{mesh.connectivity()}; + chi.fill(0.); + rationorm.fill(0.); + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + const auto& cell_nodes = cell_to_node_matrix[j]; + + Rd momentum_fluxes = zero; + double energy_fluxes = 0; + Rdxd gradv = zero; + for (size_t R = 0; R < cell_nodes.size(); ++R) { + const NodeId r = cell_nodes[R]; + gradv += tensorProduct(ur[r], Cjr(j, R)); + momentum_fluxes += Fjr(j, R); + energy_fluxes += dot(Fjr(j, R), ur[r]); + } + R3x3 I = identity; + R3x3 gradv3d = to3D(gradv); + R3x3 sigmas = sigma[j] - 1. / 3 * trace(sigma[j]) * I; + const R3x3 elastic_fluxes = gradv3d * Fe[j]; + const double dt_over_Mj = dt / (rho[j] * Vj[j]); + const double dt_over_Vj = dt / Vj[j]; + new_u[j] += dt_over_Mj * momentum_fluxes; + new_E[j] += dt_over_Mj * energy_fluxes; + new_Fe[j] += dt_over_Vj * elastic_fluxes; + const double fenorm0 = frobeniusNorm(new_Fe[j]); + chi[j] = _compute_chi(sigma[j], yield[j]); + const R3x3 M = -dt * chi[j] * zeta[j] * sigmas; + + new_Fe[j] = EigenvalueSolver{}.computeExpForTinyMatrix(M) * new_Fe[j]; + const double fenorm1 = frobeniusNorm(new_Fe[j]); + rationorm[j] = fenorm1 / fenorm0; + }); + std::cout << "sum_chi " << sum(chi) << " min norm ratio " << min(rationorm) << " max norm ratio " << max(rationorm) + << "\n"; + + CellValue<const double> new_Vj = MeshDataManager::instance().getMeshData(*new_mesh).Vj(); + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { new_rho[j] *= Vj[j] / new_Vj[j]; }); + + return {std::make_shared<MeshVariant>(new_mesh), + std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction(new_mesh, new_rho)), + std::make_shared<DiscreteFunctionVariant>(DiscreteVectorFunction(new_mesh, new_u)), + std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction(new_mesh, new_E)), + std::make_shared<DiscreteFunctionVariant>(DiscreteTensorFunction3d(new_mesh, new_Fe))}; + } + + std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> + apply_fluxes2(const double& dt, + const MeshType& mesh, + const DiscreteScalarFunction& rho, + const DiscreteVectorFunction& u, + const DiscreteScalarFunction& E, + const DiscreteTensorFunction3d& Fe, + const DiscreteScalarFunction& zeta, + const DiscreteScalarFunction& yield, + const DiscreteTensorFunction3d& sigma, + const NodeValue<const Rd>& ur, + const NodeValuePerCell<const Rd>& Fjr) const + { + const auto& cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + + if ((mesh.shared_connectivity() != ur.connectivity_ptr()) or + (mesh.shared_connectivity() != Fjr.connectivity_ptr())) { + throw NormalError("fluxes are not defined on the same connectivity than the mesh"); + } + + NodeValue<Rd> new_xr = copy(mesh.xr()); + parallel_for( + mesh.numberOfNodes(), PUGS_LAMBDA(NodeId r) { new_xr[r] += dt * ur[r]; }); + + std::shared_ptr<const MeshType> new_mesh = std::make_shared<MeshType>(mesh.shared_connectivity(), new_xr); + + CellValue<const double> Vj = MeshDataManager::instance().getMeshData(mesh).Vj(); + const NodeValuePerCell<const Rd> Cjr = MeshDataManager::instance().getMeshData(mesh).Cjr(); + + CellValue<double> new_rho = copy(rho.cellValues()); + CellValue<Rd> new_u = copy(u.cellValues()); + CellValue<double> new_E = copy(E.cellValues()); + CellValue<R3x3> new_Fe = copy(Fe.cellValues()); + CellValue<double> new_zeta = copy(zeta.cellValues()); + CellValue<double> new_yield = copy(yield.cellValues()); + CellValue<R3x3> sigma_s = copy(sigma.cellValues()); + CellValue<double> chi{mesh.connectivity()}; + CellValue<double> rationorm{mesh.connectivity()}; + chi.fill(0.); + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + const auto& cell_nodes = cell_to_node_matrix[j]; + + Rd momentum_fluxes = zero; + double energy_fluxes = 0; + Rdxd gradv = zero; + for (size_t R = 0; R < cell_nodes.size(); ++R) { + const NodeId r = cell_nodes[R]; + gradv += tensorProduct(ur[r], Cjr(j, R)); + momentum_fluxes += Fjr(j, R); + energy_fluxes += dot(Fjr(j, R), ur[r]); + } + R3x3 I = identity; + R3x3 gradv3d = to3D(gradv); + R3x3 sigmas = sigma[j] - 1. / 3 * trace(sigma[j]) * I; + const R3x3 elastic_fluxes = gradv3d * Fe[j]; + const double dt_over_Mj = dt / (rho[j] * Vj[j]); + const double dt_over_Vj = dt / Vj[j]; + new_u[j] += dt_over_Mj * momentum_fluxes; + new_E[j] += dt_over_Mj * energy_fluxes; + new_Fe[j] += dt_over_Vj * elastic_fluxes; + chi[j] = _compute_chi(sigma[j], yield[j]); + int sub_it = static_cast<int>(std::ceil(dt * chi[j] * zeta[j] * frobeniusNorm(sigmas))); + if (sub_it > 1) { + std::cout << sub_it << " dt " << dt << " chi[j] " << chi[j] << " zeta[j] " << zeta[j] + << " frobeniusNorm(sigmas) " << frobeniusNorm(sigmas) << "\n"; + } + for (int i = 0; i < sub_it; i++) { + const R3x3 M = I + dt / static_cast<double>(sub_it) * chi[j] * zeta[j] * sigmas; + new_Fe[j] = inverse(M) * new_Fe[j]; + } + }); + std::cout << "sum_chi " << sum(chi) << "\n"; + + CellValue<const double> new_Vj = MeshDataManager::instance().getMeshData(*new_mesh).Vj(); + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { new_rho[j] *= Vj[j] / new_Vj[j]; }); + + return {std::make_shared<MeshVariant>(new_mesh), + std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction(new_mesh, new_rho)), + std::make_shared<DiscreteFunctionVariant>(DiscreteVectorFunction(new_mesh, new_u)), + std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction(new_mesh, new_E)), + std::make_shared<DiscreteFunctionVariant>(DiscreteTensorFunction3d(new_mesh, new_Fe))}; + } + + std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> + apply_fluxes_B(const double& dt, + const MeshType& mesh, + const DiscreteScalarFunction& rho, + const DiscreteVectorFunction& u, + const DiscreteScalarFunction& E, + const DiscreteTensorFunction3d& Be, + const DiscreteScalarFunction& zeta, + const DiscreteScalarFunction& yield, + const DiscreteTensorFunction3d& sigma, + const NodeValue<const Rd>& ur, + const NodeValuePerCell<const Rd>& Fjr) const + { + const auto& cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + + if ((mesh.shared_connectivity() != ur.connectivity_ptr()) or + (mesh.shared_connectivity() != Fjr.connectivity_ptr())) { + throw NormalError("fluxes are not defined on the same connectivity than the mesh"); + } + + NodeValue<Rd> new_xr = copy(mesh.xr()); + parallel_for( + mesh.numberOfNodes(), PUGS_LAMBDA(NodeId r) { new_xr[r] += dt * ur[r]; }); + + std::shared_ptr<const MeshType> new_mesh = std::make_shared<MeshType>(mesh.shared_connectivity(), new_xr); + + CellValue<const double> Vj = MeshDataManager::instance().getMeshData(mesh).Vj(); + const NodeValuePerCell<const Rd> Cjr = MeshDataManager::instance().getMeshData(mesh).Cjr(); + + CellValue<double> new_rho = copy(rho.cellValues()); + CellValue<Rd> new_u = copy(u.cellValues()); + CellValue<double> new_E = copy(E.cellValues()); + CellValue<R3x3> new_Be = copy(Be.cellValues()); + CellValue<double> new_zeta = copy(zeta.cellValues()); + CellValue<double> new_yield = copy(yield.cellValues()); + CellValue<R3x3> sigma_s = copy(sigma.cellValues()); + CellValue<double> chi{mesh.connectivity()}; + CellValue<double> rationorm{mesh.connectivity()}; + chi.fill(0.); + // rationorm.fill(0.); + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + const auto& cell_nodes = cell_to_node_matrix[j]; + Rd momentum_fluxes = zero; + double energy_fluxes = 0; + Rdxd gradv = zero; + for (size_t R = 0; R < cell_nodes.size(); ++R) { + const NodeId r = cell_nodes[R]; + gradv += tensorProduct(ur[r], Cjr(j, R)); + momentum_fluxes += Fjr(j, R); + energy_fluxes += dot(Fjr(j, R), ur[r]); + } + R3x3 I = identity; + R3x3 gradv3d = to3D(gradv); + R3x3 sigmas = sigma[j] - 1. / 3 * trace(sigma[j]) * I; + // const R3x3 elastic_fluxes = gradv3d + transpose(gradv3d); + const double dt_over_Mj = dt / (rho[j] * Vj[j]); + const double dt_over_Vj = dt / Vj[j]; + new_u[j] += dt_over_Mj * momentum_fluxes; + new_E[j] += dt_over_Mj * energy_fluxes; + // new_Be[j] += dt_over_Vj * elastic_fluxes; + // const double fenorm0 = frobeniusNorm(new_Be[j]); + chi[j] = _compute_chi(sigma[j], yield[j]); + const R3x3 M = dt_over_Vj * gradv3d - dt * chi[j] * zeta[j] * sigmas; + const R3x3 expM = EigenvalueSolver{}.computeExpForTinyMatrix(M); + const R3x3 expMT = EigenvalueSolver{}.computeExpForTinyMatrix(transpose(M)); + new_Be[j] = expM * Be[j] * expMT; + // const double fenorm1 = frobeniusNorm(new_Be[j]); + // rationorm[j] = fenorm1 / fenorm0; + }); + std::cout << "sum_chi " << sum(chi) << "\n"; + + CellValue<const double> new_Vj = MeshDataManager::instance().getMeshData(*new_mesh).Vj(); + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { new_rho[j] *= Vj[j] / new_Vj[j]; }); + + return {std::make_shared<MeshVariant>(new_mesh), + std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction(new_mesh, new_rho)), + std::make_shared<DiscreteFunctionVariant>(DiscreteVectorFunction(new_mesh, new_u)), + std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction(new_mesh, new_E)), + std::make_shared<DiscreteFunctionVariant>(DiscreteTensorFunction3d(new_mesh, new_Be))}; + } + + std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> + apply_fluxes(const double& dt, + const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, + const std::shared_ptr<const DiscreteFunctionVariant>& yield, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::shared_ptr<const ItemValueVariant>& ur, + const std::shared_ptr<const SubItemValuePerItemVariant>& Fjr, + const RelaxationType& relaxation_type) const + { + std::shared_ptr mesh_v = getCommonMesh({rho, u, E}); + if (not mesh_v) { + throw NormalError("discrete functions are not defined on the same mesh"); + } + + if (not checkDiscretizationType({rho, u, E}, DiscreteFunctionType::P0)) { + throw NormalError("hyperplastic solver expects P0 functions"); + } + switch (relaxation_type) { + case RelaxationType::Exponential: { + return this->apply_fluxes(dt, // + *mesh_v->get<MeshType>(), // + rho->get<DiscreteScalarFunction>(), // + u->get<DiscreteVectorFunction>(), // + E->get<DiscreteScalarFunction>(), // + Fe->get<DiscreteTensorFunction3d>(), // + zeta->get<DiscreteScalarFunction>(), // + yield->get<DiscreteScalarFunction>(), // + sigma->get<DiscreteTensorFunction3d>(), // + ur->get<NodeValue<const Rd>>(), // + Fjr->get<NodeValuePerCell<const Rd>>()); + } + case RelaxationType::Implicit: { + return this->apply_fluxes2(dt, // + *mesh_v->get<MeshType>(), // + rho->get<DiscreteScalarFunction>(), // + u->get<DiscreteVectorFunction>(), // + E->get<DiscreteScalarFunction>(), // + Fe->get<DiscreteTensorFunction3d>(), // + zeta->get<DiscreteScalarFunction>(), // + yield->get<DiscreteScalarFunction>(), // + sigma->get<DiscreteTensorFunction3d>(), // + ur->get<NodeValue<const Rd>>(), // + Fjr->get<NodeValuePerCell<const Rd>>()); + } + case RelaxationType::CauchyGreen: { + return this->apply_fluxes_B(dt, // + *mesh_v->get<MeshType>(), // + rho->get<DiscreteScalarFunction>(), // + u->get<DiscreteVectorFunction>(), // + E->get<DiscreteScalarFunction>(), // + Fe->get<DiscreteTensorFunction3d>(), // + zeta->get<DiscreteScalarFunction>(), // + yield->get<DiscreteScalarFunction>(), // + sigma->get<DiscreteTensorFunction3d>(), // + ur->get<NodeValue<const Rd>>(), // + Fjr->get<NodeValuePerCell<const Rd>>()); + } + default: { + throw UnexpectedError("invalid relaxation type"); + } + } + } + + std::shared_ptr<const DiscreteFunctionVariant> + apply_plastic_relaxation(const RelaxationType& relaxation_type, + const double& dt, + const std::shared_ptr<const MeshType>& mesh, + const DiscreteTensorFunction3d& Fe, + const DiscreteScalarFunction& zeta, + const DiscreteScalarFunction& yield, + const DiscreteTensorFunction3d& sigman, + const DiscreteTensorFunction3d& sigmanp1) const + { + CellValue<R3x3> new_Fe = copy(Fe.cellValues()); + CellValue<double> chi{mesh->connectivity()}; + chi.fill(0.); + CellValue<const double> Vj = MeshDataManager::instance().getMeshData(*mesh).Vj(); + R3x3 I = identity; + switch (relaxation_type) { + case RelaxationType::Exponential: { + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId j) { + R3x3 sigmasn = sigman[j] - 1. / 3 * trace(sigman[j]) * I; + R3x3 sigmasnp1 = sigmanp1[j] - 1. / 3 * trace(sigmanp1[j]) * I; + chi[j] = _compute_chi(0.5 * (sigmasn + sigmasnp1), yield[j]); + const R3x3 M = -dt * chi[j] * zeta[j] * 0.5 * (sigmasn + sigmasnp1); + new_Fe[j] = EigenvalueSolver{}.computeExpForTinyMatrix(M) * new_Fe[j]; + }); + break; + } + case RelaxationType::Implicit: { + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId j) { + R3x3 sigmasn = sigman[j] - 1. / 3 * trace(sigman[j]) * I; + R3x3 sigmasnp1 = sigmanp1[j] - 1. / 3 * trace(sigmanp1[j]) * I; + chi[j] = _compute_chi(0.5 * (sigmasn + sigmasnp1), yield[j]); + int sub_it = static_cast<int>(std::ceil(dt * chi[j] * zeta[j] * frobeniusNorm(0.5 * (sigmasn + sigmasnp1)))); + if (sub_it > 1) { + std::cout << sub_it << " dt " << dt << " chi[j] " << chi[j] << " zeta[j] " << zeta[j] + << " frobeniusNorm(sigmas) " << frobeniusNorm(0.5 * (sigmasn + sigmasnp1)) << "\n"; + } + for (int i = 0; i < sub_it; i++) { + const R3x3 M = I + 0.5 * dt / static_cast<double>(sub_it) * chi[j] * zeta[j] * (sigmasn + sigmasnp1); + new_Fe[j] = inverse(M) * new_Fe[j]; + } + }); + break; + } + default: { + throw UnexpectedError("invalid relaxation type"); + } + } + + // for (CellId j = 0; j < mesh->numberOfCells(); j++) { + // if ((std::abs(frobeniusNorm(new_Fe[j]) - std::sqrt(3.)) > 1.e-1) or (j == 6399)) { + // std::cout << j << " new_Fe " << new_Fe[j] << " chi " << chi[j] << "\n"; + // } + // } + std::cout << "sum_chi " << sum(chi) << "\n"; + + return {std::make_shared<DiscreteFunctionVariant>(DiscreteTensorFunction3d(mesh, new_Fe))}; + } + + std::shared_ptr<const DiscreteFunctionVariant> + apply_plastic_relaxation(const RelaxationType& relaxation_type, + const double& dt, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, + const std::shared_ptr<const DiscreteFunctionVariant>& yield, + const std::shared_ptr<const DiscreteFunctionVariant>& sigman, + const std::shared_ptr<const DiscreteFunctionVariant>& sigmanp1) const + { + std::shared_ptr mesh_v = getCommonMesh({sigman, sigmanp1}); + if (not mesh_v) { + throw NormalError("discrete functions are not defined on the same mesh"); + } + + if (not checkDiscretizationType({sigman, sigmanp1}, DiscreteFunctionType::P0)) { + throw NormalError("hyperplastic solver expects P0 functions"); + } + + return this->apply_plastic_relaxation(relaxation_type, dt, // + mesh_v->get<MeshType>(), // + Fe->get<DiscreteTensorFunction3d>(), // + zeta->get<DiscreteScalarFunction>(), // + yield->get<DiscreteScalarFunction>(), // + sigman->get<DiscreteTensorFunction3d>(), + sigmanp1->get<DiscreteTensorFunction3d>()); + } + + std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> + apply_elastic_fluxes(const double& dt, + const MeshType& mesh, + const DiscreteScalarFunction& rho, + const DiscreteVectorFunction& u, + const DiscreteScalarFunction& E, + const DiscreteTensorFunction3d& Fe, + const NodeValue<const Rd>& ur, + const NodeValuePerCell<const Rd>& Fjr) const + { + const auto& cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + + if ((mesh.shared_connectivity() != ur.connectivity_ptr()) or + (mesh.shared_connectivity() != Fjr.connectivity_ptr())) { + throw NormalError("fluxes are not defined on the same connectivity than the mesh"); + } + + NodeValue<Rd> new_xr = copy(mesh.xr()); + parallel_for( + mesh.numberOfNodes(), PUGS_LAMBDA(NodeId r) { new_xr[r] += dt * ur[r]; }); + + std::shared_ptr<const MeshType> new_mesh = std::make_shared<MeshType>(mesh.shared_connectivity(), new_xr); + + CellValue<const double> Vj = MeshDataManager::instance().getMeshData(mesh).Vj(); + const NodeValuePerCell<const Rd> Cjr = MeshDataManager::instance().getMeshData(mesh).Cjr(); + + CellValue<double> new_rho = copy(rho.cellValues()); + CellValue<Rd> new_u = copy(u.cellValues()); + CellValue<double> new_E = copy(E.cellValues()); + CellValue<R3x3> new_Fe = copy(Fe.cellValues()); + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + const auto& cell_nodes = cell_to_node_matrix[j]; + + Rd momentum_fluxes = zero; + double energy_fluxes = 0; + Rdxd gradv = zero; + for (size_t R = 0; R < cell_nodes.size(); ++R) { + const NodeId r = cell_nodes[R]; + gradv += tensorProduct(ur[r], Cjr(j, R)); + momentum_fluxes += Fjr(j, R); + energy_fluxes += dot(Fjr(j, R), ur[r]); + } + R3x3 gradv3d = to3D(gradv); + const R3x3 elastic_fluxes = gradv3d * Fe[j]; + const double dt_over_Mj = dt / (rho[j] * Vj[j]); + const double dt_over_Vj = dt / Vj[j]; + new_u[j] += dt_over_Mj * momentum_fluxes; + new_E[j] += dt_over_Mj * energy_fluxes; + new_Fe[j] += dt_over_Vj * elastic_fluxes; + }); + CellValue<const double> new_Vj = MeshDataManager::instance().getMeshData(*new_mesh).Vj(); + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { new_rho[j] *= Vj[j] / new_Vj[j]; }); + + return {std::make_shared<MeshVariant>(new_mesh), + std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction(new_mesh, new_rho)), + std::make_shared<DiscreteFunctionVariant>(DiscreteVectorFunction(new_mesh, new_u)), + std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction(new_mesh, new_E)), + std::make_shared<DiscreteFunctionVariant>(DiscreteTensorFunction3d(new_mesh, new_Fe))}; + } + + std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> + apply_elastic_fluxes(const double& dt, + const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const ItemValueVariant>& ur, + const std::shared_ptr<const SubItemValuePerItemVariant>& Fjr) const + { + std::shared_ptr mesh_v = getCommonMesh({rho, u, E}); + if (not mesh_v) { + throw NormalError("discrete functions are not defined on the same mesh"); + } + + if (not checkDiscretizationType({rho, u, E}, DiscreteFunctionType::P0)) { + throw NormalError("hyperplastic solver expects P0 functions"); + } + + return this->apply_elastic_fluxes(dt, // + *mesh_v->get<MeshType>(), // + rho->get<DiscreteScalarFunction>(), // + u->get<DiscreteVectorFunction>(), // + E->get<DiscreteScalarFunction>(), // + Fe->get<DiscreteTensorFunction3d>(), // + ur->get<NodeValue<const Rd>>(), // + Fjr->get<NodeValuePerCell<const Rd>>()); + } + + std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> + apply(const SolverType& solver_type, + const RelaxationType& relaxation_type, + const double& dt, + const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, + const std::shared_ptr<const DiscreteFunctionVariant>& yield, + const std::shared_ptr<const DiscreteFunctionVariant>& aL, + const std::shared_ptr<const DiscreteFunctionVariant>& aT, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const + { + auto [ur, Fjr] = compute_fluxes(solver_type, rho, aL, aT, u, sigma, bc_descriptor_list); + return apply_fluxes(dt, rho, u, E, Fe, zeta, yield, sigma, ur, Fjr, relaxation_type); + } + + std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> + apply_elastic(const SolverType& solver_type, + const double& dt, + const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& aL, + const std::shared_ptr<const DiscreteFunctionVariant>& aT, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const + { + auto [ur, Fjr] = compute_fluxes(solver_type, rho, aL, aT, u, sigma, bc_descriptor_list); + return apply_elastic_fluxes(dt, rho, u, E, Fe, ur, Fjr); + } + + HyperplasticSolver() = default; + HyperplasticSolver(HyperplasticSolver&&) = default; + ~HyperplasticSolver() = default; +}; + +template <MeshConcept MeshType> +void +HyperplasticSolverHandler::HyperplasticSolver<MeshType>::_applyPressureBC(const BoundaryConditionList& bc_list, + const MeshType& mesh, + NodeValue<Rd>& br) const +{ + for (const auto& boundary_condition : bc_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<PressureBoundaryCondition, T>) { + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(mesh); + if constexpr (Dimension == 1) { + const NodeValuePerCell<const Rd> Cjr = mesh_data.Cjr(); + + const auto& node_to_cell_matrix = mesh.connectivity().nodeToCellMatrix(); + const auto& node_local_numbers_in_their_cells = mesh.connectivity().nodeLocalNumbersInTheirCells(); + + const auto& node_list = bc.nodeList(); + const auto& value_list = bc.valueList(); + parallel_for( + node_list.size(), PUGS_LAMBDA(size_t i_node) { + const NodeId node_id = node_list[i_node]; + const auto& node_cell_list = node_to_cell_matrix[node_id]; + Assert(node_cell_list.size() == 1); + + CellId node_cell_id = node_cell_list[0]; + size_t node_local_number_in_cell = node_local_numbers_in_their_cells(node_id, 0); + + br[node_id] -= value_list[i_node] * Cjr(node_cell_id, node_local_number_in_cell); + }); + } else { + const NodeValuePerFace<const Rd> Nlr = mesh_data.Nlr(); + + const auto& face_to_cell_matrix = mesh.connectivity().faceToCellMatrix(); + const auto& face_to_node_matrix = mesh.connectivity().faceToNodeMatrix(); + const auto& face_local_numbers_in_their_cells = mesh.connectivity().faceLocalNumbersInTheirCells(); + const auto& face_cell_is_reversed = mesh.connectivity().cellFaceIsReversed(); + + const auto& face_list = bc.faceList(); + const auto& value_list = bc.valueList(); + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + const auto& face_cell_list = face_to_cell_matrix[face_id]; + Assert(face_cell_list.size() == 1); + + CellId face_cell_id = face_cell_list[0]; + size_t face_local_number_in_cell = face_local_numbers_in_their_cells(face_id, 0); + + const double sign = face_cell_is_reversed(face_cell_id, face_local_number_in_cell) ? -1 : 1; + + const auto& face_nodes = face_to_node_matrix[face_id]; + + for (size_t i_node = 0; i_node < face_nodes.size(); ++i_node) { + NodeId node_id = face_nodes[i_node]; + br[node_id] -= sign * value_list[i_face] * Nlr(face_id, i_node); + } + } + } + } + }, + boundary_condition); + } +} + +template <MeshConcept MeshType> +void +HyperplasticSolverHandler::HyperplasticSolver<MeshType>::_applyNormalStressBC(const BoundaryConditionList& bc_list, + const MeshType& mesh, + NodeValue<Rd>& br) const +{ + for (const auto& boundary_condition : bc_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<NormalStressBoundaryCondition, T>) { + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(mesh); + if constexpr (Dimension == 1) { + const NodeValuePerCell<const Rd> Cjr = mesh_data.Cjr(); + + const auto& node_to_cell_matrix = mesh.connectivity().nodeToCellMatrix(); + const auto& node_local_numbers_in_their_cells = mesh.connectivity().nodeLocalNumbersInTheirCells(); + + const auto& node_list = bc.nodeList(); + const auto& value_list = bc.valueList(); + parallel_for( + node_list.size(), PUGS_LAMBDA(size_t i_node) { + const NodeId node_id = node_list[i_node]; + const auto& node_cell_list = node_to_cell_matrix[node_id]; + Assert(node_cell_list.size() == 1); + + CellId node_cell_id = node_cell_list[0]; + size_t node_local_number_in_cell = node_local_numbers_in_their_cells(node_id, 0); + + br[node_id] += value_list[i_node] * Cjr(node_cell_id, node_local_number_in_cell); + }); + } else { + const NodeValuePerFace<const Rd> Nlr = mesh_data.Nlr(); + + const auto& face_to_cell_matrix = mesh.connectivity().faceToCellMatrix(); + const auto& face_to_node_matrix = mesh.connectivity().faceToNodeMatrix(); + const auto& face_local_numbers_in_their_cells = mesh.connectivity().faceLocalNumbersInTheirCells(); + const auto& face_cell_is_reversed = mesh.connectivity().cellFaceIsReversed(); + + const auto& face_list = bc.faceList(); + const auto& value_list = bc.valueList(); + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + const auto& face_cell_list = face_to_cell_matrix[face_id]; + Assert(face_cell_list.size() == 1); + + CellId face_cell_id = face_cell_list[0]; + size_t face_local_number_in_cell = face_local_numbers_in_their_cells(face_id, 0); + + const double sign = face_cell_is_reversed(face_cell_id, face_local_number_in_cell) ? -1 : 1; + + const auto& face_nodes = face_to_node_matrix[face_id]; + + for (size_t i_node = 0; i_node < face_nodes.size(); ++i_node) { + NodeId node_id = face_nodes[i_node]; + br[node_id] += sign * value_list[i_face] * Nlr(face_id, i_node); + } + } + } + } + }, + boundary_condition); + } +} + +template <MeshConcept MeshType> +void +HyperplasticSolverHandler::HyperplasticSolver<MeshType>::_applySymmetryBC(const BoundaryConditionList& bc_list, + NodeValue<Rdxd>& Ar, + NodeValue<Rd>& br) const +{ + for (const auto& boundary_condition : bc_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<SymmetryBoundaryCondition, T>) { + const Rd& n = bc.outgoingNormal(); + + const Rdxd I = identity; + const Rdxd nxn = tensorProduct(n, n); + const Rdxd P = I - nxn; + + const Array<const NodeId>& node_list = bc.nodeList(); + parallel_for( + bc.numberOfNodes(), PUGS_LAMBDA(int r_number) { + const NodeId r = node_list[r_number]; + + Ar[r] = P * Ar[r] * P + nxn; + br[r] = P * br[r]; + }); + } + }, + boundary_condition); + } +} + +template <MeshConcept MeshType> +void +HyperplasticSolverHandler::HyperplasticSolver<MeshType>::_applyVelocityBC(const BoundaryConditionList& bc_list, + NodeValue<Rdxd>& Ar, + NodeValue<Rd>& br) const +{ + for (const auto& boundary_condition : bc_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<VelocityBoundaryCondition, T>) { + const auto& node_list = bc.nodeList(); + const auto& value_list = bc.valueList(); + + parallel_for( + node_list.size(), PUGS_LAMBDA(size_t i_node) { + NodeId node_id = node_list[i_node]; + const auto& value = value_list[i_node]; + + Ar[node_id] = identity; + br[node_id] = value; + }); + } else if constexpr (std::is_same_v<FixedBoundaryCondition, T>) { + const auto& node_list = bc.nodeList(); + parallel_for( + node_list.size(), PUGS_LAMBDA(size_t i_node) { + NodeId node_id = node_list[i_node]; + + Ar[node_id] = identity; + br[node_id] = zero; + }); + } + }, + boundary_condition); + } +} + +template <MeshConcept MeshType> +class HyperplasticSolverHandler::HyperplasticSolver<MeshType>::FixedBoundaryCondition +{ + private: + const MeshNodeBoundary m_mesh_node_boundary; + + public: + const Array<const NodeId>& + nodeList() const + { + return m_mesh_node_boundary.nodeList(); + } + + FixedBoundaryCondition(const MeshNodeBoundary& mesh_node_boundary) : m_mesh_node_boundary{mesh_node_boundary} {} + + ~FixedBoundaryCondition() = default; +}; + +template <MeshConcept MeshType> +class HyperplasticSolverHandler::HyperplasticSolver<MeshType>::PressureBoundaryCondition +{ + private: + const MeshFaceBoundary m_mesh_face_boundary; + const Array<const double> m_value_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_mesh_face_boundary.faceList(); + } + + const Array<const double>& + valueList() const + { + return m_value_list; + } + + PressureBoundaryCondition(const MeshFaceBoundary& mesh_face_boundary, const Array<const double>& value_list) + : m_mesh_face_boundary{mesh_face_boundary}, m_value_list{value_list} + {} + + ~PressureBoundaryCondition() = default; +}; + +template <> +class HyperplasticSolverHandler::HyperplasticSolver<Mesh<1>>::PressureBoundaryCondition +{ + private: + const MeshNodeBoundary m_mesh_node_boundary; + const Array<const double> m_value_list; + + public: + const Array<const NodeId>& + nodeList() const + { + return m_mesh_node_boundary.nodeList(); + } + + const Array<const double>& + valueList() const + { + return m_value_list; + } + + PressureBoundaryCondition(const MeshNodeBoundary& mesh_node_boundary, const Array<const double>& value_list) + : m_mesh_node_boundary{mesh_node_boundary}, m_value_list{value_list} + {} + + ~PressureBoundaryCondition() = default; +}; + +template <MeshConcept MeshType> +class HyperplasticSolverHandler::HyperplasticSolver<MeshType>::NormalStressBoundaryCondition +{ + private: + const MeshFaceBoundary m_mesh_face_boundary; + const Array<const Rdxd> m_value_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_mesh_face_boundary.faceList(); + } + + const Array<const Rdxd>& + valueList() const + { + return m_value_list; + } + + NormalStressBoundaryCondition(const MeshFaceBoundary& mesh_face_boundary, const Array<const Rdxd>& value_list) + : m_mesh_face_boundary{mesh_face_boundary}, m_value_list{value_list} + {} + + ~NormalStressBoundaryCondition() = default; +}; + +template <> +class HyperplasticSolverHandler::HyperplasticSolver<Mesh<1>>::NormalStressBoundaryCondition +{ + private: + const MeshNodeBoundary m_mesh_node_boundary; + const Array<const Rdxd> m_value_list; + + public: + const Array<const NodeId>& + nodeList() const + { + return m_mesh_node_boundary.nodeList(); + } + + const Array<const Rdxd>& + valueList() const + { + return m_value_list; + } + + NormalStressBoundaryCondition(const MeshNodeBoundary& mesh_node_boundary, const Array<const Rdxd>& value_list) + : m_mesh_node_boundary{mesh_node_boundary}, m_value_list{value_list} + {} + + ~NormalStressBoundaryCondition() = default; +}; + +template <MeshConcept MeshType> +class HyperplasticSolverHandler::HyperplasticSolver<MeshType>::VelocityBoundaryCondition +{ + private: + const MeshNodeBoundary m_mesh_node_boundary; + + const Array<const TinyVector<Dimension>> m_value_list; + + public: + const Array<const NodeId>& + nodeList() const + { + return m_mesh_node_boundary.nodeList(); + } + + const Array<const TinyVector<Dimension>>& + valueList() const + { + return m_value_list; + } + + VelocityBoundaryCondition(const MeshNodeBoundary& mesh_node_boundary, + const Array<const TinyVector<Dimension>>& value_list) + : m_mesh_node_boundary{mesh_node_boundary}, m_value_list{value_list} + {} + + ~VelocityBoundaryCondition() = default; +}; + +template <MeshConcept MeshType> +class HyperplasticSolverHandler::HyperplasticSolver<MeshType>::SymmetryBoundaryCondition +{ + public: + using Rd = TinyVector<Dimension, double>; + + private: + const MeshFlatNodeBoundary<MeshType> m_mesh_flat_node_boundary; + + public: + const Rd& + outgoingNormal() const + { + return m_mesh_flat_node_boundary.outgoingNormal(); + } + + size_t + numberOfNodes() const + { + return m_mesh_flat_node_boundary.nodeList().size(); + } + + const Array<const NodeId>& + nodeList() const + { + return m_mesh_flat_node_boundary.nodeList(); + } + + SymmetryBoundaryCondition(const MeshFlatNodeBoundary<MeshType>& mesh_flat_node_boundary) + : m_mesh_flat_node_boundary(mesh_flat_node_boundary) + { + ; + } + + ~SymmetryBoundaryCondition() = default; +}; + +HyperplasticSolverHandler::HyperplasticSolverHandler(const std::shared_ptr<const MeshVariant>& mesh_v) +{ + if (not mesh_v) { + throw NormalError("discrete functions are not defined on the same mesh"); + } + + std::visit( + [&](auto&& mesh) { + using MeshType = mesh_type_t<decltype(mesh)>; + if constexpr (is_polygonal_mesh_v<MeshType>) { + m_hyperplastic_solver = std::make_unique<HyperplasticSolver<MeshType>>(); + } else { + throw NormalError("unexpected mesh type"); + } + }, + mesh_v->variant()); +} diff --git a/src/scheme/HyperplasticSolver.hpp b/src/scheme/HyperplasticSolver.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8a96fffd2cced6de61ce34e232577dcd02508c18 --- /dev/null +++ b/src/scheme/HyperplasticSolver.hpp @@ -0,0 +1,131 @@ +#ifndef HYPERPLASTIC_SOLVER_HPP +#define HYPERPLASTIC_SOLVER_HPP + +#include <mesh/MeshTraits.hpp> + +#include <memory> +#include <tuple> +#include <vector> + +class IBoundaryConditionDescriptor; +class MeshVariant; +class ItemValueVariant; +class SubItemValuePerItemVariant; +class DiscreteFunctionVariant; + +double hyperplastic_dt(const std::shared_ptr<const DiscreteFunctionVariant>& c); + +class HyperplasticSolverHandler +{ + public: + enum class SolverType + { + Glace, + Eucclhyd + }; + + enum class RelaxationType + { + Implicit, + Exponential, + CauchyGreen + }; + + private: + struct IHyperplasticSolver + { + virtual std::tuple<const std::shared_ptr<const ItemValueVariant>, + const std::shared_ptr<const SubItemValuePerItemVariant>> + compute_fluxes( + const SolverType& solver_type, + const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& aL, + const std::shared_ptr<const DiscreteFunctionVariant>& aT, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const = 0; + + virtual std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> + apply_fluxes(const double& dt, + const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, + const std::shared_ptr<const DiscreteFunctionVariant>& yield, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::shared_ptr<const ItemValueVariant>& ur, + const std::shared_ptr<const SubItemValuePerItemVariant>& Fjr, + const RelaxationType& relaxation_type) const = 0; + + virtual std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> + apply(const SolverType& solver_type, + const RelaxationType& relaxation_type, + const double& dt, + const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, + const std::shared_ptr<const DiscreteFunctionVariant>& yield, + const std::shared_ptr<const DiscreteFunctionVariant>& aL, + const std::shared_ptr<const DiscreteFunctionVariant>& aT, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const = 0; + + virtual std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> + apply_elastic(const SolverType& solver_type, + const double& dt, + const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& aL, + const std::shared_ptr<const DiscreteFunctionVariant>& aT, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const = 0; + + virtual std::shared_ptr<const DiscreteFunctionVariant> apply_plastic_relaxation( + const RelaxationType& relaxation_type, + const double& dt, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, + const std::shared_ptr<const DiscreteFunctionVariant>& yield, + const std::shared_ptr<const DiscreteFunctionVariant>& sigman, + const std::shared_ptr<const DiscreteFunctionVariant>& sigmanp1) const = 0; + + IHyperplasticSolver() = default; + IHyperplasticSolver(IHyperplasticSolver&&) = default; + IHyperplasticSolver& operator=(IHyperplasticSolver&&) = default; + + virtual ~IHyperplasticSolver() = default; + }; + + template <MeshConcept MeshType> + class HyperplasticSolver; + + std::unique_ptr<IHyperplasticSolver> m_hyperplastic_solver; + + public: + const IHyperplasticSolver& + solver() const + { + return *m_hyperplastic_solver; + } + + HyperplasticSolverHandler(const std::shared_ptr<const MeshVariant>& mesh); +}; + +#endif // HYPERPLASTIC_SOLVER_HPP diff --git a/src/scheme/HyperplasticSolverO2.cpp b/src/scheme/HyperplasticSolverO2.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9a9eff713b6bebc6b722b87c71b35bb947db25dd --- /dev/null +++ b/src/scheme/HyperplasticSolverO2.cpp @@ -0,0 +1,1773 @@ +#include <algebra/CRSMatrix.hpp> +#include <algebra/CRSMatrixDescriptor.hpp> +#include <algebra/EigenvalueSolver.hpp> +#include <algebra/SmallMatrix.hpp> +#include <analysis/Polynomial.hpp> +#include <language/utils/InterpolateItemValue.hpp> +#include <mesh/ItemValueUtils.hpp> +#include <mesh/ItemValueVariant.hpp> +#include <mesh/MeshFaceBoundary.hpp> +#include <mesh/MeshFlatNodeBoundary.hpp> +#include <mesh/MeshNodeBoundary.hpp> +#include <mesh/MeshTraits.hpp> +#include <mesh/StencilArray.hpp> +#include <mesh/StencilManager.hpp> +#include <mesh/SubItemValuePerItemVariant.hpp> +#include <scheme/DirichletBoundaryConditionDescriptor.hpp> +#include <scheme/DiscreteFunctionDPk.hpp> +#include <scheme/DiscreteFunctionDPkVariant.hpp> +#include <scheme/DiscreteFunctionP0.hpp> +#include <scheme/DiscreteFunctionUtils.hpp> +#include <scheme/DiscreteFunctionVariant.hpp> +#include <scheme/ExternalBoundaryConditionDescriptor.hpp> +#include <scheme/FixedBoundaryConditionDescriptor.hpp> +#include <scheme/HyperplasticSolverO2.hpp> +#include <scheme/IBoundaryConditionDescriptor.hpp> +#include <scheme/IDiscreteFunctionDescriptor.hpp> +#include <scheme/PolynomialReconstruction.hpp> +#include <scheme/PolynomialReconstructionDescriptor.hpp> +#include <scheme/SymmetryBoundaryConditionDescriptor.hpp> +#include <utils/Socket.hpp> +#include <variant> +#include <vector> + +template <MeshConcept MeshType> +class HyperplasticSolverO2Handler::HyperplasticSolverO2 final + : public HyperplasticSolverO2Handler::IHyperplasticSolverO2 +{ + private: + static constexpr size_t Dimension = MeshType::Dimension; + using Rdxd = TinyMatrix<Dimension>; + using R3x3 = TinyMatrix<3>; + using Rd = TinyVector<Dimension>; + using R3 = TinyVector<3>; + + using MeshDataType = MeshData<MeshType>; + + using DiscreteScalarFunction = DiscreteFunctionP0<const double>; + using DiscreteVectorFunction = DiscreteFunctionP0<const Rd>; + using DiscreteTensorFunction = DiscreteFunctionP0<const Rdxd>; + using DiscreteTensorFunction3d = DiscreteFunctionP0<const R3x3>; + + class FixedBoundaryCondition; + class PressureBoundaryCondition; + class NormalStressBoundaryCondition; + class SymmetryBoundaryCondition; + class VelocityBoundaryCondition; + + using BoundaryCondition = std::variant<FixedBoundaryCondition, + PressureBoundaryCondition, + NormalStressBoundaryCondition, + SymmetryBoundaryCondition, + VelocityBoundaryCondition>; + + using BoundaryConditionList = std::vector<BoundaryCondition>; + R3x3 + to3D(const Rdxd tensor) const + { + R3x3 tensor3d = zero; + for (size_t i = 0; i < Dimension; i++) { + for (size_t j = 0; j < Dimension; j++) { + tensor3d(i, j) = tensor(i, j); + } + } + return tensor3d; + } + + R3 + to3D(const Rd vector) const + { + R3 vector3d = zero; + for (size_t i = 0; i < Dimension; i++) { + vector3d[i] = vector[i]; + } + return vector3d; + } + + CellValue<const R3x3> + to3D(const MeshType& mesh, const CellValue<Rdxd>& tensor) const + { + CellValue<R3x3> tensor3d{mesh.connectivity()}; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { tensor3d[j] = to3D(tensor[j]); }); + return tensor3d; + } + + CellValue<const R3> + to3D(const MeshType& mesh, const CellValue<Rd> vector) const + { + CellValue<R3> vector3d{mesh.connectivity()}; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { vector3d[j] = to3D(vector[j]); }); + return vector3d; + } + + Rdxd + toDimension(const R3x3 tensor3d) const + { + Rdxd tensor = zero; + for (size_t i = 0; i < Dimension; i++) { + for (size_t j = 0; j < Dimension; j++) { + tensor(i, j) = tensor3d(i, j); + } + } + return tensor; + } + + Rd + toDimension(const R3 vector3d) const + { + Rd vector = zero; + for (size_t i = 0; i < Dimension; i++) { + vector[i] = vector3d[i]; + } + return vector; + } + + CellValue<const Rdxd> + toDimension(const MeshType& mesh, const CellValue<R3x3>& tensor3d) const + { + CellValue<Rdxd> tensor{mesh.connectivity()}; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { tensor[j] = toDimension(tensor3d[j]); }); + return tensor; + } + + CellValue<const Rd> + toDimension(const MeshType& mesh, const CellValue<R3>& vector3d) const + { + CellValue<Rd> vector{mesh.connectivity()}; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { vector[j] = to3D(vector3d[j]); }); + return vector; + } + + double + _compute_chi(const R3x3 S, const double yield) const + { + const R3x3 Id = identity; + const R3x3 Sd = S - 1. / 3 * trace(S) * Id; + double chi = 0; + const double normS2 = frobeniusNorm(Sd) * frobeniusNorm(Sd); + double limit2 = 2. / 3 * yield * yield; + const double power = 0.5; + if ((normS2 - limit2) > 0) { + chi = std::pow((normS2 - limit2), power); + } + return chi; + } + + double + _compute_chi2(const R3x3 S, const double yield) const + { + const R3x3 Id = identity; + const R3x3 Sd = S - 1. / 3 * trace(S) * Id; + double chi = 0; + const double normS2 = frobeniusNorm(Sd) * frobeniusNorm(Sd); + double limit2 = 2. / 3 * yield * yield; + double alpha = (normS2 - limit2); + double sign = alpha / std::abs(alpha); + if (sign < 0.) { + return chi; + } + int power = 10; + double ratio = normS2 / limit2; + chi = ratio; + for (int i = 0; i < power; i++) { + chi *= ratio; + } + return chi; + } + + DiscreteFunctionDPk<Dimension, Rd> + _limit(const MeshType& mesh, + const DiscreteFunctionP0<const Rd>& f, + const DiscreteFunctionDPk<Dimension, const Rd>& DPk_fh, + const double eps) const + { + MeshData<MeshType>& mesh_data = MeshDataManager::instance().getMeshData(mesh); + StencilManager::BoundaryDescriptorList symmetry_boundary_descriptor_list; + StencilDescriptor stencil_descriptor{1, StencilDescriptor::ConnectionType::by_nodes}; + auto stencil = StencilManager::instance().getCellToCellStencilArray(mesh.connectivity(), stencil_descriptor, + symmetry_boundary_descriptor_list); + DiscreteFunctionDPk<Dimension, Rd> DPk_fh_lim = copy(DPk_fh); + const auto xj = mesh_data.xj(); + // return DPk_fh_lim; + const double eps2 = 1E-14; + double cmin = 1. - eps; + double cmax = 1. + eps; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + const Rd fj = f[cell_id]; + const auto cell_stencil = stencil[cell_id]; + Rd coef1; + Rd coef2; + for (size_t dim = 0; dim < Dimension; ++dim) { + coef1[dim] = 1.; + coef2[dim] = 1.; + } + for (size_t dim = 0; dim < Dimension; ++dim) { + double f_min = std::min(cmin * fj[dim], cmax * fj[dim]); + double f_max = std::max(cmin * fj[dim], cmax * fj[dim]); + + for (size_t i_cell = 0; i_cell < cell_stencil.size(); ++i_cell) { + f_min = std::min(f_min, std::min(cmin * f[cell_stencil[i_cell]][dim], cmax * f[cell_stencil[i_cell]][dim])); + f_max = std::max(f_max, std::max(cmin * f[cell_stencil[i_cell]][dim], cmax * f[cell_stencil[i_cell]][dim])); + } + double f_bar_min = fj[dim]; + double f_bar_max = fj[dim]; + + for (size_t i_cell = 0; i_cell < cell_stencil.size(); ++i_cell) { + const CellId cell_k_id = cell_stencil[i_cell]; + const double f_xk = DPk_fh[cell_id](xj[cell_k_id])[dim]; + + f_bar_min = std::min(f_bar_min, f_xk); + f_bar_max = std::max(f_bar_max, f_xk); + } + if (std::abs(f_bar_max - fj[dim]) > eps2) { + coef1[dim] = (f_max - fj[dim]) / ((f_bar_max - fj[dim])); + } + + if (std::abs(f_bar_min - fj[dim]) > eps2) { + coef2[dim] = (f_min - fj[dim]) / ((f_bar_min - fj[dim])); + } + } + double min_coef1 = min(coef1); + double min_coef2 = min(coef2); + + const double lambda = std::max(0., std::min(1., std::min(min_coef1, min_coef2))); + + auto coefficients = DPk_fh_lim.coefficients(cell_id); + + coefficients[0] = (1 - lambda) * f[cell_id] + lambda * coefficients[0]; + for (size_t i = 1; i < coefficients.size(); ++i) { + coefficients[i] *= lambda; + } + }); + synchronize(DPk_fh_lim.cellArrays()); + return DPk_fh_lim; + } + + double + _min(const Rdxd M) const + { + double min = M(0, 0); + for (size_t i = 0; i < Dimension; ++i) { + for (size_t j = 0; j < Dimension; ++j) { + min = std::min(min, M(i, j)); + } + } + return min; + } + + double + _max(const Rdxd M) const + { + double max = M(0, 0); + for (size_t i = 0; i < Dimension; ++i) { + for (size_t j = 0; j < Dimension; ++j) { + max = std::max(max, M(i, j)); + } + } + return max; + } + + DiscreteFunctionDPk<Dimension, Rdxd> + _limit(const MeshType& mesh, + const DiscreteFunctionP0<const Rdxd>& f, + const DiscreteFunctionDPk<Dimension, const Rdxd>& DPk_fh, + const double eps) const + { + MeshData<MeshType>& mesh_data = MeshDataManager::instance().getMeshData(mesh); + StencilManager::BoundaryDescriptorList symmetry_boundary_descriptor_list; + StencilDescriptor stencil_descriptor{1, StencilDescriptor::ConnectionType::by_nodes}; + auto stencil = StencilManager::instance().getCellToCellStencilArray(mesh.connectivity(), stencil_descriptor, + symmetry_boundary_descriptor_list); + DiscreteFunctionDPk<Dimension, Rdxd> DPk_fh_lim = copy(DPk_fh); + const auto xj = mesh_data.xj(); + + const double eps2 = 1E-14; + double cmin = 1. - eps; + double cmax = 1. + eps; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + const Rdxd fj = f[cell_id]; + const auto cell_stencil = stencil[cell_id]; + Rdxd coef1; + Rdxd coef2; + for (size_t i = 0; i < Dimension; ++i) { + for (size_t j = 0; j < Dimension; ++j) { + coef1(i, j) = 1.; + coef2(i, j) = 1.; + } + } + for (size_t i = 0; i < Dimension; ++i) { + for (size_t j = 0; j < Dimension; ++j) { + double f_min = std::min(cmin * fj(i, j), cmax * fj(i, j)); + double f_max = std::max(cmin * fj(i, j), cmax * fj(i, j)); + + for (size_t i_cell = 0; i_cell < cell_stencil.size(); ++i_cell) { + f_min = + std::min(f_min, std::min(cmin * f[cell_stencil[i_cell]](i, j), cmax * f[cell_stencil[i_cell]](i, j))); + f_max = + std::max(f_max, std::max(cmin * f[cell_stencil[i_cell]](i, j), cmax * f[cell_stencil[i_cell]](i, j))); + } + double f_bar_min = fj(i, j); + double f_bar_max = fj(i, j); + + for (size_t i_cell = 0; i_cell < cell_stencil.size(); ++i_cell) { + const CellId cell_k_id = cell_stencil[i_cell]; + const double f_xk = DPk_fh[cell_id](xj[cell_k_id])(i, j); + + f_bar_min = std::min(f_bar_min, f_xk); + f_bar_max = std::max(f_bar_max, f_xk); + } + if (std::abs(f_bar_max - fj(i, j)) > eps2) { + coef1(i, j) = (f_max - fj(i, j)) / ((f_bar_max - fj(i, j))); + } + + if (std::abs(f_bar_min - fj(i, j)) > eps2) { + coef2(i, j) = (f_min - fj(i, j)) / ((f_bar_min - fj(i, j))); + } + } + } + double min_coef1 = _min(coef1); + double min_coef2 = _min(coef2); + + const double lambda = std::max(0., std::min(1., std::min(min_coef1, min_coef2))); + + auto coefficients = DPk_fh_lim.coefficients(cell_id); + + coefficients[0] = (1 - lambda) * f[cell_id] + lambda * coefficients[0]; + for (size_t i = 1; i < coefficients.size(); ++i) { + coefficients[i] *= lambda; + } + }); + + synchronize(DPk_fh_lim.cellArrays()); + return DPk_fh_lim; + } + + NodeValuePerCell<const Rdxd> + _computeGlaceAjr(const MeshType& mesh, const DiscreteScalarFunction& rhoaL, const DiscreteScalarFunction& rhoaT) const + { + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(mesh); + + const NodeValuePerCell<const Rd> Cjr = mesh_data.Cjr(); + const NodeValuePerCell<const Rd> njr = mesh_data.njr(); + + NodeValuePerCell<Rdxd> Ajr{mesh.connectivity()}; + const Rdxd I = identity; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + const size_t& nb_nodes = Ajr.numberOfSubValues(j); + const double& rhoaL_j = rhoaL[j]; + const double& rhoaT_j = rhoaT[j]; + for (size_t r = 0; r < nb_nodes; ++r) { + const Rdxd& M = tensorProduct(Cjr(j, r), njr(j, r)); + Ajr(j, r) = rhoaL_j * M + rhoaT_j * (l2Norm(Cjr(j, r)) * I - M); + } + }); + + return Ajr; + } + + NodeValuePerCell<const Rdxd> + _computeEucclhydAjr(const MeshType& mesh, + const DiscreteScalarFunction& rhoaL, + const DiscreteScalarFunction& rhoaT) const + { + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(mesh); + + const NodeValuePerFace<const Rd> Nlr = mesh_data.Nlr(); + const NodeValuePerFace<const Rd> nlr = mesh_data.nlr(); + + const auto& face_to_node_matrix = mesh.connectivity().faceToNodeMatrix(); + const auto& cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + const auto& cell_to_face_matrix = mesh.connectivity().cellToFaceMatrix(); + + NodeValuePerCell<Rdxd> Ajr{mesh.connectivity()}; + + parallel_for( + Ajr.numberOfValues(), PUGS_LAMBDA(size_t jr) { Ajr[jr] = zero; }); + const Rdxd I = identity; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + const auto& cell_nodes = cell_to_node_matrix[j]; + + const auto& cell_faces = cell_to_face_matrix[j]; + + const double& rho_aL = rhoaL[j]; + const double& rho_aT = rhoaT[j]; + + for (size_t L = 0; L < cell_faces.size(); ++L) { + const FaceId& l = cell_faces[L]; + const auto& face_nodes = face_to_node_matrix[l]; + + auto local_node_number_in_cell = [&](NodeId node_number) { + for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { + if (node_number == cell_nodes[i_node]) { + return i_node; + } + } + return std::numeric_limits<size_t>::max(); + }; + + for (size_t rl = 0; rl < face_nodes.size(); ++rl) { + const size_t R = local_node_number_in_cell(face_nodes[rl]); + const Rdxd& M = tensorProduct(Nlr(l, rl), nlr(l, rl)); + Ajr(j, R) += rho_aL * M + rho_aT * (l2Norm(Nlr(l, rl)) * I - M); + } + } + }); + + return Ajr; + } + + NodeValuePerCell<const Rdxd> + _computeAjr(const SolverType& solver_type, + const MeshType& mesh, + const DiscreteScalarFunction& rhoaL, + const DiscreteScalarFunction& rhoaT) const + { + if constexpr (Dimension == 1) { + return _computeGlaceAjr(mesh, rhoaL, rhoaT); + } else { + switch (solver_type) { + case SolverType::Glace: { + return _computeGlaceAjr(mesh, rhoaL, rhoaT); + } + case SolverType::Eucclhyd: { + return _computeEucclhydAjr(mesh, rhoaL, rhoaT); + } + default: { + throw UnexpectedError("invalid solver type"); + } + } + } + } + + NodeValue<Rdxd> + _computeAr(const MeshType& mesh, const NodeValuePerCell<const Rdxd>& Ajr) const + { + const auto& node_to_cell_matrix = mesh.connectivity().nodeToCellMatrix(); + const auto& node_local_numbers_in_their_cells = mesh.connectivity().nodeLocalNumbersInTheirCells(); + + NodeValue<Rdxd> Ar{mesh.connectivity()}; + + parallel_for( + mesh.numberOfNodes(), PUGS_LAMBDA(NodeId r) { + Rdxd sum = zero; + const auto& node_to_cell = node_to_cell_matrix[r]; + const auto& node_local_number_in_its_cells = node_local_numbers_in_their_cells.itemArray(r); + + for (size_t j = 0; j < node_to_cell.size(); ++j) { + const CellId J = node_to_cell[j]; + const unsigned int R = node_local_number_in_its_cells[j]; + sum += Ajr(J, R); + } + Ar[r] = sum; + }); + + return Ar; + } + + NodeValue<Rd> + _computeBr(const Mesh<Dimension>& mesh, + const NodeValuePerCell<const Rdxd>& Ajr, + const DiscreteFunctionDPk<Dimension, const Rd>& DPk_u, + const DiscreteFunctionDPk<Dimension, const Rdxd>& DPk_sigma) const + { + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(mesh); + + const NodeValuePerCell<const Rd>& Cjr = mesh_data.Cjr(); + + const auto& node_to_cell_matrix = mesh.connectivity().nodeToCellMatrix(); + const auto& node_local_numbers_in_their_cells = mesh.connectivity().nodeLocalNumbersInTheirCells(); + + NodeValue<Rd> b{mesh.connectivity()}; + auto xr = mesh.xr(); + + parallel_for( + mesh.numberOfNodes(), PUGS_LAMBDA(NodeId r) { + const auto& node_to_cell = node_to_cell_matrix[r]; + const auto& node_local_number_in_its_cells = node_local_numbers_in_their_cells.itemArray(r); + + Rd br = zero; + for (size_t j = 0; j < node_to_cell.size(); ++j) { + const CellId J = node_to_cell[j]; + const unsigned int R = node_local_number_in_its_cells[j]; + br += Ajr(J, R) * DPk_u[J](xr[r]) - DPk_sigma[J](xr[r]) * Cjr(J, R); + } + + b[r] = br; + }); + + return b; + } + + BoundaryConditionList + _getBCList(const MeshType& mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const + { + BoundaryConditionList bc_list; + + for (const auto& bc_descriptor : bc_descriptor_list) { + bool is_valid_boundary_condition = true; + + switch (bc_descriptor->type()) { + case IBoundaryConditionDescriptor::Type::symmetry: { + bc_list.emplace_back( + SymmetryBoundaryCondition(getMeshFlatNodeBoundary(mesh, bc_descriptor->boundaryDescriptor()))); + break; + } + case IBoundaryConditionDescriptor::Type::fixed: { + bc_list.emplace_back(FixedBoundaryCondition(getMeshNodeBoundary(mesh, bc_descriptor->boundaryDescriptor()))); + break; + } + case IBoundaryConditionDescriptor::Type::dirichlet: { + const DirichletBoundaryConditionDescriptor& dirichlet_bc_descriptor = + dynamic_cast<const DirichletBoundaryConditionDescriptor&>(*bc_descriptor); + if (dirichlet_bc_descriptor.name() == "velocity") { + MeshNodeBoundary mesh_node_boundary = getMeshNodeBoundary(mesh, dirichlet_bc_descriptor.boundaryDescriptor()); + + Array<const Rd> value_list = + InterpolateItemValue<Rd(Rd)>::template interpolate<ItemType::node>(dirichlet_bc_descriptor.rhsSymbolId(), + mesh.xr(), + mesh_node_boundary.nodeList()); + + bc_list.emplace_back(VelocityBoundaryCondition{mesh_node_boundary, value_list}); + } else if (dirichlet_bc_descriptor.name() == "pressure") { + const FunctionSymbolId pressure_id = dirichlet_bc_descriptor.rhsSymbolId(); + + if constexpr (Dimension == 1) { + MeshNodeBoundary mesh_node_boundary = getMeshNodeBoundary(mesh, bc_descriptor->boundaryDescriptor()); + + Array<const double> node_values = + InterpolateItemValue<double(Rd)>::template interpolate<ItemType::node>(pressure_id, mesh.xr(), + mesh_node_boundary.nodeList()); + + bc_list.emplace_back(PressureBoundaryCondition{mesh_node_boundary, node_values}); + } else { + MeshFaceBoundary mesh_face_boundary = getMeshFaceBoundary(mesh, bc_descriptor->boundaryDescriptor()); + + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(mesh); + Array<const double> face_values = + InterpolateItemValue<double(Rd)>::template interpolate<ItemType::face>(pressure_id, mesh_data.xl(), + mesh_face_boundary.faceList()); + bc_list.emplace_back(PressureBoundaryCondition{mesh_face_boundary, face_values}); + } + + } else if (dirichlet_bc_descriptor.name() == "normal-stress") { + const FunctionSymbolId normal_stress_id = dirichlet_bc_descriptor.rhsSymbolId(); + + if constexpr (Dimension == 1) { + MeshNodeBoundary mesh_node_boundary = getMeshNodeBoundary(mesh, bc_descriptor->boundaryDescriptor()); + + Array<const Rdxd> node_values = + InterpolateItemValue<Rdxd(Rd)>::template interpolate<ItemType::node>(normal_stress_id, mesh.xr(), + mesh_node_boundary.nodeList()); + + bc_list.emplace_back(NormalStressBoundaryCondition{mesh_node_boundary, node_values}); + } else { + MeshFaceBoundary mesh_face_boundary = getMeshFaceBoundary(mesh, bc_descriptor->boundaryDescriptor()); + + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(mesh); + Array<const Rdxd> face_values = + InterpolateItemValue<Rdxd(Rd)>::template interpolate<ItemType::face>(normal_stress_id, mesh_data.xl(), + mesh_face_boundary.faceList()); + bc_list.emplace_back(NormalStressBoundaryCondition{mesh_face_boundary, face_values}); + } + + } else { + is_valid_boundary_condition = false; + } + break; + } + default: { + is_valid_boundary_condition = false; + } + } + if (not is_valid_boundary_condition) { + std::ostringstream error_msg; + error_msg << *bc_descriptor << " is an invalid boundary condition for hyperplastic solver"; + throw NormalError(error_msg.str()); + } + } + + return bc_list; + } + + void _applyPressureBC(const BoundaryConditionList& bc_list, const MeshType& mesh, NodeValue<Rd>& br) const; + void _applyNormalStressBC(const BoundaryConditionList& bc_list, const MeshType& mesh, NodeValue<Rd>& br) const; + void _applySymmetryBC(const BoundaryConditionList& bc_list, NodeValue<Rdxd>& Ar, NodeValue<Rd>& br) const; + void _applyVelocityBC(const BoundaryConditionList& bc_list, NodeValue<Rdxd>& Ar, NodeValue<Rd>& br) const; + void + _applyBoundaryConditions(const BoundaryConditionList& bc_list, + const MeshType& mesh, + NodeValue<Rdxd>& Ar, + NodeValue<Rd>& br) const + { + this->_applyPressureBC(bc_list, mesh, br); + this->_applyNormalStressBC(bc_list, mesh, br); + this->_applySymmetryBC(bc_list, Ar, br); + this->_applyVelocityBC(bc_list, Ar, br); + } + + void + _projectOnBoundary(const BoundaryConditionList& bc_list, NodeValue<Rd>& xr) const + { + for (const auto& boundary_condition : bc_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<SymmetryBoundaryCondition, T>) { + const Rd& n = bc.outgoingNormal(); + const Rd& o = bc.origin(); + + const Array<const NodeId>& node_list = bc.nodeList(); + parallel_for( + bc.numberOfNodes(), PUGS_LAMBDA(int r_number) { + const NodeId r = node_list[r_number]; + Rd& x = xr[node_list[r]]; + x -= dot(x - o, n) * n; + }); + } + }, + boundary_condition); + } + } + NodeValue<const Rd> + _computeUr(const MeshType& mesh, const NodeValue<Rdxd>& Ar, const NodeValue<Rd>& br) const + { + NodeValue<Rd> u{mesh.connectivity()}; + parallel_for( + mesh.numberOfNodes(), PUGS_LAMBDA(NodeId r) { u[r] = inverse(Ar[r]) * br[r]; }); + + return u; + } + + NodeValuePerCell<Rd> + _computeFjr(const MeshType& mesh, + const NodeValuePerCell<const Rdxd>& Ajr, + const NodeValue<const Rd>& ur, + const DiscreteFunctionDPk<Dimension, const Rd> DPk_uh, + const DiscreteFunctionDPk<Dimension, const Rdxd> DPk_sigmah) const + { + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(mesh); + + const NodeValuePerCell<const Rd> Cjr = mesh_data.Cjr(); + + const auto& cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + const NodeValue<const Rd>& xr = mesh.xr(); + + NodeValuePerCell<Rd> F{mesh.connectivity()}; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + const auto& cell_nodes = cell_to_node_matrix[j]; + + for (size_t r = 0; r < cell_nodes.size(); ++r) { + const NodeId R = cell_nodes[r]; + F(j, r) = -Ajr(j, r) * (DPk_uh[j](xr[R]) - ur[R]) + DPk_sigmah[j](xr[R]) * Cjr(j, r); + } + }); + + return F; + } + + public: + std::tuple<const std::shared_ptr<const ItemValueVariant>, const std::shared_ptr<const SubItemValuePerItemVariant>> + compute_fluxes(const SolverType& solver_type, + const std::shared_ptr<const DiscreteFunctionVariant>& rho_v, + const std::shared_ptr<const DiscreteFunctionVariant>& aL_v, + const std::shared_ptr<const DiscreteFunctionVariant>& aT_v, + const std::shared_ptr<const DiscreteFunctionVariant>& u_v, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma_v, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const + { + std::shared_ptr mesh_v = getCommonMesh({rho_v, aL_v, aT_v, u_v, sigma_v}); + if (not mesh_v) { + throw NormalError("discrete functions are not defined on the same mesh"); + } + + if (not checkDiscretizationType({rho_v, aL_v, u_v, sigma_v}, DiscreteFunctionType::P0)) { + throw NormalError("hyperplastic solver expects P0 functions"); + } + + const MeshType& mesh = *mesh_v->get<MeshType>(); + const DiscreteScalarFunction& rho = rho_v->get<DiscreteScalarFunction>(); + const DiscreteVectorFunction& u = u_v->get<DiscreteVectorFunction>(); + const DiscreteScalarFunction& aL = aL_v->get<DiscreteScalarFunction>(); + const DiscreteScalarFunction& aT = aT_v->get<DiscreteScalarFunction>(); + const DiscreteTensorFunction3d& sigma3d = sigma_v->get<DiscreteTensorFunction3d>(); + const R3x3 I = identity; + + // std::shared_ptr<const MeshType> p_mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh); + CellValue<R3x3> tensor_values3d{mesh.connectivity()}; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { tensor_values3d[j] = sigma3d[j]; }); + CellValue<const Rdxd> tensor_values = toDimension(mesh, tensor_values3d); + DiscreteTensorFunction sigma(mesh_v, tensor_values); + + std::vector<std::shared_ptr<const IBoundaryDescriptor>> symmetry_boundary_descriptor_list; + + for (auto&& bc_descriptor : bc_descriptor_list) { + if (bc_descriptor->type() == IBoundaryConditionDescriptor::Type::symmetry) { + symmetry_boundary_descriptor_list.push_back(bc_descriptor->boundaryDescriptor_shared()); + } + } + + PolynomialReconstructionDescriptor reconstruction_descriptor(IntegrationMethodType::cell_center, 1, + symmetry_boundary_descriptor_list); + + auto reconstruction = PolynomialReconstruction{reconstruction_descriptor}.build(u, sigma); + auto DPk_uh = reconstruction[0]->template get<DiscreteFunctionDPk<Dimension, const Rd>>(); + auto DPk_sigmah = reconstruction[1]->template get<DiscreteFunctionDPk<Dimension, const Rdxd>>(); + + // DiscreteFunctionDPk<Dimension, Rd> u_lim = copy(DPk_uh); + // DiscreteFunctionDPk<Dimension, Rdxd> sigma_lim = copy(DPk_sigmah); + double epsilon = 1.e-6; + auto u_lim = _limit(mesh, u, DPk_uh, epsilon); + auto sigma_lim = _limit(mesh, sigma, DPk_sigmah, epsilon); + + NodeValuePerCell<const Rdxd> Ajr = this->_computeAjr(solver_type, mesh, rho * aL, rho * aT); + + NodeValue<Rdxd> Ar = this->_computeAr(mesh, Ajr); + NodeValue<Rd> br = this->_computeBr(mesh, Ajr, u_lim, sigma_lim); + + const BoundaryConditionList bc_list = this->_getBCList(mesh, bc_descriptor_list); + this->_applyBoundaryConditions(bc_list, mesh, Ar, br); + + synchronize(Ar); + synchronize(br); + + NodeValue<const Rd> ur = this->_computeUr(mesh, Ar, br); + NodeValuePerCell<const Rd> Fjr = this->_computeFjr(mesh, Ajr, ur, u_lim, sigma_lim); + + return std::make_tuple(std::make_shared<const ItemValueVariant>(ur), + std::make_shared<const SubItemValuePerItemVariant>(Fjr)); + } + + std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> + apply_fluxes(const double& dt, + const MeshType& mesh, + const DiscreteScalarFunction& rho, + const DiscreteVectorFunction& u, + const DiscreteScalarFunction& E, + const DiscreteTensorFunction3d& Fe, + const DiscreteScalarFunction& zeta, + const DiscreteScalarFunction& yield, + const DiscreteTensorFunction3d& sigma, + const NodeValue<const Rd>& ur, + const NodeValuePerCell<const Rd>& Fjr, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const + { + const auto& cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + + if ((mesh.shared_connectivity() != ur.connectivity_ptr()) or + (mesh.shared_connectivity() != Fjr.connectivity_ptr())) { + throw NormalError("fluxes are not defined on the same connectivity than the mesh"); + } + + NodeValue<Rd> new_xr = copy(mesh.xr()); + parallel_for( + mesh.numberOfNodes(), PUGS_LAMBDA(NodeId r) { new_xr[r] += dt * ur[r]; }); + const BoundaryConditionList bc_list = this->_getBCList(mesh, bc_descriptor_list); + this->_projectOnBoundary(bc_list, new_xr); + std::shared_ptr<const MeshType> new_mesh = std::make_shared<MeshType>(mesh.shared_connectivity(), new_xr); + + CellValue<const double> Vj = MeshDataManager::instance().getMeshData(mesh).Vj(); + const NodeValuePerCell<const Rd> Cjr = MeshDataManager::instance().getMeshData(mesh).Cjr(); + + CellValue<double> new_rho = copy(rho.cellValues()); + CellValue<Rd> new_u = copy(u.cellValues()); + CellValue<double> new_E = copy(E.cellValues()); + CellValue<R3x3> new_Fe = copy(Fe.cellValues()); + CellValue<double> new_zeta = copy(zeta.cellValues()); + CellValue<double> new_yield = copy(yield.cellValues()); + CellValue<R3x3> sigma_s = copy(sigma.cellValues()); + CellValue<double> chi{mesh.connectivity()}; + CellValue<double> rationorm{mesh.connectivity()}; + chi.fill(0.); + rationorm.fill(0.); + // for (CellId j = 0; j < mesh.numberOfCells(); ++j) { + // const auto& cell_nodes = cell_to_node_matrix[j]; + + // Rd momentum_fluxes = zero; + // double energy_fluxes = 0; + // Rdxd gradv = zero; + // for (size_t R = 0; R < cell_nodes.size(); ++R) { + // const NodeId r = cell_nodes[R]; + // gradv += tensorProduct(ur[r], Cjr(j, R)); + // momentum_fluxes += Fjr(j, R); + // energy_fluxes += dot(Fjr(j, R), ur[r]); + // } + // R3x3 I = identity; + // R3x3 gradv3d = to3D(gradv); + // R3x3 sigmas = sigma[j] - 1. / 3 * trace(sigma[j]) * I; + // const R3x3 elastic_fluxes = gradv3d * Fe[j]; + // const double dt_over_Mj = dt / (rho[j] * Vj[j]); + // const double dt_over_Vj = dt / Vj[j]; + // new_u[j] += dt_over_Mj * momentum_fluxes; + // new_E[j] += dt_over_Mj * energy_fluxes; + // new_Fe[j] += dt_over_Vj * elastic_fluxes; + // const double fenorm0 = frobeniusNorm(new_Fe[j]); + // chi[j] = _compute_chi(sigma[j], yield[j]); + // const R3x3 M = -dt * chi[j] * zeta[j] * sigmas; + + // new_Fe[j] = EigenvalueSolver{}.computeExpForTinyMatrix(M) * new_Fe[j]; + // const double fenorm1 = frobeniusNorm(new_Fe[j]); + // rationorm[j] = fenorm1 / fenorm0; + // } + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + const auto& cell_nodes = cell_to_node_matrix[j]; + + Rd momentum_fluxes = zero; + double energy_fluxes = 0; + Rdxd gradv = zero; + for (size_t R = 0; R < cell_nodes.size(); ++R) { + const NodeId r = cell_nodes[R]; + gradv += tensorProduct(ur[r], Cjr(j, R)); + momentum_fluxes += Fjr(j, R); + energy_fluxes += dot(Fjr(j, R), ur[r]); + } + R3x3 I = identity; + R3x3 gradv3d = to3D(gradv); + R3x3 sigmas = sigma[j] - 1. / 3 * trace(sigma[j]) * I; + const R3x3 elastic_fluxes = gradv3d * Fe[j]; + const double dt_over_Mj = dt / (rho[j] * Vj[j]); + const double dt_over_Vj = dt / Vj[j]; + new_u[j] += dt_over_Mj * momentum_fluxes; + new_E[j] += dt_over_Mj * energy_fluxes; + new_Fe[j] += dt_over_Vj * elastic_fluxes; + const double fenorm0 = frobeniusNorm(new_Fe[j]); + chi[j] = _compute_chi(sigma[j], yield[j]); + const R3x3 M = -dt * chi[j] * zeta[j] * sigmas; + + new_Fe[j] = EigenvalueSolver{}.computeExpForTinyMatrix(M) * new_Fe[j]; + const double fenorm1 = frobeniusNorm(new_Fe[j]); + rationorm[j] = fenorm1 / fenorm0; + }); + std::cout << "sum_chi " << sum(chi) << " min norm ratio " << min(rationorm) << " max norm ratio " << max(rationorm) + << "\n"; + + CellValue<const double> new_Vj = MeshDataManager::instance().getMeshData(*new_mesh).Vj(); + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { new_rho[j] *= Vj[j] / new_Vj[j]; }); + + return {std::make_shared<MeshVariant>(new_mesh), + std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction(new_mesh, new_rho)), + std::make_shared<DiscreteFunctionVariant>(DiscreteVectorFunction(new_mesh, new_u)), + std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction(new_mesh, new_E)), + std::make_shared<DiscreteFunctionVariant>(DiscreteTensorFunction3d(new_mesh, new_Fe))}; + } + + std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> + apply_fluxes2(const double& dt, + const MeshType& mesh, + const DiscreteScalarFunction& rho, + const DiscreteVectorFunction& u, + const DiscreteScalarFunction& E, + const DiscreteTensorFunction3d& Fe, + const DiscreteScalarFunction& zeta, + const DiscreteScalarFunction& yield, + const DiscreteTensorFunction3d& sigma, + const NodeValue<const Rd>& ur, + const NodeValuePerCell<const Rd>& Fjr, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const + { + const auto& cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + + if ((mesh.shared_connectivity() != ur.connectivity_ptr()) or + (mesh.shared_connectivity() != Fjr.connectivity_ptr())) { + throw NormalError("fluxes are not defined on the same connectivity than the mesh"); + } + + NodeValue<Rd> new_xr = copy(mesh.xr()); + parallel_for( + mesh.numberOfNodes(), PUGS_LAMBDA(NodeId r) { new_xr[r] += dt * ur[r]; }); + const BoundaryConditionList bc_list = this->_getBCList(mesh, bc_descriptor_list); + this->_projectOnBoundary(bc_list, new_xr); + + std::shared_ptr<const MeshType> new_mesh = std::make_shared<MeshType>(mesh.shared_connectivity(), new_xr); + + CellValue<const double> Vj = MeshDataManager::instance().getMeshData(mesh).Vj(); + const NodeValuePerCell<const Rd> Cjr = MeshDataManager::instance().getMeshData(mesh).Cjr(); + + CellValue<double> new_rho = copy(rho.cellValues()); + CellValue<Rd> new_u = copy(u.cellValues()); + CellValue<double> new_E = copy(E.cellValues()); + CellValue<R3x3> new_Fe = copy(Fe.cellValues()); + CellValue<double> new_zeta = copy(zeta.cellValues()); + CellValue<double> new_yield = copy(yield.cellValues()); + CellValue<R3x3> sigma_s = copy(sigma.cellValues()); + CellValue<double> chi{mesh.connectivity()}; + CellValue<double> rationorm{mesh.connectivity()}; + chi.fill(0.); + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + const auto& cell_nodes = cell_to_node_matrix[j]; + + Rd momentum_fluxes = zero; + double energy_fluxes = 0; + Rdxd gradv = zero; + for (size_t R = 0; R < cell_nodes.size(); ++R) { + const NodeId r = cell_nodes[R]; + gradv += tensorProduct(ur[r], Cjr(j, R)); + momentum_fluxes += Fjr(j, R); + energy_fluxes += dot(Fjr(j, R), ur[r]); + } + R3x3 I = identity; + R3x3 gradv3d = to3D(gradv); + R3x3 sigmas = sigma[j] - 1. / 3 * trace(sigma[j]) * I; + const R3x3 elastic_fluxes = gradv3d * Fe[j]; + const double dt_over_Mj = dt / (rho[j] * Vj[j]); + const double dt_over_Vj = dt / Vj[j]; + new_u[j] += dt_over_Mj * momentum_fluxes; + new_E[j] += dt_over_Mj * energy_fluxes; + new_Fe[j] += dt_over_Vj * elastic_fluxes; + chi[j] = _compute_chi(sigma[j], yield[j]); + int sub_it = static_cast<int>(std::ceil(dt * chi[j] * zeta[j] * frobeniusNorm(sigmas))); + if (sub_it > 1) { + std::cout << sub_it << " dt " << dt << " chi[j] " << chi[j] << " zeta[j] " << zeta[j] + << " frobeniusNorm(sigmas) " << frobeniusNorm(sigmas) << "\n"; + } + for (int i = 0; i < sub_it; i++) { + const R3x3 M = I + dt / static_cast<double>(sub_it) * chi[j] * zeta[j] * sigmas; + new_Fe[j] = inverse(M) * new_Fe[j]; + } + }); + std::cout << "sum_chi " << sum(chi) << "\n"; + + CellValue<const double> new_Vj = MeshDataManager::instance().getMeshData(*new_mesh).Vj(); + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { new_rho[j] *= Vj[j] / new_Vj[j]; }); + + return {std::make_shared<MeshVariant>(new_mesh), + std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction(new_mesh, new_rho)), + std::make_shared<DiscreteFunctionVariant>(DiscreteVectorFunction(new_mesh, new_u)), + std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction(new_mesh, new_E)), + std::make_shared<DiscreteFunctionVariant>(DiscreteTensorFunction3d(new_mesh, new_Fe))}; + } + + std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> + apply_fluxes_B(const double& dt, + const MeshType& mesh, + const DiscreteScalarFunction& rho, + const DiscreteVectorFunction& u, + const DiscreteScalarFunction& E, + const DiscreteTensorFunction3d& Be, + const DiscreteScalarFunction& zeta, + const DiscreteScalarFunction& yield, + const DiscreteTensorFunction3d& sigma, + const NodeValue<const Rd>& ur, + const NodeValuePerCell<const Rd>& Fjr, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const + { + const auto& cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + + if ((mesh.shared_connectivity() != ur.connectivity_ptr()) or + (mesh.shared_connectivity() != Fjr.connectivity_ptr())) { + throw NormalError("fluxes are not defined on the same connectivity than the mesh"); + } + + NodeValue<Rd> new_xr = copy(mesh.xr()); + parallel_for( + mesh.numberOfNodes(), PUGS_LAMBDA(NodeId r) { new_xr[r] += dt * ur[r]; }); + const BoundaryConditionList bc_list = this->_getBCList(mesh, bc_descriptor_list); + this->_projectOnBoundary(bc_list, new_xr); + + std::shared_ptr<const MeshType> new_mesh = std::make_shared<MeshType>(mesh.shared_connectivity(), new_xr); + + CellValue<const double> Vj = MeshDataManager::instance().getMeshData(mesh).Vj(); + const NodeValuePerCell<const Rd> Cjr = MeshDataManager::instance().getMeshData(mesh).Cjr(); + + CellValue<double> new_rho = copy(rho.cellValues()); + CellValue<Rd> new_u = copy(u.cellValues()); + CellValue<double> new_E = copy(E.cellValues()); + CellValue<R3x3> new_Be = copy(Be.cellValues()); + CellValue<double> new_zeta = copy(zeta.cellValues()); + CellValue<double> new_yield = copy(yield.cellValues()); + CellValue<R3x3> sigma_s = copy(sigma.cellValues()); + CellValue<double> chi{mesh.connectivity()}; + CellValue<double> rationorm{mesh.connectivity()}; + chi.fill(0.); + // rationorm.fill(0.); + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + const auto& cell_nodes = cell_to_node_matrix[j]; + Rd momentum_fluxes = zero; + double energy_fluxes = 0; + Rdxd gradv = zero; + for (size_t R = 0; R < cell_nodes.size(); ++R) { + const NodeId r = cell_nodes[R]; + gradv += tensorProduct(ur[r], Cjr(j, R)); + momentum_fluxes += Fjr(j, R); + energy_fluxes += dot(Fjr(j, R), ur[r]); + } + R3x3 I = identity; + R3x3 gradv3d = to3D(gradv); + R3x3 sigmas = sigma[j] - 1. / 3 * trace(sigma[j]) * I; + // const R3x3 elastic_fluxes = gradv3d + transpose(gradv3d); + const double dt_over_Mj = dt / (rho[j] * Vj[j]); + const double dt_over_Vj = dt / Vj[j]; + new_u[j] += dt_over_Mj * momentum_fluxes; + new_E[j] += dt_over_Mj * energy_fluxes; + // new_Be[j] += dt_over_Vj * elastic_fluxes; + // const double fenorm0 = frobeniusNorm(new_Be[j]); + chi[j] = _compute_chi(sigma[j], yield[j]); + const R3x3 M = dt_over_Vj * gradv3d - dt * chi[j] * zeta[j] * sigmas; + const R3x3 expM = EigenvalueSolver{}.computeExpForTinyMatrix(M); + const R3x3 expMT = EigenvalueSolver{}.computeExpForTinyMatrix(transpose(M)); + new_Be[j] = expM * Be[j] * expMT; + // const double fenorm1 = frobeniusNorm(new_Be[j]); + // rationorm[j] = fenorm1 / fenorm0; + }); + std::cout << "sum_chi " << sum(chi) << "\n"; + + CellValue<const double> new_Vj = MeshDataManager::instance().getMeshData(*new_mesh).Vj(); + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { new_rho[j] *= Vj[j] / new_Vj[j]; }); + + return {std::make_shared<MeshVariant>(new_mesh), + std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction(new_mesh, new_rho)), + std::make_shared<DiscreteFunctionVariant>(DiscreteVectorFunction(new_mesh, new_u)), + std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction(new_mesh, new_E)), + std::make_shared<DiscreteFunctionVariant>(DiscreteTensorFunction3d(new_mesh, new_Be))}; + } + + std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> + apply_fluxes(const double& dt, + const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, + const std::shared_ptr<const DiscreteFunctionVariant>& yield, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::shared_ptr<const ItemValueVariant>& ur, + const std::shared_ptr<const SubItemValuePerItemVariant>& Fjr, + const RelaxationType& relaxation_type, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const + { + std::shared_ptr mesh_v = getCommonMesh({rho, u, E}); + if (not mesh_v) { + throw NormalError("discrete functions are not defined on the same mesh"); + } + + if (not checkDiscretizationType({rho, u, E}, DiscreteFunctionType::P0)) { + throw NormalError("hyperplastic solver expects P0 functions"); + } + switch (relaxation_type) { + case RelaxationType::Exponential: { + return this->apply_fluxes(dt, // + *mesh_v->get<MeshType>(), // + rho->get<DiscreteScalarFunction>(), // + u->get<DiscreteVectorFunction>(), // + E->get<DiscreteScalarFunction>(), // + Fe->get<DiscreteTensorFunction3d>(), // + zeta->get<DiscreteScalarFunction>(), // + yield->get<DiscreteScalarFunction>(), // + sigma->get<DiscreteTensorFunction3d>(), // + ur->get<NodeValue<const Rd>>(), // + Fjr->get<NodeValuePerCell<const Rd>>(), bc_descriptor_list); + } + case RelaxationType::Implicit: { + return this->apply_fluxes2(dt, // + *mesh_v->get<MeshType>(), // + rho->get<DiscreteScalarFunction>(), // + u->get<DiscreteVectorFunction>(), // + E->get<DiscreteScalarFunction>(), // + Fe->get<DiscreteTensorFunction3d>(), // + zeta->get<DiscreteScalarFunction>(), // + yield->get<DiscreteScalarFunction>(), // + sigma->get<DiscreteTensorFunction3d>(), // + ur->get<NodeValue<const Rd>>(), // + Fjr->get<NodeValuePerCell<const Rd>>(), bc_descriptor_list); + } + case RelaxationType::CauchyGreen: { + return this->apply_fluxes_B(dt, // + *mesh_v->get<MeshType>(), // + rho->get<DiscreteScalarFunction>(), // + u->get<DiscreteVectorFunction>(), // + E->get<DiscreteScalarFunction>(), // + Fe->get<DiscreteTensorFunction3d>(), // + zeta->get<DiscreteScalarFunction>(), // + yield->get<DiscreteScalarFunction>(), // + sigma->get<DiscreteTensorFunction3d>(), // + ur->get<NodeValue<const Rd>>(), // + Fjr->get<NodeValuePerCell<const Rd>>(), bc_descriptor_list); + } + default: { + throw UnexpectedError("invalid relaxation type"); + } + } + } + + std::shared_ptr<const DiscreteFunctionVariant> + apply_plastic_relaxation(const RelaxationType& relaxation_type, + const double& dt, + const std::shared_ptr<const MeshType>& mesh, + const DiscreteTensorFunction3d& Fe, + const DiscreteScalarFunction& zeta, + const DiscreteScalarFunction& yield, + const DiscreteTensorFunction3d& sigman, + const DiscreteTensorFunction3d& sigmanp1) const + { + CellValue<R3x3> new_Fe = copy(Fe.cellValues()); + CellValue<double> chi{mesh->connectivity()}; + chi.fill(0.); + CellValue<const double> Vj = MeshDataManager::instance().getMeshData(*mesh).Vj(); + R3x3 I = identity; + switch (relaxation_type) { + case RelaxationType::Exponential: { + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId j) { + R3x3 sigmasn = sigman[j] - 1. / 3 * trace(sigman[j]) * I; + R3x3 sigmasnp1 = sigmanp1[j] - 1. / 3 * trace(sigmanp1[j]) * I; + chi[j] = _compute_chi(0.5 * (sigmasn + sigmasnp1), yield[j]); + const R3x3 M = -dt * chi[j] * zeta[j] * 0.5 * (sigmasn + sigmasnp1); + new_Fe[j] = EigenvalueSolver{}.computeExpForTinyMatrix(M) * new_Fe[j]; + }); + break; + } + case RelaxationType::Implicit: { + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId j) { + R3x3 sigmasn = sigman[j] - 1. / 3 * trace(sigman[j]) * I; + R3x3 sigmasnp1 = sigmanp1[j] - 1. / 3 * trace(sigmanp1[j]) * I; + chi[j] = _compute_chi(0.5 * (sigmasn + sigmasnp1), yield[j]); + int sub_it = static_cast<int>(std::ceil(dt * chi[j] * zeta[j] * frobeniusNorm(0.5 * (sigmasn + sigmasnp1)))); + if (sub_it > 1) { + std::cout << sub_it << " dt " << dt << " chi[j] " << chi[j] << " zeta[j] " << zeta[j] + << " frobeniusNorm(sigmas) " << frobeniusNorm(0.5 * (sigmasn + sigmasnp1)) << "\n"; + } + for (int i = 0; i < sub_it; i++) { + const R3x3 M = I + 0.5 * dt / static_cast<double>(sub_it) * chi[j] * zeta[j] * (sigmasn + sigmasnp1); + new_Fe[j] = inverse(M) * new_Fe[j]; + } + }); + break; + } + default: { + throw UnexpectedError("invalid relaxation type"); + } + } + + // for (CellId j = 0; j < mesh->numberOfCells(); j++) { + // if ((std::abs(frobeniusNorm(new_Fe[j]) - std::sqrt(3.)) > 1.e-1) or (j == 6399)) { + // std::cout << j << " new_Fe " << new_Fe[j] << " chi " << chi[j] << "\n"; + // } + // } + std::cout << "sum_chi " << sum(chi) << "\n"; + + return {std::make_shared<DiscreteFunctionVariant>(DiscreteTensorFunction3d(mesh, new_Fe))}; + } + + std::shared_ptr<const DiscreteFunctionVariant> + apply_plastic_relaxation(const RelaxationType& relaxation_type, + const double& dt, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, + const std::shared_ptr<const DiscreteFunctionVariant>& yield, + const std::shared_ptr<const DiscreteFunctionVariant>& sigman, + const std::shared_ptr<const DiscreteFunctionVariant>& sigmanp1) const + { + std::shared_ptr mesh_v = getCommonMesh({sigman, sigmanp1}); + if (not mesh_v) { + throw NormalError("discrete functions are not defined on the same mesh"); + } + + if (not checkDiscretizationType({sigman, sigmanp1}, DiscreteFunctionType::P0)) { + throw NormalError("hyperplastic solver expects P0 functions"); + } + + return this->apply_plastic_relaxation(relaxation_type, dt, // + mesh_v->get<MeshType>(), // + Fe->get<DiscreteTensorFunction3d>(), // + zeta->get<DiscreteScalarFunction>(), // + yield->get<DiscreteScalarFunction>(), // + sigman->get<DiscreteTensorFunction3d>(), + sigmanp1->get<DiscreteTensorFunction3d>()); + } + + std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> + apply_elastic_fluxes(const double& dt, + const MeshType& mesh, + const DiscreteScalarFunction& rho, + const DiscreteVectorFunction& u, + const DiscreteScalarFunction& E, + const DiscreteTensorFunction3d& Fe, + const NodeValue<const Rd>& ur, + const NodeValuePerCell<const Rd>& Fjr) const + { + const auto& cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + + if ((mesh.shared_connectivity() != ur.connectivity_ptr()) or + (mesh.shared_connectivity() != Fjr.connectivity_ptr())) { + throw NormalError("fluxes are not defined on the same connectivity than the mesh"); + } + + NodeValue<Rd> new_xr = copy(mesh.xr()); + parallel_for( + mesh.numberOfNodes(), PUGS_LAMBDA(NodeId r) { new_xr[r] += dt * ur[r]; }); + + std::shared_ptr<const MeshType> new_mesh = std::make_shared<MeshType>(mesh.shared_connectivity(), new_xr); + + CellValue<const double> Vj = MeshDataManager::instance().getMeshData(mesh).Vj(); + const NodeValuePerCell<const Rd> Cjr = MeshDataManager::instance().getMeshData(mesh).Cjr(); + + CellValue<double> new_rho = copy(rho.cellValues()); + CellValue<Rd> new_u = copy(u.cellValues()); + CellValue<double> new_E = copy(E.cellValues()); + CellValue<R3x3> new_Fe = copy(Fe.cellValues()); + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + const auto& cell_nodes = cell_to_node_matrix[j]; + + Rd momentum_fluxes = zero; + double energy_fluxes = 0; + Rdxd gradv = zero; + for (size_t R = 0; R < cell_nodes.size(); ++R) { + const NodeId r = cell_nodes[R]; + gradv += tensorProduct(ur[r], Cjr(j, R)); + momentum_fluxes += Fjr(j, R); + energy_fluxes += dot(Fjr(j, R), ur[r]); + } + R3x3 gradv3d = to3D(gradv); + const R3x3 elastic_fluxes = gradv3d * Fe[j]; + const double dt_over_Mj = dt / (rho[j] * Vj[j]); + const double dt_over_Vj = dt / Vj[j]; + new_u[j] += dt_over_Mj * momentum_fluxes; + new_E[j] += dt_over_Mj * energy_fluxes; + new_Fe[j] += dt_over_Vj * elastic_fluxes; + }); + CellValue<const double> new_Vj = MeshDataManager::instance().getMeshData(*new_mesh).Vj(); + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { new_rho[j] *= Vj[j] / new_Vj[j]; }); + + return {std::make_shared<MeshVariant>(new_mesh), + std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction(new_mesh, new_rho)), + std::make_shared<DiscreteFunctionVariant>(DiscreteVectorFunction(new_mesh, new_u)), + std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction(new_mesh, new_E)), + std::make_shared<DiscreteFunctionVariant>(DiscreteTensorFunction3d(new_mesh, new_Fe))}; + } + + std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> + apply_elastic_fluxes(const double& dt, + const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const ItemValueVariant>& ur, + const std::shared_ptr<const SubItemValuePerItemVariant>& Fjr) const + { + std::shared_ptr mesh_v = getCommonMesh({rho, u, E}); + if (not mesh_v) { + throw NormalError("discrete functions are not defined on the same mesh"); + } + + if (not checkDiscretizationType({rho, u, E}, DiscreteFunctionType::P0)) { + throw NormalError("hyperplastic solver expects P0 functions"); + } + + return this->apply_elastic_fluxes(dt, // + *mesh_v->get<MeshType>(), // + rho->get<DiscreteScalarFunction>(), // + u->get<DiscreteVectorFunction>(), // + E->get<DiscreteScalarFunction>(), // + Fe->get<DiscreteTensorFunction3d>(), // + ur->get<NodeValue<const Rd>>(), // + Fjr->get<NodeValuePerCell<const Rd>>()); + } + + std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> + apply(const SolverType& solver_type, + const RelaxationType& relaxation_type, + const double& dt, + const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, + const std::shared_ptr<const DiscreteFunctionVariant>& yield, + const std::shared_ptr<const DiscreteFunctionVariant>& aL, + const std::shared_ptr<const DiscreteFunctionVariant>& aT, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const + { + auto [ur, Fjr] = compute_fluxes(solver_type, rho, aL, aT, u, sigma, bc_descriptor_list); + return apply_fluxes(dt, rho, u, E, Fe, zeta, yield, sigma, ur, Fjr, relaxation_type, bc_descriptor_list); + } + + std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> + apply_elastic(const SolverType& solver_type, + const double& dt, + const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& aL, + const std::shared_ptr<const DiscreteFunctionVariant>& aT, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const + { + auto [ur, Fjr] = compute_fluxes(solver_type, rho, aL, aT, u, sigma, bc_descriptor_list); + return apply_elastic_fluxes(dt, rho, u, E, Fe, ur, Fjr); + } + + HyperplasticSolverO2() = default; + HyperplasticSolverO2(HyperplasticSolverO2&&) = default; + ~HyperplasticSolverO2() = default; +}; + +template <MeshConcept MeshType> +void +HyperplasticSolverO2Handler::HyperplasticSolverO2<MeshType>::_applyPressureBC(const BoundaryConditionList& bc_list, + const MeshType& mesh, + NodeValue<Rd>& br) const +{ + for (const auto& boundary_condition : bc_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<PressureBoundaryCondition, T>) { + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(mesh); + if constexpr (Dimension == 1) { + const NodeValuePerCell<const Rd> Cjr = mesh_data.Cjr(); + + const auto& node_to_cell_matrix = mesh.connectivity().nodeToCellMatrix(); + const auto& node_local_numbers_in_their_cells = mesh.connectivity().nodeLocalNumbersInTheirCells(); + + const auto& node_list = bc.nodeList(); + const auto& value_list = bc.valueList(); + parallel_for( + node_list.size(), PUGS_LAMBDA(size_t i_node) { + const NodeId node_id = node_list[i_node]; + const auto& node_cell_list = node_to_cell_matrix[node_id]; + Assert(node_cell_list.size() == 1); + + CellId node_cell_id = node_cell_list[0]; + size_t node_local_number_in_cell = node_local_numbers_in_their_cells(node_id, 0); + + br[node_id] -= value_list[i_node] * Cjr(node_cell_id, node_local_number_in_cell); + }); + } else { + const NodeValuePerFace<const Rd> Nlr = mesh_data.Nlr(); + + const auto& face_to_cell_matrix = mesh.connectivity().faceToCellMatrix(); + const auto& face_to_node_matrix = mesh.connectivity().faceToNodeMatrix(); + const auto& face_local_numbers_in_their_cells = mesh.connectivity().faceLocalNumbersInTheirCells(); + const auto& face_cell_is_reversed = mesh.connectivity().cellFaceIsReversed(); + + const auto& face_list = bc.faceList(); + const auto& value_list = bc.valueList(); + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + const auto& face_cell_list = face_to_cell_matrix[face_id]; + Assert(face_cell_list.size() == 1); + + CellId face_cell_id = face_cell_list[0]; + size_t face_local_number_in_cell = face_local_numbers_in_their_cells(face_id, 0); + + const double sign = face_cell_is_reversed(face_cell_id, face_local_number_in_cell) ? -1 : 1; + + const auto& face_nodes = face_to_node_matrix[face_id]; + + for (size_t i_node = 0; i_node < face_nodes.size(); ++i_node) { + NodeId node_id = face_nodes[i_node]; + br[node_id] -= sign * value_list[i_face] * Nlr(face_id, i_node); + } + } + } + } + }, + boundary_condition); + } +} + +template <MeshConcept MeshType> +void +HyperplasticSolverO2Handler::HyperplasticSolverO2<MeshType>::_applyNormalStressBC(const BoundaryConditionList& bc_list, + const MeshType& mesh, + NodeValue<Rd>& br) const +{ + for (const auto& boundary_condition : bc_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<NormalStressBoundaryCondition, T>) { + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(mesh); + if constexpr (Dimension == 1) { + const NodeValuePerCell<const Rd> Cjr = mesh_data.Cjr(); + + const auto& node_to_cell_matrix = mesh.connectivity().nodeToCellMatrix(); + const auto& node_local_numbers_in_their_cells = mesh.connectivity().nodeLocalNumbersInTheirCells(); + + const auto& node_list = bc.nodeList(); + const auto& value_list = bc.valueList(); + parallel_for( + node_list.size(), PUGS_LAMBDA(size_t i_node) { + const NodeId node_id = node_list[i_node]; + const auto& node_cell_list = node_to_cell_matrix[node_id]; + Assert(node_cell_list.size() == 1); + + CellId node_cell_id = node_cell_list[0]; + size_t node_local_number_in_cell = node_local_numbers_in_their_cells(node_id, 0); + + br[node_id] += value_list[i_node] * Cjr(node_cell_id, node_local_number_in_cell); + }); + } else { + const NodeValuePerFace<const Rd> Nlr = mesh_data.Nlr(); + + const auto& face_to_cell_matrix = mesh.connectivity().faceToCellMatrix(); + const auto& face_to_node_matrix = mesh.connectivity().faceToNodeMatrix(); + const auto& face_local_numbers_in_their_cells = mesh.connectivity().faceLocalNumbersInTheirCells(); + const auto& face_cell_is_reversed = mesh.connectivity().cellFaceIsReversed(); + + const auto& face_list = bc.faceList(); + const auto& value_list = bc.valueList(); + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + const auto& face_cell_list = face_to_cell_matrix[face_id]; + Assert(face_cell_list.size() == 1); + + CellId face_cell_id = face_cell_list[0]; + size_t face_local_number_in_cell = face_local_numbers_in_their_cells(face_id, 0); + + const double sign = face_cell_is_reversed(face_cell_id, face_local_number_in_cell) ? -1 : 1; + + const auto& face_nodes = face_to_node_matrix[face_id]; + + for (size_t i_node = 0; i_node < face_nodes.size(); ++i_node) { + NodeId node_id = face_nodes[i_node]; + br[node_id] += sign * value_list[i_face] * Nlr(face_id, i_node); + } + } + } + } + }, + boundary_condition); + } +} + +template <MeshConcept MeshType> +void +HyperplasticSolverO2Handler::HyperplasticSolverO2<MeshType>::_applySymmetryBC(const BoundaryConditionList& bc_list, + NodeValue<Rdxd>& Ar, + NodeValue<Rd>& br) const +{ + for (const auto& boundary_condition : bc_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<SymmetryBoundaryCondition, T>) { + const Rd& n = bc.outgoingNormal(); + + const Rdxd I = identity; + const Rdxd nxn = tensorProduct(n, n); + const Rdxd P = I - nxn; + + const Array<const NodeId>& node_list = bc.nodeList(); + parallel_for( + bc.numberOfNodes(), PUGS_LAMBDA(int r_number) { + const NodeId r = node_list[r_number]; + + Ar[r] = P * Ar[r] * P + nxn; + br[r] = P * br[r]; + }); + } + }, + boundary_condition); + } +} + +template <MeshConcept MeshType> +void +HyperplasticSolverO2Handler::HyperplasticSolverO2<MeshType>::_applyVelocityBC(const BoundaryConditionList& bc_list, + NodeValue<Rdxd>& Ar, + NodeValue<Rd>& br) const +{ + for (const auto& boundary_condition : bc_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<VelocityBoundaryCondition, T>) { + const auto& node_list = bc.nodeList(); + const auto& value_list = bc.valueList(); + + parallel_for( + node_list.size(), PUGS_LAMBDA(size_t i_node) { + NodeId node_id = node_list[i_node]; + const auto& value = value_list[i_node]; + + Ar[node_id] = identity; + br[node_id] = value; + }); + } else if constexpr (std::is_same_v<FixedBoundaryCondition, T>) { + const auto& node_list = bc.nodeList(); + parallel_for( + node_list.size(), PUGS_LAMBDA(size_t i_node) { + NodeId node_id = node_list[i_node]; + + Ar[node_id] = identity; + br[node_id] = zero; + }); + } + }, + boundary_condition); + } +} + +template <MeshConcept MeshType> +class HyperplasticSolverO2Handler::HyperplasticSolverO2<MeshType>::FixedBoundaryCondition +{ + private: + const MeshNodeBoundary m_mesh_node_boundary; + + public: + const Array<const NodeId>& + nodeList() const + { + return m_mesh_node_boundary.nodeList(); + } + + FixedBoundaryCondition(const MeshNodeBoundary& mesh_node_boundary) : m_mesh_node_boundary{mesh_node_boundary} {} + + ~FixedBoundaryCondition() = default; +}; + +template <MeshConcept MeshType> +class HyperplasticSolverO2Handler::HyperplasticSolverO2<MeshType>::PressureBoundaryCondition +{ + private: + const MeshFaceBoundary m_mesh_face_boundary; + const Array<const double> m_value_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_mesh_face_boundary.faceList(); + } + + const Array<const double>& + valueList() const + { + return m_value_list; + } + + PressureBoundaryCondition(const MeshFaceBoundary& mesh_face_boundary, const Array<const double>& value_list) + : m_mesh_face_boundary{mesh_face_boundary}, m_value_list{value_list} + {} + + ~PressureBoundaryCondition() = default; +}; + +template <> +class HyperplasticSolverO2Handler::HyperplasticSolverO2<Mesh<1>>::PressureBoundaryCondition +{ + private: + const MeshNodeBoundary m_mesh_node_boundary; + const Array<const double> m_value_list; + + public: + const Array<const NodeId>& + nodeList() const + { + return m_mesh_node_boundary.nodeList(); + } + + const Array<const double>& + valueList() const + { + return m_value_list; + } + + PressureBoundaryCondition(const MeshNodeBoundary& mesh_node_boundary, const Array<const double>& value_list) + : m_mesh_node_boundary{mesh_node_boundary}, m_value_list{value_list} + {} + + ~PressureBoundaryCondition() = default; +}; + +template <MeshConcept MeshType> +class HyperplasticSolverO2Handler::HyperplasticSolverO2<MeshType>::NormalStressBoundaryCondition +{ + private: + const MeshFaceBoundary m_mesh_face_boundary; + const Array<const Rdxd> m_value_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_mesh_face_boundary.faceList(); + } + + const Array<const Rdxd>& + valueList() const + { + return m_value_list; + } + + NormalStressBoundaryCondition(const MeshFaceBoundary& mesh_face_boundary, const Array<const Rdxd>& value_list) + : m_mesh_face_boundary{mesh_face_boundary}, m_value_list{value_list} + {} + + ~NormalStressBoundaryCondition() = default; +}; + +template <> +class HyperplasticSolverO2Handler::HyperplasticSolverO2<Mesh<1>>::NormalStressBoundaryCondition +{ + private: + const MeshNodeBoundary m_mesh_node_boundary; + const Array<const Rdxd> m_value_list; + + public: + const Array<const NodeId>& + nodeList() const + { + return m_mesh_node_boundary.nodeList(); + } + + const Array<const Rdxd>& + valueList() const + { + return m_value_list; + } + + NormalStressBoundaryCondition(const MeshNodeBoundary& mesh_node_boundary, const Array<const Rdxd>& value_list) + : m_mesh_node_boundary{mesh_node_boundary}, m_value_list{value_list} + {} + + ~NormalStressBoundaryCondition() = default; +}; + +template <MeshConcept MeshType> +class HyperplasticSolverO2Handler::HyperplasticSolverO2<MeshType>::VelocityBoundaryCondition +{ + private: + const MeshNodeBoundary m_mesh_node_boundary; + + const Array<const TinyVector<Dimension>> m_value_list; + + public: + const Array<const NodeId>& + nodeList() const + { + return m_mesh_node_boundary.nodeList(); + } + + const Array<const TinyVector<Dimension>>& + valueList() const + { + return m_value_list; + } + + VelocityBoundaryCondition(const MeshNodeBoundary& mesh_node_boundary, + const Array<const TinyVector<Dimension>>& value_list) + : m_mesh_node_boundary{mesh_node_boundary}, m_value_list{value_list} + {} + + ~VelocityBoundaryCondition() = default; +}; + +template <MeshConcept MeshType> +class HyperplasticSolverO2Handler::HyperplasticSolverO2<MeshType>::SymmetryBoundaryCondition +{ + public: + using Rd = TinyVector<Dimension, double>; + + private: + const MeshFlatNodeBoundary<MeshType> m_mesh_flat_node_boundary; + + public: + const Rd& + outgoingNormal() const + { + return m_mesh_flat_node_boundary.outgoingNormal(); + } + + const Rd& + origin() const + { + return m_mesh_flat_node_boundary.origin(); + } + + size_t + numberOfNodes() const + { + return m_mesh_flat_node_boundary.nodeList().size(); + } + + const Array<const NodeId>& + nodeList() const + { + return m_mesh_flat_node_boundary.nodeList(); + } + + SymmetryBoundaryCondition(const MeshFlatNodeBoundary<MeshType>& mesh_flat_node_boundary) + : m_mesh_flat_node_boundary(mesh_flat_node_boundary) + { + ; + } + + ~SymmetryBoundaryCondition() = default; +}; + +HyperplasticSolverO2Handler::HyperplasticSolverO2Handler(const std::shared_ptr<const MeshVariant>& mesh_v) +{ + if (not mesh_v) { + throw NormalError("discrete functions are not defined on the same mesh"); + } + + std::visit( + [&](auto&& mesh) { + using MeshType = mesh_type_t<decltype(mesh)>; + if constexpr (is_polygonal_mesh_v<MeshType>) { + m_hyperplastic_solver = std::make_unique<HyperplasticSolverO2<MeshType>>(); + } else { + throw NormalError("unexpected mesh type"); + } + }, + mesh_v->variant()); +} diff --git a/src/scheme/HyperplasticSolverO2.hpp b/src/scheme/HyperplasticSolverO2.hpp new file mode 100644 index 0000000000000000000000000000000000000000..063dcc7d66226d12847b250a57da530590090902 --- /dev/null +++ b/src/scheme/HyperplasticSolverO2.hpp @@ -0,0 +1,132 @@ +#ifndef HYPERPLASTIC_SOLVER_O2_HPP +#define HYPERPLASTIC_SOLVER_O2_HPP + +#include <mesh/MeshTraits.hpp> + +#include <memory> +#include <tuple> +#include <vector> + +class IBoundaryConditionDescriptor; +class MeshVariant; +class ItemValueVariant; +class SubItemValuePerItemVariant; +class DiscreteFunctionVariant; + +double hyperplastic_dt(const std::shared_ptr<const DiscreteFunctionVariant>& c); + +class HyperplasticSolverO2Handler +{ + public: + enum class SolverType + { + Glace, + Eucclhyd + }; + + enum class RelaxationType + { + Implicit, + Exponential, + CauchyGreen + }; + + private: + struct IHyperplasticSolverO2 + { + virtual std::tuple<const std::shared_ptr<const ItemValueVariant>, + const std::shared_ptr<const SubItemValuePerItemVariant>> + compute_fluxes( + const SolverType& solver_type, + const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& aL, + const std::shared_ptr<const DiscreteFunctionVariant>& aT, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const = 0; + + virtual std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> + apply_fluxes(const double& dt, + const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, + const std::shared_ptr<const DiscreteFunctionVariant>& yield, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::shared_ptr<const ItemValueVariant>& ur, + const std::shared_ptr<const SubItemValuePerItemVariant>& Fjr, + const RelaxationType& relaxation_type, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const = 0; + + virtual std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> + apply(const SolverType& solver_type, + const RelaxationType& relaxation_type, + const double& dt, + const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, + const std::shared_ptr<const DiscreteFunctionVariant>& yield, + const std::shared_ptr<const DiscreteFunctionVariant>& aL, + const std::shared_ptr<const DiscreteFunctionVariant>& aT, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const = 0; + + virtual std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> + apply_elastic(const SolverType& solver_type, + const double& dt, + const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& aL, + const std::shared_ptr<const DiscreteFunctionVariant>& aT, + const std::shared_ptr<const DiscreteFunctionVariant>& sigma, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const = 0; + + virtual std::shared_ptr<const DiscreteFunctionVariant> apply_plastic_relaxation( + const RelaxationType& relaxation_type, + const double& dt, + const std::shared_ptr<const DiscreteFunctionVariant>& Fe, + const std::shared_ptr<const DiscreteFunctionVariant>& zeta, + const std::shared_ptr<const DiscreteFunctionVariant>& yield, + const std::shared_ptr<const DiscreteFunctionVariant>& sigman, + const std::shared_ptr<const DiscreteFunctionVariant>& sigmanp1) const = 0; + + IHyperplasticSolverO2() = default; + IHyperplasticSolverO2(IHyperplasticSolverO2&&) = default; + IHyperplasticSolverO2& operator=(IHyperplasticSolverO2&&) = default; + + virtual ~IHyperplasticSolverO2() = default; + }; + + template <MeshConcept MeshType> + class HyperplasticSolverO2; + + std::unique_ptr<IHyperplasticSolverO2> m_hyperplastic_solver; + + public: + const IHyperplasticSolverO2& + solver() const + { + return *m_hyperplastic_solver; + } + + HyperplasticSolverO2Handler(const std::shared_ptr<const MeshVariant>& mesh); +}; + +#endif // HYPERPLASTIC_SOLVER_O2_HPP diff --git a/src/scheme/ODEGD1D.hpp b/src/scheme/ODEGD1D.hpp new file mode 100644 index 0000000000000000000000000000000000000000..19198305f6d80f183c764503cdc7717e54ae9b0e --- /dev/null +++ b/src/scheme/ODEGD1D.hpp @@ -0,0 +1,218 @@ +#ifndef ODE_DG_1D +#define ODE_DG_1D + +#include <rang.hpp> + +#include <utils/ArrayUtils.hpp> + +#include <utils/PugsAssert.hpp> + +#include <mesh/Mesh.hpp> +#include <mesh/MeshData.hpp> +#include <mesh/MeshDataManager.hpp> +#include <mesh/MeshNodeBoundary.hpp> + +#include <algebra/TinyMatrix.hpp> +#include <algebra/TinyVector.hpp> + +#include <analysis/Polynomial.hpp> +#include <analysis/PolynomialBasis.hpp> + +#include <mesh/ItemValueUtils.hpp> +#include <mesh/SubItemValuePerItem.hpp> + +#include <scheme/DiscontinuousGalerkin1DTools.hpp> +#include <utils/Exceptions.hpp> +#include <utils/Messenger.hpp> + +#include <iostream> +enum class FluxType +{ + centered, + stable +}; + +template <size_t Degree> +class ODEDG1D +{ + using MeshType = Mesh<Connectivity1D>; + constexpr static size_t Dimension = MeshType::Dimension; + static_assert(Dimension == 1, "Galerkin 1D only works in dimension 1"); + using MeshDataType = MeshData<Dimension>; + // using UnknownsType = FiniteVolumesEulerUnknowns<MeshType>; + + const BasisType& m_basis_type; + std::shared_ptr<const MeshType> m_mesh; + const typename MeshType::Connectivity& m_connectivity; + + // class DirichletBoundaryCondition; + + // using BoundaryCondition = std::variant<DirichletBoundaryCondition>; + + // using BoundaryConditionList = std::vector<BoundaryCondition>; + // DiscontinuousGalerkin1DTools<Degree>& m_dgtools; + using Rd = TinyVector<Degree>; + using Rdd = TinyMatrix<Degree>; + + PolynomialBasis<Degree> m_basis; + using R1 = TinyVector<Dimension>; + // const BoundaryConditionList m_boundary_condition_list; + // NodeValue<R1> m_flux; + TinyVector<Degree + 1> + _buildZeros(double xmin, double xmax) + { + TinyVector<Degree + 1> zeros(zero); + if constexpr (Degree == 0) { + zeros[0] = 0.5 * (xmax - xmin); + } else { + zeros[0] = xmin; + double dx = (xmax - xmin) / Degree; + for (size_t i = 1; i <= Degree; ++i) { + zeros[i] = xmin + i * dx; + } + } + return zeros; + } + + public: + // void + // _applyBC() + // { + // for (const auto& boundary_condition : m_boundary_condition_list) { + // std::visit( + // [&](auto&& bc) { + // using T = std::decay_t<decltype(bc)>; + // if constexpr (std::is_same_v<DirichletBoundaryCondition, T>) { + // const auto& node_list = bc.nodeList(); + // const auto& value_list = bc.valueList(); + + // parallel_for( + // node_list.size(), PUGS_LAMBDA(size_t i_node) { + // NodeId node_id = node_list[i_node]; + // const auto& value = value_list[i_node]; + + // m_flux[node_id] = value; + // }); + // } + // }, + // boundary_condition); + // } + // } + + // NodeValue<R1> + // computeFluxes(FluxType flux_type, const PolynomialBasis<Degree> Basis, Polynomial<Degree>& U) + // { + // switch (flux_type) { + // // case FluxType::centered: { + // // return computeCenteredFlux(Basis, U); + // // break; + // // } + // case FluxType::stable: { + // return computeStableFlux(Basis, U); + // break; + // } + // default: + // throw UnexpectedError("unknown flux type"); + // } + // } + + double + computeStableFluxes(const CellValue<const Polynomial<Degree>>& U, NodeId r) + { + double flux; + const NodeValue<const R1> xr = m_mesh->xr(); + const auto& node_to_cell_matrix = m_mesh->connectivity().nodeToCellMatrix(); + + const auto& node_cells = node_to_cell_matrix[r]; + if (node_cells.size() == 1) { + flux = 1.; + } else { + const CellId j1 = node_cells[0]; + const double xr0 = xr[r][0]; + flux = U[j1](xr0); + std::cout << "U[" << j1 << "]=" << U[j1] << "\n"; + } + return flux; + } + + void + globalSolve(CellValue<Polynomial<Degree>>& U) + { + const NodeValue<const R1> xr = m_mesh->xr(); + const auto& cell_to_node_matrix = m_mesh->connectivity().cellToNodeMatrix(); + CellValue<TinyVector<Degree + 1>> moments(m_connectivity); + for (CellId j = 0; j < m_mesh->numberOfCells(); j++) { + const auto& cell_nodes = cell_to_node_matrix[j]; + const NodeId r1 = cell_nodes[0]; + const NodeId r2 = cell_nodes[1]; + const TinyVector<Dimension> xr1 = xr[r1]; + const TinyVector<Dimension> xr2 = xr[r2]; + const TinyVector<Degree + 1> zeros = _buildZeros(xr1[0], xr2[0]); + std::cout << "x1 " << xr1[0] << " x2 " << xr2[0] << " zeros " << zeros << "\n"; + m_basis.build(m_basis_type, 0.5 * (xr1[0] + xr2[0]), zeros); + std::cout << " B[" << j << "]=" << m_basis.elements()[0] << "\n"; + const double fluxg = computeStableFluxes(U, r1); + std::cout << j << " flux " << fluxg << "\n"; + moments[j] = buildMoments(m_basis, fluxg, xr1[0]); + std::cout << " moments[" << j << "]=" << moments[j] << "\n"; + + U[j] = elementarySolve(moments[j], m_basis, xr1[0], xr2[0]); + std::cout << " U[" << j << "]=" << U[j] << "\n"; + } + } + + // void + // integrate() const + // {} + PUGS_INLINE constexpr TinyVector<Degree + 1> + buildMoments(PolynomialBasis<Degree> basis, double fluxg, double xr1) + { + TinyVector<Degree + 1> moments(zero); + for (size_t i = 0; i <= Degree; i++) { + // moments[i] = (basis.elements()[i](xr2) - basis.elements()[i](xr1)) * fluxg; + moments[i] = -basis.elements()[i](xr1) * fluxg; + } + return moments; + } + + PUGS_INLINE constexpr Polynomial<Degree> + elementarySolve(const TinyVector<Degree + 1> moments, + const PolynomialBasis<Degree> Basis, + const double& xinf, + const double& xsup) const + { + Polynomial<Degree> U; + U.coefficients() = zero; + TinyMatrix<Degree + 1> M(zero); + for (size_t i = 0; i <= Degree; ++i) { + for (size_t j = 0; j <= Degree; ++j) { + Polynomial<2 * Degree> P = Basis.elements()[j] * Basis.elements()[i]; + P += derivative(Basis.elements()[i]) * Basis.elements()[j]; + double fluxd = Basis.elements()[i](xsup) * Basis.elements()[j](xsup); + M(i, j) = integrate(P, xinf, xsup) - fluxd; + } + } + std::cout << " M " << M << "\n"; + TinyMatrix inv_M = ::inverse(M); + TinyVector<Degree + 1> coefficients = inv_M * moments; + for (size_t i = 0; i <= Degree; ++i) { + U += coefficients[i] * Basis.elements()[i]; + } + std::cout << "U(" << xsup << ")=" << U(xsup) << "\n"; + return U; + } + + public: + // TODO add a flux manager to constructor + // TODO add a basis to constructor + ODEDG1D(const BasisType& basis_type, std::shared_ptr<const IMesh> i_mesh) //, const BoundaryConditionList& bc_list) + : m_basis_type(basis_type), + m_mesh(std::dynamic_pointer_cast<const MeshType>(i_mesh)), + m_connectivity(m_mesh->connectivity()) //, + // m_boundary_condition_list(bc_list) + { + ; + } +}; + +#endif // ODE_DG_1D diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e3739abe7afd3a625b6510d9be792bdb3b3a76dd..52d3a91ecb06c63b44bb6b391eb1c51bde95ed83 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -146,6 +146,10 @@ add_executable (unit_tests test_ParseError.cpp test_PartitionerOptions.cpp test_PETScUtils.cpp + test_Polynomial.cpp + test_PolynomialP.cpp + test_TaylorPolynomial.cpp + test_PolynomialBasis.cpp test_PrimalToDiamondDualConnectivityDataMapper.cpp test_PrimalToDual1DConnectivityDataMapper.cpp test_PrimalToMedianDualConnectivityDataMapper.cpp diff --git a/tests/test_DiscontinuousGalerkin1D.cpp b/tests/test_DiscontinuousGalerkin1D.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3b1e02dbb0fc5b692629dcc2a9a0ee0faf0546dc --- /dev/null +++ b/tests/test_DiscontinuousGalerkin1D.cpp @@ -0,0 +1,57 @@ +#include <catch2/catch_approx.hpp> +#include <catch2/catch_test_macros.hpp> + +#include <Kokkos_Core.hpp> + +#include <utils/PugsAssert.hpp> +#include <utils/Types.hpp> + +#include <algebra/TinyMatrix.hpp> +#include <analysis/Polynomial.hpp> +#include <analysis/PolynomialBasis.hpp> +#include <mesh/CartesianMeshBuilder.hpp> +#include <mesh/DiamondDualConnectivityManager.hpp> +#include <mesh/DiamondDualMeshManager.hpp> +#include <scheme/DiscontinuousGalerkin1DTools.hpp> +#include <scheme/ODEGD1D.hpp> + +// Instantiate to ensure full coverage is performed +template class Polynomial<0>; +template class Polynomial<1>; +template class Polynomial<2>; +template class Polynomial<3>; +template class Polynomial<4>; +template class Polynomial<5>; + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("Discontinuous-Galerkin-1D", "[discontinuous-galerkin-1d]") +{ + SECTION("Elementary-tests") + { + TinyVector<1> a{0}; + TinyVector<1> b{1}; + TinyVector<1, uint64_t> nbcells{10}; + // CartesianMeshBuilder mesh_builder = CartesianMeshBuilder(a, b, nbcells); + // auto mesh = mesh_builder.mesh(); + // DiscontinuousGalerkin1D<2>{mesh}; + REQUIRE_NOTHROW(DiscontinuousGalerkin1DTools<2>()); + DiscontinuousGalerkin1DTools dg = DiscontinuousGalerkin1DTools<1>(); + Polynomial<1> U; + TinyVector<2> F({3, 2}); + PolynomialBasis<1> Basis; + Basis.build(BasisType::canonical); + dg.elementarySolve(U, F, Basis, -1, 1); + REQUIRE(U == Polynomial<1>{{3. / 2, 3}}); + Polynomial<2> U2; + TinyVector<3> F2({1, 1, 1}); + PolynomialBasis<2> Basis2; + Basis2.build(BasisType::canonical); + DiscontinuousGalerkin1DTools dg2 = DiscontinuousGalerkin1DTools<2>(); + dg2.elementarySolve(U2, F2, Basis2, -1, 1); + REQUIRE(integrate(U2, -1, 1) == Catch::Approx(1).epsilon(1E-14)); + REQUIRE(integrate(U2 * Polynomial<2>{{0, 0, 1}}, -1, 1) == 1.); + dg2.elementarySolve(U2, F2, Basis2, 0, 1); + REQUIRE(integrate(U2 * Polynomial<2>{{0, 0, 1}}, 0, 1) == Catch::Approx(1).epsilon(1E-14)); + } +} diff --git a/tests/test_EigenvalueSolver.cpp b/tests/test_EigenvalueSolver.cpp index 325f3e39b0a6f34e028f99372956f67a226e39cc..f76faab82f397e251e3d4c10bb10681e113bb85a 100644 --- a/tests/test_EigenvalueSolver.cpp +++ b/tests/test_EigenvalueSolver.cpp @@ -5,6 +5,8 @@ #include <algebra/CRSMatrixDescriptor.hpp> #include <algebra/EigenvalueSolver.hpp> #include <algebra/SmallMatrix.hpp> +#include <algebra/TinyMatrix.hpp> +#include <scheme/HyperelasticSolver.hpp> #include <utils/pugs_config.hpp> @@ -633,5 +635,100 @@ TEST_CASE("EigenvalueSolver", "[algebra]") } } } +#ifdef PUGS_HAS_SLEPC + SECTION("symmetric tiny matrix") + { + TinyMatrix<3> TestA{3e10, 2e10, 4e10, 2e10, 0, 2e10, 4e10, 2e10, 3e10}; + TinyMatrix<3> TestA2{4, 2, 3, 2, 0, 2, 3, 2, 4}; + TinyMatrix<3> TestB{1, -1, 0, -1, 1, 0, 0, 0, 3}; + TinyMatrix<3> TestC{0, 0, 0, 0, 0, 0, 0, 0, 0}; + TinyMatrix<3> TestD{1e10, 0, 0, 0, 1, 0, 0, 0, 1}; + TinyMatrix<3> TestE{3e-10, 2e-10, 4e-10, 2e-10, 0, 2e-10, 4e-10, 2e-10, 3e-10}; + + TinyMatrix<3> expA2; + + auto [eigenvalues, eigenmatrix] = EigenvalueSolver{}.findEigen(TestA); + TinyMatrix<3> Diag = zero; + for (size_t i = 0; i < 3; ++i) { + Diag(i, i) = eigenvalues[i]; + } + TinyMatrix<3> B = eigenmatrix * Diag * transpose(eigenmatrix); + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE((B(i, j) - TestA(i, j)) / frobeniusNorm(TestA) == Catch::Approx(0).margin(1E-8)); + } + } + auto [eigenvalues2, eigenmatrix2] = EigenvalueSolver{}.findEigen(TestA2); + Diag = zero; + for (size_t i = 0; i < 3; ++i) { + Diag(i, i) = eigenvalues2[i]; + } + B = eigenmatrix2 * Diag * transpose(eigenmatrix2); + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE((B(i, j) - TestA2(i, j)) / frobeniusNorm(TestA2) == Catch::Approx(0).margin(1E-8)); + } + } + expA2 = EigenvalueSolver{}.computeExpForTinyMatrix(TestA2); + for (size_t i = 0; i < 3; ++i) { + Diag(i, i) = std::exp(eigenvalues2[i]); + } + B = eigenmatrix2 * Diag * transpose(eigenmatrix2); + + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(expA2(i, j) - B(i, j) == Catch::Approx(0).margin(1E-12)); + } + } + + auto [eigenvalues3, eigenmatrix3] = EigenvalueSolver{}.findEigen(TestB); + Diag = zero; + for (size_t i = 0; i < 3; ++i) { + Diag(i, i) = eigenvalues3[i]; + } + B = eigenmatrix3 * Diag * transpose(eigenmatrix3); + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE((B(i, j) - TestB(i, j)) / frobeniusNorm(TestB) == Catch::Approx(0).margin(1E-8)); + } + } + + auto [eigenvalues4, eigenmatrix4] = EigenvalueSolver{}.findEigen(TestC); + Diag = zero; + for (size_t i = 0; i < 3; ++i) { + Diag(i, i) = eigenvalues4[i]; + } + B = eigenmatrix4 * Diag * transpose(eigenmatrix4); + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE((B(i, j) - TestC(i, j)) == Catch::Approx(0).margin(1E-8)); + } + } + + auto [eigenvalues5, eigenmatrix5] = EigenvalueSolver{}.findEigen(TestD); + Diag = zero; + for (size_t i = 0; i < 3; ++i) { + Diag(i, i) = eigenvalues5[i]; + } + B = eigenmatrix5 * Diag * transpose(eigenmatrix5); + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE((B(i, j) - TestD(i, j)) / frobeniusNorm(TestD) == Catch::Approx(0).margin(1E-8)); + } + } + + auto [eigenvalues6, eigenmatrix6] = EigenvalueSolver{}.findEigen(TestE); + Diag = zero; + for (size_t i = 0; i < 3; ++i) { + Diag(i, i) = eigenvalues6[i]; + } + B = eigenmatrix6 * Diag * transpose(eigenmatrix6); + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE((B(i, j) - TestE(i, j)) / frobeniusNorm(TestE) == Catch::Approx(0).margin(1E-8)); + } + } + } +#endif } } diff --git a/tests/test_Polynomial.cpp b/tests/test_Polynomial.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4089bf27d42b6c704e1f707ccb01387a79121565 --- /dev/null +++ b/tests/test_Polynomial.cpp @@ -0,0 +1,218 @@ +#include <catch2/catch_test_macros.hpp> + +#include <Kokkos_Core.hpp> + +#include <utils/PugsAssert.hpp> +#include <utils/Types.hpp> + +#include <algebra/TinyMatrix.hpp> +#include <analysis/Polynomial.hpp> +#include <analysis/PolynomialP.hpp> + +// Instantiate to ensure full coverage is performed +template class Polynomial<0>; +template class Polynomial<1>; +template class Polynomial<2>; +template class Polynomial<3>; +template class Polynomial<4>; +template class Polynomial<5>; +template class PolynomialP<0, 2>; +template class PolynomialP<1, 2>; +template class PolynomialP<3, 2>; + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("Polynomial", "[analysis]") +{ + SECTION("construction") + { + REQUIRE_NOTHROW(Polynomial<2>(2, 3, 4)); + } + + SECTION("degree") + { + Polynomial<2> P(2, 3, 4); + REQUIRE(P.degree() == 2); + } + + SECTION("equality") + { + Polynomial<2> P(2, 3, 4); + Polynomial<2> Q(2, 3, 4); + Polynomial<2> S(2, 3, 5); + + REQUIRE(P == Q); + REQUIRE(P != S); + } + + SECTION("addition") + { + Polynomial<2> P(2, 3, 4); + Polynomial<2> Q(-1, -3, 2); + Polynomial<2> S(1, 0, 6); + Polynomial<3> T(0, 3, 1, -2); + Polynomial<3> U(2, 6, 5, -2); + REQUIRE(S == (P + Q)); + REQUIRE((T + P) == U); + } + + SECTION("opposed") + { + Polynomial<2> P(2, 3, 4); + Polynomial<2> Q = -P; + REQUIRE(Q == Polynomial<2>(-2, -3, -4)); + } + + SECTION("difference") + { + Polynomial<2> P(2, 3, 4); + Polynomial<2> Q(3, 4, 5); + Polynomial<2> D(-1, -1, -1); + REQUIRE(D == (P - Q)); + Polynomial<3> R(2, 3, 4, 1); + REQUIRE(D == (P - Q)); + REQUIRE((P - R) == Polynomial<3>{0, 0, 0, -1}); + R -= P; + REQUIRE(R == Polynomial<3>(0, 0, 0, 1)); + } + + SECTION("product_by_scalar") + { + Polynomial<2> P(2, 3, 4); + Polynomial<2> M(6, 9, 12); + REQUIRE(M == (P * 3)); + REQUIRE(M == (3 * P)); + } + + SECTION("product") + { + Polynomial<2> P(2, 3, 4); + Polynomial<3> Q(1, 2, -1, 1); + Polynomial<4> R; + Polynomial<5> S; + R = P; + S = P; + S *= Q; + REQUIRE(Polynomial<5>(2, 7, 8, 7, -1, 4) == (P * Q)); + REQUIRE(Polynomial<5>(2, 7, 8, 7, -1, 4) == S); + // REQUIRE_THROWS_AS(R *= Q, AssertError); + } + + SECTION("divide") + { + Polynomial<2> P(1, 0, 1); + Polynomial<1> Q(0, 1); + Polynomial<1> Q1(0, 1); + + Polynomial<2> R; + Polynomial<2> S; + REQUIRE(P.realDegree() == 2); + REQUIRE(Q.realDegree() == 1); + REQUIRE(Q1.realDegree() == 1); + + divide(P, Q1, R, S); + REQUIRE(Polynomial<2>{1, 0, 0} == S); + REQUIRE(Polynomial<2>{0, 1, 0} == R); + Polynomial<3> P2(5, 1, 3, 2); + Polynomial<2> Q2(1, 0, 1); + + Polynomial<3> R2; + Polynomial<3> S2; + + divide(P2, Q2, R2, S2); + REQUIRE(Polynomial<3>{3, 2, 0, 0} == R2); + REQUIRE(Polynomial<3>{2, -1, 0, 0} == S2); + } + + SECTION("evaluation") + { + Polynomial<2> P(2, -3, 4); + REQUIRE(P(3) == 29); + } + + SECTION("primitive") + { + Polynomial<2> P(2, -3, 4); + TinyVector<4> coefs = zero; + Polynomial<3> Q(coefs); + Q = primitive(P); + Polynomial<3> R(0, 2, -3. / 2, 4. / 3); + REQUIRE(Q == R); + } + + SECTION("integrate") + { + Polynomial<2> P(2, -3, 3); + double xinf = -1; + double xsup = 1; + double result = integrate(P, xinf, xsup); + REQUIRE(result == 6); + result = symmetricIntegrate(P, 2); + REQUIRE(result == 24); + } + + SECTION("derivative") + { + Polynomial<2> P(2, -3, 3); + Polynomial<1> Q = derivative(P); + REQUIRE(Q == Polynomial<1>(-3, 6)); + + Polynomial<0> P2(3); + + Polynomial<0> R(0); + REQUIRE(derivative(P2) == R); + } + + SECTION("affectation") + { + Polynomial<2> Q(2, -3, 3); + Polynomial<4> R(2, -3, 3, 0, 0); + Polynomial<4> P(0, 1, 2, 3, 3); + P = Q; + REQUIRE(P == R); + } + + SECTION("affectation addition") + { + Polynomial<2> Q(2, -3, 3); + Polynomial<4> R(2, -2, 5, 3, 3); + Polynomial<4> P(0, 1, 2, 3, 3); + P += Q; + REQUIRE(P == R); + } + + SECTION("power") + { + Polynomial<2> P(2, -3, 3); + Polynomial<4> R(4, -12, 21, -18, 9); + Polynomial<1> Q(0, 2); + Polynomial<2> S = Q.pow<2>(2); + REQUIRE(P.pow<2>(2) == R); + REQUIRE(S == Polynomial<2>(0, 0, 4)); + } + + SECTION("composition") + { + Polynomial<2> P(2, -3, 3); + Polynomial<1> Q(0, 2); + Polynomial<2> R(2, -1, 3); + Polynomial<2> S(1, 2, 2); + REQUIRE(P.compose(Q) == Polynomial<2>(2, -6, 12)); + REQUIRE(P.compose2(Q) == Polynomial<2>(2, -6, 12)); + REQUIRE(R(S) == Polynomial<4>(4, 10, 22, 24, 12)); + } + + SECTION("Lagrange polynomial") + { + Polynomial<1> S(0.5, -0.5); + Polynomial<1> Q; + Q = lagrangePolynomial<1>(TinyVector<2>{-1, 1}, 0); + REQUIRE(S == Q); + Polynomial<2> P(0, -0.5, 0.5); + Polynomial<2> R; + R = lagrangePolynomial<2>(TinyVector<3>{-1, 0, 1}, 0); + REQUIRE(R == P); + const std::array<Polynomial<2>, 3> basis = lagrangeBasis(TinyVector<3>{-1, 0, 1}); + REQUIRE(lagrangeToCanonical(TinyVector<3>{1, 0, 1}, basis) == Polynomial<2>(TinyVector<3>{0, 0, 1})); + } +} diff --git a/tests/test_PolynomialBasis.cpp b/tests/test_PolynomialBasis.cpp new file mode 100644 index 0000000000000000000000000000000000000000..446a7278a36c5358b93069f5f0d5dc10ac00aa5a --- /dev/null +++ b/tests/test_PolynomialBasis.cpp @@ -0,0 +1,45 @@ +#include <catch2/catch_test_macros.hpp> + +#include <Kokkos_Core.hpp> + +#include <utils/PugsAssert.hpp> +#include <utils/Types.hpp> + +#include <algebra/TinyMatrix.hpp> +#include <analysis/Polynomial.hpp> +#include <analysis/PolynomialBasis.hpp> + +// Instantiate to ensure full coverage is performed +template class Polynomial<0>; +template class Polynomial<1>; +template class Polynomial<2>; +template class PolynomialBasis<2>; + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("PolynomialBasis", "[analysis]") +{ + SECTION("construction") + { + REQUIRE_NOTHROW(PolynomialBasis<2>{}); + } + SECTION("size") + { + PolynomialBasis<2> B; + REQUIRE(B.size() == 3); + REQUIRE(B.degree() == 2); + } + SECTION("build") + { + PolynomialBasis<2> B; + REQUIRE(B.displayType() == "undefined"); + B.build(BasisType::canonical); + REQUIRE(B.elements()[1] == Polynomial<2>{0, 1, 0}); + REQUIRE(B.elements()[2] == Polynomial<2>{0, 0, 1}); + PolynomialBasis<2> C; + C.build(BasisType::taylor); + REQUIRE(B.elements()[1] == C.elements()[1]); + C.build(BasisType::taylor, 1); + REQUIRE(C.elements()[2] == Polynomial<2>{1, -2, 1}); + } +} diff --git a/tests/test_PolynomialP.cpp b/tests/test_PolynomialP.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bd6d4a82d57f16447ffdb70bba302eee1e9267e5 --- /dev/null +++ b/tests/test_PolynomialP.cpp @@ -0,0 +1,369 @@ +#include <Kokkos_Core.hpp> +#include <algebra/TinyMatrix.hpp> +#include <analysis/GaussLegendreQuadratureDescriptor.hpp> +#include <analysis/GaussQuadratureDescriptor.hpp> +#include <analysis/PolynomialP.hpp> +#include <analysis/QuadratureManager.hpp> +#include <analysis/SquareGaussQuadrature.hpp> +#include <analysis/TaylorPolynomial.hpp> +#include <catch2/catch_approx.hpp> +#include <catch2/catch_test_macros.hpp> +#include <utils/PugsAssert.hpp> +#include <utils/Types.hpp> + +// Instantiate to ensure full coverage is performed +template class PolynomialP<0, 2>; +template class PolynomialP<1, 2>; +template class PolynomialP<2, 2>; +template class PolynomialP<3, 2>; + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("PolynomialP", "[analysis]") +{ + SECTION("construction") + { + TinyVector<6> coef(1, 2, 3, 4, 5, 6); + REQUIRE_NOTHROW(PolynomialP<2, 2>(coef)); + } + + SECTION("degree") + { + TinyVector<3> coef(1, 2, 3); + PolynomialP<1, 2> P(coef); + REQUIRE(P.degree() == 1); + REQUIRE(P.dim() == 2); + } + + SECTION("equality") + { + TinyVector<6> coef(1, 2, 3, 4, 5, 6); + TinyVector<3> coef2(1, 2, 3); + TinyVector<6> coef3(1, 2, 3, 3, 3, 3); + PolynomialP<2, 2> P(coef); + PolynomialP<2, 2> Q(coef); + PolynomialP<2, 2> R(coef3); + + REQUIRE(P == Q); + REQUIRE(P != R); + } + + SECTION("addition") + { + TinyVector<6> coef(1, 2, 3, 4, 5, 6); + TinyVector<6> coef2(1, 2, 3, -2, -1, -3); + TinyVector<6> coef3(2, 4, 6, 2, 4, 3); + PolynomialP<2, 2> P(coef); + PolynomialP<2, 2> Q(coef2); + PolynomialP<2, 2> R(coef3); + REQUIRE(R == (P + Q)); + REQUIRE((P + Q) == R); + } + + SECTION("opposed") + { + TinyVector<6> coef(1, 2, 3, 4, 5, 6); + TinyVector<6> coef2(-1, -2, -3, -4, -5, -6); + PolynomialP<2, 2> P(coef); + REQUIRE(-P == PolynomialP<2, 2>(coef2)); + } + + SECTION("difference") + { + TinyVector<6> coef(1, 2, 3, 4, 5, 6); + TinyVector<6> coef2(1, 2, 3, -2, -1, -3); + TinyVector<6> coef3(0, 0, 0, 6, 6, 9); + PolynomialP<2, 2> P(coef); + PolynomialP<2, 2> Q(coef2); + PolynomialP<2, 2> R(coef3); + R = P - Q; + REQUIRE(R == PolynomialP<2, 2>(coef3)); + } + + SECTION("product_by_scalar") + { + TinyVector<6> coef(1, 2, 3, 4, 5, 6); + TinyVector<6> coef2(2, 4, 6, 8, 10, 12); + PolynomialP<2, 2> P(coef); + PolynomialP<2, 2> Q(coef2); + REQUIRE(Q == (P * 2)); + REQUIRE(Q == (2 * P)); + } + + SECTION("access_coef") + { + TinyVector<6> coef(1, -2, 10, 7, 2, 9); + TinyVector<10> coef2(2, -4, -1, -3, 3, -5, -6, 0, 1, 7); + TinyVector<10> coef3(2, -4, -1, -3, 3, -5, -6, 0, 1, 7); + TinyVector<20> coef4(2, -4, -1, -3, 3, -5, -6, -2, 1, 7, 3, -2, 1, 2.5, 6, -9, 0.5, 4, -5, -8); + PolynomialP<2, 2> P(coef); + PolynomialP<3, 2> Q(coef2); + PolynomialP<2, 3> R(coef3); + PolynomialP<3, 3> S(coef4); + TinyVector<2, size_t> relative_coef(1, 1); + TinyVector<2, size_t> relative_coef2(1, 2); + TinyVector<3, size_t> relative_coef3(1, 0, 1); + TinyVector<3, size_t> relative_coef3b(0, 0, 2); + TinyVector<3, size_t> relative_coef4(1, 1, 1); + TinyVector<3, size_t> relative_coef5(0, 2, 1); + REQUIRE(P[relative_coef] == 2); + REQUIRE(Q[relative_coef2] == 1); + REQUIRE(Q[relative_coef] == 3); + REQUIRE(R[relative_coef3] == -6); + REQUIRE(R[relative_coef3b] == 7); + REQUIRE(S[relative_coef4] == 6); + REQUIRE(S[relative_coef5] == 4); + } + + SECTION("evaluation") + { + TinyVector<6> coef(1, -2, 10, 7, 2, 9); + TinyVector<10> coef2(2, -4, -1, -3, 3, -5, -6, 0, 1, 7); + TinyVector<10> coef3(2, -4, -1, -3, 3, -5, -6, 0, 1, 7); + PolynomialP<2, 2> P(coef); + PolynomialP<3, 2> Q(coef2); + PolynomialP<2, 3> R(coef3); + TinyVector<6> coefx(1, -2, 0, 7, 0, 0); + TinyVector<6> coefy(2, 0, -2, 0, 0, 7); + TinyVector<2> pos(1, -1); + TinyVector<2> pos2(-1, 2); + TinyVector<3> pos3(-1, 2, 1); + PolynomialP<2, 2> Px(coefx); + PolynomialP<2, 2> Py(coefy); + TinyVector<10> coef3x(2, -4, 0, 0, 3, 0, 0, 0, 0, 0); + TinyVector<10> coef3y(2, 0, -1, 0, 0, 0, 0, 1, 0, 0); + TinyVector<10> coef3z(2, 0, 0, -3, 0, 0, 0, 0, 0, 7); + PolynomialP<2, 3> Rx(coef3x); + PolynomialP<2, 3> Ry(coef3y); + PolynomialP<2, 3> Rz(coef3z); + REQUIRE(Px(pos) == 6); + REQUIRE(Py(pos) == 11); + REQUIRE(Px(pos2) == 10); + REQUIRE(P(pos2) == 62); + REQUIRE(Q(pos) == -24); + REQUIRE(Rx(pos3) == 9); + REQUIRE(Ry(pos3) == 4); + REQUIRE(Rz(pos3) == 6); + REQUIRE(R(pos3) == 29); + } + + SECTION("derivation") + { + TinyVector<6> coef(1, -2, 10, 7, 2, 9); + TinyVector<6> coef2(-2, 14, 2, 0, 0, 0); + TinyVector<6> coef3(10, 2, 18, 0, 0, 0); + TinyVector<10> coef4(2, -4, -1, -3, 3, -5, -6, 1, 1, 7); + TinyVector<10> coef5(-1, -5, 2, 1, 0, 0, 0, 0, 0, 0); + PolynomialP<2, 2> P(coef); + PolynomialP<2, 2> Q(coef2); + PolynomialP<2, 2> R(coef3); + PolynomialP<2, 3> S(coef4); + PolynomialP<2, 3> T(coef5); + + REQUIRE(Q == P.derivative(0)); + REQUIRE(R == P.derivative(1)); + REQUIRE(T == S.derivative(1)); + } + + SECTION("integrate") + { + TinyVector<6> coef(1, -2, 10, 7, 2, 9); + TinyVector<10> coef2(1, -2, 10, 7, 2, 9, 0, 0, 0, 1); + std::array<TinyVector<2>, 4> positions; + std::array<TinyVector<2>, 3> positions2; + std::array<TinyVector<2>, 2> positions4; + std::array<TinyVector<2>, 4> positions3; + std::array<TinyVector<2>, 4> positions5; + positions[0] = TinyVector<2>{0, 0}; + positions[1] = TinyVector<2>{0, 0.5}; + positions[2] = TinyVector<2>{0.3, 0.7}; + positions[3] = TinyVector<2>{0.4, 0.1}; + positions2[0] = TinyVector<2>{0, 0}; + positions2[1] = TinyVector<2>{0, 0.5}; + positions2[2] = TinyVector<2>{0.3, 0.7}; + positions4[0] = TinyVector<2>{0, 0.5}; + positions4[1] = TinyVector<2>{0.3, 0.7}; + positions3[0] = TinyVector<2>{0, 0}; + positions3[1] = TinyVector<2>{1, 0}; + positions3[2] = TinyVector<2>{1, 1}; + positions3[3] = TinyVector<2>{0, 1}; + positions5[0] = TinyVector<2>{0, 0}; + positions5[1] = TinyVector<2>{1, 0}; + positions5[2] = TinyVector<2>{1, 3}; + positions5[3] = TinyVector<2>{1, 1}; + + PolynomialP<2, 2> P(coef); + PolynomialP<3, 2> Q(coef2); + auto p1 = [](const TinyVector<2>& X) { + const double x = X[0]; + const double y = X[1]; + return 1 - 2. * x + 10 * y + 7 * x * x + 2 * x * y + 9 * y * y; + }; + auto q1 = [](const TinyVector<2>& X) { + const double x = X[0]; + const double y = X[1]; + return 1 - 2. * x + 10 * y + 7 * x * x + 2 * x * y + 9 * y * y + y * y * y; + }; + const QuadratureFormula<2>& l3 = + QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor(4)); + auto point_list = l3.pointList(); + auto weight_list = l3.weightList(); + SquareTransformation<2> s{positions[0], positions[1], positions[2], positions[3]}; + auto value = weight_list[0] * s.jacobianDeterminant(point_list[0]) * q1(s(point_list[0])); + for (size_t i = 1; i < weight_list.size(); ++i) { + value += weight_list[i] * s.jacobianDeterminant(point_list[i]) * q1(s(point_list[i])); + } + // const QuadratureFormula<2>& l3bis = + // QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor(4)); + // auto point_listbis = l3bis.pointList(); + // auto weight_listbis = l3bis.weightList(); + // auto valuebis = weight_listbis[0] * s.jacobianDeterminant(point_listbis[0]) * p1(s(point_listbis[0])); + // for (size_t i = 1; i < weight_listbis.size(); ++i) { + // valuebis += weight_listbis[i] * s.jacobianDeterminant(point_listbis[i]) * p1(s(point_listbis[i])); + // } + const QuadratureFormula<2>& t3 = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(3)); + auto point_list2 = t3.pointList(); + auto weight_list2 = t3.weightList(); + TriangleTransformation<2> t{positions2[0], positions2[1], positions2[2]}; + auto value2 = weight_list2[0] * p1(t(point_list2[0])); + for (size_t i = 1; i < weight_list2.size(); ++i) { + value2 += weight_list2[i] * p1(t(point_list2[i])); + } + value2 *= t.jacobianDeterminant(); + const QuadratureFormula<1>& l1 = QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(2)); + const LineTransformation<2> u{positions4[0], positions4[1]}; + double value4 = 0.; + for (size_t i = 0; i < l1.numberOfPoints(); ++i) { + value4 += l1.weight(i) * u.velocityNorm() * p1(u(l1.point(i))); + } + SquareTransformation<2> s3{positions3[0], positions3[1], positions3[2], positions3[3]}; + auto value3 = weight_list[0] * s3.jacobianDeterminant(point_list[0]) * p1(s3(point_list[0])); + for (size_t i = 1; i < weight_list.size(); ++i) { + value3 += weight_list[i] * s3.jacobianDeterminant(point_list[i]) * p1(s3(point_list[i])); + } + + REQUIRE(value == Catch::Approx(integrate(Q, positions))); + REQUIRE(value2 == Catch::Approx(integrate(P, positions2))); + REQUIRE(value3 == Catch::Approx(integrate(P, positions3))); + REQUIRE(value4 == Catch::Approx(integrate(P, positions4))); + // REQUIRE(valuebis == Catch::Approx(integrate(P, positions))); + // REQUIRE(valuebis == value); + } + + // // SECTION("product") + // { + // Polynomial<2> P(2, 3, 4); + // Polynomial<3> Q(1, 2, -1, 1); + // Polynomial<4 > R; + // Polynomial<5> S; + // R = P; + // S = P; + // S *= Q; + // REQUIRE(Polynomial<5>(2, 7, 8, 7, -1, 4) == (P * Q)); + // REQUIRE(Polynomial<5>(2, 7, 8, 7, -1, 4) == S); + // // REQUIRE_THROWS_AS(R *= Q, AssertError); + // } + + // SECTION("divide") + // { + // Polynomial<2> P(1, 0, 1); + // Polynomial<1> Q(0, 1); + // Polynomial<1> Q1(0, 1); + + // Polynomial<2> R; + // Polynomial<2> S; + // REQUIRE(P.realDegree() == 2); + // REQUIRE(Q.realDegree() == 1); + // REQUIRE(Q1.realDegree() == 1); + + // divide(P, Q1, R, S); + // REQUIRE(Polynomial<2>{1, 0, 0} == S); + // REQUIRE(Polynomial<2>{0, 1, 0} == R); + // } + + // SECTION("primitive") + // { + // Polynomial<2> P(2, -3, 4); + // TinyVector<4> coefs = zero; + // Polynomial<3> Q(coefs); + // Q = primitive(P); + // Polynomial<3> R(0, 2, -3. / 2, 4. / 3); + // REQUIRE(Q == R); + // } + + // SECTION("integrate") + // { + // Polynomial<2> P(2, -3, 3); + // double xinf = -1; + // double xsup = 1; + // double result = integrate(P, xinf, xsup); + // REQUIRE(result == 6); + // result = symmetricIntegrate(P, 2); + // REQUIRE(result == 24); + // } + + // SECTION("derivative") + // { + // Polynomial<2> P(2, -3, 3); + // Polynomial<1> Q = derivative(P); + // REQUIRE(Q == Polynomial<1>(-3, 6)); + + // Polynomial<0> P2(3); + + // Polynomial<0> R(0); + // REQUIRE(derivative(P2) == R); + // } + + // SECTION("affectation") + // { + // Polynomial<2> Q(2, -3, 3); + // Polynomial<4> R(2, -3, 3, 0, 0); + // Polynomial<4> P(0, 1, 2, 3, 3); + // P = Q; + // REQUIRE(P == R); + // } + + // SECTION("affectation addition") + // { + // Polynomial<2> Q(2, -3, 3); + // Polynomial<4> R(2, -2, 5, 3, 3); + // Polynomial<4> P(0, 1, 2, 3, 3); + // P += Q; + // REQUIRE(P == R); + // } + + // SECTION("power") + // { + // Polynomial<2> P(2, -3, 3); + // Polynomial<4> R(4, -12, 21, -18, 9); + // Polynomial<1> Q(0, 2); + // Polynomial<2> S = Q.pow<2>(2); + // REQUIRE(P.pow<2>(2) == R); + // REQUIRE(S == Polynomial<2>(0, 0, 4)); + // } + + // SECTION("composition") + // { + // Polynomial<2> P(2, -3, 3); + // Polynomial<1> Q(0, 2); + // Polynomial<2> R(2, -1, 3); + // Polynomial<2> S(1, 2, 2); + // REQUIRE(P.compose(Q) == Polynomial<2>(2, -6, 12)); + // REQUIRE(P.compose2(Q) == Polynomial<2>(2, -6, 12)); + // REQUIRE(R(S) == Polynomial<4>(4, 10, 22, 24, 12)); + // } + + // SECTION("Lagrange polynomial") + // { + // Polynomial<1> S(0.5, -0.5); + // Polynomial<1> Q; + // Q = lagrangePolynomial<1>(TinyVector<2>{-1, 1}, 0); + // REQUIRE(S == Q); + // Polynomial<2> P(0, -0.5, 0.5); + // Polynomial<2> R; + // R = lagrangePolynomial<2>(TinyVector<3>{-1, 0, 1}, 0); + // REQUIRE(R == P); + // const std::array<Polynomial<2>, 3> basis = lagrangeBasis(TinyVector<3>{-1, 0, 1}); + // REQUIRE(lagrangeToCanonical(TinyVector<3>{1, 0, 1}, basis) == Polynomial<2>(TinyVector<3>{0, 0, 1})); + // } +} diff --git a/tests/test_TaylorPolynomial.cpp b/tests/test_TaylorPolynomial.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4d9149232202adf49de64107fb3bfbe8e20eed22 --- /dev/null +++ b/tests/test_TaylorPolynomial.cpp @@ -0,0 +1,357 @@ +#include <Kokkos_Core.hpp> +#include <algebra/TinyMatrix.hpp> +#include <analysis/GaussQuadratureDescriptor.hpp> +#include <analysis/QuadratureManager.hpp> +#include <analysis/SquareGaussQuadrature.hpp> +#include <analysis/TaylorPolynomial.hpp> +#include <catch2/catch_approx.hpp> +#include <catch2/catch_test_macros.hpp> +#include <utils/PugsAssert.hpp> +#include <utils/Types.hpp> + +// Instantiate to ensure full coverage is performed +template class TaylorPolynomial<0, 2>; +template class TaylorPolynomial<1, 2>; +template class TaylorPolynomial<2, 2>; +template class TaylorPolynomial<3, 2>; + +// clazy:excludeall=non-pod-global-static +TinyVector<2> x0{1, -1}; +TinyVector<3> x1{1, -1, 1}; + +TEST_CASE("TaylorPolynomial", "[analysis]") +{ + SECTION("construction") + { + TinyVector<6> coef(1, 2, 3, 4, 5, 6); + REQUIRE_NOTHROW(TaylorPolynomial<2, 2>(coef, x0)); + } + + SECTION("degree") + { + TinyVector<3> coef(1, 2, 3); + TaylorPolynomial<1, 2> P(coef, x0); + REQUIRE(P.degree() == 1); + REQUIRE(P.dim() == 2); + } + + SECTION("equality") + { + TinyVector<6> coef(1, 2, 3, 4, 5, 6); + TinyVector<3> coef2(1, 2, 3); + TinyVector<6> coef3(1, 2, 3, 3, 3, 3); + TaylorPolynomial<2, 2> P(coef, x0); + TaylorPolynomial<2, 2> Q(coef, x0); + TaylorPolynomial<2, 2> R(coef3, x0); + + REQUIRE(P == Q); + REQUIRE(P != R); + } + + SECTION("addition") + { + TinyVector<6> coef(1, 2, 3, 4, 5, 6); + TinyVector<6> coef2(1, 2, 3, -2, -1, -3); + TinyVector<6> coef3(2, 4, 6, 2, 4, 3); + TaylorPolynomial<2, 2> P(coef, x0); + TaylorPolynomial<2, 2> Q(coef2, x0); + TaylorPolynomial<2, 2> R(coef3, x0); + REQUIRE(R == (P + Q)); + REQUIRE((P + Q) == R); + } + + SECTION("opposed") + { + TinyVector<6> coef(1, 2, 3, 4, 5, 6); + TinyVector<6> coef2(-1, -2, -3, -4, -5, -6); + TaylorPolynomial<2, 2> P(coef, x0); + REQUIRE(-P == TaylorPolynomial<2, 2>(coef2, x0)); + } + + SECTION("difference") + { + TinyVector<6> coef(1, 2, 3, 4, 5, 6); + TinyVector<6> coef2(1, 2, 3, -2, -1, -3); + TinyVector<6> coef3(0, 0, 0, 6, 6, 9); + TaylorPolynomial<2, 2> P(coef, x0); + TaylorPolynomial<2, 2> Q(coef2, x0); + TaylorPolynomial<2, 2> R(coef3, x0); + R = P - Q; + REQUIRE(R == TaylorPolynomial<2, 2>(coef3, x0)); + } + + SECTION("product_by_scalar") + { + TinyVector<6> coef(1, 2, 3, 4, 5, 6); + TinyVector<6> coef2(2, 4, 6, 8, 10, 12); + TaylorPolynomial<2, 2> P(coef, x0); + TaylorPolynomial<2, 2> Q(coef2, x0); + REQUIRE(Q == (P * 2)); + REQUIRE(Q == (2 * P)); + } + + SECTION("access_coef") + { + TinyVector<6> coef(1, -2, 10, 7, 2, 9); + TinyVector<10> coef2(2, -4, -1, -3, 3, -5, -6, 0, 1, 7); + TinyVector<10> coef3(2, -4, -1, -3, 3, -5, -6, 0, 1, 7); + TinyVector<20> coef4(2, -4, -1, -3, 3, -5, -6, -2, 1, 7, 3, -2, 1, 2.5, 6, -9, 0.5, 4, -5, -8); + TaylorPolynomial<2, 2> P(coef, x0); + TaylorPolynomial<3, 2> Q(coef2, x0); + TaylorPolynomial<2, 3> R(coef3, x1); + TaylorPolynomial<3, 3> S(coef4, x1); + TinyVector<2, size_t> relative_coef(1, 1); + TinyVector<2, size_t> relative_coef2(1, 2); + TinyVector<3, size_t> relative_coef3(1, 0, 1); + TinyVector<3, size_t> relative_coef3b(0, 0, 2); + TinyVector<3, size_t> relative_coef4(1, 1, 1); + TinyVector<3, size_t> relative_coef5(0, 2, 1); + REQUIRE(P[relative_coef] == 2); + REQUIRE(Q[relative_coef2] == 1); + REQUIRE(Q[relative_coef] == 3); + REQUIRE(R[relative_coef3] == -6); + REQUIRE(R[relative_coef3b] == 7); + REQUIRE(S[relative_coef4] == 6); + REQUIRE(S[relative_coef5] == 4); + } + + SECTION("evaluation") + { + TinyVector<6> coef(1, -2, 10, 7, 2, 9); + TinyVector<10> coef2(2, -4, -1, -3, 3, -5, -6, 0, 1, 7); + TinyVector<10> coef3(2, -4, -1, -3, 3, -5, -6, 0, 1, 7); + TaylorPolynomial<2, 2> P(coef, x0); + TaylorPolynomial<3, 2> Q(coef2, x0); + TaylorPolynomial<2, 3> R(coef3, x1); + TinyVector<6> coefx(1, -2, 0, 7, 0, 0); + TinyVector<6> coefy(2, 0, -2, 0, 0, 7); + TinyVector<2> pos(1, -1); + TinyVector<2> pos2(-1, 2); + TinyVector<3> pos3(-1, 2, 1); + + TaylorPolynomial<2, 2> Px(coefx, x0); + TaylorPolynomial<2, 2> Py(coefy, x0); + REQUIRE(Px(pos) == 1); + REQUIRE(Py(pos) == 2); + REQUIRE(Px(pos2) == 33); + REQUIRE(P(pos2) == 132); + REQUIRE(Q(pos) == 2); + REQUIRE(R(pos3) == 49); + } + + SECTION("derivation") + { + TinyVector<6> coef(1, -2, 10, 7, 2, 9); + TinyVector<6> coef2(-2, 14, 2, 0, 0, 0); + TinyVector<6> coef3(10, 2, 18, 0, 0, 0); + TinyVector<10> coef4(2, -4, -1, -3, 3, -5, -6, 1, 1, 7); + TinyVector<10> coef5(-1, -5, 2, 1, 0, 0, 0, 0, 0, 0); + TaylorPolynomial<2, 2> P(coef, x0); + TaylorPolynomial<2, 2> Q(coef2, x0); + TaylorPolynomial<2, 2> R(coef3, x0); + TaylorPolynomial<2, 3> S(coef4, x1); + TaylorPolynomial<2, 3> T(coef5, x1); + + REQUIRE(Q == P.derivative(0)); + REQUIRE(R == P.derivative(1)); + REQUIRE(T == S.derivative(1)); + } + + SECTION("integrate") + { + TinyVector<6> coef(1, -2, 10, 7, 2, 9); + TinyVector<10> coef2(1, -2, 10, 7, 2, 9, 0, 0, 0, 1); + std::array<TinyVector<2>, 4> positions; + std::array<TinyVector<2>, 3> positions2; + std::array<TinyVector<2>, 4> positions3; + std::array<TinyVector<2>, 2> positions4; + positions[0] = TinyVector<2>{0, 0}; + positions[1] = TinyVector<2>{0, 0.5}; + positions[2] = TinyVector<2>{0.3, 0.7}; + positions[3] = TinyVector<2>{0.4, 0.1}; + positions2[0] = TinyVector<2>{0, 0}; + positions2[1] = TinyVector<2>{0, 0.5}; + positions2[2] = TinyVector<2>{0.3, 0.7}; + positions3[0] = TinyVector<2>{0, 0}; + positions3[1] = TinyVector<2>{1, 0}; + positions3[2] = TinyVector<2>{1, 1}; + positions3[3] = TinyVector<2>{0, 1}; + positions4[0] = TinyVector<2>{0, 0.5}; + positions4[1] = TinyVector<2>{0.3, 0.7}; + TaylorPolynomial<2, 2> P(coef, x0); + TaylorPolynomial<2, 2> Q(coef, zero); + TaylorPolynomial<3, 2> R(coef2, x0); + auto p1 = [](const TinyVector<2>& X) { + const double x = X[0]; + const double y = X[1]; + return 1 - 2. * (x - 1.) + 10 * (y + 1) + 7 * (x - 1.) * (x - 1) + 2 * (x - 1) * (y + 1) + 9 * (y + 1) * (y + 1) + + (y + 1) * (y + 1) * (y + 1); + }; + auto p2 = [](const TinyVector<2>& X) { + const double x = X[0]; + const double y = X[1]; + return 1 - 2. * (x - 1.) + 10 * (y + 1) + 7 * (x - 1.) * (x - 1) + 2 * (x - 1) * (y + 1) + 9 * (y + 1) * (y + 1); + }; + auto q1 = [](const TinyVector<2>& X) { + const double x = X[0]; + const double y = X[1]; + return 1 - 2. * x + 10 * y + 7 * x * x + 2 * x * y + 9 * y * y; + }; + const QuadratureFormula<2>& l3 = + QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor(4)); + auto point_list = l3.pointList(); + auto weight_list = l3.weightList(); + SquareTransformation<2> s{positions[0], positions[1], positions[2], positions[3]}; + auto value = weight_list[0] * s.jacobianDeterminant(point_list[0]) * p1(s(point_list[0])); + for (size_t i = 1; i < weight_list.size(); ++i) { + value += weight_list[i] * s.jacobianDeterminant(point_list[i]) * p1(s(point_list[i])); + } + auto valuebis = weight_list[0] * s.jacobianDeterminant(point_list[0]) * q1(s(point_list[0])); + for (size_t i = 1; i < weight_list.size(); ++i) { + valuebis += weight_list[i] * s.jacobianDeterminant(point_list[i]) * q1(s(point_list[i])); + } + SquareTransformation<2> s3{positions3[0], positions3[1], positions3[2], positions3[3]}; + auto value3 = weight_list[0] * s3.jacobianDeterminant(point_list[0]) * p2(s3(point_list[0])); + for (size_t i = 1; i < weight_list.size(); ++i) { + value3 += weight_list[i] * s3.jacobianDeterminant(point_list[i]) * p2(s3(point_list[i])); + } + const QuadratureFormula<2>& t3 = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(3)); + auto point_list2 = t3.pointList(); + auto weight_list2 = t3.weightList(); + TriangleTransformation<2> t{positions2[0], positions2[1], positions2[2]}; + auto value2 = weight_list2[0] * p2(t(point_list2[0])); + for (size_t i = 1; i < weight_list2.size(); ++i) { + value2 += weight_list2[i] * p2(t(point_list2[i])); + } + value2 *= t.jacobianDeterminant(); + const QuadratureFormula<1>& l1 = QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor(2)); + const LineTransformation<2> u{positions4[0], positions4[1]}; + double value4 = 0.; + for (size_t i = 0; i < l1.numberOfPoints(); ++i) { + value4 += l1.weight(i) * u.velocityNorm() * p2(u(l1.point(i))); + } + + REQUIRE(value == Catch::Approx(integrate(R, positions))); + REQUIRE(valuebis == Catch::Approx(integrate(Q, positions))); + REQUIRE(value2 == Catch::Approx(integrate(P, positions2))); + REQUIRE(value3 == Catch::Approx(integrate(P, positions3))); + REQUIRE(value4 == Catch::Approx(integrate(P, positions4))); + } + + // // SECTION("product") + // { + // Polynomial<2> P(2, 3, 4); + // Polynomial<3> Q(1, 2, -1, 1); + // Polynomial<4 > R; + // Polynomial<5> S; + // R = P; + // S = P; + // S *= Q; + // REQUIRE(Polynomial<5>(2, 7, 8, 7, -1, 4) == (P * Q)); + // REQUIRE(Polynomial<5>(2, 7, 8, 7, -1, 4) == S); + // // REQUIRE_THROWS_AS(R *= Q, AssertError); + // } + + // SECTION("divide") + // { + // Polynomial<2> P(1, 0, 1); + // Polynomial<1> Q(0, 1); + // Polynomial<1> Q1(0, 1); + + // Polynomial<2> R; + // Polynomial<2> S; + // REQUIRE(P.realDegree() == 2); + // REQUIRE(Q.realDegree() == 1); + // REQUIRE(Q1.realDegree() == 1); + + // divide(P, Q1, R, S); + // REQUIRE(Polynomial<2>{1, 0, 0} == S); + // REQUIRE(Polynomial<2>{0, 1, 0} == R); + // } + + // SECTION("primitive") + // { + // Polynomial<2> P(2, -3, 4); + // TinyVector<4> coefs = zero; + // Polynomial<3> Q(coefs); + // Q = primitive(P); + // Polynomial<3> R(0, 2, -3. / 2, 4. / 3); + // REQUIRE(Q == R); + // } + + // SECTION("integrate") + // { + // Polynomial<2> P(2, -3, 3); + // double xinf = -1; + // double xsup = 1; + // double result = integrate(P, xinf, xsup); + // REQUIRE(result == 6); + // result = symmetricIntegrate(P, 2); + // REQUIRE(result == 24); + // } + + // SECTION("derivative") + // { + // Polynomial<2> P(2, -3, 3); + // Polynomial<1> Q = derivative(P); + // REQUIRE(Q == Polynomial<1>(-3, 6)); + + // Polynomial<0> P2(3); + + // Polynomial<0> R(0); + // REQUIRE(derivative(P2) == R); + // } + + // SECTION("affectation") + // { + // Polynomial<2> Q(2, -3, 3); + // Polynomial<4> R(2, -3, 3, 0, 0); + // Polynomial<4> P(0, 1, 2, 3, 3); + // P = Q; + // REQUIRE(P == R); + // } + + // SECTION("affectation addition") + // { + // Polynomial<2> Q(2, -3, 3); + // Polynomial<4> R(2, -2, 5, 3, 3); + // Polynomial<4> P(0, 1, 2, 3, 3); + // P += Q; + // REQUIRE(P == R); + // } + + // SECTION("power") + // { + // Polynomial<2> P(2, -3, 3); + // Polynomial<4> R(4, -12, 21, -18, 9); + // Polynomial<1> Q(0, 2); + // Polynomial<2> S = Q.pow<2>(2); + // REQUIRE(P.pow<2>(2) == R); + // REQUIRE(S == Polynomial<2>(0, 0, 4)); + // } + + // SECTION("composition") + // { + // Polynomial<2> P(2, -3, 3); + // Polynomial<1> Q(0, 2); + // Polynomial<2> R(2, -1, 3); + // Polynomial<2> S(1, 2, 2); + // REQUIRE(P.compose(Q) == Polynomial<2>(2, -6, 12)); + // REQUIRE(P.compose2(Q) == Polynomial<2>(2, -6, 12)); + // REQUIRE(R(S) == Polynomial<4>(4, 10, 22, 24, 12)); + // } + + // SECTION("Lagrange polynomial") + // { + // Polynomial<1> S(0.5, -0.5); + // Polynomial<1> Q; + // Q = lagrangePolynomial<1>(TinyVector<2>{-1, 1}, 0); + // REQUIRE(S == Q); + // Polynomial<2> P(0, -0.5, 0.5); + // Polynomial<2> R; + // R = lagrangePolynomial<2>(TinyVector<3>{-1, 0, 1}, 0); + // REQUIRE(R == P); + // const std::array<Polynomial<2>, 3> basis = lagrangeBasis(TinyVector<3>{-1, 0, 1}); + // REQUIRE(lagrangeToCanonical(TinyVector<3>{1, 0, 1}, basis) == Polynomial<2>(TinyVector<3>{0, 0, 1})); + // } +}