Exemple #1
0
 template <typename F> GpuQmrCs<F>::GpuQmrCs (const OpenCL::StubPool& pool, const std::vector<cl::CommandQueue>& queues, const DDAParams<ftype>& ddaParams, GpuMatVec<ftype>& matVec, csize_t maxIter, OpenCL::VectorAccounting& accounting) : 
   GpuIterativeSolver<F> (pool, queues, ddaParams, matVec, 50000, maxIter, accounting),
   varsVec (pool, 1, accounting, "vars"),
   vars (varsVec.pointer ()),
   tmpVec2_ (pool, queues, g (), accounting, "tmpVec2"),
   tmpVec3_ (pool, queues, g (), accounting, "tmpVec3"),
   tmpVec4_ (pool, queues, g (), accounting, "tmpVec4")
 {
   this->Avecbuffer ().setToZero (queues);
   this->rvec ().setToZero (queues);
   this->xvec ().setToZero (queues);
   this->tmpVec1 ().setToZero (queues);
   tmpVec2 ().setToZero (queues);
   tmpVec3 ().setToZero (queues);
   tmpVec4 ().setToZero (queues);
 }
Exemple #2
0
int SVD(Mat3d & F, Mat3d & U, Vec3d & Sigma, Mat3d & V, double singularValue_eps, int modifiedSVD)
{
  // The code handles the following special situations:

  //---------------------------------------------------------
  // 1. det(V) == -1
  //    - multiply the first column of V by -1
  //---------------------------------------------------------
  // 2. An entry of Sigma is near zero
  //---------------------------------------------------------
  // (if modifiedSVD == 1) :
  // 3. negative determinant (Tet is inverted in solid mechanics).
  //    - check if det(U) == -1
  //    - If yes, then negate the minimal element of Sigma
  //      and the corresponding column of U
  //---------------------------------------------------------

  // form F^T F and do eigendecomposition
  Mat3d normalEq = trans(F) * F;
  Vec3d eigenValues;
  Vec3d eigenVectors[3];

  eigen_sym(normalEq, eigenValues, eigenVectors);

  V.set(eigenVectors[0][0], eigenVectors[1][0], eigenVectors[2][0],
    eigenVectors[0][1], eigenVectors[1][1], eigenVectors[2][1],
    eigenVectors[0][2], eigenVectors[1][2], eigenVectors[2][2]);
  /*
    printf("--- original V ---\n");
    V.print();
    printf("--- eigenValues ---\n");
    printf("%G %G %G\n", eigenValues[0], eigenValues[1], eigenValues[2]);
  */

  // Handle situation:
  // 1. det(V) == -1
  //    - multiply the first column of V by -1
  if (det(V) < 0.0)
  {
    // convert V into a rotation (multiply column 1 by -1)
    V[0][0] *= -1.0;
    V[1][0] *= -1.0;
    V[2][0] *= -1.0;
  }

  Sigma[0] = (eigenValues[0] > 0.0) ? sqrt(eigenValues[0]) : 0.0;
  Sigma[1] = (eigenValues[1] > 0.0) ? sqrt(eigenValues[1]) : 0.0;
  Sigma[2] = (eigenValues[2] > 0.0) ? sqrt(eigenValues[2]) : 0.0;

  //printf("--- Sigma ---\n");
  //printf("%G %G %G\n", Sigma[0][0], Sigma[1][1], Sigma[2][2]);

  // compute inverse of singular values
  // also check if singular values are close to zero
  Vec3d SigmaInverse;
  SigmaInverse[0] = (Sigma[0] > singularValue_eps) ? (1.0 / Sigma[0]) : 0.0;
  SigmaInverse[1] = (Sigma[1] > singularValue_eps) ? (1.0 / Sigma[1]) : 0.0;
  SigmaInverse[2] = (Sigma[2] > singularValue_eps) ? (1.0 / Sigma[2]) : 0.0;
  
  // compute U using the formula:
  // U = F * V * diag(SigmaInverse)
  U = F * V;
  U.multiplyDiagRight(SigmaInverse);

  // In theory, U is now orthonormal, U^T U = U U^T = I .. it may be a rotation or a reflection, depending on F.
  // But in practice, if singular values are small or zero, it may not be orthonormal, so we need to fix it.
  // Handle situation:
  // 2. An entry of Sigma is near zero
  // ---------------------------------------------------------

  /*
    printf("--- SigmaInverse ---\n");
    SigmaInverse.print();
    printf(" --- U ---\n");
    U.print();
  */
  
  if ((Sigma[0] < singularValue_eps) && (Sigma[1] < singularValue_eps) && (Sigma[2] < singularValue_eps))
  {
    // extreme case, all singular values are small, material has collapsed almost to a point
    // see [Irving 04], p. 4
    U.set(1.0, 0.0, 0.0,
          0.0, 1.0, 0.0,
          0.0, 0.0, 1.0);
  }
  else 
  {
    // handle the case where two singular values are small, but the third one is not
    // handle it by computing two (arbitrary) vectors orthogonal to the eigenvector for the large singular value
    int done = 0;
    for(int dim=0; dim<3; dim++)
    {
      int dimA = dim;
      int dimB = (dim + 1) % 3;
      int dimC = (dim + 2) % 3;
      if ((Sigma[dimB] < singularValue_eps) && (Sigma[dimC] < singularValue_eps))
      {
        // only the column dimA can be trusted, columns dimB and dimC correspond to tiny singular values
        Vec3d tmpVec1(U[0][dimA], U[1][dimA], U[2][dimA]); // column dimA
        Vec3d tmpVec2;
        tmpVec2 = tmpVec1.findOrthonormalVector();
        Vec3d tmpVec3 = norm(cross(tmpVec1, tmpVec2));
        U[0][dimB] = tmpVec2[0];
        U[1][dimB] = tmpVec2[1];
        U[2][dimB] = tmpVec2[2];
        U[0][dimC] = tmpVec3[0];
        U[1][dimC] = tmpVec3[1];
        U[2][dimC] = tmpVec3[2];
        if (det(U) < 0.0)
        {
          U[0][dimB] *= -1.0;
          U[1][dimB] *= -1.0;
          U[2][dimB] *= -1.0;
        }
        done = 1;
        break; // out of for
      }
    }

    // handle the case where one singular value is small, but the other two are not
    // handle it by computing the cross product of the two eigenvectors for the two large singular values
    if (!done) 
    {
      for(int dim=0; dim<3; dim++)
      {
        int dimA = dim;
        int dimB = (dim + 1) % 3;
        int dimC = (dim + 2) % 3;

        if (Sigma[dimA] < singularValue_eps)
        {
          // columns dimB and dimC are both good, but column dimA corresponds to a tiny singular value
          Vec3d tmpVec1(U[0][dimB], U[1][dimB], U[2][dimB]); // column dimB
          Vec3d tmpVec2(U[0][dimC], U[1][dimC], U[2][dimC]); // column dimC
          Vec3d tmpVec3 = norm(cross(tmpVec1, tmpVec2));
          U[0][dimA] = tmpVec3[0];
          U[1][dimA] = tmpVec3[1];
          U[2][dimA] = tmpVec3[2];
          if (det(U) < 0.0)
          {
            U[0][dimA] *= -1.0;
            U[1][dimA] *= -1.0;
            U[2][dimA] *= -1.0;
          }
          done = 1;
          break; // out of for
        }
      }
    }

    if ((!done) && (modifiedSVD == 1))
    {
      // Handle situation:
      // 3. negative determinant (Tet is inverted in solid mechanics)
      //    - check if det(U) == -1
      //    - If yes, then negate the minimal element of Sigma
      //      and the corresponding column of U

      double detU = det(U);
      if (detU < 0.0)
      {
        // negative determinant
        // find the smallest singular value (they are all non-negative)
        int smallestSingularValueIndex = 0;
        for(int dim=1; dim<3; dim++)
          if (Sigma[dim] < Sigma[smallestSingularValueIndex])
            smallestSingularValueIndex = dim;

        // negate the smallest singular value
        Sigma[smallestSingularValueIndex] *= -1.0;
        U[0][smallestSingularValueIndex] *= -1.0;
        U[1][smallestSingularValueIndex] *= -1.0;
        U[2][smallestSingularValueIndex] *= -1.0;
      }
    }
  }

  /*
    printf("U = \n");
    U.print();
    printf("Sigma = \n");
    Sigma.print();
    printf("V = \n");
    V.print();
  */

  return 0;
}
Exemple #3
0
  GcmJointTildeInfo<S_V,S_M,D_V,D_M,P_V,P_M,Q_V,Q_M>::GcmJointTildeInfo(
  const GpmsaComputerModelOptions&                     gcmOptionsObj,
  const GcmExperimentInfo<S_V,S_M,D_V,D_M,P_V,P_M>&    e,
  const GcmJointInfo<S_V,S_M,D_V,D_M,P_V,P_M,Q_V,Q_M>& jj)
  :
  m_env                  (jj.m_env),
  m_Bmat_tilde           (m_env,e.m_y_space.map(),jj.m_Bmat_rank),
  m_Bmat_tilde_rank      (0),
  m_vu_tilde_space       (m_env, "vu_tilde_", jj.m_Bmat_rank, NULL),          // rr0 check
  m_Lbmat                (m_env,m_vu_tilde_space.map(),jj.m_Bmat_with_permut->numCols()), // rr0 check
  m_Btildet_Wy_Btilde    (m_vu_tilde_space.zeroVector()),
  m_Btildet_Wy_Btilde_inv(m_vu_tilde_space.zeroVector()),
  m_Zvec_tilde_hat_vu    (m_vu_tilde_space.zeroVector()),
  m_a_y_modifier_tilde   (0),
  m_b_y_modifier_tilde   (0)
{
  std::set<unsigned int> tmpSet;
  tmpSet.insert(m_env.subId());
    //******************************************************************************
    // Tilde situation: form 'm_Bmat_tilde'
    // Tilde situation: form 'm_vu_tilde_space'
    // Tilde situation: form 'm_Lbmat'
    //******************************************************************************
    if (jj.m_Bmat_with_permut->numRowsGlobal() >= jj.m_Bmat_with_permut->numCols()) {
      D_M matbU(m_env,e.m_y_space.map(),jj.m_vu_size); // same as m_Bmat
      matbU = jj.m_Bmat_with_permut->svdMatU();
      unsigned int buMatRank   = matbU.rank(0.,1.e-8 ); // todo: should be an option
      unsigned int buMatRank14 = matbU.rank(0.,1.e-14);
      if (m_env.subDisplayFile()) {
        *m_env.subDisplayFile() << "In GcmJointTildeInfo<S_V,S_M,D_V,D_M,P_V,P_M,Q_V,Q_M>::constructor()"
                                << ": matbU.numRowsLocal() = "  << matbU.numRowsLocal()
                                << ", matbU.numCols() = "       << matbU.numCols()
                                << ", matbU.rank(0.,1.e-8) = "  << buMatRank
                                << ", matbU.rank(0.,1.e-14) = " << buMatRank14
                                << std::endl;
      }

      if (m_env.checkingLevel() >= 1) {
        D_M matbUcheck(jj.m_vu_space.zeroVector());
        D_V vecI(e.m_y_space.zeroVector());
        D_V vecJ(e.m_y_space.zeroVector());
        for (unsigned int i = 0; i < matbU.numCols(); ++i) {
          matbU.getColumn(i,vecI);
          for (unsigned int j = i; j < matbU.numCols(); ++j) {
            matbU.getColumn(j,vecJ);
            matbUcheck(i,j) = scalarProduct(vecI,vecJ);
          }
        }
        matbUcheck.setPrintHorizontally(false);
        if (m_env.subDisplayFile()) {
          *m_env.subDisplayFile() << "In GcmJointTildeInfo<S_V,S_M,D_V,D_M,P_V,P_M,Q_V,Q_M>::constructor()"
                                  << ": m_Bmat_with_permut->numRowsLocal() = " << jj.m_Bmat_with_permut->numRowsLocal()
                                  << ", m_Bmat_with_permut->numCols() = "      << jj.m_Bmat_with_permut->numCols()
                                  << ", m_Bmat_rank = "                       << jj.m_Bmat_rank
                                  << ", matbU.numRowsLocal() = "              << matbU.numRowsLocal()
                                  << ", matbU.numCols() = "                   << matbU.numCols()
                                  << ", buMatrank(0.,1.e-8) = "               << buMatRank
                                  << ", buMatrank(0.,1.e-14) = "              << buMatRank14
                                  << ", matbUcheck.numRowsLocal() = "         << matbUcheck.numRowsLocal()
                                  << ", matbUcheck.numCols() = "              << matbUcheck.numCols()
                                  << ", matbUcheck =\n"                       << matbUcheck
                                  << std::endl;
        }
      }

      D_V vecbJ(e.m_y_space.zeroVector());
      for (unsigned int j = 0; j < jj.m_Bmat_rank; ++j) {
        matbU.getColumn(j,vecbJ);
        m_Bmat_tilde.setColumn(j,vecbJ);
      }
      if (gcmOptionsObj.m_ov.m_dataOutputAllowedSet.find(m_env.subId()) != gcmOptionsObj.m_ov.m_dataOutputAllowedSet.end()) {
        m_Bmat_tilde.subWriteContents("Btilde",
                                       "Btilde1",
                                       "m",
                                       tmpSet);
      }
      if (m_env.subDisplayFile()) {
        *m_env.subDisplayFile() << "In GcmJointTildeInfo<S_V,S_M,D_V,D_M,P_V,P_M,Q_V,Q_M>::constructor()"
                                << ": m_Bmat_tilde computed (1)"
                                << std::endl;
      }
    }
    else {
      D_M Bmat_t(m_env,jj.m_vu_space.map(),e.m_paper_n_y); // same as m_Bmat^T
      Bmat_t.fillWithTranspose(0,0,*jj.m_Bmat_with_permut,true,true);

      D_M matbV(e.m_y_space.zeroVector());
      matbV = Bmat_t.svdMatV();
      unsigned int bvMatRank   = matbV.rank(0.,1.e-8); // todo: should be an option
      unsigned int bvMatRank14 = matbV.rank(0.,1.e-14);
      if (m_env.subDisplayFile()) {
        *m_env.subDisplayFile() << "In GcmJointTildeInfo<S_V,S_M,D_V,D_M,P_V,P_M,Q_V,Q_M>::constructor()"
                                << ": matbV.numRowsLocal() = "  << matbV.numRowsLocal()
                                << ", matbV.numCols() = "       << matbV.numCols()
                                << ", matbV.rank(0.,1.e-8) = "  << bvMatRank
                                << ", matbV.rank(0.,1.e-14) = " << bvMatRank14
                                << std::endl;
      }

      if (m_env.checkingLevel() >= 1) {
        D_M matbVcheck(e.m_y_space.zeroVector());
        D_V vecI(e.m_y_space.zeroVector());
        D_V vecJ(e.m_y_space.zeroVector());
        for (unsigned int i = 0; i < matbV.numCols(); ++i) {
          matbV.getColumn(i,vecI);
          for (unsigned int j = i; j < matbV.numCols(); ++j) {
            matbV.getColumn(j,vecJ);
            matbVcheck(i,j) = scalarProduct(vecI,vecJ);
          }
        }
        matbVcheck.setPrintHorizontally(false);
        if (m_env.subDisplayFile()) {
          *m_env.subDisplayFile() << "In GcmJointTildeInfo<S_V,S_M,D_V,D_M,P_V,P_M,Q_V,Q_M>::constructor()"
                                  << ": m_Bmat_with_permut->numRowsLocal() = " << jj.m_Bmat_with_permut->numRowsLocal()
                                  << ", m_Bmat_with_permut->numCols() = "      << jj.m_Bmat_with_permut->numCols()
                                  << ", m_Bmat_rank = "                       << jj.m_Bmat_rank
                                  << ", matbV.numRowsLocal() = "              << matbV.numRowsLocal()
                                  << ", matbV.numCols() = "                   << matbV.numCols()
                                  << ", bvMatrank(0.,1.e-8) = "               << bvMatRank
                                  << ", bvMatrank(0.,1.e-14) = "              << bvMatRank14
                                  << ", matbVcheck.numRowsLocal() = "         << matbVcheck.numRowsLocal()
                                  << ", matbVcheck.numCols() = "              << matbVcheck.numCols()
                                  << ", matbVcheck =\n"                       << matbVcheck
                                  << std::endl;
        }
      }

      D_V vecbJ(e.m_y_space.zeroVector());
      for (unsigned int j = 0; j < jj.m_Bmat_rank; ++j) {
        matbV.getColumn(j,vecbJ);
        m_Bmat_tilde.setColumn(j,vecbJ);
      }
      if (gcmOptionsObj.m_ov.m_dataOutputAllowedSet.find(m_env.subId()) != gcmOptionsObj.m_ov.m_dataOutputAllowedSet.end()) {
        m_Bmat_tilde.subWriteContents("Btilde",
                                       "Btilde2",
                                       "m",
                                       tmpSet);
      }
      if (m_env.subDisplayFile()) {
        *m_env.subDisplayFile() << "In GcmJointTildeInfo<S_V,S_M,D_V,D_M,P_V,P_M,Q_V,Q_M>::constructor()"
                                << ": m_Bmat_tilde computed (2)"
                                << std::endl;
      }
    }

    if (m_env.subDisplayFile()) {
      *m_env.subDisplayFile() << "In GcmJointTildeInfo<S_V,S_M,D_V,D_M,P_V,P_M,Q_V,Q_M>::constructor()"
                              << ": finished forming 'm_Bmat_tilde'"
                              << ", m_Bmat_tilde.numRowsLocal() = " << m_Bmat_tilde.numRowsLocal()
                              << ", m_Bmat_tilde.numCols() = "      << m_Bmat_tilde.numCols()
                              << std::endl;
    }

    m_Bmat_tilde.svdSolve(*jj.m_Bmat_with_permut,m_Lbmat);
    if (gcmOptionsObj.m_ov.m_dataOutputAllowedSet.find(m_env.subId()) != gcmOptionsObj.m_ov.m_dataOutputAllowedSet.end()) {
      m_Lbmat.subWriteContents("Lbmat",
                                "Lbmat",
                                "m",
                                tmpSet);
    }
    if (m_env.subDisplayFile()) {
      *m_env.subDisplayFile() << "In GcmJointTildeInfo<S_V,S_M,D_V,D_M,P_V,P_M,Q_V,Q_M>::constructor()"
                              << ": m_Lbmat_tilde computed"
                              << std::endl;
    }

    //********************************************************************************
    // Form 'Btilde^T' matrix
    //********************************************************************************
    D_M Btildet(m_env,m_vu_tilde_space.map(),e.m_paper_n_y);
    Btildet.fillWithTranspose(0,0,m_Bmat_tilde,true,true);

    if (m_env.checkingLevel() >= 1) {
      // Check transpose operation
      D_M Btildett(m_env,e.m_y_space.map(),m_vu_tilde_space.dimGlobal());
      Btildett.fillWithTranspose(0,0,Btildet,true,true);
      Btildett -= m_Bmat_tilde;
      double btDiffNorm = Btildett.normFrob();
      if (m_env.subDisplayFile()) {
        *m_env.subDisplayFile() << "In GcmJointTildeInfo<S_V,S_M,D_V,D_M,P_V,P_M,Q_V,Q_M>::constructor()"
                                << ": ||Btildett - Btilde||_2 = " << btDiffNorm
                                << std::endl;
      }
    }

    //********************************************************************************
    // Compute 'Btilde' rank
    //********************************************************************************
    double bTildeRank14 = 0.;
    if (m_Bmat_tilde.numRowsGlobal() >= m_Bmat_tilde.numCols()) {
      m_Bmat_tilde_rank = m_Bmat_tilde.rank(0.,1.e-8 ); // todo: should be an option
      bTildeRank14      = m_Bmat_tilde.rank(0.,1.e-14);
    }
    else {
      m_Bmat_tilde_rank = Btildet.rank(0.,1.e-8 ); // todo: should be an option
      bTildeRank14      = Btildet.rank(0.,1.e-14);
    }

    if (m_env.subDisplayFile()) {
      *m_env.subDisplayFile() << "In GcmJointTildeInfo<S_V,S_M,D_V,D_M,P_V,P_M,Q_V,Q_M>::constructor()"
                              << ": m_Bmat_tilde.numRowsLocal() = "  << m_Bmat_tilde.numRowsLocal()
                              << ", m_Bmat_tilde.numCols() = "       << m_Bmat_tilde.numCols()
                              << ", m_Bmat_tilde.rank(0.,1.e-8) = "  << m_Bmat_tilde_rank
                              << ", m_Bmat_tilde.rank(0.,1.e-14) = " << bTildeRank14
                              << std::endl;
    }
    queso_require_equal_to_msg(m_Bmat_tilde_rank, std::min(m_Bmat_tilde.numRowsGlobal(),m_Bmat_tilde.numCols()), "'m_Bmat_tilde' does not have a proper rank");

    //******************************************************************************
    // Tilde situation: compute 'Btilde^T W_y Btilde' matrix, and its inverse
    //******************************************************************************
    m_Btildet_Wy_Btilde = Btildet * (*e.m_Wy * m_Bmat_tilde); // todo: add 1.e-4 to diagonal
    if (m_env.subDisplayFile()) {
      *m_env.subDisplayFile() << "In GcmJointTildeInfo<S_V,S_M,D_V,D_M,P_V,P_M,Q_V,Q_M>::constructor()"
                              << ": finished computing 'm_Btildet_Wy_Btilde'"
                              << std::endl;
    }
    if (gcmOptionsObj.m_ov.m_dataOutputAllowedSet.find(m_env.subId()) != gcmOptionsObj.m_ov.m_dataOutputAllowedSet.end()) {
      m_Btildet_Wy_Btilde.subWriteContents("Btildet_Wy_Btilde",
                                            "Btildet_Wy_Btilde",
                                            "m",
                                            tmpSet);
    }
    if (m_env.subDisplayFile()) {
      *m_env.subDisplayFile() << "In GcmJointTildeInfo<S_V,S_M,D_V,D_M,P_V,P_M,Q_V,Q_M>::constructor()"
                              << ": m_Btildet_Wy_Btilde computed"
                              << std::endl;
    }

    double       btildetWyBtildeLnDeterminant = m_Btildet_Wy_Btilde.lnDeterminant();
    unsigned int btildetWyBtildeRank          = m_Btildet_Wy_Btilde.rank(0.,1.e-8 ); // todo: should be an option
    unsigned int btildetWyBtildeRank14        = m_Btildet_Wy_Btilde.rank(0.,1.e-14);
    if (m_env.subDisplayFile()) {
      *m_env.subDisplayFile() << "In GcmJointTildeInfo<S_V,S_M,D_V,D_M,P_V,P_M,Q_V,Q_M>::constructor()"
                              << ": m_Btildet_Wy_Btilde.numRowsLocal() = "  << m_Btildet_Wy_Btilde.numRowsLocal()
                              << ", m_Btildet_Wy_Btilde.numCols() = "       << m_Btildet_Wy_Btilde.numCols()
                              << ", m_Btildet_Wy_Btilde.lnDeterminant() = " << btildetWyBtildeLnDeterminant
                              << ": m_Btildet_Wy_Btilde.rank(0.,1.e-8) = "  << btildetWyBtildeRank
                              << ": m_Btildet_Wy_Btilde.rank(0.,1.e-14) = " << btildetWyBtildeRank14
                              << std::endl;
    }

    m_Btildet_Wy_Btilde_inv = m_Btildet_Wy_Btilde.inverse(); // todo: add 1.e-6 to diagonal
    if (m_env.subDisplayFile()) {
      *m_env.subDisplayFile() << "In GcmJointTildeInfo<S_V,S_M,D_V,D_M,P_V,P_M,Q_V,Q_M>::constructor()"
                              << ": finished computing 'm_Btildet_Wy_Btilde_inv'"
                              << ", m_Btildet_Wy_Btilde_inv.lnDeterminant() = " << m_Btildet_Wy_Btilde_inv.lnDeterminant()
                              << std::endl;
    }
    if (gcmOptionsObj.m_ov.m_dataOutputAllowedSet.find(m_env.subId()) != gcmOptionsObj.m_ov.m_dataOutputAllowedSet.end()) {
      m_Btildet_Wy_Btilde_inv.subWriteContents("Btildet_Wy_Btilde_inv",
                                                "Btildet_Wy_Btilde_inv",
                                                "m",
                                                tmpSet);
    }
    if (m_env.subDisplayFile()) {
      *m_env.subDisplayFile() << "In GcmJointTildeInfo<S_V,S_M,D_V,D_M,P_V,P_M,Q_V,Q_M>::constructor()"
                              << ": m_Btildet_Wy_Btilde_inv computed"
                              << std::endl;
    }

    double       btildetWyBtildeInvLnDeterminant = m_Btildet_Wy_Btilde_inv.lnDeterminant();
    unsigned int btildetWyBtildeInvRank          = m_Btildet_Wy_Btilde_inv.rank(0.,1.e-8 ); // todo: should be an option
    unsigned int btildetWyBtildeInvRank14        = m_Btildet_Wy_Btilde_inv.rank(0.,1.e-14);
    if (m_env.subDisplayFile()) {
      *m_env.subDisplayFile() << "In GcmJointTildeInfo<S_V,S_M,D_V,D_M,P_V,P_M,Q_V,Q_M>::constructor()"
                              << ": m_Btildet_Wy_Btilde_inv.numRowsLocal() = "  << m_Btildet_Wy_Btilde_inv.numRowsLocal()
                              << ", m_Btildet_Wy_Btilde_inv.numCols() = "       << m_Btildet_Wy_Btilde_inv.numCols()
                              << ": m_Btildet_Wy_Btilde_inv.lnDeterminant() = " << btildetWyBtildeInvLnDeterminant
                              << ": m_Btildet_Wy_Btilde_inv.rank(0.,1.e-8) = "  << btildetWyBtildeInvRank
                              << ": m_Btildet_Wy_Btilde_inv.rank(0.,1.e-14) = " << btildetWyBtildeInvRank14
                              << std::endl;
    }

    //********************************************************************************
    // Compute 'tilde' exponent modifiers
    //********************************************************************************
    m_a_y_modifier_tilde   = ((double) (e.m_paper_n_y - m_Bmat_tilde_rank)) / 2.;
    if (m_env.subDisplayFile()) {
      *m_env.subDisplayFile() << "In GcmJointTildeInfo<S_V,S_M,D_V,D_M,P_V,P_M,Q_V,Q_M>::constructor()"
                              << ": m_a_y_modifier_tilde = " << m_a_y_modifier_tilde
                              << std::endl;
    }

    D_V yVec_transformed(e.m_experimentStorage.yVec_transformed());
    if (m_env.subDisplayFile()) {
      *m_env.subDisplayFile() << "In GcmJointTildeInfo<S_V,S_M,D_V,D_M,P_V,P_M,Q_V,Q_M>::constructor()"
                              << ": m_Zvec_tilde_hat_vu.sizeLocal() = "        << m_Zvec_tilde_hat_vu.sizeLocal()
                              << ", m_Btildet_Wy_Btilde_inv.numRowsLocal() = " << m_Btildet_Wy_Btilde_inv.numRowsLocal()
                              << ", m_Btildet_Wy_Btilde_inv.numCols() = "      << m_Btildet_Wy_Btilde_inv.numCols()
                              << ", Btildet.numRowsLocal() = "                 << Btildet.numRowsLocal()
                              << ", Btildet.numCols() = "                      << Btildet.numCols()
                              << ", m_Wy->numRowsLocal() = "                   << e.m_Wy->numRowsLocal()
                              << ", m_Wy->numCols() = "                        << e.m_Wy->numCols()
                              << ", yVec_transformed.sizeLocal() = "           << yVec_transformed.sizeLocal()
                              << std::endl;
    }
    m_Zvec_tilde_hat_vu = m_Btildet_Wy_Btilde_inv * (Btildet * (*e.m_Wy * yVec_transformed));
    D_V tmpVec2(yVec_transformed - (m_Bmat_tilde * m_Zvec_tilde_hat_vu));
    tmpVec2 = *e.m_Wy * tmpVec2;
    m_b_y_modifier_tilde = scalarProduct(yVec_transformed,tmpVec2) / 2.;

    if (m_env.subDisplayFile()) {
      *m_env.subDisplayFile() << "In GcmJointTildeInfo<S_V,S_M,D_V,D_M,P_V,P_M,Q_V,Q_M>::constructor()"
                              << ": m_b_y_modifier_tilde = " << m_b_y_modifier_tilde
                              << std::endl;
    }

    if (m_env.subDisplayFile()) {
      *m_env.subDisplayFile() << "In GcmJointTildeInfo<S_V,S_M,D_V,D_M,P_V,P_M,Q_V,Q_M>::constructor()"
                              << ": finished computing 'tilde' exponent modifiers"
                              << std::endl;
    }
}
int ReducedStVKCubatureForceModel::ModifiedSVD(Mat3d & F, Mat3d & U, Vec3d & Fhat, Mat3d & V) const
{
    // The code handles the following necessary special situations (see the code below) :

    //---------------------------------------------------------
    // 1. det(V) == -1
    //    - simply multiply a column of V by -1
    //---------------------------------------------------------
    // 2. An entry of Fhat is near zero
    //---------------------------------------------------------
    // 3. Tet is inverted.
    //    - check if det(U) == -1
    //    - If yes, then negate the minimal element of Fhat
    //      and the corresponding column of U
    //---------------------------------------------------------

    double modifiedSVD_singularValue_eps = 1e-8;

    // form F^T F and do eigendecomposition
    Mat3d normalEq = trans(F) * F;
    Vec3d eigenValues;
    Vec3d eigenVectors[3];

    // note that normalEq is changed after calling eigen_sym
    eigen_sym(normalEq, eigenValues, eigenVectors);

    V.set(eigenVectors[0][0], eigenVectors[1][0], eigenVectors[2][0],
          eigenVectors[0][1], eigenVectors[1][1], eigenVectors[2][1],
          eigenVectors[0][2], eigenVectors[1][2], eigenVectors[2][2]);
    /*
      printf("--- original V ---\n");
      V.print();
      printf("--- eigenValues ---\n");
      printf("%G %G %G\n", eigenValues[0], eigenValues[1], eigenValues[2]);
    */

    // Handle situation:
    // 1. det(V) == -1
    //    - simply multiply a column of V by -1
    if (det(V) < 0.0)
    {
        // convert V into a rotation (multiply column 1 by -1)
        V[0][0] *= -1.0;
        V[1][0] *= -1.0;
        V[2][0] *= -1.0;
    }

    Fhat[0] = (eigenValues[0] > 0.0) ? sqrt(eigenValues[0]) : 0.0;
    Fhat[1] = (eigenValues[1] > 0.0) ? sqrt(eigenValues[1]) : 0.0;
    Fhat[2] = (eigenValues[2] > 0.0) ? sqrt(eigenValues[2]) : 0.0;

    //printf("--- Fhat ---\n");
    //printf("%G %G %G\n", Fhat[0][0], Fhat[1][1], Fhat[2][2]);

    // compute inverse of singular values
    // also check if singular values are close to zero
    Vec3d FhatInverse;
    FhatInverse[0] = (Fhat[0] > modifiedSVD_singularValue_eps) ? (1.0 / Fhat[0]) : 0.0;
    FhatInverse[1] = (Fhat[1] > modifiedSVD_singularValue_eps) ? (1.0 / Fhat[1]) : 0.0;
    FhatInverse[2] = (Fhat[2] > modifiedSVD_singularValue_eps) ? (1.0 / Fhat[2]) : 0.0;

    // compute U using the formula:
    // U = F * V * diag(FhatInverse)
    U = F * V;
    U.multiplyDiagRight(FhatInverse);

    // In theory, U is now orthonormal, U^T U = U U^T = I .. it may be a rotation or a reflection, depending on F.
    // But in practice, if singular values are small or zero, it may not be orthonormal, so we need to fix it.
    // Handle situation:
    // 2. An entry of Fhat is near zero
    // ---------------------------------------------------------

    /*
      printf("--- FhatInverse ---\n");
      FhatInverse.print();
      printf(" --- U ---\n");
      U.print();
    */

    if ((Fhat[0] < modifiedSVD_singularValue_eps) && (Fhat[1] < modifiedSVD_singularValue_eps) && (Fhat[2] < modifiedSVD_singularValue_eps))
    {
        // extreme case, material has collapsed almost to a point
        // see [Irving 04], p. 4
        U.set(1.0, 0.0, 0.0,
              0.0, 1.0, 0.0,
              0.0, 0.0, 1.0);
    }
    else
    {
        int done = 0;
        for(int dim=0; dim<3; dim++)
        {
            int dimA = dim;
            int dimB = (dim + 1) % 3;
            int dimC = (dim + 2) % 3;
            if ((Fhat[dimB] < modifiedSVD_singularValue_eps) && (Fhat[dimC] < modifiedSVD_singularValue_eps))
            {
                // only the column dimA can be trusted, columns dimB and dimC correspond to tiny singular values
                Vec3d tmpVec1(U[0][dimA], U[1][dimA], U[2][dimA]); // column dimA
                Vec3d tmpVec2;
                FindOrthonormalVector(tmpVec1, tmpVec2);
                Vec3d tmpVec3 = norm(cross(tmpVec1, tmpVec2));
                U[0][dimB] = tmpVec2[0];
                U[1][dimB] = tmpVec2[1];
                U[2][dimB] = tmpVec2[2];
                U[0][dimC] = tmpVec3[0];
                U[1][dimC] = tmpVec3[1];
                U[2][dimC] = tmpVec3[2];
                if (det(U) < 0.0)
                {
                    U[0][dimB] *= -1.0;
                    U[1][dimB] *= -1.0;
                    U[2][dimB] *= -1.0;
                }
                done = 1;
                break; // out of for
            }
        }

        if (!done)
        {
            for(int dim=0; dim<3; dim++)
            {
                int dimA = dim;
                int dimB = (dim + 1) % 3;
                int dimC = (dim + 2) % 3;

                if (Fhat[dimA] < modifiedSVD_singularValue_eps)
                {
                    // columns dimB and dimC are both good, but column dimA corresponds to a tiny singular value
                    Vec3d tmpVec1(U[0][dimB], U[1][dimB], U[2][dimB]); // column dimB
                    Vec3d tmpVec2(U[0][dimC], U[1][dimC], U[2][dimC]); // column dimC
                    Vec3d tmpVec3 = norm(cross(tmpVec1, tmpVec2));
                    U[0][dimA] = tmpVec3[0];
                    U[1][dimA] = tmpVec3[1];
                    U[2][dimA] = tmpVec3[2];
                    if (det(U) < 0.0)
                    {
                        U[0][dimA] *= -1.0;
                        U[1][dimA] *= -1.0;
                        U[2][dimA] *= -1.0;
                    }
                    done = 1;
                    break; // out of for
                }
            }
        }

        if (!done)
        {
            // Handle situation:
            // 3. Tet is inverted.
            //    - check if det(U) == -1
            //    - If yes, then negate the minimal element of Fhat
            //      and the corresponding column of U

            double detU = det(U);
            if (detU < 0.0)
            {
                // tet is inverted
                // find smallest singular value (they are all non-negative)
                int smallestSingularValueIndex = 0;
                for(int dim=1; dim<3; dim++)
                    if (Fhat[dim] < Fhat[smallestSingularValueIndex])
                        smallestSingularValueIndex = dim;

                // negate smallest singular value
                Fhat[smallestSingularValueIndex] *= -1.0;
                U[0][smallestSingularValueIndex] *= -1.0;
                U[1][smallestSingularValueIndex] *= -1.0;
                U[2][smallestSingularValueIndex] *= -1.0;
            }
        }
    }

    /*
      printf("U = \n");
      U.print();
      printf("Fhat = \n");
      Fhat.print();
      printf("V = \n");
      V.print();
    */

    return 0;
}