diff --git a/Common/GmshDefines.h b/Common/GmshDefines.h index a4e360daaa272678f1c3363ce209a9e85db74e7c..c85fe0d7908dee82fd193808c17c51733e33ae86 100644 --- a/Common/GmshDefines.h +++ b/Common/GmshDefines.h @@ -6,6 +6,8 @@ #ifndef _GMSH_DEFINES_H_ #define _GMSH_DEFINES_H_ +//#define METRICSHAPEMEASURE + // IO file formats (numbers should not be changed) #define FORMAT_MSH 1 #define FORMAT_UNV 2 diff --git a/Fltk/graphicWindow.cpp b/Fltk/graphicWindow.cpp index 9ec0a454626f20e7f043129bdf827e97255284c4..3909284927e03dca7900734da84211f3be175764 100644 --- a/Fltk/graphicWindow.cpp +++ b/Fltk/graphicWindow.cpp @@ -1624,7 +1624,11 @@ static std::vector<std::string> getInfoStrings(MElement *ele) { std::ostringstream sstream; sstream << " Quality: " +#ifdef METRICSHAPEMEASURE + << "metric = " << ele->metricShapeMeasure() << " " +#else << "rho = " << ele->rhoShapeMeasure() << " " +#endif << "gamma = " << ele->gammaShapeMeasure() << " " << "eta = " << ele->etaShapeMeasure(); info.push_back(sstream.str()); diff --git a/Fltk/statisticsWindow.cpp b/Fltk/statisticsWindow.cpp index 455227cd197ae6c2ba3879bc120e278094844750..0feddfb744c59b17dea9ac307d526297369281c6 100644 --- a/Fltk/statisticsWindow.cpp +++ b/Fltk/statisticsWindow.cpp @@ -75,7 +75,14 @@ static void statistics_histogram_cb(Fl_Widget *w, void *data) else if(name == "Eta3D") d[e->getNum()].push_back(e->etaShapeMeasure()); else if(name == "Rho3D") - d[e->getNum()].push_back(e->rhoShapeMeasure()); +#ifdef METRICSHAPEMEASURE + if (e->getDim() == 3) + d[e->getNum()].push_back(e->metricShapeMeasure()); + else + d[e->getNum()].push_back(1); +#else + d[e->getNum()].push_back(e->rhoShapeMeasure()); +#endif else d[e->getNum()].push_back(e->distoShapeMeasure()); } diff --git a/Geo/GModelVertexArrays.cpp b/Geo/GModelVertexArrays.cpp index 52f7600f90e25ae0710046877bbc3b5ee27dffff..bae77544602b4d41370fe0d9db7929ca109ecaf4 100644 --- a/Geo/GModelVertexArrays.cpp +++ b/Geo/GModelVertexArrays.cpp @@ -104,7 +104,11 @@ bool isElementVisible(MElement *ele) if(CTX::instance()->mesh.qualityType == 3) q = ele->distoShapeMeasure(); else if(CTX::instance()->mesh.qualityType == 2) +#ifdef METRICSHAPEMEASURE + q = ele->metricShapeMeasure(); +#else q = ele->rhoShapeMeasure(); +#endif else if(CTX::instance()->mesh.qualityType == 1) q = ele->etaShapeMeasure(); else diff --git a/Geo/MElement.cpp b/Geo/MElement.cpp index 108be656e24437a6fade04c53840970a65c5cb49..40b6164f7987041bb510727cf865a07b4c614d46 100644 --- a/Geo/MElement.cpp +++ b/Geo/MElement.cpp @@ -22,6 +22,7 @@ #include "GEntity.h" #include "StringUtils.h" #include "Numeric.h" +#include "MetricBasis.h" #include "Context.h" #define SQU(a) ((a)*(a)) @@ -112,6 +113,11 @@ double MElement::rhoShapeMeasure() return 0.; } +double MElement::metricShapeMeasure() +{ + return MetricBasis::minRCorner(this); +} + double MElement::maxDistToStraight() { const nodalBasis *lagBasis = getFunctionSpace(); @@ -924,7 +930,11 @@ void MElement::writePOS(FILE *fp, bool printElementary, bool printElementNumber, } } if(printRho){ +#ifdef METRICSHAPEMEASURE + double rho = metricShapeMeasure(); +#else double rho = rhoShapeMeasure(); +#endif for(int i = 0; i < n; i++){ if(first) first = false; else fprintf(fp, ","); fprintf(fp, "%g", rho); diff --git a/Geo/MElement.h b/Geo/MElement.h index db684dbd13b84267e0771d43e201ff9183e12303..90772a261fa47851a4fc5c25b7d53dc97aaf5ca6 100644 --- a/Geo/MElement.h +++ b/Geo/MElement.h @@ -191,6 +191,7 @@ class MElement } virtual double angleShapeMeasure() { return 1.0; } virtual void scaledJacRange(double &jmin, double &jmax, GEntity *ge = 0); + virtual double metricShapeMeasure(); // get the radius of the inscribed circle/sphere if it exists, // otherwise get the minimum radius of all the circles/spheres diff --git a/Mesh/Generator.cpp b/Mesh/Generator.cpp index c1fc939494ed6d2386b90273709ed74a070dfa55..6c3999989ccd48cf172497b41999c3f41e19d6a4 100644 --- a/Mesh/Generator.cpp +++ b/Mesh/Generator.cpp @@ -64,10 +64,19 @@ static void GetQualityMeasure(std::vector<T*> &ele, eta += e; etaMin = std::min(etaMin, e); etaMax = std::max(etaMax, e); +#ifdef METRICSHAPEMEASURE + static int aa = 0; + if (++aa == 1) Msg::Warning("Computing metric shape measure instead of rho shape measure !"); + double r = ele[i]->metricShapeMeasure(); + rho += r; + rhoMin = std::min(rhoMin, r); + rhoMax = std::max(rhoMax, r); +#else double r = ele[i]->rhoShapeMeasure(); rho += r; rhoMin = std::min(rhoMin, r); rhoMax = std::max(rhoMax, r); +#endif double jmin,jmax; ele[i]->scaledJacRange(jmin,jmax); double d = jmin; disto += d; diff --git a/Numeric/BasisFactory.cpp b/Numeric/BasisFactory.cpp index d39b63f57cc0050abd138748d70da6c3da6ded64..b2906da4c8888cafd4297ab46bc9f61f8762c3a6 100644 --- a/Numeric/BasisFactory.cpp +++ b/Numeric/BasisFactory.cpp @@ -13,10 +13,10 @@ #include "MElement.h" std::map<int, nodalBasis*> BasisFactory::fs; -std::map<int, JacobianBasis*> BasisFactory::js; std::map<int, MetricBasis*> BasisFactory::ms; BasisFactory::Cont_bezierBasis BasisFactory::bs; BasisFactory::Cont_gradBasis BasisFactory::gs; +BasisFactory::Cont_jacBasis BasisFactory::js; const nodalBasis* BasisFactory::getNodalBasis(int tag) { @@ -67,14 +67,15 @@ const nodalBasis* BasisFactory::getNodalBasis(int tag) return inserted.first->second; } -const JacobianBasis* BasisFactory::getJacobianBasis(int tag) +const JacobianBasis* BasisFactory::getJacobianBasis(int tag, int order) { - std::map<int, JacobianBasis*>::const_iterator it = js.find(tag); + std::pair<int, int> key(tag, order); + Cont_jacBasis::iterator it = js.find(key); if (it != js.end()) return it->second; - JacobianBasis* J = new JacobianBasis(tag); - js.insert(std::make_pair(tag, J)); + JacobianBasis* J = new JacobianBasis(tag, order); + js.insert(std::make_pair(key, J)); return J; } @@ -122,13 +123,27 @@ void BasisFactory::clearAll() } fs.clear(); - std::map<int, JacobianBasis*>::iterator itJ = js.begin(); + std::map<int, MetricBasis*>::iterator itM = ms.begin(); + while (itM != ms.end()) { + delete itM->second; + itM++; + } + ms.clear(); + + Cont_jacBasis::iterator itJ = js.begin(); while (itJ != js.end()) { delete itJ->second; itJ++; } js.clear(); + Cont_gradBasis::iterator itG = gs.begin(); + while (itG != gs.end()) { + delete itG->second; + itG++; + } + gs.clear(); + Cont_bezierBasis::iterator itB = bs.begin(); while (itB != bs.end()) { delete itB->second; diff --git a/Numeric/BasisFactory.h b/Numeric/BasisFactory.h index 82b5c3731aecc9ea79abafe0e01ef9a5c6c1b975..f2f4e41ee97f647c315a347a2e6d5655af6482dc 100644 --- a/Numeric/BasisFactory.h +++ b/Numeric/BasisFactory.h @@ -15,13 +15,14 @@ class BasisFactory { + typedef std::map<std::pair<int, int>, JacobianBasis*> Cont_jacBasis; typedef std::map<std::pair<int, int>, bezierBasis*> Cont_bezierBasis; typedef std::map<std::pair<int, int>, GradientBasis*> Cont_gradBasis; private: static std::map<int, nodalBasis*> fs; - static std::map<int, JacobianBasis*> js; static std::map<int, MetricBasis*> ms; + static Cont_jacBasis js; static Cont_gradBasis gs; static Cont_bezierBasis bs; // store bezier bases by parentType and order (no serendipity..) @@ -29,7 +30,11 @@ class BasisFactory public: // Caution: the returned pointer can be NULL static const nodalBasis* getNodalBasis(int tag); - static const JacobianBasis* getJacobianBasis(int tag); + static const JacobianBasis* getJacobianBasis(int tag, int order); + static const JacobianBasis* getJacobianBasis(int tag) { + return getJacobianBasis(ElementType::ParentTypeFromTag(tag), + ElementType::OrderFromTag(tag) ); + } static const MetricBasis* getMetricBasis(int tag); static const GradientBasis* getGradientBasis(int tag, int order); diff --git a/Numeric/MetricBasis.cpp b/Numeric/MetricBasis.cpp index 093a1d3d86f8270795734a65aa1481c92872b6f9..f484e683ee4e080901981474129b35de07a6161d 100644 --- a/Numeric/MetricBasis.cpp +++ b/Numeric/MetricBasis.cpp @@ -75,6 +75,107 @@ double MetricBasis::boundMinR(MElement *el) return metric->getBoundRmin(el, md, dummy); } +double MetricBasis::minRCorner(MElement *el) +{ + int tag = el->getTypeForMSH(); + int order = 1; + if (el->getType() == TYPE_TRI || el->getType() == TYPE_TET) order = 0; + + const GradientBasis *gradients = BasisFactory::getGradientBasis(tag, 1); + const JacobianBasis *jacobian = BasisFactory::getJacobianBasis(tag, 1); + + int nSampPnts = jacobian->getNumJacNodes(); + if (el->getType() == TYPE_PYR) nSampPnts = 4; + + int nMapping = gradients->getNumMapNodes(); + fullMatrix<double> nodes(nMapping, 3); + el->getNodesCoord(nodes); + + // Metric coefficients + fullMatrix<double> metCoeffLag; + + switch (el->getDim()) { + case 0 : + return -1.; + case 1 : + case 2 : + Msg::Fatal("not implemented"); + break; + + case 3 : + { + fullMatrix<double> dxyzdX(nSampPnts,3), dxyzdY(nSampPnts,3), dxyzdZ(nSampPnts,3); + gradients->getGradientsFromNodes(nodes, &dxyzdX, &dxyzdY, &dxyzdZ); + + metCoeffLag.resize(nSampPnts, 7); + for (int i = 0; i < nSampPnts; i++) { + const double &dxdX = dxyzdX(i,0), &dydX = dxyzdX(i,1), &dzdX = dxyzdX(i,2); + const double &dxdY = dxyzdY(i,0), &dydY = dxyzdY(i,1), &dzdY = dxyzdY(i,2); + const double &dxdZ = dxyzdZ(i,0), &dydZ = dxyzdZ(i,1), &dzdZ = dxyzdZ(i,2); + const double dvxdX = dxdX*dxdX + dydX*dydX + dzdX*dzdX; + const double dvxdY = dxdY*dxdY + dydY*dydY + dzdY*dzdY; + const double dvxdZ = dxdZ*dxdZ + dydZ*dydZ + dzdZ*dzdZ; + metCoeffLag(i, 0) = (dvxdX + dvxdY + dvxdZ) / 3; + metCoeffLag(i, 1) = dvxdX - metCoeffLag(i, 0); + metCoeffLag(i, 2) = dvxdY - metCoeffLag(i, 0); + metCoeffLag(i, 3) = dvxdZ - metCoeffLag(i, 0); + const double fact = std::sqrt(2); + metCoeffLag(i, 4) = fact * (dxdX*dxdY + dydX*dydY + dzdX*dzdY); + metCoeffLag(i, 5) = fact * (dxdZ*dxdY + dydZ*dydY + dzdZ*dzdY); + metCoeffLag(i, 6) = fact * (dxdX*dxdZ + dydX*dydZ + dzdX*dzdZ); + } + } + break; + } + + // Jacobian coefficients + fullVector<double> jacLag(jacobian->getNumJacNodes()); + jacobian->getSignedJacobian(nodes, jacLag); + + // Compute min_corner(R) + double Rmin = 1.; + + for (int i = 0; i < nSampPnts; ++i) { + if (jacLag(i) <= 0.) { + Rmin = std::min(Rmin, 0.); + } + else { + double q = metCoeffLag(i, 0); + double p = 0; + for (int k = 1; k < 7; ++k) { + p += pow_int(metCoeffLag(i, k), 2); + } + p = std::sqrt(p/6); + const double a = q/p; + if (a > 1e4) { + Rmin = std::min(Rmin, std::sqrt((a - std::sqrt(3)) / (a + std::sqrt(3)))); + } + else { + const double x = .5 * (jacLag(i)/p/p*jacLag(i)/p - a*a*a + 3*a); + if (x > 1.1 || x < -1.1) { + Msg::Error("x much smaller than -1 or much greater than 1"); + } + + double tmpR; + if (x >= 1) + tmpR = (a - 1) / (a + 2); + else if (x <= -1) + tmpR = (a - 2) / (a + 1); + else { + const double phi = std::acos(x)/3; + tmpR = (a + 2*std::cos(phi + 2*M_PI/3)) / (a + 2*std::cos(phi)); + } + if (tmpR < 0) { + if (tmpR < -1e-7) Msg::Error("negative R !!!"); + else tmpR = 0; + } + Rmin = std::min(Rmin, std::sqrt(tmpR)); + } + } + } + return Rmin; +} + double MetricBasis::sampleR(MElement *el, int order) { MetricBasis *metric = (MetricBasis*)BasisFactory::getMetricBasis(el->getTypeForMSH()); diff --git a/Numeric/MetricBasis.h b/Numeric/MetricBasis.h index f1cf8e061caa22b7e18c4c471520954738d2bcd9..2202b903fc36af447734a3c2d684001ea808c65c 100644 --- a/Numeric/MetricBasis.h +++ b/Numeric/MetricBasis.h @@ -66,6 +66,7 @@ public: double getMinR(MElement*, MetricData*&, int) const; bool notStraight(MElement*, double &metric, int order) const; static double boundMinR(MElement *el); + static double minRCorner(MElement *el); static double sampleR(MElement *el, int order); //double getBoundRmin(int, MElement**, double*); //static double boundRmin(int, MElement**, double*, bool sameType = false);