From 833ee6bdb414cea384e5634f9ffc809b4510509a Mon Sep 17 00:00:00 2001
From: Thomas Toulorge <thomas.toulorge@mines-paristech.fr>
Date: Fri, 20 Apr 2012 14:27:19 +0000
Subject: [PATCH] Fixed scaling problem by preconditioning with scales based on
 parametrization gradients

---
 contrib/HighOrderMeshOptimizer/OptHOM.cpp     | 83 ++++++++++++++-----
 contrib/HighOrderMeshOptimizer/OptHOM.h       |  4 +-
 contrib/HighOrderMeshOptimizer/OptHomMesh.cpp | 19 +++++
 contrib/HighOrderMeshOptimizer/OptHomMesh.h   |  2 +
 4 files changed, 83 insertions(+), 25 deletions(-)

diff --git a/contrib/HighOrderMeshOptimizer/OptHOM.cpp b/contrib/HighOrderMeshOptimizer/OptHOM.cpp
index 355a0c443f..18fc68ee70 100644
--- a/contrib/HighOrderMeshOptimizer/OptHOM.cpp
+++ b/contrib/HighOrderMeshOptimizer/OptHOM.cpp
@@ -149,45 +149,82 @@ void printProgressFunc(const alglib::real_1d_array &x, double Obj, void *HOInst)
 
 
 
+void OptHOM::calcScale(alglib::real_1d_array &scale)
+{
+
+  scale.setlength(mesh.nPC());
+
+  // Calculate scale
+  for (int iFV = 0; iFV < mesh.nFV(); iFV++) {
+    std::vector<double> scaleFV(mesh.nPCFV(iFV),1.);
+    mesh.pcScale(iFV,scaleFV);
+    for (int iPC = 0; iPC < mesh.nPCFV(iFV); iPC++) scale[mesh.indPCFV(iFV,iPC)] = scaleFV[iPC];
+  }
+
+  // Normalize scale vector (otherwise ALGLIB routines may fail)
+  double scaleNormSq = 0.;
+  for (int i = 0; i < mesh.nPC(); i++) scaleNormSq += scale[i]*scale[i];
+  const double scaleNorm = sqrt(scaleNormSq);
+  for (int i = 0; i < mesh.nPC(); i++) scale[i] /= scaleNorm;
+
+}
+
+
+
+
 void OptHOM::OptimPass(alglib::real_1d_array &x, const alglib::real_1d_array &initGradObj, int itMax)
 {
 
   static const double EPSG = 0.;
   static const double EPSF = 0.;
   static const double EPSX = 0.;
+  static int OPTMETHOD = 1;
 
   std::cout << "--- Optimization pass with jacBar = " << jacBar << ", lambda = " << lambda << ", lambda2 = " << lambda2 << std::endl;
 
-//  alglib::minlbfgsstate state;
-//  alglib::minlbfgsreport rep;
-  alglib::mincgstate state;
-  alglib::mincgreport rep;
-
-//  minlbfgscreate(3, x, state);
-//  minlbfgssetcond(state, EPSG, EPSF, EPSX, itMax);
-//  minlbfgssetxrep(state, true);
-  mincgcreate(x, state);
-  mincgsetcond(state, EPSG, EPSF, EPSX, itMax);
-  mincgsetxrep(state, true);
-
   iter = 0;
 
-//  alglib::minlbfgsoptimize(state, evalObjGradFunc, printProgressFunc, this);
-  alglib::mincgoptimize(state, evalObjGradFunc, printProgressFunc, this);
-
-//  minlbfgsresults(state, x, rep);
-  mincgresults(state, x, rep);
+  int iterationscount = 0, nfev = 0, terminationtype = -1;
+  if (OPTMETHOD == 1) {
+    alglib::mincgstate state;
+    alglib::mincgreport rep;
+    mincgcreate(x, state);
+    alglib::real_1d_array scale;
+    calcScale(scale);
+    mincgsetscale(state,scale);
+    mincgsetprecscale(state);
+    mincgsetcond(state, EPSG, EPSF, EPSX, itMax);
+    mincgsetxrep(state, true);
+    alglib::mincgoptimize(state, evalObjGradFunc, printProgressFunc, this);
+    mincgresults(state, x, rep);
+    iterationscount = rep.iterationscount;
+    nfev = rep.nfev;
+    terminationtype = rep.terminationtype;
+  }
+  else {
+    alglib::minlbfgsstate state;
+    alglib::minlbfgsreport rep;
+    minlbfgscreate(3, x, state);
+    alglib::real_1d_array scale;
+    calcScale(scale);
+    minlbfgssetscale(state,scale);
+    minlbfgssetprecscale(state);
+    minlbfgssetcond(state, EPSG, EPSF, EPSX, itMax);
+    minlbfgssetxrep(state, true);
+    alglib::minlbfgsoptimize(state, evalObjGradFunc, printProgressFunc, this);
+    minlbfgsresults(state, x, rep);
+    iterationscount = rep.iterationscount;
+    nfev = rep.nfev;
+    terminationtype = rep.terminationtype;
+  }
 
-  std::cout << "Optimization finalized after " << rep.iterationscount << " iterations (" << rep.nfev << " functions evaluations)";
-  switch(int(rep.terminationtype)) {
-//  case -2: std::cout << ", because rounding errors prevented further improvement"; break;
-//  case -1: std::cout << ", because incorrect parameters were specified"; break;
+  std::cout << "Optimization finalized after " << iterationscount << " iterations (" << nfev << " functions evaluations)";
+  switch(int(terminationtype)) {
   case 1: std::cout << ", because relative function improvement is no more than EpsF"; break;
   case 2: std::cout << ", because relative step is no more than EpsX"; break;
   case 4: std::cout << ", because gradient norm is no more than EpsG"; break;
   case 5: std::cout << ", because the maximum number of steps was taken"; break;
-//  case 7: std::cout << ", because stopping conditions are too stringent, further improvement is impossible"; break;
-  default: std::cout << " with code " << int(rep.terminationtype); break;
+  default: std::cout << " with code " << int(terminationtype); break;
   }
   std::cout << "." << std::endl;
 
diff --git a/contrib/HighOrderMeshOptimizer/OptHOM.h b/contrib/HighOrderMeshOptimizer/OptHOM.h
index e16620958d..29880cfb16 100644
--- a/contrib/HighOrderMeshOptimizer/OptHOM.h
+++ b/contrib/HighOrderMeshOptimizer/OptHOM.h
@@ -39,11 +39,12 @@ private:
   double initObj, initMaxDist, initAvgDist;  // Values for reporting
   double minJac, maxJac, maxDist, avgDist;  // Values for reporting
 
-  inline double setBarrierTerm(double jacBarrier) { bTerm = jacBarrier/(1.-jacBarrier); }
+  inline void setBarrierTerm(double jacBarrier) { bTerm = jacBarrier/(1.-jacBarrier); }
   inline double compute_f(double v);
   inline double compute_f1(double v);
   bool addJacObjGrad(double &Obj, alglib::real_1d_array &gradObj);
   bool addDistObjGrad(double Fact, double Fact2, double &Obj, alglib::real_1d_array &gradObj);
+  void calcScale(alglib::real_1d_array &scale);
   void OptimPass(alglib::real_1d_array &x, const alglib::real_1d_array &initGradObj, int itMax);
 
 };
@@ -83,7 +84,6 @@ inline double OptHOM::compute_f1(double v)
 void OptHOM::getJacDist(double &minJ, double &maxJ, double &maxD, double &avgD)
 {
   minJ = minJac; maxJ = maxJac; maxD = maxDist; avgD = avgDist;
-  std::cout << "getJacDist: minJac = " << minJac << ", maxJac = " << maxJac << ", maxDist = " << maxDist << ", avgDist = " << avgDist << std::endl;
 }
 
 
diff --git a/contrib/HighOrderMeshOptimizer/OptHomMesh.cpp b/contrib/HighOrderMeshOptimizer/OptHomMesh.cpp
index 58885217fc..3b03ad6ecf 100644
--- a/contrib/HighOrderMeshOptimizer/OptHomMesh.cpp
+++ b/contrib/HighOrderMeshOptimizer/OptHomMesh.cpp
@@ -440,6 +440,25 @@ void Mesh::gradScaledJac(int iEl, std::vector<double> &gSJ)
 
 
 
+void Mesh::pcScale(int iFV, std::vector<double> &scale)
+{
+
+  // Calc. derivative of x, y & z w.r.t. parametric coordinates
+  const SPoint3 dX(1.,0.,0.), dY(0.,1.,0.), dZ(0.,0.,1.);
+  SPoint3 gX, gY, gZ;
+  _pc->gXyz2gUvw(_freeVert[iFV],_uvw[iFV],dX,gX);
+  _pc->gXyz2gUvw(_freeVert[iFV],_uvw[iFV],dY,gY);
+  _pc->gXyz2gUvw(_freeVert[iFV],_uvw[iFV],dZ,gZ);
+
+  // Scale = inverse norm. of vector (dx/du, dy/du, dz/du)
+  scale[0] = 1./sqrt(gX[0]*gX[0]+gY[0]*gY[0]+gZ[0]*gZ[0]);
+  if (_nPCFV[iFV] >= 2) scale[1] = 1./sqrt(gX[1]*gX[1]+gY[1]*gY[1]+gZ[1]*gZ[1]);
+  if (_nPCFV[iFV] == 3) scale[2] = 1./sqrt(gX[2]*gX[2]+gY[2]*gY[2]+gZ[2]*gZ[2]);
+
+}
+
+
+
 void Mesh::writeMSH(const char *filename)
 {
   FILE *f = fopen(filename, "w");
diff --git a/contrib/HighOrderMeshOptimizer/OptHomMesh.h b/contrib/HighOrderMeshOptimizer/OptHomMesh.h
index 20329df038..2e2741fbb4 100644
--- a/contrib/HighOrderMeshOptimizer/OptHomMesh.h
+++ b/contrib/HighOrderMeshOptimizer/OptHomMesh.h
@@ -37,6 +37,8 @@ public:
   inline double distSq(int iFV);
   inline void gradDistSq(int iV, std::vector<double> &gDSq);
 
+  void pcScale(int iFV, std::vector<double> &scale);          // Calc. scale of parametric coordinates for preconditioning
+
   void getUvw(double *it);
   void updateMesh(const double *it);
   void distSqToStraight(std::vector<double> &dSq);
-- 
GitLab