Ejemplo n.º 1
0
void ConjugateGradient::initialize(PatchData &pd, MsqError &err)
{
  if (get_parallel_size())
    MSQ_DBGOUT(2) << "\nP[" << get_parallel_rank() << "] " << "o   Performing Conjugate Gradient optimization.\n";
  else
    MSQ_DBGOUT(2) << "\no   Performing Conjugate Gradient optimization.\n";
  pMemento=pd.create_vertices_memento(err);
}
Ejemplo n.º 2
0
std::string process_itaps_error( int ierr )
{
  std::string result( "ITAPS ERROR: " );
  switch (ierr) {
    case iBase_MESH_ALREADY_LOADED:      result += "File Already Loaded";   break;
    case iBase_NO_MESH_DATA:             result += "No Mesh";               break;
    case iBase_FILE_NOT_FOUND:           result += "File Not Found";        break;
    case iBase_FILE_WRITE_ERROR:         result += "File Write Error";      break;
    case iBase_NIL_ARRAY:                result += "NULL Array";            break;
    case iBase_BAD_ARRAY_SIZE:           result += "Bad Array Size";        break;
    case iBase_BAD_ARRAY_DIMENSION:      result += "Bad Array Dimension";   break;
    case iBase_INVALID_ENTITY_HANDLE:    result += "Invalid Handle";        break;
    case iBase_INVALID_ENTITY_COUNT:     result += "Invalid Count";         break;
    case iBase_INVALID_ENTITY_TYPE:      result += "Invalid Type";          break;
    case iBase_INVALID_ENTITY_TOPOLOGY:  result += "Invalid Topology";      break;
    case iBase_BAD_TYPE_AND_TOPO:        result += "Invalid Type";          break;
    case iBase_ENTITY_CREATION_ERROR:    result += "Creation Failed";       break;
    case iBase_INVALID_TAG_HANDLE:       result += "Invalid Tag";           break;
    case iBase_TAG_NOT_FOUND:            result += "Tag Not Found";         break;
    case iBase_TAG_ALREADY_EXISTS:       result += "Tag Exists";            break;
    case iBase_TAG_IN_USE:               result += "Tag In Use";            break;
    case iBase_INVALID_ENTITYSET_HANDLE: result += "Invalid Handle";        break;
    case iBase_INVALID_ITERATOR_HANDLE:  result += "Invalid Iterator";      break;
    case iBase_INVALID_ARGUMENT:         result += "Invalid Argument";      break;
    case iBase_MEMORY_ALLOCATION_FAILED: result += "Out of Memory";         break;
    case iBase_NOT_SUPPORTED:            result += "Not Supported";         break;
    default:                             result += "Uknown/Internal Error"; break;
  }
  MSQ_DBGOUT(1) << result << std::endl;
  return result;
}
void ShapeImprovementWrapper::run_wrapper( Mesh* mesh,
                                           ParallelMesh* pmesh,
                                           MeshDomain* domain,
                                           Settings* settings,
                                           QualityAssessor* qa,
                                           MsqError& err )
{
    // Define an untangler
  UntangleBetaQualityMetric untangle_metric( untBeta );
  LPtoPTemplate untangle_func( 2, &untangle_metric );
  ConjugateGradient untangle_global( &untangle_func );
  TerminationCriterion untangle_inner, untangle_outer;
  untangle_global.use_global_patch();
  untangle_inner.add_absolute_quality_improvement( 0.0 );
  untangle_inner.add_absolute_successive_improvement( successiveEps );
  untangle_outer.add_iteration_limit( 1 );
  untangle_global.set_inner_termination_criterion( &untangle_inner );
  untangle_global.set_outer_termination_criterion( &untangle_outer );

    // define shape improver
  IdealWeightInverseMeanRatio inverse_mean_ratio;
  inverse_mean_ratio.set_averaging_method( QualityMetric::LINEAR );
  LPtoPTemplate obj_func( 2, &inverse_mean_ratio );
  FeasibleNewton feas_newt( &obj_func );
  TerminationCriterion term_inner, term_outer;
  feas_newt.use_global_patch();
  qa->add_quality_assessment( &inverse_mean_ratio );
  term_inner.add_absolute_gradient_L2_norm( gradNorm );
  term_inner.add_relative_successive_improvement( successiveEps );
  term_outer.add_iteration_limit( pmesh ? parallelIterations : 1 );
  feas_newt.set_inner_termination_criterion( &term_inner );
  feas_newt.set_outer_termination_criterion( &term_outer );

    // Apply CPU time limit to untangler
  if (maxTime > 0.0)
    untangle_inner.add_cpu_time( maxTime );
  
    // Run untangler
  InstructionQueue q1;
  Timer totalTimer;
  q1.set_master_quality_improver( &untangle_global, err ); MSQ_ERRRTN(err);
  q1.add_quality_assessor( qa, err ); MSQ_ERRRTN(err);
  q1.run_common( mesh, pmesh, domain, settings, err ); MSQ_ERRRTN(err);
  
    // If limited by CPU time, limit next step to remaning time
  if (maxTime > 0.0) {
    double remaining = maxTime - totalTimer.since_birth();
    if (remaining <= 0.0 ){
      MSQ_DBGOUT(2) << "Optimization is terminating without perfoming shape improvement." << std::endl;
      remaining = 0.0;
    }
    term_inner.add_cpu_time( remaining );
  }
  
    // Run shape improver
  InstructionQueue q2;
  q2.set_master_quality_improver( &feas_newt, err ); MSQ_ERRRTN(err);
  q2.add_quality_assessor( qa, err ); MSQ_ERRRTN(err);
  q2.run_common( mesh, pmesh, domain, settings, err ); MSQ_ERRRTN(err);
}
Ejemplo n.º 4
0
/*!Performs Conjugate gradient minimization on the PatchData, pd.*/
void ConjugateGradient::optimize_vertex_positions(PatchData &pd, 
                                                MsqError &err){
  // pd.reorder();

  MSQ_FUNCTION_TIMER( "ConjugateGradient::optimize_vertex_positions" );

  Timer c_timer;
  size_t num_vert=pd.num_free_vertices();
  if(num_vert<1){
     MSQ_DBGOUT(1) << "\nEmpty free vertex list in ConjugateGradient\n";
     return;
  }
/*  
    //zero out arrays
  int zero_loop=0;
  while(zero_loop<arraySize){
    fGrad[zero_loop].set(0,0,0);
    pGrad[zero_loop].set(0,0,0);
    fNewGrad[zero_loop].set(0,0,0);
    ++zero_loop;
  }
*/
  
  // get OF evaluator
  OFEvaluator& objFunc = get_objective_function_evaluator();
  
  size_t ind;
    //Michael cull list:  possibly set soft_fixed flags here
  
   //MsqFreeVertexIndexIterator free_iter(pd, err);  MSQ_ERRRTN(err);
  
      
   double f=0;
   //Michael, this isn't equivalent to CUBIT because we only want to check
   //the objective function value of the 'bad' elements
   //if invalid initial patch set an error.
   bool temp_bool = objFunc.update(pd, f, fGrad, err);
   assert(fGrad.size() == num_vert);
   if(MSQ_CHKERR(err))
      return;
  if( ! temp_bool){
    MSQ_SETERR(err)("Conjugate Gradient not able to get valid gradient "
                    "and function values on intial patch.", 
                    MsqError::INVALID_MESH);
    return;
  }
  double grad_norm=MSQ_MAX_CAP;
  
  if(conjGradDebug>0){
    MSQ_PRINT(2)("\nCG's DEGUB LEVEL = %i \n",conjGradDebug);
    grad_norm=Linf(arrptr(fGrad),fGrad.size());
    MSQ_PRINT(2)("\nCG's FIRST VALUE = %f,grad_norm = %f",f,grad_norm);
    MSQ_PRINT(2)("\n   TIME %f",c_timer.since_birth());
    grad_norm=MSQ_MAX_CAP;
  }

    //Initializing pGrad (search direction).  
  pGrad.resize(fGrad.size());
  for (ind = 0; ind < num_vert; ++ind)
    pGrad[ind]=(-fGrad[ind]);

  int j=0; // total nb of step size changes ... not used much
  int i=0; // iteration counter
  unsigned m=0; // 
  double alp=MSQ_MAX_CAP; // alp: scale factor of search direction
    //we know inner_criterion is false because it was checked in
    //loop_over_mesh before being sent here.
  TerminationCriterion* term_crit=get_inner_termination_criterion();
  
    //while ((i<maxIteration && alp>stepBound && grad_norm>normGradientBound)
    //     && !inner_criterion){
  while(!term_crit->terminate()){
    ++i;
      //std::cout<<"\Michael delete i = "<<i;
    int k=0;
    alp=get_step(pd,f,k,err);
    j+=k;
    if(conjGradDebug>2){
      MSQ_PRINT(2)("\n  Alp initial, alp = %20.18f",alp);
    }

     // if alp == 0, revert to steepest descent search direction
    if(alp==0){
      for (m = 0; m < num_vert; ++m) {
        pGrad[m]=(-fGrad[m]);
      }
      alp=get_step(pd,f,k,err);
      j+=k;
      if(conjGradDebug>1){
        MSQ_PRINT(2)("\n CG's search direction reset.");
        if(conjGradDebug>2)
          MSQ_PRINT(2)("\n  Alp was zero, alp = %20.18f",alp);
      }
      
    }
    if(alp!=0){
      pd.move_free_vertices_constrained( arrptr(pGrad), num_vert, alp, err );
      MSQ_ERRRTN(err);
      
      if (! objFunc.update(pd, f, fNewGrad, err)){
        MSQ_SETERR(err)("Error inside Conjugate Gradient, vertices moved "
                        "making function value invalid.", 
                        MsqError::INVALID_MESH);
        return;
      }
      assert(fNewGrad.size() == (unsigned)num_vert);
      
      if(conjGradDebug>0){
        grad_norm=Linf(arrptr(fNewGrad),num_vert);
        MSQ_PRINT(2)("\nCG's VALUE = %f,  iter. = %i,  grad_norm = %f,  alp = %f",f,i,grad_norm,alp);
        MSQ_PRINT(2)("\n   TIME %f",c_timer.since_birth());
      }
      double s11=0;
      double s12=0;
      double s22=0;
      //free_iter.reset();
      //while (free_iter.next()) {
      //  m=free_iter.value();
      for (m = 0; m < num_vert; ++m) {
        s11+=fGrad[m]%fGrad[m];
        s12+=fGrad[m]%fNewGrad[m];
        s22+=fNewGrad[m]%fNewGrad[m];
      }
      
        // Steepest Descent (takes 2-3 times as long as P-R)
        //double bet=0;
      
        // Fletcher-Reeves (takes twice as long as P-R)
        //double bet = s22/s11;

        // Polack-Ribiere  
      double bet;  
      if (!divide( s22-s12, s11, bet ))
        return; // gradient is zero    
      //free_iter.reset();
      //while (free_iter.next()) {
      //  m=free_iter.value();
      for (m = 0; m < num_vert; ++m) {
        pGrad[m]=(-fNewGrad[m]+(bet*pGrad[m]));
        fGrad[m]=fNewGrad[m];
      }
      if(conjGradDebug>2){
        MSQ_PRINT(2)(" \nSEARCH DIRECTION INFINITY NORM = %e",
                   Linf(arrptr(fNewGrad),num_vert));
      }
      
    }//end if on alp == 0

    term_crit->accumulate_patch( pd, err ); MSQ_ERRRTN(err);
    term_crit->accumulate_inner( pd, f, arrptr(fGrad), err );  MSQ_ERRRTN(err);
  }//end while
  if(conjGradDebug>0){
    MSQ_PRINT(2)("\nConjugate Gradient complete i=%i ",i);
    MSQ_PRINT(2)("\n-  FINAL value = %f, alp=%4.2e grad_norm=%4.2e",f,alp,grad_norm);
    MSQ_PRINT(2)("\n   FINAL TIME %f",c_timer.since_birth());
  }
}
/*!  This function evaluates the needed information and then evaluates
  the termination criteria.  If any of the selected criteria are satisfied,
  the function returns true.  Otherwise, the function returns false.
 */
bool TerminationCriterion::terminate( )
{
  bool return_flag = false;
  //std::cout<<"\nInside terminate(pd,of,err):  flag = "<<terminationCriterionFlag << std::endl;
  int type=0;

    //First check for an interrupt signal
  if (MsqInterrupt::interrupt())
  {
     MSQ_DBGOUT_P0_ONLY(debugLevel) << par_string() << "  o TermCrit -- INTERRUPTED" << " " << std::endl;
    return true;
  }
  
    //if terminating on numbering of inner iterations
  if (NUMBER_OF_ITERATES & terminationCriterionFlag
    && iterationCounter >= iterationBound)
  {
    return_flag = true;
    type=1;
    MSQ_DBGOUT_P0_ONLY(debugLevel) << par_string() << "  o TermCrit -- Reached " << " " << iterationBound << " iterations." << std::endl;
  }
  
  if (CPU_TIME & terminationCriterionFlag && mTimer.since_birth()>=timeBound)
  {
    return_flag=true;
    type=2;
    MSQ_DBGOUT_P0_ONLY(debugLevel) << par_string() << "  o TermCrit -- Exceeded CPU time. " << " " << std::endl;
  }
  
  
  if (MOVEMENT_FLAGS & terminationCriterionFlag
      && maxSquaredMovement >= 0.0)
  {
    MSQ_DBGOUT_P0_ONLY(debugLevel) << par_string() << "  o Info -- Maximum vertex movement: " << " "
                                   << RPM(sqrt(maxSquaredMovement)) << std::endl;

    if (VERTEX_MOVEMENT_ABSOLUTE & terminationCriterionFlag 
        && maxSquaredMovement <= vertexMovementAbsoluteEps)
    {
      MSQ_DBGOUT_P0_ONLY(debugLevel) << par_string() << "  o TermCrit -- VERTEX_MOVEMENT_ABSOLUTE: " << " "
                                     << RPM(sqrt(maxSquaredMovement)) << std::endl;
      return_flag = true;
    type=3;
    }
  
    if (VERTEX_MOVEMENT_RELATIVE & terminationCriterionFlag
        && maxSquaredMovement <= vertexMovementRelativeEps*maxSquaredInitialMovement)
    {
      MSQ_DBGOUT_P0_ONLY(debugLevel) << par_string() << "  o TermCrit -- VERTEX_MOVEMENT_RELATIVE: " << " "
                                     << RPM(sqrt(maxSquaredMovement)) << std::endl;
      return_flag = true;
    type=4;
    }

    if (VERTEX_MOVEMENT_ABS_EDGE_LENGTH & terminationCriterionFlag)
    {
      assert( vertexMovementAbsoluteAvgEdge > -1e-12 ); // make sure value actually got calculated
      if (maxSquaredMovement <= vertexMovementAbsoluteAvgEdge)
      {
        MSQ_DBGOUT_P0_ONLY(debugLevel) << par_string() << "  o TermCrit -- VERTEX_MOVEMENT_ABS_EDGE_LENGTH: " << " "
                                       << RPM(sqrt(maxSquaredMovement)) << std::endl;
        return_flag = true;
        type=5;
      }
    }
  }

  if (GRADIENT_L2_NORM_ABSOLUTE & terminationCriterionFlag &&
      currentGradL2NormSquared <= gradL2NormAbsoluteEpsSquared)
  {
    MSQ_DBGOUT_P0_ONLY(debugLevel) << par_string() << "  o TermCrit -- GRADIENT_L2_NORM_ABSOLUTE: " << " " << RPM(currentGradL2NormSquared) << std::endl;
    return_flag = true;
    type=6;
  }
  
  if (GRADIENT_INF_NORM_ABSOLUTE & terminationCriterionFlag &&
      currentGradInfNorm <= gradInfNormAbsoluteEps)
  {
    MSQ_DBGOUT_P0_ONLY(debugLevel) << par_string() << "  o TermCrit -- GRADIENT_INF_NORM_ABSOLUTE: " << " " << RPM(currentGradInfNorm) << std::endl;
    return_flag = true;
    type=7;
  }
  
  if (GRADIENT_L2_NORM_RELATIVE & terminationCriterionFlag &&
      currentGradL2NormSquared <= (gradL2NormRelativeEpsSquared * initialGradL2NormSquared))
  {
    MSQ_DBGOUT_P0_ONLY(debugLevel) << par_string() << "  o TermCrit -- GRADIENT_L2_NORM_RELATIVE: " << " " << RPM(currentGradL2NormSquared) << std::endl;
    return_flag = true;
    type=8;
  }
  
  if (GRADIENT_INF_NORM_RELATIVE & terminationCriterionFlag &&
      currentGradInfNorm <= (gradInfNormRelativeEps * initialGradInfNorm))
  {
    MSQ_DBGOUT_P0_ONLY(debugLevel) << par_string() << "  o TermCrit -- GRADIENT_INF_NORM_RELATIVE: " << " " << RPM(currentGradInfNorm) << std::endl;
    return_flag = true;
    type=9;
  }
    //Quality Improvement and Successive Improvements are below.
    // The relative forms are only valid after the first iteration.
  if ((QUALITY_IMPROVEMENT_ABSOLUTE & terminationCriterionFlag) &&
      currentOFValue <= qualityImprovementAbsoluteEps)
  {
    MSQ_DBGOUT_P0_ONLY(debugLevel) << par_string() << "  o TermCrit -- QUALITY_IMPROVEMENT_ABSOLUTE: " << " " << RPM(currentOFValue) << std::endl;
    return_flag = true;
    type=10;
  }
  
    //only valid if an iteration has occurred, see above.
  if(iterationCounter > 0){
    if (SUCCESSIVE_IMPROVEMENTS_ABSOLUTE & terminationCriterionFlag &&
        (previousOFValue - currentOFValue) <= successiveImprovementsAbsoluteEps)
    {  
      MSQ_DBGOUT_P0_ONLY(debugLevel) << par_string() << "  o TermCrit -- SUCCESSIVE_IMPROVEMENTS_ABSOLUTE: previousOFValue= " << " " << previousOFValue << " currentOFValue= " << RPM(currentOFValue) 
                             << " successiveImprovementsAbsoluteEps= " << successiveImprovementsAbsoluteEps
                             << std::endl;
      return_flag = true;
    type=11;
    }
    if (QUALITY_IMPROVEMENT_RELATIVE & terminationCriterionFlag &&
        (currentOFValue - lowerOFBound) <= 
        qualityImprovementRelativeEps * (initialOFValue - lowerOFBound))
    {
      MSQ_DBGOUT_P0_ONLY(debugLevel) << par_string() << "  o TermCrit -- QUALITY_IMPROVEMENT_RELATIVE: " << " " << std::endl;
      return_flag = true;
    type=12;
    }
    if (SUCCESSIVE_IMPROVEMENTS_RELATIVE & terminationCriterionFlag &&
        (previousOFValue - currentOFValue) <= 
        successiveImprovementsRelativeEps * (initialOFValue - currentOFValue))
    {
      MSQ_DBGOUT_P0_ONLY(debugLevel) << par_string() << "  o TermCrit -- SUCCESSIVE_IMPROVEMENTS_RELATIVE: previousOFValue= " << " " << previousOFValue << " currentOFValue= " << RPM(currentOFValue) 
                             << " successiveImprovementsRelativeEps= " << successiveImprovementsRelativeEps
                             << std::endl;
      return_flag = true;
    type=13;
    }
  }
  
  if (BOUNDED_VERTEX_MOVEMENT & terminationCriterionFlag && vertexMovementExceedsBound)
  {
    return_flag = true;
    type=14;
    MSQ_DBGOUT_P0_ONLY(debugLevel) << par_string() << "  o TermCrit -- " << " " << vertexMovementExceedsBound
                           << " vertices out of bounds." << std::endl;
  }
  
  if (UNTANGLED_MESH & terminationCriterionFlag)
  {
    if (innerOuterType==TYPE_OUTER) {
      //MSQ_DBGOUT_P0_ONLY(debugLevel) 
      MSQ_DBGOUT(debugLevel) 
        << par_string() << "  o Num Inverted: " << " " << globalInvertedCount << std::endl;
    }
    if (!globalInvertedCount)
      {
        MSQ_DBGOUT_P0_ONLY(debugLevel) << par_string() << "  o TermCrit -- UNTANGLED_MESH: "<< " " << std::endl;
        return_flag = true;
        type=15;
      }
  }
  
    // clear this value at the end of each iteration
  vertexMovementExceedsBound = 0;
  maxSquaredMovement = -1.0;

  if (timeStepFileType == GNUPLOT && return_flag) {
    MsqError err;
    MeshWriter::write_gnuplot_overlay( iterationCounter, timeStepFileName.c_str(), err );
  }

  if (0 && return_flag && MSQ_DBG(2))
    std::cout << "P[" << get_parallel_rank() << "] tmp TerminationCriterion::terminate: " << moniker << " return_flag= " << return_flag << " type= " << type
              << " terminationCriterionFlag= " << terminationCriterionFlag << " debugLevel= " << debugLevel << std::endl;
  
    //if none of the criteria were satisfied
  return return_flag;
}
void SteepestDescent::optimize_vertex_positions(PatchData &pd, 
                                                MsqError &err)
{
  MSQ_FUNCTION_TIMER( "SteepestDescent::optimize_vertex_positions" );

  const int SEARCH_MAX = 100;
  const double c1 = 1e-4;
  //std::vector<Vector3D> unprojected(pd.num_free_vertices()); 
  std::vector<Vector3D> gradient(pd.num_free_vertices()); 
  bool feasible=true;//bool for OF values
  double min_edge_len, max_edge_len;
  double step_size=0, original_value=0, new_value=0;
  double norm_squared=0;
  PatchDataVerticesMemento* pd_previous_coords;
  TerminationCriterion* term_crit=get_inner_termination_criterion();
  OFEvaluator& obj_func = get_objective_function_evaluator();
  
    // get vertex memento so we can restore vertex coordinates for bad steps.
  pd_previous_coords = pd.create_vertices_memento( err ); MSQ_ERRRTN(err);
    // use auto_ptr to automatically delete memento when we exit this function
  std::auto_ptr<PatchDataVerticesMemento> memento_deleter( pd_previous_coords );

    // Evaluate objective function.
    //
    // Always use 'update' version when beginning optimization so that
    // if doing block coordinate descent the OF code knows the set of
    // vertices we are modifying during the optimziation (the subset
    // of the mesh contained in the current patch.)  This has to be
    // done up-front because typically an OF will just store the portion
    // of the OF value (e.g. the numeric contribution to the sum for an
    // averaging OF) for the initial patch.
  feasible = obj_func.update( pd, original_value, gradient, err ); MSQ_ERRRTN(err);
    // calculate gradient dotted with itself
  norm_squared = length_squared( gradient );
  
    //set an error if initial patch is invalid.
  if(!feasible){
    MSQ_SETERR(err)("SteepestDescent passed invalid initial patch.",
                    MsqError::INVALID_ARG);
    return;
  }

    // use edge length as an initial guess for for step size
  pd.get_minmax_edge_length( min_edge_len, max_edge_len );
  //step_size = max_edge_len / std::sqrt(norm_squared);
  //if (!finite(step_size))  // zero-length gradient
  //  return;
//  if (norm_squared < DBL_EPSILON)
//    return;
  if (norm_squared >= DBL_EPSILON)
    step_size = max_edge_len / std::sqrt(norm_squared) * pd.num_free_vertices();

    // The steepest descent loop...
    // We loop until the user-specified termination criteria are met.
  while (!term_crit->terminate()) {
    MSQ_DBGOUT(3) << "Iteration " << term_crit->get_iteration_count() << std::endl;
    MSQ_DBGOUT(3) << "  o  original_value: " << original_value << std::endl;
    MSQ_DBGOUT(3) << "  o  grad norm suqared: " << norm_squared << std::endl;

      // Save current vertex coords so that they can be restored if
      // the step was bad.
    pd.recreate_vertices_memento( pd_previous_coords, err ); MSQ_ERRRTN(err);

      // Reduce step size until it satisfies Armijo condition
    int counter = 0;
    for (;;) {
      if (++counter > SEARCH_MAX || step_size < DBL_EPSILON) {
        MSQ_DBGOUT(3) << "    o  No valid step found.  Giving Up." << std::endl;
        return;
      }
      
      // Move vertices to new positions.
      // Note: step direction is -gradient so we pass +gradient and 
      //       -step_size to achieve the same thing.
      pd.move_free_vertices_constrained( arrptr(gradient), gradient.size(), -step_size, err ); MSQ_ERRRTN(err);
      // Evaluate objective function for new vertices.  We call the
      // 'evaluate' form here because we aren't sure yet if we want to
      // keep these vertices.  Until we call 'update', we have the option
      // of reverting a block coordinate decent objective function's state
      // to that of the initial vertex coordinates.  However, for block
      // coordinate decent to work correctly, we will need to call an
      // 'update' form if we decide to keep the new vertex coordinates.
      feasible = obj_func.evaluate( pd, new_value, err ); 
      if (err.error_code() == err.BARRIER_VIOLATED) 
        err.clear();  // barrier violated does not represent an actual error here
      MSQ_ERRRTN(err);
      MSQ_DBGOUT(3) << "    o  step_size: " << step_size << std::endl;
      MSQ_DBGOUT(3) << "    o  new_value: " << new_value << std::endl;

      if (!feasible) {
        // OF value is invalid, decrease step_size a lot
        step_size *= 0.2;
      }
      else if (new_value > original_value - c1 * step_size * norm_squared) {
        // Armijo condition not met.
        step_size *= 0.5;
      }
      else {
        // Armijo condition met, stop
        break;
      }
      
      // undo previous step : restore vertex coordinates
      pd.set_to_vertices_memento( pd_previous_coords, err );  MSQ_ERRRTN(err);
    }
   
      // Re-evaluate objective function to get gradient.
      // Calling the 'update' form here incorporates the new vertex 
      // positions into the 'accumulated' value if we are doing a 
      // block coordinate descent optimization.
    obj_func.update(pd, original_value, gradient, err ); MSQ_ERRRTN(err);
    if (projectGradient) {
      //if (cosineStep) {
      //  unprojected = gradient;
      //  pd.project_gradient( gradient, err ); MSQ_ERRRTN(err);
      //  double dot = inner_product( arrptr(gradient), arrptr(unprojected), gradient.size() );
      //  double lensqr1 = length_squared( gradient );
      //  double lensqr2 = length_squared( unprojected );
      //  double cossqr = dot * dot / lensqr1 / lensqr2;
      //  step_size *= sqrt(cossqr);
      //}
      //else {
        pd.project_gradient( gradient, err ); MSQ_ERRRTN(err);
      //}      
    }
      
      // Update terination criterion for next iteration.
      // This is necessary for efficiency.  Some values can be adjusted
      // for each iteration so we don't need to re-caculate the value
      // over the entire mesh.
    term_crit->accumulate_patch( pd, err );  MSQ_ERRRTN(err);
    term_crit->accumulate_inner( pd, original_value, arrptr(gradient), err ); MSQ_ERRRTN(err); 
      
      // Calculate initial step size for next iteration using step size 
      // from this iteration
    step_size *= norm_squared;
    norm_squared = length_squared( gradient );
//    if (norm_squared < DBL_EPSILON)
//      break;
  if (norm_squared >= DBL_EPSILON)
    step_size /= norm_squared;
  }
}
bool IdealWeightInverseMeanRatio::evaluate_with_Hessian( PatchData& pd,
                    size_t handle,
                    double& m,
                    std::vector<size_t>& indices,
                    std::vector<Vector3D>& g,
                    std::vector<Matrix3D>& h,
                    MsqError& err )
{
  const MsqMeshEntity* e = &pd.element_by_index(handle);
  EntityTopology topo = e->get_element_type();

  if (!analytical_average_hessian() &&
      topo != TRIANGLE &&
      topo != TETRAHEDRON) {
    static bool print = true;
    if (print) {
      MSQ_DBGOUT(1) << "Analyical gradient not available for selected averaging scheme. "
                    << "Using (possibly much slower) numerical approximation of gradient"
                    << " of quality metric. " << std::endl;
      print = false;
    }
    return QualityMetric::evaluate_with_Hessian( pd, handle, m, indices, g, h, err );
  }

  const MsqVertex *vertices = pd.get_vertex_array(err);  MSQ_ERRZERO(err);
  const size_t *v_i = e->get_vertex_index_array();


  Vector3D n;			// Surface normal for 2D objects

  // Prism and Hex element descriptions
  static const int locs_pri[6][4] = {{0, 1, 2, 3}, {1, 2, 0, 4},
				     {2, 0, 1, 5}, {3, 5, 4, 0},
				     {4, 3, 5, 1}, {5, 4, 3, 2}};
  static const int locs_hex[8][4] = {{0, 1, 3, 4}, {1, 2, 0, 5},
				     {2, 3, 1, 6}, {3, 0, 2, 7},
				     {4, 7, 5, 0}, {5, 4, 6, 1},
				     {6, 5, 7, 2}, {7, 6, 4, 3}};

  const Vector3D d_con(1.0, 1.0, 1.0);

  int i;

  bool metric_valid = false;
  const uint32_t fm = fixed_vertex_bitmap( pd, e, indices );

  m = 0.0;

  switch(topo) {
  case TRIANGLE:
    pd.get_domain_normal_at_element(e, n, err); MSQ_ERRZERO(err);
    n /= n.length();		// Need unit normal
    mCoords[0] = vertices[v_i[0]];
    mCoords[1] = vertices[v_i[1]];
    mCoords[2] = vertices[v_i[2]];
    g.resize(3); h.resize(6);
    if (!h_fcn_2e(m, arrptr(g), arrptr(h), mCoords, n, a2Con, b2Con, c2Con)) return false;
    break;

  case QUADRILATERAL:
    pd.get_domain_normal_at_element(e, n, err); MSQ_ERRZERO(err);
    n /= n.length();	// Need unit normal
    for (i = 0; i < 4; ++i) {
      mCoords[0] = vertices[v_i[locs_hex[i][0]]];
      mCoords[1] = vertices[v_i[locs_hex[i][1]]];
      mCoords[2] = vertices[v_i[locs_hex[i][2]]];
      if (!h_fcn_2i(mMetrics[i], mGradients+3*i, mHessians+6*i, mCoords, n,
		    a2Con, b2Con, c2Con, d_con)) return false;
    }

    g.resize(4); h.resize(10);
    m = average_corner_hessians( QUADRILATERAL, fm, 4,
                                 mMetrics, mGradients, mHessians,
                                 arrptr(g), arrptr(h), err );
    MSQ_ERRZERO( err );
    break;

  case TETRAHEDRON:
    mCoords[0] = vertices[v_i[0]];
    mCoords[1] = vertices[v_i[1]];
    mCoords[2] = vertices[v_i[2]];
    mCoords[3] = vertices[v_i[3]];
    g.resize(4); h.resize(10);
    metric_valid = h_fcn_3e(m, arrptr(g), arrptr(h), mCoords, a3Con, b3Con, c3Con);
    if (!metric_valid) return false;
    break;

  case PYRAMID:
    for (i = 0; i < 4; ++i) {
      mCoords[0] = vertices[v_i[ i     ]];
      mCoords[1] = vertices[v_i[(i+1)%4]];
      mCoords[2] = vertices[v_i[(i+3)%4]];
      mCoords[3] = vertices[v_i[ 4     ]];
      metric_valid = h_fcn_3p(mMetrics[i], mGradients+4*i, 
                              mHessians+10*i, mCoords, a3Con, b3Con, c3Con);

      if (!metric_valid) return false;
    }

    g.resize(5); h.resize(15);
    m = average_corner_hessians( PYRAMID, fm, 4,
                                 mMetrics, mGradients, mHessians,
                                 arrptr(g), arrptr(h), err );
    MSQ_ERRZERO( err );
    break;

  case PRISM:
    for (i = 0; i < 6; ++i) {
      mCoords[0] = vertices[v_i[locs_pri[i][0]]];
      mCoords[1] = vertices[v_i[locs_pri[i][1]]];
      mCoords[2] = vertices[v_i[locs_pri[i][2]]];
      mCoords[3] = vertices[v_i[locs_pri[i][3]]];
      if (!h_fcn_3w(mMetrics[i], mGradients+4*i, mHessians+10*i, mCoords,
		    a3Con, b3Con, c3Con)) return false;
    }

    g.resize(6); h.resize(21);
    m = average_corner_hessians( PRISM, fm, 6,
                                 mMetrics, mGradients, mHessians,
                                 arrptr(g), arrptr(h), err );
    MSQ_ERRZERO( err );
    break;

  case HEXAHEDRON:
    for (i = 0; i < 8; ++i) {
      mCoords[0] = vertices[v_i[locs_hex[i][0]]];
      mCoords[1] = vertices[v_i[locs_hex[i][1]]];
      mCoords[2] = vertices[v_i[locs_hex[i][2]]];
      mCoords[3] = vertices[v_i[locs_hex[i][3]]];
      if (!h_fcn_3i(mMetrics[i], mGradients+4*i, mHessians+10*i, mCoords,
		    a3Con, b3Con, c3Con, d_con)) return false;
    }

    g.resize(8); h.resize(36);
    m = average_corner_hessians( HEXAHEDRON, fm, 8,
                                 mMetrics, mGradients, mHessians,
                                 arrptr(g), arrptr(h), err );
    MSQ_ERRZERO( err );
    break;

  default:
    MSQ_SETERR(err)(MsqError::UNSUPPORTED_ELEMENT,
                    "Element type (%d) not supported in IdealWeightInverseMeanRatio",
                    (int)topo);
    return false;
  } // end switch over element type

  remove_fixed_gradients( topo, fm, g );
  remove_fixed_hessians( topo, fm, h );
  return true;
}