Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
staticmatrixtools.hh 18.48 KiB
// -*- tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
// vi: set et ts=8 sw=4 sts=4:
#ifndef STATIC_MATRIX_TOOL_HH
#define STATIC_MATRIX_TOOL_HH

#include "dune/common/diagonalmatrix.hh"
#include "dune/common/fmatrix.hh"
#include "dune/istl/scaledidmatrix.hh"
#include "dune/istl/bcrsmatrix.hh"
#include "dune/istl/matrixindexset.hh"

class StaticMatrix
{
    public:

        // type promotion (smallest matrix that can hold the sum of two matrices *******************
        template<class MatrixA, class MatrixB>
        struct Promote
        {
            typedef Dune::FieldMatrix<typename MatrixA::field_type, MatrixA::rows, MatrixA::cols> Type;
        };

        template<class Matrix>
        struct Promote<Matrix, Matrix>
        {
            typedef Matrix Type;
        };

        template<typename FieldType, int n>
        struct Promote<Dune::FieldMatrix<FieldType,n,n>, Dune::DiagonalMatrix<FieldType,n> >
        {
            typedef Dune::FieldMatrix<FieldType,n,n> Type;
        };

        template<typename FieldType, int n>
        struct Promote<Dune::DiagonalMatrix<FieldType,n>, Dune::FieldMatrix<FieldType,n,n> >
        {
            typedef Dune::FieldMatrix<FieldType,n,n> Type;
        };

        template<typename FieldType, int n>
        struct Promote<Dune::DiagonalMatrix<FieldType,n>, Dune::ScaledIdentityMatrix<FieldType,n> >
        {
            typedef Dune::DiagonalMatrix<FieldType,n> Type;
        };

        template<typename FieldType, int n>
        struct Promote<Dune::ScaledIdentityMatrix<FieldType,n>, Dune::DiagonalMatrix<FieldType,n> >
        {
            typedef Dune::DiagonalMatrix<FieldType,n> Type;
        };



        // add scalar to matrix diagonal ***********************************************************
        template<class Matrix>
        static void addToDiagonal(Matrix& x, const typename Matrix::field_type a)
        {
            for(int i=0; i<Matrix::rows; ++i)
                x[i][i] += a;
        }

        template <typename FieldType, int n>
//        static void addToDiagonal(Dune::ScaledIdentityMatrix<FieldType,n>& x, const FieldType a)
        // the commented line should NOT be used as it may lead to ambiguities in which case the general method above will be used.
        // an example is the line taken from massassembler.hh (line 83 at the time):
        //   StaticMatrix::addToDiagonal(localMatrix[i][i], values[i] * zi);
        // where values[i] is of type FieldVector<FieldType,1> and zi is a double. This call then does not exactly fit this specialization (without the implicit cast of FV<FT,1>)
        // and hence some wild template voodoo decides which of the templates is to be taken - in this case with a gcc 4.4.5 that was the one above leading to a wrong factor n
        // in the diagonal value
        static void addToDiagonal(Dune::ScaledIdentityMatrix<FieldType,n>& x, const typename Dune::ScaledIdentityMatrix<FieldType,n>::field_type a)
        {
            x.scalar() += a;
        }



        // add scalar times matrix to matrix *******************************************************
        template<class MatrixA, class MatrixB>
        static void axpy(MatrixA& A, const typename MatrixA::field_type a, const MatrixB& B)
        {
            for(size_t i=0; i<B.N(); ++i)
            {
                typename MatrixB::row_type::ConstIterator it=B[i].begin();
                typename MatrixB::row_type::ConstIterator end=B[i].end();
                for(; it!=end; ++it)
                    A[i][it.index()] += a * (*it);
            }
        }

//        template<class MatrixA, class MatrixB>
//        static void axpy(MatrixA& x, const typename MatrixA::field_type a, const MatrixB& y)
//        {
//            for(int i=0; i<MatrixA::rows; ++i)
//                for(int j=0; j<MatrixA::cols; ++j)
//                    x[i][j] += a * y[i][j];
//        }

        template <typename FieldType, int n, class MatrixB>
        static void axpy(Dune::ScaledIdentityMatrix<FieldType,n>& x, const typename Dune::ScaledIdentityMatrix<FieldType,n>::field_type a, const MatrixB& y)
        {
            x.scalar() += a * y[0][0];
        }

        template <typename FieldType, int n, class MatrixB>
        static void axpy(Dune::DiagonalMatrix<FieldType,n>& x, const typename Dune::DiagonalMatrix<FieldType,n>::field_type a, const MatrixB& y)
        {
            for(int i=0; i<n; ++i)
                x.diagonal()[i] += a * y[i][i];
        }

        template <typename FieldType, int n, typename Scalar>
        static void axpy(Dune::FieldVector<FieldType,n>& x, const Scalar a, const Dune::FieldVector<FieldType,n>& y)
        {
            x.axpy(a, y);
        }





        // add transformed matrix X = A^T*Y*B ******************************************************
        template<class MatrixA, class MatrixB, class TransformationMatrix>
        static void addTransformedMatrix(MatrixA& A, const TransformationMatrix& T1, const MatrixB& B, const TransformationMatrix& T2)
        {
            for (size_t i=0; i<A.N(); ++i)
            {
                typename MatrixA::row_type::ConstIterator jIt=A[i].begin();
                typename MatrixA::row_type::ConstIterator jEnd=A[i].end();
                for(; jIt!=jEnd; ++jIt)
                {
                    for (size_t k=0; k<B.N(); ++k)
                    {
                        if (T1[k][i]!=0)
                        {
                            typename MatrixB::row_type::ConstIterator lIt=B[k].begin();
                            typename MatrixB::row_type::ConstIterator lEnd=B[k].end();
                            for(; lIt!=lEnd; ++lIt)
                                A[i][jIt.index()] += T1[k][i] * B[k][lIt.index()] * T2[lIt.index()][jIt.index()];
                        }
                    }
                }
            }
        }

        template<class K1, class K2, class K3, int n, int m>
        static void addTransformedMatrix(
            typename Dune::FieldMatrix<K1, m, m>& A,
            const typename Dune::FieldMatrix<K2, n, m>& T1,
            const typename Dune::FieldMatrix<K3, n, n>& B,
            const typename Dune::FieldMatrix<K2, n, m>& T2)
        {
            typename Dune::FieldMatrix<K1, m, n> T1transposedB;
            T1transposedB = 0;
            for (size_t i=0; i<A.N(); ++i)
            {
                for (size_t k=0; k<B.N(); ++k)
                {
                    if (T1[k][i]!=0)
                        for (size_t l=0; l<B.M(); ++l)
                            T1transposedB[i][l] += T1[k][i] * B[k][l];
                }
            }
            for (size_t k=0; k<T2.N(); ++k)
            {
                for (size_t l=0; l<T2.M(); ++l)
                {
                    if (T2[k][l]!=0)
                        for (size_t i=0; i<A.N(); ++i)
                            A[i][l] += T1transposedB[i][k] * T2[k][l];
                }
            }
        }

        template<class MatrixA, class MatrixB>
        static void addTransformedMatrix(MatrixA& X, const double& A, const MatrixB& Y, const double& B)
        {
            axpy(X, A*B, Y);
        }

        template<class MatrixA, typename FieldType, int n, class TransformationMatrix>
        static void addTransformedMatrix(MatrixA& X, const TransformationMatrix& A, const Dune::DiagonalMatrix<FieldType,n>& Y, const TransformationMatrix& B)
        {
            for (int i=0; i<MatrixA::rows; ++i)
                for (int j=0; j<MatrixA::cols; ++j)
                    for (int k=0; k<n; k++)
                        X[i][j] += A[k][i] * Y.diagonal()[k] * B[k][j];
        }

        template<class MatrixA, typename FieldType, int n, class TransformationMatrix>
        static void addTransformedMatrix(MatrixA& X, const TransformationMatrix& A, const Dune::ScaledIdentityMatrix<FieldType,n>& Y, const TransformationMatrix& B)
        {
            for (int i=0; i<MatrixA::rows; ++i)
                for (int j=0; j<MatrixA::cols; ++j)
                    for (int k=0; k<n; k++)
                        X[i][j] += A[k][i] * Y.scalar() * B[k][j];
        }

        template <typename FieldType, int n, class TransformationMatrix>
        static void addTransformedMatrix(Dune::DiagonalMatrix<FieldType,n>& X, const TransformationMatrix& A, const Dune::DiagonalMatrix<FieldType,n>& Y, const TransformationMatrix& B)
        {
            for (int i=0; i<n; ++i)
                for (int k=0; k<n; k++)
                    X.diagonal()[i] += A[k][i] * Y.diagonal()[k] * B[k][i];
        }

        template <typename FieldType, int n, class TransformationMatrix>
        static void addTransformedMatrix(Dune::ScaledIdentityMatrix<FieldType,n>& X, const TransformationMatrix& A, const Dune::ScaledIdentityMatrix<FieldType,n>& Y, const TransformationMatrix& B)
        {
            for (int k=0; k<n; k++)
                X.scalar() += A[k][0] * Y.scalar() * B[k][0];
        }



        // compute v^T*A*v for an edge vector e = e_i - e_j with i!=j ******************************
        template<class Matrix>
        static typename Matrix::field_type simplexEdgeDiagonal(const Matrix& A, int i, int j)
        {
            return A[i][i] - A[i][j] - A[j][i] + A[j][j];
        }

        template <typename FieldType, int n>
        static FieldType simplexEdgeDiagonal(const Dune::DiagonalMatrix<FieldType,n>& A, int i, int j)
        {
            return A.diagonal(i) + A.diagonal(j);
        }

        template <typename FieldType, int n>
        static FieldType simplexEdgeDiagonal(const Dune::ScaledIdentityMatrix<FieldType,n>& A, int i, int j)
        {
            return 2*A.scalar();
        }



        // compute i-th row of A*v for an edge vector e = e_i - e_j with i!=j **********************
        template<class Matrix>
        static typename Matrix::field_type simplexEdgeResidual(const Matrix& A, int i, int j)
        {
            return A[i][i] - A[i][j];
        }

        template <typename FieldType, int n>
        static FieldType simplexEdgeResidual(const Dune::DiagonalMatrix<FieldType,n>& A, int i, int j)
        {
            return A.diagonal(i);
        }

        template <typename FieldType, int n>
        static FieldType simplexEdgeResidual(const Dune::ScaledIdentityMatrix<FieldType,n>& A, int i, int j)
        {
            return A.scalar();
        }


        // transform matrix A = X^T * B * Y ********************************************************
        template<class MatrixA, class MatrixB, class MatrixT>
        static void transformMatrix(MatrixA& A, const MatrixT& X, const MatrixB& B, const MatrixT& Y)
        {
            A = 0.0;
            for (int k=0; k<B.N(); ++k)
                for (int l=0; l<B.M(); ++l)
                    for (int i=0; i<X.M(); ++i)
                        for (int j=0; j<Y.M(); ++j)
                            A[i][j] = X[k][i] * B[k][l] * Y[l][j];
        }

        template<class MatrixA, class MatrixB>
        static void transformMatrix(MatrixA& A, const double& X, const MatrixB& B, const double& Y)
        {
            A = 0.0;
            axpy(A, X*Y, B);
        }

        template<class MatrixA, class MatrixT>
        static void transformMatrix(MatrixA& A, const MatrixT& X, const double& B, const MatrixT& Y)
        {
            A = X*B*Y;
        }

        template<class MatrixBlockA, class MatrixB, class MatrixT>
        static void transformMatrix(Dune::BCRSMatrix<MatrixBlockA>& A, const MatrixT& X, const MatrixB& B, const MatrixT& Y)
        {
            transformMatrixPattern(A, X, B, Y);
            A = 0.0;
            for (int k=0; k<B.N(); ++k)
            {
                typename MatrixB::row_type::ConstIterator BklIt = B[k].begin();
                typename MatrixB::row_type::ConstIterator BklEnd = B[k].end();
                for (; BklIt!=BklEnd; ++BklIt)
                {
                    int l = BklIt.index();

                    typename MatrixT::row_type::ConstIterator XkiIt = X[k].begin();
                    typename MatrixT::row_type::ConstIterator XkiEnd = X[k].end();
                    for (; XkiIt!=XkiEnd; ++XkiIt)
                    {
                        int i = XkiIt.index();

                        typename MatrixT::row_type::ConstIterator YljIt = Y[l].begin();
                        typename MatrixT::row_type::ConstIterator YljEnd = Y[l].end();
                        for (; YljIt!=YljEnd; ++YljIt)
                        {
                            int j = YljIt.index();
                            MatrixBlockA Aij;
                            transformMatrix(Aij,*XkiIt, *BklIt, *YljIt);
                            A[i][j] += Aij;
                        }
                    }
                }
            }
        }

        template<class MatrixBlockA, class MatrixB, class MatrixT>
        static void transformMatrixPattern(Dune::BCRSMatrix<MatrixBlockA>& A, const MatrixT& X, const MatrixB& B, const MatrixT& Y)
        {
            Dune::MatrixIndexSet indices(X.M(), Y.M());
            for (int k=0; k<B.N(); ++k)
            {
                typename MatrixB::row_type::ConstIterator BklIt = B[k].begin();
                typename MatrixB::row_type::ConstIterator BklEnd = B[k].end();
                for (; BklIt!=BklEnd; ++BklIt)
                {
                    int l = BklIt.index();

                    typename MatrixT::row_type::ConstIterator XkiIt = X[k].begin();
                    typename MatrixT::row_type::ConstIterator XkiEnd = X[k].end();
                    for (; XkiIt!=XkiEnd; ++XkiIt)
                    {
                        int i = XkiIt.index();

                        typename MatrixT::row_type::ConstIterator YljIt = Y[l].begin();
                        typename MatrixT::row_type::ConstIterator YljEnd = Y[l].end();
                        for (; YljIt!=YljEnd; ++YljIt)
                        {
                            int j = YljIt.index();
                            indices.add(i, j);
                        }
                    }
                }
            }
            indices.exportIdx(A);
        }



        // compute i-th row of A*v for an edge vector e = e_i - e_j with i!=j **********************
        template <class K>
        static Dune::FieldMatrix<K,1,1>& toMatrix(K& x)
        {
            return *(reinterpret_cast< Dune::FieldMatrix<K,1,1>* > (&x));
        }

        template <class K, int n, int m>
        static Dune::FieldMatrix<K,n,m>& toMatrix(Dune::FieldMatrix<K,n,m>& x)
        {
            return x;
        }

        template <class K, int n>
        static Dune::ScaledIdentityMatrix<K,n>& toMatrix(Dune::ScaledIdentityMatrix<K,n>& x)
        {
            return x;
        }

        template <class K, int n>
        static Dune::DiagonalMatrix<K,n>& toMatrix(Dune::DiagonalMatrix<K,n>& x)
        {
            return x;
        }

        template <class K>
        static Dune::FieldMatrix<K,1,1>& toMatrix(Dune::ScaledIdentityMatrix<K,1>& x)
        {
            return *(reinterpret_cast< Dune::FieldMatrix<K,1,1>* > (&x));
        }

        template <class K>
        static Dune::FieldMatrix<K,1,1>& toMatrix(Dune::DiagonalMatrix<K,1>& x)
        {
            return *(reinterpret_cast< Dune::FieldMatrix<K,1,1>* > (&x));
        }

        /** \brief Compute an LDL^T decomposition
         *
         * The methods computes a decomposition A=LDL^T of a given dense
         * symmetric matrix A such that L is lower triangular with all
         * diagonal elements equal to 1 and D is diagonal. If A is positive
         * definite then A=(LD^0.5)(LD^0.5)^T is the Cholesky decomposition.
         * However, the LDL^T decomposition does also work for indefinite
         * symmetric matrices and is more stable than the Cholesky decomposition
         * since no square roots are required.
         *
         * The method does only change the nontrivial entries of the given matrix
         * L and D, i.e., it does not set the trivial 0 and 1 entries.
         * Thus one can store both in a single matrix M and use
         * M as argument for L and D.
         *
         * The method can furthermore work in-place, i.e., it is safe to
         * use A as argument for L and D. In this case the entries of A
         * below and on the diagonal are changed to those to those of
         * L and D, respectively.
         *
         * \param A Matrix to be decomposed. Only the lower triangle is used.
         * \param L Matrix to store the lower triangle. Only entries below the diagonal are written.
         * \param D Matrix to store the diagonal. Only diagonal entries are written.
         */
        template<class SymmetricMatrix, class LowerTriangularMatrix, class DiagonalMatrix>
        static void ldlt(const SymmetricMatrix& A, LowerTriangularMatrix& L, DiagonalMatrix& D)
        {
          for(unsigned int i = 0; i < A.N(); ++i)
          {
            D[i][i] = A[i][i];
            for(unsigned int j = 0; j < i; ++j)
            {
              L[i][j] = A[i][j];
              for(unsigned int k = 0; k < j; ++k)
                L[i][j] -= L[i][k] * L[j][k] * D[k][k];
              L[i][j] /= D[j][j];
            }
            for(unsigned int k = 0; k < i; ++k)
              D[i][i] -= L[i][k]*L[i][k] * D[k][k];
          }
        }

        /** \brief Solve linear system using a LDL^T decomposition.
         *
         * The methods solves a linear system Mx=b where A is given
         * by a decomposition A=LDL^T. The method does only use
         * the values of L and D below and on the diagonal, respectively.
         * The 1 entries on the diagonal of L are not required.
         * If L and D are stored in a single matrix it is safe
         * the use this matrix as argument for both.
         *
         * Note that the solution vector must already have the correct size.
         *
         * \param L Matrix containing the lower triangle of the decomposition
         * \param D Matrix containing the diagonal of the decomposition
         * \param b Right hand side on the linear system
         * \param x Vector to store the solution of the linear system
         */
        template<class LowerTriangularMatrix, class DiagonalMatrix, class RhsVector, class SolVector>
        static void ldltSolve(const LowerTriangularMatrix& L, const DiagonalMatrix& D, const RhsVector& b, SolVector& x)
        {
          for(unsigned int i = 0; i < x.size(); ++i)
          {
            x[i] = b[i];
            for(unsigned int j = 0; j < i; ++j)
              x[i] -= L[i][j] * x[j];
          }
          for(unsigned int i = 0; i < x.size(); ++i)
            x[i] /= D[i][i];
          for(int i = x.size()-1; i >=0; --i)
          {
            for(unsigned int j = i+1; j < x.size(); ++j)
              x[i] -= L[j][i] * x[j];
          }
        }

};

#endif