Skip to content
Snippets Groups Projects
rotationtest.cc 8.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • Oliver Sander's avatar
    Oliver Sander committed
    #include <config.h>
    
    #include <iostream>
    
    #include <array>
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    #include <dune/common/fmatrix.hh>
    
    
    #include <dune/gfe/spaces/rotation.hh>
    
    #include "valuefactory.hh"
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    using namespace Dune;
    
    
      std::array<FieldVector<double,3>, 125> v;
      int ct = 0;
      double eps = 1e-4;
    
      for (int i=-2; i<3; i++)
        for (int j=-2; j<3; j++)
          for (int k=-2; k<3; k++) {
            v[ct][0] = i;
            v[ct][1] = j;
            v[ct][2] = k;
            ct++;
          }
    
      for (size_t i=0; i<v.size(); i++) {
    
        // Compute FD approximation of second derivative of exp
        std::array<Dune::FieldMatrix<double,3,3>, 4> fdDDExp;
    
              SkewMatrix<double,3> forward(v[i]);
              forward.axial()[j] += eps;
              Rotation<double,3> forwardQ  = Rotation<double,3>::exp(forward);
    
              SkewMatrix<double,3> center(v[i]);
              Rotation<double,3> centerQ   = Rotation<double,3>::exp(center);
    
              SkewMatrix<double,3> backward(v[i]);
              backward.axial()[j] -= eps;
              Rotation<double,3> backwardQ = Rotation<double,3>::exp(backward);
    
              for (int l=0; l<4; l++)
                fdDDExp[l][j][j] = (forwardQ[l] - 2*centerQ[l] + backwardQ[l]) / (eps*eps);
    
              SkewMatrix<double,3> ffV(v[i]);      ffV.axial()[j] += eps;     ffV.axial()[k] += eps;
              SkewMatrix<double,3> fbV(v[i]);      fbV.axial()[j] += eps;     fbV.axial()[k] -= eps;
              SkewMatrix<double,3> bfV(v[i]);      bfV.axial()[j] -= eps;     bfV.axial()[k] += eps;
              SkewMatrix<double,3> bbV(v[i]);      bbV.axial()[j] -= eps;     bbV.axial()[k] -= eps;
    
              Rotation<double,3> forwardForwardQ = Rotation<double,3>::exp(ffV);
              Rotation<double,3> forwardBackwardQ = Rotation<double,3>::exp(fbV);
              Rotation<double,3> backwardForwardQ = Rotation<double,3>::exp(bfV);
              Rotation<double,3> backwardBackwardQ = Rotation<double,3>::exp(bbV);
    
              for (int l=0; l<4; l++)
                fdDDExp[l][j][k] = (forwardForwardQ[l] + backwardBackwardQ[l]
                                    - forwardBackwardQ[l] - backwardForwardQ[l]) / (4*eps*eps);
    
    
        // Compute analytical second derivative of exp
        std::array<Dune::FieldMatrix<double,3,3>, 4> ddExp;
        Rotation<double,3>::DDexp(v[i], ddExp);
    
        for (int m=0; m<4; m++)
          for (int j=0; j<3; j++)
            for (int k=0; k<3; k++)
              if ( std::abs(fdDDExp[m][j][k] - ddExp[m][j][k]) > eps) {
                std::cout << "Error at v = " << v[i]
                          << "[" << m << ", " << j << ", " << k << "] "
                          << "    fd: " << fdDDExp[m][j][k]
                          << "    analytical: " << ddExp[m][j][k] << std::endl;
              }
      }
    
    void testRotation(Rotation<double,3> q)
    
    Oliver Sander's avatar
    Oliver Sander committed
    {
    
      // Make sure it really is a unit quaternion
      q.normalize();
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    
      assert(std::abs(1-q.two_norm()) < 1e-12);
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    
      // Turn it into a matrix
      FieldMatrix<double,3,3> matrix;
      q.matrix(matrix);
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    
      // make sure it is an orthogonal matrix
      if (std::abs(1-matrix.determinant()) > 1e-12 )
        DUNE_THROW(Exception, "Expected determinant 1, but the computed value is " << matrix.determinant());
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    
      assert( std::abs( matrix[0]*matrix[1] ) < 1e-12 );
      assert( std::abs( matrix[0]*matrix[2] ) < 1e-12 );
      assert( std::abs( matrix[1]*matrix[2] ) < 1e-12 );
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    
      // Turn the matrix back into a quaternion, and check whether it is the same one
      // Since the quaternions form a double covering of SO(3), we may either get q back
      // or -q.  We have to check both.
      Rotation<double,3> newQ;
      newQ.set(matrix);
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    
      Rotation<double,3> diff = newQ;
      diff -= q;
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    
      Rotation<double,3> sum  = newQ;
      sum += q;
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    
      if (diff.infinity_norm() > 1e-12 && sum.infinity_norm() > 1e-12)
        DUNE_THROW(Exception, "Backtransformation failed for " << q << ". ");
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    
      // //////////////////////////////////////////////////////
      //   Check the director vectors
      // //////////////////////////////////////////////////////
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    
      for (int i=0; i<3; i++)
        for (int j=0; j<3; j++)
          assert( std::abs(matrix[i][j] - q.director(j)[i]) < 1e-12 );
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    
      // //////////////////////////////////////////////////////
      //   Check multiplication with another unit quaternion
      // //////////////////////////////////////////////////////
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    
      for (int i=-2; i<2; i++)
        for (int j=-2; j<2; j++)
          for (int k=-2; k<2; k++)
            for (int l=-2; l<2; l++)
              if (i!=0 || j!=0 || k!=0 || l!=0) {
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    
                Rotation<double,3> q2(Quaternion<double>(i,j,k,l));
                q2.normalize();
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    
                // set up corresponding rotation matrix
                FieldMatrix<double,3,3> q2Matrix;
                q2.matrix(q2Matrix);
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    
                // q2 = q2 * q   Quaternion multiplication
                q2 = q2.mult(q);
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    
                // q2 = q2 * q   Matrix multiplication
                q2Matrix.rightmultiply(matrix);
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    
                FieldMatrix<double,3,3> productMatrix;
                q2.matrix(productMatrix);
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    
                // Make sure we got identical results
                productMatrix -= q2Matrix;
                assert(productMatrix.infinity_norm() < 1e-10);
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    
      // ////////////////////////////////////////////////////////////////
      //   Check the operators 'B' that create an orthonormal basis of H
      // ////////////////////////////////////////////////////////////////
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    
      Quaternion<double> Bq[4];
      Bq[0] = q;
      Bq[1] = q.B(0);
      Bq[2] = q.B(1);
      Bq[3] = q.B(2);
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    
          double prod = Bq[i]*Bq[j];
          assert( std::abs( prod - (i==j) ) < 1e-6 );
    
      //////////////////////////////////////////////////////////////////////
      //  Check whether the derivativeOfMatrixToQuaternion methods works
      //////////////////////////////////////////////////////////////////////
    
      Tensor3<double,4,3,3> derivative = Rotation<double,3>::derivativeOfMatrixToQuaternion(matrix);
    
      const double eps = 1e-8;
      Tensor3<double,4,3,3> derivativeFD;
    
      for (size_t i=0; i<3; i++)
      {
        for (size_t j=0; j<3; j++)
        {
          auto forwardMatrix = matrix;
          forwardMatrix[i][j] += eps;
          auto backwardMatrix = matrix;
          backwardMatrix[i][j] -= eps;
    
          Rotation<double,3> forwardRotation, backwardRotation;
          forwardRotation.set(forwardMatrix);
          backwardRotation.set(backwardMatrix);
    
          for (size_t k=0; k<4; k++)
            derivativeFD[k][i][j] = (forwardRotation.globalCoordinates()[k] - backwardRotation.globalCoordinates()[k]) / (2*eps);
    
      }
    
      if ((derivative - derivativeFD).infinity_norm() > 1e-6)
      {
        std::cout << "At matrix:\n" << matrix << std::endl;
    
        std::cout << "Derivative of matrix to quaternion map does not match its FD approximation" << std::endl;
        std::cout << "Analytical derivative:" << std::endl;
        std::cout << derivative << std::endl;
        std::cout << "Finite difference approximation" << std::endl;
        std::cout << derivativeFD << std::endl;
        abort();
      }
    
    Oliver Sander's avatar
    Oliver Sander committed
    }
    
    
    //! test interpolation between two rotations
    bool testInterpolation(const Rotation<double, 3>& a, const Rotation<double, 3>& b) {
    
    
      // Compute difference on T_a SO(3)
      Rotation<double, 3> newB = Rotation<double, 3>::interpolate(a, b, 1.0);
    
      // Compare matrix representation
      FieldMatrix<double, 3, 3> matB;
      b.matrix(matB);
    
      FieldMatrix<double, 3, 3> matNewB;
      newB.matrix(matNewB);
    
      matNewB -= matB;
      if (matNewB.infinity_norm() > 1e-14)
        std::cout << " Interpolation failed with difference " << matNewB.infinity_norm()  << std::endl;
    
      return (matNewB.infinity_norm() < 1e-14);
    
    Oliver Sander's avatar
    Oliver Sander committed
    int main (int argc, char *argv[]) try
    {
    
      std::vector<Rotation<double,3> > testPoints;
      ValueFactory<Rotation<double,3> >::get(testPoints);
    
      int nTestPoints = testPoints.size();
    
      // Test each element in the list
      for (int i=0; i<nTestPoints; i++)
        testRotation(testPoints[i]);
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    
      bool passed(true);
      // Test interpolating between pairs of rotations
      for (int i=0; i<nTestPoints-1; i++)
        passed = passed and testInterpolation(testPoints[i], testPoints[i+1]);
    
      // //////////////////////////////////////////////
      //   Test second derivative of exp
      // //////////////////////////////////////////////
      testDDExp();
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    
      std::cout << e.what() << std::endl;
    
    Oliver Sander's avatar
    Oliver Sander committed