void QuasiNewton::optimize_vertex_positions( PatchData& pd, MsqError& err ) { TerminationCriterion& term = *get_inner_termination_criterion(); OFEvaluator& func = get_objective_function_evaluator(); const double sigma = 1e-4; const double beta0 = 0.25; const double beta1 = 0.80; const double tol1 = 1e-8; const double epsilon = 1e-10; double norm_r; //, norm_g; double alpha, beta; double obj, objn; size_t i; // Initialize stuff const size_t nn = pd.num_free_vertices(); double a[QNVEC], b[QNVEC], r[QNVEC]; for (i = 0; i < QNVEC; ++i) r[i] = 0; for (i = 0; i <= QNVEC; ++i) { v[i].clear(); v[i].resize( nn, Vector3D(0.0) ); w[i].clear(); w[i].resize( nn, Vector3D(0.0) ); } d.resize( nn ); mHess.resize( nn ); //hMesh(mesh); bool valid = func.update( pd, obj, v[QNVEC], mHess, err ); MSQ_ERRRTN(err); if (!valid) { MSQ_SETERR(err)("Initial objective function is not valid", MsqError::INVALID_MESH); return; } while (!term.terminate()) { pd.recreate_vertices_memento( mMemento, err ); MSQ_ERRRTN(err); pd.get_free_vertex_coordinates( w[QNVEC] ); x = v[QNVEC]; for (i = QNVEC; i--; ) { a[i] = r[i] * inner( &(w[i][0]), arrptr(x), nn ); plus_eq_scaled( arrptr(x), -a[i], &v[i][0], nn ); } solve( arrptr(d), arrptr(x) ); for (i = QNVEC; i--; ) { b[i] = r[i] * inner( &(v[i][0]), arrptr(d), nn ); plus_eq_scaled( arrptr(d), a[i]-b[i], &(w[i][0]), nn ); } alpha = -inner( &(v[QNVEC][0]), arrptr(d), nn ); /* direction is negated */ if (alpha > 0.0) { MSQ_SETERR(err)("No descent.", MsqError::INVALID_MESH); return; } alpha *= sigma; beta = 1.0; pd.move_free_vertices_constrained( arrptr(d), nn, -beta, err ); MSQ_ERRRTN(err); valid = func.evaluate( pd, objn, v[QNVEC], err ); if (err.error_code() == err.BARRIER_VIOLATED) err.clear(); // barrier violated does not represent an actual error here MSQ_ERRRTN(err); if (!valid || (obj - objn < -alpha*beta - epsilon && length( &(v[QNVEC][0]), nn ) >= tol1)) { if (!valid) // function not defined at trial point beta *= beta0; else // unacceptable iterate beta *= beta1; for (;;) { if (beta < tol1) { pd.set_to_vertices_memento( mMemento, err ); MSQ_ERRRTN(err); MSQ_SETERR(err)("Newton step not good", MsqError::INTERNAL_ERROR); return; } pd.set_free_vertices_constrained( mMemento, arrptr(d), nn, -beta, err ); MSQ_ERRRTN(err); valid = func.evaluate( pd, objn, err ); if (err.error_code() == err.BARRIER_VIOLATED) err.clear(); // barrier violated does not represent an actual error here MSQ_ERRRTN(err); if (!valid) // function undefined at trial point beta *= beta0; else if (obj - objn < -alpha*beta - epsilon) // unacceptlable iterate beta *= beta1; else break; } } for (i = 0; i < QNVEC-1; ++i) { r[i] = r[i+1]; w[i].swap( w[i+1] ); v[i].swap( v[i+1] ); } w[QNVEC-1].swap( w[0] ); v[QNVEC-1].swap( v[0] ); func.update( pd, obj, v[QNVEC], mHess, err ); MSQ_ERRRTN(err); norm_r = length_squared( &(v[QNVEC][0]), nn ); //norm_g = sqrt(norm_r); // checks stopping criterion term.accumulate_patch( pd, err ); MSQ_ERRRTN(err); term.accumulate_inner( pd, objn, &v[QNVEC][0], err ); MSQ_ERRRTN(err); } }
/*!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()); } }
void TrustRegion::optimize_vertex_positions( PatchData& pd, MsqError& err ) { TerminationCriterion& term = *get_inner_termination_criterion(); OFEvaluator& func = get_objective_function_evaluator(); const double cg_tol = 1e-2; const double eta_1 = 0.01; const double eta_2 = 0.90; const double tr_incr = 10; const double tr_decr_def = 0.25; const double tr_decr_undef = 0.25; const double tr_num_tol = 1e-6; const int max_cg_iter = 10000; double radius = 1000; /* delta*delta */ const int nn = pd.num_free_vertices(); wVect.resize(nn); Vector3D* w = arrptr(wVect); zVect.resize(nn); Vector3D* z = arrptr(zVect); dVect.resize(nn); Vector3D* d = arrptr(dVect); pVect.resize(nn); Vector3D* p = arrptr(pVect); rVect.resize(nn); Vector3D* r = arrptr(rVect); double norm_r, norm_g; double alpha, beta, kappa; double rz, rzm1; double dMp, norm_d, norm_dp1, norm_p; double obj, objn; int cg_iter; bool valid; mHess.initialize( pd, err ); //hMesh(mesh); valid = func.update( pd, obj, mGrad, mHess, err ); MSQ_ERRRTN(err); if (!valid) { MSQ_SETERR(err)("Initial objective function is not valid", MsqError::INVALID_MESH); return; } compute_preconditioner( err ); MSQ_ERRRTN(err); pd.recreate_vertices_memento( mMemento, err ); MSQ_ERRRTN(err); while (!term.terminate() && (radius > 1e-20)) { norm_r = length_squared(arrptr(mGrad), nn); norm_g = sqrt(norm_r); memset(d, 0, 3*sizeof(double)*nn); memcpy(r, arrptr(mGrad), nn*sizeof(Vector3D)); //memcpy(r, mesh->g, 3*sizeof(double)*nn); norm_g *= cg_tol; apply_preconditioner( z, r, err); MSQ_ERRRTN(err); //prec->apply(z, r, prec, mesh); negate(p, z, nn); rz = inner(r, z, nn); dMp = 0; norm_p = rz; norm_d = 0; cg_iter = 0; while ((sqrt(norm_r) > norm_g) && #ifdef DO_STEEP_DESC (norm_d > tr_num_tol) && #endif (cg_iter < max_cg_iter)) { ++cg_iter; memset(w, 0, 3*sizeof(double)*nn); //matmul(w, mHess, p); //matmul(w, mesh, p); mHess.product( w, p ); kappa = inner(p, w, nn); if (kappa <= 0.0) { alpha = (sqrt(dMp*dMp+norm_p*(radius-norm_d))-dMp)/norm_p; plus_eq_scaled( d, alpha, p, nn ); break; } alpha = rz / kappa; norm_dp1 = norm_d + 2.0*alpha*dMp + alpha*alpha*norm_p; if (norm_dp1 >= radius) { alpha = (sqrt(dMp*dMp+norm_p*(radius-norm_d))-dMp)/norm_p; plus_eq_scaled( d, alpha, p, nn ); break; } plus_eq_scaled( d, alpha, p, nn ); plus_eq_scaled( r, alpha, w, nn ); norm_r = length_squared(r, nn); apply_preconditioner( z, r, err); MSQ_ERRRTN(err); //prec->apply(z, r, prec, mesh); rzm1 = rz; rz = inner(r, z, nn); beta = rz / rzm1; times_eq_minus( p, beta, z, nn ); dMp = beta*(dMp + alpha*norm_p); norm_p = rz + beta*beta*norm_p; norm_d = norm_dp1; } #ifdef DO_STEEP_DESC if (norm_d <= tr_num_tol) { norm_g = length(arrptr(mGrad), nn); double ll = 1.0; if (norm_g < tr_num_tol) break; if (norm_g > radius) ll = radius / nurm_g; for (int i = 0; i < nn; ++i) d[i] = ll * mGrad[i]; } #endif alpha = inner( arrptr(mGrad), d, nn ); // inner(mesh->g, d, nn); memset(p, 0, 3*sizeof(double)*nn); //matmul(p, mHess, d); //matmul(p, mesh, d); mHess.product( p, d ); beta = 0.5*inner(p, d, nn); kappa = alpha + beta; /* Put the new point into the locations */ pd.move_free_vertices_constrained( d, nn, 1.0, err ); MSQ_ERRRTN(err); valid = func.evaluate( pd, objn, err ); MSQ_ERRRTN(err); if (!valid) { /* Function not defined at trial point */ radius *= tr_decr_undef; pd.set_to_vertices_memento( mMemento, err ); MSQ_ERRRTN(err); continue; } if ((fabs(kappa) <= tr_num_tol) && (fabs(objn - obj) <= tr_num_tol)) { kappa = 1; } else { kappa = (objn - obj) / kappa; } if (kappa < eta_1) { /* Iterate is unacceptable */ radius *= tr_decr_def; pd.set_to_vertices_memento( mMemento, err ); MSQ_ERRRTN(err); continue; } /* Iterate is acceptable */ if (kappa >= eta_2) { /* Iterate is a very good step, increase radius */ radius *= tr_incr; if (radius > 1e20) { radius = 1e20; } } func.update( pd, obj, mGrad, mHess, err ); compute_preconditioner( err ); MSQ_ERRRTN(err); pd.recreate_vertices_memento( mMemento, err ); MSQ_ERRRTN(err); // checks stopping criterion term.accumulate_patch( pd, err ); MSQ_ERRRTN(err); term.accumulate_inner( pd, objn, arrptr(mGrad), err ); MSQ_ERRRTN(err); } }
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; } }
double ConjugateGradient::get_step(PatchData &pd,double f0,int &j, MsqError &err) { // get OF evaluator OFEvaluator& objFunc = get_objective_function_evaluator(); size_t num_vertices=pd.num_free_vertices(); //initial guess for alp double alp=1.0; int jmax=100; double rho=0.5; //feasible=false implies the mesh is not in the feasible region bool feasible=false; int found=0; //f and fnew hold the objective function value double f=0; double fnew=0; //Counter to avoid infinitly scaling alp j=0; //save memento pd.recreate_vertices_memento(pMemento, err); //if we must check feasiblility //while step takes mesh into infeasible region and ... while (j<jmax && !feasible && alp>MSQ_MIN) { ++j; pd.set_free_vertices_constrained(pMemento,arrptr(pGrad),num_vertices,alp,err); feasible=objFunc.evaluate(pd,f,err); if(err.error_code() == err.BARRIER_VIOLATED) err.clear(); // barrier violation does not represent an actual error here MSQ_ERRZERO(err); //if not feasible, try a smaller alp (take smaller step) if(!feasible){ alp*=rho; } }//end while ... //if above while ended due to j>=jmax, no valid step was found. if(j>=jmax){ MSQ_PRINT(2)("\nFeasible Point Not Found"); return 0.0; } //Message::print_info("\nOriginal f %f, first new f = %f, alp = %f",f0,f,alp); //if new f is larger than original, our step was too large if(f>=f0){ j=0; while (j<jmax && found == 0){ ++j; alp *= rho; pd.set_free_vertices_constrained(pMemento,arrptr(pGrad),num_vertices,alp,err); //Get new obj value //if patch is now invalid, then the feasible region is convex or //we have an error. For now, we assume an error. if(! objFunc.evaluate(pd,f,err) ){ MSQ_SETERR(err)("Non-convex feasiblility region found.",MsqError::INVALID_MESH); } pd.set_to_vertices_memento(pMemento,err);MSQ_ERRZERO(err); //if our step has now improved the objective function value if(f<f0){ found=1; } }// end while j less than jmax //Message::print_info("\nj = %d found = %d f = %20.18f f0 = %20.18f\n",j,found,f,f0); //if above ended because of j>=jmax, take no step if(found==0){ //Message::print_info("alp = %10.8f, but returning zero\n",alp); alp=0.0; return alp; } j=0; //while shrinking the step improves the objFunc value further, //scale alp down. Return alp, when scaling once more would //no longer improve the objFunc value. while(j<jmax){ ++j; alp*=rho; //step alp in search direction from original positions pd.set_free_vertices_constrained(pMemento,arrptr(pGrad),num_vertices,alp,err);MSQ_ERRZERO(err); //get new objective function value if (! objFunc.evaluate(pd,fnew,err)) MSQ_SETERR(err)("Non-convex feasiblility region found while " "computing new f.",MsqError::INVALID_MESH); if(fnew<f){ f=fnew; } else{ //Reset the vertices to original position pd.set_to_vertices_memento(pMemento,err);MSQ_ERRZERO(err); alp/=rho; return alp; } } //Reset the vertices to original position and return alp pd.set_to_vertices_memento(pMemento,err);MSQ_ERRZERO(err); return alp; } //else our new f was already smaller than our original else{ j=0; //check to see how large of step we can take while (j<jmax && found == 0) { ++j; //scale alp up (rho must be less than 1) alp /= rho; //step alp in search direction from original positions pd.set_free_vertices_constrained(pMemento,arrptr(pGrad),num_vertices,alp,err);MSQ_ERRZERO(err); feasible = objFunc.evaluate(pd,fnew, err); if(err.error_code() == err.BARRIER_VIOLATED) err.clear(); // evaluate() error does not represent an actual problem here MSQ_ERRZERO(err); if ( ! feasible ){ alp *= rho; //Reset the vertices to original position and return alp pd.set_to_vertices_memento(pMemento,err);MSQ_ERRZERO(err); return alp; } if (fnew<f) { f = fnew; } else { found=1; alp *= rho; } } //Reset the vertices to original position and return alp pd.set_to_vertices_memento(pMemento,err);MSQ_ERRZERO(err); return alp; } }
/*! \brief Improves the quality of the MeshSet, calling some methods specified in a class derived from VertexMover \param const MeshSet &: this MeshSet is looped over. Only the mutable data members are changed (such as currentVertexInd). */ double VertexMover::loop_over_mesh( ParallelMesh* mesh, MeshDomain* domain, const Settings* settings, MsqError& err ) { std::vector<size_t> junk; Mesh::VertexHandle vertex_handle; // Get the patch data to use for the first iteration OFEvaluator& obj_func = get_objective_function_evaluator(); PatchData patch; patch.set_mesh( (Mesh*) mesh ); patch.set_domain( domain ); patch.attach_settings( settings ); ParallelHelper* helper = mesh->get_parallel_helper(); if (!helper) { MSQ_SETERR(err)("No ParallelHelper instance", MsqError::INVALID_STATE); return 0; } helper->smoothing_init(err); MSQ_ERRZERO(err); bool did_some, all_culled; std::vector<Mesh::VertexHandle> patch_vertices; std::vector<Mesh::ElementHandle> patch_elements; std::vector<Mesh::VertexHandle> fixed_vertices; std::vector<Mesh::VertexHandle> free_vertices; // Get termination criteria TerminationCriterion* outer_crit=this->get_outer_termination_criterion(); TerminationCriterion* inner_crit=this->get_inner_termination_criterion(); if(outer_crit == 0){ MSQ_SETERR(err)("Termination Criterion pointer is Null", MsqError::INVALID_STATE); return 0.; } if(inner_crit == 0){ MSQ_SETERR(err)("Termination Criterion pointer for inner loop is Null", MsqError::INVALID_STATE); return 0.; } PatchSet* patch_set = get_patch_set(); if (!patch_set) { MSQ_SETERR(err)("No PatchSet for QualityImprover!", MsqError::INVALID_STATE); return 0.0; } patch_set->set_mesh( (Mesh*)mesh ); std::vector<PatchSet::PatchHandle> patch_list; patch_set->get_patch_handles( patch_list, err ); MSQ_ERRZERO(err); // Initialize outer loop this->initialize(patch, err); if (MSQ_CHKERR(err)) goto ERROR; obj_func.initialize( (Mesh*)mesh, domain, settings, patch_set, err ); if (MSQ_CHKERR(err)) goto ERROR; outer_crit->reset_outer( (Mesh*)mesh, domain, obj_func, settings, err); if (MSQ_CHKERR(err)) goto ERROR; // Loop until outer termination criterion is met did_some = true; while (!outer_crit->terminate()) { if (!did_some) { MSQ_SETERR(err)("Inner termiation criterion satisfied for all patches " "without meeting outer termination criterion. This is " "an infinite loop. Aborting.", MsqError::INVALID_STATE); break; } did_some = false; all_culled = true; ///*** smooth the interior ***//// // get the fixed vertices (i.e. the ones *not* part of the first independent set) helper->compute_first_independent_set(fixed_vertices); // sort the fixed vertices std::sort(fixed_vertices.begin(), fixed_vertices.end()); // Loop over each patch std::vector<PatchSet::PatchHandle>::iterator p_iter = patch_list.begin(); while( p_iter != patch_list.end() ) { // loop until we get a non-empty patch. patch will be empty // for culled vertices with element-on-vertex patches do { patch_set->get_patch( *p_iter, patch_elements, patch_vertices, err ); if (MSQ_CHKERR(err)) goto ERROR; ++p_iter; } while (patch_elements.empty() && p_iter != patch_list.end()) ; if (patch_elements.empty()) { // no more non-culled vertices break; } if (patch_vertices.empty()) // global patch hack (means all mesh vertices) { mesh->get_all_vertices(patch_vertices, err); } free_vertices.clear(); for (size_t i = 0; i < patch_vertices.size(); ++i) if (!std::binary_search(fixed_vertices.begin(), fixed_vertices.end(), patch_vertices[i])) free_vertices.push_back(patch_vertices[i]); if (free_vertices.empty()) { // all vertices were fixed -> skip patch continue; } all_culled = false; patch.set_mesh_entities( patch_elements, free_vertices, err ); if (MSQ_CHKERR(err)) goto ERROR; // Initialize for inner iteration this->initialize_mesh_iteration(patch, err); if (MSQ_CHKERR(err)) goto ERROR; obj_func.reset(); outer_crit->reset_patch( patch, err ); if (MSQ_CHKERR(err)) goto ERROR; inner_crit->reset_inner( patch, obj_func, err ); if (MSQ_CHKERR(err)) goto ERROR; inner_crit->reset_patch( patch, err ); if (MSQ_CHKERR(err)) goto ERROR; // Don't even call optimizer if inner termination // criterion has already been met. if (!inner_crit->terminate()) { did_some = true; // Call optimizer - should loop on inner_crit->terminate() this->optimize_vertex_positions( patch, err ); if (MSQ_CHKERR(err)) goto ERROR; // Update for changes during inner iteration // (during optimizer loop) outer_crit->accumulate_patch( patch, err ); if (MSQ_CHKERR(err)) goto ERROR; inner_crit->cull_vertices( patch, obj_func, err ); if (MSQ_CHKERR(err)) goto ERROR; patch.update_mesh( err ); if (MSQ_CHKERR(err)) goto ERROR; } } helper->communicate_first_independent_set(err); if (MSQ_CHKERR(err)) goto ERROR; ///*** smooth the boundary ***//// while (helper->compute_next_independent_set()) { // Loop over all boundary elements while(helper->get_next_partition_boundary_vertex(vertex_handle)) { patch_vertices.clear(); patch_vertices.push_back(vertex_handle); patch_elements.clear(); mesh->vertices_get_attached_elements( &vertex_handle, 1, patch_elements, junk, err ); all_culled = false; patch.set_mesh_entities( patch_elements, patch_vertices, err ); if (MSQ_CHKERR(err)) goto ERROR; // Initialize for inner iteration this->initialize_mesh_iteration(patch, err); if (MSQ_CHKERR(err)) goto ERROR; obj_func.reset(); outer_crit->reset_patch( patch, err ); if (MSQ_CHKERR(err)) goto ERROR; inner_crit->reset_inner( patch, obj_func, err ); if (MSQ_CHKERR(err)) goto ERROR; inner_crit->reset_patch( patch, err ); if (MSQ_CHKERR(err)) goto ERROR; // Don't even call optimizer if inner termination // criterion has already been met. if (!inner_crit->terminate()) { did_some = true; // Call optimizer - should loop on inner_crit->terminate() this->optimize_vertex_positions( patch, err ); if (MSQ_CHKERR(err)) goto ERROR; // Update for changes during inner iteration // (during optimizer loop) outer_crit->accumulate_patch( patch, err ); if (MSQ_CHKERR(err)) goto ERROR; inner_crit->cull_vertices( patch, obj_func, err ); if (MSQ_CHKERR(err)) goto ERROR; patch.update_mesh( err ); if (MSQ_CHKERR(err)) goto ERROR; } } helper->communicate_next_independent_set(err); if (MSQ_CHKERR(err)) goto ERROR; } this->terminate_mesh_iteration(patch, err); if (MSQ_CHKERR(err)) goto ERROR; outer_crit->accumulate_outer( mesh, domain, obj_func, settings, err ); if (MSQ_CHKERR(err)) goto ERROR; if (all_culled) break; } ERROR: //call the criteria's cleanup funtions. outer_crit->cleanup(mesh,domain,err); MSQ_CHKERR(err); inner_crit->cleanup(mesh,domain,err); MSQ_CHKERR(err); //call the optimization cleanup function. this->cleanup(); // close the helper helper->smoothing_close(err); MSQ_CHKERR(err); return 0.; }
/*! \brief Improves the quality of the MeshSet, calling some methods specified in a class derived from VertexMover \param const MeshSet &: this MeshSet is looped over. Only the mutable data members are changed (such as currentVertexInd). */ double VertexMover::loop_over_mesh( Mesh* mesh, MeshDomain* domain, const Settings* settings, MsqError& err ) { // Get the patch data to use for the first iteration OFEvaluator& obj_func = get_objective_function_evaluator(); PatchData patch; patch.set_mesh( mesh ); patch.set_domain( domain ); if (settings) patch.attach_settings( settings ); bool one_patch = false, did_some, all_culled; std::vector<Mesh::VertexHandle> patch_vertices; std::vector<Mesh::ElementHandle> patch_elements; PatchSet* patch_set = get_patch_set(); if (!patch_set) { MSQ_SETERR(err)("No PatchSet for QualityImprover!", MsqError::INVALID_STATE); return 0.0; } patch_set->set_mesh( mesh ); std::vector<PatchSet::PatchHandle> patch_list; patch_set->get_patch_handles( patch_list, err ); MSQ_ERRZERO(err); // Get termination criteria TerminationCriterion* outer_crit=this->get_outer_termination_criterion(); TerminationCriterion* inner_crit=this->get_inner_termination_criterion(); if(outer_crit == 0){ MSQ_SETERR(err)("Termination Criterion pointer is Null", MsqError::INVALID_STATE); return 0.; } if(inner_crit == 0){ MSQ_SETERR(err)("Termination Criterion pointer for inner loop is Null", MsqError::INVALID_STATE); return 0.; } // If using a local patch, suppress output of inner termination criterion if (patch_list.size() > 1) inner_crit->set_debug_output_level(3); else one_patch = true; // Initialize outer loop this->initialize(patch, err); if (MSQ_CHKERR(err)) goto ERROR; obj_func.initialize( mesh, domain, settings, patch_set, err ); if (MSQ_CHKERR(err)) goto ERROR; outer_crit->reset_outer(mesh, domain, obj_func, settings, err); if (MSQ_CHKERR(err)) goto ERROR; // if only one patch, get the patch now if (one_patch) { patch_set->get_patch( patch_list[0], patch_elements, patch_vertices, err ); if (MSQ_CHKERR(err)) goto ERROR; patch.set_mesh_entities( patch_elements, patch_vertices, err ); if (MSQ_CHKERR(err)) goto ERROR; } // Loop until outer termination criterion is met did_some = true; while (!outer_crit->terminate()) { if (!did_some) { MSQ_SETERR(err)("Inner termiation criterion satisfied for all patches " "without meeting outer termination criterion. This is " "an infinite loop. Aborting.", MsqError::INVALID_STATE); break; } did_some = false; all_culled = true; // Loop over each patch std::vector<PatchSet::PatchHandle>::iterator p_iter = patch_list.begin(); while( p_iter != patch_list.end() ) { if (!one_patch) { // if only one patch (global) re-use the previous one // loop until we get a non-empty patch. patch will be empty // for culled vertices with element-on-vertex patches do { patch_set->get_patch( *p_iter, patch_elements, patch_vertices, err ); if (MSQ_CHKERR(err)) goto ERROR; ++p_iter; } while (patch_elements.empty() && p_iter != patch_list.end()) ; if (patch_elements.empty()) { // no more non-culled vertices break; } all_culled = false; patch.set_mesh_entities( patch_elements, patch_vertices, err ); if (MSQ_CHKERR(err)) goto ERROR; } else { ++p_iter; all_culled = false; } // Initialize for inner iteration this->initialize_mesh_iteration(patch, err); if (MSQ_CHKERR(err)) goto ERROR; obj_func.reset(); outer_crit->reset_patch( patch, err ); if (MSQ_CHKERR(err)) goto ERROR; inner_crit->reset_inner( patch, obj_func, err ); if (MSQ_CHKERR(err)) goto ERROR; inner_crit->reset_patch( patch, err ); if (MSQ_CHKERR(err)) goto ERROR; // Don't even call optimizer if inner termination // criterion has already been met. if (!inner_crit->terminate()) { did_some = true; // Call optimizer - should loop on inner_crit->terminate() this->optimize_vertex_positions( patch, err ); if (MSQ_CHKERR(err)) goto ERROR; // Update for changes during inner iteration // (during optimizer loop) outer_crit->accumulate_patch( patch, err ); if (MSQ_CHKERR(err)) goto ERROR; inner_crit->cull_vertices( patch, obj_func, err ); if (MSQ_CHKERR(err)) goto ERROR; patch.update_mesh( err ); if (MSQ_CHKERR(err)) goto ERROR; } } this->terminate_mesh_iteration(patch, err); if (MSQ_CHKERR(err)) goto ERROR; outer_crit->accumulate_outer( mesh, domain, obj_func, settings, err ); if (MSQ_CHKERR(err)) goto ERROR; if (all_culled) break; } ERROR: //call the criteria's cleanup funtions. outer_crit->cleanup(mesh,domain,err); inner_crit->cleanup(mesh,domain,err); //call the optimization cleanup function. this->cleanup(); return 0.; }