diff --git a/CMakeLists.txt b/CMakeLists.txt index de5f29e34a6fa86898c48d35dbe25fb3a5bfb7cb..af556300d5df1b17c8873506e6d4e1933c102f9a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,7 @@ option(ENABLE_FL_TREE "Enable FLTK tree browser widget" ${DEFAULT}) option(ENABLE_FOURIER_MODEL "Enable Fourier geometrical models" OFF) option(ENABLE_GMM "Enable GMM linear algebra solvers" ${DEFAULT}) option(ENABLE_GRAPHICS "Compile-in OpenGL graphics even if there is no GUI" OFF) +option(ENABLE_OPTHOM "Enable optimization tool for high order meshes" ON) option(ENABLE_KBIPACK "Enable Kbipack for homology solver" ${DEFAULT}) option(ENABLE_MATHEX "Enable MathEx expression parser" ${DEFAULT}) option(ENABLE_MED "Enable MED mesh and post-processing file formats" ${DEFAULT}) @@ -108,6 +109,9 @@ set(GMSH_API contrib/kbipack/gmp_normal_form.h contrib/kbipack/gmp_matrix.h contrib/kbipack/gmp_blas.h contrib/kbipack/mpz.h contrib/DiscreteIntegration/Integration3D.h + contrib/HighOrderMeshOptimizer/OptHOM.h + contrib/HighOrderMeshOptimizer/OptHOM_Mesh.h + contrib/HighOrderMeshOptimizer/ParamCoord.h contrib/MathEx/mathex.h) execute_process(COMMAND date "+%Y%m%d" OUTPUT_VARIABLE DATE @@ -508,6 +512,13 @@ if(ENABLE_DINTEGRATION) set_config_option(HAVE_DINTEGRATION "DIntegration") endif(ENABLE_DINTEGRATION) +if(ENABLE_OPTHOM) + add_subdirectory(contrib/HighOrderMeshOptimizer) + include_directories(contrib/HighOrderMeshOptimizer) + set_config_option(HAVE_OPTHOM "OptHom") +endif(ENABLE_OPTHOM) + + if(ENABLE_KBIPACK) find_library(GMP_LIB gmp) if(GMP_LIB) diff --git a/Fltk/highOrderToolsWindow.cpp b/Fltk/highOrderToolsWindow.cpp index 142b217e0175cab07112735f5af503953a146dc7..cf40d0e66b6cfabc65731c7ba67827ef7dbbf6ad 100644 --- a/Fltk/highOrderToolsWindow.cpp +++ b/Fltk/highOrderToolsWindow.cpp @@ -35,6 +35,7 @@ typedef unsigned long intptr_t; #include "Options.h" #include "Context.h" #include "HighOrder.h" +#include "OptHomRun.h" #if defined(HAVE_PARSER) #include "Parser.h" @@ -69,9 +70,17 @@ static void highordertools_runelas_cb(Fl_Widget *w, void *data) bool elastic = (bool)o->butt[3]->value(); double threshold = o->value[1]->value(); bool onlyVisible = (bool)o->butt[1]->value(); - int nLayers = (int) o->value[2]->value(); + int nbLayers = (int) o->value[2]->value(); if(elastic)ElasticAnalogy(GModel::current(), threshold, onlyVisible); + else { + OptHomParameters p; + p.nbLayers = nbLayers; + p.BARRIER = threshold; + p.onlyVisible = onlyVisible; + p.dim = GModel::current()->getNumRegions() ? 3 : 2; + HighOrderMeshOptimizer (GModel::current(),p); + } CTX::instance()->mesh.changed |= (ENT_LINE | ENT_SURFACE | ENT_VOLUME); drawContext::global()->draw(); diff --git a/contrib/HighOrderMeshOptimizer/CMakeLists.txt b/contrib/HighOrderMeshOptimizer/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..31963a192265dadb4c534c86254aa28de27512b2 --- /dev/null +++ b/contrib/HighOrderMeshOptimizer/CMakeLists.txt @@ -0,0 +1,14 @@ +# Gmsh - Copyright (C) 1997-2012 C. Geuzaine, J.-F. Remacle +# +# See the LICENSE.txt file for license information. Please report all +# bugs and problems to <gmsh@geuz.org>. + +set(SRC + OptHomMesh.cpp + OptHOM.cpp + OptHomRun.cpp + ParamCoord.cpp +) + +file(GLOB_RECURSE HDR RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.hpp) +append_gmsh_src(contrib/HighOrderMeshOptimizer "${SRC};${HDR}") diff --git a/contrib/HighOrderMeshOptimizer/OptHOM.cpp b/contrib/HighOrderMeshOptimizer/OptHOM.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ce38c6b41dee62e436e49040a2a1f29eb1496519 --- /dev/null +++ b/contrib/HighOrderMeshOptimizer/OptHOM.cpp @@ -0,0 +1,267 @@ +#include <iostream> +#include <algorithm> +#include "OptHomMesh.h" +#include "OptHOM.h" +#include "GmshConfig.h" + +#ifdef HAVE_BFGS + +#include "ap.h" +#include "alglibinternal.h" +#include "alglibmisc.h" +#include "linalg.h" +#include "optimization.h" + + + +// Constructor +OptHOM::OptHOM(GEntity *ge, std::set<MVertex*> & toFix, int method) : + mesh(ge, toFix, method) +{ +}; + +// Contribution of the element Jacobians to the objective function value and gradients (2D version) +bool OptHOM::addJacObjGrad(double &Obj, alglib::real_1d_array &gradObj) +{ + + for (int iEl = 0; iEl < mesh.nEl(); iEl++) { + std::vector<double> sJ(mesh.nBezEl(iEl)); // Scaled Jacobians + mesh.scaledJac(iEl,sJ); + std::vector<double> gSJ(mesh.nBezEl(iEl)*mesh.nPCEl(iEl)); // Gradients of scaled Jacobians + mesh.gradScaledJac(iEl,gSJ); + for (int l = 0; l < mesh.nBezEl(iEl); l++) { + Obj += compute_f(sJ[l]); + const double f1 = compute_f1(sJ[l]); + for (int iPC = 0; iPC < mesh.nPCEl(iEl); iPC++) + gradObj[mesh.indPCEl(iEl,iPC)] += f1*gSJ[mesh.indGSJ(iEl,l,iPC)]; + } + } + + return true; + +} + + + +// Contribution of the vertex distance to the objective function value and gradients (2D version) +bool OptHOM::addDistObjGrad(double Fact, double Fact2, double &Obj, alglib::real_1d_array &gradObj) +{ + + for (int iFV = 0; iFV < mesh.nFV(); iFV++) { + const double Factor = invLengthScaleSq*(mesh.forced(iFV) ? Fact : Fact2); + Obj += Factor * mesh.distSq(iFV); + std::vector<double> gDSq(mesh.nPCFV(iFV)); + mesh.gradDistSq(iFV,gDSq); + for (int iPC = 0; iPC < mesh.nPCFV(iFV); iPC++) gradObj[mesh.indPCFV(iFV,iPC)] += Factor*gDSq[iPC]; + } + + return true; + +} + + + +void OptHOM::evalObjGrad(const alglib::real_1d_array &x, double &Obj, alglib::real_1d_array &gradObj) +{ + + mesh.updateMesh(x.getcontent()); + + Obj = 0.; + for (int i = 0; i < gradObj.length(); i++) gradObj[i] = 0.; + + addJacObjGrad(Obj, gradObj); + addDistObjGrad(lambda, lambda2, Obj, gradObj); + +} + + + +void evalObjGradFunc(const alglib::real_1d_array &x, double &Obj, alglib::real_1d_array &gradObj, void *HOInst) +{ + OptHOM &HO = *static_cast<OptHOM*> (HOInst); + HO.evalObjGrad(x, Obj, gradObj); + double distMaxBnd, distAvgBnd, minJac, maxJac; + HO.getDistances(distMaxBnd, distAvgBnd, minJac, maxJac); + if (minJac > HO.barrier) { + printf("STOP\n"); + for (int i = 0; i < gradObj.length(); ++i) { + gradObj[i] = 0; + } + } +} + + + +void OptHOM::getDistances(double &distMaxBND, double &distAvgBND, double &minJac, double &maxJac) +{ + + distMaxBND = 0; + distAvgBND = 0; + int nbBnd = 0; + + for (int iFV = 0; iFV < mesh.nFV(); iFV++) { + if (mesh.forced(iFV)) { + double dSq = mesh.distSq(iFV); + distMaxBND = std::max(distMaxBND, sqrt(dSq)); + distAvgBND += sqrt(dSq); + nbBnd++; + } + } + if (nbBnd != 0) distAvgBND /= nbBnd; + + minJac = 1000; + maxJac = -1000; + for (int iEl = 0; iEl < mesh.nEl(); iEl++) { + std::vector<double> sJ(mesh.nBezEl(iEl)); // Scaled Jacobians + mesh.scaledJac(iEl,sJ); + for (int l = 0; l < mesh.nBezEl(iEl); l++) { + minJac = std::min(minJac, sJ[l]); + maxJac = std::max(maxJac, sJ[l]); + } + } + +} + + + +void OptHOM::printProgress(const alglib::real_1d_array &x, double Obj) +{ + + iter++; + + if (iter % progressInterv == 0) { + double maxD, avgD, minJ, maxJ; + getDistances(maxD, avgD, minJ, maxJ); + + printf("--> Iteration %3d --- OBJ %12.5E (relative decrease = %12.5E) -- minJ = %12.5E maxJ = %12.5E Max D = %12.5E Avg D = %12.5E\n", iter, Obj, Obj/initObj, minJ, maxJ, maxD, avgD); + } + +} + + + +void printProgressFunc(const alglib::real_1d_array &x, double Obj, void *HOInst) +{ + ((OptHOM*)HOInst)->printProgress(x,Obj); +} + + + +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 = 1.e-8; +// static const double EPSF = 0.; + static const double EPSX = 0.; +// const double EPSX = x.length()*1.e-4/sqrt(invLengthScaleSq); +// std::cout << "DEBUG: EPSX = " << EPSX << ", EPSX/x.length() = " << EPSX/x.length() << std::endl; + +// double iGONorm = 0; +// for (int i=0; i<initGradObj.length(); i++) iGONorm += initGradObj[i]*initGradObj[i]; +// const double EPSG = 1.e-2*sqrt(iGONorm); + + 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); + + 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; +// 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; + } + std::cout << "." << std::endl; + +} + + + +int OptHOM::optimize(double weightFixed, double weightFree, double barrier_, int pInt, int itMax) +{ + barrier = barrier_; + progressInterv = pInt; +// powM = 4; +// powP = 3; + + // Set weights & length scale for non-dimensionalization + lambda = weightFixed; + lambda2 = weightFree; + std::vector<double> dSq(mesh.nVert()); + mesh.distSqToStraight(dSq); + const double maxDSq = *max_element(dSq.begin(),dSq.end()); + invLengthScaleSq = 1./maxDSq; // Length scale for non-dimensional distance + + // Set initial guess + alglib::real_1d_array x; + x.setlength(mesh.nPC()); + mesh.getUvw(x.getcontent()); + + // Calculate initial performance + double minJ, maxJ; + getDistances(initMaxD, initAvgD, minJ, maxJ); + + const double jacBarStart = (minJ > 0.) ? 0.9*minJ : 1.1*minJ; + jacBar = jacBarStart; + setBarrierTerm(jacBarStart); + std::cout << "DEBUG: jacBarStart = " << jacBarStart << std::endl; + + // Calculate initial objective function value and gradient + initObj = 0.; + alglib::real_1d_array gradObj; + gradObj.setlength(mesh.nPC()); + for (int i = 0; i < mesh.nPC(); i++) gradObj[i] = 0.; + evalObjGrad(x, initObj, gradObj); + + std::cout << "Initial mesh: Obj = " << initObj << ", minJ = " << minJ << ", maxJ = " << maxJ << ", maxD = " << initMaxD << ", avgD = " << initAvgD << std::endl; + + std::cout << "Start optimizing with barrier = " << barrier << std::endl; + + while (minJ < barrier) { + OptimPass(x, gradObj, itMax); + double dumMaxD, dumAvgD; + getDistances(dumMaxD, dumAvgD, minJ, maxJ); + jacBar = (minJ > 0.) ? 0.9*minJ : 1.1*minJ; + setBarrierTerm(jacBar); + } + +// for (int i = 0; i<3; i++) { +// lambda *= 10; +// OptimPass(x, gradObj, itMax); +// } + + double finalObj = 0., finalMaxD, finalAvgD; + evalObjGrad(x, finalObj, gradObj); + getDistances(finalMaxD, finalAvgD, minJ, maxJ); + std::cout << "Final mesh: Obj = " << finalObj << ", maxD = " << finalMaxD << ", avgD = " << finalAvgD << ", minJ = " << minJ << ", maxJ = " << maxJ << std::endl; + + return 0; + +} + + + +#endif diff --git a/contrib/HighOrderMeshOptimizer/OptHOM.h b/contrib/HighOrderMeshOptimizer/OptHOM.h new file mode 100644 index 0000000000000000000000000000000000000000..575a5eb3d3037c1050ee14c9ea7908c3433988a9 --- /dev/null +++ b/contrib/HighOrderMeshOptimizer/OptHOM.h @@ -0,0 +1,85 @@ +#ifndef _OPTHOM_H_ +#define _OPTHOM_H_ + +#include <string> +#include <math.h> + + + +#ifdef HAVE_BFGS + +#include "ap.h" + + + +class Mesh; + +class OptHOM +{ + +public: + + Mesh mesh; + + OptHOM(GEntity *gf, std::set<MVertex*> & toFix, int method); + void getDistances(double &distMaxBND, double &distAvgBND, double &minJac, double &maxJac); + int optimize(double lambda, double lambda2, double barrier, int pInt, int itMax); // optimize one list of elements + void updateMesh(const alglib::real_1d_array &x); + void evalObjGrad(const alglib::real_1d_array &x, double &Obj, alglib::real_1d_array &gradObj); + void printProgress(const alglib::real_1d_array &x, double Obj); + + double barrier; + +private: + +// double lambda, lambda2, powM, powP, invLengthScaleSq; + double lambda, lambda2, jacBar, bTerm, invLengthScaleSq; + int iter, progressInterv; // Current iteration, interval of iterations for reporting + double initObj, initMaxD, initAvgD; // Values for reporting + + inline double 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 OptimPass(alglib::real_1d_array &x, const alglib::real_1d_array &initGradObj, int itMax); + +}; + + + +inline double OptHOM::compute_f(double v) +{ + if (v > jacBar) { + const double l = log((1 + bTerm) * v - bTerm); + const double m = (v - 1); + return l * l + m * m; + } + else return 1.e300; +// if (v < 1.) return pow(1.-v,powM); +// if (v < 1.) return exp((long double)pow(1.-v,3)); +// else return pow(v-1.,powP); +} + + + +inline double OptHOM::compute_f1(double v) +{ + if (v > jacBar) { + const double veps = (1 + bTerm) * v - bTerm; + const double m = 2 * (v - 1); + return m + 2 * log(veps) / veps * (1 + bTerm); + } + else return -1.e300; +// if (v < 1.) return -powM*pow(1.-v,powM-1.); +// if (v < 1.) return -3.*pow(1.-v,2)*exp((long double)pow(1.-v,3)); +// else return powP*pow(v-1.,powP-1.); +} + + + +#endif + + + +#endif diff --git a/contrib/HighOrderMeshOptimizer/OptHomMesh.cpp b/contrib/HighOrderMeshOptimizer/OptHomMesh.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a4249443864900a6c8daf6a7384bac04613b7754 --- /dev/null +++ b/contrib/HighOrderMeshOptimizer/OptHomMesh.cpp @@ -0,0 +1,460 @@ +#include "GRegion.h" +#include "MTriangle.h" +#include "MQuadrangle.h" +#include "MTetrahedron.h" +#include "ParamCoord.h" +#include "OptHomMesh.h" +#include "JacobianBasis.h" + +std::map<int, std::vector<double> > Mesh::_jacBez; + +static std::vector<double> _computeJB(const polynomialBasis *lagrange, const bezierBasis *bezier) +{ + int nbNodes = lagrange->points.size1(); + + // bezier points are defined in the [0,1] x [0,1] quad + fullMatrix<double> bezierPoints = bezier->points; + if (lagrange->parentType == TYPE_QUA) { + for (int i = 0; i < bezierPoints.size1(); ++i) { + bezierPoints(i, 0) = -1 + 2 * bezierPoints(i, 0); + bezierPoints(i, 1) = -1 + 2 * bezierPoints(i, 1); + } + } + + fullMatrix<double> allDPsi; + lagrange->df(bezierPoints, allDPsi); + int size = bezier->points.size1(); + std::vector<double> JB; + for (int d = 0; d < lagrange->dimension; ++d) { + size *= nbNodes; + } + JB.resize(size, 0.); + for (int k = 0; k < bezier->points.size1(); ++k) { + fullMatrix<double> dPsi(allDPsi, k * 3, 3); + if (lagrange->dimension == 2) { + for (int i = 0; i < nbNodes; i++) { + for (int j = 0; j < nbNodes; j++) { + double Jij = dPsi(i, 0) * dPsi(j, 1) - dPsi(i, 1) * dPsi(j,0); + for (int l = 0; l < bezier->points.size1(); l++) { + JB[(l * nbNodes + i) * nbNodes + j] += bezier->matrixLag2Bez(l, k) * Jij; + } + } + } + } + if (lagrange->dimension == 3) { + for (int i = 0; i < nbNodes; i++) { + for (int j = 0; j < nbNodes; j++) { + for (int m = 0; m < nbNodes; m++) { + double Jijm = + (dPsi(j, 1) * dPsi(m, 2) - dPsi(j, 2) * dPsi(m, 1)) * dPsi(i, 0) + + (dPsi(j, 2) * dPsi(m, 0) - dPsi(j, 0) * dPsi(m, 2)) * dPsi(i, 1) + + (dPsi(j, 0) * dPsi(m, 1) - dPsi(j, 1) * dPsi(m, 0)) * dPsi(i, 2); + for (int l = 0; l < bezier->points.size1(); l++) { + JB[(l * nbNodes + (i * nbNodes + j)) * nbNodes + m] += bezier->matrixLag2Bez(l, k) * Jijm; + } + } + } + } + } + } + return JB; +} + +Mesh::Mesh(GEntity *ge, std::set<MVertex*> &toFix, int method) : + _ge(ge) +{ + _dim = _ge->dim(); + + if (method & METHOD_PHYSCOORD) { + if (_dim == 2) _pc = new ParamCoordPhys2D; + else _pc = new ParamCoordPhys3D; + std::cout << "METHOD: Using physical coordinates" << std::endl; + } + else if (method & METHOD_SURFCOORD) { + if (_dim == 2) { + _pc = new ParamCoordSurf(_ge); + std::cout << "METHOD: Using surface parametric coordinates" << std::endl; + } + else std::cout << "ERROR: Surface parametric coordinates only for 2D optimization" << std::endl; + } + else { + _pc = new ParamCoordParent; + std::cout << "METHOD: Using parent parametric coordinates" << std::endl; + } + + if (method & METHOD_RELAXBND) std::cout << "METHOD: Relaxing boundary vertices" << std::endl; + else if (method & METHOD_FIXBND) std::cout << "METHOD: Fixing all boundary vertices" << std::endl; + else std::cout << "METHOD: Fixing vertices on geometric points and \"toFix\" boundary" << std::endl; + + // Initialize elements, vertices, free vertices and element->vertices connectivity + _nPC = 0; + _el.resize(_ge->getNumMeshElements()); + _el2FV.resize(_ge->getNumMeshElements()); + _el2V.resize(_ge->getNumMeshElements()); + _nBezEl.resize(_ge->getNumMeshElements()); + _nNodEl.resize(_ge->getNumMeshElements()); + _indPCEl.resize(_ge->getNumMeshElements()); + for (int iEl = 0; iEl < nEl(); iEl++) { + MElement *el = _ge->getMeshElement(iEl); + _el[iEl] = el; + const polynomialBasis *lagrange = el->getFunctionSpace(); + const bezierBasis *bezier = JacobianBasis::find(lagrange->type)->bezier; + if (_jacBez.find(lagrange->type) == _jacBez.end()) { + _jacBez[lagrange->type] = _computeJB(lagrange, bezier); + } + _nBezEl[iEl] = bezier->points.size1(); + _nNodEl[iEl] = lagrange->points.size1(); + for (int iVEl = 0; iVEl < lagrange->points.size1(); iVEl++) { + MVertex *vert = el->getVertex(iVEl); + int iV = addVert(vert); + _el2V[iEl].push_back(iV); + const int nPCV = _pc->nCoord(vert); + bool isFV = false; + if (method & METHOD_RELAXBND) isFV = true; + else if (method & METHOD_FIXBND) isFV = (vert->onWhat()->dim() == _dim) && (toFix.find(vert) == toFix.end()); + else isFV = (vert->onWhat()->dim() >= 1) && (toFix.find(vert) == toFix.end()); + if (isFV) { + int iFV = addFreeVert(vert,iV,nPCV,toFix); + _el2FV[iEl].push_back(iFV); + for (int i=_startPCFV[iFV]; i<_startPCFV[iFV]+nPCV; i++) _indPCEl[iEl].push_back(i); + } + else _el2FV[iEl].push_back(-1); + } + } + + // Initial coordinates + _ixyz.resize(nVert()); + for (int iV = 0; iV < nVert(); iV++) _ixyz[iV] = _vert[iV]->point(); + _iuvw.resize(nFV()); + for (int iFV = 0; iFV < nFV(); iFV++) _iuvw[iFV] = _pc->getUvw(_freeVert[iFV]); + + // Set current coordinates + _xyz = _ixyz; + _uvw = _iuvw; + + // Set normals to 2D elements (with magnitude of inverse Jacobian) or initial Jacobians of 3D elements + if (_dim == 2) { + _normEl.resize(nEl()); + for (int iEl = 0; iEl < nEl(); iEl++) _normEl[iEl] = getNormalEl(iEl); + } + else { + _invStraightJac.resize(nEl(),1.); + double dumJac[3][3]; + for (int iEl = 0; iEl < nEl(); iEl++) _invStraightJac[iEl] = 1. / _el[iEl]->getPrimaryJacobian(0.,0.,0.,dumJac); + } + if ((_dim == 2) && (method & METHOD_PROJJAC)) { + projJac = true; + std::cout << "METHOD: Using projected Jacobians" << std::endl; + } + else { + projJac = false; + std::cout << "METHOD: Using usual Jacobians" << std::endl; + } +} + +SVector3 Mesh::getNormalEl(int iEl) +{ + + switch (_el[iEl]->getType()) { + case TYPE_TRI: { + const int iV0 = _el2V[iEl][0], iV1 = _el2V[iEl][1], iV2 = _el2V[iEl][2]; + SVector3 v10 = _xyz[iV1]-_xyz[iV0], v20 = _xyz[iV2]-_xyz[iV0]; + SVector3 n = crossprod(v10, v20); + double xxx = n.norm(); + n *= 1./(xxx*xxx); + return n; + break; + } + case TYPE_QUA: { + const int iV0 = _el2V[iEl][0], iV1 = _el2V[iEl][1], iV3 = _el2V[iEl][3]; + SVector3 v10 = _xyz[iV1]-_xyz[iV0], v30 = _xyz[iV3]-_xyz[iV0]; + SVector3 n = crossprod(v10, v30); + double xxx = n.norm(); + n *= 4./(xxx*xxx); + return n; + break; + } + case TYPE_TET: { + return SVector3(0.); + break; + } + default: + std::cout << "ERROR: getNormalEl: Unknown element type" << std::endl; + break; + } + + return SVector3(0.); // Just to avoid compilation warnings... + +} + + + +int Mesh::addVert(MVertex* vert) +{ + + std::vector<MVertex*>::iterator itVert = find(_vert.begin(),_vert.end(),vert); + if (itVert == _vert.end()) { + _vert.push_back(vert); + return _vert.size()-1; + } + else return std::distance(_vert.begin(),itVert); + +} + + + +int Mesh::addFreeVert(MVertex* vert, const int iV, const int nPCV, std::set<MVertex*> &toFix) +{ + + std::vector<MVertex*>::iterator itVert = find(_freeVert.begin(),_freeVert.end(),vert); + if (itVert == _freeVert.end()) { + const int iStart = (_startPCFV.size() == 0)? 0 : _startPCFV.back()+_nPCFV.back(); + const bool forcedV = (vert->onWhat()->dim() < 2) || (toFix.find(vert) != toFix.end()); + _freeVert.push_back(vert); + _fv2V.push_back(iV); + _startPCFV.push_back(iStart); + _nPCFV.push_back(nPCV); + _nPC += nPCV; + _forced.push_back(forcedV); + return _freeVert.size()-1; + } + else return std::distance(_freeVert.begin(),itVert); + +} + + + +void Mesh::getUvw(double *it) +{ + +// std::vector<double>::iterator it = uvw.begin(); + for (int iFV = 0; iFV < nFV(); iFV++) { + SPoint3 &uvwV = _uvw[iFV]; + *it = uvwV[0]; it++; + if (_nPCFV[iFV] >= 2) { *it = uvwV[1]; it++; } + if (_nPCFV[iFV] == 3) { *it = uvwV[2]; it++; } + } + +} + + + +void Mesh::updateMesh(const double *it) +{ + +// std::vector<double>::const_iterator it = uvw.begin(); + for (int iFV = 0; iFV < nFV(); iFV++) { + int iV = _fv2V[iFV]; + SPoint3 &uvwV = _uvw[iFV]; + uvwV[0] = *it; it++; + if (_nPCFV[iFV] >= 2) { uvwV[1] = *it; it++; } + if (_nPCFV[iFV] == 3) { uvwV[2] = *it; it++; } + _xyz[iV] = _pc->uvw2Xyz(_freeVert[iFV],uvwV); + } + +} + + + +void Mesh::distSqToStraight(std::vector<double> &dSq) +{ + std::vector<SPoint3> sxyz(nVert()); + for (int iEl = 0; iEl < nEl(); iEl++) { + MElement *el = _el[iEl]; + const polynomialBasis *lagrange = el->getFunctionSpace(); + const polynomialBasis *lagrange1 = el->getFunctionSpace(1); + int nV = lagrange->points.size1(); + int nV1 = lagrange1->points.size1(); + for (int i = 0; i < nV1; ++i) { + sxyz[_el2V[iEl][i]] = _vert[_el2V[iEl][i]]->point(); + } + for (int i = nV1; i < nV; ++i) { + double f[256]; + lagrange1->f(lagrange->points(i, 0), lagrange->points(i, 1), lagrange->points(i, 2), f); + for (int j = 0; j < nV1; ++j) + sxyz[_el2V[iEl][i]] += sxyz[_el2V[iEl][j]] * f[j]; + } + } + + for (int iV = 0; iV < nVert(); iV++) { + SPoint3 d = _xyz[iV]-sxyz[iV]; + dSq[iV] = d[0]*d[0]+d[1]*d[1]+d[2]*d[2]; + } +} + + + +void Mesh::updateGEntityPositions() +{ + + for (int iV = 0; iV < nVert(); iV++) _vert[iV]->setXYZ(_xyz[iV].x(),_xyz[iV].y(),_xyz[iV].z()); + +} + + + +void Mesh::scaledJac(int iEl, std::vector<double> &sJ) +{ + const std::vector<double> &jacBez = _jacBez[_el[iEl]->getTypeForMSH()]; + if (_dim == 2) { + SVector3 &n = _normEl[iEl]; + if (projJac) { + for (int l = 0; l < _nBezEl[iEl]; l++) { + sJ[l] = 0.; + for (int i = 0; i < _nNodEl[iEl]; i++) { + int &iVi = _el2V[iEl][i]; + for (int j = 0; j < _nNodEl[iEl]; j++) { + int &iVj = _el2V[iEl][j]; + sJ[l] += jacBez[indJB2D(iEl,l,i,j)] + * (_xyz[iVi].x() * _xyz[iVj].y() * n.z() - _xyz[iVi].x() * _xyz[iVj].z() * n.y() + + _xyz[iVi].y() * _xyz[iVj].z() * n.x()); + } + } + } + } + else + for (int l = 0; l < _nBezEl[iEl]; l++) { + sJ[l] = 0.; + for (int i = 0; i < _nNodEl[iEl]; i++) { + int &iVi = _el2V[iEl][i]; + for (int j = 0; j < _nNodEl[iEl]; j++) { + int &iVj = _el2V[iEl][j]; + sJ[l] += jacBez[indJB2D(iEl,l,i,j)] * _xyz[iVi].x() * _xyz[iVj].y(); + } + } + sJ[l] *= n.z(); + } + } + else { + for (int l = 0; l < _nBezEl[iEl]; l++) { + sJ[l] = 0.; + for (int i = 0; i < _nNodEl[iEl]; i++) { + int &iVi = _el2V[iEl][i]; + for (int j = 0; j < _nNodEl[iEl]; j++) { + int &iVj = _el2V[iEl][j]; + for (int m = 0; m < _nNodEl[iEl]; m++) { + int &iVm = _el2V[iEl][m]; + sJ[l] += jacBez[indJB3D(iEl,l,i,j,m)] * _xyz[iVi].x() * _xyz[iVj].y() * _xyz[iVm].z(); + } + } + } + sJ[l] *= _invStraightJac[iEl]; + } + } + +} + + + +void Mesh::gradScaledJac(int iEl, std::vector<double> &gSJ) +{ + const std::vector<double> &jacBez = _jacBez[_el[iEl]->getTypeForMSH()]; + if (_dim == 2) { + int iPC = 0; + SVector3 n = _normEl[iEl]; + if (projJac) { + for (int i = 0; i < _nNodEl[iEl]; i++) { + int &iFVi = _el2FV[iEl][i]; + if (iFVi >= 0) { + std::vector<SPoint3> gXyzV(_nBezEl[iEl],SPoint3(0.,0.,0.)); + std::vector<SPoint3> gUvwV(_nBezEl[iEl]); + for (int m = 0; m < _nNodEl[iEl]; m++) { + int &iVm = _el2V[iEl][m]; + const double vpx = _xyz[iVm].y() * n.z() - _xyz[iVm].z() * n.y(); + const double vpy = -_xyz[iVm].x() * n.z() + _xyz[iVm].z() * n.x(); + const double vpz = _xyz[iVm].x() * n.y() - _xyz[iVm].y() * n.x(); + for (int l = 0; l < _nBezEl[iEl]; l++) { + gXyzV[l][0] += jacBez[indJB2D(iEl,l,i,m)] * vpx; + gXyzV[l][1] += jacBez[indJB2D(iEl,l,i,m)] * vpy; + gXyzV[l][2] += jacBez[indJB2D(iEl,l,i,m)] * vpz; + } + } + _pc->gXyz2gUvw(_freeVert[iFVi],_uvw[iFVi],gXyzV,gUvwV); + for (int l = 0; l < _nBezEl[iEl]; l++) { + gSJ[indGSJ(iEl,l,iPC)] = gUvwV[l][0]; + if (_nPCFV[iFVi] >= 2) gSJ[indGSJ(iEl,l,iPC+1)] = gUvwV[l][1]; + } + iPC += _nPCFV[iFVi]; + } + } + } + else + for (int i = 0; i < _nNodEl[iEl]; i++) { + int &iFVi = _el2FV[iEl][i]; + if (iFVi >= 0) { + std::vector<SPoint3> gXyzV(_nBezEl[iEl],SPoint3(0.,0.,0.)); + std::vector<SPoint3> gUvwV(_nBezEl[iEl]); + for (int m = 0; m < _nNodEl[iEl]; m++) { + int &iVm = _el2V[iEl][m]; + for (int l = 0; l < _nBezEl[iEl]; l++) { + gXyzV[l][0] += jacBez[indJB2D(iEl,l,i,m)] * _xyz[iVm].y() * n.z(); + gXyzV[l][1] += jacBez[indJB2D(iEl,l,m,i)] * _xyz[iVm].x() * n.z(); + } + } + _pc->gXyz2gUvw(_freeVert[iFVi],_uvw[iFVi],gXyzV,gUvwV); + for (int l = 0; l < _nBezEl[iEl]; l++) { + gSJ[indGSJ(iEl,l,iPC)] = gUvwV[l][0]; + if (_nPCFV[iFVi] >= 2) gSJ[indGSJ(iEl,l,iPC+1)] = gUvwV[l][1]; + } + iPC += _nPCFV[iFVi]; + } + } + } + else { + int iPC = 0; + for (int i = 0; i < _nNodEl[iEl]; i++) { + int &iFVi = _el2FV[iEl][i]; + if (iFVi >= 0) { + std::vector<SPoint3> gXyzV(_nBezEl[iEl],SPoint3(0.,0.,0.)); + std::vector<SPoint3> gUvwV(_nBezEl[iEl]); + for (int a = 0; a < _nNodEl[iEl]; a++) { + int &iVa = _el2V[iEl][a]; + for (int b = 0; b < _nNodEl[iEl]; b++) { + int &iVb = _el2V[iEl][b]; + for (int l = 0; l < _nBezEl[iEl]; l++) { + gXyzV[l][0] += jacBez[indJB3D(iEl,l,i,a,b)] * _xyz[iVa].y() * _xyz[iVb].z() * _invStraightJac[iEl]; + gXyzV[l][1] += jacBez[indJB3D(iEl,l,a,i,b)] * _xyz[iVa].x() * _xyz[iVb].z() * _invStraightJac[iEl]; + gXyzV[l][2] += jacBez[indJB3D(iEl,l,a,b,i)] * _xyz[iVa].x() * _xyz[iVb].y() * _invStraightJac[iEl]; + } + } + } + _pc->gXyz2gUvw(_freeVert[iFVi],_uvw[iFVi],gXyzV,gUvwV); + for (int l = 0; l < _nBezEl[iEl]; l++) { + gSJ[indGSJ(iEl,l,iPC)] = gUvwV[l][0]; + if (_nPCFV[iFVi] >= 2) gSJ[indGSJ(iEl,l,iPC)] = gUvwV[l][1]; + if (_nPCFV[iFVi] == 3) gSJ[indGSJ(iEl,l,iPC)] = gUvwV[l][2]; + } + iPC += _nPCFV[iFVi]; + } + } + } + +} + +void Mesh::writeMSH(const char *filename) +{ + FILE *f = fopen(filename, "w"); + + fprintf(f, "$MeshFormat\n"); + fprintf(f, "2.2 0 8\n"); + fprintf(f, "$EndMeshFormat\n"); + + fprintf(f, "$Nodes\n"); + fprintf(f, "%d\n", nVert()); + for (int i = 0; i < nVert(); i++) + fprintf(f, "%d %22.15E %22.15E %22.15E\n", i + 1, _xyz[i].x(), _xyz[i].y(), _xyz[i].z()); + fprintf(f, "$EndNodes\n"); + + fprintf(f, "$Elements\n"); + fprintf(f, "%d\n", nEl()); + for (int iEl = 0; iEl < nEl(); iEl++) { +// MElement *MEl = _el[iEl]; + fprintf(f, "%d %d 2 0 %d", iEl+1, _el[iEl]->getTypeForMSH(), _ge->tag()); + for (int iVEl = 0; iVEl < _el2V[iEl].size(); iVEl++) fprintf(f, " %d", _el2V[iEl][iVEl] + 1); + fprintf(f, "\n"); + } + fprintf(f, "$EndElements\n"); + + fclose(f); + +} diff --git a/contrib/HighOrderMeshOptimizer/OptHomMesh.h b/contrib/HighOrderMeshOptimizer/OptHomMesh.h new file mode 100644 index 0000000000000000000000000000000000000000..6a9067d98ed956fe63e3d053f3265f1fd870abf1 --- /dev/null +++ b/contrib/HighOrderMeshOptimizer/OptHomMesh.h @@ -0,0 +1,113 @@ +#ifndef _OPT_HOM_MESH_H_ +#define _OPT_HOM_MESH_H_ + +#include <vector> +#include <map> +#include <bitset> +#include "GFace.h" +#include "MVertex.h" +#include "ParamCoord.h" + +class Mesh +{ + +public: + + Mesh(GEntity *ge, std::set<MVertex*> & toFix, int method); + + inline const int &nPC() { return _nPC; } + inline int nVert() { return _vert.size(); } + inline int nFV() { return _freeVert.size(); } + inline const bool forced(int iV) { return _forced[iV]; } + inline int nEl() { return _el.size(); } + inline const int &nPCFV(int iFV) { return _nPCFV[iFV]; } + inline int indPCFV(int iFV, int iPC) { return _startPCFV[iFV]+iPC; } + inline int nPCEl(int iEl) { return _indPCEl[iEl].size(); } + inline const int &indPCEl(int iEl, int iPC) { return _indPCEl[iEl][iPC]; } + inline const int &nBezEl(int iEl) { return _nBezEl[iEl]; } + + void scaledJac(int iEl, std::vector<double> &sJ); + void gradScaledJac(int iEl, std::vector<double> &gSJ); + inline int indGSJ(int iEl, int l, int iPC) { return iPC*_nBezEl[iEl]+l; } + + inline double distSq(int iFV); + inline void gradDistSq(int iV, std::vector<double> &gDSq); + + void getUvw(double *it); + void updateMesh(const double *it); + void distSqToStraight(std::vector<double> &dSq); + + void updateGEntityPositions(); + void writeMSH(const char *filename); + + enum { METHOD_RELAXBND = 1 << 0, METHOD_FIXBND = 1 << 1, METHOD_PHYSCOORD = 1 << 2, + METHOD_SURFCOORD = 1 << 3, METHOD_PROJJAC = 1 << 4 }; + +// inline double xyzDBG(int iV, int iCoord) { return _xyz[iV][iCoord]; } +// inline double ixyzDBG(int iV, int iCoord) { return _ixyz[iV][iCoord]; } +// inline double sxyzDBG(int iV, int iCoord) { return _sxyz[iV][iCoord]; } +// inline double uvwDBG(int iFV, int iCoord) { return _uvw[iFV][iCoord]; } +// inline int fv2VDBG(int iFV) { return _fv2V[iFV]; } +// inline bool forcedDBG(int iV) { return _forced[iV]; } + +private: + + GEntity *_ge; + int _dim; + int _nPC; // Total nb. of parametric coordinates + + std::vector<MElement*> _el; // List of elements + std::vector<SVector3> _normEl; // Normals to 2D elements + std::vector<double> _invStraightJac; // Initial Jacobians for 3D elements + std::vector<MVertex*> _vert, _freeVert; // List of vert., free vert. + std::vector<int> _fv2V; // Index of free vert. -> index of vert. + std::vector<bool> _forced; // Is vertex forced? + std::vector<SPoint3> _xyz, _ixyz; // Physical coord. of ALL vertices (current, straight, init.) + std::vector<SPoint3> _uvw, _iuvw; // Parametric coord. of FREE vertices (current, straight, init.) + std::vector<int> _startPCFV; // Start index of parametric coordinates for a free vertex + std::vector<int> _nPCFV; // Number of parametric coordinates for a free vertex + std::vector<std::vector<int> > _el2FV, _el2V; // Free vertices, vertices in element + std::vector<int> _nBezEl, _nNodEl; // Number of Bezier poly. and nodes for an el. + std::vector<std::vector<int> > _indPCEl; // Index of parametric coord. for an el. + + ParamCoord *_pc; + bool projJac; // Using "projected" Jacobians or not + + static std::map<int, std::vector<double> > _jacBez; + + int addVert(MVertex* vert); + int addFreeVert(MVertex* vert, const int iV, const int nPCV, std::set<MVertex*> &toFix); + SVector3 getNormalEl(int iEl); + inline int indJB2D(int iEl, int l, int i, int j) { return (l*_nNodEl[iEl]+i)*_nNodEl[iEl]+j; } + inline int indJB3D(int iEl, int l, int i, int j, int m) { return ((l*_nNodEl[iEl]+i)*_nNodEl[iEl]+j)*_nNodEl[iEl]+m; } + +}; + + + +double Mesh::distSq(int iFV) +{ + + SPoint3 d = _xyz[_fv2V[iFV]]-_ixyz[_fv2V[iFV]]; + return d[0]*d[0]+d[1]*d[1]+d[2]*d[2]; + +} + + + +void Mesh::gradDistSq(int iFV, std::vector<double> &gDSq) +{ + + SPoint3 gXyz = (_xyz[_fv2V[iFV]]-_ixyz[_fv2V[iFV]])*2.; + SPoint3 gUvw; + _pc->gXyz2gUvw(_freeVert[iFV],_uvw[iFV],gXyz,gUvw); + + gDSq[0] = gUvw[0]; + if (_nPCFV[iFV] >= 2) gDSq[1] = gUvw[1]; + if (_nPCFV[iFV] == 3) gDSq[2] = gUvw[2]; + +} + + + +#endif diff --git a/contrib/HighOrderMeshOptimizer/OptHomRun.cpp b/contrib/HighOrderMeshOptimizer/OptHomRun.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b0d03813eb10b64aaee7c2a5c7309a60645efbd5 --- /dev/null +++ b/contrib/HighOrderMeshOptimizer/OptHomRun.cpp @@ -0,0 +1,228 @@ +//#include <fenv.h> +#include <stdio.h> +#include <sstream> +#include <string.h> +#include "OptHomRun.h" +#include "GModel.h" +#include "Gmsh.h" +#include "MTriangle.h" +#include "MQuadrangle.h" +#include "MTetrahedron.h" +#include "highOrderTools.h" +#include "OptHomMesh.h" +#include "OptHOM.h" + +std::set<MVertex*> filter2D(GFace *gf, int nbLayers, double _qual, bool onlytheworst = false) +{ + std::set<MVertex*> touched; + std::set<MVertex*> not_touched; + std::set<MTriangle *> ts; + std::set<MQuadrangle *> qs; + + if (onlytheworst) { + double minQ = 1.e22; + MQuadrangle *worst; + for (int i = 0; i < gf->quadrangles.size(); i++) { + MQuadrangle *q = gf->quadrangles[i]; + double Q = q->distoShapeMeasure(); + if (Q < minQ) { + worst = q; + minQ = Q; + } + } + qs.insert(worst); + for (int j = 0; j < worst->getNumVertices(); j++) + touched.insert(worst->getVertex(j)); + } + + else { + for (int i = 0; i < gf->triangles.size(); i++) { + MTriangle *t = gf->triangles[i]; + double Q = t->distoShapeMeasure(); + if (Q < _qual || Q > 1.01) { + ts.insert(t); + for (int j = 0; j < t->getNumVertices(); j++) + touched.insert(t->getVertex(j)); + } + } + for (int i = 0; i < gf->quadrangles.size(); i++) { + MQuadrangle *q = gf->quadrangles[i]; + double Q = q->distoShapeMeasure(); + if (Q < _qual || Q > 1.0) { + qs.insert(q); + for (int j = 0; j < q->getNumVertices(); j++) + touched.insert(q->getVertex(j)); + } + } + } + printf("FILTER2D : %d bad triangles found among %6d\n", ts.size(), gf->triangles.size()); + printf("FILTER2D : %d bad quads found among %6d\n", qs.size(), gf->quadrangles.size()); + + // add layers of elements around bad elements + for (int layer = 0; layer < nbLayers; layer++) { + for (int i = 0; i < gf->triangles.size(); i++) { + MTriangle *t = gf->triangles[i]; + bool add_ = false; + for (int j = 0; j < t->getNumVertices(); j++) { + if (touched.find(t->getVertex(j)) != touched.end()) { + add_ = true; + } + } + if (add_) ts.insert(t); + } + for (int i = 0; i < gf->quadrangles.size(); i++) { + bool add_ = false; + MQuadrangle *q = gf->quadrangles[i]; + for (int j = 0; j < q->getNumVertices(); j++) { + if (touched.find(q->getVertex(j)) != touched.end()) { + add_ = true; + } + } + if (add_) qs.insert(q); + } + + for (std::set<MTriangle*>::iterator it = ts.begin(); it != ts.end(); ++it) { + for (int j = 0; j < (*it)->getNumVertices(); j++) { + touched.insert((*it)->getVertex(j)); + } + } + for (std::set<MQuadrangle*>::iterator it = qs.begin(); it != qs.end(); ++it) { + for (int j = 0; j < (*it)->getNumVertices(); j++) { + touched.insert((*it)->getVertex(j)); + } + } + } + + for (int i = 0; i < gf->triangles.size(); i++) { + if (ts.find(gf->triangles[i]) == ts.end()) { + for (int j = 0; j < gf->triangles[i]->getNumVertices(); j++) { + if (touched.find(gf->triangles[i]->getVertex(j)) != touched.end()) not_touched.insert(gf->triangles[i]->getVertex(j)); + } + } + } + for (int i = 0; i < gf->quadrangles.size(); i++) { + if (qs.find(gf->quadrangles[i]) == qs.end()) { + for (int j = 0; j < gf->quadrangles[i]->getNumVertices(); j++) { + if (touched.find(gf->quadrangles[i]->getVertex(j)) != touched.end()) not_touched.insert(gf->quadrangles[i]->getVertex(j)); + } + } + } + + gf->triangles.clear(); + gf->quadrangles.clear(); + gf->triangles.insert(gf->triangles.begin(), ts.begin(), ts.end()); + gf->quadrangles.insert(gf->quadrangles.begin(), qs.begin(), qs.end()); + + printf("FILTER2D : AFTER FILTERING %d triangles %d quads remain %d nodes on the boundary\n", gf->triangles.size(), gf->quadrangles.size(), not_touched.size()); + return not_touched; +} + +std::set<MVertex*> filter3D(GRegion *gr, int nbLayers, double _qual) +{ + std::set<MVertex*> touched; + std::set<MVertex*> not_touched; + std::set<MTetrahedron *> ts; + for (int i = 0; i < gr->tetrahedra.size(); i++) { + MTetrahedron *t = gr->tetrahedra[i]; + if (t->distoShapeMeasure() < _qual) { + ts.insert(t); + for (int j = 0; j < t->getNumVertices(); j++) + touched.insert(t->getVertex(j)); + } + if (ts.size() == 1) break; + } + + printf("FILTER3D : %d bad tets found among %6d\n", ts.size(), gr->tetrahedra.size()); + + // add layers of elements around bad elements + for (int layer = 0; layer < nbLayers; layer++) { + for (int i = 0; i < gr->tetrahedra.size(); i++) { + MTetrahedron *t = gr->tetrahedra[i]; + bool add_ = false; + for (int j = 0; j < t->getNumVertices(); j++) { + if (touched.find(t->getVertex(j)) != touched.end()) { + add_ = true; + } + } + if (add_) ts.insert(t); + } + + for (std::set<MTetrahedron*>::iterator it = ts.begin(); it != ts.end(); ++it) { + for (int j = 0; j < (*it)->getNumVertices(); j++) { + touched.insert((*it)->getVertex(j)); + } + } + } + + for (int i = 0; i < gr->tetrahedra.size(); i++) { + if (ts.find(gr->tetrahedra[i]) == ts.end()) { + for (int j = 0; j < gr->tetrahedra[i]->getNumVertices(); j++) { + if (touched.find(gr->tetrahedra[i]->getVertex(j)) != touched.end()) not_touched.insert(gr->tetrahedra[i]->getVertex(j)); + } + } + } + + gr->tetrahedra.clear(); + gr->tetrahedra.insert(gr->tetrahedra.begin(), ts.begin(), ts.end()); + + printf("FILTER3D : AFTER FILTERING %d tets remain %d nodes on the boundary\n", gr->tetrahedra.size(), not_touched.size()); + return not_touched; +} + +void HighOrderMeshOptimizer (GModel *gm, OptHomParameters &p) +{ + int samples = 30; + +// int method = Mesh::METHOD_RELAXBND | Mesh::METHOD_PHYSCOORD | Mesh::METHOD_PROJJAC; +// int method = Mesh::METHOD_FIXBND | Mesh::METHOD_PHYSCOORD | Mesh::METHOD_PROJJAC; +// int method = Mesh::METHOD_FIXBND | Mesh::METHOD_SURFCOORD | Mesh::METHOD_PROJJAC; +// int method = Mesh::METHOD_FIXBND; +// int method = Mesh::METHOD_SURFCOORD | Mesh::METHOD_PROJJAC; +// int method = Mesh::METHOD_RELAXBND | Mesh::METHOD_SURFCOORD | Mesh::METHOD_PROJJAC; + int method = Mesh::METHOD_PROJJAC; + + SVector3 BND(gm->bounds().max(), gm->bounds().min()); + double SIZE = BND.norm(); + + if (p.dim == 2) { + for (GModel::fiter itf = gm->firstFace(); itf != gm->lastFace(); ++itf) { + + std::vector<MTriangle*> tris = (*itf)->triangles; + std::vector<MQuadrangle*> quas = (*itf)->quadrangles; + std::set<MVertex*> toFix = filter2D(*itf, p.nbLayers, p.BARRIER); + OptHOM *temp = new OptHOM(*itf, toFix, method); + (*itf)->triangles = tris; + (*itf)->quadrangles = quas; + + double distMaxBND, distAvgBND, minJac, maxJac; + temp->getDistances(distMaxBND, distAvgBND, minJac, maxJac); + std::ostringstream ossI; + ossI << "initial_" << (*itf)->tag() << ".msh"; + temp->mesh.writeMSH(ossI.str().c_str()); + printf("--> Mesh Loaded for GFace %d : minJ %12.5E -- maxJ %12.5E\n", (*itf)->tag(), minJac, maxJac); + if (distMaxBND < 1.e-8 * SIZE && minJac > p.BARRIER) continue; + int result = temp->optimize(p.weightFixed, p.weightFree, p.BARRIER, samples, p.itMax); + temp->mesh.updateGEntityPositions(); + std::ostringstream ossF; + ossF << "final_" << (*itf)->tag() << ".msh"; + temp->mesh.writeMSH(ossF.str().c_str()); + } + } + else if (p.dim == 3) { + for (GModel::riter itr = gm->firstRegion(); itr != gm->lastRegion(); ++itr) { + std::vector<MTetrahedron*> tets = (*itr)->tetrahedra; + std::set<MVertex*> toFix; + filter3D(*itr, p.nbLayers, p.BARRIER); + OptHOM *temp = new OptHOM(*itr, toFix, method); + double distMaxBND, distAvgBND, minJac, maxJac; + temp->getDistances(distMaxBND, distAvgBND, minJac, maxJac); + temp->mesh.writeMSH("initial.msh"); + printf("--> Mesh Loaded for GRegion %d : minJ %12.5E -- maxJ %12.5E\n", (*itr)->tag(), minJac, maxJac); + if (distMaxBND < 1.e-8 * SIZE && minJac > p.BARRIER) continue; + int result = temp->optimize(p.weightFixed, p.weightFree, p.BARRIER, samples, p.itMax); + temp->mesh.updateGEntityPositions(); + (*itr)->tetrahedra = tets; + temp->mesh.writeMSH("final.msh"); + } + } +} diff --git a/contrib/HighOrderMeshOptimizer/OptHomRun.h b/contrib/HighOrderMeshOptimizer/OptHomRun.h new file mode 100644 index 0000000000000000000000000000000000000000..10ad8a3f86390d0327571c97eedfde2ee8b4d399 --- /dev/null +++ b/contrib/HighOrderMeshOptimizer/OptHomRun.h @@ -0,0 +1,33 @@ +#ifndef _OPTHOMRUN_H_ +#define _OPTHOMRUN_H_ + +class GModel; + +struct OptHomParameters { + // INPUT ------> + double STOP ; // stop criterion + double BARRIER ; // minimum scaled jcaobian + double weightFixed ; // weight of the energy for fixed nodes + double weightFree ; // weight of the energy for free nodes + int nbLayers ; // number of layers taken around a bad element + int dim ; // which dimension to optimize + int itMax ; // max number of iterations in the optimization process + double TMAX ; // max CPU time allowed + bool onlyVisible ; // apply optimization to visible entities ONLY + // OUTPUT ------> + int SUCCESS ; // 0 --> success , 1 --> Not converged + double minJac, maxJac; // after optimization, range of jacobians + double DT; // Time for optimization + + OptHomParameters () + // default values + : STOP (0.01) , BARRIER (0.1) , weightFixed (10.), weightFree (1.e-3), + nbLayers (6) , dim(3) , itMax(10000), onlyVisible(true) + { + } +}; + +void HighOrderMeshOptimizer (GModel *gm, OptHomParameters &p); + + +#endif diff --git a/contrib/HighOrderMeshOptimizer/ParamCoord.cpp b/contrib/HighOrderMeshOptimizer/ParamCoord.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ccaba35d92abcfb79de17512b383fe578514cea3 --- /dev/null +++ b/contrib/HighOrderMeshOptimizer/ParamCoord.cpp @@ -0,0 +1,174 @@ +#include <iostream> +#include "GFace.h" +#include "GEdge.h" +#include "MVertex.h" +#include "ParamCoord.h" + + + +ParamCoordSurf::ParamCoordSurf(GEntity *ge) +{ + if ((ge->dim() == 2) && (ge->geomType() != GEntity::DiscreteSurface)) _gf = static_cast<GFace*>(ge); + else std::cout << "ERROR: Using surface parametric coordinates with non-surface geometric entity" << std::endl; +} + + + +SPoint3 ParamCoordSurf::getUvw(MVertex* vert) +{ + SPoint2 p; + reparamMeshVertexOnFace(vert,_gf,p); + return SPoint3(p[0],p[1],0.); +} + + + +SPoint3 ParamCoordSurf::uvw2Xyz(MVertex* vert, const SPoint3 &uvw) +{ + GPoint gp = _gf->point(uvw[0],uvw[1]); + return SPoint3(gp.x(),gp.y(),gp.z()); +} + + + +void ParamCoordSurf::gXyz2gUvw(MVertex* vert, const SPoint3 &uvw, const SPoint3 &gXyz, SPoint3 &gUvw) +{ + Pair<SVector3,SVector3> der = _gf->firstDer(SPoint2(uvw[0],uvw[1])); + gUvw[0] = gXyz.x() * der.first().x() + gXyz.y() * der.first().y() + gXyz.z() * der.first().z(); + gUvw[1] = gXyz.x() * der.second().x() + gXyz.y() * der.second().y() + gXyz.z() * der.second().z(); +} + + + +void ParamCoordSurf::gXyz2gUvw(MVertex* vert, const SPoint3 &uvw, const std::vector<SPoint3> &gXyz, std::vector<SPoint3> &gUvw) +{ + Pair<SVector3,SVector3> der = _gf->firstDer(SPoint2(uvw[0],uvw[1])); + std::vector<SPoint3>::iterator itUvw=gUvw.begin(); + for (std::vector<SPoint3>::const_iterator itXyz=gXyz.begin(); itXyz != gXyz.end(); itXyz++) { + (*itUvw)[0] = itXyz->x() * der.first().x() + itXyz->y() * der.first().y() + itXyz->z() * der.first().z(); + (*itUvw)[1] = itXyz->x() * der.second().x() + itXyz->y() * der.second().y() + itXyz->z() * der.second().z(); + itUvw++; + } +} + + + +SPoint3 ParamCoordParent::getUvw(MVertex* vert) +{ + + GEntity *ge = vert->onWhat(); + if ((ge->geomType() == GEntity::DiscreteCurve) || (ge->geomType() == GEntity::DiscreteSurface)) + std::cout << "ERROR: using parent coordinates on discrete curve or surface" << std::endl; + + switch (ge->dim()) { + case 1: { + SPoint3 p(0.,0.,0.); + reparamMeshVertexOnEdge(vert,static_cast<GEdge*>(ge),p[0]); + return p; + break; + } + case 2: { + SPoint2 p; + reparamMeshVertexOnFace(vert,static_cast<GFace*>(ge),p); + return SPoint3(p[0],p[1],0.); + break; + } + case 3: { + return vert->point(); + break; + } + } + +} + + + +SPoint3 ParamCoordParent::uvw2Xyz(MVertex* vert, const SPoint3 &uvw) +{ + + GEntity *ge = vert->onWhat(); + + switch (ge->dim()) { + case 1: { + GPoint gp = static_cast<GEdge*>(ge)->point(uvw[0]); + return SPoint3(gp.x(),gp.y(),gp.z()); + break; + } + case 2: { + GPoint gp = static_cast<GFace*>(ge)->point(uvw[0],uvw[1]); + return SPoint3(gp.x(),gp.y(),gp.z()); + break; + } + case 3: { + return uvw; + break; + } + } + +} + + + +void ParamCoordParent::gXyz2gUvw(MVertex* vert, const SPoint3 &uvw, const SPoint3 &gXyz, SPoint3 &gUvw) +{ + + GEntity *ge = vert->onWhat(); + + switch (ge->dim()) { + case 1: { + SVector3 der = static_cast<GEdge*>(ge)->firstDer(uvw[0]); + gUvw[0] = gXyz.x() * der.x() + gXyz.y() * der.y() + gXyz.z() * der.z(); + break; + } + case 2: { + Pair<SVector3,SVector3> der = static_cast<GFace*>(ge)->firstDer(SPoint2(uvw[0],uvw[1])); + gUvw[0] = gXyz.x() * der.first().x() + gXyz.y() * der.first().y() + gXyz.z() * der.first().z(); + gUvw[1] = gXyz.x() * der.second().x() + gXyz.y() * der.second().y() + gXyz.z() * der.second().z(); + break; + } + case 3: { + gUvw = gXyz; + break; + } + } + +} + + + +void ParamCoordParent::gXyz2gUvw(MVertex* vert, const SPoint3 &uvw, const std::vector<SPoint3> &gXyz, std::vector<SPoint3> &gUvw) +{ + + GEntity *ge = vert->onWhat(); + + switch (ge->dim()) { + case 1: { + SVector3 der = static_cast<GEdge*>(ge)->firstDer(uvw[0]); + std::vector<SPoint3>::iterator itUvw=gUvw.begin(); + for (std::vector<SPoint3>::const_iterator itXyz=gXyz.begin(); itXyz != gXyz.end(); itXyz++) { + (*itUvw)[0] = itXyz->x() * der.x() + itXyz->y() * der.y() + itXyz->z() * der.z(); + itUvw++; + } + break; + } + case 2: { + Pair<SVector3,SVector3> der = static_cast<GFace*>(ge)->firstDer(SPoint2(uvw[0],uvw[1])); + std::vector<SPoint3>::iterator itUvw=gUvw.begin(); + for (std::vector<SPoint3>::const_iterator itXyz=gXyz.begin(); itXyz != gXyz.end(); itXyz++) { + (*itUvw)[0] = itXyz->x() * der.first().x() + itXyz->y() * der.first().y() + itXyz->z() * der.first().z(); + (*itUvw)[1] = itXyz->x() * der.second().x() + itXyz->y() * der.second().y() + itXyz->z() * der.second().z(); + itUvw++; + } + break; + } + case 3: { + std::vector<SPoint3>::iterator itUvw=gUvw.begin(); + for (std::vector<SPoint3>::const_iterator itXyz=gXyz.begin(); itXyz != gXyz.end(); itXyz++) { + *itUvw = *itXyz; + itUvw++; + } + break; + } + } + +} diff --git a/contrib/HighOrderMeshOptimizer/ParamCoord.h b/contrib/HighOrderMeshOptimizer/ParamCoord.h new file mode 100644 index 0000000000000000000000000000000000000000..4f70573e9aeddeeb18e695d24f9cc9d48c158562 --- /dev/null +++ b/contrib/HighOrderMeshOptimizer/ParamCoord.h @@ -0,0 +1,101 @@ +#ifndef _PARAMCOORD_H_ +#define _PARAMCOORD_H_ + + + +class ParamCoord +{ + +public: + + virtual int nCoord(MVertex* vert) = 0; // Number of parametric coordinates for vertex + virtual SPoint3 getUvw(MVertex* vert) = 0; // Get parametric coordinates of vertex + virtual SPoint3 uvw2Xyz(MVertex* vert, const SPoint3 &uvw) = 0; // Calculate physical coordinates from parametric coordinates of vertex + virtual void gXyz2gUvw(MVertex* vert, const SPoint3 &uvw, const SPoint3 &gXyz, SPoint3 &gUvw) = 0; // Calculate derivatives w.r.t parametric coordinates + virtual void gXyz2gUvw(MVertex* vert, const SPoint3 &uvw, const std::vector<SPoint3> &gXyz, std::vector<SPoint3> &gUvw) = 0; // Calculate derivatives w.r.t parametric coordinates + virtual ~ParamCoord() {} + +}; + + + +class ParamCoordPhys3D : public ParamCoord +{ + +public: + + int nCoord(MVertex* vert) { return 3; } + virtual SPoint3 getUvw(MVertex* vert) { return vert->point(); } + virtual SPoint3 uvw2Xyz(MVertex* vert, const SPoint3 &uvw) { return uvw; } + virtual void gXyz2gUvw(MVertex* vert, const SPoint3 &uvw, const SPoint3 &gXyz, SPoint3 &gUvw) { gUvw = gXyz; } + virtual void gXyz2gUvw(MVertex* vert, const SPoint3 &uvw, const std::vector<SPoint3> &gXyz, std::vector<SPoint3> &gUvw) + { + std::vector<SPoint3>::iterator itUvw=gUvw.begin(); + for (std::vector<SPoint3>::const_iterator itXyz=gXyz.begin(); itXyz != gXyz.end(); itXyz++) { + *itUvw = *itXyz; + itUvw++; + } + } + +}; + + + +class ParamCoordPhys2D : public ParamCoord +{ + +public: + + int nCoord(MVertex* vert) { return 2; } + virtual SPoint3 getUvw(MVertex* vert) { return vert->point(); } + virtual SPoint3 uvw2Xyz(MVertex* vert, const SPoint3 &uvw) { return uvw; } + virtual void gXyz2gUvw(MVertex* vert, const SPoint3 &uvw, const SPoint3 &gXyz, SPoint3 &gUvw) { gUvw = gXyz; } + virtual void gXyz2gUvw(MVertex* vert, const SPoint3 &uvw, const std::vector<SPoint3> &gXyz, std::vector<SPoint3> &gUvw) + { + std::vector<SPoint3>::iterator itUvw=gUvw.begin(); + for (std::vector<SPoint3>::const_iterator itXyz=gXyz.begin(); itXyz != gXyz.end(); itXyz++) { + *itUvw = *itXyz; + itUvw++; + } + } + +}; + + + +class ParamCoordSurf : public ParamCoord +{ + +public: + + ParamCoordSurf(GEntity *ge); + int nCoord(MVertex* vert) { return 2; } + virtual SPoint3 getUvw(MVertex* vert); + virtual SPoint3 uvw2Xyz(MVertex* vert, const SPoint3 &uvw); + virtual void gXyz2gUvw(MVertex* vert, const SPoint3 &uvw, const SPoint3 &gXyz, SPoint3 &gUvw); + virtual void gXyz2gUvw(MVertex* vert, const SPoint3 &uvw, const std::vector<SPoint3> &gXyz, std::vector<SPoint3> &gUvw); + +private: + + GFace *_gf; + +}; + + + +class ParamCoordParent : public ParamCoord +{ + +public: + + int nCoord(MVertex* vert) { return vert->onWhat()->dim(); } + virtual SPoint3 getUvw(MVertex* vert); + virtual SPoint3 uvw2Xyz(MVertex* vert, const SPoint3 &uvw); + virtual void gXyz2gUvw(MVertex* vert, const SPoint3 &uvw, const SPoint3 &gXyz, SPoint3 &gUvw); + virtual void gXyz2gUvw(MVertex* vert, const SPoint3 &uvw, const std::vector<SPoint3> &gXyz, std::vector<SPoint3> &gUvw); + +}; + + + +#endif