Commit: 629688763674aaeef26e03e7f74b431656cec887
Author: over0219
Date:   Mon Jul 20 19:24:34 2020 -0500
Branches: soc-2020-soft-body
https://developer.blender.org/rB629688763674aaeef26e03e7f74b431656cec887

added nonlinear local step

===================================================================

M       extern/softbody/src/admmpd_collision.h
M       extern/softbody/src/admmpd_energy.cpp
M       extern/softbody/src/admmpd_energy.h
M       extern/softbody/src/admmpd_types.h

===================================================================

diff --git a/extern/softbody/src/admmpd_collision.h 
b/extern/softbody/src/admmpd_collision.h
index 43660bf8ee5..4d917ccfc72 100644
--- a/extern/softbody/src/admmpd_collision.h
+++ b/extern/softbody/src/admmpd_collision.h
@@ -35,7 +35,7 @@ public:
             floor_z(-std::numeric_limits<double>::max()),
             floor_collision(true),
             obs_collision(true),
-            self_collision(true)
+            self_collision(false)
             {}
     } settings;
 
diff --git a/extern/softbody/src/admmpd_energy.cpp 
b/extern/softbody/src/admmpd_energy.cpp
index a454bf1a86c..6189abf5bcd 100644
--- a/extern/softbody/src/admmpd_energy.cpp
+++ b/extern/softbody/src/admmpd_energy.cpp
@@ -3,11 +3,12 @@
 
 #include "admmpd_energy.h"
 #include <iostream>
+#include <Eigen/Eigenvalues> 
 
 namespace admmpd {
 using namespace Eigen;
 
-Lame::Lame() : m_model(0)
+Lame::Lame() : m_model(ELASTIC_NH)
 {
        set_from_youngs_poisson(10000000,0.399);
 }
@@ -31,12 +32,12 @@ void EnergyTerm::signed_svd(
        V = svd.matrixV();
        Matrix3d J = Matrix3d::Identity();
        J(2,2) = -1.0;
-       if( U.determinant() < 0.0 )
+       if (U.determinant() < 0.0)
        {
                U = U * J;
                S[2] = -S[2];
        }
-       if( V.determinant() < 0.0 )
+       if (V.determinant() < 0.0)
        {
                Matrix3d Vt = V.transpose();
                Vt = J * Vt;
@@ -63,15 +64,29 @@ void EnergyTerm::update(
        Matrix3d U, V;
        Vector3d S;
        signed_svd(zi, U, S, V);
-       S = Vector3d::Ones();
+       Vector3d s0 = S;
 
-       Matrix3d p = U * S.asDiagonal() * V.transpose();
-       double k = lame.m_bulk_mod;
-       double kv = k * rest_volume;
-       double w2 = weight*weight;
-       zi = (kv*p + w2*zi) / (w2 + kv);
-       ui += (Dix - zi);
+       switch (lame.m_model)
+       {
+               default:
+               case ELASTIC_ARAP:
+               {
+                       S = Vector3d::Ones();
+                       double k = lame.m_bulk_mod;
+                       double kv = k * rest_volume;
+                       double w2 = weight*weight;
+                       Matrix3d p = U * S.asDiagonal() * V.transpose();
+                       zi = (kv*p + w2*zi) / (w2 + kv);
+               } break;
+               case ELASTIC_NH:
+               {
+                       S = Vector3d::Ones();
+                       solve_prox(index,lame,s0,S);
+                       zi = U * S.asDiagonal() * V.transpose();
+               } break;
+       }
 
+       ui += (Dix - zi);
        u->block<3,3>(index,0) = ui;
        z->block<3,3>(index,0) = zi;
 
@@ -92,7 +107,7 @@ int EnergyTerm::init_tet(
        edges.col(2) = x->row(prim[3]) - x->row(prim[0]);
        Matrix<double,3,3> edges_inv = edges.inverse();
        volume = edges.determinant() / 6.0f;
-       if( volume < 0 )
+       if (volume < 0)
        {
                printf("**admmpd::EnergyTerm Error: Inverted initial tet: 
%f\n",volume);
                return 0;
@@ -106,12 +121,187 @@ int EnergyTerm::init_tet(
        Eigen::Matrix<double,3,4> Dt = D.transpose();
        int rows[3] = { index+0, index+1, index+2 };
        int cols[4] = { prim[0], prim[1], prim[2], prim[3] };
-       for( int r=0; r<3; ++r ){
-               for( int c=0; c<4; ++c ){
+       for( int r=0; r<3; ++r )
+       {
+               for( int c=0; c<4; ++c )
+               {
                        triplets.emplace_back(rows[r], cols[c], Dt(r,c));
                }
        }
        return 3;
 }
 
+void EnergyTerm::solve_prox(
+               int index,
+               const Lame &lame,
+               const Eigen::Vector3d &s0,
+               Eigen::Vector3d &s)
+{
+
+       Vector3d g; // gradient
+       Vector3d p; // descent
+       Vector3d s_prev;
+       Matrix3d H = Matrix3d::Identity();
+       double energy_k, energy_k1;
+       const bool add_admm_pen = true;
+       const double eps = 1e-6;
+
+       int iter = 0;
+       for(; iter<10; ++iter)
+       {
+               g.setZero();
+               energy_k = energy_density(lame,add_admm_pen,s0,s,&g); // e and g
+               if (g.norm() < eps)
+                       break;
+
+               hessian_spd(lame,add_admm_pen,s,H);
+               p = H.ldlt().solve(-g); // Newton step direction
+
+               s_prev = s;
+               s = s_prev + p;
+               energy_k1 = energy_density(lame,add_admm_pen,s0,s);
+
+               // Backtracking line search
+               double alpha = 1;
+               int ls_iter = 0;
+               const int max_ls_iter = 20;
+               while(energy_k1>energy_k && ls_iter<max_ls_iter)
+               {
+                       alpha *= 0.5;
+                       s = s_prev + alpha*p;
+                       energy_k1 = energy_density(lame,add_admm_pen,s0,s);
+                       ls_iter++;
+               }
+
+               // Sometimes flattened tets will have a hard time
+               // uninverting, in which case they get linesearch
+               // blocked. There are ways around this, but for now
+               // simply quitting is sufficient.
+               if (ls_iter>=max_ls_iter)
+               {
+                       s = s_prev;
+                       break;
+               }
+
+               if ((s-s_prev).norm() < eps)
+                       break;
+
+       } // end Newton iterations
+
+       if (std::isnan(s[0]) || std::isnan(s[1]) || std::isnan(s[2]))
+               throw std::runtime_error("*EnergyTerm::solve_prox: Got nans");
+
+} // end solve prox
+
+double EnergyTerm::energy_density(
+       const Lame &lame,
+       bool add_admm_penalty,
+       const Eigen::Vector3d &s0,
+       const Eigen::Vector3d &s,
+       Eigen::Vector3d *g)
+{
+       double e = 0;
+
+       // Compute energy and gradient
+       // 
https://github.com/mattoverby/admm-elastic/blob/master/src/TetEnergyTerm.cpp
+       // I suppose I should add ARAP for completeness even though it's not 
used
+       switch (lame.m_model)
+       {
+               default: {
+                       if (g != nullptr) g->setZero();
+               } break;
+
+               case ELASTIC_NH: {
+                       if (s.minCoeff() <= 0)
+                       { // barrier energy to prevent inversions
+                               if (g != nullptr) g->setZero();
+                               return std::numeric_limits<float>::max();
+                       }
+                       double J = s.prod();
+                       double I_1 = s.dot(s);
+                       double log_I3 = std::log(J*J);
+                       double t1 = 0.5 * lame.m_mu * (I_1-log_I3-3.0);
+                       double t2 = 0.125 * lame.m_lambda * log_I3 * log_I3;
+                       e = t1 + t2;
+                       if (g != nullptr)
+                       {
+                               Vector3d s_inv = s.cwiseInverse();
+                               *g = lame.m_mu*(s-s_inv) + 
lame.m_lambda*std::log(J)*s_inv;
+                       }
+               } break;
+       }
+
+       if (add_admm_penalty)
+       {
+               const double &k = lame.m_bulk_mod;
+               Vector3d s_min_s0 = s-s0;
+               e += (k*0.5) * s_min_s0.squaredNorm();
+               if (g != nullptr) *g = *g + k*s_min_s0;
+       }
+
+       return e;
+
+} // end energy
+
+void EnergyTerm::hessian_spd(
+               const Lame &lame,
+               bool add_admm_penalty,
+               const Eigen::Vector3d &s,
+               Eigen::Matrix3d &H)
+{
+       static const Matrix3d I = Matrix3d::Identity();
+
+       // Compute specific Hessian
+       switch (lame.m_model)
+       {
+               default:
+               {
+                       H.setIdentity();
+               } break;
+
+               case ELASTIC_NH:
+               {
+                       double J = s.prod();
+                       Vector3d s_inv = s.cwiseInverse();
+                       Matrix3d P = Matrix3d::Zero();
+                       P(0,0) = 1.0 / (s[0]*s[0]);
+                       P(1,1) = 1.0 / (s[1]*s[1]);
+                       P(2,2) = 1.0 / (s[2]*s[2]);
+                       H =     lame.m_mu*(I - 2.0*P) +
+                               lame.m_lambda * std::log(J) * P +
+                               lame.m_lambda * s_inv * s_inv.transpose();
+               } break;
+       }
+
+       // ADMM penalty
+       if(add_admm_penalty)
+       {
+               const double &k = lame.m_bulk_mod;
+               for (int i=0; i<3; ++i)
+                       H(i,i) += k;
+       }
+
+       // Projects a matrix to nearest SPD
+       // 
https://github.com/penn-graphics-research/DOT/blob/master/src/Utils/IglUtils.hpp
+       auto project_spd = [](Matrix3d &H_)
+       {
+               SelfAdjointEigenSolver<Matrix3d> eigenSolver(H_);
+               if (eigenSolver.eigenvalues()[0] >= 0.0)
+                       return;
+               Eigen::DiagonalMatrix<double,3> D(eigenSolver.eigenvalues());
+               for (int i = 0; i<3; ++i)
+               {
+                       if (D.diagonal()[i] < 0)
+                               D.diagonal()[i] = 0;
+                       else
+                               break;
+               }
+               H_ = 
eigenSolver.eigenvectors()*D*eigenSolver.eigenvectors().transpose();
+       };
+
+       project_spd(H);
+
+} // end hessian
+
+
 } // end namespace mcl
diff --git a/extern/softbody/src/admmpd_energy.h 
b/extern/softbody/src/admmpd_energy.h
index 6a659613661..55a15377a2e 100644
--- a/extern/softbody/src/admmpd_energy.h
+++ b/extern/softbody/src/admmpd_energy.h
@@ -10,9 +10,15 @@
 
 namespace admmpd {
 
+enum ELASTIC_ENERGY
+{
+       ELASTIC_ARAP, // As-rigid-as-possible
+       ELASTIC_NH // NeoHookean
+};
+
 class Lame {
 public:
-       int m_model; // 0=ARAP
+       ELASTIC_ENERGY m_model;
        double m_mu;
        double m_lambda;
        double m_bulk_mod;
@@ -50,6 +56,30 @@ public:
                double &weight,
                std::vector< Eigen::Triplet<double> > &triplets);
 
+       // Solves proximal energy function
+       void solve_prox(
+               int index,
+               const Lame &lame,
+               const Eigen::Vector3d &s0,
+               Eigen::Vector3d &s);
+
+       // Returns gradient and energy (+ADMM penalty)
+       // of a material evaluated at s (singular values).
+       double energy_density(
+               const Lame &lame,
+               bool add_admm_penalty,
+               const Eigen::Vector3d &s0,
+               const Eigen::Vector3d &s,
+               Eigen::Vector3d *g=nullptr);
+
+       // Returns the Hessian of a material at S
+       // projected to nearest SPD
+       void hessian_spd(
+               const Lame &lame,
+               bool add_admm_penalty,
+               const Eigen::Vector3d &s,
+               Eigen::Matrix3d &H);
+
 };
 
 } // end namespace admmpd
diff --git a/extern/softbody/src/admmpd_types.h 
b/extern/softbody/src/admmpd_types.h
index 2e70d727aea..232587576c2 100644
--- a/extern/softbody/src/admmpd_types.h
+++ b/extern/softbody/src/admmpd_types.h
@@ -28,7 +28,7 @@ struct Options {
     double poisson; // Poisson ratio // TODO variable per-tet
     Eigen::Vector3d grav;
     Options() :
-        timestep_s(1.0/100.0),
+        timestep_s(1.0/24.0),
         max_admm_iters(30),
         max_cg_iters(10),
         max_gs_iters(100),
@@ -37,7 +37,7 @@ struct Options {
         mult_pk(0.01),
         min_res(1e-8),
         youngs(1000000),
-        poisson(0.299),
+        poisson(0.199),
         grav(0,0,-9.8)
         {}
 };

_______________________________________________
Bf-blender-cvs mailing list
[email protected]
https://lists.blender.org/mailman/listinfo/bf-blender-cvs

Reply via email to